diff --git a/lib/sl/Makefile.am b/lib/sl/Makefile.am index 691dcbe73..ee4eb1fa8 100644 --- a/lib/sl/Makefile.am +++ b/lib/sl/Makefile.am @@ -25,10 +25,13 @@ libss_la_SOURCES = $(libsl_la_SOURCES) ss.c ss.h # install these? bin_PROGRAMS = mk_cmds +noinst_PROGRAMS = slc mk_cmds_SOURCES = make_cmds.c make_cmds.h parse.y lex.l mk_cmds_LDADD = libsl.la $(LDADD) +slc_SOURCES = slc-gram.y slc-lex.l slc.h + ssincludedir = $(includedir)/ss ssinclude_HEADERS = ss.h diff --git a/lib/sl/slc-gram.y b/lib/sl/slc-gram.y new file mode 100644 index 000000000..3eb8bc7f5 --- /dev/null +++ b/lib/sl/slc-gram.y @@ -0,0 +1,597 @@ +%{ +/* + * Copyright (c) 2004 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +/* $Id$ */ + +#include +#include +#include +#include +#include + +#include "slc.h" +extern FILE *yyin; +extern struct assignment *a; +%} + +%union { + char *string; + struct assignment *assignment; +} + +%token LITERAL +%token STRING +%type assignment assignments + +%start start + +%% + +start : assignments + { + a = $1; + } + ; + +assignments : assignment assignments + { + $1->next = $2; + $$ = $1; + } + | assignment + ; + +assignment : LITERAL '=' STRING + { + $$ = malloc(sizeof(*$$)); + $$->name = $1; + $$->type = a_value; + $$->lineno = lineno; + $$->u.value = $3; + $$->next = NULL; + } + | LITERAL '=' '{' assignments '}' + { + $$ = malloc(sizeof(*$$)); + $$->name = $1; + $$->type = a_assignment; + $$->lineno = lineno; + $$->u.assignment = $4; + $$->next = NULL; + } + ; + +%% +char *filename; +FILE *cfile, *hfile; +int error_flag; +struct assignment *a; + + +static void +ex(struct assignment *a, const char *msg) +{ + fprintf(stderr, "%s:%d: %s\n", a->name, a->lineno, msg); +} + + + +static int +check_option(struct assignment *as) +{ + struct assignment *a; + int seen_long = 0; + int seen_short = 0; + int seen_type = 0; + int seen_argument = 0; + int seen_help = 0; + int seen_default = 0; + int ret = 0; + + for(a = as; a != NULL; a = a->next) { + if(strcmp(a->name, "long") == 0) + seen_long++; + else if(strcmp(a->name, "short") == 0) + seen_short++; + else if(strcmp(a->name, "type") == 0) + seen_type++; + else if(strcmp(a->name, "argument") == 0) + seen_argument++; + else if(strcmp(a->name, "help") == 0) + seen_help++; + else if(strcmp(a->name, "default") == 0) + seen_default++; + else { + ex(a, "unknown name"); + ret++; + } + } + if(seen_long == 0 && seen_short == 0) { + ex(as, "neither long nor short option"); + ret++; + } + if(seen_long > 1) { + ex(as, "multiple long options"); + ret++; + } + if(seen_short > 1) { + ex(as, "multiple short options"); + ret++; + } + if(seen_type > 1) { + ex(as, "multiple types"); + ret++; + } + if(seen_argument > 1) { + ex(as, "multiple arguments"); + ret++; + } + if(seen_help > 1) { + ex(as, "multiple help strings"); + ret++; + } + if(seen_default > 1) { + ex(as, "multiple default values"); + ret++; + } + return ret; +} + +static int +check_command(struct assignment *as) +{ + struct assignment *a; + int seen_name = 0; + int seen_function = 0; + int seen_help = 0; + int seen_argument = 0; + int ret = 0; + for(a = as; a != NULL; a = a->next) { + if(strcmp(a->name, "name") == 0) + seen_name++; + else if(strcmp(a->name, "function") == 0) { + seen_function++; + } else if(strcmp(a->name, "option") == 0) + ret += check_option(a->u.assignment); + else if(strcmp(a->name, "help") == 0) { + seen_help++; + } else if(strcmp(a->name, "argument") == 0) { + seen_argument++; + } else { + ex(a, "unknown name"); + ret++; + } + } + if(seen_name == 0) { + ex(as, "no command name"); + ret++; + } + if(seen_function > 1) { + ex(as, "multiple function names"); + ret++; + } + if(seen_help > 1) { + ex(as, "multiple help strings"); + ret++; + } + if(seen_argument > 1) { + ex(as, "multiple argument strings"); + ret++; + } + + return ret; +} + +static int +check(struct assignment *as) +{ + struct assignment *a; + int ret = 0; + for(a = as; a != NULL; a = a->next) { + if(strcmp(a->name, "command")) { + fprintf(stderr, "unknown type %s line %d\n", a->name, a->lineno); + ret++; + continue; + } + if(a->type != a_assignment) { + fprintf(stderr, "bad command definition %s line %d\n", a->name, a->lineno); + ret++; + continue; + } + ret += check_command(a->u.assignment); + } + return ret; +} + +static struct assignment * +find_next(struct assignment *as, const char *name) +{ + for(as = as->next; as != NULL; as = as->next) { + if(strcmp(as->name, name) == 0) + return as; + } + return NULL; +} + +static struct assignment * +find(struct assignment *as, const char *name) +{ + for(; as != NULL; as = as->next) { + if(strcmp(as->name, name) == 0) + return as; + } + return NULL; +} + +static void +space(FILE *f, int level) +{ + fprintf(f, "%*.*s", level * 4, level * 4, " "); +} + +static void +cprint(int level, const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + space(cfile, level); + vfprintf(cfile, fmt, ap); + va_end(ap); +} + +static void +hprint(int level, const char *fmt, ...) +{ + va_list ap; + va_start(ap, fmt); + space(hfile, level); + vfprintf(hfile, fmt, ap); + va_end(ap); +} + +static void gen_name(char *str); + +static void +gen_command(struct assignment *as) +{ + struct assignment *a, *b; + char *f; + a = find(as, "name"); + f = strdup(a->u.value); + gen_name(f); + cprint(1, " { "); + fprintf(cfile, "\"%s\", ", a->u.value); + fprintf(cfile, "%s_wrap, ", f); + b = find(as, "argument"); + if(b) + fprintf(cfile, "\"%s %s\", ", a->u.value, b->u.value); + else + fprintf(cfile, "\"%s\", ", a->u.value); + b = find(as, "help"); + if(b) + fprintf(cfile, "\"%s\"", b->u.value); + else + fprintf(cfile, "NULL"); + fprintf(cfile, " },\n"); + for(a = a->next; a != NULL; a = a->next) + if(strcmp(a->name, "name") == 0) + cprint(1, " { \"%s\" },\n", a->u.value); + cprint(0, "\n"); +} + +static void +gen_name(char *str) +{ + char *p; + for(p = str; *p != '\0'; p++) + if(!isalnum(*p)) + *p = '_'; +} + +static char * +make_name(struct assignment *as) +{ + struct assignment *lopt; + struct assignment *type; + char *s; + + lopt = find(as, "long"); + if(lopt == NULL) + lopt = find(as, "name"); + if(lopt == NULL) + return NULL; + + type = find(as, "type"); + asprintf(&s, "%s_%s", lopt->u.value, type->u.value); + gen_name(s); + return s; +} + +static void +gen_options(struct assignment *opt1, const char *name) +{ + struct assignment *tmp; + + hprint(0, "struct %s_options {\n", name); + + for(tmp = opt1; + tmp != NULL; + tmp = find_next(tmp, "option")) { + struct assignment *type; + char *s; + + s = make_name(tmp->u.assignment); + type = find(tmp->u.assignment, "type"); + if(strcmp(type->u.value, "string") == 0) + hprint(1, "char *%s;\n", s); + else if(strcmp(type->u.value, "integer") == 0) + hprint(1, "int %s;\n", s); + else if(strcmp(type->u.value, "flag") == 0) + hprint(1, "int %s;\n", s); + else + abort(); + free(s); + } + hprint(0, "};\n"); +} + +static void +gen_wrapper(struct assignment *as) +{ + struct assignment *name; + struct assignment *arg; + struct assignment *opt1; + struct assignment *function; + struct assignment *tmp; + char *f; + int nargs = 0; + + name = find(as, "name"); + arg = find(as, "argument"); + opt1 = find(as, "option"); + function = find(as, "function"); + if(function == NULL) + function = name; + f = strdup(name->u.value); + gen_name(f); + + if(opt1 != NULL) { + gen_options(opt1, name->u.value); + hprint(0, "int %s(struct %s_options*, int, char **);\n", + function->u.value, name->u.value); + } else { + hprint(0, "int %s(void*, int, char **);\n", + function->u.value); + } + + fprintf(cfile, "static int\n"); + fprintf(cfile, "%s_wrap(int argc, char **argv)\n", f); + fprintf(cfile, "{\n"); + if(opt1 != NULL) + cprint(1, "struct %s_options opt;\n", name->u.value); + cprint(1, "int optind = 0;\n"); + cprint(1, "struct getargs args[] = {\n"); + for(tmp = find(as, "option"); + tmp != NULL; + tmp = find_next(tmp, "option")) { + struct assignment *type = find(tmp->u.assignment, "type"); + struct assignment *lopt = find(tmp->u.assignment, "long"); + struct assignment *sopt = find(tmp->u.assignment, "short"); + struct assignment *arg = find(tmp->u.assignment, "argument"); + struct assignment *help = find(tmp->u.assignment, "help"); + + cprint(2, "{ "); + if(lopt) + fprintf(cfile, "\"%s\", ", lopt->u.value); + else + fprintf(cfile, "NULL, "); + if(sopt) + fprintf(cfile, "'%c', ", *sopt->u.value); + else + fprintf(cfile, "0, "); + if(strcmp(type->u.value, "string") == 0) + fprintf(cfile, "arg_string, "); + else if(strcmp(type->u.value, "integer") == 0) + fprintf(cfile, "arg_integer, "); + else if(strcmp(type->u.value, "flag") == 0) + fprintf(cfile, "arg_flag, "); + else + abort(); + fprintf(cfile, "NULL, "); + if(help) + fprintf(cfile, "\"%s\", ", help->u.value); + else + fprintf(cfile, "NULL, "); + if(arg) + fprintf(cfile, "\"%s\"", arg->u.value); + else + fprintf(cfile, "NULL"); + fprintf(cfile, " },\n"); + } + cprint(2, "{ \"help\", 'h', arg_flag, NULL, NULL, NULL }\n"); + cprint(1, "};\n"); + cprint(1, "int help_flag = 0;\n"); + + for(tmp = find(as, "option"); + tmp != NULL; + tmp = find_next(tmp, "option")) { + char *s; + struct assignment *type = find(tmp->u.assignment, "type"); + + struct assignment *defval = find(tmp->u.assignment, "default"); + s = make_name(tmp->u.assignment); + if(strcmp(type->u.value, "string") == 0) { + if(defval != NULL) + cprint(1, "opt.%s = \"%s\";\n", s, defval->u.value); + else + cprint(1, "opt.%s = NULL;\n", s); + } else if(strcmp(type->u.value, "integer") == 0) { + if(defval != NULL) + cprint(1, "opt.%s = %s;\n", s, defval->u.value); + else + cprint(1, "opt.%s = 0;\n", s); + } else if(strcmp(type->u.value, "flag") == 0) { + if(defval != NULL) + cprint(1, "opt.%s = %s;\n", s, defval->u.value); + else + cprint(1, "opt.%s = 0;\n", s); + } else + abort(); + free(s); + } + + for(tmp = find(as, "option"); + tmp != NULL; + tmp = find_next(tmp, "option")) { + char *s; + s = make_name(tmp->u.assignment); + cprint(1, "args[%d].value = &opt.%s;\n", nargs++, s); + free(s); + } + cprint(1, "args[%d].value = &help_flag;\n", nargs++); + cprint(1, "if(getarg(args, %d, argc, argv, &optind))\n", nargs); + cprint(2, "goto usage;\n"); + + if(arg == NULL) { + cprint(1, "if(optind < argc)\n"); + cprint(2, "goto usage;\n"); + } + + cprint(1, "if(help_flag)\n"); + cprint(2, "goto usage;\n"); + + cprint(1, "return %s(%s, argc - optind, argv + optind);\n", + function->u.value, + opt1 ? "&opt": "NULL"); + cprint(0, "usage:\n"); + cprint(1, "arg_printusage (args, %d, \"%s\", \"%s\");\n", nargs, + name->u.value, arg ? arg->u.value : ""); + cprint(1, "return 0;\n"); + cprint(0, "}\n"); + cprint(0, "\n"); +} + +static void +gen(struct assignment *as) +{ + struct assignment *a; + cprint(0, "#include \n"); + cprint(0, "#include \n"); + cprint(0, "#include \n"); + cprint(0, "#include \"kadmin-commands.h\"\n\n"); + + hprint(0, "#include \n"); + hprint(0, "#include \n"); + hprint(0, "\n"); + + + for(a = as; a != NULL; a = a->next) + gen_wrapper(a->u.assignment); + + cprint(0, "SL_cmd commands[] = {\n"); + for(a = as; a != NULL; a = a->next) + gen_command(a->u.assignment); + cprint(1, "{ NULL }\n"); + cprint(0, "};\n"); + + hprint(0, "extern SL_cmd commands[];\n"); +} + +int version_flag; +int help_flag; +struct getargs args[] = { + { "version", 0, arg_flag, &version_flag }, + { "help", 0, arg_flag, &help_flag } +}; +int num_args = sizeof(args) / sizeof(args[0]); + +static void +usage(int code) +{ + arg_printusage(args, num_args, NULL, "command-table"); + exit(code); +} + +int +main(int argc, char **argv) +{ + char cname[PATH_MAX]; + char hname[PATH_MAX]; + char *p; + + int optind = 0; + + setprogname(argv[0]); + if(getarg(args, num_args, argc, argv, &optind)) + usage(1); + if(help_flag) + usage(0); + if(version_flag) { + print_version(NULL); + exit(0); + } + + if(argc == optind) + usage(1); + + filename = argv[optind]; + yyin = fopen(filename, "r"); + if(yyin == NULL) + err(1, "%s", filename); + p = strrchr(filename, '/'); + if(p) + strlcpy(cname, p + 1, sizeof(cname)); + else + strlcpy(cname, filename, sizeof(cname)); + p = strrchr(cname, '.'); + if(p) + *p = '\0'; + strlcpy(hname, cname, sizeof(hname)); + strlcat(cname, ".c", sizeof(cname)); + strlcat(hname, ".h", sizeof(hname)); + yyparse(); + if(error_flag) + exit(1); + if(check(a) == 0) { + cfile = fopen(cname, "w"); + if(cfile == NULL) + err(1, "%s", cname); + hfile = fopen(hname, "w"); + if(hfile == NULL) + err(1, "%s", hname); + gen(a); + fclose(cfile); + fclose(hfile); + } + return 0; +} diff --git a/lib/sl/slc-lex.l b/lib/sl/slc-lex.l new file mode 100644 index 000000000..8913f7bb1 --- /dev/null +++ b/lib/sl/slc-lex.l @@ -0,0 +1,156 @@ +%{ +/* + * Copyright (c) 2004 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* $Id$ */ + +#include +#include +#include +#include "slc.h" +#include "slc-gram.h" +unsigned lineno = 1; + +static void handle_comment(void); +static char * handle_string(void); +%} +%% +[A-Za-z][-A-Za-z0-9_]* { + yylval.string = strdup ((const char *)yytext); + return LITERAL; + } +"\"" { yylval.string = handle_string(); return STRING; } +\n { ++lineno; } +\/\* { handle_comment(); } +[={}] { return *yytext; } +[ \t] ; +%% + +void +error_message (const char *format, ...) +{ + va_list args; + + va_start (args, format); + fprintf (stderr, "%s:%d: ", filename, lineno); + vfprintf (stderr, format, args); + va_end (args); + error_flag++; +} + +void +yyerror (char *s) +{ + error_message("%s\n", s); +} + +static void +handle_comment(void) +{ + int c; + int start_lineno = lineno; + int level = 1; + int seen_star = 0; + int seen_slash = 0; + while((c = input()) != EOF) { + if(c == '/') { + if(seen_star) { + if(--level == 0) + return; + seen_star = 0; + continue; + } + seen_slash = 1; + continue; + } + if(seen_star && c == '/') { + if(--level == 0) + return; + seen_star = 0; + continue; + } + if(c == '*') { + if(seen_slash) { + level++; + seen_star = seen_slash = 0; + continue; + } + seen_star = 1; + continue; + } + seen_star = seen_slash = 0; + if(c == '\n') { + lineno++; + continue; + } + } + if(c == EOF) + error_message("unterminated comment, possibly started on line %d\n", start_lineno); +} + +static char * +handle_string(void) +{ + char x[1024]; + int i = 0; + int c; + int quote = 0; + while((c = input()) != EOF){ + if(quote) { + x[i++] = '\\'; + x[i++] = c; + quote = 0; + continue; + } + if(c == '\n'){ + error_message("unterminated string"); + lineno++; + break; + } + if(c == '\\'){ + quote++; + continue; + } + if(c == '\"') + break; + x[i++] = c; + } + x[i] = '\0'; + return strdup(x); +} + +int +yywrap () +{ + return 1; +} diff --git a/lib/sl/slc.h b/lib/sl/slc.h new file mode 100644 index 000000000..abc3b72b7 --- /dev/null +++ b/lib/sl/slc.h @@ -0,0 +1,55 @@ +/* + * Copyright (c) 2004 Kungliga Tekniska Högskolan + * (Royal Institute of Technology, Stockholm, Sweden). + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * 3. Neither the name of the Institute nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* $Id$ */ +#include +#include +#include + +struct assignment { + char *name; + enum { a_value, a_assignment } type; + union { + char *value; + struct assignment *assignment; + } u; + unsigned int lineno; + struct assignment *next; +}; + +extern char *filename; +extern int error_flag; +void error_message (const char *format, ...); +int yylex(void); +void yyerror (char *s); +extern unsigned lineno;