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