From c4eb5cb1248abfd944f8ebc4f7982efa2158c57f Mon Sep 17 00:00:00 2001 From: Joachim Nilsson Date: Sun, 25 Oct 2015 22:50:10 +0100 Subject: [PATCH] Add simple parser for UNIX configuration files. Signed-off-by: Joachim Nilsson --- Makefile | 5 +- README.md | 23 ++++++ lfile.c | 220 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ lfile.mk | 4 + 4 files changed, 250 insertions(+), 2 deletions(-) create mode 100644 lfile.c create mode 100644 lfile.mk diff --git a/Makefile b/Makefile index e0fc13b..c271185 100644 --- a/Makefile +++ b/Makefile @@ -34,8 +34,8 @@ LDFLAGS = $(EXTRA_LDFLAGS) DISTFILES = README LICENSE HEADERS = lite.h OBJS := chomp.o copyfile.o dir.o fexist.o fisdir.o fmode.o fsendfile.o -OBJS += ifconfig.o makepath.o pidfile.o pidfilefn.o progress.o rsync.o -OBJS += strlcpy.o strlcat.o strtonum.o tempfile.o tree.o +OBJS += ifconfig.o lfile.o makepath.o pidfile.o pidfilefn.o progress.o +OBJS += rsync.o strlcpy.o strlcat.o strtonum.o tempfile.o tree.o DEPS := $(OBJS:.o=.d) JUNK = *~ *.bak *.map .*.d *.d DEADJOE semantic.cache *.gdb *.elf core core.* @@ -57,6 +57,7 @@ test: clean $(MAKE) -f fexist.mk $(MAKE) -f fisdir.mk $(MAKE) -f fsendfile.mk + $(MAKE) -f lfile.mk $(MAKE) -f makepath.mk $(MAKE) -f pidfilefn.mk $(MAKE) -f progress.mk diff --git a/README.md b/README.md index 0477f82..3683b3e 100644 --- a/README.md +++ b/README.md @@ -133,6 +133,29 @@ Generic Functions Basic ifconfig like operations on an interface. Only supports IPv4 adresses. Note that mask is not CIDR notation. +- `lfopen(file, sep)`, `lfclose(lf)` + + API for parsing UNIX style configuration files like `/etc/protocols` + and `/etc/services`. + +- `lftok(lf)` + + Read tokens, delimeted by `sep`, from file opened with `lfopen()`. + +- `lfgetkey(lf, key)` + + Find `key` in file opened with `lfopen()`, return value/argument. + +- `lfgetint(lf, key)` + + Wrapper for `lfgetkey()`, returns positive integer value to `key`, + or `-1` if `key` is not found. + +- `fgetint(file, sep, key)` + + Wrapper for `lfopen()`, `lfgetint()`, and `lfclose()`. Useful for + when only reading a single `key` from a file. + - `makepath(dir)` Create all components of the specified directory. diff --git a/lfile.c b/lfile.c new file mode 100644 index 0000000..d589279 --- /dev/null +++ b/lfile.c @@ -0,0 +1,220 @@ +/* Parse UNIX /etc configuration files like /etc/protocols and /etc/services + * + * Copyright (c) 2015 Joachim Nilsson + * + * Permission to use, copy, modify, and/or distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include /* FILE */ +#include /* atoi() */ +#include /* strlen(), strncmp(), strtok_r() */ +#include /* MAX() */ + +typedef struct { + FILE *fp; + char buf[256]; + char *sep, *save; +} lfile_t; + +/** + * lfopen - Open file and return parsing context + * @file: File to parse + * @sep: Separator(s) to use in lftok() + * + * Returns: + * Pointer to a &lftile_t parser context, or %NULL on error. + */ +lfile_t *lfopen(char *file, char *sep) +{ + lfile_t *lf; + + if (!file || !sep) { + errno = EINVAL; + return NULL; + } + + /* Use calloc for clearing buf on behalf of lftok() */ + lf = calloc(sizeof(*lf), sizeof(char)); + if (lf) { + lf->fp = fopen(file, "r"); + lf->sep = sep; + + if (!lf->fp) { + free(lf); + return NULL; + } + } + + return lf; +} + +/** + * lfclose - Close a parser context + * @lf: Pointer to &lfile_t context from lfopen() + */ +void lfclose(lfile_t *lf) +{ + if (!lf) + return; + + if (lf->fp) + fclose(lf->fp); + free(lf); +} + +/** + * lftok - Get next token in file + * @lf: Pointer to &lfile_t context from lfopen() + * + * Returns: + * Next token, read from file previously opened with lfopen(), + * or %NULL if EOF. + */ +char *lftok(lfile_t *lf) +{ + char *token; + + if (!lf || !lf->fp || !lf->sep) { + errno = EINVAL; + return NULL; + } + + token = strtok_r(NULL, lf->sep, &lf->save); + if (!token) { + while (fgets(lf->buf, sizeof(lf->buf), lf->fp)) { + if (lf->buf[0] == '#') + continue; + + token = strtok_r(lf->buf, lf->sep, &lf->save); + if (!token) + continue; + + return token; + } + + errno = ENOENT; + return NULL; + } + + return token; +} + +/** + * lfgetkey - Find key in file + * @lf: Pointer to &lfile_t context from lfopen() + * @key: Key to look for + * + * Locate @key from the current position in the file parser context + * returned from lfopen(). Please note, the search for @key does not + * start from the beginning of the file, it searches from the current + * position. To restart search from the beginning use rewind() on the + * lf->fp. + * + * Returns: + * The value to @key, or %NULL if not found. + */ +char *lfgetkey(lfile_t *lf, char *key) +{ + char *token; + + while ((token = lftok(lf))) { + if (token[0] == '#') + continue; + + if (!strncmp(token, key, MAX(strlen(token), strlen(key)))) + return lftok(lf); + } + + return NULL; +} + +/** + * lfgetint - Same as lfgetkey() but returns an integer + * @lf: Pointer to &lfile_t context from lfopen() + * @key: Key to look for + * + * This function is the same as lfgetkey() but returns the positive + * integer value for the matching @key, if found. + * + * Returns: + * The positive integer value for @key, or -1 if not found. + */ +int lfgetint(lfile_t *lf, char *key) +{ + char *token = lfgetkey(lf, key); + + if (token) + return atoi(token); + + return -1; +} + +/** + * fgetint - Find the integer value for key in a file + * @file: File to search for @key + * @sep: Separator for tokens in @file + * @key: Key to look for in @file + * + * This is a convenience wrapper for lfopen(), lfgetint(), and + * lfclose(). + * + * Returns: + * The positive integer value for @key, or -1 if not found. + */ +int fgetint(char *file, char *sep, char *key) +{ + int val = -1; + lfile_t *lf; + + lf = lfopen(file, sep); + if (lf) { + val = lfgetint(lf, key); + lfclose(lf); + } + + return val; +} + +#ifdef UNITTEST +int main(void) +{ + int val; + lfile_t lf; + + val = fgetint("/etc/protocols", " \n\t", "udp"); + if (val == -1) { + perror("Failed locating 'udp' protocol"); + return 1; + } + printf("udp has proto %d\n", val); + + val = fgetint("/etc/services", " /\n\t", "ftp"); + if (val == -1) { + perror("Failed locating 'ftp' service"); + return 1; + } + printf("ftp is inet port %d\n", val); + + return 0; +} +#endif + +/** + * Local Variables: + * compile-command: "make V=1 -f lfile.mk" + * version-control: t + * indent-tabs-mode: t + * c-file-style: "linux" + * End: + */ diff --git a/lfile.mk b/lfile.mk new file mode 100644 index 0000000..91fadb8 --- /dev/null +++ b/lfile.mk @@ -0,0 +1,4 @@ +OBJS = +TARGET = lfile.test clean + +include rules.mk