sgsmsg.c revision 7c478bd95313f5f23a4c958a745db2134aa03244
/*
* 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
* 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.
*
* sgsmsg generates several message files from an input template file. Messages
* are constructed for use with gettext(3i) - the default - or catgets(3c). The
* files generate are:
*
* msg.h a header file containing definitions for each message. The -h
* option triggers the creation of these definitions and specifies
* the name to use.
*
* offsets into this array. The -d option triggers the creation of
* these definitions and specifies the name to use.
*
* messages a message file suitable for catgets(3c) or gettext(3i) use. The
* -m option triggers this output and specifies the filename to be
* used.
*
* The template file is processed based on the first character of each line:
*
* # or $ entries are copied (as is) to the message file (messages).
*
* @ token(s) entries are translated. Two translations are possible dependent
* on whether one or more tokens are supplied:
*
* A single token is interpreted as one of two reserved message
* output indicators, or a message identifier. The reserved output
* indicator _START_ enables output to the message file - Note that
* the occurance of any other @ token will also enable message
* output. The reserved output indicator _END_ disables output to
* the message file. The use of these two indicators provides for
* only those message strings that require translation to be output
* to the message file.
*
* Besides the reserved output indicators, a single token is taken
* to be a message identifier which will be subsituted for a
* `setid' for catgets(3c) output, or a `domain' name for
* gettext(3i) output. This value is determine by substituting the
* token for the associated definition found in the message
* identifier file (specified with the -i option).
*
* Multiple tokens are taken to be a message definition followed by
* the associated message string. The message string is copied to
* the data array being built in msg.c. The index into this array
* becomes the `message' identifier created in the msg.h file.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <limits.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <sgs.h>
#include <string_table.h>
/*
* Define any error message strings.
*/
static const char
* Errmsg_malt = "sgsmsg: file %s: line %d: malformed input "
"at line\n",
* Errmsg_nmem = "sgsmsg: memory allocation failed: %s\n",
* Errmsg_opne = "sgsmsg: file %s: open failed: %s\n",
* Errmsg_wrte = "sgsmsg: file %s: write failed: %s\n",
* Errmsg_read = "sgsmsg: file %s: read failed %s\n",
* Errmsg_stnw = "sgsmsg: st_new(): failed: %s\n",
* Errmsg_stin = "sgsmsg: Str_tbl insert failed: %s\n",
* Errmsg_mnfn = "sgsmsg: message not found in Str_tbl: %s\n",
* Errmsg_use = "usage: sgsmsg [-clv] [-d mesgdata] [-h mesgdefs] "
"[-m messages] [-n name] [-i mesgident] file ...\n";
/*
* Define all output filenames and associated descriptors.
*/
static char fllint[MAXPATHLEN];
/*
* Define any default strings.
*/
static const char
*nmlint = "/tmp/sgsmsg.lint",
*interface = "sgs_msg",
*start = "_START_",
*end = "_END_";
/*
* Define any default flags and data items.
*/
typedef struct msg_string {
char *ms_defn;
char *ms_message;
struct msg_string *ms_next;
} msg_string;
static msg_string *msg_head;
static msg_string *msg_tail;
/*
* message_append() is responsible for both inserting strings into
* the master Str_tbl as well as maintaining a list of the
* DEFINITIONS associated with each string.
*
* The list of strings is traversed at the end once the full
* Str_tbl has been constructed - and string offsets can be
* assigned.
*/
static void
{
exit(1);
}
if (stp == 0) {
/*
* Initialize string table
*/
exit(1);
}
}
exit(1);
}
exit(1);
}
message);
exit(1);
}
if (msg_head == 0) {
return;
}
}
/*
* Initialize a setid value. Given a setid definition determine its numeric
* value from the specified message identifier file (specified with the -i
* option). Return a pointer to the numeric string.
*/
static int
{
/*
* If we're being asked to interpret a message id but the user didn't
* provide the required message identifier file (-i option) we're in
* trouble.
*/
if (flmids == 0) {
"unable to process mesgid\n\t"
"no message identifier file specified "
return (1);
}
return (1);
}
/*
* Read the message identifier file and locate the required mesgid.
*/
continue;
/*
* Establish individual strings for the mesgid, setid and domain
* values.
*/
token++;
*token++ = 0;
token++;
token++;
*token++ = 0;
token++;
token++;
*token = 0;
break;
}
/*
* Did we find a match?
*/
"unable to process mesgid\n\t"
"identifier does not exist in file %s\n",
return (1);
}
/*
* Have we been here before?
*/
if (mesgid) {
if (cflag == 1) {
/*
* If we're being asked to process more than one mesgid
* warn the user that only one mesgid can be used for
* the catgets(3c) call.
*/
"setid %s: warning: multiple mesgids "
"encountered\n\t"
"last setting used in messaging code\n",
}
}
/*
* Generate the message file output (insure output flag is enabled).
*/
if (prtmsgs != -1)
prtmsgs = 1;
if (cflag == 1) {
setid) < 0) {
return (1);
}
} else {
return (1);
}
}
}
/*
* For catgets(3c) output generate a setid definition in the message
* definition file.
*/
return (1);
}
return (0);
}
/*
* Dump contents of String Table to standard out
*/
static void
{
uint_t i;
(void) printf("uncompressed strings: %d\n",
return;
}
for (i = 0; i < stp->st_hbckcnt; i++) {
(void) printf("Bucket: [%3d]\n", i);
if (stroff == 0) {
(void) printf(" %2d %s <master>\n",
} else {
const char *str;
(void) printf(" %2d %s <suffix of> -> %s\n",
}
}
}
(void) printf("fullstringsize: %d compressed: %d\n",
}
/*
* Initialize the message definition header file stream.
*/
static int
init_defs(void)
{
char *optr;
/*
* Establish a header guard name using the files basename.
*/
if (*_ptr == '/')
}
if (iptr == 0)
if (*iptr == '.') {
*optr++ = '_';
*optr++ = 'D';
*optr++ = 'O';
*optr++ = 'T';
*optr = '_';
} else
}
return (1);
}
return (1);
}
/*
* add "typedef int Msg;"
*/
return (1);
}
/*
* If the associated data array is global define a prototype.
* Define a macro to access the array elements.
*/
if (lflag == 0) {
interface) < 0) {
return (1);
}
}
interface) < 0) {
return (1);
}
/*
* Generate a prototype to access the associated data array.
*/
interface) < 0) {
return (1);
}
interface) < 0) {
return (1);
}
return (0);
}
/*
* Finish the message definition header file.
*/
static int
fini_defs(void)
{
return (1);
}
/*
* When __lint is defined, Msg is a char *. This allows lint to
* check our format strings against it's arguments.
*/
return (1);
}
interface) < 0) {
return (1);
}
if (lflag == 0) {
interface) < 0) {
return (1);
}
}
"#define MSG_ORIG(x)\tx\n#define MSG_INTL(x)\tx\n") < 0) {
return (1);
}
/*
* Copy the temporary lint defs file into the new header.
*/
if (fdlint) {
long size;
char *buf;
return (1);
}
return (1);
}
return (1);
}
}
return (1);
}
return (1);
}
return (0);
}
/*
* The entire messaging file has been scanned - and all strings have been
* inserted into the string_table. We can now walk the message queue
* and create the '#define <DEFN>' for each string - with the strings
* assigned offset into the string_table.
*/
static int
output_defs(void)
{
char *stbuf;
exit(1);
}
return (1);
}
return (1);
}
return (1);
}
}
return (0);
}
/*
* Finish off the data structure definition.
*/
static int
output_data(void)
{
const char *stbuf;
const char *fmtstr;
/*
* Determine from the local flag whether the data declaration should
* be static.
*/
if (lflag)
fmtstr = (const char *)"static const";
else
fmtstr = (const char *)"const";
return (1);
}
if (column == 1) {
"\n/* %4d */ 0x%.2x,", ndx,
return (1);
}
} else {
return (1);
}
}
if (column++ == 10)
column = 1;
}
if (column == 1)
fmtstr = "\n\t0x%.2x };\n";
else
fmtstr = " 0x%.2x };\n";
return (1);
}
return (0);
}
static int
file()
{
char *token_buffer;
int escape = 0;
return (1);
}
line = 1;
int len;
switch (*token) {
case '#':
case '$':
if (escape) {
line);
return (1);
}
/*
* If a msgid has been output a msgstr must follow
* before we digest the new token. A msgid is only set
* if fdmsgs is in use.
*/
if (msgid) {
msgid = 0;
return (1);
}
}
/*
* Pass lines directly through to the output message
* file.
*/
char comment;
if (cflag == 0)
comment = '#';
else
comment = '$';
++token) < 0) {
return (1);
}
}
break;
case '@':
if (escape) {
line);
return (1);
}
/*
* If a msgid has been output a msgstr must follow
* before we digest the new token.
*/
if (msgid) {
msgid = 0;
return (1);
}
}
/*
* Determine whether we have one or more tokens.
*/
token++;
token++;
token++;
*token++ = 0;
token++;
/*
* Determine whether the single token is one of the
* reserved message output delimiters otherwise
* translate it as a message identifier.
*/
if (*token == 0) {
prtmsgs = 1;
prtmsgs = -1;
return (1);
break;
}
/*
* Multiple tokens are translated by taking the first
* token as the message definition, and the rest of the
* line as the message itself. A message line ending
* with an escape ('\') is expected to be continued on
* the next line.
*/
if (prtmsgs != -1)
prtmsgs = 1;
/*
* For catgets(3c) make sure a message
* identifier has been established (this is
* normally a domain for gettext(3i), but for
* sgsmsg use this could be argued as being
* redundent). Also make sure that the message
* definitions haven't exceeeded the maximum
* value allowed by gencat(1) before generating
* any message file entries.
*/
if (cflag == 1) {
if (setid == 0) {
"%s: no message identifier "
"has been established\n",
fldesc);
return (1);
}
"%s: message definition "
"(%d) exceeds allowable "
"limit (NL_MSGMAX)\n",
return (1);
}
}
/*
* For catgets(3c) write the definition and the
* message string to the message file. For
* gettext(3i) write the message string as a
* mesgid - indicate a mesgid has been output
* so that a msgstr can follow.
*/
if (cflag == 1) {
token) < 0) {
return (1);
}
} else {
return (1);
}
msgid = 1;
}
}
/*
* The message itself is a quoted string as this makes
* embedding spaces at the start (or the end) of the
* string very easy.
*/
if (*token != '"') {
line);
return (1);
}
/*
* Write the tag to the lint definitions.
*/
if (fdlint) {
_defn) < 0) {
return (1);
}
}
len = 0;
/*
* Write each character of the message string to the
* data array. Translate any escaped characters - use
* the same specially recognized characters as defined
* by gencat(1).
*/
if (*token == '"') {
if (fdlint &&
return (1);
}
token++;
}
while (*token) {
char _token;
escape = 1;
return (1);
}
token++;
continue;
}
if (escape) {
if (*token == 'n')
_token = '\n';
else if (*token == 't')
_token = '\t';
else if (*token == 'v')
_token = '\v';
else if (*token == 'b')
_token = '\b';
else if (*token == 'f')
_token = '\f';
else if (*token == '\\')
_token = '\\';
else if (*token == '"')
_token = '"';
else if (*token == '\n')
break;
else
return (1);
}
} else {
/*
* If this is the trailing quote then
* thats the last of the message string.
* Eat up any remaining white space and
* unless an escape character is found
* terminate the data string with a 0.
*/
if (*token == '"') {
"%c", *token) < 0)) {
return (1);
}
*token) < 0)) {
return (1);
}
while (*++token) {
if (*token == '\n')
break;
}
_token = '\0';
} else
}
return (1);
}
"%c", *token) < 0) {
return (1);
}
if ((token_buffer = realloc(
token_buffer, bufsize)) == 0) {
return (1);
}
}
escape = 0;
if (_token == '\0')
break;
}
/*
* After the complete message string has been processed
* (including its continuation beyond one line), create
* a string size definition.
*/
if (escape == 0) {
const char *form = "#define\t%s_SIZE\t%d\n";
(len - 1)) < 0) {
return (1);
}
}
break;
default:
/*
* Empty lines are passed through to the message file.
*/
token++;
if (*token == 0) {
/*
* If a msgid has been output a msgstr
* must follow before we digest the new
* token.
*/
if (msgid) {
msgid = 0;
str = "msgstr\t\"\"\n\n";
} else
str = "\n";
return (1);
}
}
break;
}
/*
* If an escape is in effect then any tokens are taken
* to be message continuations.
*/
if (escape) {
escape = 0;
goto message;
}
"input does not start with #, $ or @\n", fldesc,
line);
return (1);
}
line++;
}
return (0);
}
int
{
opterr = 0;
switch (line) {
case 'c': /* catgets instead of gettext */
cflag = 1;
break;
case 'd': /* new message data filename */
break;
case 'h': /* new message defs filename */
break;
case 'i': /* input message ids from */
break;
case 'l': /* define message data arrays */
break;
case 'm': /* generate message database */
break;
case 'n': /* new data array and func */
break;
case 'v':
break;
case '?':
exit(1);
default:
break;
}
}
/*
* Validate the we have been given at least one input file.
*/
exit(1);
}
/*
* Open all the required output files.
*/
if (fldefs) {
return (1);
}
}
if (fldata) {
return (1);
}
}
return (1);
}
}
if (flmsgs) {
return (1);
}
}
if (flmids) {
return (1);
}
}
/*
* Initialize the message definition and message data streams.
*/
if (fddefs) {
if (init_defs())
return (1);
}
/*
* Read the input message file, and for each line process accordingly.
*/
int err;
return (1);
}
if (err != 0)
return (1);
}
/*
* If a msgid has been output a msgstr must follow before we end the
* file.
*/
if (msgid) {
msgid = 0;
return (1);
}
}
if (fdmids)
if (fdmsgs)
if (fddefs) {
if (output_defs())
return (1);
}
/*
* Finish off any generated data and header file.
*/
if (fldata) {
if (output_data())
return (1);
}
if (fddefs) {
if (fini_defs())
return (1);
}
if (vflag)
/*
* Close up everything and go home.
*/
if (fddata)
}
if (stp)
return (0);
}