dummy $(static_gl_objects): $(gnulib_headers)
bin_PROGRAMS = cdecl99
-cdecl99_SOURCES = common/src/help.c src/commands.c src/cdecl99.h
+cdecl99_SOURCES = common/src/help.c src/commands.c src/cdecl99.h src/getline.h
EXTRA_cdecl99_DEPENDENCIES = $(libmain_a_OBJECTS) $(libexec_a_OBJECTS)
cdecl99_LDADD = $(EXTRA_cdecl99_DEPENDENCIES) libcdecl.la libgnu.a \
$(LTLIBINTL) $(LTLIBREADLINE)
-dnl Copyright © 2011-2013, 2020-2023 Nick Bowler
+dnl Copyright © 2011-2013, 2020-2024 Nick Bowler
dnl
dnl License WTFPL2: Do What The Fuck You Want To Public License, version 2.
dnl This is free software: you are free to do what the fuck you want to.
AC_CONFIG_TESTDIR([.], [t:.])
DX_PROG_AUTOTEST_AM
+AC_CACHE_CHECK([for getline], [dx_cv_have_getline],
+ [AC_LINK_IFELSE([AC_LANG_PROGRAM([#include <stdio.h>
+], [ssize_t (*x)() = getline;
+char *p = 0;
+size_t n = 0;
+return getline(&p, &n, stdin);
+])], [dx_cv_have_getline=yes], [dx_cv_have_getline=no])])
+AS_CASE([$dx_cv_have_getline], [yes],
+ [AC_DEFINE([HAVE_GETLINE], [1],
+ [Define to 1 if the getline function is available.])])
+
AC_CONFIG_FILES([Makefile])
AC_OUTPUT
# --avoid=gperf \
# --avoid=std-gnu11 \
# dx-nls \
-# getline \
# getopt-gnu \
# gitlog-to-changelog \
# inttypes-incomplete \
gl_LOCAL_DIR([lib/local])
gl_MODULES([
dx-nls
- getline
getopt-gnu
gitlog-to-changelog
inttypes-incomplete
/*
* Command line utility for making sense of C declarations.
- * Copyright © 2011-2012, 2020-2023 Nick Bowler
+ * Copyright © 2011-2012, 2020-2024 Nick Bowler
*
* 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
#include "xtra.h"
#include "options.h"
#include "version.h"
+#include "getline.h"
#if HAVE_READLINE_READLINE_H
# include <readline/readline.h>
printf(_("Report bugs to <%s>.\n"), PACKAGE_BUGREPORT);
}
-static int do_getline(char **linebuf, size_t *n)
-{
- ssize_t rc;
-
- if ((rc = getline(linebuf, n, stdin)) < 0) {
- if (ferror(stdin))
- print_error("%s", strerror(errno));
- return 0;
- }
-
- if (rc-- && (*linebuf)[rc] == '\n')
- (*linebuf)[rc] = '\0';
- return 1;
-}
-
static int do_readline(char **linebuf, size_t *n, bool batch)
{
#if !HAVE_READLINE
{
char *line = NULL;
bool fail = 0;
- size_t n;
+ size_t n = 0;
while (do_readline(&line, &n, batch)) {
int rc = run_command(line, batch);
--- /dev/null
+/*
+ * Copyright © 2024 Nick Bowler
+ *
+ * getline-like function which removes trailing newline (if any), and
+ * returns nonzero if and only if a line was successfully read.
+ *
+ * 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 <https://www.gnu.org/licenses/>.
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <limits.h>
+#include <errno.h>
+
+#ifdef CDECL_TEST_H_
+/*
+ * Use the minimum initial alloc size in the test programs to ensure we at
+ * least are exercising the realloc path on a reasonably regular basis.
+ */
+# define CDECL99_GETLINE_INITIAL_ALLOC 1
+#else
+# define CDECL99_GETLINE_INITIAL_ALLOC 75
+#endif
+
+static inline int do_getline(char **linebuf, size_t *n)
+{
+ extern void print_error(const char *fmt, ...);
+ const char *errmsg;
+
+#if HAVE_GETLINE
+ ssize_t rc;
+
+ if ((rc = getline(linebuf, n, stdin)) < 0) {
+ if (ferror(stdin))
+ goto input_error;
+ return 0;
+ }
+
+ if (rc-- && (*linebuf)[rc] == '\n')
+ (*linebuf)[rc] = '\0';
+ return 1;
+#else
+ char *work = *linebuf;
+ size_t pos = 0;
+ size_t sz;
+
+ if (!work) {
+ sz = CDECL99_GETLINE_INITIAL_ALLOC;
+ goto initial_alloc;
+ }
+
+ for (sz = *n;;) {
+ if (!fgets(&work[pos], sz - pos, stdin)) {
+ if (ferror(stdin))
+ goto input_error;
+
+ return !!pos;
+ }
+
+ pos += strlen(&work[pos]);
+ if (work[pos-1] == '\n') {
+ work[pos-1] = '\0';
+ return 1;
+ }
+
+ if (sz > INT_MAX/2 || sz > ((size_t)-1)/4)
+ break;
+
+ sz = ((sz*4) + 2) / 3;
+initial_alloc:
+ work = realloc(work, sz);
+ if (!work)
+ break;
+ *linebuf = work;
+ *n = sz;
+ }
+
+ errmsg = _("failed to allocate memory");
+#endif
+ if (0) input_error: errmsg = strerror(errno);
+ print_error("%s", errmsg);
+ return 0;
+}
+
/*
- * Copyright © 2023 Nick Bowler
+ * Copyright © 2023-2024 Nick Bowler
*
* 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
#define version_blurb(s, copyright) \
s "\n" \
- copyright "2023 Nick Bowler\n" \
+ copyright "2024 Nick Bowler\n" \
"License GPLv3+: GNU GPL version 3 or any later version.\n" \
"This is free software: you are free to change and redistribute it.\n" \
"There is NO WARRANTY, to the extent permitted by law." \
#define PROGNAME "crossparse"
#include "test.h"
+#include "getline.h"
static const char sopts[] = "f:ECVH";
static const struct option lopts[] = {
int i;
const char *filename = NULL;
- FILE *infile = NULL;
if (argc > 0)
progname = argv[0];
mode = MODE_ENGLISH;
break;
case 'f':
- infile = stdin;
filename = optarg;
break;
case 'V':
}
}
- if (infile) {
+ if (filename && !freopen(filename, "r", stdin)) {
+ print_error("%s: %s", filename, strerror(errno));
+ return EXIT_FAILURE;
+ } else if (filename) {
char *line = NULL;
- size_t n;
-
- if (filename) {
- infile = fopen(filename, "r");
- if (!infile) {
- print_error("%s: %s", filename,
- strerror(errno));
- return EXIT_FAILURE;
- }
- }
+ size_t n = 0;
- while (getline(&line, &n, infile) >= 0) {
- char *c = strchr(line, '\n');
- if (c)
- *c = '\0';
+ while (do_getline(&line, &n)) {
if (!test_crossparse(line, mode))
ret = EXIT_FAILURE;
}
free(line);
- fclose(infile);
} else if (argv[optind]) {
for (i = optind; i < argc; i++) {
if (!test_crossparse(argv[i], mode))
#define PROGNAME "normalize"
#include "test.h"
+#include "getline.h"
static const char sopts[] = "f:VH";
static const struct option lopts[] = {
}
}
- if (filename) {
- infile = fopen(filename, "r");
- if (!infile) {
- perror(filename);
- return EXIT_FAILURE;
- }
+ if (filename && !freopen(filename, "r", stdin)) {
+ print_error("%s: %s", filename, strerror(errno));
+ return EXIT_FAILURE;
}
- while ((rc = getline(&line, &n, infile)) >= 0) {
- if (rc > 0 && line[rc-1] == '\n')
- line[rc-1] = 0;
+ while (do_getline(&line, &n)) {
if (do_normalize(line, n) < 0)
ret = EXIT_FAILURE;
}
#define PROGNAME "rendertest"
#include "test.h"
+#include "getline.h"
static const char sopts[] = "n:ECVH";
static const struct option lopts[] = {
}
}
- line = malloc_nofail((sz = n+1));
- while (getline(&line, &sz, stdin) >= 0) {
- char *c = strchr(line, '\n');
- if (c)
- *c = '\0';
-
+ /*
+ * Ensure the preallocated buffer is more than one byte, otherwise we
+ * will hit a bug in AIX 7.2 getline and fall into an infinite loop.
+ */
+ line = malloc_nofail((sz = MAX(n+1, 10)));
+ while (do_getline(&line, &sz)) {
if (do_test(line, n, mode) < 0)
ret = EXIT_FAILURE;
}
#endif
#define MIN(a, b) ((a) < (b) ? (a) : (b))
+#define MAX(a, b) ((a) > (b) ? (a) : (b))
struct cdecl_declspec;
struct option;
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <stdarg.h>
#include <errno.h>
#include <getopt.h>
])
AT_CLEANUP
+
+AT_SETUP([cdecl99 EOF in batch mode])
+
+AT_CHECK([AS_ECHO_N(["explain int"]) | cdecl99 --batch | head], [0],
+[[type int
+]])
+AT_CHECK([cdecl99 --batch </dev/null])
+
+AT_CLEANUP