NetBSD-Bugs archive
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]
lib/59828: getopt(3) GNU extension wrong behavior
>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
>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@localhost:/usr/src/sys/arch/amd
64/compile/GENERIC amd64
>Description:
Originally posted in freebsd-bugs%freebsd.org@localhost (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 */
Home |
Main Index |
Thread Index |
Old Index