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

Reply via email to