/* * In-place file filter */ /* To do: inplace old data write test */ #include #include #include #include #include #ifndef _WIN32 # include # include # include #else typedef signed long ssize_t; # include # include #define ftruncate( fd, olen ) _chsize(fd, olen) #endif #include #include #ifndef O_BINARY # ifdef _O_BINARY # define O_BINARY _O_BINARY # else # define O_BINARY 0 # endif #endif int verbose = 0; int Makefile = 0; /* * Transform Tabulators to Spaces using 'tabsize' * Remove Ctrl-M, Spaces and Tabulators at the end of lines * Stop file interpreting at Ctrl-Z * Remove multiple linefeeds at the end of a file */ size_t convert ( char* dst, const char* src, ssize_t ilen, unsigned int tabsize ) { char* d; const char* srce; unsigned int col; unsigned int n; for ( srce = src + ilen, d = dst, col = 0; src < srce; src++ ) { switch ( *src ) { case '\x1A': srce = src; case '\n': while ( d > dst && d[-1] == '\r' ) d--; while ( d > dst && d[-1] == ' ' ) d--; *d++ = '\n'; col = 0; if (Makefile) { if ( src[1] == ' ' || src[1] == '\t' ) { while ( src[1] == ' ' || src[1] == '\t' ) src++; *d++ = '\t'; } } break; case '\t': n = tabsize - col % tabsize; memset ( d, ' ', n ); d += n; col += n; break; default: *d++ = *src; col++; break; } } *d++ = '\n'; while ( d > dst && d[-1] == '\n' && d[-2] == '\n' ) d--; if ( d-1 == dst && d[-1] == '\n' ) d--; return d - dst; } /* * Transform a file in-place. To avoid data loss, it uses atomic overwrite of data. * new file < old file: * - rewind * - write all new data with one atomic write * - cut size to new size * new file == old file: * - rewind * - write all new data with one atomic write * new file > old file: * - seek to end * - append data, if it fails, cut size down to old size and exit * - rewind * - write all new data with one atomic write */ int process ( const char* filename, unsigned int tabsize ) { static char input [4 * 1024 * 1024]; static char output [4 * 1024 * 1024]; ssize_t ilen; ssize_t olen; ssize_t owrite; int fd; int ret = 0; const char* msg = NULL; fd = open ( filename, O_RDWR | O_BINARY ); if ( fd < 0 ) { msg = "File can't be opened in Read/Write mode"; goto end; } ilen = read ( fd, input, sizeof input ); olen = convert ( output, input, ilen, tabsize ); if ( ilen >= sizeof input || olen >= sizeof output ) { msg = "File is too large for this convert program"; goto end; } if ( olen < ilen ) { // smaller if ( 0 != lseek ( fd, 0L, SEEK_SET ) ) { msg = "Lseek failed. File not modified"; goto end; } owrite = write ( fd, output, olen ); // atomic overwrite if ( owrite <= 0 ) { msg = "Writing new file failed"; goto end; } if ( owrite != olen ) { msg = "Writing new file incomplete. *** DATA LOSS *** "; goto end; } ftruncate ( fd, olen ); if ( verbose ) fprintf ( stderr, "Converted '%s'\n", filename ); ret = 1; } else if ( olen > ilen ) { // larger if ( ilen != lseek ( fd, ilen, SEEK_SET ) ) { msg = "Lseek failed. File not modified"; goto end; } owrite = write ( fd, output + ilen, olen - ilen ); // append if ( owrite <= 0 ) { msg = "Writing new file failed"; goto end; } if ( owrite != olen - ilen ) { ftruncate ( fd, ilen ); msg = "Writing new file incomplete"; goto end; } if ( 0 != lseek ( fd, 0L, SEEK_SET ) ) { msg = "Rewind failed. File not modified"; goto end; } owrite = write ( fd, output, olen ); if ( owrite <= 0 ) { msg = "Writing new file failed"; goto end; } if ( owrite != olen ) { msg = "Writing new file incomplete. *** DATA LOSS *** "; goto end; } if ( verbose ) fprintf ( stderr, "Converted '%s'\n", filename ); ret = 1; } else if ( 0 != memcmp (input, output, ilen) ) { if ( 0 != lseek ( fd, 0L, SEEK_SET ) ) { msg = "Lseek failed. File not modified"; goto end; } owrite = write ( fd, output, olen ); if ( owrite <= 0 ) { msg = "Writing new file failed"; goto end; } if ( olen != owrite ) { msg = "Writing new file incomplete. *** DATA LOSS *** "; goto end; } if ( verbose ) fprintf ( stderr, "Converted '%s'\n", filename ); ret = 1; } end: if ( fd >= 0 ) close (fd); if ( msg != NULL ) { fprintf ( stderr, msg ); fprintf ( stderr, ": %s (%s)\n", filename, strerror (errno) ); } return ret; } /* * determine tab size (default: 8) * do converting file for file */ void mysetargv ( int* argc, char*** argv, const char** extentions ); int complete_read ( int fd, void* ptr, size_t len ) { return read ( fd, ptr, len ); } int main ( int argc, char** argv ) { unsigned int tabsize = 8; unsigned int changed = 0; static const char* extentions [] = { ".c", ".cpp", ".h", ".hpp", ".htm", ".html", ".nas", ".inc", ".s", NULL }; if ( argv[1] != NULL && argv[1][0] == '-' && argv[1][1] == 'v' && argv[1][2] == '\0' ) { verbose = 1; ++argv; argc--; } if ( argv[1] != NULL && argv[1][0] == '-' && (unsigned int)(argv[1][1]-'0') < 10u ) { tabsize = atoi ( *++argv + 1 ); argc--; } else if ( argv[1] != NULL && argv[1][0] == '+' && (unsigned int)(argv[1][1]-'0') < 10u ) { Makefile = 1; tabsize = atoi ( *++argv + 1 ); argc--; } if ( argv[1] == NULL ) { fprintf ( stderr, "usage: Remove.tab [-v] [-tabsize] file [file...]\n" ); fprintf ( stderr, " Remove.tab [-v] +tabsize file [file...] for Makefile\n" ); return 1; } mysetargv ( &argc, &argv, extentions ); if ( verbose ) fprintf ( stderr, "Tabsize is %u.\n", tabsize ); while ( *++argv ) changed += process ( *argv, tabsize ); if ( changed ) fprintf ( stderr, "%u files modified.\n", changed ); return 0; } /* end of Remove.tab.c */