// ---------------------------------------------------------------------------- // detox.cpp // ---------------------------------------------------------------------------- // A simple utility to change the line ends of a text file to conform to the // line end convention of the current machine. // This can help when you move source files between Windows, Unix and Mac. // ---------------------------------------------------------------------------- // COPYRIGHT: // Copyright (c) 2002 Barrett Davis, Tree Frog Software, All rights reserved. // This source code and the binaries that result may be freely distributed, // used and modified as long as the above copyright notice remains intact. // ---------------------------------------------------------------------------- // WARRANTY: // The author of this work (hereafter referred to as "the author") makes no // warranty of any kind, expressed or implied, including without limitation, // any warranties of merchantability and/or fitness for a particular purpose. // The author shall not be liable for any damages, whether direct, indirect, // special, or consequential arising from a failure of this program to // operate in the manner desired by the user. The author shall not be liable // for any damage to data or property which may be caused directly or // indirectly by use of the program. // // In no event will the author be liable to the user for any damages, // including any lost profits, lost savings, or other incidental or // consequential damages arising out of the use or inability to use the // program, or for any claim by any other party. // ---------------------------------------------------------------------------- // This code is released into the public domain. // Please send me an email if you find this little utility useful or if you // find a bug. // - Regards, Barrett Davis. // Tree Frog Software. // ---------------------------------------------------------------------------- // I have compiled this code on Solaris and Linux with the following: // g++ detox.cpp // ---------------------------------------------------------------------------- // Version 2, 21 Jan 2002. initial version: 1, 4 Jan 2002. // Barrett Davis, shareware@thefrog.com // Tree Frog Software. www.thefrog.com // ---------------------------------------------------------------------------- #include #include #include #include // memset() #include #include #include const int VERSION = 2; const int SUCCESS = 0; const int FAILURE = -1; const char APP_NAME[] = { "detox" }; const char AUTHOR[] = { "Barrett Davis" }; const char EMAIL[] = { "shareware@thefrog.com" }; void description( void ) { // ------------------------------------------------------------------------- // Describe the use of this application. // ------------------------------------------------------------------------- cout << " " << APP_NAME << " version " << VERSION << ", compiled on " __DATE__ "\n" "\n" " This application changes the end of line characters in a text file to\n" " match the end of line convention of the platform that detox is compiled\n" " for.\n" "\n" " An example of it's use:\n" " " << APP_NAME << " test.cpp main.cpp Makefile doc/*.txt *.h\n" "\n" " Please send your comments and suggestions to:\n" " " << AUTHOR << ", " << EMAIL << "\n" "\n"; return; } size_t file_size( int fd ) { // ------------------------------------------------------------------------- // Return the file size in bytes, or 0 on error. // ------------------------------------------------------------------------- struct stat statbuf; memset( &statbuf, 0, sizeof( statbuf )); if( fd < 0 || fstat( fd, &statbuf ) != 0 ) { return 0; // Error, return 0. } return statbuf.st_size; // Return the size of the file in bytes. } unsigned char* read_file( const char *file_name ) { // ------------------------------------------------------------------------- // Open a file and read it's contents into memory. // ------------------------------------------------------------------------- // We use unbuffered IO here because all that we are doing is allocating // our own buffer and reading the entire file into memory. // ------------------------------------------------------------------------- if( file_name == 0 || *file_name == 0 ) { return 0; } int fd = open( file_name, O_RDONLY ); if( fd < 0 ) { cerr << "Error: Unable to open file.\n"; return 0; } const size_t FILE_SIZE = file_size( fd ); if( FILE_SIZE == 0 ) { cerr << "Warning: Empty file.\n"; close( fd ); return 0; } const size_t BUFFER_SIZE = FILE_SIZE + 1; // Make room for trailing null. unsigned char *data = 0; try { data = new unsigned char[ BUFFER_SIZE ]; } catch( ... ) { data = 0; } if( data == 0 ) { cerr << "Error: unable to allocate " << BUFFER_SIZE << " bytes.\n"; close( fd ); return 0; } int rc = read( fd, data, FILE_SIZE ); close( fd ); if( rc < 0 ) { cerr << "Error: unable to read from the file.\n"; delete[] data; return 0; } if( rc != FILE_SIZE ) { cerr << "Error: read " << rc << " bytes, expected " << FILE_SIZE << "\n"; delete[] data; return 0; } data[ FILE_SIZE ] = 0; // Null terminate. return data; } inline bool line_end( const unsigned char value ) { // ------------------------------------------------------------------------- // Return true if the value is a line end character. // ------------------------------------------------------------------------- const unsigned char CR = 0x0D; const unsigned char LF = 0x0A; return value == CR || value == LF; } bool detox_file( const char *file_name, const unsigned char *data ) { // ------------------------------------------------------------------------- // Open the file for writing, using buffered I/O. // Write out the data, byte by byte, converting OxOD and 0x0A as needed. // We make sure that we end with a new line, as well. // ------------------------------------------------------------------------- if( file_name == 0 || *file_name == 0 || data == 0 ) { cerr << "Error: bad input parameter in detox_file()\n"; return false; } if( *data == 0 ) { return true; // NOP - zero length file. } ofstream stream( file_name ); if( stream.bad()) { cerr << "Error: unable to open file for write.\n"; return false; } unsigned char last_character = 0; while( *data != 0 ) { if( line_end( *data )) { stream << "\n"; if( line_end( *(data+1)) && *(data+1) != *data ) { data++; // Skip second char of 0x0D 0x0A pair. } } else { stream << *data; // Normal bulk characters. } last_character = *data++; // Remember the last character. } if( !line_end( last_character )) { cout << "note: appending a final new line. "; stream << "\n"; // Make sure that we end with a line end. } stream.close(); return true; } bool process( const char *file_name ) { // ------------------------------------------------------------------------- // Process each file. // ------------------------------------------------------------------------- if( file_name == 0 || *file_name == 0 ) { return true; // NOP } cout << "processing " << file_name << " - "; const unsigned char *data = read_file( file_name ); if( data == 0 ) { return false; } bool result = detox_file( file_name, data ); delete[] data; if( result ) { cout << "complete\n"; } return result; } int main( const int argc, const char *argv[] ) { // ------------------------------------------------------------------------- // Application main entry point. // Returns 0 for success, -1 on error. // ------------------------------------------------------------------------- if( argc < 2 ) { // ---------------------------------------------------------------------- // No file names to operate on, so we show the user information on the // use of this application. // ---------------------------------------------------------------------- description(); return SUCCESS; } if( argv == 0 ) { // ----------------------------------------------------------------------- // Invalid application command line argument. // ----------------------------------------------------------------------- cerr << "Internal error: argv == 0\n"; return FAILURE; } int result = SUCCESS; for( int ii = 1; ii < argc; ii++ ) { // ----------------------------------------------------------------------- // Process each file. // ----------------------------------------------------------------------- if( !process( argv[ ii ] )) { result = FAILURE; } } return result; }