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