Skip to content
This repository
branch: master
Fetching contributors…

Octocat-spinner-32-eaf2f5

Cannot retrieve contributors at this time

executable file 309 lines (252 sloc) 6.61 kb
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308
#! parrot

=head1 NAME

pgegrep - A simple grep using PGE for matching

=head1 SYNOPSIS

B<pgegrep> [I<OPTIONS>] B<PATTERN> [I<FILE...>]

=head1 DESCRIPTION

pgegrep aims to be a small and easy to use program in replacement of the
standard grep utility. Regex support is whatever PGE will allow. It
searches through files line by line and tests if the given pattern matches.

=head1 OPTIONS

=over 4

=item -v

=item --invert-match

print lines not matching PATTERN

=item -V

=item --version

print the version and exit

=item --help

show this help and exit

=item -r

=item --recursive

recursively descend into directories

=item -L

=item --files-without-matches

print a list of files that do not match PATTERN

=item -l

=item --files-with-matches

print a list of files that do match PATTERN

=item -a

=item --text

treat binary files as text.

This uses a basic heuristic to discover if a file is binary or not. Files are
read line by line, and it keeps processing "normally" until a control character
is found, and then stops and goes onto the next file is that line matches.

=item -n

=item --line-number

print the line number for each match

=item -H

=item --with-filename

print the filename for each match

=back

=cut

# Readability improved!
.include 'hllmacros.pir'

# for getstdin and friends
.loadlib 'io_ops'

.sub main :main
.param pmc argv # the script name, then our options.
.local string progname
progname = shift argv
load_bytecode 'Getopt/Obj.pbc'
load_bytecode 'PGE.pbc'
.local pmc getopts
getopts = new [ 'Getopt';'Obj' ]
getopts.'notOptStop'(1)
push getopts, 'with-filename|H'
push getopts, 'files-with-matches|l'
push getopts, 'files-without-matches|L'
push getopts, 'line-number|n'
push getopts, 'text|a'
push getopts, 'recursive|r'
push getopts, 'invert-match|v'
push getopts, 'version|V'
push getopts, 'help'
push_eh handler
.local pmc opts
opts = getopts.'get_options'(argv)
$I0 = defined opts['help']
.If($I0, {
showhelp()
})
$I0 = defined opts['version']
.If($I0, {
showversion()
})

        .local int argc
        argc = elements argv
        .Unless(argc>1, { showhelp() }) # need rule and at least one file

.local string rule
.local pmc p6rule_compile, matchsub
rule = shift argv
p6rule_compile = compreg 'PGE::Perl6Regex'
matchsub = p6rule_compile(rule)
.If(null matchsub, { die 'Unable to compile regex' })

.local int i, filecount
.local string filename
.local pmc File, OS, files, handle
files = new 'ResizableStringArray'
files = argv
filecount = files
# define with-filename if there's more than one file
.If(filecount >= 2, { opts['with-filename'] = 1 })
        $P0 = loadlib 'file'
File = new 'File'
        $P0 = loadlib 'os'
OS = new 'OS'
# This must be here, or else it'll get filled with junk data we use stdin...
i = 0

.Unless(filecount, {
# no args, use stdin
stdindashhack:
handle = getstdin
filename = '(standard input)'
goto stdinhack
})
.For(, i < filecount, inc i, {
filename = files[i]
.If(filename == '-', {
goto stdindashhack
})
$I1 = File.'is_file'(filename)
.IfElse($I1, {
# Is a file
handle = open filename, 'r'
},{
# Not a file, hopefully a directory
$I1 = File.'is_dir'(filename)
$I0 = defined opts['recursive']
$I1 &= $I0
.Unless($I1, {
printerr "pgegrep: '"
printerr filename
printerr "': Operation not supported.\n"
goto nextfor_0
})
$P0 = OS.'readdir'(filename)
.Foreach($S0, $P0, {
.If($S0 != '.', {
.If($S0 != '..', {
$S1 = filename . '/'
$S0 = $S1 . $S0
$P1 = new 'ResizableStringArray'
$P1[0] = $S0
$I0 = i + 1
splice files, $P1, $I0, 0
}) })
})
filecount = files
goto nextfor_0
})
stdinhack:
checkfile(handle, filename, matchsub, opts)
close handle
nextfor_0:
})

end
handler:
.local pmc exception, pmcmsg
.local string message
.get_results (exception)
pmcmsg = getattribute exception, 'message'
pop_eh
        message = pmcmsg
        message = "pgegrep: " . message
        die message
.end

.sub checkfile
.param pmc handle
.param string filename
.param pmc matchsub
.param pmc opts

.local pmc match
.local string line
.local int lineno, linelen, matched
lineno = 1
matched = 0 # Only used for --files-without-matches
line = readline handle
linelen = length line

.local pmc p6rule_compile, cntrlchar
$S0 = '<+cntrl-[\t\r\n]>'
p6rule_compile = compreg 'PGE::Perl6Regex'
cntrlchar = p6rule_compile($S0)

.For(, linelen, {
                line = readline handle
                linelen = length line
                inc lineno
        }, {
match = matchsub(line)
                $I1 = istrue match
match = cntrlchar(line)

                $I2 = istrue match
$I0 = defined opts['files-without-matches']
.If($I0, {
.If($I1, { matched = 1 })
goto next
})
$I0 = defined opts['files-with-matches']
$I0 = $I0 && $I1
.If($I0, {
say filename
.return()
})

$I0 = defined opts['invert-match']
not $I0
$I1 = xor $I1, $I0
.Unless($I1, {
$I0 = defined opts['text']
$I0 = xor $I0, $I2
.If($I0, {
print 'Binary file '
print filename
say ' matches'
.return()
})
$I0 = defined opts['with-filename']
$I1 = defined opts['recursive']
$I0 = $I0 || $I1
.If($I0, {
                                print filename
                                print ':'
                        })
$I0 = defined opts['line-number']
.If($I0, {
                                print lineno
                                print ':'
                        })
print line
})
#---------
next:
})
$I0 = defined opts['files-without-matches']
.If($I0, { say filename })
.return()
.end

.sub showhelp
print <<'HELP'
Usage: pgegrep [OPTIONS] PATTERN [FILE...]
Search for the Perl 6 Rule PATTERN in each file.

  -v --invert-match print lines not matching PATTERN
  -V --version print the version and exit
     --help show this help and exit
  -r --recursive recursively descend into directories
  -L --files-without-matches print a list of files that do not match PATTERN
  -l --files-with-matches print a list of files that do match PATTERN
  -a --text treat binary files as text
  -n --line-number print the line number for each match
  -H --with-filename print the filename for each match

HELP
end
.end

.sub showversion
print <<'VERSION'
pgegrep v0.0.1
VERSION
end
.end

# Local Variables:
# mode: pir
# fill-column: 100
# End:
# vim: expandtab shiftwidth=4 ft=pir:
Something went wrong with that request. Please try again.