#include "parse.h" #include #include #include #include "macro.h" // #include "vcal.h" #include "err.h" #include #include "struct.h" #include "guile_type_helpers.h" // #define TYPE vcomponent // #include "linked_list.inc.h" // #undef TYPE // #define T strbuf // #define V strbuf // #include "pair.h" // #include "pair.inc.h" // #undef T // #undef V /* +-------------------------------------------------------+ v | BEGIN → key -------------------------------→ ':' → value → CRLF -+-→ EOF | ^ v | ';' → param-key → ':' → param-value --+ ^ | +------------------------------------+ vcomponent := map> line := pair attributes := map> */ #define string_eq(a, b) scm_is_true(scm_string_eq(a, b, SCM_UNDEFINED,SCM_UNDEFINED,SCM_UNDEFINED,SCM_UNDEFINED)) /* * name *(";" param) ":" value CRLF */ int parse_file(char* filename, FILE* f, SCM root) { part_context p_ctx = p_key; SNEW(parse_ctx, ctx, f, filename); // PUSH(LLIST(vcomponent))(&ctx.comp_stack, root); /* * Create a content_line which we use as storage while we are * parsing. This object is constantly broken down and rebuilt. * * {cline,param}_key is also temporary register used during * parsing. */ // SNEW(content_line, cline); // SNEW(strbuf, param_key); // SNEW(strbuf, param_val); // SNEW(strbuf, attr_key); // SNEW(strbuf, attr_val); SNEW(strbuf, str); SCM component = root; SCM line = scm_make_vline(SCM_UNDEFINED); SCM attr_key; /* string */ SCM line_key = scm_from_utf8_string(""); /* string */ SCM scm_filename = scm_from_utf8_stringn(filename, strlen(filename)); SCM filename_key = scm_from_utf8_string("X-HNH-FILENAME"); char c; while ( (c = fgetc(f)) != EOF) { /* We have a linebreak */ if (c == '\r' || c == '\n') { if (fold(&ctx, c) > 0) { /* Actuall end of line, handle value */ // TRANSFER(CLINE_CUR_VAL(&cline), &ctx.str); /* * The key being BEGIN means that we decend into a new component. */ if (string_eq(line_key, scm_from_utf8_string("BEGIN"))) { /* key \in { VCALENDAR, VEVENT, VALARM, VTODO, VTIMEZONE, ... } */ SCM child = scm_make_vcomponent(scm_string_to_symbol(scm_from_strbuf(&str))); scm_add_child_x (component, child); /* TODO it should be possible to create this object once at the top of this function */ scm_add_line_x(child, filename_key, scm_make_vline(scm_filename)); component = child; } else if (string_eq(line_key, scm_from_utf8_string("END"))) { component = scm_component_parent(component); /* * A regular key, value pair. Push it into to the current * component. */ } else { scm_struct_set_x(line, vline_value, scm_from_strbuf(&str)); scm_add_line_x(component, line_key, line); line = scm_make_vline(SCM_UNDEFINED); } strbuf_soft_reset (&str); p_ctx = p_key; } /* Else continue on current line */ /* We have an escaped character */ } else if (c == '\\') { char esc = handle_escape (&ctx); strbuf_append(&str, esc); /* Border between param {key, value} */ } else if (p_ctx == p_param_name && c == '=') { /* Save the current parameter key */ attr_key = scm_from_strbuf(&str); p_ctx = p_param_value; strbuf_soft_reset (&str); /* * One of four cases: * 1) end of key , start of value * 2) ,, key , ,, param * 3) ,, param, ,, param * 4) ,, param, ,, value */ } else if ((p_ctx == p_key || p_ctx == p_param_value) && (c == ':' || c == ';')) { /* We got a parameter value, push the current string to * the current parameter set. */ if (p_ctx == p_param_value) { /* save current parameter value. */ scm_add_attribute_x(line, attr_key, scm_from_strbuf(&str)); strbuf_soft_reset (&str); } /* * Top level key. * Copy the key into the current cline, and create a * content_set for the upcomming value and (possible) * parameters. */ if (p_ctx == p_key) { line_key = scm_from_strbuf(&str); strbuf_soft_reset (&str); } if (c == ':') p_ctx = p_value; else if (c == ';') p_ctx = p_param_name; /* * Nothing interesting happened, append the read character to * the current string. */ } else { strbuf_append(&str, c); ++ctx.column; ++ctx.pcolumn; } } if (! feof(f)) { ERR_F("Error parsing errno = %i", errno); } /* Check to see if empty line */ else if (str.ptr != 0) { /* * The standard (3.4, l. 2675) says that each icalobject must * end with CRLF. My files however does not, so we also parse * the end here. * * Actually we don't any more. * Since the last thing in a file should always be END:VCALENDAR * it might be a good idea to verify that. Or we could just, you * know, not. */ } // FREE(content_line)(&cline); // FREE(strbuf)(&cline_key); // FREE(strbuf)(¶m_key); FREE(strbuf)(&str); // assert(POP(LLIST(vcomponent))(&ctx.comp_stack) == root); // assert(EMPTY(LLIST(strbuf))(&ctx.key_stack)); // assert(EMPTY(LLIST(vcomponent))(&ctx.comp_stack)); FREE(parse_ctx)(&ctx); return 0; } int fold(parse_ctx* ctx, char c) { int retval; char buf[2] = { (c == '\n' ? '\n' : (char) fgetc(ctx->f)), (char) fgetc(ctx->f) }; ctx->pcolumn = 1; if (buf[0] != '\n') { ERR_P(ctx, "expected new_line after CR"); retval = -1; } else if (buf[1] == ' ' || buf[1] == '\t') { retval = 0; ctx->pcolumn++; } else if (ungetc(buf[1], ctx->f) != buf[1]) { ERR_P(ctx, "Failed to put character back on FILE"); retval = -2; } else { retval = 1; ++ctx->line; ctx->column = 0; } ++ctx->pline; return retval; } INIT_F(parse_ctx, FILE* f, char* filename) { // INIT(LLIST(strbuf), &self->key_stack); // INIT(LLIST(vcomponent), &self->comp_stack); self->filename = (char*) calloc(sizeof(*filename), strlen(filename) + 1); strcpy(self->filename, filename); self->f = f; self->line = 0; self->column = 0; self->pline = 1; self->pcolumn = 1; // INIT(strbuf, &self->str); return 0; } FREE_F(parse_ctx) { // FREE(LLIST(strbuf))(&self->key_stack); // FREE(LLIST(vcomponent))(&self->comp_stack); free(self->filename); self->line = 0; self->column = 0; // FREE(strbuf)(&self->str); return 0; } char handle_escape (parse_ctx* ctx) { char esc = fgetc(ctx->f); /* * Escape character '\' and escaped token sepparated by a newline * (since the standard for some reason allows that (!!!)) * We are at least guaranteed that it's a folded line, so just * unfold it and continue trying to find a token to escape. */ if (esc == '\r' || esc == '\n') { int ret; if ( (ret = fold(ctx, esc)) != 0) { if (ret == 1) ERR_P(ctx, "ESC before not folded line"); else ERR_P(ctx, "other error: val = %i", ret); exit (2); } else { esc = fgetc(ctx->f); } } /* Escaped new_line */ if (esc == 'n' || esc == 'N') { esc = '\n'; /* "Standard" escaped character */ } else if (esc == ';' || esc == ',' || esc == '\\') { /* esc already contains character, do nothing */ /* Invalid escaped character */ } else { ERR_P(ctx, "Non escapable character '%c' (%i)", esc, esc); } ++ctx->column; ++ctx->pcolumn; return esc; /* save escapade character as a normal character */ // strbuf_append(&ctx->str, esc); // return 0; }