/*********************************************************************** * * * This software is part of the ast package * * Copyright (c) 1985-2007 AT&T Knowledge Ventures * * and is licensed under the * * Common Public License, Version 1.0 * * by AT&T Knowledge Ventures * * * * A copy of the License is available at * * http://www.opensource.org/licenses/cpl1.0.txt * * (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * * * * Information and Software Systems Research * * AT&T Research * * Florham Park NJ * * * * Glenn Fowler * * David Korn * * Phong Vo * * * ***********************************************************************/ #pragma prototyped /* * Glenn Fowler * AT&T Research * * machine independent binary message catalog implementation */ #include "sfhdr.h" #include "lclib.h" #include #define _MC_PRIVATE_ \ size_t nstrs; \ size_t nmsgs; \ iconv_t cvt; \ Sfio_t* tmp; \ Vmalloc_t* vm; #include #include #include #include /* * find the binary message catalog path for * result placed in path of size PATH_MAX * pointer to path returned * catalog==0 tests for category directory or file * nls!=0 enables NLSPATH+LANG hack (not implemented yet) */ char* mcfind(char* path, const char* locale, const char* catalog, int category, int nls) { register int c; register char* s; register char* e; register char* p; register const char* v; int i; int first; int next; int last; int oerrno; Lc_t* lc; char file[PATH_MAX]; char* paths[5]; static char lc_messages[] = "LC_MESSAGES"; if ((category = lcindex(category, 1)) < 0) return 0; if (!(lc = locale ? lcmake(locale) : locales[category])) return 0; oerrno = errno; if (catalog && *catalog == '/') { i = eaccess(catalog, R_OK); errno = oerrno; if (i) return 0; strncpy(path, catalog, PATH_MAX-1); return path; } i = 0; #if !_lib_catopen if ((p = getenv("NLSPATH")) && *p) paths[i++] = p; #endif paths[i++] = "share/lib/locale/%l/%C/%N"; paths[i++] = "share/locale/%l/%C/%N"; paths[i++] = "lib/locale/%l/%C/%N"; paths[i] = 0; next = 1; for (i = 0; p = paths[i]; i += next) { first = 1; last = 0; e = &file[elementsof(file) - 1]; while (*p) { s = file; for (;;) { switch (c = *p++) { case 0: p--; break; case ':': break; case '%': if (s < e) { switch (c = *p++) { case 0: p--; continue; case 'N': v = catalog; break; case 'L': if (first) { first = 0; if (next) { v = lc->code; if (lc->code != lc->language->code) next = 0; } else { next = 1; v = lc->language->code; } } break; case 'l': v = lc->language->code; break; case 't': v = lc->territory->code; break; case 'c': v = lc->charset->code; break; case 'C': case_C: if (!catalog) last = 1; v = categories[category].name; break; default: *s++ = c; continue; } if (v) while (*v && s < e) *s++ = *v++; } continue; case '/': if (last) break; if (category != AST_LC_MESSAGES && strneq(p, lc_messages, sizeof(lc_messages) - 1) && p[sizeof(lc_messages)-1] == '/') { p += sizeof(lc_messages) - 1; goto case_C; } /*FALLTHROUGH*/ default: if (s < e) *s++ = c; continue; } break; } if (s > file) *s = 0; else if (!catalog) continue; else strncpy(file, catalog, elementsof(file)); if (ast.locale.set & AST_LC_find) sfprintf(sfstderr, "locale find %s\n", file); if (s = pathpath(path, file, "", (!catalog && category == AST_LC_MESSAGES) ? PATH_READ : (PATH_REGULAR|PATH_READ|PATH_ABSOLUTE))) { if (ast.locale.set & (AST_LC_find|AST_LC_setlocale)) sfprintf(sfstderr, "locale path %s\n", s); errno = oerrno; return s; } } } errno = oerrno; return 0; } /* * allocate and read the binary message catalog ip * if ip==0 then space is allocated for mcput() * 0 returned on any error */ Mc_t* mcopen(register Sfio_t* ip) { register Mc_t* mc; register char** mp; register char* sp; Vmalloc_t* vm; char* rp; int i; int j; int oerrno; size_t n; char buf[MC_MAGIC_SIZE]; oerrno = errno; if (ip) { /* * check the magic */ if (sfread(ip, buf, MC_MAGIC_SIZE) != MC_MAGIC_SIZE) { errno = oerrno; return 0; } if (memcmp(buf, MC_MAGIC, MC_MAGIC_SIZE)) return 0; } /* * allocate the region */ if (!(vm = vmopen(Vmdcheap, Vmbest, 0)) || !(mc = vmnewof(vm, 0, Mc_t, 1, 0))) { errno = oerrno; return 0; } mc->vm = vm; mc->cvt = (iconv_t)(-1); if (ip) { /* * read the translation record */ if (!(sp = sfgetr(ip, 0, 0)) || !(mc->translation = vmstrdup(vm, sp))) goto bad; /* * read the optional header records */ do { if (!(sp = sfgetr(ip, 0, 0))) goto bad; } while (*sp); /* * get the component dimensions */ mc->nstrs = sfgetu(ip); mc->nmsgs = sfgetu(ip); mc->num = sfgetu(ip); if (sfeof(ip)) goto bad; } else if (!(mc->translation = vmnewof(vm, 0, char, 1, 0))) goto bad; /* * allocate the remaining space */ if (!(mc->set = vmnewof(vm, 0, Mcset_t, mc->num + 1, 0))) goto bad; if (!ip) return mc; if (!(mp = vmnewof(vm, 0, char*, mc->nmsgs + mc->num + 1, 0))) goto bad; if (!(rp = sp = vmalloc(vm, mc->nstrs + 1))) goto bad; /* * get the set dimensions and initialize the msg pointers */ while (i = sfgetu(ip)) { if (i > mc->num) goto bad; n = sfgetu(ip); mc->set[i].num = n; mc->set[i].msg = mp; mp += n + 1; } /* * read the msg sizes and set up the msg pointers */ for (i = 1; i <= mc->num; i++) for (j = 1; j <= mc->set[i].num; j++) if (n = sfgetu(ip)) { mc->set[i].msg[j] = sp; sp += n; } /* * read the string table */ if (sfread(ip, rp, mc->nstrs) != mc->nstrs || sfgetc(ip) != EOF) goto bad; if (!(mc->tmp = sfstropen())) goto bad; mc->cvt = iconv_open("", "utf"); errno = oerrno; return mc; bad: vmclose(vm); errno = oerrno; return 0; } /* * return the message in mc * msg returned on error * utf message text converted to ucs */ char* mcget(register Mc_t* mc, int set, int num, const char* msg) { char* s; size_t n; int p; if (!mc || set < 0 || set > mc->num || num < 1 || num > mc->set[set].num || !(s = mc->set[set].msg[num])) return (char*)msg; if (mc->cvt == (iconv_t)(-1)) return s; if ((p = sfstrtell(mc->tmp)) > sfstrsize(mc->tmp) / 2) { p = 0; sfstrseek(mc->tmp, p, SEEK_SET); } n = strlen(s) + 1; iconv_write(mc->cvt, mc->tmp, &s, &n, NiL); return sfstrbase(mc->tmp) + p; } /* * set message to msg * msg==0 deletes the message * the message and set counts are adjusted * 0 returned on success, -1 otherwise */ int mcput(register Mc_t* mc, int set, int num, const char* msg) { register int i; register char* s; register Mcset_t* sp; register char** mp; /* * validate the arguments */ if (!mc || set > MC_SET_MAX || num > MC_NUM_MAX) return -1; /* * deletions don't kick in allocations (duh) */ if (!msg) { if (set <= mc->num && num <= mc->set[set].num && (s = mc->set[set].msg[num])) { /* * decrease the string table size */ mc->set[set].msg[num] = 0; mc->nstrs -= strlen(s) + 1; if (mc->set[set].num == num) { /* * decrease the max msg num */ mp = mc->set[set].msg + num; while (num && !mp[--num]); mc->nmsgs -= mc->set[set].num - num; if (!(mc->set[set].num = num) && mc->num == set) { /* * decrease the max set num */ while (num && !mc->set[--num].num); mc->num = num; } } } return 0; } /* * keep track of the highest set and allocate if necessary */ if (set > mc->num) { if (set > mc->gen) { i = MC_SET_MAX; if (!(sp = vmnewof(mc->vm, 0, Mcset_t, i + 1, 0))) return -1; mc->gen = i; for (i = 1; i <= mc->num; i++) sp[i] = mc->set[i]; mc->set = sp; } mc->num = set; } sp = mc->set + set; /* * keep track of the highest msg and allocate if necessary */ if (num > sp->num) { if (num > sp->gen) { if (!mc->gen) { i = (MC_NUM_MAX + 1) / 32; if (i <= num) i = 2 * num; if (i > MC_NUM_MAX) i = MC_NUM_MAX; if (!(mp = vmnewof(mc->vm, 0, char*, i + 1, 0))) return -1; mc->gen = i; sp->msg = mp; for (i = 1; i <= sp->num; i++) mp[i] = sp->msg[i]; } else { i = 2 * mc->gen; if (i > MC_NUM_MAX) i = MC_NUM_MAX; if (!(mp = vmnewof(mc->vm, sp->msg, char*, i + 1, 0))) return -1; sp->gen = i; sp->msg = mp; } } mc->nmsgs += num - sp->num; sp->num = num; } /* * decrease the string table size */ if (s = sp->msg[num]) { /* * no-op if no change */ if (streq(s, msg)) return 0; mc->nstrs -= strlen(s) + 1; } /* * allocate, add and adjust the string table size */ if (!(s = vmstrdup(mc->vm, msg))) return -1; sp->msg[num] = s; mc->nstrs += strlen(s) + 1; return 0; } /* * dump message catalog mc to op * 0 returned on success, -1 otherwise */ int mcdump(register Mc_t* mc, register Sfio_t* op) { register int i; register int j; register int n; register char* s; register Mcset_t* sp; /* * write the magic */ if (sfwrite(op, MC_MAGIC, MC_MAGIC_SIZE) != MC_MAGIC_SIZE) return -1; /* * write the translation record */ sfputr(op, mc->translation, 0); /* optional header records here */ /* * end of optional header records */ sfputu(op, 0); /* * write the global dimensions */ sfputu(op, mc->nstrs); sfputu(op, mc->nmsgs); sfputu(op, mc->num); /* * write the set dimensions */ for (i = 1; i <= mc->num; i++) if (mc->set[i].num) { sfputu(op, i); sfputu(op, mc->set[i].num); } sfputu(op, 0); /* * write the message sizes */ for (i = 1; i <= mc->num; i++) if (mc->set[i].num) { sp = mc->set + i; for (j = 1; j <= sp->num; j++) { n = (s = sp->msg[j]) ? (strlen(s) + 1) : 0; sfputu(op, n); } } /* * write the string table */ for (i = 1; i <= mc->num; i++) if (mc->set[i].num) { sp = mc->set + i; for (j = 1; j <= sp->num; j++) if (s = sp->msg[j]) sfputr(op, s, 0); } /* * sync and return */ return sfsync(op); } /* * parse number from s * e!=0 is set to the next char after the parse * set!=0 is set to message set number * msg!=0 is set to message number * the message set number is returned * * the base 36 hash gives reasonable values for these: * * "ast" : ((((36#a^36#s^36#t)-9)&63)+1) = 3 * "gnu" : ((((36#g^36#n^36#u)-9)&63)+1) = 17 * "sgi" : ((((36#s^36#g^36#i)-9)&63)+1) = 22 * "sun" : ((((36#s^36#u^36#n)-9)&63)+1) = 13 */ int mcindex(register const char* s, char** e, int* set, int* msg) { register int c; register int m; register int n; register int r; register unsigned char* cv; char* t; m = 0; n = strtol(s, &t, 0); if (t == (char*)s) { SFCVINIT(); cv = _Sfcv36; for (n = m = 0; (c = cv[*s]) < 36; s++) { m++; n ^= c; } m = (m <= 3) ? 63 : ((1 << (m + 3)) - 1); n = ((n - 9) & m) + 1; } else s = (const char*)t; r = n; if (*s) m = strtol(s + 1, e, 0); else { if (e) *e = (char*)s; if (m) m = 0; else { m = n; n = 1; } } if (set) *set = n; if (msg) *msg = m; return r; } /* * close the message catalog mc */ int mcclose(register Mc_t* mc) { if (!mc) return -1; if (mc->tmp) sfclose(mc->tmp); if (mc->cvt != (iconv_t)(-1)) iconv_close(mc->cvt); vmclose(mc->vm); return 0; }