diff options
author | stevel@tonic-gate <none@none> | 2005-06-14 00:00:00 -0700 |
---|---|---|
committer | stevel@tonic-gate <none@none> | 2005-06-14 00:00:00 -0700 |
commit | 7c478bd95313f5f23a4c958a745db2134aa03244 (patch) | |
tree | c871e58545497667cbb4b0a4f2daf204743e1fe7 /usr/src/cmd/msgfmt/msgfmt.c | |
download | illumos-joyent-7c478bd95313f5f23a4c958a745db2134aa03244.tar.gz |
OpenSolaris Launch
Diffstat (limited to 'usr/src/cmd/msgfmt/msgfmt.c')
-rw-r--r-- | usr/src/cmd/msgfmt/msgfmt.c | 1145 |
1 files changed, 1145 insertions, 0 deletions
diff --git a/usr/src/cmd/msgfmt/msgfmt.c b/usr/src/cmd/msgfmt/msgfmt.c new file mode 100644 index 0000000000..e6d4b01df4 --- /dev/null +++ b/usr/src/cmd/msgfmt/msgfmt.c @@ -0,0 +1,1145 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License, Version 1.0 only + * (the "License"). You may not use this file except in compliance + * with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright 2004 Sun Microsystems, Inc. All rights reserved. + * Use is subject to license terms. + */ + +#pragma ident "%Z%%M% %I% %E% SMI" + +#include "sun_msgfmt.h" + +static void read_psffm(char *); +static void sortit(char *, char *); +static wchar_t *consume_whitespace(wchar_t *); +static char expand_meta(wchar_t **); +static struct domain_struct *find_domain_node(char *); +static void insert_message(struct domain_struct *, char *, char *); +static void output_all_mo_files(void); +static void output_one_mo_file(struct domain_struct *); +static size_t _mbsntowcs(wchar_t **, char **, size_t *); + +#ifdef DEBUG +static void printlist(void); +#endif + +static char gcurrent_domain[TEXTDOMAINMAX+1]; +static char *gmsgid; /* Stores msgid when read po file */ +static char *gmsgstr; /* Stores msgstr when read po file */ +static int gmsgid_size; /* The current size of msgid buffer */ +static int gmsgstr_size; /* The current size of msgstr buffer */ +static char *outfile = NULL; +static int linenum; /* The line number in the file */ +static int msgid_linenum; /* The last msgid token line number */ +static int msgstr_linenum; /* The last msgstr token line number */ + +static int oflag = 0; +static int sun_p = 0; +int verbose = 0; + +static struct domain_struct *first_domain = NULL; +static struct domain_struct *last_used_domain = NULL; + +static int mbcurmax; + +static char **oargv; +static char *inputdir; + +extern void check_gnu(char *, size_t); + +#define GNU_MSGFMT "/usr/lib/gmsgfmt" +void +invoke_gnu_msgfmt(void) +{ + /* + * Transferring to /usr/lib/gmsgfmt + */ + char *gnu_msgfmt; +#ifdef DEBUG_MSGFMT + gnu_msgfmt = getenv("GNU_MSGFMT"); + if (!gnu_msgfmt) + gnu_msgfmt = GNU_MSGFMT; +#else + gnu_msgfmt = GNU_MSGFMT; +#endif + + if (verbose) { + diag(gettext(DIAG_INVOKING_GNU)); + } + + (void) execv(gnu_msgfmt, oargv); + /* exec failed */ + error(gettext(ERR_EXEC_FAILED), gnu_msgfmt); + /* NOTREACHED */ +} + +static void +usage(void) +{ + (void) fprintf(stderr, gettext(ERR_USAGE)); + exit(2); +} + +/* + * msgfmt - Generate binary tree for runtime gettext() using psffm: "Portable + * Source File Format for Messages" file template. This file may have + * previously been generated by the xgettext filter for c source files. + */ + +int +main(int argc, char **argv) +{ + int ret; + static struct flags flag; + + (void) setlocale(LC_ALL, ""); +#if !defined(TEXT_DOMAIN) +#define TEXT_DOMAIN "SYS_TEST" +#endif + (void) textdomain(TEXT_DOMAIN); + + oargv = argv; + ret = parse_option(&argc, &argv, &flag); + if (ret == -1) { + usage(); + /* NOTREACHED */ + } + + if (flag.sun_p) { + /* never invoke gnu msgfmt */ + if (flag.gnu_p) { + error(gettext(ERR_GNU_ON_SUN)); + /* NOTREACHED */ + } + sun_p = flag.sun_p; + } + if (flag.idir) { + inputdir = flag.idir; + } + if (flag.ofile) { + oflag = 1; + outfile = flag.ofile; + } + if (flag.verbose) { + verbose = 1; + } + + if (flag.gnu_p) { + /* invoke /usr/lib/gmsgfmt */ + invoke_gnu_msgfmt(); + /* NOTREACHED */ + } + + /* + * read all portable object files specified in command arguments. + * Allocate initial size for msgid and msgstr. If it needs more + * spaces, realloc later. + */ + gmsgid = (char *)Xmalloc(MAX_VALUE_LEN); + gmsgstr = (char *)Xmalloc(MAX_VALUE_LEN); + + gmsgid_size = gmsgstr_size = MAX_VALUE_LEN; + (void) memset(gmsgid, 0, gmsgid_size); + (void) memset(gmsgstr, 0, gmsgstr_size); + + mbcurmax = MB_CUR_MAX; + + while (argc-- > 0) { + if (verbose) { + diag(gettext(DIAG_START_PROC), *argv); + } + read_psffm(*argv++); + } + + output_all_mo_files(); + +#ifdef DEBUG + printlist(); +#endif + + return (0); + +} /* main */ + + + +/* + * read_psffm - read in "psffm" format file, check syntax, printing error + * messages as needed, output binary tree to file <domain> + */ + +static void +read_psffm(char *file) +{ + int fd; + static char msgfile[MAXPATHLEN]; + wchar_t *linebufptr, *p; + char *bufptr = 0; + int quotefound; /* double quote was seen */ + int inmsgid = 0; /* indicates "msgid" was seen */ + int inmsgstr = 0; /* indicates "msgstr" was seen */ + int indomain = 0; /* indicates "domain" was seen */ + wchar_t wc; + char mb; + int n; + char token_found; /* Boolean value */ + unsigned int bufptr_index = 0; /* current index of bufptr */ + char *mbuf, *addr; + size_t fsize, ln_size, ll; + wchar_t *linebufhead = NULL; + struct stat64 statbuf; + char *filename; + + /* + * For each po file to be read, + * 1) set domain to default and + * 2) set linenumer to 0. + */ + (void) strcpy(gcurrent_domain, DEFAULT_DOMAIN); + linenum = 0; + + if (!inputdir) { + filename = Xstrdup(file); + } else { + size_t dirlen, filelen, len; + + dirlen = strlen(inputdir); + filelen = strlen(file); + len = dirlen + 1 + filelen + 1; + filename = (char *)Xmalloc(len); + (void) memcpy(filename, inputdir, dirlen); + *(filename + dirlen) = '/'; + (void) memcpy(filename + dirlen + 1, file, filelen); + *(filename + dirlen + 1 + filelen) = '\0'; + } + + fd = open(filename, O_RDONLY); + if (fd == -1) { + error(gettext(ERR_OPEN_FAILED), filename); + /* NOTREACHED */ + } + if (fstat64(fd, &statbuf) == -1) { + error(gettext(ERR_STAT_FAILED), filename); + /* NOTREACHED */ + } + fsize = (size_t)statbuf.st_size; + if (fsize == 0) { + /* + * The size of the specified po file is 0. + * In Solaris 8 and earlier, msgfmt was silent + * for the null po file. So, just returns + * without generating an error message. + */ + (void) close(fd); + free(filename); + return; + } + addr = mmap(NULL, fsize, PROT_READ, MAP_SHARED, fd, 0); + if (addr == MAP_FAILED) { + error(gettext(ERR_MMAP_FAILED), filename); + /* NOTREACHED */ + } + (void) close(fd); + + if (!sun_p) + check_gnu(addr, fsize); + + mbuf = addr; + for (;;) { + if (linebufhead) { + free(linebufhead); + linebufhead = NULL; + } + ln_size = _mbsntowcs(&linebufhead, &mbuf, &fsize); + if (ln_size == (size_t)-1) { + error(gettext(ERR_READ_FAILED), filename); + /* NOTREACHED */ + } else if (ln_size == 0) { + break; /* End of File. */ + } + linenum++; + + linebufptr = linebufhead; + quotefound = 0; + + switch (*linebufptr) { + case L'#': /* comment */ + case L'\n': /* empty line */ + continue; + case L'\"': /* multiple lines of msgid and msgstr */ + quotefound = 1; + break; + } + + /* + * Process MSGID Tokens. + */ + token_found = (wcsncmp(MSGID_TOKEN, linebufptr, + MSGID_LEN) == 0) ? 1 : 0; + + if (token_found || (quotefound && inmsgid)) { + + if (token_found) { + if (!CK_NXT_CH(linebufptr, MSGID_LEN+1)) { + diag(gettext(ERR_NOSPC), linenum); + error(gettext(ERR_EXITING)); + /* NOTREACHED */ + } + } + + if (inmsgid && !quotefound) { + warning(gettext(WARN_NO_MSGSTR), msgid_linenum); + continue; + } + if (inmsgstr) { + sortit(gmsgid, gmsgstr); + (void) memset(gmsgid, 0, gmsgid_size); + (void) memset(gmsgstr, 0, gmsgstr_size); + } + + if (inmsgid) { + /* multiple lines of msgid */ + /* cancel the previous null termination */ + bufptr_index--; + } else { + /* + * The first line of msgid. + * Save linenum of msgid to be used when + * printing warning or error message. + */ + msgid_linenum = linenum; + p = linebufptr; + linebufptr = consume_whitespace( + linebufptr + MSGID_LEN); + ln_size -= linebufptr - p; + bufptr = gmsgid; + bufptr_index = 0; + } + + inmsgid = 1; + inmsgstr = 0; + indomain = 0; + goto load_buffer; + } + + /* + * Process MSGSTR Tokens. + */ + token_found = (wcsncmp(MSGSTR_TOKEN, linebufptr, + MSGSTR_LEN) == 0) ? 1 : 0; + if (token_found || (quotefound && inmsgstr)) { + + if (token_found) { + if (!CK_NXT_CH(linebufptr, MSGSTR_LEN+1)) { + diag(gettext(ERR_NOSPC), linenum); + error(gettext(ERR_EXITING)); + /* NOTREACHED */ + } + } + + + if (inmsgstr && !quotefound) { + warning(gettext(WARN_NO_MSGID), msgstr_linenum); + continue; + } + if (inmsgstr) { + /* multiple lines of msgstr */ + /* cancel the previous null termination */ + bufptr_index--; + } else { + /* + * The first line of msgstr. + * Save linenum of msgid to be used when + * printing warning or error message. + */ + msgstr_linenum = linenum; + p = linebufptr; + linebufptr = consume_whitespace( + linebufptr + MSGSTR_LEN); + ln_size -= linebufptr - p; + bufptr = gmsgstr; + bufptr_index = 0; + } + + inmsgstr = 1; + inmsgid = 0; + indomain = 0; + goto load_buffer; + } + + /* + * Process DOMAIN Tokens. + * Add message id and message string to sorted list + * if msgstr was processed last time. + */ + token_found = (wcsncmp(DOMAIN_TOKEN, linebufptr, + DOMAIN_LEN) == 0) ? 1 : 0; + if ((token_found) || (quotefound && indomain)) { + if (token_found) { + if (!CK_NXT_CH(linebufptr, DOMAIN_LEN+1)) { + diag(gettext(ERR_NOSPC), linenum); + error(gettext(ERR_EXITING)); + /* NOTREACHED */ + } + } + + + /* + * process msgid and msgstr pair for previous domain + */ + if (inmsgstr) { + sortit(gmsgid, gmsgstr); + } + + /* refresh msgid and msgstr buffer */ + if (inmsgstr || inmsgid) { + (void) memset(gmsgid, 0, gmsgid_size); + (void) memset(gmsgstr, 0, gmsgstr_size); + } + + if (indomain) { + /* multiple lines of domain */ + /* cancel the previous null termination */ + bufptr_index--; + } else { + p = linebufptr; + linebufptr = consume_whitespace( + linebufptr + DOMAIN_LEN); + (void) memset(gcurrent_domain, 0, + sizeof (gcurrent_domain)); + ln_size -= linebufptr - p; + bufptr = gcurrent_domain; + bufptr_index = 0; + } + + indomain = 1; + inmsgid = 0; + inmsgstr = 0; + } /* if */ + +load_buffer: + /* + * Now, fill up the buffer pointed by bufptr. + * At this point bufptr should point to one of + * msgid, msgptr, or current_domain. + * Otherwise, the entire line is ignored. + */ + + if (!bufptr) { + warning(gettext(WARN_SYNTAX_ERR), linenum); + continue; + } + + if (*linebufptr++ != L'\"') { + warning(gettext(WARN_MISSING_QUOTE), linenum); + --linebufptr; + } + quotefound = 0; + + /* + * If there is not enough space in the buffer, + * increase buffer by ln_size by realloc. + */ + ll = ln_size * mbcurmax; + if (bufptr == gmsgid) { + if (gmsgid_size < (bufptr_index + ll)) { + gmsgid = (char *)Xrealloc(gmsgid, + bufptr_index + ll); + bufptr = gmsgid; + gmsgid_size = bufptr_index + ll; + } + } else if (bufptr == gmsgstr) { + if (gmsgstr_size < (bufptr_index + ll)) { + gmsgstr = (char *)Xrealloc(gmsgstr, + bufptr_index + ll); + bufptr = gmsgstr; + gmsgstr_size = bufptr_index + ll; + } + } + + while (wc = *linebufptr++) { + switch (wc) { + case L'\n': + if (!quotefound) { +warning(gettext(WARN_MISSING_QUOTE_AT_EOL), linenum); + } + break; + + case L'\"': + quotefound = 1; + break; + + case L'\\': + if ((mb = expand_meta(&linebufptr)) != NULL) + bufptr[bufptr_index++] = mb; + break; + + default: + if ((n = wctomb(&bufptr[bufptr_index], wc)) > 0) + bufptr_index += n; + } /* switch */ + if (quotefound) { + /* + * Check if any remaining characters + * after closing quote. + */ + linebufptr = consume_whitespace(linebufptr); + if (*linebufptr != L'\n') { + warning(gettext(WARN_INVALID_STRING), + linenum); + } + break; + } + } /* while */ + + bufptr[bufptr_index++] = '\0'; + + (void) strcpy(msgfile, gcurrent_domain); + (void) strcat(msgfile, ".mo"); + } /* for(;;) */ + + if (inmsgstr) { + sortit(gmsgid, gmsgstr); + } + + if (linebufhead) + free(linebufhead); + if (munmap(addr, statbuf.st_size) == -1) { + error(gettext(ERR_MUNMAP_FAILED), filename); + /* NOTREACHED */ + } + + free(filename); + return; + +} /* read_psffm */ + + +/* + * Skip leading white spaces and tabs. + */ +static wchar_t * +consume_whitespace(wchar_t *buf) +{ + wchar_t *bufptr = buf; + wchar_t c; + + /* + * Skip leading white spaces. + */ + while ((c = *bufptr) != L'\0') { + if (c == L' ' || c == L'\t') { + bufptr++; + continue; + } + break; + } + return (bufptr); +} /* consume_white_space */ + + +/* + * handle escape sequences. + */ +static char +expand_meta(wchar_t **buf) +{ + wchar_t wc = **buf; + char n; + + switch (wc) { + case L'"': + (*buf)++; + return ('\"'); + case L'\\': + (*buf)++; + return ('\\'); + case L'b': + (*buf)++; + return ('\b'); + case L'f': + (*buf)++; + return ('\f'); + case L'n': + (*buf)++; + return ('\n'); + case L'r': + (*buf)++; + return ('\r'); + case L't': + (*buf)++; + return ('\t'); + case L'v': + (*buf)++; + return ('\v'); + case L'a': + (*buf)++; + return ('\a'); + case L'\'': + (*buf)++; + return ('\''); + case L'?': + (*buf)++; + return ('\?'); + case L'0': + case L'1': + case L'2': + case L'3': + case L'4': + case L'5': + case L'6': + case L'7': + /* + * This case handles \ddd where ddd is octal number. + * There could be one, two, or three octal numbers. + */ + (*buf)++; + n = (char)(wc - L'0'); + wc = **buf; + if (wc >= L'0' && wc <= L'7') { + (*buf)++; + n = 8*n + (char)(wc - L'0'); + wc = **buf; + if (wc >= L'0' && wc <= L'7') { + (*buf)++; + n = 8*n + (char)(wc - L'0'); + } + } + return (n); + default: + return (NULL); + } +} /* expand_meta */ + +/* + * Finds the head of the current domain linked list and + * call insert_message() to insert msgid and msgstr pair + * to the linked list. + */ +static void +sortit(char *msgid, char *msgstr) +{ + struct domain_struct *dom; + +#ifdef DEBUG + (void) fprintf(stderr, + "==> sortit(), domain=<%s> msgid=<%s> msgstr=<%s>\n", + gcurrent_domain, msgid, msgstr); +#endif + + /* + * If "-o filename" is specified, then all "domain" directive + * are ignored and, all messages will be stored in domain + * whose name is filename. + */ + if (oflag) { + dom = find_domain_node(outfile); + } else { + dom = find_domain_node(gcurrent_domain); + } + + insert_message(dom, msgid, msgstr); +} + +/* + * This routine inserts message in the current domain message list. + * It is inserted in ascending order. + */ +static void +insert_message(struct domain_struct *dom, + char *msgid, char *msgstr) +{ + struct msg_chain *p1; + struct msg_chain *node, *prev_node; + int b; + + /* + * Find the optimal starting search position. + * The starting search position is either the first node + * or the current_elem of domain. + * The current_elem is the pointer to the node which + * is most recently accessed in domain. + */ + if (dom->current_elem != NULL) { + b = strcmp(msgid, dom->current_elem->msgid); + if (b == 0) { + if (verbose) + warning(gettext(WARN_DUP_MSG), + msgid, msgid_linenum); + return; + } else if (b > 0) { /* to implement descending order */ + p1 = dom->first_elem; + } else { + p1 = dom->current_elem; + } + } else { + p1 = dom->first_elem; + } + + /* + * search msgid insert position in the list + * Search starts from the node pointed by p1. + */ + prev_node = NULL; + while (p1) { + b = strcmp(msgid, p1->msgid); + if (b == 0) { + if (verbose) + warning(gettext(WARN_DUP_MSG), + msgid, msgid_linenum); + return; + } else if (b < 0) { /* to implement descending order */ + /* move to the next node */ + prev_node = p1; + p1 = p1->next; + } else { + /* insert a new msg node */ + node = (struct msg_chain *) + Xmalloc(sizeof (struct msg_chain)); + node->next = p1; + node->msgid = Xstrdup(msgid); + node->msgstr = Xstrdup(msgstr); + + if (prev_node) { + prev_node->next = node; + } else { + dom->first_elem = node; + } + dom->current_elem = node; + return; + } + } /* while */ + + /* + * msgid is smaller than any of msgid in the list or + * list is empty. + * Therefore, append it. + */ + node = (struct msg_chain *) + Xmalloc(sizeof (struct msg_chain)); + node->next = NULL; + node->msgid = Xstrdup(msgid); + node->msgstr = Xstrdup(msgstr); + + if (prev_node) { + prev_node->next = node; + } else { + dom->first_elem = node; + } + dom->current_elem = node; + + return; + +} /* insert_message */ + + +/* + * This routine will find head of the linked list for the given + * domain_name. This looks up cache entry first and if cache misses, + * scans the list. + * If not found, then create a new node. + */ +static struct domain_struct * +find_domain_node(char *domain_name) +{ + struct domain_struct *p1; + struct domain_struct *node; + struct domain_struct *prev_node; + int b; + + + /* for perfomance, check cache 'last_used_domain' */ + if (last_used_domain) { + b = strcmp(domain_name, last_used_domain->domain); + if (b == 0) { + return (last_used_domain); + } else if (b < 0) { + p1 = first_domain; + } else { + p1 = last_used_domain; + } + } else { + p1 = first_domain; + } + + prev_node = NULL; + while (p1) { + b = strcmp(domain_name, p1->domain); + if (b == 0) { + /* node found */ + last_used_domain = p1; + return (p1); + } else if (b > 0) { + /* move to the next node */ + prev_node = p1; + p1 = p1->next; + } else { + /* insert a new domain node */ + node = (struct domain_struct *) + Xmalloc(sizeof (struct domain_struct)); + node->next = p1; + node->domain = Xstrdup(domain_name); + node->first_elem = NULL; + node->current_elem = NULL; + if (prev_node) { + /* insert the node in the middle */ + prev_node->next = node; + } else { + /* node inserted is the smallest */ + first_domain = node; + } + last_used_domain = node; + return (node); + } + } /* while */ + + /* + * domain_name is larger than any of domain name in the list or + * list is empty. + */ + node = (struct domain_struct *) + Xmalloc(sizeof (struct domain_struct)); + node->next = NULL; + node->domain = Xstrdup(domain_name); + node->first_elem = NULL; + node->current_elem = NULL; + if (prev_node) { + /* domain list is not empty */ + prev_node->next = node; + } else { + /* domain list is empty */ + first_domain = node; + } + last_used_domain = node; + + return (node); + +} /* find_domain_node */ + + +/* + * binary_compute() is used for pre-computing a binary search. + */ +static int +binary_compute(int i, int j, int *more, int *less) +{ + int k; + + if (i > j) { + return (LEAFINDICATOR); + } + k = (i + j) / 2; + + less[k] = binary_compute(i, k - 1, more, less); + more[k] = binary_compute(k + 1, j, more, less); + + return (k); + +} /* binary_compute */ + + +/* + * Write all domain data to file. + * Each domain will create one file. + */ +static void +output_all_mo_files(void) +{ + struct domain_struct *p; + + p = first_domain; + while (p) { + /* + * generate message object file only if there is + * at least one element. + */ + if (p->first_elem) { + output_one_mo_file(p); + } + p = p->next; + } + return; + +} /* output_all_mo_files */ + + +/* + * Write one domain data list to file. + */ +static void +output_one_mo_file(struct domain_struct *dom) +{ + FILE *fp; + struct msg_chain *p; + int message_count; + int string_count_msgid; + int string_count_msg; + int msgid_index = 0; + int msgstr_index = 0; + int *less, *more; + int i; + char fname [TEXTDOMAINMAX+1]; + + if (!dom || !dom->first_elem) + return; + + /* + * If -o flag is specified, then file name is used as domain name. + * If not, ".mo" is appended to the domain name. + */ + (void) strcpy(fname, dom->domain); + if (!oflag) { + (void) strcat(fname, ".mo"); + } + fp = fopen(fname, "w"); + if (fp == NULL) { + error(gettext(ERR_OPEN_FAILED), fname); + /* NOTREACHED */ + } + + /* compute offsets and counts */ + message_count = 0; + p = dom->first_elem; + while (p) { + p->msgid_offset = msgid_index; + p->msgstr_offset = msgstr_index; + msgid_index += strlen(p->msgid) + 1; + msgstr_index += strlen(p->msgstr) + 1; + message_count++; + p = p->next; + } + + /* + * Fill up less and more entries to be used for binary search. + */ + string_count_msgid = msgid_index; + string_count_msg = msgstr_index; + less = (int *)Xcalloc(message_count, sizeof (int)); + more = (int *)Xcalloc(message_count, sizeof (int)); + + (void) binary_compute(0, message_count - 1, more, less); + +#ifdef DEBUG + { + int i; + for (i = 0; i < message_count; i++) { + (void) fprintf(stderr, + " less[%2d]=%2d, more[%2d]=%2d\n", + i, less[i], i, more[i]); + } + } +#endif + + /* + * write out the message object file. + * The middle one is the first message to check by gettext(). + */ + i = (message_count - 1) / 2; + (void) fwrite(&i, sizeof (int), 1, fp); + (void) fwrite(&message_count, sizeof (int), 1, fp); + (void) fwrite(&string_count_msgid, sizeof (int), 1, fp); + (void) fwrite(&string_count_msg, sizeof (int), 1, fp); + i = MSG_STRUCT_SIZE * message_count; + (void) fwrite(&i, sizeof (int), 1, fp); + + /* march through linked list and write out all nodes. */ + i = 0; + p = dom->first_elem; + while (p) { /* put out message struct */ + (void) fwrite(&less[i], sizeof (int), 1, fp); + (void) fwrite(&more[i], sizeof (int), 1, fp); + (void) fwrite(&p->msgid_offset, sizeof (int), 1, fp); + (void) fwrite(&p->msgstr_offset, sizeof (int), 1, fp); + i++; + p = p->next; + } + + /* put out message id strings */ + p = dom->first_elem; + while (p) { + (void) fwrite(p->msgid, strlen(p->msgid)+1, 1, fp); + p = p->next; + } + + /* put out message strings */ + p = dom->first_elem; + while (p) { + (void) fwrite(p->msgstr, strlen(p->msgstr)+1, 1, fp); + p = p->next; + } + + (void) fclose(fp); + free(less); + free(more); + + return; + +} /* output_one_mo_file */ + + +/* + * read one line from *mbuf, + * skip preceding whitespaces, + * convert the line to wide characters, + * place the wide characters into *bufhead, and + * return the number of wide characters placed. + * + * INPUT: + * **bufhead - address of a variable that is the pointer + * to wchar_t. + * The variable should been initialized to NULL. + * **mbuf - address of a variable that is the pointer + * to char. + * The pointer should point to the memory mmapped to + * the file to input. + * **fsize - address of a size_t variable that contains + * the size of unread bytes in the file to input. + * OUTPUT: + * return - the number of wide characters placed. + * **bufhead - _mbsntowcs allocates the buffer to store + * one line in wchar_t from *mbuf and sets the address + * to *bufhead. + * **mbuf - _mbsntowcs reads one line from *mbuf and sets *mbuf + * to the beginning of the next line. + * **fsize - *fsize will be set to the size of the unread + * bytes in the file. + */ +static size_t +_mbsntowcs(wchar_t **bufhead, char **mbuf, size_t *fsize) +{ + wchar_t *tp, *th; + wchar_t wc; + size_t tbufsize = LINE_SIZE; + size_t ttbufsize, nc; + char *pc = *mbuf; + int nb; + + if (*fsize == 0) { + /* eof */ + return (0); + } + + th = (wchar_t *)Xmalloc(sizeof (wchar_t) * tbufsize); + nc = tbufsize; + + /* skip preceding whitespaces */ + while ((*pc != '\0')) { + if ((*pc == ' ') || (*pc == '\t')) { + pc++; + (*fsize)--; + } else { + break; + } + } + + tp = th; + while (*fsize > 0) { + nb = mbtowc(&wc, pc, mbcurmax); + if (nb == -1) { + return ((size_t)-1); + } + + if (*pc == '\n') { + /* found eol */ + if (nc <= 1) { + /* + * not enough buffer + * at least 2 more bytes are required for + * L'\n' and L'\0' + */ + ttbufsize = tbufsize + 2; + th = (wchar_t *)Xrealloc(th, + sizeof (wchar_t) * ttbufsize); + tp = th + tbufsize - nc; + tbufsize = ttbufsize; + } + *tp++ = L'\n'; + *tp++ = L'\0'; + pc += nb; + *fsize -= nb; + *mbuf = pc; + *bufhead = th; + return ((size_t)(tp - th)); + } + if (nc == 0) { + ttbufsize = tbufsize + LINE_SIZE; + th = (wchar_t *)Xrealloc(th, + sizeof (wchar_t) * ttbufsize); + tp = th + tbufsize; + nc = LINE_SIZE; + tbufsize = ttbufsize; + } + *tp++ = wc; + nc--; + pc += nb; + *fsize -= nb; + } /* while */ + + /* + * At this point, the input file has been consumed, + * but there is no ending '\n'; we add it to + * the output file. + */ + if (nc <= 1) { + /* + * not enough buffer + * at least 2 more bytes are required for + * L'\n' and L'\0' + */ + ttbufsize = tbufsize + 2; + th = (wchar_t *)Xrealloc(th, + sizeof (wchar_t) * ttbufsize); + tp = th + tbufsize - nc; + tbufsize = ttbufsize; + } + *tp++ = L'\n'; + *tp++ = L'\0'; + *mbuf = pc; + *bufhead = th; + return ((size_t)(tp - th)); +} + + +/* + * This is debug function. Not compiled in the final executable. + */ +#ifdef DEBUG +static void +printlist(void) +{ + struct domain_struct *p; + struct msg_chain *m; + + (void) fprintf(stderr, "\n=== Printing contents of all domains ===\n"); + p = first_domain; + while (p) { + (void) fprintf(stderr, "domain name = <%s>\n", p->domain); + m = p->first_elem; + while (m) { + (void) fprintf(stderr, " msgid=<%s>, msgstr=<%s>\n", + m->msgid, m->msgstr); + m = m->next; + } + p = p->next; + } +} /* printlist */ +#endif |