29
29
#include "catalog/pg_collation.h"
30
30
#include "catalog/pg_type.h"
31
31
#include "common/ip.h"
32
+ #include "common/string.h"
32
33
#include "funcapi.h"
33
34
#include "libpq/ifaddr.h"
34
35
#include "libpq/libpq.h"
54
55
55
56
56
57
#define MAX_TOKEN 256
57
- #define MAX_LINE 8192
58
58
59
59
/* callback data for check_network_callback */
60
60
typedef struct check_network_data
@@ -166,11 +166,19 @@ pg_isblank(const char c)
166
166
/*
167
167
* Grab one token out of the string pointed to by *lineptr.
168
168
*
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.)
174
182
*
175
183
* The token, if any, is returned at *buf (a buffer of size bufsz), and
176
184
* *lineptr is advanced past the token.
@@ -470,6 +478,7 @@ static MemoryContext
470
478
tokenize_file (const char * filename , FILE * file , List * * tok_lines , int elevel )
471
479
{
472
480
int line_number = 1 ;
481
+ StringInfoData buf ;
473
482
MemoryContext linecxt ;
474
483
MemoryContext oldcxt ;
475
484
@@ -478,47 +487,72 @@ tokenize_file(const char *filename, FILE *file, List **tok_lines, int elevel)
478
487
ALLOCSET_SMALL_SIZES );
479
488
oldcxt = MemoryContextSwitchTo (linecxt );
480
489
490
+ initStringInfo (& buf );
491
+
481
492
* tok_lines = NIL ;
482
493
483
494
while (!feof (file ) && !ferror (file ))
484
495
{
485
- char rawline [MAX_LINE ];
486
496
char * lineptr ;
487
497
List * current_line = NIL ;
488
498
char * err_msg = NULL ;
499
+ int last_backslash_buflen = 0 ;
500
+ int continuations = 0 ;
489
501
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 );
493
504
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 ))
505
506
{
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
+ }
514
549
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
+ }
519
553
520
554
/* Parse fields */
521
- lineptr = rawline ;
555
+ lineptr = buf . data ;
522
556
while (* lineptr && err_msg == NULL )
523
557
{
524
558
List * current_field ;
@@ -538,12 +572,12 @@ tokenize_file(const char *filename, FILE *file, List **tok_lines, int elevel)
538
572
tok_line = (TokenizedLine * ) palloc (sizeof (TokenizedLine ));
539
573
tok_line -> fields = current_line ;
540
574
tok_line -> line_num = line_number ;
541
- tok_line -> raw_line = pstrdup (rawline );
575
+ tok_line -> raw_line = pstrdup (buf . data );
542
576
tok_line -> err_msg = err_msg ;
543
577
* tok_lines = lappend (* tok_lines , tok_line );
544
578
}
545
579
546
- line_number ++ ;
580
+ line_number += continuations + 1 ;
547
581
}
548
582
549
583
MemoryContextSwitchTo (oldcxt );
0 commit comments