/******************************************************************** * FUNCTION write_full_check_val * * generate entire val_value_t *w/filter) * Write an entire val_value_t out as XML, including the top level * Using an optional testfn to filter output * * INPUTS: * scb == session control block * msg == xml_msg_hdr_t in progress * val == value to write * startindent == start indent amount if indent enabled * testcb == callback function to use, NULL if not used * justone == TRUE if just one of these; FALSE if more than one * isfirst == TRUE if this is the first (top) val printed * isfirstchild == TRUE if this is the first value of an array * == FALSE if this is the 2nd - Nth value of an array * RETURNS: * status *********************************************************************/ static status_t write_full_check_val (ses_cb_t *scb, xml_msg_hdr_t *msg, val_value_t *val, int32 startindent, val_nodetest_fn_t testfn, boolean justone, boolean isfirst, boolean isfirstchild) { val_value_t *out; boolean malloced = FALSE; status_t res = NO_ERR; out = val_get_value(scb, msg, val, testfn, TRUE, &malloced, &res); if (!out || res != NO_ERR) { if (res == ERR_NCX_SKIPPED) { res = NO_ERR; } if (out && malloced) { val_free_value(out); } return res; } /* check if this is an external file to send */ if (out->btyp == NCX_BT_EXTERN || out->btyp == NCX_BT_INTERN || typ_is_simple(out->btyp)) { if (isfirst) { write_json_string_value(scb, out); } else { /* write the name of the node */ ses_putchar(scb, '"'); ses_putjstr(scb, out->name, -1); ses_putchar(scb, '"'); ses_putchar(scb, ':'); switch (out->btyp) { case NCX_BT_EXTERN: ses_putchar(scb, '"'); val_write_extern(scb, out); ses_putchar(scb, '"'); break; case NCX_BT_INTERN: ses_putchar(scb, '"'); val_write_intern(scb, out); ses_putchar(scb, '"'); break; case NCX_BT_ENUM: ses_putchar(scb, '"'); if (VAL_ENUM_NAME(out)) { ses_putjstr(scb, VAL_ENUM_NAME(out), -1); } ses_putchar(scb, '"'); break; case NCX_BT_EMPTY: if (out->v.boo) { ses_putjstr(scb, NCX_EL_NULL, -1); } // else skip this value! should already be checked break; case NCX_BT_BOOLEAN: if (out->v.boo) { ses_putjstr(scb, NCX_EL_TRUE, -1); } else { ses_putjstr(scb, NCX_EL_FALSE, -1); } break; default: write_json_string_value(scb, out); } } } else { val_value_t *chval; val_value_t *lastch = NULL; val_value_t *nextch = NULL; int32 indent = (startindent < 0) ? -1 : startindent + ses_indent_count(scb); if (isfirst) { ses_putchar(scb, '{'); } /* render a complex type; either an object or an array */ if (isfirstchild) { ses_putchar(scb, '"'); ses_putjstr(scb, out->name, -1); ses_putchar(scb, '"'); ses_putchar(scb, ':'); if (!justone) { ses_putchar(scb, '['); } } ses_indent(scb, indent); ses_putchar(scb, '{'); for (chval = val_get_first_child(out); chval != NULL; chval = nextch) { /* JSON ignores XML namespaces, so foo:a and bar:a * are both encoded in the same array */ uint32 childcnt = val_instance_count(out, NULL, chval->name); boolean firstchild = (lastch && !xml_strcmp(lastch->name, chval->name)) ? FALSE : TRUE; lastch = chval; nextch = val_get_next_child(chval); res = write_full_check_val(scb, msg, chval, indent, testfn, (childcnt > 1) ? FALSE : TRUE, FALSE, firstchild); if (res == ERR_NCX_SKIPPED) { ; } else if (res != NO_ERR) { /* FIXME: do something about error; * may not always be OK to continue to next child node */ ; } else if (nextch) { ses_putchar(scb, ','); if (indent >= 0 && (typ_is_simple(nextch->btyp) || xml_strcmp(nextch->name, chval->name))) { ses_indent(scb, indent); ses_putchar(scb, ' '); } } } ses_indent(scb, indent); ses_putchar(scb, '}'); if (!justone) { val_value_t *peeknext = val_get_next_child(val); if (!peeknext || xml_strcmp(val->name, peeknext->name)) { ses_putchar(scb, ']'); } } if (isfirst) { ses_indent(scb, startindent); ses_putchar(scb, '}'); } } if (malloced && out) { val_free_value(out); } return res; } /* write_full_check_val */
/******************************************************************** * 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 show_user_var * * generate the output for a global or local variable * * INPUTS: * server_cb == server control block to use * varname == variable name to show * vartype == type of user variable * val == value associated with this variable * mode == help mode in use * * RETURNS: * status *********************************************************************/ static status_t show_user_var (server_cb_t *server_cb, const xmlChar *varname, var_type_t vartype, val_value_t *val, help_mode_t mode) { session_cb_t *session_cb = server_cb->cur_session_cb; xmlChar *objbuff; logfn_t logfn; boolean imode = interactive_mode(); int32 doubleindent = 1; status_t res = NO_ERR; if (imode) { logfn = log_stdout; } else { logfn = log_write; } switch (vartype) { case VAR_TYP_GLOBAL: case VAR_TYP_LOCAL: case VAR_TYP_SESSION: case VAR_TYP_SYSTEM: case VAR_TYP_CONFIG: if (xml_strcmp(varname, val->name)) { doubleindent = 2; (*logfn)("\n %s ", varname); if (val->obj && obj_is_data_db(val->obj)) { res = obj_gen_object_id(val->obj, &objbuff); if (res != NO_ERR) { (*logfn)("[no object id]\n "); } else { (*logfn)("[%s]\n ", objbuff); m__free(objbuff); } } } else if (session_cb->display_mode == NCX_DISPLAY_MODE_JSON) { (*logfn)("\n %s: ", varname); if (!typ_is_simple(val->btyp)) { (*logfn)("\n"); } } break; default: ; } if (!typ_is_simple(val->btyp) && mode == HELP_MODE_BRIEF) { if (doubleindent == 1) { (*logfn)("\n %s (%s)", varname, tk_get_btype_sym(val->btyp)); } else { (*logfn)("\n (%s)", tk_get_btype_sym(val->btyp)); } } else { val_dump_value_max(val, session_cb->defindent * doubleindent, session_cb->defindent, (imode) ? DUMP_VAL_STDOUT : DUMP_VAL_LOG, session_cb->display_mode, FALSE, FALSE); } return res; } /* show_user_var */