1%{
2/*
3 * Copyright (c) 2004-2006 Kungliga Tekniska Högskolan
4 * (Royal Institute of Technology, Stockholm, Sweden).
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 *
11 * 1. Redistributions of source code must retain the above copyright
12 *    notice, this list of conditions and the following disclaimer.
13 *
14 * 2. Redistributions in binary form must reproduce the above copyright
15 *    notice, this list of conditions and the following disclaimer in the
16 *    documentation and/or other materials provided with the distribution.
17 *
18 * 3. Neither the name of the Institute nor the names of its contributors
19 *    may be used to endorse or promote products derived from this software
20 *    without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 */
34
35#include <config.h>
36
37#include <stdio.h>
38#include <stdlib.h>
39#include <err.h>
40#include <ctype.h>
41#include <limits.h>
42#include <getarg.h>
43#include <vers.h>
44#include <roken.h>
45
46#include "slc.h"
47extern FILE *yyin;
48extern struct assignment *assignment;
49
50/* Declarations for Bison:
51 */
52#define YYMALLOC        malloc
53#define YYFREE          free
54
55%}
56
57%union {
58	char *string;
59	struct assignment *assignment;
60}
61
62%token <string> LITERAL
63%token <string> STRING
64%type <assignment> assignment assignments
65
66%start start
67
68%%
69
70start		: assignments
71		{
72			assignment = $1;
73		}
74		;
75
76assignments	: assignment assignments
77		{
78			$1->next = $2;
79			$$ = $1;
80		}
81		| assignment
82		;
83
84assignment	: LITERAL '=' STRING
85		{
86			$$ = malloc(sizeof(*$$));
87			$$->name = $1;
88			$$->type = a_value;
89			$$->lineno = lineno;
90			$$->u.value = $3;
91			$$->next = NULL;
92		}
93		| LITERAL '=' '{' assignments '}'
94		{
95			$$ = malloc(sizeof(*$$));
96			$$->name = $1;
97			$$->type = a_assignment;
98			$$->lineno = lineno;
99			$$->u.assignment = $4;
100			$$->next = NULL;
101		}
102		;
103
104%%
105char *filename;
106FILE *cfile, *hfile;
107int error_flag;
108struct assignment *assignment;
109
110static void ex(struct assignment *a, const char *fmt, ...)
111    __attribute__((__format__(printf, 2, 3)));
112static void cprint(int level, const char *fmt, ...)
113    __attribute__((__format__(printf, 2, 3)));
114static void hprint(int level, const char *fmt, ...)
115    __attribute__((__format__(printf, 2, 3)));
116
117
118
119static void
120ex(struct assignment *a, const char *fmt, ...)
121{
122    va_list ap;
123    fprintf(stderr, "%s:%d: ", a->name, a->lineno);
124    va_start(ap, fmt);
125    vfprintf(stderr, fmt, ap);
126    va_end(ap);
127    fprintf(stderr, "\n");
128}
129
130
131
132static int
133check_option(struct assignment *as)
134{
135    struct assignment *a;
136    int seen_long = 0;
137    int seen_name = 0;
138    int seen_short = 0;
139    int seen_type = 0;
140    int seen_argument = 0;
141    int seen_help = 0;
142    int seen_default = 0;
143    int ret = 0;
144
145    for(a = as; a != NULL; a = a->next) {
146	if(strcmp(a->name, "long") == 0)
147	    seen_long++;
148	else if(strcmp(a->name, "short") == 0)
149	    seen_short++;
150	else if(strcmp(a->name, "name") == 0)
151	    seen_name++;
152	else if(strcmp(a->name, "type") == 0)
153	    seen_type++;
154	else if(strcmp(a->name, "argument") == 0)
155	    seen_argument++;
156	else if(strcmp(a->name, "help") == 0)
157	    seen_help++;
158	else if(strcmp(a->name, "default") == 0)
159	    seen_default++;
160	else {
161	    ex(a, "unknown name %s", a->name);
162	    ret++;
163	}
164    }
165    if(seen_long == 0 && seen_short == 0) {
166	ex(as, "neither long nor short option");
167	ret++;
168    }
169    if (seen_long == 0 && seen_name == 0) {
170	ex(as, "either of long or name option must be used");
171	ret++;
172    }
173    if(seen_long > 1) {
174	ex(as, "multiple long options");
175	ret++;
176    }
177    if(seen_short > 1) {
178	ex(as, "multiple short options");
179	ret++;
180    }
181    if(seen_type > 1) {
182	ex(as, "multiple types");
183	ret++;
184    }
185    if(seen_argument > 1) {
186	ex(as, "multiple arguments");
187	ret++;
188    }
189    if(seen_help > 1) {
190	ex(as, "multiple help strings");
191	ret++;
192    }
193    if(seen_default > 1) {
194	ex(as, "multiple default values");
195	ret++;
196    }
197    return ret;
198}
199
200static int
201check_command(struct assignment *as)
202{
203	struct assignment *a;
204	int seen_name = 0;
205	int seen_function = 0;
206	int seen_help = 0;
207	int seen_argument = 0;
208	int seen_minargs = 0;
209	int seen_maxargs = 0;
210	int ret = 0;
211	for(a = as; a != NULL; a = a->next) {
212		if(strcmp(a->name, "name") == 0)
213			seen_name++;
214		else if(strcmp(a->name, "function") == 0) {
215			seen_function++;
216		} else if(strcmp(a->name, "option") == 0)
217			ret += check_option(a->u.assignment);
218		else if(strcmp(a->name, "help") == 0) {
219			seen_help++;
220		} else if(strcmp(a->name, "argument") == 0) {
221			seen_argument++;
222		} else if(strcmp(a->name, "min_args") == 0) {
223			seen_minargs++;
224		} else if(strcmp(a->name, "max_args") == 0) {
225			seen_maxargs++;
226		} else {
227			ex(a, "unknown name: %s", a->name);
228			ret++;
229		}
230	}
231	if(seen_name == 0) {
232		ex(as, "no command name");
233		ret++;
234	}
235	if(seen_function > 1) {
236		ex(as, "multiple function names");
237		ret++;
238	}
239	if(seen_help > 1) {
240		ex(as, "multiple help strings");
241		ret++;
242	}
243	if(seen_argument > 1) {
244		ex(as, "multiple argument strings");
245		ret++;
246	}
247	if(seen_minargs > 1) {
248		ex(as, "multiple min_args strings");
249		ret++;
250	}
251	if(seen_maxargs > 1) {
252		ex(as, "multiple max_args strings");
253		ret++;
254	}
255
256	return ret;
257}
258
259static int
260check(struct assignment *as)
261{
262    struct assignment *a;
263    int ret = 0;
264    for(a = as; a != NULL; a = a->next) {
265	if(strcmp(a->name, "command")) {
266	    fprintf(stderr, "unknown type %s line %d\n", a->name, a->lineno);
267	    ret++;
268	    continue;
269	}
270	if(a->type != a_assignment) {
271	    fprintf(stderr, "bad command definition %s line %d\n", a->name, a->lineno);
272	    ret++;
273	    continue;
274	}
275	ret += check_command(a->u.assignment);
276    }
277    return ret;
278}
279
280static struct assignment *
281find_next(struct assignment *as, const char *name)
282{
283    for(as = as->next; as != NULL; as = as->next) {
284	if(strcmp(as->name, name) == 0)
285	    return as;
286    }
287    return NULL;
288}
289
290static struct assignment *
291find(struct assignment *as, const char *name)
292{
293    for(; as != NULL; as = as->next) {
294	if(strcmp(as->name, name) == 0)
295	    return as;
296    }
297    return NULL;
298}
299
300static void
301space(FILE *f, int level)
302{
303    fprintf(f, "%*.*s", level * 4, level * 4, " ");
304}
305
306static void
307cprint(int level, const char *fmt, ...)
308{
309    va_list ap;
310    va_start(ap, fmt);
311    space(cfile, level);
312    vfprintf(cfile, fmt, ap);
313    va_end(ap);
314}
315
316static void
317hprint(int level, const char *fmt, ...)
318{
319    va_list ap;
320    va_start(ap, fmt);
321    space(hfile, level);
322    vfprintf(hfile, fmt, ap);
323    va_end(ap);
324}
325
326static void gen_name(char *str);
327
328static void
329gen_command(struct assignment *as)
330{
331    struct assignment *a, *b;
332    char *f;
333    a = find(as, "name");
334    f = strdup(a->u.value);
335    gen_name(f);
336    cprint(1, "    { ");
337    fprintf(cfile, "\"%s\", ", a->u.value);
338    fprintf(cfile, "%s_wrap, ", f);
339    b = find(as, "argument");
340    if(b)
341	fprintf(cfile, "\"%s %s\", ", a->u.value, b->u.value);
342    else
343	fprintf(cfile, "\"%s\", ", a->u.value);
344    b = find(as, "help");
345    if(b)
346	fprintf(cfile, "\"%s\"", b->u.value);
347    else
348	fprintf(cfile, "NULL");
349    fprintf(cfile, " },\n");
350    for(a = a->next; a != NULL; a = a->next)
351	if(strcmp(a->name, "name") == 0)
352	    cprint(1, "    { \"%s\" },\n", a->u.value);
353    cprint(0, "\n");
354}
355
356static void
357gen_name(char *str)
358{
359    char *p;
360    for(p = str; *p != '\0'; p++)
361	if(!isalnum((unsigned char)*p))
362	    *p = '_';
363}
364
365static char *
366make_name(struct assignment *as)
367{
368    struct assignment *lopt;
369    struct assignment *type;
370    char *s;
371
372    lopt = find(as, "long");
373    if(lopt == NULL)
374	lopt = find(as, "name");
375    if(lopt == NULL)
376	return NULL;
377
378    type = find(as, "type");
379    if(strcmp(type->u.value, "-flag") == 0)
380	asprintf(&s, "%s_flag", lopt->u.value);
381    else
382	asprintf(&s, "%s_%s", lopt->u.value, type->u.value);
383    gen_name(s);
384    return s;
385}
386
387
388static void defval_int(const char *name, struct assignment *defval)
389{
390    if(defval != NULL)
391	cprint(1, "opt.%s = %s;\n", name, defval->u.value);
392    else
393	cprint(1, "opt.%s = 0;\n", name);
394}
395static void defval_neg_flag(const char *name, struct assignment *defval)
396{
397    if(defval != NULL)
398	cprint(1, "opt.%s = %s;\n", name, defval->u.value);
399    else
400	cprint(1, "opt.%s = 1;\n", name);
401}
402static void defval_string(const char *name, struct assignment *defval)
403{
404    if(defval != NULL)
405	cprint(1, "opt.%s = (char *)(unsigned long)\"%s\";\n", name, defval->u.value);
406    else
407	cprint(1, "opt.%s = NULL;\n", name);
408}
409static void defval_strings(const char *name, struct assignment *defval)
410{
411    cprint(1, "opt.%s.num_strings = 0;\n", name);
412    cprint(1, "opt.%s.strings = NULL;\n", name);
413}
414
415static void free_strings(const char *name)
416{
417    cprint(1, "free_getarg_strings (&opt.%s);\n", name);
418}
419
420struct type_handler {
421    const char *typename;
422    const char *c_type;
423    const char *getarg_type;
424    void (*defval)(const char*, struct assignment*);
425    void (*free)(const char*);
426} type_handlers[] = {
427	{ "integer",
428	  "int",
429	  "arg_integer",
430	  defval_int,
431	  NULL
432	},
433	{ "string",
434	  "char*",
435	  "arg_string",
436	  defval_string,
437	  NULL
438	},
439	{ "strings",
440	  "struct getarg_strings",
441	  "arg_strings",
442	  defval_strings,
443	  free_strings
444	},
445	{ "flag",
446	  "int",
447	  "arg_flag",
448	  defval_int,
449	  NULL
450	},
451	{ "-flag",
452	  "int",
453	  "arg_negative_flag",
454	  defval_neg_flag,
455	  NULL
456	},
457	{ NULL }
458};
459
460static struct type_handler *find_handler(struct assignment *type)
461{
462    struct type_handler *th;
463    for(th = type_handlers; th->typename != NULL; th++)
464	if(strcmp(type->u.value, th->typename) == 0)
465	    return th;
466    ex(type, "unknown type \"%s\"", type->u.value);
467    exit(1);
468}
469
470static void
471gen_options(struct assignment *opt1, const char *name)
472{
473    struct assignment *tmp;
474
475    hprint(0, "struct %s_options {\n", name);
476
477    for(tmp = opt1;
478	tmp != NULL;
479	tmp = find_next(tmp, "option")) {
480	struct assignment *type;
481	struct type_handler *th;
482	char *s;
483
484	s = make_name(tmp->u.assignment);
485	type = find(tmp->u.assignment, "type");
486	th = find_handler(type);
487	hprint(1, "%s %s;\n", th->c_type, s);
488	free(s);
489    }
490    hprint(0, "};\n");
491}
492
493static void
494gen_wrapper(struct assignment *as)
495{
496    struct assignment *name;
497    struct assignment *arg;
498    struct assignment *opt1;
499    struct assignment *function;
500    struct assignment *tmp;
501    char *n, *f;
502    int nargs = 0;
503    int narguments = 0;
504
505    name = find(as, "name");
506    if (name == NULL) {
507        ex(as, "no name");
508        exit(1);
509    }
510    n = strdup(name->u.value);
511    gen_name(n);
512    arg = find(as, "argument");
513    if (arg)
514        narguments++;
515    opt1 = find(as, "option");
516    function = find(as, "function");
517    if(function)
518	f = function->u.value;
519    else
520	f = n;
521
522
523    if(opt1 != NULL) {
524	gen_options(opt1, n);
525	hprint(0, "int %s(struct %s_options*, int, char **);\n", f, n);
526    } else {
527	hprint(0, "int %s(void*, int, char **);\n", f);
528    }
529
530    fprintf(cfile, "static int\n");
531    fprintf(cfile, "%s_wrap(int argc, char **argv)\n", n);
532    fprintf(cfile, "{\n");
533    if(opt1 != NULL)
534	cprint(1, "struct %s_options opt;\n", n);
535    cprint(1, "int ret;\n");
536    cprint(1, "int optidx = 0;\n");
537    cprint(1, "struct getargs args[] = {\n");
538    for(tmp = find(as, "option");
539	tmp != NULL;
540	tmp = find_next(tmp, "option")) {
541	struct assignment *type = find(tmp->u.assignment, "type");
542	struct assignment *lopt = find(tmp->u.assignment, "long");
543	struct assignment *sopt = find(tmp->u.assignment, "short");
544	struct assignment *aarg = find(tmp->u.assignment, "argument");
545	struct assignment *help = find(tmp->u.assignment, "help");
546
547	struct type_handler *th;
548
549	cprint(2, "{ ");
550	if(lopt)
551	    fprintf(cfile, "\"%s\", ", lopt->u.value);
552	else
553	    fprintf(cfile, "NULL, ");
554	if(sopt)
555	    fprintf(cfile, "'%c', ", *sopt->u.value);
556	else
557	    fprintf(cfile, "0, ");
558	th = find_handler(type);
559	fprintf(cfile, "%s, ", th->getarg_type);
560	fprintf(cfile, "NULL, ");
561	if(help)
562	    fprintf(cfile, "\"%s\", ", help->u.value);
563	else
564	    fprintf(cfile, "NULL, ");
565	if(aarg) {
566	    fprintf(cfile, "\"%s\"", aarg->u.value);
567            narguments++;
568	} else
569	    fprintf(cfile, "NULL");
570	fprintf(cfile, " },\n");
571    }
572    cprint(2, "{ \"help\", 'h', arg_flag, NULL, NULL, NULL }\n");
573    cprint(1, "};\n");
574    cprint(1, "int help_flag = 0;\n");
575
576    for(tmp = find(as, "option");
577	tmp != NULL;
578	tmp = find_next(tmp, "option")) {
579	char *s;
580	struct assignment *type = find(tmp->u.assignment, "type");
581
582	struct assignment *defval = find(tmp->u.assignment, "default");
583
584	struct type_handler *th;
585
586	s = make_name(tmp->u.assignment);
587	th = find_handler(type);
588	(*th->defval)(s, defval);
589	free(s);
590    }
591
592    for(tmp = find(as, "option");
593	tmp != NULL;
594	tmp = find_next(tmp, "option")) {
595	char *s;
596	s = make_name(tmp->u.assignment);
597	cprint(1, "args[%d].value = &opt.%s;\n", nargs++, s);
598	free(s);
599    }
600    cprint(1, "args[%d].value = &help_flag;\n", nargs++);
601    cprint(1, "if(getarg(args, %d, argc, argv, &optidx))\n", nargs);
602    cprint(2, "goto usage;\n");
603
604    {
605	int min_args = -1;
606	int max_args = -1;
607	char *end;
608	if(narguments == 0) {
609	    if (find(as, "min_args")) {
610		ex(as, "seen min_args without argument parameter");
611		exit(1);
612	    }
613	    if (find(as, "max_args")) {
614		ex(as, "seen min_args without argument");
615		exit(1);
616	    }
617	    max_args = 0;
618	} else {
619	    if((tmp = find(as, "min_args")) != NULL) {
620		min_args = strtol(tmp->u.value, &end, 0);
621		if(*end != '\0') {
622		    ex(tmp, "min_args is not numeric");
623		    exit(1);
624		}
625		if(min_args < 0) {
626		    ex(tmp, "min_args must be non-negative");
627		    exit(1);
628		}
629	    }
630	    if((tmp = find(as, "max_args")) != NULL) {
631		max_args = strtol(tmp->u.value, &end, 0);
632		if(*end != '\0') {
633		    ex(tmp, "max_args is not numeric");
634		    exit(1);
635		}
636		if(max_args < 0) {
637		    ex(tmp, "max_args must be non-negative");
638		    exit(1);
639		}
640	    }
641	}
642	if(min_args != -1 || max_args != -1) {
643	    if(min_args == max_args) {
644		cprint(1, "if(argc - optidx != %d) {\n",
645		       min_args);
646		cprint(2, "fprintf(stderr, \"Need exactly %u parameters (%%u given).\\n\\n\", argc - optidx);\n", min_args);
647		cprint(2, "goto usage;\n");
648		cprint(1, "}\n");
649	    } else {
650		if(max_args != -1) {
651		    cprint(1, "if(argc - optidx > %d) {\n", max_args);
652		    cprint(2, "fprintf(stderr, \"Arguments given (%%u) are more than expected (%u).\\n\\n\", argc - optidx);\n", max_args);
653		    cprint(2, "goto usage;\n");
654		    cprint(1, "}\n");
655		}
656		if(min_args != -1) {
657		    cprint(1, "if(argc - optidx < %d) {\n", min_args);
658		    cprint(2, "fprintf(stderr, \"Arguments given (%%u) are less than expected (%u).\\n\\n\", argc - optidx);\n", min_args);
659		    cprint(2, "goto usage;\n");
660		    cprint(1, "}\n");
661		}
662	    }
663	}
664    }
665
666    cprint(1, "if(help_flag)\n");
667    cprint(2, "goto usage;\n");
668
669    cprint(1, "ret = %s(%s, argc - optidx, argv + optidx);\n",
670	   f, opt1 ? "&opt": "NULL");
671
672    /* free allocated data */
673    for(tmp = find(as, "option");
674	tmp != NULL;
675	tmp = find_next(tmp, "option")) {
676	char *s;
677	struct assignment *type = find(tmp->u.assignment, "type");
678	struct type_handler *th;
679	th = find_handler(type);
680	if(th->free == NULL)
681	    continue;
682	s = make_name(tmp->u.assignment);
683	(*th->free)(s);
684	free(s);
685    }
686    cprint(1, "return ret;\n");
687
688    cprint(0, "usage:\n");
689    cprint(1, "arg_printusage (args, %d, \"%s\", \"%s\");\n", nargs,
690	   name->u.value, arg ? arg->u.value : "");
691    /* free allocated data */
692    for(tmp = find(as, "option");
693	tmp != NULL;
694	tmp = find_next(tmp, "option")) {
695	char *s;
696	struct assignment *type = find(tmp->u.assignment, "type");
697	struct type_handler *th;
698	th = find_handler(type);
699	if(th->free == NULL)
700	    continue;
701	s = make_name(tmp->u.assignment);
702	(*th->free)(s);
703	free(s);
704    }
705    cprint(1, "return 0;\n");
706    cprint(0, "}\n");
707    cprint(0, "\n");
708}
709
710char cname[PATH_MAX];
711char hname[PATH_MAX];
712
713static void
714gen(struct assignment *as)
715{
716    struct assignment *a;
717    cprint(0, "#include <stdio.h>\n");
718    cprint(0, "#include <getarg.h>\n");
719    cprint(0, "#include <sl.h>\n");
720    cprint(0, "#include \"%s\"\n\n", hname);
721
722    hprint(0, "#include <stdio.h>\n");
723    hprint(0, "#include <sl.h>\n");
724    hprint(0, "\n");
725
726
727    for(a = as; a != NULL; a = a->next)
728	gen_wrapper(a->u.assignment);
729
730    cprint(0, "SL_cmd commands[] = {\n");
731    for(a = as; a != NULL; a = a->next)
732	gen_command(a->u.assignment);
733    cprint(1, "{ NULL }\n");
734    cprint(0, "};\n");
735
736    hprint(0, "extern SL_cmd commands[];\n");
737}
738
739int version_flag;
740int help_flag;
741struct getargs args[] = {
742    { "version", 0, arg_flag, &version_flag },
743    { "help", 0, arg_flag, &help_flag }
744};
745int num_args = sizeof(args) / sizeof(args[0]);
746
747static void
748usage(int code)
749{
750    arg_printusage(args, num_args, NULL, "command-table");
751    exit(code);
752}
753
754int
755main(int argc, char **argv)
756{
757    char *p;
758
759    int optidx = 0;
760
761    setprogname(argv[0]);
762    if(getarg(args, num_args, argc, argv, &optidx))
763	usage(1);
764    if(help_flag)
765	usage(0);
766    if(version_flag) {
767	print_version(NULL);
768	exit(0);
769    }
770
771    if(argc == optidx)
772	usage(1);
773
774    filename = argv[optidx];
775    yyin = fopen(filename, "r");
776    if(yyin == NULL)
777	err(1, "%s", filename);
778    p = strrchr(filename, '/');
779    if(p)
780	strlcpy(cname, p + 1, sizeof(cname));
781    else
782	strlcpy(cname, filename, sizeof(cname));
783    p = strrchr(cname, '.');
784    if(p)
785	*p = '\0';
786    strlcpy(hname, cname, sizeof(hname));
787    strlcat(cname, ".c", sizeof(cname));
788    strlcat(hname, ".h", sizeof(hname));
789    yyparse();
790    if(error_flag)
791	exit(1);
792    if(check(assignment) == 0) {
793	cfile = fopen(cname, "w");
794	if(cfile == NULL)
795	  err(1, "%s", cname);
796	hfile = fopen(hname, "w");
797	if(hfile == NULL)
798	  err(1, "%s", hname);
799	gen(assignment);
800	fclose(cfile);
801	fclose(hfile);
802    }
803    fclose(yyin);
804    return 0;
805}
806