X-Git-Url: http://mj.ucw.cz/gitweb/?a=blobdiff_plain;f=int.c;h=d5a39bfadc8f58d06a81c16a4196a7a802799df2;hb=50516646f2475a9c9350e0fc7e924e039b739175;hp=f7fe023b592f5c5538c33d003251a2494e02d86a;hpb=fe8a488cd0901903cf93448366b3164c94674ac9;p=umpf.git diff --git a/int.c b/int.c index f7fe023..d5a39bf 100644 --- a/int.c +++ b/int.c @@ -2,6 +2,13 @@ #include #include #include +#include +#include +#include +#include +#include +#include +#include #include "cond.tab.h" #include "umpf.h" @@ -10,59 +17,122 @@ #define HASHSIZE 103 #define MAGIC 19 -struct list* -new_var_hash(void) +#define INT_TO_STRING_LEN ((sizeof(int)*4*CHAR_BIT)/10 + 6) + +struct procstat { + int fd; + int exit; + char* name; +}; + +void __attribute__ ((noreturn)) +bye(int code, char* msg, ...) { - struct list* res; - int i; + va_list args; - res = xmalloc (HASHSIZE * sizeof(struct list)); - for (i = 0; i < HASHSIZE; i++) - list_init(res + i); + if (current_body->tmpfile) + unlink(current_body->tmpfile); + if (msg) { + va_start(args, msg); + vfprintf(stderr, msg, args); + fputc('\n', stderr); + va_end(args); + } + exit(code); +} - return res; +static void +set_modif_flag(int var, int flag) +{ + var_tab[var].modif = flag; } static int -get_bucket_number(char* name) +get_modif_flag(int var) { - unsigned int n = 0; - unsigned char* p = name; + return var_tab[var].modif; +} - while (*p != '\0'){ - n = n * MAGIC + toupper(*p++); - } - n %= HASHSIZE; +void +free_string(char* c) +{ + if (c != empty) + free(c); +} - return n; +static void +clear_var(int var) +{ + if ((var_tab[var].value != NULL)) + free_string(var_tab[var].value); + var_tab[var].modif = 0; } -/* value NULL for finding without modyfiing */ -static struct variable* -find_var(char* name, char* value, struct list* hash) +static void +set_var(int var, char* value) { - int n; - struct variable *p; + clear_var(var); + var_tab[var].modif = 1; + var_tab[var].value = xstrdup(value); +} - n = get_bucket_number(name); - int nocase = isupper(*name); - LIST_FOREACH(p, hash + n) - if (!(nocase ? strcasecmp : strcmp)(p->name,name)){ - if (value){ - free(p->value); - p->value = value; - p->modified = 1; - } - return p; - } +void +set_cur_mail_length_var(int len, struct list* hash) +{ + struct variable* pv; + char buf[INT_TO_STRING_LEN]; + + pv = get_var_struct(INT_VAR_MAIL_LEN, VAR_INTERN, hash); + + if (! pv) /* user is not interested */ + return; + + sprintf(buf,"%d", len); + set_var(pv->varcode, buf); +} + +static void +set_exit_code_var(int code, struct list* hash) +{ + struct variable* pv; + char buf[INT_TO_STRING_LEN]; + + pv = get_var_struct(INT_VAR_LAST_EXIT, VAR_INTERN, hash); - p = xmalloc(sizeof(struct variable)); - p->name = xstrdup(name); - p->value = (value? value:xstrdup("")); - p->modified = 1; - list_add_last(hash+n, &p->car); - return p; + if (! pv) /* user is not interested */ + return; + + sprintf(buf,"%d", code); + set_var(pv->varcode, buf); +} + +static void +set_last_pipe_var(char* buf, struct list* hash) +{ + struct variable* pv; + + pv = get_var_struct(INT_VAR_LAST_EXIT, VAR_INTERN, hash); + + if (! pv) /* user is not interested */ + return; + set_var(pv->varcode, buf); +} + +static char* +get_var(int var) +{ + if (var < 0) + return xstrdup(const_tab[-var]); + return xstrdup(var_tab[var].value); +} + +static char* +get_var_nc(int var) +{ + if (var < 0) + return const_tab[-var]; + return var_tab[var].value; } static int @@ -83,182 +153,135 @@ regex_cmp(char* s, char* r) return res; } -void -print_tree(struct tree* t, int ind) -{ - if (!t) - return; - - switch (t->st){ - case ST_IF: - printf("%*s if\n", ind, ""); - print_tree(t->pt.tif.c,ind+1); - printf("%*s then\n", ind, ""); - print_tree(t->pt.tif.i,ind+1); - printf("%*s else\n", ind, ""); - print_tree(t->pt.tif.e,ind+1); - break; - case ST_COND: #define UPPER(a) ((a) >> 8) #define LOWER(a) ((a) & 0xFF) - print_tree(t->pt.cond.left, ind+1); - printf("%*s", ind, ""); - if (UPPER(t->pt.cond.op) > 0) - putchar(UPPER(t->pt.cond.op)); - putchar(LOWER(t->pt.cond.op)); - putchar('\n'); - print_tree(t->pt.cond.right, ind+1); - break; - case ST_BLOCK: - print_tree(t->pt.block.head,ind); - print_tree(t->pt.block.tail,ind); - break; - case ST_ASS: - print_tree(t->pt.ass.left, ind+1); - printf("%*s =\n", ind, ""); - print_tree(t->pt.ass.right, ind+1); - break; - case ST_LEAF: - printf("%*s", ind, ""); - switch (t->pt.leaf.type){ - case L_VAR: - putchar('$'); - case L_CONST: - puts(t->pt.leaf.value); - break; - } - break; - case ST_ARROW: - if (t->pt.arrow.kw_left) - printf("%*s%s\n", ind+1, "", t->pt.arrow.kw_left); - printf("%*s ->\n", ind, ""); - if (t->pt.arrow.kw_right) - printf("%*s%s\n", ind+1, "", t->pt.arrow.kw_right); - if (t->pt.arrow.s) - print_tree(t->pt.arrow.s,ind+1); - break; - case ST_OP: - print_tree(t->pt.op.left, ind+1); - printf("%*s%c\n", ind, "", t->pt.op.op); - print_tree(t->pt.op.right, ind+1); - break; - case ST_EMPTY: - break; - - - } -} static char* xcat(char* left, char* right) { char* res = xmalloc(strlen(left) + strlen(right) + 1); - strcpy(res,left); - strcat(left,right); + strcpy(res, left); + strcat(res, right); - free(left); - free(right); + free_string(left); + free_string(right); return res; } static char* -interp_ass_right(struct tree* t, struct list* hash) -{ - switch (t->st){ - case ST_LEAF: - if (t->pt.leaf.type == L_VAR) - return xstrdup(find_var(t->pt.leaf.value,NULL,hash)->value); - else - return xstrdup(t->pt.leaf.value); - case ST_OP: - switch (t->pt.op.op){ - case '.': - return xcat(interp_ass_right(t->pt.op.left, hash),interp_ass_right(t->pt.op.right, hash)); - } - case ST_EMPTY: - return xstrdup(""); - default: - die("interp_ass_right: got to default"); +fold(const char* value, int taken) +{ + int i; + char* ret; + int len = strlen(value); + int newlines[len / 40 + 5]; + int newl = 0; + int curr_ws = -1; + int pos = 0; + int newl_done = 0; + + if (len + taken <= 78) + return xstrdup(value); + for(i = 0; i < len; i++) { + if (value[i] == ' ' || value[i]=='\t') + curr_ws = i; + taken++; + if (taken >= 78) { + if (curr_ws > 0){ + newlines[newl++] = curr_ws; + i = curr_ws; + while(value[i] && (value[i] == '\t' + || value[i] == ' ')) + i++; + taken = i - curr_ws; + curr_ws = -1; + } + } + } + ret = xmalloc(2*newl + len + 1); + for (i = 0; i < len; i++) { + if (newl_done == newl) { + strcpy(ret + pos, value + i); + break; } -} + if(i != newlines[newl_done]) + ret[pos++] = value[i]; + else { + newl_done++; + ret[pos++] = '\n'; + ret[pos++] = '\t'; + } + } + return ret; +} -// FIXME: we would like to be able also do things like ($a & $b) == $c -static int -interp_cond(struct tree* t, struct list* hash) -{ - if (t->st != ST_COND) - die("Muhehehechlemst?"); - - if (t->pt.cond.type == OP_REL){ - if (t->pt.cond.left->st != ST_LEAF || t->pt.cond.right->st != ST_LEAF) - die("Chlemst"); - - char* left = (t->pt.cond.left->pt.leaf.type == L_VAR ? find_var(t->pt.cond.left->pt.leaf.value,NULL,hash)->value : t->pt.cond.left->pt.leaf.value); - char* right = (t->pt.cond.right->pt.leaf.type == L_VAR ? find_var(t->pt.cond.right->pt.leaf.value,NULL,hash)->value : t->pt.cond.right->pt.leaf.value); - switch (t->pt.cond.op){ - case CC('=','='): - return !strcmp(left,right); - case CC('!','='): - return strcmp(left,right); - case CC('~','~'): - return regex_cmp(left,right); - case CC('!','~'): - return !regex_cmp(left,right); - default: - die("Brrrchlemst!"); - } //TODO: add numbers +static char* +unfold(const char* u) +{ + char* new; + const char* pu = u; + char* pn; - } else { - int left = interp_cond(t->pt.cond.left, hash); - int right; - - if (t->pt.cond.op != '!') - right = interp_cond(t->pt.cond.right, hash); - - switch (t->pt.cond.op){ - case '&': - return left && right; - case '|': - return left || right; - case '^': - return (left || right) && !(left && right); - case '!': - return !left; - default: - die("Brrrchlemst!"); - } + new = xmalloc(strlen(u)+1); + pn = new; + +#define IS_WHITE(c) ((c) == '\t' || (c)==' ' || c=='\n') + + while (IS_WHITE(*pu)) + pu++; + + while (*pu != 0){ + if (IS_WHITE(*pu)){ + while (IS_WHITE(*pu)) + pu++; + if (*pu != 0) + *pn++ = ' '; + } else + *pn++ = *pu++; } + *pn = 0; + + return new; } static void modify_headers(struct list* headers, struct list* hash) { struct hlist* p; - struct variable* pv; int i; + struct variable* pv; + char* u, * value; + LIST_FOREACH(p, headers){ - pv = find_var(p->name,NULL,hash); - if (pv->modified){ - pv->modified = 0; - free(p->value); - p->value = xstrdup(pv->value); //FIXME: fold it + pv = get_var_struct(p->name, VAR_HEADER, hash); + if (!pv) + continue; + u = unfold(p->value); + value = get_var_nc(pv->varcode); + if (strcmp(u, value)){ + set_modif_flag(pv->varcode, 0); + free_string(p->value); + p->value = fold(value, + strlen(p->name) + 2); } + free_string(u); } - /* find new headers */ + // find new headers for (i = 0; i < HASHSIZE; i++){ LIST_FOREACH(pv, hash + i){ - if (isupper(pv->name[0]) && pv->modified){ - pv->modified = 0; + if (pv->type == VAR_HEADER && + get_modif_flag(pv->varcode)){ + set_modif_flag(pv->varcode, 0); p = xmalloc(sizeof(struct hlist)); p->name = xstrdup(pv->name); - p->value = xstrdup(pv->value); + p->value = get_var(pv->varcode); - list_add_last(headers,&p->car); + list_add_last(headers, &p->car); } } } @@ -276,6 +299,7 @@ copy_headers(struct list* orig) pn = xmalloc(sizeof(struct hlist)); pn->name = xstrdup(po->name); pn->value = xstrdup(po->value); + pn->have_var = 0; list_add_last(new, &pn->car); } @@ -283,114 +307,514 @@ copy_headers(struct list* orig) return new; } +static struct email +prepare_email(struct list* hash) +{ + struct email em; + + modify_headers(current_headers, hash); + em.headers = copy_headers(current_headers); + em.body_len = current_body->body_len; + em.fd = current_body->fd; + if (current_body->body) { + em.body = xmalloc(em.body_len); + memcpy(em.body, current_body->body, em.body_len); + em.tmpfile = NULL; + } else { + em.tmpfile = xstrdup(current_body->tmpfile); + em.body = NULL; + } + + return em; +} + static void -new_action(char* l, char* r, char* s, struct list* hash) +destroy_headers(struct list* h) { - struct action* a; + struct hlist* p; - a = xmalloc(sizeof(struct action)); + while ((p = list_del_last(h))) { + free(p->name); + free(p->value); + free(p); + } + free(h); +} - modify_headers(current_headers, hash); - a->e.headers = copy_headers(current_headers); - a->e.body_len = current_body->body_len; - a->e.body = xmalloc(a->e.body_len); - memcpy(a->e.body, current_body->body, a->e.body_len); - a->l = l; - a->r = r; - a->s = s; +static void +destroy_body(struct email* b) +{ + if (b->tmpfile) { + unlink(b->tmpfile); + free(b->tmpfile); + } + if (b->body) + free(b->body); +} - do_action(a); +static void +destroy_email(struct email em) +{ + destroy_headers(em.headers); + destroy_body(&em); } -void -interp(struct tree* t, struct list* hash) +static void +do_string_ternary_op(struct code* p) { - if (!t) - return; + char* l = get_var_nc(p->u.tpop.l); + char* r = get_var_nc(p->u.tpop.r); + char* result; + + switch(p->opcode) { + case OPC_RE: + result = xmalloc(INT_TO_STRING_LEN); + sprintf(result, "%d", regex_cmp(l, r)); + break; + case OPC_NRE: + result = xmalloc(INT_TO_STRING_LEN); + sprintf(result, "%d", !regex_cmp(l, r)); + break; + case OPC_CAT: + result = xcat(l, r); + break; + default: + break; + }; + free_string(result); + set_var(p->u.tpop.res, result); +} + +static void +do_num_ternary_op(struct code* p) +{ + int l = 0, r = 0, res; + char* result = xmalloc(INT_TO_STRING_LEN); + + sscanf(get_var_nc(p->u.tpop.l),"%d", &l); + sscanf(get_var_nc(p->u.tpop.r),"%d", &r); - switch(t->st){ - case ST_BLOCK: - interp(t->pt.block.head, hash); - interp(t->pt.block.tail, hash); + switch(p->opcode) { + case OPC_GT: + res = (l > r); break; - case ST_ASS: - find_var(t->pt.ass.left->pt.leaf.value, interp_ass_right(t->pt.ass.right, hash), hash); + case OPC_LT: + res = (l < r); break; - case ST_IF: - if (interp_cond(t->pt.tif.c, hash)) - interp(t->pt.tif.i, hash); - else - interp(t->pt.tif.e, hash); + case OPC_LE: + res = (l <= r); break; - case ST_ARROW: - new_action(t->pt.arrow.kw_left, t->pt.arrow.kw_right, interp_ass_right(t->pt.arrow.s, hash),hash); + case OPC_GE: + res = (l >= r); + break; + case OPC_EQ: + res = (l == r); + break; + case OPC_NEQ: + res = (l != r); + break; + case OPC_AND: + res = (l && r); + break; + case OPC_OR: + res = (l || r); + break; + case OPC_XOR: + res = ((l || r) && !(l && r)); + break; + case OPC_PLUS: + res = (l + r); + break; + case OPC_MINUS: + res = (l - r); + break; + case OPC_MUL: + res = (l * r); + break; + case OPC_DIV: + res = (l / r); break; - case ST_EMPTY: - break; default: - die("interp: got to default"); + break; } + sprintf(result, "%d", res); + free_string(result); + set_var(p->u.tpop.res, result); } -void -print_vars(struct list* hash) +static int +eval_cond(int var) { - int i; - struct variable* p; + char* val = get_var_nc(var); + int v; - for (i=0; iname, p->value); + if (! val) + return 0; + if (! *val) + return 0; + sscanf(val, "%d", &v); + + return !!v; +} + +static void +deliver(char* where, int copy, struct list* hash) +{ + int res = 0; + struct email em = prepare_email(hash); + + res = deliver_local_email(where, &em); + + destroy_email(em); + + if (!copy) { + if (res) + bye(EX_TEMPFAIL, "Cannot save mail to mailbox %s: %m", where); + else + bye(0, NULL); } } -static char* -unfold(char* u) +static void +send_mail(char* where, int copy, struct list* hash) { - char* new; - char* pu = u; - char* pn; + int pd[2]; + int pid, status; + int res; + struct email em = prepare_email(hash); + + res = pipe(pd); + if (res < 0) + goto end; + + if ((pid = fork()) < 0) + goto end; + else if (pid == 0) { + close(0); + dup(pd[0]); + close(pd[0]); + //FIXME From? + execl("/usr/lib/sendmail", "sendmail", where, NULL); + exit(1); + } + close(pd[0]); + write_email_to_fd(pd[1], &em); + close(pd[1]); + wait(&status); +end: + destroy_email(em); + if (!copy) { + if (res) + bye(EX_TEMPFAIL, "Cannot forward e-mail: %m"); + else + bye(0, NULL); + } +} - new = xmalloc(strlen(u)+1); - pn = new; +static struct procstat* +pipe_to(char* program, struct email* em) +{ + int pd_in[2]; + int pd_out[2]; + int pid, status; + struct pollfd pfd[2]; + char* name = xstrdup("/tmp/umpf.XXXXXX"); + char buf[BUFSIZE]; + int r; + int nfds = 2; + int data_pos = 0; + int fd = mkstemp(name); + struct procstat* tmp = xmalloc(sizeof(struct procstat)); + char* e = NULL; + if (fd < 0) + return NULL; + + tmp->name = name; + tmp->fd = fd; + + int res = pipe(pd_in); + if (res < 0) + goto bad; + res = pipe(pd_out); + if (res < 0) + goto bad; + + if ((pid = fork()) < 0) + goto bad; + else if (pid == 0) { + close(0); + dup(pd_in[0]); + + close(1); + dup(pd_out[1]); + + close(pd_in[0]); + close(pd_out[1]); + close(pd_in[1]); + close(pd_out[0]); + execl("/bin/sh", "sh", "-c", program, NULL); + exit(1); + } + close(pd_in[0]); + close(pd_out[1]); + + pfd[1].fd = pd_in[1]; + pfd[0].fd = pd_out[0]; + pfd[1].events = POLLOUT; + pfd[0].events = POLLIN; + + open_email(); + for (;;) { + if (poll(pfd, nfds, -1) < 0) + goto bad; + if ((pfd[0].revents & POLLERR) || (pfd[1].revents & POLLERR)) + goto bad; + + if (pfd[0].revents & POLLIN) { + r = read(pfd[0].fd, buf, BUFSIZE); + if (r <= 0) + break; + write(fd, buf, r); + } + if (pfd[0].revents & POLLHUP) { + if (e) + free_string(e); + break; + } -#define IS_WHITE(c) ((c) == '\t' || (c)==' ' || c=='\n') + if (nfds < 2) + continue; + + if (pfd[1].revents & POLLOUT) { + if (!chars_written) { + e = read_email(em); + data_pos = 0; + if (!chars_written) { + close(pfd[1].fd); + nfds = 1; + continue; + } + } + if (data_pos < chars_written) { + r = write(pfd[1].fd, e + data_pos, + chars_written - data_pos); + data_pos += r; + } else { + free_string(e); + data_pos = 0; + e = read_email(em); + } + } + } + + close(pd_out[0]); + wait(&status); + + tmp->exit = WEXITSTATUS(status); + return tmp; +bad: + close(fd); + unlink(name); + return NULL; +} - while (IS_WHITE(*pu)) - pu++; +static void +fix_content_length(struct list* h, int len) +{ + struct hlist* ph; + int found = 0; + char* clheader = "Content-Length: "; + + LIST_FOREACH(ph, h) { + if (!strcasecmp("Content-Length", ph->name)) { + found = 1; + free_string(ph->value); + ph->value = xmalloc(INT_TO_STRING_LEN); + sprintf(ph->value, "%d", len); + break; + } + } + if (! found) { + char* buf, * tmpbuf; + buf = xmalloc(INT_TO_STRING_LEN + strlen(clheader)); + tmpbuf = xmalloc(INT_TO_STRING_LEN); + sprintf(tmpbuf, "%d", len); + strcpy(buf, clheader); + strcat(buf, tmpbuf); + new_header(buf, h); + free_string(buf); + free_string(tmpbuf); + } +} - while (*pu != 0){ - if (IS_WHITE(*pu)){ - while (IS_WHITE(*pu)) - pu++; - if (*pu != 0) - *pn++ = ' '; - } else - *pn++ = *pu++; +static void +do_filter(char* program, struct list* hash) +{ + struct email em = prepare_email(hash); + struct procstat* f; + int res = 0; + + f = pipe_to(program, &em); + if (!f) { + res++; + goto end; } - *pn = 0; + lseek(f->fd, 0, SEEK_SET); + + destroy_headers(current_headers); + destroy_body(current_body); + current_headers = make_hlist(f->fd); + current_body = get_body(f->fd); + fix_content_length(current_headers, current_body->body_len); + save_current_headers(hash); + set_cur_mail_length_var(curr_email_len, hash); + set_exit_code_var(f->exit, hash); + + unlink(f->name); + free(f); +end: + //FIXME: what to do with exit code when pipe failed? + destroy_email(em); +} - return new; +static void +do_pipe(char* program, struct list* hash) +{ + struct email em = prepare_email(hash); + struct procstat* f; + int res = 0; + char* buf; + off_t pos; + + f = pipe_to(program, &em); + if (!f) { + res++; + goto end; + } + pos = lseek(f->fd, 0, SEEK_END); + lseek(f->fd, 0, SEEK_SET); + buf = xmalloc(pos); + + read(f->fd, buf, pos); + set_exit_code_var(f->exit, hash); + set_last_pipe_var(buf, hash); + free(buf); + + unlink(f->name); + free(f); +end: + destroy_email(em); +} + +void +interp(struct list* ins, struct list* hash) +{ + struct code* p; + char* result; + int v; + + p = (void*) ins->head.next; + while ((struct node*) p != &ins->head) { + switch (p->opcode) { + case OPC_SET: + result = get_var_nc(p->u.set.r); + set_var(p->u.set.l, result); + break; + case OPC_JUMP: + p = p->u.jump.target; + continue; + case OPC_JUMP_IF: + if (eval_cond(p->u.jump_if.cond)) { + p = p->u.jump_if.target; + continue; + } + break; + case OPC_JUMP_UNLESS: + if (!eval_cond(p->u.jump_unless.cond)) { + p = p->u.jump_unless.target; + continue; + } + break; + case OPC_NOP: + break; + case OPC_GT: + case OPC_LT: + case OPC_LE: + case OPC_GE: + case OPC_EQ: + case OPC_NEQ: + case OPC_AND: + case OPC_OR: + case OPC_XOR: + case OPC_PLUS: + case OPC_MINUS: + case OPC_MUL: + case OPC_DIV: + do_num_ternary_op(p); + break; + case OPC_NOT: + result = xmalloc(INT_TO_STRING_LEN); + sscanf(get_var_nc(p->u.dpop.par),"%d", &v); + sprintf(result, "%d", !v); + set_var(p->u.dpop.res, result); + free_string(result); + break; + case OPC_CAT: + case OPC_RE: + case OPC_NRE: + do_string_ternary_op(p); + break; + case OPC_PIPE: + do_pipe(get_var_nc(p->u.arrow.what), hash); + break; + case OPC_FILTER: + do_filter(get_var_nc(p->u.arrow.what), hash); + break; + case OPC_MAIL: + send_mail(get_var_nc(p->u.arrow.what), + p->u.arrow.copy, hash); + break; + case OPC_DELIVER: + deliver(get_var_nc(p->u.arrow.what), + p->u.arrow.copy, hash); + break; + case OPC_DISCARD: + bye(0, NULL); + break; + } + p = (void*) ((struct node*) p)->next; + } + deliver(default_mailbox, 0, hash); +} + +void +print_vars(struct list* hash) +{ + int i; + struct variable* p; + + for (i=0; iname, get_var_nc(p->varcode)); + } } void save_current_headers(struct list* hash) { struct hlist* p; - struct variable* pv; char* u; + struct variable* pv; LIST_FOREACH(p, current_headers){ + pv = get_var_struct(p->name, VAR_HEADER, hash); + if (!pv) + continue; u = unfold(p->value); - pv = find_var(p->name,u,hash); - pv->modified = 0; + set_var(pv->varcode, u); + free_string(u); + set_modif_flag(pv->varcode, 0); + p->have_var = 1; } - -} - -void -get_default_mailbox(char* mb) -{ - default_mailbox = mb; }