/[winpt]/trunk/Src/wptNLS.cpp
ViewVC logotype

Diff of /trunk/Src/wptNLS.cpp

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 278 by twoaday, Mon Jan 15 22:02:04 2007 UTC revision 372 by twoaday, Fri Dec 9 10:59:16 2011 UTC
# Line 1  Line 1 
1  /* wptNLS.cpp - W32 Native Language Support  /* wptNLS.cpp - W32 Native Language Support
2   *      Copyright (C) 2001, 2002, 2003, 2006 Timo Schulz   *      Copyright (C) 2001-2003, 2006, 2008, 2009 Timo Schulz
3   *      Copyright (C) 1995-1999 Free Software Foundation, Inc.   *      Copyright (C) 1995-1999 Free Software Foundation, Inc.
4   *   *
5   * This code is a stripped down version of simple-gettext.c   * This code is a stripped down version of simple-gettext.c
# Line 21  Line 21 
21    
22  #include <stdio.h>  #include <stdio.h>
23  #include <string.h>  #include <string.h>
 #include <ctype.h>  
 #include <errno.h>  
 #include <sys/types.h>  
24  #include <sys/stat.h>  #include <sys/stat.h>
25    #include <time.h>
26  #include <windows.h>  #include <windows.h>
27    
28  #include "wptNLS.h"  #include "wptNLS.h"
29  #include "wptTypes.h"  #include "wptTypes.h"
30    #include "wptUtil.h"
31    #include "wptW32API.h"
32    #include "wptVersion.h"
33    #include "StringBuffer.h"
34    
35  /* The magic number of the GNU message catalog format.  */  /* The magic number of the GNU message catalog format.  */
36  #define MAGIC         0x950412de  #define MAGIC         0x950412de
# Line 62  struct string_desc { Line 63  struct string_desc {
63      DWORD offset; /* Offset of string in file.  */      DWORD offset; /* Offset of string in file.  */
64  };  };
65    
66  struct loaded_domain {  struct loaded_domain_s {
67    char *data;      /* Full file name to the MO file */
68    int must_swap;      char *file_name;
69    DWORD nstrings;      /* Last modification date of the MO file */
70    char *mapped;      time_t modify_time;
71    struct string_desc *orig_tab;      
72    struct string_desc *trans_tab;      char *data;
73    DWORD hash_size;      int must_swap;
74    DWORD *hash_tab;      DWORD nstrings;
75        char *mapped;
76        struct string_desc *orig_tab;
77        struct string_desc *trans_tab;
78        DWORD hash_size;
79        DWORD *hash_tab;
80  };  };
81    typedef struct loaded_domain_s *gettext_domain_t;
82    
83  /* List of all available languages. */  /* List of all available languages. */
84  struct lang_table_s lang_list[] = {  struct lang_table_s lang_list[] = {
85      {"en", "English",   LANG_ENGLISH},      {"en", "English",   LANG_ENGLISH},
86      {"de", "German",    LANG_GERMAN},      {"de", "German",    LANG_GERMAN},
87      {"fr", "French",    LANG_FRENCH},      {"ru", "Russian",   LANG_RUSSIAN},
88      {"jp", "Japanese",  LANG_JAPANESE},      {NULL, NULL, 0}
     // XXX: add new languages.  
     {"pt_BR", "Portuguese (Brazil)", SUBLANG_PORTUGUESE_BRAZILIAN},  
     {NULL, 0}  
89  };  };
90    
91    
92  /* The current domain. */  /* The current gettext domain. */
93  static struct loaded_domain *the_domain;  static gettext_domain_t the_domain;
94    
95    
96    /* Return TRUE if the native user language is not English
97       and thus, gettext support is available */
98    static bool
99    gettext_is_required (void)
100    {
101        LANGID lang = GetUserDefaultLangID ();
102        if (PRIMARYLANGID (lang) == LANG_ENGLISH)
103            return false;
104        return true;
105    }
106    
107    
108  static DWORD  static DWORD
# Line 119  hash_string (const char *str_param) Line 135  hash_string (const char *str_param)
135  }  }
136    
137    
138  /* Missing W32 functions. */  static gettext_domain_t
 static char*  
 w32_stpcpy (char *a,const char *b)  
 {  
     while (*b)  
         *a++ = *b++;  
     *a = 0;  
     return (char*)a;  
 }  
   
   
 static struct loaded_domain*  
139  load_domain (const char *filename)  load_domain (const char *filename)
140  {  {
141      FILE *fp;      FILE *fp;
142      size_t size;      size_t size;
143      struct stat st;      struct stat st;
144      struct mo_file_header *data = NULL;      struct mo_file_header *data = NULL;
145      struct loaded_domain *domain = NULL;      gettext_domain_t domain = NULL;
146      size_t to_read;      size_t to_read;
147      char *read_ptr;      char *read_ptr;
148    
# Line 145  load_domain (const char *filename) Line 150  load_domain (const char *filename)
150      if (!fp)      if (!fp)
151         return NULL;         return NULL;
152      /* we must know about the size of the file */      /* we must know about the size of the file */
153      if( fstat( fileno(fp ), &st )      if (fstat (fileno(fp), &st) ||
154          || (size = (size_t)st.st_size) != (size_t)st.st_size          (size = (size_t)st.st_size) != (size_t)st.st_size ||
155          || size < sizeof (struct mo_file_header) ) {          size < sizeof (struct mo_file_header)) {
156          fclose( fp );            fclose (fp);
157          return NULL;          return NULL;
158      }      }
159    
# Line 159  load_domain (const char *filename) Line 164  load_domain (const char *filename)
164      to_read = size;      to_read = size;
165      read_ptr = (char *) data;      read_ptr = (char *) data;
166      do {      do {
167          size_t nb = fread( read_ptr, 1, to_read, fp );          size_t nb = fread (read_ptr, 1, to_read, fp);
168          if( nb < to_read ) {          if (nb < to_read) {
169              fclose (fp);              fclose (fp);
170              free(data);              free (data);        
171              return NULL; /* read error */              return NULL; /* read error */              
                   
172          }          }
173          read_ptr += nb;          read_ptr += nb;
174          to_read -= nb;          to_read -= nb;
175      } while( to_read > 0 );      } while (to_read > 0);
176      fclose (fp);      fclose (fp);
177    
178      /* Using the magic number we can test whether it really is a message      /* Using the magic number we can test whether it really is a message catalog file.  */
179       * catalog file.  */      if (data->magic != MAGIC && data->magic != MAGIC_SWAPPED) {
     if( data->magic != MAGIC && data->magic != MAGIC_SWAPPED ) {  
180          /* The magic number is wrong: not a message catalog file.  */          /* The magic number is wrong: not a message catalog file.  */
181          free( data );          free (data);
182          return NULL;          return NULL;
183      }      }
184    
185      domain = (struct loaded_domain *)calloc( 1, sizeof *domain );      domain = (struct loaded_domain_s *)calloc (1, sizeof *domain);
186      if (!domain)      if (!domain)
187          BUG (0);          BUG (0);
188      domain->data = (char *) data;      domain->data = (char *) data;
189      domain->must_swap = data->magic != MAGIC;      domain->must_swap = data->magic != MAGIC;
190    
191      /* Fill in the information about the available tables.  */      /* Fill in the information about the available tables.  */
192      switch( SWAPIT(domain->must_swap, data->revision) ) {      switch (SWAPIT(domain->must_swap, data->revision)) {
193        case 0:        case 0:
194            domain->nstrings = SWAPIT(domain->must_swap, data->nstrings);            domain->nstrings = SWAPIT (domain->must_swap, data->nstrings);
195            domain->orig_tab = (struct string_desc *)            domain->orig_tab = (struct string_desc *)
196                ((char *) data + SWAPIT(domain->must_swap, data->orig_tab_offset));                ((char *) data + SWAPIT (domain->must_swap, data->orig_tab_offset));
197            domain->trans_tab = (struct string_desc *)            domain->trans_tab = (struct string_desc *)
198                ((char *) data + SWAPIT(domain->must_swap, data->trans_tab_offset));                ((char *) data + SWAPIT (domain->must_swap, data->trans_tab_offset));
199            domain->hash_size = SWAPIT(domain->must_swap, data->hash_tab_size);              domain->hash_size = SWAPIT (domain->must_swap, data->hash_tab_size);  
200            domain->hash_tab = (DWORD *)              domain->hash_tab = (DWORD *)  
201                ((char *) data + SWAPIT(domain->must_swap, data->hash_tab_offset));                      ((char *) data + SWAPIT (domain->must_swap, data->hash_tab_offset));      
202            break;            break;
203    
204        default: /* This is an invalid revision.  */        default: /* This is an invalid revision.  */
205            free( data );            free (data);
206            free( domain );            free (domain);
207            return NULL;            return NULL;
208      }      }
209    
210      /* allocate an array to keep track of code page mappings */      /* allocate an array to keep track of code page mappings */
211      domain->mapped = (char *)calloc( 1, domain->nstrings );      domain->mapped = (char *)calloc (1, domain->nstrings);
212      if (!domain->mapped)      if (!domain->mapped)
213          BUG (0);          BUG (0);
214        
215        domain->file_name = strdup (filename);
216        domain->modify_time = st.st_mtime;
217      return domain;      return domain;
218  }  }
219    
220    
221    /* To avoid problems that the current directory is not the
222     * same folder the WinPT.exe is located in, we extract the
223     * module directory.
224     */
225    char*
226    get_module_dir (char *buf, DWORD buflen)
227    {
228        if (buflen < 1 || !GetModuleFileName (glob_hinst, buf, buflen-1))
229            return NULL;
230    
231        size_t pos = strlen (buf);
232        while (pos > 0 && buf[--pos] != '\\')
233            ;
234        buf[pos + 1] = '\0';
235        return buf;
236    }
237    
238    
239    /* Returns a localisation name for a specific resource (file).
240     * For instance, lang=DE and file=winpt.chm will become winpt.DE.chm
241     */
242    char*
243    get_locale_name (const char *file)
244    {
245        const char *lang = gettext_get_langid ();
246        char buf[MAX_PATH+1];
247        
248        char *p = strrchr (file, '.');
249        if (p == NULL) {
250            p = new char[strlen(file)+strlen(lang)+2+1];
251            sprintf (p, "%s.%s", file, lang);
252            return p;
253        }
254        char *name = substr (file, 0, p - file);
255        char *ext = substr (file, p - file + 1, strlen(file));
256        p = get_module_dir (buf, MAX_PATH);
257        if (!p)
258            BUG (NULL);
259        StringBuffer s = p;
260        s = s + name + "." + lang + "." + ext;
261        p = s.getBufferCopy();
262        free_if_alloc (name);
263        free_if_alloc (ext);
264        return p;
265    }
266    
267    
268  /* Deallocate static resources. */  /* Deallocate static resources. */
269  void  void
270  gettext_free_current_domain (void)  gettext_free_current_domain (void)
271  {  {
272      if (!the_domain)      if (the_domain == NULL)
273          return;          return;
274      free (the_domain->data);      free (the_domain->data);
275      free (the_domain->mapped);      free (the_domain->mapped);
276        free (the_domain->file_name);
277      free (the_domain);      free (the_domain);
278      the_domain = NULL;      the_domain = NULL;
279  }  }
280    
281    
282  /* Set the file used for translations. Pass a NULL to disable translation.  /* Replace and release the old domain and assign a new domain then */
283     A new filename may be set at anytime. */  static void
284  int  gettext_update_domain (gettext_domain_t new_dom)
 gettext_set_file (const char *filename, const char *nls_dir)  
285  {  {
286      struct loaded_domain *domain = NULL;      gettext_free_current_domain ();
287        the_domain = new_dom;
288    }
289    
290      if (filename && *filename) {  
291          if (filename[1] == ':' && filename[2] == '\\')  char* get_reg_entry_mo (void);
292              domain = load_domain (filename); /* absolute path - use it as is */  
293          else { /* relative path - append ".mo" and get dir from the environment */  /* Fallback code which is used when no new NLS file were found.
294              char *buf, *dir;     Then we read the MODir registry key and check if a file named
295                       'winpt.mo' exists in this directory. */
296              dir = strdup (nls_dir);  static gettext_domain_t
297              if (!dir)  load_modir_domain (void)
298                  BUG (0);  {        
299              buf= (char *)malloc (strlen (dir) + strlen (filename)+1+3+1);      char *modir = get_reg_entry_mo ();
300              if (!buf)      if (!modir)
301                  BUG (0);          return NULL;
302        if (dir_exist_check (modir)) {
303              strcpy (w32_stpcpy (w32_stpcpy (          free_if_alloc (modir);
304                      w32_stpcpy (buf, dir),"\\"), filename),".mo");          return NULL;
             domain = load_domain(buf);  
             free (buf);  
             free (dir);  
         }  
         if (!domain)  
             return -1;  
305      }      }
306        StringBuffer s = modir;
307        if (modir[strlen (modir)-1] != '\\')
308            s += "\\";
309        s += "winpt.mo";
310        
311        gettext_domain_t domain = NULL;
312        const char *fname = s.getBuffer();
313        if (!file_exist_check (fname))
314            domain = load_domain (fname);
315        free_if_alloc (modir);
316        return domain;
317    }
318    
319      gettext_free_current_domain ();    
320      the_domain = domain;  /* Try to load the user domain, either from
321      return 0;     the WinPT.exe directory or from the registry */
322    void
323    gettext_set_user_domain (void)
324    {
325        if (!gettext_is_required ())
326            return;
327        
328        gettext_domain_t domain = NULL;
329        char *file = get_locale_name ("winpt.mo");
330        if (file && !file_exist_check (file))
331            domain = load_domain (file);
332        else
333            domain = load_modir_domain ();
334        free_if_alloc (file);
335        gettext_update_domain (domain);
336  }  }
337    
338    
339    int
340    gettext_domain_needs_refresh (void)
341    {
342        struct stat st;
343        
344        if (the_domain == NULL || stat (the_domain->file_name, &st))
345            return 0;
346        /* '1' means that the MO file were modified in the mean time
347           and the application should reload the domain. */
348        if (st.st_mtime != the_domain->modify_time)
349            return 1;
350        
351        return 0;
352    }
353    
354  static const char*  static const char*
355  get_string( struct loaded_domain *domain, DWORD idx )  get_string (gettext_domain_t domain, DWORD idx)
356  {  {
357      char *p = domain->data + SWAPIT(domain->must_swap,      char *p = domain->data + SWAPIT(domain->must_swap,
358                                      domain->trans_tab[idx].offset);                                      domain->trans_tab[idx].offset);
359      if( !domain->mapped[idx] ) {              if (!domain->mapped[idx])
360          domain->mapped[idx] = 1;                          domain->mapped[idx] = 1;
361      }      /* FIXME: all text is in UTF-8 and need to be converted to
362      return (const char*)p;         the locale charset. But the caller does not free the data and
363           thus we will get a leak */
364        return (const char *)p;
365  }  }
366    
367    
368  const char *  const char *
369  gettext( const char *msgid )  gettext (const char *msgid)
370  {  {
     struct loaded_domain *domain;  
371      size_t act = 0;      size_t act = 0;
372      size_t top, bottom;      size_t top, bottom;
373        
374      if (!(domain = the_domain))      gettext_domain_t domain = the_domain;
375        if (domain == NULL)
376          goto not_found;          goto not_found;
377    
378      /* Locate the MSGID and its translation.  */      /* Locate the MSGID and its translation.  */
379      if( domain->hash_size > 2 && domain->hash_tab ) {                if (domain->hash_size > 2 && domain->hash_tab) {
380          /* Use the hashing table.  */                    /* Use the hashing table.  */          
381          DWORD len = strlen (msgid);              DWORD len = strlen (msgid);    
382          DWORD hash_val = hash_string (msgid);            DWORD hash_val = hash_string (msgid);  
# Line 293  gettext( const char *msgid ) Line 384  gettext( const char *msgid )
384          DWORD incr = 1 + (hash_val % (domain->hash_size - 2));            DWORD incr = 1 + (hash_val % (domain->hash_size - 2));  
385          DWORD nstr = SWAPIT (domain->must_swap, domain->hash_tab[idx]);          DWORD nstr = SWAPIT (domain->must_swap, domain->hash_tab[idx]);
386    
387          if ( !nstr ) /* Hash table entry is empty.  */            if (!nstr) /* Hash table entry is empty.  */    
388              goto not_found;              goto not_found;
389            
390                            if (SWAPIT(domain->must_swap,  
         if( SWAPIT(domain->must_swap,    
391              domain->orig_tab[nstr - 1].length) == len                domain->orig_tab[nstr - 1].length) == len  
392              && !strcmp( msgid,                && !strcmp (msgid,  
393              domain->data + SWAPIT(domain->must_swap,                          domain->data + SWAPIT (domain->must_swap,
394                                      domain->orig_tab[nstr - 1].offset)) )                                                 domain->orig_tab[nstr - 1].offset)))
395                                      return get_string( domain, nstr - 1 );              return get_string (domain, nstr - 1);
396          for(;;) {          for(;;) {
397              if (idx >= domain->hash_size - incr)              if (idx >= domain->hash_size - incr)
398                  idx -= domain->hash_size - incr;                  idx -= domain->hash_size - incr;
399              else              else
400                  idx += incr;                  idx += incr;
401              nstr = SWAPIT(domain->must_swap, domain->hash_tab[idx]);              nstr = SWAPIT (domain->must_swap, domain->hash_tab[idx]);
402              if( !nstr )              if (!nstr)  
403                  goto not_found; /* Hash table entry is empty.  */                  goto not_found; /* Hash table entry is empty.  */
404    
405              if ( SWAPIT(domain->must_swap,              if (SWAPIT (domain->must_swap,
406                                  domain->orig_tab[nstr - 1].length) == len                          domain->orig_tab[nstr - 1].length) == len
407                                  && !strcmp (msgid,                  && !strcmp (msgid,
408                                  domain->data + SWAPIT(domain->must_swap,                              domain->data + SWAPIT (domain->must_swap,
409                                             domain->orig_tab[nstr - 1].offset)))                                                     domain->orig_tab[nstr - 1].offset)))
410                                            
411                                             return get_string( domain, nstr-1 );                  return get_string (domain, nstr-1);
412          } /* NOTREACHED */          } /* NOTREACHED */
413      }      }
414    
# Line 326  gettext( const char *msgid ) Line 416  gettext( const char *msgid )
416         array of messages.  */         array of messages.  */
417      bottom = 0;      bottom = 0;
418      top = domain->nstrings;      top = domain->nstrings;
419      while( bottom < top ) {                  while (bottom < top) {      
         int cmp_val;  
           
420          act = (bottom + top) / 2;                act = (bottom + top) / 2;      
421          cmp_val = strcmp(msgid, domain->data + SWAPIT(domain->must_swap,          int cmp_val = strcmp (msgid, domain->data + SWAPIT(domain->must_swap,
422                          domain->orig_tab[act].offset));                                                             domain->orig_tab[act].offset));
423          if (cmp_val < 0)          if (cmp_val < 0)
424              top = act;              top = act;
425          else if (cmp_val > 0)          else if (cmp_val > 0)
426              bottom = act + 1;              bottom = act + 1;
427          else              else
428              return get_string( domain, act );              return get_string (domain, act);
429      }      }
430    
431  not_found:  not_found:
# Line 345  not_found: Line 433  not_found:
433  }  }
434    
435    
436    
437  /* Map the user specific language ID to a gettext conform language string.  /* Map the user specific language ID to a gettext conform language string.
438       English "en" is defined as the fallback.
439     Example: LANG_GERMAN -> "de" */     Example: LANG_GERMAN -> "de" */
440  const char*  const char*
441  gettext_get_langid (void)  gettext_get_langid (void)
442  {  {
443      LANGID lang;      LANGID lang = GetUserDefaultLangID ();
444      int i;      
445        for (int i=0; lang_list[i].id; i++) {
     lang = GetUserDefaultLangID ();  
     if (PRIMARYLANGID (lang) == LANG_ENGLISH)  
         return NULL;  
   
     for (i=0; lang_list[i].id; i++) {  
446          if (PRIMARYLANGID (lang) == lang_list[i].langid)          if (PRIMARYLANGID (lang) == lang_list[i].langid)
447              return lang_list[i].id;              return lang_list[i].id;
448      }      }
449      return NULL;      
450        return "en";
451  }  }
452    
453    
454  /* Take a table with control item IDs and their translation  /* Take a table with control item IDs and their translation
455     and set each text to the translated value. */     and set each text to the translated value. */
456  void  void
457  gettext_localize_dialog (HWND dlg, struct gettext_tab_s *tab,  gettext_localize_dialog (HWND dlg, struct gettext_tab_s *tab, const char *title)
                          const char *title)  
458  {  {
459      int i;      for (int i=0; tab[i].trans != NULL; i++)
   
     for (i=0; tab[i].trans != NULL; i++)  
460          SetDlgItemText (dlg, tab[i].ctlid, tab[i].trans);          SetDlgItemText (dlg, tab[i].ctlid, tab[i].trans);
461      if (title)      if (title != NULL)
462          SetWindowText (dlg, title);          SetWindowText (dlg, title);
463  }  }

Legend:
Removed from v.278  
changed lines
  Added in v.372

[email protected]
ViewVC Help
Powered by ViewVC 1.1.26