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