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