/* Collect the next input line, handling backslash continuations */
resetStringInfo(&buf);
- while (!feof(file) && !ferror(file))
+ while (pg_get_line_append(file, &buf))
{
- /* Make sure there's a reasonable amount of room in the buffer */
- enlargeStringInfo(&buf, 128);
-
- /* Read some data, appending it to what we already have */
- if (fgets(buf.data + buf.len, buf.maxlen - buf.len, file) == NULL)
- {
- int save_errno = errno;
-
- if (!ferror(file))
- break; /* normal EOF */
- /* I/O error! */
- ereport(elevel,
- (errcode_for_file_access(),
- errmsg("could not read file \"%s\": %m", filename)));
- err_msg = psprintf("could not read file \"%s\": %s",
- filename, strerror(save_errno));
- resetStringInfo(&buf);
- break;
- }
- buf.len += strlen(buf.data + buf.len);
-
- /* If we haven't got a whole line, loop to read more */
- if (!(buf.len > 0 && buf.data[buf.len - 1] == '\n'))
- continue;
-
/* Strip trailing newline, including \r in case we're on Windows */
buf.len = pg_strip_crlf(buf.data);
break;
}
+ if (ferror(file))
+ {
+ /* I/O error! */
+ int save_errno = errno;
+
+ ereport(elevel,
+ (errcode_for_file_access(),
+ errmsg("could not read file \"%s\": %m", filename)));
+ err_msg = psprintf("could not read file \"%s\": %s",
+ filename, strerror(save_errno));
+ break;
+ }
+
/* Parse fields */
lineptr = buf.data;
while (*lineptr && err_msg == NULL)
* Note that while I/O errors are reflected back to the caller to be
* dealt with, an OOM condition for the palloc'd buffer will not be;
* there'll be an ereport(ERROR) or exit(1) inside stringinfo.c.
+ *
+ * Also note that the palloc'd buffer is usually a lot longer than
+ * strictly necessary, so it may be inadvisable to use this function
+ * to collect lots of long-lived data. A less memory-hungry option
+ * is to use pg_get_line_append() in a loop, then pstrdup() each line.
*/
char *
pg_get_line(FILE *stream)
initStringInfo(&buf);
- /* Read some data, appending it to whatever we already have */
- while (fgets(buf.data + buf.len, buf.maxlen - buf.len, stream) != NULL)
- {
- buf.len += strlen(buf.data + buf.len);
-
- /* Done if we have collected a newline */
- if (buf.len > 0 && buf.data[buf.len - 1] == '\n')
- return buf.data;
-
- /* Make some more room in the buffer, and loop to read more data */
- enlargeStringInfo(&buf, 128);
- }
-
- /* Did fgets() fail because of an I/O error? */
- if (ferror(stream))
+ if (!pg_get_line_append(stream, &buf))
{
/* ensure that free() doesn't mess up errno */
int save_errno = errno;
return NULL;
}
- /* If we read no data before reaching EOF, we should return NULL */
- if (buf.len == 0)
+ return buf.data;
+}
+
+/*
+ * pg_get_line_append()
+ *
+ * This has similar behavior to pg_get_line(), and thence to fgets(),
+ * except that the collected data is appended to whatever is in *buf.
+ *
+ * Returns true if a line was successfully collected (including the
+ * case of a non-newline-terminated line at EOF). Returns false if
+ * there was an I/O error or no data was available before EOF.
+ * (Check ferror(stream) to distinguish these cases.)
+ *
+ * In the false-result case, the contents of *buf are logically unmodified,
+ * though it's possible that the buffer has been resized.
+ */
+bool
+pg_get_line_append(FILE *stream, StringInfo buf)
+{
+ int orig_len = buf->len;
+
+ /* Read some data, appending it to whatever we already have */
+ while (fgets(buf->data + buf->len, buf->maxlen - buf->len, stream) != NULL)
+ {
+ buf->len += strlen(buf->data + buf->len);
+
+ /* Done if we have collected a newline */
+ if (buf->len > orig_len && buf->data[buf->len - 1] == '\n')
+ return true;
+
+ /* Make some more room in the buffer, and loop to read more data */
+ enlargeStringInfo(buf, 128);
+ }
+
+ /* Check for I/O errors and EOF */
+ if (ferror(stream) || buf->len == orig_len)
{
- pfree(buf.data);
- return NULL;
+ /* Discard any data we collected before detecting error */
+ buf->len = orig_len;
+ buf->data[orig_len] = '\0';
+ return false;
}
- /* No newline at EOF ... so return what we have */
- return buf.data;
+ /* No newline at EOF, but we did collect some data */
+ return true;
}
#ifndef COMMON_STRING_H
#define COMMON_STRING_H
+struct StringInfoData; /* avoid including stringinfo.h here */
+
/* functions in src/common/string.c */
extern bool pg_str_endswith(const char *str, const char *end);
extern int strtoint(const char *pg_restrict str, char **pg_restrict endptr,
/* functions in src/common/pg_get_line.c */
extern char *pg_get_line(FILE *stream);
+extern bool pg_get_line_append(FILE *stream, struct StringInfoData *buf);
/* functions in src/common/sprompt.c */
extern char *simple_prompt(const char *prompt, bool echo);