xref: /illumos-gate/usr/src/cmd/genmsg/main.c (revision 7a6d80f1660abd4755c68cbd094d4a914681d26e)
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
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
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
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