Annotation of coherent/d/bin/tr.c, revision 1.1.1.1

1.1       root        1: /*
                      2:  * Tr translates characters from the standard input to the standard output.
                      3:  * It usage is as follows:
                      4:  *     tr [-c] [-d] [-s] [string1] [string2]
                      5:  * In the two strings, in addition to normal characters, one can include
                      6:  *     `x-y'   all characters between `x' and `y'
                      7:  *     \r      ascii carriage return
                      8:  *     \n      ascii line feed
                      9:  *     \b      ascii back space
                     10:  *     \t      ascii horizontal tab
                     11:  *     \f      ascii form feed
                     12:  *     \d, \dd or \ddd
                     13:  *             character with ascii code d (or dd or ddd) in octal
                     14:  *     \x      `x' for any `x' not listed above
                     15:  * In the absence of any options, tr simply converts any character appearing
                     16:  * in string1 to the character in the same position in string2.  If string2
                     17:  * is shorter then string1, then it is extended by replicating the last
                     18:  * character.
                     19:  * The `-c' option simply compliments string1 with respect to the character
                     20:  * set.  The resulting set of characters is used in increasing order.
                     21:  * The `-d' option causes characters in string1 to be deleted rather than
                     22:  * being translated.
                     23:  * The `-s' option causes characters in string2 (or string1 if there is no
                     24:  * string2 and the -d option is not specified) to be compressed on output.
                     25:  * This means that multiple occurances of the same character are compressed
                     26:  * to one occurance.
                     27:  */
                     28: #include <stdio.h>
                     29: #include <sys/mdata.h>
                     30: 
                     31: 
                     32: #define        bool    char                    /* boolean type */
                     33: #define        not     !                       /* logical negation operator */
                     34: #define        and     &&                      /* logical conjunction */
                     35: #define        or      ||                      /* logical disjunction */
                     36: #define        TRUE    (0 == 0)
                     37: #define        FALSE   (not TRUE)
                     38: #define        EOS     '\0'                    /* end-of-string char */
                     39: #define        CSIZE   (1 << NBCHAR)           /* character set size */
                     40: #define        MAXDIG  ((NBCHAR+2) / 3)        /* max digits in character constant */
                     41: 
                     42: 
                     43: /*
                     44:  * List is used to hold a character list from which characters are begin
                     45:  * extrated.  It allows expansion of character ranges, backslash-protected
                     46:  * characters and character ranges.
                     47:  */
                     48: typedef struct list {
                     49:        unsigned char   *l_next,        /* next character from string */
                     50:                        *l_start;       /* start of string */
                     51:        bool            l_inr;          /* iff we are in a-b range */
                     52:        int             l_rnext,        /* next to give if inrange */
                     53:                        l_rhi;          /* maximum to give if inrange */
                     54: }      list;
                     55: 
                     56: 
                     57: bool           cflag,                  /* compliment string 1 */
                     58:                dflag,                  /* delete chars in string 1 */
                     59:                sflag,                  /* remove duplicates in string 2 */
                     60:                delete[CSIZE],          /* set of chars to delete on input */
                     61:                squeeze[CSIZE];         /* set of chars to squeeze on output */
                     62: unsigned char  map[CSIZE];             /* map to apply to characters */
                     63: 
                     64: int            nextchar();             /* get next char of list */
                     65: void           die(),                  /* print error message and exit */
                     66:                usage(),                /* print usage message and exit */
                     67:                scan(),                 /* actually do copy */
                     68:                startlist(),            /* start list from string */
                     69:                makeset(),              /* make set of chars in list */
                     70:                maketrans();            /* make transformation table */
                     71: unsigned char  *cstr();                /* compliment character list */
                     72: char           *alloc(),               /* unfailable malloc */
                     73:                *ralloc();              /* unfailable realloc */
                     74: 
                     75: 
                     76: int
                     77: main(argc, argv)
                     78: int            argc;
                     79: register char  *argv[];
                     80: {
                     81:        register unsigned char  *str1,
                     82:                                *str2;
                     83: 
                     84:        for (str1=*++argv; str1 != NULL  &&  *str1 == '-'; str1=*++argv)
                     85:                while (*++str1 != EOS)
                     86:                        switch (*str1) {
                     87:                        case 'c':
                     88:                                cflag = TRUE;
                     89:                                break;
                     90:                        case 'd':
                     91:                                dflag = TRUE;
                     92:                                break;
                     93:                        case 's':
                     94:                                sflag = TRUE;
                     95:                                break;
                     96:                        default:
                     97:                                usage();
                     98:                        }
                     99:        if (str1 == NULL)
                    100:                usage();
                    101:        if (cflag)
                    102:                str1 = cstr(str1);
                    103:        str2 = *++argv;
                    104:        if (str2 != NULL  &&  *++argv != NULL)
                    105:                usage();
                    106:        if (dflag)
                    107:                if (sflag) {            /* -d and -s */
                    108:                        if (str2 == NULL)
                    109:                                usage();
                    110:                        makeset(str1, delete);
                    111:                        makeset(str2, squeeze);
                    112:                        maketrans(str1, str2, map);
                    113:                } else {                /* -d and no -s */
                    114:                        if (str2 != NULL)
                    115:                                usage();
                    116:                        makeset(str1, delete);
                    117:                        maketrans("", "", map);
                    118:                }
                    119:        else
                    120:                if (sflag)
                    121:                        if (str2 == NULL) {     /* -s, no -d and one string */
                    122:                                maketrans("", "", map);
                    123:                                makeset(str1, squeeze);
                    124:                        } else {                /* -s, no -d and two strings */
                    125:                                maketrans(str1, str2, map);
                    126:                                makeset(str2, squeeze);
                    127:                        }
                    128:                else {                  /* no -s, no -d */
                    129:                        if (str2 == NULL)
                    130:                                usage();
                    131:                        maketrans(str1, str2, map);
                    132:                }
                    133:        scan();
                    134:        return (0);
                    135: }
                    136: 
                    137: 
                    138: /*
                    139:  * Die simply sends an error message to stderr and exits.
                    140:  */
                    141: void
                    142: die(str)
                    143: char   *str;
                    144: {
                    145:        fprintf(stderr, "%r\n", &str);
                    146:        exit(1);
                    147: }
                    148: 
                    149: 
                    150: /*
                    151:  * Usage gives a usage error message and exits.
                    152:  */
                    153: void
                    154: usage()
                    155: {
                    156:        die("usage: tr [-cds] [from_list [to_list]]");
                    157: }
                    158: 
                    159: 
                    160: /*
                    161:  * Scan does the acutal copying.  It deletes any input characters in
                    162:  * the set `delete'.  It then transforms input characters to output
                    163:  * characters useing the mapping `map'.  Finally, it changes multiple
                    164:  * occurances of output characters in the set `squeeze' to single
                    165:  * occurances.
                    166:  */
                    167: void
                    168: scan()
                    169: {
                    170:        register int    ch,
                    171:                        lastch;
                    172: 
                    173:        lastch = EOF;
                    174:        while ((ch=getchar()) != EOF) {
                    175:                if (delete[ch])
                    176:                        continue;
                    177:                ch = map[ch];
                    178:                if (ch == lastch  &&  squeeze[ch])
                    179:                        continue;
                    180:                putchar(ch);
                    181:                lastch = ch;
                    182:        }
                    183: }
                    184: 
                    185: 
                    186: /*
                    187:  * Makeset sets the array of bools `set' (indexed by chars) such that
                    188:  * the i'th entry is TRUE iff character i appears in the character
                    189:  * list `str'.
                    190:  */
                    191: void
                    192: makeset(str, set)
                    193: unsigned char  *str;
                    194: register bool  *set;
                    195: {
                    196:        register bool   *rp;
                    197:        register int    n;
                    198:        list            cl;
                    199: 
                    200:        for (rp=set, n=CSIZE; --n >= 0;)
                    201:                *rp++ = FALSE;
                    202:        startlist(&cl, str);
                    203:        while ((n=nextchar(&cl)) != EOF)
                    204:                set[n] = TRUE;
                    205:        return;
                    206: }
                    207: 
                    208: 
                    209: /*
                    210:  * Maketrans sets the array of chars `map' (indexed by chars) to
                    211:  * the mapping which converts all characters in the character list
                    212:  * `str1' to the corresponding character in the character list `str2'.
                    213:  * If `str2' is short, the last character is duplicated.
                    214:  */
                    215: void
                    216: maketrans(str1, str2, map)
                    217: unsigned char  *str1,
                    218:                *str2,
                    219:                *map;
                    220: {
                    221:        register unsigned char  *rp;
                    222:        register int            n,
                    223:                                m;
                    224:        list                    l1,
                    225:                                l2;
                    226:        bool                    extra;
                    227: 
                    228:        startlist(&l1, str1);
                    229:        startlist(&l2, str2);
                    230:        for (rp=map, n=0; n < CSIZE; ++n)
                    231:                *rp++ = n;
                    232:        rp = map;
                    233:        n = nextchar(&l2);
                    234:        if (n == EOF) {
                    235:                if (nextchar(&l1) != EOF)
                    236:                        die("Second string empty");
                    237:                return;
                    238:        }
                    239:        for (extra=FALSE; (m=nextchar(&l1)) != EOF;) {
                    240:                rp[m] = n;
                    241:                if (not extra) {
                    242:                        m = nextchar(&l2);
                    243:                        if (m != EOF)
                    244:                                n = m;
                    245:                        else
                    246:                                extra = TRUE;
                    247:                }
                    248:        }
                    249:        if (not extra  &&  nextchar(&l2) != EOF)
                    250:                die("Extra characters in second string");
                    251:        return;
                    252: }
                    253: 
                    254: 
                    255: /*
                    256:  * Cstr returns a string which is the compliment of the string `str'.
                    257:  */
                    258: unsigned char  *
                    259: cstr(str)
                    260: unsigned char  *str;
                    261: {
                    262:        register unsigned char  *rp;
                    263:        register bool           *sp;
                    264:        register int            n;
                    265:        bool                    set[CSIZE];
                    266:        unsigned char           *res;
                    267: 
                    268:        makeset(str, set);
                    269:        res = rp = (unsigned char *)alloc(3 + 2 + CSIZE + 1);
                    270:        sp = set;
                    271:        if (not *sp++) {                /* handle EOS specially */
                    272:                *rp++ = '\\';
                    273:                *rp++ = '0';
                    274:                *rp++ = '0';
                    275:                *rp++ = '0';
                    276:        }
                    277:        for (n=0; ++n < CSIZE;)
                    278:                if (not *sp++) {
                    279:                        if (n == '\\'  ||  n == '-')
                    280:                                *rp++ = '\\';
                    281:                        *rp++ = n;
                    282:                }
                    283:        *rp++ = EOS;
                    284:        return ((unsigned char *)ralloc(res, rp - res));
                    285: }
                    286: 
                    287: 
                    288: /*
                    289:  * Startlist sets the list pointed to by `lp' to the string `str'.
                    290:  */
                    291: void
                    292: startlist(lp, str)
                    293: register list  *lp;
                    294: unsigned char  *str;
                    295: {
                    296:        lp->l_next = lp->l_start = str;
                    297:        lp->l_inr = FALSE;
                    298: }
                    299: 
                    300: 
                    301: /*
                    302:  * Nextchar returns the next character from the list pointed to by
                    303:  * `lp'.  This includes backslash protection and character ranges.
                    304:  * When there are no more characters, it returns EOF.
                    305:  */
                    306: int
                    307: nextchar(lp)
                    308: register list  *lp;
                    309: {
                    310:        register int    res;
                    311:        int             getprot();
                    312: 
                    313:        if (lp->l_inr) {
                    314:                res = lp->l_rnext;
                    315:                lp->l_inr = ++lp->l_rnext <= lp->l_rhi;
                    316:                return (res);
                    317:        }
                    318:        res = *lp->l_next++;
                    319:        if (res == EOS)
                    320:                return (EOF);
                    321:        if (res == '\\')
                    322:                res = getprot(lp);
                    323:        if (*lp->l_next != '-')
                    324:                return (res);
                    325:        ++lp->l_next;
                    326:        lp->l_rnext = res;
                    327:        res = *lp->l_next++;
                    328:        if (res == EOS)
                    329:                die("Unexpected end of character list in `%s'", lp->l_start);
                    330:        if (res == '\\')
                    331:                res = getprot(lp);
                    332:        if (lp->l_rnext > res)
                    333:                die("Bad character range in `%s'", lp->l_start);
                    334:        lp->l_rhi = res;
                    335:        res = lp->l_rnext;
                    336:        lp->l_inr = ++lp->l_rnext <= lp->l_rhi;
                    337:        return (res);
                    338: }
                    339: 
                    340: 
                    341: /*
                    342:  * Getprot is used to get a backslash protected character from the
                    343:  * character list pointed to by `lp'.
                    344:  */
                    345: int
                    346: getprot(lp)
                    347: register list  *lp;
                    348: {
                    349:        register unsigned char  ch;
                    350:        register unsigned       n;
                    351:        int                     m;
                    352: 
                    353:        ch = *lp->l_next++;
                    354:        switch (ch) {
                    355:        case EOS:
                    356:                die("Unexpected end of character list in `%s'", lp->l_start);
                    357:        case 'r':
                    358:                return ('\r');
                    359:        case 'b':
                    360:                return ('\b');
                    361:        case 't':
                    362:                return ('\t');
                    363:        case 'n':
                    364:                return ('\n');
                    365:        case 'f':
                    366:                return ('\f');
                    367:        default:
                    368:                break;
                    369:        }
                    370:        if ('0' > ch  ||  ch > '7')
                    371:                return (ch);
                    372:        n = ch - '0';
                    373:        ch = *lp->l_next;
                    374:        for (m=MAXDIG; --m > 0  &&  '0' <= ch  &&  ch <= '7'; ch=*++lp->l_next)
                    375:                n = n*8 + ch-'0';
                    376:        if (n >= CSIZE)
                    377:                die("Illegal character constant in `%s'", lp->l_start);
                    378:        return (n);
                    379: }
                    380: 
                    381: 
                    382: /*
                    383:  * Alloc is simply an interface to malloc which does not return if
                    384:  * there is no space.
                    385:  */
                    386: char   *
                    387: alloc(len)
                    388: unsigned       len;
                    389: {
                    390:        register char   *res;
                    391:        extern char     *malloc();
                    392: 
                    393:        res = malloc(len);
                    394:        if (res == NULL)
                    395:                die("Out of space");
                    396:        return (res);
                    397: }
                    398: 
                    399: 
                    400: /*
                    401:  * Ralloc is simply an interface to realloc which does not return if
                    402:  * there is no space.
                    403:  */
                    404: char   *
                    405: ralloc(cp, len)
                    406: char           *cp;
                    407: unsigned       len;
                    408: {
                    409:        register char   *res;
                    410:        extern char *realloc();
                    411: 
                    412:        res = realloc(cp, len);
                    413:        if (res == NULL)
                    414:                die("Out of space");
                    415:        return (res);
                    416: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.