Subject: bug#11044: RFE: -g <goal> for fmt(1) command

From: Bruce Korb <[email-masked]>
Date: Mon, 19 Mar 2012 16:05:51 -0700

diff --git a/src/fmt.c b/src/fmt.c
index 89d13a6..68d95f1
--- a/src/fmt.c
+++ b/src/fmt.c
@@ -68,7 +68,7 @@ typedef long int COST;
 #define SQR(n)		((n) * (n))
 #define EQUIV(n)	SQR ((COST) (n))
 
-/* Cost of a filled line n chars longer or shorter than best_width.  */
+/* Cost of a filled line n chars longer or shorter than goal_width.  */
 #define SHORT_COST(n)	EQUIV ((n) * 10)
 
 /* Cost of the difference between adjacent filled lines.  */
@@ -167,6 +167,7 @@ static void put_paragraph (WORD *finish);
 static void put_line (WORD *w, int indent);
 static void put_word (WORD *w);
 static void put_space (int space);
+static void check_for_goals (char ** argv);
 
 /* Option values.  */
 
@@ -201,7 +202,7 @@ static int prefix_lead_space;
 static int prefix_length;
 
 /* The preferred width of text lines, set to LEEWAY % less than max_width.  */
-static int best_width;
+static int goal_width;
 
 /* Dynamic variables.  */
 
@@ -286,6 +287,7 @@ Mandatory arguments to long options are mandatory for short options too.\n\
   -t, --tagged-paragraph    indentation of first line different from second\n\
   -u, --uniform-spacing     one space between words, two after sentences\n\
   -w, --width=WIDTH         maximum line width (default of 75 columns)\n\
+  -g, --goal=WIDTH          goal width (default of 93% of width)\n\
 "), stdout);
       fputs (HELP_OPTION_DESCRIPTION, stdout);
       fputs (VERSION_OPTION_DESCRIPTION, stdout);
@@ -308,6 +310,7 @@ static struct option const long_options[] =
   {"tagged-paragraph", no_argument, NULL, 't'},
   {"uniform-spacing", no_argument, NULL, 'u'},
   {"width", required_argument, NULL, 'w'},
+  {"goal", required_argument, NULL, 'g'},
   {GETOPT_HELP_OPTION_DECL},
   {GETOPT_VERSION_OPTION_DECL},
   {NULL, 0, NULL, 0},
@@ -319,6 +322,7 @@ main (int argc, char **argv)
   int optchar;
   bool ok = true;
   char const *max_width_option = NULL;
+  char const *goal_width_option = NULL;
 
   initialize_main (&argc, &argv);
   set_program_name (argv[0]);
@@ -376,6 +380,10 @@ main (int argc, char **argv)
         max_width_option = optarg;
         break;
 
+      case 'g':
+        goal_width_option = optarg;
+        break;
+
       case 'p':
         set_prefix (optarg);
         break;
@@ -398,7 +406,23 @@ main (int argc, char **argv)
       max_width = tmp;
     }
 
-  best_width = max_width * (2 * (100 - LEEWAY) + 1) / 200;
+  if (goal_width_option)
+    {
+      /* Limit goal_width to max_width.  */
+      unsigned long int tmp;
+      if (! (xstrtoul (goal_width_option, NULL, 10, &tmp, "") == LONGINT_OK
+             && tmp <= max_width))
+        error (EXIT_FAILURE, 0, _("invalid width: %s"),
+               quote (goal_width_option));
+      goal_width = tmp;
+    }
+  else
+    {
+      goal_width = max_width * (2 * (100 - LEEWAY) + 1) / 200;
+    }
+
+  if ((max_width_option == NULL) && (goal_width_option == NULL))
+    check_for_goals (argv);
 
   if (optind == argc)
     fmt (stdin);
@@ -435,6 +459,53 @@ main (int argc, char **argv)
   exit (ok ? EXIT_SUCCESS : EXIT_FAILURE);
 }
 
+/* Check the first two operands for being numbers without a file by that name.
+   If there are no such files and the numbers are not too big, then accept
+   them as -g and -w options, respectively.  */
+
+static void
+check_for_goals (char ** argv)
+{
+  unsigned long v;
+
+  /* see if the first operand is a number.  That means there is no file
+     by that name and the operand fully translates to a number.  */
+  char * num = argv[optind];
+  if (access (num, R_OK))
+    return;
+  errno = 0;
+  v = strtoul (num, &num, 0);
+  if ((errno == 0) && (*num == '\0') && (v > 0) && (v < MAXCHARS/2))
+    goal_width = v;
+  else
+    return;
+  optind++;
+
+  /* see if the second operand is a number.  That means there is no file
+     by that name and the operand fully translates to a number.  */
+  num = argv[optind];
+  if (access (num, R_OK))
+    {
+      max_width = goal_width + 10;
+      return;
+    }
+  errno = 0;
+  v = strtoul (num, &num, 0);
+  if ((errno == 0) && (*num == '\0') && (v > 0) && (v < MAXCHARS/2))
+    {
+      max_width = v;
+      if (goal_width > max_width)
+        error (EXIT_FAILURE, 0, _("goal exceeds width:  %u > %u"),
+               goal_width, max_width);
+    }
+  else
+    {
+      max_width = goal_width + 10;
+      return;
+    }
+  optind++;
+}
+
 /* Trim space from the front and back of the string P, yielding the prefix,
    and record the lengths of the prefix and the space trimmed.  */
 
@@ -924,7 +995,7 @@ line_cost (WORD *next, int len)
 
   if (next == word_limit)
     return 0;
-  n = best_width - len;
+  n = goal_width - len;
   cost = SHORT_COST (n);
   if (next->next_break != word_limit)
     {
@@ -1010,3 +1081,10 @@ put_space (int space)
       out_column++;
     }
 }
+/*
+ * Local Variables:
+ * mode: C
+ * c-file-style: "gnu"
+ * indent-tabs-mode: nil
+ * End:
+ * end of fmt.c */