From 675fbd30d44edd7ecfca9e9013424b187eb3db70 Mon Sep 17 00:00:00 2001 From: Jim Meyering Date: Sat, 4 Jul 2009 16:44:19 +0200 Subject: [PATCH] argv-iter: new module * MODULES.html.sh: Add argv-iter. * lib/argv-iter.c, lib/argv-iter.h: New files. * modules/argv-iter: New file. * modules/argv-iter-tests: New file. * tests/test-argv-iter.c: Test it. --- ChangeLog | 9 ++++ MODULES.html.sh | 1 + lib/argv-iter.c | 111 ++++++++++++++++++++++++++++++++++++++++++++++ lib/argv-iter.h | 47 ++++++++++++++++++++ modules/argv-iter | 24 ++++++++++ modules/argv-iter-tests | 10 +++++ tests/test-argv-iter.c | 114 ++++++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 316 insertions(+) create mode 100644 lib/argv-iter.c create mode 100644 lib/argv-iter.h create mode 100644 modules/argv-iter create mode 100644 modules/argv-iter-tests create mode 100644 tests/test-argv-iter.c diff --git a/ChangeLog b/ChangeLog index 253130718..1dc20c03d 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,12 @@ +2009-07-04 Jim Meyering + + argv-iter: new module + * MODULES.html.sh: Add argv-iter. + * lib/argv-iter.c, lib/argv-iter.h: New files. + * modules/argv-iter: New file. + * modules/argv-iter-tests: New file. + * tests/test-argv-iter.c: Test it. + 2009-07-04 Bruno Haible Fix assertion. diff --git a/MODULES.html.sh b/MODULES.html.sh index f74b72404..6adef2046 100755 --- a/MODULES.html.sh +++ b/MODULES.html.sh @@ -1869,6 +1869,7 @@ func_all_modules () func_begin_table func_module argmatch + func_module argv-iter func_module version-etc func_module version-etc-fsf func_module long-options diff --git a/lib/argv-iter.c b/lib/argv-iter.c new file mode 100644 index 000000000..05b06efd8 --- /dev/null +++ b/lib/argv-iter.c @@ -0,0 +1,111 @@ +/* Iterate over arguments from argv or --files0-from=FILE + Copyright (C) 2008-2009 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 Jim Meyering. */ + +#include +#include "argv-iter.h" + +#include +#include + +struct argv_iterator +{ + /* Test FP to determine whether in read-mode or argv-mode. */ + /* file-mode: fp records position */ + FILE *fp; + size_t item_idx; + char *tok; + size_t buf_len; + + /* argv-mode: record just argv and current pointer */ + char **arg_list; + char **p; +}; + +struct argv_iterator * +argv_iter_init_argv (char **argv) +{ + struct argv_iterator *ai = malloc (sizeof *ai); + if (!ai) + return NULL; + ai->fp = NULL; + ai->arg_list = argv; + ai->p = argv; + return ai; +} + +/* Initialize to read from the stream, FP. + The input is expected to contain a list of NUL-delimited tokens. */ +struct argv_iterator * +argv_iter_init_stream (FILE *fp) +{ + struct argv_iterator *ai = malloc (sizeof *ai); + if (!ai) + return NULL; + ai->fp = fp; + ai->tok = NULL; + ai->buf_len = 0; + + ai->item_idx = 0; + ai->arg_list = NULL; + return ai; +} + +char * +argv_iter (struct argv_iterator *ai, enum argv_iter_err *err) +{ + if (ai->fp) + { + ssize_t len = getdelim (&ai->tok, &ai->buf_len, '\0', ai->fp); + if (len < 0) + { + *err = feof (ai->fp) ? AI_ERR_EOF : AI_ERR_READ; + return NULL; + } + + *err = AI_ERR_OK; + ai->item_idx++; + return ai->tok; + } + else + { + if (*(ai->p) == NULL) + { + *err = AI_ERR_EOF; + return NULL; + } + else + { + *err = AI_ERR_OK; + return *(ai->p++); + } + } +} + +size_t +argv_iter_n_args (struct argv_iterator const *ai) +{ + return ai->fp ? ai->item_idx : ai->p - ai->arg_list; +} + +void +argv_iter_free (struct argv_iterator *ai) +{ + if (ai->fp) + free (ai->tok); + free (ai); +} diff --git a/lib/argv-iter.h b/lib/argv-iter.h new file mode 100644 index 000000000..537fb1ea2 --- /dev/null +++ b/lib/argv-iter.h @@ -0,0 +1,47 @@ +/* Iterate over arguments from argv or --files0-from=FILE + Copyright (C) 2008-2009 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 + +struct argv_iterator; +enum argv_iter_err; + +#undef _ATTRIBUTE_NONNULL_ +#if __GNUC__ == 3 && __GNUC_MINOR__ >= 3 || 3 < __GNUC__ +# define _ATTRIBUTE_NONNULL_(m) __attribute__ ((__nonnull__ (m))) +#else +# define _ATTRIBUTE_NONNULL_(m) +#endif + +enum argv_iter_err +{ + AI_ERR_OK = 1, + AI_ERR_EOF, + AI_ERR_MEM, + AI_ERR_READ +}; + +struct argv_iterator *argv_iter_init_argv (char **argv) + _ATTRIBUTE_NONNULL_ (1); +struct argv_iterator *argv_iter_init_stream (FILE *fp) + _ATTRIBUTE_NONNULL_ (1); +char *argv_iter (struct argv_iterator *, enum argv_iter_err *) + _ATTRIBUTE_NONNULL_ (1) _ATTRIBUTE_NONNULL_ (2); +size_t argv_iter_n_args (struct argv_iterator const *) + _ATTRIBUTE_NONNULL_ (1); +void argv_iter_free (struct argv_iterator *) + _ATTRIBUTE_NONNULL_ (1); diff --git a/modules/argv-iter b/modules/argv-iter new file mode 100644 index 000000000..e9fc63391 --- /dev/null +++ b/modules/argv-iter @@ -0,0 +1,24 @@ +Description: +iterate through argv or a --files0-from=-specified file + +Files: +lib/argv-iter.c +lib/argv-iter.h + +Depends-on: +getdelim +stdbool + +configure.ac: + +Makefile.am: +lib_SOURCES += argv-iter.c argv-iter.h + +Include: +"argv-iter.h" + +License +GPL + +Maintainer: +Jim Meyering diff --git a/modules/argv-iter-tests b/modules/argv-iter-tests new file mode 100644 index 000000000..af0405bfb --- /dev/null +++ b/modules/argv-iter-tests @@ -0,0 +1,10 @@ +Files: +tests/test-argv-iter.c + +Depends-on: + +configure.ac: + +Makefile.am: +TESTS += test-argv-iter +check_PROGRAMS += test-argv-iter diff --git a/tests/test-argv-iter.c b/tests/test-argv-iter.c new file mode 100644 index 000000000..7682c4a02 --- /dev/null +++ b/tests/test-argv-iter.c @@ -0,0 +1,114 @@ +/* Test argv iterator + Copyright (C) 2008-2009 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 Jim Meyering. */ + +#include +#include +#include +#include + +#define ARRAY_CARDINALITY(Array) (sizeof (Array) / sizeof *(Array)) +#define STREQ(s1, s2) ((strcmp (s1, s2) == 0)) +#define ASSERT(expr) \ + do \ + { \ + if (!(expr)) \ + { \ + fprintf (stderr, "%s:%d: assertion failed\n", __FILE__, __LINE__); \ + fflush (stderr); \ + abort (); \ + } \ + } \ + while (0) + +#include "argv-iter.h" + +static FILE * +write_nul_delimited_argv (char **argv) +{ + FILE *fp = tmpfile (); + ASSERT (fp); + while (*argv) + { + size_t len = strlen (*argv) + 1; + ASSERT (fwrite (*argv, len, 1, fp) == 1); + argv++; + } + ASSERT (fflush (fp) == 0); + rewind (fp); + return fp; +} + +int +main () +{ + /* set_program_name (argv[0]); placate overzealous "syntax-check" test. */ + static char *av[][4] = { + {NULL}, + {"1", NULL}, + {"1", "2", NULL}, + {"1", "2", "3", NULL} + }; + + int use_stream; + for (use_stream = 0; use_stream < 2; use_stream++) + { + size_t i; + for (i = 0; i < ARRAY_CARDINALITY (av); i++) + { + FILE *fp; + struct argv_iterator *ai; + size_t n_found = 0; + if (use_stream) + { + /* Generate an identical list to be read via FP. */ + ASSERT ((fp = write_nul_delimited_argv (av[i])) != NULL); + ai = argv_iter_init_stream (fp); + } + else + { + fp = NULL; + ai = argv_iter_init_argv (av[i]); + } + ASSERT (ai); + + while (1) + { + enum argv_iter_err ai_err; + char *s = argv_iter (ai, &ai_err); + ASSERT ((i == n_found) == (ai_err == AI_ERR_EOF)); + ASSERT ((s == NULL) ^ (ai_err == AI_ERR_OK)); + ASSERT (ai_err == AI_ERR_OK || ai_err == AI_ERR_EOF); + if (ai_err == AI_ERR_OK) + ++n_found; + if (ai_err == AI_ERR_EOF) + break; + /* In stream mode, the strings are equal, but + in argv mode the actual pointers are equal. */ + ASSERT (use_stream + ? STREQ (s, av[i][n_found - 1]) + : s == av[i][n_found - 1]); + } + ASSERT (argv_iter_n_args (ai) == i); + argv_iter_free (ai); + if (fp) + ASSERT (fclose (fp) == 0); + } + } + + return 0; +} -- 2.11.0