home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / unix / volume22 / nn6.4 / part03 / pack_name.c
Encoding:
C/C++ Source or Header  |  1990-06-07  |  10.0 KB  |  513 lines

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