4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License"). You may not use this file except in compliance
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
23 * Copyright 1991, 1999, 2001-2002 Sun Microsystems, Inc.
24 * All rights reserved.
25 * Use is subject to license terms.
27 #pragma ident "%Z%%M% %I% %E% SMI"
36 #define MAX_PATH_LEN 1024
37 #define MAX_DOMAIN_LEN 1024
38 #define MAX_STRING_LEN 2048
40 #define USAGE "Usage: xgettext [-a [-x exclude-file]] [-jns]\
41 [-c comment-tag]\n [-d default-domain] [-m prefix] \
42 [-M suffix] [-p pathname] files ...\n\
45 #define DEFAULT_DOMAIN "messages"
48 extern int yylex(void);
51 * Contains a list of strings to be used to store ANSI-C style string.
52 * Each quoted string is stored in one node.
56 struct strlist_st
*next
;
60 * istextdomain : Boolean telling if this node contains textdomain call.
61 * isduplicate : Boolean telling if this node duplicate of any other msgid.
62 * msgid : contains msgid or textdomain if istextdomain is true.
63 * msgstr : contains msgstr.
64 * comment : comment extracted in case of -c option.
65 * fname : tells which file contains msgid.
66 * linenum : line number in the file.
72 struct strlist_st
*msgid
;
73 struct strlist_st
*msgstr
;
74 struct strlist_st
*comment
;
77 struct element_st
*next
;
81 * dname : domain name. NULL if default domain.
82 * gettext_head : Head of linked list containing [d]gettext().
83 * gettext_tail : Tail of linked list containing [d]gettext().
84 * textdomain_head : Head of linked list containing textdomain().
85 * textdomain_tail : Tail of linked list containing textdomain().
88 * Each domain contains two linked list.
89 * (gettext_head, textdomain_head)
90 * If -s option is used, then textdomain_head contains all
91 * textdomain() calls and no textdomain() calls are stored in gettext_head.
92 * If -s option is not used, textdomain_head is empty list and
93 * gettext_head contains all gettext() dgettext(), and textdomain() calls.
97 struct element_st
*gettext_head
;
98 struct element_st
*gettext_tail
;
99 struct element_st
*textdomain_head
;
100 struct element_st
*textdomain_tail
;
101 struct domain_st
*next
;
105 * There are two domain linked lists.
106 * def_dom contains default domain linked list and
107 * dom_head contains all other deomain linked lists to be created by
110 static struct domain_st
*def_dom
= NULL
;
111 static struct domain_st
*dom_head
= NULL
;
112 static struct domain_st
*dom_tail
= NULL
;
115 * This linked list contains a list of strings to be excluded when
118 static struct exclude_st
{
119 struct strlist_st
*exstr
;
120 struct exclude_st
*next
;
124 * All option flags and values for each option if any.
126 static int aflg
= FALSE
;
127 static int cflg
= FALSE
;
128 static char *comment_tag
= NULL
;
129 static char *default_domain
= NULL
;
130 static int hflg
= FALSE
;
131 static int jflg
= FALSE
;
132 static int mflg
= FALSE
;
133 static int Mflg
= FALSE
;
134 static char *suffix
= NULL
;
135 static char *prefix
= NULL
;
136 static int nflg
= FALSE
;
137 static int pflg
= FALSE
;
138 static char *pathname
= NULL
;
139 static int sflg
= FALSE
;
140 static int tflg
= FALSE
; /* Undocumented option to extract dcgettext */
141 static int xflg
= FALSE
;
142 static char *exclude_file
= NULL
;
145 * Each variable shows the current state of parsing input file.
147 * in_comment : Means inside comment block (C or C++).
148 * in_cplus_comment : Means inside C++ comment block.
149 * in_gettext : Means inside gettext call.
150 * in_dgettext : Means inside dgettext call.
151 * in_dcgettext : Means inside dcgettext call.
152 * in_textdomain : Means inside textdomain call.
153 * in_str : Means currently processing ANSI style string.
154 * in_quote : Means currently processing double quoted string.
155 * in_skippable_string : Means currently processing double quoted string,
156 * that occurs outside a call to gettext, dgettext,
157 * dcgettext, textdomain, with -a not specified.
158 * is_last_comment_line : Means the current line is the last line
159 * of the comment block. This is necessary because
160 * in_comment becomes FALSE when '* /' is encountered.
161 * is_first_comma_found : This is used only for dcgettext because dcgettext()
162 * requires 2 commas. So need to do different action
163 * depending on which commas encountered.
164 * num_nested_open_paren : This keeps track of the number of open parens to
165 * handle dcgettext ((const char *)0,"msg",LC_TIME);
167 static int in_comment
= FALSE
;
168 static int in_cplus_comment
= FALSE
;
169 static int in_gettext
= FALSE
;
170 static int in_dgettext
= FALSE
;
171 static int in_dcgettext
= FALSE
;
172 static int in_textdomain
= FALSE
;
173 static int in_str
= FALSE
;
174 static int in_quote
= FALSE
;
175 static int is_last_comment_line
= FALSE
;
176 static int is_first_comma_found
= FALSE
;
177 static int in_skippable_string
= FALSE
;
178 static int num_nested_open_paren
= 0;
181 * This variable contains the first line of gettext(), dgettext(), or
182 * textdomain() calls.
183 * This is necessary for multiple lines of a single call to store
186 static int linenum_saved
= 0;
188 int stdin_only
= FALSE
; /* Read input from stdin */
191 * curr_file : Contains current file name processed.
192 * curr_domain : Contains the current domain for each dgettext().
193 * This is NULL for gettext().
194 * curr_line : Contains the current line processed.
195 * qstring_buf : Contains the double quoted string processed.
196 * curr_linenum : Line number being processed in the current input file.
197 * warn_linenum : Line number of current warning message.
199 char curr_file
[MAX_PATH_LEN
];
200 static char curr_domain
[MAX_DOMAIN_LEN
];
201 static char curr_line
[MAX_STRING_LEN
];
202 static char qstring_buf
[MAX_STRING_LEN
];
203 int curr_linenum
= 1;
204 int warn_linenum
= 0;
207 * strhead : This list contains ANSI style string.
208 * Each node contains double quoted string.
209 * strtail : This is the tail of strhead.
210 * commhead : This list contains comments string.
211 * Each node contains one line of comment.
212 * commtail : This is the tail of commhead.
214 static struct strlist_st
*strhead
= NULL
;
215 static struct strlist_st
*strtail
= NULL
;
216 static struct strlist_st
*commhead
= NULL
;
217 static struct strlist_st
*commtail
= NULL
;
220 * gargc : Same as argc. Used to pass argc to lex routine.
221 * gargv : Same as argv. Used to pass argc to lex routine.
226 static void add_line_to_comment(void);
227 static void add_qstring_to_str(void);
228 static void add_str_to_element_list(int, char *);
229 static void copy_strlist_to_str(char *, struct strlist_st
*);
230 static void end_ansi_string(void);
231 static void free_strlist(struct strlist_st
*);
232 void handle_newline(void);
233 static void initialize_globals(void);
234 static void output_comment(FILE *, struct strlist_st
*);
235 static void output_msgid(FILE *, struct strlist_st
*, int);
236 static void output_textdomain(FILE *, struct element_st
*);
237 static void print_help(void);
238 static void read_exclude_file(void);
239 static void trim_line(char *);
240 static void write_all_files(void);
241 static void write_one_file(struct domain_st
*);
243 static void lstrcat(char *, const char *);
246 * Utility functions to malloc a node and initialize fields.
248 static struct domain_st
*new_domain(void);
249 static struct strlist_st
*new_strlist(void);
250 static struct element_st
*new_element(void);
251 static struct exclude_st
*new_exclude(void);
254 * Main program of xgettext.
257 main(int argc
, char **argv
)
262 initialize_globals();
264 while ((c
= getopt(argc
, argv
, "jhax:nsc:d:m:M:p:t")) != EOF
) {
271 comment_tag
= optarg
;
274 default_domain
= optarg
;
305 exclude_file
= optarg
;
313 /* if -h is used, ignore all other options. */
315 (void) fprintf(stderr
, USAGE
);
320 /* -x can be used only with -a */
321 if ((xflg
== TRUE
) && (aflg
== FALSE
))
324 /* -j cannot be used with -a */
325 if ((jflg
== TRUE
) && (aflg
== TRUE
)) {
326 (void) fprintf(stderr
,
327 "-a and -j options cannot be used together.\n");
331 /* -j cannot be used with -s */
332 if ((jflg
== TRUE
) && (sflg
== TRUE
)) {
333 (void) fprintf(stderr
,
334 "-j and -s options cannot be used together.\n");
338 if (opterr
== TRUE
) {
339 (void) fprintf(stderr
, USAGE
);
343 /* error, if no files are specified. */
344 if (optind
== argc
) {
345 (void) fprintf(stderr
, USAGE
);
353 /* If files are -, then read from stdin */
354 if (argv
[optind
][0] == '-') {
361 /* Store argc and argv to pass to yylex() */
366 (void) printf("optind=%d\n", optind
);
369 for (; i
< argc
; i
++) {
370 (void) printf(" %d, <%s>\n", i
, argv
[i
]);
375 if (stdin_only
== FALSE
) {
376 if (freopen(argv
[optind
], "r", stdin
) == NULL
) {
377 (void) fprintf(stderr
,
378 "ERROR, can't open input file: %s\n", argv
[optind
]);
381 (void) strcpy(curr_file
, gargv
[optind
]);
391 printf("\n======= default_domain ========\n");
392 print_one_domain(def_dom
);
393 printf("======= domain list ========\n");
394 print_all_domain(dom_head
);
398 * Write out all .po files.
406 * Prints help information for each option.
411 (void) fprintf(stderr
, "\n");
412 (void) fprintf(stderr
,
413 "-a\t\t\tfind ALL strings\n");
414 (void) fprintf(stderr
,
415 "-c <comment-tag>\tget comments containing <flag>\n");
416 (void) fprintf(stderr
,
417 "-d <default-domain>\tuse <default-domain> for default domain\n");
418 (void) fprintf(stderr
,
420 (void) fprintf(stderr
,
421 "-j\t\t\tupdate existing file with the current result\n");
422 (void) fprintf(stderr
,
423 "-M <suffix>\t\tfill in msgstr with msgid<suffix>\n");
424 (void) fprintf(stderr
,
425 "-m <prefix>\t\tfill in msgstr with <prefix>msgid\n");
426 (void) fprintf(stderr
,
427 "-n\t\t\tline# file name and line number info in output\n");
428 (void) fprintf(stderr
,
429 "-p <pathname>\t\tuse <pathname> for output file directory\n");
430 (void) fprintf(stderr
,
431 "-s\t\t\tgenerate sorted output files\n");
432 (void) fprintf(stderr
,
433 "-x <exclude-file>\texclude strings in file <exclude-file> from output\n");
434 (void) fprintf(stderr
,
435 "-\t\t\tread stdin, use as a filter (input only)\n");
439 * Extract file name and line number information from macro line
440 * and set the global variable accordingly.
441 * The valid line format is
445 * where nnn is line number and xxxxx is file name.
448 extract_filename_linenumber(char *mline
)
454 * mline can contain multi newline.
455 * line number should be increased by the number of newlines.
458 while ((p
= strchr(p
, '\n')) != NULL
) {
462 p
= strchr(mline
, ' ');
465 q
= strchr(++p
, ' ');
468 if ((num
= atoi(p
)) > 0) {
482 if ((num
= atoi(p
)) > 0) {
484 (void) strcpy(curr_file
, q
);
488 } /* extract_filename_linenumber */
491 * Handler for MACRO line which starts with #.
494 handle_macro_line(void)
497 (void) printf("Macro line=<%s>\n", yytext
);
500 lstrcat(curr_line
, yytext
);
502 if (in_quote
== TRUE
) {
503 lstrcat(qstring_buf
, yytext
);
504 } else if (in_comment
== FALSE
) {
505 extract_filename_linenumber(yytext
);
510 } /* handle_macro_line */
513 * Handler for C++ comments which starts with //.
516 handle_cplus_comment_line(void)
519 lstrcat(curr_line
, yytext
);
521 if (in_quote
== TRUE
) {
522 lstrcat(qstring_buf
, yytext
);
523 } else if ((in_comment
== FALSE
) &&
524 (in_skippable_string
== FALSE
)) {
527 * If already in c comments, don't do anything.
528 * Set both flags to TRUE here.
529 * Both flags will be set to FALSE when newline
532 in_cplus_comment
= TRUE
;
535 } /* handle_cplus_comment_line */
538 * Handler for the comment start (slash asterisk) in input file.
541 handle_open_comment(void)
544 lstrcat(curr_line
, yytext
);
546 if (in_quote
== TRUE
) {
547 lstrcat(qstring_buf
, yytext
);
548 } else if ((in_comment
== FALSE
) &&
549 (in_skippable_string
== FALSE
)) {
552 is_last_comment_line
= FALSE
;
554 * If there is any comment extracted before accidently,
555 * clean it up and start the new comment again.
557 free_strlist(commhead
);
558 commhead
= commtail
= NULL
;
563 * Handler for the comment end (asterisk slash) in input file.
566 handle_close_comment(void)
569 lstrcat(curr_line
, yytext
);
571 if (in_quote
== TRUE
) {
572 lstrcat(qstring_buf
, yytext
);
573 } else if (in_skippable_string
== FALSE
) {
575 is_last_comment_line
= TRUE
;
580 * Handler for "gettext" in input file.
586 * If -t option is specified to extrct dcgettext,
587 * don't do anything for gettext().
593 num_nested_open_paren
= 0;
596 lstrcat(curr_line
, yytext
);
598 if (in_quote
== TRUE
) {
599 lstrcat(qstring_buf
, yytext
);
600 } else if (in_comment
== FALSE
) {
602 linenum_saved
= curr_linenum
;
604 * gettext will be put into default domain .po file
605 * curr_domain does not change for gettext.
607 curr_domain
[0] = NULL
;
609 } /* handle_gettext */
612 * Handler for "dgettext" in input file.
615 handle_dgettext(void)
618 * If -t option is specified to extrct dcgettext,
619 * don't do anything for dgettext().
625 num_nested_open_paren
= 0;
628 lstrcat(curr_line
, yytext
);
630 if (in_quote
== TRUE
) {
631 lstrcat(qstring_buf
, yytext
);
632 } else if (in_comment
== FALSE
) {
634 linenum_saved
= curr_linenum
;
636 * dgettext will be put into domain file specified.
637 * curr_domain will follow.
639 curr_domain
[0] = NULL
;
641 } /* handle_dgettext */
644 * Handler for "dcgettext" in input file.
647 handle_dcgettext(void)
650 * dcgettext will be extracted only when -t flag is specified.
656 num_nested_open_paren
= 0;
658 is_first_comma_found
= FALSE
;
661 lstrcat(curr_line
, yytext
);
663 if (in_quote
== TRUE
) {
664 lstrcat(qstring_buf
, yytext
);
665 } else if (in_comment
== FALSE
) {
667 linenum_saved
= curr_linenum
;
669 * dcgettext will be put into domain file specified.
670 * curr_domain will follow.
672 curr_domain
[0] = NULL
;
674 } /* handle_dcgettext */
677 * Handler for "textdomain" in input file.
680 handle_textdomain(void)
683 lstrcat(curr_line
, yytext
);
685 if (in_quote
== TRUE
) {
686 lstrcat(qstring_buf
, yytext
);
687 } else if (in_comment
== FALSE
) {
688 in_textdomain
= TRUE
;
689 linenum_saved
= curr_linenum
;
690 curr_domain
[0] = NULL
;
692 } /* handle_textdomain */
695 * Handler for '(' in input file.
698 handle_open_paren(void)
701 lstrcat(curr_line
, yytext
);
703 if (in_quote
== TRUE
) {
704 lstrcat(qstring_buf
, yytext
);
705 } else if (in_comment
== FALSE
) {
706 if ((in_gettext
== TRUE
) ||
707 (in_dgettext
== TRUE
) ||
708 (in_dcgettext
== TRUE
) ||
709 (in_textdomain
== TRUE
)) {
711 num_nested_open_paren
++;
714 } /* handle_open_paren */
717 * Handler for ')' in input file.
720 handle_close_paren(void)
723 lstrcat(curr_line
, yytext
);
725 if (in_quote
== TRUE
) {
726 lstrcat(qstring_buf
, yytext
);
727 } else if (in_comment
== FALSE
) {
728 if ((in_gettext
== TRUE
) ||
729 (in_dgettext
== TRUE
) ||
730 (in_dcgettext
== TRUE
) ||
731 (in_textdomain
== TRUE
)) {
733 * If this is not the matching close paren with
734 * the first open paren, no action is necessary.
736 if (--num_nested_open_paren
> 0)
738 add_str_to_element_list(in_textdomain
, curr_domain
);
742 in_dcgettext
= FALSE
;
743 in_textdomain
= FALSE
;
744 } else if (aflg
== TRUE
) {
748 } /* handle_close_paren */
751 * Handler for '\\n' in input file.
753 * This is a '\' followed by new line.
754 * This can be treated like a new line except when this is a continuation
755 * of a ANSI-C string.
756 * If this is a part of ANSI string, treat the current line as a double
757 * quoted string and the next line is the start of the double quoted
761 handle_esc_newline(void)
764 lstrcat(curr_line
, "\\");
768 if (in_quote
== TRUE
) {
769 add_qstring_to_str();
770 } else if ((in_comment
== TRUE
) ||
771 (is_last_comment_line
== TRUE
)) {
772 if (in_cplus_comment
== FALSE
) {
773 add_line_to_comment();
778 } /* handle_esc_newline */
781 * Handler for '"' in input file.
787 lstrcat(curr_line
, yytext
);
789 if (in_comment
== TRUE
) {
791 } else if ((in_gettext
== TRUE
) ||
792 (in_dgettext
== TRUE
) ||
793 (in_dcgettext
== TRUE
) ||
794 (in_textdomain
== TRUE
)) {
795 if (in_str
== TRUE
) {
796 if (in_quote
== FALSE
) {
799 add_qstring_to_str();
803 } else if (aflg
== TRUE
) {
805 * The quote is found outside of gettext, dgetext, and
806 * textdomain. Everytime a quoted string is found,
807 * add it to the string list.
808 * in_str stays TRUE until ANSI string ends.
810 if (in_str
== TRUE
) {
811 if (in_quote
== TRUE
) {
813 add_qstring_to_str();
820 linenum_saved
= curr_linenum
;
823 in_skippable_string
= (in_skippable_string
== TRUE
) ?
829 * Handler for ' ' or TAB in input file.
835 lstrcat(curr_line
, yytext
);
837 if (in_quote
== TRUE
) {
838 lstrcat(qstring_buf
, yytext
);
840 } /* handle_spaces */
843 * Flattens a linked list containing ANSI string to the one string.
846 copy_strlist_to_str(char *str
, struct strlist_st
*strlist
)
848 struct strlist_st
*p
;
852 if (strlist
!= NULL
) {
855 if (p
->str
!= NULL
) {
856 lstrcat(str
, p
->str
);
861 } /* copy_strlist_to_str */
864 * Handler for ',' in input file.
870 lstrcat(curr_line
, yytext
);
872 if (in_quote
== TRUE
) {
873 lstrcat(qstring_buf
, yytext
);
874 } else if (in_comment
== FALSE
) {
875 if (in_str
== TRUE
) {
876 if (in_dgettext
== TRUE
) {
877 copy_strlist_to_str(curr_domain
, strhead
);
878 free_strlist(strhead
);
879 strhead
= strtail
= NULL
;
880 } else if (in_dcgettext
== TRUE
) {
882 * Ignore the second comma.
884 if (is_first_comma_found
== FALSE
) {
885 copy_strlist_to_str(curr_domain
,
887 free_strlist(strhead
);
888 strhead
= strtail
= NULL
;
889 is_first_comma_found
= TRUE
;
891 } else if (aflg
== TRUE
) {
899 * Handler for any other character that does not have special handler.
902 handle_character(void)
905 lstrcat(curr_line
, yytext
);
907 if (in_quote
== TRUE
) {
908 lstrcat(qstring_buf
, yytext
);
909 } else if (in_comment
== FALSE
) {
910 if (in_str
== TRUE
) {
916 } /* handle_character */
919 * Handler for new line in input file.
927 * in_quote is always FALSE here for ANSI-C code.
929 if ((in_comment
== TRUE
) ||
930 (is_last_comment_line
== TRUE
)) {
931 if (in_cplus_comment
== TRUE
) {
932 in_cplus_comment
= FALSE
;
935 add_line_to_comment();
941 * C++ comment always ends with new line.
943 } /* handle_newline */
946 * Process ANSI string.
949 end_ansi_string(void)
951 if ((aflg
== TRUE
) &&
953 (in_gettext
== FALSE
) &&
954 (in_dgettext
== FALSE
) &&
955 (in_dcgettext
== FALSE
) &&
956 (in_textdomain
== FALSE
)) {
957 add_str_to_element_list(FALSE
, curr_domain
);
960 } /* end_ansi_string */
963 * Initialize global variables if necessary.
966 initialize_globals(void)
968 default_domain
= strdup(DEFAULT_DOMAIN
);
969 curr_domain
[0] = NULL
;
971 qstring_buf
[0] = NULL
;
972 } /* initialize_globals() */
975 * Extract only string part when read a exclude file by removing
976 * keywords (e.g. msgid, msgstr, # ) and heading and trailing blanks and
980 trim_line(char *line
)
990 * Find the position of the last non-whitespace character.
996 if ((c
!= ' ') && (c
!= '\n') && (c
!= '\t')) {
1003 * Find the position of the first non-whitespace character
1004 * by skipping "msgid" initially.
1006 if (strncmp("msgid ", line
, 6) == 0) {
1008 } else if (strncmp("msgstr ", line
, 7) == 0) {
1010 } else if (strncmp("# ", line
, 2) == 0) {
1019 if ((c
!= ' ') && (c
!= '\n') && (c
!= '\t')) {
1026 * For Backward compatibility, we consider both double quoted
1027 * string and non-quoted string.
1028 * The double quote is removed before being stored if exists.
1030 if (line
[first
] == '"') {
1033 if (line
[last
] == '"') {
1038 * Now copy the valid part of the string.
1041 for (i
= 0; i
<= (last
-first
); i
++) {
1042 line
[i
] = line
[p
++];
1048 * Read exclude file and stores it in the global linked list.
1051 read_exclude_file(void)
1054 struct exclude_st
*tmp_excl
;
1055 struct strlist_st
*tail
;
1057 char line
[MAX_STRING_LEN
];
1059 if ((fp
= fopen(exclude_file
, "r")) == NULL
) {
1060 (void) fprintf(stderr
, "ERROR, can't open exclude file: %s\n",
1066 while (fgets(line
, MAX_STRING_LEN
, fp
) != NULL
) {
1068 * Line starting with # is a comment line and ignored.
1069 * Blank line is ignored, too.
1071 if ((line
[0] == '\n') || (line
[0] == '#')) {
1073 } else if (strncmp(line
, "msgstr", 6) == 0) {
1075 } else if (strncmp(line
, "domain", 6) == 0) {
1077 } else if (strncmp(line
, "msgid", 5) == 0) {
1078 ignore_line
= FALSE
;
1079 tmp_excl
= new_exclude();
1080 tmp_excl
->exstr
= new_strlist();
1082 tmp_excl
->exstr
->str
= strdup(line
);
1083 tail
= tmp_excl
->exstr
;
1085 * Prepend new exclude string node to the list.
1087 tmp_excl
->next
= excl_head
;
1088 excl_head
= tmp_excl
;
1091 * If more than one line of string forms msgid,
1092 * append it to the string linked list.
1094 if (ignore_line
== FALSE
) {
1096 tail
->next
= new_strlist();
1097 tail
->next
->str
= strdup(line
);
1104 tmp_excl
= excl_head
;
1105 while (tmp_excl
!= NULL
) {
1106 printf("============================\n");
1107 tail
= tmp_excl
->exstr
;
1108 while (tail
!= NULL
) {
1109 printf("%s###\n", tail
->str
);
1112 tmp_excl
= tmp_excl
->next
;
1115 } /* read_exclude_file */
1118 * Get next character from the string list containing ANSI style string.
1119 * This function returns three valus. (p, *m, *c).
1120 * p is returned by return value and, *m and *c are returned by changing
1121 * values in the location pointed.
1123 * p : points node in the linked list for ANSI string.
1124 * Each node contains double quoted string.
1125 * m : The location of the next characters in the double quoted string
1126 * as integer index in the string.
1127 * When it gets to end of quoted string, the next node will be
1128 * read and m starts as zero for every new node.
1129 * c : Stores the value of the characterto be returned.
1131 static struct strlist_st
*
1132 get_next_ch(struct strlist_st
*p
, int *m
, char *c
)
1138 * From the string list, find non-null string first.
1145 } else if (p
->str
== NULL
) {
1147 } else if (p
->str
[*m
] == NULL
) {
1156 * No more character is available.
1164 * Check if the character back slash.
1165 * If yes, ANSI defined escape sequence rule is used.
1167 if (p
->str
[*m
] != '\\') {
1173 * Get next character after '\'.
1208 * Get maximum of three octal digits.
1211 for (i
= 0; i
< 2; i
++) {
1214 if ((oct
>= '0') && (oct
<= '7')) {
1215 value
= value
* 8 + (oct
- '0');
1223 /* (void) fprintf(stderr, "octal=%d\n", value); */
1229 * Remove all heading zeros first and
1230 * get one or two valuid hexadecimal charaters.
1233 while (p
->str
[*m
] == '0') {
1237 for (i
= 0; i
< 2; i
++) {
1241 value
= value
* 16 + (hex
- '0');
1242 } else if (isxdigit(hex
)) {
1244 value
= value
* 16 + (hex
- 'a' + 10);
1252 (void) fprintf(stderr
, "hex=%d\n", value
);
1258 * Undefined by ANSI.
1265 * Advance pointer to point the next character to be parsed.
1273 * Compares two msgids.
1274 * Comparison is done by values, not by characters represented.
1275 * For example, '\t', '\011' and '0x9' are identical values.
1276 * Return values are same as in strcmp.
1277 * 1 if msgid1 > msgid2
1278 * 0 if msgid1 = msgid2
1279 * -1 if msgid1 < msgid2
1282 msgidcmp(struct strlist_st
*id1
, struct strlist_st
*id2
)
1292 id1
= get_next_ch(id1
, &m1
, &c1
);
1293 id2
= get_next_ch(id2
, &m2
, &c2
);
1295 if ((c1
== 0) && (c2
== 0)) {
1301 } else if (c1
< c2
) {
1309 * Check if a ANSI string (which is a linked list itself) is a duplicate
1310 * of any string in the list of ANSI string.
1313 isduplicate(struct element_st
*list
, struct strlist_st
*str
)
1315 struct element_st
*p
;
1323 if (p
->msgid
!= NULL
) {
1324 if (msgidcmp(p
->msgid
, str
) == 0) {
1335 * Extract a comment line and add to the linked list containing
1337 * Each comment line is stored in the node.
1340 add_line_to_comment(void)
1342 struct strlist_st
*tmp_str
;
1344 tmp_str
= new_strlist();
1345 tmp_str
->str
= strdup(curr_line
);
1346 tmp_str
->next
= NULL
;
1348 if (commhead
== NULL
) {
1349 /* Empty comment list */
1353 /* append it to the list */
1354 commtail
->next
= tmp_str
;
1355 commtail
= commtail
->next
;
1358 is_last_comment_line
= FALSE
;
1359 } /* add_line_to_comment */
1362 * Add a double quoted string to the linked list containing ANSI string.
1365 add_qstring_to_str(void)
1367 struct strlist_st
*tmp_str
;
1369 tmp_str
= new_strlist();
1370 tmp_str
->str
= strdup(qstring_buf
);
1371 tmp_str
->next
= NULL
;
1373 if (strhead
== NULL
) {
1374 /* Null ANSI string */
1378 /* Append it to the ANSI string linked list */
1379 strtail
->next
= tmp_str
;
1380 strtail
= strtail
->next
;
1383 qstring_buf
[0] = NULL
;
1384 } /* add_qstring_to_str */
1387 * Finds the head of domain nodes given domain name.
1389 static struct domain_st
*
1390 find_domain_node(char *dname
)
1392 struct domain_st
*tmp_dom
, *p
;
1395 * If -a option is specified everything will be written to the
1396 * default domain file.
1399 if (def_dom
== NULL
) {
1400 def_dom
= new_domain();
1405 if ((dname
== NULL
) ||
1406 (dname
[0] == NULL
) ||
1407 (strcmp(dname
, default_domain
) == 0)) {
1408 if (def_dom
== NULL
) {
1409 def_dom
= new_domain();
1411 if (strcmp(dname
, default_domain
) == 0) {
1412 (void) fprintf(stderr
,
1413 "%s \"%s\" is used in dgettext of file:%s line:%d.\n",
1414 "Warning: default domain name",
1415 default_domain
, curr_file
, curr_linenum
);
1421 if (strcmp(p
->dname
, dname
) == 0) {
1427 tmp_dom
= new_domain();
1428 tmp_dom
->dname
= strdup(dname
);
1430 if (dom_head
== NULL
) {
1434 dom_tail
->next
= tmp_dom
;
1435 dom_tail
= dom_tail
->next
;
1439 } /* find_domain_node */
1442 * Frees the ANSI string linked list.
1445 free_strlist(struct strlist_st
*ptr
)
1447 struct strlist_st
*p
;
1457 } /* free_strlist */
1460 * Finds if a ANSI string is contained in the exclude file.
1463 isexcluded(struct strlist_st
*strlist
)
1465 struct exclude_st
*p
;
1469 if (msgidcmp(p
->exstr
, strlist
) == 0) {
1478 * Finds if a comment block is to be extracted.
1480 * When -c option is specified, find out if comment block contains
1481 * comment-tag as a token separated by blanks. If it does, this
1482 * comment block is associated with the next msgid encountered.
1483 * Comment block is a linked list where each node contains one line
1487 isextracted(struct strlist_st
*strlist
)
1489 struct strlist_st
*p
;
1495 first
= strdup(p
->str
);
1496 while ((first
!= NULL
) && (first
[0] != NULL
)) {
1503 } else if ((*pc
== ' ') || (*pc
== '\t')) {
1509 if (strcmp(first
, comment_tag
) == 0) {
1524 * Adds ANSI string to the domain element list.
1527 add_str_to_element_list(int istextdomain
, char *domain_list
)
1529 struct element_st
*tmp_elem
;
1530 struct element_st
*p
, *q
;
1531 struct domain_st
*tmp_dom
;
1535 * This can happen if something like gettext(USAGE) is used
1536 * and it is impossible to get msgid for this gettext.
1537 * Since -x option should be used in this kind of cases,
1538 * it is OK not to catch msgid.
1540 if (strhead
== NULL
) {
1545 * The global variable curr_domain contains either NULL
1546 * for default_domain or domain name for dgettext().
1548 tmp_dom
= find_domain_node(domain_list
);
1551 * If this msgid is in the exclude file,
1552 * then free the linked list and return.
1554 if ((istextdomain
== FALSE
) &&
1555 (isexcluded(strhead
) == TRUE
)) {
1556 free_strlist(strhead
);
1557 strhead
= strtail
= NULL
;
1561 tmp_elem
= new_element();
1562 tmp_elem
->msgid
= strhead
;
1563 tmp_elem
->istextdomain
= istextdomain
;
1565 * If -c option is specified and TAG matches,
1566 * then associate the comment to the next [d]gettext() calls
1567 * encountered in the source code.
1568 * textdomain() calls will not have any effect.
1570 if (istextdomain
== FALSE
) {
1571 if ((cflg
== TRUE
) && (commhead
!= NULL
)) {
1572 if (isextracted(commhead
) == TRUE
) {
1573 tmp_elem
->comment
= commhead
;
1575 free_strlist(commhead
);
1577 commhead
= commtail
= NULL
;
1581 tmp_elem
->linenum
= linenum_saved
;
1582 tmp_elem
->fname
= strdup(curr_file
);
1587 * If this is textdomain() call and -s option is specified,
1588 * append this node to the textdomain linked list.
1590 if (istextdomain
== TRUE
) {
1591 if (tmp_dom
->textdomain_head
== NULL
) {
1592 tmp_dom
->textdomain_head
= tmp_elem
;
1593 tmp_dom
->textdomain_tail
= tmp_elem
;
1595 tmp_dom
->textdomain_tail
->next
= tmp_elem
;
1596 tmp_dom
->textdomain_tail
= tmp_elem
;
1598 strhead
= strtail
= NULL
;
1603 * Insert the node to the properly sorted position.
1606 p
= tmp_dom
->gettext_head
;
1608 result
= msgidcmp(strhead
, p
->msgid
);
1611 * Duplicate id. Do not store.
1613 free_strlist(strhead
);
1614 strhead
= strtail
= NULL
;
1616 } else if (result
> 0) {
1617 /* move to the next node */
1625 tmp_dom
->gettext_head
= tmp_elem
;
1627 strhead
= strtail
= NULL
;
1633 * New msgid is the largest or empty list.
1640 tmp_dom
->gettext_head
= tmp_elem
;
1644 * Check if this msgid is already in the same domain.
1646 if (tmp_dom
!= NULL
) {
1647 if (isduplicate(tmp_dom
->gettext_head
,
1648 tmp_elem
->msgid
) == TRUE
) {
1649 tmp_elem
->isduplicate
= TRUE
;
1653 * If -s option is not specified, then everything
1654 * is stored in gettext linked list.
1656 if (tmp_dom
->gettext_head
== NULL
) {
1657 tmp_dom
->gettext_head
= tmp_elem
;
1658 tmp_dom
->gettext_tail
= tmp_elem
;
1660 tmp_dom
->gettext_tail
->next
= tmp_elem
;
1661 tmp_dom
->gettext_tail
= tmp_elem
;
1665 strhead
= strtail
= NULL
;
1666 } /* add_str_to_element_list */
1669 * Write all domain linked list to the files.
1672 write_all_files(void)
1674 struct domain_st
*tmp
;
1677 * Write out default domain file.
1679 write_one_file(def_dom
);
1682 * If dgettext() exists and -a option is not used,
1683 * then there are non-empty linked list.
1686 while (tmp
!= NULL
) {
1687 write_one_file(tmp
);
1690 } /* write_all_files */
1693 * add an element_st list to the linked list.
1696 add_node_to_polist(struct element_st
**pohead
,
1697 struct element_st
**potail
, struct element_st
*elem
)
1703 if (*pohead
== NULL
) {
1704 *pohead
= *potail
= elem
;
1706 (*potail
)->next
= elem
;
1707 *potail
= (*potail
)->next
;
1709 } /* add_node_to_polist */
1711 #define INIT_STATE 0
1714 #define IN_COMMENT 3
1716 * Reads existing po file into the linked list and returns the head
1717 * of the linked list.
1719 static struct element_st
*
1720 read_po(char *fname
)
1722 struct element_st
*tmp_elem
= NULL
;
1723 struct element_st
*ehead
= NULL
, *etail
= NULL
;
1724 struct strlist_st
*comment_tail
= NULL
;
1725 struct strlist_st
*msgid_tail
= NULL
;
1726 struct strlist_st
*msgstr_tail
= NULL
;
1727 int state
= INIT_STATE
;
1728 char line
[MAX_STRING_LEN
];
1731 if ((fp
= fopen(fname
, "r")) == NULL
) {
1735 while (fgets(line
, MAX_STRING_LEN
, fp
) != NULL
) {
1737 * Line starting with # is a comment line and ignored.
1738 * Blank line is ignored, too.
1740 if (line
[0] == '\n') {
1742 } else if (line
[0] == '#') {
1744 * If tmp_elem is not NULL, there is msgid pair
1745 * stored. Therefore, add it.
1747 if ((tmp_elem
!= NULL
) && (state
== IN_MSGSTR
)) {
1748 add_node_to_polist(&ehead
, &etail
, tmp_elem
);
1751 if ((state
== INIT_STATE
) || (state
== IN_MSGSTR
)) {
1753 tmp_elem
= new_element();
1754 tmp_elem
->comment
= comment_tail
=
1757 * remove new line and skip "# "
1758 * in the beginning of the existing
1761 line
[strlen(line
)-1] = 0;
1762 comment_tail
->str
= strdup(line
+2);
1763 } else if (state
== IN_COMMENT
) {
1764 comment_tail
->next
= new_strlist();
1765 comment_tail
= comment_tail
->next
;
1767 * remove new line and skip "# "
1768 * in the beginning of the existing
1771 line
[strlen(line
)-1] = 0;
1772 comment_tail
->str
= strdup(line
+2);
1775 } else if (strncmp(line
, "domain", 6) == 0) {
1776 /* ignore domain line */
1778 } else if (strncmp(line
, "msgid", 5) == 0) {
1779 if (state
== IN_MSGSTR
) {
1780 add_node_to_polist(&ehead
, &etail
, tmp_elem
);
1781 tmp_elem
= new_element();
1782 } else if (state
== INIT_STATE
) {
1783 tmp_elem
= new_element();
1788 tmp_elem
->msgid
= msgid_tail
= new_strlist();
1789 msgid_tail
->str
= strdup(line
);
1791 } else if (strncmp(line
, "msgstr", 6) == 0) {
1794 tmp_elem
->msgstr
= msgstr_tail
= new_strlist();
1795 msgstr_tail
->str
= strdup(line
);
1798 * If more than one line of string forms msgid,
1799 * append it to the string linked list.
1801 if (state
== IN_MSGID
) {
1803 msgid_tail
->next
= new_strlist();
1804 msgid_tail
= msgid_tail
->next
;
1805 msgid_tail
->str
= strdup(line
);
1806 } else if (state
== IN_MSGSTR
) {
1808 msgstr_tail
->next
= new_strlist();
1809 msgstr_tail
= msgstr_tail
->next
;
1810 msgstr_tail
->str
= strdup(line
);
1816 * To insert the last msgid pair.
1818 if (tmp_elem
!= NULL
) {
1819 add_node_to_polist(&ehead
, &etail
, tmp_elem
);
1824 struct domain_st
*tmp_domain
= new_domain();
1827 sprintf(tmpstr
, "existing_po file : <%s>", fname
);
1828 tmp_domain
->dname
= strdup(tmpstr
);
1829 tmp_domain
->gettext_head
= ehead
;
1830 printf("======= existing po file <%s> ========\n", fname
);
1831 print_one_domain(tmp_domain
);
1840 * This function will append the second list to the first list.
1841 * If the msgid in the second list contains msgid in the first list,
1842 * it will be marked as duplicate.
1844 static struct element_st
*
1845 append_list(struct element_st
*l1
, struct element_st
*l2
)
1847 struct element_st
*p
= NULL
, *q
= NULL
, *l1_tail
= NULL
;
1855 * in this while loop, just mark isduplicate field of node in the
1856 * l2 list if the same msgid exists in l1 list.
1862 if (msgidcmp(p
->msgid
, q
->msgid
) == 0) {
1863 p
->isduplicate
= TRUE
;
1871 /* Now connect two linked lists. */
1873 while (l1_tail
->next
!= NULL
) {
1874 if (l1
->next
== NULL
)
1876 l1_tail
= l1_tail
-> next
;
1884 * Writes one domain list to the file.
1887 write_one_file(struct domain_st
*head
)
1890 char fname
[MAX_PATH_LEN
];
1891 char dname
[MAX_DOMAIN_LEN
];
1892 struct element_st
*p
;
1893 struct element_st
*existing_po_list
;
1896 * If head is NULL, then it still has to create .po file
1897 * so that it will guarantee that the previous .po file was
1899 * This is why checking NULL pointer has been moved to after
1900 * creating .po file.
1904 * If domain name is NULL, it is the default domain list.
1905 * The domain name is either "messages" or specified by option -d.
1906 * The default domain name is contained in default_domain variable.
1909 if ((head
!= NULL
) &&
1910 (head
->dname
!= NULL
)) {
1911 (void) strcpy(dname
, head
->dname
);
1913 (void) strcpy(dname
, default_domain
);
1917 * path is the current directory if not specified by option -p.
1921 (void) strcat(fname
, pathname
);
1922 (void) strcat(fname
, "/");
1924 (void) strcat(fname
, dname
);
1925 (void) strcat(fname
, ".po");
1928 * If -j flag is specified, read exsiting .po file and
1929 * append the current list to the end of the list read from
1930 * the existing .po file.
1934 * If head is NULL, we don't have to change existing file.
1935 * Therefore, just return it.
1940 existing_po_list
= read_po(fname
);
1941 head
->gettext_head
= append_list(existing_po_list
,
1942 head
->gettext_head
);
1944 if (head
->dname
!= NULL
) {
1945 printf("===after merge (-j option): <%s>===\n",
1948 printf("===after merge (-j option): <NULL>===\n");
1950 print_one_domain(head
);
1955 if ((fp
= fopen(fname
, "w")) == NULL
) {
1956 (void) fprintf(stderr
,
1957 "ERROR, can't open output file: %s\n", fname
);
1961 (void) fprintf(fp
, "domain \"%s\"\n", dname
);
1963 /* See comments above in the beginning of this function */
1968 * There are separate storage for textdomain() calls if
1969 * -s option is used (textdomain_head linked list).
1970 * Otherwise, textdomain() is mixed with gettext(0 and dgettext().
1971 * If mixed, the boolean varaible istextdomain is used to see
1972 * if the current node contains textdomain() or [d]gettext().
1975 p
= head
->textdomain_head
;
1978 * textdomain output line already contains
1979 * FIle name and line number information.
1980 * Therefore, does not have to check for nflg.
1982 output_textdomain(fp
, p
);
1987 p
= head
->gettext_head
;
1991 * Comment is printed only if -c is used and
1992 * associated with gettext or dgettext.
1993 * textdomain is not associated with comments.
1995 * comments should be extracted in case of -j option
1996 * because there are read from exising file.
1998 if (((cflg
== TRUE
) || (jflg
== TRUE
)) &&
1999 (p
->istextdomain
!= TRUE
)) {
2000 output_comment(fp
, p
->comment
);
2004 * If -n is used, then file number and line number
2005 * information is printed.
2006 * In case of textdomain(), this information is redundant
2007 * and is not printed.
2008 * If linenum is 0, it means this information has been
2009 * read from existing po file and it already contains
2010 * file and line number info as a comment line. So, it
2011 * should not printed in such case.
2013 if ((nflg
== TRUE
) && (p
->istextdomain
== FALSE
) &&
2015 (void) fprintf(fp
, "# File:%s, line:%d\n",
2016 p
->fname
, p
->linenum
);
2020 * Depending on the type of node, output textdomain comment
2023 if ((sflg
== FALSE
) &&
2024 (p
->istextdomain
== TRUE
)) {
2025 output_textdomain(fp
, p
);
2027 output_msgid(fp
, p
->msgid
, p
->isduplicate
);
2034 } /* write_one_file */
2037 * Prints out textdomain call as a comment line with file name and
2038 * the line number information.
2041 output_textdomain(FILE *fp
, struct element_st
*p
)
2048 * Write textdomain() line as a comment.
2050 (void) fprintf(fp
, "# File:%s, line:%d, textdomain(\"%s\");\n",
2051 p
->fname
, p
->linenum
, p
->msgid
->str
);
2052 } /* output_textdomain */
2055 * Prints out comments from linked list.
2058 output_comment(FILE *fp
, struct strlist_st
*p
)
2064 * Write comment section.
2067 (void) fprintf(fp
, "# %s\n", p
->str
);
2070 } /* output_comment */
2073 * Prints out msgid along with msgstr.
2076 output_msgid(FILE *fp
, struct strlist_st
*p
, int duplicate
)
2078 struct strlist_st
*q
;
2084 * Write msgid section.
2085 * If duplciate flag is ON, prepend "# " in front of every line
2086 * so that they are considered as comment lines in .po file.
2088 if (duplicate
== TRUE
) {
2089 (void) fprintf(fp
, "# ");
2091 (void) fprintf(fp
, "msgid \"%s\"\n", p
->str
);
2094 if (duplicate
== TRUE
) {
2095 (void) fprintf(fp
, "# ");
2097 (void) fprintf(fp
, " \"%s\"\n", q
->str
);
2102 * Write msgstr section.
2103 * if -M option is specified, append <suffix> to msgid.
2104 * if -m option is specified, prepend <prefix> to msgid.
2106 if (duplicate
== TRUE
) {
2107 (void) fprintf(fp
, "# ");
2109 if ((mflg
== TRUE
) || (Mflg
== TRUE
)) {
2112 * If single line msgid, add suffix to the same line
2114 if ((Mflg
== TRUE
) && (p
->next
== NULL
)) {
2115 /* -M and -m and single line case */
2117 "msgstr \"%s%s%s\"\n",
2118 prefix
, p
->str
, suffix
);
2120 /* -M and -m and multi line case */
2122 "msgstr \"%s%s\"\n",
2126 if ((Mflg
== TRUE
) && (p
->next
== NULL
)) {
2127 /* -M only with single line case */
2128 (void) fprintf(fp
, "msgstr \"%s%s\"\n",
2131 /* -M only with multi line case */
2132 (void) fprintf(fp
, "msgstr \"%s\"\n", p
->str
);
2137 if (duplicate
== TRUE
) {
2138 (void) fprintf(fp
, "# ");
2140 (void) fprintf(fp
, " \"%s\"\n", q
->str
);
2144 * If multi line msgid, add suffix after the last line.
2146 if ((Mflg
== TRUE
) && (p
->next
!= NULL
) &&
2147 (suffix
[0] != NULL
)) {
2148 (void) fprintf(fp
, " \"%s\"\n", suffix
);
2151 (void) fprintf(fp
, "msgstr\n");
2153 } /* output_msgid */
2156 * Malloc a new element node and initialize fields.
2158 static struct element_st
*
2161 struct element_st
*tmp
;
2163 tmp
= (struct element_st
*)malloc(sizeof (struct element_st
));
2164 tmp
->istextdomain
= FALSE
;
2165 tmp
->isduplicate
= FALSE
;
2168 tmp
->comment
= NULL
;
2177 * Malloc a new domain node and initialize fields.
2179 static struct domain_st
*
2182 struct domain_st
*tmp
;
2184 tmp
= (struct domain_st
*)malloc(sizeof (struct domain_st
));
2186 tmp
->gettext_head
= NULL
;
2187 tmp
->gettext_tail
= NULL
;
2188 tmp
->textdomain_head
= NULL
;
2189 tmp
->textdomain_tail
= NULL
;
2196 * Malloc a new string list node and initialize fields.
2198 static struct strlist_st
*
2201 struct strlist_st
*tmp
;
2203 tmp
= (struct strlist_st
*)malloc(sizeof (struct strlist_st
));
2211 * Malloc a new exclude string list node and initialize fields.
2213 static struct exclude_st
*
2216 struct exclude_st
*tmp
;
2218 tmp
= (struct exclude_st
*)malloc(sizeof (struct exclude_st
));
2226 * Local version of strcat to keep within maximum string size.
2229 lstrcat(char *s1
, const char *s2
)
2231 char *es1
= &s1
[MAX_STRING_LEN
];
2237 while (*s1
++ = *s2
++)
2240 if ((in_comment
== TRUE
|| in_quote
== TRUE
) &&
2241 (warn_linenum
!= curr_linenum
)) {
2242 if (stdin_only
== FALSE
) {
2243 (void) fprintf(stderr
,
2244 "WARNING: file %s line %d exceeds "\
2245 "%d characters: \"%15.15s\"\n",
2246 curr_file
, curr_linenum
,
2247 MAX_STRING_LEN
, ss1
);
2249 (void) fprintf(stderr
,
2250 "WARNING: line %d exceeds "\
2251 "%d characters: \"%15.15s\"\n",
2252 curr_linenum
, MAX_STRING_LEN
, ss1
);
2254 warn_linenum
= curr_linenum
;
2262 * Debug print routine. Compiled only with DEBUG on.
2265 print_element_list(struct element_st
*q
)
2267 struct strlist_st
*r
;
2270 printf(" istextdomain = %d\n", q
->istextdomain
);
2271 printf(" isduplicate = %d\n", q
->isduplicate
);
2272 if ((q
->msgid
!= NULL
) && (q
->msgid
->str
!= NULL
)) {
2273 printf(" msgid = <%s>\n", q
->msgid
->str
);
2276 printf(" <%s>\n", r
->str
);
2280 printf(" msgid = <NULL>\n");
2282 if ((q
->msgstr
!= NULL
) && (q
->msgstr
->str
!= NULL
)) {
2283 printf(" msgstr= <%s>\n", q
->msgstr
->str
);
2284 r
= q
->msgstr
->next
;
2286 printf(" <%s>\n", r
->str
);
2290 printf(" msgstr= <NULL>\n");
2293 if (q
->comment
== NULL
) {
2294 printf(" comment = <NULL>\n");
2296 printf(" comment = <%s>\n", q
->comment
->str
);
2297 r
= q
->comment
->next
;
2299 printf(" <%s>\n", r
->str
);
2304 if (q
->fname
== NULL
) {
2305 printf(" fname = <NULL>\n");
2307 printf(" fname = <%s>\n", q
->fname
);
2309 printf(" linenum = %d\n", q
->linenum
);
2316 * Debug print routine. Compiled only with DEBUG on.
2319 print_one_domain(struct domain_st
*p
)
2321 struct element_st
*q
;
2324 printf("domain pointer = <NULL>\n");
2326 } else if (p
->dname
== NULL
) {
2327 printf("domain_name = <%s>\n", "<NULL>");
2329 printf("domain_name = <%s>\n", p
->dname
);
2331 q
= p
->gettext_head
;
2332 print_element_list(q
);
2334 q
= p
->textdomain_head
;
2335 print_element_list(q
);
2336 } /* print_one_domain */
2339 print_all_domain(struct domain_st
*dom_list
)
2341 struct domain_st
*p
;
2342 struct element_st
*q
;
2346 print_one_domain(p
);
2349 } /* print_all_domain */