From 74a613c67d9536c8f2971135ce6d8803aec96424 Mon Sep 17 00:00:00 2001 From: Nicolas Williams Date: Tue, 23 Dec 2025 18:15:46 -0600 Subject: [PATCH] sl: Add extended MANDOC generation --- lib/sl/sl.c | 177 +++++++++++++++++++++++++++++++++++++++++++++- lib/sl/sl.h | 13 ++++ lib/sl/sl_locl.h | 1 + lib/sl/slc-gram.y | 124 ++++++++++++++++++++++---------- 4 files changed, 277 insertions(+), 38 deletions(-) diff --git a/lib/sl/sl.c b/lib/sl/sl.c index dc840cedb..7d8457f01 100644 --- a/lib/sl/sl.c +++ b/lib/sl/sl.c @@ -36,6 +36,17 @@ #include "sl_locl.h" #include +static SL_cmd_info * +find_cmd_info(SL_cmd_info *info, const char *name) +{ + if (info == NULL) + return NULL; + for (; info->name != NULL; info++) + if (strcmp(info->name, name) == 0) + return info; + return NULL; +} + static void mandoc_template(SL_cmd *cmds, const char *extra_string) @@ -108,6 +119,156 @@ mandoc_template(SL_cmd *cmds, printf(".\\\".Sh BUGS\n"); } +static void +mandoc_template_ext(SL_cmd *cmds, + SL_cmd_info *info, + const char *extra_string) +{ + SL_cmd *c, *prev; + SL_cmd_info *ci; + char timestr[64], cmd[64]; + const char *p; + time_t t; + int i; + + printf(".\\\" Things to fix:\n"); + printf(".\\\" * correct section, and operating system\n"); + printf(".\\\" * remove Op from mandatory flags\n"); + printf(".\\\" * use better macros for arguments (like .Pa for files)\n"); + printf(".\\\"\n"); + t = time(NULL); + strftime(timestr, sizeof(timestr), "%b %d, %Y", localtime(&t)); + printf(".Dd %s\n", timestr); +#ifdef HAVE_GETPROGNAME + p = getprogname(); +#else + p = "unknown-application"; +#endif + strncpy(cmd, p, sizeof(cmd)); + cmd[sizeof(cmd)-1] = '\0'; + strupr(cmd); + + printf(".Dt %s SECTION\n", cmd); + printf(".Os OPERATING_SYSTEM\n"); + printf(".Sh NAME\n"); + printf(".Nm %s\n", p); + printf(".Nd\n"); + printf("in search of a description\n"); + printf(".Sh SYNOPSIS\n"); + printf(".Nm\n"); + for(c = cmds; c->name; ++c) { + if (c->func == NULL) + continue; + printf(".Nm\n"); + printf(".Ic %s\n", c->name); + ci = find_cmd_info(info, c->name); + if (ci && ci->args) { + for (i = 0; i < ci->nargs; i++) { + struct getargs *arg = &ci->args[i]; + if (arg->long_name == NULL) + continue; + /* Skip the implicit --help */ + if (strcmp(arg->long_name, "help") == 0) + continue; + if (arg->type == arg_flag || arg->type == arg_negative_flag) { + if (arg->short_name) + printf(".Op Fl %c | Fl \\-%s\n", + arg->short_name, arg->long_name); + else + printf(".Op Fl \\-%s\n", arg->long_name); + } else { + if (arg->short_name) + printf(".Op Fl %c Ar %s | Fl \\-%s Ns = Ns Ar %s\n", + arg->short_name, + arg->arg_help ? arg->arg_help : "value", + arg->long_name, + arg->arg_help ? arg->arg_help : "value"); + else + printf(".Op Fl \\-%s Ns = Ns Ar %s\n", + arg->long_name, + arg->arg_help ? arg->arg_help : "value"); + } + } + } + if (ci && ci->argument) + printf(".Ar %s\n", ci->argument); + } + if (extra_string && *extra_string) + printf (".Ar %s\n", extra_string); + + printf(".Sh DESCRIPTION\n"); + printf("The following commands are supported:\n"); + printf(".Bl -tag -width Ds\n"); + prev = NULL; + for(c = cmds; c->name; ++c) { + if (c->func) { + if (prev) + printf ("\n%s\n", prev->help ? prev->help : ""); + + printf (".It Ic %s", c->name); + prev = c; + } else + printf (" , Ic %s", c->name); + } + if (prev) + printf ("\n%s\n", prev->help ? prev->help : ""); + + printf(".El\n"); + + /* Now output detailed option descriptions for each command */ + printf(".Sh COMMAND OPTIONS\n"); + for(c = cmds; c->name; ++c) { + if (c->func == NULL) + continue; + ci = find_cmd_info(info, c->name); + if (ci == NULL || ci->args == NULL || ci->nargs == 0) + continue; + + printf(".Ss %s\n", c->name); + printf(".Bl -tag -width Ds -compact\n"); + for (i = 0; i < ci->nargs; i++) { + struct getargs *arg = &ci->args[i]; + if (arg->long_name == NULL) + continue; + /* Skip the implicit --help */ + if (strcmp(arg->long_name, "help") == 0) + continue; + + if (arg->short_name) { + if (arg->type == arg_flag || arg->type == arg_negative_flag) + printf(".It Fl %c , Fl \\-%s\n", + arg->short_name, arg->long_name); + else + printf(".It Fl %c Ar %s , Fl \\-%s Ns = Ns Ar %s\n", + arg->short_name, + arg->arg_help ? arg->arg_help : "value", + arg->long_name, + arg->arg_help ? arg->arg_help : "value"); + } else { + if (arg->type == arg_flag || arg->type == arg_negative_flag) + printf(".It Fl \\-%s\n", arg->long_name); + else + printf(".It Fl \\-%s Ns = Ns Ar %s\n", + arg->long_name, + arg->arg_help ? arg->arg_help : "value"); + } + if (arg->help) + printf("%s\n", arg->help); + } + printf(".El\n"); + } + + printf(".\\\".Sh ENVIRONMENT\n"); + printf(".\\\".Sh FILES\n"); + printf(".\\\".Sh EXAMPLES\n"); + printf(".\\\".Sh DIAGNOSTICS\n"); + printf(".\\\".Sh SEE ALSO\n"); + printf(".\\\".Sh STANDARDS\n"); + printf(".\\\".Sh HISTORY\n"); + printf(".\\\".Sh AUTHORS\n"); + printf(".\\\".Sh BUGS\n"); +} + SL_cmd * sl_match (SL_cmd *cmds, char *cmd, int exactp) { @@ -131,12 +292,26 @@ sl_match (SL_cmd *cmds, char *cmd, int exactp) return NULL; } +void +sl_help_ext(SL_cmd *cmds, SL_cmd_info *info, int argc, char **argv) +{ + if (getenv("SLMANDOC")) { + if (info) + mandoc_template_ext(cmds, info, NULL); + else + mandoc_template(cmds, NULL); + return; + } + sl_slc_help(cmds, argc, argv); +} + void sl_help (SL_cmd *cmds, int argc, char **argv) { SL_cmd *c, *prev_c; - if (getenv("SLMANDOC")) { + if (getenv("SLMANDOC") && + strcmp(getenv("SLMANDOC"), "extended") == 0) { mandoc_template(cmds, NULL); return; } diff --git a/lib/sl/sl.h b/lib/sl/sl.h index affdb6e1a..2717fc38b 100644 --- a/lib/sl/sl.h +++ b/lib/sl/sl.h @@ -49,6 +49,18 @@ struct sl_cmd { typedef struct sl_cmd SL_cmd; +/* + * Extended command info for mandoc generation. + * Generated by slc alongside SL_cmd. + */ +struct sl_cmd_info { + const char *name; + const char *argument; + struct getargs *args; + int nargs; +}; +typedef struct sl_cmd_info SL_cmd_info; + #ifdef __cplusplus extern "C" { #endif @@ -61,6 +73,7 @@ int sl_make_argv(char*, int*, char***); void sl_apropos (SL_cmd *cmd, const char *topic); SL_cmd *sl_match (SL_cmd *cmds, char *cmd, int exactp); void sl_slc_help (SL_cmd *cmds, int argc, char **argv); +void sl_help_ext (SL_cmd *cmds, SL_cmd_info *info, int argc, char **argv); void sl_did_you_mean(SL_cmd *cmds, const char *match); diff --git a/lib/sl/sl_locl.h b/lib/sl/sl_locl.h index 50eeadec1..66c50c62b 100644 --- a/lib/sl/sl_locl.h +++ b/lib/sl/sl_locl.h @@ -42,4 +42,5 @@ #include +#include #include diff --git a/lib/sl/slc-gram.y b/lib/sl/slc-gram.y index eed7fc10f..d58092b3f 100644 --- a/lib/sl/slc-gram.y +++ b/lib/sl/slc-gram.y @@ -486,6 +486,58 @@ gen_options(struct assignment *opt1, const char *name) hprint(0, "};\n"); } +static int +count_options(struct assignment *as) +{ + struct assignment *tmp; + int nargs = 0; + for(tmp = find(as, "option"); tmp != NULL; tmp = find_next(tmp, "option")) + nargs++; + return nargs + 1; /* +1 for help */ +} + +static void +gen_args_array(struct assignment *as, const char *n) +{ + struct assignment *tmp; + + cprint(0, "static struct getargs %s_args[] = {\n", 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 *aarg = find(tmp->u.assignment, "argument"); + struct assignment *help = find(tmp->u.assignment, "help"); + struct type_handler *th; + + cprint(1, "{ "); + if(lopt) + fprintf(cfile, "\"%s\", ", lopt->u.value); + else + fprintf(cfile, "NULL, "); + if(sopt) + fprintf(cfile, "'%c', ", *sopt->u.value); + else + fprintf(cfile, "0, "); + th = find_handler(type); + fprintf(cfile, "%s, ", th->getarg_type); + fprintf(cfile, "NULL, "); + if(help) + fprintf(cfile, "\"%s\", ", help->u.value); + else + fprintf(cfile, "NULL, "); + if(aarg) + fprintf(cfile, "\"%s\"", aarg->u.value); + else + fprintf(cfile, "NULL"); + fprintf(cfile, " },\n"); + } + cprint(1, "{ \"help\", 'h', arg_flag, NULL, NULL, NULL }\n"); + cprint(0, "};\n\n"); +} + static void gen_wrapper(struct assignment *as) { @@ -519,6 +571,9 @@ gen_wrapper(struct assignment *as) hprint(0, "int %s(void*, int, char **);\n", f); } + /* Generate the static args array */ + gen_args_array(as, n); + fprintf(cfile, "static int\n"); fprintf(cfile, "%s_wrap(int argc, char **argv)\n", n); fprintf(cfile, "{\n"); @@ -526,43 +581,7 @@ gen_wrapper(struct assignment *as) cprint(1, "struct %s_options opt;\n", n); cprint(1, "int ret;\n"); cprint(1, "int optidx = 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 *aarg = find(tmp->u.assignment, "argument"); - struct assignment *help = find(tmp->u.assignment, "help"); - - struct type_handler *th; - - 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, "); - th = find_handler(type); - fprintf(cfile, "%s, ", th->getarg_type); - fprintf(cfile, "NULL, "); - if(help) - fprintf(cfile, "\"%s\", ", help->u.value); - else - fprintf(cfile, "NULL, "); - if(aarg) { - fprintf(cfile, "\"%s\"", aarg->u.value); - narguments++; - } else - fprintf(cfile, "NULL"); - fprintf(cfile, " },\n"); - } - cprint(2, "{ \"help\", 'h', arg_flag, NULL, NULL, NULL }\n"); - cprint(1, "};\n"); + cprint(1, "struct getargs *args = %s_args;\n", n); cprint(1, "int help_flag = 0;\n"); for(tmp = find(as, "option"); @@ -695,6 +714,28 @@ gen_wrapper(struct assignment *as) char cname[PATH_MAX]; char hname[PATH_MAX]; +static void +gen_command_info(struct assignment *as) +{ + struct assignment *a, *arg; + char *n; + + a = find(as, "name"); + n = strdup(a->u.value); + gen_name(n); + arg = find(as, "argument"); + + cprint(1, "{ \"%s\", ", a->u.value); + if (arg) + fprintf(cfile, "\"%s\", ", arg->u.value); + else + fprintf(cfile, "NULL, "); + fprintf(cfile, "%s_args, ", n); + fprintf(cfile, "%d ", count_options(as)); + fprintf(cfile, "},\n"); + free(n); +} + static void gen(struct assignment *as) { @@ -707,6 +748,7 @@ gen(struct assignment *as) hprint(0, "#include \n"); hprint(0, "#include \n"); + hprint(0, "#include \n"); hprint(0, "\n"); @@ -717,9 +759,17 @@ gen(struct assignment *as) for(a = as; a != NULL; a = a->next) gen_command(a->u.assignment); cprint(1, "{ NULL, NULL, NULL, NULL }\n"); + cprint(0, "};\n\n"); + + /* Generate the commands_info array for mandoc generation */ + cprint(0, "SL_cmd_info commands_info[] = {\n"); + for(a = as; a != NULL; a = a->next) + gen_command_info(a->u.assignment); + cprint(1, "{ NULL, NULL, NULL, 0 }\n"); cprint(0, "};\n"); hprint(0, "extern SL_cmd commands[];\n"); + hprint(0, "extern SL_cmd_info commands_info[];\n"); } int version_flag;