/* * Copyright (c) 2004, 2006 Sendmail, Inc. and its suppliers. * All rights reserved. * * By using this file, you agree to the terms and conditions set * forth in the LICENSE file which can be found at the top level of * the sendmail distribution. */ #include "sm/generic.h" SM_RCSID("@(#)$Id: sm-conf-get.c,v 1.16 2006/01/09 19:06:25 ca Exp $") #if SM_LIBCONF_ALONE #include #include #include #include "sm-util.h" #include "sm-conf-util.h" #else /* SM_LIBCONF_ALONE */ #include "sm/ctype.h" #include "sm/error.h" #include "sm/memops.h" #include "sm/string.h" #endif /* SM_LIBCONF_ALONE */ #include "sm-conf-state.h" #include "sm-conf-node.h" #include "sm-conf-scan.h" #include "sm-conf-token.h" #include "sm-conf-type.h" /* ** PATHNAME_TOKEN -- utility: extract token or token pair from a node name ** ** pathname: [segment [sep segment]] ** segment: name [open sub close] ** sep: +sepchar ** sepchar: ** open: "<" | "(" | "{" | "[" | <"> | "'" | "`" ** close: ">" | ")" | "}" | "]" | <"> | "'" ** ** Opening and closing paren must match, respectively. ** A closing ' closes both an open ` (m4-style) and an open ' (sh-style). ** ** If we were to further restrict the separators (".", maybe?) and ** the open/close syntax, I'd be happy to make the parser code ** less ambiguous. This is simply the most generic syntax that ** lets us try different "looks". --Jutta ** ** Parameters: ** s -- (pointer to) string to scan ** e -- end of string ** name_out -- name (output) ** name_n_out -- length of name_out (output) ** sub_out -- sub (output) ** sub_n_out -- length of sub_out (output) ** ** Returns: ** true/false: did we get a token? */ static bool pathname_token( char const **s, char const *e, char const **name_out, size_t *name_n_out, char const **sub_out, size_t *sub_n_out) { char const *p, *q; char cl; static char const obraces[] = "<({[\"'`", cbraces[] = ">)}]\"''"; SM_REQUIRE(name_out != NULL); SM_REQUIRE(name_n_out != NULL); SM_REQUIRE(sub_out != NULL); SM_REQUIRE(sub_n_out != NULL); *name_out = NULL; *name_n_out = 0; *sub_out = NULL; *sub_n_out = 0; /* skip leading garbage */ for (p = *s; p < e && !isalnum(*p) && *p != '_'; p++) ; /* skip an identifier */ *name_out = p; for (; p < e && !(*p & 0x80) && (isalnum(*p) || *p == '_'); p++) ; if (p == *name_out) return false; *name_n_out = p - *name_out; /* ** If we've got some sort of bracketing action, this names both a ** section type and its title. Assign the title to the "sub" ** output parameter. */ if (*p != '\0' && (q = strchr(obraces, *p)) != NULL) { p++; *sub_out = p; cl = cbraces[q - obraces]; for (; p < e && *p != cl; p++) { if (*p == '\\') { p++; /* backslash as last char? -> error! */ if (p >= e) return 0; } } *sub_n_out = p - *sub_out; } /* skip trailing garbage */ for (; p < e && !isalnum(*p) && *p != '_'; p++) ; *s = p; return true; } /* ** SM_CONF_GET_NODE_SCAN -- scan a type ** ** Parameters: ** smc -- handle ** parent -- NULL or node relative to which we're searching ** name -- path name of the option or section ** type -- type of the result value ** type_data -- parameters of the type. ** flags -- SM_CONF_FLAG_*, controls conversion process. ** data -- assign the result to the pointed-to address. ** size -- # of bytes pointed to by . ** ** Returns: ** 0 on success, ** ** SM_CONF_ERR_NOT_FOUND if no value is available, ** other errors as returned by the type. */ static int sm_conf_get_node_scan( sm_conf_T *smc, sm_conf_node_T *parent, char const *name, sm_conf_type_T const *type, void const *type_data, unsigned int flags, void *data, size_t size) { sm_conf_definition_T def[2]; def[0].sm_magic = SM_CONF_DEF_MAGIC; def[0].scd_name = name; def[0].scd_type = type; def[0].scd_offset = 0; def[0].scd_size = size; def[0].scd_flags = flags; def[0].scd_contents = type_data; def[0].scd_default = NULL; def[0].scd_check = NULL; def[0].scd_check_data = NULL; def[1].scd_name = NULL; return sm_conf_scan_node(smc, def, parent, data); } /* ** SM_CONF_GET_RELATIVE -- get a property relative to a node. ** ** Parameters: ** smc -- handle ** node -- NULL or node relative to which we're searching ** name -- path name of the option or section ** type -- type of the result value ** type_data -- ancillary data for result value. ** flags -- SM_CONF_FLAG_* flags, or'ed together ** data -- assign the result to the pointed-to address. ** size -- # of bytes pointed to by . ** ** Returns: ** 0 on success, ** SM_CONF_ERR_NOT_FOUND if no value is available, ** other errors as returned by the type. */ int sm_conf_get_relative( sm_conf_T *smc, sm_conf_node_T *node, char const *path, sm_conf_type_T const *type, void const *type_data, unsigned int flags, void *data, size_t size) { char const *kw, *name, *elem; size_t kw_n, name_n, elem_n; int err; sm_conf_node_T *sub; if (path == NULL) return sm_conf_get_node_scan(smc, node, "", type, type_data, flags, data, size); if (!pathname_token(&path, path + strlen(path), &kw, &kw_n, &name, &name_n)) return sm_conf_get_node_scan(smc, node, "", type, type_data, flags, data, size); if (node == NULL) return SM_CONF_ERR_NOT_FOUND; if ( type != sm_conf_type_section && *path == '\0' && name == NULL) { /* We may be looking for a property. */ sub = NULL; while ((sub = sm_conf_section_next(smc, node, &elem, &elem_n, sub)) != NULL) { if (sm_memncaseeq(elem, elem_n, kw, kw_n)) { return sm_conf_get_node_scan(smc, sub, elem, type, type_data, flags, data, size); } } } /* We are looking only for a subsection. */ sub = NULL; while ((sub = sm_conf_section_next_subsection(smc, node, kw, kw_n, NULL, 0, sub)) != NULL) { if (sm_conf_section_name(smc, sub, &elem, &elem_n)) continue; if ( name == NULL ? elem == NULL : sm_conf_token_match( smc, name, name_n, elem, elem_n)) { err = sm_conf_get_relative(smc, sub, path, type, type_data, flags, data, size); if (err == 0) return err; } } return SM_CONF_ERR_NOT_FOUND; } /* ** SM_CONF_GET -- get the value of a configuration option. ** ** Parameters: ** smc -- handle ** name -- path name of the option or section ** type -- type of the result value ** type_data -- ancillary data for result value. ** flags -- SM_CONF_FLAG_* flags, or'ed together ** data -- assign the result to the pointed-to address. ** size -- # of bytes pointed to by . ** ** Returns: ** 0 on success, ** SM_CONF_ERR_NOT_FOUND if no value is available, ** other errors as returned by the type. */ int sm_conf_get( sm_conf_T *smc, char const *name, sm_conf_type_T const *type, void const *type_data, unsigned int flags, void *data, size_t size) { if (smc == NULL) return SM_CONF_ERR_INVALID; return sm_conf_get_relative( smc, smc->smc_root, name, type, type_data, flags, data, size); } /* ** SM_CONF_SCAN_NEXT_RELATIVE -- get a property relative to a parent node. ** ** Parameters: ** smc -- handle ** parent -- a node that is relative to, or NULL ** path -- pathname of the section we're searching for ** defs -- definition of the section elements ** flags -- SM_CONF_FLAG_* flags for the section ** name_out -- NULL or a place for a pointer to the section's name ** name_n_out -- NULL or a place for the section's name's length ** data -- assign the scanned section data to the pointed-to ** address. ** iter -- iteration pointer. Must point to NULL initially; ** otherwise opaque to the application. ** ** Returns: ** 0 on success, ** SM_CONF_ERR_NOT_FOUND if no value is available, ** other errors as returned by the type. */ int sm_conf_scan_next_relative( sm_conf_T *smc, sm_conf_node_T *parent, char const *path, sm_conf_definition_T const *defs, unsigned int flags, char const **name_out, size_t *name_n_out, void *data, void **iter) { int err; sm_conf_node_T *prev, *my_prev; sm_conf_definition_T section_def[2]; char const *name, *elem, *kw; size_t name_n, elem_n, kw_n; if (smc == NULL) return SM_CONF_ERR_INVALID; if (defs == NULL || defs->scd_name == NULL) return SM_CONF_ERR_INVALID; SM_IS_CONF_DEF(defs); if (name_out != NULL) *name_out = NULL; if (name_n_out != NULL) *name_n_out = 0; if ( parent == NULL && (parent = smc->smc_root) == NULL) return SM_CONF_ERR_NOT_FOUND; if (path == NULL) path = ""; prev = (iter == NULL ? NULL : *iter); /* ** If we run out of here, is the node we're ** looking for; assign it. */ if (!pathname_token(&path, path + strlen(path), &kw, &kw_n, &name, &name_n)) { if (prev != NULL) return SM_CONF_ERR_NOT_FOUND; /* ** If our definitions have a non-empty name, they're ** the contents of a section that we're looking for. ** Make the obvious definition for that section. */ if (defs->scd_name[0] != '\0') { sm_memset(§ion_def, 0, sizeof section_def); section_def[0].sm_magic = SM_CONF_DEF_MAGIC; section_def[0].scd_name = ""; section_def[0].scd_type = sm_conf_type_section; section_def[0].scd_default = NULL; section_def[0].scd_flags = flags; section_def[0].scd_contents = defs; section_def[1].scd_name = NULL; defs = section_def; } err = sm_conf_scan_node(smc, defs, parent, data); if (err == 0) { if (sm_conf_section_name(smc, parent, &name, &name_n) == 0) { if (name_out != NULL) *name_out = name; if (name_n_out != NULL) *name_n_out = name_n; } if (iter != NULL) *iter = parent; } return err; } /* ** my_prev := the node directly below that's above ** or equal to */ my_prev = prev; while (my_prev != NULL) { sm_conf_node_T *par; par = sm_conf_node_parent(smc, my_prev); if (par == parent) break; my_prev = par; } /* The `prev' pointer must be either NULL or below . */ if (prev != NULL && my_prev == NULL) return SM_CONF_ERR_INVALID; /* ** If we have a , and we can iterate successfully below ** that, use the result of that iteration. */ if (my_prev != NULL && my_prev != prev) { err = sm_conf_scan_next_relative( smc, my_prev, path, defs, flags, name_out, name_n_out, data, iter); if (err != SM_CONF_ERR_NOT_FOUND) return err; } /* ** We need to step outside the subtree controlled ** by . */ *iter = NULL; while ((my_prev = sm_conf_section_next_subsection(smc, parent, kw, kw_n, NULL, 0, my_prev)) != NULL) { /* The kw matches. How about the name? */ if (sm_conf_section_name(smc, my_prev, &elem, &elem_n) != 0) continue; if ( name == NULL || ( elem != NULL && sm_conf_token_match(smc, name, name_n, elem, elem_n))) { err = sm_conf_scan_next_relative( smc, my_prev, path, defs, flags, name_out, name_n_out, data, iter); if (err != SM_CONF_ERR_NOT_FOUND) return err; } } return SM_CONF_ERR_NOT_FOUND; } /* ** SM_CONF_SCAN_NEXT -- scan a named section. ** ** Successive calls to sm_conf_scan_next() return scanned ** successive instances of sections that match a pathname. ** ** Parameters: ** smc -- handle ** path -- pathname of the section we're searching for ** defs -- array of definitions of the section elements, ** terminated by a definition with NULL name. ** flags -- SM_CONF_FLAG_* flags for the section ** name_out -- NULL or a place for a pointer to the section's name ** name_n_out -- NULL or a place for the section's name's length ** data -- assign the scanned section data to the pointed-to ** address. ** iter -- iteration pointer. Must points to NULL initially; ** otherwise opaque to the application. ** ** Returns: ** 0 on success, ** SM_CONF_ERR_NOT_FOUND if no value is available, ** other errors as returned by the type. */ int sm_conf_scan_next( sm_conf_T *smc, char const *path, sm_conf_definition_T const *defs, unsigned int flags, char const **name_out, size_t *name_n_out, void *data, void **iter) { return sm_conf_scan_next_relative( smc, NULL, path, defs, flags, name_out, name_n_out, data, iter); }