• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  *
3  * Copyright (c) 2004
4  * John Maddock
5  *
6  * Use, modification and distribution are subject to the
7  * Boost Software License, Version 1.0. (See accompanying file
8  * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
9  *
10  */
11 
12 #include <boost/regex.hpp>
13 #include <iostream>
14 #include <fstream>
15 #include <string>
16 #include <vector>
17 
18 #ifdef BOOST_MSVC
19 #pragma warning(disable:4512 4244)
20 #endif
21 
22 #include <boost/program_options.hpp>
23 
24 namespace po = boost::program_options;
25 
26 int after_context;
27 int before_context;
28 bool print_byte_offset;
29 bool count_only;
30 std::string pattern;
31 bool print_non_matching_files;
32 bool files_only;
33 bool print_line_numbers;
34 
35 boost::regex_constants::syntax_option_type flags = boost::regex_constants::basic;
36 boost::regex re;
37 boost::smatch what;
38 std::string current_file;
39 int file_count;
40 
process_stream(std::istream & is)41 void process_stream(std::istream& is)
42 {
43    std::string line;
44    int match_found = 0;
45    int linenum = 1;
46    while(std::getline(is, line))
47    {
48       bool result = boost::regex_search(line, what, re);
49       if(result)
50       {
51          if(print_non_matching_files)
52             return;
53          if(files_only)
54          {
55             std::cout << current_file << std::endl;
56             return;
57          }
58          if(!match_found && !count_only && (file_count > 1))
59          {
60             std::cout << current_file << ":\n";
61          }
62          ++match_found;
63          if(!count_only)
64          {
65             if(print_line_numbers)
66             {
67                std::cout << linenum << ":";
68             }
69             if(print_byte_offset)
70             {
71                std::cout << what.position() << ":";
72             }
73             std::cout << what[0] << std::endl;
74          }
75       }
76       ++linenum;
77    }
78    if(count_only && match_found)
79    {
80       std::cout << match_found << " matches found in file " << current_file << std::endl;
81    }
82    else if(print_non_matching_files && !match_found)
83    {
84       std::cout << current_file << std::endl;
85    }
86 }
87 
process_file(const std::string & name)88 void process_file(const std::string& name)
89 {
90    current_file = name;
91    std::ifstream is(name.c_str());
92    if(is.bad())
93    {
94       std::cerr << "Unable to open file " << name << std::endl;
95    }
96    process_stream(is);
97 }
98 
main(int argc,char * argv[])99 int main(int argc, char * argv[])
100 {
101    try{
102       po::options_description opts("Options");
103       opts.add_options()
104          ("help,h", "produce help message")
105          //("after-context,A", po::value<int>(&after_context)->default_value(0), "Print arg  lines  of  trailing  context  after  matching  lines. Places  a  line  containing  --  between  contiguous  groups  of matches.")
106          //("before-context,B", po::value<int>(&before_context)->default_value(0), "Print  arg  lines  of  leading  context  before  matching lines. Places  a  line  containing  --  between  contiguous  groups  of matches.")
107          //("context,C", po::value<int>(), "Print  arg lines of output context.  Places a line containing -- between contiguous groups of matches.")
108          ("byte-offset,b", "Print the byte offset within the input file before each line  of output.")
109          ("count,c", "Suppress normal output; instead print a count of matching  lines for  each  input  file.  With the -v, --invert-match option (see below), count non-matching lines.")
110          ("extended-regexp,E", "Interpret PATTERN as an POSIX-extended regular expression.")
111          ("perl-regexp,P", "Interpret PATTERN as a Perl regular expression.")
112          //("regexp,e", po::value<std::string>(&pattern), "Use PATTERN as the pattern; useful to protect patterns beginning with -.")
113          ("basic-regexp,G", "Interpret arg as a POSIX-basic regular expression (see below).  This is the default.")
114          ("ignore-case,i", "Ignore case distinctions in  both  the  PATTERN  and  the  input files.")
115          ("files-without-match,L", "Suppress  normal  output;  instead  print the name of each input file from which no output would normally have been printed.  The scanning will stop on the first match.")
116          ("files-with-matches,l", "Suppress  normal  output;  instead  print the name of each input file from which output would normally have  been  printed.   The scanning will stop on the first match.")
117          ("line-number,n", "Prefix each line of output with the line number within its input file.")
118          ;
119       // Hidden options, will be allowed both on command line and
120       // in config file, but will not be shown to the user.
121       po::options_description hidden("Hidden options");
122       hidden.add_options()
123          ("input-file", po::value< std::vector<std::string> >(), "input file")
124          ("input-pattern", po::value< std::string >(), "input file")
125          ;
126 
127       po::options_description cmdline_options;
128       cmdline_options.add(opts).add(hidden);
129 
130       po::positional_options_description p;
131       p.add("input-pattern", 1);
132       p.add("input-file", -1);
133 
134       po::variables_map vm;
135       po::store(po::command_line_parser(argc, argv).options(cmdline_options)/*.options(hidden)*/.positional(p).run(), vm);
136       po::notify(vm);
137 
138       if (vm.count("help"))
139       {
140          std::cout << opts << "\n";
141          return 0;
142       }
143       if (vm.count("context"))
144       {
145          after_context = vm["context"].as< int >();
146          before_context = after_context;
147       }
148       if(vm.count("extended-regexp"))
149       {
150          flags = boost::regex_constants::extended;
151       }
152       if(vm.count("basic-regexp"))
153       {
154          flags = boost::regex_constants::basic;
155       }
156       if(vm.count("perl-regexp"))
157       {
158          flags = boost::regex_constants::perl;
159       }
160       if(vm.count("ignore-case"))
161       {
162          flags |= boost::regex_constants::icase;
163       }
164       if(vm.count("byte-offset"))
165       {
166          print_byte_offset = true;
167       }
168       if(vm.count("count"))
169       {
170          count_only = true;
171       }
172       if(vm.count("files-without-match"))
173       {
174          print_non_matching_files = true;
175       }
176       if(vm.count("files-with-matches"))
177       {
178          files_only = true;
179       }
180       if(vm.count("line-number"))
181       {
182          print_line_numbers = true;
183       }
184       if(vm.count("input-pattern"))
185       {
186          pattern = vm["input-pattern"].as< std::string >();
187          re.assign(pattern, flags);
188       }
189       else
190       {
191          std::cerr << "No pattern specified" << std::endl;
192          return 1;
193       }
194       if (vm.count("input-file"))
195       {
196          const std::vector<std::string>& files = vm["input-file"].as< std::vector<std::string> >();
197          file_count = files.size();
198          for(std::vector<std::string>::const_iterator i = files.begin(); i != files.end(); ++i)
199          {
200             process_file(*i);
201          }
202       }
203       else
204       {
205          // no input files, scan stdin instead:
206          process_stream(std::cin);
207       }
208 
209    }
210    catch(const std::exception& e)
211    {
212       std::cerr << e.what() << std::endl;
213    }
214 
215    return 0;
216 }
217 
218