sl: Add extended MANDOC generation

This commit is contained in:
Nicolas Williams
2025-12-23 18:15:46 -06:00
parent 90d116d641
commit 74a613c67d
4 changed files with 277 additions and 38 deletions

View File

@@ -36,6 +36,17 @@
#include "sl_locl.h"
#include <setjmp.h>
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;
}

View File

@@ -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);

View File

@@ -42,4 +42,5 @@
#include <roken.h>
#include <getarg.h>
#include <sl.h>

View File

@@ -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 <stdio.h>\n");
hprint(0, "#include <sl.h>\n");
hprint(0, "#include <getarg.h>\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;