/******************************************************************** * FUNCTION skip_object * * Skip until the end of the object * current token is the parmset name * * INPUTS: * tkc == token chain * * RETURNS: * status of the operation *********************************************************************/ static status_t skip_object (tk_chain_t *tkc) { status_t res; uint32 brace_count; boolean done; brace_count = 0; done = FALSE; /* get the next token */ while (!done) { res = TK_ADV(tkc); if (res != NO_ERR) { return res; } switch (TK_CUR_TYP(tkc)) { case TK_TT_LBRACE: brace_count++; break; case TK_TT_RBRACE: if (brace_count <= 1) { done = TRUE; } else { brace_count--; } default: ; } } return NO_ERR; } /* skip_object */
/******************************************************************** * 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 advance_token * * Move to the next token * * INPUTS: * *********************************************************************/ static status_t advance_token (yang_pcb_t *pcb) { status_t res; res = TK_ADV(pcb->tkc); #ifdef YANGYIN_DEBUG if (res == NO_ERR && LOGDEBUG3) { tk_dump_token(pcb->tkc->cur); } #endif return res; } /* advance_token */
/******************************************************************** * FUNCTION get_tk * * Get the next token * Skip over TK_TT_NEWLINE until a different type is found * * INPUTS: * tkc == token chain * * RETURNS: * status *********************************************************************/ static status_t get_tk (tk_chain_t *tkc) { boolean done; status_t res; res = NO_ERR; done = FALSE; while (!done) { res = TK_ADV(tkc); if (res != NO_ERR) { done = TRUE; } else if (TK_CUR_TYP(tkc) != TK_TT_NEWLINE) { done = TRUE; } } return res; } /* get_tk */
/******************************************************************** * FUNCTION process_do_command * (config mode input received) * Handle the do command escape * * INPUTS: * server_cb == server control block to use * session_cb == session control block to use * tkc == token chain in process * line == command line received * * RETURNS: * status *********************************************************************/ static status_t process_do_command (server_cb_t *server_cb, session_cb_t *session_cb, tk_chain_t *tkc, const xmlChar *line) { status_t res = TK_ADV(tkc); if (res != NO_ERR) { log_error("\nError: expected a command name\n"); return res; } if (TK_CUR_TYP(tkc) != TK_TT_TSTRING) { log_error("\nError: expected a command name\n"); return ERR_NCX_WRONG_TKTYPE; } res = run_do_command(server_cb, session_cb, &line[2]); set_completion_state_config_mode(&server_cb->completion_state); return res; } /* process_do_command */
/******************************************************************** * 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 */
/******************************************************************** * FUNCTION parse_node * (config mode input received) * Parse the next word using the comstate and session_cb state * Expecting this word to represent a datastore node, not a key or value * * e.g., * interface eth0 mtu 1500 * ^ ^ ^ ^ * node key node value * * INPUTS: * tkc == token chain in progress * session_cb == session control block to use * done == address of return done flag * gotexit == address of return gotexit flag * gotapply == address of return gotapply flag * gotdo == address of return do command flag * * OUTPUTS: * *done == TRUE if parsing done; no more tokens; * ignore unless return NO_ERR * *gotexit == TRUE if got 'exit' command * ignore unless return NO_ERR and *done == TRUE * *gotapply == TRUE if got 'apply' command * ignore unless return NO_ERR and *done == TRUE * *gotdo == TRUE if got 'do' command * RETURNS: * status *********************************************************************/ static status_t parse_node (tk_chain_t *tkc, session_cb_t *session_cb, boolean *done, boolean *gotexit, boolean *gotapply, boolean *gotdo) { *done = FALSE; *gotexit = FALSE; *gotapply = FALSE; *gotdo = FALSE; /* get the next token to use */ status_t res = TK_ADV(tkc); if (res != NO_ERR) { if (res == ERR_NCX_EOF) { *done = TRUE; return NO_ERR; } else { log_error("\nError: Expecting node identifier\n"); return res; } } boolean first = tk_cur_is_first(tkc); /* check if the 'do' form of command was given */ if (first && TK_CUR_TYP(tkc) == TK_TT_TSTRING && !xml_strcmp(TK_CUR_VAL(tkc), NCX_EL_DO) && tk_next_typ(tkc) != TK_TT_NONE) { *gotdo = TRUE; *done = TRUE; return NO_ERR; } /* check if the 'no' form of command was given */ if (first && TK_CUR_TYP(tkc) == TK_TT_TSTRING && !xml_strcmp(TK_CUR_VAL(tkc), NCX_EL_NO) && tk_next_typ(tkc) != TK_TT_NONE) { /* get the next token to use as the first identifier */ res = TK_ADV(tkc); if (res != NO_ERR) { log_error("\nError: Expecting identifier token after " "'no' keyword\n"); return res; } first = FALSE; session_cb->config_no_active = TRUE; } /* check if the current token is an identifier */ if (!TK_CUR_ID(tkc)) { log_error("\nError: expecting a node identifier\n"); res = ERR_NCX_WRONG_TKTYPE; return res; } /* check if the exit command was given */ if (first && TK_CUR_TYP(tkc) == TK_TT_TSTRING && !xml_strcmp(TK_CUR_VAL(tkc), NCX_EL_EXIT) && tk_next_typ(tkc) == TK_TT_NONE) { *done = TRUE; *gotexit = TRUE; return NO_ERR; } /* check if the apply command was given */ if (first && TK_CUR_TYP(tkc) == TK_TT_TSTRING && !xml_strcmp(TK_CUR_VAL(tkc), NCX_EL_APPLY) && tk_next_typ(tkc) == TK_TT_NONE) { *done = TRUE; *gotapply = TRUE; return NO_ERR; } obj_template_t *startobj = session_cb->config_curobj; obj_template_t *topobj = NULL; if (startobj == NULL) { /* getting top-level config object */ topobj = find_top_obj(session_cb, TK_CUR_VAL(tkc)); if (topobj && !(obj_is_data_db(topobj) && obj_is_config(topobj))) { topobj = NULL; } } else { /* getting child node config object */ topobj = find_child_obj(session_cb, startobj, TK_CUR_VAL(tkc)); if (topobj && !obj_is_config(topobj)) { topobj = NULL; } } if (topobj == NULL) { log_error("\nError: No config object found matching '%s'\n", TK_CUR_VAL(tkc)); return ERR_NCX_UNKNOWN_OBJECT; } /* got a top object (relative to the current context) */ session_cb->config_curobj = topobj; /* make a template object for the edit operation for non-terminals */ val_value_t *newval = NULL; if (!obj_is_leafy(topobj)) { newval = val_new_value(); if (newval == NULL) { log_error("\nError: malloc failed for new value\n"); return ERR_INTERNAL_MEM; } val_init_from_template(newval, topobj); add_enode(session_cb, newval); } else if (session_cb->config_no_active && obj_is_leaf(topobj)) { /* no ... foo-leaf */ newval = xml_val_new_flag(obj_get_name(topobj), obj_get_nsid(topobj)); if (newval == NULL) { log_error("\nError: malloc failed for new value\n"); return ERR_INTERNAL_MEM; } add_enode(session_cb, newval); } if (obj_is_list(topobj)) { boolean anykeys = FALSE; obj_key_t *newkey = obj_first_key(topobj); while (newkey) { anykeys = TRUE; /* get the next token to use as the key value */ res = TK_ADV(tkc); if (res != NO_ERR) { log_error("\nError: Expecting value for '%s' key leaf\n", obj_get_name(newkey->keyobj)); return res; } /* parse the value as a key value */ /*** do something different if TK_CUR_MOD(tkc) is not NULL ***/ val_value_t *keyval = val_make_simval_obj(newkey->keyobj, TK_CUR_VAL(tkc), &res); if (res != NO_ERR) { log_error("\nError: Invalid value for '%s' key leaf\n", obj_get_name(newkey->keyobj)); val_free_value(keyval); return res; } /* save keyval */ if (newval) { val_add_child(keyval, newval); } else { val_free_value(keyval); } /* set up the next key in the list */ newkey = obj_next_key(newkey); } if (anykeys) { res = val_gen_index_chain(topobj, newval); } } if (res == NO_ERR && obj_is_leafy(topobj)) { /* expecting a value node to follow or done if 'no' command */ *done = TRUE; } return res; } /* parse_node */
/******************************************************************** * FUNCTION parse_value * (config mode input received) * Parse the next word using the comstate and session_cb state * Expecting this word to represent a leaf or leaf-list value * * e.g., * interface eth0 mtu 1500 * ^ ^ ^ ^ * node key node value * * INPUTS: * session_cb == session control block to use * tkc == token chain in progress * * RETURNS: * status *********************************************************************/ static status_t parse_value (session_cb_t *session_cb, tk_chain_t *tkc) { obj_template_t *obj = session_cb->config_curobj; if (obj == NULL) { return SET_ERROR(ERR_INTERNAL_VAL); } const xmlChar *usestr = NULL; status_t res = NO_ERR; boolean isleaf = obj_is_leaf(obj); boolean isempty = (obj_is_leafy(obj) && obj_get_basetype(obj) == NCX_BT_EMPTY); if (!session_cb->config_no_active && isempty) { /* do not get the next token; treat next token as a sibling * of this empty leaf or leaf-list */ usestr = EMPTY_STRING; } else { /* get the next token to use */ res = TK_ADV(tkc); if (session_cb->config_no_active) { /* got no [command] */ if (isleaf || isempty) { if (res == NO_ERR) { log_error("\nError: Not expecting value string for '%s' " "in 'no' command\n", obj_get_name(obj)); return ERR_NCX_EXTRA_NODE; } if (isleaf) { // node already added in parse_node return NO_ERR; } } else if (res != NO_ERR) { log_error("\nError: Expecting value string for node '%s'\n", obj_get_name(obj)); return res; } } else if (res != NO_ERR) { log_error("\nError: Expecting value string for node '%s'\n", obj_get_name(obj)); return res; } else { usestr = TK_CUR_VAL(tkc); } } /* parse the value as a leaf value */ /*** do something different if TK_CUR_MOD(tkc) is not NULL ***/ val_value_t *leafval = val_make_simval_obj(obj, usestr, &res); if (res != NO_ERR) { log_error("\nError: Invalid value for '%s' leaf%s\n", obj_get_name(obj), obj_is_leaf(obj) ? EMPTY_STRING : (const xmlChar *)"-list"); val_free_value(leafval); return res; } add_enode(session_cb, leafval); return NO_ERR; } /* parse_value */