[PATCH] add fparseln

Strake strake888 at gmail.com
Tue Aug 7 06:44:30 PDT 2012


diff --git a/include/bsd/libutil.h b/include/bsd/libutil.h
index 648473c..ebb6160 100644
--- a/include/bsd/libutil.h
+++ b/include/bsd/libutil.h
@@ -40,8 +40,10 @@
 #define _LIBUTIL_H_

 #include <features.h>
+#include <sys/cdefs.h>
 #include <sys/types.h>
 #include <stdint.h>
+#include <stdio.h>

 /* for pidfile.c */
 struct pidfh {
@@ -62,6 +64,8 @@ struct pidfh *pidfile_open(const char *path, mode_t
mode, pid_t *pidptr);
 int pidfile_write(struct pidfh *pfh);
 int pidfile_close(struct pidfh *pfh);
 int pidfile_remove(struct pidfh *pfh);
+
+char   *fparseln(FILE *, size_t *, size_t *, const char[3], int);
 __END_DECLS

 /* humanize_number(3) */
@@ -73,4 +77,13 @@ __END_DECLS
 #define HN_GETSCALE             0x10
 #define HN_AUTOSCALE            0x20

+/*
+ * fparseln() specific operation flags.
+ */
+#define FPARSELN_UNESCESC	0x01
+#define FPARSELN_UNESCCONT	0x02
+#define FPARSELN_UNESCCOMM	0x04
+#define FPARSELN_UNESCREST	0x08
+#define FPARSELN_UNESCALL	0x0f
+
 #endif /* !_LIBUTIL_H_ */
diff --git a/man/Makefile.am b/man/Makefile.am
index 16d3cb0..007e26a 100644
--- a/man/Makefile.am
+++ b/man/Makefile.am
@@ -26,6 +26,7 @@ dist_man_MANS = \
 	fgetln.3 \
 	flopen.3 \
 	fmtcheck.3 \
+	fparseln.3 \
 	getmode.3 \
 	getpeereid.3 \
 	getprogname.3 \
diff --git a/man/fparseln.3 b/man/fparseln.3
new file mode 100644
index 0000000..2655102
--- /dev/null
+++ b/man/fparseln.3
@@ -0,0 +1,148 @@
+.\"	$OpenBSD: fparseln.3,v 1.7 2007/05/31 19:19:38 jmc Exp $
+.\"	$NetBSD: fparseln.3,v 1.7 1999/07/02 15:49:12 simonb Exp $
+.\"
+.\" Copyright (c) 1997 Christos Zoulas.  All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\"    notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\"    notice, this list of conditions and the following disclaimer in the
+.\"    documentation and/or other materials provided with the distribution.
+.\" 3. All advertising materials mentioning features or use of this software
+.\"    must display the following acknowledgement:
+.\"	This product includes software developed by Christos Zoulas.
+.\" 4. The name of the author may not be used to endorse or promote products
+.\"    derived from this software without specific prior written permission.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+.\"
+.Dd $Mdocdate: May 31 2007 $
+.Dt FPARSELN 3
+.Os
+.Sh NAME
+.Nm fparseln
+.Nd return the next logical line from a stream
+.Sh SYNOPSIS
+.Fd #include <stdio.h>
+.Fd #include <util.h>
+.Ft "char *"
+.Fo "fparseln"
+.Fa "FILE *stream" "size_t *len" "size_t *lineno"
+.Fa "const char delim[3]" "int flags"
+.Fc
+.Sh DESCRIPTION
+The
+.Fn fparseln
+function
+returns a pointer to the next logical line from the stream referenced by
+.Fa stream .
+This string is null terminated, contains no trailing newline,
+and is dynamically allocated on each invocation.
+It is the responsibility of the caller to free the pointer.
+.Pp
+By default, if a character is escaped, both it and the preceding escape
+character will be present in the returned string.
+Various
+.Fa flags
+alter this behaviour.
+.Pp
+The meaning of the arguments is as follows:
+.Bl -tag -width "lineno"
+.It Fa stream
+The stream to read from.
+.It Fa len
+If not
+.Dv NULL ,
+the length of the string is stored in the memory location referenced by
+.Fa len .
+.It Fa lineno
+If not
+.Dv NULL ,
+the value of the memory location to which
+.Fa lineno
+references is incremented by the number of lines actually read from the file.
+.It Fa delim
+Contains the escape, continuation, and comment characters.
+If a character is
+.Tn NUL
+then processing for that character is disabled.
+If
+.Dv NULL ,
+all characters default to values specified below.
+The contents of
+.Fa delim
+is as follows:
+.Bl -tag -width "delim[0]"
+.It Fa delim[0]
+The escape character, which defaults to
+.Ql \e ,
+is used to remove any special meaning from the next character.
+.It Fa delim[1]
+The continuation character, which defaults to
+.Ql \e ,
+is used to indicate that the next line should be concatenated with the
+current one if this character is the last character on the current line
+and is not escaped.
+.It Fa delim[2]
+The comment character, which defaults to
+.Ql # ,
+if not escaped indicates the beginning of a comment that extends until the
+end of the current line.
+.El
+.It Fa flags
+If non-zero, alter the operation of
+.Fn fparseln .
+The various flags, which may be
+.Tn OR Ns 'ed
+together, are:
+.Bl -tag -width "FPARSELN_UNESCCOMM"
+.It Dv FPARSELN_UNESCCOMM
+Remove escape preceding an escaped comment.
+.It Dv FPARSELN_UNESCCONT
+Remove escape preceding an escaped continuation.
+.It Dv FPARSELN_UNESCESC
+Remove escape preceding an escaped escape.
+.It Dv FPARSELN_UNESCREST
+Remove escape preceding any other character.
+.It Dv FPARSELN_UNESCALL
+All of the above.
+.El
+.El
+.Sh RETURN VALUES
+Upon successful completion a pointer to the parsed line is returned;
+otherwise,
+.Dv NULL
+is returned.
+.Pp
+Internally, the
+.Fn fparseln
+function uses
+.Xr fgetln 3 ,
+so all error conditions that apply to
+.Xr fgetln 3
+apply to
+.Fn fparseln
+as well.
+In addition
+.Fn fparseln
+may set
+.Va errno
+to
+.Er ENOMEM
+and return
+.Dv NULL
+if it runs out of memory.
+.Sh SEE ALSO
+.Xr fgetln 3
diff --git a/src/Makefile.am b/src/Makefile.am
index 8b2b91a..be8889c 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -45,6 +45,7 @@ libbsd_la_SOURCES = \
 	fgetln.c \
 	flopen.c \
 	fmtcheck.c \
+	fparseln.c \
 	fpurge.c \
 	getpeereid.c \
 	hash/md5.c \
diff --git a/src/fparseln.c b/src/fparseln.c
new file mode 100644
index 0000000..a0107c3
--- /dev/null
+++ b/src/fparseln.c
@@ -0,0 +1,210 @@
+/*	$OpenBSD: fparseln.c,v 1.6 2005/08/02 21:46:23 espie Exp $	*/
+/*	$NetBSD: fparseln.c,v 1.7 1999/07/02 15:49:12 simonb Exp $	*/
+
+/*
+ * Copyright (c) 1997 Christos Zoulas.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *	This product includes software developed by Christos Zoulas.
+ * 4. The name of the author may not be used to endorse or promote products
+ *    derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "libutil.h"
+
+static int isescaped(const char *, const char *, int);
+
+/* isescaped():
+ *	Return true if the character in *p that belongs to a string
+ *	that starts in *sp, is escaped by the escape character esc.
+ */
+static int
+isescaped(const char *sp, const char *p, int esc)
+{
+	const char     *cp;
+	size_t		ne;
+
+	/* No escape character */
+	if (esc == '\0')
+		return 1;
+
+	/* Count the number of escape characters that precede ours */
+	for (ne = 0, cp = p; --cp >= sp && *cp == esc; ne++)
+		continue;
+
+	/* Return true if odd number of escape characters */
+	return (ne & 1) != 0;
+}
+
+
+/* fparseln():
+ *	Read a line from a file parsing continuations ending in \
+ *	and eliminating trailing newlines, or comments starting with
+ *	the comment char.
+ */
+char *
+fparseln(FILE *fp, size_t *size, size_t *lineno, const char str[3],
+    int flags)
+{
+	static const char dstr[3] = { '\\', '\\', '#' };
+	char	*buf = NULL, *ptr, *cp, esc, con, nl, com;
+	size_t	s, len = 0;
+	int	cnt = 1;
+
+	if (str == NULL)
+		str = dstr;
+
+	esc = str[0];
+	con = str[1];
+	com = str[2];
+
+	/*
+	 * XXX: it would be cool to be able to specify the newline character,
+	 * but unfortunately, fgetln does not let us
+	 */
+	nl  = '\n';
+
+	while (cnt) {
+		cnt = 0;
+
+		if (lineno)
+			(*lineno)++;
+
+		if ((ptr = fgetln(fp, &s)) == NULL)
+			break;
+
+		if (s && com) {		/* Check and eliminate comments */
+			for (cp = ptr; cp < ptr + s; cp++)
+				if (*cp == com && !isescaped(ptr, cp, esc)) {
+					s = cp - ptr;
+					cnt = s == 0 && buf == NULL;
+					break;
+				}
+		}
+
+		if (s && nl) {		/* Check and eliminate newlines */
+			cp = &ptr[s - 1];
+
+			if (*cp == nl)
+				s--;	/* forget newline */
+		}
+
+		if (s && con) {		/* Check and eliminate continuations */
+			cp = &ptr[s - 1];
+
+			if (*cp == con && !isescaped(ptr, cp, esc)) {
+				s--;	/* forget escape */
+				cnt = 1;
+			}
+		}
+
+		if (s == 0 && buf != NULL)
+			continue;
+
+		if ((cp = realloc(buf, len + s + 1)) == NULL) {
+			free(buf);
+			return NULL;
+		}
+		buf = cp;
+
+		(void) memcpy(buf + len, ptr, s);
+		len += s;
+		buf[len] = '\0';
+	}
+
+	if ((flags & FPARSELN_UNESCALL) != 0 && esc && buf != NULL &&
+	    strchr(buf, esc) != NULL) {
+		ptr = cp = buf;
+		while (cp[0] != '\0') {
+			int skipesc;
+
+			while (cp[0] != '\0' && cp[0] != esc)
+				*ptr++ = *cp++;
+			if (cp[0] == '\0' || cp[1] == '\0')
+				break;
+
+			skipesc = 0;
+			if (cp[1] == com)
+				skipesc += (flags & FPARSELN_UNESCCOMM);
+			if (cp[1] == con)
+				skipesc += (flags & FPARSELN_UNESCCONT);
+			if (cp[1] == esc)
+				skipesc += (flags & FPARSELN_UNESCESC);
+			if (cp[1] != com && cp[1] != con && cp[1] != esc)
+				skipesc = (flags & FPARSELN_UNESCREST);
+
+			if (skipesc)
+				cp++;
+			else
+				*ptr++ = *cp++;
+			*ptr++ = *cp++;
+		}
+		*ptr = '\0';
+		len = strlen(buf);
+	}
+
+	if (size)
+		*size = len;
+	return buf;
+}
+
+#ifdef TEST
+
+int main(int, char **);
+
+int
+main(argc, argv)
+	int argc;
+	char **argv;
+{
+	char   *ptr;
+	size_t	size, line;
+
+	line = 0;
+	while ((ptr = fparseln(stdin, &size, &line, NULL,
+	    FPARSELN_UNESCALL)) != NULL)
+		printf("line %d (%d) |%s|\n", line, size, ptr);
+	return 0;
+}
+
+/*
+
+# This is a test
+line 1
+line 2 \
+line 3 # Comment
+line 4 \# Not comment \\\\
+
+# And a comment \
+line 5 \\\
+line 6
+
+*/
+
+#endif /* TEST */


More information about the libbsd mailing list