rak 0.8.0

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