chiark / gitweb /
Debianization for 1.01-2
[qmail] / token822.c
1 #include "stralloc.h"
2 #include "alloc.h"
3 #include "str.h"
4 #include "token822.h"
5 #include "gen_allocdefs.h"
6
7 static struct token822 comma = { TOKEN822_COMMA };
8
9 void token822_reverse(ta)
10 token822_alloc *ta;
11 {
12  int i;
13  int n;
14  struct token822 temp;
15
16  n = ta->len - 1;
17  for (i = 0;i + i < n;++i)
18   {
19    temp = ta->t[i];
20    ta->t[i] = ta->t[n - i];
21    ta->t[n - i] = temp;
22   }
23 }
24
25 GEN_ALLOC_ready(token822_alloc,struct token822,t,len,a,i,n,x,30,token822_ready)
26 GEN_ALLOC_readyplus(token822_alloc,struct token822,t,len,a,i,n,x,30,token822_readyplus)
27 GEN_ALLOC_append(token822_alloc,struct token822,t,len,a,i,n,x,30,token822_readyplus,token822_append)
28
29 static int needspace(t1,t2)
30 int t1;
31 int t2;
32 {
33  if (!t1) return 0;
34  if (t1 == TOKEN822_COLON) return 1;
35  if (t1 == TOKEN822_COMMA) return 1;
36  if (t2 == TOKEN822_LEFT) return 1;
37  switch(t1)
38   {
39    case TOKEN822_ATOM: case TOKEN822_LITERAL:
40    case TOKEN822_QUOTE: case TOKEN822_COMMENT:
41      switch(t2)
42       {
43        case TOKEN822_ATOM: case TOKEN822_LITERAL:
44        case TOKEN822_QUOTE: case TOKEN822_COMMENT:
45          return 1;
46       }
47   }
48  return 0;
49 }
50
51 static int atomok(ch)
52 char ch;
53 {
54  switch(ch)
55   {
56    case ' ': case '\t': case '\r': case '\n':
57    case '(': case '[': case '"':
58    case '<': case '>': case ';': case ':':
59    case '@': case ',': case '.':
60      return 0;
61   }
62  return 1;
63 }
64
65 static void atomcheck(t)
66 struct token822 *t;
67 {
68  int i;
69  char ch;
70  for (i = 0;i < t->slen;++i)
71   {
72    ch = t->s[i];
73    if ((ch < 32) || (ch > 126) || (ch == ')') || (ch == ']') || (ch == '\\'))
74     {
75      t->type = TOKEN822_QUOTE;
76      return;
77     }
78   }
79 }
80
81 int token822_unparse(sa,ta,linelen)
82 stralloc *sa;
83 token822_alloc *ta;
84 unsigned int linelen;
85 {
86  struct token822 *t;
87  int len;
88  int ch;
89  int i;
90  int j;
91  int lasttype;
92  int newtype;
93  char *s;
94  char *lineb;
95  char *linee;
96
97  len = 0;
98  lasttype = 0;
99  for (i = 0;i < ta->len;++i)
100   {
101    t = ta->t + i;
102    newtype = t->type;
103    if (needspace(lasttype,newtype))
104      ++len;
105    lasttype = newtype;
106    switch(newtype)
107     {
108      case TOKEN822_COMMA:
109        len += 3; break;
110      case TOKEN822_AT: case TOKEN822_DOT: case TOKEN822_LEFT: case TOKEN822_RIGHT:
111      case TOKEN822_SEMI: case TOKEN822_COLON:
112        ++len; break;
113      case TOKEN822_ATOM: case TOKEN822_QUOTE: case TOKEN822_LITERAL: case TOKEN822_COMMENT:
114        if (t->type != TOKEN822_ATOM) len += 2;
115        for (j = 0;j < t->slen;++j)
116          switch(ch = t->s[j])
117           {
118            case '"': case '[': case ']': case '(': case ')':
119            case '\\': case '\r': case '\n': ++len;
120            default: ++len;
121           }
122        break;
123     }
124   }
125  len += 2;
126
127  if (!stralloc_ready(sa,len))
128    return -1;
129
130  s = sa->s;
131  lineb = s;
132  linee = 0;
133
134  lasttype = 0;
135  for (i = 0;i < ta->len;++i)
136   {
137    t = ta->t + i;
138    newtype = t->type;
139    if (needspace(lasttype,newtype))
140      *s++ = ' ';
141    lasttype = newtype;
142    switch(newtype)
143     {
144      case TOKEN822_COMMA:
145        *s++ = ',';
146 #define NSUW \
147  s[0] = '\n'; s[1] = ' '; \
148  if (linee && (!linelen || (s - lineb <= linelen))) \
149   { while (linee < s) { linee[0] = linee[2]; ++linee; } linee -= 2; } \
150  else { if (linee) lineb = linee + 1; linee = s; s += 2; }
151        NSUW
152        break;
153      case TOKEN822_AT: *s++ = '@'; break;
154      case TOKEN822_DOT: *s++ = '.'; break;
155      case TOKEN822_LEFT: *s++ = '<'; break;
156      case TOKEN822_RIGHT: *s++ = '>'; break;
157      case TOKEN822_SEMI: *s++ = ';'; break;
158      case TOKEN822_COLON: *s++ = ':'; break;
159      case TOKEN822_ATOM: case TOKEN822_QUOTE: case TOKEN822_LITERAL: case TOKEN822_COMMENT:
160        if (t->type == TOKEN822_QUOTE) *s++ = '"';
161        if (t->type == TOKEN822_LITERAL) *s++ = '[';
162        if (t->type == TOKEN822_COMMENT) *s++ = '(';
163        for (j = 0;j < t->slen;++j)
164          switch(ch = t->s[j])
165           {
166            case '"': case '[': case ']': case '(': case ')':
167            case '\\': case '\r': case '\n': *s++ = '\\';
168            default: *s++ = ch;
169           }
170        if (t->type == TOKEN822_QUOTE) *s++ = '"';
171        if (t->type == TOKEN822_LITERAL) *s++ = ']';
172        if (t->type == TOKEN822_COMMENT) *s++ = ')';
173        break;
174     }
175   }
176  NSUW
177  --s;
178  sa->len = s - sa->s;
179  return 1;
180 }
181
182 int token822_unquote(sa,ta)
183 stralloc *sa;
184 token822_alloc *ta;
185 {
186  struct token822 *t;
187  int len;
188  int i;
189  int j;
190  char *s;
191
192  len = 0;
193  for (i = 0;i < ta->len;++i)
194   {
195    t = ta->t + i;
196    switch(t->type)
197     {
198      case TOKEN822_COMMA: case TOKEN822_AT: case TOKEN822_DOT: case TOKEN822_LEFT: 
199      case TOKEN822_RIGHT: case TOKEN822_SEMI: case TOKEN822_COLON: 
200        ++len; break;
201      case TOKEN822_LITERAL:
202        len += 2;
203      case TOKEN822_ATOM: case TOKEN822_QUOTE:
204        len += t->slen;
205     }
206   }
207
208  if (!stralloc_ready(sa,len))
209    return -1;
210
211  s = sa->s;
212
213  for (i = 0;i < ta->len;++i)
214   {
215    t = ta->t + i;
216    switch(t->type)
217     {
218      case TOKEN822_COMMA: *s++ = ','; break;
219      case TOKEN822_AT: *s++ = '@'; break;
220      case TOKEN822_DOT: *s++ = '.'; break;
221      case TOKEN822_LEFT: *s++ = '<'; break;
222      case TOKEN822_RIGHT: *s++ = '>'; break;
223      case TOKEN822_SEMI: *s++ = ';'; break;
224      case TOKEN822_COLON: *s++ = ':'; break;
225      case TOKEN822_ATOM: case TOKEN822_QUOTE: case TOKEN822_LITERAL:
226        if (t->type == TOKEN822_LITERAL) *s++ = '[';
227        for (j = 0;j < t->slen;++j)
228          *s++ = t->s[j];
229        if (t->type == TOKEN822_LITERAL) *s++ = ']';
230        break;
231      case TOKEN822_COMMENT: break;
232     }
233   }
234  sa->len = s - sa->s;
235  return 1;
236 }
237
238 int token822_parse(ta,sa,buf)
239 token822_alloc *ta;
240 stralloc *sa;
241 stralloc *buf;
242 {
243  int i;
244  int salen;
245  int level;
246  struct token822 *t;
247  int numtoks;
248  int numchars;
249  char *cbuf;
250
251  salen = sa->len;
252
253  numchars = 0;
254  numtoks = 0;
255  for (i = 0;i < salen;++i)
256    switch(sa->s[i])
257     {
258      case '.': case ',': case '@': case '<': case '>': case ':': case ';':
259        ++numtoks; break;
260      case ' ': case '\t': case '\r': case '\n': break;
261      case ')': case ']': return 0;
262      /* other control chars and non-ASCII chars are also bad, in theory */
263      case '(':
264        level = 1;
265        while (level)
266         {
267          if (++i >= salen) return 0;
268          switch(sa->s[i])
269           {
270            case '(': ++level; break;
271            case ')': --level; break;
272            case '\\': if (++i >= salen) return 0;
273            default: ++numchars;
274           }
275         }
276        ++numtoks;
277        break;
278      case '"':
279        level = 1;
280        while (level)
281         {
282          if (++i >= salen) return 0;
283          switch(sa->s[i])
284           {
285            case '"': --level; break;
286            case '\\': if (++i >= salen) return 0;
287            default: ++numchars;
288           }
289         }
290        ++numtoks;
291        break;
292      case '[':
293        level = 1;
294        while (level)
295         {
296          if (++i >= salen) return 0;
297          switch(sa->s[i])
298           {
299            case ']': --level; break;
300            case '\\': if (++i >= salen) return 0;
301            default: ++numchars;
302           }
303         }
304        ++numtoks;
305        break;
306      default:
307        do
308         {
309          ++numchars;
310          if (++i >= salen)
311            break;
312         }
313        while (atomok(sa->s[i]));
314        --i;
315        ++numtoks;
316     }
317
318  if (!token822_ready(ta,numtoks))
319    return -1;
320  if (!stralloc_ready(buf,numchars))
321    return -1;
322  cbuf = buf->s;
323  ta->len = numtoks;
324
325  t = ta->t;
326  for (i = 0;i < salen;++i)
327    switch(sa->s[i])
328     {
329      case '.': t->type = TOKEN822_DOT; ++t; break;
330      case ',': t->type = TOKEN822_COMMA; ++t; break;
331      case '@': t->type = TOKEN822_AT; ++t; break;
332      case '<': t->type = TOKEN822_LEFT; ++t; break;
333      case '>': t->type = TOKEN822_RIGHT; ++t; break;
334      case ':': t->type = TOKEN822_COLON; ++t; break;
335      case ';': t->type = TOKEN822_SEMI; ++t; break;
336      case ' ': case '\t': case '\r': case '\n': break;
337      case '(':
338        t->type = TOKEN822_COMMENT; t->s = cbuf; t->slen = 0;
339        level = 1;
340        while (level)
341         {
342          ++i; /* assert: < salen */
343          switch(sa->s[i])
344           {
345            case '(': ++level; break;
346            case ')': --level; break;
347            case '\\': ++i; /* assert: < salen */
348            default: *cbuf++ = sa->s[i]; ++t->slen;
349           }
350         }
351        ++t;
352        break;
353      case '"':
354        t->type = TOKEN822_QUOTE; t->s = cbuf; t->slen = 0;
355        level = 1;
356        while (level)
357         {
358          ++i; /* assert: < salen */
359          switch(sa->s[i])
360           {
361            case '"': --level; break;
362            case '\\': ++i; /* assert: < salen */
363            default: *cbuf++ = sa->s[i]; ++t->slen;
364           }
365         }
366        ++t;
367        break;
368      case '[':
369        t->type = TOKEN822_LITERAL; t->s = cbuf; t->slen = 0;
370        level = 1;
371        while (level)
372         {
373          ++i; /* assert: < salen */
374          switch(sa->s[i])
375           {
376            case ']': --level; break;
377            case '\\': ++i; /* assert: < salen */
378            default: *cbuf++ = sa->s[i]; ++t->slen;
379           }
380         }
381        ++t;
382        break;
383      default:
384        t->type = TOKEN822_ATOM; t->s = cbuf; t->slen = 0;
385        do
386         {
387          *cbuf++ = sa->s[i]; ++t->slen;
388          if (++i >= salen)
389            break;
390         }
391        while (atomok(sa->s[i]));
392        atomcheck(t);
393        --i;
394        ++t;
395     }
396  return 1;
397 }
398
399 static int gotaddr(taout,taaddr,callback)
400 token822_alloc *taout;
401 token822_alloc *taaddr;
402 int (*callback)();
403 {
404  int i;
405
406  if (callback(taaddr) != 1)
407    return 0;
408
409  if (!token822_readyplus(taout,taaddr->len))
410    return 0;
411  
412  for (i = 0;i < taaddr->len;++i)
413    taout->t[taout->len++] = taaddr->t[i];
414
415  taaddr->len = 0;
416  return 1;
417 }
418
419 int token822_addrlist(taout,taaddr,ta,callback)
420 token822_alloc *taout;
421 token822_alloc *taaddr;
422 token822_alloc *ta;
423 int (*callback)();
424 {
425  struct token822 *t;
426  struct token822 *beginning;
427  int ingroup;
428  int wordok;
429
430  taout->len = 0;
431  taaddr->len = 0;
432
433  if (!token822_readyplus(taout,1)) return -1;
434  if (!token822_readyplus(taaddr,1)) return -1;
435  
436  ingroup = 0;
437  wordok = 1;
438
439  beginning = ta->t + 2;
440  t = ta->t + ta->len - 1;
441
442  /* rfc 822 address lists are easy to parse from right to left */
443
444 #define FLUSH if (taaddr->len) if (!gotaddr(taout,taaddr,callback)) return -1;
445 #define FLUSHCOMMA if (taaddr->len) { \
446 if (!gotaddr(taout,taaddr,callback)) return -1; \
447 if (!token822_append(taout,&comma)) return -1; }
448 #define ADDRLEFT if (!token822_append(taaddr,t--)) return -1;
449 #define OUTLEFT if (!token822_append(taout,t--)) return -1;
450
451  while (t >= beginning)
452   {
453    switch(t->type)
454     {
455      case TOKEN822_SEMI:
456        FLUSHCOMMA
457        if (ingroup) return 0;
458        ingroup = 1;
459        wordok = 1;
460        break;
461      case TOKEN822_COLON:
462        FLUSH
463        if (!ingroup) return 0;
464        ingroup = 0;
465        while ((t >= beginning) && (t->type != TOKEN822_COMMA))
466          OUTLEFT
467        if (t >= beginning)
468          OUTLEFT
469        wordok = 1;
470        continue;
471      case TOKEN822_RIGHT:
472        FLUSHCOMMA
473        OUTLEFT
474        while ((t >= beginning) && (t->type != TOKEN822_LEFT))
475          ADDRLEFT
476        /* important to use address here even if it's empty: <> */
477        if (!gotaddr(taout,taaddr,callback)) return -1;
478        if (t < beginning) return 0;
479        OUTLEFT
480        while ((t >= beginning) && ((t->type == TOKEN822_COMMENT) || (t->type == TOKEN822_ATOM) || (t->type == TOKEN822_QUOTE) || (t->type == TOKEN822_AT) || (t->type == TOKEN822_DOT)))
481          OUTLEFT
482        wordok = 0;
483        continue;
484      case TOKEN822_ATOM: case TOKEN822_QUOTE: case TOKEN822_LITERAL:
485        if (!wordok)
486          FLUSHCOMMA
487        wordok = 0;
488        ADDRLEFT
489        continue;
490      case TOKEN822_COMMENT:
491        /* comment is lexically a space; shouldn't affect wordok */
492        break;
493      case TOKEN822_COMMA:
494        FLUSH
495        wordok = 1;
496        break;
497      default:
498        wordok = 1;
499        ADDRLEFT
500        continue;
501     }
502    OUTLEFT
503   }
504  FLUSH
505  ++t;
506  while (t > ta->t)
507    if (!token822_append(taout,--t)) return -1;
508
509  token822_reverse(taout);
510  return 1;
511 }