1 /*********************************************************************** 2 * * 3 * This software is part of the ast package * 4 * Copyright (c) 1985-2009 AT&T Intellectual Property * 5 * and is licensed under the * 6 * Common Public License, Version 1.0 * 7 * by AT&T Intellectual Property * 8 * * 9 * A copy of the License is available at * 10 * http://www.opensource.org/licenses/cpl1.0.txt * 11 * (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) * 12 * * 13 * Information and Software Systems Research * 14 * AT&T Research * 15 * Florham Park NJ * 16 * * 17 * Glenn Fowler <gsf@research.att.com> * 18 * David Korn <dgk@research.att.com> * 19 * Phong Vo <kpv@research.att.com> * 20 * * 21 ***********************************************************************/ 22 #pragma prototyped 23 24 /* 25 * Glenn Fowler 26 * AT&T Research 27 * 28 * machine independent binary message catalog implementation 29 */ 30 31 #include "sfhdr.h" 32 #include "lclib.h" 33 34 #include <iconv.h> 35 36 #define _MC_PRIVATE_ \ 37 size_t nstrs; \ 38 size_t nmsgs; \ 39 iconv_t cvt; \ 40 Sfio_t* tmp; \ 41 Vmalloc_t* vm; 42 43 #include <vmalloc.h> 44 #include <error.h> 45 #include <mc.h> 46 #include <nl_types.h> 47 48 /* 49 * find the binary message catalog path for <locale,catalog> 50 * result placed in path of size PATH_MAX 51 * pointer to path returned 52 * catalog==0 tests for category directory or file 53 * nls!=0 enables NLSPATH+LANG hack (not implemented yet) 54 */ 55 56 char* 57 mcfind(char* path, const char* locale, const char* catalog, int category, int nls) 58 { 59 register int c; 60 register char* s; 61 register char* e; 62 register char* p; 63 register const char* v; 64 int i; 65 int first; 66 int next; 67 int last; 68 int oerrno; 69 Lc_t* lc; 70 char file[PATH_MAX]; 71 char* paths[5]; 72 73 static char lc_messages[] = "LC_MESSAGES"; 74 75 if ((category = lcindex(category, 1)) < 0) 76 return 0; 77 if (!(lc = locale ? lcmake(locale) : locales[category])) 78 return 0; 79 oerrno = errno; 80 if (catalog && *catalog == '/') 81 { 82 i = eaccess(catalog, R_OK); 83 errno = oerrno; 84 if (i) 85 return 0; 86 strncpy(path, catalog, PATH_MAX-1); 87 return path; 88 } 89 i = 0; 90 #if !_lib_catopen 91 if ((p = getenv("NLSPATH")) && *p) 92 paths[i++] = p; 93 #endif 94 paths[i++] = "share/lib/locale/%l/%C/%N"; 95 paths[i++] = "share/locale/%l/%C/%N"; 96 paths[i++] = "lib/locale/%l/%C/%N"; 97 paths[i] = 0; 98 next = 1; 99 for (i = 0; p = paths[i]; i += next) 100 { 101 first = 1; 102 last = 0; 103 e = &file[elementsof(file) - 1]; 104 while (*p) 105 { 106 s = file; 107 for (;;) 108 { 109 switch (c = *p++) 110 { 111 case 0: 112 p--; 113 break; 114 case ':': 115 break; 116 case '%': 117 if (s < e) 118 { 119 switch (c = *p++) 120 { 121 case 0: 122 p--; 123 continue; 124 case 'N': 125 v = catalog; 126 break; 127 case 'L': 128 if (first) 129 { 130 first = 0; 131 if (next) 132 { 133 v = lc->code; 134 if (lc->code != lc->language->code) 135 next = 0; 136 } 137 else 138 { 139 next = 1; 140 v = lc->language->code; 141 } 142 } 143 break; 144 case 'l': 145 v = lc->language->code; 146 break; 147 case 't': 148 v = lc->territory->code; 149 break; 150 case 'c': 151 v = lc->charset->code; 152 break; 153 case 'C': 154 case_C: 155 if (!catalog) 156 last = 1; 157 v = lc_categories[category].name; 158 break; 159 default: 160 *s++ = c; 161 continue; 162 } 163 if (v) 164 while (*v && s < e) 165 *s++ = *v++; 166 } 167 continue; 168 case '/': 169 if (last) 170 break; 171 if (category != AST_LC_MESSAGES && strneq(p, lc_messages, sizeof(lc_messages) - 1) && p[sizeof(lc_messages)-1] == '/') 172 { 173 p += sizeof(lc_messages) - 1; 174 goto case_C; 175 } 176 /*FALLTHROUGH*/ 177 default: 178 if (s < e) 179 *s++ = c; 180 continue; 181 } 182 break; 183 } 184 if (s > file) 185 *s = 0; 186 else if (!catalog) 187 continue; 188 else 189 strncpy(file, catalog, elementsof(file)); 190 if (ast.locale.set & AST_LC_find) 191 sfprintf(sfstderr, "locale find %s\n", file); 192 if (s = pathpath(path, file, "", (!catalog && category == AST_LC_MESSAGES) ? PATH_READ : (PATH_REGULAR|PATH_READ|PATH_ABSOLUTE))) 193 { 194 if (ast.locale.set & (AST_LC_find|AST_LC_setlocale)) 195 sfprintf(sfstderr, "locale path %s\n", s); 196 errno = oerrno; 197 return s; 198 } 199 } 200 } 201 errno = oerrno; 202 return 0; 203 } 204 205 /* 206 * allocate and read the binary message catalog ip 207 * if ip==0 then space is allocated for mcput() 208 * 0 returned on any error 209 */ 210 211 Mc_t* 212 mcopen(register Sfio_t* ip) 213 { 214 register Mc_t* mc; 215 register char** mp; 216 register char* sp; 217 Vmalloc_t* vm; 218 char* rp; 219 int i; 220 int j; 221 int oerrno; 222 size_t n; 223 char buf[MC_MAGIC_SIZE]; 224 225 oerrno = errno; 226 if (ip) 227 { 228 /* 229 * check the magic 230 */ 231 232 if (sfread(ip, buf, MC_MAGIC_SIZE) != MC_MAGIC_SIZE) 233 { 234 errno = oerrno; 235 return 0; 236 } 237 if (memcmp(buf, MC_MAGIC, MC_MAGIC_SIZE)) 238 return 0; 239 } 240 241 /* 242 * allocate the region 243 */ 244 245 if (!(vm = vmopen(Vmdcheap, Vmbest, 0)) || !(mc = vmnewof(vm, 0, Mc_t, 1, 0))) 246 { 247 errno = oerrno; 248 return 0; 249 } 250 mc->vm = vm; 251 mc->cvt = (iconv_t)(-1); 252 if (ip) 253 { 254 /* 255 * read the translation record 256 */ 257 258 if (!(sp = sfgetr(ip, 0, 0)) || !(mc->translation = vmstrdup(vm, sp))) 259 goto bad; 260 261 /* 262 * read the optional header records 263 */ 264 265 do 266 { 267 if (!(sp = sfgetr(ip, 0, 0))) 268 goto bad; 269 } while (*sp); 270 271 /* 272 * get the component dimensions 273 */ 274 275 mc->nstrs = sfgetu(ip); 276 mc->nmsgs = sfgetu(ip); 277 mc->num = sfgetu(ip); 278 if (sfeof(ip)) 279 goto bad; 280 } 281 else if (!(mc->translation = vmnewof(vm, 0, char, 1, 0))) 282 goto bad; 283 284 /* 285 * allocate the remaining space 286 */ 287 288 if (!(mc->set = vmnewof(vm, 0, Mcset_t, mc->num + 1, 0))) 289 goto bad; 290 if (!ip) 291 return mc; 292 if (!(mp = vmnewof(vm, 0, char*, mc->nmsgs + mc->num + 1, 0))) 293 goto bad; 294 if (!(rp = sp = vmalloc(vm, mc->nstrs + 1))) 295 goto bad; 296 297 /* 298 * get the set dimensions and initialize the msg pointers 299 */ 300 301 while (i = sfgetu(ip)) 302 { 303 if (i > mc->num) 304 goto bad; 305 n = sfgetu(ip); 306 mc->set[i].num = n; 307 mc->set[i].msg = mp; 308 mp += n + 1; 309 } 310 311 /* 312 * read the msg sizes and set up the msg pointers 313 */ 314 315 for (i = 1; i <= mc->num; i++) 316 for (j = 1; j <= mc->set[i].num; j++) 317 if (n = sfgetu(ip)) 318 { 319 mc->set[i].msg[j] = sp; 320 sp += n; 321 } 322 323 /* 324 * read the string table 325 */ 326 327 if (sfread(ip, rp, mc->nstrs) != mc->nstrs || sfgetc(ip) != EOF) 328 goto bad; 329 if (!(mc->tmp = sfstropen())) 330 goto bad; 331 mc->cvt = iconv_open("", "utf"); 332 errno = oerrno; 333 return mc; 334 bad: 335 vmclose(vm); 336 errno = oerrno; 337 return 0; 338 } 339 340 /* 341 * return the <set,num> message in mc 342 * msg returned on error 343 * utf message text converted to ucs 344 */ 345 346 char* 347 mcget(register Mc_t* mc, int set, int num, const char* msg) 348 { 349 char* s; 350 size_t n; 351 int p; 352 353 if (!mc || set < 0 || set > mc->num || num < 1 || num > mc->set[set].num || !(s = mc->set[set].msg[num])) 354 return (char*)msg; 355 if (mc->cvt == (iconv_t)(-1)) 356 return s; 357 if ((p = sfstrtell(mc->tmp)) > sfstrsize(mc->tmp) / 2) 358 { 359 p = 0; 360 sfstrseek(mc->tmp, p, SEEK_SET); 361 } 362 n = strlen(s) + 1; 363 iconv_write(mc->cvt, mc->tmp, &s, &n, NiL); 364 return sfstrbase(mc->tmp) + p; 365 } 366 367 /* 368 * set message <set,num> to msg 369 * msg==0 deletes the message 370 * the message and set counts are adjusted 371 * 0 returned on success, -1 otherwise 372 */ 373 374 int 375 mcput(register Mc_t* mc, int set, int num, const char* msg) 376 { 377 register int i; 378 register char* s; 379 register Mcset_t* sp; 380 register char** mp; 381 382 /* 383 * validate the arguments 384 */ 385 386 if (!mc || set > MC_SET_MAX || num > MC_NUM_MAX) 387 return -1; 388 389 /* 390 * deletions don't kick in allocations (duh) 391 */ 392 393 if (!msg) 394 { 395 if (set <= mc->num && num <= mc->set[set].num && (s = mc->set[set].msg[num])) 396 { 397 /* 398 * decrease the string table size 399 */ 400 401 mc->set[set].msg[num] = 0; 402 mc->nstrs -= strlen(s) + 1; 403 if (mc->set[set].num == num) 404 { 405 /* 406 * decrease the max msg num 407 */ 408 409 mp = mc->set[set].msg + num; 410 while (num && !mp[--num]); 411 mc->nmsgs -= mc->set[set].num - num; 412 if (!(mc->set[set].num = num) && mc->num == set) 413 { 414 /* 415 * decrease the max set num 416 */ 417 418 while (num && !mc->set[--num].num); 419 mc->num = num; 420 } 421 } 422 } 423 return 0; 424 } 425 426 /* 427 * keep track of the highest set and allocate if necessary 428 */ 429 430 if (set > mc->num) 431 { 432 if (set > mc->gen) 433 { 434 i = MC_SET_MAX; 435 if (!(sp = vmnewof(mc->vm, 0, Mcset_t, i + 1, 0))) 436 return -1; 437 mc->gen = i; 438 for (i = 1; i <= mc->num; i++) 439 sp[i] = mc->set[i]; 440 mc->set = sp; 441 } 442 mc->num = set; 443 } 444 sp = mc->set + set; 445 446 /* 447 * keep track of the highest msg and allocate if necessary 448 */ 449 450 if (num > sp->num) 451 { 452 if (num > sp->gen) 453 { 454 if (!mc->gen) 455 { 456 i = (MC_NUM_MAX + 1) / 32; 457 if (i <= num) 458 i = 2 * num; 459 if (i > MC_NUM_MAX) 460 i = MC_NUM_MAX; 461 if (!(mp = vmnewof(mc->vm, 0, char*, i + 1, 0))) 462 return -1; 463 mc->gen = i; 464 sp->msg = mp; 465 for (i = 1; i <= sp->num; i++) 466 mp[i] = sp->msg[i]; 467 } 468 else 469 { 470 i = 2 * mc->gen; 471 if (i > MC_NUM_MAX) 472 i = MC_NUM_MAX; 473 if (!(mp = vmnewof(mc->vm, sp->msg, char*, i + 1, 0))) 474 return -1; 475 sp->gen = i; 476 sp->msg = mp; 477 } 478 } 479 mc->nmsgs += num - sp->num; 480 sp->num = num; 481 } 482 483 /* 484 * decrease the string table size 485 */ 486 487 if (s = sp->msg[num]) 488 { 489 /* 490 * no-op if no change 491 */ 492 493 if (streq(s, msg)) 494 return 0; 495 mc->nstrs -= strlen(s) + 1; 496 } 497 498 /* 499 * allocate, add and adjust the string table size 500 */ 501 502 if (!(s = vmstrdup(mc->vm, msg))) 503 return -1; 504 sp->msg[num] = s; 505 mc->nstrs += strlen(s) + 1; 506 return 0; 507 } 508 509 /* 510 * dump message catalog mc to op 511 * 0 returned on success, -1 otherwise 512 */ 513 514 int 515 mcdump(register Mc_t* mc, register Sfio_t* op) 516 { 517 register int i; 518 register int j; 519 register int n; 520 register char* s; 521 register Mcset_t* sp; 522 523 /* 524 * write the magic 525 */ 526 527 if (sfwrite(op, MC_MAGIC, MC_MAGIC_SIZE) != MC_MAGIC_SIZE) 528 return -1; 529 530 /* 531 * write the translation record 532 */ 533 534 sfputr(op, mc->translation, 0); 535 536 /* optional header records here */ 537 538 /* 539 * end of optional header records 540 */ 541 542 sfputu(op, 0); 543 544 /* 545 * write the global dimensions 546 */ 547 548 sfputu(op, mc->nstrs); 549 sfputu(op, mc->nmsgs); 550 sfputu(op, mc->num); 551 552 /* 553 * write the set dimensions 554 */ 555 556 for (i = 1; i <= mc->num; i++) 557 if (mc->set[i].num) 558 { 559 sfputu(op, i); 560 sfputu(op, mc->set[i].num); 561 } 562 sfputu(op, 0); 563 564 /* 565 * write the message sizes 566 */ 567 568 for (i = 1; i <= mc->num; i++) 569 if (mc->set[i].num) 570 { 571 sp = mc->set + i; 572 for (j = 1; j <= sp->num; j++) 573 { 574 n = (s = sp->msg[j]) ? (strlen(s) + 1) : 0; 575 sfputu(op, n); 576 } 577 } 578 579 /* 580 * write the string table 581 */ 582 583 for (i = 1; i <= mc->num; i++) 584 if (mc->set[i].num) 585 { 586 sp = mc->set + i; 587 for (j = 1; j <= sp->num; j++) 588 if (s = sp->msg[j]) 589 sfputr(op, s, 0); 590 } 591 592 /* 593 * sync and return 594 */ 595 596 return sfsync(op); 597 } 598 599 /* 600 * parse <set,msg> number from s 601 * e!=0 is set to the next char after the parse 602 * set!=0 is set to message set number 603 * msg!=0 is set to message number 604 * the message set number is returned 605 * 606 * the base 36 hash gives reasonable values for these: 607 * 608 * "ast" : ((((36#a^36#s^36#t)-9)&63)+1) = 3 609 * "gnu" : ((((36#g^36#n^36#u)-9)&63)+1) = 17 610 * "sgi" : ((((36#s^36#g^36#i)-9)&63)+1) = 22 611 * "sun" : ((((36#s^36#u^36#n)-9)&63)+1) = 13 612 */ 613 614 int 615 mcindex(register const char* s, char** e, int* set, int* msg) 616 { 617 register int c; 618 register int m; 619 register int n; 620 register int r; 621 register unsigned char* cv; 622 char* t; 623 624 m = 0; 625 n = strtol(s, &t, 0); 626 if (t == (char*)s) 627 { 628 SFCVINIT(); 629 cv = _Sfcv36; 630 for (n = m = 0; (c = cv[*s]) < 36; s++) 631 { 632 m++; 633 n ^= c; 634 } 635 m = (m <= 3) ? 63 : ((1 << (m + 3)) - 1); 636 n = ((n - 9) & m) + 1; 637 } 638 else 639 s = (const char*)t; 640 r = n; 641 if (*s) 642 m = strtol(s + 1, e, 0); 643 else 644 { 645 if (e) 646 *e = (char*)s; 647 if (m) 648 m = 0; 649 else 650 { 651 m = n; 652 n = 1; 653 } 654 } 655 if (set) 656 *set = n; 657 if (msg) 658 *msg = m; 659 return r; 660 } 661 662 /* 663 * close the message catalog mc 664 */ 665 666 int 667 mcclose(register Mc_t* mc) 668 { 669 if (!mc) 670 return -1; 671 if (mc->tmp) 672 sfclose(mc->tmp); 673 if (mc->cvt != (iconv_t)(-1)) 674 iconv_close(mc->cvt); 675 vmclose(mc->vm); 676 return 0; 677 } 678