bool find(Area **ap, Frame **fp, int dir, bool wrap, bool stack) { Rectangle r; Frame *f; Area *a; f = *fp; a = *ap; r = f ? f->r : a->r; if(dir == North || dir == South) { *fp = stack_find(a, f, dir, stack); if(*fp) return true; if(!a->floating) *ap = area_find(a->view, r, dir, wrap); if(!*ap) return false; *fp = stack_find(*ap, *fp, dir, stack); return true; } if(dir != East && dir != West) die("not reached"); *ap = area_find(a->view, r, dir, wrap); if(!*ap) return false; *fp = ap[0]->sel; return true; }
const char *html_parse_stream(HtmlParseState *state, const char *stream, const char *token, size_t len) { //TODO: support entities //TODO: support more xml bullcrap, like CDATA #define ADVANCE_TOKEN token = stream; \ state->stringlen = 0; \ state->space = 0 char c; char *text; HtmlElement *elem_tmp; HtmlAttrib *attrib_tmp; size_t i; if(!(state && stream && len)) return NULL; for(i = 0; i < len; i++) { c = *stream++; reswitch: switch(state->state) { case STATE_CHILD: switch(c) { case '<': //add containing text if(state->stringlen) { if(state->stringlen > 1 || !isspace(*token)) { text = stringduplicate_length(token, state->stringlen); if(!(elem_tmp = html_new_element(HTML_TAG_NONE, NULL, NULL, NULL, NULL, NULL, text))) goto error; if(state->elem) { state->elem->sibling = elem_tmp; state->elem->sibling->count = state->elem->count + 1; state->elem->sibling->parent = state->elem->parent; state->elem = elem_tmp; } else { state->elem = stack_peek(&state->stack); state->elem->child = elem_tmp; state->elem->child->parent = state->elem; state->elem = elem_tmp; } } } ADVANCE_TOKEN; state->state = STATE_OPEN; //tag = 0; continue; CASE_SPACE: if(html_tag_is_script(state->tag)) { ADVANCE_TOKEN; } else if(!state->space) { state->space = 1; state->stringlen++; } continue; default: if(html_tag_is_script(state->tag)) { ADVANCE_TOKEN; } else { state->space = 0; state->stringlen++; } continue; } case STATE_OPEN: if(html_tag_is_script(state->tag)) { /*I hate script tags*/ if(c != '/') { ADVANCE_TOKEN; state->state = STATE_CHILD; continue; } } switch(c) { CASE_SPACE: ADVANCE_TOKEN; /*testing this*/ //state = STATE_CHILD; continue; /*Comments, doctypes, xml-stuff and other crap we don't care about*/ case '!': ADVANCE_TOKEN; state->state = STATE_DECLARATION; continue; case '?': state->state = STATE_BEGIN; continue; case '/': ADVANCE_TOKEN; state->state = STATE_END; continue; default: state->state = STATE_BEGIN; continue; } case STATE_DECLARATION: ADVANCE_TOKEN; switch(c) { case '-': state->state = STATE_COMMENT_BEGIN; continue; default: state->state = STATE_END_CLOSE; continue; } case STATE_COMMENT_BEGIN: ADVANCE_TOKEN; state->state = STATE_COMMENT; continue; case STATE_COMMENT: case STATE_COMMENT_END1: ADVANCE_TOKEN; switch(c) { case '-': state->state++; continue; default: state->state = STATE_COMMENT; continue; } case STATE_COMMENT_END2: ADVANCE_TOKEN; switch(c) { case '>': state->state = STATE_CHILD; continue; default: state->state = STATE_COMMENT; continue; } case STATE_BEGIN: switch(c) { CASE_SPACE: state->tag = html_lookup_length_tag(token, (stream - 1) - token); state->tag_name = stringduplicate_length(token, (stream - 1) - token); state->state = STATE_ATTRIB; ADVANCE_TOKEN; continue; case '>': state->tag = html_lookup_length_tag(token, (stream - 1) - token); state->tag_name = stringduplicate_length(token, (stream - 1) - token); state->state = STATE_CLOSE; ADVANCE_TOKEN; goto reswitch; case '/': state->tag = html_lookup_length_tag(token, (stream - 1) - token); state->tag_name = stringduplicate_length(token, (stream - 1) - token); state->state = STATE_SELFCLOSE; ADVANCE_TOKEN; continue; default: continue; } case STATE_ATTRIB: switch(c) { CASE_SPACE: ADVANCE_TOKEN; continue; case '/': case '?': state->state = STATE_SELFCLOSE; ADVANCE_TOKEN; continue; case '>': state->state = STATE_CLOSE; ADVANCE_TOKEN; goto reswitch; default: state->state = STATE_ATTRIB_KEY; continue; } case STATE_ATTRIB_KEY: switch(c) { CASE_SPACE: //key key state->attrib_key = html_lookup_length_attrib_key(token, (stream - 1) - token); state->attrib_key_name = stringduplicate_length(token, (stream - 1) - token); attrib_tmp = html_new_element_attrib(state->attrib_key, state->attrib_key_name, NULL, 0); attrib_append(&state->attrib, attrib_tmp); attrib_tmp = NULL; state->state = STATE_ATTRIB; ADVANCE_TOKEN; continue; case '>': state->attrib_key = html_lookup_length_attrib_key(token, (stream - 1) - token); state->attrib_key_name = stringduplicate_length(token, (stream - 1) - token); attrib_tmp = html_new_element_attrib(state->attrib_key, state->attrib_key_name, NULL, 0); attrib_append(&state->attrib, attrib_tmp); attrib_tmp = NULL; state->state = STATE_CLOSE; ADVANCE_TOKEN; goto reswitch; case '/': state->attrib_key = html_lookup_length_attrib_key(token, (stream - 1) - token); state->attrib_key_name = stringduplicate_length(token, (stream - 1) - token); attrib_tmp = html_new_element_attrib(state->attrib_key, state->attrib_key_name, NULL, 0); attrib_append(&state->attrib, attrib_tmp); attrib_tmp = NULL; state->state = STATE_SELFCLOSE; ADVANCE_TOKEN; continue; case '=': state->attrib_key = html_lookup_length_attrib_key(token, (stream - 1) - token); state->attrib_key_name = stringduplicate_length(token, (stream - 1) - token); state->state = STATE_ATTRIB_VALUE; ADVANCE_TOKEN; continue; default: continue; } case STATE_ATTRIB_VALUE: switch(c) { CASE_SPACE: attrib_tmp = html_new_element_attrib(state->attrib_key, state->attrib_key_name, token, (stream - 1) - token); attrib_append(&state->attrib, attrib_tmp); attrib_tmp = NULL; state->state = STATE_ATTRIB; ADVANCE_TOKEN; continue; case '>': attrib_tmp = html_new_element_attrib(state->attrib_key, state->attrib_key_name, token, (stream - 1) - token); attrib_append(&state->attrib, attrib_tmp); attrib_tmp = NULL; state->state = STATE_CLOSE; ADVANCE_TOKEN; goto reswitch; case '\'': state->state = STATE_ATTRIB_QUOTE_SINGLE; ADVANCE_TOKEN; continue; case '"': state->state = STATE_ATTRIB_QUOTE_DOUBLE; ADVANCE_TOKEN; continue; case '/': attrib_tmp = html_new_element_attrib(state->attrib_key, state->attrib_key_name, token, (stream - 1) - token); attrib_append(&state->attrib, attrib_tmp); attrib_tmp = NULL; state->state = STATE_SELFCLOSE; ADVANCE_TOKEN; continue; default: continue; } case STATE_ATTRIB_QUOTE_SINGLE: switch(c) { case '\'': attrib_tmp = html_new_element_attrib(state->attrib_key, state->attrib_key_name, token, (stream - 1) - token); attrib_append(&state->attrib, attrib_tmp); attrib_tmp = NULL; state->state = STATE_ATTRIB; ADVANCE_TOKEN; continue; default: continue; } break; // TODO: This is very redundant to the code above, try to optimize later case STATE_ATTRIB_QUOTE_DOUBLE: switch(c) { case '"': attrib_tmp = html_new_element_attrib(state->attrib_key, state->attrib_key_name, token, (stream - 1) - token); attrib_append(&state->attrib, attrib_tmp); attrib_tmp = NULL; state->state = STATE_ATTRIB; ADVANCE_TOKEN; continue; default: continue; } break; case STATE_CLOSE: switch(c) { case '>': ADVANCE_TOKEN; if(html_tag_is_script(state->tag)) { state->state = STATE_CHILD; continue; } //add to stack if(!(elem_tmp = html_new_element(state->tag, state->tag_name, state->attrib, NULL, NULL, NULL, NULL))) goto error; if(state->elem) { state->elem->sibling = elem_tmp; state->elem->sibling->count = state->elem->count + 1; state->elem->sibling->parent = state->elem->parent; state->elem = elem_tmp; } else { state->elem = stack_peek(&state->stack); state->elem->child = elem_tmp; state->elem->child->parent = state->elem; state->elem = elem_tmp; } if(!html_tag_is_selfclose(state->tag)) { stack_push(&state->stack, state->elem); state->elem = NULL; } state->tag = 0; state->tag_name = 0; state->attrib = 0; state->attrib_key_name = 0; state->state = STATE_CHILD; continue; default: ADVANCE_TOKEN; continue; } case STATE_SELFCLOSE: switch(c) { case '>': ADVANCE_TOKEN; if(html_tag_is_script(state->tag)) { state->state = STATE_CHILD; continue; } //add to stack if(!(elem_tmp = html_new_element(state->tag, state->tag_name, state->attrib, NULL, NULL, NULL, NULL))) goto error; if(state->elem) { state->elem->sibling = elem_tmp; state->elem->sibling->count = state->elem->count + 1; state->elem->sibling->parent = state->elem->parent; state->elem = elem_tmp; } else { state->elem = stack_peek(&state->stack); state->elem->child = elem_tmp; state->elem->child->parent = state->elem; state->elem = elem_tmp; } state->tag = 0; state->attrib = 0; state->attrib_key_name = 0; state->state = STATE_CHILD; continue; default: ADVANCE_TOKEN; continue; } case STATE_END: switch(c) { CASE_SPACE: //find tag to close if(html_tag_is_script(state->tag)) { if((stream - 1) - token > strlen(html_tag[state->tag])) state->state = STATE_CHILD; else if(html_lookup_length_tag(token, (stream - 1) - token) == state->tag) { state->tag = 0; state->state = STATE_END_CLOSE; } else state->state = STATE_CHILD; ADVANCE_TOKEN; continue; } if((state->tag = html_lookup_length_tag(token, (stream - 1) - token)) < 0) state->tag = state->elem->tag; ADVANCE_TOKEN; if(!stack_find(&state->stack, findtag, &state->tag)) { state->state = STATE_CHILD; continue; } do { /*check for null, broken pages*/ elem_tmp = stack_pop(&state->stack); } while(elem_tmp->tag != state->tag); state->elem = elem_tmp; state->state = STATE_END_CLOSE; continue; case '>': //find tag to close if(html_tag_is_script(state->tag)) { if((stream - 1) - token > strlen(html_tag[state->tag])) state->state = STATE_CHILD; else if(html_lookup_length_tag(token, (stream - 1) - token) == state->tag) { state->tag = 0; state->state = STATE_CHILD; } else state->state = STATE_CHILD; ADVANCE_TOKEN; continue; } if((state->tag = html_lookup_length_tag(token, (stream - 1) - token)) < 0) state->tag = state->elem->tag; ADVANCE_TOKEN; if(!stack_find(&state->stack, findtag, &state->tag)) { state->state = STATE_CHILD; continue; } do { /*check for null, broken pages*/ elem_tmp = stack_pop(&state->stack); } while(elem_tmp->tag != state->tag); state->elem = elem_tmp; state->tag = 0; state->attrib = 0; state->attrib_key_name = 0; state->state = STATE_CHILD; continue; default: continue; } case STATE_END_CLOSE: switch(c) { case '>': state->state = STATE_CHILD; ADVANCE_TOKEN; continue; default: continue; } case STATE_ENTITY: case STATES: break; } } return token; error: /*Handle malloc fail somehow*/ return NULL; #undef ADVANCE_TOKEN }