/******************************************************************** * FUNCTION adv_tk * * Advance to the next token * Print error message if EOF found instead * * INPUTS: * tkc == token chain * * RETURNS: * status *********************************************************************/ static status_t adv_tk (tk_chain_t *tkc) { status_t res; res = TK_ADV(tkc); if (res != NO_ERR) { ncx_print_errormsg(tkc, NULL, res); } return res; } /* adv_tk */
/******************************************************************** * FUNCTION parse_index * * Parse, and fill the indexQ for one val_value_t struct during * processing of a text config file * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * The value name is the current token. * Based on the value typdef, the res of the tokens * comprising the value statement will be processed * * INPUTS: * tkc == token chain * obj == the object template to use for filling in 'val' * val == initialized value struct, without any value, * which will be filled in by this function * nsid == namespace ID to use for this value * * OUTPUTS: * indexQ filled in as tokens representing the index components * are parsed. NEWLINE tokens are skipped as needed * * RETURNS: * status of the operation *********************************************************************/ static status_t parse_index (tk_chain_t *tkc, obj_template_t *obj, val_value_t *val, xmlns_id_t nsid) { obj_key_t *indef, *infirst; val_value_t *inval; status_t res; infirst = obj_first_key(obj); /* first make value nodes for all the index values */ for (indef = infirst; indef != NULL; indef = obj_next_key(indef)) { /* advance to the next non-NEWLINE token */ res = get_tk(tkc); if (res != NO_ERR) { ncx_conf_exp_err(tkc, res, "index value"); return res; } /* check if a valid token is given for the index value */ if (TK_CUR_TEXT(tkc)) { inval = val_make_simval_obj(indef->keyobj, TK_CUR_VAL(tkc), &res); if (!inval) { ncx_conf_exp_err(tkc, res, "index value"); return res; } else { val_change_nsid(inval, nsid); val_add_child(inval, val); } } else { res = ERR_NCX_WRONG_TKTYPE; ncx_conf_exp_err(tkc, res, "index value"); return res; } } /* generate the index chain in the indexQ */ res = val_gen_index_chain(obj, val); if (res != NO_ERR) { ncx_print_errormsg(tkc, NULL, res); } return res; } /* parse_index */
/******************************************************************** * FUNCTION conf_parse_from_filespec * * Parse a file as an NCX text config file against * a specific parmset definition. Fill in an * initialized parmset (and could be partially filled in) * * Error messages are printed by this function!! * * If a value is already set, and only one value is allowed * then the 'keepvals' parameter will control whether that * value will be kept or overwitten * * INPUTS: * filespec == absolute path or relative path * This string is used as-is without adjustment. * val == value struct to fill in, must be initialized * already with val_new_value or val_init_value * keepvals == TRUE if old values should always be kept * FALSE if old vals should be overwritten * fileerr == TRUE to generate a missing file error * FALSE to return NO_ERR instead, if file not found * * RETURNS: * status of the operation *********************************************************************/ status_t conf_parse_val_from_filespec (const xmlChar *filespec, val_value_t *val, boolean keepvals, boolean fileerr) { tk_chain_t *tkc; FILE *fp; xmlChar *sourcespec; status_t res; #ifdef DEBUG if (!filespec || !val) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif if (*filespec == 0) { log_error("\nError: no config file name specified"); return ERR_NCX_INVALID_VALUE; } res = NO_ERR; sourcespec = ncx_get_source(filespec, &res); if (!sourcespec) { return res; } fp = fopen((const char *)sourcespec, "r"); if (!fp) { m__free(sourcespec); if (fileerr) { log_error("\nError: config file '%s' could not be opened", filespec); return ERR_FIL_OPEN; } else { return NO_ERR; } } if (LOGINFO) { log_info("\nLoading CLI parameters from '%s'", sourcespec); } m__free(sourcespec); sourcespec = NULL; /* get a new token chain */ res = NO_ERR; tkc = tk_new_chain(); if (!tkc) { res = ERR_INTERNAL_MEM; ncx_print_errormsg(NULL, NULL, res); fclose(fp); return res; } /* else setup the token chain and parse this config file */ tk_setup_chain_conf(tkc, fp, filespec); res = tk_tokenize_input(tkc, NULL); #ifdef CONF_TK_DEBUG if (LOGDEBUG3) { tk_dump_chain(tkc); } #endif if (res == NO_ERR) { res = parse_top(tkc, val, keepvals); } fclose(fp); tkc->fp = NULL; tk_free_chain(tkc); if (res != NO_ERR) { log_error("\nError: invalid .conf file '%s' (%s)", filespec, get_error_string(res)); } return res; } /* conf_parse_val_from_filespec */
/******************************************************************** * FUNCTION parse_parm * * Parse, and fill one val_value_t struct during * processing of a parmset * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * * INPUTS: * tkc == token chain * val == container val to fill in * keepvals == TRUE to save existing parms in 'ps', as needed * FALSE to overwrite old parms in 'ps', as needed * * RETURNS: * status of the operation *********************************************************************/ static status_t parse_parm (tk_chain_t *tkc, val_value_t *val, boolean keepvals) { obj_template_t *obj; const xmlChar *modname; val_value_t *curparm, *newparm; status_t res; ncx_iqual_t iqual; boolean match, usewarning, isdefault; /* get the next token, which must be a TSTRING * representing the parameter name */ if (TK_CUR_TYP(tkc) != TK_TT_TSTRING) { res = ERR_NCX_WRONG_TKTYPE; ncx_conf_exp_err(tkc, res, "parameter name"); return res; } curparm = NULL; usewarning = ncx_warning_enabled(ERR_NCX_CONF_PARM_EXISTS); /* check if this TSTRING is a parameter in this parmset * make sure to always check for prefix:identifier * This is automatically processed in tk.c */ if (TK_CUR_MOD(tkc)) { modname = xmlns_get_module (xmlns_find_ns_by_prefix(TK_CUR_MOD(tkc))); if (modname) { curparm = val_find_child(val, modname, TK_CUR_VAL(tkc)); } } else { curparm = val_find_child(val, val_get_mod_name(val), TK_CUR_VAL(tkc)); } if (curparm) { obj = curparm->obj; } else { obj = obj_find_child(val->obj, TK_CUR_MOD(tkc), TK_CUR_VAL(tkc)); } if (!obj) { res = ERR_NCX_UNKNOWN_PARM; if (TK_CUR_MOD(tkc)) { log_error("\nError: parameter '%s:%s' not found", TK_CUR_MOD(tkc), TK_CUR_VAL(tkc)); } else { log_error("\nError: parameter '%s' not found", TK_CUR_VAL(tkc)); } ncx_conf_exp_err(tkc, res, "parameter name"); return res; } /* got a valid parameter name, now create a new parm * even if it may not be kept. There are corner-cases * that require the new value be parsed before knowing * if a parm value is a duplicate or not */ newparm = val_new_value(); if (!newparm) { res = ERR_INTERNAL_MEM; ncx_print_errormsg(tkc, NULL, res); return res; } val_init_from_template(newparm, obj); /* parse the parameter value */ res = parse_val(tkc, obj, newparm); if (res != NO_ERR) { val_free_value(newparm); return res; } /* check if a potential current value exists, or just * add the newparm to the parmset */ if (curparm) { isdefault = val_set_by_default(curparm); iqual = obj_get_iqualval(obj); if (iqual == NCX_IQUAL_ONE || iqual == NCX_IQUAL_OPT) { /* only one allowed, check really a match */ match = TRUE; if (val_has_index(curparm) && !val_index_match(newparm, curparm)) { match = FALSE; } if (!match) { val_add_child(newparm, val); } else if (isdefault) { dlq_remove(curparm); val_free_value(curparm); val_add_child(newparm, val); } else if (keepvals) { if (usewarning) { /* keep current value and toss new value */ log_warn("\nWarning: Parameter '%s' already exists. " "Not using new value\n", curparm->name); if (LOGDEBUG2) { val_dump_value(newparm, NCX_DEF_INDENT); log_debug2("\n"); } } val_free_value(newparm); } else { if (usewarning) { /* replace current value and warn old value tossed */ log_warn("\nconf: Parameter '%s' already exists. " "Overwriting with new value\n", curparm->name); if (LOGDEBUG2) { val_dump_value(newparm, NCX_DEF_INDENT); log_debug2("\n"); } } dlq_remove(curparm); val_free_value(curparm); val_add_child(newparm, val); } } else { /* mutliple instances allowed */ val_add_child(newparm, val); } } else { val_add_child(newparm, val); } return NO_ERR; } /* parse_parm */
/******************************************************************** * FUNCTION parse_val * * Parse, and fill one val_value_t struct during * processing of a text config file * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * The value name is the current token. * Based on the value typdef, the res of the tokens * comprising the value statement will be processed * * INPUTS: * tkc == token chain * obj == the object template struct to use for filling in 'val' * val == initialized value struct, without any value, * which will be filled in by this function * nsid == namespace ID to use for this value * valname == name of the value struct * * RETURNS: * status of the operation *********************************************************************/ static status_t parse_val (tk_chain_t *tkc, obj_template_t *obj, val_value_t *val) { obj_template_t *chobj; val_value_t *chval; const xmlChar *valname, *useval; typ_def_t *typdef; status_t res; ncx_btype_t btyp; boolean done; xmlns_id_t nsid; btyp = obj_get_basetype(obj); nsid = obj_get_nsid(obj); valname = obj_get_name(obj); typdef = obj_get_typdef(obj); /* check if there is an index clause expected */ if (typ_has_index(btyp)) { res = parse_index(tkc, obj, val, nsid); if (res != NO_ERR) { return res; } } /* get next token, NEWLINE is significant at this point */ res = adv_tk(tkc); if (res != NO_ERR) { return res; } /* the current token should be the value for a leaf * or a left brace for the start of a complex type * A NEWLINE is treated as if the user entered a * zero-length string for the value. (Unless the * base type is NCX_BT_EMPTY, in which case the NEWLINE * is the expected token */ if (typ_is_simple(btyp)) { /* form for a leaf is: foo [value] NEWLINE */ if (TK_CUR_TYP(tkc)==TK_TT_NEWLINE) { useval = NULL; } else { useval = TK_CUR_VAL(tkc); } res = val_set_simval(val, typdef, nsid, valname, useval); if (res != NO_ERR) { log_error("\nError: '%s' cannot be set to '%s'", valname, (TK_CUR_VAL(tkc)) ? TK_CUR_VAL(tkc) : EMPTY_STRING); if (btyp == NCX_BT_EMPTY) { ncx_conf_exp_err(tkc, res, "empty"); } else { ncx_conf_exp_err(tkc, res, "simple value string"); } return res; } /* get a NEWLINE unless current token is already a NEWLINE */ if (TK_CUR_TYP(tkc) != TK_TT_NEWLINE) { res = adv_tk(tkc); if (res != NO_ERR) { return res; } if (TK_CUR_TYP(tkc) != TK_TT_NEWLINE) { res = ERR_NCX_WRONG_TKTYPE; ncx_conf_exp_err(tkc, res, "\\n"); } } } else { /* complex type is foo { ... } or * foo index1 index2 { ... } * If there is an index, it was already parsed */ res = consume_tk(tkc, TK_TT_LBRACE); if (res != NO_ERR) { ncx_conf_exp_err(tkc, res, "left brace"); return res; } /* get all the child nodes specified for this complex type */ res = NO_ERR; done = FALSE; while (!done && res==NO_ERR) { /* start out looking for a child node name or a * right brace to end the sub-section */ if (tk_next_typ(tkc)==TK_TT_NEWLINE) { /* skip the NEWLINE token */ (void)adv_tk(tkc); } else if (tk_next_typ(tkc)==TK_TT_RBRACE) { /* found end of sub-section */ done = TRUE; } else { /* get the next token */ res = adv_tk(tkc); if (res != NO_ERR) { continue; } /* make sure cur token is an identifier string * if so, find the child node and call this function * recursively to fill it in and add it to * the parent 'val' */ if (TK_CUR_ID(tkc)) { /* parent 'typdef' must have a child with a name * that matches the current token vale */ chobj = obj_find_child(obj, TK_CUR_MOD(tkc), TK_CUR_VAL(tkc)); if (chobj) { chval = val_new_value(); if (!chval) { res = ERR_INTERNAL_MEM; ncx_print_errormsg(tkc, NULL, res); } else { val_init_from_template(chval, chobj); res = parse_val(tkc, chobj, chval); if (res == NO_ERR) { val_add_child(chval, val); } else { val_free_value(chval); } } } else { /* string is not a child name in this typdef */ res = ERR_NCX_DEF_NOT_FOUND; ncx_conf_exp_err(tkc, res, "identifier string"); } } else { /* token is not an identifier string */ res = ERR_NCX_WRONG_TKTYPE; ncx_conf_exp_err(tkc, res, "identifier string"); } } } /* end loop through all the child nodes */ /* expecting a right brace to finish the complex value */ if (res == NO_ERR) { res = consume_tk(tkc, TK_TT_RBRACE); if (res != NO_ERR) { ncx_conf_exp_err(tkc, res, "right brace"); return res; } } } return res; } /* parse_val */
/******************************************************************** * FUNCTION yang_ext_consume_extension * * Parse the next N tokens as an extension-stmt * Create an ext_template_t struct and add it to the specified Q * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * Current token is the 'extension' keyword * * INPUTS: * tkc == token chain * mod == module in progress * * RETURNS: * status of the operation *********************************************************************/ status_t yang_ext_consume_extension (tk_chain_t *tkc, ncx_module_t *mod) { ext_template_t *ext, *testext; const xmlChar *val; const char *expstr; yang_stmt_t *stmt; tk_type_t tktyp; boolean done, arg, stat, desc, ref; status_t res, retres; #ifdef DEBUG if (!tkc || !mod) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif val = NULL; expstr = "keyword"; done = FALSE; arg = FALSE; stat = FALSE; desc = FALSE; ref = FALSE; res = NO_ERR; retres = NO_ERR; /* Get a new ext_template_t to fill in */ ext = ext_new_template(); if (!ext) { res = ERR_INTERNAL_MEM; ncx_print_errormsg(tkc, mod, res); return res; } ncx_set_error(&ext->tkerr, mod, TK_CUR_LNUM(tkc), TK_CUR_LPOS(tkc)); /* Get the mandatory extension name */ res = yang_consume_id_string(tkc, mod, &ext->name); CHK_EXT_EXIT; /* Get the starting left brace for the sub-clauses * or a semi-colon to end the extension-stmt */ res = TK_ADV(tkc); if (res != NO_ERR) { ncx_print_errormsg(tkc, mod, res); ext_free_template(ext); return res; } switch (TK_CUR_TYP(tkc)) { case TK_TT_SEMICOL: done = TRUE; break; case TK_TT_LBRACE: break; default: retres = ERR_NCX_WRONG_TKTYPE; expstr = "semi-colon or left brace"; ncx_mod_exp_err(tkc, mod, retres, expstr); done = TRUE; } /* get the extension statements and any appinfo extensions */ while (!done) { /* get the next token */ res = TK_ADV(tkc); if (res != NO_ERR) { ncx_print_errormsg(tkc, mod, res); ext_free_template(ext); return res; } tktyp = TK_CUR_TYP(tkc); val = TK_CUR_VAL(tkc); /* check the current token type */ switch (tktyp) { case TK_TT_NONE: res = ERR_NCX_EOF; ncx_print_errormsg(tkc, mod, res); ext_free_template(ext); return res; case TK_TT_MSTRING: /* vendor-specific clause found instead */ res = ncx_consume_appinfo(tkc, mod, &ext->appinfoQ); CHK_EXT_EXIT; continue; case TK_TT_RBRACE: done = TRUE; continue; case TK_TT_TSTRING: break; /* YANG clause assumed */ default: retres = ERR_NCX_WRONG_TKTYPE; ncx_mod_exp_err(tkc, mod, retres, expstr); continue; } /* Got a token string so check the value */ if (!xml_strcmp(val, YANG_K_ARGUMENT)) { res = consume_yang_arg(tkc, mod, ext, &arg); } else if (!xml_strcmp(val, YANG_K_STATUS)) { res = yang_consume_status(tkc, mod, &ext->status, &stat, &ext->appinfoQ); } else if (!xml_strcmp(val, YANG_K_DESCRIPTION)) { res = yang_consume_descr(tkc, mod, &ext->descr, &desc, &ext->appinfoQ); } else if (!xml_strcmp(val, YANG_K_REFERENCE)) { res = yang_consume_descr(tkc, mod, &ext->ref, &ref, &ext->appinfoQ); } else { res = ERR_NCX_WRONG_TKVAL; ncx_mod_exp_err(tkc, mod, res, expstr); } CHK_EXT_EXIT; } /* save or delete the ext_template_t struct */ if (ext->name && ncx_valid_name2(ext->name)) { testext = ext_find_extension_all(mod, ext->name); if (testext) { log_error("\nError: extension '%s' already defined " "in '%s' at line %u", ext->name, testext->tkerr.mod->name, testext->tkerr.linenum); retres = ERR_NCX_DUP_ENTRY; ncx_print_errormsg(tkc, mod, retres); ext_free_template(ext); } else { dlq_enque(ext, &mod->extensionQ); /* may have some errors */ if (mod->stmtmode) { stmt = yang_new_ext_stmt(ext); if (stmt) { dlq_enque(stmt, &mod->stmtQ); } else { log_error("\nError: malloc failure for ext_stmt"); retres = ERR_INTERNAL_MEM; ncx_print_errormsg(tkc, mod, retres); } } } } else { ext_free_template(ext); } return retres; } /* yang_ext_consume_extension */
/******************************************************************** * FUNCTION consume_yang_arg * * Parse the next N tokens as an argument-stmt * Fill in the arg anf argel fields in the ext_template_t struct * * Error messages are printed by this function!! * Do not duplicate error messages upon error return * * Current token is the 'argument' keyword * * INPUTS: * tkc == token chain * mod == module in progress * ext == extension template in progress * argdone == address of duplicate entry error flag * * OUTPUTS: * *argdone == TRUE upon exit * * RETURNS: * status of the operation *********************************************************************/ static status_t consume_yang_arg (tk_chain_t *tkc, ncx_module_t *mod, ext_template_t *ext, boolean *argdone) { const xmlChar *val; const char *expstr; xmlChar *errstr; dlq_hdr_t errQ; tk_type_t tktyp; boolean done, yinel, save, errsave; status_t res, retres; #ifdef DEBUG if (!tkc || !mod) { return SET_ERROR(ERR_INTERNAL_PTR); } #endif val = NULL; expstr = NULL; done = FALSE; yinel = FALSE; save = TRUE; res = NO_ERR; retres = NO_ERR; dlq_createSQue(&errQ); /* check duplicate entry error first */ if (*argdone) { retres = ERR_NCX_ENTRY_EXISTS; ncx_print_errormsg(tkc, mod, retres); save = FALSE; } else { *argdone = TRUE; } /* Get the mandatory argument name */ if (save) { res = yang_consume_id_string(tkc, mod, &ext->arg); } else { errstr = NULL; res = yang_consume_id_string(tkc, mod, &errstr); if (errstr) { m__free(errstr); } } CHK_EXIT(res, retres); /* Get the starting left brace for the sub-clauses * or a semi-colon to end the extension-stmt */ res = TK_ADV(tkc); if (res != NO_ERR) { ncx_print_errormsg(tkc, mod, res); return res; } switch (TK_CUR_TYP(tkc)) { case TK_TT_SEMICOL: done = TRUE; break; case TK_TT_LBRACE: break; default: retres = ERR_NCX_WRONG_TKTYPE; expstr = "semi-colon or left brace"; ncx_mod_exp_err(tkc, mod, retres, expstr); done = TRUE; } /* get the extension statements and any appinfo extensions */ while (!done) { /* get the next token */ res = TK_ADV(tkc); if (res != NO_ERR) { ncx_print_errormsg(tkc, mod, res); return res; } tktyp = TK_CUR_TYP(tkc); val = TK_CUR_VAL(tkc); /* check the current token type */ switch (tktyp) { case TK_TT_NONE: res = ERR_NCX_EOF; ncx_print_errormsg(tkc, mod, res); return res; case TK_TT_MSTRING: /* vendor-specific clause found instead */ if (save) { res = ncx_consume_appinfo(tkc, mod, &ext->appinfoQ); } else { res = ncx_consume_appinfo(tkc, mod, &errQ); ncx_clean_appinfoQ(&errQ); } CHK_EXIT(res, retres); continue; case TK_TT_RBRACE: done = TRUE; continue; case TK_TT_TSTRING: break; /* YANG clause assumed */ default: retres = ERR_NCX_WRONG_TKTYPE; ncx_mod_exp_err(tkc, mod, retres, expstr); continue; } /* Got a token string so check the value */ if (!xml_strcmp(val, YANG_K_YIN_ELEMENT)) { if (save) { res = yang_consume_boolean(tkc, mod, &ext->argel, &yinel, &ext->appinfoQ); } else { res = yang_consume_boolean(tkc, mod, &errsave, NULL, NULL); } } else { res = ERR_NCX_WRONG_TKVAL; ncx_mod_exp_err(tkc, mod, res, expstr); } CHK_EXIT(res, retres); } return retres; } /* consume_yang_arg */