#include #include #include #include #include #include #include #include #define NL '\n' #define NUL '\0' #define HT '\t' static void fserr(char const * fn, char const * op) { char const * err = strerror(errno); fprintf(stderr, "fs error %u (%s) %s on %s\n", errno, err, op, fn); exit(EXIT_FAILURE); } static inline char * detab(char * line) { static union { unsigned long spaces; unsigned char sp[sizeof(unsigned long)]; } const u = { .sp = { ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ' } }; int tab_ct = 0; unsigned long * lp = (void *)line; while (*lp == u.spaces) { tab_ct++; lp++; } line = (char *)lp; // Trim trailing white space { char * eol = line + strlen(line); while ((eol > line) && isspace((unsigned)eol[-1])) eol--; *(eol++) = NL; *eol = NUL; if (eol == line + 1) return line; // blank line } // insert "tab_ct" tab characters before the point where "line" points // while (tab_ct-- > 0) *(--line) = HT; return line; } static inline void fix_file(char const * fname) { char line[0x2000]; // any line over 8K deserves being mangled. FILE * ifp = fopen(fname, "r"); if (ifp == NULL) fserr("open-read", fname); char tpl[] = "fix-tabs-XXXXXX"; int fd = mkstemp(tpl); if (fd < 0) fserr("mkstemp", tpl); FILE * ofp = fdopen(fd, "w"); if (ofp == NULL) fserr("open-write", tpl); for (;; ) { char * p = fgets(line, sizeof(line), ifp); if (p == NULL) break; fputs(detab(line), ofp); } { struct stat sb; if (fstat(fileno(ifp), &sb) == 0) fchmod(fd, sb.st_mode); } fclose(ifp); fclose(ofp); unlink(fname); rename(tpl, fname); } int main(int argc, char ** argv) { while (--argc > 0) fix_file(*(++argv)); }