xref: /netbsd-src/external/ibm-public/postfix/dist/src/util/readlline.c (revision e89934bbf778a6d6d6894877c4da59d0c7835b0f)
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 
readllines(VSTRING * buf,VSTREAM * fp,int * lineno,int * first_line)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