/*
* 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 2004 Sun Microsystems, Inc. All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include "sun_msgfmt.h"
static void read_psffm(char *);
static void sortit(char *, char *);
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 *);
#ifdef DEBUG
static void printlist(void);
#endif
static int oflag = 0;
static int sun_p = 0;
int verbose = 0;
static int mbcurmax;
static char **oargv;
static char *inputdir;
void
invoke_gnu_msgfmt(void)
{
/*
*/
char *gnu_msgfmt;
#ifdef DEBUG_MSGFMT
if (!gnu_msgfmt)
#else
#endif
if (verbose) {
}
/* exec failed */
/* NOTREACHED */
}
static void
usage(void)
{
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
{
int ret;
#if !defined(TEXT_DOMAIN)
#endif
(void) textdomain(TEXT_DOMAIN);
if (ret == -1) {
usage();
/* NOTREACHED */
}
/* never invoke gnu msgfmt */
/* NOTREACHED */
}
}
}
oflag = 1;
}
verbose = 1;
}
/* NOTREACHED */
}
/*
* read all portable object files specified in command arguments.
* Allocate initial size for msgid and msgstr. If it needs more
* spaces, realloc later.
*/
while (argc-- > 0) {
if (verbose) {
}
read_psffm(*argv++);
}
#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 <domain>
*/
static void
{
int fd;
char *bufptr = 0;
char mb;
int n;
char *filename;
/*
* For each po file to be read,
* 1) set domain to default and
* 2) set linenumer to 0.
*/
linenum = 0;
if (!inputdir) {
} else {
}
if (fd == -1) {
/* NOTREACHED */
}
/* NOTREACHED */
}
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.
*/
return;
}
if (addr == MAP_FAILED) {
/* NOTREACHED */
}
if (!sun_p)
for (;;) {
if (linebufhead) {
linebufhead = NULL;
}
/* NOTREACHED */
} else if (ln_size == 0) {
break; /* End of File. */
}
linenum++;
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.
*/
MSGID_LEN) == 0) ? 1 : 0;
if (token_found) {
/* NOTREACHED */
}
}
if (inmsgid && !quotefound) {
continue;
}
if (inmsgstr) {
}
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.
*/
p = linebufptr;
linebufptr + MSGID_LEN);
ln_size -= linebufptr - p;
bufptr_index = 0;
}
inmsgid = 1;
inmsgstr = 0;
indomain = 0;
goto load_buffer;
}
/*
* Process MSGSTR Tokens.
*/
MSGSTR_LEN) == 0) ? 1 : 0;
if (token_found) {
/* NOTREACHED */
}
}
if (inmsgstr && !quotefound) {
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.
*/
p = linebufptr;
linebufptr + MSGSTR_LEN);
ln_size -= linebufptr - p;
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.
*/
DOMAIN_LEN) == 0) ? 1 : 0;
if (token_found) {
/* NOTREACHED */
}
}
/*
* process msgid and msgstr pair for previous domain
*/
if (inmsgstr) {
}
/* refresh msgid and msgstr buffer */
}
if (indomain) {
/* multiple lines of domain */
/* cancel the previous null termination */
bufptr_index--;
} else {
p = linebufptr;
linebufptr + DOMAIN_LEN);
(void) memset(gcurrent_domain, 0,
sizeof (gcurrent_domain));
ln_size -= linebufptr - p;
bufptr_index = 0;
}
indomain = 1;
inmsgid = 0;
inmsgstr = 0;
} /* if */
/*
* 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) {
continue;
}
if (*linebufptr++ != L'\"') {
--linebufptr;
}
quotefound = 0;
/*
* If there is not enough space in the buffer,
* increase buffer by ln_size by realloc.
*/
bufptr_index + ll);
}
bufptr_index + ll);
}
}
while (wc = *linebufptr++) {
switch (wc) {
case L'\n':
if (!quotefound) {
}
break;
case L'\"':
quotefound = 1;
break;
case L'\\':
break;
default:
bufptr_index += n;
} /* switch */
if (quotefound) {
/*
* Check if any remaining characters
* after closing quote.
*/
if (*linebufptr != L'\n') {
linenum);
}
break;
}
} /* while */
} /* for(;;) */
if (inmsgstr) {
}
if (linebufhead)
/* NOTREACHED */
}
return;
} /* read_psffm */
/*
* Skip leading white spaces and tabs.
*/
static wchar_t *
{
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
{
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');
(*buf)++;
(*buf)++;
}
}
return (n);
default:
return (NULL);
}
} /* 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
{
#ifdef DEBUG
"==> sortit(), domain=<%s> msgid=<%s> msgstr=<%s>\n",
#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) {
} else {
}
}
/*
* This routine inserts message in the current domain message list.
* It is inserted in ascending order.
*/
static void
{
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 (b == 0) {
if (verbose)
return;
} else if (b > 0) { /* to implement descending order */
} else {
}
} else {
}
/*
* search msgid insert position in the list
* Search starts from the node pointed by p1.
*/
while (p1) {
if (b == 0) {
if (verbose)
return;
} else if (b < 0) { /* to implement descending order */
/* move to the next node */
} else {
/* insert a new msg node */
if (prev_node) {
} else {
}
return;
}
} /* while */
/*
* msgid is smaller than any of msgid in the list or
* list is empty.
* Therefore, append it.
*/
if (prev_node) {
} else {
}
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 *
{
int b;
/* for perfomance, check cache 'last_used_domain' */
if (last_used_domain) {
if (b == 0) {
return (last_used_domain);
} else if (b < 0) {
p1 = first_domain;
} else {
}
} else {
p1 = first_domain;
}
while (p1) {
if (b == 0) {
/* node found */
return (p1);
} else if (b > 0) {
/* move to the next node */
} else {
/* insert a new domain node */
node = (struct domain_struct *)
Xmalloc(sizeof (struct domain_struct));
if (prev_node) {
/* insert the node in the middle */
} else {
/* node inserted is the smallest */
first_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));
if (prev_node) {
/* domain list is not empty */
} else {
/* domain list is empty */
first_domain = node;
}
return (node);
} /* find_domain_node */
/*
* binary_compute() is used for pre-computing a binary search.
*/
static int
{
int k;
if (i > j) {
return (LEAFINDICATOR);
}
k = (i + j) / 2;
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) {
}
p = p->next;
}
return;
} /* output_all_mo_files */
/*
* Write one domain data list to file.
*/
static void
{
struct msg_chain *p;
int message_count;
int string_count_msgid;
int string_count_msg;
int msgid_index = 0;
int msgstr_index = 0;
int i;
return;
/*
* If -o flag is specified, then file name is used as domain name.
* If not, ".mo" is appended to the domain name.
*/
if (!oflag) {
}
/* NOTREACHED */
}
/* compute offsets and counts */
message_count = 0;
p = dom->first_elem;
while (p) {
p->msgid_offset = msgid_index;
p->msgstr_offset = msgstr_index;
p = p->next;
}
/*
* Fill up less and more entries to be used for binary search.
*/
#ifdef DEBUG
{
int i;
for (i = 0; i < message_count; i++) {
" less[%2d]=%2d, more[%2d]=%2d\n",
}
}
#endif
/*
* write out the message object file.
* The middle one is the first message to check by gettext().
*/
i = MSG_STRUCT_SIZE * message_count;
/* march through linked list and write out all nodes. */
i = 0;
p = dom->first_elem;
while (p) { /* put out message struct */
i++;
p = p->next;
}
/* put out message id strings */
p = dom->first_elem;
while (p) {
p = p->next;
}
/* put out message strings */
p = dom->first_elem;
while (p) {
p = p->next;
}
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
{
int nb;
if (*fsize == 0) {
/* eof */
return (0);
}
/* skip preceding whitespaces */
while ((*pc != '\0')) {
pc++;
(*fsize)--;
} else {
break;
}
}
while (*fsize > 0) {
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'
*/
}
*tp++ = L'\n';
*tp++ = L'\0';
}
if (nc == 0) {
}
nc--;
} /* 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'
*/
}
*tp++ = L'\n';
*tp++ = L'\0';
}
/*
* This is debug function. Not compiled in the final executable.
*/
#ifdef DEBUG
static void
printlist(void)
{
struct domain_struct *p;
struct msg_chain *m;
p = first_domain;
while (p) {
m = p->first_elem;
while (m) {
m = m->next;
}
p = p->next;
}
} /* printlist */
#endif