X-Git-Url: http://wien.tomnetworks.com/gitweb/?a=blobdiff_plain;f=mono%2Fio-layer%2Fversioninfo.c;h=2dc95f326156918a028873d82738fb35d6cbb933;hb=46097ccbb1dea1e60c8628cbe116f63bd17e7209;hp=a95676206d847bfc8a3538fe6c19ba7b56283f32;hpb=b97f88c9981d4735fa8e0250f173ea025d75a239;p=mono.git diff --git a/mono/io-layer/versioninfo.c b/mono/io-layer/versioninfo.c index a95676206d8..2dc95f32615 100644 --- a/mono/io-layer/versioninfo.c +++ b/mono/io-layer/versioninfo.c @@ -11,7 +11,6 @@ #include #include #include -#include #include #include #include @@ -24,12 +23,20 @@ #include #include #include +#include -#undef DEBUG +#if 0 +// #define DEBUG(...) g_message(__VA_ARGS__) +#else +#define DEBUG(...) +#endif + +#define ALIGN32(ptr) ptr = (gpointer)((char *)ptr + 3); ptr = (gpointer)((char *)ptr - ((gsize)ptr & 3)); -static WapiImageSectionHeader *get_enclosing_section_header (guint32 rva, WapiImageNTHeaders *nt_headers) +static WapiImageSectionHeader * +get_enclosing_section_header (guint32 rva, WapiImageNTHeaders32 *nt_headers) { - WapiImageSectionHeader *section = IMAGE_FIRST_SECTION (nt_headers); + WapiImageSectionHeader *section = _WAPI_IMAGE_FIRST_SECTION32 (nt_headers); guint32 i; for (i = 0; i < GUINT16_FROM_LE (nt_headers->FileHeader.NumberOfSections); i++, section++) { @@ -47,8 +54,11 @@ static WapiImageSectionHeader *get_enclosing_section_header (guint32 rva, WapiIm return(NULL); } -static gpointer get_ptr_from_rva (guint32 rva, WapiImageNTHeaders *ntheaders, - gpointer file_map) +/* This works for both 32bit and 64bit files, as the differences are + * all after the section header block + */ +static gpointer +get_ptr_from_rva (guint32 rva, WapiImageNTHeaders32 *ntheaders, gpointer file_map) { WapiImageSectionHeader *section_header; guint32 delta; @@ -64,12 +74,13 @@ static gpointer get_ptr_from_rva (guint32 rva, WapiImageNTHeaders *ntheaders, return((guint8 *)file_map + rva - delta); } -static gpointer scan_resource_dir (WapiImageResourceDirectory *root, - WapiImageNTHeaders *nt_headers, - gpointer file_map, - WapiImageResourceDirectoryEntry *entry, - int level, guint32 res_id, guint32 lang_id, - guint32 *size) +static gpointer +scan_resource_dir (WapiImageResourceDirectory *root, + WapiImageNTHeaders32 *nt_headers, + gpointer file_map, + WapiImageResourceDirectoryEntry *entry, + int level, guint32 res_id, guint32 lang_id, + guint32 *size) { WapiImageResourceDirectoryEntry swapped_entry; gboolean is_string, is_dir; @@ -138,12 +149,13 @@ static gpointer scan_resource_dir (WapiImageResourceDirectory *root, } } -static gpointer find_pe_file_resources (gpointer file_map, guint32 map_size, - guint32 res_id, guint32 lang_id, - guint32 *size) +static gpointer +find_pe_file_resources32 (gpointer file_map, guint32 map_size, + guint32 res_id, guint32 lang_id, + guint32 *size) { WapiImageDosHeader *dos_header; - WapiImageNTHeaders *nt_headers; + WapiImageNTHeaders32 *nt_headers; WapiImageResourceDirectory *resource_dir; WapiImageResourceDirectoryEntry *resource_dir_entry; guint32 resource_rva, entries, i; @@ -151,30 +163,22 @@ static gpointer find_pe_file_resources (gpointer file_map, guint32 map_size, dos_header = (WapiImageDosHeader *)file_map; if (dos_header->e_magic != IMAGE_DOS_SIGNATURE) { -#ifdef DEBUG - g_message ("%s: Bad dos signature 0x%x", __func__, - dos_header->e_magic); -#endif + DEBUG ("%s: Bad dos signature 0x%x", __func__, dos_header->e_magic); SetLastError (ERROR_INVALID_DATA); return(NULL); } - if (map_size < sizeof(WapiImageNTHeaders) + GUINT32_FROM_LE (dos_header->e_lfanew)) { -#ifdef DEBUG - g_message ("%s: File is too small: %d", __func__, map_size); -#endif + if (map_size < sizeof(WapiImageNTHeaders32) + GUINT32_FROM_LE (dos_header->e_lfanew)) { + DEBUG ("%s: File is too small: %d", __func__, map_size); SetLastError (ERROR_BAD_LENGTH); return(NULL); } - nt_headers = (WapiImageNTHeaders *)((guint8 *)file_map + GUINT32_FROM_LE (dos_header->e_lfanew)); + nt_headers = (WapiImageNTHeaders32 *)((guint8 *)file_map + GUINT32_FROM_LE (dos_header->e_lfanew)); if (nt_headers->Signature != IMAGE_NT_SIGNATURE) { -#ifdef DEBUG - g_message ("%s: Bad NT signature 0x%x", __func__, - nt_headers->Signature); -#endif + DEBUG ("%s: Bad NT signature 0x%x", __func__, nt_headers->Signature); SetLastError (ERROR_INVALID_DATA); return(NULL); @@ -188,18 +192,16 @@ static gpointer find_pe_file_resources (gpointer file_map, guint32 map_size, } if (resource_rva == 0) { -#ifdef DEBUG - g_message ("%s: No resources in file!", __func__); -#endif + DEBUG ("%s: No resources in file!", __func__); + SetLastError (ERROR_INVALID_DATA); return(NULL); } - resource_dir = (WapiImageResourceDirectory *)get_ptr_from_rva (resource_rva, nt_headers, file_map); + resource_dir = (WapiImageResourceDirectory *)get_ptr_from_rva (resource_rva, (WapiImageNTHeaders32 *)nt_headers, file_map); if (resource_dir == NULL) { -#ifdef DEBUG - g_message ("%s: Can't find resource directory", __func__); -#endif + DEBUG ("%s: Can't find resource directory", __func__); + SetLastError (ERROR_INVALID_DATA); return(NULL); } @@ -209,17 +211,110 @@ static gpointer find_pe_file_resources (gpointer file_map, guint32 map_size, for (i = 0; i < entries; i++) { WapiImageResourceDirectoryEntry *direntry = &resource_dir_entry[i]; - ret = scan_resource_dir (resource_dir, nt_headers, file_map, - direntry, 0, res_id, lang_id, size); + ret = scan_resource_dir (resource_dir, + (WapiImageNTHeaders32 *)nt_headers, + file_map, direntry, 0, res_id, + lang_id, size); if (ret != NULL) { return(ret); } } + + return(NULL); +} + +static gpointer +find_pe_file_resources64 (gpointer file_map, guint32 map_size, + guint32 res_id, guint32 lang_id, + guint32 *size) +{ + WapiImageDosHeader *dos_header; + WapiImageNTHeaders64 *nt_headers; + WapiImageResourceDirectory *resource_dir; + WapiImageResourceDirectoryEntry *resource_dir_entry; + guint32 resource_rva, entries, i; + gpointer ret = NULL; + + dos_header = (WapiImageDosHeader *)file_map; + if (dos_header->e_magic != IMAGE_DOS_SIGNATURE) { + DEBUG ("%s: Bad dos signature 0x%x", __func__, dos_header->e_magic); + + SetLastError (ERROR_INVALID_DATA); + return(NULL); + } + if (map_size < sizeof(WapiImageNTHeaders64) + GUINT32_FROM_LE (dos_header->e_lfanew)) { + DEBUG ("%s: File is too small: %d", __func__, map_size); + + SetLastError (ERROR_BAD_LENGTH); + return(NULL); + } + + nt_headers = (WapiImageNTHeaders64 *)((guint8 *)file_map + GUINT32_FROM_LE (dos_header->e_lfanew)); + if (nt_headers->Signature != IMAGE_NT_SIGNATURE) { + DEBUG ("%s: Bad NT signature 0x%x", __func__, + nt_headers->Signature); + + SetLastError (ERROR_INVALID_DATA); + return(NULL); + } + + if (nt_headers->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC) { + /* Do 64-bit stuff */ + resource_rva = GUINT32_FROM_LE (((WapiImageNTHeaders64 *)nt_headers)->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress); + } else { + resource_rva = GUINT32_FROM_LE (nt_headers->OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_RESOURCE].VirtualAddress); + } + + if (resource_rva == 0) { + DEBUG ("%s: No resources in file!", __func__); + + SetLastError (ERROR_INVALID_DATA); + return(NULL); + } + + resource_dir = (WapiImageResourceDirectory *)get_ptr_from_rva (resource_rva, (WapiImageNTHeaders32 *)nt_headers, file_map); + if (resource_dir == NULL) { + DEBUG ("%s: Can't find resource directory", __func__); + + SetLastError (ERROR_INVALID_DATA); + return(NULL); + } + + entries = GUINT16_FROM_LE (resource_dir->NumberOfNamedEntries) + GUINT16_FROM_LE (resource_dir->NumberOfIdEntries); + resource_dir_entry = (WapiImageResourceDirectoryEntry *)(resource_dir + 1); + + for (i = 0; i < entries; i++) { + WapiImageResourceDirectoryEntry *direntry = &resource_dir_entry[i]; + ret = scan_resource_dir (resource_dir, + (WapiImageNTHeaders32 *)nt_headers, + file_map, direntry, 0, res_id, + lang_id, size); + if (ret != NULL) { + return(ret); + } + } + return(NULL); } -static gpointer map_pe_file (gunichar2 *filename, guint32 *map_size) +static gpointer +find_pe_file_resources (gpointer file_map, guint32 map_size, + guint32 res_id, guint32 lang_id, + guint32 *size) +{ + /* Figure this out when we support 64bit PE files */ + if (1) { + return find_pe_file_resources32 (file_map, map_size, res_id, + lang_id, size); + } else { + return find_pe_file_resources64 (file_map, map_size, res_id, + lang_id, size); + } +} + +static gpointer +map_pe_file (gunichar2 *filename, gint32 *map_size, void **handle) { gchar *filename_ext; int fd; @@ -233,9 +328,7 @@ static gpointer map_pe_file (gunichar2 *filename, guint32 *map_size) filename_ext = mono_unicode_to_external (filename); if (filename_ext == NULL) { -#ifdef DEBUG - g_message ("%s: unicode conversion returned NULL", __func__); -#endif + DEBUG ("%s: unicode conversion returned NULL", __func__); SetLastError (ERROR_INVALID_NAME); return(NULL); @@ -243,10 +336,7 @@ static gpointer map_pe_file (gunichar2 *filename, guint32 *map_size) fd = _wapi_open (filename_ext, O_RDONLY, 0); if (fd == -1) { -#ifdef DEBUG - g_message ("%s: Error opening file %s: %s", __func__, - filename_ext, strerror (errno)); -#endif + DEBUG ("%s: Error opening file %s: %s", __func__, filename_ext, strerror (errno)); SetLastError (_wapi_get_win32_file_error (errno)); g_free (filename_ext); @@ -255,10 +345,7 @@ static gpointer map_pe_file (gunichar2 *filename, guint32 *map_size) } if (fstat (fd, &statbuf) == -1) { -#ifdef DEBUG - g_message ("%s: Error stat()ing file %s: %s", __func__, - filename_ext, strerror (errno)); -#endif + DEBUG ("%s: Error stat()ing file %s: %s", __func__, filename_ext, strerror (errno)); SetLastError (_wapi_get_win32_file_error (errno)); g_free (filename_ext); @@ -266,13 +353,10 @@ static gpointer map_pe_file (gunichar2 *filename, guint32 *map_size) return(NULL); } *map_size = statbuf.st_size; - + /* Check basic file size */ if (statbuf.st_size < sizeof(WapiImageDosHeader)) { -#ifdef DEBUG - g_message ("%s: File %s is too small: %ld", __func__, - filename_ext, statbuf.st_size); -#endif + DEBUG ("%s: File %s is too small: %lld", __func__, filename_ext, statbuf.st_size); SetLastError (ERROR_BAD_LENGTH); g_free (filename_ext); @@ -280,12 +364,9 @@ static gpointer map_pe_file (gunichar2 *filename, guint32 *map_size) return(NULL); } - file_map = mmap (NULL, statbuf.st_size, PROT_READ, MAP_PRIVATE, fd, 0); - if (file_map == MAP_FAILED) { -#ifdef DEBUG - g_message ("%s: Error mmap()int file %s: %s", __func__, - filename_ext, strerror (errno)); -#endif + file_map = mono_file_map (statbuf.st_size, MONO_MMAP_READ | MONO_MMAP_PRIVATE, fd, 0, handle); + if (file_map == NULL) { + DEBUG ("%s: Error mmap()int file %s: %s", __func__, filename_ext, strerror (errno)); SetLastError (_wapi_get_win32_file_error (errno)); g_free (filename_ext); @@ -295,16 +376,19 @@ static gpointer map_pe_file (gunichar2 *filename, guint32 *map_size) /* Don't need the fd any more */ close (fd); + g_free (filename_ext); return(file_map); } -static void unmap_pe_file (gpointer file_map, guint32 map_size) +static void +unmap_pe_file (gpointer file_map, void *handle) { - munmap (file_map, map_size); + mono_file_unmap (file_map, handle); } -static guint32 unicode_chars (const gunichar2 *str) +static guint32 +unicode_chars (const gunichar2 *str) { guint32 len = 0; @@ -316,7 +400,8 @@ static guint32 unicode_chars (const gunichar2 *str) } while(1); } -static gboolean unicode_compare (const gunichar2 *str1, const gunichar2 *str2) +static gboolean +unicode_compare (const gunichar2 *str1, const gunichar2 *str2) { while (*str1 && *str2) { if (*str1 != *str2) { @@ -332,7 +417,8 @@ static gboolean unicode_compare (const gunichar2 *str1, const gunichar2 *str2) /* compare a little-endian null-terminated utf16 string and a normal string. * Can be used only for ascii or latin1 chars. */ -static gboolean unicode_string_equals (const gunichar2 *str1, const gchar *str2) +static gboolean +unicode_string_equals (const gunichar2 *str1, const gchar *str2) { while (*str1 && *str2) { if (GUINT16_TO_LE (*str1) != *str2) { @@ -356,8 +442,8 @@ typedef struct /* Returns a pointer to the value data, because there's no way to know * how big that data is (value_len is set to zero for most blocks :-( ) */ -static gconstpointer get_versioninfo_block (gconstpointer data, - version_data *block) +static gconstpointer +get_versioninfo_block (gconstpointer data, version_data *block) { block->data_len = GUINT16_FROM_LE (*((guint16 *)data)); data = (char *)data + sizeof(guint16); @@ -373,50 +459,43 @@ static gconstpointer get_versioninfo_block (gconstpointer data, data = ((gunichar2 *)data) + (unicode_chars (block->key) + 1); /* align on a 32-bit boundary */ - data = (gpointer)((char *)data + 3); - data = (gpointer)((char *)data - (GPOINTER_TO_INT (data) & 3)); + ALIGN32 (data); return(data); } -static gconstpointer get_fixedfileinfo_block (gconstpointer data, - version_data *block) +static gconstpointer +get_fixedfileinfo_block (gconstpointer data, version_data *block) { gconstpointer data_ptr; - gint32 data_len; /* signed to guard against underflow */ WapiFixedFileInfo *ffi; data_ptr = get_versioninfo_block (data, block); - data_len = block->data_len; if (block->value_len != sizeof(WapiFixedFileInfo)) { -#ifdef DEBUG - g_message ("%s: FIXEDFILEINFO size mismatch", __func__); -#endif + DEBUG ("%s: FIXEDFILEINFO size mismatch", __func__); return(NULL); } if (!unicode_string_equals (block->key, "VS_VERSION_INFO")) { -#ifdef DEBUG - g_message ("%s: VS_VERSION_INFO mismatch", __func__); -#endif + DEBUG ("%s: VS_VERSION_INFO mismatch", __func__); + return(NULL); } ffi = ((WapiFixedFileInfo *)data_ptr); if ((ffi->dwSignature != VS_FFI_SIGNATURE) || (ffi->dwStrucVersion != VS_FFI_STRUCVERSION)) { -#ifdef DEBUG - g_message ("%s: FIXEDFILEINFO bad signature", __func__); -#endif + DEBUG ("%s: FIXEDFILEINFO bad signature", __func__); + return(NULL); } return(data_ptr); } -static gconstpointer get_varfileinfo_block (gconstpointer data_ptr, - version_data *block) +static gconstpointer +get_varfileinfo_block (gconstpointer data_ptr, version_data *block) { /* data is pointing at a Var block */ @@ -425,33 +504,32 @@ static gconstpointer get_varfileinfo_block (gconstpointer data_ptr, return(data_ptr); } -static gconstpointer get_string_block (gconstpointer data_ptr, - const gunichar2 *string_key, - gpointer *string_value, - guint32 *string_value_len, - version_data *block) +static gconstpointer +get_string_block (gconstpointer data_ptr, + const gunichar2 *string_key, + gpointer *string_value, + guint32 *string_value_len, + version_data *block) { guint16 data_len = block->data_len; guint16 string_len = 28; /* Length of the StringTable block */ - + char *orig_data_ptr = (char *)data_ptr - 28; + /* data_ptr is pointing at an array of one or more String blocks * with total length (not including alignment padding) of * data_len */ - while (string_len < data_len) { + while (((char *)data_ptr - (char *)orig_data_ptr) < data_len) { /* align on a 32-bit boundary */ - data_ptr = (gpointer)((char *)data_ptr + 3); - data_ptr = (gpointer)((char *)data_ptr - (GPOINTER_TO_INT (data_ptr) & 3)); + ALIGN32 (data_ptr); data_ptr = get_versioninfo_block (data_ptr, block); if (block->data_len == 0) { /* We must have hit padding, so give up * processing now */ -#ifdef DEBUG - g_message ("%s: Hit 0-length block, giving up", - __func__); -#endif + DEBUG ("%s: Hit 0-length block, giving up", __func__); + return(NULL); } @@ -476,17 +554,21 @@ static gconstpointer get_string_block (gconstpointer data_ptr, * NULL if the data read hits padding. We can't recover from this * because the data length does not include padding bytes, so it's not * possible to just return the start position + length + * + * If lang == NULL it means we're just stepping through this block */ -static gconstpointer get_stringtable_block (gconstpointer data_ptr, - gchar *lang, - const gunichar2 *string_key, - gpointer *string_value, - guint32 *string_value_len, - version_data *block) +static gconstpointer +get_stringtable_block (gconstpointer data_ptr, + gchar *lang, + const gunichar2 *string_key, + gpointer *string_value, + guint32 *string_value_len, + version_data *block) { guint16 data_len = block->data_len; guint16 string_len = 36; /* length of the StringFileInfo block */ gchar *found_lang; + gchar *lowercase_lang; /* data_ptr is pointing at an array of StringTable blocks, * with total length (not including alignment padding) of @@ -495,18 +577,14 @@ static gconstpointer get_stringtable_block (gconstpointer data_ptr, while(string_len < data_len) { /* align on a 32-bit boundary */ - data_ptr = (gpointer)((char *)data_ptr + 3); - data_ptr = (gpointer)((char *)data_ptr - (GPOINTER_TO_INT (data_ptr) & 3)); + ALIGN32 (data_ptr); data_ptr = get_versioninfo_block (data_ptr, block); if (block->data_len == 0) { /* We must have hit padding, so give up * processing now */ -#ifdef DEBUG - g_message ("%s: Hit 0-length block, giving up", - __func__); -#endif + DEBUG ("%s: Hit 0-length block, giving up", __func__); return(NULL); } @@ -514,15 +592,16 @@ static gconstpointer get_stringtable_block (gconstpointer data_ptr, found_lang = g_utf16_to_utf8 (block->key, 8, NULL, NULL, NULL); if (found_lang == NULL) { -#ifdef DEBUG - g_message ("%s: Didn't find a valid language key, giving up", __func__); -#endif + DEBUG ("%s: Didn't find a valid language key, giving up", __func__); return(NULL); } - g_strdown (found_lang); + lowercase_lang = g_utf8_strdown (found_lang, -1); + g_free (found_lang); + found_lang = lowercase_lang; + lowercase_lang = NULL; - if (!strcmp (found_lang, lang)) { + if (lang != NULL && !strcmp (found_lang, lang)) { /* Got the one we're interested in */ data_ptr = get_string_block (data_ptr, string_key, string_value, @@ -536,9 +615,7 @@ static gconstpointer get_stringtable_block (gconstpointer data_ptr, if (data_ptr == NULL) { /* Child block hit padding */ -#ifdef DEBUG - g_message ("%s: Child block hit 0-length block, giving up", __func__); -#endif + DEBUG ("%s: Child block hit 0-length block, giving up", __func__); return(NULL); } } @@ -547,31 +624,28 @@ static gconstpointer get_stringtable_block (gconstpointer data_ptr, } #if G_BYTE_ORDER == G_BIG_ENDIAN -static gconstpointer big_up_string_block (gconstpointer data_ptr, - version_data *block) +static gconstpointer +big_up_string_block (gconstpointer data_ptr, version_data *block) { guint16 data_len = block->data_len; guint16 string_len = 28; /* Length of the StringTable block */ gchar *big_value; + char *orig_data_ptr = (char *)data_ptr - 28; /* data_ptr is pointing at an array of one or more String * blocks with total length (not including alignment padding) * of data_len */ - while (string_len < data_len) { + while (((char *)data_ptr - (char *)orig_data_ptr) < data_len) { /* align on a 32-bit boundary */ - data_ptr = (gpointer)((char *)data_ptr + 3); - data_ptr = (gpointer)((char *)data_ptr - (GPOINTER_TO_INT (data_ptr) & 3)); + ALIGN32 (data_ptr); data_ptr = get_versioninfo_block (data_ptr, block); if (block->data_len == 0) { /* We must have hit padding, so give up * processing now */ -#ifdef DEBUG - g_message ("%s: Hit 0-length block, giving up", - __func__); -#endif + DEBUG ("%s: Hit 0-length block, giving up", __func__); return(NULL); } @@ -582,10 +656,7 @@ static gconstpointer big_up_string_block (gconstpointer data_ptr, "UTF-16BE", "UTF-16LE", NULL, NULL, NULL); if (big_value == NULL) { -#ifdef DEBUG - g_message ("%s: Didn't find a valid string, giving up", - __func__); -#endif + DEBUG ("%s: Didn't find a valid string, giving up", __func__); return(NULL); } @@ -602,9 +673,7 @@ static gconstpointer big_up_string_block (gconstpointer data_ptr, "UTF-16BE", "UTF-16LE", NULL, NULL, NULL); if (big_value == NULL) { -#ifdef DEBUG - g_message ("%s: Didn't find a valid data string, giving up", __func__); -#endif + DEBUG ("%s: Didn't find a valid data string, giving up", __func__); return(NULL); } memcpy ((gpointer)data_ptr, big_value, @@ -622,8 +691,8 @@ static gconstpointer big_up_string_block (gconstpointer data_ptr, * because the data length does not include padding bytes, so it's not * possible to just return the start position + length */ -static gconstpointer big_up_stringtable_block (gconstpointer data_ptr, - version_data *block) +static gconstpointer +big_up_stringtable_block (gconstpointer data_ptr, version_data *block) { guint16 data_len = block->data_len; guint16 string_len = 36; /* length of the StringFileInfo block */ @@ -636,18 +705,14 @@ static gconstpointer big_up_stringtable_block (gconstpointer data_ptr, while(string_len < data_len) { /* align on a 32-bit boundary */ - data_ptr = (gpointer)((char *)data_ptr + 3); - data_ptr = (gpointer)((char *)data_ptr - (GPOINTER_TO_INT (data_ptr) & 3)); + ALIGN32 (data_ptr); data_ptr = get_versioninfo_block (data_ptr, block); if (block->data_len == 0) { /* We must have hit padding, so give up * processing now */ -#ifdef DEBUG - g_message ("%s: Hit 0-length block, giving up", - __func__); -#endif + DEBUG ("%s: Hit 0-length block, giving up", __func__); return(NULL); } @@ -656,10 +721,7 @@ static gconstpointer big_up_stringtable_block (gconstpointer data_ptr, big_value = g_convert ((gchar *)block->key, 16, "UTF-16BE", "UTF-16LE", NULL, NULL, NULL); if (big_value == NULL) { -#ifdef DEBUG - g_message ("%s: Didn't find a valid string, giving up", - __func__); -#endif + DEBUG ("%s: Didn't find a valid string, giving up", __func__); return(NULL); } @@ -670,9 +732,7 @@ static gconstpointer big_up_stringtable_block (gconstpointer data_ptr, if (data_ptr == NULL) { /* Child block hit padding */ -#ifdef DEBUG - g_message ("%s: Child block hit 0-length block, giving up", __func__); -#endif + DEBUG ("%s: Child block hit 0-length block, giving up", __func__); return(NULL); } } @@ -683,7 +743,8 @@ static gconstpointer big_up_stringtable_block (gconstpointer data_ptr, /* Follows the data structures and turns all UTF-16 strings from the * LE found in the resource section into UTF-16BE */ -static void big_up (gconstpointer datablock, guint32 size) +static void +big_up (gconstpointer datablock, guint32 size) { gconstpointer data_ptr; gint32 data_len; /* signed to guard against underflow */ @@ -716,18 +777,14 @@ static void big_up (gconstpointer datablock, guint32 size) */ while (data_len > 0) { /* align on a 32-bit boundary */ - data_ptr = (gpointer)((char *)data_ptr + 3); - data_ptr = (gpointer)((char *)data_ptr - (GPOINTER_TO_INT (data_ptr) & 3)); + ALIGN32 (data_ptr); data_ptr = get_versioninfo_block (data_ptr, &block); if (block.data_len == 0) { /* We must have hit padding, so give * up processing now */ -#ifdef DEBUG - g_message ("%s: Hit 0-length block, giving up", - __func__); -#endif + DEBUG ("%s: Hit 0-length block, giving up", __func__); return; } @@ -743,17 +800,13 @@ static void big_up (gconstpointer datablock, guint32 size) &block); } else { /* Bogus data */ -#ifdef DEBUG - g_message ("%s: Not a valid VERSIONINFO child block", __func__); -#endif + DEBUG ("%s: Not a valid VERSIONINFO child block", __func__); return; } if (data_ptr == NULL) { /* Child block hit padding */ -#ifdef DEBUG - g_message ("%s: Child block hit 0-length block, giving up", __func__); -#endif + DEBUG ("%s: Child block hit 0-length block, giving up", __func__); return; } } @@ -761,8 +814,8 @@ static void big_up (gconstpointer datablock, guint32 size) } #endif -gboolean VerQueryValue (gconstpointer datablock, const gunichar2 *subblock, - gpointer *buffer, guint32 *len) +gboolean +VerQueryValue (gconstpointer datablock, const gunichar2 *subblock, gpointer *buffer, guint32 *len) { gchar *subblock_utf8, *lang_utf8 = NULL; gboolean ret = FALSE; @@ -775,6 +828,7 @@ gboolean VerQueryValue (gconstpointer datablock, const gunichar2 *subblock, const gunichar2 *string_key = NULL; gpointer string_value = NULL; guint32 string_value_len = 0; + gchar *lowercase_lang; subblock_utf8 = g_utf16_to_utf8 (subblock, -1, NULL, NULL, NULL); if (subblock_utf8 == NULL) { @@ -787,7 +841,10 @@ gboolean VerQueryValue (gconstpointer datablock, const gunichar2 *subblock, want_string = TRUE; memcpy (lang, subblock + 16, 8 * sizeof(gunichar2)); lang_utf8 = g_utf16_to_utf8 (lang, 8, NULL, NULL, NULL); - g_strdown (lang_utf8); + lowercase_lang = g_utf8_strdown (lang_utf8, -1); + g_free (lang_utf8); + lang_utf8 = lowercase_lang; + lowercase_lang = NULL; string_key = subblock + 25; } @@ -813,8 +870,7 @@ gboolean VerQueryValue (gconstpointer datablock, const gunichar2 *subblock, */ while (data_len > 0) { /* align on a 32-bit boundary */ - data_ptr = (gpointer)((char *)data_ptr + 3); - data_ptr = (gpointer)((char *)data_ptr - (GPOINTER_TO_INT (data_ptr) & 3)); + ALIGN32 (data_ptr); data_ptr = get_versioninfo_block (data_ptr, &block); @@ -822,9 +878,7 @@ gboolean VerQueryValue (gconstpointer datablock, const gunichar2 *subblock, /* We must have hit padding, * so give up processing now */ -#ifdef DEBUG - g_message ("%s: Hit 0-length block, giving up", __func__); -#endif + DEBUG ("%s: Hit 0-length block, giving up", __func__); goto done; } @@ -853,17 +907,13 @@ gboolean VerQueryValue (gconstpointer datablock, const gunichar2 *subblock, } } else { /* Bogus data */ -#ifdef DEBUG - g_message ("%s: Not a valid VERSIONINFO child block", __func__); -#endif + DEBUG ("%s: Not a valid VERSIONINFO child block", __func__); goto done; } if (data_ptr == NULL) { /* Child block hit padding */ -#ifdef DEBUG - g_message ("%s: Child block hit 0-length block, giving up", __func__); -#endif + DEBUG ("%s: Child block hit 0-length block, giving up", __func__); goto done; } } @@ -879,29 +929,24 @@ gboolean VerQueryValue (gconstpointer datablock, const gunichar2 *subblock, return(ret); } -guint32 VerLanguageName (guint32 lang, gunichar2 *lang_out, guint32 lang_len) -{ - return(0); -} - - -guint32 GetFileVersionInfoSize (gunichar2 *filename, guint32 *handle) +guint32 +GetFileVersionInfoSize (gunichar2 *filename, guint32 *handle) { gpointer file_map; gpointer versioninfo; - guint32 map_size; + void *map_handle; + gint32 map_size; guint32 size; - + /* This value is unused, but set to zero */ *handle = 0; - file_map = map_pe_file (filename, &map_size); + file_map = map_pe_file (filename, &map_size, &map_handle); if (file_map == NULL) { return(0); } - versioninfo = find_pe_file_resources (file_map, map_size, RT_VERSION, - 0, &size); + versioninfo = find_pe_file_resources (file_map, map_size, RT_VERSION, 0, &size); if (versioninfo == NULL) { /* Didn't find the resource, so set the return value * to 0 @@ -909,21 +954,22 @@ guint32 GetFileVersionInfoSize (gunichar2 *filename, guint32 *handle) size = 0; } - unmap_pe_file (file_map, map_size); + unmap_pe_file (file_map, map_handle); return(size); } -gboolean GetFileVersionInfo (gunichar2 *filename, guint32 handle G_GNUC_UNUSED, - guint32 len, gpointer data) +gboolean +GetFileVersionInfo (gunichar2 *filename, guint32 handle G_GNUC_UNUSED, guint32 len, gpointer data) { gpointer file_map; gpointer versioninfo; - guint32 map_size; + void *map_handle; + gint32 map_size; guint32 size; gboolean ret = FALSE; - file_map = map_pe_file (filename, &map_size); + file_map = map_pe_file (filename, &map_size, &map_handle); if (file_map == NULL) { return(FALSE); } @@ -944,7 +990,1326 @@ gboolean GetFileVersionInfo (gunichar2 *filename, guint32 handle G_GNUC_UNUSED, #endif } - unmap_pe_file (file_map, map_size); + unmap_pe_file (file_map, map_handle); + + return(ret); +} + +static guint32 +copy_lang (gunichar2 *lang_out, guint32 lang_len, const gchar *text) +{ + gunichar2 *unitext; + int chars = strlen (text); + int ret; + + unitext = g_utf8_to_utf16 (text, -1, NULL, NULL, NULL); + g_assert (unitext != NULL); + + if (chars < (lang_len - 1)) { + memcpy (lang_out, (gpointer)unitext, chars * 2); + lang_out[chars] = '\0'; + ret = chars; + } else { + memcpy (lang_out, (gpointer)unitext, (lang_len - 1) * 2); + lang_out[lang_len] = '\0'; + ret = lang_len; + } + g_free (unitext); + return(ret); } + +guint32 +VerLanguageName (guint32 lang, gunichar2 *lang_out, guint32 lang_len) +{ + int primary, secondary; + const char *name = NULL; + + primary = lang & 0x3FF; + secondary = (lang >> 10) & 0x3F; + + switch(primary) { + case 0x00: + switch(secondary) { + case 0x01: + name = "Process Default Language"; + break; + } + break; + case 0x01: + switch(secondary) { + case 0x00: + case 0x01: + name = "Arabic (Saudi Arabia)"; + break; + case 0x02: + name = "Arabic (Iraq)"; + break; + case 0x03: + name = "Arabic (Egypt)"; + break; + case 0x04: + name = "Arabic (Libya)"; + break; + case 0x05: + name = "Arabic (Algeria)"; + break; + case 0x06: + name = "Arabic (Morocco)"; + break; + case 0x07: + name = "Arabic (Tunisia)"; + break; + case 0x08: + name = "Arabic (Oman)"; + break; + case 0x09: + name = "Arabic (Yemen)"; + break; + case 0x0a: + name = "Arabic (Syria)"; + break; + case 0x0b: + name = "Arabic (Jordan)"; + break; + case 0x0c: + name = "Arabic (Lebanon)"; + break; + case 0x0d: + name = "Arabic (Kuwait)"; + break; + case 0x0e: + name = "Arabic (U.A.E.)"; + break; + case 0x0f: + name = "Arabic (Bahrain)"; + break; + case 0x10: + name = "Arabic (Qatar)"; + break; + } + break; + case 0x02: + switch(secondary) { + case 0x00: + name = "Bulgarian (Bulgaria)"; + break; + case 0x01: + name = "Bulgarian"; + break; + } + break; + case 0x03: + switch(secondary) { + case 0x00: + name = "Catalan (Spain)"; + break; + case 0x01: + name = "Catalan"; + break; + } + break; + case 0x04: + switch(secondary) { + case 0x00: + case 0x01: + name = "Chinese (Taiwan)"; + break; + case 0x02: + name = "Chinese (PRC)"; + break; + case 0x03: + name = "Chinese (Hong Kong S.A.R.)"; + break; + case 0x04: + name = "Chinese (Singapore)"; + break; + case 0x05: + name = "Chinese (Macau S.A.R.)"; + break; + } + break; + case 0x05: + switch(secondary) { + case 0x00: + name = "Czech (Czech Republic)"; + break; + case 0x01: + name = "Czech"; + break; + } + break; + case 0x06: + switch(secondary) { + case 0x00: + name = "Danish (Denmark)"; + break; + case 0x01: + name = "Danish"; + break; + } + break; + case 0x07: + switch(secondary) { + case 0x00: + case 0x01: + name = "German (Germany)"; + break; + case 0x02: + name = "German (Switzerland)"; + break; + case 0x03: + name = "German (Austria)"; + break; + case 0x04: + name = "German (Luxembourg)"; + break; + case 0x05: + name = "German (Liechtenstein)"; + break; + } + break; + case 0x08: + switch(secondary) { + case 0x00: + name = "Greek (Greece)"; + break; + case 0x01: + name = "Greek"; + break; + } + break; + case 0x09: + switch(secondary) { + case 0x00: + case 0x01: + name = "English (United States)"; + break; + case 0x02: + name = "English (United Kingdom)"; + break; + case 0x03: + name = "English (Australia)"; + break; + case 0x04: + name = "English (Canada)"; + break; + case 0x05: + name = "English (New Zealand)"; + break; + case 0x06: + name = "English (Ireland)"; + break; + case 0x07: + name = "English (South Africa)"; + break; + case 0x08: + name = "English (Jamaica)"; + break; + case 0x09: + name = "English (Caribbean)"; + break; + case 0x0a: + name = "English (Belize)"; + break; + case 0x0b: + name = "English (Trinidad and Tobago)"; + break; + case 0x0c: + name = "English (Zimbabwe)"; + break; + case 0x0d: + name = "English (Philippines)"; + break; + case 0x10: + name = "English (India)"; + break; + case 0x11: + name = "English (Malaysia)"; + break; + case 0x12: + name = "English (Singapore)"; + break; + } + break; + case 0x0a: + switch(secondary) { + case 0x00: + name = "Spanish (Spain)"; + break; + case 0x01: + name = "Spanish (Traditional Sort)"; + break; + case 0x02: + name = "Spanish (Mexico)"; + break; + case 0x03: + name = "Spanish (International Sort)"; + break; + case 0x04: + name = "Spanish (Guatemala)"; + break; + case 0x05: + name = "Spanish (Costa Rica)"; + break; + case 0x06: + name = "Spanish (Panama)"; + break; + case 0x07: + name = "Spanish (Dominican Republic)"; + break; + case 0x08: + name = "Spanish (Venezuela)"; + break; + case 0x09: + name = "Spanish (Colombia)"; + break; + case 0x0a: + name = "Spanish (Peru)"; + break; + case 0x0b: + name = "Spanish (Argentina)"; + break; + case 0x0c: + name = "Spanish (Ecuador)"; + break; + case 0x0d: + name = "Spanish (Chile)"; + break; + case 0x0e: + name = "Spanish (Uruguay)"; + break; + case 0x0f: + name = "Spanish (Paraguay)"; + break; + case 0x10: + name = "Spanish (Bolivia)"; + break; + case 0x11: + name = "Spanish (El Salvador)"; + break; + case 0x12: + name = "Spanish (Honduras)"; + break; + case 0x13: + name = "Spanish (Nicaragua)"; + break; + case 0x14: + name = "Spanish (Puerto Rico)"; + break; + case 0x15: + name = "Spanish (United States)"; + break; + } + break; + case 0x0b: + switch(secondary) { + case 0x00: + name = "Finnish (Finland)"; + break; + case 0x01: + name = "Finnish"; + break; + } + break; + case 0x0c: + switch(secondary) { + case 0x00: + case 0x01: + name = "French (France)"; + break; + case 0x02: + name = "French (Belgium)"; + break; + case 0x03: + name = "French (Canada)"; + break; + case 0x04: + name = "French (Switzerland)"; + break; + case 0x05: + name = "French (Luxembourg)"; + break; + case 0x06: + name = "French (Monaco)"; + break; + } + break; + case 0x0d: + switch(secondary) { + case 0x00: + name = "Hebrew (Israel)"; + break; + case 0x01: + name = "Hebrew"; + break; + } + break; + case 0x0e: + switch(secondary) { + case 0x00: + name = "Hungarian (Hungary)"; + break; + case 0x01: + name = "Hungarian"; + break; + } + break; + case 0x0f: + switch(secondary) { + case 0x00: + name = "Icelandic (Iceland)"; + break; + case 0x01: + name = "Icelandic"; + break; + } + break; + case 0x10: + switch(secondary) { + case 0x00: + case 0x01: + name = "Italian (Italy)"; + break; + case 0x02: + name = "Italian (Switzerland)"; + break; + } + break; + case 0x11: + switch(secondary) { + case 0x00: + name = "Japanese (Japan)"; + break; + case 0x01: + name = "Japanese"; + break; + } + break; + case 0x12: + switch(secondary) { + case 0x00: + name = "Korean (Korea)"; + break; + case 0x01: + name = "Korean"; + break; + } + break; + case 0x13: + switch(secondary) { + case 0x00: + case 0x01: + name = "Dutch (Netherlands)"; + break; + case 0x02: + name = "Dutch (Belgium)"; + break; + } + break; + case 0x14: + switch(secondary) { + case 0x00: + case 0x01: + name = "Norwegian (Bokmal)"; + break; + case 0x02: + name = "Norwegian (Nynorsk)"; + break; + } + break; + case 0x15: + switch(secondary) { + case 0x00: + name = "Polish (Poland)"; + break; + case 0x01: + name = "Polish"; + break; + } + break; + case 0x16: + switch(secondary) { + case 0x00: + case 0x01: + name = "Portuguese (Brazil)"; + break; + case 0x02: + name = "Portuguese (Portugal)"; + break; + } + break; + case 0x17: + switch(secondary) { + case 0x01: + name = "Romansh (Switzerland)"; + break; + } + break; + case 0x18: + switch(secondary) { + case 0x00: + name = "Romanian (Romania)"; + break; + case 0x01: + name = "Romanian"; + break; + } + break; + case 0x19: + switch(secondary) { + case 0x00: + name = "Russian (Russia)"; + break; + case 0x01: + name = "Russian"; + break; + } + break; + case 0x1a: + switch(secondary) { + case 0x00: + name = "Croatian (Croatia)"; + break; + case 0x01: + name = "Croatian"; + break; + case 0x02: + name = "Serbian (Latin)"; + break; + case 0x03: + name = "Serbian (Cyrillic)"; + break; + case 0x04: + name = "Croatian (Bosnia and Herzegovina)"; + break; + case 0x05: + name = "Bosnian (Latin, Bosnia and Herzegovina)"; + break; + case 0x06: + name = "Serbian (Latin, Bosnia and Herzegovina)"; + break; + case 0x07: + name = "Serbian (Cyrillic, Bosnia and Herzegovina)"; + break; + case 0x08: + name = "Bosnian (Cyrillic, Bosnia and Herzegovina)"; + break; + } + break; + case 0x1b: + switch(secondary) { + case 0x00: + name = "Slovak (Slovakia)"; + break; + case 0x01: + name = "Slovak"; + break; + } + break; + case 0x1c: + switch(secondary) { + case 0x00: + name = "Albanian (Albania)"; + break; + case 0x01: + name = "Albanian"; + break; + } + break; + case 0x1d: + switch(secondary) { + case 0x00: + name = "Swedish (Sweden)"; + break; + case 0x01: + name = "Swedish"; + break; + case 0x02: + name = "Swedish (Finland)"; + break; + } + break; + case 0x1e: + switch(secondary) { + case 0x00: + name = "Thai (Thailand)"; + break; + case 0x01: + name = "Thai"; + break; + } + break; + case 0x1f: + switch(secondary) { + case 0x00: + name = "Turkish (Turkey)"; + break; + case 0x01: + name = "Turkish"; + break; + } + break; + case 0x20: + switch(secondary) { + case 0x00: + name = "Urdu (Islamic Republic of Pakistan)"; + break; + case 0x01: + name = "Urdu"; + break; + } + break; + case 0x21: + switch(secondary) { + case 0x00: + name = "Indonesian (Indonesia)"; + break; + case 0x01: + name = "Indonesian"; + break; + } + break; + case 0x22: + switch(secondary) { + case 0x00: + name = "Ukrainian (Ukraine)"; + break; + case 0x01: + name = "Ukrainian"; + break; + } + break; + case 0x23: + switch(secondary) { + case 0x00: + name = "Belarusian (Belarus)"; + break; + case 0x01: + name = "Belarusian"; + break; + } + break; + case 0x24: + switch(secondary) { + case 0x00: + name = "Slovenian (Slovenia)"; + break; + case 0x01: + name = "Slovenian"; + break; + } + break; + case 0x25: + switch(secondary) { + case 0x00: + name = "Estonian (Estonia)"; + break; + case 0x01: + name = "Estonian"; + break; + } + break; + case 0x26: + switch(secondary) { + case 0x00: + name = "Latvian (Latvia)"; + break; + case 0x01: + name = "Latvian"; + break; + } + break; + case 0x27: + switch(secondary) { + case 0x00: + name = "Lithuanian (Lithuania)"; + break; + case 0x01: + name = "Lithuanian"; + break; + } + break; + case 0x28: + switch(secondary) { + case 0x01: + name = "Tajik (Tajikistan)"; + break; + } + break; + case 0x29: + switch(secondary) { + case 0x00: + name = "Farsi (Iran)"; + break; + case 0x01: + name = "Farsi"; + break; + } + break; + case 0x2a: + switch(secondary) { + case 0x00: + name = "Vietnamese (Viet Nam)"; + break; + case 0x01: + name = "Vietnamese"; + break; + } + break; + case 0x2b: + switch(secondary) { + case 0x00: + name = "Armenian (Armenia)"; + break; + case 0x01: + name = "Armenian"; + break; + } + break; + case 0x2c: + switch(secondary) { + case 0x00: + name = "Azeri (Latin) (Azerbaijan)"; + break; + case 0x01: + name = "Azeri (Latin)"; + break; + case 0x02: + name = "Azeri (Cyrillic)"; + break; + } + break; + case 0x2d: + switch(secondary) { + case 0x00: + name = "Basque (Spain)"; + break; + case 0x01: + name = "Basque"; + break; + } + break; + case 0x2e: + switch(secondary) { + case 0x01: + name = "Upper Sorbian (Germany)"; + break; + case 0x02: + name = "Lower Sorbian (Germany)"; + break; + } + break; + case 0x2f: + switch(secondary) { + case 0x00: + name = "FYRO Macedonian (Former Yugoslav Republic of Macedonia)"; + break; + case 0x01: + name = "FYRO Macedonian"; + break; + } + break; + case 0x32: + switch(secondary) { + case 0x00: + name = "Tswana (South Africa)"; + break; + case 0x01: + name = "Tswana"; + break; + } + break; + case 0x34: + switch(secondary) { + case 0x00: + name = "Xhosa (South Africa)"; + break; + case 0x01: + name = "Xhosa"; + break; + } + break; + case 0x35: + switch(secondary) { + case 0x00: + name = "Zulu (South Africa)"; + break; + case 0x01: + name = "Zulu"; + break; + } + break; + case 0x36: + switch(secondary) { + case 0x00: + name = "Afrikaans (South Africa)"; + break; + case 0x01: + name = "Afrikaans"; + break; + } + break; + case 0x37: + switch(secondary) { + case 0x00: + name = "Georgian (Georgia)"; + break; + case 0x01: + name = "Georgian"; + break; + } + break; + case 0x38: + switch(secondary) { + case 0x00: + name = "Faroese (Faroe Islands)"; + break; + case 0x01: + name = "Faroese"; + break; + } + break; + case 0x39: + switch(secondary) { + case 0x00: + name = "Hindi (India)"; + break; + case 0x01: + name = "Hindi"; + break; + } + break; + case 0x3a: + switch(secondary) { + case 0x00: + name = "Maltese (Malta)"; + break; + case 0x01: + name = "Maltese"; + break; + } + break; + case 0x3b: + switch(secondary) { + case 0x00: + name = "Sami (Northern) (Norway)"; + break; + case 0x01: + name = "Sami, Northern (Norway)"; + break; + case 0x02: + name = "Sami, Northern (Sweden)"; + break; + case 0x03: + name = "Sami, Northern (Finland)"; + break; + case 0x04: + name = "Sami, Lule (Norway)"; + break; + case 0x05: + name = "Sami, Lule (Sweden)"; + break; + case 0x06: + name = "Sami, Southern (Norway)"; + break; + case 0x07: + name = "Sami, Southern (Sweden)"; + break; + case 0x08: + name = "Sami, Skolt (Finland)"; + break; + case 0x09: + name = "Sami, Inari (Finland)"; + break; + } + break; + case 0x3c: + switch(secondary) { + case 0x02: + name = "Irish (Ireland)"; + break; + } + break; + case 0x3e: + switch(secondary) { + case 0x00: + case 0x01: + name = "Malay (Malaysia)"; + break; + case 0x02: + name = "Malay (Brunei Darussalam)"; + break; + } + break; + case 0x3f: + switch(secondary) { + case 0x00: + name = "Kazakh (Kazakhstan)"; + break; + case 0x01: + name = "Kazakh"; + break; + } + break; + case 0x40: + switch(secondary) { + case 0x00: + name = "Kyrgyz (Kyrgyzstan)"; + break; + case 0x01: + name = "Kyrgyz (Cyrillic)"; + break; + } + break; + case 0x41: + switch(secondary) { + case 0x00: + name = "Swahili (Kenya)"; + break; + case 0x01: + name = "Swahili"; + break; + } + break; + case 0x42: + switch(secondary) { + case 0x01: + name = "Turkmen (Turkmenistan)"; + break; + } + break; + case 0x43: + switch(secondary) { + case 0x00: + name = "Uzbek (Latin) (Uzbekistan)"; + break; + case 0x01: + name = "Uzbek (Latin)"; + break; + case 0x02: + name = "Uzbek (Cyrillic)"; + break; + } + break; + case 0x44: + switch(secondary) { + case 0x00: + name = "Tatar (Russia)"; + break; + case 0x01: + name = "Tatar"; + break; + } + break; + case 0x45: + switch(secondary) { + case 0x00: + case 0x01: + name = "Bengali (India)"; + break; + } + break; + case 0x46: + switch(secondary) { + case 0x00: + name = "Punjabi (India)"; + break; + case 0x01: + name = "Punjabi"; + break; + } + break; + case 0x47: + switch(secondary) { + case 0x00: + name = "Gujarati (India)"; + break; + case 0x01: + name = "Gujarati"; + break; + } + break; + case 0x49: + switch(secondary) { + case 0x00: + name = "Tamil (India)"; + break; + case 0x01: + name = "Tamil"; + break; + } + break; + case 0x4a: + switch(secondary) { + case 0x00: + name = "Telugu (India)"; + break; + case 0x01: + name = "Telugu"; + break; + } + break; + case 0x4b: + switch(secondary) { + case 0x00: + name = "Kannada (India)"; + break; + case 0x01: + name = "Kannada"; + break; + } + break; + case 0x4c: + switch(secondary) { + case 0x00: + case 0x01: + name = "Malayalam (India)"; + break; + } + break; + case 0x4d: + switch(secondary) { + case 0x01: + name = "Assamese (India)"; + break; + } + break; + case 0x4e: + switch(secondary) { + case 0x00: + name = "Marathi (India)"; + break; + case 0x01: + name = "Marathi"; + break; + } + break; + case 0x4f: + switch(secondary) { + case 0x00: + name = "Sanskrit (India)"; + break; + case 0x01: + name = "Sanskrit"; + break; + } + break; + case 0x50: + switch(secondary) { + case 0x00: + name = "Mongolian (Mongolia)"; + break; + case 0x01: + name = "Mongolian (Cyrillic)"; + break; + case 0x02: + name = "Mongolian (PRC)"; + break; + } + break; + case 0x51: + switch(secondary) { + case 0x01: + name = "Tibetan (PRC)"; + break; + case 0x02: + name = "Tibetan (Bhutan)"; + break; + } + break; + case 0x52: + switch(secondary) { + case 0x00: + name = "Welsh (United Kingdom)"; + break; + case 0x01: + name = "Welsh"; + break; + } + break; + case 0x53: + switch(secondary) { + case 0x01: + name = "Khmer (Cambodia)"; + break; + } + break; + case 0x54: + switch(secondary) { + case 0x01: + name = "Lao (Lao PDR)"; + break; + } + break; + case 0x56: + switch(secondary) { + case 0x00: + name = "Galician (Spain)"; + break; + case 0x01: + name = "Galician"; + break; + } + break; + case 0x57: + switch(secondary) { + case 0x00: + name = "Konkani (India)"; + break; + case 0x01: + name = "Konkani"; + break; + } + break; + case 0x5a: + switch(secondary) { + case 0x00: + name = "Syriac (Syria)"; + break; + case 0x01: + name = "Syriac"; + break; + } + break; + case 0x5b: + switch(secondary) { + case 0x01: + name = "Sinhala (Sri Lanka)"; + break; + } + break; + case 0x5d: + switch(secondary) { + case 0x01: + name = "Inuktitut (Syllabics, Canada)"; + break; + case 0x02: + name = "Inuktitut (Latin, Canada)"; + break; + } + break; + case 0x5e: + switch(secondary) { + case 0x01: + name = "Amharic (Ethiopia)"; + break; + } + break; + case 0x5f: + switch(secondary) { + case 0x02: + name = "Tamazight (Algeria, Latin)"; + break; + } + break; + case 0x61: + switch(secondary) { + case 0x01: + name = "Nepali (Nepal)"; + break; + } + break; + case 0x62: + switch(secondary) { + case 0x01: + name = "Frisian (Netherlands)"; + break; + } + break; + case 0x63: + switch(secondary) { + case 0x01: + name = "Pashto (Afghanistan)"; + break; + } + break; + case 0x64: + switch(secondary) { + case 0x01: + name = "Filipino (Philippines)"; + break; + } + break; + case 0x65: + switch(secondary) { + case 0x00: + name = "Divehi (Maldives)"; + break; + case 0x01: + name = "Divehi"; + break; + } + break; + case 0x68: + switch(secondary) { + case 0x01: + name = "Hausa (Nigeria, Latin)"; + break; + } + break; + case 0x6a: + switch(secondary) { + case 0x01: + name = "Yoruba (Nigeria)"; + break; + } + break; + case 0x6b: + switch(secondary) { + case 0x00: + case 0x01: + name = "Quechua (Bolivia)"; + break; + case 0x02: + name = "Quechua (Ecuador)"; + break; + case 0x03: + name = "Quechua (Peru)"; + break; + } + break; + case 0x6c: + switch(secondary) { + case 0x00: + name = "Northern Sotho (South Africa)"; + break; + case 0x01: + name = "Northern Sotho"; + break; + } + break; + case 0x6d: + switch(secondary) { + case 0x01: + name = "Bashkir (Russia)"; + break; + } + break; + case 0x6e: + switch(secondary) { + case 0x01: + name = "Luxembourgish (Luxembourg)"; + break; + } + break; + case 0x6f: + switch(secondary) { + case 0x01: + name = "Greenlandic (Greenland)"; + break; + } + break; + case 0x78: + switch(secondary) { + case 0x01: + name = "Yi (PRC)"; + break; + } + break; + case 0x7a: + switch(secondary) { + case 0x01: + name = "Mapudungun (Chile)"; + break; + } + break; + case 0x7c: + switch(secondary) { + case 0x01: + name = "Mohawk (Mohawk)"; + break; + } + break; + case 0x7e: + switch(secondary) { + case 0x01: + name = "Breton (France)"; + break; + } + break; + case 0x7f: + switch(secondary) { + case 0x00: + name = "Invariant Language (Invariant Country)"; + break; + } + break; + case 0x80: + switch(secondary) { + case 0x01: + name = "Uighur (PRC)"; + break; + } + break; + case 0x81: + switch(secondary) { + case 0x00: + name = "Maori (New Zealand)"; + break; + case 0x01: + name = "Maori"; + break; + } + break; + case 0x83: + switch(secondary) { + case 0x01: + name = "Corsican (France)"; + break; + } + break; + case 0x84: + switch(secondary) { + case 0x01: + name = "Alsatian (France)"; + break; + } + break; + case 0x85: + switch(secondary) { + case 0x01: + name = "Yakut (Russia)"; + break; + } + break; + case 0x86: + switch(secondary) { + case 0x01: + name = "K'iche (Guatemala)"; + break; + } + break; + case 0x87: + switch(secondary) { + case 0x01: + name = "Kinyarwanda (Rwanda)"; + break; + } + break; + case 0x88: + switch(secondary) { + case 0x01: + name = "Wolof (Senegal)"; + break; + } + break; + case 0x8c: + switch(secondary) { + case 0x01: + name = "Dari (Afghanistan)"; + break; + } + break; + + default: + name = "Language Neutral"; + + } + + if (!name) + name = "Language Neutral"; + + return copy_lang (lang_out, lang_len, name); +}