home *** CD-ROM | disk | FTP | other *** search
/ Visual Basic Source Code / Visual Basic Source Code.iso / vbsource / vbdatabs / ffind.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  1999-03-26  |  16.8 KB  |  538 lines

  1. // ------------------------------- //
  2. // -------- Start of File -------- //
  3. // ------------------------------- //
  4. // ----------------------------------------------------------- // 
  5. // C++ Source Code File Name: ffind.cpp 
  6. // Compiler Used: MSVC40, DJGPP 2.7.2.1, GCC 2.7.2.1, HP CPP 10.24
  7. // Produced By: gaer@nhc.noaa.gov
  8. // File Creation Date: 03/09/1999 
  9. // Date Last Modified: 03/26/1999
  10. // ----------------------------------------------------------- // 
  11. // ------------- Program description and details ------------- // 
  12. // ----------------------------------------------------------- // 
  13. /*
  14. THIS SOFTWARE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND.
  15. THE ENTIRE RISK OF THE QUALITY AND PERFORMANCE OF THIS SOFTWARE
  16. IS WITH YOU. SHOULD ANY ELEMENT OF THIS SOFTWARE PROVE DEFECTIVE,
  17. YOU WILL ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR, OR
  18. CORRECTION.
  19.   
  20. This program is used to find and replace strings in a file.
  21. */
  22. // ----------------------------------------------------------- // 
  23. #include <stdio.h>
  24. #include <string.h>
  25. #include <stdlib.h>
  26. #include <fstream.h>
  27. #include <iostream.h>
  28. #include "strutil.h"
  29. #include "ustring.h"
  30. #include "dllist.h"
  31.  
  32. // Version number and program name
  33. const double FFindVersionNumber = 1031.102;
  34. const char *ProgramName = "ffind";
  35.  
  36. // Program globals
  37. const int MAX_LINE = 1024;   // Maximum characters per line
  38. char *open_file = 0;         // Name of file currently opened
  39. unsigned num_files = 0;      // Total number of files processed
  40. char *string_to_find = 0;    // String to find in each line
  41. int find_string = 0;         // Find specified string flag
  42. int find_all = 0;            // Find all occurrences
  43. int check_case = 1;          // Case sensitive/insensitive compare flag
  44. unsigned num_matches = 0;    // Total number of matches found
  45. int cat_file = 0;            // Concatenate file flag
  46. char *string_to_replace = 0; // String to replace
  47. char *string_to_insert = 0;  // String to insert
  48. int replacing_string = 0;    // Replace string flag
  49. int num_replaced = 0;        // Number of strings replaced
  50. int replacing_line = 0;      // Replace line flag
  51. int inserting_string = 0;    // Insert line flag
  52. int append_lines = 1;        // Append lines connected with a backslash
  53. int num_processed = 0;       // Number of string processed per file
  54.  
  55. void HelpMessage(const char *program_name, const double version_number)
  56. {
  57.   char vbuffer[255];
  58.   sprintf(vbuffer, "%.3f", version_number);
  59.   cout << endl;
  60.   cout << program_name << " program version "
  61.        << vbuffer  << endl;
  62.   cout << "Usage: " << program_name << " [switches] iofile.txt " << endl; 
  63.   cout << "Switches: " << endl;
  64.   cout << "          -a = Do not append lines connected by a backslash"
  65.        << endl;
  66.   cout << "          -c = Concatenate file to stdout." << endl;
  67.   cout << "          -f = Find first occurrence of specified string: "
  68.        << "-f\"string\"" << endl;
  69.   cout << "          -F = Find all occurrences of specified string: "
  70.        << "-F\"string\"" << endl;
  71.   cout << "          -i = Perform case insensitive compare." << endl;
  72.   cout << endl;
  73.   cout << "          -R -W = Replace all occurrences of a string in a file."
  74.        << endl;
  75.   cout << "          Usage: -R\"string\" -W\"replacement\"" << endl;
  76.   cout << endl;
  77.   cout << "          -L -W = Replace entire line after the specifed string."
  78.        << endl;
  79.   cout << "          Usage: -L\"string\" -W\"replacement\"" << endl;
  80.   cout << endl;
  81.   cout << "          -I -W = Insert a line after the specifed string."
  82.        << endl;
  83.   cout << "          Usage: -I\"string\" -W\"insert\"" << endl;
  84.   cout << endl;
  85.   exit(0);
  86. }
  87.  
  88. int ProcessArgs(char *arg)
  89. {
  90.   switch(arg[1]) {
  91.     case 'a':
  92.       append_lines = 0;
  93.       break;
  94.       
  95.     case 'c':
  96.       cat_file = 1;
  97.       break;
  98.  
  99.     case 'f':                
  100.       if(arg[2] == '\0') {
  101.     cerr << endl;
  102.     cerr << "You must enter a string following the -f option." << endl;
  103.     cerr << "Example usage: " << ProgramName << " -f\"string\"" << endl; 
  104.     cerr << endl;
  105.     return 0;
  106.       }
  107.       find_string = 1;
  108.       find_all = 0;
  109.       replacing_line = 0;  
  110.       replacing_string = 0;  
  111.       inserting_string = 0;
  112.       string_to_find = arg+2;
  113.       break;
  114.  
  115.     case 'F':                
  116.       if(arg[2] == '\0') {
  117.     cerr << endl;
  118.     cerr << "You must enter a string following the -F option." << endl;
  119.     cerr << "Example usage: " << ProgramName << " -F\"string\"" << endl; 
  120.     cerr << endl;
  121.     return 0;
  122.       }
  123.       find_string = 1;
  124.       find_all =1;
  125.       replacing_line = 0;  
  126.       replacing_string = 0;  
  127.       inserting_string = 0;
  128.       string_to_find = arg+2;
  129.       break;
  130.  
  131.     case 'i':
  132.       check_case = 0;
  133.       break;
  134.       
  135.     case 'R':
  136.       if(arg[2] == '\0') {
  137.     cerr << endl;
  138.     cerr << "You must enter a string following the -R option." << endl;
  139.     cerr << "Example usage: " << ProgramName << " -R\"string\"" << endl; 
  140.     cerr << endl;
  141.     return 0;
  142.       }
  143.       string_to_replace = arg+2;
  144.       replacing_string = 1;  
  145.       replacing_line = 0;
  146.       inserting_string = 0;
  147.       find_string = 0;
  148.       find_all = 0;
  149.       break;
  150.  
  151.     case 'L':
  152.       if(arg[2] == '\0') {
  153.     cerr << endl;
  154.     cerr << "You must enter a string following the -L option." << endl;
  155.     cerr << "Example usage: " << ProgramName << " -L\"string\"" << endl; 
  156.     cerr << endl;
  157.     return 0;
  158.       }
  159.       string_to_replace = arg+2;
  160.       replacing_line = 1;  
  161.       replacing_string = 0;
  162.       inserting_string = 0;
  163.       find_string = 0;
  164.       find_all = 0;
  165.       break;
  166.  
  167.     case 'I':
  168.       if(arg[2] == '\0') {
  169.     cerr << endl;
  170.     cerr << "You must enter a string following the -I option." << endl;
  171.     cerr << "Example usage: " << ProgramName << " -I\"string\"" << endl; 
  172.     cerr << endl;
  173.     return 0;
  174.       }
  175.       string_to_replace = arg+2;
  176.       inserting_string = 1;
  177.       replacing_line = 0;  
  178.       replacing_string = 0;  
  179.       find_string = 0;
  180.       find_all = 0;
  181.       break;
  182.  
  183.     case 'W':
  184.       if(arg[2] == '\0') {
  185.     cerr << endl;
  186.     cerr << "You must enter a string following the -W option." << endl;
  187.     cerr << "Example usage: " << ProgramName << " -W\"string\"" << endl; 
  188.     cerr << endl;
  189.     return 0;
  190.       }
  191.  
  192.       if(string_to_replace == 0) {
  193.     cerr << endl;
  194.     cerr << "The -W option must be used with the -R, -L, or -I option."
  195.          << endl;
  196.     cerr << "Usage: -R\"string\" -W\"replacement\"" << endl;
  197.     cerr << endl;
  198.     return 0;
  199.       }
  200.  
  201.       string_to_insert = arg+2;
  202.       break;
  203.       
  204.     default:
  205.       cerr << endl;
  206.       cerr << "Unknown switch " << arg << endl;
  207.       cerr << "Exiting..." << endl;
  208.       cerr << endl;
  209.       return 0;
  210.   }
  211.   arg[0] = '\0';
  212.   return 1; // All command line arguments were valid
  213. }
  214.  
  215. int ProcessTextFile(fstream &iofile, ostream &stream)
  216. {
  217.   char LineBuffer[MAX_LINE];
  218.   char rawLineBuffer[MAX_LINE];
  219.   UString LineData;
  220.   UString appendLineBuffer;
  221.   DLList<UString> list;
  222.   int i, j, rv, offset = 0;
  223.   unsigned long line_number = 0;
  224.   
  225.   while(!iofile.eof()) { // Read in the file line by line
  226.  
  227.     // Clear the buffers
  228.     for(i = 0; i < MAX_LINE; i++) LineBuffer[i] = '\0';
  229.     for(i = 0; i < MAX_LINE; i++) rawLineBuffer[i] = '\0';
  230.     LineData.DeleteAt(0, LineData.length());
  231.     offset = 0; // Reset string offset
  232.     
  233.     iofile.getline(rawLineBuffer, MAX_LINE);
  234.     line_number++;
  235.         
  236.     // Filter each line of text
  237.     for(i = 0, j = 0; i < MAX_LINE; i++) {
  238.       if((rawLineBuffer[i] == '\r') || (rawLineBuffer[i] == '\n')) break;
  239.       LineBuffer[j++] = rawLineBuffer[i];
  240.     }
  241.  
  242.     // Detect backslashes used to mark the continuation of a line
  243.     if((LineBuffer[strlen(LineBuffer)-1] == '\\') && (append_lines == 1)) {
  244.       appendLineBuffer += LineBuffer;
  245.       appendLineBuffer += '\n'; // Insert LF after the backslash 
  246.       continue;  // Goto the top of loop and append next line
  247.     }
  248.     if(appendLineBuffer.length() > 0) { // Append the next line
  249.       appendLineBuffer += LineBuffer;
  250.       LineData = appendLineBuffer;
  251.       appendLineBuffer.DeleteAt(0, appendLineBuffer.length());
  252.     }
  253.     else
  254.       LineData = LineBuffer;
  255.     
  256.     // Routine to cat file
  257.     // ----------------------------------------------------------- 
  258.     if(cat_file) {
  259.       if(!iofile.eof()) // Get rid of EOF marker
  260.     stream << LineData << endl;
  261.       else
  262.     stream << LineData;
  263.     }
  264.     
  265.     // Routine to find a string
  266.     // ----------------------------------------------------------- 
  267.     if(string_to_find != 0 && find_string != 0) {
  268.       if(find_all) {
  269.     while(1) {
  270.       if(check_case)
  271.         offset = LineData.Find(string_to_find, offset);
  272.       else
  273.         offset = LineData.IFind(string_to_find, offset);
  274.       if(offset == UString::NoMatch) break;
  275.       num_matches++; num_processed++;
  276.       offset++;
  277.     }
  278.       }
  279.       else {
  280.     if(check_case)
  281.       rv = LineData.Find(string_to_find);
  282.     else
  283.       rv = LineData.IFind(string_to_find);
  284.     if(rv != UString::NoMatch) {
  285.       num_matches++; num_processed++;
  286.       cout << "Found a match on line #" << line_number << endl;
  287.     }
  288.       }
  289.     }
  290.  
  291.     // Get rid of EOF marker
  292.     if((strcmp(LineData.c_str(), "\0") == 0) && (iofile.eof())) break;
  293.  
  294.     // Routine to replace a string
  295.     // ----------------------------------------------------------- 
  296.     if(replacing_string) {
  297.       while(1) {
  298.     if(check_case)
  299.       offset = LineData.Find(string_to_replace, offset);
  300.     else
  301.       offset = LineData.IFind(string_to_replace, offset);
  302.     if(offset == UString::NoMatch) break;
  303.     LineData.DeleteAt(offset, (strlen(string_to_replace)));
  304.     LineData.InsertAt(offset, string_to_insert);
  305.     num_replaced++;  // Update global replacement counter
  306.     num_processed++; // Record number of string processed for this file
  307.     offset += strlen(string_to_insert);
  308.       }
  309.       list.StoreNode(LineData); // Store all the lines
  310.     }
  311.     
  312.     // Routine to replace a line
  313.     // ----------------------------------------------------------- 
  314.     if(replacing_line) {
  315.       if(check_case)
  316.     offset = LineData.Find(string_to_replace, offset);
  317.       else
  318.     offset = LineData.IFind(string_to_replace, offset);
  319.  
  320.       if(offset != UString::NoMatch) {
  321.     offset += strlen(string_to_replace);
  322.     LineData.DeleteAt(offset, (LineData.length() - offset));
  323.     LineData.InsertAt(offset, string_to_insert);
  324.     num_replaced++;  // Update global replacement counter
  325.     num_processed++; // Record number of string processed for this file
  326.       }
  327.       list.StoreNode(LineData); // Store all the lines 
  328.     }
  329.     
  330.     // Routine to insert a line
  331.     // ----------------------------------------------------------- 
  332.     if(inserting_string) {
  333.       if(check_case)
  334.     offset = LineData.Find(string_to_replace, offset);
  335.       else
  336.     offset = LineData.IFind(string_to_replace, offset);
  337.  
  338.       if(offset != UString::NoMatch) {
  339.     offset += strlen(string_to_replace);
  340.     LineData.InsertAt(offset, string_to_insert);
  341.     num_replaced++;  // Update global replacement counter
  342.     num_processed++; // Record number of string processed for this file
  343.       }
  344.       list.StoreNode(LineData); // Store all the lines 
  345.     }
  346.   } 
  347.  
  348.   // If any strings were replaced in the file, rewrite the file
  349.   // ----------------------------------------------------------- 
  350.   if(num_replaced > 0) {
  351.     char *EndOfLineSequence;
  352.     iofile.close(); 
  353. #if defined(__DOS__)
  354.     // In DOS/Windows there are two file types, text and binary
  355.     fstream iofile(open_file, ios::out|ios::trunc|ios::binary);
  356.     EndOfLineSequence = "\r\n"; 
  357. #else 
  358.     // In UNIX there is only one file type
  359.     fstream iofile(open_file, ios::out|ios::in|ios::trunc);
  360.     EndOfLineSequence = "\n"; 
  361. #endif
  362.     
  363.     if(!iofile) {
  364.       cerr << endl;
  365.       cerr << "Cannot write to: " << open_file << endl;
  366.       cerr << "Exiting..." << endl;
  367.       cerr << endl;
  368.       exit(0);
  369.     }
  370.  
  371.     DNode<UString> *list_ptr = list.GetFront();
  372.     while(!list.IsHeader(list_ptr)) {
  373.       iofile.write(list_ptr->Data.c_str(), list_ptr->Data.length());
  374.       iofile.write(EndOfLineSequence, strlen(EndOfLineSequence));
  375.       list_ptr = list_ptr->GetNext(); 
  376.     }
  377.  
  378.     iofile.close();
  379.   }
  380.  
  381.   list.Clear();
  382.   return 1;
  383. }
  384.  
  385. // Program's main thread of execution.
  386. // ----------------------------------------------------------- 
  387. int main(int argc,     // Number of strings in array argv.
  388.      char *argv[], // Array of command-line argument strings.
  389.      char *envp[]) // Array of environment variable strings.
  390. // NOTE: None of the MSVC compilers will expand wildcard characters
  391. // used in command-line arguments unless linked with the setargv.obj
  392. // library. All the UNIX compliers will expand wildcard characters
  393. // by default.
  394. {
  395.   // If no arguments are given print usage message to the screen 
  396.   if(argc < 3) {
  397.     HelpMessage(ProgramName, FFindVersionNumber);
  398.     return 0;
  399.   }
  400.  
  401.   // Process command ling arguments and files 
  402.   int narg;
  403.   char *arg = argv[narg = 1];
  404.   while (narg < argc) {
  405.     if (arg[0] != '\0') {
  406.       if (arg[0] == '-') { // Look for command line arguments
  407.     if(!ProcessArgs(arg)) return 0; // Exit if argument is not valid
  408.       }
  409.       else { 
  410.     open_file = arg; // Update the open file name pointer
  411. #if defined(__DOS__) 
  412.     // In MS-DOS there are two file types, text and binary
  413.     fstream iofile(open_file, ios::in|ios::nocreate|ios::binary);
  414. #else 
  415.     // In UNIX there is only one file type
  416.     fstream iofile(open_file, ios::in|ios::nocreate);
  417. #endif
  418.     if(!iofile) {
  419.       cerr << endl;
  420.       cerr << "Cannot open file: " << open_file << endl;
  421.       cerr << "Exiting..." << endl;
  422.       cerr << endl;
  423.       return 0;
  424.     }
  425.     num_files++;
  426.  
  427.     // Echo the name of the file and search operation
  428.     // ----------------------------------------------------------- 
  429.     if(replacing_string == 1) {
  430.       if(string_to_insert == 0) {
  431.         cerr << endl;
  432.         cerr << "The -R option must be used with the -W option." << endl;
  433.         cerr << "Usage: -R\"string\" -W\"replacement\"" << endl;
  434.         cerr << endl;
  435.         return 0;
  436.       }
  437.       num_processed = 0; // Number of string processed for this file
  438.       cout << endl;
  439.       cout << "Opening file: " << open_file << endl;
  440.       cout << "Replacing: " << string_to_replace << endl;
  441.       cout << "With: " << string_to_insert << endl;
  442.     }
  443.  
  444.     if(replacing_line) {
  445.       if(string_to_insert == 0) {
  446.         cerr << endl;
  447.         cerr << "The -L option must be used with the -W option." << endl;
  448.         cerr << "Usage: -L\"string\" -W\"replacement\"" << endl;
  449.         return 0;
  450.       }
  451.       num_processed = 0; // Number of string processed for this file
  452.       cout << endl;
  453.       cout << "Opening file: " << open_file << endl;
  454.       cout << "Replacing line after: " << string_to_replace << endl;
  455.       cout << "With: " << string_to_insert << endl;
  456.     }
  457.  
  458.     if(inserting_string ) {
  459.       if(string_to_insert == 0) {
  460.         cerr << endl;
  461.         cerr << "The -I option must be used with the -W option." << endl;
  462.         cerr << "Usage: -I\"string\" -W\"insert\"" << endl;
  463.         cerr << endl;
  464.         return 0;
  465.       }
  466.       num_processed = 0; // Number of string processed for this file
  467.       cout << endl;
  468.       cout << "Opening file: " << open_file << endl;
  469.       cout << "Inserting: " << string_to_replace << endl;
  470.       cout << "After: " << string_to_insert << endl;
  471.     }
  472.  
  473.     if(find_string) {
  474.       num_processed = 0; // Number of string processed for this file
  475.       cout << endl;
  476.       cout << "Opening file: " << open_file << endl;
  477.       cout << "Seraching for: " << string_to_find << endl;
  478.     }
  479.  
  480.     // Process the test file
  481.     num_matches = 0; num_replaced = 0; num_processed = 0;
  482.     ProcessTextFile(iofile, cout);
  483.     if(num_files) iofile.close();
  484.     
  485.     // Report the total number of strings replaced/inserted 
  486.     // ----------------------------------------------------------- 
  487.     if(replacing_string) {
  488.       cout << "Replaced " << num_processed << " occurrence(s)." << endl;
  489.       cout << endl;
  490.     }
  491.  
  492.     if(replacing_line) {
  493.       cout << "Replaced " << num_processed << " occurrence(s)." << endl;
  494.       cout << endl;
  495.     }
  496.  
  497.     if(inserting_string ) {
  498.       cout << "Inserted " << num_processed << " line(s)." << endl;
  499.       cout << endl;
  500.     }
  501.  
  502.     if(find_string) {
  503.       if(find_all) {
  504.         cout << "Found " << num_processed << " matches" << endl;
  505.         cout << endl;
  506.       }
  507.       if(num_matches == 0) { // Used by -f command if search fails
  508.         cout << "No matches found." << endl;
  509.         cout << endl;
  510.       }
  511.     }
  512.       }
  513.       arg = argv[++narg];
  514.     }
  515.   }
  516.   
  517.   if(num_files == 0) {
  518.     cerr << endl;
  519.     cerr << "You must enter a file name." << endl;
  520.     cerr << "Exiting..." << endl;
  521.     cerr << endl;
  522.     return 0;
  523.   }
  524.   
  525.   // Return the total number of strings replaced/inserted 
  526.   // ----------------------------------------------------------- 
  527.   if(replacing_string) return num_replaced;
  528.   if(replacing_line) return num_replaced;
  529.   if(inserting_string ) return num_replaced;
  530.   if(find_string) return num_matches;
  531.  
  532.   return 0;
  533. }
  534. // ----------------------------------------------------------- // 
  535. // ------------------------------- //
  536. // --------- End of File --------- //
  537. // ------------------------------- //
  538.