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