【问题标题】:what's the portable way to reset getopt()?重置getopt()的便携式方法是什么?
【发布时间】:2020-06-14 10:44:27
【问题描述】:

我将getopt() 机器瞄准我自己的类似argv 的阵列,在真正的argv 上使用它之后。接口是不可重入的(保持状态),并且各种实现以不同的方式重置。

  • 在 XPG3/SVID 中为 optreset = 1;(freebsd、macosx)
  • 在 XPG4/POSIX/SUS 中是 optind = 1;(suse tumbleweed)
  • 在 Linux/GLIBC 中也是 optind = 0; (debian)

我不想仅仅为了这个而自动配置。

#ifdef 的可靠集合是什么?

【问题讨论】:

标签: posix libc getopt sus


【解决方案1】:

唯一的标准是设置optind = 1

SUSv4 standard 没有提及optreset 并声明optind = 0“未指定”:

如果应用程序在调用getopt() 之前将optind 设置为零,则行为未指定。

否则,标准就含糊不清,根本没有说多次调用getopt(),既不允许也不声明未定义。

此外,没有定义说明是否支持optind = 0optreset(OpenBSD 支持前者,但 FreeBSD 不支持,Linux/musl 支持后者,但 Linux/glibc 不支持) ,所以没有任何可靠的方法可以通过#ifdefs 检测到它。

不是针对错误,在花哨的 getopt/_long 实现中(见下文),只需将 optind = 1 设置为重新启动 getopt(),只要

a)你没有使用任何GNU extensions

扫描多个参数向量或重新扫描相同的程序 vector 不止一次,并希望使用 GNU 扩展,例如 +- 在 optstring 的开头,或更改 POSIXLY_CORRECT 在两次扫描之间,必须通过重置重新初始化 getopt() optind0,

b) 在它返回-1 之前,您不会在中间重新启动getopt()。例如。来自FreeBSD's getopt

#define BADCH   (int)'?'
#define BADARG  (int)':'
#define EMSG    ""
...
int
getopt(int nargc, char * const nargv[], const char *ostr)
{
        static char *place = EMSG;              /* option letter processing */
        ...
        if (optreset || *place == 0) {          /* update scanning pointer */
                optreset = 0;
                ...
                        place = EMSG;
                        return (-1);
                ...
                        place = EMSG;
                        return (-1);
                ...
                        place = EMSG;
                        if (strchr(ostr, '-') == NULL)
                                return (-1);
        ...
                return (BADCH);
        ...
                                return (BADARG);
        ...
                        return (BADCH);
        ...
        return (optopt);                        /* return option letter */
}

错误

即使在返回-1 之后,getopt 的 glibc 实现也会cling to stale state。如果释放旧字符串,这可能会导致愚蠢的错误和崩溃。

OpenBSD 实现将执行likewise,但在使用虚假- 选项时,如getopt("q", ["prog", "-q-", NULL])。请注意,OpenBSD 的 getopt_long 实现可能已作为默认的 getopt() 进入其他系统,如 Solaris 和 Android。

【讨论】:

  • 测试了这些方法不能跨系统移植。我们发现在我们尝试过的任何地方都可以使用github.com/dnsdb/dnsdbq/commit/…
  • 不,它坏了。 Solaris 不支持optreset。您究竟在哪个系统上尝试了optind = 1(在答案中描述的条件下)但不起作用?
  • 我们安装了一个 Solaris 11.4 VM,对 git 和 cc 进行了 pkg 安装,然后能够使用“CC=gcc make”和上面 github 链接中显示的更改进行构建。
  • 太好了,也许他们已经在最新版本的 Solaris 中添加了optreset。或者您正在使用一些兼容库。但是,请问您在什么系统上尝试了“那些方法”(即如我的回答中所述)并且没有工作。谢谢。
  • mosvy,欢迎您的挑战。我们试图隔离一个测试用例。 (不,在 solaris 案例中没有兼容库。)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-11-16
  • 2023-03-17
  • 2015-07-18
  • 2011-04-24
  • 2010-09-21
  • 2023-03-26
相关资源
最近更新 更多