static int methodline_parse(struct buffer *bu, struct methodline_state *ms, struct buffer *method, struct buffer *uri, struct buffer *version) { const char *s = bu->data; unsigned rem = bu->cur; char ch; while (rem > 0) { ch = *s; s++; rem--; switch (ms->state) { case 0: /* method */ if (ch == ' ') ms->state = 1; else buffer_addch(method, ch); break; case 1: /* uri */ if (ch == ' ') ms->state = 2; else buffer_addch(uri, ch); break; case 2: /* version */ if (ch == '\r') ms->state = 3; else if (isspace(ch)) goto parse_error; else buffer_addch(version, ch); break; case 3: /* CR */ if (ch == '\n') goto complete; else goto parse_error; break; default: assert(0); } } buffer_consume(bu, s - bu->data); return 0; complete: ms->done = true; buffer_consume(bu, s - bu->data); return 0; parse_error: Error("%s():parse failure (%.*s)\n", __func__, bu->cur, bu->data); buffer_consume(bu, s - bu->data); Error("%s():parse failure @ %d (state=%d)\n", __func__, bu->cur, ms->state); return -1; }
static int _csv_escaped_dquote(struct csv_parser *cp) { if (buffer_addch(&cp->buf, '"')) return -1; cp->s = CSV_ESCAPED; return 0; }
static int headers_parse(struct buffer *bu, struct headers_state *hs, struct buffer *name, struct buffer *value, struct env *env) { const char *s = bu->data; unsigned rem = bu->cur; char ch; // TODO: 413 Entity Too Large while (rem > 0) { ch = *s; s++; rem--; switch (hs->state) { case 0: /* first char */ if (ch == '\r') { if (name->cur) env_set(env, name->data, value->data); hs->state = 6; /* CR/LF - end of headers */ } else if (ch == ' ' || ch == '\t') { /* indented continuation line */ // TODO: this is completely untested .. device a test for it! // TODO: insert single SP for these leading LWS hs->state = 4; /* append to existing value data */ } else { if (name->cur) env_set(env, name->data, value->data); buffer_reset(name); buffer_reset(value); buffer_addch(name, ch); hs->state = 11; } break; case 11: /* name */ if (ch == ' ' || ch == '\t') { /* LWS after name */ hs->state = 1; } else if (ch == ':') { hs->state = 2; } else if (isspace(ch)) { goto parse_error; } else { buffer_addch(name, ch); } break; case 1: /* LWS after name */ if (ch == ' ' || ch == '\t') ; /* ignore */ else if (ch == ':') hs->state = 2; else goto parse_error; break; case 2: /* ':' */ if (ch == ' ' || ch == '\t') { hs->state = 3; } else { buffer_addch(value, ch); hs->state = 4; } break; case 3: /* LWS before value */ if (ch == ' ' || ch == '\t') { /* ignore */ } else if (isspace(ch)) { goto parse_error; } else { buffer_addch(value, ch); hs->state = 4; } break; case 4: /* value */ if (ch == '\r') hs->state = 5; else buffer_addch(value, ch); break; case 5: /* CR after value */ if (ch == '\n') hs->state = 0; else goto parse_error; break; case 6: /* CR on first column */ if (ch == '\n') goto complete; else goto parse_error; break; default: assert(0); } } buffer_consume(bu, s - bu->data); return 0; complete: hs->done = true; buffer_consume(bu, s - bu->data); return 0; parse_error: Error("%s():parse failure (%.*s)\n", __func__, bu->cur, bu->data); buffer_consume(bu, s - bu->data); Error("%s():parse failure @ %d (ch=%c state=%d)\n", __func__, bu->cur, ch, hs->state); return -1; }
static int csv(struct csv_parser *cp, const char *buf, size_t len) { while (len > 0) { char ch = *buf; buf++; len--; switch (cp->s) { case CSV_START: if (ch == '\r') cp->s = CSV_NEWLINE; else if (ch == '\n') _csv_next_row(cp); else if (ch == '"') cp->s = CSV_ESCAPED; else if (ch == ',') _csv_next_field(cp); else goto unescaped; break; case CSV_ESCAPED: if (ch == '"') cp->s = CSV_ESCAPED_DQUOTE; else if (buffer_addch(&cp->buf, ch)) return -1; break; case CSV_ESCAPED_DQUOTE: if (ch == '\r') cp->s = CSV_NEWLINE; else if (ch == '\n') _csv_next_row(cp); else if (ch == '"') { if (_csv_escaped_dquote(cp)) return -1; } else if (ch == ',') _csv_next_field(cp); else goto unescaped; /* deviates from RFC 4180 */ break; unescaped: cp->s = CSV_UNESCAPED; case CSV_UNESCAPED: if (ch == '\r') cp->s = CSV_NEWLINE; else if (ch == '\n') _csv_next_row(cp); else if (ch == '"') cp->s = CSV_ESCAPED; /* deviates from RFC 4180 */ else if (ch == ',') _csv_next_field(cp); else if (buffer_addch(&cp->buf, ch)) return -1; break; case CSV_NEWLINE: if (ch == '\n') _csv_next_row(cp); else return -1; case CSV_ERROR: return -1; } } return cp->s == CSV_ERROR ? -1 : 0; }