# # CDDL HEADER START # # The contents of this file are subject to the terms of the # Common Development and Distribution License (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 2006 Sun Microsystems, Inc. All rights reserved. # Use is subject to license terms. # # Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T # All Rights Reserved # # Copyright 2017 Joyent, Inc. # # cmd/msgfmt/Makefile include ../Makefile.cmd XOBJS= xgettext.o LXOBJS= xgettext.lx.o GOBJS= gnu_msgfmt.o gnu_handle.o gnu_lex.o gnu_hash.o gnu_check.o YOBJS= gnu_po.o BOBJS= gnu_msgs.o LOBJS= gnu_msgs_rev.o SOBJS= msgfmt.o check_header.o COBJS= option.o util.o BINPROG= msgfmt xgettext LIBPROG= gmsgfmt PROG= $(BINPROG) $(LIBPROG) YFLAGS += -d POFILE= msgfmt_all.po POFILES= $(SOBJS:%.o=%.po) $(GOBJS:%.o=%.po) $(COBJS:%.o=%.po) \ $(BOBJS:%.o=%.po) $(YOBJS:%.o=%.po) CLOBBERFILES += gmsgfmt_rev ROOTBINPROG= $(BINPROG:%=$(ROOTBIN)/%) ROOTLIBPROG= $(LIBPROG:%=$(ROOTLIB)/%) CERRWARN += -Wno-parentheses CERRWARN += $(CNOWARN_UNINIT) CERRWARN += -Wno-unused-variable # because of labels from yacc gnu_po.o : CERRWARN += -Wno-unused-label xgettext.lx.o : CERRWARN += -Wno-unused-label .KEEP_STATE: .PARALLEL: $(SOBJS) $(GOBJS) $(YOBJS) $(COBJS) $(BOBJS) $(LOBJS) \ $(XOBJS) $(LXOBJS) all: $(PROG) gmsgfmt_rev msgfmt: $(SOBJS) $(COBJS) $(LINK.c) $(SOBJS) $(COBJS) -o $@ $(LDLIBS) $(POST_PROCESS) gmsgfmt: $(GOBJS) $(YOBJS) $(BOBJS) $(COBJS) $(LINK.c) $(GOBJS) $(YOBJS) $(BOBJS) $(COBJS) -o $@ $(LDLIBS) $(POST_PROCESS) gmsgfmt_rev: $(GOBJS) $(YOBJS) $(LOBJS) $(COBJS) $(LINK.c) $(GOBJS) $(YOBJS) $(LOBJS) $(COBJS) -o $@ $(LDLIBS) $(POST_PROCESS) xgettext: $(XOBJS) $(LXOBJS) $(LINK.c) $(XOBJS) $(LXOBJS) -o $@ $(LDLIBS) $(POST_PROCESS) $(POFILES): y.tab.h $(POFILE): $(POFILES) $(RM) $@ cat $(POFILES) > $@ install: all $(ROOTBINPROG) $(ROOTLIBPROG) clean: $(RM) $(SOBJS) $(GOBJS) $(YOBJS) $(COBJS) \ $(XOBJS) $(LXOBJS) $(BOBJS) $(LOBJS) \ $(POFILE) $(POFILES) gnu_po.c y.tab.h xgettext.lx.c # Hammerhead: GNU Make grouped target (was dmake +) gnu_po.c y.tab.h &: gnu_po.y $(RM) gnu_po.c y.tab.h $(YACC.y) gnu_po.y mv y.tab.c gnu_po.c gnu_lex.o: y.tab.h include ../Makefile.targ /* * 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 (c) 2001 by Sun Microsystems, Inc. * All rights reserved. */ #include "sun_msgfmt.h" static const char *mandatory_fields[] = { "Project-Id-Version", "PO-Revision-Date", "Last-Translator", "Language-Team", "Content-Type", "Content-Transfer-Encoding", NULL }; static const char *mandatory_fields_new[] = { "POT-Creation-Date", "Plural-Forms", NULL }; extern int verbose; extern void invoke_gnu_msgfmt(void); static size_t get_one_line(char **bufhead, char **mbuf, size_t *fsize) { size_t len; char *p = *mbuf; char *q, *tmp; if (*bufhead) { free(*bufhead); *bufhead = NULL; } if (*fsize == 0) { /* eof */ return (0); } q = p; while (((*fsize) != 0) && (*p++ != '\n')) { (*fsize)--; } len = p - q; if (len == 0) { return (0); } tmp = (char *)Xmalloc(len + 1); (void) memcpy(tmp, q, len); tmp[len] = '\0'; *bufhead = tmp; *mbuf = p; return (len); } void check_gnu(char *addr, size_t fsize) { int i; char c, mc; char *linebuf; char *mbuf, *p, *buf; unsigned int n; size_t ln_size; size_t bufsize, index; size_t size = fsize; int quotefound = 0; const char *field; buf = NULL; linebuf = NULL; mbuf = addr; loop: ln_size = get_one_line(&linebuf, &mbuf, &size); if ((ln_size == (size_t)-1) || (ln_size == 0)) { goto no_gnu; } p = linebuf; while ((*p == '#') || (*p == '\n')) { ln_size = get_one_line(&linebuf, &mbuf, &size); if ((ln_size == (size_t)-1) || (ln_size == 0)) { goto no_gnu; } p = linebuf; } if (strncmp(p, "domain", 6) == 0) goto loop; if (strncmp(p, "msgid", 5) != 0) { /* error */ goto no_gnu; } p += 5; if ((*p != ' ') && (*p != '\t') && (*p != '\n') && (*p != '\0')) { /* no space after msgid */ goto no_gnu; } /* skip spaces */ while ((*p == ' ') || (*p == '\t')) p++; /* check if this entry is an empty string */ if ((*p != '\"') || (*(p + 1) != '\"')) { /* this is not an empty string */ goto no_gnu; } p += 2; while (*p && ((*p == ' ') || (*p == '\t'))) { p++; } if ((*p != '\n') && (*p != '\0')) { /* other characters than '\n' and '\0' found */ goto no_gnu; } for (; ; ) { ln_size = get_one_line(&linebuf, &mbuf, &size); if ((ln_size == (size_t)-1) || (ln_size == 0)) { goto no_gnu; } p = linebuf; /* skip leading spaces */ while ((*p == ' ') || (*p == '\t')) p++; if (*p != '\"') { if (strncmp(p, "msgstr", 6) == 0) { break; } /* not a valid entry */ goto no_gnu; } if (*(p + 1) != '\"') { /* not an empty string */ goto no_gnu; } p += 2; while ((*p == ' ') || (*p == '\t')) p++; if ((*p != '\n') && (*p != '\0')) { /* other characters than '\n' and '\0' found */ goto no_gnu; } } /* * msgid for the header entry found * Now p points to "msgstr" */ p += 6; if ((*p != ' ') && (*p != '\t') && (*p != '\n') && (*p != '\0')) { /* no space after msgid */ goto no_gnu; } /* skip spaces */ while ((*p == ' ') || (*p == '\t')) p++; if (*p != '\"') { /* no quote */ goto no_gnu; } bufsize = ln_size + 1; index = 0; buf = (char *)Xmalloc(bufsize); for (; ; ) { if (*p != '\"') { /* msgstr entry ends */ buf[index] = '\0'; break; } if (*p++ != '\"') { /* no beginning quote */ goto no_gnu; } while (*p) { switch (mc = *p++) { case '\n': if (!quotefound) { /* error */ goto no_gnu; } break; case '\"': quotefound = 1; break; case '\\': if (!*p) break; switch (c = *p++) { case 'b': buf[index++] = '\b'; break; case 'f': buf[index++] = '\f'; break; case 'n': buf[index++] = '\n'; break; case 'r': buf[index++] = '\r'; break; case 't': buf[index++] = '\t'; break; case 'v': buf[index++] = '\v'; break; case 'a': buf[index++] = '\a'; break; case '\"': case '\\': case '\'': case '?': buf[index++] = c; break; default: if (isdigit((unsigned char)c)) { unsigned int x; unsigned char *up = (unsigned char *)p; n = c - '0'; if (isdigit(*up)) { x = *up++ - '0'; n = 8 * n + x; if (isdigit(*up)) { x = *up++ - '0'; n = 8 * n + x; } } p = (char *)up; buf[index++] = n; } break; } break; default: buf[index++] = mc; break; } if (quotefound) { while (*p && ((*p == ' ') || (*p == '\t'))) { p++; } if ((*p != '\n') && (*p != '\0')) { goto no_gnu; } quotefound = 0; break; } } ln_size = get_one_line(&linebuf, &mbuf, &size); if ((ln_size == (size_t)-1) || (ln_size == 0)) { goto no_gnu; } p = linebuf; /* skip spaces */ while ((*p == ' ') || (*p == '\t')) p++; bufsize += ln_size; buf = (char *)Xrealloc(buf, bufsize); } for (i = 0; (field = mandatory_fields[i]) != NULL; i++) { if (strstr(buf, field) == NULL) continue; /* one of mandatory fields found */ free(linebuf); free(buf); (void) munmap(addr, fsize); if (verbose) diag(gettext(DIAG_GNU_FOUND)); invoke_gnu_msgfmt(); /* NOTREACHED */ } for (i = 0; (field = mandatory_fields_new[i]) != NULL; i++) { if (strstr(buf, field) == NULL) continue; /* one of mandatory fields found */ free(linebuf); free(buf); (void) munmap(addr, fsize); if (verbose) diag(gettext(DIAG_GNU_FOUND)); invoke_gnu_msgfmt(); /* NOTREACHED */ } no_gnu: free(linebuf); if (buf) free(buf); } /* * 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 (c) 2001 by Sun Microsystems, Inc. * All rights reserved. */ #ifndef _COMMON_H #define _COMMON_H #include #include #include #include #include #ifdef __cplusplus extern "C" { #endif /* implementation dependent default domain name */ #define DEFAULT_DOMAIN "messages" #define DEFAULT_DOMAIN_MO "messages.mo" #define ERR_ERROR \ "ERROR: " #define WARN_WARNING \ "WARNING: " #define ERR_MALLOC \ "failed to allocate memory\n" #define ERR_OPEN_FAILED \ "Cannot open file %s.\n" #define ERR_READ_FAILED \ "Error in reading %s.\n" #define DIAG_START_PROC \ "Processing file \"%s\"...\n" struct flags { char *idir; char *ofile; int fuzzy; int verbose; int strict; int gnu_p; int sun_p; }; extern int parse_option(int *, char ***, struct flags *); extern void error(char *, ...) __NORETURN; extern void warning(char *, ...); extern void diag(char *, ...); extern void *Xmalloc(size_t); extern void *Xcalloc(size_t, size_t); extern void *Xrealloc(void *, size_t); extern char *Xstrdup(const char *); #ifdef __cplusplus } #endif #endif /* _COMMON_H */ /* * 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 2003 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include "gnu_msgfmt.h" #define OPT_L 0x01 #define OPT_l 0x02 #define OPT_ll 0x04 #define OPT_w 0x08 #define OPT_h 0x10 #define OPT_hh 0x20 #define OPT_j 0x40 static int extract_format(char *norm, const char *sfmt, size_t sz) { const unsigned char *fmt = (const unsigned char *)sfmt; unsigned char c; int t, arg, ap; int dotseen; char flag, conv; int lastarg = -1; int prevarg; int max = 0; int lflag; for (; *fmt; fmt++) { if (*fmt == '%') { if (*++fmt == '%') continue; if (!*fmt) break; prevarg = lastarg; arg = ++lastarg; t = 0; while (*fmt && isdigit(*fmt)) t = t * 10 + *fmt++ - '0'; if (*fmt == '$') { lastarg = arg = t - 1; fmt++; } if (!*fmt) goto end; dotseen = 0; flag = 0; lflag = 0; again: /* Skip flags */ while ((c = *fmt) != '\0') { if (c == '\'' || c == '+' || c == '-' || c == ' ' || c == '#' || c == '0') { fmt++; continue; } break; } while (*fmt && isdigit(*fmt)) fmt++; if (*fmt == '*') { if (isdigit(*(fmt + 1))) { fmt++; t = 0; while (*fmt && isdigit(*fmt)) t = t * 10 + *fmt++ - '0'; if (*fmt == '$') { /* * %*4$ */ ap = t - 1; if ((ap * 2 + 1 >= sz) || (norm[ap * 2] && norm[ap * 2] != '*')) { /* error in format */ return (-1); } else { if (ap >= max) max = ap + 1; norm[ap * 2] = '*'; } } /* * If digits follow a '*', it is * not loaded as an argument, the * digits are used instead. */ } else { /* * %* */ if (*(fmt + 1) == '$') { fmt++; } else { ap = arg; prevarg = arg; lastarg = ++arg; if ((ap * 2 + 1 >= sz) || (norm[ap * 2] && norm[ap * 2] != '*')) { /* error in format */ return (-1); } else { if (ap >= max) max = ap + 1; norm[ap * 2] = '*'; } } } fmt++; } if ((*fmt == '.') || (*fmt == '*')) { if (dotseen) return (-1); dotseen = 1; fmt++; goto again; } if (!*fmt) goto end; while (*fmt) { switch (*fmt) { case 'l': if (!(flag & OPT_ll)) { if (lflag) { flag &= ~OPT_l; flag |= OPT_ll; } else { flag |= OPT_l; } } lflag++; break; case 'L': flag |= OPT_L; break; case 'w': flag |= OPT_w; break; case 'h': if (flag & (OPT_h|OPT_hh)) flag |= OPT_hh; else flag |= OPT_h; break; case 'j': flag |= OPT_j; break; case 'z': case 't': if (!(flag & OPT_ll)) { flag |= OPT_l; } break; case '\'': case '+': case '-': case ' ': case '#': case '.': case '*': goto again; default: if (isdigit(*fmt)) goto again; else goto done; } fmt++; } done: if (!*fmt) goto end; if ((c = *fmt) == 'C') { flag |= OPT_l; conv = 'c'; } else if (c == 'd') { conv = 'd'; } else if (c == 'S') { flag |= OPT_l; conv = 's'; } else if (c == 's') { conv = 's'; } else if (c == 'i') { conv = 'i'; } else if (c == 'o') { conv = 'o'; } else if (c == 'u') { conv = 'u'; } else if (c == 'c') { conv = 'c'; } else if (c == 'x') { conv = 'x'; } else if (c == 'X') { conv = 'X'; } else if (c == 'e') { conv = 'e'; } else if (c == 'E') { conv = 'E'; } else if (c == 'f') { conv = 'f'; } else if (c == 'F') { conv = 'F'; } else if (c == 'a') { conv = 'a'; } else if (c == 'A') { conv = 'A'; } else if (c == 'g') { conv = 'g'; } else if (c == 'G') { conv = 'G'; } else if (c == 'p') { conv = 'p'; } else if (c == 'n') { conv = 'n'; } else { lastarg = prevarg; continue; } if ((arg * 2 + 1 >= sz) || (norm[arg * 2] && (norm[arg * 2] != conv))) { return (-1); } else { if (arg >= max) max = arg + 1; norm[arg * 2] = conv; } norm[arg * 2 + 1] = flag; } } end: for (arg = 0; arg < max; arg++) { if (norm[arg * 2] == '\0') return (-1); } return (max); } void check_format(struct entry *id, struct entry *str, int is_c_format) { int i, n; int id_b_newline, id_e_newline; int plural_b_newline, plural_e_newline; int str_b_newline, str_e_newline; int id_fmt, plural_fmt, str_fmt; int *pstr_fmt; char *msgid, *plural, *msgstr; char *id_norm, *plural_norm, *str_norm; char **pstr_norm; size_t id_len, id_num; size_t plural_off, plural_len, plural_num; size_t str_len, str_num; size_t osz, nsz; struct loc *p; if (id->len == 1) { /* * null string: header entry * no check is performed */ return; } msgid = id->str; id_num = id->num; msgstr = str->str; if (id->no > 1) { /* plural */ id_len = id->pos[0].len; plural_off = id->pos[1].off; plural_len = id->pos[1].len; plural_num = id->pos[1].num; plural = msgid + plural_off; } else { /* no plural form */ id_len = id->len; str_len = str->len; str_num = str->num; plural = NULL; } /* * First checking the newline */ if (!plural) { /* no plural form */ id_b_newline = (msgid[0] == '\n'); id_e_newline = (msgid[id_len - 1 - 1] == '\n'); str_b_newline = (msgstr[0] == '\n'); str_e_newline = (msgstr[str_len - 1 - 1] == '\n'); if (id_b_newline && !str_b_newline) { diag(gettext(ERR_BEGIN_NEWLINE_1), id_num, str_num, cur_po); po_error++; } else if (!id_b_newline && str_b_newline) { diag(gettext(ERR_BEGIN_NEWLINE_2), id_num, str_num, cur_po); po_error++; } if (id_e_newline && !str_e_newline) { diag(gettext(ERR_END_NEWLINE_1), id_num, str_num, cur_po); po_error++; } else if (!id_e_newline && str_e_newline) { diag(gettext(ERR_END_NEWLINE_2), id_num, str_num, cur_po); po_error++; } } else { /* plural form */ id_b_newline = (msgid[0] == '\n'); id_e_newline = (msgid[id_len - 1 - 1] == '\n'); plural_b_newline = (plural[0] == '\n'); plural_e_newline = (plural[plural_len - 1 -1 ] == '\n'); /* between msgid and msgid_plural */ if (id_b_newline && !plural_b_newline) { diag(gettext(ERR_BEGIN_NEWLINE_3), id_num, plural_num, cur_po); po_error++; } else if (!id_b_newline && plural_b_newline) { diag(gettext(ERR_BEGIN_NEWLINE_4), id_num, plural_num, cur_po); po_error++; } if (id_e_newline && !plural_e_newline) { diag(gettext(ERR_END_NEWLINE_3), id_num, plural_num, cur_po); po_error++; } else if (!id_e_newline && plural_e_newline) { diag(gettext(ERR_END_NEWLINE_4), id_num, plural_num, cur_po); po_error++; } for (i = 0; i < str->no; i++) { p = str->pos + i; str_b_newline = (msgstr[p->off] == '\n'); str_e_newline = (msgstr[p->off + p->len - 1 - 1] == '\n'); if (id_b_newline && !str_b_newline) { diag(gettext(ERR_BEGIN_NEWLINE_5), id_num, p->num, cur_po, i); po_error++; } else if (!id_b_newline && str_b_newline) { diag(gettext(ERR_BEGIN_NEWLINE_6), id_num, p->num, cur_po, i); po_error++; } if (id_e_newline && !str_e_newline) { diag(gettext(ERR_END_NEWLINE_5), id_num, p->num, cur_po, i); po_error++; } else if (!id_e_newline && str_e_newline) { diag(gettext(ERR_END_NEWLINE_6), id_num, p->num, cur_po, i); po_error++; } } } /* * if c-format is not specified, no printf-format check * is performed. */ if (!is_c_format) { return; } osz = id_len * 2; id_norm = (char *)Xcalloc(1, osz); id_fmt = extract_format(id_norm, msgid, osz); if (id_fmt == -1) { diag(gettext(ERR_INVALID_FMT), id_num, cur_po); po_error++; } if (!plural) { /* no plural */ nsz = str_len * 2; str_norm = (char *)Xcalloc(1, nsz); str_fmt = extract_format(str_norm, msgstr, nsz); if (str_fmt == -1) { diag(gettext(ERR_INVALID_FMT), str_num, cur_po); po_error++; } if (id_fmt != str_fmt) { diag(gettext(ERR_INCMP_FMT), id_num, str_num, cur_po); diag(gettext(ERR_INCMP_FMT_DIFF_1), id_fmt, str_fmt); po_error++; } else { for (n = 0; n < id_fmt; n++) { if ((id_norm[n * 2] != str_norm[n * 2]) || (id_norm[n * 2 + 1] != str_norm[n * 2 + 1])) { diag(gettext(ERR_INCMP_FMT), id_num, str_num, cur_po); diag(gettext(ERR_INCMP_FMT_DIFF_2), n + 1); po_error++; } } } free(str_norm); free(id_norm); return; } /* plural */ nsz = plural_len * 2; plural_norm = (char *)Xcalloc(1, nsz); plural_fmt = extract_format(plural_norm, plural, nsz); if (plural_fmt == -1) { diag(gettext(ERR_INVALID_FMT), plural_num, cur_po); po_error++; } pstr_norm = (char **)Xcalloc(str->no, sizeof (char *)); pstr_fmt = (int *)Xcalloc(str->no, sizeof (int)); for (i = 0; i < str->no; i++) { p = str->pos + i; nsz = p->len * 2; pstr_norm[i] = (char *)Xcalloc(1, nsz); pstr_fmt[i] = extract_format(pstr_norm[i], msgstr + p->off, nsz); if (pstr_fmt[i] == -1) { diag(gettext(ERR_INVALID_FMT), p->num, cur_po); po_error++; } } /* between msgid and msgid_plural */ if (id_fmt != plural_fmt) { diag(gettext(ERR_INCMP_FMT), id_num, plural_num, cur_po); diag(gettext(ERR_INCMP_FMT_DIFF_1), id_fmt, plural_fmt); po_error++; } else { for (n = 0; n < id_fmt; n++) { if ((id_norm[n * 2] != plural_norm[n * 2]) || (id_norm[n * 2 + 1] != plural_norm[n * 2 + 1])) { diag(gettext(ERR_INCMP_FMT), id_num, plural_num, cur_po); diag(gettext(ERR_INCMP_FMT_DIFF_2), n + 1); po_error++; } } } free(plural_norm); /* between msgid and msgstr */ for (i = 0; i < str->no; i++) { p = str->pos + i; if (id_fmt != pstr_fmt[i]) { diag(gettext(ERR_INCMP_FMT), id_num, p->num, cur_po); diag(gettext(ERR_INCMP_FMT_DIFF_1), id_fmt, pstr_fmt[i]); po_error++; } else { for (n = 0; n < id_fmt; n++) { if ((id_norm[n * 2] != pstr_norm[i][n * 2]) || (id_norm[n * 2 + 1] != pstr_norm[i][n * 2 + 1])) { diag(gettext(ERR_INCMP_FMT), id_num, p->num, cur_po); diag(gettext(ERR_INCMP_FMT_DIFF_2), n + 1); po_error++; } } } free(pstr_norm[i]); } free(pstr_norm); free(pstr_fmt); free(id_norm); } /* * 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 (c) 2001 by Sun Microsystems, Inc. * All rights reserved. */ #ifndef _ERRMSG_H #define _ERRMSG_H #ifdef __cplusplus extern "C" { #endif #define ERR_USAGE \ "Usage: %s [-D dir | --directory=dir] [-f | --use-fuzzy]\n" \ " [-g] [-o outfile | --output-file=outfile]\n" \ " [--strict] [-v | --verbose] files ...\n" #define ERR_SUN_ON_GNU \ "-s option cannot be specified to %s.\n" #define ERR_PRIME \ "Internal error: no prime number under 1100 found for %d.\n" #define ERR_ERROR_FOUND \ "%d error(s) found.\n" #define ERR_INVALID_CHAR \ "Line %d (%s): Invalid character found.\n" #define ERR_INTERNAL \ "Line %d (%s): Internal error.\n" #define ERR_LOCATION \ "Line %d (%s): " #define ERR_NO_MSGSTR \ "Line %d (%s): missing \"msgstr\" corresponding to \"msgid\".\n" #define ERR_NO_MSGSTRS \ "Line %d (%s): missing \"msgstr[]\" corresponding to \"msgid\".\n" #define ERR_NO_MSGID_PLURAL \ "Line %d (%s): missing \"msgid_plural\" corresponding to \"msgid\".\n" #define ERR_INVALID_PLURALS \ "Line %d (%s): invalid index for \"msgstr[]\".\n" #define ERR_UNEXP_EOF \ "Line %d (%s): unexpected EOF found.\n" #define ERR_UNEXP_EOL \ "Line %d (%s): unexpected EOL found.\n" #define ERR_DUP_ENTRIES \ "Lines %d (%s), %d (%s): " \ "duplicate \"msgid\" entries found.\n" #define ERR_BEGIN_NEWLINE_1 \ "Lines %d, %d (%s): \"msgid\" begins with newline, " \ "but \"msgstr\" doesn't.\n" #define ERR_BEGIN_NEWLINE_2 \ "Lines %d, %d (%s): \"msgstr\" begins with newline, " \ "but \"msgid\" doesn't.\n" #define ERR_BEGIN_NEWLINE_3 \ "Lines %d, %d (%s): \"msgid\" begins with newline, " \ "but \"msgid_plural\" doesn't.\n" #define ERR_BEGIN_NEWLINE_4 \ "Lines %d, %d (%s): \"msgid_plural\" begins with newline, " \ "but \"msgid\" doesn't.\n" #define ERR_BEGIN_NEWLINE_5 \ "Lines %d, %d (%s): \"msgid\" begins with newline, " \ "but \"msgidstr[%d]\" doesn't.\n" #define ERR_BEGIN_NEWLINE_6 \ "Lines %d, %d (%s): \"msgstr[%d]\" begins with newline, " \ "but \"msgid\" doesn't.\n" #define ERR_END_NEWLINE_1 \ "Lines %d, %d (%s): \"msgid\" ends with newline, " \ "but \"msgstr\" doesn't.\n" #define ERR_END_NEWLINE_2 \ "Lines %d, %d (%s): \"msgstr\" ends with newline, " \ "but \"msgid\" doesn't.\n" #define ERR_END_NEWLINE_3 \ "Lines %d, %d (%s): \"msgid\" ends with newline, " \ "but \"msgid_plural\" doesn't.\n" #define ERR_END_NEWLINE_4 \ "Lines %d, %d (%s): \"msgid_plural\" ends with newline, " \ "but \"msgid\" doesn't.\n" #define ERR_END_NEWLINE_5 \ "Lines %d, %d (%s): \"msgid\" ends with newline, " \ "but \"msgstr[%d]\" doesn't.\n" #define ERR_END_NEWLINE_6 \ "Lines %d, %d (%s): \"msgstr[%d]\" ends with newline, " \ "but \"msgid\" doesn't.\n" #define ERR_INVALID_FMT \ "Lines %d (%s): invalid printf-format.\n" #define ERR_INCMP_FMT \ "Lines %d, %d (%s): incompatible printf-format.\n" #define ERR_INCMP_FMT_DIFF_1 \ " %d format specifier(s) in \"msgid\", but " \ "%d format specifier(s) in \"msgstr\".\n" #define ERR_INCMP_FMT_DIFF_2 \ " format specifier mismatch in the argument (#%d).\n" #define WARN_NOCHARSET \ "Line %d (%s): charset specification is missing " \ "in the header entry.\n" \ "Using the default charset.\n" #define WARN_NOCONV \ "Line %d (%s): No iconv conversion from \"%s\" to \"%s\" is " \ "supported.\n" \ "Using the default charset.\n" #define WARN_DUP_ENTRIES \ "Lines %d (%s), %d (%s): " \ "duplicate \"msgid\" and \"msgstr\" entries found.\n" #define DIAG_IGNORE_DOMAIN \ "Line %d (%s): `domain %s' directive ignored.\n" #define DIAG_RESULTS \ "%d translated message(s), %d fuzzy translation(s), " \ "%d untranslated message(s).\n" #ifdef __cplusplus } #endif #endif /* _ERRMSG_H */ /* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (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 2006 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include "gnu_msgfmt.h" static int next_entry_is_fuzzy = 0; static int next_entry_is_c_format = 0; static struct catalog *cur_catalog = NULL; static char *cur_mo = NULL; FILE *fp; iconv_t cd = (iconv_t)-1; struct catalog *catalog_head = NULL; int cur_po_index = 0; static size_t search_alias(char **paddr, size_t size, const char *variant) { char *addr = *paddr; char *p, *sp, *q; size_t var_len, can_len; var_len = strlen(variant); p = addr; q = addr + size; while (q > p) { if (*p == '#') { /* * Line beginning with '#' is a comment */ p++; while ((q > p) && (*p++ != '\n')) ; continue; } /* skip leading spaces */ while ((q > p) && ((*p == ' ') || (*p == '\t'))) p++; if (q <= p) break; sp = p; while ((q > p) && (*p != ' ') && (*p != '\t') && (*p != '\n')) p++; if (q <= p) { /* invalid entry */ break; } if (*p == '\n') { /* invalid entry */ p++; continue; } if (((p - sp) != var_len) || ((strncmp(sp, variant, var_len) != 0) && (strncasecmp(sp, variant, var_len) != 0))) { /* * didn't match */ /* skip remaining chars in this line */ p++; while ((q > p) && (*p++ != '\n')) ; continue; } /* matching entry found */ /* skip spaces */ while ((q > p) && ((*p == ' ') || (*p == '\t'))) p++; if (q <= p) break; sp = p; while ((q > p) && (*p != ' ') && (*p != '\t') && (*p != '\n')) p++; can_len = p - sp; if (can_len == 0) { while ((q > p) && (*p++ != '\n')) ; continue; } *paddr = sp; return (can_len); } return (0); } /* * Checks if the specified charset is equivalent to UTF-8. * If it's equivalent to UTF-8, returns 1; Otherwise, returns 0. */ static int check_utf8(const char *charset) { int fd; struct stat64 statbuf; caddr_t addr; size_t buflen, charset_len, utf8_len; char *c_charset, *c_utf8, *p; if (strcmp(charset, DEST_CHARSET) == 0) return (1); fd = open(_ENCODING_ALIAS_PATH, O_RDONLY); if (fd == -1) { /* no alias file found */ return (0); } if (fstat64(fd, &statbuf) == -1) { (void) close(fd); return (0); } buflen = (size_t)statbuf.st_size; addr = mmap(NULL, buflen, PROT_READ, MAP_SHARED, fd, 0); (void) close(fd); if (addr == MAP_FAILED) { warning("mmap() for %s failed.", _ENCODING_ALIAS_PATH); return (0); } p = (char *)addr; charset_len = search_alias(&p, buflen, charset); if (charset_len) { c_charset = alloca(charset_len + 1); (void) memcpy(c_charset, p, charset_len); c_charset[charset_len] = '\0'; } else { c_charset = (char *)charset; } p = (char *)addr; utf8_len = search_alias(&p, buflen, DEST_CHARSET); if (utf8_len) { c_utf8 = alloca(utf8_len + 1); (void) memcpy(c_utf8, p, utf8_len); c_utf8[utf8_len] = '\0'; } else { c_utf8 = DEST_CHARSET; } (void) munmap(addr, buflen); if (charset_len == 0 && utf8_len == 0) { /* * Entry for neither charset nor utf8 found */ return (0); } if (strcmp(c_charset, c_utf8) == 0) return (1); else return (0); } static void conv_init(const char *charset) { if (charset == NULL) { /* * No conversion */ cd = (iconv_t)-1; return; } if (check_utf8(charset)) { /* * Charset is UTF-8. * No conversion is required. */ cd = (iconv_t)-1; return; } cd = iconv_open(DEST_CHARSET, charset); if (cd == (iconv_t)-1) { /* * No such a conversion */ warning(gettext(WARN_NOCONV), cur_line, cur_po, charset, DEST_CHARSET); return; } } void clear_state(void) { next_entry_is_fuzzy = 0; next_entry_is_c_format = 0; } void handle_domain(char *domainname) { if (outfile) { /* * outfile has been specified by -o option * ignore all domain directives */ if (verbose_flag) { diag(gettext(DIAG_IGNORE_DOMAIN), cur_line, cur_po, domainname); } free(domainname); return; } if (strict_flag) { /* * add ".mo" to the domain */ char *tmp; tmp = Xrealloc(domainname, strlen(domainname) + 3 + 1); (void) strcat(tmp, ".mo"); domainname = tmp; } catalog_init(domainname); free(domainname); } void catalog_init(const char *filename) { struct catalog *p; if (!catalog_head) { p = Xcalloc(1, sizeof (struct catalog)); p->fname = Xstrdup(filename); p->msg_size = DEF_MSG_NUM; p->nmsg = 0; p->msg = Xcalloc(p->msg_size, sizeof (struct messages)); p->thash_size = find_prime(DEF_MSG_NUM); p->thash = Xcalloc(p->thash_size, sizeof (unsigned int)); catalog_head = p; } else { p = catalog_head; for (; ; ) { struct catalog *tmp; if (strcmp(p->fname, filename) == 0) { /* already registered */ break; } if (p->next) { p = p->next; continue; } /* * this domain hasn't been registered */ tmp = Xcalloc(1, sizeof (struct catalog)); tmp->fname = Xstrdup(filename); tmp->msg_size = DEF_MSG_NUM; tmp->nmsg = 0; tmp->msg = Xcalloc(tmp->msg_size, sizeof (struct messages)); tmp->thash_size = find_prime(DEF_MSG_NUM); tmp->thash = Xcalloc(tmp->thash_size, sizeof (unsigned int)); p->next = tmp; p = tmp; break; } } cur_catalog = p; cur_mo = p->fname; } void handle_comment(char *comment) { char *p; p = comment; if (*p != ',') { /* * This comment is just informative only. */ free(comment); return; } /* * Checks "fuzzy", "c-format", and "no-c-format" */ p++; if (strstr(p, "fuzzy") != NULL) { next_entry_is_fuzzy = 1; } if (strstr(p, "no-c-format") != NULL) { next_entry_is_c_format = 0; } else if (strstr(p, "c-format") != NULL) { next_entry_is_c_format = 1; } free(comment); } void handle_message(struct entry *id, struct entry *str) { char *charset, *nplurals, *tmp, *p; struct messages *msg, *dupmsg; size_t len; unsigned int hash_val; unsigned int nmsg, n, thash_idx; if (cur_mo == NULL) { /* * output file hasn't been specified, nor * no domain directive found */ char *default_domain; default_domain = strict_flag ? DEFAULT_DOMAIN_MO : DEFAULT_DOMAIN; catalog_init(default_domain); } /* * cur_catalog should be valid, at this point */ hash_val = hashpjw(id->str); dupmsg = search_msg(cur_catalog, id->str, hash_val); if (dupmsg) { if ((dupmsg->str_len == str->len) && (memcmp(dupmsg->str, str->str, str->len) == 0)) { /* totally same entry */ if (verbose_flag) { warning(gettext(WARN_DUP_ENTRIES), dupmsg->num, po_names[dupmsg->po], id->num, cur_po); } free(id->str); if (id->pos) free(id->pos); free(str->str); if (str->pos) free(str->pos); return; } /* duplicate msgid */ if (verbose_flag) { diag(gettext(ERR_DUP_ENTRIES), dupmsg->num, po_names[dupmsg->po], id->num, cur_po); po_error++; } /* ignore this etnry */ free(id->str); if (id->pos) free(id->pos); free(str->str); if (str->pos) free(str->pos); return; } if (next_entry_is_fuzzy) { /* fuzzy entry */ cur_catalog->fnum++; if (!fuzzy_flag) { /* ignore this entry */ free(id->str); if (id->pos) free(id->pos); free(str->str); if (str->pos) free(str->pos); return; } } if (str->len == str->no) { /* this entry is not translated */ cur_catalog->unum++; free(id->str); if (id->pos) free(id->pos); free(str->str); if (str->pos) free(str->pos); return; } /* Checks if this is the header entry */ if ((id->no == 1) && (id->len == 1)) { /* * Header entry */ cur_catalog->header++; /* * Need to extract the charset information */ charset = strstr(str->str, CHARSET_STR); if (charset == NULL) { /* no charset information */ warning(gettext(WARN_NOCHARSET), id->num, cur_po, str->num); conv_init(NULL); } else { charset += CHARSET_LEN; p = strpbrk(charset, " \t\n"); if (p != NULL) { /* p points to a space, tab or new line char */ len = p - charset; } else { /* not found */ len = strlen(charset); } tmp = Xmalloc(len + 1); (void) memcpy(tmp, charset, len); *(tmp + len) = '\0'; charset = tmp; conv_init(charset); free(charset); } nplurals = strstr(str->str, NPLURALS_STR); if (nplurals == NULL) { cur_catalog->nplurals = 0; } else { unsigned int num; nplurals += NPLURALS_LEN; p = nplurals; num = 0; while (isdigit((unsigned char)*p)) { num = num * 10 + *p++ - '0'; } cur_catalog->nplurals = num; } } if (verbose_flag) check_format(id, str, next_entry_is_c_format); if (id->pos) free(id->pos); if (str->pos) free(str->pos); msg = cur_catalog->msg; nmsg = cur_catalog->nmsg; msg[nmsg].po = cur_po_index; msg[nmsg].num = id->num; msg[nmsg].id = id->str; msg[nmsg].id_len = id->len; msg[nmsg].str = str->str; msg[nmsg].str_len = str->len; msg[nmsg].hash = hash_val; thash_idx = get_hash_index(cur_catalog->thash, hash_val, cur_catalog->thash_size); cur_catalog->thash[thash_idx] = nmsg + 1; cur_catalog->nmsg++; if (cur_catalog->nmsg >= cur_catalog->msg_size) { /* no vacancy in message array */ cur_catalog->msg_size += DEF_MSG_NUM; cur_catalog->msg = Xrealloc(cur_catalog->msg, cur_catalog->msg_size * sizeof (struct messages)); cur_catalog->thash_size = find_prime(cur_catalog->msg_size); free(cur_catalog->thash); cur_catalog->thash = Xcalloc(cur_catalog->thash_size, sizeof (unsigned int)); for (n = 0; n < cur_catalog->nmsg; n++) { thash_idx = get_hash_index(cur_catalog->thash, cur_catalog->msg[n].hash, cur_catalog->thash_size); cur_catalog->thash[thash_idx] = n + 1; } } } void po_init(const char *file) { char *filename; if (!inputdir) { filename = Xstrdup(file); } else { size_t dirlen, filelen, len; dirlen = strlen(inputdir); filelen = strlen(file); len = dirlen + 1 + filelen + 1; filename = Xmalloc(len); (void) memcpy(filename, inputdir, dirlen); *(filename + dirlen) = '/'; (void) memcpy(filename + dirlen + 1, file, filelen); *(filename + dirlen + 1 + filelen) = '\0'; } fp = fopen(filename, "r"); if (fp == NULL) { error(gettext(ERR_OPEN_FAILED), filename); /* NOTREACHED */ } po_names[cur_po_index] = filename; cur_line = 1; cd = (iconv_t)-1; if (!outfile) cur_mo = NULL; } void po_fini(void) { cur_po_index++; (void) fclose(fp); if (cd != (iconv_t)-1) (void) iconv_close(cd); } /* * 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 2001-2002 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include "gnu_msgfmt.h" #include "gnu_prime.h" /* * hashpjw * * Calculates the hash value from the specified string. * Actual hashid will be mod(hash value, PRIME_NUMBER). * * Ref: Compilers - Principles, Techniques, and Tools * Aho, Sethi, and Ullman */ unsigned int hashpjw(const char *str) { const char *p; unsigned int h = 0, g; for (p = str; *p; p++) { h = (h << 4) + *p; g = h & 0xf0000000; if (g) { h = h ^ (g >> 24); h = h ^ g; } } return (h); } static unsigned int find_prime_big(unsigned int n) { int t; unsigned int max_tbl_prime, prd; max_tbl_prime = prime[MAX_PRIME_INDEX] + 2; for (; ; ) { for (t = 1; t <= MAX_PRIME_INDEX; t++) { if (n % prime[t] == 0) { /* n is not a prime number */ break; } } if (t <= MAX_PRIME_INDEX) { n += 2; continue; } prd = max_tbl_prime; while ((prd * prd < n) && (n % prd != 0)) { prd += 2; } if (n % prd == 0) { n += 2; continue; } return (n); } /* NOTREACHED */ } unsigned int find_prime(unsigned int tbl_size) { int t, d; unsigned int n, prd; /* for compatibility with GNU msgfmt */ if (tbl_size == 1) return (1); else if (tbl_size == 2) return (5); n = 4 * tbl_size / 3; /* make n an odd number */ n |= 1; prd = n / 100; if (prd <= MAX_INDEX_INDEX) { /* first, search the table */ for (t = index[prd] + 1; t <= MAX_PRIME_INDEX; t++) { if (prime[t] >= n) return (prime[t]); } error(ERR_PRIME, n); /* NOTREACHED */ } t = START_SEARCH_INDEX; for (; ; ) { while (prime[t] * prime[t] < n) { if (t == MAX_PRIME_INDEX) { return (find_prime_big(n)); } t++; } for (d = 1; d <= t; d++) { if (n % prime[d] == 0) { /* n is not a prime number */ break; } } if (d > t) { /* n is a prime number */ return (n); } n += 2; } /* NOTREACHED */ } unsigned int get_hash_index(unsigned int *hash_tbl, unsigned int hash_value, unsigned int hash_size) { unsigned int idx, inc; idx = hash_value % hash_size; inc = 1 + (hash_value % (hash_size - 2)); for (; ; ) { if (!hash_tbl[idx]) return (idx); idx = (idx + inc) % hash_size; } /* NOTREACHED */ } /* * 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 2001, 2002 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include "gnu_msgfmt.h" #include "gnu_lex.h" #include "y.tab.h" int cur_line = 1; static char backbuf[MB_LEN_MAX]; static int backlen = 0; /* * get_mb() returns one multibyte character. * * This function uses the iconv() function to find out one * multibyte character from a sequence of bytes in the file stream. * The conversion from the codeset specified in the PO file to UTF-8 * is performed. The funcition reads another byte and calls iconv(), * until iconv() successfully returns as a valid UTF-8 character has * been converted or returns EILSEQ. If iconv() successfully returned, * the function returns the read bytes as one character. Otherwise, * returns error. The string converted to UTF-8 in outbuf won't be * used at all. */ static size_t get_mb(unsigned char *tmpbuf, unsigned char fc) { int c; char outbuf[8]; /* max size of a UTF-8 char */ const char *inptr; char *outptr; size_t insize = 0, inlen, outlen, ret; tmpbuf[insize++] = fc; /* size of tmpbuf is MB_LEN_MAX+1 */ if (cd == (iconv_t)-1) { /* no conversion */ tmpbuf[insize] = '\0'; return (insize); } for (; ; ) { inptr = (const char *)tmpbuf; outptr = &outbuf[0]; inlen = insize; outlen = sizeof (outbuf); errno = 0; ret = iconv(cd, &inptr, &inlen, &outptr, &outlen); if (ret == (size_t)-1) { /* iconv failed */ switch (errno) { case EILSEQ: /* invalid character found */ error(gettext(ERR_INVALID_CHAR), cur_line, cur_po); /* NOTREACHED */ case EINVAL: /* not enough input */ if (insize == MB_LEN_MAX) { /* invalid character found */ error(gettext(ERR_INVALID_CHAR), cur_line, cur_po); /* NOTREACHED */ } c = getc(fp); if (c == EOF) { error(gettext(ERR_UNEXP_EOF), cur_line, cur_po); /* NOTREACHED */ } tmpbuf[insize++] = (unsigned char)c; /* initialize the conversion */ outptr = &outbuf[0]; outlen = sizeof (outbuf); (void) iconv(cd, NULL, NULL, &outptr, &outlen); continue; /* NOTREACHED */ default: /* should never happen */ error(ERR_INTERNAL, cur_line, cur_po); /* NOTREACHED */ } /* NOTREACHED */ } tmpbuf[insize] = '\0'; return (insize); /* NOTRECHED */ } } static void po_uninput(int c) { (void) ungetc(c, fp); if (c == '\n') cur_line--; } static void po_ungetc(struct ch *pch) { if (backlen) { error(gettext(ERR_INTERNAL), cur_line, cur_po); /* NOTREACHED */ } if (!pch->eof) { backlen = pch->len; (void) memcpy(backbuf, pch->buf, backlen); } } static struct ch * po_getc(void) { static struct ch och; int c; if (backlen) { och.len = backlen; (void) memcpy(och.buf, backbuf, backlen); backlen = 0; return (&och); } for (; ; ) { c = getc(fp); if (c == EOF) { if (ferror(fp)) { /* error happend */ error(gettext(ERR_READ_FAILED), cur_po); /* NOTREACHED */ } och.len = 0; och.eof = 1; return (&och); } if (c == '\\') { c = getc(fp); if (c == '\n') { /* this newline should be escaped */ cur_line++; continue; } else { po_uninput(c); och.len = 1; och.eof = 0; och.buf[0] = '\\'; return (&och); } /* NOTREACHED */ } if (c == '\n') { cur_line++; och.len = 1; och.eof = 0; och.buf[0] = '\n'; return (&och); } if (isascii((unsigned char)c)) { /* single byte ascii */ och.len = 1; och.eof = 0; och.buf[0] = (unsigned char)c; return (&och); } och.len = get_mb(&och.buf[0], (unsigned char)c); och.eof = 0; return (&och); } /* NOTREACHED */ } static void extend_buf(char **buf, size_t *size, size_t add) { char *tmp; *size += add; tmp = (char *)Xrealloc(*buf, *size); *buf = tmp; } static struct ch * expand_es(void) { int c, n, loop; static struct ch och; struct ch *pch; pch = po_getc(); if (pch->eof) { error(gettext(ERR_UNEXP_EOF), cur_line, cur_po); /* NOTREACHED */ } if (pch->len > 1) { /* not a valid escape sequence */ return (pch); } och.len = 1; och.eof = 0; switch (pch->buf[0]) { case '"': case '\\': och.buf[0] = pch->buf[0]; break; case 'b': och.buf[0] = '\b'; break; case 'f': och.buf[0] = '\f'; break; case 'n': och.buf[0] = '\n'; break; case 'r': och.buf[0] = '\r'; break; case 't': och.buf[0] = '\t'; break; case 'v': och.buf[0] = '\v'; break; case 'a': och.buf[0] = '\a'; break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': /* octal */ c = pch->buf[0]; for (n = 0, loop = 0; ; ) { n = n * 8 + c - '0'; loop++; if (loop >= 3) break; pch = po_getc(); if (pch->eof) { error(gettext(ERR_UNEXP_EOF), cur_line, cur_po); /* NOTREACHED */ } if ((pch->len > 1) || (pch->buf[0] < '0') || (pch->buf[0] > '7')) break; c = pch->buf[0]; } po_ungetc(pch); och.buf[0] = (unsigned char)n; break; case 'x': /* hex */ pch = po_getc(); if (pch->eof) { error(gettext(ERR_UNEXP_EOF), cur_line, cur_po); /* NOTREACHED */ } if (pch->len > 1) { po_ungetc(pch); och.buf[0] = 'x'; break; } c = pch->buf[0]; if (!isxdigit((unsigned char)c)) { po_ungetc(pch); och.buf[0] = 'x'; break; } if (isdigit((unsigned char)c)) { n = c - '0'; } else if (isupper((unsigned char)c)) { n = c - 'A' + 10; } else { n = c - 'a' + 10; } pch = po_getc(); if (pch->eof) { error(gettext(ERR_UNEXP_EOF), cur_line, cur_po); /* NOTREACHED */ } if (pch->len > 1) { po_ungetc(pch); och.buf[0] = (unsigned char)n; break; } c = pch->buf[0]; if (!isxdigit((unsigned char)c)) { po_ungetc(pch); och.buf[0] = (unsigned char)n; break; } n *= 16; if (isdigit((unsigned char)c)) { n += c - '0'; } else if (isupper((unsigned char)c)) { n += c - 'A' + 10; } else { n += c - 'a' + 10; } och.buf[0] = (unsigned char)n; break; default: och.buf[0] = pch->buf[0]; break; } return (&och); } int yylex(void) { unsigned int uc; struct ch *pch; char *buf; size_t buf_size, buf_pos; for (; ; ) { pch = po_getc(); if (pch->eof) { /* EOF */ return (0); } if (pch->len > 1) { /* multi byte */ yylval.c.len = pch->len; (void) memcpy(yylval.c.buf, pch->buf, pch->len); return (CHR); } /* single byte */ switch (pch->buf[0]) { case ' ': case '\t': case '\n': break; case '#': /* comment start */ buf_size = CBUFSIZE; buf = (char *)Xmalloc(buf_size); buf_pos = 0; pch = po_getc(); while (!pch->eof && ((pch->len != 1) || (pch->buf[0] != '\n'))) { if (buf_pos + pch->len + 1 > buf_size) extend_buf(&buf, &buf_size, CBUFSIZE); (void) memcpy(buf + buf_pos, pch->buf, pch->len); buf_pos += pch->len; pch = po_getc(); } buf[buf_pos] = '\0'; yylval.str = buf; return (COMMENT); /* NOTREACHED */ case '[': case ']': return (pch->buf[0]); /* NOTREACHED */ case '"': buf_size = MBUFSIZE; buf = (char *)Xmalloc(buf_size); buf_pos = 0; for (; ; ) { pch = po_getc(); if (pch->eof) { /* EOF */ error(gettext(ERR_UNEXP_EOF), cur_line, cur_po); /* NOTREACHED */ } if (pch->len == 1) { uc = pch->buf[0]; if (uc == '\n') { error(gettext(ERR_UNEXP_EOL), cur_line, cur_po); /* NOTREACHED */ } if (uc == '"') break; if (uc == '\\') pch = expand_es(); } if (buf_pos + pch->len + 1 > buf_size) extend_buf(&buf, &buf_size, MBUFSIZE); (void) memcpy(buf + buf_pos, pch->buf, pch->len); buf_pos += pch->len; } buf[buf_pos] = '\0'; yylval.str = buf; return (STR); /* NOTREACHED */ default: uc = pch->buf[0]; if (isalpha(uc) || (uc == '_')) { buf_size = KBUFSIZE; buf = (char *)Xmalloc(buf_size); buf_pos = 0; buf[buf_pos++] = (char)uc; pch = po_getc(); while (!pch->eof && (pch->len == 1) && (isalpha(uc = pch->buf[0]) || isdigit(uc) || (uc == '_'))) { if (buf_pos + 1 + 1 > buf_size) extend_buf(&buf, &buf_size, KBUFSIZE); buf[buf_pos++] = (char)uc; pch = po_getc(); } /* push back the last char */ po_ungetc(pch); buf[buf_pos] = '\0'; yylval.str = buf; if (buf_pos > MAX_KW_LEN) { /* kbuf is longer than any keywords */ return (SYMBOL); } yylval.num = cur_line; if (strcmp(buf, KW_DOMAIN) == 0) { free(buf); return (DOMAIN); } else if (strcmp(buf, KW_MSGID) == 0) { free(buf); return (MSGID); } else if (strcmp(buf, KW_MSGID_PLURAL) == 0) { free(buf); return (MSGID_PLURAL); } else if (strcmp(buf, KW_MSGSTR) == 0) { free(buf); return (MSGSTR); } else { free(buf); return (SYMBOL); } /* NOTREACHED */ } if (isdigit(uc)) { buf_size = NBUFSIZE; buf = (char *)Xmalloc(buf_size); buf_pos = 0; buf[buf_pos++] = (char)uc; pch = po_getc(); while (!pch->eof && (pch->len == 1) && isdigit(uc = pch->buf[0])) { if (buf_pos + 1 + 1 > buf_size) extend_buf(&buf, &buf_size, NBUFSIZE); buf[buf_pos++] = (char)uc; pch = po_getc(); } /* push back the last char */ po_ungetc(pch); buf[buf_pos] = '\0'; yylval.num = atoi(buf); free(buf); return (NUM); } /* just a char */ yylval.c.len = 1; yylval.c.buf[0] = uc; return (CHR); /* NOTREACHED */ } } } /* * 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 (c) 2001 by Sun Microsystems, Inc. * All rights reserved. */ #ifndef _GNU_LEX_H #define _GNU_LEX_H #include #ifdef __cplusplus extern "C" { #endif #define KW_DOMAIN "domain" #define KW_MSGID "msgid" #define KW_MSGID_PLURAL "msgid_plural" #define KW_MSGSTR "msgstr" #define MAX_KW_LEN 12 /* msgid_plural */ struct ch { int len; int eof; unsigned char buf[MB_LEN_MAX+1]; /* including a null */ }; #ifdef __cplusplus } #endif #endif /* _GNU_LEX_H */ /* * 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 (c) 2001 by Sun Microsystems, Inc. * All rights reserved. */ #include "gnu_msgfmt.h" static char *cmd; int fuzzy_flag = 0; int verbose_flag = 0; int strict_flag = 0; int po_error = 0; char *inputdir = NULL; char *outfile = NULL; char **po_names; static void usage(void) { (void) fprintf(stderr, gettext(ERR_USAGE), cmd); exit(1); } int main(int argc, char **argv) { int i, ret; static struct flags flag; (void) setlocale(LC_ALL, ""); #if !defined(TEXT_DOMAIN) #define TEXT_DOMAIN "SYS_TEST" #endif (void) textdomain(TEXT_DOMAIN); if (cmd = strrchr(argv[0], '/')) ++cmd; else cmd = argv[0]; ret = parse_option(&argc, &argv, &flag); if (ret == -1) { usage(); /* NOTREACHED */ } if (flag.idir) { inputdir = flag.idir; } if (flag.ofile) { outfile = flag.ofile; catalog_init(outfile); } if (flag.fuzzy) { fuzzy_flag = 1; } if (flag.sun_p) { error(gettext(ERR_SUN_ON_GNU), cmd); /* NOTREACHED */ } if (flag.verbose) { verbose_flag = 1; } if (flag.strict) { strict_flag = 1; } po_names = (char **)Xmalloc(argc * sizeof (char *)); while (argc-- > 0) { if (verbose_flag) { diag(gettext(DIAG_START_PROC), *argv); } po_init(*argv); (void) yyparse(); po_fini(); argv++; } for (i = 0; i < cur_po_index; i++) { free(po_names[i]); } free(po_names); if (po_error) { /* error found */ error(gettext(ERR_ERROR_FOUND), po_error); /* NOTREACHED */ } output_all_gnu_mo_files(); return (0); } /* * 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. */ #ifndef _GNU_MSGFMT_H #define _GNU_MSGFMT_H #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "gnu_errmsg.h" #include "common.h" #ifdef __cplusplus extern "C" { #endif #define _ENCODING_ALIAS_PATH "/usr/lib/iconv/alias" #define DEST_CHARSET "UTF-8" #define CHARSET_STR "charset=" #define CHARSET_LEN 8 #define NPLURALS_STR "nplurals=" #define NPLURALS_LEN 9 #define CBUFSIZE 128 #define MBUFSIZE 128 #define KBUFSIZE 16 #define NBUFSIZE 8 #define cur_po (po_names[cur_po_index]) struct loc { off_t off; /* offset to the string */ size_t len; /* length of the string including null-termination */ unsigned int num; /* line number */ }; struct entry { int no; /* # of plural forms */ unsigned int num; /* line number */ char *str; /* string */ size_t len; /* length of the string including null-termination */ struct loc *pos; }; struct messages { char *id; /* msgid + (msgid_plural) */ char *str; /* msgstr + (msgstr[n]) */ size_t id_len; /* length of id */ size_t str_len; /* length of str */ unsigned int hash; /* hash value of msgid */ unsigned int num; /* line number */ int po; /* po index */ }; #define DEF_MSG_NUM 100 struct catalog { int header; /* 1: header found, 0: header missing */ char *fname; /* mo filename */ struct messages *msg; /* ptr to the messages struct array */ unsigned int nmsg; /* # of messages */ unsigned int msg_size; /* message_size */ unsigned int fnum; /* # of fuzzy translations */ unsigned int unum; /* # of untranslated msgs */ unsigned int hash_size; /* hash table size */ unsigned int nplurals; /* # of plural forms */ unsigned int *thash; unsigned int thash_size; struct catalog *next; /* next catalog */ }; struct msgtbl { unsigned int len; unsigned int offset; }; extern int yyparse(void); extern int yylex(void); extern int yyerror(const char *) __NORETURN; extern void handle_domain(char *); extern void handle_comment(char *); extern void handle_message(struct entry *, struct entry *); extern void clear_state(void); extern void po_init(const char *); extern void po_fini(void); extern void catalog_init(const char *); extern struct messages *search_msg(struct catalog *, const char *, unsigned int); extern unsigned int hashpjw(const char *); extern unsigned int find_prime(unsigned int); extern void output_all_gnu_mo_files(void); extern unsigned int get_hash_index(unsigned int *, unsigned int, unsigned int); extern void check_format(struct entry *, struct entry *, int); extern char **po_names; extern int cur_po_index; extern int po_error; extern char *inputdir; extern char *outfile; extern int cur_line; extern int fuzzy_flag; extern int verbose_flag; extern int strict_flag; extern struct catalog *catalog_head; extern FILE *fp; extern iconv_t cd; #ifdef __cplusplus } #endif #endif /* _GNU_MSGFMT_H */ /* * 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 2001, 2002 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include "gnu_msgfmt.h" #include "../../lib/libc/inc/msgfmt.h" struct messages * search_msg(struct catalog *p, const char *id, unsigned int hash_val) { unsigned int i, idx, inc; struct messages *m; idx = hash_val % p->thash_size; inc = 1 + (hash_val % (p->thash_size - 2)); if (!p->thash[idx]) return (NULL); m = p->msg; for (m = p->msg; (i = p->thash[idx]) != 0; idx = (idx + inc) % p->thash_size) { if (strcmp(m[i - 1].id, id) == 0) { /* found */ return (&m[i - 1]); } } return (NULL); } static int msg_cmp(struct messages *m1, struct messages *m2) { return (strcmp(m1->id, m2->id)); } void output_all_gnu_mo_files(void) { struct catalog *p, *op; struct messages *m; size_t id_len, str_len, id_off, str_off, ids_top, strs_top; unsigned int *hash_tbl; unsigned int hash_size; unsigned int num = 0, fnum = 0, unum = 0; unsigned int i, idx; char *ids, *strs; struct msgtbl *id_tbl, *str_tbl; struct gnu_msg_info header; FILE *out; p = catalog_head; while (p) { num += p->nmsg; fnum += p->fnum; unum += p->unum; free(p->thash); if (p->nmsg == 0) { /* * no message in this file * skip generating a mo */ goto skip; } if (p->header) num--; p->msg = (struct messages *)Xrealloc(p->msg, sizeof (struct messages) * p->nmsg); /* * Sort the message array */ qsort(p->msg, p->nmsg, sizeof (struct messages), (int (*)(const void *, const void *))msg_cmp); hash_size = find_prime(p->nmsg); hash_tbl = (unsigned int *)Xcalloc(hash_size, sizeof (unsigned int)); /* Setting Header info */ header.magic = GNU_MAGIC; header.revision = GNU_REVISION; header.num_of_str = p->nmsg; header.off_msgid_tbl = sizeof (struct gnu_msg_info); header.off_msgstr_tbl = sizeof (struct gnu_msg_info) + p->nmsg * sizeof (struct msgtbl); header.sz_hashtbl = hash_size; header.off_hashtbl = header.off_msgstr_tbl + p->nmsg * sizeof (struct msgtbl); m = p->msg; id_len = 0; str_len = 0; for (i = 0; i < p->nmsg; i++) { id_len += m[i].id_len; str_len += m[i].str_len; } ids = (char *)Xmalloc(id_len); strs = (char *)Xmalloc(str_len); id_tbl = (struct msgtbl *)Xmalloc(sizeof (struct msgtbl) * p->nmsg); str_tbl = (struct msgtbl *)Xmalloc(sizeof (struct msgtbl) * p->nmsg); id_off = 0; str_off = 0; ids_top = header.off_hashtbl + sizeof (unsigned int) * hash_size; strs_top = ids_top + id_len; for (i = 0; i < p->nmsg; i++) { /* * Set the hash table */ idx = get_hash_index(hash_tbl, m[i].hash, hash_size); hash_tbl[idx] = i + 1; /* * rearrange msgid and msgstr */ id_tbl[i].len = m[i].id_len - 1; str_tbl[i].len = m[i].str_len - 1; id_tbl[i].offset = id_off + ids_top; str_tbl[i].offset = str_off + strs_top; (void) memcpy(ids + id_off, m[i].id, m[i].id_len); (void) memcpy(strs + str_off, m[i].str, m[i].str_len); id_off += m[i].id_len; str_off += m[i].str_len; free(m[i].id); free(m[i].str); } if ((out = fopen(p->fname, "w")) == NULL) { error(gettext(ERR_OPEN_FAILED), p->fname); /* NOTREACHED */ } /* writing header */ (void) fwrite(&header, sizeof (struct gnu_msg_info), 1, out); /* writing msgid offset table */ (void) fwrite(id_tbl, sizeof (struct msgtbl), p->nmsg, out); /* writing msgstr offset table */ (void) fwrite(str_tbl, sizeof (struct msgtbl), p->nmsg, out); /* writing hash table */ (void) fwrite(hash_tbl, sizeof (unsigned int), hash_size, out); /* writing msgid table */ (void) fwrite(ids, id_len, 1, out); /* writing msgstr table */ (void) fwrite(strs, str_len, 1, out); (void) fclose(out); free(id_tbl); free(str_tbl); free(hash_tbl); free(ids); free(strs); skip: free(p->fname); free(p->msg); op = p->next; free(p); p = op; } if (verbose_flag) { diag(gettext(DIAG_RESULTS), num, fnum, unum); } } /* * 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 (c) 2001 by Sun Microsystems, Inc. * All rights reserved. */ #include "gnu_msgfmt.h" #include "../../lib/libc/inc/msgfmt.h" static unsigned int doswap(unsigned int n) { unsigned int r; r = (n << 24) | ((n & 0xff00) << 8) | ((n >> 8) & 0xff00) | (n >> 24); return (r); } struct messages * search_msg(struct catalog *p, const char *id, unsigned int hash_val) { unsigned int i, idx, inc; struct messages *m; idx = hash_val % p->thash_size; inc = 1 + (hash_val % (p->thash_size - 2)); if (!p->thash[idx]) return (NULL); m = p->msg; for (m = p->msg; (i = p->thash[idx]) != 0; idx = (idx + inc) % p->thash_size) { if (strcmp(m[i - 1].id, id) == 0) { /* found */ return (&m[i - 1]); } } return (NULL); } static int msg_cmp(struct messages *m1, struct messages *m2) { return (strcmp(m1->id, m2->id)); } void output_all_gnu_mo_files(void) { struct catalog *p, *op; struct messages *m; size_t id_len, str_len, id_off, str_off, ids_top, strs_top; unsigned int *hash_tbl; unsigned int hash_size, off_msgstr_tbl, off_hashtbl; unsigned int num = 0, fnum = 0, unum = 0; unsigned int i, idx; char *ids, *strs; struct msgtbl *id_tbl, *str_tbl; struct gnu_msg_info header; FILE *out; p = catalog_head; while (p) { num += p->nmsg; fnum += p->fnum; unum += p->unum; if (p->header) num--; p->msg = (struct messages *)Xrealloc(p->msg, sizeof (struct messages) * p->nmsg); free(p->thash); /* * Sort the message array */ qsort(p->msg, p->nmsg, sizeof (struct messages), (int (*)(const void *, const void *))msg_cmp); hash_size = find_prime(p->nmsg); hash_tbl = (unsigned int *)Xcalloc(hash_size, sizeof (unsigned int)); /* Setting Header info */ off_msgstr_tbl = sizeof (struct gnu_msg_info) + p->nmsg * sizeof (struct msgtbl); off_hashtbl = off_msgstr_tbl + p->nmsg * sizeof (struct msgtbl); header.magic = doswap(GNU_MAGIC); header.revision = doswap(GNU_REVISION); header.num_of_str = doswap(p->nmsg); header.off_msgid_tbl = doswap(sizeof (struct gnu_msg_info)); header.off_msgstr_tbl = doswap(off_msgstr_tbl); header.sz_hashtbl = doswap(hash_size); header.off_hashtbl = doswap(off_hashtbl); m = p->msg; id_len = 0; str_len = 0; for (i = 0; i < p->nmsg; i++) { id_len += m[i].id_len; str_len += m[i].str_len; } ids = (char *)Xmalloc(id_len); strs = (char *)Xmalloc(str_len); id_tbl = (struct msgtbl *)Xmalloc(sizeof (struct msgtbl) * p->nmsg); str_tbl = (struct msgtbl *)Xmalloc(sizeof (struct msgtbl) * p->nmsg); id_off = 0; str_off = 0; ids_top = off_hashtbl + sizeof (unsigned int) * hash_size; strs_top = ids_top + id_len; for (i = 0; i < p->nmsg; i++) { /* * Set the hash table */ idx = get_hash_index(hash_tbl, m[i].hash, hash_size); hash_tbl[idx] = doswap(i + 1); /* * rearrange msgid and msgstr */ id_tbl[i].len = doswap(m[i].id_len - 1); str_tbl[i].len = doswap(m[i].str_len - 1); id_tbl[i].offset = doswap(id_off + ids_top); str_tbl[i].offset = doswap(str_off + strs_top); (void) memcpy(ids + id_off, m[i].id, m[i].id_len); (void) memcpy(strs + str_off, m[i].str, m[i].str_len); id_off += m[i].id_len; str_off += m[i].str_len; free(m[i].id); free(m[i].str); } if ((out = fopen(p->fname, "w")) == NULL) { error(gettext(ERR_OPEN_FAILED), p->fname); /* NOTREACHED */ } /* writing header */ (void) fwrite(&header, sizeof (struct gnu_msg_info), 1, out); /* writing msgid offset table */ (void) fwrite(id_tbl, sizeof (struct msgtbl), p->nmsg, out); /* writing msgstr offset table */ (void) fwrite(str_tbl, sizeof (struct msgtbl), p->nmsg, out); /* writing hash table */ (void) fwrite(hash_tbl, sizeof (unsigned int), hash_size, out); /* writing msgid table */ (void) fwrite(ids, id_len, 1, out); /* writing msgstr table */ (void) fwrite(strs, str_len, 1, out); (void) fclose(out); free(p->fname); free(p->msg); free(id_tbl); free(str_tbl); free(hash_tbl); free(ids); free(strs); op = p->next; free(p); p = op; } if (verbose_flag) { diag(gettext(DIAG_RESULTS), num, fnum, unum); } } %{ /* * 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 (c) 2001 by Sun Microsystems, Inc. * All rights reserved. */ #include "gnu_msgfmt.h" #include "gnu_lex.h" static int plural_index; %} %union { char *str; int num; struct entry msg; struct ch c; } %token DOMAIN %token MSGID %token MSGID_PLURAL %token MSGSTR %token NUM %token STR %token COMMENT %token SYMBOL %token CHR %type message_string plural_messages plural_message %% start : | start po ; po : comment | domain | body ; domain : DOMAIN STR { handle_domain($2); } ; comment : COMMENT { handle_comment($1); } body : MSGID message_string MSGSTR message_string { struct entry och1, och2; och1.no = 1; och1.num = $1; och1.str = $2.str; och1.len = $2.len; och1.pos = NULL; och2.no = 1; och2.num = $3; och2.str = $4.str; och2.len = $4.len; och2.pos = NULL; handle_message(&och1, &och2); clear_state(); } | MSGID message_string MSGID_PLURAL message_string {plural_index = 0;} plural_messages { size_t len; struct entry och1, och2; struct loc *pos1; char *id_str; len = $2.len + $4.len; id_str = (char *)Xmalloc(len); (void) memcpy(id_str, $2.str, $2.len); (void) memcpy(id_str + $2.len, $4.str, $4.len); free($2.str); free($4.str); pos1 = (struct loc *)Xmalloc(2 * sizeof (struct loc)); pos1[0].off = 0; pos1[0].len = $2.len; pos1[0].num = $1; pos1[1].off = $2.len; pos1[1].len = $4.len; pos1[1].num = $3; och1.no = 2; och1.num = $1; och1.str = id_str; och1.len = len; och1.pos = pos1; och2 = $6; handle_message(&och1, &och2); clear_state(); } | MSGID message_string { error(gettext(ERR_NO_MSGSTR), $1, cur_po); /* NOTREACHED */ } | MSGID message_string MSGID_PLURAL message_string { error(gettext(ERR_NO_MSGSTRS), $1, cur_po); /* NOTRECHED */ } | MSGID message_string plural_messages { error(gettext(ERR_NO_MSGID_PLURAL), $1, cur_po); /* NOTREACHED */ } ; message_string : STR { $$.str = $1; $$.len = strlen($1) + 1; } | message_string STR { size_t len, len1, len2; char *str; /* $1.len includes null-termination */ len1 = $1.len - 1; len2 = strlen($2); /* len doesn't include null-termination */ len = len1 + len2; str = (char *)Xmalloc(len + 1); (void) memcpy(str, $1.str, len1); (void) memcpy(str + len1, $2, len2 + 1); free($1.str); free($2); $$.str = str; $$.len = len + 1; } ; plural_messages : plural_message { $$ = $1; } | plural_messages plural_message { struct loc *tmp; size_t len; char *plural_str; int no; no = $1.no + 1; tmp = (struct loc *)Xrealloc($1.pos, no * sizeof (struct loc)); tmp[no - 1].off = $1.len; tmp[no - 1].len = $2.len; tmp[no - 1].num = $2.num; free($2.pos); len = $1.len + $2.len; plural_str = (char *)Xmalloc(len); (void) memcpy(plural_str, $1.str, $1.len); (void) memcpy(plural_str + $1.len, $2.str, $2.len); $$.no = no; $$.num = $1.num; $$.str = plural_str; $$.len = len; $$.pos = tmp; free($1.str); free($2.str); } ; plural_message : MSGSTR '[' NUM ']' message_string { struct loc *pos; if ($3 != plural_index) { error(gettext(ERR_INVALID_PLURALS), $1, cur_po); /* NOTREACHED */ } plural_index++; pos = (struct loc *)Xmalloc(sizeof (struct loc)); pos->off = 0; pos->len = $5.len; pos->num = $1; $$.no = 1; $$.num = $1; $$.str = $5.str; $$.len = $5.len; $$.pos = pos; } ; %% int yyerror(const char *err) { (void) fprintf(stderr, gettext(ERR_LOCATION), cur_line, cur_po); (void) fprintf(stderr, "%s\n", err); exit(1); } /* * 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 2001-2002 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #ifndef _GNU_PRIME_H #define _GNU_PRIME_H #ifdef __cplusplus extern "C" { #endif static const unsigned int prime[] = { 2, 3, 5, 7, 11, /* 0 - 4 */ 13, 17, 19, 23, 29, /* 5 - 9 */ 31, 37, 41, 43, 47, /* 10 - 14 */ 53, 59, 61, 67, 71, /* 15 - 19 */ 73, 79, 83, 89, 97, /* 20 - 24 */ 101, 103, 107, 109, 113, /* 25 - 29 */ 127, 131, 137, 139, 149, /* 30 - 34 */ 151, 157, 163, 167, 173, /* 35 - 39 */ 179, 181, 191, 193, 197, /* 40 - 44 */ 199, 211, 223, 227, 229, /* 45 - 49 */ 233, 239, 241, 251, 257, /* 50 - 54 */ 263, 269, 271, 277, 281, /* 55 - 59 */ 283, 293, 307, 311, 313, /* 60 - 64 */ 317, 331, 337, 347, 349, /* 65 - 69 */ 353, 359, 367, 373, 379, /* 70 - 74 */ 383, 389, 397, 401, 409, /* 75 - 79 */ 419, 421, 431, 433, 439, /* 80 - 84 */ 443, 449, 457, 461, 463, /* 85 - 89 */ 467, 479, 487, 491, 499, /* 90 - 94 */ 503, 509, 521, 523, 541, /* 95 - 99 */ 547, 557, 563, 569, 571, /* 100 - 104 */ 577, 587, 593, 599, 601, /* 105 - 109 */ 607, 613, 617, 619, 631, /* 110 - 114 */ 641, 643, 647, 653, 659, /* 115 - 119 */ 661, 673, 677, 683, 691, /* 120 - 124 */ 701, 709, 719, 727, 733, /* 125 - 129 */ 739, 743, 751, 757, 761, /* 130 - 134 */ 769, 773, 787, 797, 809, /* 135 - 139 */ 811, 821, 823, 827, 829, /* 140 - 144 */ 839, 853, 857, 859, 863, /* 145 - 149 */ 877, 881, 883, 887, 907, /* 150 - 154 */ 911, 919, 929, 937, 941, /* 155 - 159 */ 947, 953, 967, 971, 977, /* 160 - 164 */ 983, 991, 997, 1009, 1013, /* 165 - 169 */ 1019, 1021, 1031, 1033, 1039, /* 170 - 174 */ 1049, 1051, 1061, 1063, 1069, /* 175 - 179 */ 1087, 1091, 1093, 1097, 1103 /* 180 - 184 */ }; static const int index[] = { 1, /* 0: 3 */ 24, /* 100: 97 */ 45, /* 200: 199 */ 61, /* 300: 293 */ 77, /* 400: 397 */ 94, /* 500: 499 */ 108, /* 600: 599 */ 124, /* 700: 691 */ 138, /* 800: 797 */ 153, /* 900: 887 */ 167 /* 1000: 997 */ }; #define MAX_INDEX_INDEX 10 #define MAX_PRIME_INDEX 184 #define START_SEARCH_INDEX 10 /* 31: 31 * 31 = 961 */ #ifdef __cplusplus } #endif #endif /* _GNU_PRIME_H */ /* * 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. */ #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 */ 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)) != '\0') 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 ('\0'); } } /* 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 /* * 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 (c) 1996-2001 by Sun Microsystems, Inc. * All rights reserved. */ #include "common.h" int parse_option(int *pargc, char ***pargv, struct flags *flag) { char c; char *arg; int argc = *pargc; char **argv = *pargv; argv++; while (--argc > 1) { arg = *argv; if (*arg == '-') { if (!*(arg + 1)) { /* not an option */ break; } loop: if ((c = *++arg) == '\0') { /* next argument */ argv++; continue; } else if (c != '-') { /* Sun option */ switch (c) { case 'D': /* * add directory to list for input * files search. */ if (*(arg + 1)) { /* * no spaces between -D and * optarg */ flag->idir = ++arg; argv++; continue; } if (--argc > 1) { if (!flag->idir) flag->idir = *++argv; else ++argv; argv++; continue; } /* not enough args */ return (-1); /* NOTREACHED */ case 'f': /* * Use fuzzy entry */ flag->fuzzy = 1; goto loop; /* NOTREACHED */ case 'o': /* * Specify output file name */ if (*(arg + 1)) { /* * no spaces between -o and * optarg */ flag->ofile = ++arg; argv++; continue; } if (--argc > 1) { flag->ofile = *++argv; argv++; continue; } /* not enough args */ return (-1); case 'g': /* * GNU mode */ flag->gnu_p = 1; goto loop; case 's': /* * Sun mode */ flag->sun_p = 1; goto loop; case 'v': /* * verbose mode */ flag->verbose = 1; goto loop; default: /* illegal option */ return (-1); } /* NOTREACHED */ } if (*(arg + 1) == '\0') { /* option end */ argv++; argc--; break; } /* GNU options */ arg++; if (strncmp(arg, "directory=", 10) == 0) { /* * add directory to list for input * files search. */ if (flag->idir) { /* * inputdir has already been specified */ argv++; continue; } arg += 10; if (*arg == '\0') { /* illegal option */ return (-1); } flag->idir = arg; argv++; continue; } if (strcmp(arg, "use-fuzzy") == 0) { /* * Use fuzzy entry */ flag->fuzzy = 1; argv++; continue; } if (strncmp(arg, "output-file=", 12) == 0) { /* * Specify output file name */ arg += 12; if (*arg == '\0') { /* illegal option */ return (-1); } flag->ofile = arg; argv++; continue; } if (strcmp(arg, "strict") == 0) { /* * strict mode */ flag->strict = 1; argv++; continue; } if (strcmp(arg, "verbose") == 0) { /* * verbose mode */ flag->verbose = 1; argv++; continue; } /* illegal option */ return (-1); } break; } if (argc == 0) return (-1); *pargc = argc; *pargv = argv; return (0); } #!/bin/sh # # 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 (c) 2000 by Sun Microsystems, Inc. # All rights reserved. # echo_file usr/src/lib/libc/inc/msgfmt.h /* * 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 (c) 2001 by Sun Microsystems, Inc. * All rights reserved. */ #ifndef _SUN_MSGFMT_H #define _SUN_MSGFMT_H #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../../lib/libc/inc/msgfmt.h" #include "common.h" #ifdef __cplusplus extern "C" { #endif #define DOMAIN_TOKEN L"domain" /* domain token in po file */ #define DOMAIN_LEN 6 #define MSGID_TOKEN L"msgid" /* msg id token in po file */ #define MSGID_LEN 5 #define MSGSTR_TOKEN L"msgstr" /* target str token in po file */ #define MSGSTR_LEN 6 #ifdef DEBUG_MMAP #define MAX_VALUE_LEN 3 /* size of msg id and target str */ #define LINE_SIZE 1 #else #define MAX_VALUE_LEN 512 /* size of msg id and target str */ #define LINE_SIZE 512 #endif /* * check if the next character is possible valid character. */ #define CK_NXT_CH(a, l) \ ((a[(l) - 1] == L' ') || (a[(l) - 1] == L'\t') || \ (a[(l) - 1] == L'\n') || (a[(l) - 1] == L'\0')) struct msg_chain { char *msgid; /* msg id string */ char *msgstr; /* msg target string */ int msgid_offset; /* msg id offset in mo file */ int msgstr_offset; /* msg target string offset in mo file */ struct msg_chain *next; /* next node */ }; struct domain_struct { char *domain; /* domain name */ struct msg_chain *first_elem; /* head of msg link list */ struct msg_chain *current_elem; /* most recently used msg */ struct domain_struct *next; /* next domain node */ }; #define ERR_EXEC_FAILED \ "failed to execute %s.\n" #define ERR_USAGE \ "Usage: msgfmt [-D dir | --directory=dir] [-f | --use-fuzzy]\n" \ " [-g] [-o outfile | --output-file=outfile]\n" \ " [-s] [--strict] [-v] [--verbose] files ...\n" #define ERR_GNU_ON_SUN \ "-g and -s are mutually exclusive.\n" #define ERR_STAT_FAILED \ "stat failed for %s.\n" #define ERR_MMAP_FAILED \ "mmap failed for %s.\n" #define ERR_MUNMAP_FAILED \ "munmap failed for %s.\n" #define ERR_NOSPC \ "Error, No space after directive at line number %d.\n" #define ERR_EXITING \ "Exiting...\n" #define WARN_NO_MSGSTR \ "Consecutive MSGID tokens " \ "encountered at line number: %d, ignored.\n" #define WARN_NO_MSGID \ "Consecutive MSGSTR tokens " \ "encountered at line number: %d, ignored.\n" #define WARN_SYNTAX_ERR \ "Syntax at line number: %d, " \ "line ignored\n" #define WARN_MISSING_QUOTE \ "Syntax at line number: %d, " \ "Missing \", ignored\n" #define WARN_MISSING_QUOTE_AT_EOL \ "Syntax at line number: %d, " \ "Missing \" at EOL, ignored\n" #define WARN_INVALID_STRING \ "the string after closing \" " \ "is ignored at line number %d.\n" #define WARN_DUP_MSG \ "Duplicate id \"%s\" at line number: " \ "%d, line ignored\n" #define DIAG_GNU_FOUND \ "GNU PO file found.\n" #define DIAG_INVOKING_GNU \ "Generating the MO file in the GNU MO format.\n" #ifdef __cplusplus } #endif #endif /* _SUN_MSGFMT_H */ /* * 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 2003 Sun Microsystems, Inc. All rights reserved. * Use is subject to license terms. */ #include "common.h" /*PRINTFLIKE1*/ void error(char *err, ...) { va_list ap; va_start(ap, err); (void) fprintf(stderr, gettext(ERR_ERROR)); (void) vfprintf(stderr, err, ap); va_end(ap); exit(2); } /*PRINTFLIKE1*/ void warning(char *err, ...) { va_list ap; va_start(ap, err); (void) fprintf(stderr, gettext(WARN_WARNING)); (void) vfprintf(stderr, err, ap); va_end(ap); } /*PRINTFLIKE1*/ void diag(char *err, ...) { va_list ap; va_start(ap, err); (void) vfprintf(stderr, err, ap); va_end(ap); } void * Xmalloc(size_t size) { void *t; t = malloc(size); if (!t) { error(gettext(ERR_MALLOC)); /* NOTREACHED */ } return (t); } void * Xcalloc(size_t nelem, size_t elsize) { void *t; t = calloc(nelem, elsize); if (!t) { error(gettext(ERR_MALLOC)); /* NOTREACHED */ } return (t); } void * Xrealloc(void *ptr, size_t size) { void *t; t = realloc(ptr, size); if (!t) { free(ptr); error(gettext(ERR_MALLOC)); /* NOTREACHED */ } return (t); } char * Xstrdup(const char *str) { char *t; t = strdup(str); if (!t) { error(gettext(ERR_MALLOC)); /* NOTREACHED */ } return (t); } /* * 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 1991, 1999, 2001-2002 Sun Microsystems, Inc. * All rights reserved. * Use is subject to license terms. */ #include #include #include #include #define TRUE 1 #define FALSE 0 #define MAX_PATH_LEN 1024 #define MAX_DOMAIN_LEN 1024 #define MAX_STRING_LEN 2048 #define USAGE "Usage: xgettext [-a [-x exclude-file]] [-jns]\ [-c comment-tag]\n [-d default-domain] [-m prefix] \ [-M suffix] [-p pathname] files ...\n\ xgettext -h\n" #define DEFAULT_DOMAIN "messages" extern char yytext[]; extern int yylex(void); /* * Contains a list of strings to be used to store ANSI-C style string. * Each quoted string is stored in one node. */ struct strlist_st { char *str; struct strlist_st *next; }; /* * istextdomain : Boolean telling if this node contains textdomain call. * isduplicate : Boolean telling if this node duplicate of any other msgid. * msgid : contains msgid or textdomain if istextdomain is true. * msgstr : contains msgstr. * comment : comment extracted in case of -c option. * fname : tells which file contains msgid. * linenum : line number in the file. * next : Next node. */ struct element_st { char istextdomain; char isduplicate; struct strlist_st *msgid; struct strlist_st *msgstr; struct strlist_st *comment; char *fname; int linenum; struct element_st *next; }; /* * dname : domain name. NULL if default domain. * gettext_head : Head of linked list containing [d]gettext(). * gettext_tail : Tail of linked list containing [d]gettext(). * textdomain_head : Head of linked list containing textdomain(). * textdomain_tail : Tail of linked list containing textdomain(). * next : Next node. * * Each domain contains two linked list. * (gettext_head, textdomain_head) * If -s option is used, then textdomain_head contains all * textdomain() calls and no textdomain() calls are stored in gettext_head. * If -s option is not used, textdomain_head is empty list and * gettext_head contains all gettext() dgettext(), and textdomain() calls. */ struct domain_st { char *dname; struct element_st *gettext_head; struct element_st *gettext_tail; struct element_st *textdomain_head; struct element_st *textdomain_tail; struct domain_st *next; }; /* * There are two domain linked lists. * def_dom contains default domain linked list and * dom_head contains all other deomain linked lists to be created by * dgettext() calls. */ static struct domain_st *def_dom = NULL; static struct domain_st *dom_head = NULL; static struct domain_st *dom_tail = NULL; /* * This linked list contains a list of strings to be excluded when * -x option is used. */ static struct exclude_st { struct strlist_st *exstr; struct exclude_st *next; } *excl_head; /* * All option flags and values for each option if any. */ static int aflg = FALSE; static int cflg = FALSE; static char *comment_tag = NULL; static char *default_domain = NULL; static int hflg = FALSE; static int jflg = FALSE; static int mflg = FALSE; static int Mflg = FALSE; static char *suffix = NULL; static char *prefix = NULL; static int nflg = FALSE; static int pflg = FALSE; static char *pathname = NULL; static int sflg = FALSE; static int tflg = FALSE; /* Undocumented option to extract dcgettext */ static int xflg = FALSE; static char *exclude_file = NULL; /* * Each variable shows the current state of parsing input file. * * in_comment : Means inside comment block (C or C++). * in_cplus_comment : Means inside C++ comment block. * in_gettext : Means inside gettext call. * in_dgettext : Means inside dgettext call. * in_dcgettext : Means inside dcgettext call. * in_textdomain : Means inside textdomain call. * in_str : Means currently processing ANSI style string. * in_quote : Means currently processing double quoted string. * in_skippable_string : Means currently processing double quoted string, * that occurs outside a call to gettext, dgettext, * dcgettext, textdomain, with -a not specified. * is_last_comment_line : Means the current line is the last line * of the comment block. This is necessary because * in_comment becomes FALSE when '* /' is encountered. * is_first_comma_found : This is used only for dcgettext because dcgettext() * requires 2 commas. So need to do different action * depending on which commas encountered. * num_nested_open_paren : This keeps track of the number of open parens to * handle dcgettext ((const char *)0,"msg",LC_TIME); */ static int in_comment = FALSE; static int in_cplus_comment = FALSE; static int in_gettext = FALSE; static int in_dgettext = FALSE; static int in_dcgettext = FALSE; static int in_textdomain = FALSE; static int in_str = FALSE; static int in_quote = FALSE; static int is_last_comment_line = FALSE; static int is_first_comma_found = FALSE; static int in_skippable_string = FALSE; static int num_nested_open_paren = 0; /* * This variable contains the first line of gettext(), dgettext(), or * textdomain() calls. * This is necessary for multiple lines of a single call to store * the starting line. */ static int linenum_saved = 0; int stdin_only = FALSE; /* Read input from stdin */ /* * curr_file : Contains current file name processed. * curr_domain : Contains the current domain for each dgettext(). * This is NULL for gettext(). * curr_line : Contains the current line processed. * qstring_buf : Contains the double quoted string processed. * curr_linenum : Line number being processed in the current input file. * warn_linenum : Line number of current warning message. */ char curr_file[MAX_PATH_LEN]; static char curr_domain[MAX_DOMAIN_LEN]; static char curr_line[MAX_STRING_LEN]; static char qstring_buf[MAX_STRING_LEN]; int curr_linenum = 1; int warn_linenum = 0; /* * strhead : This list contains ANSI style string. * Each node contains double quoted string. * strtail : This is the tail of strhead. * commhead : This list contains comments string. * Each node contains one line of comment. * commtail : This is the tail of commhead. */ static struct strlist_st *strhead = NULL; static struct strlist_st *strtail = NULL; static struct strlist_st *commhead = NULL; static struct strlist_st *commtail = NULL; /* * gargc : Same as argc. Used to pass argc to lex routine. * gargv : Same as argv. Used to pass argc to lex routine. */ int gargc; char **gargv; static void add_line_to_comment(void); static void add_qstring_to_str(void); static void add_str_to_element_list(int, char *); static void copy_strlist_to_str(char *, struct strlist_st *); static void end_ansi_string(void); static void free_strlist(struct strlist_st *); void handle_newline(void); static void initialize_globals(void); static void output_comment(FILE *, struct strlist_st *); static void output_msgid(FILE *, struct strlist_st *, int); static void output_textdomain(FILE *, struct element_st *); static void print_help(void); static void read_exclude_file(void); static void trim_line(char *); static void write_all_files(void); static void write_one_file(struct domain_st *); static void lstrcat(char *, const char *); /* * Utility functions to malloc a node and initialize fields. */ static struct domain_st *new_domain(void); static struct strlist_st *new_strlist(void); static struct element_st *new_element(void); static struct exclude_st *new_exclude(void); /* * Main program of xgettext. */ int main(int argc, char **argv) { int opterr = FALSE; int c; initialize_globals(); while ((c = getopt(argc, argv, "jhax:nsc:d:m:M:p:t")) != EOF) { switch (c) { case 'a': aflg = TRUE; break; case 'c': cflg = TRUE; comment_tag = optarg; break; case 'd': default_domain = optarg; break; case 'h': hflg = TRUE; break; case 'j': jflg = TRUE; break; case 'M': Mflg = TRUE; suffix = optarg; break; case 'm': mflg = TRUE; prefix = optarg; break; case 'n': nflg = TRUE; break; case 'p': pflg = TRUE; pathname = optarg; break; case 's': sflg = TRUE; break; case 't': tflg = TRUE; break; case 'x': xflg = TRUE; exclude_file = optarg; break; case '?': opterr = TRUE; break; } } /* if -h is used, ignore all other options. */ if (hflg == TRUE) { (void) fprintf(stderr, USAGE); print_help(); exit(0); } /* -x can be used only with -a */ if ((xflg == TRUE) && (aflg == FALSE)) opterr = TRUE; /* -j cannot be used with -a */ if ((jflg == TRUE) && (aflg == TRUE)) { (void) fprintf(stderr, "-a and -j options cannot be used together.\n"); opterr = TRUE; } /* -j cannot be used with -s */ if ((jflg == TRUE) && (sflg == TRUE)) { (void) fprintf(stderr, "-j and -s options cannot be used together.\n"); opterr = TRUE; } if (opterr == TRUE) { (void) fprintf(stderr, USAGE); exit(2); } /* error, if no files are specified. */ if (optind == argc) { (void) fprintf(stderr, USAGE); exit(2); } if (xflg == TRUE) { read_exclude_file(); } /* If files are -, then read from stdin */ if (argv[optind][0] == '-') { stdin_only = TRUE; optind++; } else { stdin_only = FALSE; } /* Store argc and argv to pass to yylex() */ gargc = argc; gargv = argv; #ifdef DEBUG (void) printf("optind=%d\n", optind); { int i = optind; for (; i < argc; i++) { (void) printf(" %d, <%s>\n", i, argv[i]); } } #endif if (stdin_only == FALSE) { if (freopen(argv[optind], "r", stdin) == NULL) { (void) fprintf(stderr, "ERROR, can't open input file: %s\n", argv[optind]); exit(2); } (void) strcpy(curr_file, gargv[optind]); optind++; } /* * Process input. */ (void) yylex(); #ifdef DEBUG printf("\n======= default_domain ========\n"); print_one_domain(def_dom); printf("======= domain list ========\n"); print_all_domain(dom_head); #endif /* * Write out all .po files. */ write_all_files(); return (0); } /* main */ /* * Prints help information for each option. */ static void print_help(void) { (void) fprintf(stderr, "\n"); (void) fprintf(stderr, "-a\t\t\tfind ALL strings\n"); (void) fprintf(stderr, "-c \tget comments containing \n"); (void) fprintf(stderr, "-d \tuse for default domain\n"); (void) fprintf(stderr, "-h\t\t\tHelp\n"); (void) fprintf(stderr, "-j\t\t\tupdate existing file with the current result\n"); (void) fprintf(stderr, "-M \t\tfill in msgstr with msgid\n"); (void) fprintf(stderr, "-m \t\tfill in msgstr with msgid\n"); (void) fprintf(stderr, "-n\t\t\tline# file name and line number info in output\n"); (void) fprintf(stderr, "-p \t\tuse for output file directory\n"); (void) fprintf(stderr, "-s\t\t\tgenerate sorted output files\n"); (void) fprintf(stderr, "-x \texclude strings in file " " from output\n"); (void) fprintf(stderr, "-\t\t\tread stdin, use as a filter (input only)\n"); } /* print_help */ /* * Extract file name and line number information from macro line * and set the global variable accordingly. * The valid line format is * 1) # nnn * or * 2) # nnn "xxxxx" * where nnn is line number and xxxxx is file name. */ static void extract_filename_linenumber(char *mline) { int num; char *p, *q, *r; /* * mline can contain multi newline. * line number should be increased by the number of newlines. */ p = mline; while ((p = strchr(p, '\n')) != NULL) { p++; curr_linenum++; } p = strchr(mline, ' '); if (p == NULL) return; q = strchr(++p, ' '); if (q == NULL) { /* case 1 */ if ((num = atoi(p)) > 0) { curr_linenum = num; return; } } else { /* case 2 */ *q++ = 0; if (*q == '"') { q++; r = strchr(q, '"'); if (r == NULL) { return; } *r = 0; if ((num = atoi(p)) > 0) { curr_linenum = num; (void) strcpy(curr_file, q); } } } } /* extract_filename_linenumber */ /* * Handler for MACRO line which starts with #. */ void handle_macro_line(void) { #ifdef DEBUG (void) printf("Macro line=<%s>\n", yytext); #endif if (cflg == TRUE) lstrcat(curr_line, yytext); if (in_quote == TRUE) { lstrcat(qstring_buf, yytext); } else if (in_comment == FALSE) { extract_filename_linenumber(yytext); } curr_linenum--; handle_newline(); } /* handle_macro_line */ /* * Handler for C++ comments which starts with //. */ void handle_cplus_comment_line(void) { if (cflg == TRUE) lstrcat(curr_line, yytext); if (in_quote == TRUE) { lstrcat(qstring_buf, yytext); } else if ((in_comment == FALSE) && (in_skippable_string == FALSE)) { /* * If already in c comments, don't do anything. * Set both flags to TRUE here. * Both flags will be set to FALSE when newline * encounters. */ in_cplus_comment = TRUE; in_comment = TRUE; } } /* handle_cplus_comment_line */ /* * Handler for the comment start (slash asterisk) in input file. */ void handle_open_comment(void) { if (cflg == TRUE) lstrcat(curr_line, yytext); if (in_quote == TRUE) { lstrcat(qstring_buf, yytext); } else if ((in_comment == FALSE) && (in_skippable_string == FALSE)) { in_comment = TRUE; is_last_comment_line = FALSE; /* * If there is any comment extracted before accidently, * clean it up and start the new comment again. */ free_strlist(commhead); commhead = commtail = NULL; } } /* * Handler for the comment end (asterisk slash) in input file. */ void handle_close_comment(void) { if (cflg == TRUE) lstrcat(curr_line, yytext); if (in_quote == TRUE) { lstrcat(qstring_buf, yytext); } else if (in_skippable_string == FALSE) { in_comment = FALSE; is_last_comment_line = TRUE; } } /* * Handler for "gettext" in input file. */ void handle_gettext(void) { /* * If -t option is specified to extrct dcgettext, * don't do anything for gettext(). */ if (tflg == TRUE) { return; } num_nested_open_paren = 0; if (cflg == TRUE) lstrcat(curr_line, yytext); if (in_quote == TRUE) { lstrcat(qstring_buf, yytext); } else if (in_comment == FALSE) { in_gettext = TRUE; linenum_saved = curr_linenum; /* * gettext will be put into default domain .po file * curr_domain does not change for gettext. */ curr_domain[0] = '\0'; } } /* handle_gettext */ /* * Handler for "dgettext" in input file. */ void handle_dgettext(void) { /* * If -t option is specified to extrct dcgettext, * don't do anything for dgettext(). */ if (tflg == TRUE) { return; } num_nested_open_paren = 0; if (cflg == TRUE) lstrcat(curr_line, yytext); if (in_quote == TRUE) { lstrcat(qstring_buf, yytext); } else if (in_comment == FALSE) { in_dgettext = TRUE; linenum_saved = curr_linenum; /* * dgettext will be put into domain file specified. * curr_domain will follow. */ curr_domain[0] = '\0'; } } /* handle_dgettext */ /* * Handler for "dcgettext" in input file. */ void handle_dcgettext(void) { /* * dcgettext will be extracted only when -t flag is specified. */ if (tflg == FALSE) { return; } num_nested_open_paren = 0; is_first_comma_found = FALSE; if (cflg == TRUE) lstrcat(curr_line, yytext); if (in_quote == TRUE) { lstrcat(qstring_buf, yytext); } else if (in_comment == FALSE) { in_dcgettext = TRUE; linenum_saved = curr_linenum; /* * dcgettext will be put into domain file specified. * curr_domain will follow. */ curr_domain[0] = '\0'; } } /* handle_dcgettext */ /* * Handler for "textdomain" in input file. */ void handle_textdomain(void) { if (cflg == TRUE) lstrcat(curr_line, yytext); if (in_quote == TRUE) { lstrcat(qstring_buf, yytext); } else if (in_comment == FALSE) { in_textdomain = TRUE; linenum_saved = curr_linenum; curr_domain[0] = '\0'; } } /* handle_textdomain */ /* * Handler for '(' in input file. */ void handle_open_paren(void) { if (cflg == TRUE) lstrcat(curr_line, yytext); if (in_quote == TRUE) { lstrcat(qstring_buf, yytext); } else if (in_comment == FALSE) { if ((in_gettext == TRUE) || (in_dgettext == TRUE) || (in_dcgettext == TRUE) || (in_textdomain == TRUE)) { in_str = TRUE; num_nested_open_paren++; } } } /* handle_open_paren */ /* * Handler for ')' in input file. */ void handle_close_paren(void) { if (cflg == TRUE) lstrcat(curr_line, yytext); if (in_quote == TRUE) { lstrcat(qstring_buf, yytext); } else if (in_comment == FALSE) { if ((in_gettext == TRUE) || (in_dgettext == TRUE) || (in_dcgettext == TRUE) || (in_textdomain == TRUE)) { /* * If this is not the matching close paren with * the first open paren, no action is necessary. */ if (--num_nested_open_paren > 0) return; add_str_to_element_list(in_textdomain, curr_domain); in_str = FALSE; in_gettext = FALSE; in_dgettext = FALSE; in_dcgettext = FALSE; in_textdomain = FALSE; } else if (aflg == TRUE) { end_ansi_string(); } } } /* handle_close_paren */ /* * Handler for '\\n' in input file. * * This is a '\' followed by new line. * This can be treated like a new line except when this is a continuation * of a ANSI-C string. * If this is a part of ANSI string, treat the current line as a double * quoted string and the next line is the start of the double quoted * string. */ void handle_esc_newline(void) { if (cflg == TRUE) lstrcat(curr_line, "\\"); curr_linenum++; if (in_quote == TRUE) { add_qstring_to_str(); } else if ((in_comment == TRUE) || (is_last_comment_line == TRUE)) { if (in_cplus_comment == FALSE) { add_line_to_comment(); } } curr_line[0] = '\0'; } /* handle_esc_newline */ /* * Handler for '"' in input file. */ void handle_quote(void) { if (cflg == TRUE) lstrcat(curr_line, yytext); if (in_comment == TRUE) { /*EMPTY*/ } else if ((in_gettext == TRUE) || (in_dgettext == TRUE) || (in_dcgettext == TRUE) || (in_textdomain == TRUE)) { if (in_str == TRUE) { if (in_quote == FALSE) { in_quote = TRUE; } else { add_qstring_to_str(); in_quote = FALSE; } } } else if (aflg == TRUE) { /* * The quote is found outside of gettext, dgetext, and * textdomain. Everytime a quoted string is found, * add it to the string list. * in_str stays TRUE until ANSI string ends. */ if (in_str == TRUE) { if (in_quote == TRUE) { in_quote = FALSE; add_qstring_to_str(); } else { in_quote = TRUE; } } else { in_str = TRUE; in_quote = TRUE; linenum_saved = curr_linenum; } } else { in_skippable_string = (in_skippable_string == TRUE) ? FALSE : TRUE; } } /* handle_quote */ /* * Handler for ' ' or TAB in input file. */ void handle_spaces(void) { if (cflg == TRUE) lstrcat(curr_line, yytext); if (in_quote == TRUE) { lstrcat(qstring_buf, yytext); } } /* handle_spaces */ /* * Flattens a linked list containing ANSI string to the one string. */ static void copy_strlist_to_str(char *str, struct strlist_st *strlist) { struct strlist_st *p; str[0] = '\0'; if (strlist != NULL) { p = strlist; while (p != NULL) { if (p->str != NULL) { lstrcat(str, p->str); } p = p->next; } } } /* copy_strlist_to_str */ /* * Handler for ',' in input file. */ void handle_comma(void) { if (cflg == TRUE) lstrcat(curr_line, yytext); if (in_quote == TRUE) { lstrcat(qstring_buf, yytext); } else if (in_comment == FALSE) { if (in_str == TRUE) { if (in_dgettext == TRUE) { copy_strlist_to_str(curr_domain, strhead); free_strlist(strhead); strhead = strtail = NULL; } else if (in_dcgettext == TRUE) { /* * Ignore the second comma. */ if (is_first_comma_found == FALSE) { copy_strlist_to_str(curr_domain, strhead); free_strlist(strhead); strhead = strtail = NULL; is_first_comma_found = TRUE; } } else if (aflg == TRUE) { end_ansi_string(); } } } } /* handle_comma */ /* * Handler for any other character that does not have special handler. */ void handle_character(void) { if (cflg == TRUE) lstrcat(curr_line, yytext); if (in_quote == TRUE) { lstrcat(qstring_buf, yytext); } else if (in_comment == FALSE) { if (in_str == TRUE) { if (aflg == TRUE) { end_ansi_string(); } } } } /* handle_character */ /* * Handler for new line in input file. */ void handle_newline(void) { curr_linenum++; /* * in_quote is always FALSE here for ANSI-C code. */ if ((in_comment == TRUE) || (is_last_comment_line == TRUE)) { if (in_cplus_comment == TRUE) { in_cplus_comment = FALSE; in_comment = FALSE; } else { add_line_to_comment(); } } curr_line[0] = '\0'; /* * C++ comment always ends with new line. */ } /* handle_newline */ /* * Process ANSI string. */ static void end_ansi_string(void) { if ((aflg == TRUE) && (in_str == TRUE) && (in_gettext == FALSE) && (in_dgettext == FALSE) && (in_dcgettext == FALSE) && (in_textdomain == FALSE)) { add_str_to_element_list(FALSE, curr_domain); in_str = FALSE; } } /* end_ansi_string */ /* * Initialize global variables if necessary. */ static void initialize_globals(void) { default_domain = strdup(DEFAULT_DOMAIN); curr_domain[0] = '\0'; curr_file[0] = '\0'; qstring_buf[0] = '\0'; } /* initialize_globals() */ /* * Extract only string part when read a exclude file by removing * keywords (e.g. msgid, msgstr, # ) and heading and trailing blanks and * double quotes. */ static void trim_line(char *line) { int i, p, len; int first = 0; int last = 0; char c; len = strlen(line); /* * Find the position of the last non-whitespace character. */ i = len - 1; /*CONSTCOND*/ while (1) { c = line[i--]; if ((c != ' ') && (c != '\n') && (c != '\t')) { last = ++i; break; } } /* * Find the position of the first non-whitespace character * by skipping "msgid" initially. */ if (strncmp("msgid ", line, 6) == 0) { i = 5; } else if (strncmp("msgstr ", line, 7) == 0) { i = 6; } else if (strncmp("# ", line, 2) == 0) { i = 2; } else { i = 0; } /*CONSTCOND*/ while (1) { c = line[i++]; if ((c != ' ') && (c != '\n') && (c != '\t')) { first = --i; break; } } /* * For Backward compatibility, we consider both double quoted * string and non-quoted string. * The double quote is removed before being stored if exists. */ if (line[first] == '"') { first++; } if (line[last] == '"') { last--; } /* * Now copy the valid part of the string. */ p = first; for (i = 0; i <= (last-first); i++) { line[i] = line[p++]; } line [i] = '\0'; } /* trim_line */ /* * Read exclude file and stores it in the global linked list. */ static void read_exclude_file(void) { FILE *fp; struct exclude_st *tmp_excl; struct strlist_st *tail; int ignore_line; char line [MAX_STRING_LEN]; if ((fp = fopen(exclude_file, "r")) == NULL) { (void) fprintf(stderr, "ERROR, can't open exclude file: %s\n", exclude_file); exit(2); } ignore_line = TRUE; while (fgets(line, MAX_STRING_LEN, fp) != NULL) { /* * Line starting with # is a comment line and ignored. * Blank line is ignored, too. */ if ((line[0] == '\n') || (line[0] == '#')) { continue; } else if (strncmp(line, "msgstr", 6) == 0) { ignore_line = TRUE; } else if (strncmp(line, "domain", 6) == 0) { ignore_line = TRUE; } else if (strncmp(line, "msgid", 5) == 0) { ignore_line = FALSE; tmp_excl = new_exclude(); tmp_excl->exstr = new_strlist(); trim_line(line); tmp_excl->exstr->str = strdup(line); tail = tmp_excl->exstr; /* * Prepend new exclude string node to the list. */ tmp_excl->next = excl_head; excl_head = tmp_excl; } else { /* * If more than one line of string forms msgid, * append it to the string linked list. */ if (ignore_line == FALSE) { trim_line(line); tail->next = new_strlist(); tail->next->str = strdup(line); tail = tail->next; } } } /* while */ #ifdef DEBUG tmp_excl = excl_head; while (tmp_excl != NULL) { printf("============================\n"); tail = tmp_excl->exstr; while (tail != NULL) { printf("%s###\n", tail->str); tail = tail->next; } tmp_excl = tmp_excl->next; } #endif } /* read_exclude_file */ /* * Get next character from the string list containing ANSI style string. * This function returns three valus. (p, *m, *c). * p is returned by return value and, *m and *c are returned by changing * values in the location pointed. * * p : points node in the linked list for ANSI string. * Each node contains double quoted string. * m : The location of the next characters in the double quoted string * as integer index in the string. * When it gets to end of quoted string, the next node will be * read and m starts as zero for every new node. * c : Stores the value of the characterto be returned. */ static struct strlist_st * get_next_ch(struct strlist_st *p, int *m, char *c) { char ch, oct, hex; int value, i; /* * From the string list, find non-null string first. */ /*CONSTCOND*/ while (1) { if (p == NULL) { break; } else if (p->str == NULL) { p = p->next; } else if (p->str[*m] == '\0') { p = p->next; *m = 0; } else { break; } } /* * No more character is available. */ if (p == NULL) { *c = 0; return (NULL); } /* * Check if the character back slash. * If yes, ANSI defined escape sequence rule is used. */ if (p->str[*m] != '\\') { *c = p->str[*m]; *m = *m + 1; return (p); } else { /* * Get next character after '\'. */ *m = *m + 1; ch = p->str[*m]; switch (ch) { case 'a': *c = '\a'; break; case 'b': *c = '\b'; break; case 'f': *c = '\f'; break; case 'n': *c = '\n'; break; case 'r': *c = '\r'; break; case 't': *c = '\t'; break; case 'v': *c = '\v'; break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': /* * Get maximum of three octal digits. */ value = ch; for (i = 0; i < 2; i++) { *m = *m + 1; oct = p->str[*m]; if ((oct >= '0') && (oct <= '7')) { value = value * 8 + (oct - '0'); } else { *m = *m - 1; break; } } *c = value; #ifdef DEBUG /* (void) fprintf(stderr, "octal=%d\n", value); */ #endif break; case 'x': value = 0; /* * Remove all heading zeros first and * get one or two valuid hexadecimal charaters. */ *m = *m + 1; while (p->str[*m] == '0') { *m = *m + 1; } value = 0; for (i = 0; i < 2; i++) { hex = p->str[*m]; *m = *m + 1; if (isdigit(hex)) { value = value * 16 + (hex - '0'); } else if (isxdigit(hex)) { hex = tolower(hex); value = value * 16 + (hex - 'a' + 10); } else { *m = *m - 1; break; } } *c = value; #ifdef DEBUG (void) fprintf(stderr, "hex=%d\n", value); #endif *m = *m - 1; break; default : /* * Undefined by ANSI. * Just ignore "\". */ *c = p->str[*m]; break; } /* * Advance pointer to point the next character to be parsed. */ *m = *m + 1; return (p); } } /* get_next_ch */ /* * Compares two msgids. * Comparison is done by values, not by characters represented. * For example, '\t', '\011' and '0x9' are identical values. * Return values are same as in strcmp. * 1 if msgid1 > msgid2 * 0 if msgid1 = msgid2 * -1 if msgid1 < msgid2 */ static int msgidcmp(struct strlist_st *id1, struct strlist_st *id2) { char c1, c2; int m1, m2; m1 = 0; m2 = 0; /*CONSTCOND*/ while (1) { id1 = get_next_ch(id1, &m1, &c1); id2 = get_next_ch(id2, &m2, &c2); if ((c1 == 0) && (c2 == 0)) { return (0); } if (c1 > c2) { return (1); } else if (c1 < c2) { return (-1); } } /*NOTREACHED*/ } /* msgidcmp */ /* * Check if a ANSI string (which is a linked list itself) is a duplicate * of any string in the list of ANSI string. */ static int isduplicate(struct element_st *list, struct strlist_st *str) { struct element_st *p; if (list == NULL) { return (FALSE); } p = list; while (p != NULL) { if (p->msgid != NULL) { if (msgidcmp(p->msgid, str) == 0) { return (TRUE); } } p = p->next; } return (FALSE); } /* isduplicate */ /* * Extract a comment line and add to the linked list containing * comment block. * Each comment line is stored in the node. */ static void add_line_to_comment(void) { struct strlist_st *tmp_str; tmp_str = new_strlist(); tmp_str->str = strdup(curr_line); tmp_str->next = NULL; if (commhead == NULL) { /* Empty comment list */ commhead = tmp_str; commtail = tmp_str; } else { /* append it to the list */ commtail->next = tmp_str; commtail = commtail->next; } is_last_comment_line = FALSE; } /* add_line_to_comment */ /* * Add a double quoted string to the linked list containing ANSI string. */ static void add_qstring_to_str(void) { struct strlist_st *tmp_str; tmp_str = new_strlist(); tmp_str->str = strdup(qstring_buf); tmp_str->next = NULL; if (strhead == NULL) { /* Null ANSI string */ strhead = tmp_str; strtail = tmp_str; } else { /* Append it to the ANSI string linked list */ strtail->next = tmp_str; strtail = strtail->next; } qstring_buf[0] = '\0'; } /* add_qstring_to_str */ /* * Finds the head of domain nodes given domain name. */ static struct domain_st * find_domain_node(char *dname) { struct domain_st *tmp_dom, *p; /* * If -a option is specified everything will be written to the * default domain file. */ if (aflg == TRUE) { if (def_dom == NULL) { def_dom = new_domain(); } return (def_dom); } if ((dname == NULL) || (dname[0] == '\0') || (strcmp(dname, default_domain) == 0)) { if (def_dom == NULL) { def_dom = new_domain(); } if (strcmp(dname, default_domain) == 0) { (void) fprintf(stderr, "%s \"%s\" is used in dgettext " "of file:%s line:%d.\n", "Warning: default domain name", default_domain, curr_file, curr_linenum); } return (def_dom); } else { p = dom_head; while (p != NULL) { if (strcmp(p->dname, dname) == 0) { return (p); } p = p->next; } tmp_dom = new_domain(); tmp_dom->dname = strdup(dname); if (dom_head == NULL) { dom_head = tmp_dom; dom_tail = tmp_dom; } else { dom_tail->next = tmp_dom; dom_tail = dom_tail->next; } return (tmp_dom); } } /* find_domain_node */ /* * Frees the ANSI string linked list. */ static void free_strlist(struct strlist_st *ptr) { struct strlist_st *p; p = ptr; ptr = NULL; while (p != NULL) { ptr = p->next; free(p->str); free(p); p = ptr; } } /* free_strlist */ /* * Finds if a ANSI string is contained in the exclude file. */ static int isexcluded(struct strlist_st *strlist) { struct exclude_st *p; p = excl_head; while (p != NULL) { if (msgidcmp(p->exstr, strlist) == 0) { return (TRUE); } p = p->next; } return (FALSE); } /* isexcluded */ /* * Finds if a comment block is to be extracted. * * When -c option is specified, find out if comment block contains * comment-tag as a token separated by blanks. If it does, this * comment block is associated with the next msgid encountered. * Comment block is a linked list where each node contains one line * of comments. */ static int isextracted(struct strlist_st *strlist) { struct strlist_st *p; char *first, *pc; p = strlist; while (p != NULL) { first = strdup(p->str); while ((first != NULL) && (first[0] != '\0')) { pc = first; /*CONSTCOND*/ while (1) { if (*pc == '\0') { break; } else if ((*pc == ' ') || (*pc == '\t')) { *pc++ = '\0'; break; } pc++; } if (strcmp(first, comment_tag) == 0) { return (TRUE); } first = pc; } p = p->next; } /* while */ /* * Not found. */ return (FALSE); } /* isextracted */ /* * Adds ANSI string to the domain element list. */ static void add_str_to_element_list(int istextdomain, char *domain_list) { struct element_st *tmp_elem; struct element_st *p, *q; struct domain_st *tmp_dom; int result; /* * This can happen if something like gettext(USAGE) is used * and it is impossible to get msgid for this gettext. * Since -x option should be used in this kind of cases, * it is OK not to catch msgid. */ if (strhead == NULL) { return; } /* * The global variable curr_domain contains either NULL * for default_domain or domain name for dgettext(). */ tmp_dom = find_domain_node(domain_list); /* * If this msgid is in the exclude file, * then free the linked list and return. */ if ((istextdomain == FALSE) && (isexcluded(strhead) == TRUE)) { free_strlist(strhead); strhead = strtail = NULL; return; } tmp_elem = new_element(); tmp_elem->msgid = strhead; tmp_elem->istextdomain = istextdomain; /* * If -c option is specified and TAG matches, * then associate the comment to the next [d]gettext() calls * encountered in the source code. * textdomain() calls will not have any effect. */ if (istextdomain == FALSE) { if ((cflg == TRUE) && (commhead != NULL)) { if (isextracted(commhead) == TRUE) { tmp_elem->comment = commhead; } else { free_strlist(commhead); } commhead = commtail = NULL; } } tmp_elem->linenum = linenum_saved; tmp_elem->fname = strdup(curr_file); if (sflg == TRUE) { /* * If this is textdomain() call and -s option is specified, * append this node to the textdomain linked list. */ if (istextdomain == TRUE) { if (tmp_dom->textdomain_head == NULL) { tmp_dom->textdomain_head = tmp_elem; tmp_dom->textdomain_tail = tmp_elem; } else { tmp_dom->textdomain_tail->next = tmp_elem; tmp_dom->textdomain_tail = tmp_elem; } strhead = strtail = NULL; return; } /* * Insert the node to the properly sorted position. */ q = NULL; p = tmp_dom->gettext_head; while (p != NULL) { result = msgidcmp(strhead, p->msgid); if (result == 0) { /* * Duplicate id. Do not store. */ free_strlist(strhead); strhead = strtail = NULL; return; } else if (result > 0) { /* move to the next node */ q = p; p = p->next; } else { tmp_elem->next = p; if (q != NULL) { q->next = tmp_elem; } else { tmp_dom->gettext_head = tmp_elem; } strhead = strtail = NULL; return; } } /* while */ /* * New msgid is the largest or empty list. */ if (q != NULL) { /* largest case */ q->next = tmp_elem; } else { /* empty list */ tmp_dom->gettext_head = tmp_elem; } } else { /* * Check if this msgid is already in the same domain. */ if (tmp_dom != NULL) { if (isduplicate(tmp_dom->gettext_head, tmp_elem->msgid) == TRUE) { tmp_elem->isduplicate = TRUE; } } /* * If -s option is not specified, then everything * is stored in gettext linked list. */ if (tmp_dom->gettext_head == NULL) { tmp_dom->gettext_head = tmp_elem; tmp_dom->gettext_tail = tmp_elem; } else { tmp_dom->gettext_tail->next = tmp_elem; tmp_dom->gettext_tail = tmp_elem; } } strhead = strtail = NULL; } /* add_str_to_element_list */ /* * Write all domain linked list to the files. */ static void write_all_files(void) { struct domain_st *tmp; /* * Write out default domain file. */ write_one_file(def_dom); /* * If dgettext() exists and -a option is not used, * then there are non-empty linked list. */ tmp = dom_head; while (tmp != NULL) { write_one_file(tmp); tmp = tmp->next; } } /* write_all_files */ /* * add an element_st list to the linked list. */ static void add_node_to_polist(struct element_st **pohead, struct element_st **potail, struct element_st *elem) { if (elem == NULL) { return; } if (*pohead == NULL) { *pohead = *potail = elem; } else { (*potail)->next = elem; *potail = (*potail)->next; } } /* add_node_to_polist */ #define INIT_STATE 0 #define IN_MSGID 1 #define IN_MSGSTR 2 #define IN_COMMENT 3 /* * Reads existing po file into the linked list and returns the head * of the linked list. */ static struct element_st * read_po(char *fname) { struct element_st *tmp_elem = NULL; struct element_st *ehead = NULL, *etail = NULL; struct strlist_st *comment_tail = NULL; struct strlist_st *msgid_tail = NULL; struct strlist_st *msgstr_tail = NULL; int state = INIT_STATE; char line [MAX_STRING_LEN]; FILE *fp; if ((fp = fopen(fname, "r")) == NULL) { return (NULL); } while (fgets(line, MAX_STRING_LEN, fp) != NULL) { /* * Line starting with # is a comment line and ignored. * Blank line is ignored, too. */ if (line[0] == '\n') { continue; } else if (line[0] == '#') { /* * If tmp_elem is not NULL, there is msgid pair * stored. Therefore, add it. */ if ((tmp_elem != NULL) && (state == IN_MSGSTR)) { add_node_to_polist(&ehead, &etail, tmp_elem); } if ((state == INIT_STATE) || (state == IN_MSGSTR)) { state = IN_COMMENT; tmp_elem = new_element(); tmp_elem->comment = comment_tail = new_strlist(); /* * remove new line and skip "# " * in the beginning of the existing * comment line. */ line[strlen(line)-1] = 0; comment_tail->str = strdup(line+2); } else if (state == IN_COMMENT) { comment_tail->next = new_strlist(); comment_tail = comment_tail->next; /* * remove new line and skip "# " * in the beginning of the existing * comment line. */ line[strlen(line)-1] = 0; comment_tail->str = strdup(line+2); } } else if (strncmp(line, "domain", 6) == 0) { /* ignore domain line */ continue; } else if (strncmp(line, "msgid", 5) == 0) { if (state == IN_MSGSTR) { add_node_to_polist(&ehead, &etail, tmp_elem); tmp_elem = new_element(); } else if (state == INIT_STATE) { tmp_elem = new_element(); } state = IN_MSGID; trim_line(line); tmp_elem->msgid = msgid_tail = new_strlist(); msgid_tail->str = strdup(line); } else if (strncmp(line, "msgstr", 6) == 0) { state = IN_MSGSTR; trim_line(line); tmp_elem->msgstr = msgstr_tail = new_strlist(); msgstr_tail->str = strdup(line); } else { /* * If more than one line of string forms msgid, * append it to the string linked list. */ if (state == IN_MSGID) { trim_line(line); msgid_tail->next = new_strlist(); msgid_tail = msgid_tail->next; msgid_tail->str = strdup(line); } else if (state == IN_MSGSTR) { trim_line(line); msgstr_tail->next = new_strlist(); msgstr_tail = msgstr_tail->next; msgstr_tail->str = strdup(line); } } } /* while */ /* * To insert the last msgid pair. */ if (tmp_elem != NULL) { add_node_to_polist(&ehead, &etail, tmp_elem); } #ifdef DEBUG { struct domain_st *tmp_domain = new_domain(); char tmpstr[256]; sprintf(tmpstr, "existing_po file : <%s>", fname); tmp_domain->dname = strdup(tmpstr); tmp_domain->gettext_head = ehead; printf("======= existing po file <%s> ========\n", fname); print_one_domain(tmp_domain); } #endif /* DEBUG */ (void) fclose(fp); return (ehead); } /* read_po */ /* * This function will append the second list to the first list. * If the msgid in the second list contains msgid in the first list, * it will be marked as duplicate. */ static struct element_st * append_list(struct element_st *l1, struct element_st *l2) { struct element_st *p = NULL, *q = NULL, *l1_tail = NULL; if (l1 == NULL) return (l2); if (l2 == NULL) return (l1); /* * in this while loop, just mark isduplicate field of node in the * l2 list if the same msgid exists in l1 list. */ p = l2; while (p != NULL) { q = l1; while (q != NULL) { if (msgidcmp(p->msgid, q->msgid) == 0) { p->isduplicate = TRUE; break; } q = q->next; } p = p->next; } /* Now connect two linked lists. */ l1_tail = l1; while (l1_tail->next != NULL) { if (l1->next == NULL) break; l1_tail = l1_tail-> next; } l1_tail->next = l2; return (l1); } /* append_list */ /* * Writes one domain list to the file. */ static void write_one_file(struct domain_st *head) { FILE *fp; char fname [MAX_PATH_LEN]; char dname [MAX_DOMAIN_LEN]; struct element_st *p; struct element_st *existing_po_list; /* * If head is NULL, then it still has to create .po file * so that it will guarantee that the previous .po file was * alwasys deleted. * This is why checking NULL pointer has been moved to after * creating .po file. */ /* * If domain name is NULL, it is the default domain list. * The domain name is either "messages" or specified by option -d. * The default domain name is contained in default_domain variable. */ dname[0] = '\0'; if ((head != NULL) && (head->dname != NULL)) { (void) strcpy(dname, head->dname); } else { (void) strcpy(dname, default_domain); } /* * path is the current directory if not specified by option -p. */ fname[0] = 0; if (pflg == TRUE) { (void) strcat(fname, pathname); (void) strcat(fname, "/"); } (void) strcat(fname, dname); (void) strcat(fname, ".po"); /* * If -j flag is specified, read exsiting .po file and * append the current list to the end of the list read from * the existing .po file. */ if (jflg == TRUE) { /* * If head is NULL, we don't have to change existing file. * Therefore, just return it. */ if (head == NULL) { return; } existing_po_list = read_po(fname); head->gettext_head = append_list(existing_po_list, head->gettext_head); #ifdef DEBUG if (head->dname != NULL) { printf("===after merge (-j option): <%s>===\n", head->dname); } else { printf("===after merge (-j option): ===\n"); } print_one_domain(head); #endif } /* if jflg */ if ((fp = fopen(fname, "w")) == NULL) { (void) fprintf(stderr, "ERROR, can't open output file: %s\n", fname); exit(2); } (void) fprintf(fp, "domain \"%s\"\n", dname); /* See comments above in the beginning of this function */ if (head == NULL) return; /* * There are separate storage for textdomain() calls if * -s option is used (textdomain_head linked list). * Otherwise, textdomain() is mixed with gettext(0 and dgettext(). * If mixed, the boolean varaible istextdomain is used to see * if the current node contains textdomain() or [d]gettext(). */ if (sflg == TRUE) { p = head->textdomain_head; while (p != NULL) { /* * textdomain output line already contains * FIle name and line number information. * Therefore, does not have to check for nflg. */ output_textdomain(fp, p); p = p->next; } } p = head->gettext_head; while (p != NULL) { /* * Comment is printed only if -c is used and * associated with gettext or dgettext. * textdomain is not associated with comments. * Changes: * comments should be extracted in case of -j option * because there are read from exising file. */ if (((cflg == TRUE) || (jflg == TRUE)) && (p->istextdomain != TRUE)) { output_comment(fp, p->comment); } /* * If -n is used, then file number and line number * information is printed. * In case of textdomain(), this information is redundant * and is not printed. * If linenum is 0, it means this information has been * read from existing po file and it already contains * file and line number info as a comment line. So, it * should not printed in such case. */ if ((nflg == TRUE) && (p->istextdomain == FALSE) && (p->linenum > 0)) { (void) fprintf(fp, "# File:%s, line:%d\n", p->fname, p->linenum); } /* * Depending on the type of node, output textdomain comment * or msgid. */ if ((sflg == FALSE) && (p->istextdomain == TRUE)) { output_textdomain(fp, p); } else { output_msgid(fp, p->msgid, p->isduplicate); } p = p->next; } /* while */ (void) fclose(fp); } /* write_one_file */ /* * Prints out textdomain call as a comment line with file name and * the line number information. */ static void output_textdomain(FILE *fp, struct element_st *p) { if (p == NULL) return; /* * Write textdomain() line as a comment. */ (void) fprintf(fp, "# File:%s, line:%d, textdomain(\"%s\");\n", p->fname, p->linenum, p->msgid->str); } /* output_textdomain */ /* * Prints out comments from linked list. */ static void output_comment(FILE *fp, struct strlist_st *p) { if (p == NULL) return; /* * Write comment section. */ while (p != NULL) { (void) fprintf(fp, "# %s\n", p->str); p = p->next; } } /* output_comment */ /* * Prints out msgid along with msgstr. */ static void output_msgid(FILE *fp, struct strlist_st *p, int duplicate) { struct strlist_st *q; if (p == NULL) return; /* * Write msgid section. * If duplciate flag is ON, prepend "# " in front of every line * so that they are considered as comment lines in .po file. */ if (duplicate == TRUE) { (void) fprintf(fp, "# "); } (void) fprintf(fp, "msgid \"%s\"\n", p->str); q = p->next; while (q != NULL) { if (duplicate == TRUE) { (void) fprintf(fp, "# "); } (void) fprintf(fp, " \"%s\"\n", q->str); q = q->next; } /* * Write msgstr section. * if -M option is specified, append to msgid. * if -m option is specified, prepend to msgid. */ if (duplicate == TRUE) { (void) fprintf(fp, "# "); } if ((mflg == TRUE) || (Mflg == TRUE)) { if (mflg == TRUE) { /* * If single line msgid, add suffix to the same line */ if ((Mflg == TRUE) && (p->next == NULL)) { /* -M and -m and single line case */ (void) fprintf(fp, "msgstr \"%s%s%s\"\n", prefix, p->str, suffix); } else { /* -M and -m and multi line case */ (void) fprintf(fp, "msgstr \"%s%s\"\n", prefix, p->str); } } else { if ((Mflg == TRUE) && (p->next == NULL)) { /* -M only with single line case */ (void) fprintf(fp, "msgstr \"%s%s\"\n", p->str, suffix); } else { /* -M only with multi line case */ (void) fprintf(fp, "msgstr \"%s\"\n", p->str); } } q = p->next; while (q != NULL) { if (duplicate == TRUE) { (void) fprintf(fp, "# "); } (void) fprintf(fp, " \"%s\"\n", q->str); q = q->next; } /* * If multi line msgid, add suffix after the last line. */ if ((Mflg == TRUE) && (p->next != NULL) && (suffix[0] != '\0')) { (void) fprintf(fp, " \"%s\"\n", suffix); } } else { (void) fprintf(fp, "msgstr\n"); } } /* output_msgid */ /* * Malloc a new element node and initialize fields. */ static struct element_st * new_element(void) { struct element_st *tmp; tmp = (struct element_st *)malloc(sizeof (struct element_st)); tmp->istextdomain = FALSE; tmp->isduplicate = FALSE; tmp->msgid = NULL; tmp->msgstr = NULL; tmp->comment = NULL; tmp->fname = NULL; tmp->linenum = 0; tmp->next = NULL; return (tmp); } /* new_element */ /* * Malloc a new domain node and initialize fields. */ static struct domain_st * new_domain(void) { struct domain_st *tmp; tmp = (struct domain_st *)malloc(sizeof (struct domain_st)); tmp->dname = NULL; tmp->gettext_head = NULL; tmp->gettext_tail = NULL; tmp->textdomain_head = NULL; tmp->textdomain_tail = NULL; tmp->next = NULL; return (tmp); } /* new_domain */ /* * Malloc a new string list node and initialize fields. */ static struct strlist_st * new_strlist(void) { struct strlist_st *tmp; tmp = (struct strlist_st *)malloc(sizeof (struct strlist_st)); tmp->str = NULL; tmp->next = NULL; return (tmp); } /* new_strlist */ /* * Malloc a new exclude string list node and initialize fields. */ static struct exclude_st * new_exclude(void) { struct exclude_st *tmp; tmp = (struct exclude_st *)malloc(sizeof (struct exclude_st)); tmp->exstr = NULL; tmp->next = NULL; return (tmp); } /* new_exclude */ /* * Local version of strcat to keep within maximum string size. */ static void lstrcat(char *s1, const char *s2) { char *es1 = &s1[MAX_STRING_LEN]; char *ss1 = s1; while (*s1++) ; --s1; while (*s1++ = *s2++) if (s1 >= es1) { s1[-1] = '\0'; if ((in_comment == TRUE || in_quote == TRUE) && (warn_linenum != curr_linenum)) { if (stdin_only == FALSE) { (void) fprintf(stderr, "WARNING: file %s line %d exceeds "\ "%d characters: \"%15.15s\"\n", curr_file, curr_linenum, MAX_STRING_LEN, ss1); } else { (void) fprintf(stderr, "WARNING: line %d exceeds "\ "%d characters: \"%15.15s\"\n", curr_linenum, MAX_STRING_LEN, ss1); } warn_linenum = curr_linenum; } break; } } /* lstrcat */ #ifdef DEBUG /* * Debug print routine. Compiled only with DEBUG on. */ void print_element_list(struct element_st *q) { struct strlist_st *r; while (q != NULL) { printf(" istextdomain = %d\n", q->istextdomain); printf(" isduplicate = %d\n", q->isduplicate); if ((q->msgid != NULL) && (q->msgid->str != NULL)) { printf(" msgid = <%s>\n", q->msgid->str); r = q->msgid->next; while (r != NULL) { printf(" <%s>\n", r->str); r = r->next; } } else { printf(" msgid = \n"); } if ((q->msgstr != NULL) && (q->msgstr->str != NULL)) { printf(" msgstr= <%s>\n", q->msgstr->str); r = q->msgstr->next; while (r != NULL) { printf(" <%s>\n", r->str); r = r->next; } } else { printf(" msgstr= \n"); } if (q->comment == NULL) { printf(" comment = \n"); } else { printf(" comment = <%s>\n", q->comment->str); r = q->comment->next; while (r != NULL) { printf(" <%s>\n", r->str); r = r->next; } } if (q->fname == NULL) { printf(" fname = \n"); } else { printf(" fname = <%s>\n", q->fname); } printf(" linenum = %d\n", q->linenum); printf("\n"); q = q->next; } } /* * Debug print routine. Compiled only with DEBUG on. */ void print_one_domain(struct domain_st *p) { struct element_st *q; if (p == NULL) { printf("domain pointer = \n"); return; } else if (p->dname == NULL) { printf("domain_name = <%s>\n", ""); } else { printf("domain_name = <%s>\n", p->dname); } q = p->gettext_head; print_element_list(q); q = p->textdomain_head; print_element_list(q); } /* print_one_domain */ void print_all_domain(struct domain_st *dom_list) { struct domain_st *p; struct element_st *q; p = dom_list; while (p != NULL) { print_one_domain(p); p = p->next; } /* while */ } /* print_all_domain */ #endif %{ /* * 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 (c) 1991, 2001 by Sun Microsystems, Inc. * All rights reserved. */ #include #include #define TRUE 1 #define FALSE 0 extern int stdin_only; extern char curr_file[]; /* Name of file currently being read. */ extern int curr_linenum; /* line number in the current input file */ extern int warn_linenum; /* line number of current warning message */ extern int optind; extern int gargc; extern char **gargv; extern void handle_macro_line(void); extern void handle_cplus_comment_line(void); extern void handle_open_comment(void); extern void handle_close_comment(void); extern void handle_gettext(void); extern void handle_dgettext(void); extern void handle_dcgettext(void); extern void handle_textdomain(void); extern void handle_character(void); extern void handle_open_paren(void); extern void handle_close_paren(void); extern void handle_esc_newline(void); extern void handle_comma(void); extern void handle_newline(void); extern void handle_quote(void); extern void handle_spaces(void); extern void handle_spaces(void); extern void handle_character(void); /* * The following lex rule basically wants to recognize tokens * that can change the current state of scanning source code. * Evertime such tokens are recognized, the specific handler will be * executed. All other tokens are treated as regular characters and * they are all handled the same way. * The tricky part was not to miss any specification in ANSI-C code * that looks like a meaningful token but not a meaningful one and * should be treated as regular characters. * For example, * c= '"';d='"'; printf("\"" "\(\)\\\""); * c = ABgettext("Not a gettext"); * c = gettextXY("Not a gettext"); * c = ABgettextXY("Not a gettext"); */ %} IDCHARS [a-zA-Z0-9_] %% ^#(.*\\\n)**.*\n { handle_macro_line(); } \/\/ { handle_cplus_comment_line(); } \/\* { handle_open_comment(); } \*\/ { handle_close_comment(); } dcgettext { handle_dcgettext(); } dgettext { handle_dgettext(); } gettext { handle_gettext(); } textdomain { handle_textdomain(); } {IDCHARS}+ | \'\"\' | \'\\\"\' | \\\\ | \\\" | \\\( | \\\) { handle_character(); } \( { handle_open_paren(); } \) { handle_close_paren(); } \\\n { handle_esc_newline(); } \, { handle_comma(); } \n { handle_newline(); } \" { handle_quote(); } " " { handle_spaces(); } "\t" { handle_spaces(); } . { handle_character(); } %% /* * Since multiple files can be processed, yywrap() should keep feeding * all input files specified. */ int yywrap(void) { FILE *fp; if ((optind >= gargc) || (stdin_only == TRUE)) { return (1); } else { /* * gargv still contains not-processed input files. */ (void) freopen(gargv[optind], "r", stdin); if ((fp = freopen(gargv[optind], "r", stdin)) == NULL) { (void) fprintf(stderr, "ERROR, can't open input file: %s\n", gargv[optind]); } #ifdef DEBUG (void) printf("In yywrap(), opening file %d, <%s>\n", optind, gargv[optind]); #endif /* * Reset global file name and line number for the new file. */ (void) strcpy(curr_file, gargv[optind]); curr_linenum = 0; warn_linenum = 0; optind++; return (0); } } /* yywrap */