/** Handle start tag */ static void handle_starttag( PPOS* ppos ) { XML_NODE* node; char* name; assert(ppos != NULL); if ((name = get_name(ppos)) == NULL) { xml_error(ppos, "Missing name in tagstart"); ppos->state = STATE_ERROR; } else { if (NULL == (node = xml_new_node(name, ppos->lineno))) { xml_error(ppos, "Can't create new node"); ppos->state = STATE_ERROR; } else { xml_append_child(top_pstack(ppos), node); if ( push_pstack(ppos, node) ) ppos->state = STATE_IN_TAG; else ppos->state = STATE_ERROR; } BMSfreeMemoryArray(&name); } }
int api_board_all(void) { db_res_t *res = db_query(BOARD_SELECT_QUERY_BASE); if (!res) return WEB_ERROR_INTERNAL; xml_node_t *root = set_response_root("bbs-board-all", XML_NODE_ANONYMOUS_JSON, XML_ENCODING_UTF8); xml_node_t *boards = xml_new_node("boards", XML_NODE_CHILD_ARRAY); xml_add_child(root, boards); for (int i = db_res_rows(res) - 1; i >= 0; --i) { board_t board; res_to_board(res, i, &board); if (!has_read_perm(&board)) continue; xml_node_t *node = xml_new_node("board", XML_NODE_ANONYMOUS_JSON); board_to_node(&board, node); xml_add_child(boards, node); } db_clear(res); return WEB_OK; }
/* { groups: [ { name: OPTIONAL TEXT, descr: OPTIONAL TEXT, boards: [ { id: INTEGER, name: TEXT, unread: OPTIONAL BOOLEAN }, ... ] }, ... ] } */ int api_board_fav(void) { if (!session_id()) return WEB_ERROR_LOGIN_REQUIRED; xml_node_t *root = set_response_root("bbs-board-fav", XML_NODE_ANONYMOUS_JSON, XML_ENCODING_UTF8); xml_node_t *groups = xml_new_node("groups", XML_NODE_CHILD_ARRAY); xml_add_child(root, groups); query_t *q = query_new(0); query_select(q, "board, name, folder"); query_from(q, "fav_boards"); query_where(q, "user_id = %"DBIdUID, session_uid()); db_res_t *boards = query_exec(q); q = query_new(0); query_select(q, "id, name, descr"); query_from(q, "fav_board_folders"); query_where(q, "user_id = %"DBIdUID, session_uid()); db_res_t *folders = query_exec(q); if (folders && boards) { attach_group(groups, boards, FAV_BOARD_ROOT_FOLDER); for (int i = db_res_rows(folders) - 1; i >= 0; --i) { int id = db_get_integer(folders, i, 0); xml_node_t *group = attach_group(groups, boards, id); xml_attr_string(group, "name", db_get_value(folders, i, 1), true); xml_attr_string(group, "descr", db_get_value(folders, i, 2), true); } } db_clear(folders); db_clear(boards); return WEB_OK; }
/* Handles PCDATA */ static void proc_pcdata( PPOS* ppos /**< input stream position */ ) { XML_NODE* node; char* data = NULL; size_t size = 0; size_t len = 0; int c; assert(ppos != NULL); assert(ppos->state == STATE_PCDATA); #ifndef SPEC_LIKE_SPACE_HANDLING if ((c = skip_space(ppos)) != EOF) ungetsymbol(ppos, c); #endif c = getsymbol(ppos); while ((c != EOF) && (c != '<')) { if (len >= size - 1) /* leave space for terminating '\0' */ { size += DATA_EXT_SIZE; if ( data == NULL ) { ALLOC_ABORT( BMSallocMemoryArray(&data, size) ); } else { ALLOC_ABORT( BMSreallocMemoryArray(&data, size) ); } } assert(data != NULL); assert(size > len + 1); data[len++] = (char)c; c = getsymbol(ppos); } if (data == NULL) { if (c == EOF) ppos->state = STATE_EOF; else if (c == '<') { ppos->state = STATE_BEFORE; ungetsymbol(ppos, c); } else { ppos->state = STATE_ERROR; } } else { assert(len < size); data[len] = '\0'; if (c == EOF) ppos->state = STATE_ERROR; else { ungetsymbol(ppos, c); if (NULL == (node = xml_new_node("#PCDATA", ppos->lineno))) { xml_error(ppos, "Can't create new node"); ppos->state = STATE_ERROR; } else { BMSduplicateMemoryArray(&node->data, data, strlen(data)+1); xml_append_child(top_pstack(ppos), node); ppos->state = STATE_BEFORE; } BMSfreeMemoryArray(&data); } } }
/** Handles declarations that start with a <!. * * This includes comments. Does currenlty not work very well, because of DTDs. */ static void handle_decl( PPOS* ppos ) { enum XmlSection { IS_COMMENT, IS_ATTLIST, IS_DOCTYPE, IS_ELEMENT, IS_ENTITY, IS_NOTATION, IS_CDATA }; typedef enum XmlSection XMLSECTION; static struct { const char* name; XMLSECTION what; } key[] = { { "--", IS_COMMENT }, { "ATTLIST", IS_ATTLIST }, { "DOCTYPE", IS_DOCTYPE }, { "ELEMENT", IS_ELEMENT }, { "ENTITY", IS_ENTITY }, { "NOTATION", IS_NOTATION }, { "[CDATA[", IS_CDATA } }; XML_NODE* node; char* data; int c; int k = 0; int beg = 0; int end = (sizeof(key) / sizeof(key[0])) - 1; assert(ppos != NULL); assert(ppos->state == STATE_BEFORE); do { c = getsymbol(ppos); for(; (beg <= end) && (c != key[beg].name[k]); beg++) ; for(; (end >= beg) && (c != key[end].name[k]); end--) ; k++; } while(beg < end); if (beg != end) { xml_error(ppos, "Unknown declaration"); while((c != EOF) && (c != '>')) c = getsymbol(ppos); } else { assert(beg == end); assert(beg < (int)(sizeof(key) / sizeof(*key))); switch(key[beg].what) { case IS_COMMENT : if (do_comment(ppos)) ppos->state = STATE_ERROR; break; case IS_CDATA : if ((data = do_cdata(ppos)) == NULL) ppos->state = STATE_ERROR; else { if (NULL == (node = xml_new_node("#CDATA", ppos->lineno))) { xml_error(ppos, "Can't create new node"); ppos->state = STATE_ERROR; } else { BMSduplicateMemoryArray(&node->data, data, strlen(data)+1); BMSfreeMemoryArray(&data); xml_append_child(top_pstack(ppos), node); } } break; case IS_ATTLIST : case IS_ELEMENT : case IS_NOTATION : case IS_ENTITY : case IS_DOCTYPE : break; default : abort(); } } }
/** Parse file */ XML_NODE* xml_process( const char* filename /**< XML file name */ ) { PPOS ppos; XML_NODE* node = NULL; XML_ATTR* attr; int result = FALSE; char* myfilename; ALLOC_FALSE( BMSduplicateMemoryArray(&myfilename, filename, strlen(filename) + 5) ); #ifdef WITH_ZLIB if (access(filename, R_OK) != 0) { strcat(myfilename, ".gz"); /* If .gz also does not work, revert to the old name * to get a better error message. */ if (access(myfilename, R_OK) != 0) strcpy(myfilename, filename); } #endif ppos.fp = FOPEN(myfilename, "r"); if ( ppos.fp == NULL ) perror(myfilename); else { ppos.filename = myfilename; ppos.buf[0] = '\0'; ppos.pos = 0; ppos.lineno = 1; ppos.nextsym = 0; ppos.lastsym = 0; ppos.state = STATE_BEFORE; ppos.top = NULL; node = xml_new_node("#ROOT", ppos.lineno); if ( node == NULL ) { xml_error(&ppos, "Can't create new node"); } else { attr = xml_new_attr("filename", myfilename); if ( attr == NULL ) xml_error(&ppos, "Can't create new attribute"); else { xml_add_attr(node, attr); /* push root node on stack and start to process */ if ( push_pstack(&ppos, node) ) { result = xml_parse(&ppos); clear_pstack(&ppos); } } } if ( ! result && (node != NULL) ) { xml_errmsg(&ppos, "Parsing error, processing stopped", TRUE, __FILE__, __LINE__); xml_free_node(node); node = NULL; } if (FCLOSE(ppos.fp)) perror(myfilename); } BMSfreeMemoryArray(&myfilename); return node; }
XMLNODE *do_parse(XSLTGLOBALDATA *gctx, char *document, char *uri) { XMLNODE *ret; XMLNODE *current = NULL; XMLNODE *attr; XMLNODE *previous = NULL; char *p = document; char *c; unsigned comment_depth = 0; unsigned ln = 0; PARSE_STATE state = INIT; ret = xml_new_node(NULL,xsl_s_root,EMPTY_NODE); ret->file = uri; while(*p) { if(state==ERROR) break; switch(state) { case INIT: // skip all until opening element if(*p == '\n') ++ln; if(*p++ == '<') state = OPEN; break; case XMLDECL: while(p[0]!='?' || p[1]!='>') { p++; } p+=2; state = INIT; break; case COMMENT: if(*p=='\n') ++ln; if(p[0]=='-' && p[1]=='-' && p[2]=='>') { p += 3; if(--comment_depth == 0) state = TEXT; } else { ++p; } break; case OPEN: if(*p == '?') { ++p; state = XMLDECL; // skip until ?> } else if(p[0]=='!' && p[1]=='-' && p[2]=='-') { // comment p += 3; comment_depth++; state = COMMENT; } else if(!memcmp(p,"![CDATA[",8)) {// cdata p+=8; state = CDATA; } else if(!memcmp(p,"!DOCTYPE",8)) { p+=8; state = DOCTYPE; } else if(*p=='!') { error("do_parse:: unknown instruction"); return NULL; } else if(*p=='/') {// closing </element> state = CLOSE; ++p; } else {// start of element p = skip_spaces(p,&ln); for(c=p; can_name(*c);++c) ; current = xml_new_node(NULL, xmls_new_string(p, c - p), ELEMENT_NODE); current->prev = previous; current->file = uri; current->line = ln; if(previous) previous->next = current; else // no previous sibling, add as first child to parent node ret->children = current; current->parent = ret; current->next = NULL; if(!ret) ret = current; // XXX first node treated as root! p = skip_spaces(c,&ln); state = INSIDE_TAG; } break; case CLOSE: p = skip_spaces(p,&ln); for(c=p; can_name(*c);++c) ; if(!memcmp(ret->name->s,p,c-p)) { for(p=c;*p!='>';++p) ; ++p; state = TEXT; previous = ret; ret = previous->parent; } else { *c = 0; error("do_parse:: closing tag mismatch <%s> </%s>", ret->name->s, p); state = ERROR; } break; case INSIDE_TAG: if(*p=='\n') ++ln; if(*p=='>') { // tag ended, possible children follow ++p; ret = current; current = previous = NULL; state = TEXT; } else if(p[0]=='/' && p[1]=='>') { // self closed tag, make it previous and proceed to next; previous = current; p+=2; state = TEXT; } else { // must be an attribute of the tag p = skip_spaces(p,&ln); for(c=p; can_name(*c);++c) ; attr = xml_new_node(NULL, xmls_new_string(p, c - p), ATTRIBUTE_NODE); attr->file = uri; attr->line = ln; attr->next = current->attributes; current->attributes = attr; attr->parent = current; p = skip_spaces(c,&ln); if(*p == '=') { p=skip_spaces(p+1,&ln); state = ATTR_VALUE; } } break; case CDATA: for(c=p;*c;++c) { if(*c=='\n') ++ln; if(c[0]==']' && c[1]==']' && c[2]=='>') { break; } } if(c>p) { current = xml_new_node(NULL,NULL, TEXT_NODE); current->file = uri; current->line = ln; current->content = xmls_new_string(p, c - p); current->flags = XML_FLAG_NOESCAPE; current->prev = previous; if(previous) previous->next = current; else // no previous sibling, add as first child to parent node ret->children = current; current->parent = ret; current->next = NULL; previous = current; p = c + 3; } state = TEXT; break; case DOCTYPE: while(!(p[0]==']' && p[1]=='>')) p++; p+=2; state = TEXT; break; case TEXT: for(c=p;(*c && *c != '<');++c) if(*c=='\n') ++ln; if(c>p) { current = xml_new_node(NULL, NULL, TEXT_NODE); current->file = uri; current->line = ln; current->content = make_unescaped_string(p,c); current->prev = previous; if(previous) previous->next = current; else // no previous sibling, add as first child to parent node ret->children = current; current->parent = ret; current->next = NULL; previous = current; p = c; } state = INIT; break; case ATTR_VALUE: if(!(*p == '"' || *p=='\'' || *p=='`')) { state = ERROR; } else { char endchar = *p++; for(c=p;*c != endchar;++c) ; attr->content = make_unescaped_string(p,c); } p = skip_spaces(c+1,&ln); state = INSIDE_TAG; break; } } return state==ERROR?NULL:ret; }