diff options
author | rofl0r <retnyg@gmx.net> | 2014-08-13 17:36:28 +0200 |
---|---|---|
committer | rofl0r <retnyg@gmx.net> | 2017-03-26 04:57:18 +0100 |
commit | 438a47cf6da6dba04b04fee983c247cc13430b6c (patch) | |
tree | 8b42f8013d497c7ac9b63b5936e763547b036acf | |
parent | 355a3ce7e46b54eff1e9185dcd637b7a3a5005a0 (diff) | |
download | gettext-tiny-438a47cf6da6dba04b04fee983c247cc13430b6c.tar.gz |
implement sysdep replacement as proposed by dalias
the .mo format was designed in a way such that all application
can used a single shared non-mutable copy of the translation strings
in memory.
years later an issue was identified with format string specifier macros
that differ between platforms (like PRIu64) and a new
syntax introduced: %<PRIuMAX>.
this however breaks the non-mutable aspect of the .mo files, since now
every application has to remap a modified copy of the strings containing
these "sysdep" format specifiers.
we simply replace these with all possible expanded values of such a macro
directly in the generated .mo file.
-rw-r--r-- | src/msgfmt.c | 114 |
1 files changed, 103 insertions, 11 deletions
diff --git a/src/msgfmt.c b/src/msgfmt.c index ab70664..627e106 100644 --- a/src/msgfmt.c +++ b/src/msgfmt.c @@ -94,26 +94,118 @@ int strmap_comp(const void *a_, const void *b_) { return strcmp(cb_for_qsort->strbuffer[0] + a->str.off, cb_for_qsort->strbuffer[0] + b->str.off); } +enum sysdep_types { + st_priu32 = 0, + st_priu64, + st_priumax, + st_max +}; +static const char sysdep_str[][10]={ + [st_priu32] = "\x08<PRIu32>", + [st_priu64] = "\x08<PRIu64>", + [st_priumax] = "\x09<PRIuMAX>", +}; +static const char sysdep_repl[][8]={ + [st_priu32] = "\x02lu\0u", + [st_priu64] = "\x02lu\0llu", + [st_priumax] = "\x01ju" +}; +static const char *get_repl(enum sysdep_types type, unsigned nr) { + assert(nr < (unsigned)sysdep_repl[type][0]); + const char* p = sysdep_repl[type]+1; + while(nr--) p+=strlen(p)+1; + return p; +} +static void replace(char* text, unsigned textlen, const char* what, const char * with) { + char*p = text; + size_t la = strlen(what), li=strlen(with); + assert(la >= li); + for(p=text;textlen >= la;) { + if(!memcmp(p,what,la)) { + memcpy(p, with, li); + textlen -= la; + memmove(p+li,p+la,textlen+1); + p+=li; + } else { + p++; + textlen--; + } + } +} +static unsigned get_form(enum sysdep_types type, unsigned no, unsigned occurences[st_max]) { + unsigned i,divisor = 1; + for(i=type+1;i<st_max;i++) if(occurences[i]) divisor *= sysdep_repl[i][0]; + return (no/divisor)%sysdep_repl[type][0]; +} +static char** sysdep_transform(const char* text, unsigned textlen, unsigned *len, unsigned *count, int simulate) { + unsigned occurences[st_max] = {0}; + const char *p=text,*o; + unsigned i,j, l = textlen; + while(l && (o=strchr(p, '<'))) { + l-=o-p;p=o; + unsigned f = 0; + for(i=0;i<st_max;i++) + if(l>=(unsigned)sysdep_str[i][0] && !memcmp(p,sysdep_str[i]+1,sysdep_str[i][0])) { + occurences[i]++; + f=1; + p+=sysdep_str[i][0]; + l-=sysdep_str[i][0]; + break; + } + if(!f) p++,l--; + } + *count = 1; + for(i=0;i<st_max;i++) if(occurences[i]) *count *= sysdep_repl[i][0]; + l = textlen * *count; + for(i=0;i<*count;i++) for(j=0;j<st_max;j++) + if(occurences[j]) l-= occurences[j] * (sysdep_str[j][0] - strlen(get_repl(j, get_form(j, i, occurences)))); + *len = l+*count-1; + + char **out = 0; + if(!simulate) { + out = malloc((sizeof(char*)+textlen+1) * *count); + assert(out); + char *p = (void*)(out+*count); + for(i=0;i<*count;i++) { + out[i]=p; + memcpy(p, text, textlen+1); + p+=textlen+1; + } + for(i=0;i<*count;i++) for(j=0;j<st_max;j++) + if(occurences[j]) + replace(out[i], textlen, sysdep_str[j]+1, get_repl(j, get_form(j, i, occurences))); + } + + return out; +} int process_line_callback(struct po_info* info, void* user) { struct callbackdata *d = (struct callbackdata *) user; assert(info->type == pe_msgid || info->type == pe_msgstr); + char **sysdeps; + unsigned len, count, i, l; switch(d->pass) { case pass_collect_sizes: - d->num[info->type] += 1; - d->len[info->type] += info->textlen +1; + sysdep_transform(info->text, info->textlen, &len, &count, 1); + d->num[info->type] += count; + d->len[info->type] += len +1; break; case pass_second: - memcpy(d->strbuffer[info->type] + d->stroff[info->type], info->text, info->textlen+1); - if(info->type == pe_msgid) - d->strlist[d->curr[info->type]].str = (struct strtbl){.len=info->textlen, .off=d->stroff[info->type]}; - else { - assert(d->curr[pe_msgid] == d->curr[pe_msgstr]+1); - d->translist[d->curr[info->type]] = (struct strtbl){.len=info->textlen, .off=d->stroff[info->type]}; - d->strlist[d->curr[info->type]].trans = &d->translist[d->curr[info->type]]; + sysdeps = sysdep_transform(info->text, info->textlen, &len, &count, 0); + for(i=0;i<count;i++) { + l = strlen(sysdeps[i]); + memcpy(d->strbuffer[info->type] + d->stroff[info->type], sysdeps[i], l+1); + if(info->type == pe_msgid) + d->strlist[d->curr[info->type]].str = (struct strtbl){.len=l, .off=d->stroff[info->type]}; + else { + if(!i) assert(d->curr[pe_msgid] == d->curr[pe_msgstr] + count); + d->translist[d->curr[info->type]] = (struct strtbl){.len=l, .off=d->stroff[info->type]}; + d->strlist[d->curr[info->type]].trans = &d->translist[d->curr[info->type]]; + } + d->curr[info->type]++; + d->stroff[info->type]+=l+1; } - d->curr[info->type]++; - d->stroff[info->type]+=info->textlen+1; + free(sysdeps); break; default: abort(); |