From 4a9ea4c1c3cd3dcd7b9000999becb31f8d21f107 Mon Sep 17 00:00:00 2001 From: Daniel Hams Date: Tue, 22 Oct 2019 23:49:36 +0100 Subject: [PATCH] Updates to add argp - move to 0.1.6 --- libdicl/configure.ac | 2 +- libdicl/gl/Makefile.am | 121 ++ libdicl/gl/argp-ba.c | 34 + libdicl/gl/argp-eexst.c | 30 + libdicl/gl/argp-fmtstream.c | 434 ++++++ libdicl/gl/argp-fmtstream.h | 310 ++++ libdicl/gl/argp-fs-xinl.c | 46 + libdicl/gl/argp-help.c | 1912 +++++++++++++++++++++++ libdicl/gl/argp-namefrob.h | 157 ++ libdicl/gl/argp-parse.c | 957 ++++++++++++ libdicl/gl/argp-pin.c | 33 + libdicl/gl/argp-pv.c | 33 + libdicl/gl/argp-pvh.c | 30 + libdicl/gl/argp-xinl.c | 46 + libdicl/gl/argp.h | 634 ++++++++ libdicl/gl/m4/argp.m4 | 62 + libdicl/gl/m4/gnulib-cache.m4 | 2 + libdicl/gl/m4/gnulib-comp.m4 | 107 +- libdicl/gl/m4/mempcpy.m4 | 26 + libdicl/gl/m4/strcase.m4 | 45 + libdicl/gl/m4/strings_h.m4 | 52 + libdicl/gl/m4/strndup.m4 | 58 + libdicl/gl/m4/strnlen.m4 | 30 + libdicl/gl/m4/sysexits.m4 | 44 + libdicl/gl/mempcpy.c | 28 + libdicl/{tests => gl}/sleep.c | 0 libdicl/gl/strcasecmp.c | 62 + libdicl/gl/strings.in.h | 122 ++ libdicl/gl/strncasecmp.c | 62 + libdicl/gl/strndup.c | 36 + libdicl/gl/strnlen.c | 30 + libdicl/gl/sysexits.in.h | 72 + libdicl/gnulibimportnotes.txt | 2 +- libdicl/src/repl_headers/Makefile.am | 4 + libdicl/tests/Makefile.am | 59 +- libdicl/tests/test-argp-2.sh | 113 ++ libdicl/tests/test-argp.c | 490 ++++++ libdicl/tests/test-getopt-gnu.c | 45 + libdicl/tests/test-getopt_long.h | 2144 ++++++++++++++++++++++++++ libdicl/tests/test-malloc-gnu.c | 31 + libdicl/tests/test-strings.c | 27 + libdicl/tests/test-strnlen.c | 68 + libdicl/tests/test-sysexits.c | 52 + 43 files changed, 8635 insertions(+), 17 deletions(-) create mode 100644 libdicl/gl/argp-ba.c create mode 100644 libdicl/gl/argp-eexst.c create mode 100644 libdicl/gl/argp-fmtstream.c create mode 100644 libdicl/gl/argp-fmtstream.h create mode 100644 libdicl/gl/argp-fs-xinl.c create mode 100644 libdicl/gl/argp-help.c create mode 100644 libdicl/gl/argp-namefrob.h create mode 100644 libdicl/gl/argp-parse.c create mode 100644 libdicl/gl/argp-pin.c create mode 100644 libdicl/gl/argp-pv.c create mode 100644 libdicl/gl/argp-pvh.c create mode 100644 libdicl/gl/argp-xinl.c create mode 100644 libdicl/gl/argp.h create mode 100644 libdicl/gl/m4/argp.m4 create mode 100644 libdicl/gl/m4/mempcpy.m4 create mode 100644 libdicl/gl/m4/strcase.m4 create mode 100644 libdicl/gl/m4/strings_h.m4 create mode 100644 libdicl/gl/m4/strndup.m4 create mode 100644 libdicl/gl/m4/strnlen.m4 create mode 100644 libdicl/gl/m4/sysexits.m4 create mode 100644 libdicl/gl/mempcpy.c rename libdicl/{tests => gl}/sleep.c (100%) create mode 100644 libdicl/gl/strcasecmp.c create mode 100644 libdicl/gl/strings.in.h create mode 100644 libdicl/gl/strncasecmp.c create mode 100644 libdicl/gl/strndup.c create mode 100644 libdicl/gl/strnlen.c create mode 100644 libdicl/gl/sysexits.in.h create mode 100755 libdicl/tests/test-argp-2.sh create mode 100644 libdicl/tests/test-argp.c create mode 100644 libdicl/tests/test-getopt-gnu.c create mode 100644 libdicl/tests/test-getopt_long.h create mode 100644 libdicl/tests/test-malloc-gnu.c create mode 100644 libdicl/tests/test-strings.c create mode 100644 libdicl/tests/test-strnlen.c create mode 100644 libdicl/tests/test-sysexits.c diff --git a/libdicl/configure.ac b/libdicl/configure.ac index 6977b88..ccf9fa8 100644 --- a/libdicl/configure.ac +++ b/libdicl/configure.ac @@ -1,6 +1,6 @@ AC_PREREQ(2.65) AC_INIT([libdicl], - [0.1.5], + [0.1.6], [daniel.hams@gmail.com]) AC_SUBST(ACLOCAL_AMFLAGS, "-I macros") diff --git a/libdicl/gl/Makefile.am b/libdicl/gl/Makefile.am index 17a2603..49677fb 100644 --- a/libdicl/gl/Makefile.am +++ b/libdicl/gl/Makefile.am @@ -34,6 +34,7 @@ # --no-libtool \ # --macro-prefix=gl \ # --no-vc-files \ +# argp \ # fprintf-posix \ # getline \ # getopt-posix \ @@ -127,6 +128,12 @@ EXTRA_DIST += alloca.in.h ## end gnulib module alloca-opt +## begin gnulib module argp + +libgnu_a_SOURCES += argp.h argp-ba.c argp-eexst.c argp-fmtstream.c argp-fmtstream.h argp-fs-xinl.c argp-help.c argp-namefrob.h argp-parse.c argp-pin.c argp-pv.c argp-pvh.c argp-xinl.c + +## end gnulib module argp + ## begin gnulib module c-ctype libgnu_a_SOURCES += c-ctype.h c-ctype.c @@ -544,6 +551,15 @@ EXTRA_libgnu_a_SOURCES += lstat.c ## end gnulib module lstat +## begin gnulib module malloc-gnu + + +EXTRA_DIST += malloc.c + +EXTRA_libgnu_a_SOURCES += malloc.c + +## end gnulib module malloc-gnu + ## begin gnulib module malloc-posix @@ -870,6 +886,15 @@ EXTRA_libgnu_a_SOURCES += memchr.c ## end gnulib module memchr +## begin gnulib module mempcpy + + +EXTRA_DIST += mempcpy.c + +EXTRA_libgnu_a_SOURCES += mempcpy.c + +## end gnulib module mempcpy + ## begin gnulib module mkdir @@ -1137,6 +1162,15 @@ libgnu_a_SOURCES += size_max.h ## end gnulib module size_max +## begin gnulib module sleep + + +EXTRA_DIST += sleep.c + +EXTRA_libgnu_a_SOURCES += sleep.c + +## end gnulib module sleep + ## begin gnulib module snippet/_Noreturn # Because this Makefile snippet defines a variable used by other @@ -1656,6 +1690,15 @@ EXTRA_DIST += stdlib.in.h ## end gnulib module stdlib +## begin gnulib module strcase + + +EXTRA_DIST += strcasecmp.c strncasecmp.c + +EXTRA_libgnu_a_SOURCES += strcasecmp.c strncasecmp.c + +## end gnulib module strcase + ## begin gnulib module strchrnul @@ -1785,6 +1828,55 @@ EXTRA_DIST += string.in.h ## end gnulib module string +## begin gnulib module strings + +BUILT_SOURCES += strings.h + +# We need the following in order to create when the system +# doesn't have one that works with the given compiler. +strings.h: strings.in.h $(top_builddir)/config.status $(CXXDEFS_H) $(WARN_ON_USE_H) $(ARG_NONNULL_H) + $(AM_V_GEN)rm -f $@-t $@ && \ + { echo '/* DO NOT EDIT! GENERATED AUTOMATICALLY! */' && \ + sed -e 's|@''GUARD_PREFIX''@|GL|g' \ + -e 's|@''HAVE_STRINGS_H''@|$(HAVE_STRINGS_H)|g' \ + -e 's|@''INCLUDE_NEXT''@|$(INCLUDE_NEXT)|g' \ + -e 's|@''PRAGMA_SYSTEM_HEADER''@|@PRAGMA_SYSTEM_HEADER@|g' \ + -e 's|@''PRAGMA_COLUMNS''@|@PRAGMA_COLUMNS@|g' \ + -e 's|@''NEXT_STRINGS_H''@|$(NEXT_STRINGS_H)|g' \ + -e 's/@''GNULIB_FFS''@/$(GNULIB_FFS)/g' \ + -e 's|@''HAVE_FFS''@|$(HAVE_FFS)|g' \ + -e 's|@''HAVE_STRCASECMP''@|$(HAVE_STRCASECMP)|g' \ + -e 's|@''HAVE_DECL_STRNCASECMP''@|$(HAVE_DECL_STRNCASECMP)|g' \ + -e '/definitions of _GL_FUNCDECL_RPL/r $(CXXDEFS_H)' \ + -e '/definition of _GL_ARG_NONNULL/r $(ARG_NONNULL_H)' \ + -e '/definition of _GL_WARN_ON_USE/r $(WARN_ON_USE_H)' \ + < $(srcdir)/strings.in.h; \ + } > $@-t && \ + mv $@-t $@ +MOSTLYCLEANFILES += strings.h strings.h-t + +EXTRA_DIST += strings.in.h + +## end gnulib module strings + +## begin gnulib module strndup + + +EXTRA_DIST += strndup.c + +EXTRA_libgnu_a_SOURCES += strndup.c + +## end gnulib module strndup + +## begin gnulib module strnlen + + +EXTRA_DIST += strnlen.c + +EXTRA_libgnu_a_SOURCES += strnlen.c + +## end gnulib module strnlen + ## begin gnulib module strtod @@ -2085,6 +2177,35 @@ EXTRA_DIST += sys_wait.in.h ## end gnulib module sys_wait +## begin gnulib module sysexits + +BUILT_SOURCES += $(SYSEXITS_H) + +# We need the following in order to create when the system +# doesn't have one that works with the given compiler. +if GL_GENERATE_SYSEXITS_H +sysexits.h: sysexits.in.h $(top_builddir)/config.status + $(AM_V_GEN)rm -f $@-t $@ && \ + { echo '/* DO NOT EDIT! GENERATED AUTOMATICALLY! */'; \ + sed -e 's|@''GUARD_PREFIX''@|GL|g' \ + -e 's|@''HAVE_SYSEXITS_H''@|$(HAVE_SYSEXITS_H)|g' \ + -e 's|@''INCLUDE_NEXT''@|$(INCLUDE_NEXT)|g' \ + -e 's|@''PRAGMA_SYSTEM_HEADER''@|@PRAGMA_SYSTEM_HEADER@|g' \ + -e 's|@''PRAGMA_COLUMNS''@|@PRAGMA_COLUMNS@|g' \ + -e 's|@''NEXT_SYSEXITS_H''@|$(NEXT_SYSEXITS_H)|g' \ + < $(srcdir)/sysexits.in.h; \ + } > $@-t && \ + mv -f $@-t $@ +else +sysexits.h: $(top_builddir)/config.status + rm -f $@ +endif +MOSTLYCLEANFILES += sysexits.h sysexits.h-t + +EXTRA_DIST += sysexits.in.h + +## end gnulib module sysexits + ## begin gnulib module tempname libgnu_a_SOURCES += tempname.c diff --git a/libdicl/gl/argp-ba.c b/libdicl/gl/argp-ba.c new file mode 100644 index 0000000..fcf60c8 --- /dev/null +++ b/libdicl/gl/argp-ba.c @@ -0,0 +1,34 @@ +/* Default definition for ARGP_PROGRAM_BUG_ADDRESS. + Copyright (C) 1996-2019 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Written by Miles Bader . + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +/* If set by the user program, it should point to string that is the + bug-reporting address for the program. It will be printed by argp_help if + the ARGP_HELP_BUG_ADDR flag is set (as it is by various standard help + messages), embedded in a sentence that says something like "Report bugs to + ADDR." */ +const char *argp_program_bug_address +/* This variable should be zero-initialized. On most systems, putting it into + BSS is sufficient. Not so on Mac OS X 10.3 and 10.4, see + + . */ +#if defined __ELF__ + /* On ELF systems, variables in BSS behave well. */ +#else + = (const char *) 0 +#endif + ; diff --git a/libdicl/gl/argp-eexst.c b/libdicl/gl/argp-eexst.c new file mode 100644 index 0000000..06b025d --- /dev/null +++ b/libdicl/gl/argp-eexst.c @@ -0,0 +1,30 @@ +/* Default definition for ARGP_ERR_EXIT_STATUS + Copyright (C) 1997, 2009-2019 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Written by Miles Bader . + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include + +#include "argp.h" + +/* The exit status that argp will use when exiting due to a parsing error. + If not defined or set by the user program, this defaults to EX_USAGE from + . */ +error_t argp_err_exit_status = EX_USAGE; diff --git a/libdicl/gl/argp-fmtstream.c b/libdicl/gl/argp-fmtstream.c new file mode 100644 index 0000000..ba6a407 --- /dev/null +++ b/libdicl/gl/argp-fmtstream.c @@ -0,0 +1,434 @@ +/* Word-wrapping and line-truncating streams + Copyright (C) 1997-2019 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Written by Miles Bader . + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +/* This package emulates glibc 'line_wrap_stream' semantics for systems that + don't have that. */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include +#include +#include + +#include "argp-fmtstream.h" +#include "argp-namefrob.h" + +#ifndef ARGP_FMTSTREAM_USE_LINEWRAP + +#ifndef isblank +#define isblank(ch) ((ch)==' ' || (ch)=='\t') +#endif + +#ifdef _LIBC +# include +# include +# define __vsnprintf(s, l, f, a) _IO_vsnprintf (s, l, f, a) +#endif + +#define INIT_BUF_SIZE 200 +#define PRINTF_SIZE_GUESS 150 + +/* Return an argp_fmtstream that outputs to STREAM, and which prefixes lines + written on it with LMARGIN spaces and limits them to RMARGIN columns + total. If WMARGIN >= 0, words that extend past RMARGIN are wrapped by + replacing the whitespace before them with a newline and WMARGIN spaces. + Otherwise, chars beyond RMARGIN are simply dropped until a newline. + Returns NULL if there was an error. */ +argp_fmtstream_t +__argp_make_fmtstream (FILE *stream, + size_t lmargin, size_t rmargin, ssize_t wmargin) +{ + argp_fmtstream_t fs; + + fs = (struct argp_fmtstream *) malloc (sizeof (struct argp_fmtstream)); + if (fs != NULL) + { + fs->stream = stream; + + fs->lmargin = lmargin; + fs->rmargin = rmargin; + fs->wmargin = wmargin; + fs->point_col = 0; + fs->point_offs = 0; + + fs->buf = (char *) malloc (INIT_BUF_SIZE); + if (! fs->buf) + { + free (fs); + fs = 0; + } + else + { + fs->p = fs->buf; + fs->end = fs->buf + INIT_BUF_SIZE; + } + } + + return fs; +} +#if 0 +/* Not exported. */ +#ifdef weak_alias +weak_alias (__argp_make_fmtstream, argp_make_fmtstream) +#endif +#endif + +/* Flush FS to its stream, and free it (but don't close the stream). */ +void +__argp_fmtstream_free (argp_fmtstream_t fs) +{ + __argp_fmtstream_update (fs); + if (fs->p > fs->buf) + { +#ifdef _LIBC + __fxprintf (fs->stream, "%.*s", (int) (fs->p - fs->buf), fs->buf); +#else + fwrite_unlocked (fs->buf, 1, fs->p - fs->buf, fs->stream); +#endif + } + free (fs->buf); + free (fs); +} +#if 0 +/* Not exported. */ +#ifdef weak_alias +weak_alias (__argp_fmtstream_free, argp_fmtstream_free) +#endif +#endif + +/* Process FS's buffer so that line wrapping is done from POINT_OFFS to the + end of its buffer. This code is mostly from glibc stdio/linewrap.c. */ +void +__argp_fmtstream_update (argp_fmtstream_t fs) +{ + char *buf, *nl; + size_t len; + + /* Scan the buffer for newlines. */ + buf = fs->buf + fs->point_offs; + while (buf < fs->p) + { + size_t r; + + if (fs->point_col == 0 && fs->lmargin != 0) + { + /* We are starting a new line. Print spaces to the left margin. */ + const size_t pad = fs->lmargin; + if (fs->p + pad < fs->end) + { + /* We can fit in them in the buffer by moving the + buffer text up and filling in the beginning. */ + memmove (buf + pad, buf, fs->p - buf); + fs->p += pad; /* Compensate for bigger buffer. */ + memset (buf, ' ', pad); /* Fill in the spaces. */ + buf += pad; /* Don't bother searching them. */ + } + else + { + /* No buffer space for spaces. Must flush. */ + size_t i; + for (i = 0; i < pad; i++) + { +#ifdef _LIBC + if (_IO_fwide (fs->stream, 0) > 0) + putwc_unlocked (L' ', fs->stream); + else +#endif + putc_unlocked (' ', fs->stream); + } + } + fs->point_col = pad; + } + + len = fs->p - buf; + nl = memchr (buf, '\n', len); + + if (fs->point_col < 0) + fs->point_col = 0; + + if (!nl) + { + /* The buffer ends in a partial line. */ + + if (fs->point_col + len < fs->rmargin) + { + /* The remaining buffer text is a partial line and fits + within the maximum line width. Advance point for the + characters to be written and stop scanning. */ + fs->point_col += len; + break; + } + else + /* Set the end-of-line pointer for the code below to + the end of the buffer. */ + nl = fs->p; + } + else if (fs->point_col + (nl - buf) < (ssize_t) fs->rmargin) + { + /* The buffer contains a full line that fits within the maximum + line width. Reset point and scan the next line. */ + fs->point_col = 0; + buf = nl + 1; + continue; + } + + /* This line is too long. */ + r = fs->rmargin - 1; + + if (fs->wmargin < 0) + { + /* Truncate the line by overwriting the excess with the + newline and anything after it in the buffer. */ + if (nl < fs->p) + { + memmove (buf + (r - fs->point_col), nl, fs->p - nl); + fs->p -= buf + (r - fs->point_col) - nl; + /* Reset point for the next line and start scanning it. */ + fs->point_col = 0; + buf += r + 1; /* Skip full line plus \n. */ + } + else + { + /* The buffer ends with a partial line that is beyond the + maximum line width. Advance point for the characters + written, and discard those past the max from the buffer. */ + fs->point_col += len; + fs->p -= fs->point_col - r; + break; + } + } + else + { + /* Do word wrap. Go to the column just past the maximum line + width and scan back for the beginning of the word there. + Then insert a line break. */ + + char *p, *nextline; + int i; + + p = buf + (r + 1 - fs->point_col); + while (p >= buf && !isblank ((unsigned char) *p)) + --p; + nextline = p + 1; /* This will begin the next line. */ + + if (nextline > buf) + { + /* Swallow separating blanks. */ + if (p >= buf) + do + --p; + while (p >= buf && isblank ((unsigned char) *p)); + nl = p + 1; /* The newline will replace the first blank. */ + } + else + { + /* A single word that is greater than the maximum line width. + Oh well. Put it on an overlong line by itself. */ + p = buf + (r + 1 - fs->point_col); + /* Find the end of the long word. */ + if (p < nl) + do + ++p; + while (p < nl && !isblank ((unsigned char) *p)); + if (p == nl) + { + /* It already ends a line. No fussing required. */ + fs->point_col = 0; + buf = nl + 1; + continue; + } + /* We will move the newline to replace the first blank. */ + nl = p; + /* Swallow separating blanks. */ + do + ++p; + while (isblank ((unsigned char) *p)); + /* The next line will start here. */ + nextline = p; + } + + /* Note: There are a bunch of tests below for + NEXTLINE == BUF + LEN + 1; this case is where NL happens to fall + at the end of the buffer, and NEXTLINE is in fact empty (and so + we need not be careful to maintain its contents). */ + + if ((nextline == buf + len + 1 + ? fs->end - nl < fs->wmargin + 1 + : nextline - (nl + 1) < fs->wmargin) + && fs->p > nextline) + { + /* The margin needs more blanks than we removed. */ + if (fs->end - fs->p > fs->wmargin + 1) + /* Make some space for them. */ + { + size_t mv = fs->p - nextline; + memmove (nl + 1 + fs->wmargin, nextline, mv); + nextline = nl + 1 + fs->wmargin; + len = nextline + mv - buf; + *nl++ = '\n'; + } + else + /* Output the first line so we can use the space. */ + { +#ifdef _LIBC + __fxprintf (fs->stream, "%.*s\n", + (int) (nl - fs->buf), fs->buf); +#else + if (nl > fs->buf) + fwrite_unlocked (fs->buf, 1, nl - fs->buf, fs->stream); + putc_unlocked ('\n', fs->stream); +#endif + + len += buf - fs->buf; + nl = buf = fs->buf; + } + } + else + /* We can fit the newline and blanks in before + the next word. */ + *nl++ = '\n'; + + if (nextline - nl >= fs->wmargin + || (nextline == buf + len + 1 && fs->end - nextline >= fs->wmargin)) + /* Add blanks up to the wrap margin column. */ + for (i = 0; i < fs->wmargin; ++i) + *nl++ = ' '; + else + for (i = 0; i < fs->wmargin; ++i) +#ifdef _LIBC + if (_IO_fwide (fs->stream, 0) > 0) + putwc_unlocked (L' ', fs->stream); + else +#endif + putc_unlocked (' ', fs->stream); + + /* Copy the tail of the original buffer into the current buffer + position. */ + if (nl < nextline) + memmove (nl, nextline, buf + len - nextline); + len -= nextline - buf; + + /* Continue the scan on the remaining lines in the buffer. */ + buf = nl; + + /* Restore bufp to include all the remaining text. */ + fs->p = nl + len; + + /* Reset the counter of what has been output this line. If wmargin + is 0, we want to avoid the lmargin getting added, so we set + point_col to a magic value of -1 in that case. */ + fs->point_col = fs->wmargin ? fs->wmargin : -1; + } + } + + /* Remember that we've scanned as far as the end of the buffer. */ + fs->point_offs = fs->p - fs->buf; +} + +/* Ensure that FS has space for AMOUNT more bytes in its buffer, either by + growing the buffer, or by flushing it. True is returned iff we succeed. */ +int +__argp_fmtstream_ensure (struct argp_fmtstream *fs, size_t amount) +{ + if ((size_t) (fs->end - fs->p) < amount) + { + ssize_t wrote; + + /* Flush FS's buffer. */ + __argp_fmtstream_update (fs); + +#ifdef _LIBC + __fxprintf (fs->stream, "%.*s", (int) (fs->p - fs->buf), fs->buf); + wrote = fs->p - fs->buf; +#else + wrote = fwrite_unlocked (fs->buf, 1, fs->p - fs->buf, fs->stream); +#endif + if (wrote == fs->p - fs->buf) + { + fs->p = fs->buf; + fs->point_offs = 0; + } + else + { + fs->p -= wrote; + fs->point_offs -= wrote; + memmove (fs->buf, fs->buf + wrote, fs->p - fs->buf); + return 0; + } + + if ((size_t) (fs->end - fs->buf) < amount) + /* Gotta grow the buffer. */ + { + size_t old_size = fs->end - fs->buf; + size_t new_size = old_size + amount; + char *new_buf; + + if (new_size < old_size || ! (new_buf = realloc (fs->buf, new_size))) + { + __set_errno (ENOMEM); + return 0; + } + + fs->buf = new_buf; + fs->end = new_buf + new_size; + fs->p = fs->buf; + } + } + + return 1; +} + +ssize_t +__argp_fmtstream_printf (struct argp_fmtstream *fs, const char *fmt, ...) +{ + int out; + size_t avail; + size_t size_guess = PRINTF_SIZE_GUESS; /* How much space to reserve. */ + + do + { + va_list args; + + if (! __argp_fmtstream_ensure (fs, size_guess)) + return -1; + + va_start (args, fmt); + avail = fs->end - fs->p; + out = __vsnprintf (fs->p, avail, fmt, args); + va_end (args); + if ((size_t) out >= avail) + size_guess = out + 1; + } + while ((size_t) out >= avail); + + fs->p += out; + + return out; +} +#if 0 +/* Not exported. */ +#ifdef weak_alias +weak_alias (__argp_fmtstream_printf, argp_fmtstream_printf) +#endif +#endif + +#endif /* !ARGP_FMTSTREAM_USE_LINEWRAP */ diff --git a/libdicl/gl/argp-fmtstream.h b/libdicl/gl/argp-fmtstream.h new file mode 100644 index 0000000..0cc8852 --- /dev/null +++ b/libdicl/gl/argp-fmtstream.h @@ -0,0 +1,310 @@ +/* Word-wrapping and line-truncating streams. + Copyright (C) 1997-2019 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Written by Miles Bader . + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +/* This package emulates glibc 'line_wrap_stream' semantics for systems that + don't have that. If the system does have it, it is just a wrapper for + that. This header file is only used internally while compiling argp, and + shouldn't be installed. */ + +#ifndef _ARGP_FMTSTREAM_H +#define _ARGP_FMTSTREAM_H + +#include +#include +#include + +/* The __attribute__ feature is available in gcc versions 2.5 and later. + The __-protected variants of the attributes 'format' and 'printf' are + accepted by gcc versions 2.6.4 (effectively 2.7) and later. + We enable _GL_ATTRIBUTE_FORMAT only if these are supported too, because + gnulib and libintl do '#define printf __printf__' when they override + the 'printf' function. */ +#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 7) +# define _GL_ATTRIBUTE_FORMAT(spec) __attribute__ ((__format__ spec)) +#else +# define _GL_ATTRIBUTE_FORMAT(spec) /* empty */ +#endif + +#if defined (__GNU_LIBRARY__) && defined (HAVE_LINEWRAP_H) +/* line_wrap_stream is available, so use that. */ +#define ARGP_FMTSTREAM_USE_LINEWRAP +#endif + +#ifdef ARGP_FMTSTREAM_USE_LINEWRAP +/* Just be a simple wrapper for line_wrap_stream; the semantics are + *slightly* different, as line_wrap_stream doesn't actually make a new + object, it just modifies the given stream (reversibly) to do + line-wrapping. Since we control who uses this code, it doesn't matter. */ + +#include + +typedef FILE *argp_fmtstream_t; + +#define argp_make_fmtstream line_wrap_stream +#define __argp_make_fmtstream line_wrap_stream +#define argp_fmtstream_free line_unwrap_stream +#define __argp_fmtstream_free line_unwrap_stream + +#define __argp_fmtstream_putc(fs,ch) putc(ch,fs) +#define argp_fmtstream_putc(fs,ch) putc(ch,fs) +#define __argp_fmtstream_puts(fs,str) fputs(str,fs) +#define argp_fmtstream_puts(fs,str) fputs(str,fs) +#define __argp_fmtstream_write(fs,str,len) fwrite(str,1,len,fs) +#define argp_fmtstream_write(fs,str,len) fwrite(str,1,len,fs) +#define __argp_fmtstream_printf fprintf +#define argp_fmtstream_printf fprintf + +#define __argp_fmtstream_lmargin line_wrap_lmargin +#define argp_fmtstream_lmargin line_wrap_lmargin +#define __argp_fmtstream_set_lmargin line_wrap_set_lmargin +#define argp_fmtstream_set_lmargin line_wrap_set_lmargin +#define __argp_fmtstream_rmargin line_wrap_rmargin +#define argp_fmtstream_rmargin line_wrap_rmargin +#define __argp_fmtstream_set_rmargin line_wrap_set_rmargin +#define argp_fmtstream_set_rmargin line_wrap_set_rmargin +#define __argp_fmtstream_wmargin line_wrap_wmargin +#define argp_fmtstream_wmargin line_wrap_wmargin +#define __argp_fmtstream_set_wmargin line_wrap_set_wmargin +#define argp_fmtstream_set_wmargin line_wrap_set_wmargin +#define __argp_fmtstream_point line_wrap_point +#define argp_fmtstream_point line_wrap_point + +#else /* !ARGP_FMTSTREAM_USE_LINEWRAP */ +/* Guess we have to define our own version. */ + + +struct argp_fmtstream +{ + FILE *stream; /* The stream we're outputting to. */ + + size_t lmargin, rmargin; /* Left and right margins. */ + ssize_t wmargin; /* Margin to wrap to, or -1 to truncate. */ + + /* Point in buffer to which we've processed for wrapping, but not output. */ + size_t point_offs; + /* Output column at POINT_OFFS, or -1 meaning 0 but don't add lmargin. */ + ssize_t point_col; + + char *buf; /* Output buffer. */ + char *p; /* Current end of text in BUF. */ + char *end; /* Absolute end of BUF. */ +}; + +typedef struct argp_fmtstream *argp_fmtstream_t; + +/* Return an argp_fmtstream that outputs to STREAM, and which prefixes lines + written on it with LMARGIN spaces and limits them to RMARGIN columns + total. If WMARGIN >= 0, words that extend past RMARGIN are wrapped by + replacing the whitespace before them with a newline and WMARGIN spaces. + Otherwise, chars beyond RMARGIN are simply dropped until a newline. + Returns NULL if there was an error. */ +extern argp_fmtstream_t __argp_make_fmtstream (FILE *__stream, + size_t __lmargin, + size_t __rmargin, + ssize_t __wmargin); +extern argp_fmtstream_t argp_make_fmtstream (FILE *__stream, + size_t __lmargin, + size_t __rmargin, + ssize_t __wmargin); + +/* Flush __FS to its stream, and free it (but don't close the stream). */ +extern void __argp_fmtstream_free (argp_fmtstream_t __fs); +extern void argp_fmtstream_free (argp_fmtstream_t __fs); + +extern ssize_t __argp_fmtstream_printf (argp_fmtstream_t __fs, + const char *__fmt, ...) + _GL_ATTRIBUTE_FORMAT ((printf, 2, 3)); +extern ssize_t argp_fmtstream_printf (argp_fmtstream_t __fs, + const char *__fmt, ...) + _GL_ATTRIBUTE_FORMAT ((printf, 2, 3)); + +#if _LIBC +extern int __argp_fmtstream_putc (argp_fmtstream_t __fs, int __ch); +extern int argp_fmtstream_putc (argp_fmtstream_t __fs, int __ch); + +extern int __argp_fmtstream_puts (argp_fmtstream_t __fs, const char *__str); +extern int argp_fmtstream_puts (argp_fmtstream_t __fs, const char *__str); + +extern size_t __argp_fmtstream_write (argp_fmtstream_t __fs, + const char *__str, size_t __len); +extern size_t argp_fmtstream_write (argp_fmtstream_t __fs, + const char *__str, size_t __len); +#endif + +/* Access macros for various bits of state. */ +#define argp_fmtstream_lmargin(__fs) ((__fs)->lmargin) +#define argp_fmtstream_rmargin(__fs) ((__fs)->rmargin) +#define argp_fmtstream_wmargin(__fs) ((__fs)->wmargin) +#define __argp_fmtstream_lmargin argp_fmtstream_lmargin +#define __argp_fmtstream_rmargin argp_fmtstream_rmargin +#define __argp_fmtstream_wmargin argp_fmtstream_wmargin + +#if _LIBC +/* Set __FS's left margin to LMARGIN and return the old value. */ +extern size_t argp_fmtstream_set_lmargin (argp_fmtstream_t __fs, + size_t __lmargin); +extern size_t __argp_fmtstream_set_lmargin (argp_fmtstream_t __fs, + size_t __lmargin); + +/* Set __FS's right margin to __RMARGIN and return the old value. */ +extern size_t argp_fmtstream_set_rmargin (argp_fmtstream_t __fs, + size_t __rmargin); +extern size_t __argp_fmtstream_set_rmargin (argp_fmtstream_t __fs, + size_t __rmargin); + +/* Set __FS's wrap margin to __WMARGIN and return the old value. */ +extern size_t argp_fmtstream_set_wmargin (argp_fmtstream_t __fs, + size_t __wmargin); +extern size_t __argp_fmtstream_set_wmargin (argp_fmtstream_t __fs, + size_t __wmargin); + +/* Return the column number of the current output point in __FS. */ +extern size_t argp_fmtstream_point (argp_fmtstream_t __fs); +extern size_t __argp_fmtstream_point (argp_fmtstream_t __fs); +#endif + +/* Internal routines. */ +extern void _argp_fmtstream_update (argp_fmtstream_t __fs); +extern void __argp_fmtstream_update (argp_fmtstream_t __fs); +extern int _argp_fmtstream_ensure (argp_fmtstream_t __fs, size_t __amount); +extern int __argp_fmtstream_ensure (argp_fmtstream_t __fs, size_t __amount); + +#if !_LIBC || defined __OPTIMIZE__ +/* Inline versions of above routines. */ + +#if !_LIBC +#define __argp_fmtstream_putc argp_fmtstream_putc +#define __argp_fmtstream_puts argp_fmtstream_puts +#define __argp_fmtstream_write argp_fmtstream_write +#define __argp_fmtstream_set_lmargin argp_fmtstream_set_lmargin +#define __argp_fmtstream_set_rmargin argp_fmtstream_set_rmargin +#define __argp_fmtstream_set_wmargin argp_fmtstream_set_wmargin +#define __argp_fmtstream_point argp_fmtstream_point +#define __argp_fmtstream_update _argp_fmtstream_update +#define __argp_fmtstream_ensure _argp_fmtstream_ensure +#ifndef _GL_INLINE_HEADER_BEGIN + #error "Please include config.h first." +#endif +_GL_INLINE_HEADER_BEGIN +#ifndef ARGP_FS_EI +# define ARGP_FS_EI _GL_INLINE +#endif +#endif + +#ifndef ARGP_FS_EI +#define ARGP_FS_EI extern inline +#endif + +ARGP_FS_EI size_t +__argp_fmtstream_write (argp_fmtstream_t __fs, const char *__str, size_t __len) +{ + if (__fs->p + __len <= __fs->end || __argp_fmtstream_ensure (__fs, __len)) + { + memcpy (__fs->p, __str, __len); + __fs->p += __len; + return __len; + } + else + return 0; +} + +ARGP_FS_EI int +__argp_fmtstream_puts (argp_fmtstream_t __fs, const char *__str) +{ + size_t __len = strlen (__str); + if (__len) + { + size_t __wrote = __argp_fmtstream_write (__fs, __str, __len); + return __wrote == __len ? 0 : -1; + } + else + return 0; +} + +ARGP_FS_EI int +__argp_fmtstream_putc (argp_fmtstream_t __fs, int __ch) +{ + if (__fs->p < __fs->end || __argp_fmtstream_ensure (__fs, 1)) + return *__fs->p++ = __ch; + else + return EOF; +} + +/* Set __FS's left margin to __LMARGIN and return the old value. */ +ARGP_FS_EI size_t +__argp_fmtstream_set_lmargin (argp_fmtstream_t __fs, size_t __lmargin) +{ + size_t __old; + if ((size_t) (__fs->p - __fs->buf) > __fs->point_offs) + __argp_fmtstream_update (__fs); + __old = __fs->lmargin; + __fs->lmargin = __lmargin; + return __old; +} + +/* Set __FS's right margin to __RMARGIN and return the old value. */ +ARGP_FS_EI size_t +__argp_fmtstream_set_rmargin (argp_fmtstream_t __fs, size_t __rmargin) +{ + size_t __old; + if ((size_t) (__fs->p - __fs->buf) > __fs->point_offs) + __argp_fmtstream_update (__fs); + __old = __fs->rmargin; + __fs->rmargin = __rmargin; + return __old; +} + +/* Set FS's wrap margin to __WMARGIN and return the old value. */ +ARGP_FS_EI size_t +__argp_fmtstream_set_wmargin (argp_fmtstream_t __fs, size_t __wmargin) +{ + size_t __old; + if ((size_t) (__fs->p - __fs->buf) > __fs->point_offs) + __argp_fmtstream_update (__fs); + __old = __fs->wmargin; + __fs->wmargin = __wmargin; + return __old; +} + +/* Return the column number of the current output point in __FS. */ +ARGP_FS_EI size_t +__argp_fmtstream_point (argp_fmtstream_t __fs) +{ + if ((size_t) (__fs->p - __fs->buf) > __fs->point_offs) + __argp_fmtstream_update (__fs); + return __fs->point_col >= 0 ? __fs->point_col : 0; +} + +#if !_LIBC +#undef __argp_fmtstream_putc +#undef __argp_fmtstream_puts +#undef __argp_fmtstream_write +#undef __argp_fmtstream_set_lmargin +#undef __argp_fmtstream_set_rmargin +#undef __argp_fmtstream_set_wmargin +#undef __argp_fmtstream_point +#undef __argp_fmtstream_update +#undef __argp_fmtstream_ensure +_GL_INLINE_HEADER_END +#endif + +#endif /* !_LIBC || __OPTIMIZE__ */ + +#endif /* ARGP_FMTSTREAM_USE_LINEWRAP */ + +#endif /* argp-fmtstream.h */ diff --git a/libdicl/gl/argp-fs-xinl.c b/libdicl/gl/argp-fs-xinl.c new file mode 100644 index 0000000..a2e843c --- /dev/null +++ b/libdicl/gl/argp-fs-xinl.c @@ -0,0 +1,46 @@ +/* Real definitions for extern inline functions in argp-fmtstream.h + Copyright (C) 1997-2019 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Written by Miles Bader . + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#ifdef _LIBC +# define ARGP_FS_EI +#else +# define ARGP_FS_EI _GL_EXTERN_INLINE +#endif +#undef __OPTIMIZE__ +#define __OPTIMIZE__ 1 +#include "argp-fmtstream.h" + +#if 0 +/* Not exported. */ +/* Add weak aliases. */ +#if _LIBC - 0 && !defined (ARGP_FMTSTREAM_USE_LINEWRAP) && defined (weak_alias) + +weak_alias (__argp_fmtstream_putc, argp_fmtstream_putc) +weak_alias (__argp_fmtstream_puts, argp_fmtstream_puts) +weak_alias (__argp_fmtstream_write, argp_fmtstream_write) +weak_alias (__argp_fmtstream_set_lmargin, argp_fmtstream_set_lmargin) +weak_alias (__argp_fmtstream_set_rmargin, argp_fmtstream_set_rmargin) +weak_alias (__argp_fmtstream_set_wmargin, argp_fmtstream_set_wmargin) +weak_alias (__argp_fmtstream_point, argp_fmtstream_point) + +#endif +#endif diff --git a/libdicl/gl/argp-help.c b/libdicl/gl/argp-help.c new file mode 100644 index 0000000..a3d3e98 --- /dev/null +++ b/libdicl/gl/argp-help.c @@ -0,0 +1,1912 @@ +/* Hierarchical argument parsing help output + Copyright (C) 1995-2019 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Written by Miles Bader . + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#ifndef _GNU_SOURCE +# define _GNU_SOURCE 1 +#endif + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef _LIBC +# include <../libio/libioP.h> +# include +#endif + +#ifdef _LIBC +# include +# undef dgettext +# define dgettext(domain, msgid) \ + __dcgettext (domain, msgid, LC_MESSAGES) +#else +# include "gettext.h" +#endif + +#include "argp.h" +#include "argp-fmtstream.h" +#include "argp-namefrob.h" + +#ifndef SIZE_MAX +# define SIZE_MAX ((size_t) -1) +#endif + +/* User-selectable (using an environment variable) formatting parameters. + + These may be specified in an environment variable called 'ARGP_HELP_FMT', + with a contents like: VAR1=VAL1,VAR2=VAL2,BOOLVAR2,no-BOOLVAR2 + Where VALn must be a positive integer. The list of variables is in the + UPARAM_NAMES vector, below. */ + +/* Default parameters. */ +#define DUP_ARGS 0 /* True if option argument can be duplicated. */ +#define DUP_ARGS_NOTE 1 /* True to print a note about duplicate args. */ +#define SHORT_OPT_COL 2 /* column in which short options start */ +#define LONG_OPT_COL 6 /* column in which long options start */ +#define DOC_OPT_COL 2 /* column in which doc options start */ +#define OPT_DOC_COL 29 /* column in which option text starts */ +#define HEADER_COL 1 /* column in which group headers are printed */ +#define USAGE_INDENT 12 /* indentation of wrapped usage lines */ +#define RMARGIN 79 /* right margin used for wrapping */ + +/* User-selectable (using an environment variable) formatting parameters. + They must all be of type 'int' for the parsing code to work. */ +struct uparams +{ + /* If true, arguments for an option are shown with both short and long + options, even when a given option has both, e.g. '-x ARG, --longx=ARG'. + If false, then if an option has both, the argument is only shown with + the long one, e.g., '-x, --longx=ARG', and a message indicating that + this really means both is printed below the options. */ + int dup_args; + + /* This is true if when DUP_ARGS is false, and some duplicate arguments have + been suppressed, an explanatory message should be printed. */ + int dup_args_note; + + /* Various output columns. */ + int short_opt_col; /* column in which short options start */ + int long_opt_col; /* column in which long options start */ + int doc_opt_col; /* column in which doc options start */ + int opt_doc_col; /* column in which option text starts */ + int header_col; /* column in which group headers are printed */ + int usage_indent; /* indentation of wrapped usage lines */ + int rmargin; /* right margin used for wrapping */ + + int valid; /* True when the values in here are valid. */ +}; + +/* This is a global variable, as user options are only ever read once. */ +static struct uparams uparams = { + DUP_ARGS, DUP_ARGS_NOTE, + SHORT_OPT_COL, LONG_OPT_COL, DOC_OPT_COL, OPT_DOC_COL, HEADER_COL, + USAGE_INDENT, RMARGIN +}; + +/* A particular uparam, and what the user name is. */ +struct uparam_name +{ + const char name[14]; /* User name. */ + bool is_bool; /* Whether it's 'boolean'. */ + unsigned char uparams_offs; /* Location of the (int) field in UPARAMS. */ +}; + +/* The name-field mappings we know about. */ +static const struct uparam_name uparam_names[] = +{ + { "dup-args", true, offsetof (struct uparams, dup_args) }, + { "dup-args-note", true, offsetof (struct uparams, dup_args_note) }, + { "short-opt-col", false, offsetof (struct uparams, short_opt_col) }, + { "long-opt-col", false, offsetof (struct uparams, long_opt_col) }, + { "doc-opt-col", false, offsetof (struct uparams, doc_opt_col) }, + { "opt-doc-col", false, offsetof (struct uparams, opt_doc_col) }, + { "header-col", false, offsetof (struct uparams, header_col) }, + { "usage-indent", false, offsetof (struct uparams, usage_indent) }, + { "rmargin", false, offsetof (struct uparams, rmargin) } +}; +#define nuparam_names (sizeof (uparam_names) / sizeof (uparam_names[0])) + +static void +validate_uparams (const struct argp_state *state, struct uparams *upptr) +{ + const struct uparam_name *up; + + for (up = uparam_names; up < uparam_names + nuparam_names; up++) + { + if (up->is_bool + || up->uparams_offs == offsetof (struct uparams, rmargin)) + continue; + if (*(int *)((char *)upptr + up->uparams_offs) >= upptr->rmargin) + { + __argp_failure (state, 0, 0, + dgettext (state->root_argp->argp_domain, + "\ +ARGP_HELP_FMT: %s value is less than or equal to %s"), + "rmargin", up->name); + return; + } + } + uparams = *upptr; + uparams.valid = 1; +} + +/* Read user options from the environment, and fill in UPARAMS appropriately. */ +static void +fill_in_uparams (const struct argp_state *state) +{ + const char *var = getenv ("ARGP_HELP_FMT"); + struct uparams new_params = uparams; + +#define SKIPWS(p) do { while (isspace ((unsigned char) *p)) p++; } while (0); + + if (var) + { + /* Parse var. */ + while (*var) + { + SKIPWS (var); + + if (isalpha ((unsigned char) *var)) + { + size_t var_len; + const struct uparam_name *un; + int unspec = 0, val = 0; + const char *arg = var; + + while (isalnum ((unsigned char) *arg) || *arg == '-' || *arg == '_') + arg++; + var_len = arg - var; + + SKIPWS (arg); + + if (*arg == '\0' || *arg == ',') + unspec = 1; + else if (*arg == '=') + { + arg++; + SKIPWS (arg); + } + + if (unspec) + { + if (var[0] == 'n' && var[1] == 'o' && var[2] == '-') + { + val = 0; + var += 3; + var_len -= 3; + } + else + val = 1; + } + else if (isdigit ((unsigned char) *arg)) + { + val = atoi (arg); + while (isdigit ((unsigned char) *arg)) + arg++; + SKIPWS (arg); + } + + for (un = uparam_names; + un < uparam_names + nuparam_names; + un++) + if (strlen (un->name) == var_len + && strncmp (var, un->name, var_len) == 0) + { + if (unspec && !un->is_bool) + __argp_failure (state, 0, 0, + dgettext (state == NULL ? NULL + : state->root_argp->argp_domain, + "\ +%.*s: ARGP_HELP_FMT parameter requires a value"), + (int) var_len, var); + else + *(int *)((char *)&new_params + un->uparams_offs) = val; + break; + } + if (un == uparam_names + nuparam_names) + __argp_failure (state, 0, 0, + dgettext (state == NULL ? NULL + : state->root_argp->argp_domain, "\ +%.*s: Unknown ARGP_HELP_FMT parameter"), + (int) var_len, var); + + var = arg; + if (*var == ',') + var++; + } + else if (*var) + { + __argp_failure (state, 0, 0, + dgettext (state == NULL ? NULL + : state->root_argp->argp_domain, + "Garbage in ARGP_HELP_FMT: %s"), var); + break; + } + } + validate_uparams (state, &new_params); + } +} + +/* Returns true if OPT hasn't been marked invisible. Visibility only affects + whether OPT is displayed or used in sorting, not option shadowing. */ +#define ovisible(opt) (! ((opt)->flags & OPTION_HIDDEN)) + +/* Returns true if OPT is an alias for an earlier option. */ +#define oalias(opt) ((opt)->flags & OPTION_ALIAS) + +/* Returns true if OPT is a documentation-only entry. */ +#define odoc(opt) ((opt)->flags & OPTION_DOC) + +/* Returns true if OPT should not be translated */ +#define onotrans(opt) ((opt)->flags & OPTION_NO_TRANS) + +/* Returns true if OPT is the end-of-list marker for a list of options. */ +#define oend(opt) __option_is_end (opt) + +/* Returns true if OPT has a short option. */ +#define oshort(opt) __option_is_short (opt) + +/* + The help format for a particular option is like: + + -xARG, -yARG, --long1=ARG, --long2=ARG Documentation... + + Where ARG will be omitted if there's no argument, for this option, or + will be surrounded by "[" and "]" appropriately if the argument is + optional. The documentation string is word-wrapped appropriately, and if + the list of options is long enough, it will be started on a separate line. + If there are no short options for a given option, the first long option is + indented slightly in a way that's supposed to make most long options appear + to be in a separate column. + + For example, the following output (from ps): + + -p PID, --pid=PID List the process PID + --pgrp=PGRP List processes in the process group PGRP + -P, -x, --no-parent Include processes without parents + -Q, --all-fields Don't elide unusable fields (normally if there's + some reason ps can't print a field for any + process, it's removed from the output entirely) + -r, --reverse, --gratuitously-long-reverse-option + Reverse the order of any sort + --session[=SID] Add the processes from the session SID (which + defaults to the sid of the current process) + + Here are some more options: + -f ZOT, --foonly=ZOT Glork a foonly + -z, --zaza Snit a zar + + -?, --help Give this help list + --usage Give a short usage message + -V, --version Print program version + + The struct argp_option array for the above could look like: + + { + {"pid", 'p', "PID", 0, "List the process PID"}, + {"pgrp", OPT_PGRP, "PGRP", 0, "List processes in the process group PGRP"}, + {"no-parent", 'P', 0, 0, "Include processes without parents"}, + {0, 'x', 0, OPTION_ALIAS}, + {"all-fields",'Q', 0, 0, "Don't elide unusable fields (normally" + " if there's some reason ps can't" + " print a field for any process, it's" + " removed from the output entirely)" }, + {"reverse", 'r', 0, 0, "Reverse the order of any sort"}, + {"gratuitously-long-reverse-option", 0, 0, OPTION_ALIAS}, + {"session", OPT_SESS, "SID", OPTION_ARG_OPTIONAL, + "Add the processes from the session" + " SID (which defaults to the sid of" + " the current process)" }, + + {0,0,0,0, "Here are some more options:"}, + {"foonly", 'f', "ZOT", 0, "Glork a foonly"}, + {"zaza", 'z', 0, 0, "Snit a zar"}, + + {0} + } + + Note that the last three options are automatically supplied by argp_parse, + unless you tell it not to with ARGP_NO_HELP. + +*/ + +/* Returns true if CH occurs between BEG and END. */ +static int +find_char (char ch, char *beg, char *end) +{ + while (beg < end) + if (*beg == ch) + return 1; + else + beg++; + return 0; +} + +struct hol_cluster; /* fwd decl */ + +struct hol_entry +{ + /* First option. */ + const struct argp_option *opt; + /* Number of options (including aliases). */ + unsigned num; + + /* A pointers into the HOL's short_options field, to the first short option + letter for this entry. The order of the characters following this point + corresponds to the order of options pointed to by OPT, and there are at + most NUM. A short option recorded in an option following OPT is only + valid if it occurs in the right place in SHORT_OPTIONS (otherwise it's + probably been shadowed by some other entry). */ + char *short_options; + + /* Entries are sorted by their group first, in the order: + 1, 2, ..., n, 0, -m, ..., -2, -1 + and then alphabetically within each group. The default is 0. */ + int group; + + /* The cluster of options this entry belongs to, or 0 if none. */ + struct hol_cluster *cluster; + + /* The argp from which this option came. */ + const struct argp *argp; + + /* Position in the array */ + unsigned ord; +}; + +/* A cluster of entries to reflect the argp tree structure. */ +struct hol_cluster +{ + /* A descriptive header printed before options in this cluster. */ + const char *header; + + /* Used to order clusters within the same group with the same parent, + according to the order in which they occurred in the parent argp's child + list. */ + int index; + + /* How to sort this cluster with respect to options and other clusters at the + same depth (clusters always follow options in the same group). */ + int group; + + /* The cluster to which this cluster belongs, or 0 if it's at the base + level. */ + struct hol_cluster *parent; + + /* The argp from which this cluster is (eventually) derived. */ + const struct argp *argp; + + /* The distance this cluster is from the root. */ + int depth; + + /* Clusters in a given hol are kept in a linked list, to make freeing them + possible. */ + struct hol_cluster *next; +}; + +/* A list of options for help. */ +struct hol +{ + /* An array of hol_entry's. */ + struct hol_entry *entries; + /* The number of entries in this hol. If this field is zero, the others + are undefined. */ + unsigned num_entries; + + /* A string containing all short options in this HOL. Each entry contains + pointers into this string, so the order can't be messed with blindly. */ + char *short_options; + + /* Clusters of entries in this hol. */ + struct hol_cluster *clusters; +}; + +/* Create a struct hol from the options in ARGP. CLUSTER is the + hol_cluster in which these entries occur, or 0, if at the root. */ +static struct hol * +make_hol (const struct argp *argp, struct hol_cluster *cluster) +{ + char *so; + const struct argp_option *o; + const struct argp_option *opts = argp->options; + struct hol_entry *entry; + unsigned num_short_options = 0; + struct hol *hol = malloc (sizeof (struct hol)); + + assert (hol); + + hol->num_entries = 0; + hol->clusters = 0; + + if (opts) + { + int cur_group = 0; + + /* The first option must not be an alias. */ + assert (! oalias (opts)); + + /* Calculate the space needed. */ + for (o = opts; ! oend (o); o++) + { + if (! oalias (o)) + hol->num_entries++; + if (oshort (o)) + num_short_options++; /* This is an upper bound. */ + } + + hol->entries = malloc (sizeof (struct hol_entry) * hol->num_entries); + hol->short_options = malloc (num_short_options + 1); + + assert (hol->entries && hol->short_options); + if (SIZE_MAX <= UINT_MAX) + assert (hol->num_entries <= SIZE_MAX / sizeof (struct hol_entry)); + + /* Fill in the entries. */ + so = hol->short_options; + for (o = opts, entry = hol->entries; ! oend (o); entry++) + { + entry->opt = o; + entry->num = 0; + entry->short_options = so; + entry->group = cur_group = + o->group + ? o->group + : ((!o->name && !o->key) + ? cur_group + 1 + : cur_group); + entry->cluster = cluster; + entry->argp = argp; + + do + { + entry->num++; + if (oshort (o) && ! find_char (o->key, hol->short_options, so)) + /* O has a valid short option which hasn't already been used.*/ + *so++ = o->key; + o++; + } + while (! oend (o) && oalias (o)); + } + *so = '\0'; /* null terminated so we can find the length */ + } + + return hol; +} + +/* Add a new cluster to HOL, with the given GROUP and HEADER (taken from the + associated argp child list entry), INDEX, and PARENT, and return a pointer + to it. ARGP is the argp that this cluster results from. */ +static struct hol_cluster * +hol_add_cluster (struct hol *hol, int group, const char *header, int index, + struct hol_cluster *parent, const struct argp *argp) +{ + struct hol_cluster *cl = malloc (sizeof (struct hol_cluster)); + if (cl) + { + cl->group = group; + cl->header = header; + + cl->index = index; + cl->parent = parent; + cl->argp = argp; + cl->depth = parent ? parent->depth + 1 : 0; + + cl->next = hol->clusters; + hol->clusters = cl; + } + return cl; +} + +/* Free HOL and any resources it uses. */ +static void +hol_free (struct hol *hol) +{ + struct hol_cluster *cl = hol->clusters; + + while (cl) + { + struct hol_cluster *next = cl->next; + free (cl); + cl = next; + } + + if (hol->num_entries > 0) + { + free (hol->entries); + free (hol->short_options); + } + + free (hol); +} + +static int +hol_entry_short_iterate (const struct hol_entry *entry, + int (*func)(const struct argp_option *opt, + const struct argp_option *real, + const char *domain, void *cookie), + const char *domain, void *cookie) +{ + unsigned nopts; + int val = 0; + const struct argp_option *opt, *real = entry->opt; + char *so = entry->short_options; + + for (opt = real, nopts = entry->num; nopts > 0 && !val; opt++, nopts--) + if (oshort (opt) && *so == opt->key) + { + if (!oalias (opt)) + real = opt; + if (ovisible (opt)) + val = (*func)(opt, real, domain, cookie); + so++; + } + + return val; +} + +static inline int +#if __GNUC__ >= 3 +__attribute__ ((always_inline)) +#endif +hol_entry_long_iterate (const struct hol_entry *entry, + int (*func)(const struct argp_option *opt, + const struct argp_option *real, + const char *domain, void *cookie), + const char *domain, void *cookie) +{ + unsigned nopts; + int val = 0; + const struct argp_option *opt, *real = entry->opt; + + for (opt = real, nopts = entry->num; nopts > 0 && !val; opt++, nopts--) + if (opt->name) + { + if (!oalias (opt)) + real = opt; + if (ovisible (opt)) + val = (*func)(opt, real, domain, cookie); + } + + return val; +} + +/* Iterator that returns true for the first short option. */ +static int +until_short (const struct argp_option *opt, const struct argp_option *real, + const char *domain, void *cookie) +{ + return oshort (opt) ? opt->key : 0; +} + +/* Returns the first valid short option in ENTRY, or 0 if there is none. */ +static char +hol_entry_first_short (const struct hol_entry *entry) +{ + return hol_entry_short_iterate (entry, until_short, + entry->argp->argp_domain, 0); +} + +/* Returns the first valid long option in ENTRY, or 0 if there is none. */ +static const char * +hol_entry_first_long (const struct hol_entry *entry) +{ + const struct argp_option *opt; + unsigned num; + for (opt = entry->opt, num = entry->num; num > 0; opt++, num--) + if (opt->name && ovisible (opt)) + return opt->name; + return 0; +} + +/* Returns the entry in HOL with the long option name NAME, or 0 if there is + none. */ +static struct hol_entry * +hol_find_entry (struct hol *hol, const char *name) +{ + struct hol_entry *entry = hol->entries; + unsigned num_entries = hol->num_entries; + + while (num_entries-- > 0) + { + const struct argp_option *opt = entry->opt; + unsigned num_opts = entry->num; + + while (num_opts-- > 0) + if (opt->name && ovisible (opt) && strcmp (opt->name, name) == 0) + return entry; + else + opt++; + + entry++; + } + + return 0; +} + +/* If an entry with the long option NAME occurs in HOL, set its special + sort position to GROUP. */ +static void +hol_set_group (struct hol *hol, const char *name, int group) +{ + struct hol_entry *entry = hol_find_entry (hol, name); + if (entry) + entry->group = group; +} + +/* Order by group: 0, 1, 2, ..., n, -m, ..., -2, -1. + EQ is what to return if GROUP1 and GROUP2 are the same. */ +static int +group_cmp (int group1, int group2, int eq) +{ + if (group1 == group2) + return eq; + else if ((group1 < 0 && group2 < 0) || (group1 >= 0 && group2 >= 0)) + return group1 - group2; + else + return group2 - group1; +} + +/* Compare clusters CL1 & CL2 by the order that they should appear in + output. */ +static int +hol_cluster_cmp (const struct hol_cluster *cl1, const struct hol_cluster *cl2) +{ + /* If one cluster is deeper than the other, use its ancestor at the same + level, so that finding the common ancestor is straightforward. */ + while (cl1->depth > cl2->depth) + cl1 = cl1->parent; + while (cl2->depth > cl1->depth) + cl2 = cl2->parent; + + /* Now reduce both clusters to their ancestors at the point where both have + a common parent; these can be directly compared. */ + while (cl1->parent != cl2->parent) + cl1 = cl1->parent, cl2 = cl2->parent; + + return group_cmp (cl1->group, cl2->group, cl2->index - cl1->index); +} + +/* Return the ancestor of CL that's just below the root (i.e., has a parent + of 0). */ +static struct hol_cluster * +hol_cluster_base (struct hol_cluster *cl) +{ + while (cl->parent) + cl = cl->parent; + return cl; +} + +/* Return true if CL1 is a child of CL2. */ +static int +hol_cluster_is_child (const struct hol_cluster *cl1, + const struct hol_cluster *cl2) +{ + while (cl1 && cl1 != cl2) + cl1 = cl1->parent; + return cl1 == cl2; +} + +/* Given the name of an OPTION_DOC option, modifies NAME to start at the tail + that should be used for comparisons, and returns true iff it should be + treated as a non-option. */ +static int +canon_doc_option (const char **name) +{ + int non_opt; + /* Skip initial whitespace. */ + while (isspace (**name)) + (*name)++; + /* Decide whether this looks like an option (leading '-') or not. */ + non_opt = (**name != '-'); + /* Skip until part of name used for sorting. */ + while (**name && !isalnum (**name)) + (*name)++; + return non_opt; +} + +#define HOL_ENTRY_PTRCMP(a,b) ((a)->ord < (b)->ord ? -1 : 1) + +/* Order ENTRY1 & ENTRY2 by the order which they should appear in a help + listing. */ +static int +hol_entry_cmp (const struct hol_entry *entry1, + const struct hol_entry *entry2) +{ + /* The group numbers by which the entries should be ordered; if either is + in a cluster, then this is just the group within the cluster. */ + int group1 = entry1->group, group2 = entry2->group; + + if (entry1->cluster != entry2->cluster) + { + /* The entries are not within the same cluster, so we can't compare them + directly, we have to use the appropriate clustering level too. */ + if (! entry1->cluster) + /* ENTRY1 is at the 'base level', not in a cluster, so we have to + compare it's group number with that of the base cluster in which + ENTRY2 resides. Note that if they're in the same group, the + clustered option always comes last. */ + return group_cmp (group1, hol_cluster_base (entry2->cluster)->group, -1); + else if (! entry2->cluster) + /* Likewise, but ENTRY2's not in a cluster. */ + return group_cmp (hol_cluster_base (entry1->cluster)->group, group2, 1); + else + /* Both entries are in clusters, we can just compare the clusters. */ + return hol_cluster_cmp (entry1->cluster, entry2->cluster); + } + else if (group1 == group2) + /* The entries are both in the same cluster and group, so compare them + alphabetically. */ + { + int short1 = hol_entry_first_short (entry1); + int short2 = hol_entry_first_short (entry2); + int doc1 = odoc (entry1->opt); + int doc2 = odoc (entry2->opt); + const char *long1 = hol_entry_first_long (entry1); + const char *long2 = hol_entry_first_long (entry2); + + if (doc1) + doc1 = long1 != NULL && canon_doc_option (&long1); + if (doc2) + doc2 = long2 != NULL && canon_doc_option (&long2); + + if (doc1 != doc2) + /* 'documentation' options always follow normal options (or + documentation options that *look* like normal options). */ + return doc1 - doc2; + else if (!short1 && !short2 && long1 && long2) + /* Only long options. */ + return __strcasecmp (long1, long2); + else + /* Compare short/short, long/short, short/long, using the first + character of long options. Entries without *any* valid + options (such as options with OPTION_HIDDEN set) will be put + first, but as they're not displayed, it doesn't matter where + they are. */ + { + char first1 = short1 ? short1 : long1 ? *long1 : 0; + char first2 = short2 ? short2 : long2 ? *long2 : 0; +#ifdef _tolower + int lower_cmp = _tolower (first1) - _tolower (first2); +#else + int lower_cmp = tolower (first1) - tolower (first2); +#endif + /* Compare ignoring case, except when the options are both the + same letter, in which case lower-case always comes first. */ + return lower_cmp ? lower_cmp : first2 - first1; + } + } + else + /* Within the same cluster, but not the same group, so just compare + groups. */ + return group_cmp (group1, group2, 0); +} + +/* Version of hol_entry_cmp with correct signature for qsort. */ +static int +hol_entry_qcmp (const void *entry1_v, const void *entry2_v) +{ + return hol_entry_cmp (entry1_v, entry2_v); +} + +/* Sort HOL by group and alphabetically by option name (with short options + taking precedence over long). Since the sorting is for display purposes + only, the shadowing of options isn't effected. */ +static void +hol_sort (struct hol *hol) +{ + if (hol->num_entries > 0) + { + unsigned i; + struct hol_entry *e; + for (i = 0, e = hol->entries; i < hol->num_entries; i++, e++) + e->ord = i; + qsort (hol->entries, hol->num_entries, sizeof (struct hol_entry), + hol_entry_qcmp); + } +} + +/* Append MORE to HOL, destroying MORE in the process. Options in HOL shadow + any in MORE with the same name. */ +static void +hol_append (struct hol *hol, struct hol *more) +{ + struct hol_cluster **cl_end = &hol->clusters; + + /* Steal MORE's cluster list, and add it to the end of HOL's. */ + while (*cl_end) + cl_end = &(*cl_end)->next; + *cl_end = more->clusters; + more->clusters = 0; + + /* Merge entries. */ + if (more->num_entries > 0) + { + if (hol->num_entries == 0) + { + hol->num_entries = more->num_entries; + hol->entries = more->entries; + hol->short_options = more->short_options; + more->num_entries = 0; /* Mark MORE's fields as invalid. */ + } + else + /* Append the entries in MORE to those in HOL, taking care to only add + non-shadowed SHORT_OPTIONS values. */ + { + unsigned left; + char *so, *more_so; + struct hol_entry *e; + unsigned num_entries = hol->num_entries + more->num_entries; + struct hol_entry *entries = + malloc (num_entries * sizeof (struct hol_entry)); + unsigned hol_so_len = strlen (hol->short_options); + char *short_options = + malloc (hol_so_len + strlen (more->short_options) + 1); + + assert (entries && short_options); + if (SIZE_MAX <= UINT_MAX) + assert (num_entries <= SIZE_MAX / sizeof (struct hol_entry)); + + __mempcpy (__mempcpy (entries, hol->entries, + hol->num_entries * sizeof (struct hol_entry)), + more->entries, + more->num_entries * sizeof (struct hol_entry)); + + __mempcpy (short_options, hol->short_options, hol_so_len); + + /* Fix up the short options pointers from HOL. */ + for (e = entries, left = hol->num_entries; left > 0; e++, left--) + e->short_options + = short_options + (e->short_options - hol->short_options); + + /* Now add the short options from MORE, fixing up its entries + too. */ + so = short_options + hol_so_len; + more_so = more->short_options; + for (left = more->num_entries; left > 0; e++, left--) + { + int opts_left; + const struct argp_option *opt; + + e->short_options = so; + + for (opts_left = e->num, opt = e->opt; opts_left; opt++, opts_left--) + { + int ch = *more_so; + if (oshort (opt) && ch == opt->key) + /* The next short option in MORE_SO, CH, is from OPT. */ + { + if (! find_char (ch, short_options, + short_options + hol_so_len)) + /* The short option CH isn't shadowed by HOL's options, + so add it to the sum. */ + *so++ = ch; + more_so++; + } + } + } + + *so = '\0'; + + free (hol->entries); + free (hol->short_options); + + hol->entries = entries; + hol->num_entries = num_entries; + hol->short_options = short_options; + } + } + + hol_free (more); +} + +/* Inserts enough spaces to make sure STREAM is at column COL. */ +static void +indent_to (argp_fmtstream_t stream, unsigned col) +{ + int needed = col - __argp_fmtstream_point (stream); + while (needed-- > 0) + __argp_fmtstream_putc (stream, ' '); +} + +/* Output to STREAM either a space, or a newline if there isn't room for at + least ENSURE characters before the right margin. */ +static void +space (argp_fmtstream_t stream, size_t ensure) +{ + if (__argp_fmtstream_point (stream) + ensure + >= __argp_fmtstream_rmargin (stream)) + __argp_fmtstream_putc (stream, '\n'); + else + __argp_fmtstream_putc (stream, ' '); +} + +/* If the option REAL has an argument, we print it in using the printf + format REQ_FMT or OPT_FMT depending on whether it's a required or + optional argument. */ +static void +arg (const struct argp_option *real, const char *req_fmt, const char *opt_fmt, + const char *domain, argp_fmtstream_t stream) +{ + if (real->arg) + { + if (real->flags & OPTION_ARG_OPTIONAL) + __argp_fmtstream_printf (stream, opt_fmt, + dgettext (domain, real->arg)); + else + __argp_fmtstream_printf (stream, req_fmt, + dgettext (domain, real->arg)); + } +} + +/* Helper functions for hol_entry_help. */ + +/* State used during the execution of hol_help. */ +struct hol_help_state +{ + /* PREV_ENTRY should contain the previous entry printed, or 0. */ + struct hol_entry *prev_entry; + + /* If an entry is in a different group from the previous one, and SEP_GROUPS + is true, then a blank line will be printed before any output. */ + int sep_groups; + + /* True if a duplicate option argument was suppressed (only ever set if + UPARAMS.dup_args is false). */ + int suppressed_dup_arg; +}; + +/* Some state used while printing a help entry (used to communicate with + helper functions). See the doc for hol_entry_help for more info, as most + of the fields are copied from its arguments. */ +struct pentry_state +{ + const struct hol_entry *entry; + argp_fmtstream_t stream; + struct hol_help_state *hhstate; + + /* True if nothing's been printed so far. */ + int first; + + /* If non-zero, the state that was used to print this help. */ + const struct argp_state *state; +}; + +/* If a user doc filter should be applied to DOC, do so. */ +static const char * +filter_doc (const char *doc, int key, const struct argp *argp, + const struct argp_state *state) +{ + if (argp && argp->help_filter) + /* We must apply a user filter to this output. */ + { + void *input = __argp_input (argp, state); + return (*argp->help_filter) (key, doc, input); + } + else + /* No filter. */ + return doc; +} + +/* Prints STR as a header line, with the margin lines set appropriately, and + notes the fact that groups should be separated with a blank line. ARGP is + the argp that should dictate any user doc filtering to take place. Note + that the previous wrap margin isn't restored, but the left margin is reset + to 0. */ +static void +print_header (const char *str, const struct argp *argp, + struct pentry_state *pest) +{ + const char *tstr = str ? dgettext (argp->argp_domain, str) : NULL; + const char *fstr = filter_doc (tstr, ARGP_KEY_HELP_HEADER, argp, pest->state); + + if (fstr) + { + if (*fstr) + { + if (pest->hhstate->prev_entry) + /* Precede with a blank line. */ + __argp_fmtstream_putc (pest->stream, '\n'); + indent_to (pest->stream, uparams.header_col); + __argp_fmtstream_set_lmargin (pest->stream, uparams.header_col); + __argp_fmtstream_set_wmargin (pest->stream, uparams.header_col); + __argp_fmtstream_puts (pest->stream, fstr); + __argp_fmtstream_set_lmargin (pest->stream, 0); + __argp_fmtstream_putc (pest->stream, '\n'); + } + + pest->hhstate->sep_groups = 1; /* Separate subsequent groups. */ + } + + if (fstr != tstr) + free ((char *) fstr); +} + +/* Inserts a comma if this isn't the first item on the line, and then makes + sure we're at least to column COL. If this *is* the first item on a line, + prints any pending whitespace/headers that should precede this line. Also + clears FIRST. */ +static void +comma (unsigned col, struct pentry_state *pest) +{ + if (pest->first) + { + const struct hol_entry *pe = pest->hhstate->prev_entry; + const struct hol_cluster *cl = pest->entry->cluster; + + if (pest->hhstate->sep_groups && pe && pest->entry->group != pe->group) + __argp_fmtstream_putc (pest->stream, '\n'); + + if (cl && cl->header && *cl->header + && (!pe + || (pe->cluster != cl + && !hol_cluster_is_child (pe->cluster, cl)))) + /* If we're changing clusters, then this must be the start of the + ENTRY's cluster unless that is an ancestor of the previous one + (in which case we had just popped into a sub-cluster for a bit). + If so, then print the cluster's header line. */ + { + int old_wm = __argp_fmtstream_wmargin (pest->stream); + print_header (cl->header, cl->argp, pest); + __argp_fmtstream_set_wmargin (pest->stream, old_wm); + } + + pest->first = 0; + } + else + __argp_fmtstream_puts (pest->stream, ", "); + + indent_to (pest->stream, col); +} + +/* Print help for ENTRY to STREAM. */ +static void +hol_entry_help (struct hol_entry *entry, const struct argp_state *state, + argp_fmtstream_t stream, struct hol_help_state *hhstate) +{ + unsigned num; + const struct argp_option *real = entry->opt, *opt; + char *so = entry->short_options; + int have_long_opt = 0; /* We have any long options. */ + /* Saved margins. */ + int old_lm = __argp_fmtstream_set_lmargin (stream, 0); + int old_wm = __argp_fmtstream_wmargin (stream); + /* PEST is a state block holding some of our variables that we'd like to + share with helper functions. */ + struct pentry_state pest = { entry, stream, hhstate, 1, state }; + + if (! odoc (real)) + for (opt = real, num = entry->num; num > 0; opt++, num--) + if (opt->name && ovisible (opt)) + { + have_long_opt = 1; + break; + } + + /* First emit short options. */ + __argp_fmtstream_set_wmargin (stream, uparams.short_opt_col); /* For truly bizarre cases. */ + for (opt = real, num = entry->num; num > 0; opt++, num--) + if (oshort (opt) && opt->key == *so) + /* OPT has a valid (non shadowed) short option. */ + { + if (ovisible (opt)) + { + comma (uparams.short_opt_col, &pest); + __argp_fmtstream_putc (stream, '-'); + __argp_fmtstream_putc (stream, *so); + if (!have_long_opt || uparams.dup_args) + arg (real, " %s", "[%s]", + state == NULL ? NULL : state->root_argp->argp_domain, + stream); + else if (real->arg) + hhstate->suppressed_dup_arg = 1; + } + so++; + } + + /* Now, long options. */ + if (odoc (real)) + /* A "documentation" option. */ + { + __argp_fmtstream_set_wmargin (stream, uparams.doc_opt_col); + for (opt = real, num = entry->num; num > 0; opt++, num--) + if (opt->name && ovisible (opt)) + { + comma (uparams.doc_opt_col, &pest); + /* Calling dgettext here isn't quite right, since sorting will + have been done on the original; but documentation options + should be pretty rare anyway... */ + __argp_fmtstream_puts (stream, + dgettext (state == NULL ? NULL + : state->root_argp->argp_domain, + opt->name)); + } + } + else + /* A real long option. */ + { + __argp_fmtstream_set_wmargin (stream, uparams.long_opt_col); + for (opt = real, num = entry->num; num > 0; opt++, num--) + if (opt->name && ovisible (opt)) + { + comma (uparams.long_opt_col, &pest); + __argp_fmtstream_printf (stream, "--%s", opt->name); + arg (real, "=%s", "[=%s]", + state == NULL ? NULL : state->root_argp->argp_domain, stream); + } + } + + /* Next, documentation strings. */ + __argp_fmtstream_set_lmargin (stream, 0); + + if (pest.first) + { + /* Didn't print any switches, what's up? */ + if (!oshort (real) && !real->name) + /* This is a group header, print it nicely. */ + print_header (real->doc, entry->argp, &pest); + else + /* Just a totally shadowed option or null header; print nothing. */ + goto cleanup; /* Just return, after cleaning up. */ + } + else + { + const char *tstr = real->doc ? dgettext (state == NULL ? NULL + : state->root_argp->argp_domain, + real->doc) : 0; + const char *fstr = filter_doc (tstr, real->key, entry->argp, state); + if (fstr && *fstr) + { + unsigned int col = __argp_fmtstream_point (stream); + + __argp_fmtstream_set_lmargin (stream, uparams.opt_doc_col); + __argp_fmtstream_set_wmargin (stream, uparams.opt_doc_col); + + if (col > (unsigned int) (uparams.opt_doc_col + 3)) + __argp_fmtstream_putc (stream, '\n'); + else if (col >= (unsigned int) uparams.opt_doc_col) + __argp_fmtstream_puts (stream, " "); + else + indent_to (stream, uparams.opt_doc_col); + + __argp_fmtstream_puts (stream, fstr); + } + if (fstr && fstr != tstr) + free ((char *) fstr); + + /* Reset the left margin. */ + __argp_fmtstream_set_lmargin (stream, 0); + __argp_fmtstream_putc (stream, '\n'); + } + + hhstate->prev_entry = entry; + +cleanup: + __argp_fmtstream_set_lmargin (stream, old_lm); + __argp_fmtstream_set_wmargin (stream, old_wm); +} + +/* Output a long help message about the options in HOL to STREAM. */ +static void +hol_help (struct hol *hol, const struct argp_state *state, + argp_fmtstream_t stream) +{ + unsigned num; + struct hol_entry *entry; + struct hol_help_state hhstate = { 0, 0, 0 }; + + for (entry = hol->entries, num = hol->num_entries; num > 0; entry++, num--) + hol_entry_help (entry, state, stream, &hhstate); + + if (hhstate.suppressed_dup_arg && uparams.dup_args_note) + { + const char *tstr = dgettext (state == NULL ? NULL + : state->root_argp->argp_domain, "\ +Mandatory or optional arguments to long options are also mandatory or \ +optional for any corresponding short options."); + const char *fstr = filter_doc (tstr, ARGP_KEY_HELP_DUP_ARGS_NOTE, + state ? state->root_argp : 0, state); + if (fstr && *fstr) + { + __argp_fmtstream_putc (stream, '\n'); + __argp_fmtstream_puts (stream, fstr); + __argp_fmtstream_putc (stream, '\n'); + } + if (fstr && fstr != tstr) + free ((char *) fstr); + } +} + +/* Helper functions for hol_usage. */ + +/* If OPT is a short option without an arg, append its key to the string + pointer pointer to by COOKIE, and advance the pointer. */ +static int +add_argless_short_opt (const struct argp_option *opt, + const struct argp_option *real, + const char *domain, void *cookie) +{ + char **snao_end = cookie; + if (!(opt->arg || real->arg) + && !((opt->flags | real->flags) & OPTION_NO_USAGE)) + *(*snao_end)++ = opt->key; + return 0; +} + +/* If OPT is a short option with an arg, output a usage entry for it to the + stream pointed at by COOKIE. */ +static int +usage_argful_short_opt (const struct argp_option *opt, + const struct argp_option *real, + const char *domain, void *cookie) +{ + argp_fmtstream_t stream = cookie; + const char *arg = opt->arg; + int flags = opt->flags | real->flags; + + if (! arg) + arg = real->arg; + + if (arg && !(flags & OPTION_NO_USAGE)) + { + arg = dgettext (domain, arg); + + if (flags & OPTION_ARG_OPTIONAL) + __argp_fmtstream_printf (stream, " [-%c[%s]]", opt->key, arg); + else + { + /* Manually do line wrapping so that it (probably) won't + get wrapped at the embedded space. */ + space (stream, 6 + strlen (arg)); + __argp_fmtstream_printf (stream, "[-%c %s]", opt->key, arg); + } + } + + return 0; +} + +/* Output a usage entry for the long option opt to the stream pointed at by + COOKIE. */ +static int +usage_long_opt (const struct argp_option *opt, + const struct argp_option *real, + const char *domain, void *cookie) +{ + argp_fmtstream_t stream = cookie; + const char *arg = opt->arg; + int flags = opt->flags | real->flags; + + if (! arg) + arg = real->arg; + + if (! (flags & OPTION_NO_USAGE)) + { + if (arg) + { + arg = dgettext (domain, arg); + if (flags & OPTION_ARG_OPTIONAL) + __argp_fmtstream_printf (stream, " [--%s[=%s]]", opt->name, arg); + else + __argp_fmtstream_printf (stream, " [--%s=%s]", opt->name, arg); + } + else + __argp_fmtstream_printf (stream, " [--%s]", opt->name); + } + + return 0; +} + +/* Print a short usage description for the arguments in HOL to STREAM. */ +static void +hol_usage (struct hol *hol, argp_fmtstream_t stream) +{ + if (hol->num_entries > 0) + { + unsigned nentries; + struct hol_entry *entry; + char *short_no_arg_opts = alloca (strlen (hol->short_options) + 1); + char *snao_end = short_no_arg_opts; + + /* First we put a list of short options without arguments. */ + for (entry = hol->entries, nentries = hol->num_entries + ; nentries > 0 + ; entry++, nentries--) + hol_entry_short_iterate (entry, add_argless_short_opt, + entry->argp->argp_domain, &snao_end); + if (snao_end > short_no_arg_opts) + { + *snao_end++ = 0; + __argp_fmtstream_printf (stream, " [-%s]", short_no_arg_opts); + } + + /* Now a list of short options *with* arguments. */ + for (entry = hol->entries, nentries = hol->num_entries + ; nentries > 0 + ; entry++, nentries--) + hol_entry_short_iterate (entry, usage_argful_short_opt, + entry->argp->argp_domain, stream); + + /* Finally, a list of long options (whew!). */ + for (entry = hol->entries, nentries = hol->num_entries + ; nentries > 0 + ; entry++, nentries--) + hol_entry_long_iterate (entry, usage_long_opt, + entry->argp->argp_domain, stream); + } +} + +/* Make a HOL containing all levels of options in ARGP. CLUSTER is the + cluster in which ARGP's entries should be clustered, or 0. */ +static struct hol * +argp_hol (const struct argp *argp, struct hol_cluster *cluster) +{ + const struct argp_child *child = argp->children; + struct hol *hol = make_hol (argp, cluster); + if (child) + while (child->argp) + { + struct hol_cluster *child_cluster = + ((child->group || child->header) + /* Put CHILD->argp within its own cluster. */ + ? hol_add_cluster (hol, child->group, child->header, + child - argp->children, cluster, argp) + /* Just merge it into the parent's cluster. */ + : cluster); + hol_append (hol, argp_hol (child->argp, child_cluster)) ; + child++; + } + return hol; +} + +/* Calculate how many different levels with alternative args strings exist in + ARGP. */ +static size_t +argp_args_levels (const struct argp *argp) +{ + size_t levels = 0; + const struct argp_child *child = argp->children; + + if (argp->args_doc && strchr (argp->args_doc, '\n')) + levels++; + + if (child) + while (child->argp) + levels += argp_args_levels ((child++)->argp); + + return levels; +} + +/* Print all the non-option args documented in ARGP to STREAM. Any output is + preceded by a space. LEVELS is a pointer to a byte vector the length + returned by argp_args_levels; it should be initialized to zero, and + updated by this routine for the next call if ADVANCE is true. True is + returned as long as there are more patterns to output. */ +static int +argp_args_usage (const struct argp *argp, const struct argp_state *state, + char **levels, int advance, argp_fmtstream_t stream) +{ + char *our_level = *levels; + int multiple = 0; + const struct argp_child *child = argp->children; + const char *tdoc = + argp->args_doc ? dgettext (argp->argp_domain, argp->args_doc) : NULL; + const char *fdoc = filter_doc (tdoc, ARGP_KEY_HELP_ARGS_DOC, argp, state); + const char *nl = NULL; + + if (fdoc) + { + const char *cp = fdoc; + nl = __strchrnul (cp, '\n'); + if (*nl != '\0') + /* This is a 'multi-level' args doc; advance to the correct position + as determined by our state in LEVELS, and update LEVELS. */ + { + int i; + multiple = 1; + for (i = 0; i < *our_level; i++) + cp = nl + 1, nl = __strchrnul (cp, '\n'); + (*levels)++; + } + + /* Manually do line wrapping so that it (probably) won't get wrapped at + any embedded spaces. */ + space (stream, 1 + nl - cp); + + __argp_fmtstream_write (stream, cp, nl - cp); + } + if (fdoc && fdoc != tdoc) + free ((char *)fdoc); /* Free user's modified doc string. */ + + if (child) + while (child->argp) + advance = !argp_args_usage ((child++)->argp, state, levels, advance, stream); + + if (advance && multiple) + { + /* Need to increment our level. */ + if (*nl) + /* There's more we can do here. */ + { + (*our_level)++; + advance = 0; /* Our parent shouldn't advance also. */ + } + else if (*our_level > 0) + /* We had multiple levels, but used them up; reset to zero. */ + *our_level = 0; + } + + return !advance; +} + +/* Print the documentation for ARGP to STREAM; if POST is false, then + everything preceding a '\v' character in the documentation strings (or + the whole string, for those with none) is printed, otherwise, everything + following the '\v' character (nothing for strings without). Each separate + bit of documentation is separated a blank line, and if PRE_BLANK is true, + then the first is as well. If FIRST_ONLY is true, only the first + occurrence is output. Returns true if anything was output. */ +static int +argp_doc (const struct argp *argp, const struct argp_state *state, + int post, int pre_blank, int first_only, + argp_fmtstream_t stream) +{ + const char *text; + const char *inp_text; + void *input = 0; + int anything = 0; + size_t inp_text_limit = 0; + const char *doc = argp->doc ? dgettext (argp->argp_domain, argp->doc) : NULL; + const struct argp_child *child = argp->children; + + if (doc) + { + char *vt = strchr (doc, '\v'); + inp_text = post ? (vt ? vt + 1 : 0) : doc; + inp_text_limit = (!post && vt) ? (vt - doc) : 0; + } + else + inp_text = 0; + + if (argp->help_filter) + /* We have to filter the doc strings. */ + { + if (inp_text_limit) + /* Copy INP_TEXT so that it's nul-terminated. */ + inp_text = __strndup (inp_text, inp_text_limit); + input = __argp_input (argp, state); + text = + (*argp->help_filter) (post + ? ARGP_KEY_HELP_POST_DOC + : ARGP_KEY_HELP_PRE_DOC, + inp_text, input); + } + else + text = (const char *) inp_text; + + if (text) + { + if (pre_blank) + __argp_fmtstream_putc (stream, '\n'); + + if (text == inp_text && inp_text_limit) + __argp_fmtstream_write (stream, inp_text, inp_text_limit); + else + __argp_fmtstream_puts (stream, text); + + if (__argp_fmtstream_point (stream) > __argp_fmtstream_lmargin (stream)) + __argp_fmtstream_putc (stream, '\n'); + + anything = 1; + } + + if (text && text != inp_text) + free ((char *) text); /* Free TEXT returned from the help filter. */ + if (inp_text && inp_text_limit && argp->help_filter) + free ((char *) inp_text); /* We copied INP_TEXT, so free it now. */ + + if (post && argp->help_filter) + /* Now see if we have to output a ARGP_KEY_HELP_EXTRA text. */ + { + text = (*argp->help_filter) (ARGP_KEY_HELP_EXTRA, 0, input); + if (text) + { + if (anything || pre_blank) + __argp_fmtstream_putc (stream, '\n'); + __argp_fmtstream_puts (stream, text); + free ((char *) text); + if (__argp_fmtstream_point (stream) + > __argp_fmtstream_lmargin (stream)) + __argp_fmtstream_putc (stream, '\n'); + anything = 1; + } + } + + if (child) + while (child->argp && !(first_only && anything)) + anything |= + argp_doc ((child++)->argp, state, + post, anything || pre_blank, first_only, + stream); + + return anything; +} + +/* Output a usage message for ARGP to STREAM. If called from + argp_state_help, STATE is the relevant parsing state. FLAGS are from the + set ARGP_HELP_*. NAME is what to use wherever a 'program name' is + needed. */ +static void +_help (const struct argp *argp, const struct argp_state *state, FILE *stream, + unsigned flags, char *name) +{ + int anything = 0; /* Whether we've output anything. */ + struct hol *hol = 0; + argp_fmtstream_t fs; + + if (! stream) + return; + +#if _LIBC || (HAVE_FLOCKFILE && HAVE_FUNLOCKFILE) + __flockfile (stream); +#endif + + if (! uparams.valid) + fill_in_uparams (state); + + fs = __argp_make_fmtstream (stream, 0, uparams.rmargin, 0); + if (! fs) + { +#if _LIBC || (HAVE_FLOCKFILE && HAVE_FUNLOCKFILE) + __funlockfile (stream); +#endif + return; + } + + if (flags & (ARGP_HELP_USAGE | ARGP_HELP_SHORT_USAGE | ARGP_HELP_LONG)) + { + hol = argp_hol (argp, 0); + + /* If present, these options always come last. */ + hol_set_group (hol, "help", -1); + hol_set_group (hol, "version", -1); + + hol_sort (hol); + } + + if (flags & (ARGP_HELP_USAGE | ARGP_HELP_SHORT_USAGE)) + /* Print a short "Usage:" message. */ + { + int first_pattern = 1, more_patterns; + size_t num_pattern_levels = argp_args_levels (argp); + char *pattern_levels = alloca (num_pattern_levels); + + memset (pattern_levels, 0, num_pattern_levels); + + do + { + int old_lm; + int old_wm = __argp_fmtstream_set_wmargin (fs, uparams.usage_indent); + char *levels = pattern_levels; + + if (first_pattern) + __argp_fmtstream_printf (fs, "%s %s", + dgettext (argp->argp_domain, "Usage:"), + name); + else + __argp_fmtstream_printf (fs, "%s %s", + dgettext (argp->argp_domain, " or: "), + name); + + /* We set the lmargin as well as the wmargin, because hol_usage + manually wraps options with newline to avoid annoying breaks. */ + old_lm = __argp_fmtstream_set_lmargin (fs, uparams.usage_indent); + + if (flags & ARGP_HELP_SHORT_USAGE) + /* Just show where the options go. */ + { + if (hol->num_entries > 0) + __argp_fmtstream_puts (fs, dgettext (argp->argp_domain, + " [OPTION...]")); + } + else + /* Actually print the options. */ + { + hol_usage (hol, fs); + flags |= ARGP_HELP_SHORT_USAGE; /* But only do so once. */ + } + + more_patterns = argp_args_usage (argp, state, &levels, 1, fs); + + __argp_fmtstream_set_wmargin (fs, old_wm); + __argp_fmtstream_set_lmargin (fs, old_lm); + + __argp_fmtstream_putc (fs, '\n'); + anything = 1; + + first_pattern = 0; + } + while (more_patterns); + } + + if (flags & ARGP_HELP_PRE_DOC) + anything |= argp_doc (argp, state, 0, 0, 1, fs); + + if (flags & ARGP_HELP_SEE) + { + __argp_fmtstream_printf (fs, dgettext (argp->argp_domain, "\ +Try '%s --help' or '%s --usage' for more information.\n"), + name, name); + anything = 1; + } + + if (flags & ARGP_HELP_LONG) + /* Print a long, detailed help message. */ + { + /* Print info about all the options. */ + if (hol->num_entries > 0) + { + if (anything) + __argp_fmtstream_putc (fs, '\n'); + hol_help (hol, state, fs); + anything = 1; + } + } + + if (flags & ARGP_HELP_POST_DOC) + /* Print any documentation strings at the end. */ + anything |= argp_doc (argp, state, 1, anything, 0, fs); + + if ((flags & ARGP_HELP_BUG_ADDR) && argp_program_bug_address) + { + if (anything) + __argp_fmtstream_putc (fs, '\n'); + __argp_fmtstream_printf (fs, dgettext (argp->argp_domain, + "Report bugs to %s.\n"), + argp_program_bug_address); + anything = 1; + } + +#if _LIBC || (HAVE_FLOCKFILE && HAVE_FUNLOCKFILE) + __funlockfile (stream); +#endif + + if (hol) + hol_free (hol); + + __argp_fmtstream_free (fs); +} + +/* Output a usage message for ARGP to STREAM. FLAGS are from the set + ARGP_HELP_*. NAME is what to use wherever a 'program name' is needed. */ +void __argp_help (const struct argp *argp, FILE *stream, + unsigned flags, char *name) +{ + _help (argp, 0, stream, flags, name); +} +#ifdef weak_alias +weak_alias (__argp_help, argp_help) +#endif + +#if ! (defined _LIBC || HAVE_DECL_PROGRAM_INVOCATION_SHORT_NAME) +char * +__argp_short_program_name (void) +{ +# if HAVE_DECL_PROGRAM_INVOCATION_NAME + char *name = strrchr (program_invocation_name, '/'); + return name ? name + 1 : program_invocation_name; +# else + /* FIXME: What now? Miles suggests that it is better to use NULL, + but currently the value is passed on directly to fputs_unlocked, + so that requires more changes. */ +# if __GNUC__ +# warning No reasonable value to return +# endif /* __GNUC__ */ + return ""; +# endif +} +#endif + +/* Output, if appropriate, a usage message for STATE to STREAM. FLAGS are + from the set ARGP_HELP_*. */ +void +__argp_state_help (const struct argp_state *state, FILE *stream, unsigned flags) +{ + if ((!state || ! (state->flags & ARGP_NO_ERRS)) && stream) + { + if (state && (state->flags & ARGP_LONG_ONLY)) + flags |= ARGP_HELP_LONG_ONLY; + + _help (state ? state->root_argp : 0, state, stream, flags, + state ? state->name : __argp_short_program_name ()); + + if (!state || ! (state->flags & ARGP_NO_EXIT)) + { + if (flags & ARGP_HELP_EXIT_ERR) + exit (argp_err_exit_status); + if (flags & ARGP_HELP_EXIT_OK) + exit (0); + } + } +} +#ifdef weak_alias +weak_alias (__argp_state_help, argp_state_help) +#endif + +/* If appropriate, print the printf string FMT and following args, preceded + by the program name and ':', to stderr, and followed by a "Try ... --help" + message, then exit (1). */ +void +__argp_error (const struct argp_state *state, const char *fmt, ...) +{ + if (!state || !(state->flags & ARGP_NO_ERRS)) + { + FILE *stream = state ? state->err_stream : stderr; + + if (stream) + { + va_list ap; + +#if _LIBC || (HAVE_FLOCKFILE && HAVE_FUNLOCKFILE) + __flockfile (stream); +#endif + + va_start (ap, fmt); + +#ifdef _LIBC + char *buf; + + if (_IO_vasprintf (&buf, fmt, ap) < 0) + buf = NULL; + + __fxprintf (stream, "%s: %s\n", + state ? state->name : __argp_short_program_name (), buf); + + free (buf); +#else + fputs_unlocked (state ? state->name : __argp_short_program_name (), + stream); + putc_unlocked (':', stream); + putc_unlocked (' ', stream); + + vfprintf (stream, fmt, ap); + + putc_unlocked ('\n', stream); +#endif + + __argp_state_help (state, stream, ARGP_HELP_STD_ERR); + + va_end (ap); + +#if _LIBC || (HAVE_FLOCKFILE && HAVE_FUNLOCKFILE) + __funlockfile (stream); +#endif + } + } +} +#ifdef weak_alias +weak_alias (__argp_error, argp_error) +#endif + +/* Similar to the standard gnu error-reporting function error(), but will + respect the ARGP_NO_EXIT and ARGP_NO_ERRS flags in STATE, and will print + to STATE->err_stream. This is useful for argument parsing code that is + shared between program startup (when exiting is desired) and runtime + option parsing (when typically an error code is returned instead). The + difference between this function and argp_error is that the latter is for + *parsing errors*, and the former is for other problems that occur during + parsing but don't reflect a (syntactic) problem with the input. */ +void +__argp_failure (const struct argp_state *state, int status, int errnum, + const char *fmt, ...) +{ + if (!state || !(state->flags & ARGP_NO_ERRS)) + { + FILE *stream = state ? state->err_stream : stderr; + + if (stream) + { +#if _LIBC || (HAVE_FLOCKFILE && HAVE_FUNLOCKFILE) + __flockfile (stream); +#endif + +#ifdef _LIBC + __fxprintf (stream, "%s", + state ? state->name : __argp_short_program_name ()); +#else + fputs_unlocked (state ? state->name : __argp_short_program_name (), + stream); +#endif + + if (fmt) + { + va_list ap; + + va_start (ap, fmt); +#ifdef _LIBC + char *buf; + + if (_IO_vasprintf (&buf, fmt, ap) < 0) + buf = NULL; + + __fxprintf (stream, ": %s", buf); + + free (buf); +#else + putc_unlocked (':', stream); + putc_unlocked (' ', stream); + + vfprintf (stream, fmt, ap); +#endif + + va_end (ap); + } + + if (errnum) + { + char buf[200]; + +#ifdef _LIBC + __fxprintf (stream, ": %s", + __strerror_r (errnum, buf, sizeof (buf))); +#else + char const *s = NULL; + putc_unlocked (':', stream); + putc_unlocked (' ', stream); +# if GNULIB_STRERROR_R_POSIX || HAVE_DECL_STRERROR_R +# if !GNULIB_STRERROR_R_POSIX && STRERROR_R_CHAR_P + s = __strerror_r (errnum, buf, sizeof buf); +# else + if (__strerror_r (errnum, buf, sizeof buf) == 0) + s = buf; +# endif +# endif + if (! s && ! (s = strerror (errnum))) + s = dgettext (state->root_argp->argp_domain, + "Unknown system error"); + fputs_unlocked (s, stream); +#endif + } + +#if _LIBC + if (_IO_fwide (stream, 0) > 0) + putwc_unlocked (L'\n', stream); + else +#endif + putc_unlocked ('\n', stream); + +#if _LIBC || (HAVE_FLOCKFILE && HAVE_FUNLOCKFILE) + __funlockfile (stream); +#endif + + if (status && (!state || !(state->flags & ARGP_NO_EXIT))) + exit (status); + } + } +} +#ifdef weak_alias +weak_alias (__argp_failure, argp_failure) +#endif diff --git a/libdicl/gl/argp-namefrob.h b/libdicl/gl/argp-namefrob.h new file mode 100644 index 0000000..2fb9ac4 --- /dev/null +++ b/libdicl/gl/argp-namefrob.h @@ -0,0 +1,157 @@ +/* Name frobnication for compiling argp outside of glibc + Copyright (C) 1997-2019 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Written by Miles Bader . + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#if !_LIBC +/* This code is written for inclusion in gnu-libc, and uses names in the + namespace reserved for libc. If we're not compiling in libc, define those + names to be the normal ones instead. */ + +/* argp-parse functions */ +#undef __argp_parse +#define __argp_parse argp_parse +#undef __option_is_end +#define __option_is_end _option_is_end +#undef __option_is_short +#define __option_is_short _option_is_short +#undef __argp_input +#define __argp_input _argp_input + +/* argp-help functions */ +#undef __argp_help +#define __argp_help argp_help +#undef __argp_error +#define __argp_error argp_error +#undef __argp_failure +#define __argp_failure argp_failure +#undef __argp_state_help +#define __argp_state_help argp_state_help +#undef __argp_usage +#define __argp_usage argp_usage + +/* argp-fmtstream functions */ +#undef __argp_make_fmtstream +#define __argp_make_fmtstream argp_make_fmtstream +#undef __argp_fmtstream_free +#define __argp_fmtstream_free argp_fmtstream_free +#undef __argp_fmtstream_putc +#define __argp_fmtstream_putc argp_fmtstream_putc +#undef __argp_fmtstream_puts +#define __argp_fmtstream_puts argp_fmtstream_puts +#undef __argp_fmtstream_write +#define __argp_fmtstream_write argp_fmtstream_write +#undef __argp_fmtstream_printf +#define __argp_fmtstream_printf argp_fmtstream_printf +#undef __argp_fmtstream_set_lmargin +#define __argp_fmtstream_set_lmargin argp_fmtstream_set_lmargin +#undef __argp_fmtstream_set_rmargin +#define __argp_fmtstream_set_rmargin argp_fmtstream_set_rmargin +#undef __argp_fmtstream_set_wmargin +#define __argp_fmtstream_set_wmargin argp_fmtstream_set_wmargin +#undef __argp_fmtstream_point +#define __argp_fmtstream_point argp_fmtstream_point +#undef __argp_fmtstream_update +#define __argp_fmtstream_update _argp_fmtstream_update +#undef __argp_fmtstream_ensure +#define __argp_fmtstream_ensure _argp_fmtstream_ensure +#undef __argp_fmtstream_lmargin +#define __argp_fmtstream_lmargin argp_fmtstream_lmargin +#undef __argp_fmtstream_rmargin +#define __argp_fmtstream_rmargin argp_fmtstream_rmargin +#undef __argp_fmtstream_wmargin +#define __argp_fmtstream_wmargin argp_fmtstream_wmargin + +/* normal libc functions we call */ +#undef __flockfile +#define __flockfile flockfile +#undef __funlockfile +#define __funlockfile funlockfile +#undef __mempcpy +#define __mempcpy mempcpy +#undef __sleep +#define __sleep sleep +#undef __strcasecmp +#define __strcasecmp strcasecmp +#undef __strchrnul +#define __strchrnul strchrnul +#undef __strerror_r +#define __strerror_r strerror_r +#undef __strndup +#define __strndup strndup +#undef __vsnprintf +#define __vsnprintf vsnprintf + +#if defined(HAVE_DECL_CLEARERR_UNLOCKED) && !HAVE_DECL_CLEARERR_UNLOCKED +# define clearerr_unlocked(x) clearerr (x) +#endif +#if defined(HAVE_DECL_FEOF_UNLOCKED) && !HAVE_DECL_FEOF_UNLOCKED +# define feof_unlocked(x) feof (x) +#endif +#if defined(HAVE_DECL_FERROR_UNLOCKED) && !HAVE_DECL_FERROR_UNLOCKED +# define ferror_unlocked(x) ferror (x) +#endif +#if defined(HAVE_DECL_FFLUSH_UNLOCKED) && !HAVE_DECL_FFLUSH_UNLOCKED +# define fflush_unlocked(x) fflush (x) +#endif +#if defined(HAVE_DECL_FGETS_UNLOCKED) && !HAVE_DECL_FGETS_UNLOCKED +# define fgets_unlocked(x,y,z) fgets (x,y,z) +#endif +#if defined(HAVE_DECL_FPUTC_UNLOCKED) && !HAVE_DECL_FPUTC_UNLOCKED +# define fputc_unlocked(x,y) fputc (x,y) +#endif +#if defined(HAVE_DECL_FPUTS_UNLOCKED) && !HAVE_DECL_FPUTS_UNLOCKED +# define fputs_unlocked(x,y) fputs (x,y) +#endif +#if defined(HAVE_DECL_FREAD_UNLOCKED) && !HAVE_DECL_FREAD_UNLOCKED +# define fread_unlocked(w,x,y,z) fread (w,x,y,z) +#endif +#if defined(HAVE_DECL_FWRITE_UNLOCKED) && !HAVE_DECL_FWRITE_UNLOCKED +# define fwrite_unlocked(w,x,y,z) fwrite (w,x,y,z) +#endif +#if defined(HAVE_DECL_GETC_UNLOCKED) && !HAVE_DECL_GETC_UNLOCKED +# define getc_unlocked(x) getc (x) +#endif +#if defined(HAVE_DECL_GETCHAR_UNLOCKED) && !HAVE_DECL_GETCHAR_UNLOCKED +# define getchar_unlocked() getchar () +#endif +#if defined(HAVE_DECL_PUTC_UNLOCKED) && !HAVE_DECL_PUTC_UNLOCKED +# define putc_unlocked(x,y) putc (x,y) +#endif +#if defined(HAVE_DECL_PUTCHAR_UNLOCKED) && !HAVE_DECL_PUTCHAR_UNLOCKED +# define putchar_unlocked(x) putchar (x) +#endif + +#endif /* !_LIBC */ + +#ifndef __set_errno +#define __set_errno(e) (errno = (e)) +#endif + +#if defined GNULIB_ARGP_DISABLE_DIRNAME +# define __argp_base_name(arg) arg +#elif defined GNULIB_ARGP_EXTERN_BASENAME +extern char *__argp_base_name (const char *arg); +#else +# include "dirname.h" +# define __argp_base_name last_component +#endif + +#if defined _LIBC || HAVE_DECL_PROGRAM_INVOCATION_SHORT_NAME +# define __argp_short_program_name() (program_invocation_short_name) +#else +extern char *__argp_short_program_name (void); +#endif diff --git a/libdicl/gl/argp-parse.c b/libdicl/gl/argp-parse.c new file mode 100644 index 0000000..6dec573 --- /dev/null +++ b/libdicl/gl/argp-parse.c @@ -0,0 +1,957 @@ +/* Hierarchical argument parsing, layered over getopt + Copyright (C) 1995-2019 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Written by Miles Bader . + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef _LIBC +# include +# undef dgettext +# define dgettext(domain, msgid) \ + __dcgettext (domain, msgid, LC_MESSAGES) +#else +# include "gettext.h" +#endif +#define N_(msgid) msgid + +#include "argp.h" +#include "argp-namefrob.h" + +#define alignto(n, d) ((((n) + (d) - 1) / (d)) * (d)) + +/* Getopt return values. */ +#define KEY_END (-1) /* The end of the options. */ +#define KEY_ARG 1 /* A non-option argument. */ +#define KEY_ERR '?' /* An error parsing the options. */ + +/* The meta-argument used to prevent any further arguments being interpreted + as options. */ +#define QUOTE "--" + +/* The number of bits we steal in a long-option value for our own use. */ +#define GROUP_BITS CHAR_BIT + +/* The number of bits available for the user value. */ +#define USER_BITS ((sizeof ((struct option *)0)->val * CHAR_BIT) - GROUP_BITS) +#define USER_MASK ((1 << USER_BITS) - 1) + +/* EZ alias for ARGP_ERR_UNKNOWN. */ +#define EBADKEY ARGP_ERR_UNKNOWN + +/* Default options. */ + +/* When argp is given the --HANG switch, _ARGP_HANG is set and argp will sleep + for one second intervals, decrementing _ARGP_HANG until it's zero. Thus + you can force the program to continue by attaching a debugger and setting + it to 0 yourself. */ +static volatile int _argp_hang; + +#define OPT_PROGNAME -2 +#define OPT_USAGE -3 +#define OPT_HANG -4 + +static const struct argp_option argp_default_options[] = +{ + {"help", '?', 0, 0, N_("give this help list"), -1}, + {"usage", OPT_USAGE, 0, 0, N_("give a short usage message"), 0}, + {"program-name",OPT_PROGNAME, N_("NAME"), OPTION_HIDDEN, + N_("set the program name"), 0}, + {"HANG", OPT_HANG, N_("SECS"), OPTION_ARG_OPTIONAL | OPTION_HIDDEN, + N_("hang for SECS seconds (default 3600)"), 0}, + {NULL, 0, 0, 0, NULL, 0} +}; + +static error_t +argp_default_parser (int key, char *arg, struct argp_state *state) +{ + switch (key) + { + case '?': + __argp_state_help (state, state->out_stream, ARGP_HELP_STD_HELP); + break; + case OPT_USAGE: + __argp_state_help (state, state->out_stream, + ARGP_HELP_USAGE | ARGP_HELP_EXIT_OK); + break; + + case OPT_PROGNAME: /* Set the program name. */ +#if defined _LIBC || HAVE_DECL_PROGRAM_INVOCATION_NAME + program_invocation_name = arg; +#endif + /* [Note that some systems only have PROGRAM_INVOCATION_SHORT_NAME (aka + __PROGNAME), in which case, PROGRAM_INVOCATION_NAME is just defined + to be that, so we have to be a bit careful here.] */ + + /* Update what we use for messages. */ + state->name = __argp_base_name (arg); + +#if defined _LIBC || HAVE_DECL_PROGRAM_INVOCATION_SHORT_NAME + program_invocation_short_name = state->name; +#endif + + if ((state->flags & (ARGP_PARSE_ARGV0 | ARGP_NO_ERRS)) + == ARGP_PARSE_ARGV0) + /* Update what getopt uses too. */ + state->argv[0] = arg; + + break; + + case OPT_HANG: + _argp_hang = atoi (arg ? arg : "3600"); + while (_argp_hang-- > 0) + __sleep (1); + break; + + default: + return EBADKEY; + } + return 0; +} + +static const struct argp argp_default_argp = + {argp_default_options, &argp_default_parser, NULL, NULL, NULL, NULL, "libc"}; + + +static const struct argp_option argp_version_options[] = +{ + {"version", 'V', 0, 0, N_("print program version"), -1}, + {NULL, 0, 0, 0, NULL, 0} +}; + +static error_t +argp_version_parser (int key, char *arg, struct argp_state *state) +{ + switch (key) + { + case 'V': + if (argp_program_version_hook) + (*argp_program_version_hook) (state->out_stream, state); + else if (argp_program_version) + fprintf (state->out_stream, "%s\n", argp_program_version); + else + __argp_error (state, "%s", + dgettext (state->root_argp->argp_domain, + "(PROGRAM ERROR) No version known!?")); + if (! (state->flags & ARGP_NO_EXIT)) + exit (0); + break; + default: + return EBADKEY; + } + return 0; +} + +static const struct argp argp_version_argp = + {argp_version_options, &argp_version_parser, NULL, NULL, NULL, NULL, "libc"}; + +/* Returns the offset into the getopt long options array LONG_OPTIONS of a + long option with called NAME, or -1 if none is found. Passing NULL as + NAME will return the number of options. */ +static int +find_long_option (struct option *long_options, const char *name) +{ + struct option *l = long_options; + while (l->name != NULL) + if (name != NULL && strcmp (l->name, name) == 0) + return l - long_options; + else + l++; + if (name == NULL) + return l - long_options; + else + return -1; +} + + +/* The state of a "group" during parsing. Each group corresponds to a + particular argp structure from the tree of such descending from the top + level argp passed to argp_parse. */ +struct group +{ + /* This group's parsing function. */ + argp_parser_t parser; + + /* Which argp this group is from. */ + const struct argp *argp; + + /* Points to the point in SHORT_OPTS corresponding to the end of the short + options for this group. We use it to determine from which group a + particular short options is from. */ + char *short_end; + + /* The number of non-option args successfully handled by this parser. */ + unsigned args_processed; + + /* This group's parser's parent's group. */ + struct group *parent; + unsigned parent_index; /* And the our position in the parent. */ + + /* These fields are swapped into and out of the state structure when + calling this group's parser. */ + void *input, **child_inputs; + void *hook; +}; + +/* Call GROUP's parser with KEY and ARG, swapping any group-specific info + from STATE before calling, and back into state afterwards. If GROUP has + no parser, EBADKEY is returned. */ +static error_t +group_parse (struct group *group, struct argp_state *state, int key, char *arg) +{ + if (group->parser) + { + error_t err; + state->hook = group->hook; + state->input = group->input; + state->child_inputs = group->child_inputs; + state->arg_num = group->args_processed; + err = (*group->parser)(key, arg, state); + group->hook = state->hook; + return err; + } + else + return EBADKEY; +} + +struct parser +{ + const struct argp *argp; + + /* SHORT_OPTS is the getopt short options string for the union of all the + groups of options. */ + char *short_opts; + /* LONG_OPTS is the array of getop long option structures for the union of + all the groups of options. */ + struct option *long_opts; + /* OPT_DATA is the getopt data used for the re-entrant getopt. */ + struct _getopt_data opt_data; + + /* States of the various parsing groups. */ + struct group *groups; + /* The end of the GROUPS array. */ + struct group *egroup; + /* A vector containing storage for the CHILD_INPUTS field in all groups. */ + void **child_inputs; + + /* True if we think using getopt is still useful; if false, then + remaining arguments are just passed verbatim with ARGP_KEY_ARG. This is + cleared whenever getopt returns KEY_END, but may be set again if the user + moves the next argument pointer backwards. */ + int try_getopt; + + /* State block supplied to parsing routines. */ + struct argp_state state; + + /* Memory used by this parser. */ + void *storage; +}; + +/* The next usable entries in the various parser tables being filled in by + convert_options. */ +struct parser_convert_state +{ + struct parser *parser; + char *short_end; + struct option *long_end; + void **child_inputs_end; +}; + +/* Converts all options in ARGP (which is put in GROUP) and ancestors + into getopt options stored in SHORT_OPTS and LONG_OPTS; SHORT_END and + CVT->LONG_END are the points at which new options are added. Returns the + next unused group entry. CVT holds state used during the conversion. */ +static struct group * +convert_options (const struct argp *argp, + struct group *parent, unsigned parent_index, + struct group *group, struct parser_convert_state *cvt) +{ + /* REAL is the most recent non-alias value of OPT. */ + const struct argp_option *real = argp->options; + const struct argp_child *children = argp->children; + + if (real || argp->parser) + { + const struct argp_option *opt; + + if (real) + for (opt = real; !__option_is_end (opt); opt++) + { + if (! (opt->flags & OPTION_ALIAS)) + /* OPT isn't an alias, so we can use values from it. */ + real = opt; + + if (! (real->flags & OPTION_DOC)) + /* A real option (not just documentation). */ + { + if (__option_is_short (opt)) + /* OPT can be used as a short option. */ + { + *cvt->short_end++ = opt->key; + if (real->arg) + { + *cvt->short_end++ = ':'; + if (real->flags & OPTION_ARG_OPTIONAL) + *cvt->short_end++ = ':'; + } + *cvt->short_end = '\0'; /* keep 0 terminated */ + } + + if (opt->name + && find_long_option (cvt->parser->long_opts, opt->name) < 0) + /* OPT can be used as a long option. */ + { + cvt->long_end->name = opt->name; + cvt->long_end->has_arg = + (real->arg + ? (real->flags & OPTION_ARG_OPTIONAL + ? optional_argument + : required_argument) + : no_argument); + cvt->long_end->flag = 0; + /* we add a disambiguating code to all the user's + values (which is removed before we actually call + the function to parse the value); this means that + the user loses use of the high 8 bits in all his + values (the sign of the lower bits is preserved + however)... */ + cvt->long_end->val = + ((opt->key ? opt->key : real->key) & USER_MASK) + + (((group - cvt->parser->groups) + 1) << USER_BITS); + + /* Keep the LONG_OPTS list terminated. */ + (++cvt->long_end)->name = NULL; + } + } + } + + group->parser = argp->parser; + group->argp = argp; + group->short_end = cvt->short_end; + group->args_processed = 0; + group->parent = parent; + group->parent_index = parent_index; + group->input = 0; + group->hook = 0; + group->child_inputs = 0; + + if (children) + /* Assign GROUP's CHILD_INPUTS field some space from + CVT->child_inputs_end.*/ + { + unsigned num_children = 0; + while (children[num_children].argp) + num_children++; + group->child_inputs = cvt->child_inputs_end; + cvt->child_inputs_end += num_children; + } + + parent = group++; + } + else + parent = 0; + + if (children) + { + unsigned index = 0; + while (children->argp) + group = + convert_options (children++->argp, parent, index++, group, cvt); + } + + return group; +} + +/* Find the merged set of getopt options, with keys appropriately prefixed. */ +static void +parser_convert (struct parser *parser, const struct argp *argp, int flags) +{ + struct parser_convert_state cvt; + + cvt.parser = parser; + cvt.short_end = parser->short_opts; + cvt.long_end = parser->long_opts; + cvt.child_inputs_end = parser->child_inputs; + + if (flags & ARGP_IN_ORDER) + *cvt.short_end++ = '-'; + else if (flags & ARGP_NO_ARGS) + *cvt.short_end++ = '+'; + *cvt.short_end = '\0'; + + cvt.long_end->name = NULL; + + parser->argp = argp; + + if (argp) + parser->egroup = convert_options (argp, 0, 0, parser->groups, &cvt); + else + parser->egroup = parser->groups; /* No parsers at all! */ +} + +/* Lengths of various parser fields which we will allocated. */ +struct parser_sizes +{ + size_t short_len; /* Getopt short options string. */ + size_t long_len; /* Getopt long options vector. */ + size_t num_groups; /* Group structures we allocate. */ + size_t num_child_inputs; /* Child input slots. */ +}; + +/* For ARGP, increments the NUM_GROUPS field in SZS by the total number of + argp structures descended from it, and the SHORT_LEN & LONG_LEN fields by + the maximum lengths of the resulting merged getopt short options string and + long-options array, respectively. */ +static void +calc_sizes (const struct argp *argp, struct parser_sizes *szs) +{ + const struct argp_child *child = argp->children; + const struct argp_option *opt = argp->options; + + if (opt || argp->parser) + { + szs->num_groups++; + if (opt) + { + int num_opts = 0; + while (!__option_is_end (opt++)) + num_opts++; + szs->short_len += num_opts * 3; /* opt + up to 2 ':'s */ + szs->long_len += num_opts; + } + } + + if (child) + while (child->argp) + { + calc_sizes ((child++)->argp, szs); + szs->num_child_inputs++; + } +} + +/* Initializes PARSER to parse ARGP in a manner described by FLAGS. */ +static error_t +parser_init (struct parser *parser, const struct argp *argp, + int argc, char **argv, int flags, void *input) +{ + error_t err = 0; + struct group *group; + struct parser_sizes szs; + struct _getopt_data opt_data = _GETOPT_DATA_INITIALIZER; + char *storage; + size_t glen, gsum; + size_t clen, csum; + size_t llen, lsum; + size_t slen, ssum; + + szs.short_len = (flags & ARGP_NO_ARGS) ? 0 : 1; + szs.long_len = 0; + szs.num_groups = 0; + szs.num_child_inputs = 0; + + if (argp) + calc_sizes (argp, &szs); + + /* Lengths of the various bits of storage used by PARSER. */ + glen = (szs.num_groups + 1) * sizeof (struct group); + clen = szs.num_child_inputs * sizeof (void *); + llen = (szs.long_len + 1) * sizeof (struct option); + slen = szs.short_len + 1; + + /* Sums of previous lengths, properly aligned. There's no need to + align gsum, since struct group is aligned at least as strictly as + void * (since it contains a void * member). And there's no need + to align lsum, since struct option is aligned at least as + strictly as char. */ + gsum = glen; + csum = alignto (gsum + clen, alignof (struct option)); + lsum = csum + llen; + ssum = lsum + slen; + + parser->storage = malloc (ssum); + if (! parser->storage) + return ENOMEM; + + storage = parser->storage; + parser->groups = parser->storage; + parser->child_inputs = (void **) (storage + gsum); + parser->long_opts = (struct option *) (storage + csum); + parser->short_opts = storage + lsum; + parser->opt_data = opt_data; + + memset (parser->child_inputs, 0, clen); + parser_convert (parser, argp, flags); + + memset (&parser->state, 0, sizeof (struct argp_state)); + parser->state.root_argp = parser->argp; + parser->state.argc = argc; + parser->state.argv = argv; + parser->state.flags = flags; + parser->state.err_stream = stderr; + parser->state.out_stream = stdout; + parser->state.next = 0; /* Tell getopt to initialize. */ + parser->state.pstate = parser; + + parser->try_getopt = 1; + + /* Call each parser for the first time, giving it a chance to propagate + values to child parsers. */ + if (parser->groups < parser->egroup) + parser->groups->input = input; + for (group = parser->groups; + group < parser->egroup && (!err || err == EBADKEY); + group++) + { + if (group->parent) + /* If a child parser, get the initial input value from the parent. */ + group->input = group->parent->child_inputs[group->parent_index]; + + if (!group->parser + && group->argp->children && group->argp->children->argp) + /* For the special case where no parsing function is supplied for an + argp, propagate its input to its first child, if any (this just + makes very simple wrapper argps more convenient). */ + group->child_inputs[0] = group->input; + + err = group_parse (group, &parser->state, ARGP_KEY_INIT, 0); + } + if (err == EBADKEY) + err = 0; /* Some parser didn't understand. */ + + if (err) + return err; + + if (parser->state.flags & ARGP_NO_ERRS) + { + parser->opt_data.opterr = 0; + if (parser->state.flags & ARGP_PARSE_ARGV0) + /* getopt always skips ARGV[0], so we have to fake it out. As long + as OPTERR is 0, then it shouldn't actually try to access it. */ + parser->state.argv--, parser->state.argc++; + } + else + parser->opt_data.opterr = 1; /* Print error messages. */ + + if (parser->state.argv == argv && argv[0]) + /* There's an argv[0]; use it for messages. */ + parser->state.name = __argp_base_name (argv[0]); + else + parser->state.name = __argp_short_program_name (); + + return 0; +} + +/* Free any storage consumed by PARSER (but not PARSER itself). */ +static error_t +parser_finalize (struct parser *parser, + error_t err, int arg_ebadkey, int *end_index) +{ + struct group *group; + + if (err == EBADKEY && arg_ebadkey) + /* Suppress errors generated by unparsed arguments. */ + err = 0; + + if (! err) + { + if (parser->state.next == parser->state.argc) + /* We successfully parsed all arguments! Call all the parsers again, + just a few more times... */ + { + for (group = parser->groups; + group < parser->egroup && (!err || err==EBADKEY); + group++) + if (group->args_processed == 0) + err = group_parse (group, &parser->state, ARGP_KEY_NO_ARGS, 0); + for (group = parser->egroup - 1; + group >= parser->groups && (!err || err==EBADKEY); + group--) + err = group_parse (group, &parser->state, ARGP_KEY_END, 0); + + if (err == EBADKEY) + err = 0; /* Some parser didn't understand. */ + + /* Tell the user that all arguments are parsed. */ + if (end_index) + *end_index = parser->state.next; + } + else if (end_index) + /* Return any remaining arguments to the user. */ + *end_index = parser->state.next; + else + /* No way to return the remaining arguments, they must be bogus. */ + { + if (!(parser->state.flags & ARGP_NO_ERRS) + && parser->state.err_stream) + fprintf (parser->state.err_stream, + dgettext (parser->argp->argp_domain, + "%s: Too many arguments\n"), + parser->state.name); + err = EBADKEY; + } + } + + /* Okay, we're all done, with either an error or success; call the parsers + to indicate which one. */ + + if (err) + { + /* Maybe print an error message. */ + if (err == EBADKEY) + /* An appropriate message describing what the error was should have + been printed earlier. */ + __argp_state_help (&parser->state, parser->state.err_stream, + ARGP_HELP_STD_ERR); + + /* Since we didn't exit, give each parser an error indication. */ + for (group = parser->groups; group < parser->egroup; group++) + group_parse (group, &parser->state, ARGP_KEY_ERROR, 0); + } + else + /* Notify parsers of success, and propagate back values from parsers. */ + { + /* We pass over the groups in reverse order so that child groups are + given a chance to do there processing before passing back a value to + the parent. */ + for (group = parser->egroup - 1 + ; group >= parser->groups && (!err || err == EBADKEY) + ; group--) + err = group_parse (group, &parser->state, ARGP_KEY_SUCCESS, 0); + if (err == EBADKEY) + err = 0; /* Some parser didn't understand. */ + } + + /* Call parsers once more, to do any final cleanup. Errors are ignored. */ + for (group = parser->egroup - 1; group >= parser->groups; group--) + group_parse (group, &parser->state, ARGP_KEY_FINI, 0); + + if (err == EBADKEY) + err = EINVAL; + + free (parser->storage); + + return err; +} + +/* Call the user parsers to parse the non-option argument VAL, at the current + position, returning any error. The state NEXT pointer is assumed to have + been adjusted (by getopt) to point after this argument; this function will + adjust it correctly to reflect however many args actually end up being + consumed. */ +static error_t +parser_parse_arg (struct parser *parser, char *val) +{ + /* Save the starting value of NEXT, first adjusting it so that the arg + we're parsing is again the front of the arg vector. */ + int index = --parser->state.next; + error_t err = EBADKEY; + struct group *group; + int key = 0; /* Which of ARGP_KEY_ARG[S] we used. */ + + /* Try to parse the argument in each parser. */ + for (group = parser->groups + ; group < parser->egroup && err == EBADKEY + ; group++) + { + parser->state.next++; /* For ARGP_KEY_ARG, consume the arg. */ + key = ARGP_KEY_ARG; + err = group_parse (group, &parser->state, key, val); + + if (err == EBADKEY) + /* This parser doesn't like ARGP_KEY_ARG; try ARGP_KEY_ARGS instead. */ + { + parser->state.next--; /* For ARGP_KEY_ARGS, put back the arg. */ + key = ARGP_KEY_ARGS; + err = group_parse (group, &parser->state, key, 0); + } + } + + if (! err) + { + if (key == ARGP_KEY_ARGS) + /* The default for ARGP_KEY_ARGS is to assume that if NEXT isn't + changed by the user, *all* arguments should be considered + consumed. */ + parser->state.next = parser->state.argc; + + if (parser->state.next > index) + /* Remember that we successfully processed a non-option + argument -- but only if the user hasn't gotten tricky and set + the clock back. */ + (--group)->args_processed += (parser->state.next - index); + else + /* The user wants to reparse some args, give getopt another try. */ + parser->try_getopt = 1; + } + + return err; +} + +/* Call the user parsers to parse the option OPT, with argument VAL, at the + current position, returning any error. */ +static error_t +parser_parse_opt (struct parser *parser, int opt, char *val) +{ + /* The group key encoded in the high bits; 0 for short opts or + group_number + 1 for long opts. */ + int group_key = opt >> USER_BITS; + error_t err = EBADKEY; + + if (group_key == 0) + /* A short option. By comparing OPT's position in SHORT_OPTS to the + various starting positions in each group's SHORT_END field, we can + determine which group OPT came from. */ + { + struct group *group; + char *short_index = strchr (parser->short_opts, opt); + + if (short_index) + for (group = parser->groups; group < parser->egroup; group++) + if (group->short_end > short_index) + { + err = group_parse (group, &parser->state, opt, + parser->opt_data.optarg); + break; + } + } + else + /* A long option. Preserve the sign in the user key, without + invoking undefined behavior. Assume two's complement. */ + { + int user_key = + ((opt & (1 << (USER_BITS - 1))) ? ~USER_MASK : 0) | (opt & USER_MASK); + err = + group_parse (&parser->groups[group_key - 1], &parser->state, + user_key, parser->opt_data.optarg); + } + + if (err == EBADKEY) + /* At least currently, an option not recognized is an error in the + parser, because we pre-compute which parser is supposed to deal + with each option. */ + { + static const char bad_key_err[] = + N_("(PROGRAM ERROR) Option should have been recognized!?"); + if (group_key == 0) + __argp_error (&parser->state, "-%c: %s", opt, + dgettext (parser->argp->argp_domain, bad_key_err)); + else + { + struct option *long_opt = parser->long_opts; + while (long_opt->val != opt && long_opt->name) + long_opt++; + __argp_error (&parser->state, "--%s: %s", + long_opt->name ? long_opt->name : "???", + dgettext (parser->argp->argp_domain, bad_key_err)); + } + } + + return err; +} + +/* Parse the next argument in PARSER (as indicated by PARSER->state.next). + Any error from the parsers is returned, and *ARGP_EBADKEY indicates + whether a value of EBADKEY is due to an unrecognized argument (which is + generally not fatal). */ +static error_t +parser_parse_next (struct parser *parser, int *arg_ebadkey) +{ + int opt; + error_t err = 0; + + if (parser->state.quoted && parser->state.next < parser->state.quoted) + /* The next argument pointer has been moved to before the quoted + region, so pretend we never saw the quoting "--", and give getopt + another chance. If the user hasn't removed it, getopt will just + process it again. */ + parser->state.quoted = 0; + + if (parser->try_getopt && !parser->state.quoted) + /* Give getopt a chance to parse this. */ + { + /* Put it back in OPTIND for getopt. */ + parser->opt_data.optind = parser->state.next; + /* Distinguish KEY_ERR from a real option. */ + parser->opt_data.optopt = KEY_END; + if (parser->state.flags & ARGP_LONG_ONLY) + opt = _getopt_long_only_r (parser->state.argc, parser->state.argv, + parser->short_opts, parser->long_opts, 0, + &parser->opt_data); + else + opt = _getopt_long_r (parser->state.argc, parser->state.argv, + parser->short_opts, parser->long_opts, 0, + &parser->opt_data); + /* And see what getopt did. */ + parser->state.next = parser->opt_data.optind; + + if (opt == KEY_END) + /* Getopt says there are no more options, so stop using + getopt; we'll continue if necessary on our own. */ + { + parser->try_getopt = 0; + if (parser->state.next > 1 + && strcmp (parser->state.argv[parser->state.next - 1], QUOTE) + == 0) + /* Not only is this the end of the options, but it's a + "quoted" region, which may have args that *look* like + options, so we definitely shouldn't try to use getopt past + here, whatever happens. */ + parser->state.quoted = parser->state.next; + } + else if (opt == KEY_ERR && parser->opt_data.optopt != KEY_END) + /* KEY_ERR can have the same value as a valid user short + option, but in the case of a real error, getopt sets OPTOPT + to the offending character, which can never be KEY_END. */ + { + *arg_ebadkey = 0; + return EBADKEY; + } + } + else + opt = KEY_END; + + if (opt == KEY_END) + { + /* We're past what getopt considers the options. */ + if (parser->state.next >= parser->state.argc + || (parser->state.flags & ARGP_NO_ARGS)) + /* Indicate that we're done. */ + { + *arg_ebadkey = 1; + return EBADKEY; + } + else + /* A non-option arg; simulate what getopt might have done. */ + { + opt = KEY_ARG; + parser->opt_data.optarg = parser->state.argv[parser->state.next++]; + } + } + + if (opt == KEY_ARG) + /* A non-option argument; try each parser in turn. */ + err = parser_parse_arg (parser, parser->opt_data.optarg); + else + err = parser_parse_opt (parser, opt, parser->opt_data.optarg); + + if (err == EBADKEY) + *arg_ebadkey = (opt == KEY_END || opt == KEY_ARG); + + return err; +} + +/* Parse the options strings in ARGC & ARGV according to the argp in ARGP. + FLAGS is one of the ARGP_ flags above. If END_INDEX is non-NULL, the + index in ARGV of the first unparsed option is returned in it. If an + unknown option is present, EINVAL is returned; if some parser routine + returned a non-zero value, it is returned; otherwise 0 is returned. */ +error_t +__argp_parse (const struct argp *argp, int argc, char **argv, unsigned flags, + int *end_index, void *input) +{ + error_t err; + struct parser parser; + + /* If true, then err == EBADKEY is a result of a non-option argument failing + to be parsed (which in some cases isn't actually an error). */ + int arg_ebadkey = 0; + +#ifndef _LIBC + if (!(flags & ARGP_PARSE_ARGV0)) + { +#if HAVE_DECL_PROGRAM_INVOCATION_NAME + if (!program_invocation_name) + program_invocation_name = argv[0]; +#endif +#if HAVE_DECL_PROGRAM_INVOCATION_SHORT_NAME + if (!program_invocation_short_name) + program_invocation_short_name = __argp_base_name (argv[0]); +#endif + } +#endif + + if (! (flags & ARGP_NO_HELP)) + /* Add our own options. */ + { + struct argp_child *child = alloca (4 * sizeof (struct argp_child)); + struct argp *top_argp = alloca (sizeof (struct argp)); + + /* TOP_ARGP has no options, it just serves to group the user & default + argps. */ + memset (top_argp, 0, sizeof (*top_argp)); + top_argp->children = child; + + memset (child, 0, 4 * sizeof (struct argp_child)); + + if (argp) + (child++)->argp = argp; + (child++)->argp = &argp_default_argp; + if (argp_program_version || argp_program_version_hook) + (child++)->argp = &argp_version_argp; + child->argp = 0; + + argp = top_argp; + } + + /* Construct a parser for these arguments. */ + err = parser_init (&parser, argp, argc, argv, flags, input); + + if (! err) + /* Parse! */ + { + while (! err) + err = parser_parse_next (&parser, &arg_ebadkey); + err = parser_finalize (&parser, err, arg_ebadkey, end_index); + } + + return err; +} +#ifdef weak_alias +weak_alias (__argp_parse, argp_parse) +#endif + +/* Return the input field for ARGP in the parser corresponding to STATE; used + by the help routines. */ +void * +__argp_input (const struct argp *argp, const struct argp_state *state) +{ + if (state) + { + struct group *group; + struct parser *parser = state->pstate; + + for (group = parser->groups; group < parser->egroup; group++) + if (group->argp == argp) + return group->input; + } + + return 0; +} +#ifdef weak_alias +weak_alias (__argp_input, _argp_input) +#endif diff --git a/libdicl/gl/argp-pin.c b/libdicl/gl/argp-pin.c new file mode 100644 index 0000000..ece7423 --- /dev/null +++ b/libdicl/gl/argp-pin.c @@ -0,0 +1,33 @@ +/* Full and short program names for argp module + Copyright (C) 2005, 2009-2019 Free Software Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#ifndef HAVE_PROGRAM_INVOCATION_SHORT_NAME +char *program_invocation_short_name = 0; +#endif +#ifndef HAVE_PROGRAM_INVOCATION_NAME +char *program_invocation_name = 0; +#endif + +#if (defined HAVE_PROGRAM_INVOCATION_SHORT_NAME \ + && defined HAVE_PROGRAM_INVOCATION_NAME) +/* This declaration is solely to ensure that after preprocessing + this file is never empty. */ +typedef int dummy; +#endif diff --git a/libdicl/gl/argp-pv.c b/libdicl/gl/argp-pv.c new file mode 100644 index 0000000..f76b365 --- /dev/null +++ b/libdicl/gl/argp-pv.c @@ -0,0 +1,33 @@ +/* Default definition for ARGP_PROGRAM_VERSION. + Copyright (C) 1996-2019 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Written by Miles Bader . + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +/* If set by the user program to a non-zero value, then a default option + --version is added (unless the ARGP_NO_HELP flag is used), which will + print this string followed by a newline and exit (unless the + ARGP_NO_EXIT flag is used). Overridden by ARGP_PROGRAM_VERSION_HOOK. */ +const char *argp_program_version +/* This variable should be zero-initialized. On most systems, putting it into + BSS is sufficient. Not so on Mac OS X 10.3 and 10.4, see + + . */ +#if defined __ELF__ + /* On ELF systems, variables in BSS behave well. */ +#else + = (const char *) 0 +#endif + ; diff --git a/libdicl/gl/argp-pvh.c b/libdicl/gl/argp-pvh.c new file mode 100644 index 0000000..be73e33 --- /dev/null +++ b/libdicl/gl/argp-pvh.c @@ -0,0 +1,30 @@ +/* Default definition for ARGP_PROGRAM_VERSION_HOOK. + Copyright (C) 1996-2019 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Written by Miles Bader . + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include "argp.h" + +/* If set by the user program to a non-zero value, then a default option + --version is added (unless the ARGP_NO_HELP flag is used), which calls + this function with a stream to print the version to and a pointer to the + current parsing state, and then exits (unless the ARGP_NO_EXIT flag is + used). This variable takes precedent over ARGP_PROGRAM_VERSION. */ +void (*argp_program_version_hook) (FILE *stream, struct argp_state *state) = NULL; diff --git a/libdicl/gl/argp-xinl.c b/libdicl/gl/argp-xinl.c new file mode 100644 index 0000000..ebc032f --- /dev/null +++ b/libdicl/gl/argp-xinl.c @@ -0,0 +1,46 @@ +/* Real definitions for extern inline functions in argp.h + Copyright (C) 1997-2019 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Written by Miles Bader . + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#if defined _LIBC || defined HAVE_FEATURES_H +# include +#endif + +#ifndef __USE_EXTERN_INLINES +# define __USE_EXTERN_INLINES 1 +#endif +#ifdef _LIBC +# define ARGP_EI +#else +# define ARGP_EI _GL_EXTERN_INLINE +#endif +#undef __OPTIMIZE__ +#define __OPTIMIZE__ 1 +#include "argp.h" + +/* Add weak aliases. */ +#if _LIBC - 0 && defined (weak_alias) + +weak_alias (__argp_usage, argp_usage) +weak_alias (__option_is_short, _option_is_short) +weak_alias (__option_is_end, _option_is_end) + +#endif diff --git a/libdicl/gl/argp.h b/libdicl/gl/argp.h new file mode 100644 index 0000000..7aba887 --- /dev/null +++ b/libdicl/gl/argp.h @@ -0,0 +1,634 @@ +/* Hierarchical argument parsing, layered over getopt. + Copyright (C) 1995-2019 Free Software Foundation, Inc. + This file is part of the GNU C Library. + Written by Miles Bader . + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#ifndef _ARGP_H +#define _ARGP_H + +#include +#include +#include +#include + +#define __need_error_t +#include + +#ifndef __THROW +# define __THROW +#endif +#ifndef __NTH +# define __NTH(fct) fct __THROW +#endif + +/* The __attribute__ feature is available in gcc versions 2.5 and later. + The __-protected variants of the attributes 'format' and 'printf' are + accepted by gcc versions 2.6.4 (effectively 2.7) and later. + We enable _GL_ATTRIBUTE_FORMAT only if these are supported too, because + gnulib and libintl do '#define printf __printf__' when they override + the 'printf' function. */ +#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 7) +# define _GL_ATTRIBUTE_FORMAT(spec) __attribute__ ((__format__ spec)) +#else +# define _GL_ATTRIBUTE_FORMAT(spec) /* empty */ +#endif + +/* GCC 2.95 and later have "__restrict"; C99 compilers have + "restrict", and "configure" may have defined "restrict". + Other compilers use __restrict, __restrict__, and _Restrict, and + 'configure' might #define 'restrict' to those words. */ +#ifndef __restrict +# if ! (2 < __GNUC__ || (2 == __GNUC__ && 95 <= __GNUC_MINOR__)) +# if 199901L <= __STDC_VERSION__ +# define __restrict restrict +# else +# define __restrict +# endif +# endif +#endif + +#ifndef __error_t_defined +typedef int error_t; +# define __error_t_defined +#endif + +#ifdef __cplusplus +extern "C" { +#endif + +/* Glibc documentation: + https://www.gnu.org/software/libc/manual/html_node/Argp.html */ + +/* A description of a particular option. A pointer to an array of + these is passed in the OPTIONS field of an argp structure. Each option + entry can correspond to one long option and/or one short option; more + names for the same option can be added by following an entry in an option + array with options having the OPTION_ALIAS flag set. */ +struct argp_option +{ + /* The long option name. For more than one name for the same option, you + can use following options with the OPTION_ALIAS flag set. */ + const char *name; + + /* What key is returned for this option. If > 0 and printable, then it's + also accepted as a short option. */ + int key; + + /* If non-NULL, this is the name of the argument associated with this + option, which is required unless the OPTION_ARG_OPTIONAL flag is set. */ + const char *arg; + + /* OPTION_ flags. */ + int flags; + + /* The doc string for this option. If both NAME and KEY are 0, This string + will be printed outdented from the normal option column, making it + useful as a group header (it will be the first thing printed in its + group); in this usage, it's conventional to end the string with a ':'. + + Write the initial value as N_("TEXT") if you want xgettext to collect + it into a POT file. */ + const char *doc; + + /* The group this option is in. In a long help message, options are sorted + alphabetically within each group, and the groups presented in the order + 0, 1, 2, ..., n, -m, ..., -2, -1. Every entry in an options array with + if this field 0 will inherit the group number of the previous entry, or + zero if it's the first one, unless its a group header (NAME and KEY both + 0), in which case, the previous entry + 1 is the default. Automagic + options such as --help are put into group -1. */ + int group; +}; + +/* The argument associated with this option is optional. */ +#define OPTION_ARG_OPTIONAL 0x1 + +/* This option isn't displayed in any help messages. */ +#define OPTION_HIDDEN 0x2 + +/* This option is an alias for the closest previous non-alias option. This + means that it will be displayed in the same help entry, and will inherit + fields other than NAME and KEY from the aliased option. */ +#define OPTION_ALIAS 0x4 + +/* This option isn't actually an option (and so should be ignored by the + actual option parser), but rather an arbitrary piece of documentation that + should be displayed in much the same manner as the options. If this flag + is set, then the option NAME field is displayed unmodified (e.g., no '--' + prefix is added) at the left-margin (where a *short* option would normally + be displayed), and the documentation string in the normal place. The NAME + field will be translated using gettext, unless OPTION_NO_TRANS is set (see + below). For purposes of sorting, any leading whitespace and punctuation is + ignored, except that if the first non-whitespace character is not '-', this + entry is displayed after all options (and OPTION_DOC entries with a leading + '-') in the same group. */ +#define OPTION_DOC 0x8 + +/* This option shouldn't be included in "long" usage messages (but is still + included in help messages). This is mainly intended for options that are + completely documented in an argp's ARGS_DOC field, in which case including + the option in the generic usage list would be redundant. For instance, + if ARGS_DOC is "FOO BAR\n-x BLAH", and the '-x' option's purpose is to + distinguish these two cases, -x should probably be marked + OPTION_NO_USAGE. */ +#define OPTION_NO_USAGE 0x10 + +/* Valid only in conjunction with OPTION_DOC. This option disables translation + of option name. */ +#define OPTION_NO_TRANS 0x20 + +struct argp; /* fwd declare this type */ +struct argp_state; /* " */ +struct argp_child; /* " */ + +/* The type of a pointer to an argp parsing function. */ +typedef error_t (*argp_parser_t) (int __key, char *__arg, + struct argp_state *__state); + +/* What to return for unrecognized keys. For special ARGP_KEY_ keys, such + returns will simply be ignored. For user keys, this error will be turned + into EINVAL (if the call to argp_parse is such that errors are propagated + back to the user instead of exiting); returning EINVAL itself would result + in an immediate stop to parsing in *all* cases. */ +#define ARGP_ERR_UNKNOWN E2BIG /* Hurd should never need E2BIG. XXX */ + +/* Special values for the KEY argument to an argument parsing function. + ARGP_ERR_UNKNOWN should be returned if they aren't understood. + + The sequence of keys to a parsing function is either (where each + uppercased word should be prefixed by 'ARGP_KEY_' and opt is a user key): + + INIT opt... NO_ARGS END SUCCESS -- No non-option arguments at all + or INIT (opt | ARG)... END SUCCESS -- All non-option args parsed + or INIT (opt | ARG)... SUCCESS -- Some non-option arg unrecognized + + The third case is where every parser returned ARGP_KEY_UNKNOWN for an + argument, in which case parsing stops at that argument (returning the + unparsed arguments to the caller of argp_parse if requested, or stopping + with an error message if not). + + If an error occurs (either detected by argp, or because the parsing + function returned an error value), then the parser is called with + ARGP_KEY_ERROR, and no further calls are made. */ + +/* This is not an option at all, but rather a command line argument. If a + parser receiving this key returns success, the fact is recorded, and the + ARGP_KEY_NO_ARGS case won't be used. HOWEVER, if while processing the + argument, a parser function decrements the NEXT field of the state it's + passed, the option won't be considered processed; this is to allow you to + actually modify the argument (perhaps into an option), and have it + processed again. */ +#define ARGP_KEY_ARG 0 +/* There are remaining arguments not parsed by any parser, which may be found + starting at (STATE->argv + STATE->next). If success is returned, but + STATE->next left untouched, it's assumed that all arguments were consume, + otherwise, the parser should adjust STATE->next to reflect any arguments + consumed. */ +#define ARGP_KEY_ARGS 0x1000006 +/* There are no more command line arguments at all. */ +#define ARGP_KEY_END 0x1000001 +/* Because it's common to want to do some special processing if there aren't + any non-option args, user parsers are called with this key if they didn't + successfully process any non-option arguments. Called just before + ARGP_KEY_END (where more general validity checks on previously parsed + arguments can take place). */ +#define ARGP_KEY_NO_ARGS 0x1000002 +/* Passed in before any parsing is done. Afterwards, the values of each + element of the CHILD_INPUT field, if any, in the state structure is + copied to each child's state to be the initial value of the INPUT field. */ +#define ARGP_KEY_INIT 0x1000003 +/* Use after all other keys, including SUCCESS & END. */ +#define ARGP_KEY_FINI 0x1000007 +/* Passed in when parsing has successfully been completed (even if there are + still arguments remaining). */ +#define ARGP_KEY_SUCCESS 0x1000004 +/* Passed in if an error occurs. */ +#define ARGP_KEY_ERROR 0x1000005 + +/* An argp structure contains a set of options declarations, a function to + deal with parsing one, documentation string, a possible vector of child + argp's, and perhaps a function to filter help output. When actually + parsing options, getopt is called with the union of all the argp + structures chained together through their CHILD pointers, with conflicts + being resolved in favor of the first occurrence in the chain. */ +struct argp +{ + /* An array of argp_option structures, terminated by an entry with both + NAME and KEY having a value of 0. */ + const struct argp_option *options; + + /* What to do with an option from this structure. KEY is the key + associated with the option, and ARG is any associated argument (NULL if + none was supplied). If KEY isn't understood, ARGP_ERR_UNKNOWN should be + returned. If a non-zero, non-ARGP_ERR_UNKNOWN value is returned, then + parsing is stopped immediately, and that value is returned from + argp_parse(). For special (non-user-supplied) values of KEY, see the + ARGP_KEY_ definitions below. */ + argp_parser_t parser; + + /* If non-NULL, a string describing what other arguments are wanted by this + program. It is only used by argp_usage to print the "Usage:" message. + If it contains newlines, the strings separated by them are considered + alternative usage patterns, and printed on separate lines (lines after + the first are prefix by " or: " instead of "Usage:"). */ + const char *args_doc; + + /* If non-NULL, a string containing extra text to be printed before and + after the options in a long help message (separated by a vertical tab + '\v' character). + Write the initial value as N_("BEFORE-TEXT") "\v" N_("AFTER-TEXT") if + you want xgettext to collect the two pieces of text into a POT file. */ + const char *doc; + + /* A vector of argp_children structures, terminated by a member with a 0 + argp field, pointing to child argps should be parsed with this one. Any + conflicts are resolved in favor of this argp, or early argps in the + CHILDREN list. This field is useful if you use libraries that supply + their own argp structure, which you want to use in conjunction with your + own. */ + const struct argp_child *children; + + /* If non-zero, this should be a function to filter the output of help + messages. KEY is either a key from an option, in which case TEXT is + that option's help text, or a special key from the ARGP_KEY_HELP_ + defines, below, describing which other help text TEXT is. The function + should return either TEXT, if it should be used as-is, a replacement + string, which should be malloced, and will be freed by argp, or NULL, + meaning "print nothing". The value for TEXT is *after* any translation + has been done, so if any of the replacement text also needs translation, + that should be done by the filter function. INPUT is either the input + supplied to argp_parse, or NULL, if argp_help was called directly. */ + char *(*help_filter) (int __key, const char *__text, void *__input); + + /* If non-zero the strings used in the argp library are translated using + the domain described by this string. Otherwise the currently installed + default domain is used. */ + const char *argp_domain; +}; + +/* Possible KEY arguments to a help filter function. */ +#define ARGP_KEY_HELP_PRE_DOC 0x2000001 /* Help text preceding options. */ +#define ARGP_KEY_HELP_POST_DOC 0x2000002 /* Help text following options. */ +#define ARGP_KEY_HELP_HEADER 0x2000003 /* Option header string. */ +#define ARGP_KEY_HELP_EXTRA 0x2000004 /* After all other documentation; + TEXT is NULL for this key. */ +/* Explanatory note emitted when duplicate option arguments have been + suppressed. */ +#define ARGP_KEY_HELP_DUP_ARGS_NOTE 0x2000005 +#define ARGP_KEY_HELP_ARGS_DOC 0x2000006 /* Argument doc string. */ + +/* When an argp has a non-zero CHILDREN field, it should point to a vector of + argp_child structures, each of which describes a subsidiary argp. */ +struct argp_child +{ + /* The child parser. */ + const struct argp *argp; + + /* Flags for this child. */ + int flags; + + /* If non-zero, an optional header to be printed in help output before the + child options. As a side-effect, a non-zero value forces the child + options to be grouped together; to achieve this effect without actually + printing a header string, use a value of "". */ + const char *header; + + /* Where to group the child options relative to the other ("consolidated") + options in the parent argp; the values are the same as the GROUP field + in argp_option structs, but all child-groupings follow parent options at + a particular group level. If both this field and HEADER are zero, then + they aren't grouped at all, but rather merged with the parent options + (merging the child's grouping levels with the parents). */ + int group; +}; + +/* Parsing state. This is provided to parsing functions called by argp, + which may examine and, as noted, modify fields. */ +struct argp_state +{ + /* The top level ARGP being parsed. */ + const struct argp *root_argp; + + /* The argument vector being parsed. May be modified. */ + int argc; + char **argv; + + /* The index in ARGV of the next arg that to be parsed. May be modified. */ + int next; + + /* The flags supplied to argp_parse. May be modified. */ + unsigned flags; + + /* While calling a parsing function with a key of ARGP_KEY_ARG, this is the + number of the current arg, starting at zero, and incremented after each + such call returns. At all other times, this is the number of such + arguments that have been processed. */ + unsigned arg_num; + + /* If non-zero, the index in ARGV of the first argument following a special + '--' argument (which prevents anything following being interpreted as an + option). Only set once argument parsing has proceeded past this point. */ + int quoted; + + /* An arbitrary pointer passed in from the user. */ + void *input; + /* Values to pass to child parsers. This vector will be the same length as + the number of children for the current parser. */ + void **child_inputs; + + /* For the parser's use. Initialized to 0. */ + void *hook; + + /* The name used when printing messages. This is initialized to ARGV[0], + or PROGRAM_INVOCATION_NAME if that is unavailable. */ + char *name; + + /* Streams used when argp prints something. */ + FILE *err_stream; /* For errors; initialized to stderr. */ + FILE *out_stream; /* For information; initialized to stdout. */ + + void *pstate; /* Private, for use by argp. */ +}; + +/* Flags for argp_parse (note that the defaults are those that are + convenient for program command line parsing): */ + +/* Don't ignore the first element of ARGV. Normally (and always unless + ARGP_NO_ERRS is set) the first element of the argument vector is + skipped for option parsing purposes, as it corresponds to the program name + in a command line. */ +#define ARGP_PARSE_ARGV0 0x01 + +/* Don't print error messages for unknown options to stderr; unless this flag + is set, ARGP_PARSE_ARGV0 is ignored, as ARGV[0] is used as the program + name in the error messages. This flag implies ARGP_NO_EXIT (on the + assumption that silent exiting upon errors is bad behaviour). */ +#define ARGP_NO_ERRS 0x02 + +/* Don't parse any non-option args. Normally non-option args are parsed by + calling the parse functions with a key of ARGP_KEY_ARG, and the actual arg + as the value. Since it's impossible to know which parse function wants to + handle it, each one is called in turn, until one returns 0 or an error + other than ARGP_ERR_UNKNOWN; if an argument is handled by no one, the + argp_parse returns prematurely (but with a return value of 0). If all + args have been parsed without error, all parsing functions are called one + last time with a key of ARGP_KEY_END. This flag needn't normally be set, + as the normal behavior is to stop parsing as soon as some argument can't + be handled. */ +#define ARGP_NO_ARGS 0x04 + +/* Parse options and arguments in the same order they occur on the command + line -- normally they're rearranged so that all options come first. */ +#define ARGP_IN_ORDER 0x08 + +/* Don't provide the standard long option --help, which causes usage and + option help information to be output to stdout, and exit (0) called. */ +#define ARGP_NO_HELP 0x10 + +/* Don't exit on errors (they may still result in error messages). */ +#define ARGP_NO_EXIT 0x20 + +/* Use the gnu getopt "long-only" rules for parsing arguments. */ +#define ARGP_LONG_ONLY 0x40 + +/* Turns off any message-printing/exiting options. */ +#define ARGP_SILENT (ARGP_NO_EXIT | ARGP_NO_ERRS | ARGP_NO_HELP) + +/* Parse the options strings in ARGC & ARGV according to the options in ARGP. + FLAGS is one of the ARGP_ flags above. If ARG_INDEX is non-NULL, the + index in ARGV of the first unparsed option is returned in it. If an + unknown option is present, ARGP_ERR_UNKNOWN is returned; if some parser + routine returned a non-zero value, it is returned; otherwise 0 is + returned. This function may also call exit unless the ARGP_NO_HELP flag + is set. INPUT is a pointer to a value to be passed in to the parser. */ +extern error_t argp_parse (const struct argp *__restrict __argp, + int /*argc*/, char **__restrict /*argv*/, + unsigned __flags, int *__restrict __arg_index, + void *__restrict __input); +extern error_t __argp_parse (const struct argp *__restrict __argp, + int /*argc*/, char **__restrict /*argv*/, + unsigned __flags, int *__restrict __arg_index, + void *__restrict __input); + +/* Global variables. */ + +/* GNULIB makes sure both program_invocation_name and + program_invocation_short_name are available */ +#ifdef GNULIB_PROGRAM_INVOCATION_NAME +extern char *program_invocation_name; +# undef HAVE_DECL_PROGRAM_INVOCATION_NAME +# define HAVE_DECL_PROGRAM_INVOCATION_NAME 1 +#endif + +#ifdef GNULIB_PROGRAM_INVOCATION_SHORT_NAME +extern char *program_invocation_short_name; +# undef HAVE_DECL_PROGRAM_INVOCATION_SHORT_NAME +# define HAVE_DECL_PROGRAM_INVOCATION_SHORT_NAME 1 +#endif + +/* If defined or set by the user program to a non-zero value, then a default + option --version is added (unless the ARGP_NO_HELP flag is used), which + will print this string followed by a newline and exit (unless the + ARGP_NO_EXIT flag is used). Overridden by ARGP_PROGRAM_VERSION_HOOK. */ +extern const char *argp_program_version; + +/* If defined or set by the user program to a non-zero value, then a default + option --version is added (unless the ARGP_NO_HELP flag is used), which + calls this function with a stream to print the version to and a pointer to + the current parsing state, and then exits (unless the ARGP_NO_EXIT flag is + used). This variable takes precedent over ARGP_PROGRAM_VERSION. */ +extern void (*argp_program_version_hook) (FILE *__restrict __stream, + struct argp_state *__restrict + __state); + +/* If defined or set by the user program, it should point to string that is + the bug-reporting address for the program. It will be printed by + argp_help if the ARGP_HELP_BUG_ADDR flag is set (as it is by various + standard help messages), embedded in a sentence that says something like + "Report bugs to ADDR." */ +extern const char *argp_program_bug_address; + +/* The exit status that argp will use when exiting due to a parsing error. + If not defined or set by the user program, this defaults to EX_USAGE from + . */ +extern error_t argp_err_exit_status; + +/* Flags for argp_help. */ +#define ARGP_HELP_USAGE 0x01 /* a Usage: message. */ +#define ARGP_HELP_SHORT_USAGE 0x02 /* " but don't actually print options. */ +#define ARGP_HELP_SEE 0x04 /* a "Try ... for more help" message. */ +#define ARGP_HELP_LONG 0x08 /* a long help message. */ +#define ARGP_HELP_PRE_DOC 0x10 /* doc string preceding long help. */ +#define ARGP_HELP_POST_DOC 0x20 /* doc string following long help. */ +#define ARGP_HELP_DOC (ARGP_HELP_PRE_DOC | ARGP_HELP_POST_DOC) +#define ARGP_HELP_BUG_ADDR 0x40 /* bug report address */ +#define ARGP_HELP_LONG_ONLY 0x80 /* modify output appropriately to + reflect ARGP_LONG_ONLY mode. */ + +/* These ARGP_HELP flags are only understood by argp_state_help. */ +#define ARGP_HELP_EXIT_ERR 0x100 /* Call exit(1) instead of returning. */ +#define ARGP_HELP_EXIT_OK 0x200 /* Call exit(0) instead of returning. */ + +/* The standard thing to do after a program command line parsing error, if an + error message has already been printed. */ +#define ARGP_HELP_STD_ERR \ + (ARGP_HELP_SEE | ARGP_HELP_EXIT_ERR) +/* The standard thing to do after a program command line parsing error, if no + more specific error message has been printed. */ +#define ARGP_HELP_STD_USAGE \ + (ARGP_HELP_SHORT_USAGE | ARGP_HELP_SEE | ARGP_HELP_EXIT_ERR) +/* The standard thing to do in response to a --help option. */ +#define ARGP_HELP_STD_HELP \ + (ARGP_HELP_SHORT_USAGE | ARGP_HELP_LONG | ARGP_HELP_EXIT_OK \ + | ARGP_HELP_DOC | ARGP_HELP_BUG_ADDR) + +/* Output a usage message for ARGP to STREAM. FLAGS are from the set + ARGP_HELP_*. */ +extern void argp_help (const struct argp *__restrict __argp, + FILE *__restrict __stream, + unsigned __flags, char *__restrict __name); +extern void __argp_help (const struct argp *__restrict __argp, + FILE *__restrict __stream, unsigned __flags, + char *__name); + +/* The following routines are intended to be called from within an argp + parsing routine (thus taking an argp_state structure as the first + argument). They may or may not print an error message and exit, depending + on the flags in STATE -- in any case, the caller should be prepared for + them *not* to exit, and should return an appropriate error after calling + them. [argp_usage & argp_error should probably be called argp_state_..., + but they're used often enough that they should be short] */ + +/* Output, if appropriate, a usage message for STATE to STREAM. FLAGS are + from the set ARGP_HELP_*. */ +extern void argp_state_help (const struct argp_state *__restrict __state, + FILE *__restrict __stream, + unsigned int __flags); +extern void __argp_state_help (const struct argp_state *__restrict __state, + FILE *__restrict __stream, + unsigned int __flags); + +#if _LIBC +/* Possibly output the standard usage message for ARGP to stderr and exit. */ +extern void argp_usage (const struct argp_state *__state); +extern void __argp_usage (const struct argp_state *__state); +#endif + +/* If appropriate, print the printf string FMT and following args, preceded + by the program name and ':', to stderr, and followed by a "Try ... --help" + message, then exit (1). */ +extern void argp_error (const struct argp_state *__restrict __state, + const char *__restrict __fmt, ...) + _GL_ATTRIBUTE_FORMAT ((__printf__, 2, 3)); +extern void __argp_error (const struct argp_state *__restrict __state, + const char *__restrict __fmt, ...) + _GL_ATTRIBUTE_FORMAT ((__printf__, 2, 3)); + +/* Similar to the standard gnu error-reporting function error(), but will + respect the ARGP_NO_EXIT and ARGP_NO_ERRS flags in STATE, and will print + to STATE->err_stream. This is useful for argument parsing code that is + shared between program startup (when exiting is desired) and runtime + option parsing (when typically an error code is returned instead). The + difference between this function and argp_error is that the latter is for + *parsing errors*, and the former is for other problems that occur during + parsing but don't reflect a (syntactic) problem with the input. */ +extern void argp_failure (const struct argp_state *__restrict __state, + int __status, int __errnum, + const char *__restrict __fmt, ...) + _GL_ATTRIBUTE_FORMAT ((__printf__, 4, 5)); +extern void __argp_failure (const struct argp_state *__restrict __state, + int __status, int __errnum, + const char *__restrict __fmt, ...) + _GL_ATTRIBUTE_FORMAT ((__printf__, 4, 5)); + +#if _LIBC +/* Returns true if the option OPT is a valid short option. */ +extern int _option_is_short (const struct argp_option *__opt) __THROW; +extern int __option_is_short (const struct argp_option *__opt) __THROW; + +/* Returns true if the option OPT is in fact the last (unused) entry in an + options array. */ +extern int _option_is_end (const struct argp_option *__opt) __THROW; +extern int __option_is_end (const struct argp_option *__opt) __THROW; +#endif + +/* Return the input field for ARGP in the parser corresponding to STATE; used + by the help routines. */ +extern void *_argp_input (const struct argp *__restrict __argp, + const struct argp_state *__restrict __state) + __THROW; +extern void *__argp_input (const struct argp *__restrict __argp, + const struct argp_state *__restrict __state) + __THROW; + +#if !_LIBC || defined __USE_EXTERN_INLINES + +# if !_LIBC +# define __argp_usage argp_usage +# define __argp_state_help argp_state_help +# define __option_is_short _option_is_short +# define __option_is_end _option_is_end +#ifndef _GL_INLINE_HEADER_BEGIN + #error "Please include config.h first." +#endif +_GL_INLINE_HEADER_BEGIN +# ifndef ARGP_EI +# define ARGP_EI _GL_INLINE +# endif +# endif + +# ifndef ARGP_EI +# define ARGP_EI __extern_inline +# endif + +ARGP_EI void +__argp_usage (const struct argp_state *__state) +{ + __argp_state_help (__state, stderr, ARGP_HELP_STD_USAGE); +} + +ARGP_EI int +__NTH (__option_is_short (const struct argp_option *__opt)) +{ + if (__opt->flags & OPTION_DOC) + return 0; + else + { + int __key = __opt->key; + return __key > 0 && __key <= UCHAR_MAX && isprint (__key); + } +} + +ARGP_EI int +__NTH (__option_is_end (const struct argp_option *__opt)) +{ + return !__opt->key && !__opt->name && !__opt->doc && !__opt->group; +} + +# if !_LIBC +# undef __argp_usage +# undef __argp_state_help +# undef __option_is_short +# undef __option_is_end +_GL_INLINE_HEADER_END +# endif +#endif /* Use extern inlines. */ + +#ifdef __cplusplus +} +#endif + +#endif /* argp.h */ diff --git a/libdicl/gl/m4/argp.m4 b/libdicl/gl/m4/argp.m4 new file mode 100644 index 0000000..8b2e2d8 --- /dev/null +++ b/libdicl/gl/m4/argp.m4 @@ -0,0 +1,62 @@ +# argp.m4 serial 15 +dnl Copyright (C) 2003-2019 Free Software Foundation, Inc. +dnl This file is free software; the Free Software Foundation +dnl gives unlimited permission to copy and/or distribute it, +dnl with or without modifications, as long as this notice is preserved. + +AC_DEFUN([gl_ARGP], +[ + AC_REQUIRE([AC_C_INLINE]) + AC_REQUIRE([AC_C_RESTRICT]) + AC_REQUIRE([gl_USE_SYSTEM_EXTENSIONS]) + + AC_CHECK_DECLS([program_invocation_name], [], + [AC_DEFINE([GNULIB_PROGRAM_INVOCATION_NAME], [1], + [Define to 1 to add extern declaration of program_invocation_name to argp.h])], + [[#include ]]) + AC_CHECK_DECLS([program_invocation_short_name], [], + [AC_DEFINE([GNULIB_PROGRAM_INVOCATION_SHORT_NAME], [1], + [Define to 1 to add extern declaration of program_invocation_short_name to argp.h])], + [[#include ]]) + + # Check if program_invocation_name and program_invocation_short_name + # are defined elsewhere. It is improbable that only one of them will + # be defined and other not, I prefer to stay on the safe side and to + # test each one separately. + AC_MSG_CHECKING([whether program_invocation_name is defined]) + AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include ]], + [[program_invocation_name = "test";]])], + [AC_DEFINE([HAVE_PROGRAM_INVOCATION_NAME], [1], + [Define if program_invocation_name is defined]) + AC_MSG_RESULT([yes])], + [AC_MSG_RESULT([no])]) + + AC_MSG_CHECKING([whether program_invocation_short_name is defined]) + AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include ]], + [[program_invocation_short_name = "test";]])], + [AC_DEFINE([HAVE_PROGRAM_INVOCATION_SHORT_NAME], [1], + [Define if program_invocation_short_name is defined]) + AC_MSG_RESULT([yes])], + [AC_MSG_RESULT([no])]) + + AC_CHECK_DECLS_ONCE([clearerr_unlocked]) + AC_CHECK_DECLS_ONCE([feof_unlocked]) + AC_CHECK_DECLS_ONCE([ferror_unlocked]) + AC_CHECK_DECLS_ONCE([fflush_unlocked]) + AC_CHECK_DECLS_ONCE([fgets_unlocked]) + AC_CHECK_DECLS_ONCE([fputc_unlocked]) + AC_CHECK_DECLS_ONCE([fputs_unlocked]) + AC_CHECK_DECLS_ONCE([fread_unlocked]) + AC_CHECK_DECLS_ONCE([fwrite_unlocked]) + AC_CHECK_DECLS_ONCE([getc_unlocked]) + AC_CHECK_DECLS_ONCE([getchar_unlocked]) + AC_CHECK_DECLS_ONCE([putc_unlocked]) + AC_CHECK_DECLS_ONCE([putchar_unlocked]) + AC_CHECK_FUNCS_ONCE([flockfile funlockfile]) + AC_CHECK_HEADERS_ONCE([features.h linewrap.h]) + AC_REQUIRE([AC_FUNC_STRERROR_R]) +]) + +dnl argp-parse.c depends on GNU getopt internals, therefore use GNU getopt +dnl always. +AC_DEFUN([gl_REPLACE_GETOPT_ALWAYS], []) diff --git a/libdicl/gl/m4/gnulib-cache.m4 b/libdicl/gl/m4/gnulib-cache.m4 index 8a1f47d..de66149 100644 --- a/libdicl/gl/m4/gnulib-cache.m4 +++ b/libdicl/gl/m4/gnulib-cache.m4 @@ -39,6 +39,7 @@ # --no-libtool \ # --macro-prefix=gl \ # --no-vc-files \ +# argp \ # fprintf-posix \ # getline \ # getopt-posix \ @@ -66,6 +67,7 @@ # Specification in the form of a few gnulib-tool.m4 macro invocations: gl_LOCAL_DIR([]) gl_MODULES([ + argp fprintf-posix getline getopt-posix diff --git a/libdicl/gl/m4/gnulib-comp.m4 b/libdicl/gl/m4/gnulib-comp.m4 index a2ddf6d..250614b 100644 --- a/libdicl/gl/m4/gnulib-comp.m4 +++ b/libdicl/gl/m4/gnulib-comp.m4 @@ -48,6 +48,8 @@ AC_DEFUN([gl_EARLY], # Code from module alloca: # Code from module alloca-opt: # Code from module alloca-opt-tests: + # Code from module argp: + # Code from module argp-tests: # Code from module arpa_inet: # Code from module arpa_inet-tests: # Code from module binary-io: @@ -119,6 +121,8 @@ AC_DEFUN([gl_EARLY], # Code from module getdtablesize-tests: # Code from module getline: # Code from module getline-tests: + # Code from module getopt-gnu: + # Code from module getopt-gnu-tests: # Code from module getopt-posix: # Code from module getopt-posix-tests: # Code from module getpagesize: @@ -160,6 +164,8 @@ AC_DEFUN([gl_EARLY], # Code from module lock-tests: # Code from module lstat: # Code from module lstat-tests: + # Code from module malloc-gnu: + # Code from module malloc-gnu-tests: # Code from module malloc-posix: # Code from module malloca: # Code from module malloca-tests: @@ -167,6 +173,7 @@ AC_DEFUN([gl_EARLY], # Code from module math-tests: # Code from module memchr: # Code from module memchr-tests: + # Code from module mempcpy: # Code from module mkdir: # Code from module mkdir-tests: # Code from module mkdtemp: @@ -275,6 +282,7 @@ AC_DEFUN([gl_EARLY], # Code from module stdio-tests: # Code from module stdlib: # Code from module stdlib-tests: + # Code from module strcase: # Code from module strchrnul: # Code from module strchrnul-tests: # Code from module strerror: @@ -284,6 +292,11 @@ AC_DEFUN([gl_EARLY], # Code from module strerror_r-posix-tests: # Code from module string: # Code from module string-tests: + # Code from module strings: + # Code from module strings-tests: + # Code from module strndup: + # Code from module strnlen: + # Code from module strnlen-tests: # Code from module strtod: # Code from module strtod-tests: # Code from module strtold: @@ -310,6 +323,8 @@ AC_DEFUN([gl_EARLY], # Code from module sys_uio-tests: # Code from module sys_wait: # Code from module sys_wait-tests: + # Code from module sysexits: + # Code from module sysexits-tests: # Code from module tempname: # Code from module test-framework-sh: # Code from module test-framework-sh-tests: @@ -378,6 +393,10 @@ AC_DEFUN([gl_INIT], gl_COMMON gl_source_base='gl' gl_FUNC_ALLOCA + gl_ARGP + m4_ifdef([AM_XGETTEXT_OPTION], + [AM_][XGETTEXT_OPTION([--flag=argp_error:2:c-format]) + AM_][XGETTEXT_OPTION([--flag=argp_failure:4:c-format])]) gl_MODULE_INDICATOR_FOR_TESTS([cloexec]) gl_FUNC_CLOSE if test $REPLACE_CLOSE = 1; then @@ -463,6 +482,10 @@ AC_DEFUN([gl_INIT], gl_PREREQ_GETLINE fi gl_STDIO_MODULE_INDICATOR([getline]) + gl_FUNC_GETOPT_GNU + dnl Because of the way gl_FUNC_GETOPT_GNU is implemented (the gl_getopt_required + dnl mechanism), there is no need to do any AC_LIBOBJ or AC_SUBST here; they are + dnl done in the getopt-posix module. gl_FUNC_GETOPT_POSIX if test $REPLACE_GETOPT = 1; then AC_LIBOBJ([getopt]) @@ -506,6 +529,11 @@ AC_DEFUN([gl_INIT], gl_PREREQ_LSTAT fi gl_SYS_STAT_MODULE_INDICATOR([lstat]) + gl_FUNC_MALLOC_GNU + if test $REPLACE_MALLOC = 1; then + AC_LIBOBJ([malloc]) + fi + gl_MODULE_INDICATOR([malloc-gnu]) gl_FUNC_MALLOC_POSIX if test $REPLACE_MALLOC = 1; then AC_LIBOBJ([malloc]) @@ -519,6 +547,12 @@ AC_DEFUN([gl_INIT], gl_PREREQ_MEMCHR fi gl_STRING_MODULE_INDICATOR([memchr]) + gl_FUNC_MEMPCPY + if test $HAVE_MEMPCPY = 0; then + AC_LIBOBJ([mempcpy]) + gl_PREREQ_MEMPCPY + fi + gl_STRING_MODULE_INDICATOR([mempcpy]) gl_FUNC_MKDIR if test $REPLACE_MKDIR = 1; then AC_LIBOBJ([mkdir]) @@ -623,6 +657,11 @@ AC_DEFUN([gl_INIT], fi gl_SIGNAL_MODULE_INDICATOR([sigprocmask]) gl_SIZE_MAX + gl_FUNC_SLEEP + if test $HAVE_SLEEP = 0 || test $REPLACE_SLEEP = 1; then + AC_LIBOBJ([sleep]) + fi + gl_UNISTD_MODULE_INDICATOR([sleep]) AC_REQUIRE([gl_SOCKETLIB]) AC_REQUIRE([gl_SOCKETS]) gl_TYPE_SOCKLEN_T @@ -649,6 +688,15 @@ AC_DEFUN([gl_INIT], gl_STDINT_H gl_STDIO_H gl_STDLIB_H + gl_STRCASE + if test $HAVE_STRCASECMP = 0; then + AC_LIBOBJ([strcasecmp]) + gl_PREREQ_STRCASECMP + fi + if test $HAVE_STRNCASECMP = 0; then + AC_LIBOBJ([strncasecmp]) + gl_PREREQ_STRNCASECMP + fi gl_FUNC_STRCHRNUL if test $HAVE_STRCHRNUL = 0 || test $REPLACE_STRCHRNUL = 1; then AC_LIBOBJ([strchrnul]) @@ -668,6 +716,18 @@ AC_DEFUN([gl_INIT], gl_PREREQ_SYS_H_WINSOCK2 fi gl_HEADER_STRING_H + gl_HEADER_STRINGS_H + gl_FUNC_STRNDUP + if test $HAVE_STRNDUP = 0 || test $REPLACE_STRNDUP = 1; then + AC_LIBOBJ([strndup]) + fi + gl_STRING_MODULE_INDICATOR([strndup]) + gl_FUNC_STRNLEN + if test $HAVE_DECL_STRNLEN = 0 || test $REPLACE_STRNLEN = 1; then + AC_LIBOBJ([strnlen]) + gl_PREREQ_STRNLEN + fi + gl_STRING_MODULE_INDICATOR([strnlen]) gl_FUNC_STRTOD if test $HAVE_STRTOD = 0 || test $REPLACE_STRTOD = 1; then AC_LIBOBJ([strtod]) @@ -706,6 +766,7 @@ AC_DEFUN([gl_INIT], AC_PROG_MKDIR_P gl_SYS_WAIT_H AC_PROG_MKDIR_P + gl_SYSEXITS gl_FUNC_GEN_TEMPNAME gl_HEADER_TIME_H gl_TIME_R @@ -983,11 +1044,6 @@ changequote([, ])dnl AC_REQUIRE([gl_FLOAT_EXPONENT_LOCATION]) AC_REQUIRE([gl_DOUBLE_EXPONENT_LOCATION]) AC_REQUIRE([gl_LONG_DOUBLE_EXPONENT_LOCATION]) - gl_FUNC_SLEEP - if test $HAVE_SLEEP = 0 || test $REPLACE_SLEEP = 1; then - AC_LIBOBJ([sleep]) - fi - gl_UNISTD_MODULE_INDICATOR([sleep]) AC_CHECK_DECLS_ONCE([alarm]) AC_REQUIRE([gl_HEADER_SYS_SOCKET]) if test "$ac_cv_header_winsock2_h" = yes; then @@ -1014,6 +1070,10 @@ changequote([, ])dnl gl_STRING_MODULE_INDICATOR([strerror_r]) dnl For the modules argp, error. gl_MODULE_INDICATOR([strerror_r-posix]) + dnl Check for prerequisites for memory fence checks. + gl_FUNC_MMAP_ANON + AC_CHECK_HEADERS_ONCE([sys/mman.h]) + AC_CHECK_FUNCS_ONCE([mprotect]) gt_LOCALE_FR gt_LOCALE_FR_UTF8 gt_LOCALE_FR @@ -1179,6 +1239,19 @@ AC_DEFUN([gl_FILE_LIST], [ lib/alloca.c lib/alloca.in.h lib/arg-nonnull.h + lib/argp-ba.c + lib/argp-eexst.c + lib/argp-fmtstream.c + lib/argp-fmtstream.h + lib/argp-fs-xinl.c + lib/argp-help.c + lib/argp-namefrob.h + lib/argp-parse.c + lib/argp-pin.c + lib/argp-pv.c + lib/argp-pvh.c + lib/argp-xinl.c + lib/argp.h lib/asnprintf.c lib/asprintf.c lib/basename-lgpl.c @@ -1252,6 +1325,7 @@ AC_DEFUN([gl_FILE_LIST], [ lib/math.in.h lib/memchr.c lib/memchr.valgrind + lib/mempcpy.c lib/mkdir.c lib/mkdtemp.c lib/mktime-internal.h @@ -1287,6 +1361,7 @@ AC_DEFUN([gl_FILE_LIST], [ lib/signbitl.c lib/sigprocmask.c lib/size_max.h + lib/sleep.c lib/sockets.c lib/sockets.h lib/spawn.c @@ -1307,13 +1382,18 @@ AC_DEFUN([gl_FILE_LIST], [ lib/stdio-impl.h lib/stdio.in.h lib/stdlib.in.h + lib/strcasecmp.c lib/strchrnul.c lib/strchrnul.valgrind lib/strerror-override.c lib/strerror-override.h lib/strerror.c lib/string.in.h + lib/strings.in.h lib/stripslash.c + lib/strncasecmp.c + lib/strndup.c + lib/strnlen.c lib/strtod.c lib/strtol.c lib/strtold.c @@ -1328,6 +1408,7 @@ AC_DEFUN([gl_FILE_LIST], [ lib/sys_types.in.h lib/sys_uio.in.h lib/sys_wait.in.h + lib/sysexits.in.h lib/tempname.c lib/tempname.h lib/time.in.h @@ -1358,6 +1439,7 @@ AC_DEFUN([gl_FILE_LIST], [ m4/__inline.m4 m4/absolute-header.m4 m4/alloca.m4 + m4/argp.m4 m4/arpa_inet_h.m4 m4/asm-underscore.m4 m4/close.m4 @@ -1426,6 +1508,7 @@ AC_DEFUN([gl_FILE_LIST], [ m4/malloca.m4 m4/math_h.m4 m4/memchr.m4 + m4/mempcpy.m4 m4/mkdir.m4 m4/mkdtemp.m4 m4/mktime.m4 @@ -1482,10 +1565,14 @@ AC_DEFUN([gl_FILE_LIST], [ m4/stdint_h.m4 m4/stdio_h.m4 m4/stdlib_h.m4 + m4/strcase.m4 m4/strchrnul.m4 m4/strerror.m4 m4/strerror_r.m4 m4/string_h.m4 + m4/strings_h.m4 + m4/strndup.m4 + m4/strnlen.m4 m4/strtod.m4 m4/strtold.m4 m4/strtoll.m4 @@ -1499,6 +1586,7 @@ AC_DEFUN([gl_FILE_LIST], [ m4/sys_types_h.m4 m4/sys_uio_h.m4 m4/sys_wait_h.m4 + m4/sysexits.m4 m4/tempname.m4 m4/thread.m4 m4/threadlib.m4 @@ -1533,6 +1621,8 @@ AC_DEFUN([gl_FILE_LIST], [ tests/signature.h tests/test-accept.c tests/test-alloca-opt.c + tests/test-argp-2.sh + tests/test-argp.c tests/test-arpa_inet.c tests/test-binary-io.c tests/test-binary-io.sh @@ -1573,9 +1663,11 @@ AC_DEFUN([gl_FILE_LIST], [ tests/test-getdelim.c tests/test-getdtablesize.c tests/test-getline.c + tests/test-getopt-gnu.c tests/test-getopt-main.h tests/test-getopt-posix.c tests/test-getopt.h + tests/test-getopt_long.h tests/test-getprogname.c tests/test-gettimeofday.c tests/test-ignore-value.c @@ -1596,6 +1688,7 @@ AC_DEFUN([gl_FILE_LIST], [ tests/test-lock.c tests/test-lstat.c tests/test-lstat.h + tests/test-malloc-gnu.c tests/test-malloca.c tests/test-math.c tests/test-memchr.c @@ -1666,6 +1759,8 @@ AC_DEFUN([gl_FILE_LIST], [ tests/test-strerror.c tests/test-strerror_r.c tests/test-string.c + tests/test-strings.c + tests/test-strnlen.c tests/test-strtod.c tests/test-strtod1.c tests/test-strtod1.sh @@ -1685,6 +1780,7 @@ AC_DEFUN([gl_FILE_LIST], [ tests/test-sys_uio.c tests/test-sys_wait.c tests/test-sys_wait.h + tests/test-sysexits.c tests/test-thread_create.c tests/test-thread_self.c tests/test-time.c @@ -1744,7 +1840,6 @@ AC_DEFUN([gl_FILE_LIST], [ tests=lib/resource-ext.h tests=lib/same-inode.h tests=lib/setsockopt.c - tests=lib/sleep.c tests=lib/socket.c tests=lib/spawn_faction_addclose.c tests=lib/spawn_faction_adddup2.c diff --git a/libdicl/gl/m4/mempcpy.m4 b/libdicl/gl/m4/mempcpy.m4 new file mode 100644 index 0000000..b6090ee --- /dev/null +++ b/libdicl/gl/m4/mempcpy.m4 @@ -0,0 +1,26 @@ +# mempcpy.m4 serial 11 +dnl Copyright (C) 2003-2004, 2006-2007, 2009-2019 Free Software Foundation, +dnl Inc. +dnl This file is free software; the Free Software Foundation +dnl gives unlimited permission to copy and/or distribute it, +dnl with or without modifications, as long as this notice is preserved. + +AC_DEFUN([gl_FUNC_MEMPCPY], +[ + dnl Persuade glibc to declare mempcpy(). + AC_REQUIRE([AC_USE_SYSTEM_EXTENSIONS]) + + dnl The mempcpy() declaration in lib/string.in.h uses 'restrict'. + AC_REQUIRE([AC_C_RESTRICT]) + + AC_REQUIRE([gl_HEADER_STRING_H_DEFAULTS]) + AC_CHECK_FUNCS([mempcpy]) + if test $ac_cv_func_mempcpy = no; then + HAVE_MEMPCPY=0 + fi +]) + +# Prerequisites of lib/mempcpy.c. +AC_DEFUN([gl_PREREQ_MEMPCPY], [ + : +]) diff --git a/libdicl/gl/m4/strcase.m4 b/libdicl/gl/m4/strcase.m4 new file mode 100644 index 0000000..9baef78 --- /dev/null +++ b/libdicl/gl/m4/strcase.m4 @@ -0,0 +1,45 @@ +# strcase.m4 serial 11 +dnl Copyright (C) 2002, 2005-2019 Free Software Foundation, Inc. +dnl This file is free software; the Free Software Foundation +dnl gives unlimited permission to copy and/or distribute it, +dnl with or without modifications, as long as this notice is preserved. + +AC_DEFUN([gl_STRCASE], +[ + gl_FUNC_STRCASECMP + gl_FUNC_STRNCASECMP +]) + +AC_DEFUN([gl_FUNC_STRCASECMP], +[ + AC_REQUIRE([gl_HEADER_STRINGS_H_DEFAULTS]) + AC_CHECK_FUNCS([strcasecmp]) + if test $ac_cv_func_strcasecmp = no; then + HAVE_STRCASECMP=0 + fi +]) + +AC_DEFUN([gl_FUNC_STRNCASECMP], +[ + AC_REQUIRE([gl_HEADER_STRINGS_H_DEFAULTS]) + AC_CHECK_FUNCS([strncasecmp]) + if test $ac_cv_func_strncasecmp = yes; then + HAVE_STRNCASECMP=1 + else + HAVE_STRNCASECMP=0 + fi + AC_CHECK_DECLS([strncasecmp]) + if test $ac_cv_have_decl_strncasecmp = no; then + HAVE_DECL_STRNCASECMP=0 + fi +]) + +# Prerequisites of lib/strcasecmp.c. +AC_DEFUN([gl_PREREQ_STRCASECMP], [ + : +]) + +# Prerequisites of lib/strncasecmp.c. +AC_DEFUN([gl_PREREQ_STRNCASECMP], [ + : +]) diff --git a/libdicl/gl/m4/strings_h.m4 b/libdicl/gl/m4/strings_h.m4 new file mode 100644 index 0000000..76128cf --- /dev/null +++ b/libdicl/gl/m4/strings_h.m4 @@ -0,0 +1,52 @@ +# Configure a replacement for . +# serial 6 + +# Copyright (C) 2007, 2009-2019 Free Software Foundation, Inc. +# This file is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +AC_DEFUN([gl_HEADER_STRINGS_H], +[ + dnl Use AC_REQUIRE here, so that the default behavior below is expanded + dnl once only, before all statements that occur in other macros. + AC_REQUIRE([gl_HEADER_STRINGS_H_BODY]) +]) + +AC_DEFUN([gl_HEADER_STRINGS_H_BODY], +[ + AC_REQUIRE([gl_HEADER_STRINGS_H_DEFAULTS]) + + gl_CHECK_NEXT_HEADERS([strings.h]) + if test $ac_cv_header_strings_h = yes; then + HAVE_STRINGS_H=1 + else + HAVE_STRINGS_H=0 + fi + AC_SUBST([HAVE_STRINGS_H]) + + dnl Check for declarations of anything we want to poison if the + dnl corresponding gnulib module is not in use. + gl_WARN_ON_USE_PREPARE([[ + /* Minix 3.1.8 has a bug: must be included before + . */ + #include + #include + ]], [ffs strcasecmp strncasecmp]) +]) + +AC_DEFUN([gl_STRINGS_MODULE_INDICATOR], +[ + dnl Use AC_REQUIRE here, so that the default settings are expanded once only. + AC_REQUIRE([gl_HEADER_STRINGS_H_DEFAULTS]) + gl_MODULE_INDICATOR_SET_VARIABLE([$1]) +]) + +AC_DEFUN([gl_HEADER_STRINGS_H_DEFAULTS], +[ + GNULIB_FFS=0; AC_SUBST([GNULIB_FFS]) + dnl Assume proper GNU behavior unless another module says otherwise. + HAVE_FFS=1; AC_SUBST([HAVE_FFS]) + HAVE_STRCASECMP=1; AC_SUBST([HAVE_STRCASECMP]) + HAVE_DECL_STRNCASECMP=1; AC_SUBST([HAVE_DECL_STRNCASECMP]) +]) diff --git a/libdicl/gl/m4/strndup.m4 b/libdicl/gl/m4/strndup.m4 new file mode 100644 index 0000000..325af5d --- /dev/null +++ b/libdicl/gl/m4/strndup.m4 @@ -0,0 +1,58 @@ +# strndup.m4 serial 22 +dnl Copyright (C) 2002-2003, 2005-2019 Free Software Foundation, Inc. +dnl This file is free software; the Free Software Foundation +dnl gives unlimited permission to copy and/or distribute it, +dnl with or without modifications, as long as this notice is preserved. + +AC_DEFUN([gl_FUNC_STRNDUP], +[ + dnl Persuade glibc to declare strndup(). + AC_REQUIRE([AC_USE_SYSTEM_EXTENSIONS]) + + AC_REQUIRE([AC_CANONICAL_HOST]) dnl for cross-compiles + AC_REQUIRE([gl_HEADER_STRING_H_DEFAULTS]) + AC_CHECK_DECLS_ONCE([strndup]) + AC_CHECK_FUNCS_ONCE([strndup]) + if test $ac_cv_have_decl_strndup = no; then + HAVE_DECL_STRNDUP=0 + fi + + if test $ac_cv_func_strndup = yes; then + HAVE_STRNDUP=1 + # AIX 4.3.3, AIX 5.1 have a function that fails to add the terminating '\0'. + AC_CACHE_CHECK([for working strndup], [gl_cv_func_strndup_works], + [AC_RUN_IFELSE([ + AC_LANG_PROGRAM([[#include + #include ]], [[ +#if !HAVE_DECL_STRNDUP + extern + #ifdef __cplusplus + "C" + #endif + char *strndup (const char *, size_t); +#endif + int result; + char *s; + s = strndup ("some longer string", 15); + free (s); + s = strndup ("shorter string", 13); + result = s[13] != '\0'; + free (s); + return result;]])], + [gl_cv_func_strndup_works=yes], + [gl_cv_func_strndup_works=no], + [ +changequote(,)dnl + case $host_os in + aix | aix[3-6]*) gl_cv_func_strndup_works="guessing no";; + *) gl_cv_func_strndup_works="guessing yes";; + esac +changequote([,])dnl + ])]) + case $gl_cv_func_strndup_works in + *no) REPLACE_STRNDUP=1 ;; + esac + else + HAVE_STRNDUP=0 + fi +]) diff --git a/libdicl/gl/m4/strnlen.m4 b/libdicl/gl/m4/strnlen.m4 new file mode 100644 index 0000000..c283c3e --- /dev/null +++ b/libdicl/gl/m4/strnlen.m4 @@ -0,0 +1,30 @@ +# strnlen.m4 serial 13 +dnl Copyright (C) 2002-2003, 2005-2007, 2009-2019 Free Software Foundation, +dnl Inc. +dnl This file is free software; the Free Software Foundation +dnl gives unlimited permission to copy and/or distribute it, +dnl with or without modifications, as long as this notice is preserved. + +AC_DEFUN([gl_FUNC_STRNLEN], +[ + AC_REQUIRE([gl_HEADER_STRING_H_DEFAULTS]) + + dnl Persuade glibc to declare strnlen(). + AC_REQUIRE([AC_USE_SYSTEM_EXTENSIONS]) + + AC_CHECK_DECLS_ONCE([strnlen]) + if test $ac_cv_have_decl_strnlen = no; then + HAVE_DECL_STRNLEN=0 + else + m4_pushdef([AC_LIBOBJ], [:]) + dnl Note: AC_FUNC_STRNLEN does AC_LIBOBJ([strnlen]). + AC_FUNC_STRNLEN + m4_popdef([AC_LIBOBJ]) + if test $ac_cv_func_strnlen_working = no; then + REPLACE_STRNLEN=1 + fi + fi +]) + +# Prerequisites of lib/strnlen.c. +AC_DEFUN([gl_PREREQ_STRNLEN], [:]) diff --git a/libdicl/gl/m4/sysexits.m4 b/libdicl/gl/m4/sysexits.m4 new file mode 100644 index 0000000..1e9f171 --- /dev/null +++ b/libdicl/gl/m4/sysexits.m4 @@ -0,0 +1,44 @@ +# sysexits.m4 serial 6 +dnl Copyright (C) 2003, 2005, 2007, 2009-2019 Free Software Foundation, Inc. +dnl This file is free software; the Free Software Foundation +dnl gives unlimited permission to copy and/or distribute it, +dnl with or without modifications, as long as this notice is preserved. + +AC_DEFUN([gl_SYSEXITS], +[ + AC_CHECK_HEADERS_ONCE([sysexits.h]) + if test $ac_cv_header_sysexits_h = yes; then + HAVE_SYSEXITS_H=1 + gl_CHECK_NEXT_HEADERS([sysexits.h]) + AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[#include ]], + [[switch (0) + { + case EX_OK: + case EX_USAGE: + case EX_DATAERR: + case EX_NOINPUT: + case EX_NOUSER: + case EX_NOHOST: + case EX_UNAVAILABLE: + case EX_SOFTWARE: + case EX_OSERR: + case EX_OSFILE: + case EX_CANTCREAT: + case EX_IOERR: + case EX_TEMPFAIL: + case EX_PROTOCOL: + case EX_NOPERM: + case EX_CONFIG: + break; + } + ]])], + [SYSEXITS_H=], + [SYSEXITS_H=sysexits.h]) + else + HAVE_SYSEXITS_H=0 + SYSEXITS_H=sysexits.h + fi + AC_SUBST([HAVE_SYSEXITS_H]) + AC_SUBST([SYSEXITS_H]) + AM_CONDITIONAL([GL_GENERATE_SYSEXITS_H], [test -n "$SYSEXITS_H"]) +]) diff --git a/libdicl/gl/mempcpy.c b/libdicl/gl/mempcpy.c new file mode 100644 index 0000000..d0220e1 --- /dev/null +++ b/libdicl/gl/mempcpy.c @@ -0,0 +1,28 @@ +/* Copy memory area and return pointer after last written byte. + Copyright (C) 2003, 2007, 2009-2019 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, see . */ + +#include + +/* Specification. */ +#include + +/* Copy N bytes of SRC to DEST, return pointer to bytes after the + last written byte. */ +void * +mempcpy (void *dest, const void *src, size_t n) +{ + return (char *) memcpy (dest, src, n) + n; +} diff --git a/libdicl/tests/sleep.c b/libdicl/gl/sleep.c similarity index 100% rename from libdicl/tests/sleep.c rename to libdicl/gl/sleep.c diff --git a/libdicl/gl/strcasecmp.c b/libdicl/gl/strcasecmp.c new file mode 100644 index 0000000..6cd0e1e --- /dev/null +++ b/libdicl/gl/strcasecmp.c @@ -0,0 +1,62 @@ +/* Case-insensitive string comparison function. + Copyright (C) 1998-1999, 2005-2007, 2009-2019 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, see . */ + +#include + +/* Specification. */ +#include + +#include +#include + +#define TOLOWER(Ch) (isupper (Ch) ? tolower (Ch) : (Ch)) + +/* Compare strings S1 and S2, ignoring case, returning less than, equal to or + greater than zero if S1 is lexicographically less than, equal to or greater + than S2. + Note: This function does not work with multibyte strings! */ + +int +strcasecmp (const char *s1, const char *s2) +{ + const unsigned char *p1 = (const unsigned char *) s1; + const unsigned char *p2 = (const unsigned char *) s2; + unsigned char c1, c2; + + if (p1 == p2) + return 0; + + do + { + c1 = TOLOWER (*p1); + c2 = TOLOWER (*p2); + + if (c1 == '\0') + break; + + ++p1; + ++p2; + } + while (c1 == c2); + + if (UCHAR_MAX <= INT_MAX) + return c1 - c2; + else + /* On machines where 'char' and 'int' are types of the same size, the + difference of two 'unsigned char' values - including the sign bit - + doesn't fit in an 'int'. */ + return (c1 > c2 ? 1 : c1 < c2 ? -1 : 0); +} diff --git a/libdicl/gl/strings.in.h b/libdicl/gl/strings.in.h new file mode 100644 index 0000000..21b1a85 --- /dev/null +++ b/libdicl/gl/strings.in.h @@ -0,0 +1,122 @@ +/* A substitute . + + Copyright (C) 2007-2019 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, see . */ + +#ifndef _@GUARD_PREFIX@_STRINGS_H + +#if __GNUC__ >= 3 +@PRAGMA_SYSTEM_HEADER@ +#endif +@PRAGMA_COLUMNS@ + +/* Minix 3.1.8 has a bug: must be included before . + But avoid namespace pollution on glibc systems. */ +#if defined __minix && !defined __GLIBC__ +# include +#endif + +/* The include_next requires a split double-inclusion guard. */ +#if @HAVE_STRINGS_H@ +# @INCLUDE_NEXT@ @NEXT_STRINGS_H@ +#endif + +#ifndef _@GUARD_PREFIX@_STRINGS_H +#define _@GUARD_PREFIX@_STRINGS_H + +#if ! @HAVE_DECL_STRNCASECMP@ +/* Get size_t. */ +# include +#endif + + +/* The definitions of _GL_FUNCDECL_RPL etc. are copied here. */ + +/* The definition of _GL_ARG_NONNULL is copied here. */ + +/* The definition of _GL_WARN_ON_USE is copied here. */ + +#ifdef __cplusplus +extern "C" { +#endif + + + /* Find the index of the least-significant set bit. */ +#if @GNULIB_FFS@ +# if !@HAVE_FFS@ +_GL_FUNCDECL_SYS (ffs, int, (int i)); +# endif +_GL_CXXALIAS_SYS (ffs, int, (int i)); +_GL_CXXALIASWARN (ffs); +#elif defined GNULIB_POSIXCHECK +# undef ffs +# if HAVE_RAW_DECL_FFS +_GL_WARN_ON_USE (ffs, "ffs is not portable - use the ffs module"); +# endif +#endif + +/* Compare strings S1 and S2, ignoring case, returning less than, equal to or + greater than zero if S1 is lexicographically less than, equal to or greater + than S2. + Note: This function does not work in multibyte locales. */ +#if ! @HAVE_STRCASECMP@ +extern int strcasecmp (char const *s1, char const *s2) + _GL_ARG_NONNULL ((1, 2)); +#endif +#if defined GNULIB_POSIXCHECK +/* strcasecmp() does not work with multibyte strings: + POSIX says that it operates on "strings", and "string" in POSIX is defined + as a sequence of bytes, not of characters. */ +# undef strcasecmp +# if HAVE_RAW_DECL_STRCASECMP +_GL_WARN_ON_USE (strcasecmp, "strcasecmp cannot work correctly on character " + "strings in multibyte locales - " + "use mbscasecmp if you care about " + "internationalization, or use c_strcasecmp , " + "gnulib module c-strcase) if you want a locale " + "independent function"); +# endif +#endif + +/* Compare no more than N bytes of strings S1 and S2, ignoring case, + returning less than, equal to or greater than zero if S1 is + lexicographically less than, equal to or greater than S2. + Note: This function cannot work correctly in multibyte locales. */ +#if ! @HAVE_DECL_STRNCASECMP@ +extern int strncasecmp (char const *s1, char const *s2, size_t n) + _GL_ARG_NONNULL ((1, 2)); +#endif +#if defined GNULIB_POSIXCHECK +/* strncasecmp() does not work with multibyte strings: + POSIX says that it operates on "strings", and "string" in POSIX is defined + as a sequence of bytes, not of characters. */ +# undef strncasecmp +# if HAVE_RAW_DECL_STRNCASECMP +_GL_WARN_ON_USE (strncasecmp, "strncasecmp cannot work correctly on character " + "strings in multibyte locales - " + "use mbsncasecmp or mbspcasecmp if you care about " + "internationalization, or use c_strncasecmp , " + "gnulib module c-strcase) if you want a locale " + "independent function"); +# endif +#endif + + +#ifdef __cplusplus +} +#endif + +#endif /* _@GUARD_PREFIX@_STRING_H */ +#endif /* _@GUARD_PREFIX@_STRING_H */ diff --git a/libdicl/gl/strncasecmp.c b/libdicl/gl/strncasecmp.c new file mode 100644 index 0000000..034011c --- /dev/null +++ b/libdicl/gl/strncasecmp.c @@ -0,0 +1,62 @@ +/* strncasecmp.c -- case insensitive string comparator + Copyright (C) 1998-1999, 2005-2007, 2009-2019 Free Software Foundation, Inc. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, see . */ + +#include + +/* Specification. */ +#include + +#include +#include + +#define TOLOWER(Ch) (isupper (Ch) ? tolower (Ch) : (Ch)) + +/* Compare no more than N bytes of strings S1 and S2, ignoring case, + returning less than, equal to or greater than zero if S1 is + lexicographically less than, equal to or greater than S2. + Note: This function cannot work correctly in multibyte locales. */ + +int +strncasecmp (const char *s1, const char *s2, size_t n) +{ + register const unsigned char *p1 = (const unsigned char *) s1; + register const unsigned char *p2 = (const unsigned char *) s2; + unsigned char c1, c2; + + if (p1 == p2 || n == 0) + return 0; + + do + { + c1 = TOLOWER (*p1); + c2 = TOLOWER (*p2); + + if (--n == 0 || c1 == '\0') + break; + + ++p1; + ++p2; + } + while (c1 == c2); + + if (UCHAR_MAX <= INT_MAX) + return c1 - c2; + else + /* On machines where 'char' and 'int' are types of the same size, the + difference of two 'unsigned char' values - including the sign bit - + doesn't fit in an 'int'. */ + return (c1 > c2 ? 1 : c1 < c2 ? -1 : 0); +} diff --git a/libdicl/gl/strndup.c b/libdicl/gl/strndup.c new file mode 100644 index 0000000..5b74828 --- /dev/null +++ b/libdicl/gl/strndup.c @@ -0,0 +1,36 @@ +/* A replacement function, for systems that lack strndup. + + Copyright (C) 1996-1998, 2001-2003, 2005-2007, 2009-2019 Free Software + Foundation, Inc. + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU General Public License as published by the + Free Software Foundation; either version 3, or (at your option) any + later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, see . */ + +#include + +#include + +#include + +char * +strndup (char const *s, size_t n) +{ + size_t len = strnlen (s, n); + char *new = malloc (len + 1); + + if (new == NULL) + return NULL; + + new[len] = '\0'; + return memcpy (new, s, len); +} diff --git a/libdicl/gl/strnlen.c b/libdicl/gl/strnlen.c new file mode 100644 index 0000000..9fb6635 --- /dev/null +++ b/libdicl/gl/strnlen.c @@ -0,0 +1,30 @@ +/* Find the length of STRING, but scan at most MAXLEN characters. + Copyright (C) 2005-2007, 2009-2019 Free Software Foundation, Inc. + Written by Simon Josefsson. + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3, or (at your option) + any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, see . */ + +#include + +#include + +/* Find the length of STRING, but scan at most MAXLEN characters. + If no '\0' terminator is found in that many characters, return MAXLEN. */ + +size_t +strnlen (const char *string, size_t maxlen) +{ + const char *end = memchr (string, '\0', maxlen); + return end ? (size_t) (end - string) : maxlen; +} diff --git a/libdicl/gl/sysexits.in.h b/libdicl/gl/sysexits.in.h new file mode 100644 index 0000000..7043a0a --- /dev/null +++ b/libdicl/gl/sysexits.in.h @@ -0,0 +1,72 @@ +/* exit() exit codes for some BSD system programs. + Copyright (C) 2003, 2006-2019 Free Software Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +/* Written by Simon Josefsson based on sysexits(3) man page */ + +#ifndef _@GUARD_PREFIX@_SYSEXITS_H + +#if __GNUC__ >= 3 +@PRAGMA_SYSTEM_HEADER@ +#endif +@PRAGMA_COLUMNS@ + +#if @HAVE_SYSEXITS_H@ + +/* IRIX 6.5 has an that defines a macro EX_OK with a nonzero + value. Override it. See + */ +# ifdef __sgi +# include +# undef EX_OK +# endif + +/* The include_next requires a split double-inclusion guard. */ +# @INCLUDE_NEXT@ @NEXT_SYSEXITS_H@ + +/* HP-UX 11 ends at EX_NOPERM. */ +# ifndef EX_CONFIG +# define EX_CONFIG 78 +# endif + +#endif + +#ifndef _@GUARD_PREFIX@_SYSEXITS_H +#define _@GUARD_PREFIX@_SYSEXITS_H + +#if !@HAVE_SYSEXITS_H@ + +# define EX_OK 0 /* same value as EXIT_SUCCESS */ + +# define EX_USAGE 64 +# define EX_DATAERR 65 +# define EX_NOINPUT 66 +# define EX_NOUSER 67 +# define EX_NOHOST 68 +# define EX_UNAVAILABLE 69 +# define EX_SOFTWARE 70 +# define EX_OSERR 71 +# define EX_OSFILE 72 +# define EX_CANTCREAT 73 +# define EX_IOERR 74 +# define EX_TEMPFAIL 75 +# define EX_PROTOCOL 76 +# define EX_NOPERM 77 +# define EX_CONFIG 78 + +#endif + +#endif /* _@GUARD_PREFIX@_SYSEXITS_H */ +#endif /* _@GUARD_PREFIX@_SYSEXITS_H */ diff --git a/libdicl/gnulibimportnotes.txt b/libdicl/gnulibimportnotes.txt index 748477a..6472216 100755 --- a/libdicl/gnulibimportnotes.txt +++ b/libdicl/gnulibimportnotes.txt @@ -8,4 +8,4 @@ #After the import, must set AM_CFLAGS= -fPIC -DPIC to create relocatable code -$GNULIB_HOME/gnulib-tool --import --lib=libgnu --source-base=gl --m4-base=/gl/m4 --doc-base=doc --tests-base=tests --aux-dir=. --with-tests --no-conditional-dependencies --no-libtool --macro-prefix=gl --no-vc-files fprintf-posix getline getopt-posix getprogname mkdtemp mktime nanosleep posix_spawn posix_spawnp printf-posix select setenv sprintf-posix strtod strtold strtoll strtoull unsetenv vasprintf-posix vfprintf-posix vsprintf-posix vsnprintf-posix wait-process +$GNULIB_HOME/gnulib-tool --import --lib=libgnu --source-base=gl --m4-base=/gl/m4 --doc-base=doc --tests-base=tests --aux-dir=. --with-tests --no-conditional-dependencies --no-libtool --macro-prefix=gl --no-vc-files argp fprintf-posix getline getopt-posix getprogname mkdtemp mktime nanosleep posix_spawn posix_spawnp printf-posix select setenv sprintf-posix strtod strtold strtoll strtoull unsetenv vasprintf-posix vfprintf-posix vsprintf-posix vsnprintf-posix wait-process diff --git a/libdicl/src/repl_headers/Makefile.am b/libdicl/src/repl_headers/Makefile.am index 9606f78..4a57079 100644 --- a/libdicl/src/repl_headers/Makefile.am +++ b/libdicl/src/repl_headers/Makefile.am @@ -4,12 +4,16 @@ SUBDIRS= \ sys \ $(NULL) +argp.h: $(top_srcdir)/gl/argp.h + cp $(top_srcdir)/gl/argp.h ./ + spawn.h: $(top_srcdir)/gl/spawn.h cp $(top_srcdir)/gl/spawn.h ./ replincludedir=$(includedir)/libdicl@PACKAGE_SUFFIX@ replinclude_HEADERS= \ + argp.h \ repl_select.h \ spawn.h \ stdio.h \ diff --git a/libdicl/tests/Makefile.am b/libdicl/tests/Makefile.am index b3fdbdb..20a99e6 100644 --- a/libdicl/tests/Makefile.am +++ b/libdicl/tests/Makefile.am @@ -86,6 +86,15 @@ EXTRA_DIST += test-alloca-opt.c ## end gnulib module alloca-opt-tests +## begin gnulib module argp-tests + +TESTS += test-argp test-argp-2.sh +check_PROGRAMS += test-argp +test_argp_LDADD = $(LDADD) @LIBINTL@ +EXTRA_DIST += test-argp.c test-argp-2.sh macros.h + +## end gnulib module argp-tests + ## begin gnulib module arpa_inet BUILT_SOURCES += arpa/inet.h @@ -477,6 +486,15 @@ EXTRA_DIST += test-getline.c signature.h macros.h ## end gnulib module getline-tests +## begin gnulib module getopt-gnu-tests + +TESTS += test-getopt-gnu +check_PROGRAMS += test-getopt-gnu +test_getopt_gnu_LDADD = $(LDADD) $(LIBINTL) +EXTRA_DIST += macros.h signature.h test-getopt-gnu.c test-getopt-main.h test-getopt.h test-getopt_long.h + +## end gnulib module getopt-gnu-tests + ## begin gnulib module getopt-posix-tests TESTS += test-getopt-posix @@ -731,6 +749,14 @@ EXTRA_DIST += test-lstat.h test-lstat.c signature.h macros.h ## end gnulib module lstat-tests +## begin gnulib module malloc-gnu-tests + +TESTS += test-malloc-gnu +check_PROGRAMS += test-malloc-gnu +EXTRA_DIST += test-malloc-gnu.c + +## end gnulib module malloc-gnu-tests + ## begin gnulib module malloca-tests TESTS += test-malloca @@ -1325,15 +1351,6 @@ EXTRA_DIST += test-sigprocmask.c signature.h macros.h ## end gnulib module sigprocmask-tests -## begin gnulib module sleep - - -EXTRA_DIST += sleep.c - -EXTRA_libtests_a_SOURCES += sleep.c - -## end gnulib module sleep - ## begin gnulib module sleep-tests TESTS += test-sleep @@ -1532,6 +1549,22 @@ EXTRA_DIST += test-string.c ## end gnulib module string-tests +## begin gnulib module strings-tests + +TESTS += test-strings +check_PROGRAMS += test-strings +EXTRA_DIST += test-strings.c + +## end gnulib module strings-tests + +## begin gnulib module strnlen-tests + +TESTS += test-strnlen +check_PROGRAMS += test-strnlen +EXTRA_DIST += test-strnlen.c zerosize-ptr.h signature.h macros.h + +## end gnulib module strnlen-tests + ## begin gnulib module strtod-tests TESTS += test-strtod @@ -1689,6 +1722,14 @@ EXTRA_DIST += test-sys_wait.c test-sys_wait.h ## end gnulib module sys_wait-tests +## begin gnulib module sysexits-tests + +TESTS += test-sysexits +check_PROGRAMS += test-sysexits +EXTRA_DIST += test-sysexits.c + +## end gnulib module sysexits-tests + ## begin gnulib module test-framework-sh-tests TESTS += test-init.sh diff --git a/libdicl/tests/test-argp-2.sh b/libdicl/tests/test-argp-2.sh new file mode 100755 index 0000000..53cc7b3 --- /dev/null +++ b/libdicl/tests/test-argp-2.sh @@ -0,0 +1,113 @@ +#! /bin/sh +# Test suite for argp. +# Copyright (C) 2006-2019 Free Software Foundation, Inc. +# This file is part of the GNUlib Library. +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . */ + +TMP=argp.$$ + +unset ARGP_HELP_FMT +ERR=0 + +func_compare() { +# If argp was compiled without base_name, it will display full program name. +# If run on mingw, it will display the program name with a .exe suffix. + sed '1{ + s,: [^ ]*/test-argp,: test-argp, + s,: test-argp\.exe,: test-argp, + }' | LC_ALL=C tr -d '\r' | diff -c $TMP - +} + +#### +# Test --usage output +cat > $TMP < $TMP <$TMP <. +EOT + +# Compare --help output, but filter out any bug-reporting email address. +${CHECKER} ./test-argp${EXEEXT} --help \ + | sed 's/^\(Report bugs to \)<[^>]*>.$/\1<>./' | func_compare || ERR=1 + +#### +# Test ambiguous option handling + +${CHECKER} ./test-argp${EXEEXT} --optio 2>/dev/null && ERR=1 + +#### +# Run built-in tests +${CHECKER} ./test-argp${EXEEXT} || ERR=1 + +rm $TMP + +exit $ERR diff --git a/libdicl/tests/test-argp.c b/libdicl/tests/test-argp.c new file mode 100644 index 0000000..86ddbf3 --- /dev/null +++ b/libdicl/tests/test-argp.c @@ -0,0 +1,490 @@ +/* Test suite for argp. + Copyright (C) 2006-2007, 2009-2019 Free Software Foundation, Inc. + This file is part of the GNUlib Library. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#include + +#include "argp.h" + +#include +#include +#include +#if HAVE_STRINGS_H +# include +#endif + +#include "macros.h" + +struct test_args +{ + int test; + int verbose; + char *file; + int read; + char *hidden; + int opt; + char *optional; + int optional_set; + int group_2_1_option; + int group_1_1_option; +}; + +static struct argp_option group1_option[] = { + { NULL, 0, NULL, 0, "Option Group 1", 0 }, + { "verbose", 'v', NULL, 0, "Simple option without arguments", 1 }, + { "file", 'f', "FILE", 0, "Option with a mandatory argument", 1 }, + { "input", 0, NULL, OPTION_ALIAS, NULL, 1 }, + { "read", 'r', NULL, OPTION_ALIAS, NULL, 1 }, + { "hidden", 'H', "FILE", OPTION_HIDDEN, "Hidden option", 1 }, + { NULL, 0, NULL, 0, NULL, 0 } +}; + +static error_t +group1_parser (int key, char *arg, struct argp_state *state) +{ + struct test_args *args = state->input; + + switch (key) + { + case 'v': + args->verbose++; + break; + + case 'r': + args->read = 1; + FALLTHROUGH; + case 'f': + args->file = arg; + break; + + case 'H': + args->hidden = arg; + break; + + default: + return ARGP_ERR_UNKNOWN; + } + return 0; +} + +struct argp group1_argp = { + group1_option, + group1_parser +}; + +struct argp_child group1_child = { + &group1_argp, + 0, + "", + 1 +}; + + +static struct argp_option group1_1_option[] = { + { NULL, 0, NULL, 0, "Option Group 1.1", 0 }, + { "cantiga", 'C', NULL, 0, "create a cantiga" }, + { "sonet", 'S', NULL, 0, "create a sonet" }, + { NULL, 0, NULL, 0, NULL, 0 } +}; + +static error_t +group1_1_parser (int key, char *arg, struct argp_state *state) +{ + struct test_args *args = state->input; + switch (key) + { + case 'C': + case 'S': + args->group_1_1_option = key; + break; + default: + return ARGP_ERR_UNKNOWN; + } + return 0; +} + +struct argp group1_1_argp = { + group1_1_option, + group1_1_parser +}; + +struct argp_child group1_1_child = { + &group1_1_argp, + 0, + "", + 2 +}; + + +static struct argp_option group2_option[] = { + { NULL, 0, NULL, 0, "Option Group 2", 0 }, + { "option", 'O', NULL, 0, "An option", 1 }, + { "optional", 'o', "ARG", OPTION_ARG_OPTIONAL, + "Option with an optional argument. ARG is one of the following:", 2 }, + { "one", 0, NULL, OPTION_DOC | OPTION_NO_TRANS, "one unit", 3 }, + { "two", 0, NULL, OPTION_DOC | OPTION_NO_TRANS, "two units", 3 }, + { "many", 0, NULL, OPTION_DOC | OPTION_NO_TRANS, "many units", 3 }, + { NULL, 0, NULL, 0, NULL, 0 } +}; + +static error_t +group2_parser (int key, char *arg, struct argp_state *state) +{ + struct test_args *args = state->input; + + switch (key) + { + case 'O': + args->opt = 1; + break; + + case 'o': + args->optional_set = 1; + args->optional = arg; + break; + + default: + return ARGP_ERR_UNKNOWN; + } + return 0; +} + +struct argp group2_argp = { + group2_option, + group2_parser +}; + +struct argp_child group2_child = { + &group2_argp, + 0, + "", + 2 +}; + + +static struct argp_option group2_1_option[] = { + { NULL, 0, NULL, 0, "Option Group 2.1", 0 }, + { "poem", 'p', NULL, 0, "create a poem" }, + { "limerick", 'l', NULL, 0, "create a limerick" }, + { NULL, 0, NULL, 0, NULL, 0 } +}; + +static error_t +group2_1_parser (int key, char *arg, struct argp_state *state) +{ + struct test_args *args = state->input; + switch (key) + { + case 'p': + case 'e': + args->group_2_1_option = key; + break; + default: + return ARGP_ERR_UNKNOWN; + } + return 0; +} + +struct argp group2_1_argp = { + group2_1_option, + group2_1_parser +}; + +struct argp_child group2_1_child = { + &group2_1_argp, + 0, + "", + 2 +}; + + +static struct argp_option main_options[] = { + { NULL, 0, NULL, 0, "Main options", 0 }, + { "test", 't', NULL, 0, NULL, 1 }, + { NULL, 0, NULL, 0, NULL, 0 } +}; + +static error_t +parse_opt (int key, char *arg, struct argp_state *state) +{ + struct test_args *args = state->input; + int i; + + switch (key) + { + case ARGP_KEY_INIT: + for (i = 0; state->root_argp->children[i].argp; i++) + state->child_inputs[i] = args; + break; + + case 't': + args->test = 1; + break; + + default: + return ARGP_ERR_UNKNOWN; + } + return 0; +} + +const char *argp_program_version = "test_argp (" PACKAGE_NAME ") " VERSION; +const char *argp_program_bug_address = "<" PACKAGE_BUGREPORT ">"; +static char doc[] = "documentation string"; + +struct argp test_argp = { + main_options, + parse_opt, + "ARGS...", + doc, + NULL, + NULL, + NULL +}; + +#define NARGS(a) (sizeof(a) / sizeof((a)[0]) - 1) +#define ARGV0 "test-argp" +#define init_args(a) memset (&(a), 0, sizeof (a)); + +#define INIT_TEST_COMMON(n) \ + int argc = NARGS (argv); \ + struct test_args test_args; \ + init_args (test_args); \ + test_number = n; + +#define INIT_TEST1(n, arg1) \ + char *argv[] = { ARGV0, arg1, NULL }; \ + INIT_TEST_COMMON (n) + +#define INIT_TEST2(n, arg1, arg2) \ + char *argv[] = { ARGV0, arg1, arg2, NULL }; \ + INIT_TEST_COMMON (n) + +#define INIT_TEST3(n, arg1, arg2, arg3) \ + char *argv[] = { ARGV0, arg1, arg2, arg3, NULL }; \ + INIT_TEST_COMMON (n) + +int test_number; +unsigned failure_count = 0; + +static void +fail (const char *msg) +{ + fprintf (stderr, "Test %d: %s\n", test_number, msg); + failure_count++; +} + +static void +test1 (struct argp *argp) +{ + INIT_TEST1 (1, "--test"); + if (argp_parse (argp, argc, argv, 0, NULL, &test_args)) + fail ("argp_parse failed"); + else if (test_args.test != 1) + fail ("option not processed"); +} + +static void +test2 (struct argp *argp) +{ + INIT_TEST1 (2, "-t"); + if (argp_parse (argp, argc, argv, 0, NULL, &test_args)) + fail ("argp_parse failed"); + else if (test_args.test != 1) + fail ("option not processed"); +} + +static void +test_file (struct argp *argp, int argc, char **argv, struct test_args *args) +{ + if (argp_parse (argp, argc, argv, 0, NULL, args)) + fail ("argp_parse failed"); + else if (!args->file) + fail ("option not processed"); + else if (strcmp (args->file, "FILE")) + fail ("option processed incorrectly"); +} + +static void +test3 (struct argp *argp) +{ + INIT_TEST1 (3, "--file=FILE"); + test_file (argp, argc, argv, &test_args); +} + +static void +test4 (struct argp *argp) +{ + INIT_TEST2 (4, "--file", "FILE"); + test_file (argp, argc, argv, &test_args); +} + +static void +test5 (struct argp *argp) +{ + INIT_TEST1 (5, "--input=FILE"); + test_file (argp, argc, argv, &test_args); +} + +static void +test6 (struct argp *argp) +{ + INIT_TEST2 (6, "--input", "FILE"); + test_file (argp, argc, argv, &test_args); +} + +static void +test_optional (struct argp *argp, int argc, char **argv, + struct test_args *args, const char *val, const char *a) +{ + int index; + if (argp_parse (argp, argc, argv, 0, &index, args)) + fail ("argp_parse failed"); + else if (!args->optional_set) + fail ("option not processed"); + + if (!val) + { + if (args->optional) + fail ("option processed incorrectly"); + } + else if (strcmp (args->optional, val)) + fail ("option processed incorrectly"); + + if (a) + { + if (index == argc) + fail ("expected command line argument not found"); + else if (strcmp (argv[index], a)) + fail ("expected command line argument does not match"); + } +} + +static void +test7 (struct argp *argp) +{ + INIT_TEST1 (7, "-oARG"); + test_optional (argp, argc, argv, &test_args, "ARG", NULL); +} + +static void +test8 (struct argp *argp) +{ + INIT_TEST2 (8, "-o", "ARG"); + test_optional (argp, argc, argv, &test_args, NULL, "ARG"); +} + +static void +test9 (struct argp *argp) +{ + INIT_TEST1 (9, "--optional=ARG"); + test_optional (argp, argc, argv, &test_args, "ARG", NULL); +} + +static void +test10 (struct argp *argp) +{ + INIT_TEST2 (10, "--optional", "ARG"); + test_optional (argp, argc, argv, &test_args, NULL, "ARG"); +} + +static void +test11 (struct argp *argp) +{ + INIT_TEST1 (11, "--optiona=ARG"); + test_optional (argp, argc, argv, &test_args, "ARG", NULL); +} + +static void +test12 (struct argp *argp) +{ + INIT_TEST3 (12, "--option", "--optional=OPT", "FILE"); + test_optional (argp, argc, argv, &test_args, "OPT", "FILE"); +} + +static void +test13 (struct argp *argp) +{ + INIT_TEST1 (1, "--cantiga"); + if (argp_parse (argp, argc, argv, 0, NULL, &test_args)) + fail ("argp_parse failed"); + else if (test_args.group_1_1_option != 'C') + fail ("option not processed"); +} + +static void +test14 (struct argp *argp) +{ + INIT_TEST1 (1, "--limerick"); + if (argp_parse (argp, argc, argv, 0, NULL, &test_args)) + fail ("argp_parse failed"); + else if (test_args.group_2_1_option != 'l') + fail ("option not processed"); +} + +static void +test15 (struct argp *argp) +{ + INIT_TEST2 (1, "-r", "FILE"); + test_file (argp, argc, argv, &test_args); + if (!test_args.read) + fail ("short alias not recognized properly"); +} + + +typedef void (*test_fp) (struct argp *argp); + +static test_fp test_fun[] = { + test1, test2, test3, test4, + test5, test6, test7, test8, + test9, test10, test11, test12, + test13, test14, test15, + NULL +}; + +int +main (int argc, char **argv) +{ + struct argp_child argp_children[3], group1_children[2], group2_children[2]; + test_fp *fun; + + group1_children[0] = group1_1_child; + group1_children[1].argp = NULL; + group1_argp.children = group1_children; + + group2_children[0] = group2_1_child; + group2_children[1].argp = NULL; + group2_argp.children = group2_children; + + argp_children[0] = group1_child; + argp_children[1] = group2_child; + argp_children[2].argp = NULL; + test_argp.children = argp_children; + + if (argc > 0) + { + struct test_args test_args; + init_args (test_args); + return argp_parse (&test_argp, argc, argv, 0, NULL, &test_args); + } + + for (fun = test_fun; *fun; fun++) + (*fun) (&test_argp); + + if (failure_count) + return 1; + + return 0; +} diff --git a/libdicl/tests/test-getopt-gnu.c b/libdicl/tests/test-getopt-gnu.c new file mode 100644 index 0000000..a02e7c7 --- /dev/null +++ b/libdicl/tests/test-getopt-gnu.c @@ -0,0 +1,45 @@ +/* Test of command line argument processing. + Copyright (C) 2009-2019 Free Software Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +/* Written by Bruno Haible , 2009. */ + +#include + +/* None of the files accessed by this test are large, so disable the + ftell link warning if we are not using the gnulib ftell module. */ +#define _GL_NO_LARGE_FILES + +/* POSIX and glibc provide the getopt() function in , see + http://pubs.opengroup.org/onlinepubs/9699919799/functions/getopt.html + https://www.gnu.org/software/libc/manual/html_node/Using-Getopt.html + But gnulib provides the getopt() function in , not in . + This is what we are testing here. */ +#include + +#ifndef __getopt_argv_const +# define __getopt_argv_const const +#endif +#include "signature.h" +SIGNATURE_CHECK (getopt_long, int, (int, char *__getopt_argv_const *, + char const *, struct option const *, + int *)); +SIGNATURE_CHECK (getopt_long_only, int, (int, char *__getopt_argv_const *, + char const *, struct option const *, + int *)); + +#define TEST_GETOPT_GNU 1 +#define TEST_GETOPT_TMP_NAME "test-getopt-gnu.tmp" +#include "test-getopt-main.h" diff --git a/libdicl/tests/test-getopt_long.h b/libdicl/tests/test-getopt_long.h new file mode 100644 index 0000000..f25e9fa --- /dev/null +++ b/libdicl/tests/test-getopt_long.h @@ -0,0 +1,2144 @@ +/* Test of command line argument processing. + Copyright (C) 2009-2019 Free Software Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +/* Written by Bruno Haible , 2009. */ + +static int a_seen; +static int b_seen; +static int q_seen; + +static const struct option long_options_required[] = + { + { "alpha", no_argument, NULL, 'a' }, + { "beta", no_argument, &b_seen, 1 }, + { "prune", required_argument, NULL, 'p' }, + { "quetsche", required_argument, &q_seen, 1 }, + { "xtremely-",no_argument, NULL, 1003 }, + { "xtra", no_argument, NULL, 1001 }, + { "xtreme", no_argument, NULL, 1002 }, + { "xtremely", no_argument, NULL, 1003 }, + { NULL, 0, NULL, 0 } + }; + +static const struct option long_options_optional[] = + { + { "alpha", no_argument, NULL, 'a' }, + { "beta", no_argument, &b_seen, 1 }, + { "prune", optional_argument, NULL, 'p' }, + { "quetsche", optional_argument, &q_seen, 1 }, + { NULL, 0, NULL, 0 } + }; + +static void +getopt_long_loop (int argc, const char **argv, + const char *options, const struct option *long_options, + const char **p_value, const char **q_value, + int *non_options_count, const char **non_options, + int *unrecognized) +{ + int option_index = -1; + int c; + + opterr = 0; + q_seen = 0; + while ((c = getopt_long (argc, (char **) argv, options, long_options, + &option_index)) + != -1) + { + switch (c) + { + case 0: + /* An option with a non-NULL flag pointer was processed. */ + if (q_seen) + *q_value = optarg; + break; + case 'a': + a_seen++; + break; + case 'b': + b_seen = 1; + break; + case 'p': + *p_value = optarg; + break; + case 'q': + *q_value = optarg; + break; + case '\1': + /* Must only happen with option '-' at the beginning. */ + ASSERT (options[0] == '-'); + non_options[(*non_options_count)++] = optarg; + break; + case ':': + /* Must only happen with option ':' at the beginning. */ + ASSERT (options[0] == ':' + || ((options[0] == '-' || options[0] == '+') + && options[1] == ':')); + FALLTHROUGH; + case '?': + *unrecognized = optopt; + break; + default: + *unrecognized = c; + break; + } + } +} + +/* Reduce casting, so we can use string literals elsewhere. + getopt_long takes an array of char*, but luckily does not modify + those elements, so we can pass const char*. */ +static int +do_getopt_long (int argc, const char **argv, const char *shortopts, + const struct option *longopts, int *longind) +{ + return getopt_long (argc, (char **) argv, shortopts, longopts, longind); +} + +static void +test_getopt_long (void) +{ + int start; + + /* Test disambiguation of options. */ + { + int argc = 0; + const char *argv[10]; + int option_index; + int c; + + argv[argc++] = "program"; + argv[argc++] = "--x"; + argv[argc] = NULL; + optind = 1; + opterr = 0; + c = do_getopt_long (argc, argv, "ab", long_options_required, &option_index); + ASSERT (c == '?'); + ASSERT (optopt == 0); + } + { + int argc = 0; + const char *argv[10]; + int option_index; + int c; + + argv[argc++] = "program"; + argv[argc++] = "--xt"; + argv[argc] = NULL; + optind = 1; + opterr = 0; + c = do_getopt_long (argc, argv, "ab", long_options_required, &option_index); + ASSERT (c == '?'); + ASSERT (optopt == 0); + } + { + int argc = 0; + const char *argv[10]; + int option_index; + int c; + + argv[argc++] = "program"; + argv[argc++] = "--xtr"; + argv[argc] = NULL; + optind = 1; + opterr = 0; + c = do_getopt_long (argc, argv, "ab", long_options_required, &option_index); + ASSERT (c == '?'); + ASSERT (optopt == 0); + } + { + int argc = 0; + const char *argv[10]; + int option_index; + int c; + + argv[argc++] = "program"; + argv[argc++] = "--xtra"; + argv[argc] = NULL; + optind = 1; + opterr = 0; + c = do_getopt_long (argc, argv, "ab", long_options_required, &option_index); + ASSERT (c == 1001); + } + { + int argc = 0; + const char *argv[10]; + int option_index; + int c; + + argv[argc++] = "program"; + argv[argc++] = "--xtre"; + argv[argc] = NULL; + optind = 1; + opterr = 0; + c = do_getopt_long (argc, argv, "ab", long_options_required, &option_index); + ASSERT (c == '?'); + ASSERT (optopt == 0); + } + { + int argc = 0; + const char *argv[10]; + int option_index; + int c; + + argv[argc++] = "program"; + argv[argc++] = "--xtrem"; + argv[argc] = NULL; + optind = 1; + opterr = 0; + c = do_getopt_long (argc, argv, "ab", long_options_required, &option_index); + ASSERT (c == '?'); + ASSERT (optopt == 0); + } + { + int argc = 0; + const char *argv[10]; + int option_index; + int c; + + argv[argc++] = "program"; + argv[argc++] = "--xtreme"; + argv[argc] = NULL; + optind = 1; + opterr = 0; + c = do_getopt_long (argc, argv, "ab", long_options_required, &option_index); + ASSERT (c == 1002); + } + { + int argc = 0; + const char *argv[10]; + int option_index; + int c; + + argv[argc++] = "program"; + argv[argc++] = "--xtremel"; + argv[argc] = NULL; + optind = 1; + opterr = 0; + c = do_getopt_long (argc, argv, "ab", long_options_required, &option_index); + ASSERT (c == 1003); + } + { + int argc = 0; + const char *argv[10]; + int option_index; + int c; + + argv[argc++] = "program"; + argv[argc++] = "--xtremely"; + argv[argc] = NULL; + optind = 1; + opterr = 0; + c = do_getopt_long (argc, argv, "ab", long_options_required, &option_index); + ASSERT (c == 1003); + } + + /* Check that -W handles unknown options. */ + { + int argc = 0; + const char *argv[10]; + int option_index; + int c; + + argv[argc++] = "program"; + argv[argc++] = "-W"; + argv[argc] = NULL; + optind = 1; + opterr = 0; + c = do_getopt_long (argc, argv, "W;", long_options_required, &option_index); + ASSERT (c == '?'); + ASSERT (optopt == 'W'); + } + { + int argc = 0; + const char *argv[10]; + int option_index; + int c; + + argv[argc++] = "program"; + argv[argc++] = "-Wunknown"; + argv[argc] = NULL; + optind = 1; + opterr = 0; + c = do_getopt_long (argc, argv, "W;", long_options_required, &option_index); + /* glibc and BSD behave differently here, but for now, we allow + both behaviors since W support is not frequently used. */ + if (c == '?') + { + ASSERT (optopt == 0); + ASSERT (optarg == NULL); + } + else + { + ASSERT (c == 'W'); + ASSERT (strcmp (optarg, "unknown") == 0); + } + } + { + int argc = 0; + const char *argv[10]; + int option_index; + int c; + + argv[argc++] = "program"; + argv[argc++] = "-W"; + argv[argc++] = "unknown"; + argv[argc] = NULL; + optind = 1; + opterr = 0; + c = do_getopt_long (argc, argv, "W;", long_options_required, &option_index); + /* glibc and BSD behave differently here, but for now, we allow + both behaviors since W support is not frequently used. */ + if (c == '?') + { + ASSERT (optopt == 0); + ASSERT (optarg == NULL); + } + else + { + ASSERT (c == 'W'); + ASSERT (strcmp (optarg, "unknown") == 0); + } + } + + /* Test that 'W' does not dump core: + https://sourceware.org/bugzilla/show_bug.cgi?id=12922 */ + { + int argc = 0; + const char *argv[10]; + int option_index; + int c; + + argv[argc++] = "program"; + argv[argc++] = "-W"; + argv[argc++] = "dummy"; + argv[argc] = NULL; + optind = 1; + opterr = 0; + c = do_getopt_long (argc, argv, "W;", NULL, &option_index); + ASSERT (c == 'W'); + ASSERT (optind == 2); + } + + /* Test processing of boolean short options. */ + for (start = 0; start <= 1; start++) + { + const char *p_value = NULL; + const char *q_value = NULL; + int non_options_count = 0; + const char *non_options[10]; + int unrecognized = 0; + int argc = 0; + const char *argv[10]; + a_seen = 0; + b_seen = 0; + + argv[argc++] = "program"; + argv[argc++] = "-a"; + argv[argc++] = "foo"; + argv[argc++] = "bar"; + argv[argc] = NULL; + optind = start; + getopt_long_loop (argc, argv, "ab", long_options_required, + &p_value, &q_value, + &non_options_count, non_options, &unrecognized); + ASSERT (a_seen == 1); + ASSERT (b_seen == 0); + ASSERT (p_value == NULL); + ASSERT (q_value == NULL); + ASSERT (non_options_count == 0); + ASSERT (unrecognized == 0); + ASSERT (optind == 2); + } + for (start = 0; start <= 1; start++) + { + const char *p_value = NULL; + const char *q_value = NULL; + int non_options_count = 0; + const char *non_options[10]; + int unrecognized = 0; + int argc = 0; + const char *argv[10]; + a_seen = 0; + b_seen = 0; + + argv[argc++] = "program"; + argv[argc++] = "-b"; + argv[argc++] = "-a"; + argv[argc++] = "foo"; + argv[argc++] = "bar"; + argv[argc] = NULL; + optind = start; + getopt_long_loop (argc, argv, "ab", long_options_required, + &p_value, &q_value, + &non_options_count, non_options, &unrecognized); + ASSERT (a_seen == 1); + ASSERT (b_seen == 1); + ASSERT (p_value == NULL); + ASSERT (q_value == NULL); + ASSERT (non_options_count == 0); + ASSERT (unrecognized == 0); + ASSERT (optind == 3); + } + for (start = 0; start <= 1; start++) + { + const char *p_value = NULL; + const char *q_value = NULL; + int non_options_count = 0; + const char *non_options[10]; + int unrecognized = 0; + int argc = 0; + const char *argv[10]; + a_seen = 0; + b_seen = 0; + + argv[argc++] = "program"; + argv[argc++] = "-ba"; + argv[argc++] = "foo"; + argv[argc++] = "bar"; + argv[argc] = NULL; + optind = start; + getopt_long_loop (argc, argv, "ab", long_options_required, + &p_value, &q_value, + &non_options_count, non_options, &unrecognized); + ASSERT (a_seen == 1); + ASSERT (b_seen == 1); + ASSERT (p_value == NULL); + ASSERT (q_value == NULL); + ASSERT (non_options_count == 0); + ASSERT (unrecognized == 0); + ASSERT (optind == 2); + } + for (start = 0; start <= 1; start++) + { + const char *p_value = NULL; + const char *q_value = NULL; + int non_options_count = 0; + const char *non_options[10]; + int unrecognized = 0; + int argc = 0; + const char *argv[10]; + a_seen = 0; + b_seen = 0; + + argv[argc++] = "program"; + argv[argc++] = "-ab"; + argv[argc++] = "-a"; + argv[argc++] = "foo"; + argv[argc++] = "bar"; + argv[argc] = NULL; + optind = start; + getopt_long_loop (argc, argv, "ab", long_options_required, + &p_value, &q_value, + &non_options_count, non_options, &unrecognized); + ASSERT (a_seen == 2); + ASSERT (b_seen == 1); + ASSERT (p_value == NULL); + ASSERT (q_value == NULL); + ASSERT (non_options_count == 0); + ASSERT (unrecognized == 0); + ASSERT (optind == 3); + } + + /* Test processing of boolean long options. */ + for (start = 0; start <= 1; start++) + { + const char *p_value = NULL; + const char *q_value = NULL; + int non_options_count = 0; + const char *non_options[10]; + int unrecognized = 0; + int argc = 0; + const char *argv[10]; + a_seen = 0; + b_seen = 0; + + argv[argc++] = "program"; + argv[argc++] = "--alpha"; + argv[argc++] = "foo"; + argv[argc++] = "bar"; + argv[argc] = NULL; + optind = start; + getopt_long_loop (argc, argv, "ab", long_options_required, + &p_value, &q_value, + &non_options_count, non_options, &unrecognized); + ASSERT (a_seen == 1); + ASSERT (b_seen == 0); + ASSERT (p_value == NULL); + ASSERT (q_value == NULL); + ASSERT (non_options_count == 0); + ASSERT (unrecognized == 0); + ASSERT (optind == 2); + } + for (start = 0; start <= 1; start++) + { + const char *p_value = NULL; + const char *q_value = NULL; + int non_options_count = 0; + const char *non_options[10]; + int unrecognized = 0; + int argc = 0; + const char *argv[10]; + a_seen = 0; + b_seen = 0; + + argv[argc++] = "program"; + argv[argc++] = "--beta"; + argv[argc++] = "--alpha"; + argv[argc++] = "foo"; + argv[argc++] = "bar"; + argv[argc] = NULL; + optind = start; + getopt_long_loop (argc, argv, "ab", long_options_required, + &p_value, &q_value, + &non_options_count, non_options, &unrecognized); + ASSERT (a_seen == 1); + ASSERT (b_seen == 1); + ASSERT (p_value == NULL); + ASSERT (q_value == NULL); + ASSERT (non_options_count == 0); + ASSERT (unrecognized == 0); + ASSERT (optind == 3); + } + for (start = 0; start <= 1; start++) + { + const char *p_value = NULL; + const char *q_value = NULL; + int non_options_count = 0; + const char *non_options[10]; + int unrecognized = 0; + int argc = 0; + const char *argv[10]; + a_seen = 0; + b_seen = 0; + + argv[argc++] = "program"; + argv[argc++] = "--alpha"; + argv[argc++] = "--beta"; + argv[argc++] = "--alpha"; + argv[argc++] = "--beta"; + argv[argc++] = "foo"; + argv[argc++] = "bar"; + argv[argc] = NULL; + optind = start; + getopt_long_loop (argc, argv, "ab", long_options_required, + &p_value, &q_value, + &non_options_count, non_options, &unrecognized); + ASSERT (a_seen == 2); + ASSERT (b_seen == 1); + ASSERT (p_value == NULL); + ASSERT (q_value == NULL); + ASSERT (non_options_count == 0); + ASSERT (unrecognized == 0); + ASSERT (optind == 5); + } + + /* Test processing of boolean long options via -W. */ + for (start = 0; start <= 1; start++) + { + const char *p_value = NULL; + const char *q_value = NULL; + int non_options_count = 0; + const char *non_options[10]; + int unrecognized = 0; + int argc = 0; + const char *argv[10]; + a_seen = 0; + b_seen = 0; + + argv[argc++] = "program"; + argv[argc++] = "-Walpha"; + argv[argc++] = "foo"; + argv[argc++] = "bar"; + argv[argc] = NULL; + optind = start; + getopt_long_loop (argc, argv, "abW;", long_options_required, + &p_value, &q_value, + &non_options_count, non_options, &unrecognized); + ASSERT (a_seen == 1); + ASSERT (b_seen == 0); + ASSERT (p_value == NULL); + ASSERT (q_value == NULL); + ASSERT (non_options_count == 0); + ASSERT (unrecognized == 0); + ASSERT (optind == 2); + } + for (start = 0; start <= 1; start++) + { + const char *p_value = NULL; + const char *q_value = NULL; + int non_options_count = 0; + const char *non_options[10]; + int unrecognized = 0; + int argc = 0; + const char *argv[10]; + a_seen = 0; + b_seen = 0; + + argv[argc++] = "program"; + argv[argc++] = "-W"; + argv[argc++] = "beta"; + argv[argc++] = "-W"; + argv[argc++] = "alpha"; + argv[argc++] = "foo"; + argv[argc++] = "bar"; + argv[argc] = NULL; + optind = start; + getopt_long_loop (argc, argv, "aW;b", long_options_required, + &p_value, &q_value, + &non_options_count, non_options, &unrecognized); + ASSERT (a_seen == 1); + ASSERT (b_seen == 1); + ASSERT (p_value == NULL); + ASSERT (q_value == NULL); + ASSERT (non_options_count == 0); + ASSERT (unrecognized == 0); + ASSERT (optind == 5); + } + for (start = 0; start <= 1; start++) + { + const char *p_value = NULL; + const char *q_value = NULL; + int non_options_count = 0; + const char *non_options[10]; + int unrecognized = 0; + int argc = 0; + const char *argv[10]; + a_seen = 0; + b_seen = 0; + + argv[argc++] = "program"; + argv[argc++] = "-Walpha"; + argv[argc++] = "-Wbeta"; + argv[argc++] = "-Walpha"; + argv[argc++] = "-Wbeta"; + argv[argc++] = "foo"; + argv[argc++] = "bar"; + argv[argc] = NULL; + optind = start; + getopt_long_loop (argc, argv, "W;ab", long_options_required, + &p_value, &q_value, + &non_options_count, non_options, &unrecognized); + ASSERT (a_seen == 2); + ASSERT (b_seen == 1); + ASSERT (p_value == NULL); + ASSERT (q_value == NULL); + ASSERT (non_options_count == 0); + ASSERT (unrecognized == 0); + ASSERT (optind == 5); + } + + /* Test processing of short options with arguments. */ + for (start = 0; start <= 1; start++) + { + const char *p_value = NULL; + const char *q_value = NULL; + int non_options_count = 0; + const char *non_options[10]; + int unrecognized = 0; + int argc = 0; + const char *argv[10]; + a_seen = 0; + b_seen = 0; + + argv[argc++] = "program"; + argv[argc++] = "-pfoo"; + argv[argc++] = "bar"; + argv[argc] = NULL; + optind = start; + getopt_long_loop (argc, argv, "p:q:", long_options_required, + &p_value, &q_value, + &non_options_count, non_options, &unrecognized); + ASSERT (a_seen == 0); + ASSERT (b_seen == 0); + ASSERT (p_value != NULL && strcmp (p_value, "foo") == 0); + ASSERT (q_value == NULL); + ASSERT (non_options_count == 0); + ASSERT (unrecognized == 0); + ASSERT (optind == 2); + } + for (start = 0; start <= 1; start++) + { + const char *p_value = NULL; + const char *q_value = NULL; + int non_options_count = 0; + const char *non_options[10]; + int unrecognized = 0; + int argc = 0; + const char *argv[10]; + a_seen = 0; + b_seen = 0; + + argv[argc++] = "program"; + argv[argc++] = "-p"; + argv[argc++] = "foo"; + argv[argc++] = "bar"; + argv[argc] = NULL; + optind = start; + getopt_long_loop (argc, argv, "p:q:", long_options_required, + &p_value, &q_value, + &non_options_count, non_options, &unrecognized); + ASSERT (a_seen == 0); + ASSERT (b_seen == 0); + ASSERT (p_value != NULL && strcmp (p_value, "foo") == 0); + ASSERT (q_value == NULL); + ASSERT (non_options_count == 0); + ASSERT (unrecognized == 0); + ASSERT (optind == 3); + } + for (start = 0; start <= 1; start++) + { + const char *p_value = NULL; + const char *q_value = NULL; + int non_options_count = 0; + const char *non_options[10]; + int unrecognized = 0; + int argc = 0; + const char *argv[10]; + a_seen = 0; + b_seen = 0; + + argv[argc++] = "program"; + argv[argc++] = "-ab"; + argv[argc++] = "-q"; + argv[argc++] = "baz"; + argv[argc++] = "-pfoo"; + argv[argc++] = "bar"; + argv[argc] = NULL; + optind = start; + getopt_long_loop (argc, argv, "abp:q:", long_options_required, + &p_value, &q_value, + &non_options_count, non_options, &unrecognized); + ASSERT (a_seen == 1); + ASSERT (b_seen == 1); + ASSERT (p_value != NULL && strcmp (p_value, "foo") == 0); + ASSERT (q_value != NULL && strcmp (q_value, "baz") == 0); + ASSERT (non_options_count == 0); + ASSERT (unrecognized == 0); + ASSERT (optind == 5); + } + + /* Test processing of long options with arguments. */ + for (start = 0; start <= 1; start++) + { + const char *p_value = NULL; + const char *q_value = NULL; + int non_options_count = 0; + const char *non_options[10]; + int unrecognized = 0; + int argc = 0; + const char *argv[10]; + a_seen = 0; + b_seen = 0; + + argv[argc++] = "program"; + argv[argc++] = "--p=foo"; + argv[argc++] = "bar"; + argv[argc] = NULL; + optind = start; + getopt_long_loop (argc, argv, "p:q:", long_options_required, + &p_value, &q_value, + &non_options_count, non_options, &unrecognized); + ASSERT (a_seen == 0); + ASSERT (b_seen == 0); + ASSERT (p_value != NULL && strcmp (p_value, "foo") == 0); + ASSERT (q_value == NULL); + ASSERT (non_options_count == 0); + ASSERT (unrecognized == 0); + ASSERT (optind == 2); + } + for (start = 0; start <= 1; start++) + { + const char *p_value = NULL; + const char *q_value = NULL; + int non_options_count = 0; + const char *non_options[10]; + int unrecognized = 0; + int argc = 0; + const char *argv[10]; + a_seen = 0; + b_seen = 0; + + argv[argc++] = "program"; + argv[argc++] = "--p"; + argv[argc++] = "foo"; + argv[argc++] = "bar"; + argv[argc] = NULL; + optind = start; + getopt_long_loop (argc, argv, "p:q:", long_options_required, + &p_value, &q_value, + &non_options_count, non_options, &unrecognized); + ASSERT (a_seen == 0); + ASSERT (b_seen == 0); + ASSERT (p_value != NULL && strcmp (p_value, "foo") == 0); + ASSERT (q_value == NULL); + ASSERT (non_options_count == 0); + ASSERT (unrecognized == 0); + ASSERT (optind == 3); + } + for (start = 0; start <= 1; start++) + { + const char *p_value = NULL; + const char *q_value = NULL; + int non_options_count = 0; + const char *non_options[10]; + int unrecognized = 0; + int argc = 0; + const char *argv[10]; + a_seen = 0; + b_seen = 0; + + argv[argc++] = "program"; + argv[argc++] = "-ab"; + argv[argc++] = "--q"; + argv[argc++] = "baz"; + argv[argc++] = "--p=foo"; + argv[argc++] = "bar"; + argv[argc] = NULL; + optind = start; + getopt_long_loop (argc, argv, "abp:q:", long_options_required, + &p_value, &q_value, + &non_options_count, non_options, &unrecognized); + ASSERT (a_seen == 1); + ASSERT (b_seen == 1); + ASSERT (p_value != NULL && strcmp (p_value, "foo") == 0); + ASSERT (q_value != NULL && strcmp (q_value, "baz") == 0); + ASSERT (non_options_count == 0); + ASSERT (unrecognized == 0); + ASSERT (optind == 5); + } + + /* Test processing of long options with arguments via -W. */ + for (start = 0; start <= 1; start++) + { + const char *p_value = NULL; + const char *q_value = NULL; + int non_options_count = 0; + const char *non_options[10]; + int unrecognized = 0; + int argc = 0; + const char *argv[10]; + a_seen = 0; + b_seen = 0; + + argv[argc++] = "program"; + argv[argc++] = "-Wp=foo"; + argv[argc++] = "bar"; + argv[argc] = NULL; + optind = start; + getopt_long_loop (argc, argv, "p:q:W;", long_options_required, + &p_value, &q_value, + &non_options_count, non_options, &unrecognized); + ASSERT (a_seen == 0); + ASSERT (b_seen == 0); + ASSERT (p_value != NULL && strcmp (p_value, "foo") == 0); + ASSERT (q_value == NULL); + ASSERT (non_options_count == 0); + ASSERT (unrecognized == 0); + ASSERT (optind == 2); + } + for (start = 0; start <= 1; start++) + { + const char *p_value = NULL; + const char *q_value = NULL; + int non_options_count = 0; + const char *non_options[10]; + int unrecognized = 0; + int argc = 0; + const char *argv[10]; + a_seen = 0; + b_seen = 0; + + argv[argc++] = "program"; + argv[argc++] = "-W"; + argv[argc++] = "p"; + argv[argc++] = "foo"; + argv[argc++] = "bar"; + argv[argc] = NULL; + optind = start; + getopt_long_loop (argc, argv, "p:W;q:", long_options_required, + &p_value, &q_value, + &non_options_count, non_options, &unrecognized); + ASSERT (a_seen == 0); + ASSERT (b_seen == 0); + ASSERT (p_value != NULL && strcmp (p_value, "foo") == 0); + ASSERT (q_value == NULL); + ASSERT (non_options_count == 0); + ASSERT (unrecognized == 0); + ASSERT (optind == 4); + } + for (start = 0; start <= 1; start++) + { + const char *p_value = NULL; + const char *q_value = NULL; + int non_options_count = 0; + const char *non_options[10]; + int unrecognized = 0; + int argc = 0; + const char *argv[10]; + a_seen = 0; + b_seen = 0; + + argv[argc++] = "program"; + argv[argc++] = "-ab"; + argv[argc++] = "-Wq"; + argv[argc++] = "baz"; + argv[argc++] = "-W"; + argv[argc++] = "p=foo"; + argv[argc++] = "bar"; + argv[argc] = NULL; + optind = start; + getopt_long_loop (argc, argv, "W;abp:q:", long_options_required, + &p_value, &q_value, + &non_options_count, non_options, &unrecognized); + ASSERT (a_seen == 1); + ASSERT (b_seen == 1); + ASSERT (p_value != NULL && strcmp (p_value, "foo") == 0); + ASSERT (q_value != NULL && strcmp (q_value, "baz") == 0); + ASSERT (non_options_count == 0); + ASSERT (unrecognized == 0); + ASSERT (optind == 6); + } + + /* Test processing of short options with optional arguments. */ + for (start = 0; start <= 1; start++) + { + const char *p_value = NULL; + const char *q_value = NULL; + int non_options_count = 0; + const char *non_options[10]; + int unrecognized = 0; + int argc = 0; + const char *argv[10]; + a_seen = 0; + b_seen = 0; + + argv[argc++] = "program"; + argv[argc++] = "-pfoo"; + argv[argc++] = "bar"; + argv[argc] = NULL; + optind = start; + getopt_long_loop (argc, argv, "p::q::", long_options_optional, + &p_value, &q_value, + &non_options_count, non_options, &unrecognized); + ASSERT (a_seen == 0); + ASSERT (b_seen == 0); + ASSERT (p_value != NULL && strcmp (p_value, "foo") == 0); + ASSERT (q_value == NULL); + ASSERT (non_options_count == 0); + ASSERT (unrecognized == 0); + ASSERT (optind == 2); + } + for (start = 0; start <= 1; start++) + { + const char *p_value = NULL; + const char *q_value = NULL; + int non_options_count = 0; + const char *non_options[10]; + int unrecognized = 0; + int argc = 0; + const char *argv[10]; + a_seen = 0; + b_seen = 0; + + argv[argc++] = "program"; + argv[argc++] = "-p"; + argv[argc++] = "foo"; + argv[argc++] = "bar"; + argv[argc] = NULL; + optind = start; + getopt_long_loop (argc, argv, "p::q::", long_options_optional, + &p_value, &q_value, + &non_options_count, non_options, &unrecognized); + ASSERT (a_seen == 0); + ASSERT (b_seen == 0); + ASSERT (p_value == NULL); + ASSERT (q_value == NULL); + ASSERT (non_options_count == 0); + ASSERT (unrecognized == 0); + ASSERT (optind == 2); + } + for (start = 0; start <= 1; start++) + { + const char *p_value = NULL; + const char *q_value = NULL; + int non_options_count = 0; + const char *non_options[10]; + int unrecognized = 0; + int argc = 0; + const char *argv[10]; + a_seen = 0; + b_seen = 0; + + argv[argc++] = "program"; + argv[argc++] = "-p"; + argv[argc++] = "-a"; + argv[argc++] = "bar"; + argv[argc] = NULL; + optind = start; + getopt_long_loop (argc, argv, "abp::q::", long_options_optional, + &p_value, &q_value, + &non_options_count, non_options, &unrecognized); + ASSERT (a_seen == 1); + ASSERT (b_seen == 0); + ASSERT (p_value == NULL); + ASSERT (q_value == NULL); + ASSERT (non_options_count == 0); + ASSERT (unrecognized == 0); + ASSERT (optind == 3); + } + + /* Test processing of long options with optional arguments. */ + for (start = 0; start <= 1; start++) + { + const char *p_value = NULL; + const char *q_value = NULL; + int non_options_count = 0; + const char *non_options[10]; + int unrecognized = 0; + int argc = 0; + const char *argv[10]; + a_seen = 0; + b_seen = 0; + + argv[argc++] = "program"; + argv[argc++] = "--p=foo"; + argv[argc++] = "bar"; + argv[argc] = NULL; + optind = start; + getopt_long_loop (argc, argv, "p::q::", long_options_optional, + &p_value, &q_value, + &non_options_count, non_options, &unrecognized); + ASSERT (a_seen == 0); + ASSERT (b_seen == 0); + ASSERT (p_value != NULL && strcmp (p_value, "foo") == 0); + ASSERT (q_value == NULL); + ASSERT (non_options_count == 0); + ASSERT (unrecognized == 0); + ASSERT (optind == 2); + } + for (start = 0; start <= 1; start++) + { + const char *p_value = NULL; + const char *q_value = NULL; + int non_options_count = 0; + const char *non_options[10]; + int unrecognized = 0; + int argc = 0; + const char *argv[10]; + a_seen = 0; + b_seen = 0; + + argv[argc++] = "program"; + argv[argc++] = "--p"; + argv[argc++] = "foo"; + argv[argc++] = "bar"; + argv[argc] = NULL; + optind = start; + getopt_long_loop (argc, argv, "p::q::", long_options_optional, + &p_value, &q_value, + &non_options_count, non_options, &unrecognized); + ASSERT (a_seen == 0); + ASSERT (b_seen == 0); + ASSERT (p_value == NULL); + ASSERT (q_value == NULL); + ASSERT (non_options_count == 0); + ASSERT (unrecognized == 0); + ASSERT (optind == 2); + } + for (start = 0; start <= 1; start++) + { + const char *p_value = NULL; + const char *q_value = NULL; + int non_options_count = 0; + const char *non_options[10]; + int unrecognized = 0; + int argc = 0; + const char *argv[10]; + a_seen = 0; + b_seen = 0; + + argv[argc++] = "program"; + argv[argc++] = "--p="; + argv[argc++] = "foo"; + argv[argc++] = "bar"; + argv[argc] = NULL; + optind = start; + getopt_long_loop (argc, argv, "p::q::", long_options_optional, + &p_value, &q_value, + &non_options_count, non_options, &unrecognized); + ASSERT (a_seen == 0); + ASSERT (b_seen == 0); + ASSERT (p_value != NULL && *p_value == '\0'); + ASSERT (q_value == NULL); + ASSERT (non_options_count == 0); + ASSERT (unrecognized == 0); + ASSERT (optind == 2); + } + for (start = 0; start <= 1; start++) + { + const char *p_value = NULL; + const char *q_value = NULL; + int non_options_count = 0; + const char *non_options[10]; + int unrecognized = 0; + int argc = 0; + const char *argv[10]; + a_seen = 0; + b_seen = 0; + + argv[argc++] = "program"; + argv[argc++] = "--p"; + argv[argc++] = "-a"; + argv[argc++] = "bar"; + argv[argc] = NULL; + optind = start; + getopt_long_loop (argc, argv, "abp::q::", long_options_optional, + &p_value, &q_value, + &non_options_count, non_options, &unrecognized); + ASSERT (a_seen == 1); + ASSERT (b_seen == 0); + ASSERT (p_value == NULL); + ASSERT (q_value == NULL); + ASSERT (non_options_count == 0); + ASSERT (unrecognized == 0); + ASSERT (optind == 3); + } + + /* Test processing of long options with optional arguments via -W. */ + for (start = 0; start <= 1; start++) + { + const char *p_value = NULL; + const char *q_value = NULL; + int non_options_count = 0; + const char *non_options[10]; + int unrecognized = 0; + int argc = 0; + const char *argv[10]; + a_seen = 0; + b_seen = 0; + + argv[argc++] = "program"; + argv[argc++] = "-Wp=foo"; + argv[argc++] = "bar"; + argv[argc] = NULL; + optind = start; + getopt_long_loop (argc, argv, "p::q::W;", long_options_optional, + &p_value, &q_value, + &non_options_count, non_options, &unrecognized); + ASSERT (a_seen == 0); + ASSERT (b_seen == 0); + ASSERT (p_value != NULL && strcmp (p_value, "foo") == 0); + ASSERT (q_value == NULL); + ASSERT (non_options_count == 0); + ASSERT (unrecognized == 0); + ASSERT (optind == 2); + } + for (start = 0; start <= 1; start++) + { + const char *p_value = NULL; + const char *q_value = NULL; + int non_options_count = 0; + const char *non_options[10]; + int unrecognized = 0; + int argc = 0; + const char *argv[10]; + a_seen = 0; + b_seen = 0; + + argv[argc++] = "program"; + argv[argc++] = "-Wp"; + argv[argc++] = "foo"; + argv[argc++] = "bar"; + argv[argc] = NULL; + optind = start; + getopt_long_loop (argc, argv, "p::q::W;", long_options_optional, + &p_value, &q_value, + &non_options_count, non_options, &unrecognized); + ASSERT (a_seen == 0); + ASSERT (b_seen == 0); + ASSERT (p_value == NULL); + ASSERT (q_value == NULL); + ASSERT (non_options_count == 0); + ASSERT (unrecognized == 0); + ASSERT (optind == 2); + } + for (start = 0; start <= 1; start++) + { + const char *p_value = NULL; + const char *q_value = NULL; + int non_options_count = 0; + const char *non_options[10]; + int unrecognized = 0; + int argc = 0; + const char *argv[10]; + a_seen = 0; + b_seen = 0; + + argv[argc++] = "program"; + argv[argc++] = "-Wp="; + argv[argc++] = "foo"; + argv[argc++] = "bar"; + argv[argc] = NULL; + optind = start; + getopt_long_loop (argc, argv, "W;p::q::", long_options_optional, + &p_value, &q_value, + &non_options_count, non_options, &unrecognized); + ASSERT (a_seen == 0); + ASSERT (b_seen == 0); + ASSERT (p_value != NULL && *p_value == '\0'); + ASSERT (q_value == NULL); + ASSERT (non_options_count == 0); + ASSERT (unrecognized == 0); + ASSERT (optind == 2); + } + for (start = 0; start <= 1; start++) + { + const char *p_value = NULL; + const char *q_value = NULL; + int non_options_count = 0; + const char *non_options[10]; + int unrecognized = 0; + int argc = 0; + const char *argv[10]; + a_seen = 0; + b_seen = 0; + + argv[argc++] = "program"; + argv[argc++] = "-W"; + argv[argc++] = "p="; + argv[argc++] = "foo"; + argv[argc++] = "bar"; + argv[argc] = NULL; + optind = start; + getopt_long_loop (argc, argv, "W;p::q::", long_options_optional, + &p_value, &q_value, + &non_options_count, non_options, &unrecognized); + ASSERT (a_seen == 0); + ASSERT (b_seen == 0); + ASSERT (p_value != NULL && *p_value == '\0'); + ASSERT (q_value == NULL); + ASSERT (non_options_count == 0); + ASSERT (unrecognized == 0); + ASSERT (optind == 3); + } + for (start = 0; start <= 1; start++) + { + const char *p_value = NULL; + const char *q_value = NULL; + int non_options_count = 0; + const char *non_options[10]; + int unrecognized = 0; + int argc = 0; + const char *argv[10]; + a_seen = 0; + b_seen = 0; + + argv[argc++] = "program"; + argv[argc++] = "-W"; + argv[argc++] = "p"; + argv[argc++] = "-a"; + argv[argc++] = "bar"; + argv[argc] = NULL; + optind = start; + getopt_long_loop (argc, argv, "W;abp::q::", long_options_optional, + &p_value, &q_value, + &non_options_count, non_options, &unrecognized); + ASSERT (a_seen == 1); + ASSERT (b_seen == 0); + /* ASSERT (p_value == NULL); */ + ASSERT (q_value == NULL); + ASSERT (non_options_count == 0); + ASSERT (unrecognized == 0); + ASSERT (optind == 4); + } + + /* Check that invalid options are recognized. */ + for (start = 0; start <= 1; start++) + { + const char *p_value = NULL; + const char *q_value = NULL; + int non_options_count = 0; + const char *non_options[10]; + int unrecognized = 0; + int argc = 0; + const char *argv[10]; + a_seen = 0; + b_seen = 0; + + argv[argc++] = "program"; + argv[argc++] = "-p"; + argv[argc++] = "foo"; + argv[argc++] = "-x"; + argv[argc++] = "-a"; + argv[argc++] = "bar"; + argv[argc] = NULL; + optind = start; + getopt_long_loop (argc, argv, "abp:q:", long_options_required, + &p_value, &q_value, + &non_options_count, non_options, &unrecognized); + ASSERT (a_seen == 1); + ASSERT (b_seen == 0); + ASSERT (p_value != NULL && strcmp (p_value, "foo") == 0); + ASSERT (q_value == NULL); + ASSERT (non_options_count == 0); + ASSERT (unrecognized == 'x'); + ASSERT (optind == 5); + } + for (start = 0; start <= 1; start++) + { + const char *p_value = NULL; + const char *q_value = NULL; + int non_options_count = 0; + const char *non_options[10]; + int unrecognized = 0; + int argc = 0; + const char *argv[10]; + a_seen = 0; + b_seen = 0; + + argv[argc++] = "program"; + argv[argc++] = "-p"; + argv[argc++] = "foo"; + argv[argc++] = "-:"; + argv[argc++] = "-a"; + argv[argc++] = "bar"; + argv[argc] = NULL; + optind = start; + getopt_long_loop (argc, argv, "abp:q:", long_options_required, + &p_value, &q_value, + &non_options_count, non_options, &unrecognized); + ASSERT (a_seen == 1); + ASSERT (b_seen == 0); + ASSERT (p_value != NULL && strcmp (p_value, "foo") == 0); + ASSERT (q_value == NULL); + ASSERT (non_options_count == 0); + ASSERT (unrecognized == ':'); + ASSERT (optind == 5); + } + + /* Check that unexpected arguments are recognized. */ + for (start = 0; start <= 1; start++) + { + const char *p_value = NULL; + const char *q_value = NULL; + int non_options_count = 0; + const char *non_options[10]; + int unrecognized = 0; + int argc = 0; + const char *argv[10]; + a_seen = 0; + b_seen = 0; + + argv[argc++] = "program"; + argv[argc++] = "-p"; + argv[argc++] = "foo"; + argv[argc++] = "--a="; + argv[argc++] = "bar"; + argv[argc] = NULL; + optind = start; + getopt_long_loop (argc, argv, "abp:q:", long_options_required, + &p_value, &q_value, + &non_options_count, non_options, &unrecognized); + ASSERT (a_seen == 0); + ASSERT (b_seen == 0); + ASSERT (p_value != NULL && strcmp (p_value, "foo") == 0); + ASSERT (q_value == NULL); + ASSERT (non_options_count == 0); + ASSERT (unrecognized == 'a'); + ASSERT (optind == 4); + } + for (start = 0; start <= 1; start++) + { + const char *p_value = NULL; + const char *q_value = NULL; + int non_options_count = 0; + const char *non_options[10]; + int unrecognized = 0; + int argc = 0; + const char *argv[10]; + a_seen = 0; + b_seen = 0; + + argv[argc++] = "program"; + argv[argc++] = "-p"; + argv[argc++] = "foo"; + argv[argc++] = "--b="; + argv[argc++] = "bar"; + argv[argc] = NULL; + optind = start; + getopt_long_loop (argc, argv, "abp:q:", long_options_required, + &p_value, &q_value, + &non_options_count, non_options, &unrecognized); + ASSERT (a_seen == 0); + ASSERT (b_seen == 0); + ASSERT (p_value != NULL && strcmp (p_value, "foo") == 0); + ASSERT (q_value == NULL); + ASSERT (non_options_count == 0); + /* When flag is non-zero, glibc sets optopt anyway, but BSD + leaves optopt unchanged. */ + ASSERT (unrecognized == 1 || unrecognized == 0); + ASSERT (optind == 4); + } + + /* Check that by default, non-options arguments are moved to the end. */ + for (start = 0; start <= 1; start++) + { + const char *p_value = NULL; + const char *q_value = NULL; + int non_options_count = 0; + const char *non_options[10]; + int unrecognized = 0; + int argc = 0; + const char *argv[10]; + a_seen = 0; + b_seen = 0; + + argv[argc++] = "program"; + argv[argc++] = "donald"; + argv[argc++] = "-p"; + argv[argc++] = "billy"; + argv[argc++] = "duck"; + argv[argc++] = "-a"; + argv[argc++] = "bar"; + argv[argc] = NULL; + optind = start; + getopt_long_loop (argc, argv, "abp:q:", long_options_required, + &p_value, &q_value, + &non_options_count, non_options, &unrecognized); + ASSERT (strcmp (argv[0], "program") == 0); + ASSERT (strcmp (argv[1], "-p") == 0); + ASSERT (strcmp (argv[2], "billy") == 0); + ASSERT (strcmp (argv[3], "-a") == 0); + ASSERT (strcmp (argv[4], "donald") == 0); + ASSERT (strcmp (argv[5], "duck") == 0); + ASSERT (strcmp (argv[6], "bar") == 0); + ASSERT (argv[7] == NULL); + ASSERT (a_seen == 1); + ASSERT (b_seen == 0); + ASSERT (p_value != NULL && strcmp (p_value, "billy") == 0); + ASSERT (q_value == NULL); + ASSERT (non_options_count == 0); + ASSERT (unrecognized == 0); + ASSERT (optind == 4); + } + + /* Check that '--' ends the argument processing. */ + for (start = 0; start <= 1; start++) + { + const char *p_value = NULL; + const char *q_value = NULL; + int non_options_count = 0; + const char *non_options[10]; + int unrecognized = 0; + int argc = 0; + const char *argv[20]; + a_seen = 0; + b_seen = 0; + + argv[argc++] = "program"; + argv[argc++] = "donald"; + argv[argc++] = "-p"; + argv[argc++] = "billy"; + argv[argc++] = "duck"; + argv[argc++] = "-a"; + argv[argc++] = "--"; + argv[argc++] = "-b"; + argv[argc++] = "foo"; + argv[argc++] = "-q"; + argv[argc++] = "johnny"; + argv[argc++] = "bar"; + argv[argc] = NULL; + optind = start; + getopt_long_loop (argc, argv, "abp:q:", long_options_required, + &p_value, &q_value, + &non_options_count, non_options, &unrecognized); + ASSERT (strcmp (argv[0], "program") == 0); + ASSERT (strcmp (argv[1], "-p") == 0); + ASSERT (strcmp (argv[2], "billy") == 0); + ASSERT (strcmp (argv[3], "-a") == 0); + ASSERT (strcmp (argv[4], "--") == 0); + ASSERT (strcmp (argv[5], "donald") == 0); + ASSERT (strcmp (argv[6], "duck") == 0); + ASSERT (strcmp (argv[7], "-b") == 0); + ASSERT (strcmp (argv[8], "foo") == 0); + ASSERT (strcmp (argv[9], "-q") == 0); + ASSERT (strcmp (argv[10], "johnny") == 0); + ASSERT (strcmp (argv[11], "bar") == 0); + ASSERT (argv[12] == NULL); + ASSERT (a_seen == 1); + ASSERT (b_seen == 0); + ASSERT (p_value != NULL && strcmp (p_value, "billy") == 0); + ASSERT (q_value == NULL); + ASSERT (non_options_count == 0); + ASSERT (unrecognized == 0); + ASSERT (optind == 5); + } + + /* Check that the '-' flag causes non-options to be returned in order. */ + for (start = 0; start <= 1; start++) + { + const char *p_value = NULL; + const char *q_value = NULL; + int non_options_count = 0; + const char *non_options[10]; + int unrecognized = 0; + int argc = 0; + const char *argv[10]; + a_seen = 0; + b_seen = 0; + + argv[argc++] = "program"; + argv[argc++] = "donald"; + argv[argc++] = "-p"; + argv[argc++] = "billy"; + argv[argc++] = "duck"; + argv[argc++] = "-a"; + argv[argc++] = "bar"; + argv[argc] = NULL; + optind = start; + getopt_long_loop (argc, argv, "-abp:q:", long_options_required, + &p_value, &q_value, + &non_options_count, non_options, &unrecognized); + ASSERT (strcmp (argv[0], "program") == 0); + ASSERT (strcmp (argv[1], "donald") == 0); + ASSERT (strcmp (argv[2], "-p") == 0); + ASSERT (strcmp (argv[3], "billy") == 0); + ASSERT (strcmp (argv[4], "duck") == 0); + ASSERT (strcmp (argv[5], "-a") == 0); + ASSERT (strcmp (argv[6], "bar") == 0); + ASSERT (argv[7] == NULL); + ASSERT (a_seen == 1); + ASSERT (b_seen == 0); + ASSERT (p_value != NULL && strcmp (p_value, "billy") == 0); + ASSERT (q_value == NULL); + ASSERT (non_options_count == 3); + ASSERT (strcmp (non_options[0], "donald") == 0); + ASSERT (strcmp (non_options[1], "duck") == 0); + ASSERT (strcmp (non_options[2], "bar") == 0); + ASSERT (unrecognized == 0); + ASSERT (optind == 7); + } + + /* Check that '--' ends the argument processing. */ + for (start = 0; start <= 1; start++) + { + const char *p_value = NULL; + const char *q_value = NULL; + int non_options_count = 0; + const char *non_options[10]; + int unrecognized = 0; + int argc = 0; + const char *argv[20]; + a_seen = 0; + b_seen = 0; + + argv[argc++] = "program"; + argv[argc++] = "donald"; + argv[argc++] = "-p"; + argv[argc++] = "billy"; + argv[argc++] = "duck"; + argv[argc++] = "-a"; + argv[argc++] = "--"; + argv[argc++] = "-b"; + argv[argc++] = "foo"; + argv[argc++] = "-q"; + argv[argc++] = "johnny"; + argv[argc++] = "bar"; + argv[argc] = NULL; + optind = start; + getopt_long_loop (argc, argv, "-abp:q:", long_options_required, + &p_value, &q_value, + &non_options_count, non_options, &unrecognized); + ASSERT (strcmp (argv[0], "program") == 0); + ASSERT (strcmp (argv[1], "donald") == 0); + ASSERT (strcmp (argv[2], "-p") == 0); + ASSERT (strcmp (argv[3], "billy") == 0); + ASSERT (strcmp (argv[4], "duck") == 0); + ASSERT (strcmp (argv[5], "-a") == 0); + ASSERT (strcmp (argv[6], "--") == 0); + ASSERT (strcmp (argv[7], "-b") == 0); + ASSERT (strcmp (argv[8], "foo") == 0); + ASSERT (strcmp (argv[9], "-q") == 0); + ASSERT (strcmp (argv[10], "johnny") == 0); + ASSERT (strcmp (argv[11], "bar") == 0); + ASSERT (argv[12] == NULL); + ASSERT (a_seen == 1); + ASSERT (b_seen == 0); + ASSERT (p_value != NULL && strcmp (p_value, "billy") == 0); + ASSERT (q_value == NULL); + if (non_options_count == 2) + { + /* glibc behaviour. */ + ASSERT (non_options_count == 2); + ASSERT (strcmp (non_options[0], "donald") == 0); + ASSERT (strcmp (non_options[1], "duck") == 0); + ASSERT (unrecognized == 0); + ASSERT (optind == 7); + } + else + { + /* Another valid behaviour. */ + ASSERT (non_options_count == 7); + ASSERT (strcmp (non_options[0], "donald") == 0); + ASSERT (strcmp (non_options[1], "duck") == 0); + ASSERT (strcmp (non_options[2], "-b") == 0); + ASSERT (strcmp (non_options[3], "foo") == 0); + ASSERT (strcmp (non_options[4], "-q") == 0); + ASSERT (strcmp (non_options[5], "johnny") == 0); + ASSERT (strcmp (non_options[6], "bar") == 0); + ASSERT (unrecognized == 0); + ASSERT (optind == 12); + } + } + + /* Check that the '-' flag has to come first. */ + for (start = 0; start <= 1; start++) + { + const char *p_value = NULL; + const char *q_value = NULL; + int non_options_count = 0; + const char *non_options[10]; + int unrecognized = 0; + int argc = 0; + const char *argv[10]; + a_seen = 0; + b_seen = 0; + + argv[argc++] = "program"; + argv[argc++] = "donald"; + argv[argc++] = "-p"; + argv[argc++] = "billy"; + argv[argc++] = "duck"; + argv[argc++] = "-a"; + argv[argc++] = "bar"; + argv[argc] = NULL; + optind = start; + getopt_long_loop (argc, argv, "abp:q:-", long_options_required, + &p_value, &q_value, + &non_options_count, non_options, &unrecognized); + ASSERT (strcmp (argv[0], "program") == 0); + ASSERT (strcmp (argv[1], "-p") == 0); + ASSERT (strcmp (argv[2], "billy") == 0); + ASSERT (strcmp (argv[3], "-a") == 0); + ASSERT (strcmp (argv[4], "donald") == 0); + ASSERT (strcmp (argv[5], "duck") == 0); + ASSERT (strcmp (argv[6], "bar") == 0); + ASSERT (argv[7] == NULL); + ASSERT (a_seen == 1); + ASSERT (b_seen == 0); + ASSERT (p_value != NULL && strcmp (p_value, "billy") == 0); + ASSERT (q_value == NULL); + ASSERT (non_options_count == 0); + ASSERT (unrecognized == 0); + ASSERT (optind == 4); + } + + /* Check that the '+' flag causes the first non-option to terminate the + loop. */ + for (start = 0; start <= 1; start++) + { + const char *p_value = NULL; + const char *q_value = NULL; + int non_options_count = 0; + const char *non_options[10]; + int unrecognized = 0; + int argc = 0; + const char *argv[10]; + a_seen = 0; + b_seen = 0; + + argv[argc++] = "program"; + argv[argc++] = "donald"; + argv[argc++] = "-p"; + argv[argc++] = "billy"; + argv[argc++] = "duck"; + argv[argc++] = "-a"; + argv[argc++] = "bar"; + argv[argc] = NULL; + optind = start; + getopt_long_loop (argc, argv, "+abp:q:", long_options_required, + &p_value, &q_value, + &non_options_count, non_options, &unrecognized); + ASSERT (strcmp (argv[0], "program") == 0); + ASSERT (strcmp (argv[1], "donald") == 0); + ASSERT (strcmp (argv[2], "-p") == 0); + ASSERT (strcmp (argv[3], "billy") == 0); + ASSERT (strcmp (argv[4], "duck") == 0); + ASSERT (strcmp (argv[5], "-a") == 0); + ASSERT (strcmp (argv[6], "bar") == 0); + ASSERT (argv[7] == NULL); + ASSERT (a_seen == 0); + ASSERT (b_seen == 0); + ASSERT (p_value == NULL); + ASSERT (q_value == NULL); + ASSERT (non_options_count == 0); + ASSERT (unrecognized == 0); + ASSERT (optind == 1); + } + for (start = 0; start <= 1; start++) + { + const char *p_value = NULL; + const char *q_value = NULL; + int non_options_count = 0; + const char *non_options[10]; + int unrecognized = 0; + int argc = 0; + const char *argv[10]; + a_seen = 0; + b_seen = 0; + + argv[argc++] = "program"; + argv[argc++] = "-+"; + argv[argc] = NULL; + optind = start; + getopt_long_loop (argc, argv, "+abp:q:", long_options_required, + &p_value, &q_value, + &non_options_count, non_options, &unrecognized); + ASSERT (a_seen == 0); + ASSERT (b_seen == 0); + ASSERT (p_value == NULL); + ASSERT (q_value == NULL); + ASSERT (non_options_count == 0); + ASSERT (unrecognized == '+'); + ASSERT (optind == 2); + } + + /* Check that '--' ends the argument processing. */ + for (start = 0; start <= 1; start++) + { + const char *p_value = NULL; + const char *q_value = NULL; + int non_options_count = 0; + const char *non_options[10]; + int unrecognized = 0; + int argc = 0; + const char *argv[20]; + a_seen = 0; + b_seen = 0; + + argv[argc++] = "program"; + argv[argc++] = "donald"; + argv[argc++] = "-p"; + argv[argc++] = "billy"; + argv[argc++] = "duck"; + argv[argc++] = "-a"; + argv[argc++] = "--"; + argv[argc++] = "-b"; + argv[argc++] = "foo"; + argv[argc++] = "-q"; + argv[argc++] = "johnny"; + argv[argc++] = "bar"; + argv[argc] = NULL; + optind = start; + getopt_long_loop (argc, argv, "+abp:q:", long_options_required, + &p_value, &q_value, + &non_options_count, non_options, &unrecognized); + ASSERT (strcmp (argv[0], "program") == 0); + ASSERT (strcmp (argv[1], "donald") == 0); + ASSERT (strcmp (argv[2], "-p") == 0); + ASSERT (strcmp (argv[3], "billy") == 0); + ASSERT (strcmp (argv[4], "duck") == 0); + ASSERT (strcmp (argv[5], "-a") == 0); + ASSERT (strcmp (argv[6], "--") == 0); + ASSERT (strcmp (argv[7], "-b") == 0); + ASSERT (strcmp (argv[8], "foo") == 0); + ASSERT (strcmp (argv[9], "-q") == 0); + ASSERT (strcmp (argv[10], "johnny") == 0); + ASSERT (strcmp (argv[11], "bar") == 0); + ASSERT (argv[12] == NULL); + ASSERT (a_seen == 0); + ASSERT (b_seen == 0); + ASSERT (p_value == NULL); + ASSERT (q_value == NULL); + ASSERT (non_options_count == 0); + ASSERT (unrecognized == 0); + ASSERT (optind == 1); + } + + /* Check that the '+' flag has to come first. */ + for (start = 0; start <= 1; start++) + { + const char *p_value = NULL; + const char *q_value = NULL; + int non_options_count = 0; + const char *non_options[10]; + int unrecognized = 0; + int argc = 0; + const char *argv[10]; + a_seen = 0; + b_seen = 0; + + argv[argc++] = "program"; + argv[argc++] = "donald"; + argv[argc++] = "-p"; + argv[argc++] = "billy"; + argv[argc++] = "duck"; + argv[argc++] = "-a"; + argv[argc++] = "bar"; + argv[argc] = NULL; + optind = start; + getopt_long_loop (argc, argv, "abp:q:+", long_options_required, + &p_value, &q_value, + &non_options_count, non_options, &unrecognized); + ASSERT (strcmp (argv[0], "program") == 0); + ASSERT (strcmp (argv[1], "-p") == 0); + ASSERT (strcmp (argv[2], "billy") == 0); + ASSERT (strcmp (argv[3], "-a") == 0); + ASSERT (strcmp (argv[4], "donald") == 0); + ASSERT (strcmp (argv[5], "duck") == 0); + ASSERT (strcmp (argv[6], "bar") == 0); + ASSERT (argv[7] == NULL); + ASSERT (a_seen == 1); + ASSERT (b_seen == 0); + ASSERT (p_value != NULL && strcmp (p_value, "billy") == 0); + ASSERT (q_value == NULL); + ASSERT (non_options_count == 0); + ASSERT (unrecognized == 0); + ASSERT (optind == 4); + } +} + +/* Test behavior of getopt_long when POSIXLY_CORRECT is set in the + environment. Options with optional arguments should not change + behavior just because of an environment variable. + https://lists.gnu.org/r/bug-m4/2006-09/msg00028.html */ +static void +test_getopt_long_posix (void) +{ + int start; + + /* Check that POSIXLY_CORRECT stops parsing the same as leading '+'. */ + for (start = 0; start <= 1; start++) + { + const char *p_value = NULL; + const char *q_value = NULL; + int non_options_count = 0; + const char *non_options[10]; + int unrecognized = 0; + int argc = 0; + const char *argv[10]; + a_seen = 0; + b_seen = 0; + + argv[argc++] = "program"; + argv[argc++] = "donald"; + argv[argc++] = "-p"; + argv[argc++] = "billy"; + argv[argc++] = "duck"; + argv[argc++] = "-a"; + argv[argc++] = "bar"; + argv[argc] = NULL; + optind = start; + getopt_long_loop (argc, argv, "abp:q:", long_options_required, + &p_value, &q_value, + &non_options_count, non_options, &unrecognized); + ASSERT (strcmp (argv[0], "program") == 0); + ASSERT (strcmp (argv[1], "donald") == 0); + ASSERT (strcmp (argv[2], "-p") == 0); + ASSERT (strcmp (argv[3], "billy") == 0); + ASSERT (strcmp (argv[4], "duck") == 0); + ASSERT (strcmp (argv[5], "-a") == 0); + ASSERT (strcmp (argv[6], "bar") == 0); + ASSERT (argv[7] == NULL); + ASSERT (a_seen == 0); + ASSERT (b_seen == 0); + ASSERT (p_value == NULL); + ASSERT (q_value == NULL); + ASSERT (non_options_count == 0); + ASSERT (unrecognized == 0); + ASSERT (optind == 1); + } + + /* Check that POSIXLY_CORRECT doesn't change optional arguments. */ + for (start = 0; start <= 1; start++) + { + const char *p_value = NULL; + const char *q_value = NULL; + int non_options_count = 0; + const char *non_options[10]; + int unrecognized = 0; + int argc = 0; + const char *argv[10]; + a_seen = 0; + b_seen = 0; + + argv[argc++] = "program"; + argv[argc++] = "-p"; + argv[argc++] = "billy"; + argv[argc] = NULL; + optind = start; + getopt_long_loop (argc, argv, "p::", long_options_required, + &p_value, &q_value, + &non_options_count, non_options, &unrecognized); + ASSERT (a_seen == 0); + ASSERT (b_seen == 0); + ASSERT (p_value == NULL); + ASSERT (q_value == NULL); + ASSERT (non_options_count == 0); + ASSERT (unrecognized == 0); + ASSERT (optind == 2); + } + + /* Check that leading - still sees options after non-options. */ + for (start = 0; start <= 1; start++) + { + const char *p_value = NULL; + const char *q_value = NULL; + int non_options_count = 0; + const char *non_options[10]; + int unrecognized = 0; + int argc = 0; + const char *argv[10]; + a_seen = 0; + b_seen = 0; + + argv[argc++] = "program"; + argv[argc++] = "-a"; + argv[argc++] = "billy"; + argv[argc++] = "-b"; + argv[argc] = NULL; + optind = start; + getopt_long_loop (argc, argv, "-ab", long_options_required, + &p_value, &q_value, + &non_options_count, non_options, &unrecognized); + ASSERT (a_seen == 1); + ASSERT (b_seen == 1); + ASSERT (p_value == NULL); + ASSERT (q_value == NULL); + ASSERT (non_options_count == 1); + ASSERT (strcmp (non_options[0], "billy") == 0); + ASSERT (unrecognized == 0); + ASSERT (optind == 4); + } +} + +/* Reduce casting, so we can use string literals elsewhere. + getopt_long_only takes an array of char*, but luckily does not + modify those elements, so we can pass const char*. */ +static int +do_getopt_long_only (int argc, const char **argv, const char *shortopts, + const struct option *longopts, int *longind) +{ + return getopt_long_only (argc, (char **) argv, shortopts, longopts, longind); +} + +static void +test_getopt_long_only (void) +{ + /* Test disambiguation of options. */ + { + int argc = 0; + const char *argv[10]; + int option_index; + int c; + + argv[argc++] = "program"; + argv[argc++] = "-x"; + argv[argc] = NULL; + optind = 1; + opterr = 0; + c = do_getopt_long_only (argc, argv, "ab", long_options_required, + &option_index); + ASSERT (c == '?'); + ASSERT (optopt == 0); + } + { + int argc = 0; + const char *argv[10]; + int option_index; + int c; + + argv[argc++] = "program"; + argv[argc++] = "-x"; + argv[argc] = NULL; + optind = 1; + opterr = 0; + c = do_getopt_long_only (argc, argv, "abx", long_options_required, + &option_index); + ASSERT (c == 'x'); + ASSERT (optopt == 0); + } + { + int argc = 0; + const char *argv[10]; + int option_index; + int c; + + argv[argc++] = "program"; + argv[argc++] = "--x"; + argv[argc] = NULL; + optind = 1; + opterr = 0; + c = do_getopt_long_only (argc, argv, "abx", long_options_required, + &option_index); + ASSERT (c == '?'); + ASSERT (optopt == 0); + } + { + int argc = 0; + const char *argv[10]; + int option_index; + int c; + + argv[argc++] = "program"; + argv[argc++] = "-b"; + argv[argc] = NULL; + optind = 1; + opterr = 0; + b_seen = 0; + c = do_getopt_long_only (argc, argv, "abx", long_options_required, + &option_index); + ASSERT (c == 'b'); + ASSERT (b_seen == 0); + } + { + int argc = 0; + const char *argv[10]; + int option_index; + int c; + + argv[argc++] = "program"; + argv[argc++] = "--b"; + argv[argc] = NULL; + optind = 1; + opterr = 0; + b_seen = 0; + c = do_getopt_long_only (argc, argv, "abx", long_options_required, + &option_index); + ASSERT (c == 0); + ASSERT (b_seen == 1); + } + { + int argc = 0; + const char *argv[10]; + int option_index; + int c; + + argv[argc++] = "program"; + argv[argc++] = "-xt"; + argv[argc] = NULL; + optind = 1; + opterr = 0; + c = do_getopt_long_only (argc, argv, "ab", long_options_required, + &option_index); + ASSERT (c == '?'); + ASSERT (optopt == 0); + } + { + int argc = 0; + const char *argv[10]; + int option_index; + int c; + + argv[argc++] = "program"; + argv[argc++] = "-xt"; + argv[argc] = NULL; + optind = 1; + opterr = 0; + c = do_getopt_long_only (argc, argv, "abx", long_options_required, + &option_index); + ASSERT (c == '?'); + ASSERT (optopt == 0); + } + { + int argc = 0; + const char *argv[10]; + int option_index; + int c; + + argv[argc++] = "program"; + argv[argc++] = "-xtra"; + argv[argc] = NULL; + optind = 1; + opterr = 0; + c = do_getopt_long_only (argc, argv, "ab", long_options_required, + &option_index); + ASSERT (c == 1001); + } + { + int argc = 0; + const char *argv[10]; + int option_index; + int c; + + argv[argc++] = "program"; + argv[argc++] = "-xtreme"; + argv[argc] = NULL; + optind = 1; + opterr = 0; + c = do_getopt_long_only (argc, argv, "abx:", long_options_required, + &option_index); + ASSERT (c == 1002); + } + { + int argc = 0; + const char *argv[10]; + int option_index; + int c; + + argv[argc++] = "program"; + argv[argc++] = "-xtremel"; + argv[argc] = NULL; + optind = 1; + opterr = 0; + c = do_getopt_long_only (argc, argv, "ab", long_options_required, + &option_index); + /* glibc getopt_long_only is intentionally different from + getopt_long when handling a prefix that is common to two + spellings, when both spellings have the same option directives. + BSD getopt_long_only treats both cases the same. */ + ASSERT (c == 1003 || c == '?'); + ASSERT (optind == 2); + } + { + int argc = 0; + const char *argv[10]; + int option_index; + int c; + + argv[argc++] = "program"; + argv[argc++] = "-xtremel"; + argv[argc] = NULL; + optind = 1; + opterr = 0; + c = do_getopt_long_only (argc, argv, "abx::", long_options_required, + &option_index); + /* glibc getopt_long_only is intentionally different from + getopt_long when handling a prefix that is common to two + spellings, when both spellings have the same option directives. + BSD getopt_long_only treats both cases the same. */ + ASSERT (c == 1003 || c == '?'); + ASSERT (optind == 2); + ASSERT (optarg == NULL); + } + { + int argc = 0; + const char *argv[10]; + int option_index; + int c; + + argv[argc++] = "program"; + argv[argc++] = "-xtras"; + argv[argc] = NULL; + optind = 1; + opterr = 0; + c = do_getopt_long_only (argc, argv, "abx::", long_options_required, + &option_index); + ASSERT (c == 'x'); + ASSERT (strcmp (optarg, "tras") == 0); + } +} diff --git a/libdicl/tests/test-malloc-gnu.c b/libdicl/tests/test-malloc-gnu.c new file mode 100644 index 0000000..ad2247b --- /dev/null +++ b/libdicl/tests/test-malloc-gnu.c @@ -0,0 +1,31 @@ +/* Test of malloc function. + Copyright (C) 2010-2019 Free Software Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +#include + +#include + +int +main () +{ + /* Check that malloc (0) is not a NULL pointer. */ + char *p = malloc (0); + if (p == NULL) + return 1; + + free (p); + return 0; +} diff --git a/libdicl/tests/test-strings.c b/libdicl/tests/test-strings.c new file mode 100644 index 0000000..b0dcfc3 --- /dev/null +++ b/libdicl/tests/test-strings.c @@ -0,0 +1,27 @@ +/* Test of substitute. + Copyright (C) 2007, 2009-2019 Free Software Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +/* Written by Bruno Haible , 2007. */ + +#include + +#include + +int +main () +{ + return 0; +} diff --git a/libdicl/tests/test-strnlen.c b/libdicl/tests/test-strnlen.c new file mode 100644 index 0000000..0147410 --- /dev/null +++ b/libdicl/tests/test-strnlen.c @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2010-2019 Free Software Foundation, Inc. + * Written by Eric Blake + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . */ + +#include + +#include + +#include "signature.h" +SIGNATURE_CHECK (strnlen, size_t, (char const *, size_t)); + +#include + +#include "zerosize-ptr.h" +#include "macros.h" + +int +main (void) +{ + size_t i; + char *page_boundary = (char *) zerosize_ptr (); + if (!page_boundary) + { + page_boundary = malloc (0x1000); + ASSERT (page_boundary); + page_boundary += 0x1000; + } + + /* Basic behavior tests. */ + ASSERT (strnlen ("a", 0) == 0); + ASSERT (strnlen ("a", 1) == 1); + ASSERT (strnlen ("a", 2) == 1); + ASSERT (strnlen ("", 0x100000) == 0); + + /* Memory fence and alignment testing. */ + for (i = 0; i < 512; i++) + { + char *start = page_boundary - i; + size_t j = i; + memset (start, 'x', i); + do + { + if (i != j) + { + start[j] = 0; + ASSERT (strnlen (start, i + j) == j); + } + ASSERT (strnlen (start, i) == j); + ASSERT (strnlen (start, j) == j); + } + while (j--); + } + + return 0; +} diff --git a/libdicl/tests/test-sysexits.c b/libdicl/tests/test-sysexits.c new file mode 100644 index 0000000..f7f79f1 --- /dev/null +++ b/libdicl/tests/test-sysexits.c @@ -0,0 +1,52 @@ +/* Test of substitute. + Copyright (C) 2007, 2009-2019 Free Software Foundation, Inc. + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ + +/* Written by Bruno Haible , 2007. */ + +#include + +#include + +int exitcode; + +int +main () +{ + /* Check that all EX_* symbols are defined to integer constant expressions + with mutually different values. */ + switch (exitcode) + { + case EX_OK: + case EX_USAGE: + case EX_DATAERR: + case EX_NOINPUT: + case EX_NOUSER: + case EX_NOHOST: + case EX_UNAVAILABLE: + case EX_SOFTWARE: + case EX_OSERR: + case EX_OSFILE: + case EX_CANTCREAT: + case EX_IOERR: + case EX_TEMPFAIL: + case EX_PROTOCOL: + case EX_NOPERM: + case EX_CONFIG: + break; + } + + return 0; +}