This brings our tree to NetBSD 7.0, as found on -current on the 10-10-2015. This updates: - LLVM to 3.6.1 - GCC to GCC 5.1 - Replace minix/commands/zdump with usr.bin/zdump - external/bsd/libelf has moved to /external/bsd/elftoolchain/ - Import ctwm - Drop sprintf from libminc Change-Id: I149836ac18e9326be9353958bab9b266efb056f0
361 lines
9.0 KiB
C
361 lines
9.0 KiB
C
/* $NetBSD: parser.c,v 1.5 2015/08/08 12:34:33 shm Exp $ */
|
|
|
|
/* Copyright (c) 2010 The NetBSD Foundation, Inc.
|
|
* All rights reserved.
|
|
*
|
|
* This code is derived from software contributed to The NetBSD Foundation
|
|
* by Mateusz Kocielski.
|
|
*
|
|
* 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 the NetBSD
|
|
* Foundation, Inc. and its contributors.
|
|
* 4. Neither the name of The NetBSD Foundation nor the names of its
|
|
* contributors may be used to endorse or promote products derived
|
|
* from this software without specific prior written permission.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. 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 FOUNDATION 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>
|
|
__RCSID("$NetBSD: parser.c,v 1.5 2015/08/08 12:34:33 shm Exp $");
|
|
|
|
#include <sys/stat.h>
|
|
#include <sys/syslimits.h> /* for PATH_MAX */
|
|
|
|
#include <ctype.h>
|
|
#include <err.h>
|
|
#include <errno.h>
|
|
#include <saslc.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "dict.h"
|
|
#include "msg.h"
|
|
#include "parser.h"
|
|
#include "saslc_private.h"
|
|
|
|
#define SASLC__COMMENT_CHAR '#'
|
|
|
|
/* config file location defines */
|
|
#define SASLC__CONFIG_PATH "/etc/saslc.d"
|
|
#define SASLC__CONFIG_MAIN_FILE "saslc"
|
|
#define SASLC__CONFIG_MECH_DIRECTORY "mech"
|
|
#define SASLC__CONFIG_SUFFIX ".conf"
|
|
#define SASLC__DEFAULT_APPNAME "saslc"
|
|
|
|
/* token types */
|
|
enum {
|
|
TOKEN_KEY, /* option (key) */
|
|
TOKEN_STRING, /* quoted string */
|
|
TOKEN_NUM, /* number */
|
|
TOKEN_COMMENT, /* comment character */
|
|
TOKEN_UNKNOWN /* unknown */
|
|
};
|
|
|
|
/* token structure */
|
|
typedef struct saslc__token_t {
|
|
int type; /**< token type */
|
|
char *val; /**< token string value */
|
|
} saslc__token_t;
|
|
|
|
static inline char *
|
|
skip_WS(char *p)
|
|
{
|
|
|
|
while (*p == ' ' || *p == '\t')
|
|
p++;
|
|
return p;
|
|
}
|
|
|
|
/**
|
|
* @brief gets token from string c and updates pointer position.
|
|
* @param c pointer to string
|
|
* @return token on success, NULL on failure (e.g. at end of string).
|
|
* On success, c is updated to point on next token. It's position is
|
|
* undefined on failure.
|
|
*
|
|
* Note: A legal key begins with an isalpha(3) character and is
|
|
* followed by isalnum(3) or '_' characters.
|
|
*/
|
|
static saslc__token_t *
|
|
saslc__parse_get_token(char **c)
|
|
{
|
|
saslc__token_t *token;
|
|
char *e;
|
|
|
|
*c = skip_WS(*c);
|
|
if (**c == '\0')
|
|
return NULL;
|
|
|
|
if ((token = calloc(1, sizeof(*token))) == NULL)
|
|
return NULL;
|
|
|
|
token->val = *c;
|
|
|
|
if (**c == SASLC__COMMENT_CHAR)
|
|
token->type = TOKEN_COMMENT;
|
|
|
|
else if (**c == '\"')
|
|
token->type = TOKEN_STRING;
|
|
|
|
else if (isdigit((unsigned char)**c))
|
|
token->type = TOKEN_NUM;
|
|
|
|
else if (isalpha((unsigned char)**c))
|
|
token->type = TOKEN_KEY;
|
|
|
|
else
|
|
token->type = TOKEN_UNKNOWN;
|
|
|
|
switch (token->type) {
|
|
case TOKEN_COMMENT:
|
|
break;
|
|
case TOKEN_NUM:
|
|
errno = 0;
|
|
(void)strtoll(*c, &e, 0);
|
|
if (errno != 0)
|
|
goto err;
|
|
*c = e;
|
|
break;
|
|
case TOKEN_KEY:
|
|
(*c)++;
|
|
while (isalnum((unsigned char)**c) || **c == '_')
|
|
(*c)++;
|
|
break;
|
|
case TOKEN_STRING:
|
|
token->val++; /* skip initial '\"' */
|
|
(*c)++;
|
|
/*
|
|
* XXX: should we allow escapes inside the string?
|
|
*/
|
|
while (**c != '\0' && **c != '\"')
|
|
(*c)++;
|
|
if (**c != '\"')
|
|
goto err;
|
|
**c = '\0'; /* kill trailing '\"' */
|
|
(*c)++;
|
|
break;
|
|
case TOKEN_UNKNOWN:
|
|
goto err;
|
|
}
|
|
|
|
if (isspace((unsigned char)**c))
|
|
*(*c)++ = '\0';
|
|
else if (**c == SASLC__COMMENT_CHAR)
|
|
**c = '\0';
|
|
else if (**c != '\0')
|
|
goto err;
|
|
|
|
return token;
|
|
err:
|
|
free(token);
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* @brief parses line and store result in dict.
|
|
* @param line input line
|
|
* @param dict dictionary in which parsed options will be stored
|
|
* @return 0 on success, -1 on failure.
|
|
*/
|
|
static int
|
|
saslc__parse_line(char *line, saslc__dict_t *dict)
|
|
{
|
|
saslc__dict_result_t rv;
|
|
saslc__token_t *t;
|
|
char *key;
|
|
|
|
key = NULL;
|
|
while ((t = saslc__parse_get_token(&line)) != NULL) {
|
|
if (t->type == TOKEN_COMMENT) {
|
|
free(t);
|
|
break;
|
|
}
|
|
|
|
if (key == NULL) { /* get the key */
|
|
if (t->type != TOKEN_KEY)
|
|
goto err;
|
|
key = t->val;
|
|
}
|
|
else { /* get the value and insert in dictionary */
|
|
if (t->type != TOKEN_STRING && t->type != TOKEN_NUM)
|
|
goto err;
|
|
rv = saslc__dict_insert(dict, key, t->val);
|
|
if (rv != DICT_OK && rv != DICT_KEYEXISTS)
|
|
goto err;
|
|
key = NULL;
|
|
}
|
|
free(t);
|
|
}
|
|
if (*line != '\0') /* processed entire line */
|
|
return -1;
|
|
if (key != NULL) /* completed key/value cycle */
|
|
return -1;
|
|
return 0;
|
|
err:
|
|
free(t);
|
|
return -1;
|
|
}
|
|
|
|
/**
|
|
* @brief parses file and store result in dict
|
|
* @param ctx saslc context
|
|
* @param path path to the file
|
|
* @param dict dictionary in which parsed options will be stored
|
|
* @return 0 on success, -1 on failure.
|
|
*/
|
|
static int
|
|
saslc__parse_file(saslc_t *ctx, char *path, saslc__dict_t *dict)
|
|
{
|
|
FILE *fp;
|
|
char *buf, *lbuf;
|
|
size_t len;
|
|
int rv;
|
|
|
|
if ((fp = fopen(path, "r")) == NULL) {
|
|
/* Don't fail if we can't open the file. */
|
|
saslc__msg_dbg("%s: fopen: %s: %s", __func__, path,
|
|
strerror(errno));
|
|
return 0;
|
|
}
|
|
saslc__msg_dbg("%s: parsing: \"%s\"", __func__, path);
|
|
rv = 0;
|
|
lbuf = NULL;
|
|
while ((buf = fgetln(fp, &len)) != NULL) {
|
|
if (buf[len - 1] == '\n')
|
|
buf[len - 1] = '\0';
|
|
else {
|
|
if ((lbuf = malloc(len + 1)) == NULL) {
|
|
saslc__error_set(ERR(ctx), ERROR_NOMEM, NULL);
|
|
rv = -1;
|
|
break;
|
|
}
|
|
memcpy(lbuf, buf, len);
|
|
lbuf[len] = '\0';
|
|
buf = lbuf;
|
|
}
|
|
if (saslc__parse_line(buf, dict) == -1) {
|
|
saslc__error_set(ERR(ctx), ERROR_PARSE,
|
|
"can't parse file");
|
|
rv = -1;
|
|
break;
|
|
}
|
|
if (lbuf != NULL) {
|
|
free(lbuf);
|
|
lbuf = NULL;
|
|
}
|
|
}
|
|
if (lbuf != NULL)
|
|
free(lbuf);
|
|
|
|
fclose(fp);
|
|
return rv;
|
|
}
|
|
|
|
/**
|
|
* @brief determine if a string indicates true or not.
|
|
* @return true if the string is "true", "yes", or any nonzero
|
|
* integer; false otherwise.
|
|
*
|
|
* XXX: does this really belong here? Used in parser.c and xsess.c.
|
|
*/
|
|
bool
|
|
saslc__parser_is_true(const char *str)
|
|
{
|
|
static const char *true_str[] = {
|
|
"true",
|
|
"yes"
|
|
};
|
|
char *e;
|
|
size_t i;
|
|
long int val;
|
|
|
|
if (str == NULL)
|
|
return false;
|
|
|
|
val = strtol(str, &e, 0);
|
|
if (*str != '\0' && *e == '\0')
|
|
return val != 0;
|
|
|
|
for (i = 0; i < __arraycount(true_str); i++)
|
|
if (strcasecmp(str, true_str[i]) == 0)
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* @brief parse configuration files. By default function reads
|
|
* files from /etc/saslc.d/saslc/ directory if appname is not setup. Otherwise
|
|
* function uses /etc/saslc.d/[appname]/ directory. /etc/saslc.d/ is default
|
|
* directory which stores configuration for all applications, but can be
|
|
* overwritten by SASLC_CONFIG variable in environment.
|
|
* @param ctx saslc context
|
|
* @return 0 on success, -1 on failure.
|
|
*/
|
|
int
|
|
saslc__parser_config(saslc_t *ctx)
|
|
{
|
|
char path[PATH_MAX + 1];
|
|
struct stat sb;
|
|
saslc__mech_list_node_t *mech_node;
|
|
const char *config_path, *debug, *appname;
|
|
|
|
config_path = ctx->pathname;
|
|
if (config_path == NULL)
|
|
config_path = getenv(SASLC_ENV_CONFIG);
|
|
if (config_path == NULL)
|
|
config_path = SASLC__CONFIG_PATH;
|
|
|
|
if (stat(config_path, &sb) == -1 || !S_ISDIR(sb.st_mode)) {
|
|
/* XXX: should this be fatal or silently ignored? */
|
|
saslc__msg_err("%s: stat: config_path='%s': %s", __func__,
|
|
config_path, strerror(errno));
|
|
return 0;
|
|
}
|
|
|
|
if ((appname = ctx->appname) == NULL)
|
|
appname = SASLC__DEFAULT_APPNAME;
|
|
|
|
/* parse global config file */
|
|
snprintf(path, sizeof(path), "%s/%s/%s%s", config_path,
|
|
appname, SASLC__CONFIG_MAIN_FILE, SASLC__CONFIG_SUFFIX);
|
|
if (saslc__parse_file(ctx, path, ctx->prop) == -1)
|
|
return -1;
|
|
|
|
/* XXX: check this as early as possible! */
|
|
debug = saslc__dict_get(ctx->prop, SASLC_PROP_DEBUG);
|
|
if (debug != NULL)
|
|
saslc_debug = saslc__parser_is_true(debug);
|
|
|
|
/* parse mechanism config files */
|
|
LIST_FOREACH(mech_node, ctx->mechanisms, nodes) {
|
|
snprintf(path, sizeof(path), "%s/%s/%s/%s%s",
|
|
config_path, appname, SASLC__CONFIG_MECH_DIRECTORY,
|
|
mech_node->mech->name, SASLC__CONFIG_SUFFIX);
|
|
if (saslc__parse_file(ctx, path, mech_node->prop) == -1)
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
}
|