/*
#ident "@(#)smail/src:RELEASE-3_2_0_121:parse.c,v 1.34 2005/07/06 23:56:41 woods Exp"
*/
/*
* Copyright (C) 1987, 1988 by Ronald S. Karr and Landon Curt Noll
* Copyright (C) 1992 Ronald S. Karr
*
* See the file COPYING, distributed with smail, for restriction
* and warranty information.
*/
/*
* parse.c: (XXX this source file should be renames parsecfg.c!)
* Parse configuration files in a standard way.
*
* The directory, router and transport files all share a common format
* which are parsed using routines in this file. Although the format is
* slightly different, the rules for lexical tokens in the config files,
* as well as those for the method, retry, and qualify tables, are very
* much the same, so routines for parsing these files are provided as
* well. The basic read_entry() and skip_space() routines are also used
* by the "lsearch" database lookup protocol, as well as the queryprog
* router driver.
*
* external functions: parse_entry, parse_config, parse_table,
* read_entry, skip_space.
*/
#include "defs.h"
#ifdef STANDALONE
# define xmalloc malloc
# define xrealloc realloc
# define xfree free
#endif /* STANDALONE */
#include <sys/types.h>
#include <stdio.h>
#include <errno.h>
#include <ctype.h>
#ifdef STDC_HEADERS
# include <stdlib.h>
# include <stddef.h>
#else
# ifdef HAVE_STDLIB_H
# include <stdlib.h>
# endif
#endif
#ifdef HAVE_STRING_H
# if !defined(STDC_HEADERS) && defined(HAVE_MEMORY_H)
# include <memory.h>
# endif
# include <string.h>
#endif
#ifdef HAVE_STRINGS_H
# include <strings.h>
#endif
#ifdef __STDC__
# include <stdarg.h>
#else
# include <varargs.h>
#endif
#if defined(HAVE_UNISTD_H)
# include <unistd.h>
#endif
#include "smail.h"
#include "alloc.h"
#include "list.h"
#include "main.h"
#include "parse.h"
#include "addr.h"
#include "log.h"
#include "smailstring.h"
#include "dys.h"
#include "exitcodes.h"
#include "debug.h"
#include "extern.h"
#include "smailport.h"
/* variables exported from this file */
char *on = "1"; /* boolean on attribute value */
char *off = "0"; /* boolean off attribute value */
/* functions local to this file */
static char *finish_parse __P((char *, struct attribute **, struct attribute **));
/*
* parse_entry - parse an entry from director, router or transport file
*
* given an entry from the given file, parse that entry, returning a vector
* containing the NUL-terminated name of the entry followed by the name/value
* pairs for all of the attributes in the entry. Names which fit the form of a
* boolean attribute will return a pointer to one of the external variables
* "on" or "off".
*
* Returns the name of the entry on a successful read. Returns NULL
* on end of file or error. For an error, a message is returned as an
* error string. Calling xfree on the name is sufficient to free all
* of the string storage. To free the list entries, step through the
* lists and free each in turn.
*
* Note this code is very similar to the code in parse_config().
*/
char *
parse_entry(entry, generic, driver, error)
register char *entry; /* entry string to be parsed */
struct attribute **generic; /* all generic attributes */
struct attribute **driver; /* all driver attributes */
char **error; /* returned error message */
{
char *start;
struct str str; /* string for processing entry */
int this_attribute; /* offset of attribute name */
DEBUG2(DBG_CONF_HI, "parse_entry(): about to process entry that begins with: \n\t'%V'\n", (size_t) 70, entry);
/* skip leading whitespace and comments */
entry = skip_space(entry);
*error = NULL; /* no error yet! */
start = entry;
STR_INIT(&str);
/*
* grab the entry name as a collection of characters followed
* by optional white space followed by a `:'
*/
while (*entry && !isspace((int) *entry) && *entry != ':' && *entry != '#') {
STR_NEXT(&str, *entry++);
}
STR_NEXT(&str, '\0'); /* NUL-terminate with the name */
if (STR_LEN(&str) == 0) {
*error = xprintf("missing field name (after entry beginning with \"%Q\")", (size_t) 40, start);
DEBUG1(DBG_CONF_LO, "parse_entry:() %s\n", *error);
STR_FREE(&str);
return NULL;
}
/* skip spaces and comments after the name */
entry = skip_space(entry);
if (*entry != ':') {
*error = "field name does not end in `:'";
DEBUG1(DBG_CONF_LO, "parse_entry:() %s\n", *error);
STR_FREE(&str);
return NULL;
}
/* skip the `:' */
entry++;
/*
* loop grabbing attributes and values until the end of
* the entry
*
* XXX this code is very similar to the code below in parse_config()
*/
while (*entry) {
size_t name_offset; /* temp */
int boolean_flag; /* `+' `-' or SPACE */
boolean_flag = ' ';
start = entry;
/* skip spaces and comments before the attribute name (or `;') */
entry = skip_space(entry);
/* a semicolon separates generic and driver attributes */
if (*entry == ';') {
/* skip spaces and comments after the semicolon */
entry = skip_space(entry + 1);
/* and if there's still anything afterward, record the semicolon */
if (*entry) {
STR_NEXT(&str, ';');
}
}
/*
* be lenient about a `;' (or `,' from last go-around) with no
* following attributes
*/
if (*entry == '\0') {
break;
}
/* remember where this attribute setting starts in the parsed copy */
this_attribute = STR_LEN(&str);
/* if it's a boolean attribute then copy the value verbatim */
if (*entry == '+' || *entry == '-') {
boolean_flag = *entry;
STR_NEXT(&str, *entry);
/* skip any spaces and comments after the flag */
entry = skip_space(entry + 1);
}
if (*entry == '\0' || *entry == ',' || *entry == ';') {
*error = xprintf("in entry %v: missing attribute name%s, in text beginning with \"%Q\"",
STR(&str),
boolean_flag != ' ' ? " after boolean flag" : "",
(size_t) 40, start);
DEBUG1(DBG_CONF_LO, "parse_entry:() %s\n", *error);
STR_FREE(&str);
return NULL;
}
/*
* scan over the attribute name and copy it to str
*
* an attribute name is of the form [+-]?[A-Za-z0-9_-]+
*
* XXX should probably require it to start with isalpha()...
*/
name_offset = STR_LEN(&str);
while (*entry && (isalnum((int) *entry) || *entry == '_' || *entry == '-')) {
STR_NEXT(&str, *entry++);
}
if (STR_LEN(&str) == name_offset) {
*error = xprintf("missing variable name%s, in entry beginning with \"%Q\"",
boolean_flag != ' ' ? " after boolean flag" : "",
(size_t) 40, start);
DEBUG1(DBG_CONF_LO, "parse_config:() %s\n", *error);
return NULL;
}
STR_NEXT(&str, '\0'); /* terminate the attribute name */
/* skip any spaces and comments after the end of the name */
entry = skip_space(entry);
if (*entry == '\0' || *entry == ',' || *entry == ';') {
/* end of a boolean attribute */
if (*entry == ',') {
/* skip over commas */
entry++;
}
continue;
}
if (*entry != '=') {
/* not boolean form and not "name = value" form */
*error = xprintf("in entry %v: attribute %v: expected `=' after the variable name",
STR(&str),
STR(&str) + this_attribute);
DEBUG1(DBG_CONF_LO, "parse_entry:() %s\n", *error);
STR_FREE(&str);
return NULL;
}
if (boolean_flag != ' ') {
/*
* if we found a leading `+' or `-' then there shouldn't be an
* assignment, but there was....
*/
*error = xprintf("in entry %v: attribute %v: unexpected pattern `= value' follows boolean variable",
STR(&str),
STR(&str) + this_attribute);
DEBUG1(DBG_CONF_LO, "parse_entry:() %s\n", *error);
STR_FREE(&str);
return NULL;
}
/* note that this is a attribute value assignment */
STR_NEXT(&str, '=');
/* skip any spaces and comments after the '=' */
entry = skip_space(entry + 1);
if (*entry == '"') {
entry++; /* skip the opening " */
/*
* if a quote, skip to the closing quote, following standard
* C convention with \-escapes. Note that read_entry will
* have already done some processing for \ chars at the end of
* input lines.
*/
while (*entry && *entry != '"') {
if (*entry == '\\') {
int c;
entry = c_dequote(entry + 1, &c);
STR_NEXT(&str, c);
} else {
STR_NEXT(&str, *entry++);
}
}
if (*entry != '"') {
/*
* make sure that the string doesn't suddenly come
* to an end at a funny spot
*/
*error = xprintf("in entry %v: attribute %v: missing closing quote for quoted value",
STR(&str),
STR(&str) + this_attribute);
DEBUG1(DBG_CONF_LO, "parse_entry:() %s\n", *error);
STR_FREE(&str);
return NULL;
}
entry++; /* skip that closing '"' */
} else {
/*
* not in double quotes, only a limited set of characters
* are allowed in an unquoted string, though \ quotes any
* character.
*/
while (*entry && (*entry == '\\' ||
strchr("!@$%^&*-_+~/?|<>:[]{}().`'", *entry) ||
isalnum((int) *entry)))
{
if (*entry == '\\') {
entry++;
if (*entry == '\0') {
/* must have something after \ */
*error = xprintf("in entry %v: attribute %v: unexpected end of unquoted value after a backslash",
STR(&str),
STR(&str) + this_attribute);
DEBUG1(DBG_CONF_LO, "parse_entry:() %s\n", *error);
STR_FREE(&str);
return NULL;
}
}
STR_NEXT(&str, *entry++);
}
/* note we check the proper termination below.... */
}
STR_NEXT(&str, '\0'); /* close off the value */
/* skip any spaces and comments after the value */
entry = skip_space(entry);
/*
* make sure the entry ends in something reasonable
*/
if (*entry == ',') {
entry++; /* commas are okay, and are ignored */
} else if (*entry != ';') {
; /* semicolons are OK too, but are not ignored */
} else if (*entry != '\0') {
/* anything else is not OK */
*error = xprintf("in entry %v: attribute %v: invalid attribute separator (%c)",
STR(&str),
STR(&str) + this_attribute,
*entry);
DEBUG1(DBG_CONF_LO, "parse_entry:() %s\n", *error);
STR_FREE(&str);
return NULL;
}
}
STR_NEXT(&str, '\0'); /* two NUL bytes signal the entry's end */
STR_DONE(&str); /* finish off the string */
/*
* turn the entry, after the entry name, into the finished attribute lists
*/
*error = finish_parse(STR(&str) + strlen(STR(&str)) + 1, generic, driver);
if (*error) {
DEBUG1(DBG_CONF_LO, "parse_entry:() finish_parse(): %s\n", *error);
STR_FREE(&str);
return NULL; /* error found in finish_parse */
}
return STR(&str); /* entry name was first */
}
/*
* finish_parse - turn NUL-separated token strings into an attribute list
*
* return an error message or NULL, return generic and driver attributes
* in the appropriate passed list pointers.
*/
static char *
finish_parse(tokens, generic, driver)
register char *tokens; /* strings of nul-terminated tokens */
struct attribute **generic; /* generic attributes go here */
struct attribute **driver; /* driver attributes go here */
{
struct attribute **attr = generic; /* begin adding generic attributes */
*generic = NULL;
*driver = NULL;
/*
* loop, snapping up tokens until no more remain
*/
while (*tokens) {
struct attribute *new;
if (*tokens == ';') {
/* after `;' parse driver attributes */
attr = driver;
tokens++; /* otherwise ignore `;' */
}
/*
* get a new token and link it into the output list
*/
new = (struct attribute *) xmalloc(sizeof(*new));
new->succ = *attr;
(*attr) = new;
/* fill in the name */
new->name = tokens;
/* step to the next token */
tokens = tokens + strlen(tokens) + 1;
/* check for boolean attribute form */
if (new->name[0] == '-' || new->name[0] == '+') {
/* boolean value */
if (*tokens == '=') {
/* XXX haven't we already checked for this? */
/* can't have both [+-] and a value */
/* note new attribute is already linked into the list */
return "mixed [+-]attribute and value assignment";
}
/*
* -name turns off attribute, +name turns it on
*/
if (new->name[0] == '-') {
new->value = off;
} else {
new->value = on;
}
new->name++; /* don't need [+-] anymore */
} else {
if (*tokens == '=') {
/* value token for attribute */
new->value = tokens + 1; /* don't include `=' in the value */
/* advance to the next token */
tokens = tokens + strlen(tokens) + 1;
} else {
/* just name is equivalent to +name */
new->value = on;
}
}
}
return NULL;
}
/*
* parse_config - parse config file name/value pairs
*
* given a string, such as returned by read_entry, turn it into a single
* attribute entry with the "name" field pointing at the start of the config
* variable name and with the "value" field pointing at the variable's value.
* Both are pointing at the same allocated storage block and can be freed by
* passing the "name" pointer to xfree().
*
* On error, return NULL, with an error message in *error.
*
* XXX this code is very similar to the inner loop in parse_entry() above...
*/
struct attribute *
parse_config(entry, error)
register char *entry; /* config from read_entry */
char **error; /* return error message */
{
char *start = entry;
struct str str; /* area for building result */
int boolean_flag = ' '; /* `+' `-' or SPACE */
int value_offset; /* offset in STR(&str) of value */
struct attribute *attr; /* new variable name/value struct */
DEBUG2(DBG_CONF_HI, "parse_config(): about to process entry that begins with:\n\t'%V'\n", (size_t) 70, entry);
/* variable setting begins at next non-white space character */
entry = skip_space(entry);
/* is this a boolean variable setting? */
if (*entry == '+' || *entry == '-') {
boolean_flag = *entry;
/* skip any spaces and comments after the flag */
entry = skip_space(entry + 1);
}
if (*entry == '\0') {
*error = xprintf("missing variable name%s, in entry beginning with \"%Q\"",
boolean_flag != ' ' ? " after boolean flag" : "",
(size_t) 40, start);
DEBUG1(DBG_CONF_LO, "parse_config:() %s\n", *error);
return NULL;
}
attr = (struct attribute *) xmalloc(sizeof(*attr));
attr->succ = NULL;
STR_INIT(&str);
/*
* scan over the variable name and copy it to str
*
* we assume there's at least one valid non-space character here
*
* attribute name is of the form [A-Za-z0-9_-]+
*
* XXX should probably require it to start with isalpha()...
*/
while (*entry && (isalnum((int) *entry) || *entry == '_' || *entry == '-')) {
STR_NEXT(&str, *entry++);
}
if (STR_LEN(&str) == 0) {
*error = xprintf("missing variable name%s, in entry beginning with \"%Q\"",
boolean_flag != ' ' ? " after boolean flag" : "",
(size_t) 40, start);
DEBUG1(DBG_CONF_LO, "parse_config:() %s\n", *error);
return NULL;
}
STR_NEXT(&str, '\0'); /* terminate variable name */
/* skip any spaces and comments after the end of the name */
entry = skip_space(entry);
if (*entry == '\0') {
/* boolean variable */
STR_DONE(&str);
attr->name = STR(&str);
if (boolean_flag == '-') {
attr->value = off;
} else {
attr->value = on;
}
return attr;
}
/* not boolean form and not "name = value" form */
if (*entry != '=') {
*error = xprintf("variable %v: expected `=' after the variable name", STR(&str));
DEBUG1(DBG_CONF_LO, "parse_config:() %s\n", *error);
xfree((char *) attr);
STR_FREE(&str);
return NULL;
}
if (boolean_flag != ' ') {
/*
* if we found a leading `+' or `-' then there shouldn't be an
* assignment, but there was....
*/
*error = xprintf("variable %s: unexpected pattern `= value' follows a boolean variable", STR(&str));
DEBUG1(DBG_CONF_LO, "parse_config:() %s\n", *error);
xfree((char *) attr);
STR_FREE(&str);
return NULL;
}
/* skip any spaces after the '=' */
entry = skip_space(entry + 1);
/* form is `name = value', remember the start position of the value */
value_offset = STR_LEN(&str);
/*
* XXX this code is very nearly a duplicate of the code above in parse_entry()
*/
if (*entry == '"') {
entry++; /* skip the opening " */
/*
* if a quote, skip to the closing quote, following standard
* C convention with \-escapes. Note that read_entry will
* have already done some processing for \ chars at the end of
* input lines.
*/
while (*entry && *entry != '"') {
if (*entry == '\\') {
int c;
entry = c_dequote(entry + 1, &c);
STR_NEXT(&str, c);
} else {
STR_NEXT(&str, *entry++);
}
}
if (*entry != '"') {
/*
* make sure that the string doesn't suddenly come
* to an end at a funny spot, normally it will be a NUL.
*/
*error = xprintf("variable %v: missing closing quote for quoted value", STR(&str));
DEBUG1(DBG_CONF_LO, "parse_config:() %s\n", *error);
xfree((char *) attr);
STR_FREE(&str);
return NULL;
}
entry++; /* skip that closing '"' */
} else {
/*
* not in double quotes, only a limited set of characters are allowed
* in an unquoted variable string (more than in an attribute string),
* though backslash (\) still quotes any character.
*
* Note too that in a config file we allow continuation of variable
* values without a trailing backslash.
*/
while (*entry && (*entry == '\\' || *entry == '\n' ||
strchr(" \t!@$%^&*-_=+~/?|<>;:[]{}(),.`'", *entry) || /* XXX " \t;,=" added */
isalnum((int) *entry)))
{
if (*entry == '\n') {
entry = skip_space(entry);
if (*entry == '\0') {
break; /* normal end of entry... */
}
}
if (*entry == '\\') {
entry++;
if (*entry == '\0') {
/* must have something after \ */
*error = xprintf("variable %v: unexpected end of unquoted value after a backslash", STR(&str));
DEBUG1(DBG_CONF_LO, "parse_config:() %s\n", *error);
xfree((char *) attr);
STR_FREE(&str);
return NULL;
}
}
STR_NEXT(&str, *entry++);
}
if (*entry != '\0') {
*error = xprintf("variable %v: unexpected, unescaped, special character '%c' in value, next 40 bytes of remaining text: \"%Q\"",
STR(&str),
*entry,
(size_t) 40, entry);
DEBUG1(DBG_CONF_LO, "parse_config:() %s\n", *error);
xfree((char *) attr);
STR_FREE(&str);
return NULL;
}
}
STR_NEXT(&str, '\0'); /* close off the string after the value */
/*
* make sure this is really the end of the entry, first eating any trailing
* whitespace and comments that would be ignored anyway....
*/
entry = skip_space(entry);
if (*entry != '\0') {
*error = xprintf("variable %v: unexpected data after end of entry, next 40 bytes of remaining text: \"%Q\"",
STR(&str),
(size_t) 40, entry);
DEBUG1(DBG_CONF_LO, "parse_config:() %s\n", *error);
xfree((char *) attr);
STR_FREE(&str);
return NULL;
}
STR_DONE(&str);
attr->name = STR(&str);
attr->value = STR(&str) + value_offset;
DEBUG3(DBG_CONF_HI, "parse_config(): got var `%v', with value that begins with:\n\t'%V'\n",
attr->name,
(size_t) 70, attr->value);
return attr;
}
/*
* parse_table - parse an entry in a table file
*
* table files have entries of the form:
*
* string1 string2
*
* the returned "struct attribute" its "name" field pointing at the beginning
* of string1 and its "value" field pointing at the beginning of string2. Both
* are pointing at the same allocated storage block and can be freed by passing
* the "name" pointer to xfree().
*/
struct attribute *
parse_table(entry, error)
register char *entry; /* config from read_entry */
char **error; /* return error message */
{
struct attribute *attr = (struct attribute *)xmalloc(sizeof(*attr));
struct str str;
int offset_transport; /* offset to transport in STR(&str) */
attr->succ = NULL;
STR_INIT(&str);
entry = skip_space(entry);
while (*entry && !isspace((int) *entry) && *entry != '#') {
STR_NEXT(&str, *entry++);
}
STR_NEXT(&str, '\0'); /* terminate name of host */
entry = skip_space(entry);
if (*entry == '\0') {
*error = "unexpected end of entry";
STR_FREE(&str);
return NULL;
}
offset_transport = STR_LEN(&str);
while (*entry && !isspace((int) *entry) && *entry != '#') {
STR_NEXT(&str, *entry++);
}
STR_NEXT(&str, '\0'); /* terminate name of transport */
entry = skip_space(entry);
if (*entry) {
*error = "expected end of entry";
STR_FREE(&str);
return NULL;
}
STR_DONE(&str);
attr->name = STR(&str);
attr->value = STR(&str) + offset_transport;
return attr;
}
/*
* skip_space - skip over comments and white space
*
* a comment is a `#' up to the end of a line
*/
char *
skip_space(p)
register char *p; /* current place in string */
{
for (;;) {
if (*p == '#') {
/* skip over comment */
p++;
while (*p && *p != '\n') {
p++;
}
} else if (!isspace((int) *p)) {
/* found something that isn't white space, return it */
return p;
} else {
p++; /* advance past the white-space char */
}
}
}
/*
* read_entry - read a standard, possibly multi-line, entry from a file
*
* A "standard" entry, e.g. from a config, director, router, or transport
* configuration file, or an entry from an alias file, is terminated by a line
* which does not begin with whitespace.
*
* All comments are included in the returned text -- only "\\\n[ ]*" (escaped
* newlines followed by optional whitespace) are stripped. All comments at the
* end of an entry are considered to be at the beginning of the next entry.
*
* return NULL on end of file or error. The region return may be
* reused for subsequent return values and should be copied if it
* is to be preserved.
*
* XXX maybe we should try to count newlines and provide the line number of the
* last line read. That means we also need to keep track of the line number at
* the trailing comment file position. Note however that parse_config() and
* similar routines deal with entries as single objects and so there will still
* be no way to determine the actual line number of a parsing error in the
* middle of an entry. Perhaps the current technique of reporting the entry's
* "name", as well as text leading up to (or following, as appropriate) the
* error is good enough for users to find and understand the error in their raw
* file format.
*/
char *
read_entry(f, infn)
register FILE *f; /* input file */
char *infn; /* input file name */
{
register int c; /* current character */
static struct str str; /* build the entry here */
static int inited = FALSE; /* TRUE if str has been STR_INIT'd */
unsigned int ptcomment = 0; /* possible trailing comment offset */
fpos_t ptcompos; /* trailing comment file position */
/*
* Note, that str is initialized only once and then reused.
*/
if (!inited) {
inited = TRUE;
STR_INIT(&str);
} else {
STR_CHECK(&str);
STR_CLEAR(&str);
}
/*
* scan for the beginning of an entry, which begins at the first
* non-white space, non-comment character
*
* We do this in a separate loop since it makes the parsing of the entry
* itself (to find its end) much easier (and also partly because once upon
* a time this code skipped leading comments instead of including them in
* the entry).
*/
while ((c = getc(f)) != EOF && (isspace((int) c) || c == '#')) {
STR_NEXT(&str, c);
if (c == '#') {
while ((c = getc(f)) != EOF && c != '\n') {
STR_NEXT(&str, c);
}
if (c == EOF) {
break;
}
STR_NEXT(&str, c); /* include the end-of-comment */
}
}
/*
* no entry was found
*/
if (c == EOF) {
return NULL;
}
STR_NEXT(&str, c);
/*
* continue copying characters up to the end of the entry.
*/
while ((c = getc(f)) != EOF) {
if (c == '\n') {
STR_NEXT(&str, c);
/*
* peek ahead to see what the next line starts with
*/
c = getc(f);
/*
* end-of-file, or a line beginning with non-white space, marks the
* end of the current entry.
*/
if (c == '\n' || c == '#') {
/* blank lines and comments don't count */
(void) ungetc(c, f); /* unpeek */
/*
* but we do want to remember where the blank lines or comments
* start in case this turns out to be the end of the entry so
* that we can trim them off and back up to read them as part
* of the next entry....
*
* Ideally we would like to trim only from a blank line (two
* consecutive newlines) onwards, i.e. consider comments
* "attached" to the end of the entry to be part of the entry,
* but the logic to do that gets a bit too complicated to be
* worthwhile.
*/
if (!ptcomment) {
ptcomment = STR_LEN(&str);
if (fgetpos(f, &ptcompos) != 0) {
panic(EX_OSFILE, "fgetpos(%s): %s", infn, strerror(errno));
}
}
continue; /* keep reading */
}
if (c == EOF || (c != ' ' && c != '\t')) {
/* indeed this is the end of the entry! */
break;
} else if (c != '#') { /* XXX does this do anything? can it? */
ptcomment = 0;
}
}
if (c == '\\') {
/* \newline is swallowed along with any following white-space */
if ((c = getc(f)) == EOF) {
break;
}
if (c == '\n') {
while ((c = getc(f)) == ' ' || c == '\t') {
;
}
} else {
STR_NEXT(&str, '\\');
}
}
STR_NEXT(&str, c);
}
/*
* that's the end of that entry
*/
if (c != EOF) {
(void) ungetc(c, f); /* first character for the next entry? */
}
/*
* trim off any trailing blank lines and/or comments and back up to read
* them as part of the next entry
*/
if (ptcomment) {
STR_TRIM(&str, ptcomment);
if (c != EOF) {
if (fsetpos(f, &ptcompos) != 0) {
panic(EX_OSFILE, "fsetpos(%s): %s", infn, strerror(errno));
}
}
}
STR_NEXT(&str, '\0'); /* end of the entry */
return STR(&str);
}
#ifdef STANDALONE
/*
* read from standard input and write out the compiled
* entry information on the standard output
*/
void
main(argc, argv)
int argc; /* count of arguments */
char *argv[]; /* vector of arguments */
{
char *entry; /* entry read from stdin */
enum { config, table, other } file_type; /* type of file to look at */
if (argc >= 2 && EQ(argv[1], "-c")) {
file_type = config;
} else if (argc >= 2 && EQ(argv[1], "-t")) {
file_type = table;
} else {
file_type = other;
}
/*
* read entries until EOF
*/
while (entry = read_entry(stdin, "stdin")) {
if (file_type == config) {
char *error;
struct attribute *attr = parse_config(entry, &error);
if (attr == NULL) {
(void) fprintf(stderr, "error in <stdin>: %s\n", error);
exit(EX_DATAERR);
}
(void) printf("%s = %s\n", attr->name, quote(attr->value));
} else if (file_type == table) {
char *error;
struct attribute *attr = parse_table(entry, &error);
if (attr == NULL) {
(void) fprintf(stderr, "error in <stdin>: %s\n", error);
exit(EX_DATAERR);
}
(void) printf("%s = %s\n", attr->name, quote(attr->value));
} else {
struct attribute *generic; /* generic attribute list */
struct attribute *driver; /* driver attribute list */
char *error; /* error message */
char *name = parse_entry(entry, &generic, &driver, &error);
if (name == NULL) {
(void) fprintf(stderr, "error in <stdin>: %s\n", error);
exit(EX_DATAERR);
}
(void) printf("Entry Name: %s:\n Generic Attributes:\n", name);
while (generic) {
(void) printf("\t%s = %s\n", generic->name, quote(generic->value));
generic = generic->succ;
}
(void) printf(" Driver Attributes:\n");
while (driver) {
(void) printf("\t%s = %s\n", driver->name, quote(driver->value));
driver = driver->succ;
}
}
}
exit(EX_OK);
}
#endif /* STANDALONE */
/*
* Local Variables:
* c-file-style: "smail"
* End:
*/
syntax highlighted by Code2HTML, v. 0.9.1