|
|
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: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.