LCOV - code coverage report
Current view: top level - src - seq.c (source / functions) Hit Total Coverage
Test: coreutils.info Lines: 115 186 61.8 %
Date: 2018-01-30 Functions: 5 8 62.5 %

          Line data    Source code
       1             : /* seq - print sequence of numbers to standard output.
       2             :    Copyright (C) 1994-2008 Free Software Foundation, Inc.
       3             : 
       4             :    This program is free software: you can redistribute it and/or modify
       5             :    it under the terms of the GNU General Public License as published by
       6             :    the Free Software Foundation, either version 3 of the License, or
       7             :    (at your option) any later version.
       8             : 
       9             :    This program is distributed in the hope that it will be useful,
      10             :    but WITHOUT ANY WARRANTY; without even the implied warranty of
      11             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      12             :    GNU General Public License for more details.
      13             : 
      14             :    You should have received a copy of the GNU General Public License
      15             :    along with this program.  If not, see <http://www.gnu.org/licenses/>.  */
      16             : 
      17             : /* Written by Ulrich Drepper.  */
      18             : 
      19             : #include <config.h>
      20             : #include <getopt.h>
      21             : #include <stdio.h>
      22             : #include <sys/types.h>
      23             : #include <math.h>
      24             : #include <float.h>
      25             : 
      26             : #include "system.h"
      27             : #include "c-strtod.h"
      28             : #include "error.h"
      29             : #include "quote.h"
      30             : #include "xstrtod.h"
      31             : 
      32             : /* Roll our own isfinite rather than using <math.h>, so that we don't
      33             :    have to worry about linking -lm just for isfinite.  */
      34             : #ifndef isfinite
      35             : # define isfinite(x) ((x) * 0 == 0)
      36             : #endif
      37             : 
      38             : /* The official name of this program (e.g., no `g' prefix).  */
      39             : #define PROGRAM_NAME "seq"
      40             : 
      41             : #define AUTHORS "Ulrich Drepper"
      42             : 
      43             : /* If true print all number with equal width.  */
      44             : static bool equal_width;
      45             : 
      46             : /* The name that this program was run with.  */
      47             : char *program_name;
      48             : 
      49             : /* The string used to separate two numbers.  */
      50             : static char const *separator;
      51             : 
      52             : /* The string output after all numbers have been output.
      53             :    Usually "\n" or "\0".  */
      54             : /* FIXME: make this an option.  */
      55             : static char const terminator[] = "\n";
      56             : 
      57             : static struct option const long_options[] =
      58             : {
      59             :   { "equal-width", no_argument, NULL, 'w'},
      60             :   { "format", required_argument, NULL, 'f'},
      61             :   { "separator", required_argument, NULL, 's'},
      62             :   {GETOPT_HELP_OPTION_DECL},
      63             :   {GETOPT_VERSION_OPTION_DECL},
      64             :   { NULL, 0, NULL, 0}
      65             : };
      66             : 
      67             : void
      68          55 : usage (int status)
      69             : {
      70          55 :   if (status != EXIT_SUCCESS)
      71          53 :     fprintf (stderr, _("Try `%s --help' for more information.\n"),
      72             :              program_name);
      73             :   else
      74             :     {
      75           2 :       printf (_("\
      76             : Usage: %s [OPTION]... LAST\n\
      77             :   or:  %s [OPTION]... FIRST LAST\n\
      78             :   or:  %s [OPTION]... FIRST INCREMENT LAST\n\
      79             : "), program_name, program_name, program_name);
      80           2 :       fputs (_("\
      81             : Print numbers from FIRST to LAST, in steps of INCREMENT.\n\
      82             : \n\
      83             :   -f, --format=FORMAT      use printf style floating-point FORMAT\n\
      84             :   -s, --separator=STRING   use STRING to separate numbers (default: \\n)\n\
      85             :   -w, --equal-width        equalize width by padding with leading zeroes\n\
      86             : "), stdout);
      87           2 :       fputs (HELP_OPTION_DESCRIPTION, stdout);
      88           2 :       fputs (VERSION_OPTION_DESCRIPTION, stdout);
      89           2 :       fputs (_("\
      90             : \n\
      91             : If FIRST or INCREMENT is omitted, it defaults to 1.  That is, an\n\
      92             : omitted INCREMENT defaults to 1 even when LAST is smaller than FIRST.\n\
      93             : FIRST, INCREMENT, and LAST are interpreted as floating point values.\n\
      94             : INCREMENT is usually positive if FIRST is smaller than LAST, and\n\
      95             : INCREMENT is usually negative if FIRST is greater than LAST.\n\
      96             : "), stdout);
      97           2 :       fputs (_("\
      98             : FORMAT must be suitable for printing one argument of type `double';\n\
      99             : it defaults to %.PRECf if FIRST, INCREMENT, and LAST are all fixed point\n\
     100             : decimal numbers with maximum precision PREC, and to %g otherwise.\n\
     101             : "), stdout);
     102           2 :       emit_bug_reporting_address ();
     103             :     }
     104          55 :   exit (status);
     105             : }
     106             : 
     107             : /* A command-line operand.  */
     108             : struct operand
     109             : {
     110             :   /* Its value, converted to 'long double'.  */
     111             :   long double value;
     112             : 
     113             :   /* Its print width, if it were printed out in a form similar to its
     114             :      input form.  An input like "-.1" is treated like "-0.1", and an
     115             :      input like "1." is treated like "1", but otherwise widths are
     116             :      left alone.  */
     117             :   size_t width;
     118             : 
     119             :   /* Number of digits after the decimal point, or INT_MAX if the
     120             :      number can't easily be expressed as a fixed-point number.  */
     121             :   int precision;
     122             : };
     123             : typedef struct operand operand;
     124             : 
     125             : /* Description of what a number-generating format will generate.  */
     126             : struct layout
     127             : {
     128             :   /* Number of bytes before and after the number.  */
     129             :   size_t prefix_len;
     130             :   size_t suffix_len;
     131             : };
     132             : 
     133             : /* Read a long double value from the command line.
     134             :    Return if the string is correct else signal error.  */
     135             : 
     136             : static operand
     137          16 : scan_arg (const char *arg)
     138             : {
     139             :   operand ret;
     140             : 
     141          16 :   if (! xstrtold (arg, NULL, &ret.value, c_strtold))
     142             :     {
     143          14 :       error (0, 0, _("invalid floating point argument: %s"), arg);
     144          14 :       usage (EXIT_FAILURE);
     145             :     }
     146             : 
     147             :   /* We don't output spaces or '+' so don't include in width */
     148           4 :   while (isspace (*arg) || *arg == '+')
     149           0 :     arg++;
     150             : 
     151           2 :   ret.width = strlen (arg);
     152           2 :   ret.precision = INT_MAX;
     153             : 
     154           2 :   if (! arg[strcspn (arg, "xX")] && isfinite (ret.value))
     155             :     {
     156           2 :       char const *decimal_point = strchr (arg, '.');
     157           2 :       if (! decimal_point)
     158           2 :         ret.precision = 0;
     159             :       else
     160             :         {
     161           0 :           size_t fraction_len = strcspn (decimal_point + 1, "eE");
     162           0 :           if (fraction_len <= INT_MAX)
     163           0 :             ret.precision = fraction_len;
     164           0 :           ret.width += (fraction_len == 0                      /* #.  -> #   */
     165             :                         ? -1
     166           0 :                         : (decimal_point == arg                /* .#  -> 0.# */
     167           0 :                            || ! ISDIGIT (decimal_point[-1]))); /* -.# -> 0.# */
     168             :         }
     169           2 :       char const *e = strchr (arg, 'e');
     170           2 :       if (! e)
     171           2 :         e = strchr (arg, 'E');
     172           2 :       if (e)
     173             :         {
     174           0 :           long exponent = strtol (e + 1, NULL, 10);
     175           0 :           ret.precision += exponent < 0 ? -exponent : 0;
     176             :         }
     177             :     }
     178             : 
     179           2 :   return ret;
     180             : }
     181             : 
     182             : /* Validate the format, FMT.  Print a diagnostic and exit
     183             :    if there is not exactly one %-directive.  */
     184             : 
     185             : static void
     186          26 : validate_format (char const *fmt)
     187             : {
     188          26 :   unsigned int n_directives = 0;
     189             :   char const *p;
     190             : 
     191         155 :   for (p = fmt; *p; p++)
     192             :     {
     193         129 :       if (p[0] == '%' && p[1] != '%' && p[1] != '\0')
     194             :         {
     195          19 :           ++n_directives;
     196          19 :           ++p;
     197             :         }
     198             :     }
     199          26 :   if (n_directives == 0)
     200             :     {
     201           8 :       error (0, 0, _("no %% directive in format string %s"), quote (fmt));
     202           8 :       usage (EXIT_FAILURE);
     203             :     }
     204          18 :   else if (1 < n_directives)
     205           1 :     error (EXIT_FAILURE, 0, _("too many %% directives in format string %s"),
     206             :            quote (fmt));
     207          17 : }
     208             : 
     209             : /* If FORMAT is a valid printf format for a double argument, return
     210             :    its long double equivalent, possibly allocated from dynamic
     211             :    storage, and store into *LAYOUT a description of the output layout;
     212             :    otherwise, return NULL.  */
     213             : 
     214             : static char const *
     215          17 : long_double_format (char const *fmt, struct layout *layout)
     216             : {
     217             :   size_t i;
     218          17 :   size_t prefix_len = 0;
     219          17 :   size_t suffix_len = 0;
     220             :   size_t length_modifier_offset;
     221             :   bool has_L;
     222             : 
     223          81 :   for (i = 0; ! (fmt[i] == '%' && fmt[i + 1] != '%'); i += (fmt[i] == '%') + 1)
     224          74 :     if (fmt[i])
     225          64 :       prefix_len++;
     226             :     else
     227          10 :       return NULL;
     228             : 
     229           7 :   i++;
     230           7 :   i += strspn (fmt + i, "-+#0 '");
     231           7 :   i += strspn (fmt + i, "0123456789");
     232           7 :   if (fmt[i] == '.')
     233             :     {
     234           2 :       i++;
     235           2 :       i += strspn (fmt + i, "0123456789");
     236             :     }
     237             : 
     238           7 :   length_modifier_offset = i;
     239           7 :   has_L = (fmt[i] == 'L');
     240           7 :   i += has_L;
     241             :   /* In a valid format string, fmt[i] must be one of these specifiers.  */
     242           7 :   if (fmt[i] == '\0' || ! strchr ("efgaEFGA", fmt[i]))
     243           5 :     return NULL;
     244             : 
     245           3 :   for (i++; ! (fmt[i] == '%' && fmt[i + 1] != '%'); i += (fmt[i] == '%') + 1)
     246           2 :     if (fmt[i])
     247           1 :       suffix_len++;
     248             :     else
     249             :       {
     250           1 :         size_t format_size = i + 1;
     251           1 :         char *ldfmt = xmalloc (format_size + 1);
     252           1 :         memcpy (ldfmt, fmt, length_modifier_offset);
     253           1 :         ldfmt[length_modifier_offset] = 'L';
     254           1 :         strcpy (ldfmt + length_modifier_offset + 1,
     255           1 :                 fmt + length_modifier_offset + has_L);
     256           1 :         layout->prefix_len = prefix_len;
     257           1 :         layout->suffix_len = suffix_len;
     258           1 :         return ldfmt;
     259             :       }
     260             : 
     261           1 :   return NULL;
     262             : }
     263             : 
     264             : /* Return the absolute relative difference from x to y.  */
     265             : static double
     266           0 : abs_rel_diff (double x, double y)
     267             : {
     268           0 :   double s = (y == 0.0 ? 1 : y);
     269           0 :   return fabs ((y - x) / s);
     270             : }
     271             : 
     272             : /* Actually print the sequence of numbers in the specified range, with the
     273             :    given or default stepping and format.  */
     274             : 
     275             : static void
     276           0 : print_numbers (char const *fmt, struct layout layout,
     277             :                long double first, long double step, long double last)
     278             : {
     279           0 :   bool out_of_range = (step < 0 ? first < last : last < first);
     280             : 
     281           0 :   if (! out_of_range)
     282             :     {
     283           0 :       long double x = first;
     284             :       long double i;
     285             : 
     286           0 :       for (i = 1; ; i++)
     287           0 :         {
     288           0 :           long double x0 = x;
     289           0 :           printf (fmt, x);
     290           0 :           if (out_of_range)
     291           0 :             break;
     292           0 :           x = first + i * step;
     293           0 :           out_of_range = (step < 0 ? x < last : last < x);
     294             : 
     295           0 :           if (out_of_range)
     296             :             {
     297             :               /* If the number just past LAST prints as a value equal
     298             :                  to LAST, and prints differently from the previous
     299             :                  number, then print the number.  This avoids problems
     300             :                  with rounding.  For example, with the x86 it causes
     301             :                  "seq 0 0.000001 0.000003" to print 0.000003 instead
     302             :                  of stopping at 0.000002.  */
     303             : 
     304           0 :               bool print_extra_number = false;
     305             :               long double x_val;
     306             :               char *x_str;
     307           0 :               int x_strlen = asprintf (&x_str, fmt, x);
     308           0 :               if (x_strlen < 0)
     309           0 :                 xalloc_die ();
     310           0 :               x_str[x_strlen - layout.suffix_len] = '\0';
     311             : 
     312           0 :               if (xstrtold (x_str + layout.prefix_len, NULL, &x_val, c_strtold)
     313           0 :                   && abs_rel_diff (x_val, last) < DBL_EPSILON)
     314             :                 {
     315           0 :                   char *x0_str = NULL;
     316           0 :                   if (asprintf (&x0_str, fmt, x0) < 0)
     317           0 :                     xalloc_die ();
     318           0 :                   print_extra_number = !STREQ (x0_str, x_str);
     319           0 :                   free (x0_str);
     320             :                 }
     321             : 
     322           0 :               free (x_str);
     323           0 :               if (! print_extra_number)
     324           0 :                 break;
     325             :             }
     326             : 
     327           0 :           fputs (separator, stdout);
     328             :         }
     329             : 
     330           0 :       fputs (terminator, stdout);
     331             :     }
     332           0 : }
     333             : 
     334             : /* Return the default format given FIRST, STEP, and LAST.  */
     335             : static char const *
     336           0 : get_default_format (operand first, operand step, operand last)
     337             : {
     338             :   static char format_buf[sizeof "%0.Lf" + 2 * INT_STRLEN_BOUND (int)];
     339             : 
     340           0 :   int prec = MAX (first.precision, step.precision);
     341             : 
     342           0 :   if (prec != INT_MAX && last.precision != INT_MAX)
     343             :     {
     344           0 :       if (equal_width)
     345             :         {
     346             :           /* increase first_width by any increased precision in step */
     347           0 :           size_t first_width = first.width + (prec - first.precision);
     348             :           /* adjust last_width to use precision from first/step */
     349           0 :           size_t last_width = last.width + (prec - last.precision);
     350           0 :           if (last.precision && prec == 0)
     351           0 :             last_width--;  /* don't include space for '.' */
     352           0 :           size_t width = MAX (first_width, last_width);
     353           0 :           if (width <= INT_MAX)
     354             :             {
     355           0 :               int w = width;
     356           0 :               sprintf (format_buf, "%%0%d.%dLf", w, prec);
     357           0 :               return format_buf;
     358             :             }
     359             :         }
     360             :       else
     361             :         {
     362           0 :           sprintf (format_buf, "%%.%dLf", prec);
     363           0 :           return format_buf;
     364             :         }
     365             :     }
     366             : 
     367           0 :   return "%Lg";
     368             : }
     369             : 
     370             : int
     371          57 : main (int argc, char **argv)
     372             : {
     373             :   int optc;
     374          57 :   operand first = { 1, 1, 0 };
     375          57 :   operand step = { 1, 1, 0 };
     376             :   operand last;
     377          57 :   struct layout layout = { 0, 0 };
     378             : 
     379             :   /* The printf(3) format used for output.  */
     380          57 :   char const *format_str = NULL;
     381             : 
     382             :   initialize_main (&argc, &argv);
     383          57 :   program_name = argv[0];
     384          57 :   setlocale (LC_ALL, "");
     385             :   bindtextdomain (PACKAGE, LOCALEDIR);
     386             :   textdomain (PACKAGE);
     387             : 
     388          57 :   atexit (close_stdout);
     389             : 
     390          57 :   equal_width = false;
     391          57 :   separator = "\n";
     392             : 
     393             :   /* We have to handle negative numbers in the command line but this
     394             :      conflicts with the command line arguments.  So explicitly check first
     395             :      whether the next argument looks like a negative number.  */
     396         149 :   while (optind < argc)
     397             :     {
     398          86 :       if (argv[optind][0] == '-'
     399          81 :           && ((optc = argv[optind][1]) == '.' || ISDIGIT (optc)))
     400             :         {
     401             :           /* means negative number */
     402             :           break;
     403             :         }
     404             : 
     405          55 :       optc = getopt_long (argc, argv, "+f:s:w", long_options, NULL);
     406          55 :       if (optc == -1)
     407           9 :         break;
     408             : 
     409          46 :       switch (optc)
     410             :         {
     411          28 :         case 'f':
     412          28 :           format_str = optarg;
     413          28 :           break;
     414             : 
     415           1 :         case 's':
     416           1 :           separator = optarg;
     417           1 :           break;
     418             : 
     419           6 :         case 'w':
     420           6 :           equal_width = true;
     421           6 :           break;
     422             : 
     423           2 :         case_GETOPT_HELP_CHAR;
     424             : 
     425           1 :         case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
     426             : 
     427           8 :         default:
     428           8 :           usage (EXIT_FAILURE);
     429             :         }
     430             :     }
     431             : 
     432          46 :   if (argc - optind < 1)
     433             :     {
     434           7 :       error (0, 0, _("missing operand"));
     435           7 :       usage (EXIT_FAILURE);
     436             :     }
     437             : 
     438          39 :   if (3 < argc - optind)
     439             :     {
     440           0 :       error (0, 0, _("extra operand %s"), quote (argv[optind + 3]));
     441           0 :       usage (EXIT_FAILURE);
     442             :     }
     443             : 
     444          39 :   if (format_str)
     445             :     {
     446          26 :       validate_format (format_str);
     447          17 :       char const *f = long_double_format (format_str, &layout);
     448          17 :       if (! f)
     449             :         {
     450          16 :           error (0, 0, _("invalid format string: %s"), quote (format_str));
     451          16 :           usage (EXIT_FAILURE);
     452             :         }
     453           1 :       format_str = f;
     454             :     }
     455             : 
     456          14 :   last = scan_arg (argv[optind++]);
     457             : 
     458           2 :   if (optind < argc)
     459             :     {
     460           2 :       first = last;
     461           2 :       last = scan_arg (argv[optind++]);
     462             : 
     463           0 :       if (optind < argc)
     464             :         {
     465           0 :           step = last;
     466           0 :           last = scan_arg (argv[optind++]);
     467             :         }
     468             :     }
     469             : 
     470           0 :   if (format_str != NULL && equal_width)
     471             :     {
     472           0 :       error (0, 0, _("\
     473             : format string may not be specified when printing equal width strings"));
     474           0 :       usage (EXIT_FAILURE);
     475             :     }
     476             : 
     477           0 :   if (format_str == NULL)
     478           0 :     format_str = get_default_format (first, step, last);
     479             : 
     480           0 :   print_numbers (format_str, layout, first.value, step.value, last.value);
     481             : 
     482           0 :   exit (EXIT_SUCCESS);
     483             : }

Generated by: LCOV version 1.10