diff --git a/src/localization.c b/src/localization.c index 6b4c908e..2c90ce44 100644 --- a/src/localization.c +++ b/src/localization.c @@ -47,13 +47,13 @@ const loc_parse parse_cmd[9] = { // Translation name and Windows LCIDs it should apply to { 'l', LC_LOCALE, "su" }, // l "English (US)" 0x0009,0x1009 // Base translation to add on top of (eg. "English (UK)" can be used to build on top of "English (US)" - { 'b', LC_BASE, "s" }, // b "English (US)" // TODO: NOT IMPLEMENTED YET + { 'b', LC_BASE, "s" }, // b "English (US)" // Version to use for the localization commandset and API { 'v', LC_VERSION, "ii" }, // v 1.0 // TODO: NOT IMPLEMENTED YET // Translate the text control associated with an ID { 't', LC_TEXT, "cs" }, // t IDC_CONTROL "Translation" // Set the section/dialog to which the next commands should apply - { 'g', LC_GROUP, "c" }, // g IDD_DIALOG + { 'g', LC_GROUP, "c" }, // g IDD_DIALOG // Resize a dialog (dx dy pixel increment) { 's', LC_SIZE, "cii" }, // s IDC_CONTROL +10 +10 // Move a dialog (dx dy pixed displacement) @@ -67,8 +67,9 @@ const loc_parse parse_cmd[9] = { }; /* Globals */ -int loc_line_nr; +int loc_line_nr; struct list_head locale_list = {NULL, NULL}; +char *loc_filename = NULL, *embedded_loc_filename = "[embedded] rufus.loc"; /* * Add a localization command to a dialog/section @@ -142,6 +143,7 @@ BOOL dispatch_loc_cmd(loc_cmd* lcmd) { size_t i; static int dlg_index = 0; + loc_cmd* base_locale = NULL; if (lcmd == NULL) return FALSE; @@ -179,6 +181,12 @@ BOOL dispatch_loc_cmd(loc_cmd* lcmd) luprintf("GOT VERSION: %d.%d\n", lcmd->num[0], lcmd->num[1]); free_loc_cmd(lcmd); break; + case LC_BASE: + uprintf("localization: using locale base '%s'", lcmd->txt[0]); + base_locale = get_locale_from_name(lcmd->txt[0]); + get_loc_data_file(NULL, (long)base_locale->num[0], (long)base_locale->num[1], base_locale->line_nr); + free_loc_cmd(lcmd); + break; default: free_loc_cmd(lcmd); break; @@ -281,7 +289,7 @@ void reset_localization(int dlg_id) * Like printf, this call takes a variable number of argument, and uses * the message ID to identify the formatted message to use. * Uses a rolling list of buffers to allow concurrency - * TODO: use dynamic realloc'd buffer in case 2048 is not enough + * TODO: use dynamic realloc'd buffer in case LOC_MESSAGE_SIZE is not enough */ char* lmprintf(int msg_id, ...) { @@ -299,7 +307,6 @@ char* lmprintf(int msg_id, ...) } if (format == NULL) { - // TODO: fallback to English! safe_sprintf(buf[buf_id], LOC_MESSAGE_SIZE-1, "MSG_%03d UNTRANSLATED", msg_id - MSG_000); } else { va_start(args, msg_id); diff --git a/src/localization.h b/src/localization.h index f98b3224..23d6919b 100644 --- a/src/localization.h +++ b/src/localization.h @@ -26,9 +26,6 @@ #define LOC_MESSAGE_NB 8 #define LOC_MESSAGE_SIZE 2048 -// TODO: display control name on mouseover -// Link to http://www.resedit.net/ - #define luprint(msg) uprintf("%s(%d): " msg "\n", loc_filename, loc_line_nr) #define luprintf(msg, ...) uprintf("%s(%d): " msg "\n", loc_filename, loc_line_nr, __VA_ARGS__) @@ -144,7 +141,7 @@ typedef struct loc_dlg_list_struct { extern const loc_parse parse_cmd[9]; extern struct list_head locale_list; int loc_line_nr; -char loc_filename[MAX_PATH]; +char *loc_filename, *embedded_loc_filename; void free_loc_cmd(loc_cmd* lcmd); BOOL dispatch_loc_cmd(loc_cmd* lcmd); diff --git a/src/parser.c b/src/parser.c index 45024a22..2702f1c7 100644 --- a/src/parser.c +++ b/src/parser.c @@ -187,21 +187,26 @@ static void get_loc_data_line(char* line) } /* - * Parse a localization file, to construct the list of available locales. - * The locale file must be UTF-8 with NO BOM. - * TODO: merge this with the next call or factorize fopen + * Open a localization file and store its file name, with special case + * when dealing with the embedded loc file. */ -BOOL get_supported_locales(const char* filename) +FILE* open_loc_file(const char* filename) { - wchar_t *wfilename = NULL; FILE* fd = NULL; - BOOL r = FALSE; - char line[1024]; - size_t i; - loc_cmd *lcmd = NULL, *last_lcmd = NULL; - long end_of_block; - - safe_strcpy(loc_filename, sizeof(loc_filename), filename); + wchar_t *wfilename = NULL; + const char* tmp_ext = ".tmp"; + + if (filename == NULL) + return NULL; + + if (loc_filename != embedded_loc_filename) { + safe_free(loc_filename); + } + if (safe_strcmp(tmp_ext, &filename[safe_strlen(filename)-4]) == 0) { + loc_filename = embedded_loc_filename; + } else { + loc_filename = safe_strdup(filename); + } wfilename = utf8_to_wchar(filename); if (wfilename == NULL) { uprintf("localization: could not convert '%s' filename to UTF-16\n", filename); @@ -210,9 +215,30 @@ BOOL get_supported_locales(const char* filename) fd = _wfopen(wfilename, L"r"); if (fd == NULL) { uprintf("localization: could not open '%s'\n", filename); - goto out; } +out: + safe_free(wfilename); + return fd; +} + +/* + * Parse a localization file, to construct the list of available locales. + * The locale file must be UTF-8 with NO BOM. + */ +BOOL get_supported_locales(const char* filename) +{ + FILE* fd = NULL; + BOOL r = FALSE; + char line[1024]; + size_t i; + loc_cmd *lcmd = NULL, *last_lcmd = NULL; + long end_of_block; + + fd = open_loc_file(filename); + if (fd == NULL) + goto out; + loc_line_nr = 0; line[0] = 0; free_locale_list(); @@ -251,41 +277,39 @@ BOOL get_supported_locales(const char* filename) out: if (fd != NULL) fclose(fd); - safe_free(wfilename); return r; } /* * Parse a locale section in a localization file (UTF-8, no BOM) + * NB: this call is reentrant for the "base" command support */ char* get_loc_data_file(const char* filename, long offset, long end_offset, int start_line) { - wchar_t *wfilename = NULL; size_t bufsize = 1024; - FILE* fd = NULL; + static FILE* fd = NULL; char *ret = NULL, *buf = NULL; size_t i = 0; int r = 0, line_nr_incr = 1; int c = 0, eol_char = 0; - BOOL eol = FALSE, escape_sequence = FALSE; + int old_loc_line_nr; + BOOL eol = FALSE, escape_sequence = FALSE, reentrant = (fd != NULL); + long cur_offset = -1; - if ((filename == NULL) || (filename[0] == 0)) - return NULL; - - free_dialog_list(); + if (reentrant) { + // Called, from a 'b' command - no need to reopen the file, + // just save the current offset and current line number + cur_offset = ftell(fd); + old_loc_line_nr = loc_line_nr; + } else { + if ((filename == NULL) || (filename[0] == 0)) + return NULL; + free_dialog_list(); + fd = open_loc_file(filename); + if (fd == NULL) + goto out; + } loc_line_nr = start_line; - safe_strcpy(loc_filename, sizeof(loc_filename), filename); - wfilename = utf8_to_wchar(filename); - if (wfilename == NULL) { - uprintf("localization: could not convert '%s' filename to UTF-16\n", filename); - goto out; - } - fd = _wfopen(wfilename, L"r"); - if (fd == NULL) { - uprintf("localization: could not open '%s'\n", filename); - goto out; - } - buf = (char*) malloc(bufsize); if (buf == NULL) { uprintf("localization: could not allocate line buffer\n"); @@ -398,9 +422,14 @@ char* get_loc_data_file(const char* filename, long offset, long end_offset, int } while(1); out: - if (fd != NULL) + // Don't close on a reentrant call + if (reentrant) { + fseek(fd, cur_offset, SEEK_SET); + loc_line_nr = old_loc_line_nr; + } else if (fd != NULL) { fclose(fd); - safe_free(wfilename); + fd = NULL; + } safe_free(buf); return ret; } diff --git a/src/rufus.c b/src/rufus.c index 57933f33..62a04799 100644 --- a/src/rufus.c +++ b/src/rufus.c @@ -1876,7 +1876,7 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine BOOL attached_console = FALSE; BYTE* loc_data; DWORD loc_size, Size; - char tmp_path[MAX_PATH], loc_file[MAX_PATH] = ""; + char tmp_path[MAX_PATH], loc_file[MAX_PATH] = "", *locale_name = NULL; char** argv = NULL; wchar_t **wenv, **wargv; PF_DECL(__wgetmainargs); @@ -1893,33 +1893,6 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine uprintf("*** " APPLICATION_NAME " init ***\n"); - // Init localization - init_localization(); - loc_data = (BYTE*)GetResource(hMainInstance, MAKEINTRESOURCEA(IDR_LC_RUFUS_LOC), _RT_RCDATA, "rufus.loc", &loc_size, FALSE); - GetTempPathU(sizeof(tmp_path), tmp_path); - GetTempFileNameU(tmp_path, APPLICATION_NAME, 0, loc_file); - - hFile = CreateFileU(loc_file, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, - NULL, CREATE_ALWAYS, 0, 0); - if ((hFile == INVALID_HANDLE_VALUE)|| (!WriteFile(hFile, loc_data, loc_size, &Size, 0)) || (loc_size != Size)) { - safe_closehandle(hFile); - uprintf("localization: unable to extract '%s': %s.\n", loc_file, WindowsErrorString()); - } else { - safe_closehandle(hFile); - uprintf("localization: extracted data to '%s'\n", loc_file); - // TODO: Add a control for "X translation by Y" - if ( (!get_supported_locales(loc_file)) -// || ((selected_locale = get_locale_from_lcid(GetUserDefaultLCID())) == NULL) ) { - || ((selected_locale = get_locale_from_name("French")) == NULL) ) { - uprintf("FATAL: Could not access default locale!\n"); - MessageBoxU(NULL, "The default locale data is missing. This application will now exit.", - "Fatal error", MB_ICONSTOP); - goto out; - } - uprintf("localization: using locale '%s'\n", selected_locale->txt[0]); - get_loc_data_file(loc_file, (long)selected_locale->num[0], (long)selected_locale->num[1], selected_locale->line_nr); - } - // Reattach the console, if we were started from commandline if (AttachConsole(ATTACH_PARENT_PROCESS) != 0) { attached_console = TRUE; @@ -1930,7 +1903,7 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine printf("\n"); } - // We have to process the arguments before we acquire the lock + // We have to process the arguments before we acquire the lock and process the locale PF_INIT(__wgetmainargs, msvcrt); if (pf__wgetmainargs != NULL) { pf__wgetmainargs(&argc, &wargv, &wenv, 1, &si); @@ -1942,7 +1915,7 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine wait_for_mutex = 150; // Try to acquire the mutex for 15 seconds } - while ((opt = getopt_long(argc, argv, "?fhi:w:", long_options, &option_index)) != EOF) + while ((opt = getopt_long(argc, argv, "?fhi:w:l:", long_options, &option_index)) != EOF) switch (opt) { case 'f': enable_fixed_disks = TRUE; @@ -1955,6 +1928,10 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine printf("Could not find ISO image '%s'\n", optarg); } break; + case 'l': + // TODO: accept a locale code such as 0x409 + locale_name = optarg; + break; case 'w': wait_for_mutex = atoi(optarg); break; @@ -1968,6 +1945,44 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine uprintf("unable to access UTF-16 args"); } + // Retrieve the current application directory + GetCurrentDirectoryU(MAX_PATH, app_dir); + + // Init localization + init_localization(); + // Seek for a loc file in the current directory + if (GetFileAttributesU("rufus.loc") == INVALID_FILE_ATTRIBUTES) { + uprintf("loc file not found in current directory - embedded one will be used"); + + loc_data = (BYTE*)GetResource(hMainInstance, MAKEINTRESOURCEA(IDR_LC_RUFUS_LOC), _RT_RCDATA, "rufus.loc", &loc_size, FALSE); + GetTempPathU(sizeof(tmp_path), tmp_path); + GetTempFileNameU(tmp_path, APPLICATION_NAME, 0, loc_file); + + hFile = CreateFileU(loc_file, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ|FILE_SHARE_WRITE, + NULL, CREATE_ALWAYS, 0, 0); + if ((hFile == INVALID_HANDLE_VALUE)|| (!WriteFile(hFile, loc_data, loc_size, &Size, 0)) || (loc_size != Size)) { + safe_closehandle(hFile); + uprintf("localization: unable to extract '%s': %s.\n", loc_file, WindowsErrorString()); + } else { + safe_closehandle(hFile); + uprintf("localization: extracted data to '%s'\n", loc_file); + } + } else { + safe_sprintf(loc_file, sizeof(loc_file), "%s\\rufus.loc", app_dir); + uprintf("using external loc file '%s'", loc_file); + } + + if ( (!get_supported_locales(loc_file)) + || ((selected_locale = ((locale_name == NULL)?get_locale_from_lcid(GetUserDefaultLCID()):get_locale_from_name(locale_name))) == NULL) ) { + uprintf("FATAL: Could not access locale!\n"); + MessageBoxU(NULL, "The locale data is missing. This application will now exit.", + "Fatal error", MB_ICONSTOP); + goto out; + } + uprintf("localization: using locale '%s'\n", selected_locale->txt[0]); + get_loc_data_file(loc_file, (long)selected_locale->num[0], (long)selected_locale->num[1], selected_locale->line_nr); + + // Prevent 2 applications from running at the same time, unless "/W" is passed as an option // in which case we wait for the mutex to be relinquished if ((safe_strlen(lpCmdLine)==2) && (lpCmdLine[0] == '/') && (lpCmdLine[1] == 'W')) @@ -1994,9 +2009,6 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine uprintf("Could not load RichEdit library - some dialogs may not display: %s\n", WindowsErrorString()); } - // Retrieve the current application directory - GetCurrentDirectoryU(MAX_PATH, app_dir); - // Set the Windows version nWindowsVersion = DetectWindowsVersion();