PotatoSalad-rak 0.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,16 @@
1
+
2
+ == 0.9 / 2008-02-03
3
+
4
+ * Added .rake to ruby files
5
+ * Colouring works on Win32 if win32console gem is installed.
6
+ * Checks that file is readable by the current user.
7
+ * Ignores socket files.
8
+ * Added .erb and .haml to Ruby filetypes.
9
+ * Added search at end-of/start-of line options
10
+ * Fixed searching up the directory tree when passed '.'
11
+
12
+ == 0.8.0 / 2007-10-30
13
+
14
+ * Initial release.
15
+
16
+
@@ -0,0 +1,8 @@
1
+ History.txt
2
+ Manifest.txt
3
+ README.txt
4
+ Rakefile
5
+ bin/rak
6
+ lib/rak.rb
7
+ spec/rak_spec.rb
8
+ spec/help_spec.rb
@@ -0,0 +1,66 @@
1
+ rak
2
+ by Daniel Lucraft
3
+ http://rubyforge.org/projects/rak
4
+
5
+ == DESCRIPTION:
6
+
7
+ Replacement for grep. Recursively scans directories to match a given
8
+ Ruby regular expression. Prints highlighted results.
9
+
10
+ Based on the Perl tool 'ack' by Andy Lester.
11
+
12
+ Examples with similar grep:
13
+
14
+ $ rak pattern
15
+ $ grep pattern $(find . | grep -v .svn)
16
+
17
+ $ rak --ruby pattern
18
+ $ grep pattern $(find . -name '*.rb' | grep -v .svn)
19
+
20
+ == FEATURES/PROBLEMS:
21
+
22
+ * Ruby regular expression syntax (uses oniguruma gem if installed).
23
+ * Highlighted output.
24
+ * Automatically recurses down the current directory or any given
25
+ directories.
26
+ * Skips version control directories, backups like '~' and '#' and your
27
+ * ruby project's pkg directory.
28
+ * Allows inclusion and exclusion of files based on types.
29
+ * Many options similar to grep.
30
+
31
+ == SYNOPSIS:
32
+
33
+ See 'rak --help' for usage information.
34
+
35
+ == REQUIREMENTS:
36
+
37
+ * Ruby
38
+
39
+ == INSTALL:
40
+
41
+ * gem install rak
42
+
43
+ == LICENSE:
44
+
45
+ (The MIT License)
46
+
47
+ Copyright (c) 2007 Daniel Lucraft
48
+
49
+ Permission is hereby granted, free of charge, to any person obtaining
50
+ a copy of this software and associated documentation files (the
51
+ 'Software'), to deal in the Software without restriction, including
52
+ without limitation the rights to use, copy, modify, merge, publish,
53
+ distribute, sublicense, and/or sell copies of the Software, and to
54
+ permit persons to whom the Software is furnished to do so, subject to
55
+ the following conditions:
56
+
57
+ The above copyright notice and this permission notice shall be
58
+ included in all copies or substantial portions of the Software.
59
+
60
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
61
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
62
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
63
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
64
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
65
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
66
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,14 @@
1
+
2
+ require 'rubygems'
3
+ require 'hoe'
4
+ require 'lib/rak'
5
+
6
+ Hoe.new('rak', Rak::VERSION) do |p|
7
+ p.rubyforge_name = 'rak'
8
+ p.author = 'Daniel Lucraft'
9
+ p.email = 'dan@fluentradical.com'
10
+ p.summary = 'A grep replacement in Ruby, type "$rak pattern".'
11
+ p.description = p.paragraphs_of('README.txt', 2..5).join("\n\n")
12
+ p.url = p.paragraphs_of('README.txt', 0).first.split(/\n/)[1..-1]
13
+ p.changes = p.paragraphs_of('History.txt', 0..1).join("\n\n")
14
+ end
data/bin/rak ADDED
@@ -0,0 +1,861 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+
5
+ if RUBY_PLATFORM =~ /mswin/
6
+ begin
7
+ require 'win32console'
8
+ rescue LoadError
9
+ ARGV << "--nocolour"
10
+ end
11
+ end
12
+
13
+ require 'getoptlong'
14
+
15
+ begin
16
+ require 'oniguruma'
17
+ require 'inline'
18
+ $use_onig = true
19
+ rescue LoadError
20
+ $use_onig = false
21
+ end
22
+
23
+ $use_onig = false
24
+
25
+ class Rak
26
+ VERSION = "0.9"
27
+
28
+ FILE_COLOUR = "\033[1;31m"
29
+ MATCH_COLOUR = "\033[1;37m\033[41m"
30
+ CLEAR_COLOURS = "\033[0m"
31
+
32
+ VERSION_INFO=<<END
33
+ rak #{VERSION}
34
+
35
+ Copyright 2008 Daniel Lucraft, all rights reserved.
36
+ Based on the perl tool 'ack' by Andy Lester.
37
+
38
+ This program is free software; you can redistribute it and/or modify it
39
+ under the same terms as Ruby.
40
+ END
41
+
42
+ FILE_TYPES = {
43
+ :asm => %w( .s .S ),
44
+ :cc => %w( .c .h .xs ),
45
+ :cpp => %w( .cpp .m .h .C .H ),
46
+ :csharp => %w( .cs ),
47
+ :css => %w( .css ),
48
+ :elisp => %w( .el ),
49
+ :haskell => %w( .hs .lhs ),
50
+ :hh => %w( .h ),
51
+ :html => %w( .htm .html .shtml ),
52
+ :lisp => %w( .lisp ),
53
+ :java => %w( .java properties ),
54
+ :js => %w( .js ),
55
+ :jsp => %w( .jsp .jspx .jhtm .jhtml ),
56
+ :make => %w( Makefile ),
57
+ :mason => %w( .mas .mhtml .mpl .mtxt ),
58
+ :ocaml => %w( .ml .mli ),
59
+ :parrot => %w( .pir .pasm .pmc .ops .pod .pg .tg ),
60
+ :perl => %w( .pl .pm .pod .t ),
61
+ :php => %w( .php .phpt .php3 .php4 .php5 ),
62
+ :prolog => %w( .pl .ecl ),
63
+ :python => %w( .py ),
64
+ :ruby => %w( .rb .rhtml .rjs .rxml Rakefile .rake .erb .haml),
65
+ :scheme => %w( .scm ),
66
+ :shell => %w( .sh .bash .csh .ksh .zsh ),
67
+ :sql => %w( .sql .ctl ),
68
+ :tcl => %w( .tcl ),
69
+ :tex => %w( .tex .cls .sty ),
70
+ :text => %w( .txt .text ),
71
+ :tt => %w( .tt .tt2 .ttml ),
72
+ :vb => %w( .bas .cls .frm .ctl .vb .resx ),
73
+ :vim => %w( .vim ),
74
+ :yaml => %w( .yaml .yml ),
75
+ :xml => %w( .xml .dtd .xslt )
76
+ }
77
+
78
+ VC_DIRS = %w(blib CVS _darcs .git .pc RCS SCCS .svn pkg)
79
+
80
+ class << self
81
+ attr_reader :opt
82
+ end
83
+
84
+ def self.search
85
+ @opt = {}
86
+
87
+ # file types
88
+ opt[:includes] = []
89
+ opt[:excludes] = []
90
+
91
+ FILE_TYPES.each do |type, exts|
92
+ if ARGV.delete('--'+type.to_s)
93
+ exts.each do |ext|
94
+ opt[:includes] << ext
95
+ end
96
+ end
97
+ if ARGV.delete('--no'+type.to_s)
98
+ exts.each do |ext|
99
+ opt[:excludes] << ext
100
+ end
101
+ end
102
+ end
103
+
104
+ opts = GetoptLong.new(
105
+ [ '--help', GetoptLong::OPTIONAL_ARGUMENT ],
106
+ [ '--max-count', '-m', GetoptLong::REQUIRED_ARGUMENT ],
107
+ [ '--files', '-f', GetoptLong::NO_ARGUMENT ],
108
+ [ '--output', GetoptLong::REQUIRED_ARGUMENT ],
109
+ [ '--version', GetoptLong::NO_ARGUMENT ],
110
+ [ '-c', '--count', GetoptLong::NO_ARGUMENT ],
111
+ [ '-h', '--no-filename', GetoptLong::NO_ARGUMENT ],
112
+ [ '-i', '--ignore-case', GetoptLong::NO_ARGUMENT ],
113
+ [ '-v', '--invert-match', GetoptLong::NO_ARGUMENT ],
114
+ [ '-n', GetoptLong::NO_ARGUMENT ],
115
+ [ '-Q', '--literal', GetoptLong::NO_ARGUMENT ],
116
+ [ '-o', GetoptLong::NO_ARGUMENT ],
117
+ [ '-w', '--word-regexp', GetoptLong::NO_ARGUMENT ],
118
+ [ '--pager', GetoptLong::REQUIRED_ARGUMENT ],
119
+ [ '--nopager', GetoptLong::NO_ARGUMENT ],
120
+ [ '--group', GetoptLong::NO_ARGUMENT ],
121
+ [ '--nogroup', GetoptLong::NO_ARGUMENT ],
122
+ [ '-l', '--files-with-matches', GetoptLong::NO_ARGUMENT ],
123
+ [ '-L', '--files-without-matches', GetoptLong::NO_ARGUMENT ],
124
+ [ '--passthru', GetoptLong::NO_ARGUMENT ],
125
+ [ '-H', '--with-filename', GetoptLong::NO_ARGUMENT ],
126
+ [ '--colour', GetoptLong::NO_ARGUMENT ],
127
+ [ '--nocolour', GetoptLong::NO_ARGUMENT ],
128
+ [ '--color', GetoptLong::NO_ARGUMENT ],
129
+ [ '--nocolor', GetoptLong::NO_ARGUMENT ],
130
+ [ '-a', '--all', GetoptLong::NO_ARGUMENT ],
131
+ [ '--type', GetoptLong::REQUIRED_ARGUMENT ],
132
+ [ '--sort-files', GetoptLong::NO_ARGUMENT ],
133
+ [ '--follow', GetoptLong::NO_ARGUMENT ],
134
+ [ '--nofollow', GetoptLong::NO_ARGUMENT ],
135
+ [ '--after-context', '-A', GetoptLong::REQUIRED_ARGUMENT ],
136
+ [ '--before-context', '-B', GetoptLong::REQUIRED_ARGUMENT ],
137
+ [ '--context', '-C', GetoptLong::OPTIONAL_ARGUMENT ],
138
+ [ '-g', GetoptLong::REQUIRED_ARGUMENT ],
139
+ [ '-x', '--line-regexp', GetoptLong::NO_ARGUMENT ],
140
+ [ '-s', '--line-start', GetoptLong::NO_ARGUMENT ],
141
+ [ '-e', '--line-end', GetoptLong::NO_ARGUMENT ]
142
+ )
143
+
144
+ dir = nil
145
+
146
+ opt[:max_count] = nil
147
+ opt[:do_search] = true
148
+ opt[:print_filename] = true
149
+ opt[:print_line_number] = true
150
+ opt[:print_filelist] = false
151
+ opt[:print_output] = nil
152
+ opt[:print_highlighted] = true
153
+ opt[:print_num_matches] = false
154
+ opt[:ignore_case] = false
155
+ opt[:invert_match] = false
156
+ opt[:descend] = true
157
+ opt[:literal] = false
158
+ opt[:print_match] = false
159
+ opt[:match_whole_words] = false
160
+ opt[:match_whole_lines] = false
161
+ opt[:match_line_starts] = false
162
+ opt[:match_line_ends] = false
163
+ opt[:pager] = nil
164
+ opt[:nopager] = false
165
+ opt[:print_file_each_line] = false
166
+ opt[:print_file_if_match] = false
167
+ opt[:print_file_if_no_match] = false
168
+ opt[:print_entire_line_if_no_match] = false
169
+ opt[:colour] = true
170
+ opt[:all_files] = false
171
+ opt[:sort_files] = false
172
+ opt[:follow_symlinks] = false
173
+ opt[:after_context] = 0
174
+ opt[:before_context] = 0
175
+ opt[:collect_context] = false
176
+ opt[:filename_regex] = nil
177
+
178
+ # if redirected (RAK_TEST allows us to redirect in testing and still
179
+ # get the non-redirected defaults).
180
+ if STDOUT.isatty == false and ENV['RAK_TEST'] != "true"
181
+ opt[:colour] = false
182
+ opt[:print_file_each_line] = true
183
+ opt[:print_filename] = false
184
+ opt[:print_line_number] = false
185
+ end
186
+
187
+ begin
188
+ opts.each do |option, arg|
189
+ case option
190
+ when '--help'
191
+ if arg == ""
192
+ puts USAGE_HELP
193
+ elsif arg == "types" or arg == "type"
194
+ puts TYPES_HELP
195
+ end
196
+ exit
197
+ when '--max-count'
198
+ opt[:max_count] = arg.to_i
199
+ when '--files'
200
+ opt[:do_search] = false
201
+ opt[:print_filelist] = true
202
+ when '--output'
203
+ opt[:print_filename] = false
204
+ opt[:print_line_number] = false
205
+ opt[:print_output] = arg
206
+ opt[:print_highlighted] = false
207
+ when '--version'
208
+ puts VERSION_INFO
209
+ exit
210
+ when '-c'
211
+ opt[:print_num_matches] = true
212
+ opt[:print_filename] = false
213
+ opt[:print_line_number] = false
214
+ opt[:print_highlighted] = false
215
+ when '-h'
216
+ opt[:print_filename] = false
217
+ opt[:print_line_number] = false
218
+ opt[:print_file_each_line] = false
219
+ when '-i'
220
+ opt[:ignore_case] = true
221
+ when '-v'
222
+ opt[:invert_match] = true
223
+ when '-n'
224
+ opt[:descend] = false
225
+ when '-Q'
226
+ opt[:literal] = true
227
+ when '-o'
228
+ opt[:print_match] = true
229
+ opt[:print_filename] = false
230
+ opt[:print_line_number] = false
231
+ opt[:print_highlighted] = false
232
+ when '-w'
233
+ opt[:match_whole_words] = true
234
+ when '--pager'
235
+ opt[:pager] = arg.to_s
236
+ when '--nopager'
237
+ opt[:nopager] = true
238
+ when '--group'
239
+ opt[:print_filename] = true
240
+ opt[:print_file_each_line] = false
241
+ when '--nogroup'
242
+ opt[:print_file_each_line] = true
243
+ opt[:print_filename] = false
244
+ opt[:print_line_number] = false
245
+ when '-l'
246
+ opt[:print_filename] = false
247
+ opt[:print_line_number] = false
248
+ opt[:print_highlighted] = false
249
+ opt[:print_file_if_match] = true
250
+ when '-L'
251
+ opt[:print_filename] = false
252
+ opt[:print_line_number] = false
253
+ opt[:print_highlighted] = false
254
+ opt[:print_file_if_no_match] = true
255
+ when '--passthru'
256
+ opt[:print_entire_line_if_no_match] = true
257
+ when '-H'
258
+ opt[:print_filename] = true
259
+ opt[:print_line_number] = true
260
+ when '--nocolour', '--nocolor'
261
+ opt[:colour] = false
262
+ when '--colour', '--color'
263
+ opt[:colour] = true
264
+ when '-a'
265
+ opt[:all_files] = true
266
+ when '--type'
267
+ if arg[0..1] == "no"
268
+ type = arg[2..-1]
269
+ arr = opt[:excludes]
270
+ else
271
+ type = arg
272
+ arr = opt[:includes]
273
+ end
274
+ exts = FILE_TYPES[type.intern]
275
+ unknown_type(type) unless exts
276
+ exts.each do |ext|
277
+ arr << ext
278
+ end
279
+ when '--sort-files'
280
+ opt[:sort_files] = true
281
+ when '--follow'
282
+ opt[:follow_symlinks] = true
283
+ when '--nofollow'
284
+ opt[:follow_symlinks] = false
285
+ when '--after-context'
286
+ opt[:use_context] = true
287
+ opt[:after_context] = arg.to_i
288
+ when '--before-context'
289
+ opt[:use_context] = true
290
+ opt[:before_context] = arg.to_i
291
+ when '--context'
292
+ opt[:use_context] = true
293
+ if arg == ""
294
+ val = 2
295
+ else
296
+ val = arg.to_i
297
+ end
298
+ opt[:before_context] = val
299
+ opt[:after_context] = val
300
+ when '-g'
301
+ opt[:filename_regex] = arg
302
+ when '-x'
303
+ opt[:match_whole_lines] = true
304
+ when '-s'
305
+ opt[:match_line_starts] = true
306
+ when '-e'
307
+ opt[:match_line_ends] = true
308
+ end
309
+ end
310
+ rescue GetoptLong::InvalidOption => ex
311
+ puts "rak: see rak --help for usage."
312
+ exit
313
+ rescue SystemExit
314
+ exit
315
+ end
316
+
317
+ if ARGV.empty? and opt[:print_filelist] == false
318
+ puts USAGE_HELP
319
+ exit
320
+ end
321
+
322
+ files = get_files
323
+
324
+ compile_match_file
325
+
326
+ unless opt[:colour]
327
+ FILE_COLOUR.replace ""
328
+ CLEAR_COLOURS.replace ""
329
+ MATCH_COLOUR.replace ""
330
+ end
331
+
332
+ run_pager(opt[:pager]) unless opt[:nopager]
333
+ print_files(files) if opt[:print_filelist]
334
+ search2(ARGV[0], files) if opt[:do_search]
335
+ end
336
+
337
+ def self.print_files(files)
338
+ files.each do |fn|
339
+ puts fn
340
+ end
341
+ end
342
+
343
+ def self.get_all_files(files=[], dir="./")
344
+ Dir[dir+"*"].each do |fn|
345
+ next if fn == ".."
346
+ if File.directory? fn
347
+ if opt[:descend]
348
+ if File.symlink? fn
349
+ if opt[:follow_symlinks]
350
+ get_all_files(files, fn+"/")
351
+ end
352
+ else
353
+ get_all_files(files, fn+"/")
354
+ end
355
+ end
356
+ else
357
+ if !File.socket? fn and File.readable? fn
358
+ if File.symlink? fn
359
+ if opt[:follow_symlinks]
360
+ files << fn
361
+ end
362
+ else
363
+ files << fn
364
+ end
365
+ end
366
+ end
367
+ end
368
+ files
369
+ end
370
+
371
+ def self.get_files(dir="./")
372
+ types_files = FILE_TYPES.invert
373
+ types = types_files.keys.flatten
374
+ files_and_dirs = ARGV[1..-1]
375
+ io_files = []
376
+ if !files_and_dirs or files_and_dirs.empty?
377
+ if STDIN.isatty == false
378
+ io_files << STDIN
379
+ opt[:print_filename] = false
380
+ files = []
381
+ else
382
+ files = get_all_files
383
+ end
384
+ else
385
+ files = []
386
+ files_and_dirs.each do |thing|
387
+ if File.file? thing
388
+ files << thing
389
+ end
390
+ if File.directory? thing
391
+ get_all_files(files, thing)
392
+ end
393
+ end
394
+ if files_and_dirs.length == 1 and
395
+ File.file? files_and_dirs[0]
396
+ opt[:print_filename] = false
397
+ end
398
+ end
399
+
400
+ # strip out based on extension
401
+ shebang_files = []
402
+ unless opt[:all_files]
403
+ files = files.select do |fn|
404
+ fn[-1..-1] != "~" and
405
+ fn[-1..-1] != "#"
406
+ end
407
+ new_files = []
408
+ files.each do |fn|
409
+ if types.any? {|ext| File.extname(fn) == ext or fn.split("/").last == ext}
410
+ new_files << fn
411
+ elsif ext = shebang_matches(fn)
412
+ shebang_files << [fn, ext]
413
+ end
414
+ end
415
+ files = new_files
416
+ end
417
+
418
+ # select based on file type
419
+ if not opt[:includes].empty?
420
+ files = files.select do |fn|
421
+ opt[:includes].any? {|ext| fn.include? ext}
422
+ end
423
+ shebang_files2 = []
424
+ shebang_files.each do |fn, ext|
425
+ if opt[:includes].include? ext
426
+ shebang_files2 << [fn, ext]
427
+ end
428
+ end
429
+ else
430
+ shebang_files2 = shebang_files
431
+ end
432
+
433
+ if not opt[:excludes].empty?
434
+ files = files.select do |fn|
435
+ not opt[:excludes].any? {|ext| fn.include? ext}
436
+ end
437
+ shebang_files3 = []
438
+ shebang_files2.each do |fn, ext|
439
+ unless opt[:excludes].any? {|ext| fn.include? ext}
440
+ shebang_files3 << [fn, ext]
441
+ end
442
+ end
443
+ else
444
+ shebang_files3 = shebang_files2
445
+ end
446
+
447
+ files += shebang_files3.map {|fn, ext| fn}
448
+
449
+ # reject version control dirs
450
+ files.reject! do |fn|
451
+ VC_DIRS.any? {|vc| fn.include? vc}
452
+ end
453
+
454
+ # filter based on -g=REGEX
455
+ if opt[:filename_regex]
456
+ if $use_onig
457
+ fn_re = Oniguruma::ORegexp.new(opt[:filename_regex])
458
+ else
459
+ fn_re = Regexp.new(opt[:filename_regex])
460
+ end
461
+ files = files.select {|fn| fn =~ fn_re }
462
+ end
463
+
464
+ # remove the "./"
465
+ files.map! {|fn| fn[0..1] == "./" ? fn[2..-1] : fn}
466
+
467
+ if opt[:sort_files]
468
+ files = files.sort_by {|fn| fn.downcase}
469
+ end
470
+
471
+ files + io_files
472
+ end
473
+
474
+ def self.shebang_matches(fn)
475
+ File.open(fn) do |f|
476
+ begin
477
+ line = f.readline
478
+ rescue
479
+ line = ""
480
+ end
481
+ if line =~ /^#!/
482
+ if line =~ /\b(ruby|perl|php|python)\b/
483
+ return "."+FILE_TYPES[$1.intern].first
484
+ elsif line =~ /\b(bash|csh|ksh|zsh)\b/
485
+ return ".sh"
486
+ end
487
+ else
488
+ if line =~ /\Q<?xml /
489
+ return ".xml"
490
+ end
491
+ end
492
+ return false
493
+ end
494
+ end
495
+
496
+ def self.search2(str, files)
497
+ if opt[:literal]
498
+ str = Regexp.quote(str)
499
+ end
500
+ if opt[:match_whole_words]
501
+ str = "\\b" + str + "\\b"
502
+ end
503
+ if opt[:match_whole_lines]
504
+ str = "^" + str + "$"
505
+ end
506
+ if opt[:match_line_starts]
507
+ str = "^" + str
508
+ end
509
+ if opt[:match_line_ends]
510
+ str = str + "$"
511
+ end
512
+ if $use_onig
513
+ if opt[:ignore_case]
514
+ re = Oniguruma::ORegexp.new(str, :options => Oniguruma::OPTION_IGNORECASE)
515
+ else
516
+ re = Oniguruma::ORegexp.new(str)
517
+ end
518
+ else
519
+ if opt[:ignore_case]
520
+ re = Regexp.new(str, Regexp::IGNORECASE)
521
+ else
522
+ re = Regexp.new(str)
523
+ end
524
+ end
525
+ files.each do |fn|
526
+ match_file(re, fn)
527
+ end
528
+ end
529
+
530
+ # this is tricky thing. Ignore the "code <<" to see the
531
+ # logic. Because the options never change we conditionally compile
532
+ # the match method based on them. This is gives a 2x speedup over
533
+ # the alternative.
534
+ def self.compile_match_file
535
+ code = ""
536
+ def code.<<(str)
537
+ self.concat(str + "\n")
538
+ end
539
+ code << %{def self.match_file(re, fn) }
540
+ code << %{ displayed_filename = false }
541
+ code << %{ count = 0 }
542
+ code << %{ i = 0 }
543
+ code << %{ matches = [] }
544
+ code << %{ print_num = 0 }
545
+
546
+ if opt[:before_context] > 0
547
+ code << %{ before_context = [] }
548
+ code << %{ before_context_size = opt[:before_context] }
549
+ end
550
+ code << %{ if fn.is_a? String }
551
+ code << %{ f = File.open(fn, "r") }
552
+ code << %{ elsif fn.is_a? IO }
553
+ code << %{ f = fn }
554
+ code << %{ end }
555
+
556
+ code << %{ f.each_line do |line| }
557
+ code << %{ i += 1 }
558
+ code << %{ rest = line }
559
+ code << %{ while md = re.match(rest) }
560
+ code << %{ rest = md.post_match }
561
+ if opt[:print_output]
562
+ code << %{ matches << eval(opt[:print_output]) }
563
+ else
564
+ code << %{ matches << md }
565
+ end
566
+ if opt[:print_match]
567
+ code << %{ puts md.to_s }
568
+ end
569
+ code << %{ count += 1 }
570
+ code << %{ end }
571
+ if opt[:invert_match]
572
+ if opt[:print_filename]
573
+ code << %{ unless displayed_filename }
574
+ code << %{ puts FILE_COLOUR + fn + CLEAR_COLOURS }
575
+ code << %{ displayed_filename = true }
576
+ code << %{ end }
577
+ end
578
+ code << %{ if matches.empty? }
579
+ if opt[:print_highlighted]
580
+ if opt[:print_line_number]
581
+ code << %{ print "\#{i.to_s.rjust(4)}|" }
582
+ end
583
+ code << %{ puts "\#{line}" }
584
+ end
585
+ code << %{ end }
586
+ code << %{ matches.clear }
587
+ else
588
+ code << %{ if matches.empty? }
589
+ if opt[:print_entire_line_if_no_match]
590
+ code << %{ puts line }
591
+ end
592
+ if opt[:use_context]
593
+ if opt[:before_context] > 0
594
+ code << %{ if print_num == 0 }
595
+ code << %{ before_context << [i, line] }
596
+ code << %{ if before_context.length == before_context_size+1 }
597
+ code << %{ before_context = before_context[1..-1] }
598
+ code << %{ end }
599
+ code << %{ end }
600
+ end
601
+ code << %{ if print_num > 0 }
602
+ code << %{ print_num -= 1 }
603
+ if opt[:print_highlighted]
604
+ if opt[:print_line_number]
605
+ code << %{ print "\#{i.to_s.rjust(4)}|" }
606
+ end
607
+ code << %{ puts "\#{line}" }
608
+ code << %{ end }
609
+ end
610
+ end
611
+ code << %{ else }
612
+ code << %{ print_num = opt[:after_context] }
613
+ if opt[:print_filename]
614
+ code << %{ unless displayed_filename }
615
+ code << %{ puts FILE_COLOUR + fn + CLEAR_COLOURS }
616
+ code << %{ displayed_filename = true }
617
+ code << %{ end }
618
+ end
619
+ if opt[:before_context] > 0
620
+ code << %{ before_context.each do |before_i, before_line| }
621
+ if opt[:print_line_number]
622
+ code << %{ print "\#{before_i.to_s.rjust(4)}|" }
623
+ end
624
+ code << %{ puts before_line }
625
+ code << %{ end }
626
+ code << %{ before_context = [] }
627
+ end
628
+ if opt[:print_output]
629
+ code << %{ matches.each {|m| puts m} }
630
+ end
631
+ if opt[:print_highlighted]
632
+ if opt[:print_line_number]
633
+ code << %{ prefix = "\#{i.to_s.rjust(4)}|" }
634
+ else
635
+ if opt[:print_file_each_line]
636
+ code << %{ fn_str = (fn.is_a?(String) ? FILE_COLOUR + fn + CLEAR_COLOURS + " " : "") }
637
+ code << %{ prefix = fn_str + "\#{i.to_s.rjust(4)}|" }
638
+ else
639
+ code << %{ prefix = "" }
640
+ end
641
+ end
642
+ code << %{ print_highlighted(prefix, line, matches) }
643
+ end
644
+ code << %{ end }
645
+ if opt[:max_count]
646
+ code << %{ if count >= opt[:max_count] }
647
+ code << %{ break }
648
+ code << %{ end }
649
+ end
650
+ code << %{ matches.clear }
651
+ end
652
+ code << %{ end }
653
+ if opt[:print_highlighted] and opt[:print_filename]
654
+ code << %{ if count > 0 }
655
+ code << %{ puts }
656
+ code << %{ end }
657
+ end
658
+ code << %{ f.close if f === File }
659
+ if opt[:print_num_matches]
660
+ if opt[:invert_match]
661
+ code << %{ puts "\#{fn}:\#{i-count}" }
662
+ else
663
+ code << %{ puts "\#{fn}:\#{count}" }
664
+ end
665
+ end
666
+ if opt[:print_file_if_match]
667
+ code << %{ if count > 0 }
668
+ code << %{ puts fn }
669
+ code << %{ end }
670
+ end
671
+
672
+ if opt[:print_file_if_no_match]
673
+ code << %{ if count == 0 }
674
+ code << %{ puts fn }
675
+ code << %{ end }
676
+ end
677
+ code << %{end }
678
+ module_eval code
679
+ end
680
+
681
+ def self.print_highlighted(prefix, line, matches)
682
+ from1 = matches[0].begin(0)
683
+ pre = ""
684
+ pre = line[0..(from1-1)] if from1 > 0
685
+ print prefix
686
+ print pre
687
+ ptr = 0
688
+ 0.upto(matches.length-1) do |n|
689
+ from = matches[n].begin(0)
690
+ to = matches[n].end(0)
691
+ print MATCH_COLOUR +
692
+ line[(from+ptr)..(to+ptr-1)] +
693
+ CLEAR_COLOURS
694
+ ptr += to
695
+ if n == matches.length - 1
696
+ puts matches.last.post_match
697
+ else
698
+ print line[ptr..(matches[n+1].begin(0)-1+ptr)]
699
+ end
700
+ end
701
+ end
702
+
703
+ def self.unknown_type(type)
704
+ puts "rak: Unknown --type \"#{type}\""
705
+ puts "rak: See rak --help types"
706
+ exit
707
+ end
708
+
709
+ def self.run_pager(opt_pager = nil)
710
+ return if PLATFORM =~ /win32/
711
+ return unless STDOUT.tty?
712
+
713
+ read, write = IO.pipe
714
+
715
+ unless Kernel.fork # Child process
716
+ STDOUT.reopen(write)
717
+ STDERR.reopen(write) if STDERR.tty?
718
+ read.close
719
+ write.close
720
+ return
721
+ end
722
+
723
+ # Parent process, become pager
724
+ STDIN.reopen(read)
725
+ read.close
726
+ write.close
727
+
728
+ ENV['LESS'] = 'FSRX' # Don't page if the input is short enough
729
+
730
+ # wait until we have input before we start the pager
731
+ Kernel.select [STDIN]
732
+ pager = opt_pager || ENV['PAGER'] || 'less -r'
733
+ exec pager rescue exec "/bin/sh", "-c", pager
734
+ rescue
735
+ end
736
+ end
737
+
738
+ USAGE_HELP=<<END
739
+ Usage: rak [OPTION]... PATTERN [FILES]
740
+
741
+ Search for PATTERN in each source file in the tree from cwd on down.
742
+ If [FILES] is specified, then only those files/directories are checked.
743
+ rak will search STDIN only if no FILES are specified.
744
+
745
+ Example: rak -i select
746
+
747
+ Searching:
748
+ -i, --ignore-case Ignore case distinctions
749
+ -v, --invert-match Invert match: select non-matching lines
750
+ -w, --word-regexp Force PATTERN to match only whole words
751
+ -x, --line-regexp Force PATTERN to match only whole lines
752
+ -Q, --literal Quote all metacharacters; expr is literal
753
+ -s, --line-start Match only at the start of a line
754
+ -e, --line-start Match only at the end of a line
755
+
756
+ Search output:
757
+ -l, --files-with-matches
758
+ Only print filenames containing matches
759
+ -L, --files-without-match
760
+ Only print filenames with no match
761
+ -o Show only the part of a line matching PATTERN
762
+ (turns off text highlighting)
763
+ --passthru Print all lines, whether matching or not
764
+ --output=expr Output the evaluation of expr for each line
765
+ (turns off text highlighting)
766
+ -m, --max-count=NUM Stop searching in a file after NUM matches
767
+ -H, --with-filename Print the filename for each match
768
+ -h, --no-filename Suppress the prefixing filename on output
769
+ -c, --count Show number of lines matching per file
770
+
771
+ --pager=COMMAND Pipes all rak output through COMMAND.
772
+ (default: ENV['PAGER'] or "less -r")
773
+ --nopager Do not send output through a pager.
774
+
775
+ --group Group matches by file name.
776
+ (default: on when used interactively)
777
+ --nogroup One result per line, including filename, like grep
778
+ (default: on when the output is redirected)
779
+
780
+ --[no]colour Highlight the matching text (default: on unless
781
+ output is redirected, or on Windows)
782
+
783
+ -A NUM, --after-context=NUM
784
+ Print NUM lines of trailing context after matching
785
+ lines.
786
+ -B NUM, --before-context=NUM
787
+ Print NUM lines of leading context before matching
788
+ lines.
789
+ -C [NUM], --context[=NUM]
790
+ Print NUM lines (default 2) of output context.
791
+
792
+ File finding:
793
+ -f Only print the files found, without searching.
794
+ The PATTERN must not be specified.
795
+ --sort-files Sort the found files lexically.
796
+
797
+ File inclusion/exclusion:
798
+ -n No descending into subdirectories
799
+ -g REGEX Only search in file matching REGEX.
800
+ -a, --all All files, regardless of extension (but still skips
801
+ blib, pkg, CVS, _darcs, .git, .pc, RCS, SCCS and .svn dirs)
802
+ --ruby Include only Ruby files.
803
+ --type=ruby Include only Ruby files.
804
+ --noruby Exclude Ruby files.
805
+ --type=noruby Exclude Ruby files.
806
+ See "rak --help type" for supported filetypes.
807
+ --[no]follow Follow symlinks. Default is off.
808
+
809
+ Miscellaneous:
810
+ --help This help
811
+ --version Display version & copyright
812
+ END
813
+
814
+ TYPES_HELP=<<END
815
+ Usage: rak [OPTION]... PATTERN [FILES]
816
+
817
+ The following is the list of filetypes supported by rak. You can
818
+ specify a file type with the --type=TYPE format, or the --TYPE
819
+ format. For example, both --type=ruby and --ruby work.
820
+
821
+ Note that some extensions may appear in multiple types. For example,
822
+ .pod files are both Perl and Parrot.
823
+
824
+ --[no]asm .s
825
+ --[no]cc .c .h .xs
826
+ --[no]cpp .cpp .m .h
827
+ --[no]csharp .cs
828
+ --[no]css .css
829
+ --[no]elisp .el
830
+ --[no]erlang .erl
831
+ --[no]fortran .f .f77 .f90 .f95 .f03 .for .ftn .fpp
832
+ --[no]haskell .hs .lhs
833
+ --[no]hh .h
834
+ --[no]html .htm .html .shtml
835
+ --[no]java .java .properties
836
+ --[no]js .js
837
+ --[no]jsp .jsp .jspx .jhtm .jhtml
838
+ --[no]lisp .lisp
839
+ --[no]make Makefile
840
+ --[no]mason .mas .mhtml .mpl .mtxt
841
+ --[no]ocaml .ml .mli
842
+ --[no]parrot .pir .pasm .pmc .ops .pod .pg .tg
843
+ --[no]perl .pl .pm .pod .t
844
+ --[no]php .php .phpt .php3 .php4 .php5
845
+ --[no]prolog .pl .ecl
846
+ --[no]python .py
847
+ --[no]ruby .rb .rhtml .rjs .rxml Rakefile .rake .erb .haml
848
+ --[no]scheme .scm
849
+ --[no]shell .sh .bash .csh .ksh .zsh
850
+ --[no]sql .sql .ctl
851
+ --[no]tcl .tcl
852
+ --[no]tex .tex .cls .sty
853
+ --[no]text .txt .text
854
+ --[no]tt .tt .tt2 .ttml
855
+ --[no]vb .bas .cls .frm .ctl .vb .resx
856
+ --[no]vim .vim
857
+ --[no]xml .xml .dtd .xslt
858
+ --[no]yaml .yaml .yml
859
+ END
860
+
861
+ Rak.search