1 /*
2 * (c) Copyright 1990, Kim Fabricius Storm. All rights reserved.
3 * Copyright (c) 1996-2005 Michael T Pins. All rights reserved.
4 *
5 * Pack sender's name into something sensible, return in packed:
6 * pack_name(packed, name, length)
7 *
8 */
9
10 #include <string.h>
11 #include "config.h"
12 #include "global.h"
13
14 /* #define NAME_TEST *//* Never define this ! */
15
16 #define SEP_DOT 0 /* . */
17 #define SEP_PERCENT 1 /* % */
18 #define SEP_SCORE 2 /* _ */
19 #define SEP_AMPERSAND 3 /* @ */
20 #define SEP_BANG 4 /* ! */
21 #define SEP_MAXIMUM 5
22
23
24 #define CL_OK 0x0100 /* letter or digit */
25 #define CL_SPACE 0x0200 /* cvt to space */
26 #define CL_IGNORE 0x0400 /* ignore */
27 #define CL_RANGE(c) 0x0800+c/* space range, end with c */
28 #define CL_HYPHEN 0x1000 /* convert to - */
29 #define CL_STOP 0x2000 /* discard rest of name */
30 #define CL_SEP | 0x4000 + /* address separator */
31
32 #define IS_OK(c) (Class[c] & CL_OK)
33 #define IS_SPACE(c) (Class[c] & CL_SPACE)
34 #define IGNORE(c) (c & 0x80 || Class[c] & CL_IGNORE)
35 #define BEGIN_RANGE(c) (Class[c] & CL_RANGE(0))
36 #define END_RANGE(c) (Class[c] & 0xff)
37 #define IS_HYPHEN(c) (Class[c] & CL_HYPHEN)
38 #define IS_STOP(c) (Class[c] & CL_STOP)
39 #define IS_SEPARATOR(c) (Class[c] & (0 CL_SEP 0))
40
41 int old_packname = 0; /* Default to new behavior */
42
43 static short Class[128] = {
44 /* NUL */ CL_STOP,
45 /* SOH */ CL_IGNORE,
46 /* STX */ CL_IGNORE,
47 /* ETX */ CL_IGNORE,
48 /* EOT */ CL_IGNORE,
49 /* ENQ */ CL_IGNORE,
50 /* ACK */ CL_IGNORE,
51 /* BEL */ CL_IGNORE,
52 /* BS */ CL_IGNORE,
53 /* TAB */ CL_SPACE,
54 /* NL */ CL_IGNORE,
55 /* VT */ CL_IGNORE,
56 /* FF */ CL_IGNORE,
57 /* CR */ CL_IGNORE,
58 /* SO */ CL_IGNORE,
59 /* SI */ CL_IGNORE,
60 /* DLE */ CL_IGNORE,
61 /* DC1 */ CL_IGNORE,
62 /* DC2 */ CL_IGNORE,
63 /* DC3 */ CL_IGNORE,
64 /* DC4 */ CL_IGNORE,
65 /* NAK */ CL_IGNORE,
66 /* SYN */ CL_IGNORE,
67 /* ETB */ CL_IGNORE,
68 /* CAN */ CL_IGNORE,
69 /* EM */ CL_IGNORE,
70 /* SUB */ CL_IGNORE,
71 /* ESC */ CL_IGNORE,
72 /* FS */ CL_IGNORE,
73 /* GS */ CL_IGNORE,
74 /* RS */ CL_IGNORE,
75 /* US */ CL_IGNORE,
76
77 /* space */ CL_SPACE,
78 /* ! */ CL_SPACE CL_SEP SEP_BANG,
79 /* " */ CL_RANGE('"'),
80 /* # */ CL_OK,
81 /* $ */ CL_OK,
82 /* % */ CL_OK CL_SEP SEP_PERCENT,
83 /* & */ CL_OK,
84 /* ' */ CL_OK,
85 /* ( */ CL_OK,
86 /* ) */ CL_OK,
87 /* * */ CL_HYPHEN,
88 /* + */ CL_HYPHEN,
89 /* , */ CL_STOP,
90 /* - */ CL_HYPHEN,
91 /* . */ CL_SPACE CL_SEP SEP_DOT,
92 /* / */ CL_OK,
93 /* 0 */ CL_OK,
94 /* 1 */ CL_OK,
95 /* 2 */ CL_OK,
96 /* 3 */ CL_OK,
97 /* 4 */ CL_OK,
98 /* 5 */ CL_OK,
99 /* 6 */ CL_OK,
100 /* 7 */ CL_OK,
101 /* 8 */ CL_OK,
102 /* 9 */ CL_OK,
103 /* : */ CL_IGNORE,
104 /* ; */ CL_STOP,
105 /* < */ CL_IGNORE,
106 /* = */ CL_HYPHEN,
107 /* > */ CL_IGNORE,
108 /* ? */ CL_IGNORE,
109 /* @ */ CL_OK CL_SEP SEP_AMPERSAND,
110 /* A */ CL_OK,
111 /* B */ CL_OK,
112 /* C */ CL_OK,
113 /* D */ CL_OK,
114 /* E */ CL_OK,
115 /* F */ CL_OK,
116 /* G */ CL_OK,
117 /* H */ CL_OK,
118 /* I */ CL_OK,
119 /* J */ CL_OK,
120 /* K */ CL_OK,
121 /* L */ CL_OK,
122 /* M */ CL_OK,
123 /* N */ CL_OK,
124 /* O */ CL_OK,
125 /* P */ CL_OK,
126 /* Q */ CL_OK,
127 /* R */ CL_OK,
128 /* S */ CL_OK,
129 /* T */ CL_OK,
130 /* U */ CL_OK,
131 /* V */ CL_OK,
132 /* W */ CL_OK,
133 /* X */ CL_OK,
134 /* Y */ CL_OK,
135 /* Z */ CL_OK,
136 /* [ */ CL_OK,
137 /* \ */ CL_OK,
138 /* ] */ CL_OK,
139 /* ^ */ CL_IGNORE,
140 /* _ */ CL_SPACE CL_SEP SEP_SCORE,
141 /* ` */ CL_IGNORE,
142 /* a */ CL_OK,
143 /* b */ CL_OK,
144 /* c */ CL_OK,
145 /* d */ CL_OK,
146 /* e */ CL_OK,
147 /* f */ CL_OK,
148 /* g */ CL_OK,
149 /* h */ CL_OK,
150 /* i */ CL_OK,
151 /* j */ CL_OK,
152 /* k */ CL_OK,
153 /* l */ CL_OK,
154 /* m */ CL_OK,
155 /* n */ CL_OK,
156 /* o */ CL_OK,
157 /* p */ CL_OK,
158 /* q */ CL_OK,
159 /* r */ CL_OK,
160 /* s */ CL_OK,
161 /* t */ CL_OK,
162 /* u */ CL_OK,
163 /* v */ CL_OK,
164 /* w */ CL_OK,
165 /* x */ CL_OK,
166 /* y */ CL_OK,
167 /* z */ CL_OK,
168 /* { */ CL_OK,
169 /* | */ CL_OK,
170 /* } */ CL_OK,
171 /* ~ */ CL_HYPHEN,
172 /* DEL */ CL_IGNORE
173 };
174
175
176 int
pack_name(char * dest,char * source,int length)177 pack_name(char *dest, char *source, int length)
178 {
179 register char *p, *q, *r, c;
180 register int n;
181 register int i;
182 register int sep;
183 register int ignore_braces = 0;
184 register int ignore_comment = 0;
185 register int rfc_addr = 0;
186 register char *name;
187 register int lname;
188 register int drop_space, prev_space;
189 char *maxq;
190 int lfirst, lmiddle, llast;
191 char namebuf[129];
192 char *separator[SEP_MAXIMUM];
193
194 dest[0] = NUL;
195
196 if (source == NULL || source[0] == NUL)
197 return 0;
198
199 p = source;
200 if (old_packname == 0) {
201 while ((c = *p++)) {
202 if (c == '<') {
203 ignore_comment = 1;
204 rfc_addr = 1;
205 break;
206 }
207 }
208 }
209 full_scan:
210 p = source, q = namebuf, n = 0;
211 maxq = namebuf + sizeof namebuf - 1;
212
213 new_partition:
214 for (i = SEP_MAXIMUM; --i >= 0; separator[i] = NULL);
215
216 while ((c = *p++)) {
217 if (c == '<') {
218 while (q > namebuf && q[-1] == SP)
219 q--;
220 if (q == namebuf)
221 continue;
222 break;
223 }
224 if (c == '(' && ignore_comment)
225 break;
226 if (!rfc_addr) {
227 if (c == ')') {
228 if (--n == 0 && !ignore_braces)
229 break;
230 continue;
231 }
232 }
233 if (IGNORE((int8) c))
234 continue;
235 if (q == namebuf && IS_SPACE((int8) c))
236 continue;
237
238 if (!rfc_addr) {
239 if (c == '(') {
240 if (n++ == 0 && !ignore_braces) {
241 q = namebuf;
242 goto new_partition;
243 }
244 continue;
245 }
246 }
247 if (n > 0)
248 if (ignore_braces || n > 1)
249 continue;
250 if (q >= maxq)
251 break;
252 *q++ = c;
253 if (IS_SEPARATOR((int8) c)) {
254 switch ((sep = (Class[(int8) c] & 0xff))) {
255
256 case SEP_DOT:
257 if (separator[SEP_AMPERSAND] && q - namebuf <= length)
258 break;
259 continue;
260
261 case SEP_BANG:
262 if (separator[SEP_AMPERSAND])
263 continue;
264 break;
265
266 default:
267 if (separator[sep])
268 continue;
269 break;
270 }
271
272 separator[sep] = q - 1;
273 }
274 }
275
276 *q = NUL;
277
278 if (namebuf[0] == NUL && !ignore_braces) {
279 ignore_braces = 1;
280 goto full_scan;
281 }
282 if (namebuf[0] == NUL)
283 return 0;
284
285 name = namebuf;
286
287 if (name[0] == '"') {
288 name++;
289 if (q[-1] == '"')
290 *--q = NUL;
291 }
292 if (q - name <= length)
293 goto name_ok;
294
295 /* sorry for the many goto's -- the 3B2 C compiler does not */
296 /* make correct code for complicated logical expressions!! */
297 /* not even without -O */
298
299 /* We must pack the name to make it fit */
300
301 /* Name_of_person%... -> Name_of_person */
302
303 if ((r = separator[SEP_PERCENT])) {
304 if (!(q = separator[SEP_SCORE]) || q > r)
305 goto no_percent;
306 if ((q = separator[SEP_AMPERSAND]) && q < r)
307 goto no_percent;
308 if ((q = separator[SEP_BANG]) && q < r)
309 goto no_percent;
310 *r = NUL;
311 goto parse_name;
312 }
313 no_percent:
314
315 /* name@site.domain -> name@site */
316
317 if ((r = separator[SEP_AMPERSAND])) {
318
319 if ((q = separator[SEP_PERCENT]) && q < r) {
320 *r = NUL;
321 if (r - name <= length)
322 goto name_ok;
323
324 *q = NUL;
325
326 if (((p = separator[SEP_BANG]) && p < q)
327 || ((p = strrchr(name, '!')) && p < q)) {
328 name = p + 1;
329 }
330 if (strchr(name, '.'))
331 goto parse_name;
332
333 goto name_ok;
334 }
335 if ((q = separator[SEP_DOT])) {
336 *q = NUL;
337 goto name_ok;
338 }
339 *r = NUL;
340 if (r - name <= length)
341 goto name_ok;
342
343 if ((q = separator[SEP_BANG]) && q < r) {
344 name = q + 1;
345 goto name_ok;
346 }
347
348 #ifdef NOTDEF
349 if (strchr(name, '!') == NULL)
350 goto parse_name; /* take the chance ... */
351 #endif
352
353 goto name_ok; /* can't do it any better */
354 }
355 /* Phase 1: Normalization (remove superfluous characters) */
356
357 parse_name:
358
359 for (p = name, lname = 0, prev_space = 0; (c = *p); p++) {
360
361 /*
362 if (IGNORE(c)) {
363 *p = TAB;
364 if (p == name) name++;
365 continue;
366 }
367 */
368
369 if (IS_OK((int8) c)) {
370 lname++;
371 prev_space = 0;
372 continue;
373 }
374 if (IS_HYPHEN((int8) c)) {
375 if (p == name) {
376 name++;
377 continue;
378 }
379 if (prev_space) {
380 *p = NUL;
381 break; /* strip from " -" */
382 /* *p = TAB; *//* do not strip from " -" */
383 } else {
384 *p = '-';
385 lname++;
386 }
387 continue;
388 }
389 if (BEGIN_RANGE((int8) c)) {
390
391 if (p == name) {
392 name++;
393 continue;
394 }
395 c = END_RANGE((int8) c);
396 for (q = p + 1; *q && *q != c; q++);
397 if (*q) {
398 if (p[-1] != ' ')
399 lname++;
400 while (p <= q)
401 *p++ = ' ';
402 p--;
403 prev_space++;
404 continue;
405 }
406 c = ' ';
407 }
408 if (IS_SPACE((int8) c)) {
409 *p = ' ';
410 if (p == name)
411 name++;
412 else if (!prev_space) {
413 lname++;
414 prev_space++;
415 }
416 continue;
417 }
418 if (IS_STOP((int8) c)) {
419 *p = NUL;
420 break;
421 }
422 }
423 drop_last_name:
424 while (p > name && (*--p == ' ' || *p == TAB))
425 *p = NUL;
426
427 if (lname < length)
428 goto name_ok;
429
430
431 /* Phase 2: Reduce middle names */
432
433 for (r = p, llast = 0; r > name && *r != ' '; r--)
434 if (*r != TAB)
435 llast++;
436
437 /* r points to space before last name */
438
439 if (strncmp(r, " Jr", 3) == 0 || strncmp(r, " II", 3) == 0) {
440 p = r + 1;
441 lname -= llast;
442 goto drop_last_name;
443 }
444 if (r == name)
445 goto phase6; /* only last name */
446
447 for (q = name, lfirst = 0; *q && *q != ' '; q++)
448 if (*q != TAB)
449 lfirst++;
450
451 /* q points at space after first name */
452
453 for (p = q, lmiddle = 0; p < r;) {
454 /* find next middle name */
455 while (p < r && (*p == ' ' || *p == TAB))
456 p++;
457
458 if (p >= r)
459 break; /* found last name */
460
461 p++; /* skip first char of middle name */
462 for (; *p != ' '; p++) {/* remove rest */
463 if (*p == TAB)
464 continue;
465 *p = TAB;
466 lname--;
467 }
468 lmiddle += 2; /* initial + space */
469 }
470
471 if (lname < length)
472 goto name_ok;
473
474 /*
475 * If removing middle names is not enough, but reducing first name
476 * instead is, do it that way
477 */
478
479 if (lname - lmiddle >= length && lname - lfirst + 1 < length)
480 goto phase4;
481
482
483 /* Phase 3: Remove middle names */
484
485 for (p = q; p < r; p++) {
486 if (*p == TAB)
487 continue;
488 if (*p == ' ')
489 continue;
490 *p = TAB;
491 lname -= 2;
492 }
493
494 if (lname < length)
495 goto name_ok;
496
497
498 /* Phase 4: Reduce first name */
499
500 phase4:
501 for (p = name + 1; p < q; p++) {
502 if (*p == TAB)
503 continue;
504 if (*p == ' ')
505 continue;
506 if (*p == '-' && (p + 1) < q) {
507 p++;
508 continue;
509 }
510 *p = TAB;
511 lname--;
512 }
513
514 if (lname < length)
515 goto name_ok;
516
517 /* Phase 5: Remove first name */
518
519 name = r + 1;
520 lname--;
521
522 if (lname < length)
523 goto name_ok;
524
525 /* Phase 6: Cut last name */
526 phase6:
527 goto name_ok;
528
529 name_ok:
530
531 q = dest;
532 maxq = q + length;
533
534 drop_space = 1;
535
536 for (p = name; *p && q < maxq; p++) {
537 if (*p == TAB)
538 continue;
539
540 if (*p == ' ') {
541 if (!drop_space) {
542 drop_space = 1;
543 *q++ = ' ';
544 }
545 continue;
546 }
547 drop_space = 0;
548 *q++ = *p;
549 }
550
551 *q = NUL;
552
553 return strlen(dest);
554 }
555
556 #ifdef NAME_TEST
main()557 main()
558 {
559 char in[512], out[512];
560
561 while (fgets(in, sizeof(in), stdin)) {
562 printf("'%s' -> (%d) ", in, pack_name(out, in, Name_Length));
563 puts(out);
564 }
565 exit(0);
566 }
567
568 #endif
569