WvStreams
wvgrep.cc
00001 /* -*- Mode: C++ -*-
00002  * Worldvisions Weaver Software:
00003  *   Copyright (C) 1997-2004 Net Integration Technologies, Inc.
00004  *
00005  * A clone of grep(1) that is written entirely in WvStreams
00006  *
00007  */
00008 
00009 #include "wvstring.h"
00010 #include "wvstringlist.h"
00011 #include "wvargs.h"
00012 #include "wvregex.h"
00013 #include "wvfile.h"
00014 
00015 
00016 #define VERSION "0.1.0"
00017 
00018 static void output_filename(WvStringParm filename,
00019         char suffix, bool display_nulls)
00020 {
00021     wvout->print(!!filename? filename: WvFastString("(standard input)"));
00022     if (display_nulls)
00023         wvout->write("\0", 1);
00024     else
00025         wvout->write(&suffix, 1);
00026 }
00027 
00028 
00029 static int match(const WvRegex &regex, WvStringParm filename, WvStream *file,
00030         bool invert_match, bool display_filename, bool display_line_number,
00031         bool display_nulls, bool display_nothing, bool end_on_first_match)
00032 {
00033     int count = 0;
00034     int lineno = 0;
00035     while (file->isok())
00036     {
00037         const char *line = file->blocking_getline(-1);
00038         if (line == NULL) break;
00039         ++lineno;
00040         
00041         bool result = regex.match(line);
00042         if (invert_match) result = !result;
00043         
00044         if (result)
00045         {
00046             ++count;
00047             if (end_on_first_match) return count;
00048         }
00049         
00050         if (!result || display_nothing) continue;
00051         
00052         if (display_filename)
00053             output_filename(filename, ':', display_nulls);
00054         if (display_line_number)
00055             wvout->print("%s:", lineno);
00056         wvout->print("%s\n", line);
00057     }
00058     return count;
00059 }
00060 
00061 
00062 int main(int argc, char **argv)
00063 {
00064     WvArgs args;
00065 
00066     args.set_version("wvgrep (WvStreams grep) " VERSION "\n");
00067     args.set_email("<" WVPACKAGE_BUGREPORT ">");
00068 
00069     bool opt_count = false;
00070     args.add_set_bool_option('c', "count", WvString::null, opt_count);
00071     
00072     bool opt_extended_regexp = false;
00073     args.add_set_bool_option('E', "extended-regexp", WvString::null, opt_extended_regexp);
00074     
00075     WvString opt_regexp;
00076     args.add_option('e', "regexp", WvString::null, WvString::null, opt_regexp);
00077   
00078     bool opt_basic_regexp = false;
00079     args.add_set_bool_option('G', "basic-regexp", WvString::null, opt_basic_regexp);
00080     
00081     bool opt_with_filename = false;
00082     args.add_set_bool_option('H', "with-filename", WvString::null, opt_with_filename);
00083     
00084     bool opt_no_filename = false;
00085     args.add_set_bool_option('h', "no-filename", WvString::null, opt_no_filename);
00086     
00087     bool opt_ignore_case = false;
00088     args.add_set_bool_option('i', "ignore-case", WvString::null, opt_ignore_case);
00089     args.add_set_bool_option('y', WvString::null, "Synonym for -i", opt_ignore_case);
00090     
00091     bool opt_files_without_match = false;
00092     args.add_set_bool_option('L', "files-without-match", WvString::null, opt_files_without_match);
00093     
00094     bool opt_files_with_matches = false;
00095     args.add_set_bool_option('l', "files-with-matches", WvString::null, opt_files_with_matches);
00096     
00097     bool opt_line_number = false;
00098     args.add_set_bool_option('n', "line-number", WvString::null, opt_line_number);
00099     
00100     bool opt_quiet = false;
00101     args.add_set_bool_option('q', "quiet", WvString::null, opt_quiet);
00102     args.add_set_bool_option(0, "silent", "Synonym for --quiet", opt_quiet);
00103     
00104     bool opt_no_messages = false;
00105     args.add_set_bool_option('s', "no-message", WvString::null, opt_no_messages);
00106     
00107     bool opt_invert_match = false;
00108     args.add_set_bool_option('v', "invert-match", WvString::null, opt_invert_match);
00109     
00110     bool opt_line_regexp = false;
00111     args.add_set_bool_option('x', "line-regexp", WvString::null, opt_line_regexp);
00112     
00113     bool opt_null = false;
00114     args.add_set_bool_option('Z', "null", WvString::null, opt_null);
00115 
00116     args.add_required_arg("PATTERN");
00117     args.add_optional_arg("FILE", true);
00118 
00119     args.set_help_header("Search for PATTERN in each FILE or standard input.");
00120     args.set_help_footer("With no FILE, this program reads standard input.");
00121 
00122     WvStringList remaining_args;    
00123     args.process(argc, argv, &remaining_args);
00124 
00125     if (!opt_regexp && !remaining_args.isempty())
00126         opt_regexp = remaining_args.popstr();
00127 
00128     int cflags = WvFastString(argv[0]) == "egrep"?
00129             WvRegex::EXTENDED: WvRegex::BASIC;
00130     if (opt_extended_regexp) cflags = WvRegex::EXTENDED;
00131     if (opt_basic_regexp) cflags = WvRegex::BASIC;
00132     if (opt_ignore_case) cflags |= WvRegex::ICASE;
00133     
00134     WvString regex_str;
00135     if (opt_line_regexp)
00136         regex_str = WvString("^%s$", opt_regexp);
00137     else regex_str = opt_regexp;
00138     
00139     WvRegex regex(regex_str, cflags);
00140     if (!regex.isok())
00141     {
00142         WvString errstr = regex.errstr();
00143         wverr->print("%s: Invalid regular expression", argv[0]); 
00144         if (!!errstr) wverr->print(errstr);
00145         wverr->write("\n", 1);
00146         return 2;
00147     }
00148     
00149     bool display_filename = remaining_args.count() >= 2;
00150     if (opt_with_filename) display_filename = true;
00151     if (opt_no_filename) display_filename = false;
00152     
00153     if (remaining_args.isempty())
00154         remaining_args.append(WvString::null);
00155     
00156     bool found_match = false;
00157     WvStringList::Iter filename(remaining_args);
00158     for (filename.rewind(); filename.next(); )
00159     {
00160         WvStream *file;
00161         if (!!*filename)
00162             file = new WvFile(*filename, O_RDONLY);
00163         else
00164             file = wvcon;
00165 
00166         if (!file->isok())
00167         {
00168             if (!opt_no_messages)
00169                 wverr->print("%s: %s: %s\n", argv[0],
00170                         *filename, file->errstr());
00171             if (!!*filename) WVRELEASE(file);
00172             continue;
00173         }
00174             
00175         int count = match(regex, *filename, file,
00176                 opt_invert_match, display_filename, opt_line_number, opt_null,
00177                 opt_count || opt_files_without_match || opt_files_with_matches,
00178                 opt_quiet);
00179         
00180         if (!!*filename) WVRELEASE(file);
00181         
00182         if (opt_files_with_matches || opt_files_without_match)
00183         {
00184             bool display = opt_files_with_matches? count>0: count==0;
00185             if (display)
00186                 output_filename(*filename, '\n', opt_null);
00187         }
00188         else if(opt_count)
00189         {
00190             if (display_filename)
00191                 output_filename(*filename, ':', opt_null);
00192             wvout->print("%s\n", count);
00193         }
00194                 
00195         found_match = found_match || count > 0;
00196         if (opt_quiet && found_match) break;
00197     }
00198 
00199     return found_match? 0: 1;
00200 }