]> mj.ucw.cz Git - umpf.git/blob - int.c
935a7260dc24fe2a6bf94f85a53f8bc95f70fe11
[umpf.git] / int.c
1 #include <stdio.h>
2 #include <string.h>
3 #include <pcre.h>
4 #include <ctype.h>
5 #include <limits.h>
6 #include <unistd.h>
7 #include <fcntl.h>
8 #include <stdarg.h>
9 #include <sys/wait.h>
10
11 #include "cond.tab.h"
12 #include "umpf.h"
13
14 #define OVECCOUNT 3
15 #define HASHSIZE 103
16 #define MAGIC 19
17
18 #define INT_TO_STRING_LEN ((sizeof(int)*4*CHAR_BIT)/10 + 6)
19
20 static void __attribute__ ((noreturn)) 
21 bye(int code, char* msg, ...)
22 {
23         va_list args;
24
25         if (current_body->tmpfile)
26                 unlink(current_body->tmpfile);
27         
28         if (msg) {
29                 va_start(args, msg);
30                 vfprintf(stderr, msg, args);
31                 fputc('\n', stderr);
32                 va_end(args);
33         }
34         exit(code);
35 }
36
37 void
38 free_string(char* c)
39 {
40         if (c != empty)
41                 free(c);
42 }
43
44 static void
45 clear_var(int var)
46 {
47         if ((var_tab[var] != NULL))
48                 free_string(var_tab[var]);
49 }
50
51 static void
52 set_var(int var, char* value)
53 {
54         clear_var(var);
55         var_tab[var] = xstrdup(value);
56 }
57
58 static char*
59 get_var(int var)
60 {
61         if (var < 0)
62                 return xstrdup(const_tab[-var]);
63         return xstrdup(var_tab[var]);
64 }
65
66 /* return var struct or NULL if not found */
67 static struct variable*
68 get_var_struct(char* name, struct list* hash)
69 {
70         int n;
71         struct variable *p;
72
73         n = get_bucket_number(name);
74         int nocase = isupper(*name);
75         LIST_FOREACH(p, hash + n)
76                 if (!(nocase ? strcasecmp : strcmp)(p->name,name))
77                         return p;
78
79         return NULL;
80 }
81
82 static int 
83 regex_cmp(char* s, char* r)
84 {
85         pcre *brum;
86         int erroroffset;
87         const char* error;
88         int ovector[OVECCOUNT];
89         
90         brum = pcre_compile(r,0,&error,&erroroffset,NULL);
91         if (!brum)
92                 return -1;
93         
94         int res = pcre_exec(brum,NULL,s,strlen(s),0,0,ovector,OVECCOUNT);
95         pcre_free(brum);
96
97         return res;
98 }
99
100 #define UPPER(a) ((a) >> 8)
101 #define LOWER(a) ((a) & 0xFF)
102
103 static char*
104 xcat(char* left, char* right)
105 {
106         char* res = xmalloc(strlen(left) + strlen(right) + 1);
107
108         strcpy(res, left);
109         strcat(res, right);
110
111         free_string(left);
112         free_string(right);     
113
114         return res;
115 }
116
117 static char*
118 fold(const char* value, int taken) 
119 {
120         int i;
121         char* ret; 
122         int len = strlen(value);
123         int newlines[len / 78 + 5];
124         int newl = 0;
125         int curr_ws = -1;
126         int pos = 0;
127         int newl_done = 0;
128
129         if (len + taken <= 78)
130                 return xstrdup(value);
131         for(i = 0; i < len; i++) {
132                 if (value[i] == ' ' || value[i]=='\t')
133                         curr_ws = i;
134                 taken++;
135                 if (taken >= 78) {
136                         if (curr_ws > 0){
137                                 newlines[newl++] = curr_ws;
138                                 i = curr_ws;
139                                 while(value[i] && (value[i] == '\t' 
140                                         || value[i] == ' '))
141                                         i++;
142                                 taken = i - curr_ws;
143                                 curr_ws = -1;
144                         }
145                 }
146         }
147         ret = xmalloc(2*newl + len + 1);
148         for (i = 0; i < len; i++) {
149                 if (newl_done == newl) {
150                         strcpy(ret + pos, value + i);
151                         break;  
152                 }
153                 if(i != newlines[newl_done])
154                         ret[pos++] = value[i];
155                 else {
156                         newl++;
157                         ret[pos++] = '\n';
158                         ret[pos++] = ' ';
159                 }
160         }
161         return ret;
162 }
163
164 static char*
165 unfold(const char* u)
166 {
167         char* new;
168         const char* pu = u; 
169         char* pn;
170
171         new = xmalloc(strlen(u)+1);
172         pn = new;
173
174 #define IS_WHITE(c) ((c) == '\t' || (c)==' ' || c=='\n')
175
176         while (IS_WHITE(*pu))
177                 pu++;
178
179         while (*pu != 0){
180                 if (IS_WHITE(*pu)){
181                         while (IS_WHITE(*pu))
182                                 pu++;
183                         if (*pu != 0)
184                                 *pn++ = ' ';
185                 } else
186                         *pn++ = *pu++;          
187         }
188         *pn = 0;
189
190         return new;
191 }
192
193 static void
194 modify_headers(struct list* headers, struct list* hash)
195 {
196         struct hlist* p;
197         int i;
198         struct variable* pv;
199         char* u, * value;
200         
201
202         LIST_FOREACH(p, headers){
203                 pv = get_var_struct(p->name, hash);
204                 if (!pv)
205                         continue;
206                 u = unfold(p->value);
207                 value = get_var(pv->varcode);
208                 if (strcmp(u, value)){
209                         pv->modified = 0;
210                         free_string(p->value);
211                         p->value = fold(value,
212                                  strlen(p->name) + 2);
213                 }
214                 free_string(u);
215                 free_string(value);
216         }
217
218         // find new headers 
219         for (i = 0; i < HASHSIZE; i++){
220                 LIST_FOREACH(pv, hash + i){
221                         if (isupper(pv->name[0]) && pv->modified){
222                                 pv->modified = 0;
223
224                                 p = xmalloc(sizeof(struct hlist));
225                                 p->name = xstrdup(pv->name);
226                                 p->value = get_var(pv->varcode);
227
228                                 list_add_last(headers,&p->car);
229                         }
230                 }
231         }
232 }
233
234 static struct list*
235 copy_headers(struct list* orig)
236 {
237         struct list* new = xmalloc(sizeof(struct list));
238         struct hlist* po, *pn;
239
240         list_init(new);
241
242         LIST_FOREACH(po, orig){
243                 pn = xmalloc(sizeof(struct hlist));
244                 pn->name = xstrdup(po->name);
245                 pn->value = xstrdup(po->value);
246                 pn->have_var = 0;
247
248                 list_add_last(new, &pn->car);
249         }
250
251         return new;
252 }
253
254 static struct email 
255 prepare_email(struct list* hash)
256 {
257         struct email em;
258
259         modify_headers(current_headers, hash);
260         em.headers = copy_headers(current_headers);
261         em.body_len = current_body->body_len;
262         em.fd = current_body->fd; 
263         if (current_body->body) {
264                 em.body = xmalloc(em.body_len);
265                 memcpy(em.body, current_body->body, em.body_len);
266                 em.tmpfile = NULL;
267         } else {
268                 em.tmpfile = xstrdup(current_body->tmpfile);
269                 em.body = NULL;
270         }
271
272         return em;
273 }
274
275 static void
276 destroy_email(struct email em)
277 {
278         if (em.body)
279                 free_string(em.body);
280         if(em.tmpfile)
281                 free_string(em.tmpfile);
282 }
283
284 static void
285 do_string_ternary_op(struct code* p)
286 {
287         char* l = get_var(p->u.tpop.l);
288         char* r = get_var(p->u.tpop.r);
289         char* result;
290
291         switch(p->opcode) {
292                 case OPC_RE:
293                         result = xmalloc(INT_TO_STRING_LEN);
294                         sprintf(result, "%d", regex_cmp(l, r));
295                         break;
296                 case OPC_NRE:
297                         result = xmalloc(INT_TO_STRING_LEN);
298                         sprintf(result, "%d", !regex_cmp(l, r));
299                         break;
300                 case OPC_CAT:
301                         result = xcat(l, r);
302                         break;
303                 default:
304                         break;
305         };
306
307         set_var(p->u.tpop.res, result); 
308 }
309
310 static void
311 do_num_ternary_op(struct code* p)
312 {
313         int l, r, res;
314         char* result = xmalloc(INT_TO_STRING_LEN);
315
316         sscanf(get_var(p->u.tpop.l),"%d", &l);
317         sscanf(get_var(p->u.tpop.r),"%d", &r);
318
319         switch(p->opcode) {
320                 case OPC_GT:
321                         res = (l > r);
322                         break;
323                 case OPC_LT:
324                         res = (l < r);
325                         break;
326                 case OPC_LE:
327                         res = (l <= r);
328                         break;
329                 case OPC_GE:
330                         res = (l >= r);
331                         break;
332                 case OPC_EQ:
333                         res = (l == r);
334                         break;
335                 case OPC_NEQ:
336                         res = (l != r);
337                         break;
338                 case OPC_AND:
339                         res = (l && r);
340                         break;
341                 case OPC_OR:
342                         res = (l || r);
343                         break;
344                 case OPC_XOR:
345                         res = ((l || r) && !(l && r));
346                         break;
347                 case OPC_PLUS:
348                         res = (l + r);
349                         break;
350                 case OPC_MINUS:
351                         res = (l - r);
352                         break;
353                 case OPC_MUL:
354                         res = (l * r);
355                         break;
356                 case OPC_DIV:
357                         res = (l / r);
358                         break;
359                 default:
360                         break;
361         }
362
363         sprintf(result, "%d", res);
364         set_var(p->u.tpop.res, result);
365 }
366
367 static int
368 eval_cond(int var)
369 {
370         char* val = get_var(var);
371         int v;
372
373         if (! val)
374                 return 0;
375         if (! *val)
376                 return 0;
377         sscanf(val, "%d", &v);
378
379         return !!v;
380 }
381
382 static void
383 deliver(char* where, int copy, struct list* hash)
384 {
385         int res = 0;
386         struct email em = prepare_email(hash);
387         
388         res = deliver_local_email(where, &em);
389
390         destroy_email(em);
391
392         if (!copy) {
393                 if (res)
394                         bye(-res, "%m");
395                 else
396                         bye(0, NULL);
397         }
398 }
399
400 static void
401 send_mail(char* where, int copy, struct list* hash)
402 {
403         int pd[2];
404         int pid, status;
405         int res;
406         struct email em = prepare_email(hash);
407
408         //write_email_to_fd(1, &em); 
409         res = pipe(pd);
410         if (res < 0)
411                 goto end;
412
413         if ((pid = fork()) < 0)
414                 goto end;
415         else if (pid == 0) {
416                 close(0);
417                 dup(pd[0]);     
418                 close(pd[0]);
419                 //FIXME From?
420                 res = execl("/usr/lib/sendmail", "sendmail", where, NULL);
421         }
422         close(pd[0]);
423         write_email_to_fd(pd[1], &em); 
424         close(pd[1]);
425         wait(&status);
426 end:
427         destroy_email(em);
428         if (!copy) {
429                 if (res)
430                         bye(-res, "%m");
431                 else
432                         bye(0, NULL);
433         }
434 }
435
436 void
437 interp(struct list* ins, struct list* hash)
438 {
439         struct code* p;
440         char* result;
441         int v;
442
443         LIST_FOREACH(p, ins) {
444         switch (p->opcode) {
445                 case OPC_SET:
446                         set_var(p->u.set.l, get_var(p->u.set.r));
447                         break;  
448                 case OPC_JUMP:
449                         p = p->u.jump.target;
450                         continue;
451                         break;
452                 case OPC_JUMP_IF:
453                         if (eval_cond(p->u.jump_if.cond))
454                                 p = p->u.jump_if.target;
455                         continue;
456                         break;
457                 case OPC_JUMP_UNLESS:
458                         if (!eval_cond(p->u.jump_unless.cond))
459                                 p = p->u.jump_unless.target;
460                         continue;
461                         break;
462                 case OPC_NOP:
463                         continue;
464                         break;
465                 case OPC_GT:
466                 case OPC_LT:
467                 case OPC_LE:
468                 case OPC_GE:
469                 case OPC_EQ:
470                 case OPC_NEQ:
471                 case OPC_AND:
472                 case OPC_OR:
473                 case OPC_XOR:
474                 case OPC_PLUS:
475                 case OPC_MINUS:
476                 case OPC_MUL:
477                 case OPC_DIV:
478                         do_num_ternary_op(p);
479                         break;
480                 case OPC_NOT:
481                         result = xmalloc(INT_TO_STRING_LEN);
482                         sscanf(get_var(p->u.tpop.l),"%d", &v);
483                         sprintf(result, "%d", !v);
484                         set_var(p->u.tpop.res, result);
485                 case OPC_CAT:
486                 case OPC_RE:
487                 case OPC_NRE:
488                         do_string_ternary_op(p);
489                         break;
490                 case OPC_PIPE:
491                         break;
492                 case OPC_MAIL:
493                         send_mail(get_var(p->u.arrow.what), 
494                                 p->u.arrow.copy, hash);
495                         break;
496                 case OPC_DELIVER:
497                         deliver(get_var(p->u.arrow.what), 
498                                 p->u.arrow.copy, hash);
499                         break;
500                 case OPC_CALL_EXT:
501                         break;
502                 case OPC_DISCARD:
503                         bye(0, NULL);
504                         break;
505         }}
506         deliver(default_mailbox, 0, hash);
507 }
508
509 void
510 print_vars(struct list* hash)
511 {
512         int i;
513         struct variable* p;
514
515         for (i=0; i<HASHSIZE; i++){
516                 LIST_FOREACH(p, hash + i)
517                         printf("%s=%s\n",p->name, get_var(p->varcode));
518         }
519 }
520
521 void
522 save_current_headers(struct list* hash)
523 {
524         struct hlist* p;
525         char* u;
526         struct variable* pv;
527
528         LIST_FOREACH(p, current_headers){
529                 pv = get_var_struct(p->name, hash);
530                 if (!pv)
531                         continue;
532                 u = unfold(p->value);
533                 set_var(pv->varcode, u);
534                 free_string(u);
535                 pv->modified = 0;
536                 p->have_var = 1;
537         }
538 }