#include <stdio.h>
#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 variable {
- char* name;
- char* value;
- struct variable* next;
-};
+#define INT_TO_STRING_LEN ((sizeof(int)*4*CHAR_BIT)/10 + 6)
-struct variable**
-new_var_hash(void)
+void __attribute__ ((noreturn))
+bye(int code, char* msg, ...)
{
- struct variable** res;
+ va_list args;
- res = xmalloc (HASHSIZE * sizeof(struct variable*));
- memset(res, 0, sizeof(struct variable*)*HASHSIZE);
+ 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 + *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 variable** hash)
+get_var_struct(char* name, struct list* hash)
{
int n;
struct variable *p;
n = get_bucket_number(name);
+ int nocase = isupper(*name);
+ LIST_FOREACH(p, hash + n)
+ if (!(nocase ? strcasecmp : strcmp)(p->name,name))
+ return p;
- p = hash[n];
- while(p && strcmp(p->name,name))
- p = p->next;
-
- if (!value)
- return p;
- if (p)
- free(p->value);
- else {
- p = xmalloc(sizeof(struct variable));
- p->next = hash[n]->next;
- hash[n]->next = p;
- p->name = xstrdup(name);
- }
-
- p->value = xstrdup(value);
-
- return p;
-}
-
-static void
-print_ind(int num, char c)
-{
- int i;
-
- for (i = 0; i < num; i++){
- putchar(c);
- }
-//printf("%*s", num, "");
+ return NULL;
}
static int
return res;
}
-void
-print_tree(struct tree* t, int ind)
-{
- if (!t)
- return;
-
- switch (t->st){
- case ST_IF:
- print_ind(ind,' ');
- puts("if");
- print_tree(t->pt.tif.c,ind+1);
- print_ind(ind,' ');
- puts("then");
- print_tree(t->pt.tif.i,ind+1);
- print_ind(ind,' ');
- puts("else");
- 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);
- print_ind(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);
- print_ind(ind,' ');
- puts("=");
- print_tree(t->pt.ass.right, ind+1);
- break;
- case ST_LEAF:
- print_ind(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){
- print_ind(ind+1, ' ');
- puts(t->pt.arrow.kw_left);
- }
- print_ind(ind, ' ');
- printf("->\n");
- if (t->pt.arrow.kw_right){
- print_ind(ind+1, ' ');
- puts(t->pt.arrow.kw_right);
+
+static char*
+xcat(char* left, char* right)
+{
+ char* res = xmalloc(strlen(left) + strlen(right) + 1);
+
+ strcpy(res, left);
+ strcat(res, right);
+
+ free_string(left);
+ free_string(right);
+
+ return res;
+}
+
+static char*
+fold(const char* value, int taken)
+{
+ 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;
}
- print_tree(t->pt.arrow.s,ind+1);
- break;
- case ST_OP:
- print_tree(t->pt.op.left, ind+1);
- print_ind(ind,' ');
- putchar(t->pt.op.op);
- putchar('\n');
- print_tree(t->pt.op.right, ind+1);
- break;
- case ST_EMPTY:
+ }
+ }
+ 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;
+}
+
+static char*
+unfold(const char* u)
+{
+ char* new;
+ const char* pu = u;
+ char* pn;
+
+ 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;
+ int i;
+ struct variable* pv;
+ char* u, * value;
+
+
+ LIST_FOREACH(p, headers){
+ 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_string(p->value);
+ p->value = fold(value,
+ strlen(p->name) + 2);
+ }
+ free_string(u);
+ free_string(value);
+ }
+
+ // find new headers
+ for (i = 0; i < HASHSIZE; i++){
+ LIST_FOREACH(pv, hash + i){
+ if (isupper(pv->name[0]) && pv->modified){
+ pv->modified = 0;
+ p = xmalloc(sizeof(struct hlist));
+ p->name = xstrdup(pv->name);
+ p->value = get_var(pv->varcode);
+ list_add_last(headers,&p->car);
+ }
+ }
+ }
+}
+
+static struct list*
+copy_headers(struct list* orig)
+{
+ struct list* new = xmalloc(sizeof(struct list));
+ struct hlist* po, *pn;
+
+ list_init(new);
+
+ LIST_FOREACH(po, 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);
+ }
+
+ 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
+destroy_email(struct email em)
+{
+ if (em.body)
+ free_string(em.body);
+ if(em.tmpfile)
+ free_string(em.tmpfile);
+}
+
+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 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;
+ };
+
+ set_var(p->u.tpop.res, result);
+}
+
+static void
+do_num_ternary_op(struct code* p)
+{
+ int l, r, res;
+ char* result = xmalloc(INT_TO_STRING_LEN);
+
+ 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 int
+eval_cond(int var)
+{
+ char* val = get_var(var);
+ int v;
+
+ 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, "%m");
+ else
+ bye(0, NULL);
+ }
+}
+
+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 tree* t)
-{
- if (!t)
- return;
-
- switch(t->st){
- case ST_BLOCK:
- interp(t->pt.block.head);
- interp(t->pt.block.tail);
- break;
- case ST_ASS:
- // find_name(t->pt.ass.left->pt.leaf.value.s, interp_ass_right(t->pt.ass.right), var_hash);
- break;
+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;
+ char* u;
+ struct variable* pv;
+
+ LIST_FOREACH(p, current_headers){
+ pv = get_var_struct(p->name, hash);
+ if (!pv)
+ continue;
+ u = unfold(p->value);
+ set_var(pv->varcode, u);
+ free_string(u);
+ pv->modified = 0;
+ p->have_var = 1;
+ }
+}