commit 574d5c2d6a16ae5600d65fedc157d03e7fcfdd43 from: Stefan Sperling date: Wed Jul 19 11:00:46 2023 UTC fill empty configuration files with an example config from /etc/examples If the user begins editing an empty file which corresponds to a file in the /etc/examples directory offer to load this example for editing. Suggested by Tom Smyth. ok tom, chris commit - cec4f8ceccb53f3f5297c9958b59c82a51a8e27c commit + 574d5c2d6a16ae5600d65fedc157d03e7fcfdd43 blob - e00aca8725b24f1c83a3c52d34067c81b03e0337 blob + 1fc6dd7d7f57306bb861c4d7e29798c158b0f32b --- ctl.c +++ ctl.c @@ -20,12 +20,15 @@ #include #include #include +#include +#include #include #include #include #include #include #include "externs.h" +#include "editing.h" #include "ctl.h" /* table variable (for pkill usage) */ @@ -815,7 +818,128 @@ call_editor(char *name, char **args, char *z) edit_file(tmpfile, daemons->mode, daemons->propername, args); } + +static void +provide_example_config(char *filename) +{ + char *name; + char path[PATH_MAX]; + char tmpprompt[sizeof(prompt)]; + FILE *f = NULL, *example = NULL; + int ret, num; + struct stat sb; + size_t len, remain; + + memset(tmpprompt, 0, sizeof(tmpprompt)); + + f = fopen(filename, "w+"); + if (f == NULL) + return; + + if (fstat(fileno(f), &sb) == -1) + goto done; + + if (sb.st_size != 0) + goto done; + + name = basename(filename); + if (name == NULL) + goto done; + + ret = snprintf(path, sizeof(path), "/etc/examples/%s", name); + if (ret < 0 || (size_t)ret >= sizeof(path)) + goto done; + /* Snip off rdomain trailer at end of filename. */ + len = strlen(path); + while (len > 0) { + if (path[len - 1] >= '0' && path[len - 1] <= '9') { + path[len - 1] = '\0'; + len--; + } else { + if (path[len - 1] == '.') { + path[len - 1] = '\0'; + len--; + } + break; + } + } + if (len == 0) + goto done; + + example = fopen(path, "r"); + if (example == NULL) + goto done; + + if (fstat(fileno(example), &sb) == -1) + goto done; + + if (sb.st_size == 0) + goto done; + + snprintf(tmpprompt, sizeof(tmpprompt), + "%s is empty. Load an example config? [Y/n]\n", filename); + setprompt(tmpprompt); + + for (;;) { + const char *buf; + + if ((buf = el_gets(elp, &num)) == NULL) { + if (num == -1) { + ret = -1; + goto done; + } + /* EOF, e.g. ^X or ^D via exit_i() in complete.c */ + goto done; + } + + if (strcmp(buf, "\n") == 0 || + strcasecmp(buf, "yes\n") == 0 || + strcasecmp(buf, "y\n") == 0) + break; + + if (strcasecmp(buf, "no\n") == 0 || + strcasecmp(buf, "n\n") == 0) + goto done; + + printf("%% Please type \"yes\" or \"no\"\n"); + } + + remain = sb.st_size; + while (remain > 0) { + char buf[8192]; + ssize_t r; + size_t w; + + len = (remain < sizeof(buf) ? remain : sizeof(buf)); + r = fread(buf, 1, len, example); + if (r != len) { + if (ferror(f)) { + printf("%% fread %s: %s\n", + path, strerror(errno)); + } + break; + } + + w = fwrite(buf, 1, len, f); + if (w != len) { + if (ferror(f)) { + printf("%% fwrite %s: %s\n", + filename, strerror(errno)); + } + break; + } + + remain -= len; + } +done: + fclose(f); + if (tmpprompt[0] != '\0') + restoreprompt(); + if (example) + fclose(example); +} + int edit_file(char *tmpfile, mode_t mode, char *propername, char **args) { @@ -830,6 +954,7 @@ edit_file(char *tmpfile, mode_t mode, char *propername } if ((fd = acq_lock(tmpfile)) > 0) { char *argv[] = { editor, tmpfile, NULL }; + provide_example_config(tmpfile); ret = cmdargs(editor, argv); if (ret == 0 && chmod(tmpfile, mode) == -1) { printf("%% chmod %o %s: %s\n",