The general format of the <filename>pg_hba.conf</filename> file is
a set of records, one per line. Blank lines are ignored, as is any
text after the <literal>#</literal> comment character.
- Records cannot be continued across lines.
+ A record can be continued onto the next line by ending the line with
+ a backslash. (Backslashes are not special except at the end of a line.)
A record is made
up of a number of fields which are separated by spaces and/or tabs.
Fields can contain white space if the field value is double-quoted.
Quoting one of the keywords in a database, user, or address field (e.g.,
<literal>all</literal> or <literal>replication</literal>) makes the word lose its special
meaning, and just match a database, user, or host with that name.
+ Backslash line continuation applies even within quoted text or comments.
</para>
<para>
<synopsis>
<replaceable>map-name</replaceable> <replaceable>system-username</replaceable> <replaceable>database-username</replaceable>
</synopsis>
- Comments and whitespace are handled in the same way as in
+ Comments, whitespace and line continuations are handled in the same way as in
<filename>pg_hba.conf</filename>. The
<replaceable>map-name</replaceable> is an arbitrary name that will be used to
refer to this mapping in <filename>pg_hba.conf</filename>. The other
#include "catalog/pg_collation.h"
#include "catalog/pg_type.h"
#include "common/ip.h"
+#include "common/string.h"
#include "funcapi.h"
#include "libpq/ifaddr.h"
#include "libpq/libpq.h"
#define MAX_TOKEN 256
-#define MAX_LINE 8192
/* callback data for check_network_callback */
typedef struct check_network_data
/*
* Grab one token out of the string pointed to by *lineptr.
*
- * Tokens are strings of non-blank
- * characters bounded by blank characters, commas, beginning of line, and
- * end of line. Blank means space or tab. Tokens can be delimited by
- * double quotes (this allows the inclusion of blanks, but not newlines).
- * Comments (started by an unquoted '#') are skipped.
+ * Tokens are strings of non-blank characters bounded by blank characters,
+ * commas, beginning of line, and end of line. Blank means space or tab.
+ *
+ * Tokens can be delimited by double quotes (this allows the inclusion of
+ * blanks or '#', but not newlines). As in SQL, write two double-quotes
+ * to represent a double quote.
+ *
+ * Comments (started by an unquoted '#') are skipped, i.e. the remainder
+ * of the line is ignored.
+ *
+ * (Note that line continuation processing happens before tokenization.
+ * Thus, if a continuation occurs within quoted text or a comment, the
+ * quoted text or comment is considered to continue to the next line.)
*
* The token, if any, is returned at *buf (a buffer of size bufsz), and
* *lineptr is advanced past the token.
tokenize_file(const char *filename, FILE *file, List **tok_lines, int elevel)
{
int line_number = 1;
+ StringInfoData buf;
MemoryContext linecxt;
MemoryContext oldcxt;
ALLOCSET_SMALL_SIZES);
oldcxt = MemoryContextSwitchTo(linecxt);
+ initStringInfo(&buf);
+
*tok_lines = NIL;
while (!feof(file) && !ferror(file))
{
- char rawline[MAX_LINE];
char *lineptr;
List *current_line = NIL;
char *err_msg = NULL;
+ int last_backslash_buflen = 0;
+ int continuations = 0;
- if (!fgets(rawline, sizeof(rawline), file))
- {
- int save_errno = errno;
+ /* Collect the next input line, handling backslash continuations */
+ resetStringInfo(&buf);
- 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));
- rawline[0] = '\0';
- }
- if (strlen(rawline) == MAX_LINE - 1)
+ while (!feof(file) && !ferror(file))
{
- /* Line too long! */
- ereport(elevel,
- (errcode(ERRCODE_CONFIG_FILE_ERROR),
- errmsg("authentication file line too long"),
- errcontext("line %d of configuration file \"%s\"",
- line_number, filename)));
- err_msg = "authentication file line too long";
- }
+ /* 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);
+
+ /*
+ * Check for backslash continuation. The backslash must be after
+ * the last place we found a continuation, else two backslashes
+ * followed by two \n's would behave surprisingly.
+ */
+ if (buf.len > last_backslash_buflen &&
+ buf.data[buf.len - 1] == '\\')
+ {
+ /* Continuation, so strip it and keep reading */
+ buf.data[--buf.len] = '\0';
+ last_backslash_buflen = buf.len;
+ continuations++;
+ continue;
+ }
- /* Strip trailing linebreak from rawline */
- lineptr = rawline + strlen(rawline) - 1;
- while (lineptr >= rawline && (*lineptr == '\n' || *lineptr == '\r'))
- *lineptr-- = '\0';
+ /* Nope, so we have the whole line */
+ break;
+ }
/* Parse fields */
- lineptr = rawline;
+ lineptr = buf.data;
while (*lineptr && err_msg == NULL)
{
List *current_field;
tok_line = (TokenizedLine *) palloc(sizeof(TokenizedLine));
tok_line->fields = current_line;
tok_line->line_num = line_number;
- tok_line->raw_line = pstrdup(rawline);
+ tok_line->raw_line = pstrdup(buf.data);
tok_line->err_msg = err_msg;
*tok_lines = lappend(*tok_lines, tok_line);
}
- line_number++;
+ line_number += continuations + 1;
}
MemoryContextSwitchTo(oldcxt);