diff --git a/Make.rules.in b/Make.rules.in index 827f4fd0b81..7760778f452 100644 --- a/Make.rules.in +++ b/Make.rules.in @@ -63,7 +63,7 @@ filter: dummy $(FLEX) $(LEXFLAGS) -o$@ $< .mc.res: - $(WMC) -U -O res -P $(top_srcdir)/po -o $@ $< + $(WMC) -U -O res $(PORCFLAGS) -o $@ $< .rc.res: $(WRC) $(RCFLAGS) -o $@ $< diff --git a/tools/wmc/po.c b/tools/wmc/po.c index 489c7d46cb6..b604ac75e6a 100644 --- a/tools/wmc/po.c +++ b/tools/wmc/po.c @@ -19,6 +19,7 @@ */ #include "config.h" +#include "wine/port.h" #include #include @@ -36,7 +37,17 @@ #include "write.h" #include "windef.h" -#ifdef HAVE_LIBGETTEXTPO +struct mo_file +{ + unsigned int magic; + unsigned int revision; + unsigned int count; + unsigned int msgid_off; + unsigned int msgstr_off; + /* ... rest of file data here */ +}; + +static lan_blk_t *new_top, *new_tail; static const struct { @@ -272,44 +283,11 @@ static const struct #endif }; -static void po_xerror( int severity, po_message_t message, - const char *filename, size_t lineno, size_t column, - int multiline_p, const char *message_text ) -{ - fprintf( stderr, "%s:%u:%u: %s\n", - filename, (unsigned int)lineno, (unsigned int)column, message_text ); - if (severity) exit(1); -} - -static void po_xerror2( int severity, po_message_t message1, - const char *filename1, size_t lineno1, size_t column1, - int multiline_p1, const char *message_text1, - po_message_t message2, - const char *filename2, size_t lineno2, size_t column2, - int multiline_p2, const char *message_text2 ) -{ - fprintf( stderr, "%s:%u:%u: %s\n", - filename1, (unsigned int)lineno1, (unsigned int)column1, message_text1 ); - fprintf( stderr, "%s:%u:%u: %s\n", - filename2, (unsigned int)lineno2, (unsigned int)column2, message_text2 ); - if (severity) exit(1); -} - -static const struct po_xerror_handler po_xerror_handler = { po_xerror, po_xerror2 }; - static BOOL is_english( int lan ) { return lan == MAKELANGID( LANG_ENGLISH, SUBLANG_DEFAULT ); } -static char *convert_string_utf8( const lanmsg_t *msg ) -{ - char *buffer = xmalloc( msg->len * 4 + 1 ); - int len = wine_utf8_wcstombs( 0, msg->msg, msg->len, buffer, msg->len * 4 ); - buffer[len] = 0; - return buffer; -} - static char *convert_msgid_ascii( const lanmsg_t *msg, int error_on_invalid_char ) { int i; @@ -346,6 +324,16 @@ static char *get_message_context( char **msgid ) return context; } +#ifdef HAVE_LIBGETTEXTPO + +static char *convert_string_utf8( const lanmsg_t *msg ) +{ + char *buffer = xmalloc( msg->len * 4 + 1 ); + int len = wine_utf8_wcstombs( 0, msg->msg, msg->len, buffer, msg->len * 4 ); + buffer[len] = 0; + return buffer; +} + static po_message_t find_message( po_file_t po, const char *msgid, const char *msgctxt, po_message_iterator_t *iterator ) { @@ -363,6 +351,31 @@ static po_message_t find_message( po_file_t po, const char *msgid, const char *m return msg; } +static void po_xerror( int severity, po_message_t message, + const char *filename, size_t lineno, size_t column, + int multiline_p, const char *message_text ) +{ + fprintf( stderr, "%s:%u:%u: %s\n", + filename, (unsigned int)lineno, (unsigned int)column, message_text ); + if (severity) exit(1); +} + +static void po_xerror2( int severity, po_message_t message1, + const char *filename1, size_t lineno1, size_t column1, + int multiline_p1, const char *message_text1, + po_message_t message2, + const char *filename2, size_t lineno2, size_t column2, + int multiline_p2, const char *message_text2 ) +{ + fprintf( stderr, "%s:%u:%u: %s\n", + filename1, (unsigned int)lineno1, (unsigned int)column1, message_text1 ); + fprintf( stderr, "%s:%u:%u: %s\n", + filename2, (unsigned int)lineno2, (unsigned int)column2, message_text2 ); + if (severity) exit(1); +} + +static const struct po_xerror_handler po_xerror_handler = { po_xerror, po_xerror2 }; + static void add_po_string( po_file_t po, const lanmsg_t *msgid, const lanmsg_t *msgstr ) { po_message_t msg; @@ -437,12 +450,120 @@ void write_pot_file( const char *outname ) po_file_free( po ); } -static lan_blk_t *new_top, *new_tail; -static lanmsg_t *translate_string( po_file_t po, lanmsg_t *str, int lang, int *found ) +#else /* HAVE_LIBGETTEXTPO */ + +void write_pot_file( const char *outname ) +{ + error( "PO files not supported in this wmc build\n" ); +} + +#endif + +static struct mo_file *mo_file; + +static void byteswap( unsigned int *data, unsigned int count ) +{ + unsigned int i; + + for (i = 0; i < count; i++) + data[i] = data[i] >> 24 | (data[i] >> 8 & 0xff00) | (data[i] << 8 & 0xff0000) | data[i] << 24; +} + +static void load_mo_file( const char *name ) +{ + struct stat st; + int res, fd; + + fd = open( name, O_RDONLY | O_BINARY ); + if (fd == -1) fatal_perror( "Failed to open %s", name ); + fstat( fd, &st ); + mo_file = xmalloc( st.st_size ); + res = read( fd, mo_file, st.st_size ); + if (res == -1) fatal_perror( "Failed to read %s", name ); + else if (res != st.st_size) error( "Failed to read %s\n", name ); + close( fd ); + + /* sanity checks */ + + if (st.st_size < sizeof(*mo_file)) + error( "%s is not a valid .mo file\n", name ); + if (mo_file->magic == 0xde120495) + byteswap( &mo_file->revision, 4 ); + else if (mo_file->magic != 0x950412de) + error( "%s is not a valid .mo file\n", name ); + if ((mo_file->revision >> 16) > 1) + error( "%s: unsupported file version %x\n", name, mo_file->revision ); + if (mo_file->msgid_off >= st.st_size || + mo_file->msgstr_off >= st.st_size || + st.st_size < sizeof(*mo_file) + 2 * 8 * mo_file->count) + error( "%s: corrupted file\n", name ); + + if (mo_file->magic == 0xde120495) + { + byteswap( (unsigned int *)((char *)mo_file + mo_file->msgid_off), 2 * mo_file->count ); + byteswap( (unsigned int *)((char *)mo_file + mo_file->msgstr_off), 2 * mo_file->count ); + } +} + +static void free_mo_file(void) +{ + free( mo_file ); + mo_file = NULL; +} + +static inline const char *get_mo_msgid( int index ) +{ + const char *base = (const char *)mo_file; + const unsigned int *offsets = (const unsigned int *)(base + mo_file->msgid_off); + return base + offsets[2 * index + 1]; +} + +static inline const char *get_mo_msgstr( int index ) +{ + const char *base = (const char *)mo_file; + const unsigned int *offsets = (const unsigned int *)(base + mo_file->msgstr_off); + return base + offsets[2 * index + 1]; +} + +static const char *get_msgstr( const char *msgid, const char *context, int *found ) +{ + int pos, res, min, max; + const char *ret = msgid; + char *id = NULL; + + if (!mo_file) /* strings containing a context still need to be transformed */ + { + if (context) (*found)++; + return ret; + } + + if (context) id = strmake( "%s%c%s", context, 4, msgid ); + min = 0; + max = mo_file->count - 1; + while (min <= max) + { + pos = (min + max) / 2; + res = strcmp( get_mo_msgid(pos), id ? id : msgid ); + if (!res) + { + const char *str = get_mo_msgstr( pos ); + if (str[0]) /* ignore empty strings */ + { + ret = str; + (*found)++; + } + break; + } + if (res > 0) max = pos - 1; + else min = pos + 1; + } + free( id ); + return ret; +} + +static lanmsg_t *translate_string( lanmsg_t *str, int lang, int *found ) { - po_message_t msg; - po_message_iterator_t iterator; lanmsg_t *new; const char *transl; int res; @@ -452,16 +573,7 @@ static lanmsg_t *translate_string( po_file_t po, lanmsg_t *str, int lang, int *f msgid = buffer; context = get_message_context( &msgid ); - msg = find_message( po, msgid, context, &iterator ); - po_message_iterator_free( iterator ); - - if (msg && !po_message_is_fuzzy( msg )) - { - transl = po_message_msgstr( msg ); - if (!transl[0]) transl = msgid; /* ignore empty strings */ - else (*found)++; - } - else transl = msgid; + transl = get_msgstr( msgid, context, found ); new = xmalloc( sizeof(*new) ); new->lan = lang; @@ -477,7 +589,7 @@ static lanmsg_t *translate_string( po_file_t po, lanmsg_t *str, int lang, int *f return new; } -static void translate_block( po_file_t po, block_t *blk, block_t *new, int lang, int *found ) +static void translate_block( block_t *blk, block_t *new, int lang, int *found ) { int i; @@ -488,12 +600,12 @@ static void translate_block( po_file_t po, block_t *blk, block_t *new, int lang, new->nmsg = blk->nmsg; for (i = 0; i < blk->nmsg; i++) { - new->msgs[i] = translate_string( po, blk->msgs[i], lang, found ); + new->msgs[i] = translate_string( blk->msgs[i], lang, found ); new->size += ((2 * new->msgs[i]->len + 3) & ~3) + 4; } } -static void translate_messages( po_file_t po, int lang ) +static void translate_messages( int lang ) { int i, found; lan_blk_t *lbp, *new; @@ -510,7 +622,7 @@ static void translate_messages( po_file_t po, int lang ) new->nblk = lbp->nblk; for (i = 0; i < lbp->nblk; i++) - translate_block( po, &lbp->blks[i], &new->blks[i], lang, &found ); + translate_block( &lbp->blks[i], &new->blks[i], lang, &found ); if (found) { if (new_tail) new_tail->next = new; @@ -529,7 +641,6 @@ static void translate_messages( po_file_t po, int lang ) void add_translations( const char *po_dir ) { lan_blk_t *lbp; - po_file_t po; char buffer[256]; char *p, *tok, *name; unsigned int i; @@ -539,6 +650,12 @@ void add_translations( const char *po_dir ) for (lbp = lanblockhead; lbp; lbp = lbp->next) if (is_english( lbp->lan )) break; if (!lbp) return; + if (!po_dir) /* run through the translation process to remove msg contexts */ + { + translate_messages( MAKELANGID( LANG_ENGLISH, SUBLANG_DEFAULT )); + goto done; + } + new_top = new_tail = NULL; name = strmake( "%s/LINGUAS", po_dir ); @@ -555,16 +672,16 @@ void add_translations( const char *po_dir ) if (i == sizeof(languages)/sizeof(languages[0])) error( "unknown language '%s'\n", tok ); - name = strmake( "%s/%s.po", po_dir, tok ); - if (!(po = po_file_read( name, &po_xerror_handler ))) - error( "cannot load po file for language '%s'\n", tok ); - translate_messages( po, MAKELANGID(languages[i].id, languages[i].sub) ); - po_file_free( po ); + name = strmake( "%s/%s.mo", po_dir, tok ); + load_mo_file( name ); + translate_messages( MAKELANGID(languages[i].id, languages[i].sub) ); + free_mo_file(); free( name ); } } fclose( f ); +done: /* prepend the translated messages to the global list */ if (new_tail) { @@ -573,16 +690,3 @@ void add_translations( const char *po_dir ) lanblockhead = new_top; } } - -#else /* HAVE_LIBGETTEXTPO */ - -void write_pot_file( const char *outname ) -{ - error( "PO files not supported in this wmc build\n" ); -} - -void add_translations( const char *po_dir ) -{ -} - -#endif diff --git a/tools/wmc/utils.c b/tools/wmc/utils.c index 276eeb4f1ea..bd3f0a6160b 100644 --- a/tools/wmc/utils.c +++ b/tools/wmc/utils.c @@ -88,6 +88,17 @@ void internal_error(const char *file, int line, const char *s, ...) exit(3); } +void fatal_perror( const char *msg, ... ) +{ + va_list valist; + va_start( valist, msg ); + fprintf(stderr, "Error: "); + vfprintf( stderr, msg, valist ); + perror( " " ); + va_end( valist ); + exit(2); +} + void error(const char *s, ...) { va_list ap; diff --git a/tools/wmc/utils.h b/tools/wmc/utils.h index 909cc9995e8..2ca62e94114 100644 --- a/tools/wmc/utils.h +++ b/tools/wmc/utils.h @@ -38,6 +38,7 @@ int mcy_error(const char *s, ...) __attribute__((format (printf, 1, 2))); int xyyerror(const char *s, ...) __attribute__((format (printf, 1, 2))); int mcy_warning(const char *s, ...) __attribute__((format (printf, 1, 2))); void internal_error(const char *file, int line, const char *s, ...) __attribute__((format (printf, 3, 4))); +void fatal_perror( const char *msg, ... ) __attribute__((format (printf, 1, 2), noreturn)); void error(const char *s, ...) __attribute__((format (printf, 1, 2))); void warning(const char *s, ...) __attribute__((format (printf, 1, 2)));