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