build: Activate ONESHELL.
[lopsub.git] / lsg.c
1 /*
2 * Copyright (C) 2016 Andre Noll <maan@tuebingen.mpg.de>
3 *
4 * Licensed under the GPL v3, see http://www.gnu.org/licenses/gpl-3.0.html
5 */
6
7 #include <stdlib.h>
8 #include <stdio.h>
9 #include <ctype.h>
10 #include <stdbool.h>
11 #include <assert.h>
12 #include <string.h>
13 #include <time.h>
14
15 #include "lsg.h"
16 #include "lopsub-internal.h"
17 #include "lopsub.h"
18
19 /*
20 * To stringify the result of the *expansion* of a macro argument, we have to
21 * use two levels of macros.
22 */
23 #define LLS_STRINGIFY_EXPAND(_arg) LLS_STRINGIFY(_arg)
24 #define LLS_STRINGIFY(_arg) #_arg
25 #define LLS_ABI_VERSION_VAR_STRING LLS_STRINGIFY_EXPAND(LLS_ABI_VERSION_VAR)
26
27 int yylex(void);
28
29 static void gen_license_header(FILE *out, const char *cmdline, const char *pfx,
30 const char *sfx)
31 {
32 fprintf(out, "%sautogenerated by lopsubgen version %s%s\n",
33 pfx, lls_version(), sfx);
34 if (cmdline) {
35 fprintf(out, "%scommand line:", pfx);
36 fprintf(out, " %s", cmdline);
37 fprintf(out, "%s\n", sfx);
38 }
39 fprintf(out, "%sPublic domain, no copyright claims.%s\n",
40 pfx, sfx);
41 }
42
43 static void inplace_sanitize(char *src)
44 {
45 for (; *src; src++)
46 if (*src == '-')
47 *src = '_';
48 }
49
50 static void inplace_toupper(char *src)
51 {
52 for (; *src; src++)
53 *src = toupper((int)*src);
54 }
55
56 static void lsg_init_name(struct lsg_name *name)
57 {
58 assert(name->orig);
59 name->sanitized = strdup(name->orig);
60 inplace_sanitize(name->sanitized);
61 name->capitalized = strdup(name->sanitized);
62 inplace_toupper(name->capitalized);
63 }
64
65 static void print_tabs(int num, FILE *out)
66 {
67 while (--num >= 0)
68 fprintf(out, "\t");
69 }
70
71 static void format_c_text(const char *text, const char *member, int indent,
72 FILE *out)
73 {
74 int i;
75
76 if (!text)
77 return;
78 print_tabs(indent, out);
79 fprintf(out, ".%s = (char []) {", member);
80 for (i = 0; text[i]; i++) {
81 if ((i % 8) == 0) {
82 fprintf(out, "\n");
83 print_tabs(indent + 1, out);
84 }
85 fprintf(out, "0x%02x,", (unsigned char)text[i]);
86 }
87 fprintf(out, "0x00\n");
88 print_tabs(indent, out);
89 fprintf(out, "},\n");
90 }
91
92 static void format_flags(struct lsg_option *opt, FILE *out)
93 {
94 fprintf(out, "\t\t\t\t\t.flags = 0");
95 if (opt->multiple)
96 fprintf(out, " | LLS_MULTIPLE");
97 if (opt->required)
98 fprintf(out, " | LLS_REQUIRED");
99 if (opt->ignored)
100 fprintf(out, " | LLS_IGNORED");
101 if (opt->default_val)
102 fprintf(out, " | LLS_HAS_DEFAULT");
103 fprintf(out, ",\n");
104 }
105
106 static int string_literal(const char *src, FILE *out)
107 {
108 int len = 0;
109
110 len += fprintf(out, "\"");
111 while (src && *src) {
112 if (*src == '"' || *src == '\\')
113 len += fprintf(out, "\\");
114 len += fprintf(out, "%c", *src);
115 src++;
116 }
117 len += fprintf(out, "\"");
118 return len;
119 }
120
121 static void format_man_text(FILE *out, bool bol, const char *text)
122 {
123 int i;
124
125 if (!text)
126 return;
127 for (i = 0; text[i]; i++) {
128 switch (text[i]) {
129 case '\n':
130 fprintf(out, "\n");
131 bol = true;
132 continue;
133 case '.':
134 case '\'':
135 if (bol)
136 fprintf(out, "\\&");
137 break;
138 case '\\':
139 fprintf(out, "\\");
140 break;
141 case ' ':
142 case '\t':
143 if (bol)
144 continue;
145 }
146 bol = false;
147 fprintf(out, "%c", text[i]);
148 }
149 }
150
151 static int format_option_arg(const struct lsg_option *opt, FILE *out,
152 bool man_format)
153 {
154 char *typestr = opt->typestr? opt->typestr : "val";
155 int arg_info, len = 0;
156
157 if (opt->ignored)
158 return 0;
159 if (!opt->arg_info)
160 arg_info = 0;
161 else if (strcmp(opt->arg_info, "LLS_NO_ARGUMENT") == 0)
162 arg_info = 0;
163 else if (strcmp(opt->arg_info, "LLS_OPTIONAL_ARGUMENT") == 0)
164 arg_info = -1;
165 else if (strcmp(opt->arg_info, "LLS_REQUIRED_ARGUMENT") == 0)
166 arg_info = 1;
167 else
168 assert(0);
169
170 if (arg_info == -1)
171 len += fprintf(out, "[");
172 if (arg_info != 0) {
173 len += fprintf(out, "=<");
174 if (man_format)
175 len += fprintf(out, "%s", typestr);
176 else {
177 len += fprintf(out, "\"");
178 len += string_literal(typestr, out);
179 len += fprintf(out, "\"");
180 }
181 fprintf(out, ">");
182 }
183 if (arg_info == -1)
184 len += fprintf(out, "]");
185 return len;
186 }
187
188 static void format_synopsis(const struct lsg_command *cmd, FILE *out,
189 bool man_format)
190 {
191 int j, len;
192
193 if (man_format)
194 fprintf(out, "\\&");
195 if (cmd->synopsis) {
196 if (man_format)
197 format_man_text(out, true, cmd->synopsis);
198 else {
199 string_literal(cmd->synopsis, out);
200 fprintf(out, ",\n");
201 }
202 return;
203 }
204 if (!man_format)
205 fprintf(out, "\"");
206 len = strlen(cmd->name.orig) + 8;
207 for (j = 0; j < cmd->num_options; j++) {
208 struct lsg_option *opt = cmd->options + j;
209 if (opt->ignored)
210 continue;
211 if (j > 0) {
212 if (!man_format && len > 70) {
213 fprintf(out, "\\n\"\n\t\t\t\t\" ");
214 len = 8;
215 } else
216 len += fprintf(out, " ");
217 }
218 if (!opt->required)
219 len += fprintf(out, "[");
220 len += fprintf(out, "--%s", opt->name.orig);
221 len += format_option_arg(opt, out, man_format);
222 if (!opt->required)
223 len += fprintf(out, "]");
224 }
225 if (cmd->non_opts_name) {
226 len += strlen(cmd->non_opts_name);
227 if (!man_format && len > 70)
228 fprintf(out, "\\n\"\n\t\t\t\t\" ");
229 if (cmd->num_options > 0)
230 fprintf(out, " [--]");
231 fprintf(out, " %s", cmd->non_opts_name);
232 }
233 if (!man_format)
234 fprintf(out, "\",");
235 }
236
237 static void gen_c_options(const struct lsg_command *cmd, FILE *out)
238 {
239 int i, j;
240
241 if (cmd->num_options == 0)
242 return;
243 fprintf(out, "\t\t.options = (struct lls_option[]) {\n");
244 for (j = 0; j < cmd->num_options; j++) {
245 struct lsg_option *opt = cmd->options + j;
246 fprintf(out, "\t\t\t{\n");
247 fprintf(out, "\t\t\t\t.name = \"%s\",\n",
248 opt->name.orig);
249 if (opt->short_opt)
250 fprintf(out, "\t\t\t\t.short_opt = '%c',\n",
251 opt->short_opt);
252 if (opt->summary) {
253 fprintf(out, "\t\t\t\t.summary = ");
254 string_literal(opt->summary, out);
255 fprintf(out, ",\n");
256 }
257 if (opt->arg_info)
258 fprintf(out, "\t\t\t\t.arg_info = %s,\n",
259 opt->arg_info);
260 if (opt->arg_type)
261 fprintf(out, "\t\t\t\t.arg_type = %s,\n",
262 opt->arg_type);
263 if (opt->typestr) {
264 fprintf(out, "\t\t\t\t.typestr = ");
265 string_literal(opt->typestr, out);
266 fprintf(out, ",\n");
267 }
268 fprintf(out, "\t\t\t\t.values = ");
269 if (opt->values) {
270 fprintf(out, "(union lls_val *)(union lls_val[]) {\n");
271 for (i = 0; i < opt->num_values; i++)
272 fprintf(out, "\t\t\t\t\t{.string_val = %s},\n",
273 opt->value_literals[i]);
274 fprintf(out, "\t\t\t\t\t{.string_val = NULL}\n");
275 fprintf(out, "\t\t\t\t},\n");
276 } else
277 fprintf(out, "NULL,\n");
278 format_flags(opt, out);
279 if (opt->default_val) {
280 bool string = strcmp(opt->arg_type, "LLS_STRING") == 0;
281 fprintf(out, "\t\t\t\t.default_val = {");
282 if (string) {
283 if (opt->values)
284 fprintf(out, ".uint32_val = ");
285 else {
286 fprintf(out, ".string_val = ");
287 string_literal(opt->default_val, out);
288 }
289 } else if (strcmp(opt->arg_type, "LLS_INT32") == 0)
290 fprintf(out, ".int32_val = ");
291 else if (strcmp(opt->arg_type, "LLS_UINT32") == 0)
292 fprintf(out, ".uint32_val = ");
293 else if (strcmp(opt->arg_type, "LLS_INT64") == 0)
294 fprintf(out, ".int64_val = ");
295 else if (strcmp(opt->arg_type, "LLS_UINT64") == 0)
296 fprintf(out, ".uint64_val = ");
297 if (!string || opt->values)
298 fprintf(out, "%s", opt->default_val);
299 fprintf(out, "},\n");
300 }
301 format_c_text(opt->help, "help", 5, out);
302 fprintf(out, "\t\t\t},\n");
303 }
304 fprintf(out, "\t\t\t{\n\t\t\t\t\t.name = NULL\n\t\t\t}\n");
305 fprintf(out, "\t\t}\n");
306 }
307
308 static void format_command(const struct lsg_command *cmd, FILE *out)
309 {
310 if (!cmd->name.orig) {
311 fprintf(out, "{.name = NULL}\n");
312 return;
313 }
314 fprintf(out, "{\n\t\t.name = \"%s\",\n", cmd->name.orig);
315 fprintf(out, "\t\t.purpose = ");
316 string_literal(cmd->purpose, out);
317 fprintf(out, ",\n");
318 format_c_text(cmd->description, "description", 3, out);
319 if (cmd->non_opts_name || cmd->synopsis)
320 fprintf(out, "\t\t.non_opts_name = \"%s\",\n",
321 cmd->non_opts_name? cmd->non_opts_name : "");
322 fprintf(out, "\t\t.synopsis = ");
323 format_synopsis(cmd, out, false);
324 format_c_text(cmd->closing, "closing", 3, out);
325 fprintf(out, "\n\t\t.user_data = &lsg_%s_com_%s_user_data,\n",
326 suite.name.sanitized, cmd->name.sanitized);
327 fprintf(out, "\t\t.num_options = %d,\n", cmd->num_options);
328 gen_c_options(cmd, out);
329 fprintf(out, "\t}");
330 }
331
332 static void format_user_data(const struct lsg_command *cmd, FILE *out)
333 {
334 if (!cmd->name.orig)
335 return;
336 fprintf(out, "extern const void *lsg_%s_com_%s_user_data "
337 "__attribute__ ((weak));\n",
338 suite.name.sanitized,
339 cmd->name.sanitized
340 );
341 }
342
343 static void gen_c(const char *outpath, const char *cmdline)
344 {
345 int i;
346
347 FILE *out = fopen(outpath, "w");
348 if (!out) {
349 perror("fopen");
350 exit(EXIT_FAILURE);
351 }
352 gen_license_header(out, cmdline, "/* ", " */");
353 fprintf(out, "#include <stdlib.h>\n");
354 fprintf(out, "#include <inttypes.h>\n");
355 fprintf(out, "#include <lopsub-internal.h>\n");
356 fprintf(out, "#include <lopsub.h>\n");
357 fprintf(out, "__attribute__ ((unused)) "
358 "static const unsigned *abi_version = &%s;\n",
359 LLS_ABI_VERSION_VAR_STRING);
360 fprintf(out, "#if LLS_ABI_VERSION - %d\n", LLS_ABI_VERSION);
361 fprintf(out, "#error: \"ABI version mismatch: header version "
362 "differs from lopsubgen version\"\n");
363 fprintf(out, "#endif\n");
364 for (i = 0; i <= suite.num_subcommands; i++)
365 format_user_data(suite.commands + i, out);
366 fprintf(out, "static const struct lls_suite the_%s_suite = {\n",
367 suite.name.sanitized);
368 fprintf(out, "\t.name = \"%s\",\n", suite.name.orig);
369 if (suite.caption)
370 fprintf(out, "\t.caption = \"%s\",\n", suite.caption);
371 fprintf(out, "\t.num_subcommands = %d,\n", suite.num_subcommands);
372 fprintf(out, "\t.commands = (struct lls_command[]) {\n");
373 for (i = 0; i <= suite.num_subcommands; i++) {
374 struct lsg_command *cmd = suite.commands + i;
375 fprintf(out, "%s", i? ", " : "\t\t");
376 format_command(cmd, out);
377 }
378 fprintf(out, ", {\n\t\t\t.name = NULL\n\t\t}\n");
379 fprintf(out, "\t}\n");
380 fprintf(out, "};\n");
381 fprintf(out, "const struct lls_suite *%s_suite = &the_%s_suite;\n",
382 suite.name.sanitized, suite.name.sanitized);
383 fclose(out);
384 }
385
386 static inline bool has_arg(struct lsg_option *opt)
387 {
388 if (!opt->arg_info)
389 return false;
390 return strcmp(opt->arg_info, "LLS_NO_ARGUMENT");
391 }
392
393 static void gen_header(const char *outpath, const char *cmdline)
394 {
395 int i, j, k;
396 FILE *out = fopen(outpath, "w");
397 char *name;
398
399 if (!out) {
400 perror("fopen");
401 exit(EXIT_FAILURE);
402 }
403 gen_license_header(out, cmdline, "/* ", " */");
404 /* generate command enum */
405 fprintf(out, "extern const struct lls_suite *%s_suite;\n",
406 suite.name.sanitized);
407 fprintf(out, "#define LSG_%s_SUBCOMMANDS \\\n", suite.name.capitalized);
408 for (i = 1; i <= suite.num_subcommands; i++)
409 fprintf(out, "\tLSG_%s_CMD(%s), \\\n", suite.name.capitalized,
410 suite.commands[i].name.sanitized);
411 fprintf(out, "\n");
412 fprintf(out, "#define LSG_%s_COMMANDS \\\n", suite.name.capitalized);
413 name = suite.commands[0].name.sanitized;
414 fprintf(out, "\tLSG_%s_CMD(%s), \\\n", suite.name.capitalized,
415 name? name : "SUPERCOMMAND_UNAVAILABLE");
416 fprintf(out, "\tLSG_%s_SUBCOMMANDS\n", suite.name.capitalized);
417 fprintf(out, "enum lsg_%s_command {\n", suite.name.sanitized);
418 for (i = 0; i <= suite.num_subcommands; i++) {
419 struct lsg_command *cmd = suite.commands + i;
420 char *name = cmd->name.capitalized;
421
422 if (!name)
423 name = "SUPERCOMMAND_UNAVAILABLE";
424 fprintf(out, "\tLSG_%s_CMD_%s,\n", suite.name.capitalized, name);
425 }
426 fprintf(out, "};\n#define LSG_NUM_%s_SUBCOMMANDS %u\n", suite.name.capitalized,
427 suite.num_subcommands);
428 fprintf(out, "#define LSG_%s_AUX_INFOS \\\n", suite.name.capitalized);
429 for (i = 0; i <= suite.num_subcommands; i++) {
430 struct lsg_command *cmd = suite.commands + i;
431 char *ai = cmd->aux_info;
432 if (!ai) {
433 ai = suite.aux_info_default;
434 if (!ai)
435 ai = "0";
436 }
437 fprintf(out, "\t%s_AUX_INFO(%s) /* %s */ \\\n",
438 suite.name.capitalized, ai, cmd->name.orig?
439 cmd->name.orig : "NO_SUPERCOMMAND");
440 }
441 fprintf(out, "\n");
442 /* generate one option enum per command */
443 for (i = 0; i <= suite.num_subcommands; i++) {
444 struct lsg_command *cmd = suite.commands + i;
445 if (!cmd->name.orig)
446 continue;
447 fprintf(out, "enum lsg_%s_%s_option {\n", suite.name.sanitized,
448 cmd->name.sanitized);
449 for (j = 0; j < cmd->num_options; j++) {
450 struct lsg_option *opt = cmd->options + j;
451 char suffix[20] = "";
452 if (opt->ignored)
453 sprintf(suffix, "%d", j);
454 fprintf(out, "\tLSG_%s_%s_OPT_%s%s,\n",
455 suite.name.capitalized,
456 cmd->name.capitalized,
457 opt->name.capitalized,
458 suffix
459 );
460 }
461 fprintf(out, "\tLSG_NUM_%s_%s_OPTIONS\n};\n",
462 suite.name.capitalized, cmd->name.capitalized);
463
464 }
465 /* generate enumeration for options of type enum */
466 for (i = 0; i <= suite.num_subcommands; i++) {
467 struct lsg_command *cmd = suite.commands + i;
468 if (!cmd->name.orig)
469 continue;
470 for (j = 0; j < cmd->num_options; j++) {
471 struct lsg_option *opt = cmd->options + j;
472 if (!opt->values)
473 continue;
474 fprintf(out, "/* cmd %s, opt %s */\n", cmd->name.orig,
475 opt->name.orig);
476 fprintf(out, "enum {");
477 for (k = 0; k < opt->num_values; k++)
478 fprintf(out, "%s, ", opt->value_ids[k]);
479 fprintf(out, "LSG_NUM_%s_%s_%s_VALUES};\n",
480 suite.name.capitalized, cmd->name.capitalized,
481 opt->name.capitalized);
482 }
483 }
484 for (i = 0; i <= suite.num_subcommands; i++) {
485 struct lsg_command *cmd = suite.commands + i;
486 if (!cmd->name.orig)
487 continue;
488 fprintf(out, "#define LSG_%s_%s_SHORT_OPTS ",
489 suite.name.capitalized, cmd->name.capitalized);
490 for (j = 0; j < cmd->num_options; j++) {
491 struct lsg_option *opt = cmd->options + j;
492 if (!opt->short_opt)
493 continue;
494 fprintf(out, "\"-%c%s\"%s", opt->short_opt,
495 has_arg(opt)? "=" : "",
496 j == cmd->num_options - 1? "" : ", ");
497 }
498 fprintf(out, "\n");
499
500 fprintf(out, "#define LSG_%s_%s_LONG_OPTS ",
501 suite.name.capitalized, cmd->name.capitalized);
502 for (j = 0; j < cmd->num_options; j++) {
503 struct lsg_option *opt = cmd->options + j;
504 fprintf(out, "\"--%s%s\"%s", opt->name.orig,
505 has_arg(opt)? "=" : "",
506 j == cmd->num_options - 1? "" : ", ");
507 }
508 fprintf(out, "\n");
509 fprintf(out, "#define LSG_%s_%s_OPTS "
510 "LSG_%s_%s_SHORT_OPTS, LSG_%s_%s_LONG_OPTS\n",
511 suite.name.capitalized, cmd->name.capitalized,
512 suite.name.capitalized, cmd->name.capitalized,
513 suite.name.capitalized, cmd->name.capitalized
514 );
515 }
516 fclose(out);
517 }
518
519 static void check_option(struct lsg_option *opt)
520 {
521 if (!opt->arg_info || !strcmp(opt->arg_info, "no_arg"))
522 return;
523 if (opt->arg_type && strcmp(opt->arg_type, "none"))
524 return;
525 fprintf(stderr, "option '%s': inconsistent arg_type/arg_info\n",
526 opt->name.orig);
527 exit(EXIT_FAILURE);
528 }
529
530 static void sanity_check(void)
531 {
532 int i, j;
533
534 if (suite.num_subcommands == 0 && !suite.commands) {
535 fprintf(stderr, "no (sub)commands defined\n");
536 exit(EXIT_FAILURE);
537 }
538 for (i = 0; i <= suite.num_subcommands; i++) {
539 struct lsg_command *cmd = suite.commands + i;
540 if (!cmd->name.orig)
541 continue;
542 for (j = 0; j < cmd->num_options; j++)
543 check_option(cmd->options + j);
544 }
545 }
546
547 static void run_yylex(void)
548 {
549 int i, j;
550
551 yylex();
552 if (!suite.name.orig)
553 suite.name.orig = strdup("lopsubgen");
554 sanity_check();
555 lsg_init_name(&suite.name);
556 for (i = 0; i <= suite.num_subcommands; i++) {
557 struct lsg_command *cmd = suite.commands + i;
558 if (!cmd->name.orig)
559 continue;
560 lsg_init_name(&cmd->name);
561 for (j = 0; j < cmd->num_options; j++)
562 lsg_init_name(&cmd->options[j].name);
563 }
564 for (i = 0; i < suite.num_sections; i++)
565 lsg_init_name(&suite.sections[i].name);
566 }
567
568 #ifdef STAGE1
569 int main(int argc, char **argv)
570 {
571 run_yylex();
572 gen_c("lopsubgen.lsg.c", NULL);
573 gen_header("lopsubgen.lsg.h", NULL);
574 return EXIT_SUCCESS;
575 }
576
577 #else /* STAGE1 */
578
579 #include "lopsubgen.lsg.h"
580
581 static char *get_output_path(const char *suffix, const char *arg,
582 const struct lls_parse_result *lpr)
583 {
584 size_t len;
585 char *result, *output_dir;
586
587 output_dir = OPT_STRING_VAL(OUTPUT_DIR, lpr);
588
589 if (arg && arg[0] == '/') {
590 result = strdup(arg);
591 assert(result);
592 return result;
593 }
594 if (arg) { /* relative path */
595 len = strlen(output_dir) + strlen(arg) + 1;
596 result = malloc(len + 1);
597 assert(result);
598 sprintf(result, "%s/%s", output_dir, arg);
599 return result;
600 }
601 /* default: suite name plus suffix */
602 len = strlen(output_dir) + 1 /* slash */ + strlen(suite.name.orig)
603 + 1 /* dot */ + 3 /* "lsg" */ + 1 /* dot */ + strlen(suffix);
604 result = malloc(len + 1);
605 assert(result);
606 sprintf(result, "%s/%s.lsg.%s", output_dir, suite.name.orig, suffix);
607 return result;
608 }
609
610 static void gen_man(struct lls_parse_result *lpr, const char *cmdline)
611 {
612 int i;
613 time_t t;
614 struct tm *tmp;
615 FILE *out;
616 char *outpath = get_output_path("man",
617 OPT_STRING_VAL(GEN_MAN, lpr), lpr);
618
619 out = fopen(outpath, "w");
620 free(outpath);
621 if (!out) {
622 perror("fopen");
623 exit(EXIT_FAILURE);
624 }
625 gen_license_header(out, cmdline, ".\\\" ", "");
626 if (suite.commands[0].name.orig) {
627 char date[200];
628 const char *version_string;
629
630 if (!suite.date) {
631 t = time(NULL);
632 tmp = localtime(&t);
633 if (tmp == NULL) {
634 perror("localtime");
635 exit(EXIT_FAILURE);
636 }
637 if (strftime(date, sizeof(date), "%B %Y", tmp) == 0) {
638 fprintf(stderr, "strftime returned 0\n");
639 exit(EXIT_FAILURE);
640 }
641 }
642 if (OPT_GIVEN(VERSION_STRING, lpr))
643 version_string = OPT_STRING_VAL(VERSION_STRING, lpr);
644 else
645 version_string = suite.version_string?
646 suite.version_string : "";
647 fprintf(out, ".TH %s \"%s\" \"%s\" \"%s\" \"%s\"\n",
648 suite.title? suite.title : suite.commands[0].name.orig,
649 suite.mansect? suite.mansect : "1",
650 suite.date? suite.date : date,
651 version_string,
652 suite.manual_title? suite.manual_title : "User commands"
653 );
654 }
655 for (i = 0; i <= suite.num_subcommands; i++) {
656 struct lsg_command *cmd = suite.commands + i;
657 int opt_num;
658 if (!cmd->name.orig)
659 continue;
660 if (i == 0) {
661 fprintf(out, ".SH NAME\n");
662 fprintf(out, ".B\n%s \\- ", cmd->name.orig);
663 format_man_text(out, false, cmd->purpose);
664 } else {
665 if (i == 1 && suite.caption) {
666 char *caption = strdup(suite.caption);
667 inplace_toupper(caption);
668 fprintf(out, ".SH %s\n.P\n", caption);
669 free(caption);
670 }
671 if (i == 1 && suite.introduction)
672 format_man_text(out, true, suite.introduction);
673 fprintf(out, ".SS \n%s \\- ", cmd->name.orig);
674 format_man_text(out, false, cmd->purpose);
675 }
676 fprintf(out, "\n.P\n");
677 if (i == 0)
678 fprintf(out, ".SH SYNOPSIS\n");
679 else
680 fprintf(out, "Usage: \n");
681 fprintf(out, ".B %s\n", cmd->name.orig);
682 format_synopsis(cmd, out, true);
683 fprintf(out, "\n.P\n");
684 if (cmd->description) {
685 if (i == 0)
686 fprintf(out, ".SH DESCRIPTION\n");
687 format_man_text(out, true, cmd->description);
688 }
689 if (cmd->num_options > 0)
690 if (i == 0)
691 fprintf(out, ".SH OPTIONS\n");
692 for (opt_num = 0; opt_num < cmd->num_options; opt_num++) {
693 struct lsg_option *opt = cmd->options + opt_num;
694
695 if (opt->ignored) {
696 fprintf(out, ".SS ");
697 format_man_text(out, false, opt->summary);
698 fprintf(out, "\n");
699 if (opt->help)
700 format_man_text(out, true, opt->help);
701 continue;
702
703 }
704 fprintf(out, ".TP\n");
705 if (opt->short_opt != '\0')
706 fprintf(out, "\\fB\\-%c\\fR, ", opt->short_opt);
707 fprintf(out, "\\fB\\-\\-%s\\fR", opt->name.orig);
708 format_option_arg(opt, out, true /* man_format */);
709 fprintf(out, "\n");
710 format_man_text(out, true, opt->summary);
711 fprintf(out, "\n");
712 if (opt->values) {
713 unsigned n, dflt;
714 fprintf(out, ".IP\n");
715 fprintf(out, "values:\n");
716 dflt = opt->default_val?
717 atoi(opt->default_val) : 0;
718 for (n = 0; n < opt->num_values; n++) {
719 if (n == dflt)
720 fprintf(out, ".B ");
721 fprintf(out, "%s%s\n", opt->values[n],
722 n == opt->num_values - 1?
723 "" : ",");
724 }
725 } else if (opt->default_val) {
726 fprintf(out, ".IP\n");
727 fprintf(out, "default: ");
728 format_man_text(out, false, opt->default_val);
729 fprintf(out, "\n.P\n");
730 }
731 if (opt->help) {
732 fprintf(out, ".IP\n");
733 format_man_text(out, true, opt->help);
734 }
735 }
736 fprintf(out, ".PP\n");
737 if (cmd->aux_info) {
738 char *pfx = suite.aux_info_prefix;
739 fprintf(out, ".RS\n");
740 if (pfx)
741 fprintf(out, "%s ", pfx);
742 fprintf(out, "%s\n.PP\n", cmd->aux_info);
743 fprintf(out, ".RE\n");
744 }
745 if (cmd->closing)
746 format_man_text(out, true, cmd->closing);
747 }
748 if (suite.conclusion)
749 format_man_text(out, true, suite.conclusion);
750 for (i = 0; i < suite.num_sections; i++) {
751 struct lsg_section *sec = suite.sections + i;
752 fprintf(out, "\n.P\n.SH \"%s\"\n", sec->name.capitalized);
753 fprintf(out, "%s\n.P\n", sec->text);
754 }
755 fclose(out);
756 }
757
758 int main(int argc, char **argv)
759 {
760 const struct lls_command *cmd = lls_cmd(0, lopsubgen_suite);
761 char *errctx, *outpath, *cmdline;
762 struct lls_parse_result *lpr;
763 int i, ret;
764
765 /* Make a copy of the command line because lls_parse() permutes argv[] */
766 for (i = 0, ret = 0; argv[i]; i++)
767 ret += strlen(argv[i]) + 1;
768 cmdline = malloc(ret + 1);
769 assert(cmdline);
770 for (i = 0, ret = 0; argv[i]; i++)
771 ret += sprintf(cmdline + ret, "%s ", argv[i]);
772 cmdline[ret - 1] = '\0';
773
774 ret = lls_parse(argc, argv, cmd, &lpr, &errctx);
775 if (ret < 0) {
776 if (errctx)
777 fprintf(stderr, "%s\n", errctx);
778 fprintf(stderr, "%s\n", lls_strerror(-ret));
779 return EXIT_FAILURE;
780 }
781 if (OPT_GIVEN(VERSION, lpr)) {
782 printf("lopsubgen-%s\n", lls_version());
783 return EXIT_SUCCESS;
784 }
785 if (OPT_GIVEN(HELP, lpr)) {
786 char *help;
787 if (OPT_GIVEN(HELP, lpr) > 1)
788 help = lls_long_help(cmd);
789 else
790 help = lls_short_help(cmd);
791 printf("%s", help);
792 free(help);
793 return EXIT_SUCCESS;
794 }
795 run_yylex();
796 if (OPT_GIVEN(GEN_C, lpr)) {
797 outpath = get_output_path("c",
798 OPT_STRING_VAL(GEN_C, lpr), lpr);
799 gen_c(outpath, cmdline);
800 free(outpath);
801 }
802 if (OPT_GIVEN(GEN_HEADER, lpr)) {
803 outpath = get_output_path("h",
804 OPT_STRING_VAL(GEN_HEADER, lpr), lpr);
805 gen_header(outpath, cmdline);
806 free(outpath);
807 }
808 if (OPT_GIVEN(GEN_MAN, lpr))
809 gen_man(lpr, cmdline);
810 return EXIT_SUCCESS;
811 }
812 #endif /* STAGE1 */