#include "SarApp.h" #include #include #include SarApp::SarApp() : caseSensitive(true) , overwriteFiles(false) , showLines(false) , showHelp(false) , unknownParameter('\0') , fileCount(0) , searchText(NULL) , replacementText(NULL) , badReplacementParameter(NULL) , currentFilename(NULL) { } int SarApp::run(int argc, char *argv[]) { // Extract each flag from parameters processParams(argc, argv); // If asked for help, show it if ( showHelp ) return printHelp(); // Now validate all parameters are OK int result = validateParams(); if ( result ) return result; // If no files were provided, trim standard input, else process each file if ( fileCount == 0 ) sarFile(stdin, stdout); else while ( --argc > 0) sarFile( *++argv ); // Done return 0; } void SarApp::processParams(int argc, char *argv[]) { // sar [-ilW] search-text [-r replacement-text] [FILES] while ( --argc > 0 ) { char* flags = *++argv; if ( *flags == '-' ) { if ( strcmp(flags, "--help") == 0 ) showHelp = true; else for ( ++flags; *flags; ++flags) switch ( *flags ) { case 'i': caseSensitive = false; break; case 'l': showLines = true; break; case 'W': overwriteFiles = true; break; case 'r': if ( argc > 1 ) { if ( **(argv + 1) != '-' ) replacementText = *++argv; else badReplacementParameter = *++argv; --argc; } break; default: unknownParameter = *flags; break; } // switch } // if else { if ( ! searchText ) searchText = flags; else ++fileCount; } } } int SarApp::validateParams() { // Do not continue with unknown parameters if ( unknownParameter ) { fprintf(stderr, "sar: -%c unknown option\n", unknownParameter ); return 2; } // Search text is mandatory if ( ! searchText ) { fprintf(stderr, "sar: no search-text was given\n"); return 3; } // When -r was specified, a valid replacement must be provided if ( badReplacementParameter ) { fprintf(stderr, "sar: invalid replacement text: \"%s\"\n", badReplacementParameter); return 4; } // If -W flag was provided and no -r replacement text, usage error if ( overwriteFiles && ! replacementText ) { fprintf(stderr, "sar: -W switch and no -r replacement text provided\n"); return 5; } // If -r was given with an invalid replacement text, report it if ( overwriteFiles && badReplacementParameter ) { fprintf(stderr, "sar: invalid replacement text: \"-r %s\"\n", badReplacementParameter); return 6; } // If -W flag was provided and no files, usage error if ( overwriteFiles && fileCount == 0 ) { fprintf(stderr, "sar: -W switch and no files were specified\n"); return 7; } // Parameters seem OK return 0; } int SarApp::printHelp() { const char* const help = "sar v1.0 [2012-sep-23] Jeisson Hidalgo-Cespedes . CC BY-SA 3.0\n" "Usage: sar [-ilW] search-text [-r replacement-text] [FILES]\n" "Prints each line of FILES where search-text is found. Each occurrence of search-text\n" "will be replaced by replacement-text if -r option is provided. Options:\n" "\n" " -i ignores case sensitivity\n" " -l prints filenames and line numbers\n" " -r replaces each occurrence of search-text by replace-text\n" " -W overwrites each original FILE, requires -r\n"; printf("%s", help); return 0; } bool SarApp::sarFile(const char *filename) { if ( *filename == '-' ) return false; // The first filename is actually the search text, ignore it if ( ! currentFilename ) { currentFilename = filename; return false; } // Store the filename for future use currentFilename = filename; FILE* source = fopen(filename, "r"); if ( ! source ) { fprintf(stderr, "sar: could not open %s\n", filename); return false; } FILE* target = stdout; char target_filename[ strlen(filename) + strlen(".tmp") + 1 ]; if ( overwriteFiles ) { // Concatenate ".tmp" to source filename strcpy(target_filename, filename); strcat(target_filename, ".tmp"); target = fopen(target_filename, "w"); if ( ! target ) { fprintf(stderr, "sar: could not create %s\n", target_filename); fclose(source); return false; } } bool result = sarFile(source, target); fclose(target); fclose(source); // Replace original file for temporary file on success, remove temporary on error if ( overwriteFiles ) { if ( result ) { remove(filename); rename(target_filename, filename); } else { fprintf(stderr, "sar: error writing to %s\n", target_filename); remove(target_filename); } } return result; } bool SarApp::sarFile(FILE *source, FILE *target) { // Read one line a time in source file, and write it with replacements to target file size_t lineNumber = 0; char buffer[BUFFER_SIZE]; while ( fgets(buffer, BUFFER_SIZE, source) ) if ( ! sarLine(buffer, target, ++lineNumber) ) return false; return true; } bool SarApp::sarLine(const char *const line, FILE *target, size_t lineNumber) { // Move in the original line char by char trying to find search_text from that char for ( const char* currentChar = line; *currentChar; ++currentChar) { const char *s = searchText, *l = currentChar; for ( ; *s && equals(*s, *l); ++s, ++l ) ; // If all chars of search_text were found in current position of line // write replacement_text to the target file and ignore those search_text characteres if ( *s == '\0' ) { if ( overwriteFiles ) { if ( fputs(replacementText, target) == EOF ) return false; } else return printOccurrence(line, target, lineNumber); currentChar = l - 1; // should be line = l, but ++line in for will move it one more char } else { // No search_text is found from current position of line, write this char to target // file and try with next char of lines if ( overwriteFiles && fputc(*currentChar, target) == EOF ) return false; } } return true; } bool SarApp::equals(char ch1, char ch2) const { return caseSensitive ? ch1 == ch2 : tolower(ch1) == tolower(ch2); } bool SarApp::printOccurrence(const char *const line, FILE *target, size_t lineNumber) { if ( showLines ) fprintf(target, "%s:%lu:", currentFilename, lineNumber); fprintf(target, "%s", line); return true; }