summaryrefslogtreecommitdiff
path: root/libpkgconf/fileio.c
diff options
context:
space:
mode:
Diffstat (limited to 'libpkgconf/fileio.c')
-rw-r--r--libpkgconf/fileio.c130
1 files changed, 69 insertions, 61 deletions
diff --git a/libpkgconf/fileio.c b/libpkgconf/fileio.c
index d4f001b9c3e3..2acec22aaee6 100644
--- a/libpkgconf/fileio.c
+++ b/libpkgconf/fileio.c
@@ -2,6 +2,8 @@
* fileio.c
* File reading utilities
*
+ * SPDX-License-Identifier: pkgconf
+ *
* Copyright (c) 2012, 2025 pkgconf authors (see AUTHORS).
*
* Permission to use, copy, modify, and/or distribute this software for any
@@ -16,98 +18,104 @@
#include <libpkgconf/stdinc.h>
#include <libpkgconf/libpkgconf.h>
+#define push_or_return_fail(buf, c) \
+ do { if (!pkgconf_buffer_push_byte((buf), (char) (c))) return false; } while (0)
+
+#define trim_or_return_fail(buf) \
+ do { if (!pkgconf_buffer_trim_byte((buf))) return false; } while (0)
+
bool
pkgconf_fgetline(pkgconf_buffer_t *buffer, FILE *stream)
{
bool quoted = false;
- int c = '\0', c2;
+ bool got_data = false;
+ char in[PKGCONF_ITEM_SIZE];
+ long unread = 0;
- while ((c = getc(stream)) != EOF)
+ while (fgets(in, sizeof in, stream) != NULL)
{
- if (c == '\\' && !quoted)
- {
- quoted = true;
- continue;
- }
- else if (c == '#')
- {
- if (!quoted) {
- /* Skip the rest of the line */
- do {
- c = getc(stream);
- } while (c != '\n' && c != EOF);
- pkgconf_buffer_push_byte(buffer, c);
- break;
- }
- else
- pkgconf_buffer_push_byte(buffer, c);
+ char *p = in;
- quoted = false;
- continue;
- }
- else if (c == '\n')
- {
- if (quoted)
- {
- /* Trim spaces */
- do {
- c2 = getc(stream);
- } while (c2 == '\t' || c2 == ' ');
+ got_data = true;
- ungetc(c2, stream);
+ while (*p != '\0')
+ {
+ unsigned char c = (unsigned char) *p++;
- quoted = false;
+ if (c == '\\' && !quoted)
+ {
+ quoted = true;
continue;
}
- else
+ else if (c == '\n')
{
- pkgconf_buffer_push_byte(buffer, c);
+ if (quoted)
+ {
+ quoted = false;
+ continue;
+ }
+ else
+ push_or_return_fail(buffer, (char) c);
+
+ goto done;
}
+ else if (c == '\r')
+ {
+ if (*p == '\n')
+ {
+ p++;
+ }
+ else if (*p == '\0')
+ {
+ /*
+ * The matching '\n' may not have been read into `in`
+ * yet if '\r' landed exactly on the fgets() buffer
+ * boundary. Peek the real stream so a split CRLF
+ * isn't misparsed as two lines.
+ */
+ int next = getc(stream);
- break;
- }
- else if (c == '\r')
- {
- pkgconf_buffer_push_byte(buffer, '\n');
+ if (next != '\n' && next != EOF && ungetc(next, stream) == EOF)
+ return false;
+ }
- if ((c2 = getc(stream)) == '\n')
- {
if (quoted)
{
quoted = false;
continue;
}
- break;
+ push_or_return_fail(buffer, '\n');
+ /* unlike '\n', a lone '\r' doesn't bound the fgets() call above */
+ unread = (long) strlen(p);
+ goto done;
}
-
- ungetc(c2, stream);
-
- if (quoted)
+ else
{
- quoted = false;
- continue;
- }
+ if (quoted)
+ {
+ push_or_return_fail(buffer, '\\');
+ quoted = false;
+ }
- break;
- }
- else
- {
- if (quoted) {
- pkgconf_buffer_push_byte(buffer, '\\');
- quoted = false;
+ push_or_return_fail(buffer, (char) c);
}
- pkgconf_buffer_push_byte(buffer, c);
}
-
}
+done:
+ if (unread > 0 && fseek(stream, -unread, SEEK_CUR) != 0)
+ return false;
+
/* Remove newline character. */
if (pkgconf_buffer_lastc(buffer) == '\n')
- pkgconf_buffer_trim_byte(buffer);
+ trim_or_return_fail(buffer);
if (pkgconf_buffer_lastc(buffer) == '\r')
- pkgconf_buffer_trim_byte(buffer);
+ trim_or_return_fail(buffer);
+
+ if (!got_data)
+ return false;
- return !(c == EOF || ferror(stream));
+ return !ferror(stream);
}