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