]> mj.ucw.cz Git - umpf.git/blobdiff - int.c
fix saving bodies
[umpf.git] / int.c
diff --git a/int.c b/int.c
index 795b843bd6ccbfa9c2765b1e44a69be2b7046466..e1c906f058342ada6c8d264876d5f4fcc48bed35 100644 (file)
--- a/int.c
+++ b/int.c
@@ -2,45 +2,71 @@
 #include <string.h>
 #include <pcre.h>
 #include <ctype.h>
+#include <limits.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#include <sys/wait.h>
+#include <sysexits.h>
 
 #include "cond.tab.h"
-#include "brum.h"
+#include "umpf.h"
 
 #define OVECCOUNT 3
 #define HASHSIZE 103
 #define MAGIC 19
 
-struct list* 
-new_var_hash(void)
+#define INT_TO_STRING_LEN ((sizeof(int)*4*CHAR_BIT)/10 + 6)
+
+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;
+void
+free_string(char* c)
+{
+       if (c != empty)
+               free(c);
 }
 
-static int
-get_bucket_number(char* name)
+static void
+clear_var(int var)
 {
-       unsigned int n = 0;
-       unsigned char* p = name;
+       if ((var_tab[var] != NULL))
+               free_string(var_tab[var]);
+}
 
-        while (*p != '\0'){
-                n = n * MAGIC + toupper(*p++);
-        }
-        n %= HASHSIZE;
+static void
+set_var(int var, char* value)
+{
+       clear_var(var);
+       var_tab[var] = xstrdup(value);
+}
 
-        return n;
+static char*
+get_var(int var)
+{
+       if (var < 0)
+               return xstrdup(const_tab[-var]);
+       return xstrdup(var_tab[var]);
 }
 
-/* value NULL for finding without modyfiing */
+/* return var struct or NULL if not found */
 static struct variable*
-find_var(char* name, char* value, struct list* hash)
+get_var_struct(char* name, struct list* hash)
 {
        int n;
        struct variable *p;
@@ -48,21 +74,10 @@ find_var(char* name, char* value, struct list* hash)
        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;
-                       }
+               if (!(nocase ? strcasecmp : strcmp)(p->name,name))
                        return p;
-               }
 
-       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;
+       return NULL;
 }
 
 static int 
@@ -83,172 +98,125 @@ 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)
+fold(const char* value, int taken) 
 {
-       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");        
+       int i;
+       char* ret; 
+       int len = strlen(value);
+       int newlines[len / 78 + 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++;
+                       ret[pos++] = '\n';
+                       ret[pos++] = ' ';
                }
-}      
+       }
+       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)
+static char*
+unfold(const char* u)
 {
-       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
+       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 = get_var_struct(p->name, hash);
+               if (!pv)
+                       continue;
+               u = unfold(p->value);
+               value = get_var(pv->varcode);
+               if (strcmp(u, value)){
                        pv->modified = 0;
-                       free(p->value);
-                       p->value = xstrdup(pv->value); //FIXME: fold it
+                       free_string(p->value);
+                       p->value = fold(value,
+                                strlen(p->name) + 2);
                }
+               free_string(u);
+               free_string(value);
        }
 
-       /* find new headers */
+       // find new headers 
        for (i = 0; i < HASHSIZE; i++){
                LIST_FOREACH(pv, hash + i){
                        if (isupper(pv->name[0]) && pv->modified){
@@ -256,7 +224,7 @@ modify_headers(struct list* headers, struct list* hash)
 
                                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);
                        }
@@ -276,6 +244,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,106 +252,287 @@ copy_headers(struct list* orig)
        return new;
 }
 
-static void
-new_action(char* l, char* r, char* s, struct list* hash)
+static struct email 
+prepare_email(struct list* hash)
 {
-       struct action* a;
-
-       a = xmalloc(sizeof(struct action));
+       struct email em;
 
        modify_headers(current_headers, hash);
-       a->e.headers = copy_headers(current_headers);
-       a->e.body = xstrdup(current_body);
-       a->l = l;
-       a->r = r;
-       a->s = s;
+       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;
+       }
 
-       do_action(a);
+       return em;
 }
 
-void
-interp(struct tree* t, struct list* hash)
+static void
+destroy_email(struct email em)
 {
-       if (!t)
-               return;
+       if (em.body)
+               free_string(em.body);
+       if(em.tmpfile)
+               free_string(em.tmpfile);
+}
 
-       switch(t->st){
-               case ST_BLOCK:
-                       interp(t->pt.block.head, hash);
-                       interp(t->pt.block.tail, hash);
-                       break;
-               case ST_ASS:
-                       find_var(t->pt.ass.left->pt.leaf.value, interp_ass_right(t->pt.ass.right, hash), hash);
+static void
+do_string_ternary_op(struct code* p)
+{
+       char* l = get_var(p->u.tpop.l);
+       char* r = get_var(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 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_NRE:
+                       result = xmalloc(INT_TO_STRING_LEN);
+                       sprintf(result, "%d", !regex_cmp(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_CAT:
+                       result = xcat(l, r);
                        break;
-               case ST_EMPTY:
-                       break;  
                default:
-                       die("interp: got to default");
-       }
+                       break;
+       };
 
+       set_var(p->u.tpop.res, result); 
 }
 
-void
-print_vars(struct list* hash)
+static void
+do_num_ternary_op(struct code* p)
 {
-       int i;
-       struct variable* p;
+       int l, r, res;
+       char* result = xmalloc(INT_TO_STRING_LEN);
 
-       for (i=0; i<HASHSIZE; i++){
-               LIST_FOREACH(p, hash + i)
-                       printf("%s=%s\n",p->name, p->value);
+       sscanf(get_var(p->u.tpop.l),"%d", &l);
+       sscanf(get_var(p->u.tpop.r),"%d", &r);
+
+       switch(p->opcode) {
+               case OPC_GT:
+                       res = (l > r);
+                       break;
+               case OPC_LT:
+                       res = (l < r);
+                       break;
+               case OPC_LE:
+                       res = (l <= r);
+                       break;
+               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;
+               default:
+                       break;
        }
+
+       sprintf(result, "%d", res);
+       set_var(p->u.tpop.res, result);
 }
 
-static char*
-unfold(char* u)
+static int
+eval_cond(int var)
 {
-       char* new;
-       char* pu = u; 
-       char* pn;
+       char* val = get_var(var);
+       int v;
 
-       new = xmalloc(strlen(u)+1);
-       pn = new;
+       if (! val)
+               return 0;
+       if (! *val)
+               return 0;
+       sscanf(val, "%d", &v);
 
-#define IS_WHITE(c) ((c) == '\t' || (c)==' ' || c=='\n')
+       return !!v;
+}
 
-       while (IS_WHITE(*pu))
-               pu++;
+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);
 
-       while (*pu != 0){
-               if (IS_WHITE(*pu)){
-                       while (IS_WHITE(*pu))
-                               pu++;
-                       if (*pu != 0)
-                               *pn++ = ' ';
-               } else
-                       *pn++ = *pu++;          
+       destroy_email(em);
+
+       if (!copy) {
+               if (res)
+                       bye(EX_TEMPFAIL, "%m");
+               else
+                       bye(0, NULL);
        }
-       *pn = 0;
+}
 
-       return new;
+static void
+send_mail(char* where, int copy, struct list* hash)
+{
+       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?
+               res = execl("/usr/lib/sendmail", "sendmail", where, NULL);
+       }
+       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, "%m");
+               else
+                       bye(0, NULL);
+       }
+}
+
+void
+interp(struct list* ins, struct list* hash)
+{
+       struct code* p;
+       char* result;
+       int v;
+
+       LIST_FOREACH(p, ins) {
+       switch (p->opcode) {
+               case OPC_SET:
+                       set_var(p->u.set.l, get_var(p->u.set.r));
+                       break;  
+               case OPC_JUMP:
+                       p = p->u.jump.target;
+                       continue;
+                       break;
+               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:
+                       continue;
+                       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(p->u.tpop.l),"%d", &v);
+                       sprintf(result, "%d", !v);
+                       set_var(p->u.tpop.res, result);
+               case OPC_CAT:
+               case OPC_RE:
+               case OPC_NRE:
+                       do_string_ternary_op(p);
+                       break;
+               case OPC_PIPE:
+                       break;
+               case OPC_MAIL:
+                       send_mail(get_var(p->u.arrow.what), 
+                               p->u.arrow.copy, hash);
+                       break;
+               case OPC_DELIVER:
+                       deliver(get_var(p->u.arrow.what), 
+                               p->u.arrow.copy, hash);
+                       break;
+               case OPC_CALL_EXT:
+                       break;
+               case OPC_DISCARD:
+                       bye(0, NULL);
+                       break;
+       }}
+       deliver(default_mailbox, 0, hash);
+}
+
+void
+print_vars(struct list* hash)
+{
+       int i;
+       struct variable* p;
+
+       for (i=0; i<HASHSIZE; i++){
+               LIST_FOREACH(p, hash + i)
+                       printf("%s=%s\n",p->name, get_var(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, hash);
+               if (!pv)
+                       continue;
                u = unfold(p->value);
-               pv = find_var(p->name,u,hash);
+               set_var(pv->varcode, u);
+               free_string(u);
                pv->modified = 0;
+               p->have_var = 1;
        }
-
 }