aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorBruce Hill <bruce@bruce-hill.com>2025-10-18 17:12:20 -0400
committerBruce Hill <bruce@bruce-hill.com>2025-10-18 17:12:20 -0400
commit383a942bb3fe05be887a6622ca5ed86e9bba0662 (patch)
tree381ca712bbbeb36308fb2866c3b4c516626b1abf /src
parent48aa524e8c5aab6632c680971a8a31c542121993 (diff)
Added automatic manpages.
Diffstat (limited to 'src')
-rw-r--r--src/compile/cli.c35
-rw-r--r--src/compile/cli.h1
-rw-r--r--src/tomo.c20
3 files changed, 54 insertions, 2 deletions
diff --git a/src/compile/cli.c b/src/compile/cli.c
index 820f0e2e..3337e250 100644
--- a/src/compile/cli.c
+++ b/src/compile/cli.c
@@ -1,5 +1,6 @@
// This file defines how to compile CLI argument parsing
+#include "../stdlib/cli.h"
#include "../environment.h"
#include "../stdlib/datatypes.h"
#include "../stdlib/optionals.h"
@@ -131,3 +132,37 @@ Text_t compile_cli_arg_call(env_t *env, Text_t fn_name, type_t *fn_type, const c
code = Texts(code, ");\n");
return code;
}
+
+public
+Text_t compile_manpage(Text_t program, OptionalText_t synopsis, OptionalText_t description, arg_t *args) {
+ Text_t date = Text(""); // TODO: use date
+ Text_t man = Texts(".\\\" Automatically generated by Tomo\n"
+ ".TH \"",
+ Text$upper(program, Text("C")), "\" \"1\" \"", date,
+ "\" \"\" \"\"\n"
+ ".SH NAME\n",
+ program, " \\- ", synopsis.tag == TEXT_NONE ? Text("a Tomo program") : synopsis, "\n");
+
+ if (description.tag != TEXT_NONE) {
+ man = Texts(man, ".SH DESCRIPTION\n", description, "\n");
+ }
+
+ man = Texts(man, ".SH OPTIONS\n");
+ for (arg_t *arg = args; arg; arg = arg->next) {
+ man = Texts(man, "\n.TP\n\\f[B]\\-\\-", Text$from_str(arg->name), "\\f[R]");
+ if (arg->alias) man = Texts(man, ", \\f[B]\\-", Text$from_str(arg->alias), "\\f[R]");
+ if (arg->type->tag == BoolType) {
+ man = Texts(man, "\n.TP\n\\f[B]\\-\\-no\\-", Text$from_str(arg->name));
+ } else if (is_numeric_type(arg->type)) {
+ man = Texts(man, " \\f[I]N\\f[R]");
+ } else if (arg->type->tag == ListType) {
+ man = Texts(man, " \\f[I]value1\\f[R] \\f[I]value2...\\f[R]");
+ } else if (arg->type->tag == TableType) {
+ man = Texts(man, " \\f[I]key1:value1\\f[R] \\f[I]key2:value2...\\f[R]");
+ } else {
+ man = Texts(man, " \\f[I]value\\f[R]");
+ }
+ }
+
+ return man;
+}
diff --git a/src/compile/cli.h b/src/compile/cli.h
index 13c3dee5..fa60eccf 100644
--- a/src/compile/cli.h
+++ b/src/compile/cli.h
@@ -7,3 +7,4 @@
#include "../types.h"
Text_t compile_cli_arg_call(env_t *env, Text_t fn_name, type_t *fn_type, const char *version);
+Text_t compile_manpage(Text_t program, OptionalText_t synopsis, OptionalText_t description, arg_t *args);
diff --git a/src/tomo.c b/src/tomo.c
index cec4b0b4..77b547e1 100644
--- a/src/tomo.c
+++ b/src/tomo.c
@@ -290,7 +290,8 @@ int main(int argc, char *argv[]) {
// Uninstall libraries:
for (int64_t i = 0; i < (int64_t)uninstall_libraries.length; i++) {
Text_t *u = (Text_t *)(uninstall_libraries.data + i * uninstall_libraries.stride);
- xsystem(as_owner, "rm -rvf '", TOMO_PATH, "'/lib/tomo_" TOMO_VERSION "/", *u);
+ xsystem(as_owner, "rm -rvf '", TOMO_PATH, "'/lib/tomo_" TOMO_VERSION "/", *u, " '", TOMO_PATH, "'/bin/", *u,
+ " '", TOMO_PATH, "'/man/man1/", *u, ".1");
print("Uninstalled ", *u);
}
@@ -373,7 +374,12 @@ int main(int argc, char *argv[]) {
List_t object_files = EMPTY_LIST, extra_ldlibs = EMPTY_LIST;
compile_files(env, List(path), &object_files, &extra_ldlibs, COMPILE_EXE);
compile_executable(env, path, exe_path, object_files, extra_ldlibs);
- if (should_install) xsystem(as_owner, "cp -v '", exe_path, "' '", TOMO_PATH, "'/bin/");
+ if (should_install) {
+ xsystem(as_owner, "mkdir -p '", TOMO_PATH, "/bin' '", TOMO_PATH, "/man/man1'");
+ xsystem(as_owner, "cp -v '", exe_path, "' '", TOMO_PATH, "/bin/'");
+ Path_t manpage_file = build_file(Path$with_extension(path, Text(".1"), true), "");
+ xsystem(as_owner, "cp -v '", manpage_file, "' '", TOMO_PATH, "/man/man1/'");
+ }
_exit(0);
}
@@ -868,6 +874,16 @@ Path_t compile_executable(env_t *base_env, Path_t path, Path_t exe_path, List_t
if (!main_binding || main_binding->type->tag != FunctionType)
print_err("No main() function has been defined for ", path, ", so it can't be run!");
+ Path_t manpage_file = build_file(Path$with_extension(path, Text(".1"), true), "");
+ if (clean_build || !Path$is_file(manpage_file, true) || is_stale(manpage_file, path, true)) {
+ Text_t manpage = compile_manpage(Path$base_name(exe_path), NONE_TEXT, NONE_TEXT,
+ Match(main_binding->type, FunctionType)->args);
+ if (!quiet) print("Wrote manpage:\t", Path$relative_to(manpage_file, Path$current_dir()));
+ Path$write(manpage_file, manpage, 0644);
+ } else {
+ if (verbose) whisper("Unchanged: ", manpage_file);
+ }
+
if (!clean_build && Path$is_file(exe_path, true) && !is_config_outdated(path)
&& !is_stale_for_any(exe_path, object_files, false)
&& !is_stale(exe_path, Path$sibling(path, Text("modules.ini")), true)