xref: /freebsd/contrib/libxo/xopo/xopo.c (revision d1a0d267b78b542fbd7e6553af2493760f49bfa8)
1 /*
2  * Copyright (c) 2014, 2015, Juniper Networks, Inc.
3  * All rights reserved.
4  * This SOFTWARE is licensed under the LICENSE provided in the
5  * ../Copyright file. By downloading, installing, copying, or otherwise
6  * using the SOFTWARE, you agree to be bound by the terms of that
7  * LICENSE.
8  * Phil Shafer, July 2015
9  */
10 
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <stdarg.h>
14 #include <unistd.h>
15 #include <string.h>
16 #include <ctype.h>
17 #include <sys/queue.h>
18 
19 #include "xo_config.h"
20 #include "xo.h"
21 
22 #include <getopt.h>		/* Include after xo.h for testing */
23 
24 #ifndef UNUSED
25 #define UNUSED __attribute__ ((__unused__))
26 #endif /* UNUSED */
27 
28 static int opt_warn;		/* Enable warnings */
29 static int opt_numbers;		/* Number our fields */
30 
31 typedef struct xopo_msg_s {
32     TAILQ_ENTRY(xopo_msg_s) xm_link;
33     char *xm_plural;		/* If plural, points to the second part */
34     char xm_data[0];		/* Start of data */
35 } xopo_msg_t;
36 
37 typedef TAILQ_HEAD(xopo_msg_list_s, xopo_msg_s) xopo_msg_list_t;
38 
39 static xopo_msg_list_t field_list;
40 
41 static void
42 xopo_msg_cb (const char *str, unsigned len, int plural)
43 {
44     int sz = sizeof(xopo_msg_t) + len + 1;
45     xopo_msg_t *xmp = malloc(sz);
46     if (xmp == NULL)
47 	return;
48 
49     bzero(xmp, sz);
50     memcpy(xmp->xm_data, str, len);
51     xmp->xm_data[len] = '\0';
52 
53     if (plural) {
54 	char *cp = strchr(xmp->xm_data, ',');
55 	if (cp) {
56 	    *cp++ = '\0';
57 	    xmp->xm_plural = cp;
58 	}
59     }
60 
61     xopo_msg_t *xmp2;
62 
63     TAILQ_FOREACH(xmp2, &field_list, xm_link) {
64 	if (strcmp(xmp->xm_data, xmp2->xm_data) == 0) {
65 	    /* Houston, we have a negative on that trajectory */
66 	    free(xmp);
67 	    return;
68 	}
69     }
70 
71     TAILQ_INSERT_TAIL(&field_list, xmp, xm_link);
72 }
73 
74 static void
75 print_version (void)
76 {
77     fprintf(stderr, "libxo version %s%s\n",
78 	    xo_version, xo_version_extra);
79     fprintf(stderr, "xopo version %s%s\n",
80 	    LIBXO_VERSION, LIBXO_VERSION_EXTRA);
81 }
82 
83 static void
84 print_help (void)
85 {
86     fprintf(stderr,
87 "Usage: xopo [options] format [fields]\n"
88 "    --help                Display this help text\n"
89 "    --option <opts> -or -O <opts> Give formatting options\n"
90 "    --output <file> -or -o <file> Use file as output destination\n"
91 "    --po <file> or -f <file> Generate new msgid's for a po file\n"
92 "    --simplify <text> OR -s <text> Show simplified form of the format string\n"
93 "    --version             Display version information\n"
94 "    --warn OR -W          Display warnings in text on stderr\n"
95 );
96 }
97 
98 static struct opts {
99     int o_help;
100     int o_version;
101 } opts;
102 
103 static struct option long_opts[] = {
104     { "help", no_argument, &opts.o_help, 1 },
105     { "number", no_argument, NULL, 'n' },
106     { "option", required_argument, NULL, 'O' },
107     { "output", required_argument, NULL, 'o' },
108     { "po", required_argument, NULL, 'f' },
109     { "simplify", no_argument, NULL, 'S' },
110     { "warn", no_argument, NULL, 'W' },
111     { NULL, 0, NULL, 0 }
112 };
113 
114 int
115 main (int argc UNUSED, char **argv)
116 {
117     char *fmt = NULL;
118     char *opt_options = NULL;
119     char *opt_input = NULL;
120     char *opt_output = NULL;
121     char *opt_simplify = NULL;
122     int rc;
123 
124     argc = xo_parse_args(argc, argv);
125     if (argc < 0)
126 	return 1;
127 
128     while ((rc = getopt_long(argc, argv, "f:no:O:s:W",
129 				long_opts, NULL)) != -1) {
130 	switch (rc) {
131 	case 'f':
132 	    opt_input = optarg;
133 	    break;
134 
135 	case 'n':
136 	    opt_numbers = 1;
137 	    break;
138 
139 	case 'o':
140 	    opt_output = optarg;
141 	    break;
142 
143 	case 'O':
144 	    opt_options = optarg;
145 	    break;
146 
147 	case 's':
148 	    opt_simplify = optarg;
149 	    break;
150 
151 	case 'W':
152 	    opt_warn = 1;
153 	    xo_set_flags(NULL, XOF_WARN);
154 	    break;
155 
156 	case ':':
157 	    xo_errx(1, "missing argument");
158 	    break;
159 
160 	case 0:
161 	    if (opts.o_help) {
162 		print_help();
163 		return 1;
164 
165 	    } else if (opts.o_version) {
166 		print_version();
167 		return 0;
168 
169 	    } else {
170 		print_help();
171 		return 1;
172 	    }
173 
174 	    bzero(&opts, sizeof(opts)); /* Reset all the options */
175 	    break;
176 
177 	default:
178 	    print_help();
179 	    return 1;
180 	}
181     }
182 
183     argc -= optind;
184     argv += optind;
185 
186     if (opt_options) {
187 	rc = xo_set_options(NULL, opt_options);
188 	if (rc < 0)
189 	    xo_errx(1, "invalid options: %s", opt_options);
190     }
191 
192     fmt = *argv++;
193 
194     if (opt_simplify) {
195 	fmt = xo_simplify_format(NULL, opt_simplify, opt_numbers, NULL);
196 	if (fmt) {
197 	    xo_emit("{:format}\n", fmt);
198 	    free(fmt);
199 	}
200 	exit(0);
201     }
202 
203     static char msgid[] = "msgid ";
204     char buf[BUFSIZ], *cp, *ep;
205     FILE *infile;
206     FILE *outfile;
207     TAILQ_INIT(&field_list);
208 
209     if (opt_input) {
210 	infile = fopen(opt_input, "r");
211 	if (infile == NULL)
212 	    xo_emit_err(1, "count not open input file: '{:filename}'",
213 			opt_input);
214     } else
215 	infile = stdin;
216 
217     if (opt_output) {
218 	unlink(opt_output);
219 	outfile = fopen(opt_output, "w");
220 	if (outfile == NULL)
221 	    xo_emit_err(1, "count not open output file: '{:filename}'",
222 			opt_output);
223     } else
224 	outfile = stdout;
225 
226     int blank = 0, line;
227 
228     for (line = 1;; line++) {
229 	if (fgets(buf, sizeof(buf), infile) == NULL)
230 	    break;
231 
232 	if (buf[0] == '#' && buf[1] == '\n')
233 	    continue;
234 
235 	blank = (buf[0] == '\n' && buf[1] == '\0');
236 
237 	if (strncmp(buf, msgid, sizeof(msgid) - 1) != 0) {
238 	    fprintf(outfile, "%s", buf);
239 	    continue;
240 	}
241 
242 	for (cp = buf + sizeof(msgid); *cp; cp++)
243 	    if (!isspace((int) *cp))
244 		break;
245 
246 	if (*cp == '"')
247 	    cp += 1;
248 
249 	ep = cp + strlen(cp);
250 	if (ep > cp)
251 	    ep -= 1;
252 
253 	while (isspace((int) *ep) && ep > cp)
254 	    ep -= 1;
255 
256 	if (*ep != '"')
257 	    *ep += 1;
258 
259 	*ep = '\0';
260 
261 	cp = xo_simplify_format(NULL, cp, opt_numbers, xopo_msg_cb);
262 	if (cp) {
263 	    fprintf(outfile, "msgid \"%s\"\n", cp);
264 	    free(cp);
265 	}
266     }
267 
268     if (!blank)
269 	fprintf(outfile, "\n");
270 
271     xopo_msg_t *xmp;
272     TAILQ_FOREACH(xmp, &field_list, xm_link) {
273 	if (xmp->xm_plural) {
274 	    fprintf(outfile, "msgid \"%s\"\n"
275 		    "msgid_plural \"%s\"\n"
276 		    "msgstr[0] \"\"\n"
277 		    "msgstr[1] \"\"\n\n",
278 		    xmp->xm_data, xmp->xm_plural);
279 	} else {
280 	    fprintf(outfile, "msgid \"%s\"\nmsgstr \"\"\n\n", xmp->xm_data);
281 	}
282     }
283 
284     if (infile != stdin)
285 	fclose(infile);
286     if (outfile != stdout)
287 	fclose(outfile);
288 
289     xo_finish();
290 
291     return 0;
292 }
293