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)