d9898ee8 |
1 | /* |
2 | ** Copyright 1998 - 2007 Double Precision, Inc. |
3 | ** See COPYING for distribution information. |
4 | */ |
5 | |
6 | /* |
7 | ** $Id: rfc822.c,v 1.21 2007/02/26 04:13:41 mrsam Exp $ |
8 | */ |
9 | #include <stdio.h> |
10 | #include <ctype.h> |
11 | #include <stdlib.h> |
12 | #include <string.h> |
13 | #include "rfc822.h" |
14 | |
15 | static void tokenize(const char *p, struct rfc822token *tokp, int *toklen, |
16 | void (*err_func)(const char *, int, void *), void *voidp) |
17 | { |
18 | const char *addr=p; |
19 | int i=0; |
20 | int inbracket=0; |
21 | |
22 | *toklen=0; |
23 | while (*p) |
24 | { |
25 | if (isspace((int)(unsigned char)*p)) |
26 | { |
27 | p++; |
28 | i++; |
29 | continue; |
30 | } |
31 | |
32 | #define SPECIALS "<>@,;:.[]()%!\"\\?=/" |
33 | |
34 | switch (*p) { |
35 | int level; |
36 | |
37 | case '(': |
38 | if (tokp) |
39 | { |
40 | tokp->token='('; |
41 | tokp->ptr=p; |
42 | tokp->len=0; |
43 | } |
44 | level=0; |
45 | for (;;) |
46 | { |
47 | if (!*p) |
48 | { |
49 | if (err_func) (*err_func)(addr, i, |
50 | voidp); |
51 | if (tokp) tokp->token='"'; |
52 | ++*toklen; |
53 | return; |
54 | } |
55 | if (*p == '(') |
56 | ++level; |
57 | if (*p == ')' && --level == 0) |
58 | { |
59 | p++; |
60 | i++; |
61 | if (tokp) tokp->len++; |
62 | break; |
63 | } |
64 | if (*p == '\\' && p[1]) |
65 | { |
66 | p++; |
67 | i++; |
68 | if (tokp) tokp->len++; |
69 | } |
70 | |
71 | i++; |
72 | if (tokp) tokp->len++; |
73 | p++; |
74 | } |
75 | if (tokp) ++tokp; |
76 | ++*toklen; |
77 | continue; |
78 | |
79 | case '"': |
80 | p++; |
81 | i++; |
82 | |
83 | if (tokp) |
84 | { |
85 | tokp->token='"'; |
86 | tokp->ptr=p; |
87 | } |
88 | while (*p != '"') |
89 | { |
90 | if (!*p) |
91 | { |
92 | if (err_func) (*err_func)(addr, i, |
93 | voidp); |
94 | ++*toklen; |
95 | return; |
96 | } |
97 | if (*p == '\\' && p[1]) |
98 | { |
99 | if (tokp) tokp->len++; |
100 | p++; |
101 | i++; |
102 | } |
103 | if (tokp) tokp->len++; |
104 | p++; |
105 | i++; |
106 | } |
107 | ++*toklen; |
108 | if (tokp) ++tokp; |
109 | p++; |
110 | i++; |
111 | continue; |
112 | case '\\': |
113 | case ')': |
114 | if (err_func) (*err_func)(addr, i, voidp); |
115 | ++p; |
116 | ++i; |
117 | continue; |
118 | |
119 | case '=': |
120 | |
121 | if (p[1] == '?') |
122 | { |
123 | int j; |
124 | |
125 | /* exception: =? ... ?= */ |
126 | |
127 | for (j=2; p[j]; j++) |
128 | { |
129 | if (p[j] == '?' && p[j+1] == '=') |
130 | break; |
131 | |
132 | if (p[j] == '?' || p[j] == '=') |
133 | continue; |
134 | |
135 | if (strchr(SPECIALS, p[j]) || |
136 | isspace(p[j])) |
137 | break; |
138 | } |
139 | |
140 | if (p[j] == '?' && p[j+1] == '=') |
141 | { |
142 | j += 2; |
143 | if (tokp) |
144 | { |
145 | tokp->token=0; |
146 | tokp->ptr=p; |
147 | tokp->len=j; |
148 | ++tokp; |
149 | } |
150 | ++*toklen; |
151 | |
152 | p += j; |
153 | i += j; |
154 | continue; |
155 | } |
156 | } |
157 | /* FALLTHROUGH */ |
158 | |
159 | case '<': |
160 | case '>': |
161 | case '@': |
162 | case ',': |
163 | case ';': |
164 | case ':': |
165 | case '.': |
166 | case '[': |
167 | case ']': |
168 | case '%': |
169 | case '!': |
170 | case '?': |
171 | case '/': |
172 | |
173 | if ( (*p == '<' && inbracket) || |
174 | (*p == '>' && !inbracket)) |
175 | { |
176 | if (err_func) (*err_func)(addr, i, voidp); |
177 | ++p; |
178 | ++i; |
179 | continue; |
180 | } |
181 | |
182 | if (*p == '<') |
183 | inbracket=1; |
184 | |
185 | if (*p == '>') |
186 | inbracket=0; |
187 | |
188 | if (tokp) |
189 | { |
190 | tokp->token= *p; |
191 | tokp->ptr=p; |
192 | tokp->len=1; |
193 | ++tokp; |
194 | } |
195 | ++*toklen; |
196 | |
197 | if (*p == '<' && p[1] == '>') |
198 | /* Fake a null address */ |
199 | { |
200 | if (tokp) |
201 | { |
202 | tokp->token=0; |
203 | tokp->ptr=""; |
204 | tokp->len=0; |
205 | ++tokp; |
206 | } |
207 | ++*toklen; |
208 | } |
209 | ++p; |
210 | ++i; |
211 | continue; |
212 | default: |
213 | |
214 | if (tokp) |
215 | { |
216 | tokp->token=0; |
217 | tokp->ptr=p; |
218 | tokp->len=0; |
219 | } |
220 | while (*p && !isspace((int)(unsigned char)*p) && strchr( |
221 | SPECIALS, *p) == 0) |
222 | { |
223 | if (tokp) ++tokp->len; |
224 | ++p; |
225 | ++i; |
226 | } |
227 | if (i == 0) /* Idiot check */ |
228 | { |
229 | if (err_func) (*err_func)(addr, i, voidp); |
230 | if (tokp) |
231 | { |
232 | tokp->token='"'; |
233 | tokp->ptr=p; |
234 | tokp->len=1; |
235 | ++tokp; |
236 | } |
237 | ++*toklen; |
238 | ++p; |
239 | ++i; |
240 | continue; |
241 | } |
242 | if (tokp) ++tokp; |
243 | ++*toklen; |
244 | } |
245 | } |
246 | } |
247 | |
248 | static void parseaddr(struct rfc822token *tokens, int ntokens, |
249 | struct rfc822addr *addrs, int *naddrs) |
250 | { |
251 | int flag, j, k; |
252 | |
253 | *naddrs=0; |
254 | |
255 | while (ntokens) |
256 | { |
257 | int i; |
258 | |
259 | /* atoms (token=0) or quoted strings, followed by a : token |
260 | is a list name. */ |
261 | |
262 | for (i=0; i<ntokens; i++) |
263 | if (tokens[i].token && tokens[i].token != '"') |
264 | break; |
265 | if (i < ntokens && tokens[i].token == ':') |
266 | { |
267 | ++i; |
268 | if (addrs) |
269 | { |
270 | addrs->tokens=0; |
271 | addrs->name=i ? tokens:0; |
272 | for (j=1; j<i; j++) |
273 | addrs->name[j-1].next=addrs->name+j; |
274 | if (i) |
275 | addrs->name[i-1].next=0; |
276 | addrs++; |
277 | } |
278 | ++*naddrs; |
279 | tokens += i; |
280 | ntokens -= i; |
281 | continue; /* Group=phrase ":" */ |
282 | } |
283 | |
284 | /* Spurious commas are skipped, ;s are recorded */ |
285 | |
286 | if (tokens->token == ',' || tokens->token == ';') |
287 | { |
288 | if (tokens->token == ';') |
289 | { |
290 | if (addrs) |
291 | { |
292 | addrs->tokens=0; |
293 | addrs->name=tokens; |
294 | addrs->name->next=0; |
295 | addrs++; |
296 | } |
297 | ++*naddrs; |
298 | } |
299 | ++tokens; |
300 | --ntokens; |
301 | continue; |
302 | } |
303 | |
304 | /* If we can find a '<' before the next comma or semicolon, |
305 | we have new style RFC path address */ |
306 | |
307 | for (i=0; i<ntokens && tokens[i].token != ';' && |
308 | tokens[i].token != ',' && |
309 | tokens[i].token != '<'; i++) |
310 | ; |
311 | |
312 | if (i < ntokens && tokens[i].token == '<') |
313 | { |
314 | int j; |
315 | |
316 | /* Ok -- what to do with the stuff before '>'??? |
317 | If it consists exclusively of atoms, leave them alone. |
318 | Else, make them all a quoted string. */ |
319 | |
320 | for (j=0; j<i && (tokens[j].token == 0 || |
321 | tokens[j].token == '('); j++) |
322 | ; |
323 | |
324 | if (j == i) |
325 | { |
326 | if (addrs) |
327 | { |
328 | addrs->name= i ? tokens:0; |
329 | for (k=1; k<i; k++) |
330 | addrs->name[k-1].next=addrs->name+k; |
331 | if (i) |
332 | addrs->name[i-1].next=0; |
333 | } |
334 | } |
335 | else /* Intentionally corrupt the original toks */ |
336 | { |
337 | if (addrs) |
338 | { |
339 | tokens->len= tokens[i-1].ptr |
340 | + tokens[i-1].len |
341 | - tokens->ptr; |
342 | /* We know that all the ptrs point |
343 | to parts of the same string. */ |
344 | tokens->token='"'; |
345 | /* Quoted string. */ |
346 | addrs->name=tokens; |
347 | addrs->name->next=0; |
348 | } |
349 | } |
350 | |
351 | /* Any comments in the name part are changed to quotes */ |
352 | |
353 | if (addrs) |
354 | { |
355 | struct rfc822token *t; |
356 | |
357 | for (t=addrs->name; t; t=t->next) |
358 | if (t->token == '(') |
359 | t->token='"'; |
360 | } |
361 | |
362 | /* Now that's done and over with, see what can |
363 | be done with the <...> part. */ |
364 | |
365 | ++i; |
366 | tokens += i; |
367 | ntokens -= i; |
368 | for (i=0; i<ntokens && tokens[i].token != '>'; i++) |
369 | ; |
370 | if (addrs) |
371 | { |
372 | addrs->tokens=i ? tokens:0; |
373 | for (k=1; k<i; k++) |
374 | addrs->tokens[k-1].next=addrs->tokens+k; |
375 | if (i) |
376 | addrs->tokens[i-1].next=0; |
377 | ++addrs; |
378 | } |
379 | ++*naddrs; |
380 | tokens += i; |
381 | ntokens -= i; |
382 | if (ntokens) /* Skip the '>' token */ |
383 | { |
384 | --ntokens; |
385 | ++tokens; |
386 | } |
387 | continue; |
388 | } |
389 | |
390 | /* Ok - old style address. Assume the worst */ |
391 | |
392 | /* Try to figure out where the address ends. It ends upon: |
393 | a comma, semicolon, or two consecutive atoms. */ |
394 | |
395 | flag=0; |
396 | for (i=0; i<ntokens && tokens[i].token != ',' && |
397 | tokens[i].token != ';'; i++) |
398 | { |
399 | if (tokens[i].token == '(') continue; |
400 | /* Ignore comments */ |
401 | if (tokens[i].token == 0 || tokens[i].token == '"') |
402 | /* Atom */ |
403 | { |
404 | if (flag) break; |
405 | flag=1; |
406 | } |
407 | else flag=0; |
408 | } |
409 | if (i == 0) /* Must be spurious comma, or something */ |
410 | { |
411 | ++tokens; |
412 | --ntokens; |
413 | continue; |
414 | } |
415 | |
416 | if (addrs) |
417 | { |
418 | addrs->name=0; |
419 | } |
420 | |
421 | /* Ok, now get rid of embedded comments in the address. |
422 | Consider the last comment to be the real name */ |
423 | |
424 | if (addrs) |
425 | { |
426 | struct rfc822token save_token; |
427 | |
428 | memset(&save_token, 0, sizeof(save_token)); |
429 | |
430 | for (j=k=0; j<i; j++) |
431 | { |
432 | if (tokens[j].token == '(') |
433 | { |
434 | save_token=tokens[j]; |
435 | continue; |
436 | } |
437 | tokens[k]=tokens[j]; |
438 | k++; |
439 | } |
440 | |
441 | if (save_token.ptr) |
442 | { |
443 | tokens[i-1]=save_token; |
444 | addrs->name=tokens+i-1; |
445 | addrs->name->next=0; |
446 | } |
447 | addrs->tokens=k ? tokens:NULL; |
448 | for (j=1; j<k; j++) |
449 | addrs->tokens[j-1].next=addrs->tokens+j; |
450 | if (k) |
451 | addrs->tokens[k-1].next=0; |
452 | ++addrs; |
453 | } |
454 | ++*naddrs; |
455 | tokens += i; |
456 | ntokens -= i; |
457 | } |
458 | } |
459 | |
460 | static void print_token(const struct rfc822token *token, |
461 | void (*print_func)(char, void *), void *ptr) |
462 | { |
463 | const char *p; |
464 | int n; |
465 | |
466 | if (token->token == 0 || token->token == '(') |
467 | { |
468 | for (n=token->len, p=token->ptr; n; --n, ++p) |
469 | (*print_func)(*p, ptr); |
470 | return; |
471 | } |
472 | |
473 | if (token->token != '"') |
474 | { |
475 | (*print_func)(token->token, ptr); |
476 | return; |
477 | } |
478 | |
479 | (*print_func)('"', ptr); |
480 | n=token->len; |
481 | p=token->ptr; |
482 | while (n) |
483 | { |
484 | if (*p == '"' || (*p == '\\' && n == 1)) (*print_func)('\\', ptr); |
485 | if (*p == '\\' && n > 1) |
486 | { |
487 | (*print_func)('\\', ptr); |
488 | ++p; |
489 | --n; |
490 | } |
491 | (*print_func)(*p++, ptr); |
492 | --n; |
493 | } |
494 | (*print_func)('"', ptr); |
495 | } |
496 | |
497 | void rfc822tok_print(const struct rfc822token *token, |
498 | void (*print_func)(char, void *), void *ptr) |
499 | { |
500 | int prev_isatom=0; |
501 | int isatom; |
502 | |
503 | while (token) |
504 | { |
505 | isatom=rfc822_is_atom(token->token); |
506 | if (prev_isatom && isatom) |
507 | (*print_func)(' ', ptr); |
508 | print_token(token, print_func, ptr); |
509 | prev_isatom=isatom; |
510 | token=token->next; |
511 | } |
512 | } |
513 | |
514 | void rfc822_print(const struct rfc822a *rfcp, void (*print_func)(char, void *), |
515 | void (*print_separator)(const char *s, void *), void *ptr) |
516 | { |
517 | rfc822_print_common(rfcp, 0, 0, print_func, print_separator, ptr); |
518 | } |
519 | |
520 | void rfc822_print_common(const struct rfc822a *rfcp, |
521 | char *(*decode_func)(const char *, const char *), const char *chset, |
522 | void (*print_func)(char, void *), |
523 | void (*print_separator)(const char *, void *), void *ptr) |
524 | { |
525 | const struct rfc822addr *addrs=rfcp->addrs; |
526 | int naddrs=rfcp->naddrs; |
527 | |
528 | while (naddrs) |
529 | { |
530 | if (addrs->tokens == 0) |
531 | { |
532 | rfc822tok_print(addrs->name, print_func, ptr); |
533 | ++addrs; |
534 | --naddrs; |
535 | if (addrs[-1].name && naddrs) |
536 | { |
537 | struct rfc822token *t; |
538 | |
539 | for (t=addrs[-1].name; t && t->next; t=t->next) |
540 | ; |
541 | |
542 | if (t && (t->token == ':' || t->token == ';')) |
543 | (*print_separator)(" ", ptr); |
544 | } |
545 | continue; |
546 | } |
547 | else if (addrs->name && addrs->name->token == '(') |
548 | { /* old style */ |
549 | char *p; |
550 | |
551 | rfc822tok_print(addrs->tokens, print_func, ptr); |
552 | (*print_func)(' ', ptr); |
553 | |
554 | if (decode_func && (p=rfc822_gettok(addrs->name))!=0) |
555 | { |
556 | char *q= (*decode_func)(p, chset); |
557 | char *r; |
558 | |
559 | for (r=q; r && *r; r++) |
560 | (*print_func)( (int)(unsigned char)*r, |
561 | ptr); |
562 | if (q) free(q); |
563 | free(p); |
564 | } |
565 | else rfc822tok_print(addrs->name, print_func, ptr); |
566 | } |
567 | else |
568 | { |
569 | int print_braces=0; |
570 | char *p; |
571 | |
572 | if (addrs->name) |
573 | { |
574 | if (decode_func && |
575 | (p=rfc822_gettok(addrs->name)) != 0) |
576 | { |
577 | char *q= (*decode_func)(p, chset); |
578 | char *r; |
579 | |
580 | for (r=q; r && *r; r++) |
581 | (*print_func)( |
582 | (int)(unsigned char)*r, |
583 | ptr); |
584 | if (q) free(q); |
585 | free(p); |
586 | } |
587 | else rfc822tok_print(addrs->name, |
588 | print_func, ptr); |
589 | (*print_func)(' ', ptr); |
590 | print_braces=1; |
591 | } |
592 | else |
593 | { |
594 | struct rfc822token *p; |
595 | |
596 | for (p=addrs->tokens; p && p->next; p=p->next) |
597 | if (rfc822_is_atom(p->token) && |
598 | rfc822_is_atom(p->next->token)) |
599 | print_braces=1; |
600 | } |
601 | if (print_braces) |
602 | (*print_func)('<', ptr); |
603 | #if 0 |
604 | rfc822tok_print(addrs->tokens, print_func, ptr); |
605 | #else |
606 | if (decode_func && |
607 | (p=rfc822_gettok(addrs->tokens))!=0) |
608 | { |
609 | char *q=(*decode_func)(p, chset); |
610 | char *r; |
611 | for (r=q; r && *r; r++) |
612 | (*print_func)((int)(unsigned char)*r, |
613 | ptr); |
614 | if (q) free(q); |
615 | free(p); |
616 | } |
617 | else rfc822tok_print(addrs->tokens, print_func, ptr); |
618 | #endif |
619 | if (print_braces) |
620 | { |
621 | (*print_func)('>', ptr); |
622 | } |
623 | } |
624 | ++addrs; |
625 | --naddrs; |
626 | if (naddrs) |
627 | if (addrs->tokens || (addrs->name && |
628 | rfc822_is_atom(addrs->name->token))) |
629 | (*print_separator)(", ", ptr); |
630 | } |
631 | } |
632 | |
633 | void rfc822t_free(struct rfc822t *p) |
634 | { |
635 | if (p->tokens) free(p->tokens); |
636 | free(p); |
637 | } |
638 | |
639 | void rfc822a_free(struct rfc822a *p) |
640 | { |
641 | if (p->addrs) free(p->addrs); |
642 | free(p); |
643 | } |
644 | |
645 | void rfc822_deladdr(struct rfc822a *rfcp, int index) |
646 | { |
647 | int i; |
648 | |
649 | if (index < 0 || index >= rfcp->naddrs) return; |
650 | |
651 | for (i=index+1; i<rfcp->naddrs; i++) |
652 | rfcp->addrs[i-1]=rfcp->addrs[i]; |
653 | if (--rfcp->naddrs == 0) |
654 | { |
655 | free(rfcp->addrs); |
656 | rfcp->addrs=0; |
657 | } |
658 | } |
659 | |
660 | static void compat_err_func(const char *p, int i, void *voidp) |
661 | { |
662 | void (*err_func)(const char *, int)= |
663 | (void (*)(const char *, int))voidp; |
664 | |
665 | if (err_func) |
666 | (*err_func)(p, i); |
667 | } |
668 | |
669 | struct rfc822t *rfc822t_alloc(const char *addr, |
670 | void (*err_func)(const char *, int)) |
671 | { |
672 | return (rfc822t_alloc_new(addr, &compat_err_func, (void *)err_func)); |
673 | } |
674 | |
675 | |
676 | struct rfc822t *rfc822t_alloc_new(const char *addr, |
677 | void (*err_func)(const char *, int, void *), void *voidp) |
678 | { |
679 | struct rfc822t *p=(struct rfc822t *)malloc(sizeof(struct rfc822t)); |
680 | |
681 | if (!p) return (NULL); |
682 | memset(p, 0, sizeof(*p)); |
683 | |
684 | tokenize(addr, NULL, &p->ntokens, err_func, voidp); |
685 | p->tokens=p->ntokens ? (struct rfc822token *) |
686 | calloc(p->ntokens, sizeof(struct rfc822token)):0; |
687 | if (p->ntokens && !p->tokens) |
688 | { |
689 | rfc822t_free(p); |
690 | return (NULL); |
691 | } |
692 | tokenize(addr, p->tokens, &p->ntokens, NULL, NULL); |
693 | return (p); |
694 | } |
695 | |
696 | struct rfc822a *rfc822a_alloc(struct rfc822t *t) |
697 | { |
698 | struct rfc822a *p=(struct rfc822a *)malloc(sizeof(struct rfc822a)); |
699 | |
700 | if (!p) return (NULL); |
701 | memset(p, 0, sizeof(*p)); |
702 | |
703 | parseaddr(t->tokens, t->ntokens, NULL, &p->naddrs); |
704 | p->addrs=p->naddrs ? (struct rfc822addr *) |
705 | calloc(p->naddrs, sizeof(struct rfc822addr)):0; |
706 | if (p->naddrs && !p->addrs) |
707 | { |
708 | rfc822a_free(p); |
709 | return (NULL); |
710 | } |
711 | parseaddr(t->tokens, t->ntokens, p->addrs, &p->naddrs); |
712 | return (p); |
713 | } |
714 | |
715 | void rfc822_praddr(const struct rfc822a *rfcp, int index, |
716 | void (*print_func)(char, void *), void *ptr) |
717 | { |
718 | const struct rfc822addr *addrs; |
719 | |
720 | if (index < 0 || index >= rfcp->naddrs) return; |
721 | |
722 | addrs=rfcp->addrs+index; |
723 | if (addrs->tokens) |
724 | { |
725 | rfc822tok_print(addrs->tokens, print_func, ptr); |
726 | (*print_func)('\n', ptr); |
727 | } |
728 | } |
729 | |
730 | void rfc822_addrlist(const struct rfc822a *rfcp, |
731 | void (*print_func)(char, void *), void *ptr) |
732 | { |
733 | int i; |
734 | |
735 | for (i=0; i<rfcp->naddrs; i++) |
736 | rfc822_praddr(rfcp, i, print_func, ptr); |
737 | } |
738 | |
739 | void rfc822_prname(const struct rfc822a *rfcp, int index, |
740 | void (*print_func)(char, void *), void *ptr) |
741 | { |
742 | const struct rfc822addr *addrs; |
743 | |
744 | if (index < 0 || index >= rfcp->naddrs) return; |
745 | |
746 | addrs=rfcp->addrs+index; |
747 | |
748 | if (!addrs->tokens) return; |
749 | rfc822_prname_orlist(rfcp, index, print_func, ptr); |
750 | } |
751 | |
752 | void rfc822_prname_orlist(const struct rfc822a *rfcp, int index, |
753 | void (*print_func)(char, void *), void *ptr) |
754 | { |
755 | const struct rfc822addr *addrs; |
756 | |
757 | if (index < 0 || index >= rfcp->naddrs) return; |
758 | |
759 | addrs=rfcp->addrs+index; |
760 | |
761 | if (addrs->name) |
762 | { |
763 | struct rfc822token *i; |
764 | int n; |
765 | int prev_isatom=0; |
766 | int isatom=0; |
767 | |
768 | for (i=addrs->name; i; i=i->next, prev_isatom=isatom) |
769 | { |
770 | isatom=rfc822_is_atom(i->token); |
771 | if (isatom && prev_isatom) |
772 | (*print_func)(' ', ptr); |
773 | |
774 | if (i->token == '"') |
775 | { |
776 | for (n=0; n<i->len; n++) |
777 | { |
778 | if (i->ptr[n] == '\\' && |
779 | n + 1 < i->len) |
780 | ++n; |
781 | (*print_func)(i->ptr[n], ptr); |
782 | } |
783 | continue; |
784 | } |
785 | |
786 | if (i->token != '(') |
787 | { |
788 | print_token(i, print_func, ptr); |
789 | continue; |
790 | } |
791 | |
792 | for (n=2; n<i->len; n++) |
793 | (*print_func)(i->ptr[n-1], ptr); |
794 | } |
795 | } else |
796 | rfc822tok_print(addrs->tokens, print_func, ptr); |
797 | (*print_func)('\n', ptr); |
798 | } |
799 | |
800 | void rfc822_namelist(const struct rfc822a *rfcp, |
801 | void (*print_func)(char, void *), void *ptr) |
802 | { |
803 | int i; |
804 | |
805 | for (i=0; i<rfcp->naddrs; i++) |
806 | rfc822_prname(rfcp, i, print_func, ptr); |
807 | } |