/******************************************************************** * FUNCTION end_ext_elem * * Generate an end tag * * INPUTS: * scb == session control block to use for writing * name == element name * indent == indent count * *********************************************************************/ static void end_ext_elem (ses_cb_t *scb, const xmlChar *prefix, const xmlChar *name, int32 indent) { ses_putstr_indent(scb, (const xmlChar *)"</", indent); ses_putstr(scb, prefix); ses_putchar(scb, ':'); ses_putstr(scb, name); ses_putchar(scb, '>'); } /* end_ext_elem */
/******************************************************************** * FUNCTION write_yin_attr * * Generate an attibute declaration * * INPUTS: * scb == session control block to use for writing * attr == attribute name * value == attribute value string * indent == start indent count * *********************************************************************/ static void write_yin_attr (ses_cb_t *scb, const xmlChar *attr, const xmlChar *value, int32 indent) { ses_putstr_indent(scb, attr, indent); ses_putchar(scb, '='); ses_putchar(scb, '"'); ses_putstr(scb, value); ses_putchar(scb, '"'); } /* write_yin_attr */
/******************************************************************** * FUNCTION write_json_string_value * * Write a simple value as a JSON string * * INPUTS: * scb == session control block * val == value to write * * RETURNS: * status *********************************************************************/ static status_t write_json_string_value (ses_cb_t *scb, val_value_t *val) { xmlChar *valstr = val_make_sprintf_string(val); if (valstr) { ses_putchar(scb, '"'); ses_putjstr(scb, valstr, -1); ses_putchar(scb, '"'); m__free(valstr); return NO_ERR; } return ERR_INTERNAL_MEM; } /* write_json_string_value */
/******************************************************************** * FUNCTION write_cur_token * * Write the full value of the current token to the session * * INPUTS: * scb == session control block * pcb == YANG parser control block * elem == TRUE if this is element content; FALSE if attribute *********************************************************************/ static void write_cur_token (ses_cb_t *scb, yang_pcb_t *pcb, boolean elem) { const xmlChar *prefix, *value; prefix = TK_CUR_MOD(pcb->tkc); if (prefix != NULL) { if (elem) { ses_putcstr(scb, prefix, -1); } else { ses_putastr(scb, prefix, -1); } ses_putchar(scb, ':'); } value = TK_CUR_VAL(pcb->tkc); if (value != NULL) { if (elem) { ses_putcstr(scb, value, -1); } else { ses_putastr(scb, value, -1); } } } /* write_cur_token */
/******************************************************************** * FUNCTION write_import_xmlns * * Generate an xmlns attribute for the import * * INPUTS: * scb == session control block to use for writing * imp == ncx_import_t struct to use * indent == start indent count * *********************************************************************/ static void write_import_xmlns (ses_cb_t *scb, ncx_import_t *imp, int32 indent) { if (imp->mod == NULL) { return; } ses_putstr_indent(scb, XMLNS, indent); ses_putchar(scb, ':'); ses_putstr(scb, imp->prefix); ses_putchar(scb, '='); ses_putchar(scb, '"'); ses_putstr(scb, ncx_get_modnamespace(imp->mod)); ses_putchar(scb, '"'); } /* write_import_xmlns */
/******************************************************************** * FUNCTION yangyin_convert_module * * The YIN namespace will be the default namespace * The imported modules will use the xmlprefix in use * which is the YANG prefix unless it is a duplicate * * INPUTS: * pcb == parser control block of module to convert * This is returned from ncxmod_load_module_ex * cp == conversion parms to use * scb == session control block for writing output * * RETURNS: * status *********************************************************************/ status_t yangyin_convert_module (yang_pcb_t *pcb, const yangdump_cvtparms_t *cp, ses_cb_t *scb) { ncx_import_t *import; status_t res; int32 indent; res = NO_ERR; indent = cp->indent; /* start the XML document */ ses_putstr(scb, XML_START_MSG); /* write the top-level start-tag for [sub]module * do this special case so the XMLNS attributes * can be generated as well as the 'name' attribute */ start_yin_elem(scb, (pcb->top->ismod) ? YANG_K_MODULE : YANG_K_SUBMODULE, 0); ses_putchar(scb, ' '); /* write the name attribute */ write_yin_attr(scb, YANG_K_NAME, pcb->top->name, indent); /* write the YIN namespace decl as the default namespace */ write_yin_attr(scb, XMLNS, YIN_URN, indent); /* write the module prefix and module namespace */ if (pcb->top->ismod) { ses_putstr_indent(scb, XMLNS, indent); ses_putchar(scb, ':'); ses_putstr(scb, ncx_get_mod_prefix(pcb->top)); ses_putchar(scb, '='); ses_putchar(scb, '"'); ses_putstr(scb, ncx_get_modnamespace(pcb->top)); ses_putchar(scb, '"'); } /* write the xmlns decls for all the imports used * by this [sub]module */ for (import = (ncx_import_t *)dlq_firstEntry(&pcb->top->importQ); import != NULL; import = (ncx_import_t *)dlq_nextEntry(import)) { if (import->used) { write_import_xmlns(scb, import, indent); } } /* finish the top-level start tag */ ses_putchar(scb, '>'); res = write_yin_contents(pcb, cp, scb); /* finish the top-level element */ end_yin_elem(scb, (pcb->top->ismod) ? YANG_K_MODULE : YANG_K_SUBMODULE, 0); return res; } /* yangyin_convert_module */
/******************************************************************** * FUNCTION write_yin_stmt * * Go through the token chain and write YIN stmts * recursively if needed, until 1 YANG stmt is handled * * INPUTS: * pcb == parser control block of module to convert * This is returned from ncxmod_load_module_ex * cp == conversion parms to use * scb == session control block for writing output * startindent == start indent count * done == address of done return var to use * * RETURNS: * status *********************************************************************/ static status_t write_yin_stmt (yang_pcb_t *pcb, const yangdump_cvtparms_t *cp, ses_cb_t *scb, int32 startindent, boolean *done) { const yin_mapping_t *mapping; ncx_import_t *import; ext_template_t *extension; const xmlChar *prefix, *modprefix; status_t res; boolean loopdone; res = NO_ERR; *done = FALSE; /* expecting a keyword [string] stmt-end sequence * or the very last closing right brace */ if (TK_CUR_TYP(pcb->tkc) == TK_TT_RBRACE) { if (tk_next_typ(pcb->tkc) == TK_TT_NONE) { *done = TRUE; return NO_ERR; } else { return ERR_NCX_WRONG_TKTYPE; } } else if (!TK_CUR_ID(pcb->tkc)) { return ERR_NCX_WRONG_TKTYPE; } /* check the keyword type */ switch (TK_CUR_TYP(pcb->tkc)) { case TK_TT_TSTRING: /* YANG keyword */ mapping = yin_find_mapping(TK_CUR_VAL(pcb->tkc)); if (mapping == NULL) { return ERR_NCX_DEF_NOT_FOUND; } /* output keyword part */ start_yin_elem(scb, mapping->keyword, startindent); /* output [string] part if expected */ if (mapping->argname == NULL) { if (tk_next_typ(pcb->tkc) == TK_TT_LBRACE) { ses_putchar(scb, '>'); } } else { /* move token pointer to the argument string */ res = advance_token(pcb); if (res != NO_ERR) { return res; } /* write the string part * do not add any extra whiespace to the XML string */ if (mapping->elem) { ses_putchar(scb, '>'); /* encode argname,value as an element */ start_yin_elem(scb, mapping->argname, startindent + cp->indent); ses_putchar(scb, '>'); write_cur_token(scb, pcb, TRUE); end_yin_elem(scb, mapping->argname, -1); } else { /* encode argname,value as an attribute */ ses_putchar(scb, ' '); ses_putstr(scb, mapping->argname); ses_putchar(scb, '='); ses_putchar(scb, '"'); write_cur_token(scb, pcb, FALSE); ses_putchar(scb, '"'); if (tk_next_typ(pcb->tkc) != TK_TT_SEMICOL) { ses_putchar(scb, '>'); } /* else end with empty element */ } } /* move token pointer to the stmt-end char */ res = advance_token(pcb); if (res != NO_ERR) { return res; } switch (TK_CUR_TYP(pcb->tkc)) { case TK_TT_SEMICOL: /* advance to next stmt, this one is done */ res = advance_token(pcb); if (res != NO_ERR) { return res; } if (mapping->elem) { /* end the complex element */ end_yin_elem(scb, mapping->keyword, startindent); } else { /* end the empty element */ ses_putstr(scb, (const xmlChar *)" />"); } break; case TK_TT_LBRACE: /* advance to next sub-stmt, this one has child nodes */ res = advance_token(pcb); if (res != NO_ERR) { return res; } /* write the nested sub-stmts as child nodes */ if (TK_CUR_TYP(pcb->tkc) != TK_TT_RBRACE) { loopdone = FALSE; while (!loopdone) { res = write_yin_stmt(pcb, cp, scb, startindent + cp->indent, done); if (res != NO_ERR) { return res; } if (TK_CUR_TYP(pcb->tkc) == TK_TT_RBRACE) { loopdone = TRUE; } } } /* move to next stmt, this one is done */ res = advance_token(pcb); if (res != NO_ERR) { return res; } /* end the complex element */ end_yin_elem(scb, mapping->keyword, startindent); break; default: return SET_ERROR(ERR_INTERNAL_VAL); } break; case TK_TT_MSTRING: /* extension keyword */ prefix = TK_CUR_MOD(pcb->tkc); modprefix = ncx_get_mod_prefix(pcb->top); if (modprefix != NULL && !xml_strcmp(prefix, modprefix)) { /* local module */ extension = ext_find_extension(pcb->top, TK_CUR_VAL(pcb->tkc)); } else { import = ncx_find_pre_import(pcb->top, prefix); if (import == NULL || import->mod == NULL) { return ERR_NCX_IMP_NOT_FOUND; } extension = ext_find_extension(import->mod, TK_CUR_VAL(pcb->tkc)); } if (extension == NULL) { return ERR_NCX_DEF_NOT_FOUND; } /* got the extension for this external keyword * output keyword part */ start_ext_elem(scb, prefix, TK_CUR_VAL(pcb->tkc), startindent); /* output [string] part if expected */ if (extension->arg == NULL) { if (tk_next_typ(pcb->tkc) == TK_TT_LBRACE) { ses_putchar(scb, '>'); } } else { /* move token pointer to the argument string */ res = advance_token(pcb); if (res != NO_ERR) { return res; } /* write the string part * do not add any extra whiespace chars to the string */ if (extension->argel) { ses_putchar(scb, '>'); /* encode argname,value as an element */ start_ext_elem(scb, prefix, extension->arg, startindent + cp->indent); ses_putchar(scb, '>'); write_cur_token(scb, pcb, TRUE); end_ext_elem(scb, prefix, extension->arg, -1); } else { /* encode argname,value as an attribute */ ses_putchar(scb, ' '); ses_putstr(scb, extension->arg); ses_putchar(scb, '='); ses_putchar(scb, '"'); write_cur_token(scb, pcb, FALSE); ses_putchar(scb, '"'); if (tk_next_typ(pcb->tkc) != TK_TT_SEMICOL) { ses_putchar(scb, '>'); } /* else end with empty element */ } } /* move token pointer to the stmt-end char */ res = advance_token(pcb); if (res != NO_ERR) { return res; } switch (TK_CUR_TYP(pcb->tkc)) { case TK_TT_SEMICOL: /* advance to next stmt, this one is done */ res = advance_token(pcb); if (res != NO_ERR) { return res; } if (extension->arg != NULL && extension->argel) { /* end the complex element */ end_ext_elem(scb, prefix, extension->name, startindent); } else { /* end the empty element */ ses_putstr(scb, (const xmlChar *)" />"); } break; case TK_TT_LBRACE: /* advance to next sub-stmt, this one has child nodes */ res = advance_token(pcb); if (res != NO_ERR) { return res; } /* write the nested sub-stmts as child nodes */ if (TK_CUR_TYP(pcb->tkc) != TK_TT_RBRACE) { loopdone = FALSE; while (!loopdone) { res = write_yin_stmt(pcb, cp, scb, startindent + cp->indent, done); if (res != NO_ERR) { return res; } if (TK_CUR_TYP(pcb->tkc) == TK_TT_RBRACE) { loopdone = TRUE; } } } /* move to next stmt, this one is done */ res = advance_token(pcb); if (res != NO_ERR) { return res; } /* end the complex element */ end_ext_elem(scb, prefix, extension->name, startindent); break; default: return SET_ERROR(ERR_INTERNAL_VAL); } break; default: return SET_ERROR(ERR_INTERNAL_VAL); } return res; } /* write_yin_stmt */
/******************************************************************** * 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 */