#include #include #include #include #include #define steq(x,y) (!strcmp((x),(y))) #define FALSE 0 #define TRUE 1 static char *Argv0; static int Entab = FALSE; static int EntabMargin = 2; static int ReplaceFiles = FALSE; static int TabStop = 8; static const char *CopyName = "cp"; static int IndentOnly = FALSE; static int FinishNewline = FALSE; void detab(FILE *in, FILE *out); void do_error(const char *fmat, ...); void do_help(int man); int main(int argc, char *argv[]) { int i, j; Argv0 = argv[0]; for (i=1; i < argc; i++) { char *tmp; int newTabStop; if (argv[i][0] != '-') break; if (argv[i][1] == '\0') break; if (steq(argv[i]+1, "-")) { ++i; break; } else if (steq(argv[i]+1, "-help") || steq(argv[i]+1, "?") || steq(argv[i]+1, "h")) do_help(0); else if (steq(argv[i]+1, "-man")) do_help(1); else if (steq(argv[i], "-c") || steq(argv[i], "-C")) { if (i >= argc-1) { do_error("Need command with option -c"); } CopyName = argv[++i]; } else if ((newTabStop = strtol(argv[i]+1, &tmp, 10)), (*tmp==0)) { TabStop = newTabStop; if (TabStop <= EntabMargin) EntabMargin = TabStop - 1; } else { for (j=1; argv[i][j]; ++j) { if (argv[i][j] == 'r' || argv[i][j] == 'R') ReplaceFiles = TRUE; else if (argv[i][j] == 'n' || argv[i][j] == 'N') FinishNewline = TRUE; else if (argv[i][j] == 'i' || argv[i][j] == 'I') IndentOnly = TRUE; else if (argv[i][j] == 'e' || argv[i][j] == 'E') { Entab = TRUE; if (isdigit(argv[i][j+1])) { EntabMargin = 0; for ( ; isdigit(argv[i][j+1]); ++j) EntabMargin = 10*EntabMargin+argv[i][j+1]-'0'; } } else do_error("Unrecognized option(s) %s; -h for help", argv[i]); } } } if (i == argc) do_error("No files to process"); for (; i < argc; i++) { FILE *fp; if (steq(argv[i], "-")) { fp = stdin; } else if (0 == (fp = fopen(argv[i], "r"))) { do_error("File does not exist: %s", argv[i]); } if (ReplaceFiles) { FILE *ftmp; char nam[L_tmpnam]; char buf[100+L_tmpnam]; if (0 == (ftmp = fopen(tmpnam(nam), "w"))) { do_error("Could not open temporary file %s for writing", nam); } detab(fp, ftmp); fclose(ftmp); fclose(fp); sprintf(buf, "%s %s %s", CopyName, nam, argv[i]); system(buf); } else { detab(fp, stdout); fclose(fp); } } return EXIT_SUCCESS; } void detab(FILE *in, FILE *out) { int cstart = 0; /* the first space, mod TabStop */ int cstop = 0; /* the current char, mod TabStop */ int spacecount = 0; /* the length of whitespace */ int new_line = TRUE; /* are we still indenting? */ int ch; while ((ch = fgetc(in)) != EOF) { if (ch == '\n') { putc('\n', out); cstart = 0; cstop = 0; spacecount = 0; new_line = TRUE; } else if (ch == '\t') { if (IndentOnly && !new_line) putc('\t', out); else spacecount += (TabStop - (cstop % TabStop)); cstop = TabStop; } else if (ch == ' ') { if (IndentOnly && !new_line) putc(' ', out); else ++spacecount; ++cstop; } else { if (spacecount) { if (Entab && (spacecount > EntabMargin)) { /* use tabs, then spaces */ int offset = TabStop - (cstart % TabStop); while (spacecount >= offset) { putc('\t', out); spacecount -= offset; offset = TabStop; } for ( ; spacecount; --spacecount) putc(' ', out); } else { /* use only spaces */ for ( ; spacecount; --spacecount) putc(' ', out); } } putc(ch, out); cstart = cstop+1; ++cstop; new_line = FALSE; } } if (FinishNewline && cstop != 0) putc('\n', out); } void do_error(const char *fmat, ...) { va_list ap; printf("%s: ", Argv0); va_start(ap, fmat); vprintf(fmat, ap); printf("\n"); va_end(ap); exit(EXIT_FAILURE); } void do_help(int man) { if (man) goto man; man: puts("detab [-?h] [-Ee[#]IiNnRr] [-c cmd] [tabstop] filenames"); puts("Removes tab characters from a text file, replacing them" " with spaces."); puts(" -E: entab files (replacing multiple spaces with tabs)"); puts(" -En (e.g., -e2): do not entab on n or fewer spaces"); puts(" -I: detab/entab only at start of line (indent)"); puts(" -N: make sure each file ends with a newline"); puts(" -R: replace named files with detabbed versions"); puts(" tabstop (e.g., -4, -20): tabstop every n characters"); puts(" -c : specify equivalent of Unix 'cp' (for use with -R)"); puts(" --help: show this message"); puts(" --man: show complete help text"); exit(0); }