xref: /netbsd-src/external/ibm-public/postfix/dist/src/util/readlline.c (revision e89934bbf778a6d6d6894877c4da59d0c7835b0f)
1*e89934bbSchristos /*	$NetBSD: readlline.c,v 1.2 2017/02/14 01:16:49 christos Exp $	*/
241fbaed0Stron 
341fbaed0Stron /*++
441fbaed0Stron /* NAME
541fbaed0Stron /*	readlline 3
641fbaed0Stron /* SUMMARY
741fbaed0Stron /*	read logical line
841fbaed0Stron /* SYNOPSIS
941fbaed0Stron /*	#include <readlline.h>
1041fbaed0Stron /*
11e262b48eSchristos /*	VSTRING	*readllines(buf, fp, lineno, first_line)
12e262b48eSchristos /*	VSTRING	*buf;
13e262b48eSchristos /*	VSTREAM	*fp;
14e262b48eSchristos /*	int	*lineno;
15e262b48eSchristos /*	int	*first_line;
16e262b48eSchristos /*
1741fbaed0Stron /*	VSTRING	*readlline(buf, fp, lineno)
1841fbaed0Stron /*	VSTRING	*buf;
1941fbaed0Stron /*	VSTREAM	*fp;
2041fbaed0Stron /*	int	*lineno;
2141fbaed0Stron /* DESCRIPTION
22e262b48eSchristos /*	readllines() reads one logical line from the named stream.
2341fbaed0Stron /* .IP "blank lines and comments"
2441fbaed0Stron /*	Empty lines and whitespace-only lines are ignored, as
2541fbaed0Stron /*	are lines whose first non-whitespace character is a `#'.
2641fbaed0Stron /* .IP "multi-line text"
2741fbaed0Stron /*	A logical line starts with non-whitespace text. A line that
2841fbaed0Stron /*	starts with whitespace continues a logical line.
2941fbaed0Stron /* .PP
3041fbaed0Stron /*	The result value is the input buffer argument or a null pointer
3141fbaed0Stron /*	when no input is found.
3241fbaed0Stron /*
33e262b48eSchristos /*	readlline() is a backwards-compatibility wrapper.
34e262b48eSchristos /*
3541fbaed0Stron /*	Arguments:
3641fbaed0Stron /* .IP buf
3741fbaed0Stron /*	A variable-length buffer for input. The result is null terminated.
3841fbaed0Stron /* .IP fp
3941fbaed0Stron /*	Handle to an open stream.
4041fbaed0Stron /* .IP lineno
4141fbaed0Stron /*	A null pointer, or a pointer to an integer that is incremented
42e262b48eSchristos /*	after reading a physical line.
43e262b48eSchristos /* .IP first_line
44e262b48eSchristos /*	A null pointer, or a pointer to an integer that will contain
45e262b48eSchristos /*	the line number of the first non-blank, non-comment line
46e262b48eSchristos /*	in the result logical line.
4741fbaed0Stron /* DIAGNOSTICS
4841fbaed0Stron /*	Warning: a continuation line that does not continue preceding text.
4941fbaed0Stron /*	The invalid input is ignored, to avoid complicating caller code.
5041fbaed0Stron /* SECURITY
5141fbaed0Stron /* .ad
5241fbaed0Stron /* .fi
5341fbaed0Stron /*	readlline() imposes no logical line length limit therefore it
5441fbaed0Stron /*	should be used for reading trusted information only.
5541fbaed0Stron /* LICENSE
5641fbaed0Stron /* .ad
5741fbaed0Stron /* .fi
5841fbaed0Stron /*	The Secure Mailer license must be distributed with this software.
5941fbaed0Stron /* AUTHOR(S)
6041fbaed0Stron /*	Wietse Venema
6141fbaed0Stron /*	IBM T.J. Watson Research
6241fbaed0Stron /*	P.O. Box 704
6341fbaed0Stron /*	Yorktown Heights, NY 10598, USA
6441fbaed0Stron /*--*/
6541fbaed0Stron 
6641fbaed0Stron /* System library. */
6741fbaed0Stron 
6841fbaed0Stron #include <sys_defs.h>
6941fbaed0Stron #include <ctype.h>
7041fbaed0Stron 
7141fbaed0Stron /* Utility library. */
7241fbaed0Stron 
7341fbaed0Stron #include "msg.h"
7441fbaed0Stron #include "vstream.h"
7541fbaed0Stron #include "vstring.h"
7641fbaed0Stron #include "readlline.h"
7741fbaed0Stron 
7841fbaed0Stron #define STR(x) vstring_str(x)
7941fbaed0Stron #define LEN(x) VSTRING_LEN(x)
8041fbaed0Stron #define END(x) vstring_end(x)
8141fbaed0Stron 
82e262b48eSchristos /* readllines - read one logical line */
8341fbaed0Stron 
readllines(VSTRING * buf,VSTREAM * fp,int * lineno,int * first_line)84e262b48eSchristos VSTRING *readllines(VSTRING *buf, VSTREAM *fp, int *lineno, int *first_line)
8541fbaed0Stron {
8641fbaed0Stron     int     ch;
8741fbaed0Stron     int     next;
8841fbaed0Stron     ssize_t start;
8941fbaed0Stron     char   *cp;
9041fbaed0Stron 
9141fbaed0Stron     VSTRING_RESET(buf);
9241fbaed0Stron 
9341fbaed0Stron     /*
9441fbaed0Stron      * Ignore comment lines, all whitespace lines, and empty lines. Terminate
9541fbaed0Stron      * at EOF or at the beginning of the next logical line.
9641fbaed0Stron      */
9741fbaed0Stron     for (;;) {
9841fbaed0Stron 	/* Read one line, possibly not newline terminated. */
9941fbaed0Stron 	start = LEN(buf);
10041fbaed0Stron 	while ((ch = VSTREAM_GETC(fp)) != VSTREAM_EOF && ch != '\n')
10141fbaed0Stron 	    VSTRING_ADDCH(buf, ch);
102e262b48eSchristos 	if (lineno != 0 && (ch == '\n' || LEN(buf) > start))
10341fbaed0Stron 	    *lineno += 1;
10441fbaed0Stron 	/* Ignore comment line, all whitespace line, or empty line. */
10541fbaed0Stron 	for (cp = STR(buf) + start; cp < END(buf) && ISSPACE(*cp); cp++)
10641fbaed0Stron 	     /* void */ ;
10741fbaed0Stron 	if (cp == END(buf) || *cp == '#')
10841fbaed0Stron 	    vstring_truncate(buf, start);
109e262b48eSchristos 	else if (start == 0 && lineno != 0 && first_line != 0)
110e262b48eSchristos 	    *first_line = *lineno;
11141fbaed0Stron 	/* Terminate at EOF or at the beginning of the next logical line. */
11241fbaed0Stron 	if (ch == VSTREAM_EOF)
11341fbaed0Stron 	    break;
11441fbaed0Stron 	if (LEN(buf) > 0) {
11541fbaed0Stron 	    if ((next = VSTREAM_GETC(fp)) != VSTREAM_EOF)
11641fbaed0Stron 		vstream_ungetc(fp, next);
11741fbaed0Stron 	    if (next != '#' && !ISSPACE(next))
11841fbaed0Stron 		break;
11941fbaed0Stron 	}
12041fbaed0Stron     }
12141fbaed0Stron     VSTRING_TERMINATE(buf);
12241fbaed0Stron 
12341fbaed0Stron     /*
12441fbaed0Stron      * Invalid input: continuing text without preceding text. Allowing this
12541fbaed0Stron      * would complicate "postconf -e", which implements its own multi-line
12641fbaed0Stron      * parsing routine. Do not abort, just warn, so that critical programs
12741fbaed0Stron      * like postmap do not leave behind a truncated table.
12841fbaed0Stron      */
12941fbaed0Stron     if (LEN(buf) > 0 && ISSPACE(*STR(buf))) {
13041fbaed0Stron 	msg_warn("%s: logical line must not start with whitespace: \"%.30s%s\"",
13141fbaed0Stron 		 VSTREAM_PATH(fp), STR(buf),
13241fbaed0Stron 		 LEN(buf) > 30 ? "..." : "");
133e262b48eSchristos 	return (readllines(buf, fp, lineno, first_line));
13441fbaed0Stron     }
13541fbaed0Stron 
13641fbaed0Stron     /*
13741fbaed0Stron      * Done.
13841fbaed0Stron      */
13941fbaed0Stron     return (LEN(buf) > 0 ? buf : 0);
14041fbaed0Stron }
141