1 /* $NetBSD: readlline.c,v 1.2 2017/02/14 01:16:49 christos Exp $ */ 2 3 /*++ 4 /* NAME 5 /* readlline 3 6 /* SUMMARY 7 /* read logical line 8 /* SYNOPSIS 9 /* #include <readlline.h> 10 /* 11 /* VSTRING *readllines(buf, fp, lineno, first_line) 12 /* VSTRING *buf; 13 /* VSTREAM *fp; 14 /* int *lineno; 15 /* int *first_line; 16 /* 17 /* VSTRING *readlline(buf, fp, lineno) 18 /* VSTRING *buf; 19 /* VSTREAM *fp; 20 /* int *lineno; 21 /* DESCRIPTION 22 /* readllines() reads one logical line from the named stream. 23 /* .IP "blank lines and comments" 24 /* Empty lines and whitespace-only lines are ignored, as 25 /* are lines whose first non-whitespace character is a `#'. 26 /* .IP "multi-line text" 27 /* A logical line starts with non-whitespace text. A line that 28 /* starts with whitespace continues a logical line. 29 /* .PP 30 /* The result value is the input buffer argument or a null pointer 31 /* when no input is found. 32 /* 33 /* readlline() is a backwards-compatibility wrapper. 34 /* 35 /* Arguments: 36 /* .IP buf 37 /* A variable-length buffer for input. The result is null terminated. 38 /* .IP fp 39 /* Handle to an open stream. 40 /* .IP lineno 41 /* A null pointer, or a pointer to an integer that is incremented 42 /* after reading a physical line. 43 /* .IP first_line 44 /* A null pointer, or a pointer to an integer that will contain 45 /* the line number of the first non-blank, non-comment line 46 /* in the result logical line. 47 /* DIAGNOSTICS 48 /* Warning: a continuation line that does not continue preceding text. 49 /* The invalid input is ignored, to avoid complicating caller code. 50 /* SECURITY 51 /* .ad 52 /* .fi 53 /* readlline() imposes no logical line length limit therefore it 54 /* should be used for reading trusted information only. 55 /* LICENSE 56 /* .ad 57 /* .fi 58 /* The Secure Mailer license must be distributed with this software. 59 /* AUTHOR(S) 60 /* Wietse Venema 61 /* IBM T.J. Watson Research 62 /* P.O. Box 704 63 /* Yorktown Heights, NY 10598, USA 64 /*--*/ 65 66 /* System library. */ 67 68 #include <sys_defs.h> 69 #include <ctype.h> 70 71 /* Utility library. */ 72 73 #include "msg.h" 74 #include "vstream.h" 75 #include "vstring.h" 76 #include "readlline.h" 77 78 #define STR(x) vstring_str(x) 79 #define LEN(x) VSTRING_LEN(x) 80 #define END(x) vstring_end(x) 81 82 /* readllines - read one logical line */ 83 84 VSTRING *readllines(VSTRING *buf, VSTREAM *fp, int *lineno, int *first_line) 85 { 86 int ch; 87 int next; 88 ssize_t start; 89 char *cp; 90 91 VSTRING_RESET(buf); 92 93 /* 94 * Ignore comment lines, all whitespace lines, and empty lines. Terminate 95 * at EOF or at the beginning of the next logical line. 96 */ 97 for (;;) { 98 /* Read one line, possibly not newline terminated. */ 99 start = LEN(buf); 100 while ((ch = VSTREAM_GETC(fp)) != VSTREAM_EOF && ch != '\n') 101 VSTRING_ADDCH(buf, ch); 102 if (lineno != 0 && (ch == '\n' || LEN(buf) > start)) 103 *lineno += 1; 104 /* Ignore comment line, all whitespace line, or empty line. */ 105 for (cp = STR(buf) + start; cp < END(buf) && ISSPACE(*cp); cp++) 106 /* void */ ; 107 if (cp == END(buf) || *cp == '#') 108 vstring_truncate(buf, start); 109 else if (start == 0 && lineno != 0 && first_line != 0) 110 *first_line = *lineno; 111 /* Terminate at EOF or at the beginning of the next logical line. */ 112 if (ch == VSTREAM_EOF) 113 break; 114 if (LEN(buf) > 0) { 115 if ((next = VSTREAM_GETC(fp)) != VSTREAM_EOF) 116 vstream_ungetc(fp, next); 117 if (next != '#' && !ISSPACE(next)) 118 break; 119 } 120 } 121 VSTRING_TERMINATE(buf); 122 123 /* 124 * Invalid input: continuing text without preceding text. Allowing this 125 * would complicate "postconf -e", which implements its own multi-line 126 * parsing routine. Do not abort, just warn, so that critical programs 127 * like postmap do not leave behind a truncated table. 128 */ 129 if (LEN(buf) > 0 && ISSPACE(*STR(buf))) { 130 msg_warn("%s: logical line must not start with whitespace: \"%.30s%s\"", 131 VSTREAM_PATH(fp), STR(buf), 132 LEN(buf) > 30 ? "..." : ""); 133 return (readllines(buf, fp, lineno, first_line)); 134 } 135 136 /* 137 * Done. 138 */ 139 return (LEN(buf) > 0 ? buf : 0); 140 } 141