/* EIMIL.c EIMIL parser/dictionary/data manager. */ #include #include #include #include #include #include "EIMILint.h" #define MAX_ELEMENT_DEPTH 10 static int EIMIL_ID_counter = 0; #define EIMIL_ID_MAX ((1 << 15) - 1) static EIMIL_value EIMIL_t_val; static const unsigned char* EIMIL_xmlns_uri = "http://www.OpenI18N.org/EIMIL/NS/1.0"; EIMIL_dictionary *pdic_internal = NULL; EIMIL_symbol *pEIMIL_nil_sym = NULL; EIMIL_symbol *pEIMIL_t_sym = NULL; EIMIL_symbol *pEIMIL_feedback_sym = NULL; EIMIL_symbol *pEIMIL_candidates_sym = NULL; static int EIMIL_inited = 0; /******************************************************************************** Char/Token manipulation functions. ********************************************************************************/ #define EIMIL_isspace(c) ((c) == ' ' || (c) == '\n' || (c) == '\t' || (c) == '\r') #define EIMIL_istag_s(c) ((c) == '<') #define EIMIL_istag_e(c) ((c) == '>') #define EIMIL_isetag_mark(c) ((c) == '/') #define EIMIL_isPImark(c) ((c) == '?') #define EIMIL_isEXmark(c) ((c) == '!') #define EIMIL_isnewline(c) ((c) == '\n') #define EIMIL_isEq(c) ((c) == '=') #define EIMIL_ispresep(c) ((c) == ':') #define EIMIL_isquote(c) ((c) == '\x27' || (c) == '"') #define EIMIL_isrefstart(c) ((c) == '&') #define EIMIL_ischrefmark(c) ((c) == '#') #define EIMIL_ischrefhexmark(c) ((c) == 'x') #define EIMIL_ischrefdec(c) (((c) >= '0' && (c) <= '9')) #define EIMIL_chrefdec(c) ((c) - '0') #define EIMIL_ischrefhex(c) (((c) >= '0' && (c) <= '9') \ || ((c) >= 'a' && (c) <= 'f') \ || ((c) >= 'A' && (c) <= 'F')) #define EIMIL_chrefhex(c) (((c) >= '0' && (c) <= '9') ? ((c) - '0') : \ ((c) >= 'a' && (c) <= 'f') ? ((c) - 'a') : ((c) - 'A')) #define EIMIL_isrefend(c) ((c) == ';') #define EIMIL_UTF32_EBYTE_MAX_FACTOR sizeof(UTF32) /* This function resolve character and entity references. */ static UTF8* EIMIL_resolve_reference( Ebyte *s, Ebyte *e ) { UTF32 ch; int size, n; UTF8 *ur, *uc; Ebyte *c1, *c2; ur = uc = (UTF8*) malloc((e - s + 1) * sizeof(UTF8)); size = 0; for (c1 = s; (c1 < e); c1++) { if ((EIMIL_isrefstart(*c1)) && (EIMIL_ischrefmark(c1[1])) && ((e - c1) > 2)) /* &#X; */ { if (EIMIL_ischrefhexmark(c1[2])) { ch = 0; for (c2 = c1 + 3; (c2 < e); c2++) { if (EIMIL_isrefend(*c2)) break; if (!EIMIL_ischrefhex(*c2)) break; ch = ch * 16 + EIMIL_chrefhex(*c2); if (ch > 0x10FFFF) goto error_cleanup; } if (((c2 - c1) > 3) && EIMIL_isrefend(*c2)) { n = EIMIL_convert_UTF32char_to_UTF8(ch, uc); uc += n; size += n; } else { goto error_cleanup; } } else { for (c2 = c1 + 2; (c2 < e); c2++) { if (EIMIL_isrefend(*c2)) break; if (!EIMIL_ischrefdec(*c2)) break; ch = ch * 10 + EIMIL_chrefdec(*c2); if (ch > 0x10FFFF) goto error_cleanup; } if (((c2 - c1) > 2) && EIMIL_isrefend(*c2)) { n = EIMIL_convert_UTF32char_to_UTF8(ch, uc); uc += n; size += n; } else { goto error_cleanup; } } } else { /* TODO:if we require decoding, do it here! Now we assume Ebyte is UTF8 octet stream. */ size++; *uc++ = *c1; } } size++; *uc = 0; ur = (UTF8*) realloc(ur, size * sizeof(UTF8)); return ur; error_cleanup: free(ur); return NULL; } static void EIMIL_compute_line( EIMIL_parser_state *pps, Ebyte *c1, int *pline ) { int n = 0; Ebyte *c = pps->current; if (c < c1) { for (; (c < c1); c++) { if (EIMIL_isnewline(*c)) { n++; } } } else { while (c > c1) { c--; if (EIMIL_isnewline(*c)) { n--; } } } *pline = pps->lineno + n; } static void EIMIL_set_point( EIMIL_parser_state *pps, Ebyte *c ) { int line; EIMIL_compute_line(pps, c, &line); pps->current = c; pps->lineno = line; return; } void EIMIL_set_error( EIMIL_data *ped, const char* fmt, ... ) { va_list va; char errbuf[EIMIL_MAXERRMSG]; va_start(va, fmt); vsnprintf(errbuf, sizeof(errbuf), fmt, va); snprintf(ped->errstr, sizeof(ped->errstr), "%s\n", errbuf); va_end(va); } void EIMIL_set_error_pt( EIMIL_data *ped, Ebyte *c, const char* fmt, ... ) { va_list va; int lineno; EIMIL_parser_state* pps = &ped->pcommon->ps; char errbuf[EIMIL_MAXERRMSG]; if (c) EIMIL_compute_line(pps, c, &lineno); else lineno = pps->lineno; va_start(va, fmt); vsnprintf(errbuf, sizeof(errbuf), fmt, va); snprintf(ped->errstr, sizeof(ped->errstr), "%s (%d)\n", errbuf, lineno); va_end(va); } void EIMIL_set_out_of_memory( EIMIL_data *ped ) { EIMIL_set_error(ped, "Out of memory."); } void EIMIL_set_EOF_error( EIMIL_data *ped, Ebyte *e ) { EIMIL_set_error_pt(ped, e, "End of file during parsing."); \ } Ebyte* EIMIL_get_ebyte_token( Ebyte *s, Ebyte *e ) { Ebyte *ret; ret = (Ebyte*) malloc((e - s + 1) * sizeof(Ebyte)); if (!ret) return NULL; memcpy(ret, s, (e - s) * sizeof(Ebyte)); ret[e - s] = 0; return ret; } UTF8* EIMIL_get_UTF8_token( Ebyte *s, Ebyte *e ) { UTF8 *pret, *p; pret = (UTF8*) malloc((e - s + 1) * sizeof(UTF8)); if (!pret) return NULL; for (p = pret; s < e; p++, s++) { /* later we maybe need to convert code. */ *p = *s; } *p = '\0'; return pret; } #define EIMIL_check_EOF(c, e, ped) \ do { \ if ((c) >= (e)) { \ EIMIL_set_EOF_error((ped), (e));\ goto error_cleanup; \ } \ }while (0) #define EIMIL_skip_S(c, e, pps) \ for (; (((c) < (e)) && EIMIL_isspace(*c)); (c)++) static Ebyte* EIMIL_match( Ebyte *c, Ebyte *e, char *str ) { Ebyte *c1; char *p; for (p = str, c1 = c; (c1 < e); c1++, p++) { if (!(*p)) return c; if (*c1 != *p) break; } return NULL; } static Ebyte* EIMIL_skip_to( Ebyte *c, Ebyte *e, char *str ) { Ebyte *c1; char *p; for (; (c < e); c++) { for (p = str, c1 = c; (c1 < e); c1++, p++) { if (!(*p)) return c; if (*c1 != *p) break; } } return NULL; } /*************************************** XML namespace ***************************************/ static unsigned char* no_default_namespace_uri = ""; #define EIMIL_XMLNS_INITIALI_SLOTS 16 static EIMIL_XMLNS* EIMIL_expand_namespace_slot( EIMIL_parser_state *pps ) { if (pps->xmlns_alloced <= pps->xmlns_entries) { int na; EIMIL_XMLNS *pns = pps->pxmlns; if (pps->xmlns_alloced == 0) { na = EIMIL_XMLNS_INITIALI_SLOTS; pns = (EIMIL_XMLNS*) malloc(na * sizeof(EIMIL_XMLNS)); } else { na = pps->xmlns_alloced * 2; pns = (EIMIL_XMLNS*) realloc(pps->pxmlns, na * sizeof(EIMIL_XMLNS)); } if (!pns) return NULL; pps->xmlns_alloced = na; pps->pxmlns = pns; } return pps->pxmlns + pps->xmlns_entries; } static unsigned char* EIMIL_get_prefix_namespace( EIMIL_parser_state *pps, unsigned char *prefix ) { int i; int n = pps->xmlns_entries; EIMIL_XMLNS *pns = pps->pxmlns + n; for (i = 0; i < n; i++) { pns--; if (!pns->uri) continue; if ((prefix == pns->prefix) || (prefix && (strcmp(prefix, pns->prefix) == 0))) { if (pns->uri == no_default_namespace_uri) return NULL; return pns->uri; } } return NULL; } static int EIMIL_set_prefix_namespace( EIMIL_parser_state *pps, unsigned char *prefix, unsigned char *uri ) { int i; int n = pps->xmlns_entries; EIMIL_XMLNS *pns = pps->pxmlns + n; for (i = 0; i < n; i++, pns--) { if (!pns->uri) { i = n; break; } if (prefix == pns->prefix) { break; } else if (prefix && (strcmp(prefix, pns->prefix) == 0)) { break; } } if (i == n) { pns = EIMIL_expand_namespace_slot(pps); if (!pns) return 0; pns->prefix = prefix; pps->xmlns_entries++; } else if ((pns->uri) && (pns->uri != no_default_namespace_uri)) { free(pns->uri); } if (*uri == '\0') { pns->uri = no_default_namespace_uri; } else { pns->uri = uri; } return 1; } static int EIMIL_namespace_newbind( EIMIL_parser_state *pps ) { EIMIL_XMLNS *pns; pns = EIMIL_expand_namespace_slot(pps); if (!pns) return 0; pps->xmlns_entries++; pns->prefix = NULL; pns->uri = NULL; return 1; } int EIMIL_namespace_unbind( EIMIL_parser_state *pps ) { int i; int n = pps->xmlns_entries; EIMIL_XMLNS *pns = pps->pxmlns + n; for (i = 1; i <= n; i++) { pns--; if (!pns->uri) break; if (pns->prefix) free(pns->prefix); if ((pns->uri) && (pns->uri != no_default_namespace_uri)) free(pns->uri); } pps->xmlns_entries -= i; return 1; } /*************************************** name ***************************************/ static Ebyte* EIMIL_get_name( EIMIL_data *ped, Ebyte *c, Ebyte *e, UTF8 **pname, UTF8 **pprefix ) { #if FAMAO EIMIL_parser_state *pps = &ped->pcommon->ps; #endif Ebyte *c1; *pprefix = *pname = NULL; c1 = c; while ((e > c) && !EIMIL_isspace(*c) && !EIMIL_ispresep(*c) && !EIMIL_istag_e(*c) && !EIMIL_isetag_mark(*c)) { c++; } if (e <= c) return NULL; if (EIMIL_ispresep(*c)) { *pprefix = EIMIL_get_UTF8_token(c1, c); c1 = c; while ((e > c) && !EIMIL_isspace(*c) && !EIMIL_istag_e(*c) && !EIMIL_isetag_mark(*c)) { c++; } if (e <= c) return NULL; } *pname = EIMIL_get_UTF8_token(c1, c); return c; } /*************************************** attribute ***************************************/ static void EIMIL_free_attrs( EIMIL_attrs *patr ) { EIMIL_attrs *pa; for (pa = patr;pa->name;pa++) { free(pa->name); free(pa->val); } free(patr); } static int EIMIL_check_attrs( EIMIL_data *ped, EIMIL_attr_template *pat, EIMIL_attrs **ppatr ) { int i, num; int eflag = 0; #if FAMAO EIMIL_parser_state *pps = &ped->pcommon->ps; #endif EIMIL_attrs *pa; if (*ppatr) for (num = 0, pa = *ppatr; pa->name; pa++) num++; else num = 0; if (!pat) return 1; for (; pat->name; pat++) { eflag = 0; for (pa = *ppatr, i = 0; i < num; pa++, i++) { if (strcmp(pat->name, pa->name) == 0) { if (eflag) { EIMIL_set_error_pt(ped, NULL, "Duplicated attribute:%s", pa->name); return 0; } if ((pat->type == EIMIL_attr_FIXED) && (strcmp(pat->default_value, pa->val) != 0)) { EIMIL_set_error_pt(ped, NULL, "attribute:%s must be %s", pat->name, pat->default_value); return 0; } eflag = 1; } } if (!eflag && pat->default_value) { if (pat->type == EIMIL_attr_REQUIRED) { EIMIL_set_error_pt(ped, NULL, "attribute:%s is missing", pat->name); return 0; } num++; *ppatr = pa = (EIMIL_attrs*) realloc(*ppatr, ((num + 1) * sizeof(EIMIL_attrs))); pa[num].name = NULL; pa[num].val = NULL; pa[num - 1].name = strdup(pat->name); pa[num - 1].val = (Ebyte*) strdup(pat->default_value); } } return 1; } static EIMIL_attrs* EIMIL_parse_attrs( EIMIL_data *ped ) { EIMIL_parser_state *pps = &ped->pcommon->ps; Ebyte *c = pps->current; Ebyte *e = pps->end; Ebyte *c1; int atrnum = 0; EIMIL_attrs *patr_s = NULL; UTF8 *p, *prefix; UTF8 *name = NULL; Ebyte *val; while (e > c) { EIMIL_skip_S(c, e, pps); EIMIL_check_EOF(c, e, ped); if (EIMIL_istag_e(*c) || EIMIL_isetag_mark(*c)) break; /* ATTR S? = S? VAL */ c1 = c; while ((e > c) && !EIMIL_isspace(*c) && !EIMIL_isEq(*c)) c++; EIMIL_check_EOF(c, e, ped); name = EIMIL_get_UTF8_token(c1, c); if (!name) goto error_cleanup; EIMIL_skip_S(c, e, pps); EIMIL_check_EOF(c, e, ped); if (!EIMIL_isEq(*c)) { EIMIL_set_error_pt(ped, c, "Attribute format is invalid"); goto error_cleanup; } c++; EIMIL_check_EOF(c, e, ped); EIMIL_skip_S(c, e, pps); EIMIL_check_EOF(c, e, ped); { /* cut attribute value out. */ c1 = c; if (!EIMIL_isquote(*c1)) { EIMIL_set_error_pt(ped, c, "Attribute value must be enclosed with quote"); goto error_cleanup; } c++; while ((e > c) && (*c1 != *c)) c++; EIMIL_check_EOF(c, e, ped); val = EIMIL_get_ebyte_token(c1 + 1, c); c++; } /* XML namespace check. */ if ((p = strchr(name, ':')) != NULL) { prefix = name; *p = '\0'; p++; } else { prefix = NULL; p = name; } if (strcmp(p, "xmlns") == 0) { EIMIL_set_prefix_namespace(pps, prefix, val); continue; } if (prefix) { /* EIMIL never use any attributes in the global paritition. */ free(name); free(val); continue; } atrnum++; patr_s = (EIMIL_attrs*) realloc(patr_s, (atrnum + 1) * sizeof(EIMIL_attrs)); patr_s[atrnum].name = NULL; patr_s[atrnum].val = NULL; patr_s[atrnum - 1].name = name; patr_s[atrnum - 1].val = val; } EIMIL_set_point(pps, c); return patr_s; error_cleanup: if (patr_s) EIMIL_free_attrs(patr_s); return NULL; } void EIMIL_remove_attr( EIMIL_attrs *patr ) { EIMIL_attrs *p; ASSERT(patr->name); for (p = patr + 1; p->name; patr++, p++) *patr = *p; *patr = *p; } Ebyte* EIMIL_get_attr_cdata( Ebyte *val, UTF8 **result ) { Ebyte *e; for (e = val; *e; e++); if (result) { *result = EIMIL_resolve_reference(val, e); if (!(*result)) return NULL; } return e; } Ebyte* EIMIL_get_attr_nmtoken( Ebyte *val, UTF8 **result ) { Ebyte *s, *e, *p; for (s = val; (*s && EIMIL_isspace(*s)); s++); if (!(*s)) return NULL; for (e = s; (*e && !EIMIL_isspace(*e)); e++); for (p = e; *p; p++) { if (!EIMIL_isspace(*p)) return NULL; } if (result) { *result = EIMIL_resolve_reference(s, e); if (!(*result)) return NULL; } return e; } /* " AAA BBBB CCC \0" ==> " BBBB CCC \0", result == "AAA" */ Ebyte* EIMIL_get_attr_nmtokens( Ebyte *val, UTF8 **result ) { Ebyte *s, *e; for (s = val; (*s && EIMIL_isspace(*s)); s++); if (!(*s)) return NULL; for (e = s; (*e && !EIMIL_isspace(*e)); e++); if (result) { *result = EIMIL_resolve_reference(s, e); if (!(*result)) return NULL; } return e; } static enum EIMIL_TYPE EIMIL_get_type_from_attrs( EIMIL_data *ped, EIMIL_attrs *patr ) { #if FAMAO EIMIL_parser_state *pps = &ped->pcommon->ps; #endif UTF8 *name; enum EIMIL_TYPE type; for (; patr->name; patr++) { if (strcmp(patr->name, "type") == 0) { if (!EIMIL_get_attr_nmtoken(patr->val, &name)) { type = EIMIL_TYPE_INVALID; break; } if (strcmp(name, "bool") == 0) type = EIMIL_TYPE_BOOL; else if (strcmp(name, "number") == 0) type = EIMIL_TYPE_NUMBER; else if (strcmp(name, "char") == 0) type = EIMIL_TYPE_CHAR; else if (strcmp(name, "mtext") == 0) type = EIMIL_TYPE_MTEXT; else type = EIMIL_TYPE_INVALID; free(name); free(patr->name); free(patr->val); EIMIL_remove_attr(patr); break; } } if (type == EIMIL_TYPE_INVALID) { EIMIL_set_error_pt(ped, NULL, "`type' attribute must be `bool', `number', `char', or `mtext'."); } return type; } static UTF8* EIMIL_get_UTF8data_token( EIMIL_data *ped, int option ) { EIMIL_parser_state *pps = &ped->pcommon->ps; Ebyte *c, *c1, *e; UTF8 *pu; c = pps->current; e = pps->end; if (option & EIMIL_PCDATA_TOKEN) { EIMIL_skip_S(c, e, pps); c1 = c; for (; c < e; c++) { if (EIMIL_isspace(*c) || EIMIL_istag_s(*c)) break; } } else if (option & EIMIL_PCDATA_QUOTED_TOKEN) { Ebyte q; EIMIL_skip_S(c, e, pps); c1 = c; q = *c; if (EIMIL_isquote(q)) { c++; for (; c < e; c++) { if (*c == q) break; } c++; if (c >= e) { EIMIL_set_error_pt(ped, c1, "Corresponding quotation mark is missing."); return NULL; } } else { for (; c < e; c++) { if (EIMIL_isspace(*c) || EIMIL_istag_s(*c) || EIMIL_isquote(*c)) break; } } } else { c1 = c; for (; c < e; c++) { if (EIMIL_istag_s(*c)) break; } } EIMIL_set_point(pps, c); pu = EIMIL_resolve_reference(c1, c); return pu; } static int EIMIL_match_name( EIMIL_parser_state *pps, char *name ) { Ebyte *c, *e; c = pps->current; e = pps->end; while (c < e) { if (!*name) return 1; if (*name != *c) return 0; name++; c++; } return 0; } /* Find out the next token. Return the point of the tag if it is found. Never move the points of pps. */ static Ebyte* EIMIL_next_token( EIMIL_data *ped ) { EIMIL_parser_state *pps = &ped->pcommon->ps; Ebyte *c, *e; c = pps->current; e = pps->end; for (; c < e; c++) { if (!EIMIL_isspace(*c)) return c; } return NULL; } /* Find out the next tag. Return the point of the tag if it is found. Never move the points of pps. */ static Ebyte* EIMIL_next_tag( EIMIL_data *ped ) { EIMIL_parser_state *pps = &ped->pcommon->ps; Ebyte *c, *e; c = pps->current; e = pps->end; for (; c < e; c++) { if (EIMIL_istag_s(*c)) return c; } return NULL; } /* Parse the current tag where pps->current points. tag's end must be in [pps->current, pps->end). Move the pps->current to the end of tag. */ static enum EIMIL_TAG_TYPE EIMIL_parse_tag( EIMIL_data *ped, UTF8 **puri, UTF8 **pname, EIMIL_attrs **ppattrs ) { EIMIL_parser_state *pps = &ped->pcommon->ps; Ebyte *c, *c1, *e; UTF8 *prefix = NULL; EIMIL_attrs *pat = NULL; enum EIMIL_TAG_TYPE tt = EIMIL_INVALID_TAG; *puri = *pname = NULL; *ppattrs = NULL; c = pps->current; e = pps->end; /* ?> or > or ]]> or or or or */ if (!EIMIL_istag_s(*c)) return EIMIL_CHARDATA; c++; EIMIL_check_EOF(c, e, ped); if (EIMIL_isPImark(*c)) { /* ?> */ c = EIMIL_skip_to(c, e, "?>"); EIMIL_set_point(pps, c + 2); return EIMIL_PI_TAG; } else if (EIMIL_isEXmark(*c)) { if ((c1 = EIMIL_match(c, e, "!--")) != NULL) { c = EIMIL_skip_to(c1, e, "--"); c += 2; if (!EIMIL_istag_e(*c)) { EIMIL_set_error_pt(ped, c, "`--' must not occur within comments."); return EIMIL_INVALID_TAG; } c++; EIMIL_check_EOF(c, e, ped); EIMIL_set_point(pps, c); return EIMIL_COMMENT_TAG; } if ((c1 = EIMIL_match(c, e, "!DOCTYPE")) != NULL) { c = EIMIL_skip_to(c1, e, ">"); EIMIL_set_point(pps, c + 1); return EIMIL_DOCTYPE_TAG; } EIMIL_check_EOF(c1, e, ped); if ((c1 = EIMIL_match(c, e, "![CDATA[")) != NULL) { c = EIMIL_skip_to(c1, e, "]]>"); EIMIL_set_point(pps, c + 3); return EIMIL_CDATA_TAG; } EIMIL_check_EOF(c1, e, ped); EIMIL_set_error_pt(ped, c, "Unknown token. Normal tags must not start with `!'."); } else if (EIMIL_isetag_mark(*c)) { /* */ tt = EIMIL_END_TAG; c++; if (!(c = EIMIL_get_name(ped, c, e, pname, &prefix))) { EIMIL_check_EOF(c, e, ped); goto error_cleanup; } EIMIL_skip_S(c, e, pps); } else { EIMIL_namespace_newbind(pps); if (!(c = EIMIL_get_name(ped, c, e, pname, &prefix))) { EIMIL_check_EOF(c, e, ped); goto error_cleanup; } if (EIMIL_isspace(*c)) { EIMIL_set_point(pps, c); pat = EIMIL_parse_attrs(ped); c = pps->current; } EIMIL_skip_S(c, e, pps); EIMIL_check_EOF(c, e, ped); if (EIMIL_isetag_mark(*c)) { tt = EIMIL_EMPTY_TAG; /* */ c++; } else { tt = EIMIL_START_TAG; } } if (!EIMIL_istag_e(*c)) goto error_cleanup; *puri = EIMIL_get_prefix_namespace(pps, prefix); if (prefix && !(*puri)) { EIMIL_set_error_pt(ped, c, "name is not belong to any namespace."); goto error_cleanup; } if (prefix) free(prefix); *ppattrs = pat; EIMIL_set_point(pps, c + 1); return tt; error_cleanup: if (prefix) free(prefix); if (*pname) free(*pname); *pname = *puri = NULL; *ppattrs = NULL; if (pat) { EIMIL_free_attrs(pat); } return EIMIL_INVALID_TAG; } /* Find out the element in contents, and parse it. pps->current is always reset to the pps->start at the startup. */ static int EIMIL_parse_element( EIMIL_data *ped, EIMIL_element_template *pet, EIMIL_element_template *pet_current, void* private, UTF8 *current_element_name, UTF8 *current_element_uri ) { EIMIL_parser_state *pps = &ped->pcommon->ps; UTF8 *name, *uri; const UTF8 *saved_uri; enum EIMIL_TAG_TYPE tt; int i, n; int option; int *pelemnums; void *saved_private; Ebyte *s, *c, *e; Ebyte *tag_end; EIMIL_attrs *pat = NULL; EIMIL_element_template *pet2; s = pps->start; e = pps->end; EIMIL_set_point(pps, s); if (pet_current) option = pet_current->option; else option = 0; if (pps->element_depth > MAX_ELEMENT_DEPTH) { EIMIL_set_error_pt(ped, NULL, "The limit of element depth is exceeded."); return 0; } /* count the number of pet */ if (pet) { for (n = 0, pet2 = pet; pet2->name; pet2++) n++; } else { n = 0; } pelemnums = (int*) alloca(sizeof(int) * n); memset(pelemnums, 0, sizeof(int) * n); pet2 = pet; name = NULL; i = 0; while ((c = EIMIL_next_token(ped)) != NULL) { EIMIL_set_point(pps, c); if (name) free(name); tt = EIMIL_parse_tag(ped, &uri, &name, &pat); tag_end = pps->current; EIMIL_set_point(pps, c); /* At this point, <-- `tag_end' points ^-- `pps->current' points */ if (tt == EIMIL_END_TAG) { if (current_element_name && (strcmp(name, current_element_name) == 0) && ((uri == current_element_uri) || (uri && current_element_uri && (strcmp(uri, current_element_uri) == 0)))) { EIMIL_namespace_unbind(pps); e = tag_end; break; } EIMIL_set_error_pt(ped, NULL, "Unbalanced end tag:%s, %s.", name, current_element_name); goto error_cleanup; } else if ((tt == EIMIL_PI_TAG) || (tt == EIMIL_COMMENT_TAG) || (tt == EIMIL_DOCTYPE_TAG) || (tt == EIMIL_CDATA_TAG)) { pps->start = tag_end; EIMIL_set_point(pps, tag_end); continue; } else if (tt == EIMIL_CHARDATA) { if (pet_current && (!(pet_current->option & EIMIL_allow_PCDATA))) { EIMIL_set_error_pt(ped, NULL, "Invalid character token."); goto error_cleanup; } if (pet_current && pet_current->func) { UTF8 *pu = EIMIL_get_UTF8data_token(ped, pet_current->option); if (!pu) goto error_cleanup; if (!((*pet_current->func)(ped, NULL, tt, pu, &private))) goto error_cleanup; free(pu); } else { c = EIMIL_next_tag(ped); if (!c) { EIMIL_set_error_pt(ped, NULL, "Cannot find out any start tag while parsing chardata."); goto error_cleanup; } EIMIL_set_point(pps, c); } continue; } else if (tt == EIMIL_INVALID_TAG) { goto error_cleanup; } if ((uri) && (strcmp(uri, pps->current_uri) == 0)) { if (pet2) { if ((i > 0) && (pet2->option & EIMIL_element_lock_template)) { /* When EIMIL_element_lock_template is set, other we don't check any other template than pet2. */ if (strcmp(pet2->name, name) != 0) { EIMIL_set_error_pt(ped, NULL, "%s must not be after %s.", name, pet2->name); if (pat) EIMIL_free_attrs(pat); goto error_cleanup; } } else { if (!(option & EIMIL_subelement_ordered)) pet2 = pet; for (; pet2->name; pet2++) { if (strcmp(pet2->name, name) == 0) break; } } } if (!pet2 || !pet2->name) { EIMIL_set_error_pt(ped, NULL, "Unknown tag:%s.", name); if (pat) EIMIL_free_attrs(pat); goto error_cleanup; } if (pet2->attrtpls) { if (!EIMIL_check_attrs(ped, pet2->attrtpls, &pat)) { goto error_cleanup; } } i++; pelemnums[pet2 - pet]++; EIMIL_SET_CURRENT_SUBELEMENT_TEMPLATE(pps, pet2->subelems); saved_uri = pps->current_uri; if (pet2->func) { pps->pcet = pet2; pps->element_idx = i; saved_private = private; if (!((*pet2->func)(ped, pat, tt, NULL, &private))) { goto error_cleanup; } } if (tt == EIMIL_EMPTY_TAG) { EIMIL_namespace_unbind(pps); if (!(pet2->option & EIMIL_element_EMPTY)) { EIMIL_set_error_pt(ped, NULL, "Tag %s must not be an empty element tag", name); goto error_cleanup; } c = tag_end; } else { int status; if (pet2->option & EIMIL_element_EMPTY) { EIMIL_set_error_pt(ped, NULL, "Tag %s must be an empty element tag", name); goto error_cleanup; } pps->start = tag_end; status = EIMIL_parse_element(ped, pps->psubet, pet2, private, name, uri); /* Now `c' points the end of element. */ c = pps->end; if (status != -1) { if (status) EIMIL_set_error_pt(ped, tag_end, "End tag is missing:%s.", name); goto error_cleanup; } if (pet2->func) { pps->start = tag_end; pps->end = pps->current; pps->element_idx = i; pps->pcet = pet2; if (!((*pet2->func)(ped, pat, EIMIL_END_TAG, NULL, &private))) { goto error_cleanup; } private = saved_private; } } EIMIL_SET_CURRENT_SUBELEMENT_TEMPLATE(pps, pet2->subelems); pps->current_uri = saved_uri; } else { int status; /* Ignore the element */ if (pat) EIMIL_free_attrs(pat); if (tt == EIMIL_EMPTY_TAG) { EIMIL_namespace_unbind(pps); c = tag_end; } else { /* Skip to the end tag. */ pps->start = tag_end; status = EIMIL_parse_element(ped, pet, NULL, private, name, uri); /* Now `c' points the end of element. */ c = pps->end; if (status != -1) { if (status) EIMIL_set_error_pt(ped, tag_end, "End tag is mising:%s.", name); goto error_cleanup; } } } pps->start = c; EIMIL_set_point(pps, c); pps->end = e; } if (name) free(name); name = NULL; pps->start = s; pps->end = e; if (pet) { /* Check the number of elements. */ int opt; int *pn; for (pet2 = pet, pn = pelemnums; pet2->name; pet2++, pn++) { opt = (pet2->option | EIMIL_element_option_mask); if ((opt == EIMIL_element_single) && (*pn != 1)) { EIMIL_set_error_pt(ped, NULL, "element:%s must be only 1.", pet2->name); return 0; } else if ((opt == EIMIL_element_morethan1) && (*pn < 1)) { EIMIL_set_error_pt(ped, NULL, "element:%s must be more than 1.", pet2->name); return 0; } else if ((opt == EIMIL_element_0or1) && (*pn > 1)) { EIMIL_set_error_pt(ped, NULL, "element:%s must be 0 or 1.", pet2->name); return 0; } } } if (tt == EIMIL_END_TAG) return -1; return 1; error_cleanup: if (name) free(name); return 0; } /******************************************************************************** EIMIL value related services. ********************************************************************************/ EIMIL_value* EIMIL_construct_number( int number ) { EIMIL_value *pv; pv = (EIMIL_value*) malloc(sizeof(EIMIL_value)); if (!pv) return NULL; pv->type = EIMIL_TYPE_NUMBER; pv->v.number = number; pv->refcount = 0; return pv; } EIMIL_value* EIMIL_construct_bool( int bool_val ) { if (!bool_val) return NULL; return &EIMIL_t_val; } EIMIL_value* EIMIL_construct_char( UTF32 ch ) { EIMIL_value *pv; pv = (EIMIL_value*) malloc(sizeof(EIMIL_value)); if (!pv) return NULL; pv->type = EIMIL_TYPE_CHAR; pv->v.ch = ch; pv->refcount = 0; return pv; } EIMIL_value* EIMIL_construct_event( UTF8 *type, EIMIL_value *pv_val, EIMIL_value *pv_mod, EIMIL_value *pv_char, EIMIL_value *pv_mtext ) { EIMIL_value *pv; ASSERT(!pv_val || pv_val->type == EIMIL_TYPE_NUMBER); ASSERT(!pv_mod || pv_mod->type == EIMIL_TYPE_NUMBER); ASSERT(!pv_char || pv_char->type == EIMIL_TYPE_CHAR); ASSERT(!pv_mtext || pv_mtext->type == EIMIL_TYPE_MTEXT); pv = (EIMIL_value*) malloc(sizeof(EIMIL_value)); if (!pv) return NULL; pv->type = EIMIL_TYPE_EVENT; pv->v.event.type = strdup(type); pv->v.event.pv_val = pv_val; if (pv_val) EIMIL_ADDREF(*pv_val); pv->v.event.pv_mod = pv_mod; if (pv_mod) EIMIL_ADDREF(*pv_mod); pv->v.event.pv_char = pv_char; if (pv_char) EIMIL_ADDREF(*pv_char); pv->v.event.pv_mtext = pv_mtext; if (pv_mtext) EIMIL_ADDREF(*pv_mtext); pv->refcount = 0; return pv; } int EIMIL_construct_events_from_IMInputEvent( IMInputEvent *pimev, EIMIL_value ***pppevs ) { EIMIL_value *pev; switch (pimev->type) { case IM_EventKeyList: { IMKeyListEvent *pimkev = (IMKeyListEvent*) pimev; IMKeyList keylist = pimkev->keylist; int i, n = pimkev->n_key; EIMIL_value **ppevs; EIMIL_value *pev_code, *pev_char, *pev_mod; ppevs = (EIMIL_value**) malloc(sizeof(EIMIL_value*) * n); if (!ppevs) return 0; for (i = 0; i < n; i++) { pev_code = EIMIL_construct_number(keylist[i].keyCode); if (!pev_code) return 0; pev_char = EIMIL_construct_char(keylist[i].keyChar); if (!pev_char) return 0; pev_mod = EIMIL_construct_number(keylist[i].modifier); if (!pev_mod) return 0; pev = EIMIL_construct_event("keyevent", pev_code, pev_char, pev_mod, NULL); if (!pev) return 0; ppevs[i] = pev; } *pppevs = ppevs; return n; } } return 0; } IMInputEvent* EIMIL_convert_event_to_IMInputEvent( EIMIL_event *pev ) { if (strcmp(pev->type, "keyevent") == 0) { IMKeyListEvent *pimkev; IMKeyList keylist; pimkev = (IMKeyListEvent*) malloc(sizeof(IMKeyListEvent)); if (!pimkev) return NULL; memset(pimkev, 0, sizeof(IMKeyListEvent)); keylist = (IMKeyList) malloc(sizeof(IMKeyEventStruct)); if (!keylist) return NULL; memset(keylist, 0, sizeof(IMKeyEventStruct)); pimkev->type = IM_EventKeyList; pimkev->n_key = 1; pimkev->keylist = keylist; if (pev->pv_val) { keylist->keyCode = pev->pv_val->v.number; keylist->keyChar = pev->pv_char->v.ch; keylist->modifier = pev->pv_mod->v.number; } return (IMInputEvent*) pimkev; } return NULL; } EIMIL_value* EIMIL_construct_prop( EIMIL_symbol *psym ) { EIMIL_value *pv; EIMIL_prop *pprop; ASSERT(psym->cat == EIMIL_CAT_PROPERTY); pv = (EIMIL_value*) malloc(sizeof(EIMIL_value)); if (!pv) return NULL; pv->type = EIMIL_TYPE_PROP; pprop = &pv->v.prop; memset(pprop, 0, sizeof(EIMIL_prop)); pprop->st = -1; pprop->end = -1; pprop->property_sym = psym; pprop->type = psym->obj.p.type; pv->refcount = 0; return pv; } EIMIL_value* EIMIL_construct_prop2( enum EIMIL_TYPE type ) { EIMIL_value *pv; EIMIL_prop *pprop; pv = (EIMIL_value*) malloc(sizeof(EIMIL_value)); if (!pv) return NULL; pv->type = EIMIL_TYPE_PROP; pprop = &pv->v.prop; memset(pprop, 0, sizeof(EIMIL_prop)); pprop->st = -1; pprop->end = -1; pprop->type = type; pv->refcount = 0; return pv; } int EIMIL_add_prop( EIMIL_prop *pprop, EIMIL_value *pv ) { EIMIL_value **ppv; ASSERT((!pv) || (pv->type == pprop->type)); ppv = pprop->pvals; ppv = (EIMIL_value**) realloc(ppv, sizeof(EIMIL_value*) * (pprop->size + 1)); if (!ppv) return 0; pprop->pvals = ppv; if (pv) EIMIL_ADDREF(*pv); ppv[pprop->size] = pv; pprop->size++; return 1; } int EIMIL_delete_prop( EIMIL_prop *pprop, int idx ) { EIMIL_value **ppv; if ((idx < 0) || (idx >= pprop->size)) return 0; ppv = pprop->pvals + idx; EIMIL_RMREF(**ppv); if ((pprop->size - idx - 1) > 0) memmove(ppv, ppv + 1, sizeof(EIMIL_value*) * (pprop->size - idx - 1)); pprop->size--; return 1; } void EIMIL_destruct_value( EIMIL_value *pv ) { if (!pv) return; switch(pv->type) { case EIMIL_TYPE_PROP: { int i; EIMIL_value **ppv; EIMIL_detach_prop_from_mtext(pv); for (ppv = pv->v.prop.pvals, i = 0; i < pv->v.prop.size; ppv++, i++) { EIMIL_RMREF(**ppv); } if (pv->v.prop.pvals) free(pv->v.prop.pvals); break; } case EIMIL_TYPE_EVENT: if (pv->v.event.type) free(pv->v.event.type); if (pv->v.event.pv_val) EIMIL_RMREF(*pv->v.event.pv_val); if (pv->v.event.pv_mod) EIMIL_RMREF(*pv->v.event.pv_mod); if (pv->v.event.pv_char) EIMIL_RMREF(*pv->v.event.pv_char); if (pv->v.event.pv_mtext) EIMIL_RMREF(*pv->v.event.pv_mtext); break; case EIMIL_TYPE_MTEXT: EIMIL_destruct_mtext(&pv->v.mtext); break; default: break; } free(pv); } EIMIL_value* EIMIL_copy_value( EIMIL_value *pv ) { EIMIL_value *pv2; if (pv->type == EIMIL_TYPE_BOOL) { ASSERT(pv == &EIMIL_t_val); return pv; } pv2 = (EIMIL_value*) malloc(sizeof(EIMIL_value)); memset(pv2, 0, sizeof(EIMIL_value)); if (!pv2) return NULL; pv2->type = pv->type; pv2->refcount = 0; switch(pv->type) { case EIMIL_TYPE_NUMBER: pv2->v.number = pv->v.number; break; case EIMIL_TYPE_CHAR: pv2->v.ch = pv->v.ch; break; case EIMIL_TYPE_PROP: { int i; EIMIL_value *pvtmp; pv2->v.prop = pv->v.prop; pv2->v.prop.pvals = (EIMIL_value**) malloc(sizeof(EIMIL_value*) * pv->v.prop.size); if (!pv2->v.prop.pvals) return NULL; for (i = 0; i < pv->v.prop.size; i++) { pvtmp = pv->v.prop.pvals[i]; EIMIL_ADDREF(*pvtmp); pv2->v.prop.pvals[i] = pvtmp; } break; } case EIMIL_TYPE_EVENT: pv2->v.event.type = strdup(pv->v.event.type); if (!pv2->v.event.type) { free(pv2); return NULL; } if (pv->v.event.pv_val) { pv2->v.event.pv_val = EIMIL_copy_value(pv->v.event.pv_val); if (!pv2->v.event.pv_val) { free(pv2->v.event.type); free(pv2); return NULL; } } if (pv->v.event.pv_mod) { pv2->v.event.pv_mod = EIMIL_copy_value(pv->v.event.pv_mod); if (!pv2->v.event.pv_mod) { EIMIL_destruct_value(pv->v.event.pv_val); free(pv2->v.event.type); free(pv2); return NULL; } } if (pv->v.event.pv_char) { pv2->v.event.pv_char = EIMIL_copy_value(pv->v.event.pv_char); if (!pv2->v.event.pv_char) { EIMIL_destruct_value(pv->v.event.pv_val); EIMIL_destruct_value(pv->v.event.pv_mod); free(pv2->v.event.type); free(pv2); return NULL; } } if (!pv->v.event.pv_mtext) break; case EIMIL_TYPE_MTEXT: { int i, j; EIMIL_mtext *pmt2, *pmt; EIMIL_mtext_props *pmp; EIMIL_value **ppv; if (pv->type == EIMIL_TYPE_MTEXT) { pv2->v.mtext = pv->v.mtext; pmt = &pv->v.mtext; pmt2 = &pv2->v.mtext; }else{ pmt = &pv->v.event.pv_mtext->v.mtext; pmt2 = &pv2->v.event.pv_mtext->v.mtext; } pmt2->ustr = (UTF32*) malloc(sizeof(UTF32) * pmt->len); if (!pmt2->ustr) { free(pv2); return NULL; } memcpy(pmt2->ustr, pmt->ustr, sizeof(UTF32) * pmt->len); pmt2->pslots = (EIMIL_mtext_props*) malloc(sizeof(EIMIL_mtext_props) * pmt->slotsnum); if (!pmt2->pslots) { free(pmt2->ustr); free(pv2); return NULL; } memcpy(pmt2->pslots, pmt->pslots, sizeof(EIMIL_mtext_props) * pmt->slotsnum); for (pmp = pmt2->pslots, i = 0; i < pmt2->slotsnum; i++, pmp++) { ppv = (EIMIL_value**) malloc(sizeof(EIMIL_value*) * pmp->num); if (!ppv) { free(pmt2->pslots); free(pmt2->ustr); free(pv2); return NULL; } memcpy(ppv, pmp->pprops, sizeof(EIMIL_value*) * pmp->num); pmp->pprops = ppv; for (j = 0; j < pmp->num; j++, ppv++) { ASSERT((*ppv)->type == EIMIL_TYPE_PROP); *ppv = EIMIL_copy_value(*ppv); if (!*ppv) { free(pmt2->pslots); free(pmt2->ustr); free(pv2); return NULL; } (*ppv)->v.prop.target = pmt2; EIMIL_ADDREF(**ppv); } } break; } default: abort(); } return pv2; } int EIMIL_value_equal( EIMIL_value *pv1, EIMIL_value *pv2 ) { if (pv1 == pv2) return 1; /* TODO!!! */ return 0; } int EIMIL_generate_diff( EIMIL_symbol *psym, EIMIL_value *pv2, IMDifferential *pdiff ) { EIMIL_value *pv1; ASSERT(psym->cat == EIMIL_CAT_VARIABLE); pv1 = psym->obj.v.pv; memset(pdiff, 0, sizeof(IMDifferential)); ASSERT((!pv2) || (psym->obj.v.type == pv2->type)); switch(psym->obj.v.type) { case EIMIL_TYPE_NUMBER: if ((pv1 != pv2) || (pv1->v.number != pv2->v.number)) { pdiff->number = pv1->v.number; return 1; } break; case EIMIL_TYPE_BOOL: if ((pv1 != pv2) || (pv1->v.bool_val != pv2->v.bool_val)) { pdiff->bool_val = pv2->v.bool_val; return 1; } break; case EIMIL_TYPE_CHAR: if ((pv1 != pv2) || (pv1->v.ch != pv2->v.ch)) { pdiff->ch = pv2->v.ch; return 1; } break; case EIMIL_TYPE_MTEXT: return EIMIL_mtext_diff(&pv2->v.mtext, &pv1->v.mtext, pdiff); break; default: abort(); } return 0; } /******************************************************************************** EIMIL dictionary service. ********************************************************************************/ #define EIMIL_DICTIONARY_DEFAULT_SIZE 53 /* should be a primary number! */ #define EIMIL_DICTIONARY_SUBSLOT_UNIT 8 struct EIMIL_dictionary { int size; EIMIL_symbol ***namedic; EIMIL_symbol ***iddic; }; EIMIL_dictionary* EIMIL_new_dictionary( int size, int id_req_p ) { EIMIL_dictionary *pdic; EIMIL_symbol ***pppsym; if (size == 0) size = EIMIL_DICTIONARY_DEFAULT_SIZE; pdic = malloc(sizeof(EIMIL_dictionary)); if (!pdic) { return NULL; } pdic->size = size; pppsym = (EIMIL_symbol***) malloc(sizeof(EIMIL_dictionary**) * size); if (!pppsym) { free(pdic); return NULL; } memset(pppsym, 0, sizeof(EIMIL_dictionary**) * size); pdic->namedic = pppsym; if (id_req_p) { pppsym = (EIMIL_symbol***) malloc(sizeof(EIMIL_dictionary**) * size); if (!pppsym) { free(pdic->namedic); free(pdic); return NULL; } memset(pppsym, 0, sizeof(EIMIL_dictionary**) * size); pdic->iddic = pppsym; } else { pdic->iddic = NULL; } return pdic; } void EIMIL_free_dictionary( EIMIL_dictionary *pdic ) { if (!pdic) return; /* TODO: free EIMIL_objects!!! */ free(pdic->namedic); if (pdic->iddic) free(pdic->iddic); free(pdic); return; } void EIMIL_free_dictionary_and_symbol( EIMIL_dictionary *pdic ) { int i; EIMIL_symbol **pps; if (!pdic) return; for (i = 0; i < pdic->size; i++) { for (pps = pdic->namedic[i]; (pps && *pps); pps++) { EIMIL_destruct_symbol(*pps); } if (pdic->namedic[i]) free(pdic->namedic[i]); } if (pdic->iddic) { for (i = 0; i < pdic->size; i++) { if (pdic->iddic[i]) free(pdic->iddic[i]); } } EIMIL_free_dictionary(pdic); return; } static int hash_function_string( unsigned char *name, int size ) { int i, v; for (v = 0; *name; i++, name++) { v = ((v << 8) + *name) % size; } return v; } #ifdef DEBUG static void list_symbols( EIMIL_dictionary *pdic ) { int i; EIMIL_symbol **pps; for (i = 0; i < pdic->size; i++) { for (pps = pdic->namedic[i]; (pps && *pps); pps++) { fprintf(stdout, "SYM(name):%s(%d)\n", (*pps)->name, (*pps)->symbolid); ASSERT(hash_function_string((*pps)->name, pdic->size) == i); } } if (pdic->iddic) { for (i = 0; i < pdic->size; i++) { for (pps = pdic->iddic[i];(pps && *pps);pps++) { fprintf(stdout, "SYM(id):%s(%d)\n", (*pps)->name, (*pps)->symbolid); ASSERT(((*pps)->symbolid % pdic->size) == i); } } } return; } #endif static CARD32BIT EIMIL_generate_symbolid( EIMIL_data *ped, enum EIMIL_CATEGORY cat, enum EIMIL_TYPE type ) { int c; CARD32BIT r = 0; switch (cat) { case EIMIL_CAT_VARIABLE: switch (type) { case EIMIL_TYPE_NIL: return EIMIL_SYMBOL_ID_NIL; case EIMIL_TYPE_T: return EIMIL_SYMBOL_ID_T; case EIMIL_TYPE_FEEDBACK: return EIMIL_SYMBOL_ID_FEEDBACK; case EIMIL_TYPE_CANDIDATES: return EIMIL_SYMBOL_ID_CANDIDATES; case EIMIL_TYPE_ANY: r = EIMIL_SYMBOL_ID_PRIVATE; break; case EIMIL_TYPE_BOOL: r = EIMIL_SYMBOL_ID_VARIABLE_BOOL; break; case EIMIL_TYPE_NUMBER: r = EIMIL_SYMBOL_ID_VARIABLE_NUMBER; break; case EIMIL_TYPE_CHAR: r = EIMIL_SYMBOL_ID_VARIABLE_CHAR; break; case EIMIL_TYPE_MTEXT: r = EIMIL_SYMBOL_ID_VARIABLE_MTEXT; break; case EIMIL_TYPE_EVENT: r = EIMIL_SYMBOL_ID_VARIABLE_EVENT; break; case EIMIL_TYPE_PROP: r = EIMIL_SYMBOL_ID_VARIABLE_PROP; break; default: abort(); } break; case EIMIL_CAT_PROPERTY: switch (type) { case EIMIL_TYPE_BOOL: r = EIMIL_SYMBOL_ID_PROPERTY_BOOL; break; case EIMIL_TYPE_NUMBER: r = EIMIL_SYMBOL_ID_PROPERTY_NUMBER; break; case EIMIL_TYPE_CHAR: r = EIMIL_SYMBOL_ID_PROPERTY_CHAR; break; case EIMIL_TYPE_MTEXT: r = EIMIL_SYMBOL_ID_PROPERTY_MTEXT; break; default: abort(); } break; case EIMIL_CAT_OPERATION: r = EIMIL_SYMBOL_ID_OPERATION; break; case EIMIL_CAT_FUNCTION: r = EIMIL_SYMBOL_ID_FUNCTION; break; case EIMIL_CAT_EXCEPTION: r = EIMIL_SYMBOL_ID_EXCEPTION; break; default: abort(); } LOCK_SYNC_OBJECT(ped->pcommon->sync_object); c = ++ped->pcommon->id_counter; UNLOCK_SYNC_OBJECT(ped->pcommon->sync_object); if (c > EIMIL_ID_MAX) { /* TODO: we should return error code. */ abort(); } return ((r << 16) | c); } static EIMIL_symbol* EIMIL_make_symbol( char *name, int len, enum EIMIL_CATEGORY cat ) { EIMIL_symbol *p; p = (EIMIL_symbol*) malloc(sizeof(EIMIL_symbol)); if (!p) return NULL; memset(p, 0, sizeof(EIMIL_symbol)); p->name = (char*) malloc(sizeof(char) * (len + 1)); if (!p->name) { free(p); return NULL; } p->namelen = len; memcpy(p->name, name, len); p->name[len] = '\0'; p->cat = cat; return p; } static EIMIL_symbol* lookup_symbol( EIMIL_dictionary *pdic, unsigned char *name ) { EIMIL_symbol **p, *psym; int len = strlen(name); int hash = hash_function_string(name, pdic->size); p = pdic->namedic[hash]; if (!p) return NULL; for (; *p; p++) { psym = *p; if ((psym->namelen == len) && (memcmp(name, psym->name, len) == 0)) return psym; } return NULL; } static EIMIL_symbol* lookup_predefined_symbol( unsigned char *name ) { return lookup_symbol(pdic_internal, name); } static int register_symbol_id( EIMIL_dictionary *pdic, EIMIL_symbol *psym ) { int i; int id = psym->symbolid; int idx = id % pdic->size; EIMIL_symbol **p; if (!pdic->iddic) return 1; p = pdic->iddic[idx]; if (!p) { /* create iddic[idx] */ p = (EIMIL_symbol**) calloc(sizeof(EIMIL_symbol *), EIMIL_DICTIONARY_SUBSLOT_UNIT); if (!p) return 0; pdic->iddic[idx] = p; p[0] = psym; return 1; } for (i = 1;; i++, p++) { if (!*p) { if ((i % EIMIL_DICTIONARY_SUBSLOT_UNIT) == 0) { /* ID dictionary is fulled with EIMIL_symbol. grow this dictionary. */ p = (EIMIL_symbol**) realloc(pdic->iddic[idx], (i + EIMIL_DICTIONARY_SUBSLOT_UNIT) * sizeof(EIMIL_symbol*)); if (!p) return 0; pdic->iddic[idx] = p; p = p + i - 1; memset(p, 0, EIMIL_DICTIONARY_SUBSLOT_UNIT * sizeof(EIMIL_symbol *)); } p[0] = psym; p[1] = NULL; return 1; } } /* notreached */ abort(); return 0; } static EIMIL_symbol* register_symbol( EIMIL_dictionary *pdic, unsigned char *name, enum EIMIL_CATEGORY cat, int id ) { int i; EIMIL_symbol **p; EIMIL_symbol *psym; int len = strlen(name); int hash = hash_function_string(name, pdic->size); int id_req_p = pdic->iddic ? 1 : 0; if (lookup_predefined_symbol(name)) return NULL; p = pdic->namedic[hash]; if (!p) { p = (EIMIL_symbol**) calloc(EIMIL_DICTIONARY_SUBSLOT_UNIT, sizeof(EIMIL_symbol*)); if (!p) return NULL; pdic->namedic[hash] = p; psym = EIMIL_make_symbol(name, len, cat); psym->symbolid = id; if (!psym) return NULL; /* register psym to id dictionary */ /* FIXME: may cause memory leaks if failed */ if (!register_symbol_id(pdic, psym)) return NULL; p[0] = psym; return psym; } for (i = 1; ; i++, p++) { if (!*p) { psym = EIMIL_make_symbol(name, len, cat); psym->symbolid = id; if (!psym) return NULL; /* register psym to id dictionary */ /* FIXME: may cause memory leaks if failed */ if (!register_symbol_id(pdic, psym)) return NULL; if ((i % EIMIL_DICTIONARY_SUBSLOT_UNIT) == 0) { /* Named dictionary is fulled with psym, grow it */ p = (EIMIL_symbol**) realloc(pdic->namedic[hash], (i + EIMIL_DICTIONARY_SUBSLOT_UNIT) * sizeof(EIMIL_symbol*)); pdic->namedic[hash] = p; p = p + i - 1; memset(p, 0, sizeof(EIMIL_DICTIONARY_SUBSLOT_UNIT) * sizeof(EIMIL_symbol*)); } p[0] = psym; p[1] = NULL; return psym; } psym = *p; /* check whether name is already registerd */ if ((psym->namelen == len) && (memcmp(name, psym->name, len) == 0)) return NULL; } return NULL; } static int re_register_symbol_internal( EIMIL_dictionary *pdic, EIMIL_symbol *psym ) { int i; int id = psym->symbolid; int idx = id % pdic->size; EIMIL_symbol **p; if (!pdic->iddic) return 1; p = pdic->iddic[idx]; if (!p) return 0; for (i = 1; *p; i++, p++) { if ((*p)->symbolid == psym->symbolid) { *p = psym; return 1; } } /* notreached */ abort(); return 0; } EIMIL_symbol* EIMIL_register_symbol( EIMIL_data *ped, EIMIL_dictionary *pdic, unsigned char *name, enum EIMIL_CATEGORY cat, enum EIMIL_TYPE type ) { int id; int id_req_p = pdic->iddic ? 1 : 0; if (id_req_p) id = EIMIL_generate_symbolid(ped, cat, type); else id = 0; return register_symbol(pdic, name, cat, id); } EIMIL_symbol* EIMIL_intern_soft( EIMIL_dictionary *pdic, unsigned char *name ) { EIMIL_symbol *psym; if ((psym = lookup_predefined_symbol(name)) != NULL) return psym; return lookup_symbol(pdic, name); } EIMIL_symbol* EIMIL_lookup_symbol_internal( EIMIL_dictionary *pdic, CARD32BIT id ) { int idx; EIMIL_symbol **p, *psym; switch (id) { case EIMIL_SYMBOL_ID_NIL: return pEIMIL_nil_sym; case EIMIL_SYMBOL_ID_T: return pEIMIL_t_sym; case EIMIL_SYMBOL_ID_FEEDBACK: return pEIMIL_feedback_sym; case EIMIL_SYMBOL_ID_CANDIDATES: return pEIMIL_candidates_sym; default: if (!pdic->iddic) return NULL; idx = id % pdic->size; p = pdic->iddic[idx]; if (!p) return NULL; for (; *p; p++) { psym = *p; if (psym->symbolid == id) return psym; } } return NULL; } void EIMIL_destruct_symbol( EIMIL_symbol *psym ) { if (!psym) return; if (psym->name) free(psym->name); switch(psym->cat) { case EIMIL_CAT_VARIABLE: if (psym->obj.v.pv) { if (!psym->obj.v.constp) EIMIL_RMREF(*psym->obj.v.pv); } break; case EIMIL_CAT_EXCEPTION: { if (psym->obj.e.msgs) { EIMIL_message *msgs; for (msgs = psym->obj.e.msgs;msgs->lang;msgs++) { free(msgs->lang); free(msgs->msg); } free(psym->obj.e.msgs); } } case EIMIL_CAT_OPERATION: { int i, n; EIMIL_dependency *pdeps; EIMIL_symbol **pps; n = psym->obj.o.numdepends; pdeps = psym->obj.o.pdeps; for (i = 0;i < n;i++, pdeps++) { pps = pdeps->depends; if (pps) free(pps); pps = pdeps->affects; if (pps) free(pps); } free(pdeps); break; } case EIMIL_CAT_FUNCTION: /* TODO */ break; default: break; } free(psym); } static EIMIL_symbol* copy_symbol( EIMIL_symbol *psym ) { EIMIL_symbol *p; p = (EIMIL_symbol*) malloc(sizeof(EIMIL_symbol)); if (!p) return NULL; *p = *psym; p->name = (char*) malloc(sizeof(char) * (psym->namelen + 1)); if (!p->name) { free(p); return NULL; } p->namelen = psym->namelen; memcpy(p->name, psym->name, p->namelen + 1); return p; } static EIMIL_symbol* duplicate_variable_symbol( EIMIL_symbol *psym ) { EIMIL_value *pvs, *pvd; EIMIL_symbol *p; ASSERT(psym->cat == EIMIL_CAT_VARIABLE); p = copy_symbol(psym); if (!p) return NULL; p->obj.v.pv = NULL; pvs = psym->obj.v.pv; if (!pvs) return p; pvd = EIMIL_copy_value(pvs); if (!pvd) { EIMIL_destruct_symbol(p); return NULL; } EIMIL_ADDREF(*pvd); p->obj.v.pv = pvd; return p; } static EIMIL_symbol* rebuild_operation_symbol( EIMIL_dictionary *pdic, EIMIL_symbol *psym ) { int i, j, n; EIMIL_operation *pop, *popd; EIMIL_dependency *pdeps_s, *pdeps_d; EIMIL_symbol *p, *psymt; EIMIL_symbol **pps_s, **pps_d; ASSERT(psym->cat == EIMIL_CAT_OPERATION); p = copy_symbol(psym); if (!p) return NULL; pop = &psym->obj.o; popd = &p->obj.o; popd->pdeps = NULL; if (pop->numdepends > 0) { pdeps_d = (EIMIL_dependency*) malloc(sizeof(EIMIL_dependency) * pop->numdepends); if (!pdeps_d) { EIMIL_destruct_symbol(p); return NULL; } memset(pdeps_d, 0, sizeof(EIMIL_dependency) * pop->numdepends); } else { pdeps_d = NULL; } popd->pdeps = pdeps_d; popd->numdepends = pop->numdepends; pdeps_s = pop->pdeps; for (i = 0; i < pop->numdepends; i++, pdeps_s++, pdeps_d++) { if (pdeps_s->numdepends > 0) { n = pdeps_s->numdepends; pps_s = pdeps_s->depends; pps_d = (EIMIL_symbol**) malloc(sizeof(EIMIL_symbol*) * n); if (!pps_d) { EIMIL_destruct_symbol(p); return NULL; } pdeps_d->numdepends = n; pdeps_d->depends = pps_d; for (j = 0; j < n; j++, pps_s++, pps_d++) { psymt = EIMIL_lookup_symbol_internal(pdic, (*pps_s)->symbolid); ASSERT(psymt); ASSERT(psymt->cat == EIMIL_CAT_VARIABLE); *pps_d = psymt; } } if (pdeps_s->numaffects > 0) { n = pdeps_s->numaffects; pps_s = pdeps_s->affects; pps_d = (EIMIL_symbol**) malloc(sizeof(EIMIL_symbol*) * n); if (!pps_d) { EIMIL_destruct_symbol(p); return NULL; } pdeps_d->numaffects = n; pdeps_d->affects = pps_d; for (j = 0; j < n; j++, pps_s++, pps_d++) { psymt = EIMIL_lookup_symbol_internal(pdic, (*pps_s)->symbolid); ASSERT(psymt); ASSERT(psymt->cat == EIMIL_CAT_VARIABLE); *pps_d = psymt; } } } return p; } EIMIL_dictionary* EIMIL_duplicate_dictionary( EIMIL_dictionary *psdic ) { int i, j, n, size; EIMIL_symbol **ppss, **ppsd, **ppsdh; EIMIL_symbol *psym; EIMIL_dictionary *pddic; pddic = EIMIL_new_dictionary(psdic->size, (psdic->iddic != NULL)); if (!pddic) return NULL; /* STEP1: shallow copy */ for (i = 0; i < psdic->size; i++) { ppss = psdic->namedic[i]; if (!ppss) continue; for (n = 0; *ppss; ppss++, n++); ppss = psdic->namedic[i]; size = (((n / EIMIL_DICTIONARY_SUBSLOT_UNIT) + 1) * EIMIL_DICTIONARY_SUBSLOT_UNIT); ppsdh = (EIMIL_symbol**) malloc(sizeof(EIMIL_symbol*) * size); if (!ppsdh) return NULL; ppsd = ppsdh; ppsd[n] = NULL; for (j = 0; j < n; j++, ppss++, ppsd++) { *ppsd = *ppss; register_symbol_id(pddic, *ppsd); } pddic->namedic[i] = ppsd; } /* STEP2: variable duplication. */ for (i = 0; i < psdic->size; i++) { ppss = psdic->namedic[i]; if (!ppss) continue; ppsd = pddic->namedic[i]; for (; *ppss; ppss++, ppsd++) { if (ppss[0]->cat == EIMIL_CAT_VARIABLE) { psym = duplicate_variable_symbol(*ppss); if (!psym) return NULL; *ppsd = psym; re_register_symbol_internal(pddic, psym); } } } /* STEP3: rebuild operation symbol. */ for (i = 0; i < psdic->size; i++) { ppss = psdic->namedic[i]; if (!ppss) continue; ppsd = pddic->namedic[i]; for (; *ppss; ppss++, ppsd++) { if (ppss[0]->cat == EIMIL_CAT_OPERATION) { psym = rebuild_operation_symbol(pddic, *ppss); if (!psym) return NULL; *ppsd = psym; re_register_symbol_internal(pddic, psym); } } } return pddic; } /******************************************************************************** EIMIL document definition. *********************************************************************************/ /*************************************** IDP parser ***************************************/ /* function declaration. */ static int EIMIL_message_element_parser( EIMIL_data *ped, EIMIL_attrs *patr, enum EIMIL_TAG_TYPE type, UTF8 *pchars, void **pprivate ); static int EIMIL_dependency_element_parser( EIMIL_data *ped, EIMIL_attrs *patr, enum EIMIL_TAG_TYPE type, UTF8 *pchars, void **pprivate ); static int EIMIL_decldata_element_parser( EIMIL_data *ped, EIMIL_attrs *patr, enum EIMIL_TAG_TYPE type, UTF8 *pchars, void **pprivate ); static int EIMIL_declprop_element_parser( EIMIL_data *ped, EIMIL_attrs *patr, enum EIMIL_TAG_TYPE type, UTF8 *pchars, void **pprivate ); static int EIMIL_declop_element_parser( EIMIL_data *ped, EIMIL_attrs *patr, enum EIMIL_TAG_TYPE type, UTF8 *pchars, void **pprivate ); static int EIMIL_commitnotify_element_parser( EIMIL_data *ped, EIMIL_attrs *patr, enum EIMIL_TAG_TYPE type, UTF8 *pchars, void **pprivate ); static int EIMIL_declexception_element_parser( EIMIL_data *ped, EIMIL_attrs *patr, enum EIMIL_TAG_TYPE type, UTF8 *pchars, void **pprivate ); static int EIMIL_UIdata_element_parser( EIMIL_data *ped, EIMIL_attrs *patr, enum EIMIL_TAG_TYPE type, UTF8 *pchars, void **pprivate ); static int EIMIL_inherit_element_parser( EIMIL_data *ped, EIMIL_attrs *patr, enum EIMIL_TAG_TYPE type, UTF8 *pchars, void **pprivate ); static int EIMIL_interface_element_parser( EIMIL_data *ped, EIMIL_attrs *patr, enum EIMIL_TAG_TYPE type, UTF8 *pchars, void **pprivate ); static int EIMIL_engine_element_parser( EIMIL_data *ped, EIMIL_attrs *patr, enum EIMIL_TAG_TYPE type, UTF8 *pchars, void **pprivate ); /* IDP document template. */ EIMIL_attr_template EIMIL_attr_langinfo[] = {{"xml:lang", EIMIL_attr_REQUIRED, NULL}, {NULL, 0, NULL}}; EIMIL_attr_template EIMIL_attr_decldata[] = {{"name", EIMIL_attr_REQUIRED, NULL}, {"type", EIMIL_attr_REQUIRED, NULL}, {NULL, 0, NULL}}; EIMIL_attr_template EIMIL_attr_declprop[] = {{"name", EIMIL_attr_REQUIRED, NULL}, {"type", EIMIL_attr_REQUIRED, NULL}, {NULL, 0, NULL}}; EIMIL_attr_template EIMIL_attr_declop[] = {{"name", EIMIL_attr_REQUIRED, NULL}, {NULL, 0, NULL}}; EIMIL_attr_template EIMIL_attr_declexception[] = {{"name", EIMIL_attr_REQUIRED, NULL}, {NULL, 0, NULL}}; EIMIL_attr_template EIMIL_attr_commitnotify[] = {{"name", EIMIL_attr_REQUIRED, NULL}, {NULL, 0, NULL}}; EIMIL_attr_template EIMIL_attr_UIdata[] = {{"depend", EIMIL_attr_REQUIRED, NULL}, {NULL, 0, NULL}}; EIMIL_attr_template EIMIL_attr_message[] = {{"xml:lang", EIMIL_attr_IMPLIED, NULL}, {NULL, 0, NULL}}; EIMIL_attr_template EIMIL_attr_dependency[] = {{"depend", EIMIL_attr_REQUIRED, NULL}, {"affect", EIMIL_attr_REQUIRED, NULL}, {NULL, 0, NULL}}; EIMIL_attr_template EIMIL_attr_EIMIL[] = {{"name", EIMIL_attr_REQUIRED, NULL}, {"revision", EIMIL_attr_REQUIRED, NULL}, {"type", EIMIL_attr_NORMAL, "concreate"}, {NULL, 0, NULL}}; EIMIL_element_template EIMIL_declexception_template[] = {{"message", EIMIL_message_element_parser, EIMIL_attr_message, EIMIL_element_single, NULL, NULL}, {NULL, NULL, NULL, 0, NULL, NULL}}; EIMIL_element_template EIMIL_declop_template[] = {{"dependency", EIMIL_dependency_element_parser, EIMIL_attr_dependency, EIMIL_element_EMPTY | EIMIL_element_single, NULL, NULL}, {NULL, NULL, NULL, 0, NULL, NULL}}; EIMIL_element_template EIMIL_interface_template[] = {{"langinfo", NULL, EIMIL_attr_langinfo, EIMIL_element_single, NULL, NULL}, {"decldata", EIMIL_decldata_element_parser, EIMIL_attr_decldata, EIMIL_element_EMPTY | EIMIL_element_multiple, NULL, NULL}, {"declprop", EIMIL_declprop_element_parser, EIMIL_attr_declprop, EIMIL_element_EMPTY | EIMIL_element_multiple, NULL, NULL}, {"declop", EIMIL_declop_element_parser, EIMIL_attr_declop, EIMIL_element_multiple, EIMIL_declop_template, NULL}, {"commitnotify", EIMIL_commitnotify_element_parser, EIMIL_attr_commitnotify, EIMIL_element_EMPTY | EIMIL_element_0or1, NULL, NULL}, {"declexception", EIMIL_declexception_element_parser, EIMIL_attr_declexception, EIMIL_element_multiple, EIMIL_declexception_template, NULL}, {"UIdata", EIMIL_UIdata_element_parser, EIMIL_attr_UIdata, EIMIL_element_EMPTY | EIMIL_element_0or1, NULL, NULL}, {NULL, NULL, NULL, 0, NULL, NULL}}; EIMIL_attr_template EIMIL_attr_inherit[] = {{"src", EIMIL_attr_REQUIRED, NULL}, {NULL, 0, NULL}}; EIMIL_attr_template EIMIL_attr_engine[] = {{"name", EIMIL_attr_REQUIRED, NULL}, {"class", EIMIL_attr_REQUIRED, NULL}, {NULL, 0, NULL}}; EIMIL_element_template EIMIL_doctemp[] = {{"inherit", EIMIL_inherit_element_parser, EIMIL_attr_inherit, EIMIL_element_EMPTY | EIMIL_element_multiple, NULL, NULL}, {"interface", EIMIL_interface_element_parser, NULL, (EIMIL_subelement_ordered | EIMIL_element_single), EIMIL_interface_template, NULL}, {"engine", EIMIL_engine_element_parser, EIMIL_attr_engine, EIMIL_element_multiple, NULL, NULL}, {NULL, NULL, NULL, 0, NULL, NULL}}; EIMIL_element_template EIMIL_docroot[] = {{"EIMIL", NULL, EIMIL_attr_EIMIL, (EIMIL_subelement_ordered | EIMIL_element_single), EIMIL_doctemp, NULL}, {NULL, NULL, NULL, 0, NULL, NULL}}; static int EIMIL_message_element_parser( EIMIL_data *ped, EIMIL_attrs *patr, enum EIMIL_TAG_TYPE type, UTF8 *pchars, void **pprivate ) { int n; EIMIL_parser_state* pps = &ped->pcommon->ps; EIMIL_message **pmsgs = (EIMIL_message**) *pprivate; EIMIL_message *msgs = *pmsgs; UTF8 *lang, *msg; UTF32 *pu32; if (type != EIMIL_END_TAG) return 1; if (msgs) { for (n = 0;msgs->lang;msgs++, n++); msgs = *pmsgs; }else{ n = 0; } for (;patr->name;patr++) { if (strcmp(patr->name, "xml:lang") == 0) { if (!EIMIL_get_attr_cdata(patr->val, &lang)) { EIMIL_set_error_pt(ped, NULL, "Invalid cdata in xml:lang"); return 0; } } else { return 0; } } { Ebyte *s, *e; for (s = pps->start;(s < e && EIMIL_isspace(*s));s++); for (e = pps->end;(e > s && EIMIL_isspace(*e));e--); if ((s >= e) || (!(msg = EIMIL_resolve_reference(s, e)))) { EIMIL_set_error_pt(ped, s, "Invalid contents in message element"); free(lang); return 0; } } n++; msgs = (EIMIL_message*) realloc(msgs, sizeof(EIMIL_message) * (n + 1)); pu32 = EIMIL_convert_UTF8_to_UTF32(lang); free(lang); if (!pu32) return 0; msgs[n - 1].lang = pu32; pu32 = EIMIL_convert_UTF8_to_UTF32(msg); free(msg); if (!pu32) return 0; msgs[n - 1].msg = pu32; msgs[n].lang = NULL; msgs[n].msg = NULL; *pmsgs = msgs; return 1; } static int EIMIL_add_symbol_to_slots( EIMIL_data *ped, int num, EIMIL_symbol ***pslots, unsigned char *name, enum EIMIL_CATEGORY cat ) { EIMIL_symbol *psym, **slots; slots = *pslots; psym = EIMIL_intern_soft(ped->pdic, name); if (!psym) { EIMIL_set_error_pt(ped, NULL, "%s is not declared by declop.", name); return 0; } if (!((psym->publicp) && (psym->cat == cat))) { EIMIL_set_error_pt(ped, NULL, "%s is registered, but it's not defined properly.", name); return 0; } slots = (EIMIL_symbol**) realloc(slots, sizeof(EIMIL_symbol*) * (num + 1)); if (!slots) { EIMIL_set_out_of_memory(ped); return 0; } slots[num] = psym; *pslots = slots; return 1; } static int EIMIL_dependency_element_parser( EIMIL_data *ped, EIMIL_attrs *patr, enum EIMIL_TAG_TYPE type, UTF8 *pchars, void **pprivate ) { UTF8 *name; EIMIL_dependency *pdep; EIMIL_operation *pop = (EIMIL_operation*) *pprivate; if (type != EIMIL_EMPTY_TAG) return 1; pop->pdeps = (EIMIL_dependency*) realloc(pop->pdeps, sizeof(EIMIL_dependency) * (pop->numdepends + 1)); if (!pop->pdeps) return 0; pdep = pop->pdeps + pop->numdepends; pop->numdepends++; memset(pdep, 0, sizeof(EIMIL_dependency)); for (; patr->name; patr++) { if (strcmp(patr->name, "depend") == 0) { Ebyte *c; c = patr->val; while ((c = EIMIL_get_attr_nmtokens(c, &name)) != NULL) { if (!EIMIL_add_symbol_to_slots(ped, pdep->numdepends, &pdep->depends, name, EIMIL_CAT_VARIABLE)) { free(name); return 0; } free(name); pdep->numdepends++; } if (pdep->numdepends == 0) { EIMIL_set_error_pt(ped, NULL, "Invalid nmtokens in `depend'"); return 0; } } else if (strcmp(patr->name, "affect") == 0) { Ebyte *c; c = patr->val; while ((c = EIMIL_get_attr_nmtokens(c, &name)) != NULL) { if (!EIMIL_add_symbol_to_slots(ped, pdep->numaffects, &pdep->affects, name, EIMIL_CAT_VARIABLE)) { free(name); return 0; } free(name); pdep->numaffects++; } if (pdep->numaffects == 0) { EIMIL_set_error_pt(ped, NULL, "Invalid nmtokens in `depend'"); return 0; } } } if (type == EIMIL_TYPE_INVALID) { EIMIL_set_error_pt(ped, NULL, "`type' attribute must be `bool', `number', `char', or `mtext'."); } return type; } static int EIMIL_decldata_element_parser( EIMIL_data *ped, EIMIL_attrs *patr, enum EIMIL_TAG_TYPE type, UTF8 *pchars, void **pprivate ) { enum EIMIL_TYPE etype; UTF8 *name; EIMIL_symbol *psym = NULL; if (type != EIMIL_EMPTY_TAG) return 1; etype = EIMIL_get_type_from_attrs(ped, patr); if (etype == EIMIL_TYPE_INVALID) { return 0; } for (;patr->name;patr++) { if (strcmp(patr->name, "name") == 0) { if (!EIMIL_get_attr_nmtoken(patr->val, &name)) { EIMIL_set_error_pt(ped, NULL, "Invalid nmtoken in `name'"); return 0; } psym = EIMIL_register_symbol(ped, ped->pdic, name, EIMIL_CAT_VARIABLE, etype); if (!psym) { EIMIL_set_error_pt(ped, NULL, "%s is already registered.", name); free(name); return 0; } free(name); psym->publicp = 1; psym->obj.v.type = etype; psym->obj.v.pv = NULL; } else { return 0; } } ASSERT(psym); return 1; } static int EIMIL_declprop_element_parser( EIMIL_data *ped, EIMIL_attrs *patr, enum EIMIL_TAG_TYPE type, UTF8 *pchars, void **pprivate ) { UTF8 *name; EIMIL_symbol *psym = NULL; if (type != EIMIL_EMPTY_TAG) return 1; type = EIMIL_get_type_from_attrs(ped, patr); if (type == EIMIL_TYPE_INVALID) { return 0; } for (;patr->name;patr++) { if (strcmp(patr->name, "name") == 0) { if (!EIMIL_get_attr_nmtoken(patr->val, &name)) { EIMIL_set_error_pt(ped, NULL, "Invalid nmtoken in `name'"); return 0; } psym = EIMIL_register_symbol(ped, ped->pdic, name, EIMIL_CAT_PROPERTY, type); if (!psym) { EIMIL_set_error_pt(ped, NULL, "%s is already registered.", name); free(name); return 0; } free(name); psym->publicp = 1; psym->obj.p.type = type; } else { return 0; } } ASSERT(psym); return 1; } static int EIMIL_declop_element_parser( EIMIL_data *ped, EIMIL_attrs *patr, enum EIMIL_TAG_TYPE type, UTF8 *pchars, void **pprivate ) { UTF8 *name; EIMIL_symbol *psym = NULL; if (type != EIMIL_START_TAG) return 1; for (; patr->name; patr++) { if (strcmp(patr->name, "name") == 0) { if (!EIMIL_get_attr_nmtoken(patr->val, &name)) { EIMIL_set_error_pt(ped, NULL, "Invalid nmtoken in `name'"); return 0; } psym = EIMIL_register_symbol(ped, ped->pdic, name, EIMIL_CAT_OPERATION, EIMIL_TYPE_INVALID); if (!psym) { EIMIL_set_error_pt(ped, NULL, "%s is already registered."); return 0; } free(name); psym->publicp = 1; psym->obj.o.commitnotifyp = 0; psym->obj.o.numdepends = 0; psym->obj.o.pdeps = NULL; } else { return 0; } } ASSERT(psym); *pprivate = &psym->obj.o; return 1; } static int EIMIL_add_commitnotify( EIMIL_data *ped, EIMIL_symbol *psym ) { int num; EIMIL_symbol **psyms; ASSERT(psym->cat == EIMIL_CAT_OPERATION); if (psym->obj.o.commitnotifyp) return 1; psyms = ped->commitnotify_ops; num = ped->commitnotify_numops; psyms = (EIMIL_symbol**) realloc(psyms, sizeof(EIMIL_symbol*) * (num + 1)); if (!psyms) return 0; psyms[num] = psym; ped->commitnotify_ops = psyms; ped->commitnotify_numops++; return 1; } static int EIMIL_del_commitnotify( EIMIL_data *ped, EIMIL_symbol *psym ) { int i, num; EIMIL_symbol **psyms; ASSERT(psym->cat == EIMIL_CAT_OPERATION); if (!psym->obj.o.commitnotifyp) return 1; psyms = ped->commitnotify_ops; num = ped->commitnotify_numops; for (i = 0; i < num; i++) { if (psyms[i] == psym) { if ((num - i - 1) > 0) { memmove(psyms + i, psyms + i + 1, sizeof(EIMIL_symbol*) * (num - i - 1)); } ped->commitnotify_numops--; return 1; } } /* not reached */ abort(); return 0; } static int EIMIL_commitnotify_element_parser( EIMIL_data *ped, EIMIL_attrs *patr, enum EIMIL_TAG_TYPE type, UTF8 *pchars, void **pprivate ) { EIMIL_attrs *patr2; UTF8 *name; int flag = -1; EIMIL_symbol *psym = NULL; if (type != EIMIL_EMPTY_TAG) return 1; for (patr2 = patr; patr2->name; patr2++) { if (strcmp(patr->name, "flag") == 0) { if (!EIMIL_get_attr_nmtoken(patr2->val, &name)) { flag = -1; break; } if (strcmp(name, "on") == 0) flag = 1; else if (strcmp(name, "off") == 0) flag = 0; else flag = -1; free(name); break; } } if (flag < 0) { EIMIL_set_error_pt(ped, NULL, "`flag' attribute must be `on' or `off'."); return 0; } for (; patr->name; patr++) { if (strcmp(patr->name, "op") == 0) { Ebyte *c; int i = 0; for (c = patr->val;c;c = EIMIL_get_attr_nmtokens(patr->val, &name)) { psym = EIMIL_intern_soft(ped->pdic, name); if (!psym) { EIMIL_set_error_pt(ped, NULL, "%s is not declared by declop."); return 0; } if (!((psym->publicp) && (psym->cat == EIMIL_CAT_OPERATION))) { EIMIL_set_error_pt(ped, NULL, "%s is registered, but it's not valid operation."); return 0; } if (flag) { EIMIL_add_commitnotify(ped, psym); psym->obj.o.commitnotifyp = 1; } else { EIMIL_del_commitnotify(ped, psym); psym->obj.o.commitnotifyp = 0; } free(name); i++; } if (i == 0) { EIMIL_set_error_pt(ped, NULL, "Invalid nmtokens in `op'"); return 0; } } else { return 0; } } return 1; } static int EIMIL_declexception_element_parser( EIMIL_data *ped, EIMIL_attrs *patr, enum EIMIL_TAG_TYPE type, UTF8 *pchars, void **pprivate ) { UTF8 *name; EIMIL_symbol *psym = NULL; if (type != EIMIL_START_TAG) return 1; for (; patr->name; patr++) { if (strcmp(patr->name, "name") == 0) { if (!EIMIL_get_attr_nmtoken(patr->val, &name)) { EIMIL_set_error_pt(ped, NULL, "Invalid nmtoken in `name'"); return 0; } psym = EIMIL_register_symbol(ped, ped->pdic, name, EIMIL_CAT_EXCEPTION, EIMIL_TYPE_INVALID); if (!psym) { EIMIL_set_error_pt(ped, NULL, "%s is already registered."); return 0; } free(name); psym->publicp = 1; psym->obj.e.msgs = NULL; } else { return 0; } } ASSERT(psym); return 1; } static int EIMIL_UIdata_element_parser( EIMIL_data *ped, EIMIL_attrs *patr, enum EIMIL_TAG_TYPE type, UTF8 *pchars, void **pprivate ) { UTF8 *name; EIMIL_symbol *psym; EIMIL_value *pv; if (type != EIMIL_EMPTY_TAG) return 1; for (; patr->name; patr++) { if (strcmp(patr->name, "depend") == 0) { if (!EIMIL_get_attr_nmtoken(patr->val, &name)) { EIMIL_set_error_pt(ped, NULL, "Invalid nmtoken in `depend'"); return 0; } psym = EIMIL_intern_soft(ped->pdic, name); if (!psym) { EIMIL_set_error_pt(ped, NULL, "%s is not declared by decldata."); return 0; } if (!((psym->publicp) && (psym->cat == EIMIL_CAT_VARIABLE) && (psym->obj.v.type == EIMIL_TYPE_MTEXT))) { EIMIL_set_error_pt(ped, NULL, "%s is registered, but it's not valid mtext data."); return 0; } if ((ped->psym_uidata) && (ped->psym_uidata->obj.v.pv)) { pv = ped->psym_uidata->obj.v.pv; ASSERT(pv->type == EIMIL_TYPE_MTEXT); pv->v.mtext.UIdatap = 0; } pv = psym->obj.v.pv; if (pv) { ASSERT(pv->type == EIMIL_TYPE_MTEXT); pv->v.mtext.UIdatap = 1; } ped->psym_uidata = psym; free(name); } else { return 0; } } return 1; } static int EIMIL_inherit_element_parser( EIMIL_data *ped, EIMIL_attrs *patr, enum EIMIL_TAG_TYPE type, UTF8 *pchars, void **pprivate ) { UTF8 *uaddr; if (type != EIMIL_EMPTY_TAG) return 1; for (;patr->name;patr++) { if (strcmp(patr->name, "src") == 0) { if (!EIMIL_get_attr_cdata(patr->val, &uaddr)) { EIMIL_set_error_pt(ped, NULL, "Invalid cdata in `src'"); return 0; } /* TODO: Load other EIMIL file. */ fprintf(stderr, "Inherit %s\n", uaddr); free(uaddr); } else { return 0; } } return 1; } static int EIMIL_interface_element_parser( EIMIL_data *ped, EIMIL_attrs *patr, enum EIMIL_TAG_TYPE type, UTF8 *pchars, void **pprivate ) { /* Do nothing specially. */ return 1; } /*************************************** Engine specific interface ***************************************/ static int num_engines; static EIMIL_engine_table *pengines; static void EIMIL_free_engine() { int i; EIMIL_engine_table *p = pengines; for (i = 0; ; ++i) { if (i == num_engines) break; free (p->classname); free (p->uri); ++p; } free (pengines); /* reset */ pengines = NULL; num_engines = 0; } static EIMIL_engine_table* EIMIL_get_engine( const UTF8 *classname ) { int i; EIMIL_engine_table *p = pengines; for (i = 0;; i++) { if (i == num_engines) return NULL; if (strcmp(classname, p->classname) == 0) break; p++; } return p; } int EIMIL_register_engine( const UTF8 *classname, EIMIL_element_template *pet, EIMIL_engine_handler handler, EIMIL_engine_execute_handler execute_handler, const UTF8 *uri ) { EIMIL_engine_table *p; p = EIMIL_get_engine(classname); if (!p) { p = (EIMIL_engine_table*) realloc(pengines, sizeof(EIMIL_engine_table) * (num_engines + 1)); if (!p) return 0; pengines = p; p += num_engines; p->classname = strdup(classname); if (!p->classname) return 0; p->uri = strdup(uri); if (!p->uri) { free(p->classname); return 0; } num_engines++; } else { if (p->uri) free(p->uri); p->uri = strdup(uri); if (!p->uri) { free(p->classname); return 0; } } p->execute_handler = execute_handler; p->handler = handler; p->pet = pet; return 1; } static int EIMIL_engine_element_parser( EIMIL_data *ped, EIMIL_attrs *patr, enum EIMIL_TAG_TYPE type, UTF8 *pchars, void **pprivate ) { if (type == EIMIL_START_TAG) { UTF8 *ustr; void **pengine_context; EIMIL_engine_table *pt; EIMIL_engine *pe; EIMIL_parser_state* pps = &ped->pcommon->ps; EIMIL_cdata *pc = ped->pcommon; for (;patr->name;patr++) { if (strcmp(patr->name, "class") == 0) { if (!EIMIL_get_attr_cdata(patr->val, &ustr)) { EIMIL_set_error_pt(ped, NULL, "Invalid class name in `class'"); return 0; } pt = EIMIL_get_engine(ustr); if (!pt) { EIMIL_set_error_pt(ped, NULL, "Class:%s does not exist.", ustr); free(ustr); return 0; } free(ustr); } else if (strcmp(patr->name, "name") == 0) { if (!EIMIL_get_attr_cdata(patr->val, &ustr)) { EIMIL_set_error_pt(ped, NULL, "Invalid engine name in `name'"); return 0; } pe = (EIMIL_engine*) realloc(pc->pengine, sizeof(EIMIL_engine) * (pc->num_engines + 1)); if (!pe) { EIMIL_set_out_of_memory(ped); return 0; } pc->pengine = pe; pe += pc->num_engines; pengine_context = (void**) realloc(ped->pengine_context, sizeof(void*) * (pc->num_engines + 1)); if (!pengine_context) { EIMIL_set_out_of_memory(ped); return 0; } ped->pengine_context = pengine_context; pengine_context += pc->num_engines; pe->name = ustr; pe->private = NULL; } else { return 0; } } /* Set up the engine. */ pe->ptable = pt; pe->private = (*pt->handler)(EIMIL_ENGINE_INSTANCIATE, ped, NULL, NULL); if (!pe->private) { EIMIL_set_out_of_memory(ped); return 0; } *pengine_context = (*pt->handler)(EIMIL_ENGINE_DUPLICATE, ped, pe->private, NULL); if (!(*pengine_context)) { EIMIL_set_out_of_memory(ped); return 0; } pc->num_engines++; pe->idx = pc->num_engines; EIMIL_SET_CURRENT_SUBELEMENT_TEMPLATE(pps, pt->pet); pps->current_uri = pt->uri; *pprivate = *pengine_context; } return 1; } /*************************************** EIMIL handle ***************************************/ EIMIL_data* EIMIL_make_handle_data( EIMIL_cdata *pbase ) { EIMIL_data *ped; EIMIL_dictionary *pdic; ped = (EIMIL_data*) malloc(sizeof(EIMIL_data)); if (!ped) return NULL; memset(ped, 0, sizeof(EIMIL_data)); if (!pbase) { pbase = (EIMIL_cdata*) malloc(sizeof(EIMIL_cdata)); if (!pbase) { free(ped); return NULL; } memset(pbase, 0, sizeof(EIMIL_cdata)); INIT_SYNC_OBJECT(pbase->sync_object); /* EIMIL_duplicate_handle() make its own dictionary, so it must not create a new dictionary here. */ pdic = EIMIL_new_dictionary(0, 1); if (!pdic) { free(ped); return NULL; } ped->pdic = pdic; } else { ped->duplicated = 1; } ped->pcommon = pbase; ped->pcur_ev = ped->pqueue_ev = ped->queueslots; return ped; } /*************************************** parser entry. ***************************************/ int EIMIL_parse_start( EIMIL_data *ped ) { ped->pcommon->ps.current_uri = EIMIL_xmlns_uri; return EIMIL_parse_element(ped, EIMIL_docroot, NULL, NULL, NULL, NULL); } /******************************************************************************** API ********************************************************************************/ EIMIL_symbol* EIMIL_lookup_symbol( EIMIL_handle eh, CARD32BIT id ) { EIMIL_data *ped = (EIMIL_data*) eh; return EIMIL_lookup_symbol_internal(ped->pdic, id); } int EIMIL_duplicate_handle( EIMIL_handle *peh, EIMIL_handle eh ) { EIMIL_data *peds, *pedd; EIMIL_symbol *psym; EIMIL_dictionary *pdic; int i, n; peds = (EIMIL_data*) eh; pedd = EIMIL_make_handle_data(peds->pcommon); if (!pedd) return 0; /* copy handle data */ *pedd = *peds; /* dupliacte dictionary */ pdic = EIMIL_duplicate_dictionary(peds->pdic); if (!pdic) return 0; pedd->pdic = pdic; /* engine context */ { void **pecs, **pecd; EIMIL_engine *pe; EIMIL_engine_table *pt; n = peds->pcommon->num_engines; pecs = pedd->pengine_context; pecd = (void**) malloc(sizeof(void*) * n); if (!pecd) return 0; for (i = 0; i < n; i++, pecs++, pecd++) { if (*pecs) { pe = peds->pcommon->pengine + i; pt = pe->ptable; *pecd = (*pt->handler)(EIMIL_ENGINE_DUPLICATE, peds, pe->private, *pecs); if (*pecd) return 0; } else { *pecd = NULL; } } } /* set psym_uidata. */ if (peds->psym_uidata) { psym = EIMIL_lookup_symbol_internal(pdic, peds->psym_uidata->symbolid); ASSERT(psym); pedd->psym_uidata = psym; } /* set commitnotify_ops. */ if (peds->commitnotify_numops > 0) { EIMIL_symbol **psyms_s, **psyms_d; psyms_d = (EIMIL_symbol**) malloc(sizeof(EIMIL_symbol*) * peds->commitnotify_numops); if (!psyms_d) return 0; pedd->commitnotify_ops = psyms_d; psyms_s = peds->commitnotify_ops; for (i = 0; peds->commitnotify_numops; i++, psyms_s++, psyms_d++) { *psyms_d = EIMIL_lookup_symbol_internal(pdic, psyms_s[0]->symbolid); ASSERT(*psyms_d); } } /* Reset journal state */ pedd->current_journal_id = 0; pedd->pjst = NULL; /* Success */ *peh = pedd; return 1; } int EIMIL_free_handle( EIMIL_handle eh ) { EIMIL_data *ped; if (!eh) return 0; ped = (EIMIL_data*) eh; EIMIL_journal_free(eh); if (!ped->duplicated) { EIMIL_cdata *pc = ped->pcommon; if (pc->ps.buf) free(pc->ps.buf); if (pc->ps.pxmlns) free(pc->ps.pxmlns); DESTROY_SYNC_OBJECT(pc->sync_object); free(pc); } EIMIL_free_dictionary(ped->pdic); free(ped); return 1; } int EIMIL_get_errormsg( EIMIL_handle eh, char **ppmsg ) { EIMIL_data *ped; if (!eh) return 0; ped = (EIMIL_data*) eh; *ppmsg = ped->errstr; return 1; } static int EIMIL_init_predefined_symbol() { EIMIL_symbol *psym; pdic_internal = EIMIL_new_dictionary(11, 1); if (!pdic_internal) return 0; psym = register_symbol(pdic_internal, "nil", EIMIL_CAT_VARIABLE, EIMIL_SYMBOL_ID_NIL); if (!psym) return 0; psym->obj.v.type = EIMIL_TYPE_NIL; psym->obj.v.constp = 1; psym->obj.v.pv = NULL; pEIMIL_nil_sym = psym; psym = register_symbol(pdic_internal, "t", EIMIL_CAT_VARIABLE, EIMIL_SYMBOL_ID_T); if (!psym) return 0; psym->obj.v.type = EIMIL_TYPE_BOOL; psym->obj.v.constp = 1; psym->obj.v.pv = EIMIL_construct_bool(1); pEIMIL_t_sym = psym; psym = register_symbol(pdic_internal, "feedback", EIMIL_CAT_PROPERTY, EIMIL_SYMBOL_ID_FEEDBACK); if (!psym) return 0; psym->obj.p.type = EIMIL_TYPE_NUMBER; pEIMIL_feedback_sym = psym; psym = register_symbol(pdic_internal, "candidates", EIMIL_CAT_PROPERTY, EIMIL_SYMBOL_ID_CANDIDATES); psym->obj.p.type = EIMIL_TYPE_MTEXT; pEIMIL_candidates_sym = psym; return 1; } int EIMIL_initialize() { if (EIMIL_inited) return 1; /* initialize internal object */ EIMIL_t_val.refcount = 1; EIMIL_t_val.type = EIMIL_TYPE_BOOL; EIMIL_t_val.v.bool_val = 1; if (!EIMIL_init_predefined_symbol()) return 0; if (!EIMILFile_init()) return 0; if (!PCE_init()) return 0; EIMIL_inited = 1; return 1; } int EIMIL_finalize() { if (!EIMIL_inited) return 1; EIMIL_free_dictionary_and_symbol(pdic_internal); EIMIL_free_engine(pengines); pEIMIL_nil_sym = NULL; pEIMIL_t_sym = NULL; pEIMIL_feedback_sym = NULL; pEIMIL_candidates_sym = NULL; pdic_internal = NULL; EIMIL_inited = 0; return 1; } void EIMIL_set_private( EIMIL_handle eh, void* private ) { EIMIL_data *ped = (EIMIL_data*) eh; ped->private = private; } void* EIMIL_get_private( EIMIL_handle eh, void* private ) { EIMIL_data *ped = (EIMIL_data*) eh; return ped->private; } /**************************************** EIMIL service IF. ****************************************/ int EIMIL_toggle_preedit( EIMIL_data *ped, int flag ) { int r; if (!ped->pcommon->uiproc) return 0; if (flag) r = (*ped->pcommon->uiproc)(ped, ped->psym_uidata->obj.v.pv, EIMIL_ENABLE_PREEDIT); else r = (*ped->pcommon->uiproc)(ped, ped->psym_uidata->obj.v.pv, EIMIL_DISABLE_PREEDIT); return r; } int EIMIL_update_preedit( EIMIL_data *ped ) { return (*ped->pcommon->uiproc)(ped, ped->psym_uidata->obj.v.pv, EIMIL_UPDATE_PREEDIT); } int EIMIL_toggle_lookup_choice( EIMIL_data *ped, int flag ) { int r; if (!ped->pcommon->uiproc) return 0; if (flag) r = (*ped->pcommon->uiproc)(ped, ped->psym_uidata->obj.v.pv, EIMIL_ENABLE_LOOKUP_CHOICE); else r = (*ped->pcommon->uiproc)(ped, ped->psym_uidata->obj.v.pv, EIMIL_DISABLE_LOOKUP_CHOICE); return r; } int EIMIL_update_lookup_choice( EIMIL_data *ped ) { if (!ped->pcommon->uiproc) return 0; return (*ped->pcommon->uiproc)(ped, ped->psym_uidata->obj.v.pv, EIMIL_UPDATE_LOOKUP_CHOICE); } int EIMIL_reply_event( EIMIL_data *ped, EIMIL_value *pv_event ) { if (!ped->pcommon->evproc) return 0; ASSERT(pv_event->type == EIMIL_TYPE_EVENT); return (*ped->pcommon->evproc)(ped, &pv_event->v.event); } int EIMIL_queue_event( EIMIL_data *ped, EIMIL_value *pv_event ) { EIMIL_value **pqn; if (ped->pqueue_ev == (ped->queueslots + EIMIL_EVENT_QUEUESIZE - 1)) pqn = ped->queueslots; else pqn = ped->pqueue_ev + 1; if (pqn == ped->pcur_ev) return 0; *ped->pqueue_ev = pv_event; ped->pqueue_ev = pqn; EIMIL_ADDREF(*pv_event); return 1; } EIMIL_value* EIMIL_next_event( EIMIL_data *ped ) { EIMIL_value **pqn, *pev; if (ped->pcur_ev == ped->pqueue_ev) return NULL; if (ped->pcur_ev == (ped->queueslots + EIMIL_EVENT_QUEUESIZE - 1)) pqn = ped->queueslots; else pqn = ped->pcur_ev + 1; pev = *ped->pcur_ev; ped->pcur_ev = pqn; if (pev) EIMIL_RMREF_WITHOUT_DESTRUCTION(*pev); return pev; } /**************************************** EIMIL application IF. ****************************************/ int EIMIL_register_handler( EIMIL_handle eh, EIMIL_EVENT_PROC evproc, EIMIL_UICHANGE_PROC uiproc, EIMIL_OPISSUE_PROC opproc ) { EIMIL_data *ped = (EIMIL_data*) eh; EIMIL_cdata *pcommon = ped->pcommon; pcommon->evproc = evproc; pcommon->uiproc = uiproc; pcommon->opproc = opproc; return 1; } int EIMIL_send_event( EIMIL_handle eh, EIMIL_value *pv_event ) { int i, n; EIMIL_data *ped = (EIMIL_data*) eh; EIMIL_cdata *pcommon = ped->pcommon; EIMIL_engine *pe = pcommon->pengine; void **pengine_ctx; n = pcommon->num_engines; pengine_ctx = ped->pengine_context; /* if event, store event */ if (pv_event) { ASSERT(pv_event->type == EIMIL_TYPE_EVENT); EIMIL_queue_event(ped, pv_event); } for (i = 0; i < n; i++) { if ((*pe->ptable->execute_handler)(*pengine_ctx) != EIMIL_ENGINE_STATUS_SKIPPED) break; pe++; pengine_ctx++; } if (pv_event) EIMIL_destruct_value(pv_event); return 1; } /* Local Variables: */ /* c-file-style: "iiim-project" */ /* End: */