1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 #pragma ident "%Z%%M% %I% %E% SMI" 28 29 #include "sun_msgfmt.h" 30 31 static void read_psffm(char *); 32 static void sortit(char *, char *); 33 static wchar_t *consume_whitespace(wchar_t *); 34 static char expand_meta(wchar_t **); 35 static struct domain_struct *find_domain_node(char *); 36 static void insert_message(struct domain_struct *, char *, char *); 37 static void output_all_mo_files(void); 38 static void output_one_mo_file(struct domain_struct *); 39 static size_t _mbsntowcs(wchar_t **, char **, size_t *); 40 41 #ifdef DEBUG 42 static void printlist(void); 43 #endif 44 45 static char gcurrent_domain[TEXTDOMAINMAX+1]; 46 static char *gmsgid; /* Stores msgid when read po file */ 47 static char *gmsgstr; /* Stores msgstr when read po file */ 48 static int gmsgid_size; /* The current size of msgid buffer */ 49 static int gmsgstr_size; /* The current size of msgstr buffer */ 50 static char *outfile = NULL; 51 static int linenum; /* The line number in the file */ 52 static int msgid_linenum; /* The last msgid token line number */ 53 static int msgstr_linenum; /* The last msgstr token line number */ 54 55 static int oflag = 0; 56 static int sun_p = 0; 57 int verbose = 0; 58 59 static struct domain_struct *first_domain = NULL; 60 static struct domain_struct *last_used_domain = NULL; 61 62 static int mbcurmax; 63 64 static char **oargv; 65 static char *inputdir; 66 67 extern void check_gnu(char *, size_t); 68 69 #define GNU_MSGFMT "/usr/lib/gmsgfmt" 70 void 71 invoke_gnu_msgfmt(void) 72 { 73 /* 74 * Transferring to /usr/lib/gmsgfmt 75 */ 76 char *gnu_msgfmt; 77 #ifdef DEBUG_MSGFMT 78 gnu_msgfmt = getenv("GNU_MSGFMT"); 79 if (!gnu_msgfmt) 80 gnu_msgfmt = GNU_MSGFMT; 81 #else 82 gnu_msgfmt = GNU_MSGFMT; 83 #endif 84 85 if (verbose) { 86 diag(gettext(DIAG_INVOKING_GNU)); 87 } 88 89 (void) execv(gnu_msgfmt, oargv); 90 /* exec failed */ 91 error(gettext(ERR_EXEC_FAILED), gnu_msgfmt); 92 /* NOTREACHED */ 93 } 94 95 static void 96 usage(void) 97 { 98 (void) fprintf(stderr, gettext(ERR_USAGE)); 99 exit(2); 100 } 101 102 /* 103 * msgfmt - Generate binary tree for runtime gettext() using psffm: "Portable 104 * Source File Format for Messages" file template. This file may have 105 * previously been generated by the xgettext filter for c source files. 106 */ 107 108 int 109 main(int argc, char **argv) 110 { 111 int ret; 112 static struct flags flag; 113 114 (void) setlocale(LC_ALL, ""); 115 #if !defined(TEXT_DOMAIN) 116 #define TEXT_DOMAIN "SYS_TEST" 117 #endif 118 (void) textdomain(TEXT_DOMAIN); 119 120 oargv = argv; 121 ret = parse_option(&argc, &argv, &flag); 122 if (ret == -1) { 123 usage(); 124 /* NOTREACHED */ 125 } 126 127 if (flag.sun_p) { 128 /* never invoke gnu msgfmt */ 129 if (flag.gnu_p) { 130 error(gettext(ERR_GNU_ON_SUN)); 131 /* NOTREACHED */ 132 } 133 sun_p = flag.sun_p; 134 } 135 if (flag.idir) { 136 inputdir = flag.idir; 137 } 138 if (flag.ofile) { 139 oflag = 1; 140 outfile = flag.ofile; 141 } 142 if (flag.verbose) { 143 verbose = 1; 144 } 145 146 if (flag.gnu_p) { 147 /* invoke /usr/lib/gmsgfmt */ 148 invoke_gnu_msgfmt(); 149 /* NOTREACHED */ 150 } 151 152 /* 153 * read all portable object files specified in command arguments. 154 * Allocate initial size for msgid and msgstr. If it needs more 155 * spaces, realloc later. 156 */ 157 gmsgid = (char *)Xmalloc(MAX_VALUE_LEN); 158 gmsgstr = (char *)Xmalloc(MAX_VALUE_LEN); 159 160 gmsgid_size = gmsgstr_size = MAX_VALUE_LEN; 161 (void) memset(gmsgid, 0, gmsgid_size); 162 (void) memset(gmsgstr, 0, gmsgstr_size); 163 164 mbcurmax = MB_CUR_MAX; 165 166 while (argc-- > 0) { 167 if (verbose) { 168 diag(gettext(DIAG_START_PROC), *argv); 169 } 170 read_psffm(*argv++); 171 } 172 173 output_all_mo_files(); 174 175 #ifdef DEBUG 176 printlist(); 177 #endif 178 179 return (0); 180 181 } /* main */ 182 183 184 185 /* 186 * read_psffm - read in "psffm" format file, check syntax, printing error 187 * messages as needed, output binary tree to file <domain> 188 */ 189 190 static void 191 read_psffm(char *file) 192 { 193 int fd; 194 static char msgfile[MAXPATHLEN]; 195 wchar_t *linebufptr, *p; 196 char *bufptr = 0; 197 int quotefound; /* double quote was seen */ 198 int inmsgid = 0; /* indicates "msgid" was seen */ 199 int inmsgstr = 0; /* indicates "msgstr" was seen */ 200 int indomain = 0; /* indicates "domain" was seen */ 201 wchar_t wc; 202 char mb; 203 int n; 204 char token_found; /* Boolean value */ 205 unsigned int bufptr_index = 0; /* current index of bufptr */ 206 char *mbuf, *addr; 207 size_t fsize, ln_size, ll; 208 wchar_t *linebufhead = NULL; 209 struct stat64 statbuf; 210 char *filename; 211 212 /* 213 * For each po file to be read, 214 * 1) set domain to default and 215 * 2) set linenumer to 0. 216 */ 217 (void) strcpy(gcurrent_domain, DEFAULT_DOMAIN); 218 linenum = 0; 219 220 if (!inputdir) { 221 filename = Xstrdup(file); 222 } else { 223 size_t dirlen, filelen, len; 224 225 dirlen = strlen(inputdir); 226 filelen = strlen(file); 227 len = dirlen + 1 + filelen + 1; 228 filename = (char *)Xmalloc(len); 229 (void) memcpy(filename, inputdir, dirlen); 230 *(filename + dirlen) = '/'; 231 (void) memcpy(filename + dirlen + 1, file, filelen); 232 *(filename + dirlen + 1 + filelen) = '\0'; 233 } 234 235 fd = open(filename, O_RDONLY); 236 if (fd == -1) { 237 error(gettext(ERR_OPEN_FAILED), filename); 238 /* NOTREACHED */ 239 } 240 if (fstat64(fd, &statbuf) == -1) { 241 error(gettext(ERR_STAT_FAILED), filename); 242 /* NOTREACHED */ 243 } 244 fsize = (size_t)statbuf.st_size; 245 if (fsize == 0) { 246 /* 247 * The size of the specified po file is 0. 248 * In Solaris 8 and earlier, msgfmt was silent 249 * for the null po file. So, just returns 250 * without generating an error message. 251 */ 252 (void) close(fd); 253 free(filename); 254 return; 255 } 256 addr = mmap(NULL, fsize, PROT_READ, MAP_SHARED, fd, 0); 257 if (addr == MAP_FAILED) { 258 error(gettext(ERR_MMAP_FAILED), filename); 259 /* NOTREACHED */ 260 } 261 (void) close(fd); 262 263 if (!sun_p) 264 check_gnu(addr, fsize); 265 266 mbuf = addr; 267 for (;;) { 268 if (linebufhead) { 269 free(linebufhead); 270 linebufhead = NULL; 271 } 272 ln_size = _mbsntowcs(&linebufhead, &mbuf, &fsize); 273 if (ln_size == (size_t)-1) { 274 error(gettext(ERR_READ_FAILED), filename); 275 /* NOTREACHED */ 276 } else if (ln_size == 0) { 277 break; /* End of File. */ 278 } 279 linenum++; 280 281 linebufptr = linebufhead; 282 quotefound = 0; 283 284 switch (*linebufptr) { 285 case L'#': /* comment */ 286 case L'\n': /* empty line */ 287 continue; 288 case L'\"': /* multiple lines of msgid and msgstr */ 289 quotefound = 1; 290 break; 291 } 292 293 /* 294 * Process MSGID Tokens. 295 */ 296 token_found = (wcsncmp(MSGID_TOKEN, linebufptr, 297 MSGID_LEN) == 0) ? 1 : 0; 298 299 if (token_found || (quotefound && inmsgid)) { 300 301 if (token_found) { 302 if (!CK_NXT_CH(linebufptr, MSGID_LEN+1)) { 303 diag(gettext(ERR_NOSPC), linenum); 304 error(gettext(ERR_EXITING)); 305 /* NOTREACHED */ 306 } 307 } 308 309 if (inmsgid && !quotefound) { 310 warning(gettext(WARN_NO_MSGSTR), msgid_linenum); 311 continue; 312 } 313 if (inmsgstr) { 314 sortit(gmsgid, gmsgstr); 315 (void) memset(gmsgid, 0, gmsgid_size); 316 (void) memset(gmsgstr, 0, gmsgstr_size); 317 } 318 319 if (inmsgid) { 320 /* multiple lines of msgid */ 321 /* cancel the previous null termination */ 322 bufptr_index--; 323 } else { 324 /* 325 * The first line of msgid. 326 * Save linenum of msgid to be used when 327 * printing warning or error message. 328 */ 329 msgid_linenum = linenum; 330 p = linebufptr; 331 linebufptr = consume_whitespace( 332 linebufptr + MSGID_LEN); 333 ln_size -= linebufptr - p; 334 bufptr = gmsgid; 335 bufptr_index = 0; 336 } 337 338 inmsgid = 1; 339 inmsgstr = 0; 340 indomain = 0; 341 goto load_buffer; 342 } 343 344 /* 345 * Process MSGSTR Tokens. 346 */ 347 token_found = (wcsncmp(MSGSTR_TOKEN, linebufptr, 348 MSGSTR_LEN) == 0) ? 1 : 0; 349 if (token_found || (quotefound && inmsgstr)) { 350 351 if (token_found) { 352 if (!CK_NXT_CH(linebufptr, MSGSTR_LEN+1)) { 353 diag(gettext(ERR_NOSPC), linenum); 354 error(gettext(ERR_EXITING)); 355 /* NOTREACHED */ 356 } 357 } 358 359 360 if (inmsgstr && !quotefound) { 361 warning(gettext(WARN_NO_MSGID), msgstr_linenum); 362 continue; 363 } 364 if (inmsgstr) { 365 /* multiple lines of msgstr */ 366 /* cancel the previous null termination */ 367 bufptr_index--; 368 } else { 369 /* 370 * The first line of msgstr. 371 * Save linenum of msgid to be used when 372 * printing warning or error message. 373 */ 374 msgstr_linenum = linenum; 375 p = linebufptr; 376 linebufptr = consume_whitespace( 377 linebufptr + MSGSTR_LEN); 378 ln_size -= linebufptr - p; 379 bufptr = gmsgstr; 380 bufptr_index = 0; 381 } 382 383 inmsgstr = 1; 384 inmsgid = 0; 385 indomain = 0; 386 goto load_buffer; 387 } 388 389 /* 390 * Process DOMAIN Tokens. 391 * Add message id and message string to sorted list 392 * if msgstr was processed last time. 393 */ 394 token_found = (wcsncmp(DOMAIN_TOKEN, linebufptr, 395 DOMAIN_LEN) == 0) ? 1 : 0; 396 if ((token_found) || (quotefound && indomain)) { 397 if (token_found) { 398 if (!CK_NXT_CH(linebufptr, DOMAIN_LEN+1)) { 399 diag(gettext(ERR_NOSPC), linenum); 400 error(gettext(ERR_EXITING)); 401 /* NOTREACHED */ 402 } 403 } 404 405 406 /* 407 * process msgid and msgstr pair for previous domain 408 */ 409 if (inmsgstr) { 410 sortit(gmsgid, gmsgstr); 411 } 412 413 /* refresh msgid and msgstr buffer */ 414 if (inmsgstr || inmsgid) { 415 (void) memset(gmsgid, 0, gmsgid_size); 416 (void) memset(gmsgstr, 0, gmsgstr_size); 417 } 418 419 if (indomain) { 420 /* multiple lines of domain */ 421 /* cancel the previous null termination */ 422 bufptr_index--; 423 } else { 424 p = linebufptr; 425 linebufptr = consume_whitespace( 426 linebufptr + DOMAIN_LEN); 427 (void) memset(gcurrent_domain, 0, 428 sizeof (gcurrent_domain)); 429 ln_size -= linebufptr - p; 430 bufptr = gcurrent_domain; 431 bufptr_index = 0; 432 } 433 434 indomain = 1; 435 inmsgid = 0; 436 inmsgstr = 0; 437 } /* if */ 438 439 load_buffer: 440 /* 441 * Now, fill up the buffer pointed by bufptr. 442 * At this point bufptr should point to one of 443 * msgid, msgptr, or current_domain. 444 * Otherwise, the entire line is ignored. 445 */ 446 447 if (!bufptr) { 448 warning(gettext(WARN_SYNTAX_ERR), linenum); 449 continue; 450 } 451 452 if (*linebufptr++ != L'\"') { 453 warning(gettext(WARN_MISSING_QUOTE), linenum); 454 --linebufptr; 455 } 456 quotefound = 0; 457 458 /* 459 * If there is not enough space in the buffer, 460 * increase buffer by ln_size by realloc. 461 */ 462 ll = ln_size * mbcurmax; 463 if (bufptr == gmsgid) { 464 if (gmsgid_size < (bufptr_index + ll)) { 465 gmsgid = (char *)Xrealloc(gmsgid, 466 bufptr_index + ll); 467 bufptr = gmsgid; 468 gmsgid_size = bufptr_index + ll; 469 } 470 } else if (bufptr == gmsgstr) { 471 if (gmsgstr_size < (bufptr_index + ll)) { 472 gmsgstr = (char *)Xrealloc(gmsgstr, 473 bufptr_index + ll); 474 bufptr = gmsgstr; 475 gmsgstr_size = bufptr_index + ll; 476 } 477 } 478 479 while (wc = *linebufptr++) { 480 switch (wc) { 481 case L'\n': 482 if (!quotefound) { 483 warning(gettext(WARN_MISSING_QUOTE_AT_EOL), linenum); 484 } 485 break; 486 487 case L'\"': 488 quotefound = 1; 489 break; 490 491 case L'\\': 492 if ((mb = expand_meta(&linebufptr)) != NULL) 493 bufptr[bufptr_index++] = mb; 494 break; 495 496 default: 497 if ((n = wctomb(&bufptr[bufptr_index], wc)) > 0) 498 bufptr_index += n; 499 } /* switch */ 500 if (quotefound) { 501 /* 502 * Check if any remaining characters 503 * after closing quote. 504 */ 505 linebufptr = consume_whitespace(linebufptr); 506 if (*linebufptr != L'\n') { 507 warning(gettext(WARN_INVALID_STRING), 508 linenum); 509 } 510 break; 511 } 512 } /* while */ 513 514 bufptr[bufptr_index++] = '\0'; 515 516 (void) strcpy(msgfile, gcurrent_domain); 517 (void) strcat(msgfile, ".mo"); 518 } /* for(;;) */ 519 520 if (inmsgstr) { 521 sortit(gmsgid, gmsgstr); 522 } 523 524 if (linebufhead) 525 free(linebufhead); 526 if (munmap(addr, statbuf.st_size) == -1) { 527 error(gettext(ERR_MUNMAP_FAILED), filename); 528 /* NOTREACHED */ 529 } 530 531 free(filename); 532 return; 533 534 } /* read_psffm */ 535 536 537 /* 538 * Skip leading white spaces and tabs. 539 */ 540 static wchar_t * 541 consume_whitespace(wchar_t *buf) 542 { 543 wchar_t *bufptr = buf; 544 wchar_t c; 545 546 /* 547 * Skip leading white spaces. 548 */ 549 while ((c = *bufptr) != L'\0') { 550 if (c == L' ' || c == L'\t') { 551 bufptr++; 552 continue; 553 } 554 break; 555 } 556 return (bufptr); 557 } /* consume_white_space */ 558 559 560 /* 561 * handle escape sequences. 562 */ 563 static char 564 expand_meta(wchar_t **buf) 565 { 566 wchar_t wc = **buf; 567 char n; 568 569 switch (wc) { 570 case L'"': 571 (*buf)++; 572 return ('\"'); 573 case L'\\': 574 (*buf)++; 575 return ('\\'); 576 case L'b': 577 (*buf)++; 578 return ('\b'); 579 case L'f': 580 (*buf)++; 581 return ('\f'); 582 case L'n': 583 (*buf)++; 584 return ('\n'); 585 case L'r': 586 (*buf)++; 587 return ('\r'); 588 case L't': 589 (*buf)++; 590 return ('\t'); 591 case L'v': 592 (*buf)++; 593 return ('\v'); 594 case L'a': 595 (*buf)++; 596 return ('\a'); 597 case L'\'': 598 (*buf)++; 599 return ('\''); 600 case L'?': 601 (*buf)++; 602 return ('\?'); 603 case L'0': 604 case L'1': 605 case L'2': 606 case L'3': 607 case L'4': 608 case L'5': 609 case L'6': 610 case L'7': 611 /* 612 * This case handles \ddd where ddd is octal number. 613 * There could be one, two, or three octal numbers. 614 */ 615 (*buf)++; 616 n = (char)(wc - L'0'); 617 wc = **buf; 618 if (wc >= L'0' && wc <= L'7') { 619 (*buf)++; 620 n = 8*n + (char)(wc - L'0'); 621 wc = **buf; 622 if (wc >= L'0' && wc <= L'7') { 623 (*buf)++; 624 n = 8*n + (char)(wc - L'0'); 625 } 626 } 627 return (n); 628 default: 629 return (NULL); 630 } 631 } /* expand_meta */ 632 633 /* 634 * Finds the head of the current domain linked list and 635 * call insert_message() to insert msgid and msgstr pair 636 * to the linked list. 637 */ 638 static void 639 sortit(char *msgid, char *msgstr) 640 { 641 struct domain_struct *dom; 642 643 #ifdef DEBUG 644 (void) fprintf(stderr, 645 "==> sortit(), domain=<%s> msgid=<%s> msgstr=<%s>\n", 646 gcurrent_domain, msgid, msgstr); 647 #endif 648 649 /* 650 * If "-o filename" is specified, then all "domain" directive 651 * are ignored and, all messages will be stored in domain 652 * whose name is filename. 653 */ 654 if (oflag) { 655 dom = find_domain_node(outfile); 656 } else { 657 dom = find_domain_node(gcurrent_domain); 658 } 659 660 insert_message(dom, msgid, msgstr); 661 } 662 663 /* 664 * This routine inserts message in the current domain message list. 665 * It is inserted in ascending order. 666 */ 667 static void 668 insert_message(struct domain_struct *dom, 669 char *msgid, char *msgstr) 670 { 671 struct msg_chain *p1; 672 struct msg_chain *node, *prev_node; 673 int b; 674 675 /* 676 * Find the optimal starting search position. 677 * The starting search position is either the first node 678 * or the current_elem of domain. 679 * The current_elem is the pointer to the node which 680 * is most recently accessed in domain. 681 */ 682 if (dom->current_elem != NULL) { 683 b = strcmp(msgid, dom->current_elem->msgid); 684 if (b == 0) { 685 if (verbose) 686 warning(gettext(WARN_DUP_MSG), 687 msgid, msgid_linenum); 688 return; 689 } else if (b > 0) { /* to implement descending order */ 690 p1 = dom->first_elem; 691 } else { 692 p1 = dom->current_elem; 693 } 694 } else { 695 p1 = dom->first_elem; 696 } 697 698 /* 699 * search msgid insert position in the list 700 * Search starts from the node pointed by p1. 701 */ 702 prev_node = NULL; 703 while (p1) { 704 b = strcmp(msgid, p1->msgid); 705 if (b == 0) { 706 if (verbose) 707 warning(gettext(WARN_DUP_MSG), 708 msgid, msgid_linenum); 709 return; 710 } else if (b < 0) { /* to implement descending order */ 711 /* move to the next node */ 712 prev_node = p1; 713 p1 = p1->next; 714 } else { 715 /* insert a new msg node */ 716 node = (struct msg_chain *) 717 Xmalloc(sizeof (struct msg_chain)); 718 node->next = p1; 719 node->msgid = Xstrdup(msgid); 720 node->msgstr = Xstrdup(msgstr); 721 722 if (prev_node) { 723 prev_node->next = node; 724 } else { 725 dom->first_elem = node; 726 } 727 dom->current_elem = node; 728 return; 729 } 730 } /* while */ 731 732 /* 733 * msgid is smaller than any of msgid in the list or 734 * list is empty. 735 * Therefore, append it. 736 */ 737 node = (struct msg_chain *) 738 Xmalloc(sizeof (struct msg_chain)); 739 node->next = NULL; 740 node->msgid = Xstrdup(msgid); 741 node->msgstr = Xstrdup(msgstr); 742 743 if (prev_node) { 744 prev_node->next = node; 745 } else { 746 dom->first_elem = node; 747 } 748 dom->current_elem = node; 749 750 return; 751 752 } /* insert_message */ 753 754 755 /* 756 * This routine will find head of the linked list for the given 757 * domain_name. This looks up cache entry first and if cache misses, 758 * scans the list. 759 * If not found, then create a new node. 760 */ 761 static struct domain_struct * 762 find_domain_node(char *domain_name) 763 { 764 struct domain_struct *p1; 765 struct domain_struct *node; 766 struct domain_struct *prev_node; 767 int b; 768 769 770 /* for perfomance, check cache 'last_used_domain' */ 771 if (last_used_domain) { 772 b = strcmp(domain_name, last_used_domain->domain); 773 if (b == 0) { 774 return (last_used_domain); 775 } else if (b < 0) { 776 p1 = first_domain; 777 } else { 778 p1 = last_used_domain; 779 } 780 } else { 781 p1 = first_domain; 782 } 783 784 prev_node = NULL; 785 while (p1) { 786 b = strcmp(domain_name, p1->domain); 787 if (b == 0) { 788 /* node found */ 789 last_used_domain = p1; 790 return (p1); 791 } else if (b > 0) { 792 /* move to the next node */ 793 prev_node = p1; 794 p1 = p1->next; 795 } else { 796 /* insert a new domain node */ 797 node = (struct domain_struct *) 798 Xmalloc(sizeof (struct domain_struct)); 799 node->next = p1; 800 node->domain = Xstrdup(domain_name); 801 node->first_elem = NULL; 802 node->current_elem = NULL; 803 if (prev_node) { 804 /* insert the node in the middle */ 805 prev_node->next = node; 806 } else { 807 /* node inserted is the smallest */ 808 first_domain = node; 809 } 810 last_used_domain = node; 811 return (node); 812 } 813 } /* while */ 814 815 /* 816 * domain_name is larger than any of domain name in the list or 817 * list is empty. 818 */ 819 node = (struct domain_struct *) 820 Xmalloc(sizeof (struct domain_struct)); 821 node->next = NULL; 822 node->domain = Xstrdup(domain_name); 823 node->first_elem = NULL; 824 node->current_elem = NULL; 825 if (prev_node) { 826 /* domain list is not empty */ 827 prev_node->next = node; 828 } else { 829 /* domain list is empty */ 830 first_domain = node; 831 } 832 last_used_domain = node; 833 834 return (node); 835 836 } /* find_domain_node */ 837 838 839 /* 840 * binary_compute() is used for pre-computing a binary search. 841 */ 842 static int 843 binary_compute(int i, int j, int *more, int *less) 844 { 845 int k; 846 847 if (i > j) { 848 return (LEAFINDICATOR); 849 } 850 k = (i + j) / 2; 851 852 less[k] = binary_compute(i, k - 1, more, less); 853 more[k] = binary_compute(k + 1, j, more, less); 854 855 return (k); 856 857 } /* binary_compute */ 858 859 860 /* 861 * Write all domain data to file. 862 * Each domain will create one file. 863 */ 864 static void 865 output_all_mo_files(void) 866 { 867 struct domain_struct *p; 868 869 p = first_domain; 870 while (p) { 871 /* 872 * generate message object file only if there is 873 * at least one element. 874 */ 875 if (p->first_elem) { 876 output_one_mo_file(p); 877 } 878 p = p->next; 879 } 880 return; 881 882 } /* output_all_mo_files */ 883 884 885 /* 886 * Write one domain data list to file. 887 */ 888 static void 889 output_one_mo_file(struct domain_struct *dom) 890 { 891 FILE *fp; 892 struct msg_chain *p; 893 int message_count; 894 int string_count_msgid; 895 int string_count_msg; 896 int msgid_index = 0; 897 int msgstr_index = 0; 898 int *less, *more; 899 int i; 900 char fname [TEXTDOMAINMAX+1]; 901 902 if (!dom || !dom->first_elem) 903 return; 904 905 /* 906 * If -o flag is specified, then file name is used as domain name. 907 * If not, ".mo" is appended to the domain name. 908 */ 909 (void) strcpy(fname, dom->domain); 910 if (!oflag) { 911 (void) strcat(fname, ".mo"); 912 } 913 fp = fopen(fname, "w"); 914 if (fp == NULL) { 915 error(gettext(ERR_OPEN_FAILED), fname); 916 /* NOTREACHED */ 917 } 918 919 /* compute offsets and counts */ 920 message_count = 0; 921 p = dom->first_elem; 922 while (p) { 923 p->msgid_offset = msgid_index; 924 p->msgstr_offset = msgstr_index; 925 msgid_index += strlen(p->msgid) + 1; 926 msgstr_index += strlen(p->msgstr) + 1; 927 message_count++; 928 p = p->next; 929 } 930 931 /* 932 * Fill up less and more entries to be used for binary search. 933 */ 934 string_count_msgid = msgid_index; 935 string_count_msg = msgstr_index; 936 less = (int *)Xcalloc(message_count, sizeof (int)); 937 more = (int *)Xcalloc(message_count, sizeof (int)); 938 939 (void) binary_compute(0, message_count - 1, more, less); 940 941 #ifdef DEBUG 942 { 943 int i; 944 for (i = 0; i < message_count; i++) { 945 (void) fprintf(stderr, 946 " less[%2d]=%2d, more[%2d]=%2d\n", 947 i, less[i], i, more[i]); 948 } 949 } 950 #endif 951 952 /* 953 * write out the message object file. 954 * The middle one is the first message to check by gettext(). 955 */ 956 i = (message_count - 1) / 2; 957 (void) fwrite(&i, sizeof (int), 1, fp); 958 (void) fwrite(&message_count, sizeof (int), 1, fp); 959 (void) fwrite(&string_count_msgid, sizeof (int), 1, fp); 960 (void) fwrite(&string_count_msg, sizeof (int), 1, fp); 961 i = MSG_STRUCT_SIZE * message_count; 962 (void) fwrite(&i, sizeof (int), 1, fp); 963 964 /* march through linked list and write out all nodes. */ 965 i = 0; 966 p = dom->first_elem; 967 while (p) { /* put out message struct */ 968 (void) fwrite(&less[i], sizeof (int), 1, fp); 969 (void) fwrite(&more[i], sizeof (int), 1, fp); 970 (void) fwrite(&p->msgid_offset, sizeof (int), 1, fp); 971 (void) fwrite(&p->msgstr_offset, sizeof (int), 1, fp); 972 i++; 973 p = p->next; 974 } 975 976 /* put out message id strings */ 977 p = dom->first_elem; 978 while (p) { 979 (void) fwrite(p->msgid, strlen(p->msgid)+1, 1, fp); 980 p = p->next; 981 } 982 983 /* put out message strings */ 984 p = dom->first_elem; 985 while (p) { 986 (void) fwrite(p->msgstr, strlen(p->msgstr)+1, 1, fp); 987 p = p->next; 988 } 989 990 (void) fclose(fp); 991 free(less); 992 free(more); 993 994 return; 995 996 } /* output_one_mo_file */ 997 998 999 /* 1000 * read one line from *mbuf, 1001 * skip preceding whitespaces, 1002 * convert the line to wide characters, 1003 * place the wide characters into *bufhead, and 1004 * return the number of wide characters placed. 1005 * 1006 * INPUT: 1007 * **bufhead - address of a variable that is the pointer 1008 * to wchar_t. 1009 * The variable should been initialized to NULL. 1010 * **mbuf - address of a variable that is the pointer 1011 * to char. 1012 * The pointer should point to the memory mmapped to 1013 * the file to input. 1014 * **fsize - address of a size_t variable that contains 1015 * the size of unread bytes in the file to input. 1016 * OUTPUT: 1017 * return - the number of wide characters placed. 1018 * **bufhead - _mbsntowcs allocates the buffer to store 1019 * one line in wchar_t from *mbuf and sets the address 1020 * to *bufhead. 1021 * **mbuf - _mbsntowcs reads one line from *mbuf and sets *mbuf 1022 * to the beginning of the next line. 1023 * **fsize - *fsize will be set to the size of the unread 1024 * bytes in the file. 1025 */ 1026 static size_t 1027 _mbsntowcs(wchar_t **bufhead, char **mbuf, size_t *fsize) 1028 { 1029 wchar_t *tp, *th; 1030 wchar_t wc; 1031 size_t tbufsize = LINE_SIZE; 1032 size_t ttbufsize, nc; 1033 char *pc = *mbuf; 1034 int nb; 1035 1036 if (*fsize == 0) { 1037 /* eof */ 1038 return (0); 1039 } 1040 1041 th = (wchar_t *)Xmalloc(sizeof (wchar_t) * tbufsize); 1042 nc = tbufsize; 1043 1044 /* skip preceding whitespaces */ 1045 while ((*pc != '\0')) { 1046 if ((*pc == ' ') || (*pc == '\t')) { 1047 pc++; 1048 (*fsize)--; 1049 } else { 1050 break; 1051 } 1052 } 1053 1054 tp = th; 1055 while (*fsize > 0) { 1056 nb = mbtowc(&wc, pc, mbcurmax); 1057 if (nb == -1) { 1058 return ((size_t)-1); 1059 } 1060 1061 if (*pc == '\n') { 1062 /* found eol */ 1063 if (nc <= 1) { 1064 /* 1065 * not enough buffer 1066 * at least 2 more bytes are required for 1067 * L'\n' and L'\0' 1068 */ 1069 ttbufsize = tbufsize + 2; 1070 th = (wchar_t *)Xrealloc(th, 1071 sizeof (wchar_t) * ttbufsize); 1072 tp = th + tbufsize - nc; 1073 tbufsize = ttbufsize; 1074 } 1075 *tp++ = L'\n'; 1076 *tp++ = L'\0'; 1077 pc += nb; 1078 *fsize -= nb; 1079 *mbuf = pc; 1080 *bufhead = th; 1081 return ((size_t)(tp - th)); 1082 } 1083 if (nc == 0) { 1084 ttbufsize = tbufsize + LINE_SIZE; 1085 th = (wchar_t *)Xrealloc(th, 1086 sizeof (wchar_t) * ttbufsize); 1087 tp = th + tbufsize; 1088 nc = LINE_SIZE; 1089 tbufsize = ttbufsize; 1090 } 1091 *tp++ = wc; 1092 nc--; 1093 pc += nb; 1094 *fsize -= nb; 1095 } /* while */ 1096 1097 /* 1098 * At this point, the input file has been consumed, 1099 * but there is no ending '\n'; we add it to 1100 * the output file. 1101 */ 1102 if (nc <= 1) { 1103 /* 1104 * not enough buffer 1105 * at least 2 more bytes are required for 1106 * L'\n' and L'\0' 1107 */ 1108 ttbufsize = tbufsize + 2; 1109 th = (wchar_t *)Xrealloc(th, 1110 sizeof (wchar_t) * ttbufsize); 1111 tp = th + tbufsize - nc; 1112 tbufsize = ttbufsize; 1113 } 1114 *tp++ = L'\n'; 1115 *tp++ = L'\0'; 1116 *mbuf = pc; 1117 *bufhead = th; 1118 return ((size_t)(tp - th)); 1119 } 1120 1121 1122 /* 1123 * This is debug function. Not compiled in the final executable. 1124 */ 1125 #ifdef DEBUG 1126 static void 1127 printlist(void) 1128 { 1129 struct domain_struct *p; 1130 struct msg_chain *m; 1131 1132 (void) fprintf(stderr, "\n=== Printing contents of all domains ===\n"); 1133 p = first_domain; 1134 while (p) { 1135 (void) fprintf(stderr, "domain name = <%s>\n", p->domain); 1136 m = p->first_elem; 1137 while (m) { 1138 (void) fprintf(stderr, " msgid=<%s>, msgstr=<%s>\n", 1139 m->msgid, m->msgstr); 1140 m = m->next; 1141 } 1142 p = p->next; 1143 } 1144 } /* printlist */ 1145 #endif 1146