1 /*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 */
21 /*
22 * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
24 */
25
26 #pragma ident "%Z%%M% %I% %E% SMI"
27
28 #include <stdio.h>
29 #include <limits.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <libgen.h>
33 #include <libintl.h>
34 #include <locale.h>
35 #include <unistd.h>
36 #include <sys/param.h>
37
38 #include "genmsg.h"
39
40 #define MSG_SUFFIX ".msg"
41 #define NEW_SUFFIX ".new"
42
43 #if !defined(TEXT_DOMAIN)
44 #define TEXT_DOMAIN "genmsg"
45 #endif
46
47 /*
48 * External functions.
49 */
50 extern void write_msgfile(char *); /* from util.c */
51 extern int read_projfile(char *); /* from util.c */
52 extern void write_projfile(char *); /* from util.c */
53 extern void read_msgfile(char *); /* from util.c */
54 extern int is_writable(char *); /* from util.c */
55 extern int file_copy(char *, char *); /* from util.c */
56 extern void init_lex(void); /* from genmsg.l */
57 extern void init_linemsgid(void); /* from genmsg.l */
58 extern FILE *yyin; /* from lex */
59 extern int yyparse(void); /* from genmsg.l */
60
61 /* Program name. */
62 char *program;
63
64 /* File pointer for auto-message-numbering. */
65 FILE *newfp = NULL;
66
67 /* Input source file. */
68 char *srcfile;
69
70 /* Tag for message comments. */
71 char *mctag = NULL;
72
73 /* Tag for set number comments. */
74 char *sctag = NULL;
75
76 /* Mode mask to define the genmsg tasks. */
77 Mode active_mode = NoMode;
78
79 /*
80 * This flag will be TRUE if a catgets() call is found
81 * in the input file.
82 */
83 int is_cat_found = FALSE;
84
85 /* Suppress an error message if this flag is TRUE. */
86 int suppress_error = FALSE;
87
88 /* Prefix and suffix of messages for testing. */
89 char *premsg = NULL;
90 char *sufmsg = NULL;
91
92 static void usage(void);
93 static void validate_options(void);
94
95 int
main(int argc,char ** argv)96 main(int argc, char **argv)
97 {
98 int c;
99 char *msgfile = NULL;
100 char *projfile = NULL;
101 char *newprojfile = NULL;
102 char *cpppath = NULL;
103 int do_msgfile = FALSE;
104 int tmpfd = -1;
105 char *cmd, *tmp;
106 char tmpfile[32];
107 size_t len;
108
109 program = basename(argv[0]);
110
111 (void) setlocale(LC_ALL, "");
112 (void) textdomain(TEXT_DOMAIN);
113
114 while ((c = getopt(argc, argv, "arndfg:o:l:p:c:s:m:M:txb")) != EOF) {
115 switch (c) {
116 case 'o':
117 SetActiveMode(MessageMode);
118 msgfile = optarg;
119 break;
120 case 'a':
121 SetActiveMode(AppendMode);
122 break;
123 case 'l':
124 projfile = optarg;
125 SetActiveMode(AutoNumMode);
126 break;
127 case 'r':
128 SetActiveMode(ReverseMode);
129 break;
130 case 'p':
131 cpppath = optarg;
132 SetActiveMode(PreProcessMode);
133 break;
134 case 'g':
135 newprojfile = optarg;
136 suppress_error = TRUE;
137 SetActiveMode(ProjectMode);
138 break;
139 case 'c':
140 mctag = optarg;
141 SetActiveMode(MsgCommentMode);
142 break;
143 case 's':
144 sctag = optarg;
145 SetActiveMode(SetCommentMode);
146 break;
147 case 'b':
148 SetActiveMode(BackCommentMode);
149 break;
150 case 'n':
151 SetActiveMode(LineInfoMode);
152 break;
153 case 'm':
154 premsg = optarg;
155 SetActiveMode(PrefixMode);
156 break;
157 case 'M':
158 sufmsg = optarg;
159 SetActiveMode(SuffixMode);
160 break;
161 case 't':
162 SetActiveMode(TripleMode);
163 break;
164 case 'd':
165 SetActiveMode(DoubleLineMode);
166 break;
167 case 'f':
168 SetActiveMode(OverwriteMode);
169 break;
170 case 'x':
171 suppress_error = TRUE;
172 SetActiveMode(NoErrorMode);
173 break;
174 default:
175 usage();
176 break;
177 }
178 }
179
180 if (optind >= argc) {
181 usage();
182 }
183
184 validate_options();
185
186 if (IsActiveMode(AutoNumMode)) {
187 if (read_projfile(projfile)) {
188 tmp = basename(projfile);
189 len = strlen(tmp) + sizeof (NEW_SUFFIX);
190 if ((newprojfile = malloc(len)) == NULL) {
191 prg_err(gettext("fatal: out of memory"));
192 exit(EXIT_FAILURE);
193 }
194 (void) snprintf(newprojfile, len, "%s%s",
195 tmp, NEW_SUFFIX);
196 } else {
197 newprojfile = basename(projfile);
198 }
199 }
200
201 if ((IsActiveMode(AutoNumMode) || IsActiveMode(ProjectMode)) &&
202 (is_writable(IsActiveMode(OverwriteMode) ?
203 projfile : newprojfile) == FALSE)) {
204 prg_err(gettext("cannot write \"%s\": permission denied"),
205 IsActiveMode(OverwriteMode) ? projfile : newprojfile);
206 exit(EXIT_FAILURE);
207 }
208
209 if (IsActiveMode(AppendMode) && msgfile != NULL) {
210 read_msgfile(msgfile);
211 }
212
213 if (msgfile == NULL) {
214 tmp = basename(argv[optind]);
215 len = strlen(tmp) + sizeof (MSG_SUFFIX);
216 if ((msgfile = malloc(len)) == NULL) {
217 prg_err(gettext("fatal: out of memory"));
218 exit(EXIT_FAILURE);
219 }
220 (void) snprintf(msgfile, len, "%s%s", tmp, MSG_SUFFIX);
221 }
222
223 while (optind < argc) {
224 is_cat_found = FALSE;
225 srcfile = argv[optind];
226
227 if (IsActiveMode(AutoNumMode) || IsActiveMode(ReverseMode)) {
228 init_linemsgid();
229 }
230
231 if (IsActiveMode(PreProcessMode)) {
232 len = strlen(cpppath) + 1 + strlen(srcfile) + 1;
233 if ((cmd = malloc(len)) == NULL) {
234 prg_err(gettext("fatal: out of memory"));
235 exit(EXIT_FAILURE);
236 }
237 (void) snprintf(cmd, len, "%s %s", cpppath, srcfile);
238 if ((yyin = popen(cmd, "r")) == NULL) {
239 prg_err(
240 gettext("fatal: cannot execute \"%s\""),
241 cpppath);
242 exit(EXIT_FAILURE);
243 }
244 free(cmd);
245 } else {
246 if ((yyin = fopen(srcfile, "r")) == NULL) {
247 prg_err(
248 gettext("cannot open \"%s\""), srcfile);
249 goto end;
250 }
251 }
252
253 init_lex();
254 (void) yyparse();
255
256 if (IsActiveMode(PreProcessMode)) {
257 if (pclose(yyin) != 0) {
258 prg_err(gettext("\"%s\" failed for \"%s\""),
259 cpppath, srcfile);
260 goto end;
261 }
262 }
263
264 if (is_cat_found == FALSE) {
265 if (!IsActiveMode(PreProcessMode)) {
266 (void) fclose(yyin);
267 }
268 goto end;
269 }
270
271 if (do_msgfile == FALSE) {
272 do_msgfile = TRUE;
273 }
274
275 if (IsActiveMode(AutoNumMode) || IsActiveMode(ReverseMode)) {
276 char *newfile;
277
278 tmp = basename(srcfile);
279
280 if (IsActiveMode(OverwriteMode)) {
281 newfile = srcfile;
282 } else {
283 len = strlen(tmp) + sizeof (NEW_SUFFIX);
284 if ((newfile = malloc(len)) == NULL) {
285 prg_err(
286 gettext("fatal: out of memory"));
287 exit(EXIT_FAILURE);
288 }
289 (void) snprintf(newfile, len, "%s%s",
290 tmp, NEW_SUFFIX);
291 }
292
293 if (is_writable(newfile) == FALSE) {
294 prg_err(gettext(
295 "cannot create \"%s\": permission denied"), newfile);
296 goto end;
297 }
298
299 (void) strlcpy(tmpfile, "/tmp/gensmg.XXXXXX",
300 sizeof (tmpfile));
301
302 if ((tmpfd = mkstemp(tmpfile)) == -1) {
303 prg_err(gettext(
304 "cannot create \"%s\""), tmpfile);
305 if (!IsActiveMode(PreProcessMode)) {
306 (void) fclose(yyin);
307 }
308 goto end;
309 }
310 if ((newfp = fdopen(tmpfd, "w")) == NULL) {
311 prg_err(gettext(
312 "cannot create \"%s\""), tmpfile);
313 if (!IsActiveMode(PreProcessMode)) {
314 (void) fclose(yyin);
315 }
316 (void) close(tmpfd);
317 (void) unlink(tmpfile);
318 goto end;
319 }
320
321 if (IsActiveMode(PreProcessMode)) {
322 if ((yyin = fopen(srcfile, "r")) == NULL) {
323 prg_err(gettext(
324 "cannot open \"%s\""), srcfile);
325 (void) fclose(newfp);
326 (void) unlink(tmpfile);
327 goto end;
328 }
329 } else {
330 rewind(yyin);
331 }
332
333 SetActiveMode(ReplaceMode);
334 init_lex();
335 (void) yyparse();
336 ResetActiveMode(ReplaceMode);
337
338 (void) fclose(newfp);
339 newfp = NULL;
340
341 (void) fclose(yyin);
342
343 (void) file_copy(tmpfile, newfile);
344
345 (void) unlink(tmpfile);
346
347 goto end;
348 }
349
350 if (!IsActiveMode(PreProcessMode)) {
351 (void) fclose(yyin);
352 }
353
354 end:
355 optind++;
356 }
357
358 if (!do_msgfile) { /* no more business. */
359 return (EXIT_SUCCESS);
360 }
361
362 if (!IsActiveMode(ReverseMode) && !IsActiveMode(ProjectMode)) {
363 write_msgfile(msgfile);
364 }
365
366 if (IsActiveMode(AutoNumMode) || IsActiveMode(ProjectMode)) {
367 write_projfile(IsActiveMode(OverwriteMode) ?
368 projfile : newprojfile);
369 }
370 return (EXIT_SUCCESS);
371 }
372
373 static void
validate_options(void)374 validate_options(void)
375 {
376 /* -r doesn't work with either -a or -l. */
377 if (IsActiveMode(ReverseMode) &&
378 (IsActiveMode(AutoNumMode) || IsActiveMode(AppendMode))) {
379 usage();
380 }
381 /* -b should be accompanied with -c, -s, -d, and -n. */
382 if (IsActiveMode(BackCommentMode) &&
383 (!IsActiveMode(MsgCommentMode) &&
384 !IsActiveMode(SetCommentMode) &&
385 !IsActiveMode(DoubleLineMode) &&
386 !IsActiveMode(LineInfoMode))) {
387 usage();
388 }
389 if (IsActiveMode(ProjectMode) &&
390 (IsActiveMode(AutoNumMode) || IsActiveMode(ReverseMode) ||
391 IsActiveMode(AppendMode) || IsActiveMode(MsgCommentMode) ||
392 IsActiveMode(LineInfoMode) || IsActiveMode(OverwriteMode) ||
393 IsActiveMode(PrefixMode) || IsActiveMode(SuffixMode) ||
394 IsActiveMode(TripleMode) || IsActiveMode(DoubleLineMode) ||
395 IsActiveMode(MessageMode) || IsActiveMode(NoErrorMode))) {
396 usage();
397 }
398 }
399
400 static void
usage(void)401 usage(void)
402 {
403 (void) fprintf(stderr, gettext(
404 "Usage: %s [-o message-file] [-a] [-d] [-p preprocessor]\n"
405 " [-s set-tag] [-c message-tag] [-b] [-n]\n"
406 " [-l project-file] [-r] [-f] [-g project-file]\n"
407 " [-m prefix] [-M suffix] [-t] [-x] files ...\n"),
408 program);
409 exit(EXIT_FAILURE);
410 }
411