xgettext.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 1991, 1999, 2001-2002 Sun Microsystems, Inc.
* All rights reserved.
* Use is subject to license terms.
*/
#pragma ident "%Z%%M% %I% %E% SMI"
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define TRUE 1
#define FALSE 0
#define MAX_PATH_LEN 1024
#define MAX_DOMAIN_LEN 1024
#define MAX_STRING_LEN 2048
#define USAGE "Usage: xgettext [-a [-x exclude-file]] [-jns]\
[-c comment-tag]\n [-d default-domain] [-m prefix] \
[-M suffix] [-p pathname] files ...\n\
xgettext -h\n"
#define DEFAULT_DOMAIN "messages"
extern char yytext[];
extern int yylex(void);
/*
* Contains a list of strings to be used to store ANSI-C style string.
* Each quoted string is stored in one node.
*/
struct strlist_st {
char *str;
struct strlist_st *next;
};
/*
* istextdomain : Boolean telling if this node contains textdomain call.
* isduplicate : Boolean telling if this node duplicate of any other msgid.
* msgid : contains msgid or textdomain if istextdomain is true.
* msgstr : contains msgstr.
* comment : comment extracted in case of -c option.
* fname : tells which file contains msgid.
* linenum : line number in the file.
* next : Next node.
*/
struct element_st {
char istextdomain;
char isduplicate;
struct strlist_st *msgid;
struct strlist_st *msgstr;
struct strlist_st *comment;
char *fname;
int linenum;
struct element_st *next;
};
/*
* dname : domain name. NULL if default domain.
* gettext_head : Head of linked list containing [d]gettext().
* gettext_tail : Tail of linked list containing [d]gettext().
* textdomain_head : Head of linked list containing textdomain().
* textdomain_tail : Tail of linked list containing textdomain().
* next : Next node.
*
* Each domain contains two linked list.
* (gettext_head, textdomain_head)
* If -s option is used, then textdomain_head contains all
* textdomain() calls and no textdomain() calls are stored in gettext_head.
* If -s option is not used, textdomain_head is empty list and
* gettext_head contains all gettext() dgettext(), and textdomain() calls.
*/
struct domain_st {
char *dname;
struct element_st *gettext_head;
struct element_st *gettext_tail;
struct element_st *textdomain_head;
struct element_st *textdomain_tail;
};
/*
* There are two domain linked lists.
* def_dom contains default domain linked list and
* dom_head contains all other deomain linked lists to be created by
* dgettext() calls.
*/
/*
* This linked list contains a list of strings to be excluded when
* -x option is used.
*/
static struct exclude_st {
struct strlist_st *exstr;
struct exclude_st *next;
} *excl_head;
/*
* All option flags and values for each option if any.
*/
static char *comment_tag = NULL;
static char *default_domain = NULL;
static char *exclude_file = NULL;
/*
* Each variable shows the current state of parsing input file.
*
* in_comment : Means inside comment block (C or C++).
* in_cplus_comment : Means inside C++ comment block.
* in_gettext : Means inside gettext call.
* in_dgettext : Means inside dgettext call.
* in_dcgettext : Means inside dcgettext call.
* in_textdomain : Means inside textdomain call.
* in_str : Means currently processing ANSI style string.
* in_quote : Means currently processing double quoted string.
* in_skippable_string : Means currently processing double quoted string,
* that occurs outside a call to gettext, dgettext,
* dcgettext, textdomain, with -a not specified.
* is_last_comment_line : Means the current line is the last line
* of the comment block. This is necessary because
* in_comment becomes FALSE when '* /' is encountered.
* is_first_comma_found : This is used only for dcgettext because dcgettext()
* requires 2 commas. So need to do different action
* depending on which commas encountered.
* num_nested_open_paren : This keeps track of the number of open parens to
* handle dcgettext ((const char *)0,"msg",LC_TIME);
*/
static int in_comment = FALSE;
static int in_cplus_comment = FALSE;
static int in_gettext = FALSE;
static int in_dgettext = FALSE;
static int in_dcgettext = FALSE;
static int in_textdomain = FALSE;
static int is_last_comment_line = FALSE;
static int is_first_comma_found = FALSE;
static int in_skippable_string = FALSE;
static int num_nested_open_paren = 0;
/*
* This variable contains the first line of gettext(), dgettext(), or
* textdomain() calls.
* This is necessary for multiple lines of a single call to store
* the starting line.
*/
static int linenum_saved = 0;
/*
* curr_file : Contains current file name processed.
* curr_domain : Contains the current domain for each dgettext().
* This is NULL for gettext().
* curr_line : Contains the current line processed.
* qstring_buf : Contains the double quoted string processed.
* curr_linenum : Line number being processed in the current input file.
* warn_linenum : Line number of current warning message.
*/
char curr_file[MAX_PATH_LEN];
static char curr_domain[MAX_DOMAIN_LEN];
static char curr_line[MAX_STRING_LEN];
static char qstring_buf[MAX_STRING_LEN];
int curr_linenum = 1;
int warn_linenum = 0;
/*
* strhead : This list contains ANSI style string.
* Each node contains double quoted string.
* strtail : This is the tail of strhead.
* commhead : This list contains comments string.
* Each node contains one line of comment.
* commtail : This is the tail of commhead.
*/
/*
* gargc : Same as argc. Used to pass argc to lex routine.
* gargv : Same as argv. Used to pass argc to lex routine.
*/
int gargc;
char **gargv;
static void add_line_to_comment(void);
static void add_qstring_to_str(void);
static void add_str_to_element_list(int, char *);
static void copy_strlist_to_str(char *, struct strlist_st *);
static void end_ansi_string(void);
static void free_strlist(struct strlist_st *);
void handle_newline(void);
static void initialize_globals(void);
static void print_help(void);
static void read_exclude_file(void);
static void trim_line(char *);
static void write_all_files(void);
static void write_one_file(struct domain_st *);
static void lstrcat(char *, const char *);
/*
* Utility functions to malloc a node and initialize fields.
*/
static struct domain_st *new_domain(void);
static struct strlist_st *new_strlist(void);
static struct element_st *new_element(void);
static struct exclude_st *new_exclude(void);
/*
* Main program of xgettext.
*/
int
{
int c;
switch (c) {
case 'a':
break;
case 'c':
break;
case 'd':
break;
case 'h':
break;
case 'j':
break;
case 'M':
break;
case 'm':
break;
case 'n':
break;
case 'p':
break;
case 's':
break;
case 't':
break;
case 'x':
break;
case '?':
break;
}
}
/* if -h is used, ignore all other options. */
print_help();
exit(0);
}
/* -x can be used only with -a */
/* -j cannot be used with -a */
"-a and -j options cannot be used together.\n");
}
/* -j cannot be used with -s */
"-j and -s options cannot be used together.\n");
}
exit(2);
}
/* error, if no files are specified. */
exit(2);
}
}
/* If files are -, then read from stdin */
stdin_only = TRUE;
optind++;
} else {
stdin_only = FALSE;
}
/* Store argc and argv to pass to yylex() */
#ifdef DEBUG
{
int i = optind;
for (; i < argc; i++) {
}
}
#endif
if (stdin_only == FALSE) {
exit(2);
}
optind++;
}
/*
* Process input.
*/
(void) yylex();
#ifdef DEBUG
printf("\n======= default_domain ========\n");
printf("======= domain list ========\n");
#endif
/*
* Write out all .po files.
*/
return (0);
} /* main */
/*
* Prints help information for each option.
*/
static void
print_help(void)
{
"-a\t\t\tfind ALL strings\n");
"-c <comment-tag>\tget comments containing <flag>\n");
"-d <default-domain>\tuse <default-domain> for default domain\n");
"-h\t\t\tHelp\n");
"-j\t\t\tupdate existing file with the current result\n");
"-M <suffix>\t\tfill in msgstr with msgid<suffix>\n");
"-m <prefix>\t\tfill in msgstr with <prefix>msgid\n");
"-n\t\t\tline# file name and line number info in output\n");
"-p <pathname>\t\tuse <pathname> for output file directory\n");
"-s\t\t\tgenerate sorted output files\n");
"-x <exclude-file>\texclude strings in file <exclude-file> from output\n");
"-\t\t\tread stdin, use as a filter (input only)\n");
} /* print_help */
/*
* Extract file name and line number information from macro line
* and set the global variable accordingly.
* The valid line format is
* 1) # nnn
* or
* 2) # nnn "xxxxx"
* where nnn is line number and xxxxx is file name.
*/
static void
extract_filename_linenumber(char *mline)
{
int num;
char *p, *q, *r;
/*
* mline can contain multi newline.
* line number should be increased by the number of newlines.
*/
p = mline;
p++;
curr_linenum++;
}
if (p == NULL)
return;
q = strchr(++p, ' ');
if (q == NULL) {
/* case 1 */
curr_linenum = num;
return;
}
} else {
/* case 2 */
*q++ = 0;
if (*q == '"') {
q++;
r = strchr(q, '"');
if (r == NULL) {
return;
}
*r = 0;
curr_linenum = num;
}
}
}
} /* extract_filename_linenumber */
/*
* Handler for MACRO line which starts with #.
*/
void
handle_macro_line(void)
{
#ifdef DEBUG
#endif
} else if (in_comment == FALSE) {
}
curr_linenum--;
} /* handle_macro_line */
/*
* Handler for C++ comments which starts with //.
*/
void
{
} else if ((in_comment == FALSE) &&
(in_skippable_string == FALSE)) {
/*
* If already in c comments, don't do anything.
* Set both flags to TRUE here.
* Both flags will be set to FALSE when newline
* encounters.
*/
in_comment = TRUE;
}
} /* handle_cplus_comment_line */
/*
* Handler for the comment start (slash asterisk) in input file.
*/
void
handle_open_comment(void)
{
} else if ((in_comment == FALSE) &&
(in_skippable_string == FALSE)) {
in_comment = TRUE;
/*
* If there is any comment extracted before accidently,
* clean it up and start the new comment again.
*/
}
}
/*
* Handler for the comment end (asterisk slash) in input file.
*/
void
handle_close_comment(void)
{
} else if (in_skippable_string == FALSE) {
in_comment = FALSE;
}
}
/*
* Handler for "gettext" in input file.
*/
void
handle_gettext(void)
{
/*
* If -t option is specified to extrct dcgettext,
* don't do anything for gettext().
*/
return;
}
} else if (in_comment == FALSE) {
in_gettext = TRUE;
/*
* gettext will be put into default domain .po file
* curr_domain does not change for gettext.
*/
curr_domain[0] = NULL;
}
} /* handle_gettext */
/*
* Handler for "dgettext" in input file.
*/
void
handle_dgettext(void)
{
/*
* If -t option is specified to extrct dcgettext,
* don't do anything for dgettext().
*/
return;
}
} else if (in_comment == FALSE) {
in_dgettext = TRUE;
/*
* dgettext will be put into domain file specified.
* curr_domain will follow.
*/
curr_domain[0] = NULL;
}
} /* handle_dgettext */
/*
* Handler for "dcgettext" in input file.
*/
void
handle_dcgettext(void)
{
/*
* dcgettext will be extracted only when -t flag is specified.
*/
return;
}
} else if (in_comment == FALSE) {
in_dcgettext = TRUE;
/*
* dcgettext will be put into domain file specified.
* curr_domain will follow.
*/
curr_domain[0] = NULL;
}
} /* handle_dcgettext */
/*
* Handler for "textdomain" in input file.
*/
void
handle_textdomain(void)
{
} else if (in_comment == FALSE) {
curr_domain[0] = NULL;
}
} /* handle_textdomain */
/*
* Handler for '(' in input file.
*/
void
handle_open_paren(void)
{
} else if (in_comment == FALSE) {
if ((in_gettext == TRUE) ||
(in_dgettext == TRUE) ||
(in_dcgettext == TRUE) ||
(in_textdomain == TRUE)) {
}
}
} /* handle_open_paren */
/*
* Handler for ')' in input file.
*/
void
handle_close_paren(void)
{
} else if (in_comment == FALSE) {
if ((in_gettext == TRUE) ||
(in_dgettext == TRUE) ||
(in_dcgettext == TRUE) ||
(in_textdomain == TRUE)) {
/*
* If this is not the matching close paren with
* the first open paren, no action is necessary.
*/
if (--num_nested_open_paren > 0)
return;
in_gettext = FALSE;
in_dgettext = FALSE;
}
}
} /* handle_close_paren */
/*
* Handler for '\\n' in input file.
*
* This is a '\' followed by new line.
* This can be treated like a new line except when this is a continuation
* of a ANSI-C string.
* If this is a part of ANSI string, treat the current line as a double
* quoted string and the next line is the start of the double quoted
* string.
*/
void
handle_esc_newline(void)
{
curr_linenum++;
} else if ((in_comment == TRUE) ||
(is_last_comment_line == TRUE)) {
if (in_cplus_comment == FALSE) {
}
}
} /* handle_esc_newline */
/*
* Handler for '"' in input file.
*/
void
handle_quote(void)
{
if (in_comment == TRUE) {
/*EMPTY*/
} else if ((in_gettext == TRUE) ||
(in_dgettext == TRUE) ||
(in_dcgettext == TRUE) ||
(in_textdomain == TRUE)) {
} else {
}
}
/*
* The quote is found outside of gettext, dgetext, and
* textdomain. Everytime a quoted string is found,
* add it to the string list.
* in_str stays TRUE until ANSI string ends.
*/
} else {
}
} else {
}
} else {
}
} /* handle_quote */
/*
* Handler for ' ' or TAB in input file.
*/
void
handle_spaces(void)
{
}
} /* handle_spaces */
/*
* Flattens a linked list containing ANSI string to the one string.
*/
static void
{
struct strlist_st *p;
p = strlist;
while (p != NULL) {
}
p = p->next;
}
}
} /* copy_strlist_to_str */
/*
* Handler for ',' in input file.
*/
void
handle_comma(void)
{
} else if (in_comment == FALSE) {
if (in_dgettext == TRUE) {
} else if (in_dcgettext == TRUE) {
/*
* Ignore the second comma.
*/
if (is_first_comma_found == FALSE) {
strhead);
}
}
}
}
} /* handle_comma */
/*
* Handler for any other character that does not have special handler.
*/
void
handle_character(void)
{
} else if (in_comment == FALSE) {
}
}
}
} /* handle_character */
/*
* Handler for new line in input file.
*/
void
handle_newline(void)
{
curr_linenum++;
/*
* in_quote is always FALSE here for ANSI-C code.
*/
if ((in_comment == TRUE) ||
(is_last_comment_line == TRUE)) {
if (in_cplus_comment == TRUE) {
in_comment = FALSE;
} else {
}
}
/*
* C++ comment always ends with new line.
*/
} /* handle_newline */
/*
* Process ANSI string.
*/
static void
end_ansi_string(void)
{
(in_gettext == FALSE) &&
(in_dgettext == FALSE) &&
(in_dcgettext == FALSE) &&
(in_textdomain == FALSE)) {
}
} /* end_ansi_string */
/*
* Initialize global variables if necessary.
*/
static void
initialize_globals(void)
{
curr_domain[0] = NULL;
qstring_buf[0] = NULL;
} /* initialize_globals() */
/*
* Extract only string part when read a exclude file by removing
* keywords (e.g. msgid, msgstr, # ) and heading and trailing blanks and
* double quotes.
*/
static void
{
int i, p, len;
int first = 0;
int last = 0;
char c;
/*
* Find the position of the last non-whitespace character.
*/
i = len - 1;
/*CONSTCOND*/
while (1) {
c = line[i--];
if ((c != ' ') && (c != '\n') && (c != '\t')) {
last = ++i;
break;
}
}
/*
* Find the position of the first non-whitespace character
* by skipping "msgid" initially.
*/
i = 5;
i = 6;
i = 2;
} else {
i = 0;
}
/*CONSTCOND*/
while (1) {
c = line[i++];
if ((c != ' ') && (c != '\n') && (c != '\t')) {
first = --i;
break;
}
}
/*
* For Backward compatibility, we consider both double quoted
* string and non-quoted string.
* The double quote is removed before being stored if exists.
*/
first++;
}
last--;
}
/*
* Now copy the valid part of the string.
*/
p = first;
}
} /* trim_line */
/*
* Read exclude file and stores it in the global linked list.
*/
static void
read_exclude_file(void)
{
struct exclude_st *tmp_excl;
struct strlist_st *tail;
int ignore_line;
char line [MAX_STRING_LEN];
exit(2);
}
ignore_line = TRUE;
/*
* Line starting with # is a comment line and ignored.
* Blank line is ignored, too.
*/
continue;
ignore_line = TRUE;
ignore_line = TRUE;
ignore_line = FALSE;
tmp_excl = new_exclude();
/*
* Prepend new exclude string node to the list.
*/
} else {
/*
* If more than one line of string forms msgid,
* append it to the string linked list.
*/
if (ignore_line == FALSE) {
}
}
} /* while */
#ifdef DEBUG
printf("============================\n");
}
}
#endif
} /* read_exclude_file */
/*
* Get next character from the string list containing ANSI style string.
* This function returns three valus. (p, *m, *c).
* p is returned by return value and, *m and *c are returned by changing
* values in the location pointed.
*
* p : points node in the linked list for ANSI string.
* Each node contains double quoted string.
* m : The location of the next characters in the double quoted string
* as integer index in the string.
* When it gets to end of quoted string, the next node will be
* read and m starts as zero for every new node.
* c : Stores the value of the characterto be returned.
*/
static struct strlist_st *
get_next_ch(struct strlist_st *p, int *m, char *c)
{
int value, i;
/*
* From the string list, find non-null string first.
*/
/*CONSTCOND*/
while (1) {
if (p == NULL) {
break;
p = p->next;
p = p->next;
*m = 0;
} else {
break;
}
}
/*
* No more character is available.
*/
if (p == NULL) {
*c = 0;
return (NULL);
}
/*
* Check if the character back slash.
* If yes, ANSI defined escape sequence rule is used.
*/
if (p->str[*m] != '\\') {
*c = p->str[*m];
*m = *m + 1;
return (p);
} else {
/*
* Get next character after '\'.
*/
*m = *m + 1;
switch (ch) {
case 'a':
*c = '\a';
break;
case 'b':
*c = '\b';
break;
case 'f':
*c = '\f';
break;
case 'n':
*c = '\n';
break;
case 'r':
*c = '\r';
break;
case 't':
*c = '\t';
break;
case 'v':
*c = '\v';
break;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
/*
* Get maximum of three octal digits.
*/
for (i = 0; i < 2; i++) {
*m = *m + 1;
} else {
*m = *m - 1;
break;
}
}
*c = value;
#ifdef DEBUG
/* (void) fprintf(stderr, "octal=%d\n", value); */
#endif
break;
case 'x':
value = 0;
/*
* Remove all heading zeros first and
* get one or two valuid hexadecimal charaters.
*/
*m = *m + 1;
while (p->str[*m] == '0') {
*m = *m + 1;
}
value = 0;
for (i = 0; i < 2; i++) {
*m = *m + 1;
} else {
*m = *m - 1;
break;
}
}
*c = value;
#ifdef DEBUG
#endif
*m = *m - 1;
break;
default :
/*
* Undefined by ANSI.
* Just ignore "\".
*/
*c = p->str[*m];
break;
}
/*
* Advance pointer to point the next character to be parsed.
*/
*m = *m + 1;
return (p);
}
} /* get_next_ch */
/*
* Compares two msgids.
* Comparison is done by values, not by characters represented.
* For example, '\t', '\011' and '0x9' are identical values.
* Return values are same as in strcmp.
* 1 if msgid1 > msgid2
* 0 if msgid1 = msgid2
* -1 if msgid1 < msgid2
*/
static int
{
m1 = 0;
m2 = 0;
/*CONSTCOND*/
while (1) {
return (0);
}
return (1);
return (-1);
}
}
/*NOTREACHED*/
} /* msgidcmp */
/*
* Check if a ANSI string (which is a linked list itself) is a duplicate
* of any string in the list of ANSI string.
*/
static int
{
struct element_st *p;
return (FALSE);
}
p = list;
while (p != NULL) {
return (TRUE);
}
}
p = p->next;
}
return (FALSE);
} /* isduplicate */
/*
* Extract a comment line and add to the linked list containing
* comment block.
* Each comment line is stored in the node.
*/
static void
add_line_to_comment(void)
{
struct strlist_st *tmp_str;
tmp_str = new_strlist();
/* Empty comment list */
} else {
/* append it to the list */
}
} /* add_line_to_comment */
/*
* Add a double quoted string to the linked list containing ANSI string.
*/
static void
add_qstring_to_str(void)
{
struct strlist_st *tmp_str;
tmp_str = new_strlist();
/* Null ANSI string */
} else {
/* Append it to the ANSI string linked list */
}
qstring_buf[0] = NULL;
} /* add_qstring_to_str */
/*
* Finds the head of domain nodes given domain name.
*/
static struct domain_st *
find_domain_node(char *dname)
{
/*
* If -a option is specified everything will be written to the
* default domain file.
*/
def_dom = new_domain();
}
return (def_dom);
}
def_dom = new_domain();
}
"%s \"%s\" is used in dgettext of file:%s line:%d.\n",
"Warning: default domain name",
}
return (def_dom);
} else {
p = dom_head;
while (p != NULL) {
return (p);
}
p = p->next;
}
tmp_dom = new_domain();
} else {
}
return (tmp_dom);
}
} /* find_domain_node */
/*
* Frees the ANSI string linked list.
*/
static void
{
struct strlist_st *p;
p = ptr;
while (p != NULL) {
free(p);
p = ptr;
}
} /* free_strlist */
/*
* Finds if a ANSI string is contained in the exclude file.
*/
static int
{
struct exclude_st *p;
p = excl_head;
while (p != NULL) {
return (TRUE);
}
p = p->next;
}
return (FALSE);
} /* isexcluded */
/*
* Finds if a comment block is to be extracted.
*
* When -c option is specified, find out if comment block contains
* comment-tag as a token separated by blanks. If it does, this
* comment block is associated with the next msgid encountered.
* Comment block is a linked list where each node contains one line
* of comments.
*/
static int
{
struct strlist_st *p;
p = strlist;
while (p != NULL) {
/*CONSTCOND*/
while (1) {
break;
break;
}
pc++;
}
return (TRUE);
}
}
p = p->next;
} /* while */
/*
* Not found.
*/
return (FALSE);
} /* isextracted */
/*
* Adds ANSI string to the domain element list.
*/
static void
{
struct element_st *tmp_elem;
struct element_st *p, *q;
int result;
/*
* This can happen if something like gettext(USAGE) is used
* and it is impossible to get msgid for this gettext.
* Since -x option should be used in this kind of cases,
* it is OK not to catch msgid.
*/
return;
}
/*
* The global variable curr_domain contains either NULL
* for default_domain or domain name for dgettext().
*/
/*
* If this msgid is in the exclude file,
* then free the linked list and return.
*/
if ((istextdomain == FALSE) &&
return;
}
tmp_elem = new_element();
/*
* If -c option is specified and TAG matches,
* then associate the comment to the next [d]gettext() calls
* encountered in the source code.
* textdomain() calls will not have any effect.
*/
if (istextdomain == FALSE) {
} else {
}
}
}
/*
* If this is textdomain() call and -s option is specified,
* append this node to the textdomain linked list.
*/
if (istextdomain == TRUE) {
} else {
}
return;
}
/*
* Insert the node to the properly sorted position.
*/
q = NULL;
p = tmp_dom->gettext_head;
while (p != NULL) {
if (result == 0) {
/*
* Duplicate id. Do not store.
*/
return;
} else if (result > 0) {
/* move to the next node */
q = p;
p = p->next;
} else {
if (q != NULL) {
} else {
}
return;
}
} /* while */
/*
* New msgid is the largest or empty list.
*/
if (q != NULL) {
/* largest case */
} else {
/* empty list */
}
} else {
/*
* Check if this msgid is already in the same domain.
*/
}
}
/*
* If -s option is not specified, then everything
* is stored in gettext linked list.
*/
} else {
}
}
} /* add_str_to_element_list */
/*
* Write all domain linked list to the files.
*/
static void
write_all_files(void)
{
/*
* Write out default domain file.
*/
/*
* If dgettext() exists and -a option is not used,
* then there are non-empty linked list.
*/
}
} /* write_all_files */
/*
* add an element_st list to the linked list.
*/
static void
{
return;
}
} else {
}
} /* add_node_to_polist */
#define INIT_STATE 0
#define IN_MSGID 1
#define IN_MSGSTR 2
#define IN_COMMENT 3
/*
* Reads existing po file into the linked list and returns the head
* of the linked list.
*/
static struct element_st *
{
int state = INIT_STATE;
char line [MAX_STRING_LEN];
return (NULL);
}
/*
* Line starting with # is a comment line and ignored.
* Blank line is ignored, too.
*/
if (line[0] == '\n') {
continue;
} else if (line[0] == '#') {
/*
* If tmp_elem is not NULL, there is msgid pair
* stored. Therefore, add it.
*/
}
state = IN_COMMENT;
tmp_elem = new_element();
new_strlist();
/*
* remove new line and skip "# "
* in the beginning of the existing
* comment line.
*/
} else if (state == IN_COMMENT) {
/*
* remove new line and skip "# "
* in the beginning of the existing
* comment line.
*/
}
/* ignore domain line */
continue;
tmp_elem = new_element();
} else if (state == INIT_STATE) {
tmp_elem = new_element();
}
} else {
/*
* If more than one line of string forms msgid,
* append it to the string linked list.
*/
}
}
} /* while */
/*
* To insert the last msgid pair.
*/
}
#ifdef DEBUG
{
char tmpstr[256];
}
#endif /* DEBUG */
return (ehead);
} /* read_po */
/*
* This function will append the second list to the first list.
* If the msgid in the second list contains msgid in the first list,
* it will be marked as duplicate.
*/
static struct element_st *
{
return (l2);
return (l1);
/*
* in this while loop, just mark isduplicate field of node in the
* l2 list if the same msgid exists in l1 list.
*/
p = l2;
while (p != NULL) {
q = l1;
while (q != NULL) {
p->isduplicate = TRUE;
break;
}
q = q->next;
}
p = p->next;
}
/* Now connect two linked lists. */
break;
}
return (l1);
} /* append_list */
/*
* Writes one domain list to the file.
*/
static void
{
char fname [MAX_PATH_LEN];
char dname [MAX_DOMAIN_LEN];
struct element_st *p;
struct element_st *existing_po_list;
/*
* If head is NULL, then it still has to create .po file
* so that it will guarantee that the previous .po file was
* alwasys deleted.
* This is why checking NULL pointer has been moved to after
* creating .po file.
*/
/*
* If domain name is NULL, it is the default domain list.
* The domain name is either "messages" or specified by option -d.
* The default domain name is contained in default_domain variable.
*/
} else {
}
/*
* path is the current directory if not specified by option -p.
*/
fname[0] = 0;
}
/*
* If -j flag is specified, read exsiting .po file and
* append the current list to the end of the list read from
* the existing .po file.
*/
/*
* If head is NULL, we don't have to change existing file.
* Therefore, just return it.
*/
return;
}
head->gettext_head);
#ifdef DEBUG
printf("===after merge (-j option): <%s>===\n",
} else {
printf("===after merge (-j option): <NULL>===\n");
}
#endif
} /* if jflg */
"ERROR, can't open output file: %s\n", fname);
exit(2);
}
/* See comments above in the beginning of this function */
return;
/*
* There are separate storage for textdomain() calls if
* -s option is used (textdomain_head linked list).
* Otherwise, textdomain() is mixed with gettext(0 and dgettext().
* If mixed, the boolean varaible istextdomain is used to see
* if the current node contains textdomain() or [d]gettext().
*/
p = head->textdomain_head;
while (p != NULL) {
/*
* textdomain output line already contains
* FIle name and line number information.
* Therefore, does not have to check for nflg.
*/
output_textdomain(fp, p);
p = p->next;
}
}
p = head->gettext_head;
while (p != NULL) {
/*
* Comment is printed only if -c is used and
* associated with gettext or dgettext.
* textdomain is not associated with comments.
* Changes:
* comments should be extracted in case of -j option
* because there are read from exising file.
*/
(p->istextdomain != TRUE)) {
}
/*
* If -n is used, then file number and line number
* information is printed.
* In case of textdomain(), this information is redundant
* and is not printed.
* If linenum is 0, it means this information has been
* read from existing po file and it already contains
* file and line number info as a comment line. So, it
* should not printed in such case.
*/
(p->linenum > 0)) {
}
/*
* Depending on the type of node, output textdomain comment
* or msgid.
*/
(p->istextdomain == TRUE)) {
output_textdomain(fp, p);
} else {
}
p = p->next;
} /* while */
} /* write_one_file */
/*
* Prints out textdomain call as a comment line with file name and
* the line number information.
*/
static void
{
if (p == NULL)
return;
/*
* Write textdomain() line as a comment.
*/
} /* output_textdomain */
/*
* Prints out comments from linked list.
*/
static void
{
if (p == NULL)
return;
/*
* Write comment section.
*/
while (p != NULL) {
p = p->next;
}
} /* output_comment */
/*
* Prints out msgid along with msgstr.
*/
static void
{
struct strlist_st *q;
if (p == NULL)
return;
/*
* Write msgid section.
* If duplciate flag is ON, prepend "# " in front of every line
* so that they are considered as comment lines in .po file.
*/
}
q = p->next;
while (q != NULL) {
}
q = q->next;
}
/*
* Write msgstr section.
* if -M option is specified, append <suffix> to msgid.
* if -m option is specified, prepend <prefix> to msgid.
*/
}
/*
* If single line msgid, add suffix to the same line
*/
/* -M and -m and single line case */
"msgstr \"%s%s%s\"\n",
} else {
/* -M and -m and multi line case */
"msgstr \"%s%s\"\n",
}
} else {
/* -M only with single line case */
} else {
/* -M only with multi line case */
}
}
q = p->next;
while (q != NULL) {
}
q = q->next;
}
/*
* If multi line msgid, add suffix after the last line.
*/
}
} else {
}
} /* output_msgid */
/*
* Malloc a new element node and initialize fields.
*/
static struct element_st *
new_element(void)
{
struct element_st *tmp;
return (tmp);
} /* new_element */
/*
* Malloc a new domain node and initialize fields.
*/
static struct domain_st *
new_domain(void)
{
return (tmp);
} /* new_domain */
/*
* Malloc a new string list node and initialize fields.
*/
static struct strlist_st *
new_strlist(void)
{
struct strlist_st *tmp;
return (tmp);
} /* new_strlist */
/*
* Malloc a new exclude string list node and initialize fields.
*/
static struct exclude_st *
new_exclude(void)
{
struct exclude_st *tmp;
return (tmp);
} /* new_exclude */
/*
* Local version of strcat to keep within maximum string size.
*/
static void
{
while (*s1++)
;
--s1;
(warn_linenum != curr_linenum)) {
if (stdin_only == FALSE) {
"WARNING: file %s line %d exceeds "\
"%d characters: \"%15.15s\"\n",
} else {
"WARNING: line %d exceeds "\
"%d characters: \"%15.15s\"\n",
}
}
break;
}
} /* lstrcat */
#ifdef DEBUG
/*
* Debug print routine. Compiled only with DEBUG on.
*/
void
print_element_list(struct element_st *q)
{
struct strlist_st *r;
while (q != NULL) {
while (r != NULL) {
r = r->next;
}
} else {
printf(" msgid = <NULL>\n");
}
while (r != NULL) {
r = r->next;
}
} else {
printf(" msgstr= <NULL>\n");
}
printf(" comment = <NULL>\n");
} else {
while (r != NULL) {
r = r->next;
}
}
printf(" fname = <NULL>\n");
} else {
}
printf("\n");
q = q->next;
}
}
/*
* Debug print routine. Compiled only with DEBUG on.
*/
void
print_one_domain(struct domain_st *p)
{
struct element_st *q;
if (p == NULL) {
printf("domain pointer = <NULL>\n");
return;
} else {
}
q = p->gettext_head;
q = p->textdomain_head;
} /* print_one_domain */
void
{
struct domain_st *p;
struct element_st *q;
p = dom_list;
while (p != NULL) {
print_one_domain(p);
p = p->next;
} /* while */
} /* print_all_domain */
#endif