summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorrofl0r <retnyg@gmx.net>2014-08-13 17:36:28 +0200
committerrofl0r <retnyg@gmx.net>2017-03-26 04:57:18 +0100
commit438a47cf6da6dba04b04fee983c247cc13430b6c (patch)
tree8b42f8013d497c7ac9b63b5936e763547b036acf
parent355a3ce7e46b54eff1e9185dcd637b7a3a5005a0 (diff)
downloadgettext-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.c114
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();