/* TR -- Character translation utility. * SYNOPSIS: tr [-cds] infile outfile * COMPILATION: cc tr * LINKAGE: l2 tr * * Version 1.1 * 09/08/86 * James Pritchett * */ #include /* #define DEBUG 1 */ #define MAXPAT 40 /* Max. size of input strings */ #define STDERR 4 int trans[256]; /* Table of translations */ int copt,dopt,sopt; /* Option flags */ char inbuff[BUFSIZ], outbuff[BUFSIZ]; /* I/O buffers */ main(argc,argv) int argc; char **argv; { char c, next; char oldc[MAXPAT], newc[MAXPAT]; /* Where input strings are kept */ int i; /* * Initializations and messages. */ puts("TR -- Character translation utility\n"); puts("Version 1.1, 09/08/86\n"); if ((i = getopts(argv)) != (argc-2)) usage(); if (fopen(argv[i++],inbuff) == ERROR) error("Can't open input file"); if (fcreat(argv[i],outbuff) == ERROR) error("Can't create output file"); /* Get the two strings */ puts("Original characters: "); if (!getline(oldc,MAXPAT)) exit(); if (!dopt && !sopt) { /* If -d or -s, no 2nd string needed */ puts("New characters: "); if (!getline(newc,MAXPAT)) exit(); } /* Set up the trans[] array from this info */ settrans(oldc,newc); /* * Do the translations by reading standard input one character * at a time. Output is sent to standard output. */ while (TRUE) { c = getc(inbuff); if (c == CPMEOF) /* EOF marker */ break; /* If there is a translation for this char, handle it */ else if (trans[c]) { if (dopt && !copt) /* Delete option given */ continue; else if (sopt) { /* Squeeze option given */ while ((next = getc(inbuff)) == c) ; ungetc(next,inbuff); } else if (!dopt) /* Simple translation */ c = trans[c]; } /* If no translation and -cd given, skip the char */ else if (copt && dopt) continue; /* All translations/deletions done. Output the char. */ putc(c,outbuff); } /* Translation complete. Close files */ putc(CPMEOF,outbuff); fclose(outbuff); } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* getopts(): Set any option flags. Bad options ignored. If * -c option set without -d, exits program. * Returns index of first non-option argument. */ int getopts(argv) char **argv; { int i; /* Index into argv[] */ char *argp; /* Pointer into argument strings */ /* * Process each argument. Options may be combined into one arg string, * or may be stated separately. All args not beginning with '-' * are ignored, as are bad options. */ for (i = 1; *(argv[i]) == '-'; i++) { for (argp = ++argv[i]; *argp; argp++) { switch (*argp) { case 'C': copt = TRUE; break; case 'D': dopt = TRUE; break; case 'S': sopt = TRUE; break; default: puts("Bad option ignored\n"); break; } } } /* * The option -c must be associated with the -d option. Also, the * -s option must be the only option given when present. Breaking * either rule aborts program. */ if (copt && !dopt) error("-c option must go with -d"); if (sopt && (dopt || copt)) error("-s option not compatible with -c or -d"); return i; } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ /* settrans(): Set up the array of character translations. * In input strings: * [a-b] = Range of chars a to b * 'c' = Literal character c * '\xx' = Hexadecimal digit xx * Aborts on any errors. */ void settrans(old,new) char *old, *new; /* Input strings */ { int olds[256], news[256]; /* Array of chars to translate */ int i; /* * Convert input string format to list of chars (in order input). */ if ((convstr(old,olds) != convstr(new,news)) && !dopt && !sopt) error("Input string mismatch"); #ifdef DEBUG plists(olds,news); #endif /* * Set positions in trans[] corresponding to chars in olds[] to * show translation to chars in news[]. If -s or -d option * given, set to TRUE. */ for (i = 0; olds[i]; i++) { if (sopt || dopt) trans[olds[i]] = TRUE; else trans[olds[i]] = news[i]; } } /* convstr(): Convert input string format to list of chars. Expand * ranges of chars, handle literals and hex digits. * Returns number of chars in list. */ int convstr(in,out) char *in; int *out; { int first, last; /* Limits of ranges */ int count; count = 0; while (*in) { switch (*in) { case '\'': /* Literal */ in = getliteral(in,out); count++; break; case '[': /* Range */ in = getrange(in,&first,&last); while (first <= last) { *out++ = first++; count++; } out--; break; default: *out = *in++; count++; break; } out++; } *out = 0; return count; } /* getliteral(): Get a literal (either char or hex number). * Returns updated input pointer. * NOTE: Literal cannot evaluate to 00. */ char *getliteral(in,out) char *in; int *out; { int c; c = 0; in++; /* skip over the single quote */ if (*in == '\\') { /* Hex identifier */ for (in++; *in != '\''; in++) { if (!ishex(*in)) error("Bad literal"); c *= 16; c += (tohex(*in)); } } else c = *in++; if (!c) error("Cannot translate NUL"); *out = c; return ++in; } int ishex(c) char c; { if ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') || (c >= 'a' && c <= 'f')) return TRUE; else return FALSE; } int tohex(c) int c; { if (c >= '0' && c <= '9') return c-'0'; else return (c&0x0f)+9; } /* getrange(): Get limits of a range. Returns updated pointer. */ int *getrange(in,first,last) char *in; int *first, *last; { in++; /* skip over bracket */ if (*in == '\'') in = getliteral(in,first); else *first = *in++; if (*in++ != '-') error("Bad range"); if (*in == '\'') in = getliteral(in,last); else *last = *in++; if (*in++ != ']') error("Bad range"); if (*first >= *last) error("Bad range"); return in; } /* error(): Print an error message and die. */ void error(s) char *s; { puts(s); exit(); } /* * usage() -- print usage message */ void usage() { puts("USAGE: tr [-cds] infile outfile\n"); puts("Options:\n"); puts("\t-c = Complement character set\n"); puts("\t-d = Delete character set\n"); puts("\t-s = Squeeze character set\n"); puts("In character sets:\n"); puts("\t[a-b] = Range of chars a to b\n"); puts("\t'c' = Literal character c\n"); puts("\t'\\xx' = Hexadecimal xx\n"); exit(); } #ifdef DEBUG void plists(olds,news) int *olds,*news; { printf("Old chars: "); while (*olds) { printf("%c (= %02x)",*olds,*olds); olds++; } printf("\nNew chars: "); while (*news) { printf("%c (= %02x)",*news,*news); news++; } printf("\n"); } #endif