9621 Make createtxg and guid properties public
[unleashed.git] / usr / src / cmd / msgfmt / msgfmt.c
blobe6d4b01df4e1e1013d7890d10858228258dec8c0
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 #pragma ident "%Z%%M% %I% %E% SMI"
29 #include "sun_msgfmt.h"
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 *);
41 #ifdef DEBUG
42 static void printlist(void);
43 #endif
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 */
55 static int oflag = 0;
56 static int sun_p = 0;
57 int verbose = 0;
59 static struct domain_struct *first_domain = NULL;
60 static struct domain_struct *last_used_domain = NULL;
62 static int mbcurmax;
64 static char **oargv;
65 static char *inputdir;
67 extern void check_gnu(char *, size_t);
69 #define GNU_MSGFMT "/usr/lib/gmsgfmt"
70 void
71 invoke_gnu_msgfmt(void)
74 * Transferring to /usr/lib/gmsgfmt
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
85 if (verbose) {
86 diag(gettext(DIAG_INVOKING_GNU));
89 (void) execv(gnu_msgfmt, oargv);
90 /* exec failed */
91 error(gettext(ERR_EXEC_FAILED), gnu_msgfmt);
92 /* NOTREACHED */
95 static void
96 usage(void)
98 (void) fprintf(stderr, gettext(ERR_USAGE));
99 exit(2);
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.
109 main(int argc, char **argv)
111 int ret;
112 static struct flags flag;
114 (void) setlocale(LC_ALL, "");
115 #if !defined(TEXT_DOMAIN)
116 #define TEXT_DOMAIN "SYS_TEST"
117 #endif
118 (void) textdomain(TEXT_DOMAIN);
120 oargv = argv;
121 ret = parse_option(&argc, &argv, &flag);
122 if (ret == -1) {
123 usage();
124 /* NOTREACHED */
127 if (flag.sun_p) {
128 /* never invoke gnu msgfmt */
129 if (flag.gnu_p) {
130 error(gettext(ERR_GNU_ON_SUN));
131 /* NOTREACHED */
133 sun_p = flag.sun_p;
135 if (flag.idir) {
136 inputdir = flag.idir;
138 if (flag.ofile) {
139 oflag = 1;
140 outfile = flag.ofile;
142 if (flag.verbose) {
143 verbose = 1;
146 if (flag.gnu_p) {
147 /* invoke /usr/lib/gmsgfmt */
148 invoke_gnu_msgfmt();
149 /* NOTREACHED */
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.
157 gmsgid = (char *)Xmalloc(MAX_VALUE_LEN);
158 gmsgstr = (char *)Xmalloc(MAX_VALUE_LEN);
160 gmsgid_size = gmsgstr_size = MAX_VALUE_LEN;
161 (void) memset(gmsgid, 0, gmsgid_size);
162 (void) memset(gmsgstr, 0, gmsgstr_size);
164 mbcurmax = MB_CUR_MAX;
166 while (argc-- > 0) {
167 if (verbose) {
168 diag(gettext(DIAG_START_PROC), *argv);
170 read_psffm(*argv++);
173 output_all_mo_files();
175 #ifdef DEBUG
176 printlist();
177 #endif
179 return (0);
181 } /* main */
186 * read_psffm - read in "psffm" format file, check syntax, printing error
187 * messages as needed, output binary tree to file <domain>
190 static void
191 read_psffm(char *file)
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;
213 * For each po file to be read,
214 * 1) set domain to default and
215 * 2) set linenumer to 0.
217 (void) strcpy(gcurrent_domain, DEFAULT_DOMAIN);
218 linenum = 0;
220 if (!inputdir) {
221 filename = Xstrdup(file);
222 } else {
223 size_t dirlen, filelen, len;
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';
235 fd = open(filename, O_RDONLY);
236 if (fd == -1) {
237 error(gettext(ERR_OPEN_FAILED), filename);
238 /* NOTREACHED */
240 if (fstat64(fd, &statbuf) == -1) {
241 error(gettext(ERR_STAT_FAILED), filename);
242 /* NOTREACHED */
244 fsize = (size_t)statbuf.st_size;
245 if (fsize == 0) {
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.
252 (void) close(fd);
253 free(filename);
254 return;
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 */
261 (void) close(fd);
263 if (!sun_p)
264 check_gnu(addr, fsize);
266 mbuf = addr;
267 for (;;) {
268 if (linebufhead) {
269 free(linebufhead);
270 linebufhead = NULL;
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. */
279 linenum++;
281 linebufptr = linebufhead;
282 quotefound = 0;
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;
294 * Process MSGID Tokens.
296 token_found = (wcsncmp(MSGID_TOKEN, linebufptr,
297 MSGID_LEN) == 0) ? 1 : 0;
299 if (token_found || (quotefound && inmsgid)) {
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 */
309 if (inmsgid && !quotefound) {
310 warning(gettext(WARN_NO_MSGSTR), msgid_linenum);
311 continue;
313 if (inmsgstr) {
314 sortit(gmsgid, gmsgstr);
315 (void) memset(gmsgid, 0, gmsgid_size);
316 (void) memset(gmsgstr, 0, gmsgstr_size);
319 if (inmsgid) {
320 /* multiple lines of msgid */
321 /* cancel the previous null termination */
322 bufptr_index--;
323 } else {
325 * The first line of msgid.
326 * Save linenum of msgid to be used when
327 * printing warning or error message.
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;
338 inmsgid = 1;
339 inmsgstr = 0;
340 indomain = 0;
341 goto load_buffer;
345 * Process MSGSTR Tokens.
347 token_found = (wcsncmp(MSGSTR_TOKEN, linebufptr,
348 MSGSTR_LEN) == 0) ? 1 : 0;
349 if (token_found || (quotefound && inmsgstr)) {
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 */
360 if (inmsgstr && !quotefound) {
361 warning(gettext(WARN_NO_MSGID), msgstr_linenum);
362 continue;
364 if (inmsgstr) {
365 /* multiple lines of msgstr */
366 /* cancel the previous null termination */
367 bufptr_index--;
368 } else {
370 * The first line of msgstr.
371 * Save linenum of msgid to be used when
372 * printing warning or error message.
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;
383 inmsgstr = 1;
384 inmsgid = 0;
385 indomain = 0;
386 goto load_buffer;
390 * Process DOMAIN Tokens.
391 * Add message id and message string to sorted list
392 * if msgstr was processed last time.
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 */
407 * process msgid and msgstr pair for previous domain
409 if (inmsgstr) {
410 sortit(gmsgid, gmsgstr);
413 /* refresh msgid and msgstr buffer */
414 if (inmsgstr || inmsgid) {
415 (void) memset(gmsgid, 0, gmsgid_size);
416 (void) memset(gmsgstr, 0, gmsgstr_size);
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;
434 indomain = 1;
435 inmsgid = 0;
436 inmsgstr = 0;
437 } /* if */
439 load_buffer:
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.
447 if (!bufptr) {
448 warning(gettext(WARN_SYNTAX_ERR), linenum);
449 continue;
452 if (*linebufptr++ != L'\"') {
453 warning(gettext(WARN_MISSING_QUOTE), linenum);
454 --linebufptr;
456 quotefound = 0;
459 * If there is not enough space in the buffer,
460 * increase buffer by ln_size by realloc.
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;
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;
479 while (wc = *linebufptr++) {
480 switch (wc) {
481 case L'\n':
482 if (!quotefound) {
483 warning(gettext(WARN_MISSING_QUOTE_AT_EOL), linenum);
485 break;
487 case L'\"':
488 quotefound = 1;
489 break;
491 case L'\\':
492 if ((mb = expand_meta(&linebufptr)) != NULL)
493 bufptr[bufptr_index++] = mb;
494 break;
496 default:
497 if ((n = wctomb(&bufptr[bufptr_index], wc)) > 0)
498 bufptr_index += n;
499 } /* switch */
500 if (quotefound) {
502 * Check if any remaining characters
503 * after closing quote.
505 linebufptr = consume_whitespace(linebufptr);
506 if (*linebufptr != L'\n') {
507 warning(gettext(WARN_INVALID_STRING),
508 linenum);
510 break;
512 } /* while */
514 bufptr[bufptr_index++] = '\0';
516 (void) strcpy(msgfile, gcurrent_domain);
517 (void) strcat(msgfile, ".mo");
518 } /* for(;;) */
520 if (inmsgstr) {
521 sortit(gmsgid, gmsgstr);
524 if (linebufhead)
525 free(linebufhead);
526 if (munmap(addr, statbuf.st_size) == -1) {
527 error(gettext(ERR_MUNMAP_FAILED), filename);
528 /* NOTREACHED */
531 free(filename);
532 return;
534 } /* read_psffm */
538 * Skip leading white spaces and tabs.
540 static wchar_t *
541 consume_whitespace(wchar_t *buf)
543 wchar_t *bufptr = buf;
544 wchar_t c;
547 * Skip leading white spaces.
549 while ((c = *bufptr) != L'\0') {
550 if (c == L' ' || c == L'\t') {
551 bufptr++;
552 continue;
554 break;
556 return (bufptr);
557 } /* consume_white_space */
561 * handle escape sequences.
563 static char
564 expand_meta(wchar_t **buf)
566 wchar_t wc = **buf;
567 char n;
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':
612 * This case handles \ddd where ddd is octal number.
613 * There could be one, two, or three octal numbers.
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');
627 return (n);
628 default:
629 return (NULL);
631 } /* expand_meta */
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.
638 static void
639 sortit(char *msgid, char *msgstr)
641 struct domain_struct *dom;
643 #ifdef DEBUG
644 (void) fprintf(stderr,
645 "==> sortit(), domain=<%s> msgid=<%s> msgstr=<%s>\n",
646 gcurrent_domain, msgid, msgstr);
647 #endif
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.
654 if (oflag) {
655 dom = find_domain_node(outfile);
656 } else {
657 dom = find_domain_node(gcurrent_domain);
660 insert_message(dom, msgid, msgstr);
664 * This routine inserts message in the current domain message list.
665 * It is inserted in ascending order.
667 static void
668 insert_message(struct domain_struct *dom,
669 char *msgid, char *msgstr)
671 struct msg_chain *p1;
672 struct msg_chain *node, *prev_node;
673 int b;
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.
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;
694 } else {
695 p1 = dom->first_elem;
699 * search msgid insert position in the list
700 * Search starts from the node pointed by p1.
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);
722 if (prev_node) {
723 prev_node->next = node;
724 } else {
725 dom->first_elem = node;
727 dom->current_elem = node;
728 return;
730 } /* while */
733 * msgid is smaller than any of msgid in the list or
734 * list is empty.
735 * Therefore, append it.
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);
743 if (prev_node) {
744 prev_node->next = node;
745 } else {
746 dom->first_elem = node;
748 dom->current_elem = node;
750 return;
752 } /* insert_message */
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.
761 static struct domain_struct *
762 find_domain_node(char *domain_name)
764 struct domain_struct *p1;
765 struct domain_struct *node;
766 struct domain_struct *prev_node;
767 int b;
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;
780 } else {
781 p1 = first_domain;
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;
810 last_used_domain = node;
811 return (node);
813 } /* while */
816 * domain_name is larger than any of domain name in the list or
817 * list is empty.
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;
832 last_used_domain = node;
834 return (node);
836 } /* find_domain_node */
840 * binary_compute() is used for pre-computing a binary search.
842 static int
843 binary_compute(int i, int j, int *more, int *less)
845 int k;
847 if (i > j) {
848 return (LEAFINDICATOR);
850 k = (i + j) / 2;
852 less[k] = binary_compute(i, k - 1, more, less);
853 more[k] = binary_compute(k + 1, j, more, less);
855 return (k);
857 } /* binary_compute */
861 * Write all domain data to file.
862 * Each domain will create one file.
864 static void
865 output_all_mo_files(void)
867 struct domain_struct *p;
869 p = first_domain;
870 while (p) {
872 * generate message object file only if there is
873 * at least one element.
875 if (p->first_elem) {
876 output_one_mo_file(p);
878 p = p->next;
880 return;
882 } /* output_all_mo_files */
886 * Write one domain data list to file.
888 static void
889 output_one_mo_file(struct domain_struct *dom)
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];
902 if (!dom || !dom->first_elem)
903 return;
906 * If -o flag is specified, then file name is used as domain name.
907 * If not, ".mo" is appended to the domain name.
909 (void) strcpy(fname, dom->domain);
910 if (!oflag) {
911 (void) strcat(fname, ".mo");
913 fp = fopen(fname, "w");
914 if (fp == NULL) {
915 error(gettext(ERR_OPEN_FAILED), fname);
916 /* NOTREACHED */
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;
932 * Fill up less and more entries to be used for binary search.
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));
939 (void) binary_compute(0, message_count - 1, more, less);
941 #ifdef DEBUG
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]);
950 #endif
953 * write out the message object file.
954 * The middle one is the first message to check by gettext().
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);
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;
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;
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;
990 (void) fclose(fp);
991 free(less);
992 free(more);
994 return;
996 } /* output_one_mo_file */
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.
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.
1026 static size_t
1027 _mbsntowcs(wchar_t **bufhead, char **mbuf, size_t *fsize)
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;
1036 if (*fsize == 0) {
1037 /* eof */
1038 return (0);
1041 th = (wchar_t *)Xmalloc(sizeof (wchar_t) * tbufsize);
1042 nc = tbufsize;
1044 /* skip preceding whitespaces */
1045 while ((*pc != '\0')) {
1046 if ((*pc == ' ') || (*pc == '\t')) {
1047 pc++;
1048 (*fsize)--;
1049 } else {
1050 break;
1054 tp = th;
1055 while (*fsize > 0) {
1056 nb = mbtowc(&wc, pc, mbcurmax);
1057 if (nb == -1) {
1058 return ((size_t)-1);
1061 if (*pc == '\n') {
1062 /* found eol */
1063 if (nc <= 1) {
1065 * not enough buffer
1066 * at least 2 more bytes are required for
1067 * L'\n' and L'\0'
1069 ttbufsize = tbufsize + 2;
1070 th = (wchar_t *)Xrealloc(th,
1071 sizeof (wchar_t) * ttbufsize);
1072 tp = th + tbufsize - nc;
1073 tbufsize = ttbufsize;
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));
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;
1091 *tp++ = wc;
1092 nc--;
1093 pc += nb;
1094 *fsize -= nb;
1095 } /* while */
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.
1102 if (nc <= 1) {
1104 * not enough buffer
1105 * at least 2 more bytes are required for
1106 * L'\n' and L'\0'
1108 ttbufsize = tbufsize + 2;
1109 th = (wchar_t *)Xrealloc(th,
1110 sizeof (wchar_t) * ttbufsize);
1111 tp = th + tbufsize - nc;
1112 tbufsize = ttbufsize;
1114 *tp++ = L'\n';
1115 *tp++ = L'\0';
1116 *mbuf = pc;
1117 *bufhead = th;
1118 return ((size_t)(tp - th));
1123 * This is debug function. Not compiled in the final executable.
1125 #ifdef DEBUG
1126 static void
1127 printlist(void)
1129 struct domain_struct *p;
1130 struct msg_chain *m;
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;
1142 p = p->next;
1144 } /* printlist */
1145 #endif