WvStreams
wvgrep.cc
1 /* -*- Mode: C++ -*-
2  * Worldvisions Weaver Software:
3  * Copyright (C) 1997-2004 Net Integration Technologies, Inc.
4  *
5  * A clone of grep(1) that is written entirely in WvStreams
6  *
7  */
8 
9 #include "wvstring.h"
10 #include "wvstringlist.h"
11 #include "wvargs.h"
12 #include "wvregex.h"
13 #include "wvfile.h"
14 
15 
16 #define VERSION "0.1.0"
17 
18 static void output_filename(WvStringParm filename,
19  char suffix, bool display_nulls)
20 {
21  wvout->print(!!filename? filename: WvFastString("(standard input)"));
22  if (display_nulls)
23  wvout->write("\0", 1);
24  else
25  wvout->write(&suffix, 1);
26 }
27 
28 
29 static int match(const WvRegex &regex, WvStringParm filename, WvStream *file,
30  bool invert_match, bool display_filename, bool display_line_number,
31  bool display_nulls, bool display_nothing, bool end_on_first_match)
32 {
33  int count = 0;
34  int lineno = 0;
35  while (file->isok())
36  {
37  const char *line = file->blocking_getline(-1);
38  if (line == NULL) break;
39  ++lineno;
40 
41  bool result = regex.match(line);
42  if (invert_match) result = !result;
43 
44  if (result)
45  {
46  ++count;
47  if (end_on_first_match) return count;
48  }
49 
50  if (!result || display_nothing) continue;
51 
52  if (display_filename)
53  output_filename(filename, ':', display_nulls);
54  if (display_line_number)
55  wvout->print("%s:", lineno);
56  wvout->print("%s\n", line);
57  }
58  return count;
59 }
60 
61 
62 int main(int argc, char **argv)
63 {
64  WvArgs args;
65 
66  args.set_version("wvgrep (WvStreams grep) " VERSION "\n");
67  args.set_email("<" WVPACKAGE_BUGREPORT ">");
68 
69  bool opt_count = false;
70  args.add_set_bool_option('c', "count", WvString::null, opt_count);
71 
72  bool opt_extended_regexp = false;
73  args.add_set_bool_option('E', "extended-regexp", WvString::null, opt_extended_regexp);
74 
75  WvString opt_regexp;
76  args.add_option('e', "regexp", WvString::null, WvString::null, opt_regexp);
77 
78  bool opt_basic_regexp = false;
79  args.add_set_bool_option('G', "basic-regexp", WvString::null, opt_basic_regexp);
80 
81  bool opt_with_filename = false;
82  args.add_set_bool_option('H', "with-filename", WvString::null, opt_with_filename);
83 
84  bool opt_no_filename = false;
85  args.add_set_bool_option('h', "no-filename", WvString::null, opt_no_filename);
86 
87  bool opt_ignore_case = false;
88  args.add_set_bool_option('i', "ignore-case", WvString::null, opt_ignore_case);
89  args.add_set_bool_option('y', WvString::null, "Synonym for -i", opt_ignore_case);
90 
91  bool opt_files_without_match = false;
92  args.add_set_bool_option('L', "files-without-match", WvString::null, opt_files_without_match);
93 
94  bool opt_files_with_matches = false;
95  args.add_set_bool_option('l', "files-with-matches", WvString::null, opt_files_with_matches);
96 
97  bool opt_line_number = false;
98  args.add_set_bool_option('n', "line-number", WvString::null, opt_line_number);
99 
100  bool opt_quiet = false;
101  args.add_set_bool_option('q', "quiet", WvString::null, opt_quiet);
102  args.add_set_bool_option(0, "silent", "Synonym for --quiet", opt_quiet);
103 
104  bool opt_no_messages = false;
105  args.add_set_bool_option('s', "no-message", WvString::null, opt_no_messages);
106 
107  bool opt_invert_match = false;
108  args.add_set_bool_option('v', "invert-match", WvString::null, opt_invert_match);
109 
110  bool opt_line_regexp = false;
111  args.add_set_bool_option('x', "line-regexp", WvString::null, opt_line_regexp);
112 
113  bool opt_null = false;
114  args.add_set_bool_option('Z', "null", WvString::null, opt_null);
115 
116  args.add_required_arg("PATTERN");
117  args.add_optional_arg("FILE", true);
118 
119  args.set_help_header("Search for PATTERN in each FILE or standard input.");
120  args.set_help_footer("With no FILE, this program reads standard input.");
121 
122  WvStringList remaining_args;
123  args.process(argc, argv, &remaining_args);
124 
125  if (!opt_regexp && !remaining_args.isempty())
126  opt_regexp = remaining_args.popstr();
127 
128  int cflags = WvFastString(argv[0]) == "egrep"?
129  WvRegex::EXTENDED: WvRegex::BASIC;
130  if (opt_extended_regexp) cflags = WvRegex::EXTENDED;
131  if (opt_basic_regexp) cflags = WvRegex::BASIC;
132  if (opt_ignore_case) cflags |= WvRegex::ICASE;
133 
134  WvString regex_str;
135  if (opt_line_regexp)
136  regex_str = WvString("^%s$", opt_regexp);
137  else regex_str = opt_regexp;
138 
139  WvRegex regex(regex_str, cflags);
140  if (!regex.isok())
141  {
142  WvString errstr = regex.errstr();
143  wverr->print("%s: Invalid regular expression", argv[0]);
144  if (!!errstr) wverr->print(errstr);
145  wverr->write("\n", 1);
146  return 2;
147  }
148 
149  bool display_filename = remaining_args.count() >= 2;
150  if (opt_with_filename) display_filename = true;
151  if (opt_no_filename) display_filename = false;
152 
153  if (remaining_args.isempty())
154  remaining_args.append(WvString::null);
155 
156  bool found_match = false;
157  WvStringList::Iter filename(remaining_args);
158  for (filename.rewind(); filename.next(); )
159  {
160  WvStream *file;
161  if (!!*filename)
162  file = new WvFile(*filename, O_RDONLY);
163  else
164  file = wvcon;
165 
166  if (!file->isok())
167  {
168  if (!opt_no_messages)
169  wverr->print("%s: %s: %s\n", argv[0],
170  *filename, file->errstr());
171  if (!!*filename) WVRELEASE(file);
172  continue;
173  }
174 
175  int count = match(regex, *filename, file,
176  opt_invert_match, display_filename, opt_line_number, opt_null,
177  opt_count || opt_files_without_match || opt_files_with_matches,
178  opt_quiet);
179 
180  if (!!*filename) WVRELEASE(file);
181 
182  if (opt_files_with_matches || opt_files_without_match)
183  {
184  bool display = opt_files_with_matches? count>0: count==0;
185  if (display)
186  output_filename(*filename, '\n', opt_null);
187  }
188  else if(opt_count)
189  {
190  if (display_filename)
191  output_filename(*filename, ':', opt_null);
192  wvout->print("%s\n", count);
193  }
194 
195  found_match = found_match || count > 0;
196  if (opt_quiet && found_match) break;
197  }
198 
199  return found_match? 0: 1;
200 }
WvArgs::set_version
void set_version(WvStringParm version)
Set the –version string.
Definition: wvargs.cc:819
WvArgs::add_option
void add_option(char short_option, WvStringParm long_option, WvStringParm desc, WvStringParm arg_desc, int &val)
Add a switch that takes an integer argument.
Definition: wvargs.cc:888
WvStream::write
virtual size_t write(const void *buf, size_t count)
Write data to the stream.
Definition: wvstream.cc:532
WvStringList::popstr
WvString popstr()
get the first string in the list, or an empty string if the list is empty.
Definition: wvstringlist.cc:55
WvStream::blocking_getline
char * blocking_getline(time_t wait_msec, int separator='\n', int readahead=1024)
This is a version of getline() that allows you to block for more data to arrive.
Definition: wvstream.cc:602
WvArgs::add_set_bool_option
void add_set_bool_option(char short_option, WvStringParm long_option, WvStringParm desc, bool &val)
Add a boolean option, which, when specified, sets the specified boolean variable to true.
Definition: wvargs.cc:856
WvFile
WvFile implements a stream connected to a file or Unix device.
Definition: wvfile.h:28
WvErrorBase::isok
virtual bool isok() const
By default, returns true if geterr() == 0.
Definition: wverror.h:39
WvString
WvString is an implementation of a simple and efficient printable-string class.
Definition: wvstring.h:329
WvArgs
WvArgs - Sane command-line argument processing for WvStreams.
Definition: wvargs.h:61
WvArgs::set_email
void set_email(WvStringParm email)
Set the e-mail address for bug reports.
Definition: wvargs.cc:825
WvRegex
WvRegex – Unified support for regular expressions.
Definition: wvregex.h:47
WvArgs::set_help_header
void set_help_header(WvStringParm header)
Set the introductory help message, printed at the beginning of –help.
Definition: wvargs.cc:831
WvStream::isok
virtual bool isok() const
return true if the stream is actually usable right now
Definition: wvstream.cc:445
WvStream
Unified support for streams, that is, sequences of bytes that may or may not be ready for read/write ...
Definition: wvstream.h:24
WvArgs::set_help_footer
void set_help_footer(WvStringParm footer)
Set the descriptive help message, printed at the end of –help.
Definition: wvargs.cc:837
WvArgs::process
bool process(int argc, char **argv, WvStringList *remaining_args=NULL)
Process the command line arguments passed to main() using the options provided through calls to add_o...
Definition: wvargs.cc:784
WvArgs::add_required_arg
void add_required_arg(WvStringParm desc, bool multiple=false)
Add a required argument to the list of parameters.
Definition: wvargs.cc:966
WvArgs::add_optional_arg
void add_optional_arg(WvStringParm desc, bool multiple=false)
Add an optional argument to the list of parameters.
Definition: wvargs.cc:982
WvStringList
This is a WvList of WvStrings, and is a really handy way to parse strings.
Definition: wvstringlist.h:27
WvFastString
A WvFastString acts exactly like a WvString, but can take (const char *) strings without needing to a...
Definition: wvstring.h:93