I tried adding documentation for declare -c but discovered that it wasn't honouring CASEMOD_CAPCASE. Looking at mkbuiltins.c, that appears to be because of the way that single_longdoc_strings is handled.
So I figured I should fix that too. Attached herewith a patch to implement both. This will alter the output of « help *cmd* » and « *cmd* --help » to omit mention of options that are not compiled into the current shell; affected commands include: « help cd » (-@), « help declare » (-a, -A, -c, -l, -u), « help set » (-b, -B, -H, -m, -o history, -o emacs, -o vi, ), and « help variables » (everything related to history). (By the time I'd figured out how it works, I'd changed some variable names to reflect "why" rather than "how", made write_file_headers and write_file_footers more symmetric, fixed some anomalous uses of 0-vs-NULL, and incidentally cleaned up some whitespace. I've left these changes in as well.) -Martin PS: I initially wondered whether I should simply remove the « -S » option, but I've left it in for now. Under what circumstances would it be preferable to put each line of text in a separate array entry? It does seem like a lot of the "help" handling would be smaller and simpler if it was all single multi-line strings rather than arrays of single line strings.
diff --git a/builtins/declare.def b/builtins/declare.def index 306458080..c21049a6d 100644 --- a/builtins/declare.def +++ b/builtins/declare.def @@ -39,14 +39,23 @@ Options: -p display the attributes and value of each NAME Options which set attributes: +#if defined (ARRAY_VARS) -a to make NAMEs indexed arrays (if supported) -A to make NAMEs associative arrays (if supported) +#endif +#if defined (CASEMOD_ATTRS) && defined (CASEMOD_CAPCASE) + -c to capitalise value on assignment to each NAME +#endif -i to make NAMEs have the `integer' attribute - -l to convert the value of each NAME to lower case on assignment +#if defined (CASEMOD_ATTRS) + -l to convert value to lowercase on assignment to each NAME +#endif -n make NAME a reference to the variable named by its value -r to make NAMEs readonly -t to make NAMEs have the `trace' attribute - -u to convert the value of each NAME to upper case on assignment +#if defined (CASEMOD_ATTRS) + -u to convert value to uppercase on assignment to each NAME +#endif -x to make NAMEs export Using `+' instead of `-' turns off the given attribute, except for a, @@ -131,7 +140,7 @@ local_builtin (WORD_LIST *list) builtin_help (); return (EX_USAGE); } - + if (variable_context) return (declare_internal (list, 1)); else @@ -199,7 +208,7 @@ declare_transform_name (char *name, int flags_on, int flags_off) { SHELL_VAR *var, *v; char *newname; - + var = find_variable (name); if (var == 0) newname = nameref_transform_name (name, ASS_MKLOCAL); @@ -234,7 +243,7 @@ declare_invalid_opts (int flags_on, int flags_off) else if (flags_on & att_array) optchar = "-a"; - sh_invalidopt (optchar); + sh_invalidopt (optchar); return (EXECUTION_FAILURE); } else if ((flags_on & att_assoc) && (flags_off & att_assoc)) @@ -409,7 +418,7 @@ declare_internal (WORD_LIST *list, int local_var) opt = declare_invalid_opts (flags_on, flags_off); if (opt != 0) return (opt); - + #define NEXT_VARIABLE() free (name); list = list->next; continue /* There are arguments left, so we are making variables. */ @@ -514,7 +523,7 @@ declare_internal (WORD_LIST *list, int local_var) } else #endif /* DEBUGGER */ - { + { t = nodefs ? name_cell (var) : named_function_string (name, function_cell (var), FUNC_MULTILINE|FUNC_EXTERNAL); printf ("%s\n", t); any_failed = sh_chkwrite (any_failed); @@ -870,7 +879,7 @@ restart_new_var_name: /* Readonly variable error checking. */ - /* Cannot use declare +r to turn off readonly attribute. */ + /* Cannot use declare +r to turn off readonly attribute. */ if (readonly_p (var) && (flags_off & att_readonly)) { sh_readonly (name_cell (var)); diff --git a/builtins/mkbuiltins.c b/builtins/mkbuiltins.c index c4cacdfc2..5d4a433d9 100644 --- a/builtins/mkbuiltins.c +++ b/builtins/mkbuiltins.c @@ -19,7 +19,7 @@ along with Bash. If not, see <http://www.gnu.org/licenses/>. */ -#if !defined (CROSS_COMPILING) +#if !defined (CROSS_COMPILING) # include <config.h> #else /* CROSS_COMPILING */ /* A conservative set of defines based on POSIX/SUS3/XPG6 */ @@ -44,6 +44,7 @@ #include "filecntl.h" #include "../bashansi.h" +#include <stdbool.h> #include <stdio.h> #include <errno.h> @@ -62,9 +63,9 @@ static void *xrealloc (void *, size_t); #define whitespace(c) (((c) == ' ') || ((c) == '\t')) /* Flag values that builtins can have. */ -#define BUILTIN_FLAG_SPECIAL 0x01 -#define BUILTIN_FLAG_ASSIGNMENT 0x02 -#define BUILTIN_FLAG_LOCALVAR 0x04 +#define BUILTIN_FLAG_SPECIAL 0x01 +#define BUILTIN_FLAG_ASSIGNMENT 0x02 +#define BUILTIN_FLAG_LOCALVAR 0x04 #define BUILTIN_FLAG_POSIX_BUILTIN 0x08 #define BUILTIN_FLAG_ARRAYREF_ARG 0x10 @@ -183,7 +184,7 @@ char *arrayvar_builtins[] = "typeset", "unset", "wait", /*]*/ (char *)NULL }; - + /* Forward declarations. */ static int is_special_builtin (char *); static int is_assignment_builtin (char *); @@ -286,9 +287,10 @@ main (int argc, char **argv) } } - if (include_filename == 0) + if (include_filename == NULL) include_filename = extern_filename; - + if (include_filename == NULL) + include_filename = "builtext.h" /* If there are no files to process, just quit now. */ if (arg_index == argc) exit (0); @@ -412,7 +414,7 @@ copy_string_array (ARRAY *array) copy->width = array->width; copy->array = (char **)xmalloc ((1 + array->sindex) * sizeof (char *)); - + for (i = 0; i < array->sindex; i++) copy->array[i] = savestring (array->array[i]); @@ -968,7 +970,7 @@ line_error (DEF_FILE *defs, char *format, char *arg1, char *arg2) fprintf (stderr, "%s", error_directory ? error_directory : "./"); fprintf (stderr, "%s:%d:", defs->filename, defs->line_number + 1); fprintf (stderr, format, arg1, arg2); - fprintf (stderr, "\n"); + fputc ('\n', stderr); fflush (stderr); } @@ -1111,11 +1113,9 @@ char *structfile_header[] = { "", "#include \"../builtins.h\"", (char *)NULL - }; +}; char *structfile_footer[] = { - " { (char *)0x0, (sh_builtin_func_t *)0x0, 0, (char **)0x0, (char *)0x0, (char *)0x0 }", - "};", "", "struct builtin *shell_builtins = static_shell_builtins;", "struct builtin *current_builtin;", @@ -1130,15 +1130,12 @@ char *structfile_footer[] = { void write_file_headers (FILE *structfile, FILE *externfile) { - register int i; - if (structfile) { - for (i = 0; structfile_header[i]; i++) + for (int i = 0; structfile_header[i]; i++) fprintf (structfile, "%s\n", structfile_header[i]); - fprintf (structfile, "#include \"%s\"\n", - include_filename ? include_filename : "builtext.h"); + fprintf (structfile, "#include \"%s\"\n", include_filename); fprintf (structfile, "#include \"bashintl.h\"\n"); @@ -1148,7 +1145,7 @@ write_file_headers (FILE *structfile, FILE *externfile) if (externfile) fprintf (externfile, "/* %s - The list of builtins found in libbuiltins.a. */\n", - include_filename ? include_filename : "builtext.h"); + include_filename); } /* Write out any necessary closing information for @@ -1156,14 +1153,13 @@ write_file_headers (FILE *structfile, FILE *externfile) void write_file_footers (FILE *structfile, FILE *externfile) { - register int i; + if (!structfile) + return; /* Write out the footers. */ - if (structfile) - { - for (i = 0; structfile_footer[i]; i++) - fprintf (structfile, "%s\n", structfile_footer[i]); - } + fprintf (structfile, " { (char *)NULL, (sh_builtin_func_t *)NULL, 0, (char **)NULL, (char *)NULL, (char *)NULL }\n};\n"); + for (int i = 0; structfile_footer[i]; i++) + fprintf (structfile, "%s\n", structfile_footer[i]); } /* Write out the information accumulated in DEFS to @@ -1355,7 +1351,7 @@ write_ifdefs (FILE *stream, char **defines) if (defines[i + 1]) fprintf (stream, " && "); } - fprintf (stream, "\n"); + fputc ('\n', stream); } /* Write an #endif string saying what defines controlled the compilation @@ -1390,129 +1386,106 @@ write_endifs (FILE *stream, char **defines) void write_documentation (FILE *stream, char **documentation, int indentation, int flags) { - register int i, j; - register char *line; - int string_array, texinfo, base_indent, filename_p; - - if (stream == 0) + if (stream == NULL) return; - string_array = flags & STRING_ARRAY; - filename_p = flags & HELPFILE; + const bool output_c_struct = flags & STRING_ARRAY; + const bool output_gettext = !(flags & HELPFILE); + const bool output_texinfo = flags & TEXINFO; - if (string_array) + if (output_c_struct) { - fprintf (stream, " {\n#if defined (HELP_BUILTIN)\n"); /* } */ - if (single_longdoc_strings) - { - if (filename_p == 0) - { - if (documentation && documentation[0] && documentation[0][0]) - fprintf (stream, "N_(\""); - else - fprintf (stream, "N_(\" "); /* the empty string translates specially. */ - } - else - fprintf (stream, "\""); - } + fprintf (stream, " {\n#if defined (HELP_BUILTIN)\n"); /* "}" */ + if (output_gettext && single_longdoc_strings) + fprintf (stream, " N_("); } - base_indent = (string_array && single_longdoc_strings && filename_p == 0) ? BASE_INDENT : 0; + const int base_indent = (output_c_struct && output_gettext) ? BASE_INDENT : 0; + const int full_indent = indentation + base_indent; - for (i = 0, texinfo = (flags & TEXINFO); documentation && (line = documentation[i]); i++) + for (int i = 0; documentation && documentation[i]; i++) { + char *line = documentation[i]; + const bool first_line = !i; + const bool last_line = !documentation[i+1]; + /* Allow #ifdef's to be written out verbatim, but don't put them into separate help files. */ if (*line == '#') { - if (string_array && filename_p == 0 && single_longdoc_strings == 0) + if (output_c_struct) fprintf (stream, "%s\n", line); continue; } - /* prefix with N_( for gettext */ - if (string_array && single_longdoc_strings == 0) + if (output_c_struct) { - if (filename_p == 0) - { - if (line[0]) - fprintf (stream, " N_(\""); - else - fprintf (stream, " N_(\" "); /* the empty string translates specially. */ - } - else - fprintf (stream, " \""); - } - - if (indentation) - for (j = 0; j < indentation; j++) - fprintf (stream, " "); - - /* Don't indent the first line, because of how the help builtin works. */ - if (i == 0) - indentation += base_indent; - - if (string_array) - { - for (j = 0; line[j]; j++) + if (output_gettext && !single_longdoc_strings) + fprintf (stream, "\tN_("); + else if (!first_line) + fputc ('\t', stream); + fputc ('"', stream); + if (output_gettext && !*line && last_line && first_line && indentation == 0) + line = " "; /* avoid entirely empty string, which translates specially. */ + if (indentation && *line) + fprintf (stream, "%*.0s", indentation, ""); + for (int j = 0; line[j]; j++) { switch (line[j]) { case '\\': case '"': - fprintf (stream, "\\%c", line[j]); + fputc ('\\', stream); break; - - default: - fprintf (stream, "%c", line[j]); } + fputc (line[j], stream); } - /* closing right paren for gettext */ - if (single_longdoc_strings == 0) - { - if (filename_p == 0) - fprintf (stream, "\"),\n"); - else - fprintf (stream, "\",\n"); - } - else if (documentation[i+1]) - /* don't add extra newline after last line */ - fprintf (stream, "\\n\\\n"); + if (last_line) + fputc ('"', stream); + else + fprintf (stream, "\\n\""); + if (output_gettext && !single_longdoc_strings) + fprintf (stream, "),"); + if (!last_line) + fprintf (stream, "\n"); } - else if (texinfo) + else if (output_texinfo) { - for (j = 0; line[j]; j++) + if (indentation && *line) + fprintf (stream, "%*.0s", indentation, ""); + for (int j = 0; line[j]; j++) { switch (line[j]) { case '@': case '{': case '}': - fprintf (stream, "@%c", line[j]); + fputc ('@', stream); break; - - default: - fprintf (stream, "%c", line[j]); } + fputc (line[j], stream); } - fprintf (stream, "\n"); + fputc ('\n', stream); } else - fprintf (stream, "%s\n", line); + fprintf (stream, "%*.0s%s\n", indentation, "", line); + + /* Don't indent the first line, because of how the help builtin works. */ + indentation = full_indent; } /* closing right paren for gettext */ - if (string_array && single_longdoc_strings) + if (output_c_struct) { - if (filename_p == 0) - fprintf (stream, "\"),\n"); - else - fprintf (stream, "\",\n"); + if (single_longdoc_strings) + { + if (output_gettext) + fputc (')', stream); + fputc (',', stream); + } + fprintf (stream, "\n#endif /* HELP_BUILTIN */\n\t(char *)NULL\n};\n"); } - - if (string_array) - fprintf (stream, "#endif /* HELP_BUILTIN */\n (char *)NULL\n};\n"); } int @@ -1521,7 +1494,7 @@ write_helpfiles (ARRAY *builtins) char *helpfile, *bname; FILE *helpfp; int i, hdlen; - BUILTIN_DESC *builtin; + BUILTIN_DESC *builtin; i = mkdir ("helpfiles", 0777); if (i < 0 && errno != EEXIST) @@ -1554,8 +1527,8 @@ write_helpfiles (ARRAY *builtins) free (helpfile); } return 0; -} - +} + static int _find_in_table (char *name, char **name_table) {