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