Add \cfg / -C directives to allow the user to choose the output file
[sgt/halibut] / main.c
CommitLineData
d7482997 1/*
2 * main.c: command line parsing and top level
3 */
4
c8fb54d2 5#include <assert.h>
d7482997 6#include <stdio.h>
7#include <stdlib.h>
8#include "halibut.h"
9
10static void dbg_prtsource(paragraph *sourceform);
11static void dbg_prtwordlist(int level, word *w);
12static void dbg_prtkws(keywordlist *kws);
13
c8fb54d2 14static const struct backend {
15 char *name;
16 void (*func)(paragraph *, keywordlist *, indexdata *);
17 int bitfield;
18} backends[] = {
19 {"text", text_backend, 0x0001},
20 {"xhtml", xhtml_backend, 0x0002},
21 {"html", xhtml_backend, 0x0002},
22 {"hlp", whlp_backend, 0x0004},
23 {"whlp", whlp_backend, 0x0004},
24 {"winhelp", whlp_backend, 0x0004},
25 {"man", man_backend, 0x0008},
26};
27
d7482997 28int main(int argc, char **argv) {
29 char **infiles;
d7482997 30 int nfiles;
31 int nogo;
32 int errs;
33 int reportcols;
34 int debug;
c8fb54d2 35 int backendbits;
36 int k, b;
6a0b9d08 37 paragraph *cfg, *cfg_tail;
d7482997 38
39 /*
40 * Set up initial (default) parameters.
41 */
42 infiles = mknewa(char *, argc);
d7482997 43 nfiles = 0;
44 nogo = errs = FALSE;
45 reportcols = 0;
46 debug = 0;
c8fb54d2 47 backendbits = 0;
6a0b9d08 48 cfg = cfg_tail = NULL;
d7482997 49
50 if (argc == 1) {
51 usage();
52 exit(EXIT_SUCCESS);
53 }
54
55 /*
56 * Parse command line arguments.
57 */
58 while (--argc) {
59 char *p = *++argv;
60 if (*p == '-') {
61 /*
62 * An option.
63 */
64 while (p && *++p) {
65 char c = *p;
66 switch (c) {
67 case '-':
68 /*
69 * Long option.
70 */
71 {
72 char *opt, *val;
73 opt = p++; /* opt will have _one_ leading - */
74 while (*p && *p != '=')
75 p++; /* find end of option */
76 if (*p == '=') {
77 *p++ = '\0';
78 val = p;
79 } else
80 val = NULL;
c8fb54d2 81
82 assert(opt[0] == '-');
83 for (k = 0; k < (int)lenof(backends); k++)
84 if (!strcmp(opt+1, backends[k].name)) {
85 backendbits |= backends[k].bitfield;
86 break;
87 }
88 if (k < (int)lenof(backends)) {
89 /* do nothing */;
90 } else if (!strcmp(opt, "-help")) {
d7482997 91 help();
92 nogo = TRUE;
93 } else if (!strcmp(opt, "-version")) {
94 showversion();
95 nogo = TRUE;
96 } else if (!strcmp(opt, "-licence") ||
97 !strcmp(opt, "-license")) {
98 licence();
99 nogo = TRUE;
d7482997 100 } else if (!strcmp(opt, "-precise")) {
101 reportcols = 1;
102 } else {
103 errs = TRUE, error(err_nosuchopt, opt);
104 }
105 }
106 p = NULL;
107 break;
108 case 'h':
109 case 'V':
110 case 'L':
111 case 'P':
112 case 'd':
113 /*
114 * Option requiring no parameter.
115 */
116 switch (c) {
117 case 'h':
118 help();
119 nogo = TRUE;
120 break;
121 case 'V':
122 showversion();
123 nogo = TRUE;
124 break;
125 case 'L':
126 licence();
127 nogo = TRUE;
128 break;
129 case 'P':
130 reportcols = 1;
131 break;
132 case 'd':
133 debug = TRUE;
134 break;
135 }
136 break;
6a0b9d08 137 case 'C':
d7482997 138 /*
139 * Option requiring parameter.
140 */
141 p++;
142 if (!*p && argc > 1)
143 --argc, p = *++argv;
144 else if (!*p) {
145 char opt[2];
146 opt[0] = c;
147 opt[1] = '\0';
148 errs = TRUE, error(err_optnoarg, opt);
149 }
150 /*
151 * Now c is the option and p is the parameter.
152 */
153 switch (c) {
6a0b9d08 154 case 'C':
155 /*
156 * -C means we split our argument up into
157 * colon-separated chunks and assemble them
158 * into a config paragraph.
159 */
160 {
161 wchar_t *keywords;
162 char *q;
163 wchar_t *u;
164 paragraph *para;
165
166 keywords = mknewa(wchar_t, 2+strlen(p));
167
168 u = keywords;
169 q = p;
170
171 while (*q) {
172 if (*q == ':') {
173 *u++ = L'\0';
174 } else {
175 if (*q == '\\' && q[1])
176 q++;
177 /* FIXME: lacks charset flexibility */
178 *u++ = *q;
179 }
180 q++;
181 }
182 *u = L'\0';
183
184 para = mknew(paragraph);
185 memset(para, 0, sizeof(*para));
186 para->type = para_Config;
187 para->keyword = keywords;
188 para->next = NULL;
189 para->fpos.filename = "<command line>";
190 para->fpos.line = para->fpos.col = -1;
191
192 if (cfg_tail)
193 cfg_tail->next = para;
194 else
195 cfg = para;
196 cfg_tail = para;
197 }
d7482997 198 break;
199 }
200 p = NULL; /* prevent continued processing */
201 break;
202 default:
203 /*
204 * Unrecognised option.
205 */
206 {
207 char opt[2];
208 opt[0] = c;
209 opt[1] = '\0';
210 errs = TRUE, error(err_nosuchopt, opt);
211 }
212 }
213 }
214 } else {
215 /*
216 * A non-option argument.
217 */
218 infiles[nfiles++] = p;
219 }
220 }
221
222 if (errs)
223 exit(EXIT_FAILURE);
224 if (nogo)
225 exit(EXIT_SUCCESS);
226
227 /*
228 * Do the work.
229 */
230 if (nfiles == 0) {
231 error(err_noinput);
232 usage();
233 exit(EXIT_FAILURE);
234 }
235
236 {
237 input in;
238 paragraph *sourceform, *p;
239 indexdata *idx;
240 keywordlist *keywords;
241
242 in.filenames = infiles;
243 in.nfiles = nfiles;
244 in.currfp = NULL;
245 in.currindex = 0;
246 in.npushback = in.pushbacksize = 0;
247 in.pushback = NULL;
248 in.reportcols = reportcols;
249 in.stack = NULL;
250
251 idx = make_index();
252
253 sourceform = read_input(&in, idx);
254 if (!sourceform)
255 exit(EXIT_FAILURE);
256
6a0b9d08 257 /*
258 * Append the config directives acquired from the command
259 * line.
260 */
261 {
262 paragraph *end;
263
264 end = sourceform;
265 while (end && end->next)
266 end = end->next;
267 assert(end);
268
269 end->next = cfg;
270 }
271
d7482997 272 sfree(in.pushback);
273
274 mark_attr_ends(sourceform);
275
276 sfree(infiles);
277
278 keywords = get_keywords(sourceform);
279 if (!keywords)
280 exit(EXIT_FAILURE);
281 gen_citations(sourceform, keywords);
282 subst_keywords(sourceform, keywords);
283
284 for (p = sourceform; p; p = p->next)
285 if (p->type == para_IM)
286 index_merge(idx, TRUE, p->keyword, p->words);
287
288 build_index(idx);
289
290 if (debug) {
291 index_debug(idx);
292 dbg_prtkws(keywords);
293 dbg_prtsource(sourceform);
294 }
295
c8fb54d2 296 /*
297 * Run the selected set of backends.
298 */
299 for (k = b = 0; k < (int)lenof(backends); k++)
300 if (b != backends[k].bitfield) {
301 b = backends[k].bitfield;
302 if (backendbits == 0 || (backendbits & b))
303 backends[k].func(sourceform, keywords, idx);
304 }
d7482997 305
306 free_para_list(sourceform);
307 free_keywords(keywords);
308 cleanup_index(idx);
309 }
310
311 return 0;
312}
313
314static void dbg_prtsource(paragraph *sourceform) {
315 /*
316 * Output source form in debugging format.
317 */
318
319 paragraph *p;
320 for (p = sourceform; p; p = p->next) {
321 wchar_t *wp;
322 printf("para %d ", p->type);
323 if (p->keyword) {
324 wp = p->keyword;
325 while (*wp) {
326 putchar('\"');
327 for (; *wp; wp++)
328 putchar(*wp);
329 putchar('\"');
330 if (*++wp)
331 printf(", ");
332 }
333 } else
334 printf("(no keyword)");
335 printf(" {\n");
336 dbg_prtwordlist(1, p->words);
337 printf("}\n");
338 }
339}
340
341static void dbg_prtkws(keywordlist *kws) {
342 /*
343 * Output keywords in debugging format.
344 */
345
346 int i;
347 keyword *kw;
348
349 for (i = 0; (kw = index234(kws->keys, i)) != NULL; i++) {
350 wchar_t *wp;
351 printf("keyword ");
352 wp = kw->key;
353 while (*wp) {
354 putchar('\"');
355 for (; *wp; wp++)
356 putchar(*wp);
357 putchar('\"');
358 if (*++wp)
359 printf(", ");
360 }
361 printf(" {\n");
362 dbg_prtwordlist(1, kw->text);
363 printf("}\n");
364 }
365}
366
367static void dbg_prtwordlist(int level, word *w) {
368 for (; w; w = w->next) {
369 wchar_t *wp;
370 printf("%*sword %d ", level*4, "", w->type);
371 if (w->text) {
372 printf("\"");
373 for (wp = w->text; *wp; wp++)
374 putchar(*wp);
375 printf("\"");
376 } else
377 printf("(no text)");
378 if (w->alt) {
379 printf(" alt = {\n");
380 dbg_prtwordlist(level+1, w->alt);
381 printf("%*s}", level*4, "");
382 }
383 printf("\n");
384 }
385}