dmake: do not set MAKEFLAGS=k
[unleashed/tickless.git] / usr / src / cmd / msgfmt / msgfmt.c
blob28d3fac2e19cae36f28ba9a3cab97590f155c604
1 /*
2 * CDDL HEADER START
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.
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.
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]
20 * CDDL HEADER END
23 * Copyright 2004 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
27 #include "sun_msgfmt.h"
29 static void read_psffm(char *);
30 static void sortit(char *, char *);
31 static wchar_t *consume_whitespace(wchar_t *);
32 static char expand_meta(wchar_t **);
33 static struct domain_struct *find_domain_node(char *);
34 static void insert_message(struct domain_struct *, char *, char *);
35 static void output_all_mo_files(void);
36 static void output_one_mo_file(struct domain_struct *);
37 static size_t _mbsntowcs(wchar_t **, char **, size_t *);
39 #ifdef DEBUG
40 static void printlist(void);
41 #endif
43 static char gcurrent_domain[TEXTDOMAINMAX+1];
44 static char *gmsgid; /* Stores msgid when read po file */
45 static char *gmsgstr; /* Stores msgstr when read po file */
46 static int gmsgid_size; /* The current size of msgid buffer */
47 static int gmsgstr_size; /* The current size of msgstr buffer */
48 static char *outfile = NULL;
49 static int linenum; /* The line number in the file */
50 static int msgid_linenum; /* The last msgid token line number */
51 static int msgstr_linenum; /* The last msgstr token line number */
53 static int oflag = 0;
54 static int sun_p = 0;
55 int verbose = 0;
57 static struct domain_struct *first_domain = NULL;
58 static struct domain_struct *last_used_domain = NULL;
60 static int mbcurmax;
62 static char **oargv;
63 static char *inputdir;
65 extern void check_gnu(char *, size_t);
67 #define GNU_MSGFMT "/usr/lib/gmsgfmt"
68 void
69 invoke_gnu_msgfmt(void)
72 * Transferring to /usr/lib/gmsgfmt
74 char *gnu_msgfmt;
75 #ifdef DEBUG_MSGFMT
76 gnu_msgfmt = getenv("GNU_MSGFMT");
77 if (!gnu_msgfmt)
78 gnu_msgfmt = GNU_MSGFMT;
79 #else
80 gnu_msgfmt = GNU_MSGFMT;
81 #endif
83 if (verbose) {
84 diag(gettext(DIAG_INVOKING_GNU));
87 (void) execv(gnu_msgfmt, oargv);
88 /* exec failed */
89 error(gettext(ERR_EXEC_FAILED), gnu_msgfmt);
90 /* NOTREACHED */
93 static void
94 usage(void)
96 (void) fprintf(stderr, gettext(ERR_USAGE));
97 exit(2);
101 * msgfmt - Generate binary tree for runtime gettext() using psffm: "Portable
102 * Source File Format for Messages" file template. This file may have
103 * previously been generated by the xgettext filter for c source files.
107 main(int argc, char **argv)
109 int ret;
110 static struct flags flag;
112 (void) setlocale(LC_ALL, "");
113 #if !defined(TEXT_DOMAIN)
114 #define TEXT_DOMAIN "SYS_TEST"
115 #endif
116 (void) textdomain(TEXT_DOMAIN);
118 oargv = argv;
119 ret = parse_option(&argc, &argv, &flag);
120 if (ret == -1) {
121 usage();
122 /* NOTREACHED */
125 if (flag.sun_p) {
126 /* never invoke gnu msgfmt */
127 if (flag.gnu_p) {
128 error(gettext(ERR_GNU_ON_SUN));
129 /* NOTREACHED */
131 sun_p = flag.sun_p;
133 if (flag.idir) {
134 inputdir = flag.idir;
136 if (flag.ofile) {
137 oflag = 1;
138 outfile = flag.ofile;
140 if (flag.verbose) {
141 verbose = 1;
144 if (flag.gnu_p) {
145 /* invoke /usr/lib/gmsgfmt */
146 invoke_gnu_msgfmt();
147 /* NOTREACHED */
151 * read all portable object files specified in command arguments.
152 * Allocate initial size for msgid and msgstr. If it needs more
153 * spaces, realloc later.
155 gmsgid = (char *)Xmalloc(MAX_VALUE_LEN);
156 gmsgstr = (char *)Xmalloc(MAX_VALUE_LEN);
158 gmsgid_size = gmsgstr_size = MAX_VALUE_LEN;
159 (void) memset(gmsgid, 0, gmsgid_size);
160 (void) memset(gmsgstr, 0, gmsgstr_size);
162 mbcurmax = MB_CUR_MAX;
164 while (argc-- > 0) {
165 if (verbose) {
166 diag(gettext(DIAG_START_PROC), *argv);
168 read_psffm(*argv++);
171 output_all_mo_files();
173 #ifdef DEBUG
174 printlist();
175 #endif
177 return (0);
179 } /* main */
184 * read_psffm - read in "psffm" format file, check syntax, printing error
185 * messages as needed, output binary tree to file <domain>
188 static void
189 read_psffm(char *file)
191 int fd;
192 static char msgfile[MAXPATHLEN];
193 wchar_t *linebufptr, *p;
194 char *bufptr = 0;
195 int quotefound; /* double quote was seen */
196 int inmsgid = 0; /* indicates "msgid" was seen */
197 int inmsgstr = 0; /* indicates "msgstr" was seen */
198 int indomain = 0; /* indicates "domain" was seen */
199 wchar_t wc;
200 char mb;
201 int n;
202 char token_found; /* Boolean value */
203 unsigned int bufptr_index = 0; /* current index of bufptr */
204 char *mbuf, *addr;
205 size_t fsize, ln_size, ll;
206 wchar_t *linebufhead = NULL;
207 struct stat64 statbuf;
208 char *filename;
211 * For each po file to be read,
212 * 1) set domain to default and
213 * 2) set linenumer to 0.
215 (void) strcpy(gcurrent_domain, DEFAULT_DOMAIN);
216 linenum = 0;
218 if (!inputdir) {
219 filename = Xstrdup(file);
220 } else {
221 size_t dirlen, filelen, len;
223 dirlen = strlen(inputdir);
224 filelen = strlen(file);
225 len = dirlen + 1 + filelen + 1;
226 filename = (char *)Xmalloc(len);
227 (void) memcpy(filename, inputdir, dirlen);
228 *(filename + dirlen) = '/';
229 (void) memcpy(filename + dirlen + 1, file, filelen);
230 *(filename + dirlen + 1 + filelen) = '\0';
233 fd = open(filename, O_RDONLY);
234 if (fd == -1) {
235 error(gettext(ERR_OPEN_FAILED), filename);
236 /* NOTREACHED */
238 if (fstat64(fd, &statbuf) == -1) {
239 error(gettext(ERR_STAT_FAILED), filename);
240 /* NOTREACHED */
242 fsize = (size_t)statbuf.st_size;
243 if (fsize == 0) {
245 * The size of the specified po file is 0.
246 * In Solaris 8 and earlier, msgfmt was silent
247 * for the null po file. So, just returns
248 * without generating an error message.
250 (void) close(fd);
251 free(filename);
252 return;
254 addr = mmap(NULL, fsize, PROT_READ, MAP_SHARED, fd, 0);
255 if (addr == MAP_FAILED) {
256 error(gettext(ERR_MMAP_FAILED), filename);
257 /* NOTREACHED */
259 (void) close(fd);
261 if (!sun_p)
262 check_gnu(addr, fsize);
264 mbuf = addr;
265 for (;;) {
266 if (linebufhead) {
267 free(linebufhead);
268 linebufhead = NULL;
270 ln_size = _mbsntowcs(&linebufhead, &mbuf, &fsize);
271 if (ln_size == (size_t)-1) {
272 error(gettext(ERR_READ_FAILED), filename);
273 /* NOTREACHED */
274 } else if (ln_size == 0) {
275 break; /* End of File. */
277 linenum++;
279 linebufptr = linebufhead;
280 quotefound = 0;
282 switch (*linebufptr) {
283 case L'#': /* comment */
284 case L'\n': /* empty line */
285 continue;
286 case L'\"': /* multiple lines of msgid and msgstr */
287 quotefound = 1;
288 break;
292 * Process MSGID Tokens.
294 token_found = (wcsncmp(MSGID_TOKEN, linebufptr,
295 MSGID_LEN) == 0) ? 1 : 0;
297 if (token_found || (quotefound && inmsgid)) {
299 if (token_found) {
300 if (!CK_NXT_CH(linebufptr, MSGID_LEN+1)) {
301 diag(gettext(ERR_NOSPC), linenum);
302 error(gettext(ERR_EXITING));
303 /* NOTREACHED */
307 if (inmsgid && !quotefound) {
308 warning(gettext(WARN_NO_MSGSTR), msgid_linenum);
309 continue;
311 if (inmsgstr) {
312 sortit(gmsgid, gmsgstr);
313 (void) memset(gmsgid, 0, gmsgid_size);
314 (void) memset(gmsgstr, 0, gmsgstr_size);
317 if (inmsgid) {
318 /* multiple lines of msgid */
319 /* cancel the previous null termination */
320 bufptr_index--;
321 } else {
323 * The first line of msgid.
324 * Save linenum of msgid to be used when
325 * printing warning or error message.
327 msgid_linenum = linenum;
328 p = linebufptr;
329 linebufptr = consume_whitespace(
330 linebufptr + MSGID_LEN);
331 ln_size -= linebufptr - p;
332 bufptr = gmsgid;
333 bufptr_index = 0;
336 inmsgid = 1;
337 inmsgstr = 0;
338 indomain = 0;
339 goto load_buffer;
343 * Process MSGSTR Tokens.
345 token_found = (wcsncmp(MSGSTR_TOKEN, linebufptr,
346 MSGSTR_LEN) == 0) ? 1 : 0;
347 if (token_found || (quotefound && inmsgstr)) {
349 if (token_found) {
350 if (!CK_NXT_CH(linebufptr, MSGSTR_LEN+1)) {
351 diag(gettext(ERR_NOSPC), linenum);
352 error(gettext(ERR_EXITING));
353 /* NOTREACHED */
358 if (inmsgstr && !quotefound) {
359 warning(gettext(WARN_NO_MSGID), msgstr_linenum);
360 continue;
362 if (inmsgstr) {
363 /* multiple lines of msgstr */
364 /* cancel the previous null termination */
365 bufptr_index--;
366 } else {
368 * The first line of msgstr.
369 * Save linenum of msgid to be used when
370 * printing warning or error message.
372 msgstr_linenum = linenum;
373 p = linebufptr;
374 linebufptr = consume_whitespace(
375 linebufptr + MSGSTR_LEN);
376 ln_size -= linebufptr - p;
377 bufptr = gmsgstr;
378 bufptr_index = 0;
381 inmsgstr = 1;
382 inmsgid = 0;
383 indomain = 0;
384 goto load_buffer;
388 * Process DOMAIN Tokens.
389 * Add message id and message string to sorted list
390 * if msgstr was processed last time.
392 token_found = (wcsncmp(DOMAIN_TOKEN, linebufptr,
393 DOMAIN_LEN) == 0) ? 1 : 0;
394 if ((token_found) || (quotefound && indomain)) {
395 if (token_found) {
396 if (!CK_NXT_CH(linebufptr, DOMAIN_LEN+1)) {
397 diag(gettext(ERR_NOSPC), linenum);
398 error(gettext(ERR_EXITING));
399 /* NOTREACHED */
405 * process msgid and msgstr pair for previous domain
407 if (inmsgstr) {
408 sortit(gmsgid, gmsgstr);
411 /* refresh msgid and msgstr buffer */
412 if (inmsgstr || inmsgid) {
413 (void) memset(gmsgid, 0, gmsgid_size);
414 (void) memset(gmsgstr, 0, gmsgstr_size);
417 if (indomain) {
418 /* multiple lines of domain */
419 /* cancel the previous null termination */
420 bufptr_index--;
421 } else {
422 p = linebufptr;
423 linebufptr = consume_whitespace(
424 linebufptr + DOMAIN_LEN);
425 (void) memset(gcurrent_domain, 0,
426 sizeof (gcurrent_domain));
427 ln_size -= linebufptr - p;
428 bufptr = gcurrent_domain;
429 bufptr_index = 0;
432 indomain = 1;
433 inmsgid = 0;
434 inmsgstr = 0;
435 } /* if */
437 load_buffer:
439 * Now, fill up the buffer pointed by bufptr.
440 * At this point bufptr should point to one of
441 * msgid, msgptr, or current_domain.
442 * Otherwise, the entire line is ignored.
445 if (!bufptr) {
446 warning(gettext(WARN_SYNTAX_ERR), linenum);
447 continue;
450 if (*linebufptr++ != L'\"') {
451 warning(gettext(WARN_MISSING_QUOTE), linenum);
452 --linebufptr;
454 quotefound = 0;
457 * If there is not enough space in the buffer,
458 * increase buffer by ln_size by realloc.
460 ll = ln_size * mbcurmax;
461 if (bufptr == gmsgid) {
462 if (gmsgid_size < (bufptr_index + ll)) {
463 gmsgid = (char *)Xrealloc(gmsgid,
464 bufptr_index + ll);
465 bufptr = gmsgid;
466 gmsgid_size = bufptr_index + ll;
468 } else if (bufptr == gmsgstr) {
469 if (gmsgstr_size < (bufptr_index + ll)) {
470 gmsgstr = (char *)Xrealloc(gmsgstr,
471 bufptr_index + ll);
472 bufptr = gmsgstr;
473 gmsgstr_size = bufptr_index + ll;
477 while (wc = *linebufptr++) {
478 switch (wc) {
479 case L'\n':
480 if (!quotefound) {
481 warning(gettext(WARN_MISSING_QUOTE_AT_EOL), linenum);
483 break;
485 case L'\"':
486 quotefound = 1;
487 break;
489 case L'\\':
490 if ((mb = expand_meta(&linebufptr)) != '\0')
491 bufptr[bufptr_index++] = mb;
492 break;
494 default:
495 if ((n = wctomb(&bufptr[bufptr_index], wc)) > 0)
496 bufptr_index += n;
497 } /* switch */
498 if (quotefound) {
500 * Check if any remaining characters
501 * after closing quote.
503 linebufptr = consume_whitespace(linebufptr);
504 if (*linebufptr != L'\n') {
505 warning(gettext(WARN_INVALID_STRING),
506 linenum);
508 break;
510 } /* while */
512 bufptr[bufptr_index++] = '\0';
514 (void) strcpy(msgfile, gcurrent_domain);
515 (void) strcat(msgfile, ".mo");
516 } /* for(;;) */
518 if (inmsgstr) {
519 sortit(gmsgid, gmsgstr);
522 free(linebufhead);
523 if (munmap(addr, statbuf.st_size) == -1) {
524 error(gettext(ERR_MUNMAP_FAILED), filename);
525 /* NOTREACHED */
528 free(filename);
529 return;
531 } /* read_psffm */
535 * Skip leading white spaces and tabs.
537 static wchar_t *
538 consume_whitespace(wchar_t *buf)
540 wchar_t *bufptr = buf;
541 wchar_t c;
544 * Skip leading white spaces.
546 while ((c = *bufptr) != L'\0') {
547 if (c == L' ' || c == L'\t') {
548 bufptr++;
549 continue;
551 break;
553 return (bufptr);
554 } /* consume_white_space */
558 * handle escape sequences.
560 static char
561 expand_meta(wchar_t **buf)
563 wchar_t wc = **buf;
564 char n;
566 switch (wc) {
567 case L'"':
568 (*buf)++;
569 return ('\"');
570 case L'\\':
571 (*buf)++;
572 return ('\\');
573 case L'b':
574 (*buf)++;
575 return ('\b');
576 case L'f':
577 (*buf)++;
578 return ('\f');
579 case L'n':
580 (*buf)++;
581 return ('\n');
582 case L'r':
583 (*buf)++;
584 return ('\r');
585 case L't':
586 (*buf)++;
587 return ('\t');
588 case L'v':
589 (*buf)++;
590 return ('\v');
591 case L'a':
592 (*buf)++;
593 return ('\a');
594 case L'\'':
595 (*buf)++;
596 return ('\'');
597 case L'?':
598 (*buf)++;
599 return ('\?');
600 case L'0':
601 case L'1':
602 case L'2':
603 case L'3':
604 case L'4':
605 case L'5':
606 case L'6':
607 case L'7':
609 * This case handles \ddd where ddd is octal number.
610 * There could be one, two, or three octal numbers.
612 (*buf)++;
613 n = (char)(wc - L'0');
614 wc = **buf;
615 if (wc >= L'0' && wc <= L'7') {
616 (*buf)++;
617 n = 8*n + (char)(wc - L'0');
618 wc = **buf;
619 if (wc >= L'0' && wc <= L'7') {
620 (*buf)++;
621 n = 8*n + (char)(wc - L'0');
624 return (n);
625 default:
626 return ('\0');
628 } /* expand_meta */
631 * Finds the head of the current domain linked list and
632 * call insert_message() to insert msgid and msgstr pair
633 * to the linked list.
635 static void
636 sortit(char *msgid, char *msgstr)
638 struct domain_struct *dom;
640 #ifdef DEBUG
641 (void) fprintf(stderr,
642 "==> sortit(), domain=<%s> msgid=<%s> msgstr=<%s>\n",
643 gcurrent_domain, msgid, msgstr);
644 #endif
647 * If "-o filename" is specified, then all "domain" directive
648 * are ignored and, all messages will be stored in domain
649 * whose name is filename.
651 if (oflag) {
652 dom = find_domain_node(outfile);
653 } else {
654 dom = find_domain_node(gcurrent_domain);
657 insert_message(dom, msgid, msgstr);
661 * This routine inserts message in the current domain message list.
662 * It is inserted in ascending order.
664 static void
665 insert_message(struct domain_struct *dom,
666 char *msgid, char *msgstr)
668 struct msg_chain *p1;
669 struct msg_chain *node, *prev_node;
670 int b;
673 * Find the optimal starting search position.
674 * The starting search position is either the first node
675 * or the current_elem of domain.
676 * The current_elem is the pointer to the node which
677 * is most recently accessed in domain.
679 if (dom->current_elem != NULL) {
680 b = strcmp(msgid, dom->current_elem->msgid);
681 if (b == 0) {
682 if (verbose)
683 warning(gettext(WARN_DUP_MSG),
684 msgid, msgid_linenum);
685 return;
686 } else if (b > 0) { /* to implement descending order */
687 p1 = dom->first_elem;
688 } else {
689 p1 = dom->current_elem;
691 } else {
692 p1 = dom->first_elem;
696 * search msgid insert position in the list
697 * Search starts from the node pointed by p1.
699 prev_node = NULL;
700 while (p1) {
701 b = strcmp(msgid, p1->msgid);
702 if (b == 0) {
703 if (verbose)
704 warning(gettext(WARN_DUP_MSG),
705 msgid, msgid_linenum);
706 return;
707 } else if (b < 0) { /* to implement descending order */
708 /* move to the next node */
709 prev_node = p1;
710 p1 = p1->next;
711 } else {
712 /* insert a new msg node */
713 node = (struct msg_chain *)
714 Xmalloc(sizeof (struct msg_chain));
715 node->next = p1;
716 node->msgid = Xstrdup(msgid);
717 node->msgstr = Xstrdup(msgstr);
719 if (prev_node) {
720 prev_node->next = node;
721 } else {
722 dom->first_elem = node;
724 dom->current_elem = node;
725 return;
727 } /* while */
730 * msgid is smaller than any of msgid in the list or
731 * list is empty.
732 * Therefore, append it.
734 node = (struct msg_chain *)
735 Xmalloc(sizeof (struct msg_chain));
736 node->next = NULL;
737 node->msgid = Xstrdup(msgid);
738 node->msgstr = Xstrdup(msgstr);
740 if (prev_node) {
741 prev_node->next = node;
742 } else {
743 dom->first_elem = node;
745 dom->current_elem = node;
747 return;
749 } /* insert_message */
753 * This routine will find head of the linked list for the given
754 * domain_name. This looks up cache entry first and if cache misses,
755 * scans the list.
756 * If not found, then create a new node.
758 static struct domain_struct *
759 find_domain_node(char *domain_name)
761 struct domain_struct *p1;
762 struct domain_struct *node;
763 struct domain_struct *prev_node;
764 int b;
767 /* for perfomance, check cache 'last_used_domain' */
768 if (last_used_domain) {
769 b = strcmp(domain_name, last_used_domain->domain);
770 if (b == 0) {
771 return (last_used_domain);
772 } else if (b < 0) {
773 p1 = first_domain;
774 } else {
775 p1 = last_used_domain;
777 } else {
778 p1 = first_domain;
781 prev_node = NULL;
782 while (p1) {
783 b = strcmp(domain_name, p1->domain);
784 if (b == 0) {
785 /* node found */
786 last_used_domain = p1;
787 return (p1);
788 } else if (b > 0) {
789 /* move to the next node */
790 prev_node = p1;
791 p1 = p1->next;
792 } else {
793 /* insert a new domain node */
794 node = (struct domain_struct *)
795 Xmalloc(sizeof (struct domain_struct));
796 node->next = p1;
797 node->domain = Xstrdup(domain_name);
798 node->first_elem = NULL;
799 node->current_elem = NULL;
800 if (prev_node) {
801 /* insert the node in the middle */
802 prev_node->next = node;
803 } else {
804 /* node inserted is the smallest */
805 first_domain = node;
807 last_used_domain = node;
808 return (node);
810 } /* while */
813 * domain_name is larger than any of domain name in the list or
814 * list is empty.
816 node = (struct domain_struct *)
817 Xmalloc(sizeof (struct domain_struct));
818 node->next = NULL;
819 node->domain = Xstrdup(domain_name);
820 node->first_elem = NULL;
821 node->current_elem = NULL;
822 if (prev_node) {
823 /* domain list is not empty */
824 prev_node->next = node;
825 } else {
826 /* domain list is empty */
827 first_domain = node;
829 last_used_domain = node;
831 return (node);
833 } /* find_domain_node */
837 * binary_compute() is used for pre-computing a binary search.
839 static int
840 binary_compute(int i, int j, int *more, int *less)
842 int k;
844 if (i > j) {
845 return (LEAFINDICATOR);
847 k = (i + j) / 2;
849 less[k] = binary_compute(i, k - 1, more, less);
850 more[k] = binary_compute(k + 1, j, more, less);
852 return (k);
854 } /* binary_compute */
858 * Write all domain data to file.
859 * Each domain will create one file.
861 static void
862 output_all_mo_files(void)
864 struct domain_struct *p;
866 p = first_domain;
867 while (p) {
869 * generate message object file only if there is
870 * at least one element.
872 if (p->first_elem) {
873 output_one_mo_file(p);
875 p = p->next;
877 return;
879 } /* output_all_mo_files */
883 * Write one domain data list to file.
885 static void
886 output_one_mo_file(struct domain_struct *dom)
888 FILE *fp;
889 struct msg_chain *p;
890 int message_count;
891 int string_count_msgid;
892 int string_count_msg;
893 int msgid_index = 0;
894 int msgstr_index = 0;
895 int *less, *more;
896 int i;
897 char fname [TEXTDOMAINMAX+1];
899 if (!dom || !dom->first_elem)
900 return;
903 * If -o flag is specified, then file name is used as domain name.
904 * If not, ".mo" is appended to the domain name.
906 (void) strcpy(fname, dom->domain);
907 if (!oflag) {
908 (void) strcat(fname, ".mo");
910 fp = fopen(fname, "w");
911 if (fp == NULL) {
912 error(gettext(ERR_OPEN_FAILED), fname);
913 /* NOTREACHED */
916 /* compute offsets and counts */
917 message_count = 0;
918 p = dom->first_elem;
919 while (p) {
920 p->msgid_offset = msgid_index;
921 p->msgstr_offset = msgstr_index;
922 msgid_index += strlen(p->msgid) + 1;
923 msgstr_index += strlen(p->msgstr) + 1;
924 message_count++;
925 p = p->next;
929 * Fill up less and more entries to be used for binary search.
931 string_count_msgid = msgid_index;
932 string_count_msg = msgstr_index;
933 less = (int *)Xcalloc(message_count, sizeof (int));
934 more = (int *)Xcalloc(message_count, sizeof (int));
936 (void) binary_compute(0, message_count - 1, more, less);
938 #ifdef DEBUG
940 int i;
941 for (i = 0; i < message_count; i++) {
942 (void) fprintf(stderr,
943 " less[%2d]=%2d, more[%2d]=%2d\n",
944 i, less[i], i, more[i]);
947 #endif
950 * write out the message object file.
951 * The middle one is the first message to check by gettext().
953 i = (message_count - 1) / 2;
954 (void) fwrite(&i, sizeof (int), 1, fp);
955 (void) fwrite(&message_count, sizeof (int), 1, fp);
956 (void) fwrite(&string_count_msgid, sizeof (int), 1, fp);
957 (void) fwrite(&string_count_msg, sizeof (int), 1, fp);
958 i = MSG_STRUCT_SIZE * message_count;
959 (void) fwrite(&i, sizeof (int), 1, fp);
961 /* march through linked list and write out all nodes. */
962 i = 0;
963 p = dom->first_elem;
964 while (p) { /* put out message struct */
965 (void) fwrite(&less[i], sizeof (int), 1, fp);
966 (void) fwrite(&more[i], sizeof (int), 1, fp);
967 (void) fwrite(&p->msgid_offset, sizeof (int), 1, fp);
968 (void) fwrite(&p->msgstr_offset, sizeof (int), 1, fp);
969 i++;
970 p = p->next;
973 /* put out message id strings */
974 p = dom->first_elem;
975 while (p) {
976 (void) fwrite(p->msgid, strlen(p->msgid)+1, 1, fp);
977 p = p->next;
980 /* put out message strings */
981 p = dom->first_elem;
982 while (p) {
983 (void) fwrite(p->msgstr, strlen(p->msgstr)+1, 1, fp);
984 p = p->next;
987 (void) fclose(fp);
988 free(less);
989 free(more);
991 return;
993 } /* output_one_mo_file */
997 * read one line from *mbuf,
998 * skip preceding whitespaces,
999 * convert the line to wide characters,
1000 * place the wide characters into *bufhead, and
1001 * return the number of wide characters placed.
1003 * INPUT:
1004 * **bufhead - address of a variable that is the pointer
1005 * to wchar_t.
1006 * The variable should been initialized to NULL.
1007 * **mbuf - address of a variable that is the pointer
1008 * to char.
1009 * The pointer should point to the memory mmapped to
1010 * the file to input.
1011 * **fsize - address of a size_t variable that contains
1012 * the size of unread bytes in the file to input.
1013 * OUTPUT:
1014 * return - the number of wide characters placed.
1015 * **bufhead - _mbsntowcs allocates the buffer to store
1016 * one line in wchar_t from *mbuf and sets the address
1017 * to *bufhead.
1018 * **mbuf - _mbsntowcs reads one line from *mbuf and sets *mbuf
1019 * to the beginning of the next line.
1020 * **fsize - *fsize will be set to the size of the unread
1021 * bytes in the file.
1023 static size_t
1024 _mbsntowcs(wchar_t **bufhead, char **mbuf, size_t *fsize)
1026 wchar_t *tp, *th;
1027 wchar_t wc;
1028 size_t tbufsize = LINE_SIZE;
1029 size_t ttbufsize, nc;
1030 char *pc = *mbuf;
1031 int nb;
1033 if (*fsize == 0) {
1034 /* eof */
1035 return (0);
1038 th = (wchar_t *)Xmalloc(sizeof (wchar_t) * tbufsize);
1039 nc = tbufsize;
1041 /* skip preceding whitespaces */
1042 while ((*pc != '\0')) {
1043 if ((*pc == ' ') || (*pc == '\t')) {
1044 pc++;
1045 (*fsize)--;
1046 } else {
1047 break;
1051 tp = th;
1052 while (*fsize > 0) {
1053 nb = mbtowc(&wc, pc, mbcurmax);
1054 if (nb == -1) {
1055 return ((size_t)-1);
1058 if (*pc == '\n') {
1059 /* found eol */
1060 if (nc <= 1) {
1062 * not enough buffer
1063 * at least 2 more bytes are required for
1064 * L'\n' and L'\0'
1066 ttbufsize = tbufsize + 2;
1067 th = (wchar_t *)Xrealloc(th,
1068 sizeof (wchar_t) * ttbufsize);
1069 tp = th + tbufsize - nc;
1070 tbufsize = ttbufsize;
1072 *tp++ = L'\n';
1073 *tp++ = L'\0';
1074 pc += nb;
1075 *fsize -= nb;
1076 *mbuf = pc;
1077 *bufhead = th;
1078 return ((size_t)(tp - th));
1080 if (nc == 0) {
1081 ttbufsize = tbufsize + LINE_SIZE;
1082 th = (wchar_t *)Xrealloc(th,
1083 sizeof (wchar_t) * ttbufsize);
1084 tp = th + tbufsize;
1085 nc = LINE_SIZE;
1086 tbufsize = ttbufsize;
1088 *tp++ = wc;
1089 nc--;
1090 pc += nb;
1091 *fsize -= nb;
1092 } /* while */
1095 * At this point, the input file has been consumed,
1096 * but there is no ending '\n'; we add it to
1097 * the output file.
1099 if (nc <= 1) {
1101 * not enough buffer
1102 * at least 2 more bytes are required for
1103 * L'\n' and L'\0'
1105 ttbufsize = tbufsize + 2;
1106 th = (wchar_t *)Xrealloc(th,
1107 sizeof (wchar_t) * ttbufsize);
1108 tp = th + tbufsize - nc;
1109 tbufsize = ttbufsize;
1111 *tp++ = L'\n';
1112 *tp++ = L'\0';
1113 *mbuf = pc;
1114 *bufhead = th;
1115 return ((size_t)(tp - th));
1120 * This is debug function. Not compiled in the final executable.
1122 #ifdef DEBUG
1123 static void
1124 printlist(void)
1126 struct domain_struct *p;
1127 struct msg_chain *m;
1129 (void) fprintf(stderr, "\n=== Printing contents of all domains ===\n");
1130 p = first_domain;
1131 while (p) {
1132 (void) fprintf(stderr, "domain name = <%s>\n", p->domain);
1133 m = p->first_elem;
1134 while (m) {
1135 (void) fprintf(stderr, " msgid=<%s>, msgstr=<%s>\n",
1136 m->msgid, m->msgstr);
1137 m = m->next;
1139 p = p->next;
1141 } /* printlist */
1142 #endif