/* $Id: parse.c 1912 2006-10-14 22:31:11Z coudercd $ */
/*
* Copyright (c) 2003-2006 Damien Couderc
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* - 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.
* - Neither the name of the copyright holder(s) 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 COPYRIGHT HOLDERS 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
* COPYRIGHT HOLDERS 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 <stdlib.h>
#include "compat/pmk_ctype.h"
#include "compat/pmk_stdio.h"
#include "compat/pmk_string.h"
#include "common.h"
#include "hash.h"
#include "parse.h"
#include "prseng.h"
/*#define PRS_DEBUG 1*/
char parse_err[MAX_ERR_MSG_LEN];
/******************
* prsdata_init() *
***********************************************************************
DESCR
initialize parsing data structure
IN
NONE
OUT
prsdata structure
***********************************************************************/
prsdata *prsdata_init(void) {
prsdata *pdata;
pdata = (prsdata *) malloc(sizeof(prsdata));
if (pdata != NULL) {
pdata->linenum = 0;
pdata->tree = prsnode_init();
if (pdata->tree == NULL) {
/* tree init failed, clean allocated structure */
prsdata_destroy(pdata);
pdata = NULL;
}
}
return(pdata);
}
/*********************
* prsdata_destroy() *
***********************************************************************
DESCR
free space allocated by parsing data structure
IN
pdata: structure to clean
OUT
NONE
***********************************************************************/
void prsdata_destroy(prsdata *pdata) {
#ifdef PRS_DEBUG
debugf("prsdata_destroy() : cleaning tree");
#endif
prsnode_destroy(pdata->tree);
#ifdef PRS_DEBUG
debugf("prsdata_destroy() : cleaning structure");
#endif
free(pdata);
#ifdef PRS_DEBUG
debugf("prsdata_destroy() : exit");
#endif
}
/******************
* prsnode_init() *
***********************************************************************
DESCR
initialize parsing node structure
IN
NONE
OUT
parsing node structure
***********************************************************************/
prsnode *prsnode_init(void) {
prsnode *pnode;
pnode = (prsnode *) malloc(sizeof(prsnode));
if (pnode != NULL) {
pnode->first = NULL;
pnode->last = NULL;
}
return(pnode);
}
/*****************
* prsnode_add() *
***********************************************************************
DESCR
add a cell to a node
IN
pnode: target node
pcell: cell to add
OUT
NONE
***********************************************************************/
void prsnode_add(prsnode *pnode, prscell *pcell) {
#ifdef PRS_DEBUG
if (pnode == NULL) {
debugf("prsnode_add() : pnode is NULL !");
}
if (pcell == NULL) {
debugf("prsnode_add() : pcell is NULL !");
}
#endif
if (pnode->last != NULL) {
/* linking to last item of node */
pnode->last->next = pcell;
} else {
/* empty node, link as first */
pnode->first = pcell;
}
pnode->last = pcell;
}
/*********************
* prsnode_destroy() *
***********************************************************************
DESCR
free space allocated by parsing node structure
IN
pnode: node structure to clean
OUT
NONE
***********************************************************************/
void prsnode_destroy(prsnode *pnode) {
prscell *p,
*n;
if (pnode != NULL) {
p = pnode->first;
while (p != NULL) {
n = p;
p = n->next;
prscell_destroy(n);
}
free(pnode);
}
}
/******************
* prscell_init() *
***********************************************************************
DESCR
initialize parsing cell structure
IN
token: cell token
type: cell type
subtoken: cell subtoken
OUT
parsing cell structure or NULL if failed
***********************************************************************/
prscell *prscell_init(int token, int type, int subtoken) {
bool rval = false;
htable *pht;
prscell *pcell;
prsnode *pnode;
prsopt *popt;
pcell = (prscell *) malloc(sizeof(prscell));
if (pcell != NULL) {
pcell->token = token;
pcell->type = type;
switch(type) {
case PRS_KW_NODE :
pnode = prsnode_init();
if (pnode != NULL) {
pnode->token = subtoken;
pcell->data = pnode;
rval = true;
}
break;
case PRS_KW_CELL :
pht = hash_init_adv(MAX_CMD_OPT,
(void *(*)(void *))po_dup,
(void (*)(void *))po_free,
(void *(*)(void *, void *, void *))po_append);
if (pht != NULL) {
pcell->data = pht;
rval = true;
}
break;
case PRS_KW_ITEM :
popt = (prsopt *) malloc(sizeof(prsopt));
if (popt != NULL) {
pcell->data = popt;
rval = true;
}
break;
default :
break;
}
}
if (rval == true) {
pcell->next = NULL;
} else {
free(pcell);
pcell = NULL;
}
return(pcell);
}
/*********************
* prscell_destroy() *
***********************************************************************
DESCR
free space allocated for parsing cell structure
IN
pcell: structure to clean
OUT
NONE
***********************************************************************/
void prscell_destroy(prscell *pcell) {
#ifdef PRS_DEBUG
/*debugf("prscell_destroy().");*/
#endif
if (pcell != NULL) {
if (pcell->data != NULL) {
switch(pcell->type) {
case PRS_KW_NODE :
prsnode_destroy(pcell->data);
break;
case PRS_KW_CELL :
hash_destroy(pcell->data);
break;
default :
break;
}
}
free(pcell);
}
}
/*****************
* prsopt_init() *
***********************************************************************
DESCR
init a prsopt structure
IN
NONE
OUT
prsopt structure
***********************************************************************/
prsopt *prsopt_init(void) {
prsopt *ppo;
ppo = (prsopt *) malloc(sizeof(prsopt));
return(ppo);
}
/*********************
* prsopt_init_adv() *
***********************************************************************
DESCR
init a prsopt structure with given aguments
IN
key: parse option key name
opchar: operation character
value: parse option value
OUT
prsopt structure
***********************************************************************/
prsopt *prsopt_init_adv(char *key, char opchar, char *value) {
prsopt *ppo;
ppo = (prsopt *) malloc(sizeof(prsopt));
if (ppo == NULL) {
return(NULL);
}
if (strlcpy_b(ppo->key, key, sizeof(ppo->key)) == false) {
free(ppo);
return (NULL); /* truncated */
}
ppo->opchar = opchar;
if (value != NULL) {
ppo->value = po_mk_str(value);
if (ppo->value == NULL) {
free(ppo);
return(NULL); /* pmkobject failed */
}
} else {
/* permit value = NULL */
ppo->value = NULL;
}
return(ppo);
}
/********************
* prsopt_destroy() *
***********************************************************************
DESCR
destroy a prsopt structure
IN
ppo: prsopt structure to free
OUT
NONE
***********************************************************************/
void prsopt_destroy(prsopt *ppo) {
if (ppo->value != NULL) {
po_free(ppo->value);
}
free(ppo);
}
/******************
* keyword_hash() *
***********************************************************************
DESCR
create hash table with keyword structures
IN
kwtab: keyword table
nbkw: size of table
OUT
hash table
***********************************************************************/
htable *keyword_hash(prskw kwtab[], int nbkw) {
htable *phkw;
int i;
prskw *pkw;
phkw = hash_init_adv(nbkw, (void *(*)(void *))strdup, free, NULL);
if (phkw != NULL) {
/* fill keywords hash */
for(i = 0 ; i < nbkw ; i++) {
pkw = (prskw *) malloc(sizeof(prskw));
memmove(pkw, &kwtab[i], sizeof(prskw));
if (hash_update(phkw, kwtab[i].kw, /* no need to strdup */
pkw) == HASH_ADD_FAIL) {
hash_destroy(phkw);
errorf("failed to fill keywords hash table.");
return(NULL);
}
}
}
return(phkw);
}
/********************
* prs_skip_blank() *
***********************************************************************
DESCR
skip blank character(s)
IN
ppe: parsing engine structure
OUT
new parsing cursor
***********************************************************************/
bool prs_skip_blank(prseng_t *ppe) {
while (prseng_test_idtf_char(" \t",
prseng_get_char(ppe)) == true) {
if (prseng_next_char(ppe) == false) {
return false;
}
}
return true;
}
/**********************
* prs_skip_comment() *
***********************************************************************
DESCR
skip a comment
IN
ppe: parsing engine structure
OUT
boolean
***********************************************************************/
bool prs_skip_comment(prseng_t *ppe) {
bool loop = true;
char c;
/* check for end of line or end of file */
while (loop == true) {
#ifdef PRS_DEBUG
debugf("check if '%c' == CR.", prseng_get_char(ppe));
#endif
c = prseng_get_char(ppe);
/* reached end of line or buffer ? */
if ((c == CHAR_CR) || (c == CHAR_EOS)) {
loop = false;
}
/* next char */
if (prseng_next_char(ppe) == false) {
return false;
}
}
return true;
}
/**********************
* prs_skip_useless() *
***********************************************************************
DESCR
skip useless text (blanks, comments, empty lines)
IN
ppe: parsing engine structure
OUT
NONE
***********************************************************************/
bool prs_skip_useless(prseng_t *ppe) {
bool loop = true;
#ifdef PRS_DEBUG
debugf("prs_skip_useless() : process start on line %d", ppe->linenum);
#endif
while (loop == true) {
#ifdef PRS_DEBUG
debugf("prs_skip_useless() : char = '%c'", prseng_get_char(ppe));
#endif
switch(prseng_get_char(ppe)) {
case ' ' :
case '\t' :
case '\n' :
if (prseng_next_char(ppe) == false) {
return false;
}
break;
case PMK_CHAR_COMMENT :
/* comment ? */
#ifdef PRS_DEBUG
debugf("prs_skip_useless() : skip comment on line %d.", ppe->linenum);
#endif
prs_skip_comment(ppe);
break;
default:
loop = false;
}
}
#ifdef PRS_DEBUG
debugf("prs_skip_useless() : process ended on line %d with char '%c'",
ppe->linenum, prseng_get_char(ppe));
#endif
return true;
}
/*****************
* parse_label() *
***********************************************************************
DESCR
parse a label
IN
ppe: parsing engine structure
pbuf: storage buffer
size: size of buffer
OUT
new parsing cursor or NULL
***********************************************************************/
bool parse_label(prseng_t *ppe, char *pbuf, size_t size) {
char c;
c = prseng_get_char(ppe);
if (c == '!') {
*pbuf = c;
pbuf++;
size--;
if (prseng_next_char(ppe) == false) {
return false;
}
}
return(prseng_get_idtf(ppe, pbuf, size, PRS_PMK_IDTF_STR));
}
/****************
* parse_bool() *
***********************************************************************
DESCR
parse a bool value
IN
ppe: parsing engine structure
po: storage pmk object
size: size of buffer
OUT
new parsing cursor or NULL
***********************************************************************/
bool parse_bool(prseng_t *ppe, pmkobj *po, size_t size) {
bool *pb;
char *pbuf,
buf[6];
pb = (bool *) malloc(sizeof (bool));
if (pb == NULL) {
strlcpy(parse_err, PRS_ERR_ALLOC,
sizeof(parse_err)); /* no check */
return false;
}
if (size > sizeof(buf))
size = sizeof(buf);
pbuf = buf;
/* get bool identifier */
if (prseng_get_idtf(ppe, buf, size, PRS_BOOL_IDTF_STR) == false) {
strlcpy(parse_err, PRS_ERR_PRSENG, sizeof(parse_err)); /* no check */
return false;
}
/* is it true value ? */
if (strncmp(buf, PMK_BOOL_TRUE, sizeof(buf)) == 0) {
*pb = true;
} else {
/* is it false value ? */
if (strncmp(buf, PMK_BOOL_FALSE, sizeof(buf)) == 0) {
*pb = false;
} else {
/* unknown value identifier */
strlcpy(parse_err, "unknown boolean identifier.",
sizeof(parse_err)); /* no check */
free(pb);
return false;
}
}
po->type = PO_BOOL;
po->data = pb;
return true;
}
/******************
* parse_quoted() *
***********************************************************************
DESCR
parse quoted string content
IN
ppe: parsing engine structure
po: storage pmk object
size: size of buffer
OUT
new parsing cursor
***********************************************************************/
bool parse_quoted(prseng_t *ppe, pmkobj *po, size_t size) {
bool escape = false,
loop = true;
char *buffer,
*pbuf,
c;
buffer = (char *) malloc(size);
if (buffer == NULL) {
return false;
}
pbuf = buffer;
/* leave one char for end of line */
size--;
/* skip starting double quote */
if (prseng_next_char(ppe) == false) {
return false;
}
if (prseng_eof(ppe) == true) {
free(buffer);
strlcpy(parse_err, "ending quote is missing.", sizeof(parse_err)); /* no check */
return false;
}
/* unset escape flag */
escape = false;
while (loop == true) {
c = prseng_get_char(ppe);
switch (c) {
case CHAR_EOS :
strlcpy(parse_err, "ending quote is missing.",
sizeof(parse_err)); /* no check */
return false;
case PMK_CHAR_ESCAPE :
#ifdef PRS_DEBUG
debugf("parse_quoted() : found escape char.");
#endif
if (escape == true) {
/* double escape */
escape = false;
#ifdef PRS_DEBUG
debugf("parse_quoted() : escape = false.");
#endif
} else {
/* found first escape, set flag */
escape = true;
#ifdef PRS_DEBUG
debugf("parse_quoted() : escape = true.");
#endif
}
break;
case PMK_CHAR_QUOTE_END :
/* if not escaped ... */
if (escape == false) {
/* ... found end quote */
loop = false;
} else {
escape = false;
}
break;
default :
escape = false;
break;
}
/* if not escape or ending quote */
if ((escape == false) && (loop == true)) {
/* copy character */
*pbuf = c;
pbuf++;
size--;
/* check remaining space */
if (size < 0) {
free(buffer);
strlcpy(parse_err, PRS_ERR_OVERFLOW, sizeof(parse_err)); /* no check */
return false;
}
}
if (prseng_next_char(ppe) == false) {
free(buffer);
return false;
}
}
/* found end of quoted string */
*pbuf = CHAR_EOS;
po->type = PO_STRING;
/* use strdup() to get rid of the extra space that could result
of the allocation with the max size of buffer */
po->data = strdup(buffer);
/* deallocate the now useless buffer */
free(buffer);
if (po->data == NULL) {
/* strdup() failed */
errorf(ERRMSG_MEM);
return false;
}
return true;
}
/****************
* parse_list() *
***********************************************************************
DESCR
parse a list
IN
ppe: parsing engine structure
po: storage pmk object
size: size of buffer
OUT
new parsing cursor
***********************************************************************/
bool parse_list(prseng_t *ppe, pmkobj *po, size_t size) {
bool loop = true,
prev_data = false;
char *buffer,
c;
dynary *pda;
pmkobj potmp;
pda = da_init();
if (pda == NULL) {
strlcpy(parse_err, PRS_ERR_ALLOC, sizeof(parse_err)); /* no check */
return false;
}
/* skip starting list char */
if (prseng_next_char(ppe) == false) {
return false;
}
while (loop == true) {
/* skip blank characters */
if (prs_skip_blank(ppe) == false) {
return false;
}
c = prseng_get_char(ppe);
switch (c) {
case PMK_CHAR_LIST_END :
#ifdef PRS_DEBUG
debugf("found end of list.");
#endif
loop = false;
break;
case PMK_CHAR_QUOTE_START :
/* check if data was right before */
if (prev_data == true) {
/* yes => syntax error */
da_destroy(pda);
strlcpy(parse_err, PRS_ERR_SYNTAX, sizeof(parse_err)); /* no check */
return false;
}
/* found string data */
if (parse_quoted(ppe, &potmp, size) == false) {
da_destroy(pda);
strlcpy(parse_err, PRS_ERR_SYNTAX, sizeof(parse_err)); /* no check */
return false;
}
#ifdef PRS_DEBUG
debugf("add '%s' into dynary (quoted)", potmp.data);
#endif
if (da_push(pda, potmp.data) == false) {
da_destroy(pda);
return false;
}
/* set data flag */
prev_data = true;
/* already at next character, continue */
continue;
break;
case PMK_CHAR_LIST_SEP :
/* check if data was right before */
if (prev_data == false) {
/* no => syntax error */
da_destroy(pda);
strlcpy(parse_err, PRS_ERR_SYNTAX, sizeof(parse_err)); /* no check */
return false;
}
#ifdef PRS_DEBUG
debugf("found separator.");
#endif
/* unset data flag */
prev_data = false;
break;
case CHAR_CR :
/* ignore end of line */
#ifdef PRS_DEBUG
debugf("found end of line.");
#endif
break;
case CHAR_EOS :
/* reached end of file */
da_destroy(pda);
strlcpy(parse_err, "end of list not found.",
sizeof(parse_err)); /* no check */
return false;
break;
default :
/* check if data was right before */
if (prev_data == true) {
/* yes => syntax error */
da_destroy(pda);
strlcpy(parse_err, PRS_ERR_SYNTAX, sizeof(parse_err)); /* no check */
return false;
}
buffer = (char *) malloc(size);
if (buffer == NULL) {
strlcpy(parse_err, PRS_ERR_ALLOC, sizeof(parse_err)); /* no check */
return false;
}
if (prseng_get_idtf(ppe, buffer, size,
PRS_PMK_IDTF_STR) == false) {
da_destroy(pda);
/* XXX better msg */
strlcpy(parse_err, PRS_ERR_OVERFLOW, sizeof(parse_err)); /* no check */
}
/* XXX useless ? */
if (*buffer == CHAR_EOS) {
da_destroy(pda);
strlcpy(parse_err, PRS_ERR_SYNTAX, sizeof(parse_err)); /* no check */
return false;
}
#ifdef PRS_DEBUG
debugf("add '%s' into dynary (simple).", buffer);
#endif
if (da_push(pda, buffer) == false) {
da_destroy(pda);
return false;
}
/* set data flag */
prev_data = true;
break;
}
if (prseng_next_char(ppe) == false) {
return false;
}
}
/* check if data was right before */
if (prev_data == false) {
/* no => syntax error */
da_destroy(pda);
strlcpy(parse_err, PRS_ERR_SYNTAX, sizeof(parse_err)); /* no check */
#ifdef PRS_DEBUG
debugf("no data before end of list.");
#endif
return(NULL);
}
/* found end of list */
po->type = PO_LIST;
po->data = pda;
return true;
}
/***************
* parse_key() *
***********************************************************************
DESCR
parse key
IN
ppe: parsing engine structure
po: storage pmk object
size: size of buffer
OUT
new parsing cursor or NULL
***********************************************************************/
bool parse_key(prseng_t *ppe, pmkobj *po, size_t size) {
char *buffer;
po->type = PO_NULL;
#ifdef PRS_DEBUG
debugf("parse_key() : cursor = '%.32s'", ppe->cur);
#endif
if (prseng_get_char(ppe) == PMK_CHAR_QUOTE_START) {
#ifdef PRS_DEBUG
debugf("parse_key() : found quoted string");
#endif
/* found a quoted string */
if (parse_quoted(ppe, po, size) == false) {
return false;
}
} else {
#ifdef PRS_DEBUG
debugf("parse_key() : found identifier");
#endif
/* identifier */
buffer = (char *) malloc(size);
if (buffer == NULL) {
strlcpy(parse_err, PRS_ERR_ALLOC, sizeof(parse_err)); /* no check */
return false;
}
if (prseng_get_idtf(ppe, buffer, size, PRS_PMK_IDTF_STR) == false) {
free(buffer);
/* XXX better msg ? */
strlcpy(parse_err, PRS_ERR_OVERFLOW, sizeof(parse_err)); /* no check */
return false;
}
po->type = PO_STRING;
po->data = buffer;
}
return true;
}
/****************
* parse_data() *
***********************************************************************
DESCR
parse data
IN
ppe: parsing engine structure
po: storage pmk object
size: size of buffer
OUT
boolean
***********************************************************************/
bool parse_data(prseng_t *ppe, pmkobj *po, size_t size) {
po->type = PO_NULL;
#ifdef PRS_DEBUG
debugf("parse_data(): first character = '%c'", prseng_get_char(ppe));
#endif
switch (prseng_get_char(ppe)) {
/* found a quoted string */
case PMK_CHAR_QUOTE_START :
#ifdef PRS_DEBUG
debugf("parse_data(): calling parse_quoted()");
#endif
if (parse_quoted(ppe, po, size) == false) {
return false;
}
break;
/* found a list */
case PMK_CHAR_LIST_START :
#ifdef PRS_DEBUG
debugf("parse_data(): calling parse_list()");
#endif
if (parse_list(ppe, po, size) == false) {
return false;
}
break;
default :
#ifdef PRS_DEBUG
debugf("parse_data(): calling parse_bool()");
#endif
if (parse_bool(ppe, po, size) == false) {
return false;
}
break;
}
return true;
}
/**********************
* parse_cmd_header() *
***********************************************************************
DESCR
parse command header
IN
ppe: parser engine structure
pnode: parser node structure
OUT
parser cell structure on success or NULL
***********************************************************************/
prscell *parse_cmd_header(prseng_t *ppe, prsnode *pnode) {
char name[CMD_LEN];
miscdata_t *pmd;
prscell *pcell;
prskw *pkw;
if (prseng_get_idtf(ppe, name, sizeof(name),
PRS_PMK_IDTF_STR) == false) {
strlcpy(parse_err, "command parsing failed.", sizeof(parse_err)); /* no check */
return(NULL);
}
#ifdef PRS_DEBUG
debugf("parse_cmd_header(): cmd name = '%s'", name);
#endif
/* get misc data */
pmd = (miscdata_t *) ppe->data;
/* check validity of the command */
pkw = hash_get(pmd->phtkw, name);
if (pkw != NULL) {
/* command ok, creating cell */
pcell = prscell_init(pkw->token, pkw->type, pkw->subtoken);
if (pcell == NULL) {
strlcpy(parse_err, "pcell init failed.", sizeof(parse_err)); /* no check */
return(NULL);
}
#ifdef PRS_DEBUG
debugf("parse_cmd_header(): cmd token = %d", pcell->token);
debugf("parse_cmd_header(): cmd type = %d", pcell->type);
#endif
/* check if options have to be checked */
if (pkw->type == PRS_KW_CELL) {
/* yes, keep pointer in prseng structure */
pmd->kwopts = pkw->kwo;
if (pmd->kwopts != NULL) {
/* assign number of required opts */
pmd->nbreq = pmd->kwopts->nbreq;
} else {
/* no required opts */
pmd->nbreq = 0;
}
} else {
pmd->kwopts = NULL;
pmd->nbreq = 0;
}
} else {
/* unknown command */
snprintf(parse_err, sizeof(parse_err), "unknown command '%s'.", name);
return(NULL);
}
/* look for a label */
if (prseng_test_char(ppe, PMK_CHAR_LABEL_START)) {
/* label delimiter found */
if (prseng_next_char(ppe) == false) {
return(NULL);
}
/* get label name */
if (parse_label(ppe, pcell->label, sizeof(pcell->label)) == false) {
strlcpy(parse_err, "label parsing failed.", sizeof(parse_err)); /* no check */
prscell_destroy(pcell);
return(NULL);
}
#ifdef PRS_DEBUG
debugf("parse_cmd_header(): cmd label = '%s'", pcell->label);
#endif
/* check label's ending delimiter */
if (prseng_test_char(ppe, PMK_CHAR_LABEL_END) == false) {
/* label name must be immediately followed by closing delimiter */
strlcpy(parse_err, "label parsing failed.", sizeof(parse_err)); /* no check */
prscell_destroy(pcell);
return(NULL);
}
if (prseng_next_char(ppe) == false) {
return(NULL);
}
#ifdef PRS_DEBUG
debugf("parse_cmd_header(): parsed command '%s' with label '%s'", name, pcell->label);
} else {
debugf("parse_cmd_header(): parsed command '%s' with no label", name);
#endif
}
/* look for command block starting sequence */
if (prseng_test_char(ppe, ' ') == false) {
strlcpy(parse_err, PRS_ERR_UNKNOWN, sizeof(parse_err)); /* no check */
prscell_destroy(pcell);
return(NULL);
}
if (prseng_next_char(ppe) == false) {
return(NULL);
}
if (prseng_test_char(ppe, PMK_CHAR_COMMAND_START) == false) {
strlcpy(parse_err, PRS_ERR_TRAILING, sizeof(parse_err)); /* no check */
prscell_destroy(pcell);
return(NULL);
}
if (prseng_next_char(ppe) == false) {
return(NULL);
}
if (prseng_test_char(ppe, CHAR_CR) == false) {
strlcpy(parse_err, PRS_ERR_TRAILING, sizeof(parse_err)); /* no check */
prscell_destroy(pcell);
return(NULL);
}
/* add cell in the node */
prsnode_add(pnode, pcell);
return(pcell);
}
/***************
* parse_opt() *
***********************************************************************
DESCR
parse an option
IN
ppe: parsing engine structure
popt: option storage structure
seplst: string that contain all separator characters
OUT
boolean
***********************************************************************/
bool parse_opt(prseng_t *ppe, prsopt *popt, char *seplst) {
char c;
pmkobj po;
#ifdef PRS_DEBUG
debugf("parse_opt() : line = '%.32s'", ppe->cur);
#endif
if (parse_key(ppe, &po, sizeof(popt->key)) == false) {
strlcpy(parse_err, PRS_ERR_SYNTAX, sizeof(parse_err)); /* no check */
return false;
}
if (strlcpy_b(popt->key, po.data, sizeof(popt->key)) == false) {
free(po.data);
return false;
}
free(po.data);
#ifdef PRS_DEBUG
debugf("parse_opt() : key = '%s'", popt->key);
#endif
if (prs_skip_blank(ppe) == false) {
return false;
}
/* check if character is in separator list */
c = prseng_get_char(ppe);
if (prseng_test_idtf_char(seplst, c) == false) {
strlcpy(parse_err, PRS_ERR_SYNTAX, sizeof(parse_err)); /* no check */
return false;
} else {
popt->opchar = c;
if (prseng_next_char(ppe) == false) {
return false;
}
}
#ifdef PRS_DEBUG
debugf("parse_opt() : assign = '%c'", popt->opchar);
#endif
if (prs_skip_blank(ppe) == false) {
return false;
}
popt->value = (pmkobj *) malloc(sizeof(pmkobj));
if (popt->value == NULL) {
strlcpy(parse_err, PRS_ERR_ALLOC, sizeof(parse_err)); /* no check */
return false;
}
/* parse data */
if (parse_data(ppe, popt->value, OPT_VALUE_LEN) == false) {
po_free(popt->value);
/*strlcpy(parse_err, PRS_ERR_SYNTAX, */
/* sizeof(parse_err)); |+ no check +|*/
return false;
}
#ifdef PRS_DEBUG
switch (popt->value->type) {
case PO_BOOL :
debugf("parse_opt() : value = *BOOL*");
break;
case PO_STRING :
debugf("parse_opt() : value = '%s'", popt->value->data);
break;
case PO_LIST :
debugf("parse_opt() : value = (*LIST*)");
break;
default :
debugf("parse_opt() : value = !unknown type!");
break;
}
#endif
return true;
}
/*****************
* parse_clopt() *
***********************************************************************
DESCR
parse a command line option
IN
line: option line
popt: storage structure
seplst: string that contain all separator characters
OUT
boolean
***********************************************************************/
bool parse_clopt(char *line, prsopt *popt, char *seplst) {
char c;
pmkobj po;
prseng_t *ppe;
#ifdef PRS_DEBUG
debugf("parse_clopt() : line = '%s'", line);
#endif
ppe = prseng_init_str(line, NULL);
if (ppe == NULL) {
strlcpy(parse_err, PRS_ERR_ALLOC, sizeof(parse_err)); /* no check */
return false;
}
if (parse_key(ppe, &po, sizeof(popt->key)) == false) {
strlcpy(parse_err, PRS_ERR_SYNTAX, sizeof(parse_err)); /* no check */
return false;
} else {
if (strlcpy_b(popt->key, po.data, sizeof(popt->key)) == false) {
free(po.data);
return false;
}
free(po.data);
}
#ifdef PRS_DEBUG
debugf("key = '%s'", popt->key);
#endif
/* check if character is in separator list */
c = prseng_get_char(ppe);
if (prseng_test_idtf_char(seplst, c) == false) {
strlcpy(parse_err, PRS_ERR_SYNTAX, sizeof(parse_err)); /* no check */
return false;
} else {
popt->opchar = c;
if (prseng_next_char(ppe) == false) {
return false;
}
}
#ifdef PRS_DEBUG
debugf("assign = '%c'", popt->opchar);
#endif
if (parse_data(ppe, &po, OPT_VALUE_LEN) == false) {
strlcpy(parse_err, PRS_ERR_SYNTAX, sizeof(parse_err)); /* no check */
return false;
} else {
if (po.type != PO_STRING) {
strlcpy(parse_err, PRS_ERR_SYNTAX, sizeof(parse_err)); /* no check */
return false;
} else {
#ifdef PRS_DEBUG
debugf("value = '%s'", po.data);
#endif
popt->value = po_mk_str(po.data);
free(po.data);
}
}
return true;
}
/*******************
* check_opt_avl() *
***********************************************************************
DESCR
check if the key is available in the given option array
IN
key: key to search
opts: option array
nbopts: number of options
OUT
return the pointer to the kw_t structure else NULL
***********************************************************************/
kw_t *check_opt_avl(char *key, kw_t *opts, size_t nbopts) {
int i;
for (i = 0 ; i < (int) nbopts ; i++) {
if (strncmp(key, opts[i].name, MAX_HASH_VALUE_LEN) == 0) {
return(&opts[i]);
}
}
return(NULL);
}
/********************
* check_opt_type() *
***********************************************************************
DESCR
check if the option has a type allowed by the keyword mask
IN
pkw: keyword structure
po: pmk object structure
OUT
boolean
NOTE
to disable type check set the mask to PO_NULL
***********************************************************************/
bool check_opt_type(kw_t *pkw, pmkobj *po) {
if ((pkw->mask != PO_NULL) && ((pkw->mask & po->type) == 0)) {
/*
a type mask has been given but doesn't match
with the option type
*/
return false;
}
/* the option type matches or the mask is set to PO_NULL */
return true;
}
/******************
* check_option() *
***********************************************************************
DESCR
check if the option name and type are valid
IN
pmd: misc data structure
ppo: parse option structure
OUT
boolean
***********************************************************************/
bool check_option(miscdata_t *pmd, prsopt *ppo) {
kw_t *pkw;
kwopt_t *pkwo;
pkwo = pmd->kwopts;
if (pkwo == NULL) {
/* nothing to check */
return true;
}
if (pkwo->req != NULL) {
/* check given required options */
pkw = check_opt_avl(ppo->key, pkwo->req, pkwo->nbreq);
if (pkw != NULL) {
/* check option's type */
if (check_opt_type(pkw, ppo->value) == false) {
/* wrong option type */
snprintf(parse_err, sizeof(parse_err),
PRS_ERR_TYP_OPT, ppo->key);
return false;
}
/* decrement required option counter */
pmd->nbreq--;
return true;
}
if (pkwo->opt == NULL) {
/* allow any optional variable names */
return true;
}
}
if (pkwo->opt != NULL) {
/* check given optional options */
pkw = check_opt_avl(ppo->key, pkwo->opt, pkwo->nbopt);
if (pkw != NULL) {
/* check option's type */
if (check_opt_type(pkw, ppo->value) == false) {
/* wrong option type */
snprintf(parse_err, sizeof(parse_err),
PRS_ERR_TYP_OPT, ppo->key);
return false;
}
return true;
}
}
/* provided option is not expected */
snprintf(parse_err, sizeof(parse_err), PRS_ERR_INV_OPT, ppo->key);
return false;
}
/***********************
* process_block_opt() *
***********************************************************************
DESCR
process a block option
IN
ppe: parsing engine structure
pnode: parsing node structure
pcell: parent cell structure
OUT
boolean
***********************************************************************/
bool process_block_opt(prseng_t *ppe, prsnode *pnode, prscell *pcell) {
prscell *ncell;
prsopt *nopt,
opt;
#ifdef PRS_DEBUG
debugf("process_block_opt() : calling parse_opt()");
#endif
/* parse option */
if (parse_opt(ppe, &opt, PRS_PMKFILE_SEP) == false) {
#ifdef PRS_DEBUG
debugf("process_block_opt() : parse_opt() returned false");
#endif
return false;
}
#ifdef PRS_DEBUG
debugf("process_block_opt() : recording '%s' key", opt.key);
#endif
switch (pcell->type) {
case PRS_KW_NODE :
/*
process a node
*/
/* init item's pcell */
ncell = prscell_init(pnode->token, PRS_KW_ITEM, PRS_TOK_NULL);
if (ncell == NULL) {
errorf("process_block_opt() : prscell init failed");
return false;
}
nopt = ncell->data;
/* duplicate opt content in item */
strlcpy(nopt->key, opt.key, sizeof(opt.key)); /* XXX check */
nopt->value = opt.value;
/* add item in cell node */
prsnode_add(pnode, ncell);
break;
case PRS_KW_CELL :
/*
process a command
*/
/* check keyword option (NULL handled) */
if (check_option(ppe->data, &opt) == false) {
/* err msg done */
return false;
}
/* verify if option is allowed */
if (hash_get(pcell->data, opt.key) != NULL) {
/* duplicate option */
strlcpy(parse_err, "duplicate option.",
sizeof(parse_err)); /* XXX */
return false;
}
if (hash_update(pcell->data, opt.key, /* no need to strdup */
opt.value) == HASH_ADD_FAIL) {
strlcpy(parse_err, PRS_ERR_HASH, sizeof(parse_err)); /* no check */
return false;
}
break;
}
return true;
}
/*********************
* parse_opt_block() *
***********************************************************************
DESCR
parse a block of options
IN
pdata: parsing data structure
ppe: parsing engine structure
pcell: parent cell structure
chk_delim: check delimiter switch
OUT
boolean
***********************************************************************/
bool parse_opt_block(prsdata *pdata, prseng_t *ppe, prscell *pcell,
bool chk_delim) {
bool loop = true;
miscdata_t *pmd;
prsnode *pnode;
pnode = pcell->data;
#ifdef PRS_DEBUG
debugf("parse_opt_block() : pcell->type = %u.", pcell->type);
debugf("parse_opt_block() : pnode->token = %u.", pnode->token);
#endif
if ((pcell->type == PRS_KW_NODE) && (pnode->token == PRS_TOK_NULL)) {
#ifdef PRS_DEBUG
debugf("parse_opt_block() : found a node, calling parse_cmd_block().");
#endif
/* found a block node (like for example IF command) => parse the block */
return(parse_cmd_block(pdata, ppe, pnode, true));
}
#ifdef PRS_DEBUG
debugf("parse_opt_block() : parsing options.");
#endif
while (loop == true) {
/* skip useless text */
if (prs_skip_useless(ppe) == false) {
return false;
}
switch(prseng_get_char(ppe)) {
case PMK_CHAR_COMMAND_END :
#ifdef PRS_DEBUG
debugf("parse_opt_block() : found end of block character.");
#endif
if (prseng_next_char(ppe) == false) {
return false;
}
if (chk_delim == true) {
/* delimiter found, exit from the loop */
loop = false;
/* check if all required options have been parsed */
pmd = (miscdata_t *) ppe->data;
if (pmd->nbreq != 0) {
snprintf(parse_err, sizeof(parse_err), /* no check */
"at least one required option is missing (%d).",
(int) pmd->nbreq);
return false;
}
} else {
/* delimiter not expected */
return false;
}
break;
case CHAR_EOS :
if (chk_delim == true) {
/* expected delimiter not found */
return false;
} else {
/* main loop, no delimiter was expected => ok */
return true;
}
break;
default :
if (process_block_opt(ppe, pnode, pcell) == false) {
/* error message already set */
return false;
}
break;
}
}
return true;
}
/*********************
* parse_cmd_block() *
***********************************************************************
DESCR
parse a block of commands
IN
pdata: parsing data structure
ppe: parsing engine structure
pnode: node structure
chk_delim: check delimiter switch
OUT
boolean
***********************************************************************/
bool parse_cmd_block(prsdata *pdata, prseng_t *ppe, prsnode *pnode,
bool chk_delim) {
bool loop = true;
prscell *pcell;
/*
start of block sequence does not exists or has already
been parsed so don't look for it.
*/
while (loop == true) {
/* skip useless text */
if (prs_skip_useless(ppe) == false) {
return false;
}
#ifdef PRS_DEBUG
debugf("parse_cmd_block() : checking character.");
#endif
switch(prseng_get_char(ppe)) {
case PMK_CHAR_COMMAND_END :
#ifdef PRS_DEBUG
debugf("parse_cmd_block() : found end of command character.");
#endif
if (prseng_next_char(ppe) == false) {
return false;
}
if (chk_delim == true) {
/* delimiter found, exit from the loop */
loop = false;
} else {
/* delimiter not expected */
return false;
}
break;
case CHAR_EOS :
#ifdef PRS_DEBUG
debugf("parse_cmd_block() : found end of string character.");
#endif
if (chk_delim == true) {
/* expected delimiter not found */
return false;
} else {
/* main loop, no delimiter was expected => ok */
return true;
}
break;
default :
#ifdef PRS_DEBUG
debugf("parse_cmd_block() : parsing command header.");
#endif
pcell = parse_cmd_header(ppe, pnode);
if (pcell == NULL) {
/* parsing failed, message done */
return false;
}
#ifdef PRS_DEBUG
debugf("parse_cmd_block() : parsing command body.");
#endif
if (parse_opt_block(pdata, ppe, pcell, true) == false) {
#ifdef PRS_DEBUG
debugf("parse_cmd_block() : command boby parsing has fail.");
#endif
return false;
}
}
}
return true;
}
/*******************
* parse_pmkfile() *
***********************************************************************
DESCR
parse pmkfile
IN
fp: file descriptor
pdata: parsing data structure
kwtab: keyword table
size: size of keyword table
OUT
boolean
***********************************************************************/
bool parse_pmkfile(FILE *fp, prsdata *pdata, prskw kwtab[], size_t size) {
bool rslt = true;
miscdata_t md;
prseng_t *ppe;
ppe = prseng_init(fp, &md);
if (ppe == NULL) {
/* cannot init engine structure */
errorf("init of parsing engine structure failed.");
return false;
}
/* init hash table of command keywords */
md.phtkw = keyword_hash(kwtab, size);
if (md.phtkw == NULL) {
/* message done */
rslt = false;
}
if ((rslt == true) && (parse_cmd_block(pdata, ppe, pdata->tree,
false) == false)) {
/* display error message */
errorf("line %d : %s", ppe->linenum, parse_err);
rslt = false;
}
if ((rslt == true) && (feof(fp) == 0)){
/* error occured before EOF */
errorf("end of file not reached.");
rslt = false;
}
hash_destroy(md.phtkw);
prseng_destroy(ppe);
return(rslt);
}
/*****************
* process_opt() *
***********************************************************************
DESCR
process option line of configuration file
IN
pht: storage hash table
popt: option structure to record
OUT
boolean
***********************************************************************/
bool process_opt(htable *pht, prsopt *popt) {
if ((popt->opchar != CHAR_COMMENT) && (popt->opchar != CHAR_EOS)) {
/* add options that are not comment neither blank lines */
if (hash_update_dup(pht, popt->key,
po_get_str(popt->value)) == HASH_ADD_FAIL) {
errorf("hash failure.");
return false;
}
}
return true;
}
/*******************
* parse_pmkconf() *
***********************************************************************
DESCR
parse configuration file
IN
fp: file to parse
pht: data used by processing function
seplst: list of separators
func: processing function
OUT
boolean
***********************************************************************/
bool parse_pmkconf(FILE *fp, htable *pht, char *seplst,
bool (*func)(htable *, prsopt *)) {
bool loop;
char *pbuf,
c,
buf[MAX_LINE_LEN];
prseng_t *ppe;
prsopt opt;
size_t s;
ppe = prseng_init(fp, NULL);
if (ppe == NULL) {
/* cannot init engine structure */
errorf("init of parsing engine structure failed.");
return false;
}
while (prseng_eof(ppe) == false) {
switch (prseng_get_char(ppe)) {
case CHAR_COMMENT :
/* comment */
strlcpy(opt.key, "comment", sizeof(opt.key)); /* no check */
opt.opchar = CHAR_COMMENT;
/* check for end of line or end of file */
pbuf = buf;
s = sizeof(buf);
loop = true;
while (loop == true) {
#ifdef PRS_DEBUG
debugf("check if '%c' == CR.", prseng_get_char(ppe));
#endif
c = prseng_get_char(ppe);
/* reached end of line or buffer ? */
if ((c == CHAR_CR) || (c == CHAR_EOS)) {
loop = false;
c = CHAR_EOS;
}
*pbuf = c;
pbuf++;
s--;
/* check remaining space */
if (s < 0) {
strlcpy(parse_err, PRS_ERR_OVERFLOW,
sizeof(parse_err)); /* no check */
prseng_destroy(ppe);
return false;
}
/* next char */
if (prseng_next_char(ppe) == false) {
prseng_destroy(ppe);
return false;
}
}
#ifdef PRS_DEBUG
debugf("parse_pmkconf() : found comment '%.32s'.", buf);
#endif
/* copy comment */
opt.value = po_mk_str(buf);
#ifdef PRS_DEBUG
debugf("parse_pmkconf() : cursor after comment '%.32s'.", ppe->cur);
#endif
/* process option */
if (func(pht, &opt) == false) {
errorf("line %d : processing failed", ppe->linenum);
return false;
}
break;
case CHAR_CR :
/* empty line */
strlcpy(opt.key, "", sizeof(opt.key)); /* no check */
opt.opchar = CHAR_EOS;
opt.value = po_mk_str("");
#ifdef PRS_DEBUG
debugf("parse_pmkconf() : found empty line.");
#endif
/* next char */
if (prseng_next_char(ppe) == false) {
prseng_destroy(ppe);
return false;
}
/* process option */
if (func(pht, &opt) == false) {
errorf("line %d : processing failed", ppe->linenum);
return false;
}
break;
case CHAR_EOS :
/* end of file reached */
loop = false;
break;
default :
if (parse_opt(ppe, &opt, seplst) == true) {
#ifdef PRS_DEBUG
strlcpy(buf, po_get_str(opt.value), sizeof(buf)); /* no check */
debugf("parse_pmkconf(): opt line to parse = '%.32s'.", buf);
#endif
/* parse ok */
if (func(pht, &opt) == false) {
errorf("line %d : processing failed", ppe->linenum);
return false;
}
} else {
#ifdef PRS_DEBUG
debugf("parse_pmkconf(): incorrect line = '%.32s'.", ppe->cur);
#endif
/* incorrect line */
errorf("line %d : %s", ppe->linenum, parse_err);
return false;
}
/* skip end of line */
if (prseng_test_char(ppe, CHAR_CR) == true) {
/* next char */
if (prseng_next_char(ppe) == false) {
prseng_destroy(ppe);
return false;
}
}
#ifdef PRS_DEBUG
debugf("parse_pmkconf(): line after opt parse = '%.32s'.", ppe->cur);
#endif
break;
}
}
if (prseng_eof(ppe) == false) {
/* error occured before EOF */
errorf("line %d : %.32s", ppe->linenum, "end of file not reached.");
return false;
}
return true;
}
/* vim: set noexpandtab tabstop=4 softtabstop=4 shiftwidth=4: */
syntax highlighted by Code2HTML, v. 0.9.1