diff options
author | dteske <dteske@FreeBSD.org> | 2014-11-04 23:46:01 +0000 |
---|---|---|
committer | dteske <dteske@FreeBSD.org> | 2014-11-04 23:46:01 +0000 |
commit | bf764fc98274724d74ce3fe2184574f26b6ec47b (patch) | |
tree | 98a67555867e474c5f716bf62b6c1120a5cfc536 /lib/libfigpar | |
parent | 95b02b5b83b0f6812368761db9c260b00d6e98bc (diff) | |
download | FreeBSD-src-bf764fc98274724d74ce3fe2184574f26b6ec47b.zip FreeBSD-src-bf764fc98274724d74ce3fe2184574f26b6ec47b.tar.gz |
Add new libraries/utilities for data throughput visualization.
dpv(3): dialog progress view library
dpv(1): stream data from stdin or multiple paths with dialog progress view
figpar(3): configuration file parsing library
Reviews: D714
Reviewed by: jelischer, shurd
Discussed at: MeetBSD California 2014 Vendor/Dev Summit
Discussed on: -current
MFC after: 21 days
X-MFC-to: stable/10 stable/9
Diffstat (limited to 'lib/libfigpar')
-rw-r--r-- | lib/libfigpar/Makefile | 21 | ||||
-rw-r--r-- | lib/libfigpar/figpar.3 | 251 | ||||
-rw-r--r-- | lib/libfigpar/figpar.c | 469 | ||||
-rw-r--r-- | lib/libfigpar/figpar.h | 99 | ||||
-rw-r--r-- | lib/libfigpar/string_m.c | 309 | ||||
-rw-r--r-- | lib/libfigpar/string_m.h | 43 |
6 files changed, 1192 insertions, 0 deletions
diff --git a/lib/libfigpar/Makefile b/lib/libfigpar/Makefile new file mode 100644 index 0000000..6f01966 --- /dev/null +++ b/lib/libfigpar/Makefile @@ -0,0 +1,21 @@ +# $FreeBSD$ + +LIB= figpar +SHLIB_MAJOR= 0 +INCS= figpar.h string_m.h +MAN= figpar.3 +MLINKS= figpar.3 get_config_option.3 \ + figpar.3 parse_config.3 \ + figpar.3 replaceall.3 \ + figpar.3 strcount.3 \ + figpar.3 strexpand.3 \ + figpar.3 strexpandnl.3 \ + figpar.3 strtolower.3 + +CFLAGS+= -I${.CURDIR} + +SRCS= figpar.c string_m.c + +WARNS?= 6 + +.include <bsd.lib.mk> diff --git a/lib/libfigpar/figpar.3 b/lib/libfigpar/figpar.3 new file mode 100644 index 0000000..549808e --- /dev/null +++ b/lib/libfigpar/figpar.3 @@ -0,0 +1,251 @@ +.\" Copyright (c) 2013-2014 Devin Teske <dteske@FreeBSD.org> +.\" 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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. +.\" +.\" $FreeBSD$ +.\" +.Dd Oct 24, 2014 +.Dt FIGPAR 3 +.Os +.Sh NAME +.Nm figpar , +.Nm parse_config , +.Nm get_config_option +.Nd configuration file parsing library +.Sh LIBRARY +.Lb libfigpar +.Sh SYNOPSIS +.In figpar.h +.Ft int +.Fo parse_config +.Fa "struct fp_config options[], const char *path" +.Fa "int \*[lp]*unknown\*[rp]\*[lp]struct fp_config *option, uint32_t line" +.Fa "char *directive, char *value\*[rp], uint8_t processing_options" +.Fc +.Ft "struct fp_config *" +.Fo get_config_option +.Fa "struct fp_config options[], const char *directive" +.Fc +.In string_m.h +.Ft int +.Fo replaceall +.Fa "char *source, const char *find, const char *replace" +.Fc +.Ft unsigned int +.Fo strcount +.Fa "const char *source, const char *find" +.Fc +.Ft void +.Fo strexpand +.Fa "char *source" +.Fc +.Ft void +.Fo strexpandnl +.Fa "char *source" +.Fc +.Ft void +.Fo strtolower +.Fa "char *source" +.Fc +.Sh DESCRIPTION +The +.Nm +library provides a light-weight, portable framework for parsing configuration +files. +The library uses +.Xr open 2 , +.Xr read 2 , +and +.Xr lseek 2 +within the file pointed to by +.Fa path +to find directives and values which are then made available to the application. +.Pp +Due to the fact that configuration files may have basic syntax differences, +the library does not attempt to impose any structure on the data but instead +provides raw data to a set of callback functions. +These callback functions can in-turn initiate abort through their return value, +allowing custom syntax validation during parsing. +.Pp +Configuration directives, types, and callback functions are provided through +data structures defined in +.In figpar.h : +.Bd -literal -offset indent +struct fp_config { + enum fp_cfgtype type; /* value type */ + const char *directive; /* keyword */ + union fp_cfgvalue value; /* value */ + + /* Pointer to function used when directive is found */ + int (*action)(struct fp_config *option, uint32_t line, + char *directive, char *value); +}; + +enum fp_cfgtype { + FP_TYPE_NONE = 0x0000, /* for directives with no value */ + FP_TYPE_BOOL = 0x0001, /* boolean */ + FP_TYPE_INT = 0x0002, /* signed 32 bit integer */ + FP_TYPE_UINT = 0x0004, /* unsigned 32 bit integer */ + FP_TYPE_STR = 0x0008, /* string pointer */ + FP_TYPE_STRARRAY = 0x0010, /* string array pointer */ + FP_TYPE_DATA1 = 0x0020, /* void data type-1 (whatever) */ + FP_TYPE_DATA2 = 0x0040, /* void data type-2 (whatever) */ + FP_TYPE_DATA3 = 0x0080, /* void data type-3 (whatever) */ + FP_TYPE_RESERVED1 = 0x0100, /* reserved data type-1 (future) */ + FP_TYPE_RESERVED2 = 0x0200, /* reserved data type-2 (future) */ + FP_TYPE_RESERVED3 = 0x0400, /* reserved data type-3 (future) */ +}; + +union fp_cfgvalue { + void *data; /* Pointer to NUL-terminated string */ + char *str; /* Pointer to NUL-terminated string */ + char **strarray; /* Pointer to an array of strings */ + int32_t num; /* Signed 32-bit integer value */ + uint32_t u_num; /* Unsigned 32-bit integer value */ + uint32_t boolean:1; /* Boolean integer value (0 or 1) */ +}; +.Ed +.Pp +The +.Fa processing_options +argument to +.Fn parse_config +is a mask of bit fields which indicate various +processing options. +The possible flags are as follows: +.Bl -tag -width FP_BREAK_ON_SEMICOLON +.It Dv FP_BREAK_ON_EQUALS +An equals sign +.Pq Ql Li = +is normally considered part of the directive. +This flag enables terminating the directive at the equals sign. +Also makes equals sign optional and transient. +.It Dv FP_BREAK_ON_SEMICOLON +A semicolon +.Pq Ql Li \; +is normally considered part of the value. +This flag enables terminating the value at the semicolon. +Also allows multiple statements on a single line separated by semicolon. +.It Dv FP_CASE_SENSITIVE +Normally directives are matched case insensitively using +.Xr fnmatch 3 . +This flag enables directive matching to be case sensitive. +.It Dv FP_REQUIRE_EQUALS +If a directive is not followed by an equals, processing is aborted. +.It Dv FP_STRICT_EQUALS +Equals must be part of the directive to be considered a delimiter between +directive and value. +.El +.Pp +The +.Fa options +struct array pointer can be NULL and every directive will invoke the +.Fn unknown +function argument. +.Pp +The directive for each fp_config item in the +.Fn parse_config +options argument is matched against each parsed directive using +.Xr fnmatch 3 +until a match is found. +If a match is found, the +.Fn action +function for that fp_config directive is invoked with the line number, +directive, and value. +Otherwise if no match, the +.Fn unknown +function is invoked +.Pq with the same arguments . +.Pp +If either +.Fa action +or +.Fa unknown +return non-zero, +.Fn parse_config +aborts reading the file and returns the error value to its caller. +.Pp +.Fn get_config_option +traverses the options-array and returns the option that matches via +.Xr strcmp 3 , +or if no match a pointer to a static dummy struct is returned +.Pq whose values are all zero or NULL . +.Pp +The use of +.Fa "struct fp_config" +is entirely optional as-is the use of +.Fa "enum fp_cfgtype" +or +.Fa "union fp_cfgvalue" . +For example, you could choose to pass a NULL pointer to +.Fn parse_config +for the first argument and then provide a simple +.Fa unknown +function based on +.Xr queue 3 +that populates a singly-linked list of your own struct containing the +.Fa directive +and +.Fa value . +.Pp +In addition, the following miscellaneous string manipulation routines are +provided by +.In string_m.h : +.Bl -tag -width strexpandnl() +.It Fn replaceall +Replace all occurrences of +.Fa find +in +.Fa source +with +.Fa replace . +.It Fn strcount +Count the number of occurrences of one string that appear in the +.Fa source +string. +Return value is the total count. +An example use would be if you need to know how large a block of memory needs +to be for a +.Fn replaceall +series. +.It Fn strexpand +Expand escape sequences in a buffer pointed to by +.Fa source . +.It Fn strexpandnl +Expand only the escaped newlines in a buffer pointed to by +.Fa source . +.It Fn strtolower +Convert a string to lower case. +.El +.Sh SEE ALSO +.Xr queue 3 +.Sh HISTORY +The +.Nm +library first appeared in +.Fx 11.0 . +.Sh AUTHORS +.An Devin Teske Aq dteske@FreeBSD.org +.Sh BUGS +This is the first implementation of the library, +and the interface may be subject to refinement. diff --git a/lib/libfigpar/figpar.c b/lib/libfigpar/figpar.c new file mode 100644 index 0000000..a97fc85 --- /dev/null +++ b/lib/libfigpar/figpar.c @@ -0,0 +1,469 @@ +/*- + * Copyright (c) 2002-2014 Devin Teske <dteske@FreeBSD.org> + * 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> + +#include <ctype.h> +#include <errno.h> +#include <fcntl.h> +#include <fnmatch.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "figpar.h" +#include "string_m.h" + +struct fp_config fp_dummy_config = {0, NULL, {0}, NULL}; + +/* + * Search for config option (struct fp_config) in the array of config options, + * returning the struct whose directive matches the given parameter. If no + * match is found, a pointer to the static dummy array (above) is returned. + * + * This is to eliminate dependency on the index position of an item in the + * array, since the index position is more apt to be changed as code grows. + */ +struct fp_config * +get_config_option(struct fp_config options[], const char *directive) +{ + uint32_t n; + + /* Check arguments */ + if (options == NULL || directive == NULL) + return (&fp_dummy_config); + + /* Loop through the array, return the index of the first match */ + for (n = 0; options[n].directive != NULL; n++) + if (strcmp(options[n].directive, directive) == 0) + return (&(options[n])); + + /* Re-initialize the dummy variable in case it was written to */ + fp_dummy_config.directive = NULL; + fp_dummy_config.type = 0; + fp_dummy_config.action = NULL; + fp_dummy_config.value.u_num = 0; + + return (&fp_dummy_config); +} + +/* + * Parse the configuration file at `path' and execute the `action' call-back + * functions for any directives defined by the array of config options (first + * argument). + * + * For unknown directives that are encountered, you can optionally pass a + * call-back function for the third argument to be called for unknowns. + * + * Returns zero on success; otherwise returns -1 and errno should be consulted. +*/ +int +parse_config(struct fp_config options[], const char *path, + int (*unknown)(struct fp_config *option, uint32_t line, char *directive, + char *value), uint16_t processing_options) +{ + uint8_t bequals; + uint8_t bsemicolon; + uint8_t case_sensitive; + uint8_t comment = 0; + uint8_t end; + uint8_t found; + uint8_t have_equals = 0; + uint8_t quote; + uint8_t require_equals; + uint8_t strict_equals; + char p[2]; + char *directive; + char *t; + char *value; + int error; + int fd; + ssize_t r = 1; + uint32_t dsize; + uint32_t line = 1; + uint32_t n; + uint32_t vsize; + uint32_t x; + off_t charpos; + off_t curpos; + char rpath[PATH_MAX]; + + /* Sanity check: if no options and no unknown function, return */ + if (options == NULL && unknown == NULL) + return (-1); + + /* Processing options */ + bequals = (processing_options & FP_BREAK_ON_EQUALS) == 0 ? 0 : 1; + bsemicolon = (processing_options & FP_BREAK_ON_SEMICOLON) == 0 ? 0 : 1; + case_sensitive = (processing_options & FP_CASE_SENSITIVE) == 0 ? 0 : 1; + require_equals = (processing_options & FP_REQUIRE_EQUALS) == 0 ? 0 : 1; + strict_equals = (processing_options & FP_STRICT_EQUALS) == 0 ? 0 : 1; + + /* Initialize strings */ + directive = value = 0; + vsize = dsize = 0; + + /* Resolve the file path */ + if (realpath(path, rpath) == 0) + return (-1); + + /* Open the file */ + if ((fd = open(rpath, O_RDONLY)) < 0) + return (-1); + + /* Read the file until EOF */ + while (r != 0) { + r = read(fd, p, 1); + + /* skip to the beginning of a directive */ + while (r != 0 && (isspace(*p) || *p == '#' || comment || + (bsemicolon && *p == ';'))) { + if (*p == '#') + comment = 1; + else if (*p == '\n') { + comment = 0; + line++; + } + r = read(fd, p, 1); + } + /* Test for EOF; if EOF then no directive was found */ + if (r == 0) { + close(fd); + return (0); + } + + /* Get the current offset */ + curpos = lseek(fd, 0, SEEK_CUR) - 1; + if (curpos == -1) { + close(fd); + return (-1); + } + + /* Find the length of the directive */ + for (n = 0; r != 0; n++) { + if (isspace(*p)) + break; + if (bequals && *p == '=') { + have_equals = 1; + break; + } + if (bsemicolon && *p == ';') + break; + r = read(fd, p, 1); + } + + /* Test for EOF, if EOF then no directive was found */ + if (n == 0 && r == 0) { + close(fd); + return (0); + } + + /* Go back to the beginning of the directive */ + error = (int)lseek(fd, curpos, SEEK_SET); + if (error == (curpos - 1)) { + close(fd); + return (-1); + } + + /* Allocate and read the directive into memory */ + if (n > dsize) { + if ((directive = realloc(directive, n + 1)) == NULL) { + close(fd); + return (-1); + } + dsize = n; + } + r = read(fd, directive, n); + + /* Advance beyond the equals sign if appropriate/desired */ + if (bequals && *p == '=') { + if (lseek(fd, 1, SEEK_CUR) != -1) + r = read(fd, p, 1); + if (strict_equals && isspace(*p)) + *p = '\n'; + } + + /* Terminate the string */ + directive[n] = '\0'; + + /* Convert directive to lower case before comparison */ + if (!case_sensitive) + strtolower(directive); + + /* Move to what may be the start of the value */ + if (!(bsemicolon && *p == ';') && + !(strict_equals && *p == '=')) { + while (r != 0 && isspace(*p) && *p != '\n') + r = read(fd, p, 1); + } + + /* An equals sign may have stopped us, should we eat it? */ + if (r != 0 && bequals && *p == '=' && !strict_equals) { + have_equals = 1; + r = read(fd, p, 1); + while (r != 0 && isspace(*p) && *p != '\n') + r = read(fd, p, 1); + } + + /* If no value, allocate a dummy value and jump to action */ + if (r == 0 || *p == '\n' || *p == '#' || + (bsemicolon && *p == ';')) { + /* Initialize the value if not already done */ + if (value == NULL && (value = malloc(1)) == NULL) { + close(fd); + return (-1); + } + value[0] = '\0'; + goto call_function; + } + + /* Get the current offset */ + curpos = lseek(fd, 0, SEEK_CUR) - 1; + if (curpos == -1) { + close(fd); + return (-1); + } + + /* Find the end of the value */ + quote = 0; + end = 0; + while (r != 0 && end == 0) { + /* Advance to the next character if we know we can */ + if (*p != '\"' && *p != '#' && *p != '\n' && + (!bsemicolon || *p != ';')) { + r = read(fd, p, 1); + continue; + } + + /* + * If we get this far, we've hit an end-key + */ + + /* Get the current offset */ + charpos = lseek(fd, 0, SEEK_CUR) - 1; + if (charpos == -1) { + close(fd); + return (-1); + } + + /* + * Go back so we can read the character before the key + * to check if the character is escaped (which means we + * should continue). + */ + error = (int)lseek(fd, -2, SEEK_CUR); + if (error == -3) { + close(fd); + return (-1); + } + r = read(fd, p, 1); + + /* + * Count how many backslashes there are (an odd number + * means the key is escaped, even means otherwise). + */ + for (n = 1; *p == '\\'; n++) { + /* Move back another offset to read */ + error = (int)lseek(fd, -2, SEEK_CUR); + if (error == -3) { + close(fd); + return (-1); + } + r = read(fd, p, 1); + } + + /* Move offset back to the key and read it */ + error = (int)lseek(fd, charpos, SEEK_SET); + if (error == (charpos - 1)) { + close(fd); + return (-1); + } + r = read(fd, p, 1); + + /* + * If an even number of backslashes was counted meaning + * key is not escaped, we should evaluate what to do. + */ + if ((n & 1) == 1) { + switch (*p) { + case '\"': + /* + * Flag current sequence of characters + * to follow as being quoted (hashes + * are not considered comments). + */ + quote = !quote; + break; + case '#': + /* + * If we aren't in a quoted series, we + * just hit an inline comment and have + * found the end of the value. + */ + if (!quote) + end = 1; + break; + case '\n': + /* + * Newline characters must always be + * escaped, whether inside a quoted + * series or not, otherwise they + * terminate the value. + */ + end = 1; + case ';': + if (!quote && bsemicolon) + end = 1; + break; + } + } else if (*p == '\n') + /* Escaped newline character. increment */ + line++; + + /* Advance to the next character */ + r = read(fd, p, 1); + } + + /* Get the current offset */ + charpos = lseek(fd, 0, SEEK_CUR) - 1; + if (charpos == -1) { + close(fd); + return (-1); + } + + /* Get the length of the value */ + n = (uint32_t)(charpos - curpos); + if (r != 0) /* more to read, but don't read ending key */ + n--; + + /* Move offset back to the beginning of the value */ + error = (int)lseek(fd, curpos, SEEK_SET); + if (error == (curpos - 1)) { + close(fd); + return (-1); + } + + /* Allocate and read the value into memory */ + if (n > vsize) { + if ((value = realloc(value, n + 1)) == NULL) { + close(fd); + return (-1); + } + vsize = n; + } + r = read(fd, value, n); + + /* Terminate the string */ + value[n] = '\0'; + + /* Cut trailing whitespace off by termination */ + t = value + n; + while (isspace(*--t)) + *t = '\0'; + + /* Escape the escaped quotes (replaceall is in string_m.c) */ + x = strcount(value, "\\\""); /* in string_m.c */ + if (x != 0 && (n + x) > vsize) { + if ((value = realloc(value, n + x + 1)) == NULL) { + close(fd); + return (-1); + } + vsize = n + x; + } + if (replaceall(value, "\\\"", "\\\\\"") < 0) { + /* Replace operation failed for some unknown reason */ + close(fd); + return (-1); + } + + /* Remove all new line characters */ + if (replaceall(value, "\\\n", "") < 0) { + /* Replace operation failed for some unknown reason */ + close(fd); + return (-1); + } + + /* Resolve escape sequences */ + strexpand(value); /* in string_m.c */ + +call_function: + /* Abort if we're seeking only assignments */ + if (require_equals && !have_equals) + return (-1); + + found = have_equals = 0; /* reset */ + + /* If there are no options defined, call unknown and loop */ + if (options == NULL && unknown != NULL) { + error = unknown(NULL, line, directive, value); + if (error != 0) { + close(fd); + return (error); + } + continue; + } + + /* Loop through the array looking for a match for the value */ + for (n = 0; options[n].directive != NULL; n++) { + error = fnmatch(options[n].directive, directive, + FNM_NOESCAPE); + if (error == 0) { + found = 1; + /* Call function for array index item */ + if (options[n].action != NULL) { + error = options[n].action( + &options[n], + line, directive, value); + if (error != 0) { + close(fd); + return (error); + } + } + } else if (error != FNM_NOMATCH) { + /* An error has occurred */ + close(fd); + return (-1); + } + } + if (!found && unknown != NULL) { + /* + * No match was found for the value we read from the + * file; call function designated for unknown values. + */ + error = unknown(NULL, line, directive, value); + if (error != 0) { + close(fd); + return (error); + } + } + } + + close(fd); + return (0); +} diff --git a/lib/libfigpar/figpar.h b/lib/libfigpar/figpar.h new file mode 100644 index 0000000..16f825a --- /dev/null +++ b/lib/libfigpar/figpar.h @@ -0,0 +1,99 @@ +/*- + * Copyright (c) 2002-2014 Devin Teske <dteske@FreeBSD.org> + * 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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. + * + * $FreeBSD$ + */ + +#ifndef _FIGPAR_H_ +#define _FIGPAR_H_ + +#include <sys/types.h> + +/* + * Union for storing various types of data in a single common container. + */ +union fp_cfgvalue { + void *data; /* Pointer to NUL-terminated string */ + char *str; /* Pointer to NUL-terminated string */ + char **strarray; /* Pointer to an array of strings */ + int32_t num; /* Signed 32-bit integer value */ + uint32_t u_num; /* Unsigned 32-bit integer value */ + uint32_t boolean:1; /* Boolean integer value (0 or 1) */ +}; + +/* + * Option types (based on above cfgvalue union) + */ +enum fp_cfgtype { + FP_TYPE_NONE = 0x0000, /* for directives with no value */ + FP_TYPE_BOOL = 0x0001, /* boolean */ + FP_TYPE_INT = 0x0002, /* signed 32 bit integer */ + FP_TYPE_UINT = 0x0004, /* unsigned 32 bit integer */ + FP_TYPE_STR = 0x0008, /* string pointer */ + FP_TYPE_STRARRAY = 0x0010, /* string array pointer */ + FP_TYPE_DATA1 = 0x0020, /* void data type-1 (whatever) */ + FP_TYPE_DATA2 = 0x0040, /* void data type-2 (whatever) */ + FP_TYPE_DATA3 = 0x0080, /* void data type-3 (whatever) */ + FP_TYPE_RESERVED1 = 0x0100, /* reserved data type-1 (future) */ + FP_TYPE_RESERVED2 = 0x0200, /* reserved data type-2 (future) */ + FP_TYPE_RESERVED3 = 0x0400, /* reserved data type-3 (future) */ +}; + +/* + * Options to parse_config() for processing_options bitmask + */ +#define FP_BREAK_ON_EQUALS 0x0001 /* stop reading directive at `=' */ +#define FP_BREAK_ON_SEMICOLON 0x0002 /* `;' starts a new line */ +#define FP_CASE_SENSITIVE 0x0004 /* directives are case sensitive */ +#define FP_REQUIRE_EQUALS 0x0008 /* assignment directives only */ +#define FP_STRICT_EQUALS 0x0010 /* `=' must be part of directive */ + +/* + * Anatomy of a config file option + */ +struct fp_config { + enum fp_cfgtype type; /* Option value type */ + const char *directive; /* config file keyword */ + union fp_cfgvalue value; /* NB: set by action */ + + /* + * Function pointer; action to be taken when the directive is found + */ + int (*action)(struct fp_config *option, uint32_t line, char *directive, + char *value); +}; +extern struct fp_config fp_dummy_config; + +__BEGIN_DECLS +int parse_config(struct fp_config _options[], + const char *_path, + int (*_unknown)(struct fp_config *_option, + uint32_t _line, char *_directive, char *_value), + uint16_t _processing_options); +struct fp_config *get_config_option(struct fp_config _options[], + const char *_directive); +__END_DECLS + +#endif /* _FIGPAR_H_ */ diff --git a/lib/libfigpar/string_m.c b/lib/libfigpar/string_m.c new file mode 100644 index 0000000..d358e90 --- /dev/null +++ b/lib/libfigpar/string_m.c @@ -0,0 +1,309 @@ +/*- + * Copyright (c) 2001-2014 Devin Teske <dteske@FreeBSD.org> + * 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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> +__FBSDID("$FreeBSD$"); + +#include <sys/types.h> + +#include <ctype.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "string_m.h" + +/* + * Counts the number of occurrences of one string that appear in the source + * string. Return value is the total count. + * + * An example use would be if you need to know how large a block of memory + * needs to be for a replaceall() series. + */ +unsigned int +strcount(const char *source, const char *find) +{ + const char *p = source; + size_t flen; + unsigned int n = 0; + + /* Both parameters are required */ + if (source == NULL || find == NULL) + return (0); + + /* Cache the length of find element */ + flen = strlen(find); + if (strlen(source) == 0 || flen == 0) + return (0); + + /* Loop until the end of the string */ + while (*p != '\0') { + if (strncmp(p, find, flen) == 0) { /* found an instance */ + p += flen; + n++; + } else + p++; + } + + return (n); +} + +/* + * Replaces all occurrences of `find' in `source' with `replace'. + * + * You should not pass a string constant as the first parameter, it needs to be + * a pointer to an allocated block of memory. The block of memory that source + * points to should be large enough to hold the result. If the length of the + * replacement string is greater than the length of the find string, the result + * will be larger than the original source string. To allocate enough space for + * the result, use the function strcount() declared above to determine the + * number of occurrences and how much larger the block size needs to be. + * + * If source is not large enough, the application will crash. The return value + * is the length (in bytes) of the result. + * + * When an error occurs, -1 is returned and the global variable errno is set + * accordingly. Returns zero on success. + */ +int +replaceall(char *source, const char *find, const char *replace) +{ + char *p; + char *t; + char *temp; + size_t flen; + size_t rlen; + size_t slen; + uint32_t n = 0; + + errno = 0; /* reset global error number */ + + /* Check that we have non-null parameters */ + if (source == NULL) + return (0); + if (find == NULL) + return (strlen(source)); + + /* Cache the length of the strings */ + slen = strlen(source); + flen = strlen(find); + rlen = replace ? strlen(replace) : 0; + + /* Cases where no replacements need to be made */ + if (slen == 0 || flen == 0 || slen < flen) + return (slen); + + /* If replace is longer than find, we'll need to create a temp copy */ + if (rlen > flen) { + temp = malloc(slen + 1); + if (errno != 0) /* could not allocate memory */ + return (-1); + strcpy(temp, source); + } else + temp = source; + + /* Reconstruct the string with the replacements */ + p = source; t = temp; /* position elements */ + + while (*t != '\0') { + if (strncmp(t, find, flen) == 0) { + /* found an occurrence */ + for (n = 0; replace && replace[n]; n++) + *p++ = replace[n]; + t += flen; + } else + *p++ = *t++; /* copy character and increment */ + } + + /* Terminate the string */ + *p = '\0'; + + /* Free the temporary allocated memory */ + if (temp != source) + free(temp); + + /* Return the length of the completed string */ + return (strlen(source)); +} + +/* + * Expands escape sequences in a buffer pointed to by `source'. This function + * steps through each character, and converts escape sequences such as "\n", + * "\r", "\t" and others into their respective meanings. + * + * You should not pass a string constant or literal to this function or the + * program will likely segmentation fault when it tries to modify the data. + * + * The string length will either shorten or stay the same depending on whether + * any escape sequences were converted but the amount of memory allocated does + * not change. + * + * Interpreted sequences are: + * + * \0NNN character with octal value NNN (0 to 3 digits) + * \N character with octal value N (0 thru 7) + * \a alert (BEL) + * \b backslash + * \f form feed + * \n new line + * \r carriage return + * \t horizontal tab + * \v vertical tab + * \xNN byte with hexadecimal value NN (1 to 2 digits) + * + * All other sequences are unescaped (ie. '\"' and '\#'). + */ +void strexpand(char *source) +{ + uint8_t c; + char *chr; + char *pos; + char d[4]; + + /* Initialize position elements */ + pos = chr = source; + + /* Loop until we hit the end of the string */ + while (*pos != '\0') { + if (*chr != '\\') { + *pos = *chr; /* copy character to current offset */ + pos++; + chr++; + continue; + } + + /* Replace the backslash with the correct character */ + switch (*++chr) { + case 'a': *pos = '\a'; break; /* bell/alert (BEL) */ + case 'b': *pos = '\b'; break; /* backspace */ + case 'f': *pos = '\f'; break; /* form feed */ + case 'n': *pos = '\n'; break; /* new line */ + case 'r': *pos = '\r'; break; /* carriage return */ + case 't': *pos = '\t'; break; /* horizontal tab */ + case 'v': *pos = '\v'; break; /* vertical tab */ + case 'x': /* hex value (1 to 2 digits)(\xNN) */ + d[2] = '\0'; /* pre-terminate the string */ + + /* verify next two characters are hex */ + d[0] = isxdigit(*(chr+1)) ? *++chr : '\0'; + if (d[0] != '\0') + d[1] = isxdigit(*(chr+1)) ? *++chr : '\0'; + + /* convert the characters to decimal */ + c = (uint8_t)strtoul(d, 0, 16); + + /* assign the converted value */ + *pos = (c != 0 || d[0] == '0') ? c : *++chr; + break; + case '0': /* octal value (0 to 3 digits)(\0NNN) */ + d[3] = '\0'; /* pre-terminate the string */ + + /* verify next three characters are octal */ + d[0] = (isdigit(*(chr+1)) && *(chr+1) < '8') ? + *++chr : '\0'; + if (d[0] != '\0') + d[1] = (isdigit(*(chr+1)) && *(chr+1) < '8') ? + *++chr : '\0'; + if (d[1] != '\0') + d[2] = (isdigit(*(chr+1)) && *(chr+1) < '8') ? + *++chr : '\0'; + + /* convert the characters to decimal */ + c = (uint8_t)strtoul(d, 0, 8); + + /* assign the converted value */ + *pos = c; + break; + default: /* single octal (\0..7) or unknown sequence */ + if (isdigit(*chr) && *chr < '8') { + d[0] = *chr; + d[1] = '\0'; + *pos = (uint8_t)strtoul(d, 0, 8); + } else + *pos = *chr; + } + + /* Increment to next offset, possible next escape sequence */ + pos++; + chr++; + } +} + +/* + * Expand only the escaped newlines in a buffer pointed to by `source'. This + * function steps through each character, and converts the "\n" sequence into + * a literal newline and the "\\n" sequence into "\n". + * + * You should not pass a string constant or literal to this function or the + * program will likely segmentation fault when it tries to modify the data. + * + * The string length will either shorten or stay the same depending on whether + * any escaped newlines were converted but the amount of memory allocated does + * not change. + */ +void strexpandnl(char *source) +{ + uint8_t backslash = 0; + char *cp1; + char *cp2; + + /* Replace '\n' with literal in dprompt */ + cp1 = cp2 = source; + while (*cp2 != '\0') { + *cp1 = *cp2; + if (*cp2 == '\\') + backslash++; + else if (*cp2 != 'n') + backslash = 0; + else if (backslash > 0) { + *(--cp1) = (backslash & 1) == 1 ? '\n' : 'n'; + backslash = 0; + } + cp1++; + cp2++; + } + *cp1 = *cp2; +} + +/* + * Convert a string to lower case. You should not pass a string constant to + * this function. Only pass pointers to allocated memory with null terminated + * string data. + */ +void +strtolower(char *source) +{ + char *p = source; + + if (source == NULL) + return; + + while (*p != '\0') { + *p = tolower(*p); + p++; /* would have just used `*p++' but gcc 3.x warns */ + } +} diff --git a/lib/libfigpar/string_m.h b/lib/libfigpar/string_m.h new file mode 100644 index 0000000..18a3fcc --- /dev/null +++ b/lib/libfigpar/string_m.h @@ -0,0 +1,43 @@ +/*- + * Copyright (c) 2001-2014 Devin Teske <dteske@FreeBSD.org> + * 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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. + * + * $FreeBSD$ + */ + +#ifndef _STRING_M_H_ +#define _STRING_M_H_ + +#include <sys/cdefs.h> + +__BEGIN_DECLS +void strexpand(char *_source); +void strexpandnl(char *_source); +void strtolower(char *_source); +int replaceall(char *_source, const char *_find, + const char *_replace); +unsigned int strcount(const char *_source, const char *_find); +__END_DECLS + +#endif /* !_STRING_M_H_ */ |