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.
34 #define MAX_PATH_LEN 1024
35 #define MAX_DOMAIN_LEN 1024
36 #define MAX_STRING_LEN 2048
38 #define USAGE "Usage: xgettext [-a [-x exclude-file]] [-jns]\
39 [-c comment-tag]\n [-d default-domain] [-m prefix] \
40 [-M suffix] [-p pathname] files ...\n\
43 #define DEFAULT_DOMAIN "messages"
46 extern int yylex(void);
49 * Contains a list of strings to be used to store ANSI-C style string.
50 * Each quoted string is stored in one node.
54 struct strlist_st
*next
;
58 * istextdomain : Boolean telling if this node contains textdomain call.
59 * isduplicate : Boolean telling if this node duplicate of any other msgid.
60 * msgid : contains msgid or textdomain if istextdomain is true.
61 * msgstr : contains msgstr.
62 * comment : comment extracted in case of -c option.
63 * fname : tells which file contains msgid.
64 * linenum : line number in the file.
70 struct strlist_st
*msgid
;
71 struct strlist_st
*msgstr
;
72 struct strlist_st
*comment
;
75 struct element_st
*next
;
79 * dname : domain name. NULL if default domain.
80 * gettext_head : Head of linked list containing [d]gettext().
81 * gettext_tail : Tail of linked list containing [d]gettext().
82 * textdomain_head : Head of linked list containing textdomain().
83 * textdomain_tail : Tail of linked list containing textdomain().
86 * Each domain contains two linked list.
87 * (gettext_head, textdomain_head)
88 * If -s option is used, then textdomain_head contains all
89 * textdomain() calls and no textdomain() calls are stored in gettext_head.
90 * If -s option is not used, textdomain_head is empty list and
91 * gettext_head contains all gettext() dgettext(), and textdomain() calls.
95 struct element_st
*gettext_head
;
96 struct element_st
*gettext_tail
;
97 struct element_st
*textdomain_head
;
98 struct element_st
*textdomain_tail
;
99 struct domain_st
*next
;
103 * There are two domain linked lists.
104 * def_dom contains default domain linked list and
105 * dom_head contains all other deomain linked lists to be created by
108 static struct domain_st
*def_dom
= NULL
;
109 static struct domain_st
*dom_head
= NULL
;
110 static struct domain_st
*dom_tail
= NULL
;
113 * This linked list contains a list of strings to be excluded when
116 static struct exclude_st
{
117 struct strlist_st
*exstr
;
118 struct exclude_st
*next
;
122 * All option flags and values for each option if any.
124 static int aflg
= FALSE
;
125 static int cflg
= FALSE
;
126 static char *comment_tag
= NULL
;
127 static char *default_domain
= NULL
;
128 static int hflg
= FALSE
;
129 static int jflg
= FALSE
;
130 static int mflg
= FALSE
;
131 static int Mflg
= FALSE
;
132 static char *suffix
= NULL
;
133 static char *prefix
= NULL
;
134 static int nflg
= FALSE
;
135 static int pflg
= FALSE
;
136 static char *pathname
= NULL
;
137 static int sflg
= FALSE
;
138 static int tflg
= FALSE
; /* Undocumented option to extract dcgettext */
139 static int xflg
= FALSE
;
140 static char *exclude_file
= NULL
;
143 * Each variable shows the current state of parsing input file.
145 * in_comment : Means inside comment block (C or C++).
146 * in_cplus_comment : Means inside C++ comment block.
147 * in_gettext : Means inside gettext call.
148 * in_dgettext : Means inside dgettext call.
149 * in_dcgettext : Means inside dcgettext call.
150 * in_textdomain : Means inside textdomain call.
151 * in_str : Means currently processing ANSI style string.
152 * in_quote : Means currently processing double quoted string.
153 * in_skippable_string : Means currently processing double quoted string,
154 * that occurs outside a call to gettext, dgettext,
155 * dcgettext, textdomain, with -a not specified.
156 * is_last_comment_line : Means the current line is the last line
157 * of the comment block. This is necessary because
158 * in_comment becomes FALSE when '* /' is encountered.
159 * is_first_comma_found : This is used only for dcgettext because dcgettext()
160 * requires 2 commas. So need to do different action
161 * depending on which commas encountered.
162 * num_nested_open_paren : This keeps track of the number of open parens to
163 * handle dcgettext ((const char *)0,"msg",LC_TIME);
165 static int in_comment
= FALSE
;
166 static int in_cplus_comment
= FALSE
;
167 static int in_gettext
= FALSE
;
168 static int in_dgettext
= FALSE
;
169 static int in_dcgettext
= FALSE
;
170 static int in_textdomain
= FALSE
;
171 static int in_str
= FALSE
;
172 static int in_quote
= FALSE
;
173 static int is_last_comment_line
= FALSE
;
174 static int is_first_comma_found
= FALSE
;
175 static int in_skippable_string
= FALSE
;
176 static int num_nested_open_paren
= 0;
179 * This variable contains the first line of gettext(), dgettext(), or
180 * textdomain() calls.
181 * This is necessary for multiple lines of a single call to store
184 static int linenum_saved
= 0;
186 int stdin_only
= FALSE
; /* Read input from stdin */
189 * curr_file : Contains current file name processed.
190 * curr_domain : Contains the current domain for each dgettext().
191 * This is NULL for gettext().
192 * curr_line : Contains the current line processed.
193 * qstring_buf : Contains the double quoted string processed.
194 * curr_linenum : Line number being processed in the current input file.
195 * warn_linenum : Line number of current warning message.
197 char curr_file
[MAX_PATH_LEN
];
198 static char curr_domain
[MAX_DOMAIN_LEN
];
199 static char curr_line
[MAX_STRING_LEN
];
200 static char qstring_buf
[MAX_STRING_LEN
];
201 int curr_linenum
= 1;
202 int warn_linenum
= 0;
205 * strhead : This list contains ANSI style string.
206 * Each node contains double quoted string.
207 * strtail : This is the tail of strhead.
208 * commhead : This list contains comments string.
209 * Each node contains one line of comment.
210 * commtail : This is the tail of commhead.
212 static struct strlist_st
*strhead
= NULL
;
213 static struct strlist_st
*strtail
= NULL
;
214 static struct strlist_st
*commhead
= NULL
;
215 static struct strlist_st
*commtail
= NULL
;
218 * gargc : Same as argc. Used to pass argc to lex routine.
219 * gargv : Same as argv. Used to pass argc to lex routine.
224 static void add_line_to_comment(void);
225 static void add_qstring_to_str(void);
226 static void add_str_to_element_list(int, char *);
227 static void copy_strlist_to_str(char *, struct strlist_st
*);
228 static void end_ansi_string(void);
229 static void free_strlist(struct strlist_st
*);
230 void handle_newline(void);
231 static void initialize_globals(void);
232 static void output_comment(FILE *, struct strlist_st
*);
233 static void output_msgid(FILE *, struct strlist_st
*, int);
234 static void output_textdomain(FILE *, struct element_st
*);
235 static void print_help(void);
236 static void read_exclude_file(void);
237 static void trim_line(char *);
238 static void write_all_files(void);
239 static void write_one_file(struct domain_st
*);
241 static void lstrcat(char *, const char *);
244 * Utility functions to malloc a node and initialize fields.
246 static struct domain_st
*new_domain(void);
247 static struct strlist_st
*new_strlist(void);
248 static struct element_st
*new_element(void);
249 static struct exclude_st
*new_exclude(void);
252 * Main program of xgettext.
255 main(int argc
, char **argv
)
260 initialize_globals();
262 while ((c
= getopt(argc
, argv
, "jhax:nsc:d:m:M:p:t")) != EOF
) {
269 comment_tag
= optarg
;
272 default_domain
= optarg
;
303 exclude_file
= optarg
;
311 /* if -h is used, ignore all other options. */
313 (void) fprintf(stderr
, USAGE
);
318 /* -x can be used only with -a */
319 if ((xflg
== TRUE
) && (aflg
== FALSE
))
322 /* -j cannot be used with -a */
323 if ((jflg
== TRUE
) && (aflg
== TRUE
)) {
324 (void) fprintf(stderr
,
325 "-a and -j options cannot be used together.\n");
329 /* -j cannot be used with -s */
330 if ((jflg
== TRUE
) && (sflg
== TRUE
)) {
331 (void) fprintf(stderr
,
332 "-j and -s options cannot be used together.\n");
336 if (opterr
== TRUE
) {
337 (void) fprintf(stderr
, USAGE
);
341 /* error, if no files are specified. */
342 if (optind
== argc
) {
343 (void) fprintf(stderr
, USAGE
);
351 /* If files are -, then read from stdin */
352 if (argv
[optind
][0] == '-') {
359 /* Store argc and argv to pass to yylex() */
364 (void) printf("optind=%d\n", optind
);
367 for (; i
< argc
; i
++) {
368 (void) printf(" %d, <%s>\n", i
, argv
[i
]);
373 if (stdin_only
== FALSE
) {
374 if (freopen(argv
[optind
], "r", stdin
) == NULL
) {
375 (void) fprintf(stderr
,
376 "ERROR, can't open input file: %s\n", argv
[optind
]);
379 (void) strcpy(curr_file
, gargv
[optind
]);
389 printf("\n======= default_domain ========\n");
390 print_one_domain(def_dom
);
391 printf("======= domain list ========\n");
392 print_all_domain(dom_head
);
396 * Write out all .po files.
404 * Prints help information for each option.
409 (void) fprintf(stderr
, "\n");
410 (void) fprintf(stderr
,
411 "-a\t\t\tfind ALL strings\n");
412 (void) fprintf(stderr
,
413 "-c <comment-tag>\tget comments containing <flag>\n");
414 (void) fprintf(stderr
,
415 "-d <default-domain>\tuse <default-domain> for default domain\n");
416 (void) fprintf(stderr
,
418 (void) fprintf(stderr
,
419 "-j\t\t\tupdate existing file with the current result\n");
420 (void) fprintf(stderr
,
421 "-M <suffix>\t\tfill in msgstr with msgid<suffix>\n");
422 (void) fprintf(stderr
,
423 "-m <prefix>\t\tfill in msgstr with <prefix>msgid\n");
424 (void) fprintf(stderr
,
425 "-n\t\t\tline# file name and line number info in output\n");
426 (void) fprintf(stderr
,
427 "-p <pathname>\t\tuse <pathname> for output file directory\n");
428 (void) fprintf(stderr
,
429 "-s\t\t\tgenerate sorted output files\n");
430 (void) fprintf(stderr
,
431 "-x <exclude-file>\texclude strings in file <exclude-file>"
433 (void) fprintf(stderr
,
434 "-\t\t\tread stdin, use as a filter (input only)\n");
438 * Extract file name and line number information from macro line
439 * and set the global variable accordingly.
440 * The valid line format is
444 * where nnn is line number and xxxxx is file name.
447 extract_filename_linenumber(char *mline
)
453 * mline can contain multi newline.
454 * line number should be increased by the number of newlines.
457 while ((p
= strchr(p
, '\n')) != NULL
) {
461 p
= strchr(mline
, ' ');
464 q
= strchr(++p
, ' ');
467 if ((num
= atoi(p
)) > 0) {
481 if ((num
= atoi(p
)) > 0) {
483 (void) strcpy(curr_file
, q
);
487 } /* extract_filename_linenumber */
490 * Handler for MACRO line which starts with #.
493 handle_macro_line(void)
496 (void) printf("Macro line=<%s>\n", yytext
);
499 lstrcat(curr_line
, yytext
);
501 if (in_quote
== TRUE
) {
502 lstrcat(qstring_buf
, yytext
);
503 } else if (in_comment
== FALSE
) {
504 extract_filename_linenumber(yytext
);
509 } /* handle_macro_line */
512 * Handler for C++ comments which starts with //.
515 handle_cplus_comment_line(void)
518 lstrcat(curr_line
, yytext
);
520 if (in_quote
== TRUE
) {
521 lstrcat(qstring_buf
, yytext
);
522 } else if ((in_comment
== FALSE
) &&
523 (in_skippable_string
== FALSE
)) {
526 * If already in c comments, don't do anything.
527 * Set both flags to TRUE here.
528 * Both flags will be set to FALSE when newline
531 in_cplus_comment
= TRUE
;
534 } /* handle_cplus_comment_line */
537 * Handler for the comment start (slash asterisk) in input file.
540 handle_open_comment(void)
543 lstrcat(curr_line
, yytext
);
545 if (in_quote
== TRUE
) {
546 lstrcat(qstring_buf
, yytext
);
547 } else if ((in_comment
== FALSE
) &&
548 (in_skippable_string
== FALSE
)) {
551 is_last_comment_line
= FALSE
;
553 * If there is any comment extracted before accidently,
554 * clean it up and start the new comment again.
556 free_strlist(commhead
);
557 commhead
= commtail
= NULL
;
562 * Handler for the comment end (asterisk slash) in input file.
565 handle_close_comment(void)
568 lstrcat(curr_line
, yytext
);
570 if (in_quote
== TRUE
) {
571 lstrcat(qstring_buf
, yytext
);
572 } else if (in_skippable_string
== FALSE
) {
574 is_last_comment_line
= TRUE
;
579 * Handler for "gettext" in input file.
585 * If -t option is specified to extrct dcgettext,
586 * don't do anything for gettext().
592 num_nested_open_paren
= 0;
595 lstrcat(curr_line
, yytext
);
597 if (in_quote
== TRUE
) {
598 lstrcat(qstring_buf
, yytext
);
599 } else if (in_comment
== FALSE
) {
601 linenum_saved
= curr_linenum
;
603 * gettext will be put into default domain .po file
604 * curr_domain does not change for gettext.
606 curr_domain
[0] = '\0';
608 } /* handle_gettext */
611 * Handler for "dgettext" in input file.
614 handle_dgettext(void)
617 * If -t option is specified to extrct dcgettext,
618 * don't do anything for dgettext().
624 num_nested_open_paren
= 0;
627 lstrcat(curr_line
, yytext
);
629 if (in_quote
== TRUE
) {
630 lstrcat(qstring_buf
, yytext
);
631 } else if (in_comment
== FALSE
) {
633 linenum_saved
= curr_linenum
;
635 * dgettext will be put into domain file specified.
636 * curr_domain will follow.
638 curr_domain
[0] = '\0';
640 } /* handle_dgettext */
643 * Handler for "dcgettext" in input file.
646 handle_dcgettext(void)
649 * dcgettext will be extracted only when -t flag is specified.
655 num_nested_open_paren
= 0;
657 is_first_comma_found
= FALSE
;
660 lstrcat(curr_line
, yytext
);
662 if (in_quote
== TRUE
) {
663 lstrcat(qstring_buf
, yytext
);
664 } else if (in_comment
== FALSE
) {
666 linenum_saved
= curr_linenum
;
668 * dcgettext will be put into domain file specified.
669 * curr_domain will follow.
671 curr_domain
[0] = '\0';
673 } /* handle_dcgettext */
676 * Handler for "textdomain" in input file.
679 handle_textdomain(void)
682 lstrcat(curr_line
, yytext
);
684 if (in_quote
== TRUE
) {
685 lstrcat(qstring_buf
, yytext
);
686 } else if (in_comment
== FALSE
) {
687 in_textdomain
= TRUE
;
688 linenum_saved
= curr_linenum
;
689 curr_domain
[0] = '\0';
691 } /* handle_textdomain */
694 * Handler for '(' in input file.
697 handle_open_paren(void)
700 lstrcat(curr_line
, yytext
);
702 if (in_quote
== TRUE
) {
703 lstrcat(qstring_buf
, yytext
);
704 } else if (in_comment
== FALSE
) {
705 if ((in_gettext
== TRUE
) ||
706 (in_dgettext
== TRUE
) ||
707 (in_dcgettext
== TRUE
) ||
708 (in_textdomain
== TRUE
)) {
710 num_nested_open_paren
++;
713 } /* handle_open_paren */
716 * Handler for ')' in input file.
719 handle_close_paren(void)
722 lstrcat(curr_line
, yytext
);
724 if (in_quote
== TRUE
) {
725 lstrcat(qstring_buf
, yytext
);
726 } else if (in_comment
== FALSE
) {
727 if ((in_gettext
== TRUE
) ||
728 (in_dgettext
== TRUE
) ||
729 (in_dcgettext
== TRUE
) ||
730 (in_textdomain
== TRUE
)) {
732 * If this is not the matching close paren with
733 * the first open paren, no action is necessary.
735 if (--num_nested_open_paren
> 0)
737 add_str_to_element_list(in_textdomain
, curr_domain
);
741 in_dcgettext
= FALSE
;
742 in_textdomain
= FALSE
;
743 } else if (aflg
== TRUE
) {
747 } /* handle_close_paren */
750 * Handler for '\\n' in input file.
752 * This is a '\' followed by new line.
753 * This can be treated like a new line except when this is a continuation
754 * of a ANSI-C string.
755 * If this is a part of ANSI string, treat the current line as a double
756 * quoted string and the next line is the start of the double quoted
760 handle_esc_newline(void)
763 lstrcat(curr_line
, "\\");
767 if (in_quote
== TRUE
) {
768 add_qstring_to_str();
769 } else if ((in_comment
== TRUE
) ||
770 (is_last_comment_line
== TRUE
)) {
771 if (in_cplus_comment
== FALSE
) {
772 add_line_to_comment();
777 } /* handle_esc_newline */
780 * Handler for '"' in input file.
786 lstrcat(curr_line
, yytext
);
788 if (in_comment
== TRUE
) {
790 } else if ((in_gettext
== TRUE
) ||
791 (in_dgettext
== TRUE
) ||
792 (in_dcgettext
== TRUE
) ||
793 (in_textdomain
== TRUE
)) {
794 if (in_str
== TRUE
) {
795 if (in_quote
== FALSE
) {
798 add_qstring_to_str();
802 } else if (aflg
== TRUE
) {
804 * The quote is found outside of gettext, dgetext, and
805 * textdomain. Everytime a quoted string is found,
806 * add it to the string list.
807 * in_str stays TRUE until ANSI string ends.
809 if (in_str
== TRUE
) {
810 if (in_quote
== TRUE
) {
812 add_qstring_to_str();
819 linenum_saved
= curr_linenum
;
822 in_skippable_string
= (in_skippable_string
== TRUE
) ?
828 * Handler for ' ' or TAB in input file.
834 lstrcat(curr_line
, yytext
);
836 if (in_quote
== TRUE
) {
837 lstrcat(qstring_buf
, yytext
);
839 } /* handle_spaces */
842 * Flattens a linked list containing ANSI string to the one string.
845 copy_strlist_to_str(char *str
, struct strlist_st
*strlist
)
847 struct strlist_st
*p
;
851 if (strlist
!= NULL
) {
854 if (p
->str
!= NULL
) {
855 lstrcat(str
, p
->str
);
860 } /* copy_strlist_to_str */
863 * Handler for ',' in input file.
869 lstrcat(curr_line
, yytext
);
871 if (in_quote
== TRUE
) {
872 lstrcat(qstring_buf
, yytext
);
873 } else if (in_comment
== FALSE
) {
874 if (in_str
== TRUE
) {
875 if (in_dgettext
== TRUE
) {
876 copy_strlist_to_str(curr_domain
, strhead
);
877 free_strlist(strhead
);
878 strhead
= strtail
= NULL
;
879 } else if (in_dcgettext
== TRUE
) {
881 * Ignore the second comma.
883 if (is_first_comma_found
== FALSE
) {
884 copy_strlist_to_str(curr_domain
,
886 free_strlist(strhead
);
887 strhead
= strtail
= NULL
;
888 is_first_comma_found
= TRUE
;
890 } else if (aflg
== TRUE
) {
898 * Handler for any other character that does not have special handler.
901 handle_character(void)
904 lstrcat(curr_line
, yytext
);
906 if (in_quote
== TRUE
) {
907 lstrcat(qstring_buf
, yytext
);
908 } else if (in_comment
== FALSE
) {
909 if (in_str
== TRUE
) {
915 } /* handle_character */
918 * Handler for new line in input file.
926 * in_quote is always FALSE here for ANSI-C code.
928 if ((in_comment
== TRUE
) ||
929 (is_last_comment_line
== TRUE
)) {
930 if (in_cplus_comment
== TRUE
) {
931 in_cplus_comment
= FALSE
;
934 add_line_to_comment();
940 * C++ comment always ends with new line.
942 } /* handle_newline */
945 * Process ANSI string.
948 end_ansi_string(void)
950 if ((aflg
== TRUE
) &&
952 (in_gettext
== FALSE
) &&
953 (in_dgettext
== FALSE
) &&
954 (in_dcgettext
== FALSE
) &&
955 (in_textdomain
== FALSE
)) {
956 add_str_to_element_list(FALSE
, curr_domain
);
959 } /* end_ansi_string */
962 * Initialize global variables if necessary.
965 initialize_globals(void)
967 default_domain
= strdup(DEFAULT_DOMAIN
);
968 curr_domain
[0] = '\0';
970 qstring_buf
[0] = '\0';
971 } /* initialize_globals() */
974 * Extract only string part when read a exclude file by removing
975 * keywords (e.g. msgid, msgstr, # ) and heading and trailing blanks and
979 trim_line(char *line
)
989 * Find the position of the last non-whitespace character.
995 if ((c
!= ' ') && (c
!= '\n') && (c
!= '\t')) {
1002 * Find the position of the first non-whitespace character
1003 * by skipping "msgid" initially.
1005 if (strncmp("msgid ", line
, 6) == 0) {
1007 } else if (strncmp("msgstr ", line
, 7) == 0) {
1009 } else if (strncmp("# ", line
, 2) == 0) {
1018 if ((c
!= ' ') && (c
!= '\n') && (c
!= '\t')) {
1025 * For Backward compatibility, we consider both double quoted
1026 * string and non-quoted string.
1027 * The double quote is removed before being stored if exists.
1029 if (line
[first
] == '"') {
1032 if (line
[last
] == '"') {
1037 * Now copy the valid part of the string.
1040 for (i
= 0; i
<= (last
-first
); i
++) {
1041 line
[i
] = line
[p
++];
1047 * Read exclude file and stores it in the global linked list.
1050 read_exclude_file(void)
1053 struct exclude_st
*tmp_excl
;
1054 struct strlist_st
*tail
;
1056 char line
[MAX_STRING_LEN
];
1058 if ((fp
= fopen(exclude_file
, "r")) == NULL
) {
1059 (void) fprintf(stderr
, "ERROR, can't open exclude file: %s\n",
1065 while (fgets(line
, MAX_STRING_LEN
, fp
) != NULL
) {
1067 * Line starting with # is a comment line and ignored.
1068 * Blank line is ignored, too.
1070 if ((line
[0] == '\n') || (line
[0] == '#')) {
1072 } else if (strncmp(line
, "msgstr", 6) == 0) {
1074 } else if (strncmp(line
, "domain", 6) == 0) {
1076 } else if (strncmp(line
, "msgid", 5) == 0) {
1077 ignore_line
= FALSE
;
1078 tmp_excl
= new_exclude();
1079 tmp_excl
->exstr
= new_strlist();
1081 tmp_excl
->exstr
->str
= strdup(line
);
1082 tail
= tmp_excl
->exstr
;
1084 * Prepend new exclude string node to the list.
1086 tmp_excl
->next
= excl_head
;
1087 excl_head
= tmp_excl
;
1090 * If more than one line of string forms msgid,
1091 * append it to the string linked list.
1093 if (ignore_line
== FALSE
) {
1095 tail
->next
= new_strlist();
1096 tail
->next
->str
= strdup(line
);
1103 tmp_excl
= excl_head
;
1104 while (tmp_excl
!= NULL
) {
1105 printf("============================\n");
1106 tail
= tmp_excl
->exstr
;
1107 while (tail
!= NULL
) {
1108 printf("%s###\n", tail
->str
);
1111 tmp_excl
= tmp_excl
->next
;
1114 } /* read_exclude_file */
1117 * Get next character from the string list containing ANSI style string.
1118 * This function returns three valus. (p, *m, *c).
1119 * p is returned by return value and, *m and *c are returned by changing
1120 * values in the location pointed.
1122 * p : points node in the linked list for ANSI string.
1123 * Each node contains double quoted string.
1124 * m : The location of the next characters in the double quoted string
1125 * as integer index in the string.
1126 * When it gets to end of quoted string, the next node will be
1127 * read and m starts as zero for every new node.
1128 * c : Stores the value of the characterto be returned.
1130 static struct strlist_st
*
1131 get_next_ch(struct strlist_st
*p
, int *m
, char *c
)
1137 * From the string list, find non-null string first.
1144 } else if (p
->str
== NULL
) {
1146 } else if (p
->str
[*m
] == '\0') {
1155 * No more character is available.
1163 * Check if the character back slash.
1164 * If yes, ANSI defined escape sequence rule is used.
1166 if (p
->str
[*m
] != '\\') {
1172 * Get next character after '\'.
1207 * Get maximum of three octal digits.
1210 for (i
= 0; i
< 2; i
++) {
1213 if ((oct
>= '0') && (oct
<= '7')) {
1214 value
= value
* 8 + (oct
- '0');
1222 /* (void) fprintf(stderr, "octal=%d\n", value); */
1228 * Remove all heading zeros first and
1229 * get one or two valuid hexadecimal charaters.
1232 while (p
->str
[*m
] == '0') {
1236 for (i
= 0; i
< 2; i
++) {
1240 value
= value
* 16 + (hex
- '0');
1241 } else if (isxdigit(hex
)) {
1243 value
= value
* 16 + (hex
- 'a' + 10);
1251 (void) fprintf(stderr
, "hex=%d\n", value
);
1257 * Undefined by ANSI.
1264 * Advance pointer to point the next character to be parsed.
1272 * Compares two msgids.
1273 * Comparison is done by values, not by characters represented.
1274 * For example, '\t', '\011' and '0x9' are identical values.
1275 * Return values are same as in strcmp.
1276 * 1 if msgid1 > msgid2
1277 * 0 if msgid1 = msgid2
1278 * -1 if msgid1 < msgid2
1281 msgidcmp(struct strlist_st
*id1
, struct strlist_st
*id2
)
1291 id1
= get_next_ch(id1
, &m1
, &c1
);
1292 id2
= get_next_ch(id2
, &m2
, &c2
);
1294 if ((c1
== 0) && (c2
== 0)) {
1300 } else if (c1
< c2
) {
1308 * Check if a ANSI string (which is a linked list itself) is a duplicate
1309 * of any string in the list of ANSI string.
1312 isduplicate(struct element_st
*list
, struct strlist_st
*str
)
1314 struct element_st
*p
;
1322 if (p
->msgid
!= NULL
) {
1323 if (msgidcmp(p
->msgid
, str
) == 0) {
1334 * Extract a comment line and add to the linked list containing
1336 * Each comment line is stored in the node.
1339 add_line_to_comment(void)
1341 struct strlist_st
*tmp_str
;
1343 tmp_str
= new_strlist();
1344 tmp_str
->str
= strdup(curr_line
);
1345 tmp_str
->next
= NULL
;
1347 if (commhead
== NULL
) {
1348 /* Empty comment list */
1352 /* append it to the list */
1353 commtail
->next
= tmp_str
;
1354 commtail
= commtail
->next
;
1357 is_last_comment_line
= FALSE
;
1358 } /* add_line_to_comment */
1361 * Add a double quoted string to the linked list containing ANSI string.
1364 add_qstring_to_str(void)
1366 struct strlist_st
*tmp_str
;
1368 tmp_str
= new_strlist();
1369 tmp_str
->str
= strdup(qstring_buf
);
1370 tmp_str
->next
= NULL
;
1372 if (strhead
== NULL
) {
1373 /* Null ANSI string */
1377 /* Append it to the ANSI string linked list */
1378 strtail
->next
= tmp_str
;
1379 strtail
= strtail
->next
;
1382 qstring_buf
[0] = '\0';
1383 } /* add_qstring_to_str */
1386 * Finds the head of domain nodes given domain name.
1388 static struct domain_st
*
1389 find_domain_node(char *dname
)
1391 struct domain_st
*tmp_dom
, *p
;
1394 * If -a option is specified everything will be written to the
1395 * default domain file.
1398 if (def_dom
== NULL
) {
1399 def_dom
= new_domain();
1404 if ((dname
== NULL
) ||
1405 (dname
[0] == '\0') ||
1406 (strcmp(dname
, default_domain
) == 0)) {
1407 if (def_dom
== NULL
) {
1408 def_dom
= new_domain();
1410 if (strcmp(dname
, default_domain
) == 0) {
1411 (void) fprintf(stderr
,
1412 "%s \"%s\" is used in dgettext of file:%s"
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] != '\0')) {
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] != '\0')) {
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 */