home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Source Code 1993 July / THE_SOURCE_CODE_CD_ROM.iso / bsd_srcs / usr.bin / tr / str.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-03-04  |  7.3 KB  |  343 lines

  1. /*-
  2.  * Copyright (c) 1991 The Regents of the University of California.
  3.  * All rights reserved.
  4.  *
  5.  * Redistribution and use in source and binary forms, with or without
  6.  * modification, are permitted provided that the following conditions
  7.  * are met:
  8.  * 1. Redistributions of source code must retain the above copyright
  9.  *    notice, this list of conditions and the following disclaimer.
  10.  * 2. Redistributions in binary form must reproduce the above copyright
  11.  *    notice, this list of conditions and the following disclaimer in the
  12.  *    documentation and/or other materials provided with the distribution.
  13.  * 3. All advertising materials mentioning features or use of this software
  14.  *    must display the following acknowledgement:
  15.  *    This product includes software developed by the University of
  16.  *    California, Berkeley and its contributors.
  17.  * 4. Neither the name of the University nor the names of its contributors
  18.  *    may be used to endorse or promote products derived from this software
  19.  *    without specific prior written permission.
  20.  *
  21.  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
  22.  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  23.  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  24.  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
  25.  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
  26.  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
  27.  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  28.  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  29.  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
  30.  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
  31.  * SUCH DAMAGE.
  32.  */
  33.  
  34. #ifndef lint
  35. static char sccsid[] = "@(#)str.c    5.9 (Berkeley) 3/4/93";
  36. #endif /* not lint */
  37.  
  38. #include <sys/cdefs.h>
  39. #include <sys/types.h>
  40.  
  41. #include <errno.h>
  42. #include <stddef.h>
  43. #include <stdio.h>
  44. #include <stdlib.h>
  45. #include <string.h>
  46.  
  47. #include "extern.h"
  48.  
  49. static int    backslash __P((STR *));
  50. static int    bracket __P((STR *));
  51. static int    c_class __P((const void *, const void *));
  52. static void    genclass __P((STR *));
  53. static void    genequiv __P((STR *));
  54. static int    genrange __P((STR *));
  55. static void    genseq __P((STR *));
  56.  
  57. int
  58. next(s)
  59.     register STR *s;
  60. {
  61.     register int ch;
  62.  
  63.     switch (s->state) {
  64.     case EOS:
  65.         return (0);
  66.     case INFINITE:
  67.         return (1);
  68.     case NORMAL:
  69.         switch (ch = *s->str) {
  70.         case '\0':
  71.             s->state = EOS;
  72.             return (0);
  73.         case '\\':
  74.             s->lastch = backslash(s);
  75.             break;
  76.         case '[':
  77.             if (bracket(s))
  78.                 return (next(s));
  79.             /* FALLTHROUGH */
  80.         default:
  81.             ++s->str;
  82.             s->lastch = ch;
  83.             break;
  84.         }
  85.  
  86.         /* We can start a range at any time. */
  87.         if (s->str[0] == '-' && genrange(s))
  88.             return (next(s));
  89.         return (1);
  90.     case RANGE:
  91.         if (s->cnt-- == 0) {
  92.             s->state = NORMAL;
  93.             return (next(s));
  94.         }
  95.         ++s->lastch;
  96.         return (1);
  97.     case SEQUENCE:
  98.         if (s->cnt-- == 0) {
  99.             s->state = NORMAL;
  100.             return (next(s));
  101.         }
  102.         return (1);
  103.     case SET:
  104.         if ((s->lastch = s->set[s->cnt++]) == OOBCH) {
  105.             s->state = NORMAL;
  106.             return (next(s));
  107.         }
  108.         return (1);
  109.     }
  110.     /* NOTREACHED */
  111. }
  112.  
  113. static int
  114. bracket(s)
  115.     register STR *s;
  116. {
  117.     register char *p;
  118.  
  119.     switch (s->str[1]) {
  120.     case ':':                /* "[:class:]" */
  121.         if ((p = strstr(s->str + 2, ":]")) == NULL)
  122.             return (0);
  123.         *p = '\0';
  124.         s->str += 2;
  125.         genclass(s);
  126.         s->str = p + 2;
  127.         return (1);
  128.     case '=':                /* "[=equiv=]" */
  129.         if ((p = strstr(s->str + 2, "=]")) == NULL)
  130.             return (0);
  131.         s->str += 2;
  132.         genequiv(s);
  133.         return (1);
  134.     default:                /* "[\###*n]" or "[#*n]" */
  135.         if ((p = strpbrk(s->str + 2, "*]")) == NULL)
  136.             return (0);
  137.         if (p[0] != '*' || index(p, ']') == NULL)
  138.             return (0);
  139.         s->str += 1;
  140.         genseq(s);
  141.         return (1);
  142.     }
  143.     /* NOTREACHED */
  144. }
  145.  
  146. int isalnum __P((int)),
  147.     isalpha __P((int)),
  148.     isblank __P((int)),
  149.     isspace __P((int)),
  150.     iscntrl __P((int)),
  151.     isdigit __P((int)),
  152.     isgraph __P((int)),
  153.     islower __P((int)),
  154.     isprint __P((int)),
  155.     ispunct __P((int)),
  156.     isupper __P((int)),
  157.     isxdigit __P((int));
  158.  
  159. typedef struct {
  160.     char *name;
  161.     int (*func) __P((int));
  162.     int *set;
  163. } CLASS;
  164.  
  165. static CLASS classes[] = {
  166.     { "alnum",  isalnum,  },
  167.     { "alpha",  isalpha,  },
  168.     { "blank",  isblank,  },
  169.     { "cntrl",  iscntrl,  },
  170.     { "digit",  isdigit,  },
  171.     { "graph",  isgraph,  },
  172.     { "lower",  islower,  },
  173.     { "print",  isupper,  },
  174.     { "punct",  ispunct,  },
  175.     { "space",  isspace,  },
  176.     { "upper",  isupper,  },
  177.     { "xdigit", isxdigit, },
  178. };
  179.  
  180. static void
  181. genclass(s)
  182.     STR *s;
  183. {
  184.     register int cnt, (*func) __P((int));
  185.     CLASS *cp, tmp;
  186.     int *p;
  187.  
  188.     tmp.name = s->str;
  189.     if ((cp = (CLASS *)bsearch(&tmp, classes, sizeof(classes) /
  190.         sizeof(CLASS), sizeof(CLASS), c_class)) == NULL)
  191.         err("unknown class %s", s->str);
  192.  
  193.     if ((cp->set = p = malloc((NCHARS + 1) * sizeof(int))) == NULL)
  194.         err("%s", strerror(errno));
  195.     bzero(p, (NCHARS + 1) * sizeof(int));
  196.     for (cnt = 0, func = cp->func; cnt < NCHARS; ++cnt)
  197.         if ((func)(cnt))
  198.             *p++ = cnt;
  199.     *p = OOBCH;
  200.  
  201.     s->cnt = 0;
  202.     s->state = SET;
  203.     s->set = cp->set;
  204. }
  205.  
  206. static int
  207. c_class(a, b)
  208.     const void *a, *b;
  209. {
  210.     return (strcmp(((CLASS *)a)->name, ((CLASS *)b)->name));
  211. }
  212.  
  213. /*
  214.  * English doesn't have any equivalence classes, so for now
  215.  * we just syntax check and grab the character.
  216.  */
  217. static void
  218. genequiv(s)
  219.     STR *s;
  220. {
  221.     if (*s->str == '\\') {
  222.         s->equiv[0] = backslash(s);
  223.         if (*s->str != '=')
  224.             err("misplaced equivalence equals sign");
  225.     } else {
  226.         s->equiv[0] = s->str[0];
  227.         if (s->str[1] != '=')
  228.             err("misplaced equivalence equals sign");
  229.     }
  230.     s->str += 2;
  231.     s->cnt = 0;
  232.     s->state = SET;
  233.     s->set = s->equiv;
  234. }
  235.  
  236. static int
  237. genrange(s)
  238.     STR *s;
  239. {
  240.     int stopval;
  241.     char *savestart;
  242.  
  243.     savestart = s->str;
  244.     stopval = *++s->str == '\\' ? backslash(s) : *s->str;
  245.     if (stopval < s->lastch) {
  246.         s->str = savestart;
  247.         return (0);
  248.     }
  249.     s->cnt = stopval - s->lastch + 1;
  250.     s->state = RANGE;
  251.     --s->lastch;
  252.     return (1);
  253. }
  254.  
  255. static void
  256. genseq(s)
  257.     STR *s;
  258. {
  259.     char *ep;
  260.  
  261.     if (s->which == STRING1)
  262.         err("sequences only valid in string2");
  263.  
  264.     if (*s->str == '\\')
  265.         s->lastch = backslash(s);
  266.     else
  267.         s->lastch = *s->str++;
  268.     if (*s->str != '*')
  269.         err("misplaced sequence asterisk");
  270.  
  271.     switch (*++s->str) {
  272.     case '\\':
  273.         s->cnt = backslash(s);
  274.         break;
  275.     case ']':
  276.         s->cnt = 0;
  277.         ++s->str;
  278.         break;
  279.     default:
  280.         if (isdigit(*s->str)) {
  281.             s->cnt = strtol(s->str, &ep, 0);
  282.             if (*ep == ']') {
  283.                 s->str = ep + 1;
  284.                 break;
  285.             }
  286.         }
  287.         err("illegal sequence count");
  288.         /* NOTREACHED */
  289.     }
  290.  
  291.     s->state = s->cnt ? SEQUENCE : INFINITE;
  292. }
  293.  
  294. /* Use the #defines isXXX() here, DON'T use them above. */
  295. #include <ctype.h>
  296.  
  297. /*
  298.  * Translate \??? into a character.  Up to 3 octal digits, if no digits either
  299.  * an escape code or a literal character.
  300.  */
  301. static int
  302. backslash(s)
  303.     register STR *s;
  304. {
  305.     register int ch, cnt, val;
  306.  
  307.     for (cnt = val = 0;;) {
  308.         ch = *++s->str;
  309.         if (!isascii(ch) || !isdigit(ch))
  310.             break;
  311.         val = val * 8 + ch - '0';
  312.         if (++cnt == 3) {
  313.             ++s->str;
  314.             break;
  315.         }
  316.     }
  317.     if (cnt)
  318.         return (val);
  319.     if (ch != '\0')
  320.         ++s->str;
  321.     switch (ch) {
  322.         case 'a':            /* escape characters */
  323.             return ('\7');
  324.         case 'b':
  325.             return ('\b');
  326.         case 'f':
  327.             return ('\f');
  328.         case 'n':
  329.             return ('\n');
  330.         case 'r':
  331.             return ('\r');
  332.         case 't':
  333.             return ('\t');
  334.         case 'v':
  335.             return ('\13');
  336.         case '\0':            /*  \" -> \ */
  337.             s->state = EOS;
  338.             return ('\\');
  339.         default:            /* \x" -> x */
  340.             return (ch);
  341.     }
  342. }
  343.