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