Home | History | Annotate | Download | only in common
      1 /*
      2  * CDDL HEADER START
      3  *
      4  * The contents of this file are subject to the terms of the
      5  * Common Development and Distribution License (the "License").
      6  * You may not use this file except in compliance with the License.
      7  *
      8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
      9  * or http://www.opensolaris.org/os/licensing.
     10  * See the License for the specific language governing permissions
     11  * and limitations under the License.
     12  *
     13  * When distributing Covered Code, include this CDDL HEADER in each
     14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
     15  * If applicable, add the following below this CDDL HEADER, with the
     16  * fields enclosed by brackets "[]" replaced with your own identifying
     17  * information: Portions Copyright [yyyy] [name of copyright owner]
     18  *
     19  * CDDL HEADER END
     20  */
     21 /*
     22  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
     23  * Use is subject to license terms.
     24  *
     25  * sgsmsg generates several message files from an input template file.  Messages
     26  * are constructed for use with gettext(3i) - the default - or catgets(3c).  The
     27  * files generate are:
     28  *
     29  * msg.h	a header file containing definitions for each message.  The -h
     30  *		option triggers the creation of these definitions and specifies
     31  *		the name to use.
     32  *
     33  * msg.c	a data array of message strings.  The msg.h definitions are
     34  *		offsets into this array.  The -d option triggers the creation of
     35  *		these definitions and specifies the name to use.
     36  *
     37  * messages	a message file suitable for catgets(3c) or gettext(3i) use.  The
     38  *		-m option triggers this output and specifies the filename to be
     39  *		used.
     40  *
     41  * The template file is processed based on the first character of each line:
     42  *
     43  * # or $	entries are copied (as is) to the message file (messages).
     44  *
     45  * @ token(s)	entries are translated.  Two translations are possible dependent
     46  *		on whether one or more tokens are supplied:
     47  *
     48  *		A single token is interpreted as one of two reserved message
     49  *		output indicators, or a message identifier.  The reserved output
     50  *		indicator _START_ enables output to the message file - Note that
     51  *		the occurance of any other @ token will also enable message
     52  *		output.  The reserved output indicator _END_ disables output to
     53  *		the message file.  The use of these two indicators provides for
     54  *		only those message strings that require translation to be output
     55  *		to the message file.
     56  *
     57  *		Besides the reserved output indicators, a single token is taken
     58  *		to be a message identifier which will be subsituted for a
     59  *		`setid' for catgets(3c) output, or a `domain' name for
     60  *		gettext(3i) output.  This value is determine by substituting the
     61  *		token for the associated definition found in the message
     62  *		identifier file (specified with the -i option).
     63  *
     64  *		Multiple tokens are taken to be a message definition followed by
     65  *		the associated message string.  The message string is copied to
     66  *		the data array being built in msg.c.  The index into this array
     67  *		becomes the `message' identifier created in the msg.h file.
     68  */
     69 
     70 #include	<fcntl.h>
     71 #include	<stdlib.h>
     72 #include	<stdio.h>
     73 #include	<unistd.h>
     74 #include	<limits.h>
     75 #include	<string.h>
     76 #include	<ctype.h>
     77 #include	<errno.h>
     78 #include	<sys/param.h>
     79 
     80 #include	<sgs.h>
     81 #include	<_string_table.h>
     82 
     83 /*
     84  * Define any error message strings.
     85  */
     86 static const char
     87 	* Errmsg_malt =	"sgsmsg: file %s: line %d: malformed input "
     88 			"at line\n",
     89 	* Errmsg_nmem =	"sgsmsg: memory allocation failed: %s\n",
     90 	* Errmsg_opne =	"sgsmsg: file %s: open failed: %s\n",
     91 	* Errmsg_wrte =	"sgsmsg: file %s: write failed: %s\n",
     92 	* Errmsg_read =	"sgsmsg: file %s: read failed %s\n",
     93 	* Errmsg_stnw =	"sgsmsg: st_new(): failed: %s\n",
     94 	* Errmsg_stin =	"sgsmsg: Str_tbl insert failed: %s\n",
     95 	* Errmsg_mnfn =	"sgsmsg: message not found in Str_tbl: %s\n",
     96 	* Errmsg_use  =	"usage: sgsmsg [-clv] [-d mesgdata] [-h mesgdefs] "
     97 			"[-m messages] [-n name] [-i mesgident] file ...\n";
     98 
     99 /*
    100  * Define all output filenames and associated descriptors.
    101  */
    102 static FILE	*fddefs, *fddata, *fdmsgs, *fdmids, *fddesc;
    103 static char	*fldefs, *fldata, *flmsgs, *flmids, *fldesc;
    104 static FILE	*fdlint;
    105 static char	fllint[MAXPATHLEN];
    106 
    107 static uint_t		vflag;	/* verbose flag */
    108 static Str_tbl		*stp;	/* string table */
    109 
    110 /*
    111  * Define any default strings.
    112  */
    113 static const char
    114 	*nmlint =	"/tmp/sgsmsg.lint",
    115 	*interface =	"sgs_msg",
    116 	*start =	"_START_",
    117 	*end =		"_END_";
    118 
    119 /*
    120  * Define any default flags and data items.
    121  */
    122 static int	cflag = 0, lflag = 0, prtmsgs = 0, line, ptr = 1, msgid = 0;
    123 static char	*mesgid = 0, *setid = 0, *domain = 0;
    124 
    125 typedef struct msg_string {
    126 	char			*ms_defn;
    127 	char			*ms_message;
    128 	struct msg_string	*ms_next;
    129 } msg_string;
    130 
    131 static msg_string	*msg_head;
    132 static msg_string	*msg_tail;
    133 
    134 /*
    135  * message_append() is responsible for both inserting strings into
    136  * the master Str_tbl as well as maintaining a list of the
    137  * DEFINITIONS associated with each string.
    138  *
    139  * The list of strings is traversed at the end once the full
    140  * Str_tbl has been constructed - and string offsets can be
    141  * assigned.
    142  */
    143 static void
    144 message_append(const char *defn, const char *message)
    145 {
    146 	msg_string	*msg;
    147 	if ((msg = calloc(sizeof (msg_string), 1)) == 0) {
    148 		(void) fprintf(stderr, Errmsg_nmem, strerror(errno));
    149 		exit(1);
    150 	}
    151 
    152 	/*
    153 	 * Initialize the string table.
    154 	 */
    155 	if ((stp == 0) && ((stp = st_new(FLG_STNEW_COMPRESS)) == NULL)) {
    156 		(void) fprintf(stderr, Errmsg_stnw, strerror(errno));
    157 		exit(1);
    158 	}
    159 
    160 
    161 	if ((msg->ms_defn = strdup(defn)) == 0) {
    162 		(void) fprintf(stderr, Errmsg_nmem, strerror(errno));
    163 		exit(1);
    164 	}
    165 	if ((msg->ms_message = strdup(message)) == 0) {
    166 		(void) fprintf(stderr, Errmsg_nmem, strerror(errno));
    167 		exit(1);
    168 	}
    169 
    170 	if (st_insert(stp, msg->ms_message) == -1) {
    171 		(void) fprintf(stderr, Errmsg_stin,
    172 		    message);
    173 		exit(1);
    174 	}
    175 
    176 	if (msg_head == 0) {
    177 		msg_head = msg_tail = msg;
    178 		return;
    179 	}
    180 	msg_tail->ms_next = msg;
    181 	msg_tail = msg;
    182 }
    183 
    184 /*
    185  * Initialize a setid value.  Given a setid definition determine its numeric
    186  * value from the specified message identifier file (specified with the -i
    187  * option).  Return a pointer to the numeric string.
    188  */
    189 static int
    190 getmesgid(char *id)
    191 {
    192 	char	*buffer, *token, *_mesgid = 0, *_setid = 0, *_domain = 0;
    193 
    194 	/*
    195 	 * If we're being asked to interpret a message id but the user didn't
    196 	 * provide the required message identifier file (-i option) we're in
    197 	 * trouble.
    198 	 */
    199 	if (flmids == 0) {
    200 		(void) fprintf(stderr, "sgsmsg: file %s: line %d: mesgid %s: "
    201 		    "unable to process mesgid\n\t"
    202 		    "no message identifier file specified "
    203 		    "(see -i option)\n", fldesc, line, id);
    204 		return (1);
    205 	}
    206 
    207 	if ((buffer = malloc(LINE_MAX)) == 0) {
    208 		(void) fprintf(stderr, Errmsg_nmem, strerror(errno));
    209 		return (1);
    210 	}
    211 
    212 	/*
    213 	 * Read the message identifier file and locate the required mesgid.
    214 	 */
    215 	rewind(fdmids);
    216 	while (fgets(buffer, LINE_MAX, fdmids) != NULL) {
    217 		if ((token = strstr(buffer, id)) == NULL)
    218 			continue;
    219 
    220 		/*
    221 		 * Establish individual strings for the mesgid, setid and domain
    222 		 * values.
    223 		 */
    224 		_mesgid = token;
    225 		while (!(isspace(*token)))
    226 			token++;
    227 		*token++ = 0;
    228 
    229 		while (isspace(*token))
    230 			token++;
    231 		_setid = token;
    232 		while (!(isspace(*token)))
    233 			token++;
    234 		*token++ = 0;
    235 
    236 		while (isspace(*token))
    237 			token++;
    238 		_domain = token;
    239 		while (!(isspace(*token)))
    240 			token++;
    241 		*token = 0;
    242 		break;
    243 	}
    244 
    245 	/*
    246 	 * Did we find a match?
    247 	 */
    248 	if ((_mesgid == 0) || (_setid == 0) || (_domain == 0)) {
    249 		(void) fprintf(stderr, "sgsmsg: file %s: line %d: mesgid %s: "
    250 		    "unable to process mesgid\n\t"
    251 		    "identifier does not exist in file %s\n",
    252 		    fldesc, line, id, flmids);
    253 		return (1);
    254 	}
    255 
    256 	/*
    257 	 * Have we been here before?
    258 	 */
    259 	if (mesgid) {
    260 		if (cflag == 1) {
    261 			/*
    262 			 * If we're being asked to process more than one mesgid
    263 			 * warn the user that only one mesgid can be used for
    264 			 * the catgets(3c) call.
    265 			 */
    266 			(void) fprintf(stderr, "sgsmsg: file %s: line %d: "
    267 			    "setid %s: warning: multiple mesgids "
    268 			    "encountered\n\t"
    269 			    "last setting used in messaging code\n",
    270 			    fldesc, line, id);
    271 		}
    272 	}
    273 
    274 	mesgid = _mesgid;
    275 	setid = _setid;
    276 	domain = _domain;
    277 
    278 	/*
    279 	 * Generate the message file output (insure output flag is enabled).
    280 	 */
    281 	if (prtmsgs != -1)
    282 		prtmsgs = 1;
    283 	if (fdmsgs && (prtmsgs == 1)) {
    284 		if (cflag == 1) {
    285 			if (fprintf(fdmsgs, "$quote \"\n$set %s\n",
    286 			    setid) < 0) {
    287 				(void) fprintf(stderr, Errmsg_wrte, flmsgs,
    288 				    strerror(errno));
    289 				return (1);
    290 			}
    291 		} else {
    292 			if (fprintf(fdmsgs, "domain\t\"%s\"\n", domain) < 0) {
    293 				(void) fprintf(stderr, Errmsg_wrte, flmsgs,
    294 				    strerror(errno));
    295 				return (1);
    296 			}
    297 		}
    298 	}
    299 
    300 	/*
    301 	 * For catgets(3c) output generate a setid definition in the message
    302 	 * definition file.
    303 	 */
    304 	if (fddefs && (cflag == 1) &&
    305 	    (fprintf(fddefs, "#define\t%s\t%s\n\n", mesgid, setid) < 0)) {
    306 		(void) fprintf(stderr, Errmsg_wrte, fldefs, strerror(errno));
    307 		return (1);
    308 	}
    309 
    310 	return (0);
    311 }
    312 
    313 /*
    314  * Dump contents of String Table to standard out
    315  */
    316 static void
    317 dump_stringtab(Str_tbl *stp)
    318 {
    319 	uint_t	cnt;
    320 
    321 	if ((stp->st_flags & FLG_STTAB_COMPRESS) == 0) {
    322 		(void) printf("string table full size: %ld: uncompressed\n",
    323 		    stp->st_fullstrsize);
    324 		return;
    325 	}
    326 
    327 	(void) printf("string table full size: %ld compressed down to: %ld\n\n",
    328 	    stp->st_fullstrsize, stp->st_strsize);
    329 	(void) printf("string table compression information [%d buckets]:\n",
    330 	    stp->st_hbckcnt);
    331 
    332 	for (cnt = 0; cnt < stp->st_hbckcnt; cnt++) {
    333 		Str_hash	*sthash = stp->st_hashbcks[cnt];
    334 
    335 		if (sthash == 0)
    336 			continue;
    337 
    338 		(void) printf(" bucket: [%d]\n", cnt);
    339 
    340 		while (sthash) {
    341 			size_t	stroff = sthash->hi_mstr->sm_strlen -
    342 			    sthash->hi_strlen;
    343 
    344 			if (stroff == 0) {
    345 				(void) printf("  [%ld]: '%s'  <master>\n",
    346 				    sthash->hi_refcnt, sthash->hi_mstr->sm_str);
    347 			} else {
    348 				(void) printf("  [%ld]: '%s'  <suffix of: "
    349 				    "'%s'>\n", sthash->hi_refcnt,
    350 				    &sthash->hi_mstr->sm_str[stroff],
    351 				    sthash->hi_mstr->sm_str);
    352 			}
    353 			sthash = sthash->hi_next;
    354 		}
    355 	}
    356 }
    357 
    358 /*
    359  * Initialize the message definition header file stream.
    360  */
    361 static int
    362 init_defs(void)
    363 {
    364 	static char	guard[FILENAME_MAX + 6];
    365 	char		*optr;
    366 	const char	*iptr, *_ptr;
    367 
    368 	/*
    369 	 * Establish a header guard name using the files basename.
    370 	 */
    371 	for (iptr = 0, _ptr = fldefs; _ptr && (*_ptr != '\0'); _ptr++) {
    372 		if (*_ptr == '/')
    373 			iptr = _ptr + 1;
    374 	}
    375 	if (iptr == 0)
    376 		iptr = fldefs;
    377 
    378 	optr = guard;
    379 	for (*optr++ = '_'; iptr && (*iptr != '\0'); iptr++, optr++) {
    380 		if (*iptr == '.') {
    381 			*optr++ = '_';
    382 			*optr++ = 'D';
    383 			*optr++ = 'O';
    384 			*optr++ = 'T';
    385 			*optr = '_';
    386 		} else
    387 			*optr = toupper(*iptr);
    388 	}
    389 
    390 	if (fprintf(fddefs, "#ifndef\t%s\n#define\t%s\n\n", guard, guard) < 0) {
    391 		(void) fprintf(stderr, Errmsg_wrte, fldefs, strerror(errno));
    392 		return (1);
    393 	}
    394 
    395 	if (fprintf(fddefs, "#include <sgsmsg.h>\t/* Msg typedef */\n\n") < 0) {
    396 		(void) fprintf(stderr, Errmsg_wrte, fldefs, strerror(errno));
    397 		return (1);
    398 	}
    399 
    400 	if (fprintf(fddefs, "#ifndef\t__lint\n\n") < 0) {
    401 		(void) fprintf(stderr, Errmsg_wrte, fldefs, strerror(errno));
    402 		return (1);
    403 	}
    404 
    405 	/*
    406 	 * The MSG_SGS_ARRAY_NAME macro supplies a generic way to
    407 	 * reference the string table regardless of its name.
    408 	 */
    409 	if (fprintf(fddefs, "#define\tMSG_SGS_LOCAL_ARRAY\t__%s\n\n",
    410 	    interface) < 0) {
    411 		(void) fprintf(stderr, Errmsg_wrte, fldefs, strerror(errno));
    412 		return (1);
    413 	}
    414 
    415 	/*
    416 	 * If the associated data array is global define a prototype.
    417 	 * Define a macro to access the array elements.
    418 	 */
    419 	if (lflag == 0) {
    420 		if (fprintf(fddefs, "extern\tconst char\t__%s[];\n\n",
    421 		    interface) < 0) {
    422 			(void) fprintf(stderr, Errmsg_wrte, fldefs,
    423 			    strerror(errno));
    424 			return (1);
    425 		}
    426 	}
    427 	if (fprintf(fddefs,
    428 	    "#define\tMSG_ORIG_STRTAB(_x, _s)\t&_s[_x]\n\n") < 0) {
    429 		(void) fprintf(stderr, Errmsg_wrte, fldefs, strerror(errno));
    430 		return (1);
    431 	}
    432 	if (fprintf(fddefs,
    433 	    "#define\tMSG_ORIG(x)\tMSG_ORIG_STRTAB(x, __%s)\n\n",
    434 	    interface) < 0) {
    435 		(void) fprintf(stderr, Errmsg_wrte, fldefs, strerror(errno));
    436 		return (1);
    437 	}
    438 
    439 	/*
    440 	 * Generate a prototype to access the associated data array.
    441 	 */
    442 	if (fprintf(fddefs, "extern\tconst char *\t_%s(Msg);\n\n",
    443 	    interface) < 0) {
    444 		(void) fprintf(stderr, Errmsg_wrte, fldefs, strerror(errno));
    445 		return (1);
    446 	}
    447 	if (fprintf(fddefs, "#define\tMSG_INTL(x)\t_%s(x)\n\n",
    448 	    interface) < 0) {
    449 		(void) fprintf(stderr, Errmsg_wrte, fldefs, strerror(errno));
    450 		return (1);
    451 	}
    452 
    453 	return (0);
    454 }
    455 
    456 
    457 /*
    458  * Finish the message definition header file.
    459  */
    460 static int
    461 fini_defs(void)
    462 {
    463 	if (fprintf(fddefs, "\n#else\t/* __lint */\n\n") < 0) {
    464 		(void) fprintf(stderr, Errmsg_wrte, fldefs, strerror(errno));
    465 		return (1);
    466 	}
    467 
    468 	if (fprintf(fddefs, "extern\tconst char *\t_%s(Msg);\n\n",
    469 	    interface) < 0) {
    470 		(void) fprintf(stderr, Errmsg_wrte, fldefs, strerror(errno));
    471 		return (1);
    472 	}
    473 
    474 	if (fprintf(fddefs, "#ifndef MSG_SGS_LOCAL_ARRAY\n"
    475 	    "#define\tMSG_SGS_LOCAL_ARRAY\t\"\"\n#endif\n\n") < 0) {
    476 		(void) fprintf(stderr, Errmsg_wrte, fldefs, strerror(errno));
    477 		return (1);
    478 	}
    479 
    480 	if (lflag == 0) {
    481 		if (fprintf(fddefs, "extern\tconst char\t__%s[];\n\n",
    482 		    interface) < 0) {
    483 			(void) fprintf(stderr, Errmsg_wrte, fldefs,
    484 			    strerror(errno));
    485 			return (1);
    486 		}
    487 	}
    488 
    489 	if (fprintf(fddefs,
    490 	    "#define MSG_ORIG_STRTAB(_x, _s)\t_x\n"
    491 	    "#define MSG_ORIG(x)\tx\n#define MSG_INTL(x)\tx\n") < 0) {
    492 		(void) fprintf(stderr, Errmsg_wrte, fldefs, strerror(errno));
    493 		return (1);
    494 	}
    495 
    496 	/*
    497 	 * Provide a way to get the array and function declarations above
    498 	 * without also getting the actual messages. This is useful in
    499 	 * our lintsup.c files that include more than one message header.
    500 	 * lintsup doesn't need the actual messages, and this prevents
    501 	 * macro name collisions.
    502 	 */
    503 	if (fprintf(fddefs, "\n#ifndef LINTSUP_SUPPRESS_STRINGS\n") < 0) {
    504 		(void) fprintf(stderr, Errmsg_wrte, fldefs, strerror(errno));
    505 		return (1);
    506 	}
    507 
    508 	/*
    509 	 * Copy the temporary lint defs file into the new header.
    510 	 */
    511 	if (fdlint) {
    512 		long	size;
    513 		char	*buf;
    514 
    515 		size = ftell(fdlint);
    516 		(void) rewind(fdlint);
    517 
    518 		if ((buf = malloc(size)) == 0) {
    519 			(void) fprintf(stderr, Errmsg_nmem, strerror(errno));
    520 			return (1);
    521 		}
    522 		if (fread(buf, size, 1, fdlint) == 0) {
    523 			(void) fprintf(stderr, Errmsg_read, fllint,
    524 			    strerror(errno));
    525 			return (1);
    526 		}
    527 		if (fwrite(buf, size, 1, fddefs) == 0) {
    528 			(void) fprintf(stderr, Errmsg_wrte, fldefs,
    529 			    strerror(errno));
    530 			return (1);
    531 		}
    532 		(void) free(buf);
    533 	}
    534 
    535 	if (fprintf(fddefs, "\n#endif\t/* LINTSUP_SUPPRESS_STRINGS */\n") < 0) {
    536 		(void) fprintf(stderr, Errmsg_wrte, fldefs, strerror(errno));
    537 		return (1);
    538 	}
    539 
    540 	if (fprintf(fddefs, "\n#endif\t/* __lint */\n") < 0) {
    541 		(void) fprintf(stderr, Errmsg_wrte, fldefs, strerror(errno));
    542 		return (1);
    543 	}
    544 
    545 	if (fprintf(fddefs, "\n#endif\n") < 0) {
    546 		(void) fprintf(stderr, Errmsg_wrte, fldefs, strerror(errno));
    547 		return (1);
    548 	}
    549 
    550 	return (0);
    551 }
    552 
    553 /*
    554  * The entire messaging file has been scanned - and all strings have been
    555  * inserted into the string_table.  We can now walk the message queue
    556  * and create the '#define <DEFN>' for each string - with the strings
    557  * assigned offset into the string_table.
    558  */
    559 static int
    560 output_defs(void)
    561 {
    562 	msg_string	*msg;
    563 	size_t		stbufsize;
    564 	char		*stbuf;
    565 
    566 	stbufsize = st_getstrtab_sz(stp);
    567 	if ((stbuf = malloc(stbufsize)) == 0) {
    568 		(void) fprintf(stderr, Errmsg_nmem, strerror(errno));
    569 		exit(1);
    570 	}
    571 	(void) st_setstrbuf(stp, stbuf, stbufsize);
    572 	for (msg = msg_head; msg; msg = msg->ms_next) {
    573 		size_t	stoff;
    574 		if ((st_setstring(stp, msg->ms_message, &stoff)) == -1) {
    575 			(void) fprintf(stderr, Errmsg_mnfn, msg->ms_message);
    576 			return (1);
    577 		}
    578 		if (fprintf(fddefs, "\n#define\t%s\t%ld\n",
    579 		    msg->ms_defn, stoff) < 0) {
    580 			(void) fprintf(stderr, Errmsg_wrte,
    581 			    fldefs, strerror(errno));
    582 			return (1);
    583 		}
    584 		if (fddefs && fprintf(fddefs, "#define\t%s_SIZE\t%d\n",
    585 		    msg->ms_defn, strlen(msg->ms_message)) < 0) {
    586 			(void) fprintf(stderr, Errmsg_wrte,
    587 			    fldefs, strerror(errno));
    588 			return (1);
    589 		}
    590 	}
    591 	return (0);
    592 }
    593 
    594 
    595 /*
    596  * Finish off the data structure definition.
    597  */
    598 static int
    599 output_data(void)
    600 {
    601 	size_t		stbufsize;
    602 	size_t		ndx;
    603 	size_t		column = 1;
    604 	const char	*stbuf;
    605 	const char	*fmtstr;
    606 
    607 	stbufsize = st_getstrtab_sz(stp);
    608 	stbuf = st_getstrbuf(stp);
    609 
    610 	assert(stbuf);
    611 
    612 	/*
    613 	 * Determine from the local flag whether the data declaration should
    614 	 * be static.
    615 	 */
    616 	if (lflag)
    617 		fmtstr = (const char *)"static const";
    618 	else
    619 		fmtstr = (const char *)"const";
    620 
    621 	if (fprintf(fddata, "\n%s char __%s[%ld] = { ",
    622 	    fmtstr, interface, stbufsize) < 0) {
    623 		(void) fprintf(stderr, Errmsg_wrte, fldata, strerror(errno));
    624 		return (1);
    625 	}
    626 
    627 	for (ndx = 0; ndx < (stbufsize - 1); ndx++) {
    628 		if (column == 1) {
    629 			if (fddata && fprintf(fddata,
    630 			    "\n/* %4ld */ 0x%.2x,", ndx,
    631 			    (unsigned char)stbuf[ndx]) < 0) {
    632 				(void) fprintf(stderr, Errmsg_wrte,
    633 				    fldata, strerror(errno));
    634 				return (1);
    635 			}
    636 		} else {
    637 			if (fddata && fprintf(fddata, "  0x%.2x,",
    638 			    (unsigned char)stbuf[ndx]) < 0) {
    639 				(void) fprintf(stderr, Errmsg_wrte,
    640 				    fldata, strerror(errno));
    641 				return (1);
    642 			}
    643 		}
    644 
    645 		if (column++ == 10)
    646 			column = 1;
    647 	}
    648 
    649 	if (column == 1)
    650 		fmtstr = "\n\t0x%.2x };\n";
    651 	else
    652 		fmtstr = "  0x%.2x };\n";
    653 
    654 	if (fprintf(fddata, fmtstr, (unsigned char)stbuf[stbufsize - 1]) < 0) {
    655 		(void) fprintf(stderr, Errmsg_wrte, fldata, strerror(errno));
    656 		return (1);
    657 	}
    658 
    659 	return (0);
    660 }
    661 
    662 static int
    663 file()
    664 {
    665 	char	buffer[LINE_MAX], * token;
    666 	uint_t	bufsize;
    667 	char	*token_buffer;
    668 	int	escape = 0;
    669 
    670 	if ((token_buffer = malloc(LINE_MAX)) == 0) {
    671 		(void) fprintf(stderr, Errmsg_nmem, strerror(errno));
    672 		return (1);
    673 	}
    674 	bufsize = LINE_MAX;
    675 
    676 	line = 1;
    677 
    678 	while ((token = fgets(buffer, LINE_MAX, fddesc)) != NULL) {
    679 		char	defn[PATH_MAX], * _defn, * str;
    680 		int	len;
    681 
    682 		switch (*token) {
    683 		case '#':
    684 		case '$':
    685 			if (escape) {
    686 				(void) fprintf(stderr, Errmsg_malt, fldesc,
    687 				    line);
    688 				return (1);
    689 			}
    690 
    691 			/*
    692 			 * If a msgid has been output a msgstr must follow
    693 			 * before we digest the new token.  A msgid is only set
    694 			 * if fdmsgs is in use.
    695 			 */
    696 			if (msgid) {
    697 				msgid = 0;
    698 				if (fprintf(fdmsgs, "msgstr\t\"\"\n") < 0) {
    699 					(void) fprintf(stderr, Errmsg_wrte,
    700 					    flmsgs, strerror(errno));
    701 					return (1);
    702 				}
    703 			}
    704 
    705 			/*
    706 			 * Pass lines directly through to the output message
    707 			 * file.
    708 			 */
    709 			if (fdmsgs && (prtmsgs == 1)) {
    710 				char	comment;
    711 
    712 				if (cflag == 0)
    713 					comment = '#';
    714 				else
    715 					comment = '$';
    716 
    717 				if (fprintf(fdmsgs, "%c%s", comment,
    718 				    ++token) < 0) {
    719 					(void) fprintf(stderr, Errmsg_wrte,
    720 					    flmsgs, strerror(errno));
    721 					return (1);
    722 				}
    723 			}
    724 			break;
    725 
    726 		case '@':
    727 			if (escape) {
    728 				(void) fprintf(stderr, Errmsg_malt, fldesc,
    729 				    line);
    730 				return (1);
    731 			}
    732 
    733 			/*
    734 			 * If a msgid has been output a msgstr must follow
    735 			 * before we digest the new token.
    736 			 */
    737 			if (msgid) {
    738 				msgid = 0;
    739 				if (fprintf(fdmsgs, "msgstr\t\"\"\n") < 0) {
    740 					(void) fprintf(stderr, Errmsg_wrte,
    741 					    flmsgs, strerror(errno));
    742 					return (1);
    743 				}
    744 			}
    745 
    746 			/*
    747 			 * Determine whether we have one or more tokens.
    748 			 */
    749 			token++;
    750 			while (isspace(*token))		/* rid any whitespace */
    751 				token++;
    752 			_defn = token;			/* definition start */
    753 			while (!(isspace(*token)))
    754 				token++;
    755 			*token++ = 0;
    756 
    757 			while (isspace(*token))		/* rid any whitespace */
    758 				token++;
    759 
    760 			/*
    761 			 * Determine whether the single token is one of the
    762 			 * reserved message output delimiters otherwise
    763 			 * translate it as a message identifier.
    764 			 */
    765 			if (*token == 0) {
    766 				if (strcmp(_defn, start) == 0)
    767 					prtmsgs = 1;
    768 				else if (strcmp(_defn, end) == 0)
    769 					prtmsgs = -1;
    770 				else if (getmesgid(_defn) == 1)
    771 					return (1);
    772 				break;
    773 			}
    774 
    775 			/*
    776 			 * Multiple tokens are translated by taking the first
    777 			 * token as the message definition, and the rest of the
    778 			 * line as the message itself.  A message line ending
    779 			 * with an escape ('\') is expected to be continued on
    780 			 * the next line.
    781 			 */
    782 			if (prtmsgs != -1)
    783 				prtmsgs = 1;
    784 			if (fdmsgs && (prtmsgs == 1)) {
    785 				/*
    786 				 * For catgets(3c) make sure a message
    787 				 * identifier has been established (this is
    788 				 * normally a domain for gettext(3i), but for
    789 				 * sgsmsg use this could be argued as being
    790 				 * redundent).  Also make sure that the message
    791 				 * definitions haven't exceeeded the maximum
    792 				 * value allowed by gencat(1) before generating
    793 				 * any message file entries.
    794 				 */
    795 				if (cflag == 1) {
    796 					if (setid == 0) {
    797 						(void) fprintf(stderr, "file "
    798 						    "%s: no message identifier "
    799 						    "has been established\n",
    800 						    fldesc);
    801 						return (1);
    802 					}
    803 					if (ptr	> NL_MSGMAX) {
    804 						(void) fprintf(stderr, "file "
    805 						    "%s: message definition "
    806 						    "(%d) exceeds allowable "
    807 						    "limit (NL_MSGMAX)\n",
    808 						    fldesc, ptr);
    809 						return (1);
    810 					}
    811 				}
    812 
    813 				/*
    814 				 * For catgets(3c) write the definition and the
    815 				 * message string to the message file.  For
    816 				 * gettext(3i) write the message string as a
    817 				 * mesgid - indicate a mesgid has been output
    818 				 * so that a msgstr can follow.
    819 				 */
    820 				if (cflag == 1) {
    821 					if (fprintf(fdmsgs, "%d\t%s", ptr,
    822 					    token) < 0) {
    823 						(void) fprintf(stderr,
    824 						    Errmsg_wrte, flmsgs,
    825 						    strerror(errno));
    826 						return (1);
    827 					}
    828 				} else {
    829 					if (fprintf(fdmsgs, "msgid\t\"") < 0) {
    830 						(void) fprintf(stderr,
    831 						    Errmsg_wrte, flmsgs,
    832 						    strerror(errno));
    833 						return (1);
    834 					}
    835 					msgid = 1;
    836 				}
    837 			}
    838 
    839 			/*
    840 			 * The message itself is a quoted string as this makes
    841 			 * embedding spaces at the start (or the end) of the
    842 			 * string very easy.
    843 			 */
    844 			if (*token != '"') {
    845 				(void) fprintf(stderr, Errmsg_malt, fldesc,
    846 				    line);
    847 				return (1);
    848 			}
    849 
    850 			(void) strcpy(defn, _defn);
    851 
    852 			/*
    853 			 * Write the tag to the lint definitions.
    854 			 */
    855 			if (fdlint) {
    856 				if (fprintf(fdlint, "\n#define\t%s\t",
    857 				    _defn) < 0) {
    858 					(void) fprintf(stderr, Errmsg_wrte,
    859 					    fllint, strerror(errno));
    860 					return (1);
    861 				}
    862 			}
    863 
    864 			len = 0;
    865 
    866 			/*
    867 			 * Write each character of the message string to the
    868 			 * data array.  Translate any escaped characters - use
    869 			 * the same specially recognized characters as defined
    870 			 * by gencat(1).
    871 			 */
    872 message:
    873 			if (*token == '"') {
    874 				if (fdlint &&
    875 				    (fprintf(fdlint, "%c", *token) < 0)) {
    876 					(void) fprintf(stderr, Errmsg_wrte,
    877 					    fllint, strerror(errno));
    878 					return (1);
    879 				}
    880 				token++;
    881 			}
    882 			while (*token) {
    883 				char	_token;
    884 
    885 				if ((*token == '\\') && (escape == 0)) {
    886 					escape = 1;
    887 					if (fdlint && (*(token + 1) != '\n') &&
    888 					    fprintf(fdlint, "%c", *token) < 0) {
    889 						(void) fprintf(stderr,
    890 						    Errmsg_wrte, fllint,
    891 						    strerror(errno));
    892 						return (1);
    893 					}
    894 					token++;
    895 					continue;
    896 				}
    897 				if (escape) {
    898 					if (*token == 'n')
    899 						_token = '\n';
    900 					else if (*token == 't')
    901 						_token = '\t';
    902 					else if (*token == 'v')
    903 						_token = '\v';
    904 					else if (*token == 'b')
    905 						_token = '\b';
    906 					else if (*token == 'f')
    907 						_token = '\f';
    908 					else if (*token == '\\')
    909 						_token = '\\';
    910 					else if (*token == '"')
    911 						_token = '"';
    912 					else if (*token == '\n')
    913 						break;
    914 					else
    915 						_token = *token;
    916 
    917 					if (fdmsgs && (prtmsgs == 1) &&
    918 					    (fprintf(fdmsgs, "\\") < 0)) {
    919 						(void) fprintf(stderr,
    920 						    Errmsg_wrte, flmsgs,
    921 						    strerror(errno));
    922 						return (1);
    923 					}
    924 				} else {
    925 					/*
    926 					 * If this is the trailing quote then
    927 					 * thats the last of the message string.
    928 					 * Eat up any remaining white space and
    929 					 * unless an escape character is found
    930 					 * terminate the data string with a 0.
    931 					 */
    932 					/* BEGIN CSTYLED */
    933 					if (*token == '"') {
    934 					    if (fdlint && (fprintf(fdlint,
    935 						"%c", *token) < 0)) {
    936 						(void) fprintf(stderr,
    937 						    Errmsg_wrte, fllint,
    938 						    strerror(errno));
    939 						return (1);
    940 					    }
    941 
    942 					    if (fdmsgs && (prtmsgs == 1) &&
    943 						(fprintf(fdmsgs, "%c",
    944 						*token) < 0)) {
    945 						(void) fprintf(stderr,
    946 						    Errmsg_wrte, flmsgs,
    947 						    strerror(errno));
    948 						return (1);
    949 					    }
    950 
    951 					    while (*++token) {
    952 						if (*token == '\n')
    953 							break;
    954 					    }
    955 					    _token = '\0';
    956 					} else
    957 					    _token = *token;
    958 					/* END CSTYLED */
    959 				}
    960 
    961 				if (fdmsgs && (prtmsgs == 1) &&
    962 				    (fprintf(fdmsgs, "%c", *token) < 0)) {
    963 					(void) fprintf(stderr, Errmsg_wrte,
    964 					    flmsgs, strerror(errno));
    965 					return (1);
    966 				}
    967 
    968 				if (fdlint && fprintf(fdlint,
    969 				    "%c", *token) < 0) {
    970 					(void) fprintf(stderr, Errmsg_wrte,
    971 					    fllint, strerror(errno));
    972 					return (1);
    973 				}
    974 
    975 				if (len >= bufsize) {
    976 					bufsize += LINE_MAX;
    977 					if ((token_buffer = realloc(
    978 					    token_buffer, bufsize)) == 0) {
    979 						(void) fprintf(stderr,
    980 						    Errmsg_nmem,
    981 						    strerror(errno));
    982 						return (1);
    983 					}
    984 				}
    985 				token_buffer[len] = _token;
    986 				ptr++, token++, len++;
    987 				escape = 0;
    988 
    989 				if (_token == '\0')
    990 					break;
    991 			}
    992 
    993 			/*
    994 			 * After the complete message string has been processed
    995 			 * (including its continuation beyond one line), create
    996 			 * a string size definition.
    997 			 */
    998 			if (escape == 0) {
    999 				const char *form = "#define\t%s_SIZE\t%d\n";
   1000 
   1001 				token_buffer[len] = '\0';
   1002 
   1003 				message_append(defn, token_buffer);
   1004 
   1005 				if (fdlint && fprintf(fdlint, form, defn,
   1006 				    (len - 1)) < 0) {
   1007 					(void) fprintf(stderr, Errmsg_wrte,
   1008 					    fllint, strerror(errno));
   1009 					return (1);
   1010 				}
   1011 			}
   1012 			break;
   1013 
   1014 		default:
   1015 			/*
   1016 			 * Empty lines are passed through to the message file.
   1017 			 */
   1018 			while (isspace(*token))
   1019 				token++;
   1020 
   1021 			if (*token == 0) {
   1022 				if (msgid || (fdmsgs && (prtmsgs == 1))) {
   1023 					/*
   1024 					 * If a msgid has been output a msgstr
   1025 					 * must follow before we digest the new
   1026 					 * token.
   1027 					 */
   1028 					if (msgid) {
   1029 						msgid = 0;
   1030 						str = "msgstr\t\"\"\n\n";
   1031 					} else
   1032 						str = "\n";
   1033 
   1034 					if (fprintf(fdmsgs, str) < 0) {
   1035 						(void) fprintf(stderr,
   1036 						    Errmsg_wrte, flmsgs,
   1037 						    strerror(errno));
   1038 						return (1);
   1039 					}
   1040 				}
   1041 				break;
   1042 			}
   1043 
   1044 			/*
   1045 			 * If an escape is in effect then any tokens are taken
   1046 			 * to be message continuations.
   1047 			 */
   1048 			if (escape) {
   1049 				escape = 0;
   1050 				goto message;
   1051 			}
   1052 
   1053 			(void) fprintf(stderr, "file %s: line %d: invalid "
   1054 			    "input does not start with #, $ or @\n", fldesc,
   1055 			    line);
   1056 			return (1);
   1057 		}
   1058 		line++;
   1059 	}
   1060 
   1061 	free(token_buffer);
   1062 
   1063 	return (0);
   1064 }
   1065 
   1066 int
   1067 main(int argc, char ** argv)
   1068 {
   1069 	opterr = 0;
   1070 	while ((line = getopt(argc, argv, "cd:h:lm:n:i:v")) != EOF) {
   1071 		switch (line) {
   1072 		case 'c':			/* catgets instead of gettext */
   1073 			cflag = 1;
   1074 			break;
   1075 		case 'd':			/* new message data filename */
   1076 			fldata = optarg;	/*	(msg.c is default) */
   1077 			break;
   1078 		case 'h':			/* new message defs filename */
   1079 			fldefs = optarg;	/*	(msg.h is default) */
   1080 			break;
   1081 		case 'i':			/* input message ids from */
   1082 			flmids = optarg;	/*	from this file */
   1083 			break;
   1084 		case 'l':			/* define message data arrays */
   1085 			lflag = 1;		/*	to be local (static) */
   1086 			break;
   1087 		case 'm':			/* generate message database */
   1088 			flmsgs = optarg;	/*	to this file */
   1089 			break;
   1090 		case 'n':			/* new data array and func */
   1091 			interface = optarg;	/*	name (msg is default) */
   1092 			break;
   1093 		case 'v':
   1094 			vflag = 1;		/* set verbose flag */
   1095 			break;
   1096 		case '?':
   1097 			(void) fprintf(stderr, Errmsg_use, argv[0]);
   1098 			exit(1);
   1099 		default:
   1100 			break;
   1101 		}
   1102 	}
   1103 
   1104 	/*
   1105 	 * Validate the we have been given at least one input file.
   1106 	 */
   1107 	if ((argc - optind) < 1) {
   1108 		(void) fprintf(stderr, Errmsg_use);
   1109 		exit(1);
   1110 	}
   1111 
   1112 	/*
   1113 	 * Open all the required output files.
   1114 	 */
   1115 	if (fldefs) {
   1116 		if ((fddefs = fopen(fldefs, "w+")) == NULL) {
   1117 			(void) fprintf(stderr, Errmsg_opne, fldefs,
   1118 			    strerror(errno));
   1119 			return (1);
   1120 		}
   1121 	}
   1122 	if (fldata) {
   1123 		if (fldefs && (strcmp(fldefs, fldata) == 0))
   1124 			fddata = fddefs;
   1125 		else if ((fddata = fopen(fldata, "w+")) == NULL) {
   1126 			(void) fprintf(stderr, Errmsg_opne, fldata,
   1127 			    strerror(errno));
   1128 			return (1);
   1129 		}
   1130 	}
   1131 	if (fddefs && fddata) {
   1132 		(void) sprintf(fllint, "%s.%d", nmlint, (int)getpid());
   1133 		if ((fdlint = fopen(fllint, "w+")) == NULL) {
   1134 			(void) fprintf(stderr, Errmsg_opne, fllint,
   1135 			    strerror(errno));
   1136 			return (1);
   1137 		}
   1138 	}
   1139 	if (flmsgs) {
   1140 		if ((fdmsgs = fopen(flmsgs, "w+")) == NULL) {
   1141 			(void) fprintf(stderr, Errmsg_opne, flmsgs,
   1142 			    strerror(errno));
   1143 			return (1);
   1144 		}
   1145 	}
   1146 	if (flmids) {
   1147 		if ((fdmids = fopen(flmids, "r")) == NULL) {
   1148 			(void) fprintf(stderr, Errmsg_opne, flmids,
   1149 			    strerror(errno));
   1150 			return (1);
   1151 		}
   1152 	}
   1153 
   1154 
   1155 	/*
   1156 	 * Initialize the message definition and message data streams.
   1157 	 */
   1158 	if (fddefs) {
   1159 		if (init_defs())
   1160 			return (1);
   1161 	}
   1162 
   1163 	/*
   1164 	 * Read the input message file, and for each line process accordingly.
   1165 	 */
   1166 	for (; optind < argc; optind++) {
   1167 		int	err;
   1168 
   1169 		fldesc = argv[optind];
   1170 
   1171 		if ((fddesc = fopen(fldesc, "r")) == NULL) {
   1172 			(void) fprintf(stderr, Errmsg_opne, fldesc,
   1173 			    strerror(errno));
   1174 			return (1);
   1175 		}
   1176 		err = file();
   1177 		(void) fclose(fddesc);
   1178 
   1179 		if (err != 0)
   1180 			return (1);
   1181 	}
   1182 
   1183 	/*
   1184 	 * If a msgid has been output a msgstr must follow before we end the
   1185 	 * file.
   1186 	 */
   1187 	if (msgid) {
   1188 		msgid = 0;
   1189 		if (fprintf(fdmsgs, "msgstr\t\"\"\n") < 0) {
   1190 			(void) fprintf(stderr, Errmsg_wrte, flmsgs,
   1191 			    strerror(errno));
   1192 			return (1);
   1193 		}
   1194 	}
   1195 
   1196 	if (fdmids)
   1197 		(void) fclose(fdmids);
   1198 	if (fdmsgs)
   1199 		(void) fclose(fdmsgs);
   1200 
   1201 	if (fddefs) {
   1202 		if (output_defs())
   1203 			return (1);
   1204 	}
   1205 
   1206 	/*
   1207 	 * Finish off any generated data and header file.
   1208 	 */
   1209 	if (fldata) {
   1210 		if (output_data())
   1211 			return (1);
   1212 	}
   1213 	if (fddefs) {
   1214 		if (fini_defs())
   1215 			return (1);
   1216 	}
   1217 
   1218 	if (vflag)
   1219 		dump_stringtab(stp);
   1220 
   1221 	/*
   1222 	 * Close up everything and go home.
   1223 	 */
   1224 	if (fddata)
   1225 		(void) fclose(fddata);
   1226 	if (fddefs && (fddefs != fddata))
   1227 		(void) fclose(fddefs);
   1228 	if (fddefs && fddata) {
   1229 		(void) fclose(fdlint);
   1230 		(void) unlink(fllint);
   1231 	}
   1232 
   1233 	if (stp)
   1234 		st_destroy(stp);
   1235 
   1236 	return (0);
   1237 }
   1238