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)
{