/* * (c) Copyright 1990, Kim Fabricius Storm. All rights reserved. * Copyright (c) 1996-2005 Michael T Pins. All rights reserved. * * Pack sender's name into something sensible, return in packed: * pack_name(packed, name, length) * */ #include #include "config.h" #include "global.h" /* #define NAME_TEST *//* Never define this ! */ #define SEP_DOT 0 /* . */ #define SEP_PERCENT 1 /* % */ #define SEP_SCORE 2 /* _ */ #define SEP_AMPERSAND 3 /* @ */ #define SEP_BANG 4 /* ! */ #define SEP_MAXIMUM 5 #define CL_OK 0x0100 /* letter or digit */ #define CL_SPACE 0x0200 /* cvt to space */ #define CL_IGNORE 0x0400 /* ignore */ #define CL_RANGE(c) 0x0800+c/* space range, end with c */ #define CL_HYPHEN 0x1000 /* convert to - */ #define CL_STOP 0x2000 /* discard rest of name */ #define CL_SEP | 0x4000 + /* address separator */ #define IS_OK(c) (Class[c] & CL_OK) #define IS_SPACE(c) (Class[c] & CL_SPACE) #define IGNORE(c) (c & 0x80 || Class[c] & CL_IGNORE) #define BEGIN_RANGE(c) (Class[c] & CL_RANGE(0)) #define END_RANGE(c) (Class[c] & 0xff) #define IS_HYPHEN(c) (Class[c] & CL_HYPHEN) #define IS_STOP(c) (Class[c] & CL_STOP) #define IS_SEPARATOR(c) (Class[c] & (0 CL_SEP 0)) int old_packname = 0; /* Default to new behavior */ static short Class[128] = { /* NUL */ CL_STOP, /* SOH */ CL_IGNORE, /* STX */ CL_IGNORE, /* ETX */ CL_IGNORE, /* EOT */ CL_IGNORE, /* ENQ */ CL_IGNORE, /* ACK */ CL_IGNORE, /* BEL */ CL_IGNORE, /* BS */ CL_IGNORE, /* TAB */ CL_SPACE, /* NL */ CL_IGNORE, /* VT */ CL_IGNORE, /* FF */ CL_IGNORE, /* CR */ CL_IGNORE, /* SO */ CL_IGNORE, /* SI */ CL_IGNORE, /* DLE */ CL_IGNORE, /* DC1 */ CL_IGNORE, /* DC2 */ CL_IGNORE, /* DC3 */ CL_IGNORE, /* DC4 */ CL_IGNORE, /* NAK */ CL_IGNORE, /* SYN */ CL_IGNORE, /* ETB */ CL_IGNORE, /* CAN */ CL_IGNORE, /* EM */ CL_IGNORE, /* SUB */ CL_IGNORE, /* ESC */ CL_IGNORE, /* FS */ CL_IGNORE, /* GS */ CL_IGNORE, /* RS */ CL_IGNORE, /* US */ CL_IGNORE, /* space */ CL_SPACE, /* ! */ CL_SPACE CL_SEP SEP_BANG, /* " */ CL_RANGE('"'), /* # */ CL_OK, /* $ */ CL_OK, /* % */ CL_OK CL_SEP SEP_PERCENT, /* & */ CL_OK, /* ' */ CL_OK, /* ( */ CL_OK, /* ) */ CL_OK, /* * */ CL_HYPHEN, /* + */ CL_HYPHEN, /* , */ CL_STOP, /* - */ CL_HYPHEN, /* . */ CL_SPACE CL_SEP SEP_DOT, /* / */ CL_OK, /* 0 */ CL_OK, /* 1 */ CL_OK, /* 2 */ CL_OK, /* 3 */ CL_OK, /* 4 */ CL_OK, /* 5 */ CL_OK, /* 6 */ CL_OK, /* 7 */ CL_OK, /* 8 */ CL_OK, /* 9 */ CL_OK, /* : */ CL_IGNORE, /* ; */ CL_STOP, /* < */ CL_IGNORE, /* = */ CL_HYPHEN, /* > */ CL_IGNORE, /* ? */ CL_IGNORE, /* @ */ CL_OK CL_SEP SEP_AMPERSAND, /* A */ CL_OK, /* B */ CL_OK, /* C */ CL_OK, /* D */ CL_OK, /* E */ CL_OK, /* F */ CL_OK, /* G */ CL_OK, /* H */ CL_OK, /* I */ CL_OK, /* J */ CL_OK, /* K */ CL_OK, /* L */ CL_OK, /* M */ CL_OK, /* N */ CL_OK, /* O */ CL_OK, /* P */ CL_OK, /* Q */ CL_OK, /* R */ CL_OK, /* S */ CL_OK, /* T */ CL_OK, /* U */ CL_OK, /* V */ CL_OK, /* W */ CL_OK, /* X */ CL_OK, /* Y */ CL_OK, /* Z */ CL_OK, /* [ */ CL_OK, /* \ */ CL_OK, /* ] */ CL_OK, /* ^ */ CL_IGNORE, /* _ */ CL_SPACE CL_SEP SEP_SCORE, /* ` */ CL_IGNORE, /* a */ CL_OK, /* b */ CL_OK, /* c */ CL_OK, /* d */ CL_OK, /* e */ CL_OK, /* f */ CL_OK, /* g */ CL_OK, /* h */ CL_OK, /* i */ CL_OK, /* j */ CL_OK, /* k */ CL_OK, /* l */ CL_OK, /* m */ CL_OK, /* n */ CL_OK, /* o */ CL_OK, /* p */ CL_OK, /* q */ CL_OK, /* r */ CL_OK, /* s */ CL_OK, /* t */ CL_OK, /* u */ CL_OK, /* v */ CL_OK, /* w */ CL_OK, /* x */ CL_OK, /* y */ CL_OK, /* z */ CL_OK, /* { */ CL_OK, /* | */ CL_OK, /* } */ CL_OK, /* ~ */ CL_HYPHEN, /* DEL */ CL_IGNORE }; int pack_name(char *dest, char *source, int length) { register char *p, *q, *r, c; register int n; register int i; register int sep; register int ignore_braces = 0; register int ignore_comment = 0; register int rfc_addr = 0; register char *name; register int lname; register int drop_space, prev_space; char *maxq; int lfirst, lmiddle, llast; char namebuf[129]; char *separator[SEP_MAXIMUM]; dest[0] = NUL; if (source == NULL || source[0] == NUL) return 0; p = source; if (old_packname == 0) { while ((c = *p++)) { if (c == '<') { ignore_comment = 1; rfc_addr = 1; break; } } } full_scan: p = source, q = namebuf, n = 0; maxq = namebuf + sizeof namebuf - 1; new_partition: for (i = SEP_MAXIMUM; --i >= 0; separator[i] = NULL); while ((c = *p++)) { if (c == '<') { while (q > namebuf && q[-1] == SP) q--; if (q == namebuf) continue; break; } if (c == '(' && ignore_comment) break; if (!rfc_addr) { if (c == ')') { if (--n == 0 && !ignore_braces) break; continue; } } if (IGNORE((int8) c)) continue; if (q == namebuf && IS_SPACE((int8) c)) continue; if (!rfc_addr) { if (c == '(') { if (n++ == 0 && !ignore_braces) { q = namebuf; goto new_partition; } continue; } } if (n > 0) if (ignore_braces || n > 1) continue; if (q >= maxq) break; *q++ = c; if (IS_SEPARATOR((int8) c)) { switch ((sep = (Class[(int8) c] & 0xff))) { case SEP_DOT: if (separator[SEP_AMPERSAND] && q - namebuf <= length) break; continue; case SEP_BANG: if (separator[SEP_AMPERSAND]) continue; break; default: if (separator[sep]) continue; break; } separator[sep] = q - 1; } } *q = NUL; if (namebuf[0] == NUL && !ignore_braces) { ignore_braces = 1; goto full_scan; } if (namebuf[0] == NUL) return 0; name = namebuf; if (name[0] == '"') { name++; if (q[-1] == '"') *--q = NUL; } if (q - name <= length) goto name_ok; /* sorry for the many goto's -- the 3B2 C compiler does not */ /* make correct code for complicated logical expressions!! */ /* not even without -O */ /* We must pack the name to make it fit */ /* Name_of_person%... -> Name_of_person */ if ((r = separator[SEP_PERCENT])) { if (!(q = separator[SEP_SCORE]) || q > r) goto no_percent; if ((q = separator[SEP_AMPERSAND]) && q < r) goto no_percent; if ((q = separator[SEP_BANG]) && q < r) goto no_percent; *r = NUL; goto parse_name; } no_percent: /* name@site.domain -> name@site */ if ((r = separator[SEP_AMPERSAND])) { if ((q = separator[SEP_PERCENT]) && q < r) { *r = NUL; if (r - name <= length) goto name_ok; *q = NUL; if (((p = separator[SEP_BANG]) && p < q) || ((p = strrchr(name, '!')) && p < q)) { name = p + 1; } if (strchr(name, '.')) goto parse_name; goto name_ok; } if ((q = separator[SEP_DOT])) { *q = NUL; goto name_ok; } *r = NUL; if (r - name <= length) goto name_ok; if ((q = separator[SEP_BANG]) && q < r) { name = q + 1; goto name_ok; } #ifdef NOTDEF if (strchr(name, '!') == NULL) goto parse_name; /* take the chance ... */ #endif goto name_ok; /* can't do it any better */ } /* Phase 1: Normalization (remove superfluous characters) */ parse_name: for (p = name, lname = 0, prev_space = 0; (c = *p); p++) { /* if (IGNORE(c)) { *p = TAB; if (p == name) name++; continue; } */ if (IS_OK((int8) c)) { lname++; prev_space = 0; continue; } if (IS_HYPHEN((int8) c)) { if (p == name) { name++; continue; } if (prev_space) { *p = NUL; break; /* strip from " -" */ /* *p = TAB; *//* do not strip from " -" */ } else { *p = '-'; lname++; } continue; } if (BEGIN_RANGE((int8) c)) { if (p == name) { name++; continue; } c = END_RANGE((int8) c); for (q = p + 1; *q && *q != c; q++); if (*q) { if (p[-1] != ' ') lname++; while (p <= q) *p++ = ' '; p--; prev_space++; continue; } c = ' '; } if (IS_SPACE((int8) c)) { *p = ' '; if (p == name) name++; else if (!prev_space) { lname++; prev_space++; } continue; } if (IS_STOP((int8) c)) { *p = NUL; break; } } drop_last_name: while (p > name && (*--p == ' ' || *p == TAB)) *p = NUL; if (lname < length) goto name_ok; /* Phase 2: Reduce middle names */ for (r = p, llast = 0; r > name && *r != ' '; r--) if (*r != TAB) llast++; /* r points to space before last name */ if (strncmp(r, " Jr", 3) == 0 || strncmp(r, " II", 3) == 0) { p = r + 1; lname -= llast; goto drop_last_name; } if (r == name) goto phase6; /* only last name */ for (q = name, lfirst = 0; *q && *q != ' '; q++) if (*q != TAB) lfirst++; /* q points at space after first name */ for (p = q, lmiddle = 0; p < r;) { /* find next middle name */ while (p < r && (*p == ' ' || *p == TAB)) p++; if (p >= r) break; /* found last name */ p++; /* skip first char of middle name */ for (; *p != ' '; p++) {/* remove rest */ if (*p == TAB) continue; *p = TAB; lname--; } lmiddle += 2; /* initial + space */ } if (lname < length) goto name_ok; /* * If removing middle names is not enough, but reducing first name * instead is, do it that way */ if (lname - lmiddle >= length && lname - lfirst + 1 < length) goto phase4; /* Phase 3: Remove middle names */ for (p = q; p < r; p++) { if (*p == TAB) continue; if (*p == ' ') continue; *p = TAB; lname -= 2; } if (lname < length) goto name_ok; /* Phase 4: Reduce first name */ phase4: for (p = name + 1; p < q; p++) { if (*p == TAB) continue; if (*p == ' ') continue; if (*p == '-' && (p + 1) < q) { p++; continue; } *p = TAB; lname--; } if (lname < length) goto name_ok; /* Phase 5: Remove first name */ name = r + 1; lname--; if (lname < length) goto name_ok; /* Phase 6: Cut last name */ phase6: goto name_ok; name_ok: q = dest; maxq = q + length; drop_space = 1; for (p = name; *p && q < maxq; p++) { if (*p == TAB) continue; if (*p == ' ') { if (!drop_space) { drop_space = 1; *q++ = ' '; } continue; } drop_space = 0; *q++ = *p; } *q = NUL; return strlen(dest); } #ifdef NAME_TEST main() { char in[512], out[512]; while (fgets(in, sizeof(in), stdin)) { printf("'%s' -> (%d) ", in, pack_name(out, in, Name_Length)); puts(out); } exit(0); } #endif