nvp.c (mairix-0.23) | : | nvp.c (mairix-0.24) | ||
---|---|---|---|---|
skipping to change at line 55 | skipping to change at line 55 | |||
char *lhs; | char *lhs; | |||
char *rhs; | char *rhs; | |||
}; | }; | |||
/*}}}*/ | /*}}}*/ | |||
struct nvp {/*{{{*/ | struct nvp {/*{{{*/ | |||
struct nvp_entry *first, *last; | struct nvp_entry *first, *last; | |||
}; | }; | |||
/*}}}*/ | /*}}}*/ | |||
static void append(struct nvp *nvp, struct nvp_entry *ne)/*{{{*/ | static void append(struct nvp *nvp, struct nvp_entry *ne)/*{{{*/ | |||
{ | { | |||
if (!ne->rhs) { | ||||
ne->rhs = Malloc(1); | ||||
ne->rhs[0] = 0; | ||||
} | ||||
if (!ne->lhs) { | ||||
ne->lhs = Malloc(1); | ||||
ne->lhs[0] = 0; | ||||
} | ||||
ne->next = NULL; | ne->next = NULL; | |||
ne->prev = nvp->last; | ne->prev = nvp->last; | |||
if (nvp->last) nvp->last->next = ne; | if (nvp->last) nvp->last->next = ne; | |||
else nvp->first = ne; | else nvp->first = ne; | |||
nvp->last = ne; | nvp->last = ne; | |||
} | } | |||
/*}}}*/ | /*}}}*/ | |||
static void append_name(struct nvp *nvp, char *name)/*{{{*/ | static void append_name(struct nvp *nvp, char **name)/*{{{*/ | |||
{ | { | |||
struct nvp_entry *ne; | struct nvp_entry *ne; | |||
ne = new(struct nvp_entry); | ne = new(struct nvp_entry); | |||
ne->type = NVP_NAME; | ne->type = NVP_NAME; | |||
ne->lhs = new_string(name); | ne->lhs = *name; | |||
*name = NULL; | ||||
append(nvp, ne); | append(nvp, ne); | |||
} | } | |||
/*}}}*/ | /*}}}*/ | |||
static void append_majorminor(struct nvp *nvp, char *major, char *minor)/*{{{*/ | static void append_majorminor(struct nvp *nvp, char **major, char **minor)/*{{{* / | |||
{ | { | |||
struct nvp_entry *ne; | struct nvp_entry *ne; | |||
ne = new(struct nvp_entry); | ne = new(struct nvp_entry); | |||
ne->type = NVP_MAJORMINOR; | ne->type = NVP_MAJORMINOR; | |||
ne->lhs = new_string(major); | ne->lhs = *major; | |||
ne->rhs = new_string(minor); | ne->rhs = *minor; | |||
*major = *minor = NULL; | ||||
append(nvp, ne); | append(nvp, ne); | |||
} | } | |||
/*}}}*/ | /*}}}*/ | |||
static void append_namevalue(struct nvp *nvp, char *name, char *value)/*{{{*/ | static void append_namevalue(struct nvp *nvp, char **name, char **value)/*{{{*/ | |||
{ | { | |||
struct nvp_entry *ne; | struct nvp_entry *ne; | |||
ne = new(struct nvp_entry); | ne = new(struct nvp_entry); | |||
ne->type = NVP_NAMEVALUE; | ne->type = NVP_NAMEVALUE; | |||
ne->lhs = new_string(name); | ne->lhs = *name; | |||
ne->rhs = new_string(value); | ne->rhs = *value; | |||
*name = *value = NULL; | ||||
append(nvp, ne); | append(nvp, ne); | |||
} | } | |||
/*}}}*/ | /*}}}*/ | |||
static void combine_namevalue(struct nvp *nvp, char *name, char *value)/*{{{*/ | static void combine_namevalue(struct nvp *nvp, char **name, char **value)/*{{{*/ | |||
{ | { | |||
struct nvp_entry *n; | struct nvp_entry *n; | |||
for (n=nvp->first; n; n=n->next) { | for (n=nvp->first; n; n=n->next) { | |||
if (n->type == NVP_NAMEVALUE) { | if (n->type == NVP_NAMEVALUE) { | |||
if (!strcmp(n->lhs, name)) { | if (!strcmp(n->lhs, *name)) { | |||
char *new_rhs; | char *new_rhs; | |||
new_rhs = new_array(char, strlen(n->rhs) + strlen(value) + 1); | new_rhs = new_array(char, strlen(n->rhs) + strlen(*value) + 1); | |||
strcpy(new_rhs, n->rhs); | strcpy(new_rhs, n->rhs); | |||
strcat(new_rhs, value); | strcat(new_rhs, *value); | |||
free(n->rhs); | free(n->rhs); | |||
n->rhs = new_rhs; | n->rhs = new_rhs; | |||
return; | return; | |||
} | } | |||
} | } | |||
} | } | |||
/* No match : it's the first one */ | /* No match : it's the first one */ | |||
append_namevalue(nvp, name, value); | append_namevalue(nvp, name, value); | |||
} | } | |||
/*}}}*/ | /*}}}*/ | |||
static int hex_to_val(int ch)/*{{{*/ | ||||
{ | ||||
if (isdigit(ch)) | ||||
return (ch - '0'); | ||||
if (ch >= 'a' && ch <= 'f') | ||||
return (10 + ch - 'a'); | ||||
if (ch >= 'A' && ch <= 'F') | ||||
return (10 + ch - 'A'); | ||||
return (-1); | ||||
} | ||||
/*}}}*/ | ||||
static void release_nvp(struct nvp *nvp)/*{{{*/ | static void release_nvp(struct nvp *nvp)/*{{{*/ | |||
{ | { | |||
struct nvp_entry *e, *ne; | struct nvp_entry *e, *ne; | |||
for (e=nvp->first; e; e=ne) { | for (e=nvp->first; e; e=ne) { | |||
ne = e->next; | ne = e->next; | |||
switch (e->type) { | switch (e->type) { | |||
case NVP_NAME: | case NVP_NAME: | |||
free(e->lhs); | free(e->lhs); | |||
break; | break; | |||
case NVP_MAJORMINOR: | case NVP_MAJORMINOR: | |||
skipping to change at line 136 | skipping to change at line 158 | |||
} | } | |||
free(e); | free(e); | |||
} | } | |||
free(nvp); | free(nvp); | |||
} | } | |||
/*}}}*/ | /*}}}*/ | |||
struct nvp *make_nvp(struct msg_src *src, char *s, const char *pfx)/*{{{*/ | struct nvp *make_nvp(struct msg_src *src, char *s, const char *pfx)/*{{{*/ | |||
{ | { | |||
int current_state; | int current_state; | |||
unsigned int tok; | unsigned int tok; | |||
char *q; | char *q, *tempsrc, *tempdst; | |||
unsigned char qq; | unsigned char qq; | |||
char name[256]; | char *name = NULL; | |||
char minor[256]; | char *minor = NULL; | |||
char value[256]; | char *value = NULL; | |||
char *copy_start; | ||||
enum nvp_action last_action, current_action; | enum nvp_action last_action, current_action; | |||
enum nvp_copier last_copier; | ||||
struct nvp *result; | struct nvp *result; | |||
size_t pfxlen; | size_t pfxlen; | |||
char *nn, *mm, *vv; | ||||
pfxlen = strlen(pfx); | pfxlen = strlen(pfx); | |||
if (strncasecmp(pfx, s, pfxlen)) | if (strncasecmp(pfx, s, pfxlen)) | |||
return NULL; | return NULL; | |||
s += pfxlen; | s += pfxlen; | |||
result = new(struct nvp); | result = new(struct nvp); | |||
result->first = result->last = NULL; | result->first = result->last = NULL; | |||
current_state = nvp_in; | current_state = nvp_in; | |||
q = s; | q = s; | |||
nn = name; | ||||
mm = minor; | ||||
vv = value; | ||||
last_action = GOT_NOTHING; | last_action = GOT_NOTHING; | |||
last_copier = COPY_NOWHERE; | ||||
do { | do { | |||
qq = *(unsigned char *) q; | qq = *(unsigned char *) q; | |||
if (qq) { | if (qq) { | |||
tok = nvp_char2tok[qq]; | tok = nvp_char2tok[qq]; | |||
} else { | } else { | |||
tok = nvp_EOS; | tok = nvp_EOS; | |||
} | } | |||
current_state = nvp_next_state(current_state, tok); | current_state = nvp_next_state(current_state, tok); | |||
#ifdef VERBOSE_TEST | #ifdef VERBOSE_TEST | |||
fprintf(stderr, "Char %02x (%c) tok=%d new_current_state=%d\n", | fprintf(stderr, "Char %02x (%c) tok=%d new_current_state=%d\n", | |||
skipping to change at line 186 | skipping to change at line 207 | |||
#ifdef TEST | #ifdef TEST | |||
fprintf(stderr, "'%s' could not be parsed\n", s); | fprintf(stderr, "'%s' could not be parsed\n", s); | |||
#else | #else | |||
fprintf(stderr, "Header '%s%s' in %s could not be parsed\n", | fprintf(stderr, "Header '%s%s' in %s could not be parsed\n", | |||
pfx, s, format_msg_src(src)); | pfx, s, format_msg_src(src)); | |||
#endif | #endif | |||
release_nvp(result); | release_nvp(result); | |||
return NULL; | return NULL; | |||
} | } | |||
switch (nvp_copier[current_state]) { | if (nvp_copier[current_state] != last_copier) { | |||
case COPY_TO_NAME: | if (last_copier != COPY_NOWHERE) { | |||
char *newstring = Malloc(q - copy_start + 1); | ||||
memcpy(newstring, copy_start, q - copy_start); | ||||
newstring[q - copy_start] = 0; | ||||
switch (last_copier) { | ||||
case COPY_TO_NAME: | ||||
free(name); | ||||
name = newstring; | ||||
#ifdef VERBOSE_TEST | #ifdef VERBOSE_TEST | |||
fprintf(stderr, " COPY_TO_NAME\n"); | fprintf(stderr, " COPY_TO_NAME \"%s\"\n", name); | |||
#endif | #endif | |||
*nn++ = *q; | break; | |||
break; | case COPY_TO_MINOR: | |||
case COPY_TO_MINOR: | free(minor); | |||
minor = newstring; | ||||
#ifdef VERBOSE_TEST | #ifdef VERBOSE_TEST | |||
fprintf(stderr, " COPY_TO_MINOR\n"); | fprintf(stderr, " COPY_TO_MINOR \"%s\"\n", minor); | |||
#endif | #endif | |||
*mm++ = *q; | break; | |||
break; | case COPY_TO_VALUE: | |||
case COPY_TO_VALUE: | free(value); | |||
value = newstring; | ||||
#ifdef VERBOSE_TEST | #ifdef VERBOSE_TEST | |||
fprintf(stderr, " COPY_TO_VALUE\n"); | fprintf(stderr, " COPY_TO_VALUE \"%s\"\n", value); | |||
#endif | #endif | |||
*vv++ = *q; | break; | |||
break; | case COPY_NOWHERE: | |||
case COPY_NOWHERE: | /* NOTREACHED */ | |||
break; | break; | |||
} | ||||
} | ||||
last_copier = nvp_copier[current_state]; | ||||
copy_start = q; | ||||
} | } | |||
current_action = nvp_action[current_state]; | current_action = nvp_action[current_state]; | |||
switch (current_action) { | switch (current_action) { | |||
case GOT_NAME: | case GOT_NAME: | |||
case GOT_NAME_TRAILING_SPACE: | case GOT_NAME_TRAILING_SPACE: | |||
case GOT_MAJORMINOR: | case GOT_MAJORMINOR: | |||
case GOT_NAMEVALUE: | case GOT_NAMEVALUE: | |||
case GOT_NAMEVALUE_CONT: | case GOT_NAMEVALUE_CONT: | |||
case GOT_NAMEVALUE_CSET: | ||||
case GOT_NAMEVALUE_CCONT: | ||||
#ifdef VERBOSE_TEST | #ifdef VERBOSE_TEST | |||
fprintf(stderr, " Setting last action to %d\n", current_action); | fprintf(stderr, " Setting last action to %d\n", current_action); | |||
#endif | #endif | |||
last_action = current_action; | last_action = current_action; | |||
break; | break; | |||
case GOT_TERMINATOR: | case GOT_TERMINATOR: | |||
#ifdef VERBOSE_TEST | #ifdef VERBOSE_TEST | |||
fprintf(stderr, " Hit terminator; last_action=%d\n", last_action); | fprintf(stderr, " Hit terminator; last_action=%d\n", last_action); | |||
#endif | #endif | |||
switch (last_action) { | switch (last_action) { | |||
case GOT_NAME: | case GOT_NAME: | |||
*nn = 0; | append_name(result, &name); | |||
append_name(result, name); | ||||
break; | break; | |||
case GOT_NAME_TRAILING_SPACE: | case GOT_NAME_TRAILING_SPACE: | |||
while (isspace(*--nn)) {} | tempdst = name + strlen(name); | |||
*++nn = 0; | while (isspace(*--tempdst)) {} | |||
append_name(result, name); | *++tempdst = 0; | |||
append_name(result, &name); | ||||
break; | break; | |||
case GOT_MAJORMINOR: | case GOT_MAJORMINOR: | |||
*nn = 0; | append_majorminor(result, &name, &minor); | |||
*mm = 0; | ||||
append_majorminor(result, name, minor); | ||||
break; | break; | |||
case GOT_NAMEVALUE: | case GOT_NAMEVALUE: | |||
*nn = 0; | append_namevalue(result, &name, &value); | |||
*vv = 0; | break; | |||
append_namevalue(result, name, value); | case GOT_NAMEVALUE_CSET: | |||
case GOT_NAMEVALUE_CCONT: | ||||
for(tempsrc = tempdst = value; *tempsrc; tempsrc++) { | ||||
if (*tempsrc == '%') { | ||||
int val = hex_to_val(*++tempsrc) << 4; | ||||
val |= hex_to_val(*++tempsrc); | ||||
if (val < 0) { | ||||
#ifdef TEST | ||||
fprintf(stderr, "'%s' could not be parsed (%%)\n", s); | ||||
#else | ||||
fprintf(stderr, "Header '%s%s' in %s could not be parsed\ | ||||
n", | ||||
pfx, s, format_msg_src(src)); | ||||
#endif | ||||
release_nvp(result); | ||||
result = NULL; | ||||
goto out; | ||||
} | ||||
*tempdst++ = val; | ||||
} else | ||||
*tempdst++ = *tempsrc; | ||||
} | ||||
*tempdst = 0; | ||||
if (current_action == GOT_NAMEVALUE_CSET) | ||||
append_namevalue(result, &name, &value); | ||||
else | ||||
combine_namevalue(result, &name, &value); | ||||
break; | break; | |||
case GOT_NAMEVALUE_CONT: | case GOT_NAMEVALUE_CONT: | |||
*nn = 0; | combine_namevalue(result, &name, &value); | |||
*vv = 0; | ||||
combine_namevalue(result, name, value); | ||||
break; | break; | |||
default: | default: | |||
break; | break; | |||
} | } | |||
nn = name; | ||||
mm = minor; | ||||
vv = value; | ||||
break; | break; | |||
case GOT_NOTHING: | case GOT_NOTHING: | |||
break; | break; | |||
} | } | |||
q++; | q++; | |||
} while (tok != nvp_EOS); | } while (tok != nvp_EOS); | |||
out: | ||||
/* Not all productions consume these values */ | ||||
free(name); | ||||
free(value); | ||||
free(minor); | ||||
return result; | return result; | |||
} | } | |||
/*}}}*/ | /*}}}*/ | |||
void free_nvp(struct nvp *nvp)/*{{{*/ | void free_nvp(struct nvp *nvp)/*{{{*/ | |||
{ | { | |||
struct nvp_entry *ne, *nne; | struct nvp_entry *ne, *nne; | |||
for (ne = nvp->first; ne; ne=nne) { | for (ne = nvp->first; ne; ne=nne) { | |||
nne = ne->next; | nne = ne->next; | |||
switch (ne->type) { | switch (ne->type) { | |||
case NVP_NAME: | case NVP_NAME: | |||
skipping to change at line 395 | skipping to change at line 454 | |||
struct nvp *n; | struct nvp *n; | |||
n = make_nvp(NULL, s, ""); | n = make_nvp(NULL, s, ""); | |||
if (n) { | if (n) { | |||
nvp_dump(n, stderr); | nvp_dump(n, stderr); | |||
free_nvp(n); | free_nvp(n); | |||
} | } | |||
} | } | |||
int main (int argc, char **argv) { | int main (int argc, char **argv) { | |||
struct nvp *n; | struct nvp *n; | |||
if (argc > 1) { | ||||
while (*++argv) | ||||
do_test(*argv); | ||||
return 0; | ||||
} | ||||
#if 0 | #if 0 | |||
do_test("attachment; filename=\"foo.c\"; prot=ro"); | do_test("attachment; filename=\"foo.c\"; prot=ro"); | |||
do_test("attachment; filename= \"foo bar.c\" ;prot=ro"); | do_test("attachment; filename= \"foo bar.c\" ;prot=ro"); | |||
do_test("attachment ; filename= \"foo bar.c\" ;prot= ro"); | do_test("attachment ; filename= \"foo bar.c\" ;prot= ro"); | |||
do_test("attachment ; filename= \"foo bar.c\" ;prot= ro"); | do_test("attachment ; filename= \"foo bar.c\" ;prot= ro"); | |||
do_test("attachment ; filename= \"foo ; bar.c\" ;prot= ro"); | do_test("attachment ; filename= \"foo ; bar.c\" ;prot= ro"); | |||
do_test("attachment ; x*0=\"hi \"; x*1=\"there\""); | do_test("attachment ; x*0=\"hi \"; x*1=\"there\""); | |||
#endif | #endif | |||
do_test("attachment; filename*=utf-8''Section%204-1%20%E2%80%93%20Response%20M | ||||
do_test("application/vnd.ms-excel; name=\"thequiz.xls\""); | atrix%20PartIIA%2Edoc"); | |||
#if 0 | #if 0 | |||
do_test("application/vnd.ms-excel; name=\"thequiz.xls\""); | ||||
do_test("inline; filename*0=\"aaaa bbbb cccc dddd eeee ffff gggg hhhh iiii jjj j\t kkkkllll\""); | do_test("inline; filename*0=\"aaaa bbbb cccc dddd eeee ffff gggg hhhh iiii jjj j\t kkkkllll\""); | |||
do_test(" text/plain ; name= \"foo bar.c\" ;prot= ro/rw; read/write; read= foo bar"); | do_test(" text/plain ; name= \"foo bar.c\" ;prot= ro/rw; read/write; read= foo bar"); | |||
#endif | #endif | |||
return 0; | return 0; | |||
} | } | |||
#endif | #endif | |||
End of changes. 36 change blocks. | ||||
54 lines changed or deleted | 122 lines changed or added |