NetBSD Problem Report #59828
From www@netbsd.org Wed Dec 10 07:09:01 2025
Return-Path: <www@netbsd.org>
Received: from mail.netbsd.org (mail.netbsd.org [199.233.217.200])
(using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)
key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256
client-signature RSA-PSS (2048 bits) client-digest SHA256)
(Client CN "mail.NetBSD.org", Issuer "mail.NetBSD.org CA" (not verified))
by mollari.NetBSD.org (Postfix) with ESMTPS id A45C91A9239
for <gnats-bugs@gnats.NetBSD.org>; Wed, 10 Dec 2025 07:09:01 +0000 (UTC)
Message-Id: <20251210070900.48EC81A923A@mollari.NetBSD.org>
Date: Wed, 10 Dec 2025 07:09:00 +0000 (UTC)
From: rootnode+netbsd@wollwage.com
Reply-To: rootnode+netbsd@wollwage.com
To: gnats-bugs@NetBSD.org
Subject: getopt(3) GNU extension wrong behavior
X-Send-Pr-Version: www-1.0
>Number: 59828
>Category: lib
>Synopsis: getopt(3) GNU extension wrong behavior
>Confidential: no
>Severity: non-critical
>Priority: low
>Responsible: lib-bug-people
>State: open
>Class: change-request
>Submitter-Id: net
>Arrival-Date: Wed Dec 10 07:10:01 +0000 2025
>Last-Modified: Thu Dec 11 17:40:00 +0000 2025
>Originator: Simon Wollwage
>Release: getopt(3) GNU extension wrong behavior
>Organization:
>Environment:
NetBSD 11.99.4 NetBSD 11.99.4 (GENERIC) #0: Mon Dec 8 23:00:22 UTC 2025 mkrepro@mkrepro.NetBSD.org:/usr/src/sys/arch/amd
64/compile/GENERIC amd64
>Description:
Originally posted in freebsd-bugs@freebsd.org (see https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=291374)
From the getopt(3) manual page:
The option string optstring may contain the following elements:
individual characters, and characters followed by a colon to indicate an
option argument is to follow. If an individual character is followed by
two colons, then the option argument is optional; optarg is set to the
rest of the current argv word, or NULL if there were no more characters
in the current word. This is a GNU extension. For example, an option
string "x" recognizes an option "-x", and an option string "x:"
recognizes an option and argument "-x argument". It does not matter to
getopt() if a following argument has leading white space.
Note the last clause about leading white space. It does not match the code.
>How-To-Repeat:
For example, our date(1) utility uses getopt(argc, argv, "f:I::jnRr:uv:z:") and look at this:
$ date -Idate
2025-12-04
$ date -I date
date: illegal time format
usage: date [-jnRu] [-I[date|hours|minutes|seconds]] [-f input_fmt]
[-r filename|seconds] [-v[+|-]val[y|m|w|d|H|M|S]]
[[[[[[cc]yy]mm]dd]HH]MM[.SS] | new_date] [+output_fmt]
#include <stdio.h>
#include <unistd.h>
int
main(int argc, char *argv[])
{
int ch;
while ((ch = getopt(argc, argv, "cCd::F:lnoOp:s:u:U:wW")) != -1) {
printf("ch is %c optarg is %s\n",
(char)ch, optarg ? optarg : " NULL");
}
return (0);
}
Option list taken from our tftpd(8) programm that uses "-s path" for a chroot and optional "-d value" for debugging.
Build it: cc -o getoptest -ansi -pedantic -Wall getoptest.c
Run it:
$ ./getoptest -l -w -d14 -s /usr/local/admin/tftproot -u admin -U 0
ch is l optarg is NULL
ch is w optarg is NULL
ch is d optarg is 14
ch is s optarg is /usr/local/admin/tftproot
ch is u optarg is admin
ch is U optarg is 0
So far, so good. And now:
$ ./getoptest -l -w -d 14 -s /usr/local/admin/tftproot -u admin -U 0
ch is l optarg is NULL
ch is w optarg is NULL
ch is d optarg is NULL
Option list silently truncated. No -s, no chrooting for tftpd.
>Fix:
Index: common/lib/libc/stdlib/getopt.c
===================================================================
RCS file: /cvsroot/src/common/lib/libc/stdlib/getopt.c,v
retrieving revision 1.2
diff -u -r1.2 getopt.c
--- common/lib/libc/stdlib/getopt.c 29 Jun 2024 07:56:56 -0000 1.2
+++ common/lib/libc/stdlib/getopt.c 10 Dec 2025 07:06:08 -0000
@@ -128,13 +128,17 @@
entire next argument. */
if (*place)
optarg = __UNCONST(place);
- else if (oli[2] == ':')
+ else if (oli[2] == ':') {
/*
* GNU Extension, for optional arguments if the rest of
* the argument is empty, we return NULL
*/
- optarg = NULL;
- else if (nargc > ++optind)
+ if (nargc > (optind + 1) && nargv[optind + 1][0] != '-') {
+ optarg = nargv[++optind];
+ } else {
+ optarg = NULL;
+ }
+ } else if (nargc > ++optind)
optarg = nargv[optind];
else {
/* option-argument absent */
>Audit-Trail:
From: mlelstv@serpens.de (Michael van Elst)
To: gnats-bugs@netbsd.org
Cc:
Subject: Re: lib/59828: getopt(3) GNU extension wrong behavior
Date: Wed, 10 Dec 2025 09:18:22 -0000 (UTC)
rootnode+netbsd@wollwage.com writes:
>$ date -Idate
>2025-12-04
>$ date -I date
>date: illegal time format
>usage: date [-jnRu] [-I[date|hours|minutes|seconds]] [-f input_fmt]
> [-r filename|seconds] [-v[+|-]val[y|m|w|d|H|M|S]]
> [[[[[[cc]yy]mm]dd]HH]MM[.SS] | new_date] [+output_fmt]
NetBSD date doesn't have an -I option.
The coreutils package from pkgsrc contains a date command, but
which works the same as on Linux:
NetBSD-10, coreutils-9.6nb1:
% /usr/pkg/gnu/bin/date -I date
date: invalid date date
% /usr/pkg/gnu/bin/date -"I date"
date: invalid argument date for --iso-8601
Valid arguments are:
- hours
- minutes
- date
- seconds
- ns
Try '/usr/pkg/gnu/bin/date --help' for more information.
% /usr/pkg/gnu/bin/date -Idate
2025-12-10
Debian trixie, coreutils 9.7-3:
$ date -I date
date: invalid date 'date'
$ date -"I date"
date: invalid argument ' date' for '--iso-8601'
Valid arguments are:
- 'hours'
- 'minutes'
- 'date'
- 'seconds'
- 'ns'
Try 'date --help' for more information.
$ date -Idate
2025-12-10
I don't think your interpretation of the manual page is correct,
but without an example for "x::" maybe misleading.
"If an individual character is followed by two colons, then the
option argument is optional; optarg is set to the rest of the
current argv word, or NULL if there were no more characters in the
current word."
So, the option argument is "the rest of the current argv word" or "NULL".
It is never "the following word if that doesn't start with a dash".
No idea why FreeBSD calls it a "GNU extension" while NetBSD calls it
a "NetBSD extension" or who actually created it...
From: Valery Ushakov <uwe@stderr.spb.ru>
To: gnats-bugs@netbsd.org
Cc:
Subject: Re: lib/59828: getopt(3) GNU extension wrong behavior
Date: Wed, 10 Dec 2025 14:30:13 +0300
I haven't surveyed all of our base system, but at least sed(1) uses
double-colon for -i[suffix]
-uwe
From: Jose Luis Duran <jlduran@gmail.com>
To: gnats-bugs@netbsd.org
Cc:
Subject: Re: lib/59828: getopt(3) GNU extension wrong behavior
Date: Wed, 10 Dec 2025 12:07:43 -0300
There appears to have been some confusion when filing this bug report.
It was my suggestion to report it here, since FreeBSD's getopt(3)
comes from NetBSD.
Given the following MWE script (getopttest.c):
#include <stdio.h>
#include <unistd.h>
int
main(int argc, char *argv[])
{
int ch;
while ((ch = getopt(argc, argv, "cCd::F:lnoOp:s:u:U:wW")) != -1) {
printf("ch is %c optarg is %s\n", (char)ch, optarg ? optarg : " NULL");
}
return (0);
}
Notice the "d" option has double colons, and is the subject at hand.
By reading the manual page for getopt(3):
If an individual character is followed by two colons,
then the option argument is optional; optarg is set
to the rest of the current argv word,
or NULL if there were no more characters in the current word.
This is a NetBSD extension.
The following output from the above MWE program is expected (or is it not?):
1. With a space between "-d" and the option argument:
$ ./getopttest -l -w -d 14 -s /usr/local/admin/tftproot -u admin -U 0
ch is l optarg is NULL
ch is w optarg is NULL
ch is d optarg is 14 # <- XXX [1]
ch is s optarg is /usr/local/admin/tftproot # <- XXX missing
ch is u optarg is admin # <- XXX missing
ch is U optarg is 0 # <- XXX missing
[1]: "It does not matter to getopt() if a following argument has
leading whitespace."
Current output:
$ ./getopttest -l -w -d 14 -s /usr/local/admin/tftproot -u admin -U 0
ch is l optarg is NULL
ch is w optarg is NULL
ch is d optarg is NULL
Notice the arguments after "d" are missing.
For completeness, all these other tests currently pass on NetBSD:
2. Without a space between "-d" and the option argument:
$ ./getopttest -l -w -d14 -s /usr/local/admin/tftproot -u admin -U 0
ch is l optarg is NULL
ch is w optarg is NULL
ch is d optarg is 14
ch is s optarg is /usr/local/admin/tftproot
ch is u optarg is admin
ch is U optarg is 0
3. The "d" option without any arguments:
$ ./getopttest -l -w -d -s /usr/local/admin/tftproot -u admin -U 0
ch is l optarg is NULL
ch is w optarg is NULL
ch is d optarg is NULL
ch is s optarg is /usr/local/admin/tftproot
ch is u optarg is admin
ch is U optarg is 0
4. Invalid options after the "d" option:
./getopttest -l -w -d -14 -s /usr/local/admin/tftproot -u admin -U 0
ch is l optarg is NULL
ch is w optarg is NULL
ch is d optarg is NULL
getopttest: unknown option -- 1
ch is ? optarg is NULL
getopttest: unknown option -- 4
ch is ? optarg is NULL
ch is s optarg is /usr/local/admin/tftproot
ch is u optarg is admin
ch is U optarg is 0
Regards,
From: Simon Wollwage <rootnode@wollwage.com>
To: "Michael van Elst via gnats" <gnats-admin@NetBSD.org>
Cc: lib-bug-people@netbsd.org, netbsd-bugs@netbsd.org,
rootnode+netbsd@wollwage.com, gnats-bugs@netbsd.org
Subject: Re: lib/59828: getopt(3) GNU extension wrong behavior
Date: Wed, 10 Dec 2025 22:57:54 +0900
"Michael van Elst via gnats" <gnats-admin@NetBSD.org> writes:
> NetBSD date doesn't have an -I option.
>
> The coreutils package from pkgsrc contains a date command, but
> which works the same as on Linux:
>
> NetBSD-10, coreutils-9.6nb1:
>
> % /usr/pkg/gnu/bin/date -I date
> date: invalid date date
Sorry, copied that stupidly forgetting that the date tool might be
different. First contribution, so I didn't have that on my radar.
> I don't think your interpretation of the manual page is correct,
> but without an example for "x::" maybe misleading.
>From a quick perusal it seems that sed ant syslogd for example are using
it. On the ports side it might be more.
> "If an individual character is followed by two colons, then the
> option argument is optional; optarg is set to the rest of the
> current argv word, or NULL if there were no more characters in the
> current word."
>
> So, the option argument is "the rest of the current argv word" or "NULL".
> It is never "the following word if that doesn't start with a dash".
The paragraph ends with
"It does not matter to getopt() if a following argument has leading
whitespace."
I guess that is the part I am unsure about and interpretet as also
counting for the double colon case.
> No idea why FreeBSD calls it a "GNU extension" while NetBSD calls it
> a "NetBSD extension" or who actually created it...
In the source code to getopt it is calledt a GNU extension.
If the consensus is that the wording about the whitespace doesn't apply
to the double colon case, then happy to drop it. From a consistency or
UX viewpoint I would say it makes sense to allow the whitespace for
both, single and double colon.
From: Valery Ushakov <uwe@stderr.spb.ru>
To: gnats-bugs@netbsd.org
Cc:
Subject: Re: lib/59828: getopt(3) GNU extension wrong behavior
Date: Wed, 10 Dec 2025 19:47:32 +0300
On Wed, Dec 10, 2025 at 15:10:02 +0000, Jose Luis Duran via gnats wrote:
> while ((ch = getopt(argc, argv, "cCd::F:lnoOp:s:u:U:wW")) != -1) {
> printf("ch is %c optarg is %s\n", (char)ch, optarg ? optarg : " NULL");
> }
[...]
> Notice the "d" option has double colons, and is the subject at hand.
> By reading the manual page for getopt(3):
>
> If an individual character is followed by two colons,
> then the option argument is optional; optarg is set
> to the rest of the current argv word,
> or NULL if there were no more characters in the current word.
> This is a NetBSD extension.
[...]
> 1. With a space between "-d" and the option argument:
So you are making a conceptual mistake here. The phrase above is
contradiction in terms.
As the man page says, for double colon optios the argument is:
optarg is set to the rest of the current argv word ...
If the current argv word, "-d", it does not have anything after 'd',
there's no argument. The space that delimts "-d" word is not
"between" anything.
If the option were "-d14", optarg would get set to point after 'd', to
the "14" part of the "-d14" word.
> Current output:
> $ ./getopttest -l -w -d 14 -s /usr/local/admin/tftproot -u admin -U 0
> ch is l optarg is NULL
> ch is w optarg is NULL
> ch is d optarg is NULL
>
> Notice the arguments after "d" are missing.
Right, b/c "14" is not an option, so getopt stops.
-uwe
From: Simon Wollwage <rootnode+netbsd@wollwage.com>
To: <gnats-bugs@NetBSD.org>
Cc:
Subject: Re: lib/59828: getopt(3) GNU extension wrong behavior
Date: Thu, 11 Dec 2025 02:08:45 +0900
"Valery Ushakov via gnats" <gnats-admin@NetBSD.org> writes:
> So you are making a conceptual mistake here. The phrase above is
> contradiction in terms.
>
> As the man page says, for double colon optios the argument is:
>
> optarg is set to the rest of the current argv word ...
>
> If the current argv word, "-d", it does not have anything after 'd',
> there's no argument. The space that delimts "-d" word is not
> "between" anything.
Isn't that maybe then just an issue with the wording? The description
of the extension in the man page also says that whitespace doesn't
matter in this case.
Or at least a conceptual issue. If non-optional arguments, like "-s
/usr/local/admin/tftproot" are allowed to have a whitespace between the
option and its value, why not also optional arguments?
From: Valery Ushakov <uwe@stderr.spb.ru>
To: gnats-bugs@netbsd.org
Cc:
Subject: Re: lib/59828: getopt(3) GNU extension wrong behavior
Date: Wed, 10 Dec 2025 20:49:19 +0300
On Wed, Dec 10, 2025 at 17:10:02 +0000, Simon Wollwage via gnats wrote:
> Isn't that maybe then just an issue with the wording? The description
> of the extension in the man page also says that whitespace doesn't
> matter in this case.
But the description you refer to is not the description of the
extension. It's the description of the "x:" example (I think Michael
has pointed that out already):
For example, an option string "x" recognizes an option “-x”, and
an option string "x:" recognizes an option and argument “-x
argument”. It does not matter to getopt() if a following
argument has leading whitespace.
Yes, it's awkward and needs to be made less confusing.
-uwe
From: Simon Wollwage <rootnode+netbsd@wollwage.com>
To: gnats-bugs@NetBSD.org
Cc:
Subject: Re: lib/59828: getopt(3) GNU extension wrong behavior
Date: Thu, 11 Dec 2025 03:02:05 +0900
"Valery Ushakov via gnats" <gnats-admin@NetBSD.org> writes:
> Yes, it's awkward and needs to be made less confusing.
I just went and checked what OpenBSD is doing and there the manpage
states
"It does not matter to getopt() if a following argument has leading
whitespace; except in the case where the argument is optional, denoted
with two colons, no leading whitespace is permitted."
That is less confusing (even if I think that UX might be better if the
whitespace were allowed, but that is besides the point.
Happy to rescind the submitted patch and create one to update the
manpage to remove the confusion.
From: Valery Ushakov <uwe@stderr.spb.ru>
To: gnats-bugs@netbsd.org
Cc:
Subject: Re: lib/59828: getopt(3) GNU extension wrong behavior
Date: Thu, 11 Dec 2025 12:31:21 +0300
On Wed, Dec 10, 2025 at 18:05:01 +0000, Simon Wollwage via gnats wrote:
> That is less confusing (even if I think that UX might be better if the
> whitespace were allowed, but that is besides the point.
The behaviour cannot be changed b/c that would break people's scripts
that do things like (a random example off the top of my head):
sed -i '/^constty/s/off/on/' $target/etc/ttys
That also opens a can of worms with an optarg that starts with a dash,
e.g.
sed -i -OLD ...
-uwe
From: Robert Elz <kre@munnari.OZ.AU>
To: gnats-bugs@netbsd.org
Cc:
Subject: Re: lib/59828: getopt(3) GNU extension wrong behavior
Date: Thu, 11 Dec 2025 17:37:40 +0700
Date: Wed, 10 Dec 2025 18:05:01 +0000 (UTC)
From: "Simon Wollwage via gnats" <gnats-admin@NetBSD.org>
Message-ID: <20251210180501.BE59C1A923A@mollari.NetBSD.org>
| That is less confusing (even if I think that UX might be better if the
| whitespace were allowed, but that is besides the point.
It is impossible, think of what happens if either:
1) you want the optarg to -d to be -14 instead of 14, what would your
proposed patch to with cmd -d -14 ... ?
2) there are no more options after the -d (whose optional value is not
wanted) and the next arg is a filename (or something - a positional arg).
Is the plan to always require using the -- arg? Or given 1 above
would that -- just be treated as the optarg to -d (cmd -d -- whatever)
With single : options, where the optarg is required, it doesn't matter,
if the optarg value doesn't immediately follow the option character
(without intervening anything) then the next arg is the optarg value,
whatever it looks like (if there is none it is an error).
But with optional optargs (::) there needs to be a clear, obvious, way
to determine if the optional optarg is there or not). And it has to work
in every conceivable case.
kre
From: Simon Wollwage <rootnode@wollwage.com>
To: gnats-bugs@NetBSD.org
Cc:
Subject: Re: lib/59828: getopt(3) GNU extension wrong behavior
Date: Thu, 11 Dec 2025 21:10:45 +0900
"Robert Elz via gnats" <gnats-admin@NetBSD.org> writes:
> With single : options, where the optarg is required, it doesn't matter,
> if the optarg value doesn't immediately follow the option character
> (without intervening anything) then the next arg is the optarg value,
> whatever it looks like (if there is none it is an error).
>
> But with optional optargs (::) there needs to be a clear, obvious, way
> to determine if the optional optarg is there or not). And it has to work
> in every conceivable case.
Agreed. But maybe at least the wording can be made clearer in the man
page. See the additional text in the OpenBSD version for example.
If that is acceptable I'll be happy to make a patch for that.
From: "Valery Ushakov" <uwe@netbsd.org>
To: gnats-bugs@gnats.NetBSD.org
Cc:
Subject: PR/59828 CVS commit: src/lib/libc/stdlib
Date: Thu, 11 Dec 2025 14:07:23 +0000
Module Name: src
Committed By: uwe
Date: Thu Dec 11 14:07:23 UTC 2025
Modified Files:
src/lib/libc/stdlib: getopt.3
Log Message:
getopt(3): be more explicit about :: extension
Make it possible to search for literal two colons (::) and actually
find something. Make the "x"/"x:"/"x::" examples more explicit and
more visibile.
PR lib/59828
To generate a diff of this commit:
cvs rdiff -u -r1.36 -r1.37 src/lib/libc/stdlib/getopt.3
Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.
From: Valery Ushakov <uwe@stderr.spb.ru>
To: gnats-bugs@netbsd.org
Cc:
Subject: Re: lib/59828: getopt(3) GNU extension wrong behavior
Date: Thu, 11 Dec 2025 17:12:01 +0300
On Thu, Dec 11, 2025 at 12:15:01 +0000, Simon Wollwage via gnats wrote:
> If that is acceptable I'll be happy to make a patch for that.
I don't like the openbsd wording either. (E.g. pedantically speaking
both -x' foo' and -x ' foo' are examples of "argument has leading
whitespace").
I've commited my own take. Let me know if it could be improved.
Thanks!
-uwe
From: Simon Wollwage <rootnode+netbsd@wollwage.com>
To: gnats-bugs@NetBSD.org
Cc:
Subject: Re: lib/59828: getopt(3) GNU extension wrong behavior
Date: Thu, 11 Dec 2025 23:51:58 +0900
"Valery Ushakov via gnats" <gnats-admin@NetBSD.org> writes:
> I don't like the openbsd wording either. (E.g. pedantically speaking
> both -x' foo' and -x ' foo' are examples of "argument has leading
> whitespace").
>
> I've commited my own take. Let me know if it could be improved.
I guess that looks ok. I would have preferred to give it a shot first,
but that is ok.
The part of the OpenBSD wording I was referring to was the "except in
the case where the argument is optional, denoted with two colons, no
leading whitespace is permitted." part, which I personally think is
quite concise as a small addendum and clears up the confusion.
It keeps most of the existing wording intact and only covers the edge
case of the whitespace for optional arguments for that original GNU
extension.
If the feeling is that the provided patch is clearer then I will yield
to that. An improved wording is an improved wording. Not a hill to
fight on :D
From: Valery Ushakov <uwe@stderr.spb.ru>
To: gnats-bugs@netbsd.org
Cc:
Subject: Re: lib/59828: getopt(3) GNU extension wrong behavior
Date: Thu, 11 Dec 2025 20:36:30 +0300
On Thu, Dec 11, 2025 at 14:55:01 +0000, Simon Wollwage via gnats wrote:
> The part of the OpenBSD wording I was referring to was the "except in
> the case where the argument is optional, denoted with two colons, no
> leading whitespace is permitted." part, which I personally think is
> quite concise as a small addendum and clears up the confusion.
But that's exactly the problem, it's a run-on sentence that is also
technically incorrect ("leading whitespace" is wrong notion here). I
wanted to provide a very explicit example that one can immediately
see, not an anstract description that requres further exegesis.
> An improved wording is an improved wording. Not a hill to
> fight on :D
Right :)
-uwe
(Contact us)
$NetBSD: query-full-pr,v 1.51 2026/08/10 02:28:17 riastradh Exp $
$NetBSD: gnats_config.sh,v 1.10 2026/05/13 22:00:09 riastradh Exp $
Copyright © 1994-2026
The NetBSD Foundation, Inc. ALL RIGHTS RESERVED.