2929#include "catalog/pg_collation.h"
3030#include "catalog/pg_type.h"
3131#include "common/ip.h"
32+ #include "common/string.h"
3233#include "funcapi.h"
3334#include "libpq/ifaddr.h"
3435#include "libpq/libpq.h"
5455
5556
5657#define MAX_TOKEN 256
57- #define MAX_LINE 8192
5858
5959/* callback data for check_network_callback */
6060typedef struct check_network_data
@@ -166,11 +166,19 @@ pg_isblank(const char c)
166166/*
167167 * Grab one token out of the string pointed to by *lineptr.
168168 *
169- * Tokens are strings of non-blank
170- * characters bounded by blank characters, commas, beginning of line, and
171- * end of line. Blank means space or tab. Tokens can be delimited by
172- * double quotes (this allows the inclusion of blanks, but not newlines).
173- * Comments (started by an unquoted '#') are skipped.
169+ * Tokens are strings of non-blank characters bounded by blank characters,
170+ * commas, beginning of line, and end of line. Blank means space or tab.
171+ *
172+ * Tokens can be delimited by double quotes (this allows the inclusion of
173+ * blanks or '#', but not newlines). As in SQL, write two double-quotes
174+ * to represent a double quote.
175+ *
176+ * Comments (started by an unquoted '#') are skipped, i.e. the remainder
177+ * of the line is ignored.
178+ *
179+ * (Note that line continuation processing happens before tokenization.
180+ * Thus, if a continuation occurs within quoted text or a comment, the
181+ * quoted text or comment is considered to continue to the next line.)
174182 *
175183 * The token, if any, is returned at *buf (a buffer of size bufsz), and
176184 * *lineptr is advanced past the token.
@@ -470,6 +478,7 @@ static MemoryContext
470478tokenize_file (const char * filename , FILE * file , List * * tok_lines , int elevel )
471479{
472480 int line_number = 1 ;
481+ StringInfoData buf ;
473482 MemoryContext linecxt ;
474483 MemoryContext oldcxt ;
475484
@@ -478,47 +487,72 @@ tokenize_file(const char *filename, FILE *file, List **tok_lines, int elevel)
478487 ALLOCSET_SMALL_SIZES );
479488 oldcxt = MemoryContextSwitchTo (linecxt );
480489
490+ initStringInfo (& buf );
491+
481492 * tok_lines = NIL ;
482493
483494 while (!feof (file ) && !ferror (file ))
484495 {
485- char rawline [MAX_LINE ];
486496 char * lineptr ;
487497 List * current_line = NIL ;
488498 char * err_msg = NULL ;
499+ int last_backslash_buflen = 0 ;
500+ int continuations = 0 ;
489501
490- if (!fgets (rawline , sizeof (rawline ), file ))
491- {
492- int save_errno = errno ;
502+ /* Collect the next input line, handling backslash continuations */
503+ resetStringInfo (& buf );
493504
494- if (!ferror (file ))
495- break ; /* normal EOF */
496- /* I/O error! */
497- ereport (elevel ,
498- (errcode_for_file_access (),
499- errmsg ("could not read file \"%s\": %m" , filename )));
500- err_msg = psprintf ("could not read file \"%s\": %s" ,
501- filename , strerror (save_errno ));
502- rawline [0 ] = '\0' ;
503- }
504- if (strlen (rawline ) == MAX_LINE - 1 )
505+ while (!feof (file ) && !ferror (file ))
505506 {
506- /* Line too long! */
507- ereport (elevel ,
508- (errcode (ERRCODE_CONFIG_FILE_ERROR ),
509- errmsg ("authentication file line too long" ),
510- errcontext ("line %d of configuration file \"%s\"" ,
511- line_number , filename )));
512- err_msg = "authentication file line too long" ;
513- }
507+ /* Make sure there's a reasonable amount of room in the buffer */
508+ enlargeStringInfo (& buf , 128 );
509+
510+ /* Read some data, appending it to what we already have */
511+ if (fgets (buf .data + buf .len , buf .maxlen - buf .len , file ) == NULL )
512+ {
513+ int save_errno = errno ;
514+
515+ if (!ferror (file ))
516+ break ; /* normal EOF */
517+ /* I/O error! */
518+ ereport (elevel ,
519+ (errcode_for_file_access (),
520+ errmsg ("could not read file \"%s\": %m" , filename )));
521+ err_msg = psprintf ("could not read file \"%s\": %s" ,
522+ filename , strerror (save_errno ));
523+ resetStringInfo (& buf );
524+ break ;
525+ }
526+ buf .len += strlen (buf .data + buf .len );
527+
528+ /* If we haven't got a whole line, loop to read more */
529+ if (!(buf .len > 0 && buf .data [buf .len - 1 ] == '\n' ))
530+ continue ;
531+
532+ /* Strip trailing newline, including \r in case we're on Windows */
533+ buf .len = pg_strip_crlf (buf .data );
534+
535+ /*
536+ * Check for backslash continuation. The backslash must be after
537+ * the last place we found a continuation, else two backslashes
538+ * followed by two \n's would behave surprisingly.
539+ */
540+ if (buf .len > last_backslash_buflen &&
541+ buf .data [buf .len - 1 ] == '\\' )
542+ {
543+ /* Continuation, so strip it and keep reading */
544+ buf .data [-- buf .len ] = '\0' ;
545+ last_backslash_buflen = buf .len ;
546+ continuations ++ ;
547+ continue ;
548+ }
514549
515- /* Strip trailing linebreak from rawline */
516- lineptr = rawline + strlen (rawline ) - 1 ;
517- while (lineptr >= rawline && (* lineptr == '\n' || * lineptr == '\r' ))
518- * lineptr -- = '\0' ;
550+ /* Nope, so we have the whole line */
551+ break ;
552+ }
519553
520554 /* Parse fields */
521- lineptr = rawline ;
555+ lineptr = buf . data ;
522556 while (* lineptr && err_msg == NULL )
523557 {
524558 List * current_field ;
@@ -538,12 +572,12 @@ tokenize_file(const char *filename, FILE *file, List **tok_lines, int elevel)
538572 tok_line = (TokenizedLine * ) palloc (sizeof (TokenizedLine ));
539573 tok_line -> fields = current_line ;
540574 tok_line -> line_num = line_number ;
541- tok_line -> raw_line = pstrdup (rawline );
575+ tok_line -> raw_line = pstrdup (buf . data );
542576 tok_line -> err_msg = err_msg ;
543577 * tok_lines = lappend (* tok_lines , tok_line );
544578 }
545579
546- line_number ++ ;
580+ line_number += continuations + 1 ;
547581 }
548582
549583 MemoryContextSwitchTo (oldcxt );
0 commit comments