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
xopo_msg_cb(const char * str,unsigned len,int plural)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
print_version(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
print_help(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
main(int argc UNUSED,char ** argv)115 main (int argc UNUSED, char **argv)
116 {
117 char *opt_options = NULL;
118 char *opt_input = NULL;
119 char *opt_output = NULL;
120 char *opt_simplify = NULL;
121 int rc;
122
123 argc = xo_parse_args(argc, argv);
124 if (argc < 0)
125 return 1;
126
127 while ((rc = getopt_long(argc, argv, "f:no:O:s:W",
128 long_opts, NULL)) != -1) {
129 switch (rc) {
130 case 'f':
131 opt_input = optarg;
132 break;
133
134 case 'n':
135 opt_numbers = 1;
136 break;
137
138 case 'o':
139 opt_output = optarg;
140 break;
141
142 case 'O':
143 opt_options = optarg;
144 break;
145
146 case 's':
147 opt_simplify = optarg;
148 break;
149
150 case 'W':
151 opt_warn = 1;
152 xo_set_flags(NULL, XOF_WARN);
153 break;
154
155 case ':':
156 xo_errx(1, "missing argument");
157 break;
158
159 case 0:
160 if (opts.o_help) {
161 print_help();
162 return 1;
163
164 } else if (opts.o_version) {
165 print_version();
166 return 0;
167
168 } else {
169 print_help();
170 return 1;
171 }
172
173 bzero(&opts, sizeof(opts)); /* Reset all the options */
174 break;
175
176 default:
177 print_help();
178 return 1;
179 }
180 }
181
182 argc -= optind;
183 argv += optind;
184
185 if (opt_options) {
186 rc = xo_set_options(NULL, opt_options);
187 if (rc < 0)
188 xo_errx(1, "invalid options: %s", opt_options);
189 }
190
191 if (opt_simplify) {
192 char *fmt = xo_simplify_format(NULL, opt_simplify, opt_numbers, NULL);
193 if (fmt) {
194 xo_emit("{:format}\n", fmt);
195 free(fmt);
196 }
197 exit(0);
198 }
199
200 static char msgid[] = "msgid ";
201 char buf[BUFSIZ], *cp, *ep;
202 FILE *infile;
203 FILE *outfile;
204 TAILQ_INIT(&field_list);
205
206 if (opt_input) {
207 infile = fopen(opt_input, "r");
208 if (infile == NULL)
209 xo_emit_err(1, "count not open input file: '{:filename}'",
210 opt_input);
211 } else
212 infile = stdin;
213
214 if (opt_output) {
215 unlink(opt_output);
216 outfile = fopen(opt_output, "w");
217 if (outfile == NULL)
218 xo_emit_err(1, "count not open output file: '{:filename}'",
219 opt_output);
220 } else
221 outfile = stdout;
222
223 int blank = 0, line;
224
225 for (line = 1;; line++) {
226 if (fgets(buf, sizeof(buf), infile) == NULL)
227 break;
228
229 if (buf[0] == '#' && buf[1] == '\n')
230 continue;
231
232 blank = (buf[0] == '\n' && buf[1] == '\0');
233
234 if (strncmp(buf, msgid, sizeof(msgid) - 1) != 0) {
235 fprintf(outfile, "%s", buf);
236 continue;
237 }
238
239 for (cp = buf + sizeof(msgid); *cp; cp++)
240 if (!isspace((int) *cp))
241 break;
242
243 if (*cp == '"')
244 cp += 1;
245
246 ep = cp + strlen(cp);
247 if (ep > cp)
248 ep -= 1;
249
250 while (isspace((int) *ep) && ep > cp)
251 ep -= 1;
252
253 if (*ep != '"')
254 *ep += 1;
255
256 *ep = '\0';
257
258 cp = xo_simplify_format(NULL, cp, opt_numbers, xopo_msg_cb);
259 if (cp) {
260 fprintf(outfile, "msgid \"%s\"\n", cp);
261 free(cp);
262 }
263 }
264
265 if (!blank)
266 fprintf(outfile, "\n");
267
268 xopo_msg_t *xmp;
269 TAILQ_FOREACH(xmp, &field_list, xm_link) {
270 if (xmp->xm_plural) {
271 fprintf(outfile, "msgid \"%s\"\n"
272 "msgid_plural \"%s\"\n"
273 "msgstr[0] \"\"\n"
274 "msgstr[1] \"\"\n\n",
275 xmp->xm_data, xmp->xm_plural);
276 } else {
277 fprintf(outfile, "msgid \"%s\"\nmsgstr \"\"\n\n", xmp->xm_data);
278 }
279 }
280
281 if (infile != stdin)
282 fclose(infile);
283 if (outfile != stdout)
284 fclose(outfile);
285
286 xo_finish();
287
288 return 0;
289 }
290