diff --git a/Makefile.am b/Makefile.am index 0ea9e26..0da1da5 100644 --- a/Makefile.am +++ b/Makefile.am @@ -3,10 +3,10 @@ ACLOCAL_AMFLAGS = -I m4 lib_LTLIBRARIES = libite.la pkgincludedir = $(includedir)/lite pkginclude_HEADERS = lite.h queue.h conio.h tree.h -libite_la_SOURCES = chomp.c copyfile.c dir.c fexist.c fisdir.c fmode.c fsendfile.c \ - ifconfig.c lfile.c makepath.c pidfile.c pidfilefn.c progress.c \ - reallocarray.c rsync.c strlcpy.c strlcat.c strtonum.c tempfile.c \ - tree.c +libite_la_SOURCES = chomp.c copyfile.c dir.c fexist.c fisdir.c fmode.c fparseln.c \ + fsendfile.c ifconfig.c lfile.c makepath.c pidfile.c pidfilefn.c \ + progress.c reallocarray.c rsync.c strlcpy.c strlcat.c strtonum.c \ + tempfile.c tree.c libite_la_CPPFLAGS = -D_GNU_SOURCE libite_la_CFLAGS = -W -Wall -Wextra diff --git a/README.md b/README.md index 4e08dea..85e189a 100644 --- a/README.md +++ b/README.md @@ -282,10 +282,15 @@ Generic Functions set. The `path` argument should be a directory. -OpenBSD Functions ------------------ +OpenBSD/NetBSD/FreeBSD Functions +-------------------------------- -The following are popular OpenBSD functions and highly useful macros. +The following are popular functions and highly useful macros from the +*BSD world of UNIX that are sadly missing on Linux. + +- `fparseln(fp, *len, *lineno, delim, flags)` + + http://man.openbsd.org/OpenBSD-current/man3/fparseln.3 - `pidfile(basename)` diff --git a/fparseln.c b/fparseln.c new file mode 100644 index 0000000..4445d01 --- /dev/null +++ b/fparseln.c @@ -0,0 +1,202 @@ +/* $NetBSD: fparseln.c,v 1.10 2009/10/21 01:07:45 snj 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. + * + * 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 +#if defined(LIBC_SCCS) && !defined(lint) +__RCSID("$NetBSD: fparseln.c,v 1.10 2009/10/21 01:07:45 snj Exp $"); +#endif /* LIBC_SCCS and not lint */ + +#include +#include +#include +#include +#include +#include "lite.h" + +#define _DIAGASSERT(t) + +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; + + _DIAGASSERT(sp != NULL); + _DIAGASSERT(p != NULL); + + /* No escape character */ + if (esc == '\0') + return 0; + + /* 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] = { '\\', '\\', '#' }; + + ssize_t s; + size_t len, ptrlen; + char *buf; + char *ptr, *cp; + int cnt; + char esc, con, nl, com; + + _DIAGASSERT(fp != NULL); + + len = 0; + buf = NULL; + ptrlen = 0; + ptr = NULL; + 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, + * getdelim(3) does let us, but supporting it would diverge from BSDs. + */ + nl = '\n'; + + flockfile(fp); + + while (cnt) { + cnt = 0; + + if (lineno) + (*lineno)++; + + s = getline(&ptr, &ptrlen, fp); + if (s < 0) + 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 continuation char */ + cnt = 1; + } + } + + if (s == 0) { + /* + * nothing to add, skip realloc except in case + * we need a minimal buf to return an empty line + */ + if (cnt || buf != NULL) + continue; + } + + if ((cp = realloc(buf, len + s + 1)) == NULL) { + funlockfile(fp); + free(buf); + free(ptr); + return NULL; + } + buf = cp; + + (void) memcpy(buf + len, ptr, s); + len += s; + buf[len] = '\0'; + } + + funlockfile(fp); + free(ptr); + + 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; +} diff --git a/lite.h b/lite.h index 7168930..971290a 100644 --- a/lite.h +++ b/lite.h @@ -34,9 +34,19 @@ #include /* MAX(), isset(), setbit(), TRUE, FALSE, et consortes. :-) */ #include +/* + * fparseln() specific operation flags. + */ +#define FPARSELN_UNESCESC 0x01 +#define FPARSELN_UNESCCONT 0x02 +#define FPARSELN_UNESCCOMM 0x04 +#define FPARSELN_UNESCREST 0x08 +#define FPARSELN_UNESCALL 0x0f + typedef struct lfile lfile_t; char *chomp (char *str); +char *fparseln (FILE *, size_t *, size_t *, const char[3], int); int fexist (char *file); int fisdir (const char *file);