Hi,

head(1) takes line count arguments in two ways.  The legacy (1977)
syntax is "-count" [1].  The "new" (1992) syntax is "-n count" [2].
In either case, "count" must be a positive decimal value.

Somewhere along the way, support for the legacy syntax was neutered.
At present it only works as expected if -count is the first option
argument to head(1), i.e. this works:

        $ head -20 file

but this is an error:

        $ head -20 -25 file

I'm not a fan of this.  If we're going to support the legacy syntax I
think it should behave like option arguments for other utilities.  You
should be able to specify -count multiple times.  We do this for
tail(1), which has a similar legacy syntax problem.

The easiest way to transparently support such invocations is to use
the GNU double-colon extension for getopt(3).  We then rebuild the
number string with asprintf(3), parse it with strtonum(3), and free
the string.

Before:

$ jot 10 | head -5 -6
head: unknown option -- 6
usage: head [-count | -n count] [file ...]

After:

$ jot 10 | head -5 -6
1
2
3
4
5
6

--

ok?

[1] 
https://svnweb.freebsd.org/csrg/usr.bin/head/head.c?revision=1026&view=markup

[2] 
https://svnweb.freebsd.org/csrg/usr.bin/head/head.c?revision=52824&view=markup

Index: head.c
===================================================================
RCS file: /cvs/src/usr.bin/head/head.c,v
retrieving revision 1.22
diff -u -p -r1.22 head.c
--- head.c      10 Oct 2021 15:57:25 -0000      1.22
+++ head.c      10 Oct 2021 16:44:01 -0000
@@ -49,27 +49,30 @@ int
 main(int argc, char *argv[])
 {
        const char *errstr;
+       char *str;
        FILE    *fp;
        long    cnt;
        int     ch, firsttime;
        long    linecnt = 10;
-       int     status = 0;
+       int error, status = 0;
 
        if (pledge("stdio rpath", NULL) == -1)
                err(1, "pledge");
 
-       /* handle obsolete -number syntax */
-       if (argc > 1 && argv[1][0] == '-' &&
-           isdigit((unsigned char)argv[1][1])) {
-               linecnt = strtonum(argv[1] + 1, 1, LONG_MAX, &errstr);
-               if (errstr != NULL)
-                       errx(1, "count is %s: %s", errstr, argv[1] + 1);
-               argc--;
-               argv++;
-       }
-
-       while ((ch = getopt(argc, argv, "n:")) != -1) {
+#define OPTSTR "0::1::2::3::4::5::6::7::8::9::n:"
+       while ((ch = getopt(argc, argv, OPTSTR)) != -1) {
                switch (ch) {
+               case '0': case '1': case '2': case '3': case '4':
+               case '5': case '6': case '7': case '8': case '9':
+                       error = asprintf(&str, "%c%s",
+                           ch, (optarg == NULL) ? "" : optarg);
+                       if (error == -1)
+                               err(1, "asprintf");
+                       linecnt = strtonum(str, 1, LONG_MAX, &errstr);
+                       if (errstr != NULL)
+                               errx(1, "count is %s: %s", errstr, str);
+                       free(str);
+                       break;
                case 'n':
                        linecnt = strtonum(optarg, 1, LONG_MAX, &errstr);
                        if (errstr != NULL)

Reply via email to