#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;
static char *gmsgstr;
static int gmsgid_size;
static int gmsgstr_size;
static char *outfile = NULL;
static int linenum;
static int msgid_linenum;
static int msgstr_linenum;
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)
{
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);
error(gettext(ERR_EXEC_FAILED), gnu_msgfmt);
}
static void
usage(void)
{
(void) fprintf(stderr, gettext(ERR_USAGE));
exit(2);
}
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();
}
if (flag.sun_p) {
if (flag.gnu_p) {
error(gettext(ERR_GNU_ON_SUN));
}
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_gnu_msgfmt();
}
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);
}
static void
read_psffm(char *file)
{
int fd;
static char msgfile[MAXPATHLEN];
wchar_t *linebufptr, *p;
char *bufptr = 0;
int quotefound;
int inmsgid = 0;
int inmsgstr = 0;
int indomain = 0;
wchar_t wc;
char mb;
int n;
char token_found;
unsigned int bufptr_index = 0;
char *mbuf, *addr;
size_t fsize, ln_size, ll;
wchar_t *linebufhead = NULL;
struct stat64 statbuf;
char *filename;
(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);
}
if (fstat64(fd, &statbuf) == -1) {
error(gettext(ERR_STAT_FAILED), filename);
}
fsize = (size_t)statbuf.st_size;
if (fsize == 0) {
(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);
}
(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);
} else if (ln_size == 0) {
break;
}
linenum++;
linebufptr = linebufhead;
quotefound = 0;
switch (*linebufptr) {
case L'#':
case L'\n':
continue;
case L'\"':
quotefound = 1;
break;
}
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));
}
}
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) {
bufptr_index--;
} else {
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;
}
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));
}
}
if (inmsgstr && !quotefound) {
warning(gettext(WARN_NO_MSGID), msgstr_linenum);
continue;
}
if (inmsgstr) {
bufptr_index--;
} else {
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;
}
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));
}
}
if (inmsgstr) {
sortit(gmsgid, gmsgstr);
}
if (inmsgstr || inmsgid) {
(void) memset(gmsgid, 0, gmsgid_size);
(void) memset(gmsgstr, 0, gmsgstr_size);
}
if (indomain) {
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;
}
load_buffer:
if (!bufptr) {
warning(gettext(WARN_SYNTAX_ERR), linenum);
continue;
}
if (*linebufptr++ != L'\"') {
warning(gettext(WARN_MISSING_QUOTE), linenum);
--linebufptr;
}
quotefound = 0;
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;
}
if (quotefound) {
linebufptr = consume_whitespace(linebufptr);
if (*linebufptr != L'\n') {
warning(gettext(WARN_INVALID_STRING),
linenum);
}
break;
}
}
bufptr[bufptr_index++] = '\0';
(void) strcpy(msgfile, gcurrent_domain);
(void) strcat(msgfile, ".mo");
}
if (inmsgstr) {
sortit(gmsgid, gmsgstr);
}
if (linebufhead)
free(linebufhead);
if (munmap(addr, statbuf.st_size) == -1) {
error(gettext(ERR_MUNMAP_FAILED), filename);
}
free(filename);
return;
}
static wchar_t *
consume_whitespace(wchar_t *buf)
{
wchar_t *bufptr = buf;
wchar_t c;
while ((c = *bufptr) != L'\0') {
if (c == L' ' || c == L'\t') {
bufptr++;
continue;
}
break;
}
return (bufptr);
}
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':
(*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');
}
}
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 (oflag) {
dom = find_domain_node(outfile);
} else {
dom = find_domain_node(gcurrent_domain);
}
insert_message(dom, msgid, msgstr);
}
static void
insert_message(struct domain_struct *dom,
char *msgid, char *msgstr)
{
struct msg_chain *p1;
struct msg_chain *node, *prev_node;
int b;
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) {
p1 = dom->first_elem;
} else {
p1 = dom->current_elem;
}
} else {
p1 = dom->first_elem;
}
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) {
prev_node = p1;
p1 = p1->next;
} else {
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;
}
}
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;
}
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;
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) {
last_used_domain = p1;
return (p1);
} else if (b > 0) {
prev_node = p1;
p1 = p1->next;
} else {
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) {
prev_node->next = node;
} else {
first_domain = node;
}
last_used_domain = node;
return (node);
}
}
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) {
prev_node->next = node;
} else {
first_domain = node;
}
last_used_domain = node;
return (node);
}
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);
}
static void
output_all_mo_files(void)
{
struct domain_struct *p;
p = first_domain;
while (p) {
if (p->first_elem) {
output_one_mo_file(p);
}
p = p->next;
}
return;
}
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;
(void) strcpy(fname, dom->domain);
if (!oflag) {
(void) strcat(fname, ".mo");
}
fp = fopen(fname, "w");
if (fp == NULL) {
error(gettext(ERR_OPEN_FAILED), fname);
}
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;
}
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
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);
i = 0;
p = dom->first_elem;
while (p) {
(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;
}
p = dom->first_elem;
while (p) {
(void) fwrite(p->msgid, strlen(p->msgid)+1, 1, fp);
p = p->next;
}
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;
}
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) {
return (0);
}
th = (wchar_t *)Xmalloc(sizeof (wchar_t) * tbufsize);
nc = tbufsize;
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') {
if (nc <= 1) {
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;
}
if (nc <= 1) {
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));
}
#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;
}
}
#endif