[cage] Unbreak the build
[parrot.git] / tools / util / pgegrep
blob54a463a8bf1d86d56fa27da9a5d40963973be832
1 #! parrot
3 =head1 NAME
5 pgegrep - A simple grep using PGE for matching
7 =head1 SYNOPSIS
9 B<pgegrep> [I<OPTIONS>] B<PATTERN> [I<FILE...>]
11 =head1 DESCRIPTION
13 pgegrep aims to be a small and easy to use program in replacement of the
14 standard grep utility.  Regex support is whatever PGE will allow.  It
15 searches through files line by line and tests if the given pattern matches.
17 =head1 OPTIONS
19 =over 4
21 =item -v
23 =item --invert-match
25 print lines not matching PATTERN
27 =item -V
29 =item --version
31 print the version and exit
33 =item --help
35 show this help and exit
37 =item -r
39 =item --recursive
41 recursively descend into directories
43 =item -L
45 =item --files-without-matches
47 print a list of files that do not match PATTERN
49 =item -l
51 =item --files-with-matches
53 print a list of files that do match PATTERN
55 =item -a
57 =item --text
59 treat binary files as text.
61 This uses a basic heuristic to discover if a file is binary or not.  Files are
62 read line by line, and it keeps processing "normally" until a control character
63 is found, and then stops and goes onto the next file is that line matches.
65 =item -n
67 =item --line-number
69 print the line number for each match
71 =item -H
73 =item --with-filename
75 print the filename for each match
77 =back
79 =cut
81 # Readability improved!
82 .include 'hllmacros.pir'
84 .sub main :main
85         .param pmc argv # the script name, then our options.
86         .local string progname
87         progname = shift argv
88         load_bytecode 'Getopt/Obj.pbc'
89         load_bytecode 'PGE.pbc'
90         .local pmc getopts
91         getopts = new 'Getopt::Obj'
92         getopts.'notOptStop'(1)
93         push getopts, 'with-filename|H'
94         push getopts, 'files-with-matches|l'
95         push getopts, 'files-without-matches|L'
96         push getopts, 'line-number|n'
97         push getopts, 'text|a'
98         push getopts, 'recursive|r'
99         push getopts, 'invert-match|v'
100         push getopts, 'version|V'
101         push getopts, 'help'
102         push_eh handler
103         .local pmc opts
104         opts = getopts.'get_options'(argv)
105         $I0 = defined opts['help']
106         .If($I0, {
107                 showhelp()
108         })
109         $I0 = defined opts['version']
110         .If($I0, {
111                 showversion()
112         })
114         .local int argc
115         argc = elements argv
116         .Unless(argc>1, { showhelp() }) # need rule and at least one file
118         .local string rule
119         .local pmc p6rule_compile, matchsub
120         rule = shift argv
121         p6rule_compile = compreg 'PGE::Perl6Regex'
122         matchsub = p6rule_compile(rule)
123         .If(null matchsub, { die 'Unable to compile regex' })
125         .local int i, filecount
126         .local string filename
127         .local pmc File, OS, files, handle
128         files = new 'ResizableStringArray'
129         files = argv
130         filecount = files
131         # define with-filename if there's more than one file
132         .If(filecount >= 2, { opts['with-filename'] = 1 })
133         File = new 'File'
134         OS = new 'OS'
135         # This must be here, or else it'll get filled with junk data we use stdin...
136         i = 0
138         .Unless(filecount, {
139                 # no args, use stdin
140         stdindashhack:
141                 handle = getstdin
142                 filename = '(standard input)'
143                 goto stdinhack
144         })
145         .For(, i < filecount, inc i, {
146                 filename = files[i]
147                 .If(filename == '-', {
148                         goto stdindashhack
149                 })
150                 $I1 = File.'is_file'(filename)
151                 .IfElse($I1, {
152                         # Is a file
153                         handle = open filename, 'r'
154                 },{
155                         # Not a file, hopefully a directory
156                         $I1 = File.'is_dir'(filename)
157                         $I0 = defined opts['recursive']
158                         $I1 &= $I0
159                         .Unless($I1, {
160                                 printerr "pgegrep: '"
161                                 printerr filename
162                                 printerr "': Operation not supported.\n"
163                                 goto nextfor_0
164                         })
165                         $P0 = OS.'readdir'(filename)
166                         .Foreach($S0, $P0, {
167                                 .If($S0 != '.', {
168                                 .If($S0 != '..', {
169                                         $S1 = filename . '/'
170                                         $S0 = $S1 . $S0
171                                         $P1 = new 'ResizableStringArray'
172                                         $P1[0] = $S0
173                                         $I0 = i + 1
174                                         splice files, $P1, $I0, 0
175                                 }) })
176                         })
177                         filecount = files
178                         goto nextfor_0
179                 })
180         stdinhack:
181                 checkfile(handle, filename, matchsub, opts)
182                 close handle
183         nextfor_0:
184         })
186         end
187 handler:
188         .local pmc exception, pmcmsg
189         .local string message
190         .get_results (exception)
191         pmcmsg = getattribute exception, 'message'
192         pop_eh
193         message = pmcmsg
194         message  = "pgegrep: " . message
195         die message
196 .end
198 .sub checkfile
199         .param pmc handle
200         .param string filename
201         .param pmc matchsub
202         .param pmc opts
204         .local pmc match
205         .local string line
206         .local int lineno, linelen, matched
207         lineno = 1
208         matched = 0 # Only used for --files-without-matches
209         line = readline handle
210         linelen = length line 
212         .local pmc p6rule_compile, cntrlchar
213         $S0 = '<+cntrl-[\t\r\n]>'
214         p6rule_compile = compreg 'PGE::Perl6Regex'
215         cntrlchar = p6rule_compile($S0)
217         .For(, linelen, {
218                 line = readline handle
219                 linelen = length line
220                 inc lineno
221         }, {
222                 match = matchsub(line)
223                 $I1 = istrue match
224                 match = cntrlchar(line)
226                 $I2 = istrue match
227                 $I0 = defined opts['files-without-matches']
228                 .If($I0, {
229                         .If($I1, { matched = 1 })
230                         goto next
231                 })
232                 $I0 = defined opts['files-with-matches']
233                 $I0 = $I0 && $I1
234                 .If($I0, {
235                         say filename
236                         .return()
237                 })
239                 $I0 = defined opts['invert-match']
240                 not $I0
241                 $I1 = xor $I1, $I0
242                 .Unless($I1, {
243                         $I0 = defined opts['text']
244                         $I0 = xor $I0, $I2
245                         .If($I0, {
246                                 print 'Binary file '
247                                 print filename
248                                 say   ' matches'
249                                 .return()
250                         })
251                         $I0 = defined opts['with-filename']
252                         $I1 = defined opts['recursive']
253                         $I0 = $I0 || $I1
254                         .If($I0, {
255                                 print filename
256                                 print ':'
257                         })
258                         $I0 = defined opts['line-number']
259                         .If($I0, {
260                                 print lineno
261                                 print ':'
262                         })
263                         print line
264                 })
265                 #---------
266         next:
267         })
268         $I0 = defined opts['files-without-matches']
269         .If($I0, { say filename })
270         .return()
271 .end
273 .sub showhelp
274         print <<'HELP'
275 Usage: pgegrep [OPTIONS] PATTERN [FILE...]
276 Search for the Perl 6 Rule PATTERN in each file.
278   -v --invert-match          print lines not matching PATTERN
279   -V --version               print the version and exit
280      --help                  show this help and exit
281   -r --recursive             recursively descend into directories
282   -L --files-without-matches print a list of files that do not match PATTERN
283   -l --files-with-matches    print a list of files that do match PATTERN
284   -a --text                  treat binary files as text
285   -n --line-number           print the line number for each match
286   -H --with-filename         print the filename for each match
288 HELP
289         end
290 .end
292 .sub showversion
293         print <<'VERSION'
294 pgegrep v0.0.1
295 VERSION
296         end
297 .end
299 # Local Variables:
300 #   mode: pir
301 #   fill-column: 100
302 # End:
303 # vim: expandtab shiftwidth=4 ft=pir: