#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 <sys/param.h>
#include <sgs.h>
#include <_string_table.h>
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";
static FILE *fddefs, *fddata, *fdmsgs, *fdmids, *fddesc;
static char *fldefs, *fldata, *flmsgs, *flmids, *fldesc;
static FILE *fdlint;
static char fllint[MAXPATHLEN];
static uint_t vflag;
static Str_tbl *stp;
static const char
*nmlint = "/tmp/sgsmsg.lint",
*interface = "sgs_msg",
*start = "_START_",
*end = "_END_";
static int cflag = 0, lflag = 0, prtmsgs = 0, line, ptr = 1, msgid = 0;
static char *mesgid = 0, *setid = 0, *domain = 0;
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;
static void
message_append(const char *defn, const char *message)
{
msg_string *msg;
if ((msg = calloc(sizeof (msg_string), 1)) == 0) {
(void) fprintf(stderr, Errmsg_nmem, strerror(errno));
exit(1);
}
if ((stp == 0) && ((stp = st_new(FLG_STNEW_COMPRESS)) == NULL)) {
(void) fprintf(stderr, Errmsg_stnw, strerror(errno));
exit(1);
}
if ((msg->ms_defn = strdup(defn)) == 0) {
(void) fprintf(stderr, Errmsg_nmem, strerror(errno));
exit(1);
}
if ((msg->ms_message = strdup(message)) == 0) {
(void) fprintf(stderr, Errmsg_nmem, strerror(errno));
exit(1);
}
if (st_insert(stp, msg->ms_message) == -1) {
(void) fprintf(stderr, Errmsg_stin,
message);
exit(1);
}
if (msg_head == 0) {
msg_head = msg_tail = msg;
return;
}
msg_tail->ms_next = msg;
msg_tail = msg;
}
static int
getmesgid(char *id)
{
char *buffer, *token, *_mesgid = 0, *_setid = 0, *_domain = 0;
if (flmids == 0) {
(void) fprintf(stderr, "sgsmsg: file %s: line %d: mesgid %s: "
"unable to process mesgid\n\t"
"no message identifier file specified "
"(see -i option)\n", fldesc, line, id);
return (1);
}
if ((buffer = malloc(LINE_MAX)) == 0) {
(void) fprintf(stderr, Errmsg_nmem, strerror(errno));
return (1);
}
rewind(fdmids);
while (fgets(buffer, LINE_MAX, fdmids) != NULL) {
if ((token = strstr(buffer, id)) == NULL)
continue;
_mesgid = token;
while (!(isspace(*token)))
token++;
*token++ = 0;
while (isspace(*token))
token++;
_setid = token;
while (!(isspace(*token)))
token++;
*token++ = 0;
while (isspace(*token))
token++;
_domain = token;
while (!(isspace(*token)))
token++;
*token = 0;
break;
}
if ((_mesgid == 0) || (_setid == 0) || (_domain == 0)) {
(void) fprintf(stderr, "sgsmsg: file %s: line %d: mesgid %s: "
"unable to process mesgid\n\t"
"identifier does not exist in file %s\n",
fldesc, line, id, flmids);
return (1);
}
if (mesgid) {
if (cflag == 1) {
(void) fprintf(stderr, "sgsmsg: file %s: line %d: "
"setid %s: warning: multiple mesgids "
"encountered\n\t"
"last setting used in messaging code\n",
fldesc, line, id);
}
}
mesgid = _mesgid;
setid = _setid;
domain = _domain;
if (prtmsgs != -1)
prtmsgs = 1;
if (fdmsgs && (prtmsgs == 1)) {
if (cflag == 1) {
if (fprintf(fdmsgs, "$quote \"\n$set %s\n",
setid) < 0) {
(void) fprintf(stderr, Errmsg_wrte, flmsgs,
strerror(errno));
return (1);
}
} else {
if (fprintf(fdmsgs, "domain\t\"%s\"\n", domain) < 0) {
(void) fprintf(stderr, Errmsg_wrte, flmsgs,
strerror(errno));
return (1);
}
}
}
if (fddefs && (cflag == 1) &&
(fprintf(fddefs, "#define\t%s\t%s\n\n", mesgid, setid) < 0)) {
(void) fprintf(stderr, Errmsg_wrte, fldefs, strerror(errno));
return (1);
}
return (0);
}
static void
dump_stringtab(Str_tbl *stp)
{
uint_t cnt;
if ((stp->st_flags & FLG_STTAB_COMPRESS) == 0) {
(void) printf("string table full size: %zu: uncompressed\n",
stp->st_fullstrsize);
return;
}
(void) printf("string table full size: %zu compressed down to: %zu\n\n",
stp->st_fullstrsize, stp->st_strsize);
(void) printf("string table compression information [%d buckets]:\n",
stp->st_hbckcnt);
for (cnt = 0; cnt < stp->st_hbckcnt; cnt++) {
Str_hash *sthash = stp->st_hashbcks[cnt];
if (sthash == 0)
continue;
(void) printf(" bucket: [%d]\n", cnt);
while (sthash) {
size_t stroff = sthash->hi_mstr->sm_strlen -
sthash->hi_strlen;
if (stroff == 0) {
(void) printf(" [%zu]: '%s' <master>\n",
sthash->hi_refcnt, sthash->hi_mstr->sm_str);
} else {
(void) printf(" [%zu]: '%s' <suffix of: "
"'%s'>\n", sthash->hi_refcnt,
&sthash->hi_mstr->sm_str[stroff],
sthash->hi_mstr->sm_str);
}
sthash = sthash->hi_next;
}
}
}
static int
init_defs(void)
{
static char guard[FILENAME_MAX + 6];
char *optr;
const char *iptr, *_ptr;
for (iptr = 0, _ptr = fldefs; _ptr && (*_ptr != '\0'); _ptr++) {
if (*_ptr == '/')
iptr = _ptr + 1;
}
if (iptr == 0)
iptr = fldefs;
optr = guard;
for (*optr++ = '_'; iptr && (*iptr != '\0'); iptr++, optr++) {
if (*iptr == '.') {
*optr++ = '_';
*optr++ = 'D';
*optr++ = 'O';
*optr++ = 'T';
*optr = '_';
} else
*optr = toupper(*iptr);
}
if (fprintf(fddefs, "#ifndef\t%s\n#define\t%s\n\n", guard, guard) < 0) {
(void) fprintf(stderr, Errmsg_wrte, fldefs, strerror(errno));
return (1);
}
if (fprintf(fddefs, "#include <sgsmsg.h>\t/* Msg typedef */\n\n") < 0) {
(void) fprintf(stderr, Errmsg_wrte, fldefs, strerror(errno));
return (1);
}
if (fprintf(fddefs, "#ifndef\t__lint\n\n") < 0) {
(void) fprintf(stderr, Errmsg_wrte, fldefs, strerror(errno));
return (1);
}
if (fprintf(fddefs, "#define\tMSG_SGS_LOCAL_ARRAY\t__%s\n\n",
interface) < 0) {
(void) fprintf(stderr, Errmsg_wrte, fldefs, strerror(errno));
return (1);
}
if (lflag == 0) {
if (fprintf(fddefs, "extern\tconst char\t__%s[];\n\n",
interface) < 0) {
(void) fprintf(stderr, Errmsg_wrte, fldefs,
strerror(errno));
return (1);
}
}
if (fprintf(fddefs,
"#define\tMSG_ORIG_STRTAB(_x, _s)\t&_s[_x]\n\n") < 0) {
(void) fprintf(stderr, Errmsg_wrte, fldefs, strerror(errno));
return (1);
}
if (fprintf(fddefs,
"#define\tMSG_ORIG(x)\tMSG_ORIG_STRTAB(x, __%s)\n\n",
interface) < 0) {
(void) fprintf(stderr, Errmsg_wrte, fldefs, strerror(errno));
return (1);
}
if (fprintf(fddefs, "extern\tconst char *\t_%s(Msg);\n\n",
interface) < 0) {
(void) fprintf(stderr, Errmsg_wrte, fldefs, strerror(errno));
return (1);
}
if (fprintf(fddefs, "#define\tMSG_INTL(x)\t_%s(x)\n\n",
interface) < 0) {
(void) fprintf(stderr, Errmsg_wrte, fldefs, strerror(errno));
return (1);
}
return (0);
}
static int
fini_defs(void)
{
if (fprintf(fddefs, "\n#else\t/* __lint */\n\n") < 0) {
(void) fprintf(stderr, Errmsg_wrte, fldefs, strerror(errno));
return (1);
}
if (fprintf(fddefs, "extern\tconst char *\t_%s(Msg);\n\n",
interface) < 0) {
(void) fprintf(stderr, Errmsg_wrte, fldefs, strerror(errno));
return (1);
}
if (fprintf(fddefs, "#ifndef MSG_SGS_LOCAL_ARRAY\n"
"#define\tMSG_SGS_LOCAL_ARRAY\t\"\"\n#endif\n\n") < 0) {
(void) fprintf(stderr, Errmsg_wrte, fldefs, strerror(errno));
return (1);
}
if (lflag == 0) {
if (fprintf(fddefs, "extern\tconst char\t__%s[];\n\n",
interface) < 0) {
(void) fprintf(stderr, Errmsg_wrte, fldefs,
strerror(errno));
return (1);
}
}
if (fprintf(fddefs,
"#define MSG_ORIG_STRTAB(_x, _s)\t_x\n"
"#define MSG_ORIG(x)\tx\n#define MSG_INTL(x)\tx\n") < 0) {
(void) fprintf(stderr, Errmsg_wrte, fldefs, strerror(errno));
return (1);
}
if (fprintf(fddefs, "\n#ifndef LINTSUP_SUPPRESS_STRINGS\n") < 0) {
(void) fprintf(stderr, Errmsg_wrte, fldefs, strerror(errno));
return (1);
}
if (fdlint) {
long size;
char *buf;
size = ftell(fdlint);
(void) rewind(fdlint);
if ((buf = malloc(size)) == 0) {
(void) fprintf(stderr, Errmsg_nmem, strerror(errno));
return (1);
}
if (fread(buf, size, 1, fdlint) == 0) {
(void) fprintf(stderr, Errmsg_read, fllint,
strerror(errno));
return (1);
}
if (fwrite(buf, size, 1, fddefs) == 0) {
(void) fprintf(stderr, Errmsg_wrte, fldefs,
strerror(errno));
return (1);
}
(void) free(buf);
}
if (fprintf(fddefs, "\n#endif\t/* LINTSUP_SUPPRESS_STRINGS */\n") < 0) {
(void) fprintf(stderr, Errmsg_wrte, fldefs, strerror(errno));
return (1);
}
if (fprintf(fddefs, "\n#endif\t/* __lint */\n") < 0) {
(void) fprintf(stderr, Errmsg_wrte, fldefs, strerror(errno));
return (1);
}
if (fprintf(fddefs, "\n#endif\n") < 0) {
(void) fprintf(stderr, Errmsg_wrte, fldefs, strerror(errno));
return (1);
}
return (0);
}
static int
output_defs(void)
{
msg_string *msg;
size_t stbufsize;
char *stbuf;
stbufsize = st_getstrtab_sz(stp);
if ((stbuf = malloc(stbufsize)) == 0) {
(void) fprintf(stderr, Errmsg_nmem, strerror(errno));
exit(1);
}
(void) st_setstrbuf(stp, stbuf, stbufsize);
for (msg = msg_head; msg; msg = msg->ms_next) {
size_t stoff;
if ((st_setstring(stp, msg->ms_message, &stoff)) == -1) {
(void) fprintf(stderr, Errmsg_mnfn, msg->ms_message);
return (1);
}
if (fprintf(fddefs, "\n#define\t%s\t%zu\n",
msg->ms_defn, stoff) < 0) {
(void) fprintf(stderr, Errmsg_wrte,
fldefs, strerror(errno));
return (1);
}
if (fddefs && fprintf(fddefs, "#define\t%s_SIZE\t%d\n",
msg->ms_defn, strlen(msg->ms_message)) < 0) {
(void) fprintf(stderr, Errmsg_wrte,
fldefs, strerror(errno));
return (1);
}
}
return (0);
}
static int
output_data(void)
{
size_t stbufsize;
size_t ndx;
size_t column = 1;
const char *stbuf;
const char *fmtstr;
stbufsize = st_getstrtab_sz(stp);
stbuf = st_getstrbuf(stp);
assert(stbuf);
if (lflag)
fmtstr = (const char *)"static const";
else
fmtstr = (const char *)"const";
if (fprintf(fddata, "\n%s char __%s[%zu] __attribute__((unused)) = { ",
fmtstr, interface, stbufsize) < 0) {
(void) fprintf(stderr, Errmsg_wrte, fldata, strerror(errno));
return (1);
}
for (ndx = 0; ndx < (stbufsize - 1); ndx++) {
if (column == 1) {
if (fddata && fprintf(fddata,
"\n/* %4zu */ 0x%.2x,", ndx,
(unsigned char)stbuf[ndx]) < 0) {
(void) fprintf(stderr, Errmsg_wrte,
fldata, strerror(errno));
return (1);
}
} else {
if (fddata && fprintf(fddata, " 0x%.2x,",
(unsigned char)stbuf[ndx]) < 0) {
(void) fprintf(stderr, Errmsg_wrte,
fldata, strerror(errno));
return (1);
}
}
if (column++ == 10)
column = 1;
}
if (column == 1)
fmtstr = "\n\t0x%.2x };\n";
else
fmtstr = " 0x%.2x };\n";
if (fprintf(fddata, fmtstr, (unsigned char)stbuf[stbufsize - 1]) < 0) {
(void) fprintf(stderr, Errmsg_wrte, fldata, strerror(errno));
return (1);
}
return (0);
}
static int
file()
{
char buffer[LINE_MAX], * token;
uint_t bufsize;
char *token_buffer;
int escape = 0;
int len = 0;
if ((token_buffer = malloc(LINE_MAX)) == 0) {
(void) fprintf(stderr, Errmsg_nmem, strerror(errno));
return (1);
}
bufsize = LINE_MAX;
line = 1;
while ((token = fgets(buffer, LINE_MAX, fddesc)) != NULL) {
char defn[PATH_MAX], * _defn, * str;
switch (*token) {
case '#':
case '$':
if (escape) {
(void) fprintf(stderr, Errmsg_malt, fldesc,
line);
return (1);
}
if (msgid) {
msgid = 0;
if (fprintf(fdmsgs, "msgstr\t\"\"\n") < 0) {
(void) fprintf(stderr, Errmsg_wrte,
flmsgs, strerror(errno));
return (1);
}
}
if (fdmsgs && (prtmsgs == 1)) {
char comment;
if (cflag == 0)
comment = '#';
else
comment = '$';
if (fprintf(fdmsgs, "%c%s", comment,
++token) < 0) {
(void) fprintf(stderr, Errmsg_wrte,
flmsgs, strerror(errno));
return (1);
}
}
break;
case '@':
if (escape) {
(void) fprintf(stderr, Errmsg_malt, fldesc,
line);
return (1);
}
if (msgid) {
msgid = 0;
if (fprintf(fdmsgs, "msgstr\t\"\"\n") < 0) {
(void) fprintf(stderr, Errmsg_wrte,
flmsgs, strerror(errno));
return (1);
}
}
token++;
while (isspace(*token))
token++;
_defn = token;
while (!(isspace(*token)))
token++;
*token++ = 0;
while (isspace(*token))
token++;
if (*token == 0) {
if (strcmp(_defn, start) == 0)
prtmsgs = 1;
else if (strcmp(_defn, end) == 0)
prtmsgs = -1;
else if (getmesgid(_defn) == 1)
return (1);
break;
}
if (prtmsgs != -1)
prtmsgs = 1;
if (fdmsgs && (prtmsgs == 1)) {
if (cflag == 1) {
if (setid == 0) {
(void) fprintf(stderr, "file "
"%s: no message identifier "
"has been established\n",
fldesc);
return (1);
}
if (ptr > NL_MSGMAX) {
(void) fprintf(stderr, "file "
"%s: message definition "
"(%d) exceeds allowable "
"limit (NL_MSGMAX)\n",
fldesc, ptr);
return (1);
}
}
if (cflag == 1) {
if (fprintf(fdmsgs, "%d\t%s", ptr,
token) < 0) {
(void) fprintf(stderr,
Errmsg_wrte, flmsgs,
strerror(errno));
return (1);
}
} else {
if (fprintf(fdmsgs, "msgid\t\"") < 0) {
(void) fprintf(stderr,
Errmsg_wrte, flmsgs,
strerror(errno));
return (1);
}
msgid = 1;
}
}
if (*token != '"') {
(void) fprintf(stderr, Errmsg_malt, fldesc,
line);
return (1);
}
(void) strcpy(defn, _defn);
if (fdlint) {
if (fprintf(fdlint, "\n#define\t%s\t",
_defn) < 0) {
(void) fprintf(stderr, Errmsg_wrte,
fllint, strerror(errno));
return (1);
}
}
len = 0;
message:
if (*token == '"') {
if (fdlint &&
(fprintf(fdlint, "%c", *token) < 0)) {
(void) fprintf(stderr, Errmsg_wrte,
fllint, strerror(errno));
return (1);
}
token++;
}
while (*token) {
char _token;
if ((*token == '\\') && (escape == 0)) {
escape = 1;
if (fdlint && (*(token + 1) != '\n') &&
fprintf(fdlint, "%c", *token) < 0) {
(void) fprintf(stderr,
Errmsg_wrte, fllint,
strerror(errno));
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
_token = *token;
if (fdmsgs && (prtmsgs == 1) &&
(fprintf(fdmsgs, "\\") < 0)) {
(void) fprintf(stderr,
Errmsg_wrte, flmsgs,
strerror(errno));
return (1);
}
} else {
if (*token == '"') {
if (fdlint && (fprintf(fdlint,
"%c", *token) < 0)) {
(void) fprintf(stderr,
Errmsg_wrte, fllint,
strerror(errno));
return (1);
}
if (fdmsgs && (prtmsgs == 1) &&
(fprintf(fdmsgs, "%c",
*token) < 0)) {
(void) fprintf(stderr,
Errmsg_wrte, flmsgs,
strerror(errno));
return (1);
}
while (*++token) {
if (*token == '\n')
break;
}
_token = '\0';
} else
_token = *token;
}
if (fdmsgs && (prtmsgs == 1) &&
(fprintf(fdmsgs, "%c", *token) < 0)) {
(void) fprintf(stderr, Errmsg_wrte,
flmsgs, strerror(errno));
return (1);
}
if (fdlint && fprintf(fdlint,
"%c", *token) < 0) {
(void) fprintf(stderr, Errmsg_wrte,
fllint, strerror(errno));
return (1);
}
if (len >= bufsize) {
bufsize += LINE_MAX;
if ((token_buffer = realloc(
token_buffer, bufsize)) == 0) {
(void) fprintf(stderr,
Errmsg_nmem,
strerror(errno));
return (1);
}
}
token_buffer[len] = _token;
ptr++, token++, len++;
escape = 0;
if (_token == '\0')
break;
}
if (escape == 0) {
const char *form = "#define\t%s_SIZE\t%d\n";
token_buffer[len] = '\0';
message_append(defn, token_buffer);
if (fdlint && fprintf(fdlint, form, defn,
(len - 1)) < 0) {
(void) fprintf(stderr, Errmsg_wrte,
fllint, strerror(errno));
return (1);
}
}
break;
default:
while (isspace(*token))
token++;
if (*token == 0) {
if (msgid || (fdmsgs && (prtmsgs == 1))) {
if (msgid) {
msgid = 0;
str = "msgstr\t\"\"\n\n";
} else
str = "\n";
if (fprintf(fdmsgs, str) < 0) {
(void) fprintf(stderr,
Errmsg_wrte, flmsgs,
strerror(errno));
return (1);
}
}
break;
}
if (escape) {
escape = 0;
goto message;
}
(void) fprintf(stderr, "file %s: line %d: invalid "
"input does not start with #, $ or @\n", fldesc,
line);
return (1);
}
line++;
}
free(token_buffer);
return (0);
}
int
main(int argc, char ** argv)
{
opterr = 0;
while ((line = getopt(argc, argv, "cd:h:lm:n:i:v")) != EOF) {
switch (line) {
case 'c':
cflag = 1;
break;
case 'd':
fldata = optarg;
break;
case 'h':
fldefs = optarg;
break;
case 'i':
flmids = optarg;
break;
case 'l':
lflag = 1;
break;
case 'm':
flmsgs = optarg;
break;
case 'n':
interface = optarg;
break;
case 'v':
vflag = 1;
break;
case '?':
(void) fprintf(stderr, Errmsg_use, argv[0]);
exit(1);
default:
break;
}
}
if ((argc - optind) < 1) {
(void) fprintf(stderr, Errmsg_use);
exit(1);
}
if (fldefs) {
if ((fddefs = fopen(fldefs, "w+")) == NULL) {
(void) fprintf(stderr, Errmsg_opne, fldefs,
strerror(errno));
return (1);
}
}
if (fldata) {
if (fldefs && (strcmp(fldefs, fldata) == 0))
fddata = fddefs;
else if ((fddata = fopen(fldata, "w+")) == NULL) {
(void) fprintf(stderr, Errmsg_opne, fldata,
strerror(errno));
return (1);
}
}
if (fddefs && fddata) {
(void) sprintf(fllint, "%s.%d.XXXXXX", nmlint, (int)getpid());
if ((mkstemp(fllint) == -1) ||
((fdlint = fopen(fllint, "w+")) == NULL)) {
(void) fprintf(stderr, Errmsg_opne, fllint,
strerror(errno));
return (1);
}
}
if (flmsgs) {
if ((fdmsgs = fopen(flmsgs, "w+")) == NULL) {
(void) fprintf(stderr, Errmsg_opne, flmsgs,
strerror(errno));
return (1);
}
}
if (flmids) {
if ((fdmids = fopen(flmids, "r")) == NULL) {
(void) fprintf(stderr, Errmsg_opne, flmids,
strerror(errno));
return (1);
}
}
if (fddefs) {
if (init_defs())
return (1);
}
for (; optind < argc; optind++) {
int err;
fldesc = argv[optind];
if ((fddesc = fopen(fldesc, "r")) == NULL) {
(void) fprintf(stderr, Errmsg_opne, fldesc,
strerror(errno));
return (1);
}
err = file();
(void) fclose(fddesc);
if (err != 0)
return (1);
}
if (msgid) {
msgid = 0;
if (fprintf(fdmsgs, "msgstr\t\"\"\n") < 0) {
(void) fprintf(stderr, Errmsg_wrte, flmsgs,
strerror(errno));
return (1);
}
}
if (fdmids)
(void) fclose(fdmids);
if (fdmsgs)
(void) fclose(fdmsgs);
if (fddefs) {
if (output_defs())
return (1);
}
if (fldata) {
if (output_data())
return (1);
}
if (fddefs) {
if (fini_defs())
return (1);
}
if (vflag)
dump_stringtab(stp);
if (fddata)
(void) fclose(fddata);
if (fddefs && (fddefs != fddata))
(void) fclose(fddefs);
if (fddefs && fddata) {
(void) fclose(fdlint);
(void) unlink(fllint);
}
if (stp)
st_destroy(stp);
return (0);
}