di 0.1.8 → 0.1.9

Sign up to get free protection for your applications and to get access to all the features.
Files changed (6) hide show
  1. data/README.rdoc +12 -9
  2. data/Rakefile +1 -1
  3. data/bin/di +6 -818
  4. data/di.gemspec +5 -4
  5. data/lib/di.rb +822 -0
  6. metadata +12 -4
data/README.rdoc CHANGED
@@ -37,20 +37,23 @@ Run di --help for help.
37
37
 
38
38
  == USAGE:
39
39
 
40
- Just use di(1) for diff(1).
40
+ Just use di(1) for diff(1) as a wrapper.
41
41
 
42
- If you want to use this with svn, you should have a non-paginating
43
- version of di because svn calls the external diff for each changed
44
- file:
42
+ There are a few occations you shouldn't simply substitute di(1) for
43
+ diff(1). For example, svn calls the external diff for each changed
44
+ file, so the pagination enabled by default would be annoying.
45
45
 
46
- ln di ndi
46
+ In such a case, you can have a non-paginating version of di as below:
47
47
 
48
- With a default option set in your shell's startup file:
48
+ # Give it another name
49
+ ln di ndi
49
50
 
51
+ # Set default options for the new command; put these lines in your
52
+ # shell's startup file
50
53
  NDI_OPTIONS=--no-pager
51
54
  export NDI_OPTIONS
52
55
 
53
- Then finally set diff-cmd to the command in ~/.subversion/config.
56
+ Then set diff-cmd to the new command in ~/.subversion/config.
54
57
 
55
58
  [helpers]
56
59
  diff-cmd = ndi
@@ -63,9 +66,9 @@ Then finally set diff-cmd to the command in ~/.subversion/config.
63
66
 
64
67
  == INSTALL:
65
68
 
66
- gem install di
69
+ gem install di
67
70
 
68
- Or just put bin/di to a directory in your PATH.
71
+ Or just install lib/di.rb as "di" to a directory in your PATH.
69
72
 
70
73
  == COPYRIGHT:
71
74
 
data/Rakefile CHANGED
@@ -1,7 +1,7 @@
1
1
  require 'rubygems'
2
2
  require 'rake'
3
3
 
4
- load "bin/di"
4
+ load 'lib/di.rb'
5
5
 
6
6
  begin
7
7
  require 'jeweler'
data/bin/di CHANGED
@@ -1,822 +1,10 @@
1
1
  #!/usr/bin/env ruby
2
- # -*- mode: ruby; coding: utf-8 -*-
3
- #
4
- # di - a wrapper around GNU diff(1)
5
- #
6
- # Copyright (c) 2008, 2009, 2010 Akinori MUSHA
7
- #
8
- # All rights reserved.
9
- #
10
- # Redistribution and use in source and binary forms, with or without
11
- # modification, are permitted provided that the following conditions
12
- # are met:
13
- # 1. Redistributions of source code must retain the above copyright
14
- # notice, this list of conditions and the following disclaimer.
15
- # 2. Redistributions in binary form must reproduce the above copyright
16
- # notice, this list of conditions and the following disclaimer in the
17
- # documentation and/or other materials provided with the distribution.
18
- #
19
- # THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
20
- # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
- # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22
- # ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
23
- # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24
- # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25
- # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26
- # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27
- # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28
- # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29
- # SUCH DAMAGE.
30
2
 
31
- MYVERSION = "0.1.8"
32
- MYNAME = File.basename($0)
33
- MYCOPYRIGHT = "Copyright (c) 2008, 2009, 2010 Akinori MUSHA"
34
-
35
- DIFF_CMD = ENV.fetch('DIFF', 'diff')
36
- ENV_NAME = "#{MYNAME.tr('-a-z', '_A-Z')}_OPTIONS"
37
- EMPTYFILE = '/dev/null'
38
-
39
- RSYNC_EXCLUDE_GLOBS = %w(
40
- RCS SCCS CVS CVS.adm
41
- RCSLOG cvslog.* tags TAGS
42
- .make.state .nse_depinfo *~
43
- \#* .\#* ,* _$*
44
- *$ *.old *.bak *.BAK
45
- *.orig *.rej *.del-* *.a
46
- *.olb *.o *.obj *.so
47
- *.exe *.Z *.elc *.ln
48
- core .svn .git .bzr .hg
49
- )
50
-
51
- FIGNORE_GLOBS = ENV.fetch('FIGNORE', '').split(':').map { |pat|
52
- '*' + pat
53
- }
54
-
55
- def main(args)
56
- setup
57
-
58
- parse_args!(args)
59
-
60
- diff_main
61
-
62
- exit $status
63
- end
64
-
65
- def warn(*lines)
66
- lines.each { |line|
67
- STDERR.puts "#{MYNAME}: #{line}"
68
- }
69
- end
70
-
71
- def setup
72
- require 'ostruct'
73
- $diff = OpenStruct.new
74
- $diff.exclude = []
75
- $diff.include = []
76
- $diff.flags = []
77
- $diff.format_flags = []
78
- $diff.format = :normal
79
- $diff.custom_format_p = false
80
- $diff.use_pager = false
81
- $diff.colorize = false
82
- $diff.highlight_whitespace = true
83
- $diff.colors = {
84
- :comment => "\033[1m",
85
- :file1 => "\033[1m",
86
- :file2 => "\033[1m",
87
- :header => "\033[36m",
88
- :function => "\033[m",
89
- :new => "\033[32m",
90
- :old => "\033[31m",
91
- :changed => "\033[33m",
92
- :unchanged => "",
93
- :whitespace => "\033[41m",
94
- :off => "\033[m",
95
- }
96
- end
97
-
98
- def parse_args!(args)
99
- require 'optparse'
100
-
101
- banner = <<-"EOF"
102
- #{MYNAME} - a wrapper around GNU diff(1)
103
- version #{MYVERSION}
104
-
105
- usage: #{MYNAME} [flags] [files]
106
- EOF
107
-
108
- opts = OptionParser.new(banner) { |opts|
109
- miniTrueClass = Class.new
110
- hash = OptionParser::CompletingHash.new
111
- hash['-'] = false
112
- opts.accept(miniTrueClass, hash) {|arg, val| val == nil or val}
113
-
114
- opts.on('--[no-]pager',
115
- 'Pipe output into pager if stdout is a terminal. [!][*]') { |val|
116
- $diff.use_pager = val if $stdout.tty?
117
- }
118
- opts.on('--[no-]color',
119
- 'Colorize output if stdout is a terminal and the format is unified or context. [!][*]') { |val|
120
- $diff.colorize = val if $stdout.tty?
121
- }
122
- opts.on('--[no-]rsync-exclude', '--[no-]cvs-exclude',
123
- 'Exclude some kinds of files and directories a la rsync(1). [!][*]') { |val|
124
- $diff.rsync_exclude = val
125
- }
126
- opts.on('--[no-]ignore-cvs-lines',
127
- 'Ignore CVS keyword lines. [!][*]') { |val|
128
- $diff.ignore_cvs_lines = val
129
- }
130
- opts.on('--[no-]fignore-exclude',
131
- 'Ignore files having suffixes specified in FIGNORE. [!][*]') { |val|
132
- $diff.fignore_exclude = val
133
- }
134
- opts.on('-R', '--relative[=-]', miniTrueClass,
135
- 'Use relative path names. [*]') { |val|
136
- $diff.relative = val
137
- }
138
- opts.on('-i', '--ignore-case[=-]', miniTrueClass,
139
- 'Ignore case differences in file contents.') { |val|
140
- set_flag('-i', val)
141
- }
142
- # not supported (yet)
143
- #opts.on("--[no-]ignore-file-name-case",
144
- # "Ignore case when comparing file names.") { |val|
145
- # set_flag("--ignore-file-name-case", val)
146
- #}
147
- opts.on('-E', '--ignore-tab-expansion[=-]', miniTrueClass,
148
- 'Ignore changes due to tab expansion.') { |val|
149
- set_flag('-E', val)
150
- }
151
- opts.on('-b', '--ignore-space-change[=-]', miniTrueClass,
152
- 'Ignore changes in the amount of white space.') { |val|
153
- set_flag('-b', val)
154
- }
155
- opts.on('-w', '--ignore-all-space[=-]', miniTrueClass,
156
- 'Ignore all white space.') { |val|
157
- set_flag('-w', val)
158
- }
159
- opts.on('-B', '--ignore-blank-lines[=-]', miniTrueClass,
160
- 'Ignore changes whose lines are all blank.') { |val|
161
- set_flag('-B', val)
162
- }
163
- opts.on('-I RE', '--ignore-matching-lines=RE',
164
- 'Ignore changes whose lines all match RE.') { |val|
165
- set_flag('-I', val)
166
- }
167
- opts.on('--[no-]strip-trailing-cr',
168
- 'Strip trailing carriage return on input.') { |val|
169
- set_flag('--strip-trailing-cr', val)
170
- }
171
- opts.on('-a', '--text[=-]', miniTrueClass,
172
- 'Treat all files as text.') { |val|
173
- set_flag('-a', val)
174
- }
175
- opts.on('-c[NUM]', '--context[=NUM]', Integer,
176
- 'Output NUM (default 3) lines of copied context.') { |val|
177
- set_format_flag('-C', val ? val.to_s : '3')
178
- }
179
- opts.on('-C NUM', Integer,
180
- 'Output NUM lines of copied context.') { |val|
181
- set_format_flag('-C', val.to_s)
182
- }
183
- opts.on('-u[NUM]', '--unified[=NUM]', Integer,
184
- 'Output NUM (default 3) lines of unified context. [!]') { |val|
185
- set_format_flag('-U', val ? val.to_s : '3')
186
- }
187
- opts.on('-U NUM', Integer,
188
- 'Output NUM lines of unified context.') { |val|
189
- set_format_flag('-U', val.to_s)
190
- }
191
- opts.on('-L LABEL', '--label=LABEL',
192
- 'Use LABEL instead of file name.') { |val|
193
- set_flag('-L', val)
194
- }
195
- opts.on('-p', '--show-c-function[=-]', miniTrueClass,
196
- 'Show which C function each change is in. [!]') { |val|
197
- set_flag('-p', val)
198
- }
199
- opts.on('-F RE', '--show-function-line=RE',
200
- 'Show the most recent line matching RE.') { |val|
201
- set_flag('-F', val)
202
- }
203
- opts.on('-q', '--brief[=-]', miniTrueClass,
204
- 'Output only whether files differ.') { |val|
205
- set_flag('-q', val)
206
- }
207
- opts.on('-e', '--ed[=-]', miniTrueClass,
208
- 'Output an ed script.') { |val|
209
- if val
210
- set_format_flag('-e', val)
211
- end
212
- }
213
- opts.on('--normal[=-]', miniTrueClass,
214
- 'Output a normal diff.') { |val|
215
- if val
216
- set_format_flag('--normal', val)
217
- end
218
- }
219
- opts.on('-n', '--rcs[=-]', miniTrueClass,
220
- 'Output an RCS format diff.') { |val|
221
- if val
222
- set_format_flag('-n', val)
223
- end
224
- }
225
- opts.on('-y', '--side-by-side[=-]', miniTrueClass,
226
- 'Output in two columns.') { |val|
227
- if val
228
- set_format_flag('-y', val)
229
- end
230
- }
231
- opts.on('-W NUM', '--width=NUM', Integer,
232
- 'Output at most NUM (default 130) print columns.') { |val|
233
- set_flag('-W', val.to_s)
234
- }
235
- opts.on('--left-column[=-]', miniTrueClass,
236
- 'Output only the left column of common lines.') { |val|
237
- set_flag('--left-column', val)
238
- }
239
- opts.on('--suppress-common-lines[=-]', miniTrueClass,
240
- 'Do not output common lines.') { |val|
241
- set_flag('--suppress-common-lines', val)
242
- }
243
- opts.on('-D NAME', '--ifdef=NAME',
244
- 'Output merged file to show `#ifdef NAME\' diffs.') { |val|
245
- set_format_flag('-D', val)
246
- }
247
- opts.on('--old-group-format=GFMT',
248
- 'Format old input groups with GFMT.') { |val|
249
- set_custom_format_flag('--old-group-format', val)
250
- }
251
- opts.on('--new-group-format=GFMT',
252
- 'Format new input groups with GFMT.') { |val|
253
- set_custom_format_flag('--new-group-format', val)
254
- }
255
- opts.on('--changed-group-format=GFMT',
256
- 'Format changed input groups with GFMT.') { |val|
257
- set_custom_format_flag('--changed-group-format', val)
258
- }
259
- opts.on('--unchanged-group-format=GFMT',
260
- 'Format unchanged input groups with GFMT.') { |val|
261
- set_custom_format_flag('--unchanged-group-format', val)
262
- }
263
- opts.on('--line-format=LFMT',
264
- 'Format all input lines with LFMT.') { |val|
265
- set_custom_format_flag('--line-format', val)
266
- }
267
- opts.on('--old-line-format=LFMT',
268
- 'Format old input lines with LFMT.') { |val|
269
- set_custom_format_flag('--old-line-format', val)
270
- }
271
- opts.on('--new-line-format=LFMT',
272
- 'Format new input lines with LFMT.') { |val|
273
- set_custom_format_flag('--new-line-format', val)
274
- }
275
- opts.on('--unchanged-line-format=LFMT',
276
- 'Format unchanged input lines with LFMT.') { |val|
277
- set_custom_format_flag('--unchanged-line-format', val)
278
- }
279
- opts.on('-l', '--paginate[=-]', miniTrueClass,
280
- 'Pass the output through `pr\' to paginate it.') { |val|
281
- set_flag('-l', val)
282
- }
283
- opts.on('-t', '--expand-tabs[=-]', miniTrueClass,
284
- 'Expand tabs to spaces in output.') { |val|
285
- set_flag('-t', val)
286
- }
287
- opts.on('-T', '--initial-tab[=-]', miniTrueClass,
288
- 'Make tabs line up by prepending a tab.') { |val|
289
- set_flag('-T', '--initial-tab', val)
290
- }
291
- opts.on('--tabsize=NUM', Integer,
292
- 'Tab stops are every NUM (default 8) print columns.') { |val|
293
- set_flag('--tabsize', val.to_s)
294
- }
295
- opts.on('-r', '--recursive[=-]', miniTrueClass,
296
- 'Recursively compare any subdirectories found. [!]') { |val|
297
- set_flag('-r', val)
298
- $diff.recursive = val
299
- }
300
- opts.on('-N', '--[no-]new-file[=-]', miniTrueClass,
301
- 'Treat absent files as empty. [!]') { |val|
302
- set_flag('-N', val)
303
- $diff.new_file = val
304
- }
305
- opts.on('--unidirectional-new-file[=-]', miniTrueClass,
306
- 'Treat absent first files as empty.') { |val|
307
- set_flag('--unidirectional-new-file', val)
308
- }
309
- opts.on('-s', '--report-identical-files[=-]', miniTrueClass,
310
- 'Report when two files are the same.') { |val|
311
- set_flag('-s', val)
312
- }
313
- opts.on('-x PAT', '--exclude=PAT',
314
- 'Exclude files that match PAT.') { |val|
315
- $diff.exclude << val
316
- }
317
- opts.on('-X FILE', '--exclude-from=FILE',
318
- 'Exclude files that match any pattern in FILE.') { |val|
319
- if val == '-'
320
- $diff.exclude.concat(STDIN.read.split(/\n/))
321
- else
322
- $diff.exclude.concat(File.read(val).split(/\n/))
323
- end
324
- }
325
- opts.on('--include=PAT',
326
- 'Do not exclude files that match PAT.') { |val|
327
- $diff.include << val
328
- }
329
- opts.on('-S FILE', '--starting-file=FILE',
330
- 'Start with FILE when comparing directories.') { |val|
331
- set_flag('-S', val)
332
- }
333
- opts.on('--from-file=FILE1',
334
- 'Compare FILE1 to all operands. FILE1 can be a directory.') { |val|
335
- $diff.from_files = [val]
336
- }
337
- opts.on('--to-file=FILE2',
338
- 'Compare all operands to FILE2. FILE2 can be a directory.') { |val|
339
- $diff.to_files = [val]
340
- }
341
- opts.on('--horizon-lines=NUM', Integer,
342
- 'Keep NUM lines of the common prefix and suffix.') { |val|
343
- set_flag('--horizon-lines', val.to_s)
344
- }
345
- opts.on('-d', '--minimal[=-]', miniTrueClass,
346
- 'Try hard to find a smaller set of changes. [!]') { |val|
347
- set_flag('-d', val)
348
- }
349
- opts.on('--speed-large-files[=-]', miniTrueClass,
350
- 'Assume large files and many scattered small changes.') { |val|
351
- set_flag('--speed-large-files', val)
352
- }
353
- opts.on('-v', '--version',
354
- 'Output version info.') { |val|
355
- print <<-"EOF"
356
- #{MYNAME} version #{MYVERSION}
357
- #{MYCOPYRIGHT}
358
-
359
- ----
360
- EOF
361
- system(DIFF_CMD, '--version')
362
- exit
363
- }
364
- opts.on('--help',
365
- 'Output this help.') { |val|
366
- invoke_pager
367
- print opts, <<EOS
368
- Options without the [*] sign will be passed through to diff(1).
369
- Options marked as [!] sign are turned on by default. To turn them off,
370
- specify -?- for short options and --no-??? for long options, respectively.
371
-
372
- Environment variables:
373
- EOS
374
- [
375
- ['DIFF', 'Path to diff(1)'],
376
- [ENV_NAME, 'User\'s preferred default options'],
377
- ['PAGER', 'Path to pager (more(1) is used if not defined)'],
378
- ].each { |name, description|
379
- printf " %-14s %s\n", name, description
380
- }
381
- exit 0
382
- }
383
- }
384
-
385
- begin
386
- opts.parse('--rsync-exclude', '--fignore-exclude', '--ignore-cvs-lines',
387
- '--pager', '--color',
388
- '-U3', '-N', '-r', '-p', '-d')
389
-
390
- if value = ENV[ENV_NAME]
391
- require 'shellwords'
392
- opts.parse(*value.shellsplit)
393
- end
394
-
395
- opts.parse!(args)
396
-
397
- $diff.format_flags.each { |format_flag|
398
- set_flag(*format_flag)
399
- }
400
-
401
- if $diff.ignore_cvs_lines
402
- opts.parse('--ignore-matching-lines="^[^\x1b]*\$[A-Z][A-Za-z0-9][A-Za-z0-9]*\(:.*\)\{0,1\}\$')
403
- end
404
- rescue OptionParser::ParseError => e
405
- warn e, "Try `#{MYNAME} --help' for more information."
406
- exit 64
407
- rescue => e
408
- warn e
409
- exit 1
410
- end
411
-
412
- begin
413
- if $diff.from_files
414
- $diff.to_files ||= args.dup
415
-
416
- if $diff.to_files.empty?
417
- raise "missing operand"
418
- end
419
- elsif $diff.to_files
420
- $diff.from_files = args.dup
421
-
422
- if $diff.from_files.empty?
423
- raise "missing operand"
424
- end
425
- else
426
- if args.size < 2
427
- raise "missing operand"
428
- end
429
-
430
- if File.directory?(args[0])
431
- $diff.to_files = args.dup
432
- $diff.from_files = [$diff.to_files.shift]
433
- else
434
- $diff.from_files = args.dup
435
- $diff.to_files = [$diff.from_files.pop]
436
- end
437
- end
438
-
439
- if $diff.from_files.size != 1 && $diff.to_files.size != 1
440
- raise "wrong number of files given"
441
- end
442
- rescue => e
443
- warn e, "Try `#{MYNAME} --help' for more information."
444
- exit 64
445
- end
446
- end
447
-
448
- def invoke_pager
449
- invoke_pager! if $diff.use_pager
450
- end
451
-
452
- def invoke_pager!
453
- $stdout.flush
454
- $stderr.flush
455
- pr, pw = IO.pipe
456
- ppid = Process.pid
457
- pid = fork {
458
- $stdin.reopen(pr)
459
- pr.close
460
- pw.close
461
- IO.select([$stdin], nil, [$stdin])
462
- if system(ENV['PAGER'] || 'more')
463
- Process.kill(:TERM, ppid)
464
- else
465
- $stderr.puts "Pager failed."
466
- Process.kill(:INT, ppid)
467
- end
468
- }
469
- trap(:TERM) { exit(0) }
470
- trap(:INT) { exit(130) }
471
- $stdout.reopen(pw)
472
- $stderr.reopen(pw) if $stderr.tty?
473
- pw.close
474
- at_exit {
475
- $stdout.flush
476
- $stderr.flush
477
- $stdout.close
478
- $stderr.close
479
- Process.waitpid(pid)
480
- }
481
- end
482
-
483
- def set_flag(flag, val)
484
- case val
485
- when false
486
- $diff.flags.reject! { |f,| f == flag }
487
- when true
488
- $diff.flags.reject! { |f,| f == flag }
489
- $diff.flags << [flag]
490
- else
491
- $diff.flags << [flag, val]
492
- end
493
- end
494
-
495
- def set_format_flag(flag, *val)
496
- $diff.format_flags.clear
497
- $diff.custom_format_p = false
498
- case flag
499
- when '-C'
500
- $diff.format = :context
501
- when '-U'
502
- $diff.format = :unified
503
- when '-e'
504
- $diff.format = :ed
505
- when '--normal'
506
- $diff.format = :normal
507
- when '-n'
508
- $diff.format = :rcs
509
- when '-y'
510
- $diff.format = :side_by_side
511
- when '-D'
512
- $diff.format = :ifdef
513
- else
514
- $diff.format = :unknown
515
- end
516
- $diff.format_flags.push([flag, *val])
517
- end
518
-
519
- def set_custom_format_flag(flag, *val)
520
- if !$diff.custom_format_p
521
- $diff.format_flags.clear
522
- $diff.custom_format_p = true
523
- end
524
- $diff.format = :custom
525
- $diff.format_flags.push([flag, *val])
526
- end
527
-
528
- def diff_main
529
- invoke_pager
530
-
531
- $status = 0
532
-
533
- $diff.from_files.each { |from_file|
534
- if File.directory?(from_file)
535
- $diff.to_files.each { |to_file|
536
- if File.directory?(to_file)
537
- if $diff.relative
538
- to_file = File.expand_path(from_file, to_file)
539
- end
540
-
541
- diff_dirs(from_file, to_file)
542
- else
543
- if $diff.relative
544
- from_file = File.expand_path(to_file, from_file)
545
- else
546
- from_file = File.expand_path(File.basename(to_file), from_file)
547
- end
548
-
549
- diff_files(from_file, to_file)
550
- end
551
- }
552
- else
553
- $diff.to_files.each { |to_file|
554
- if File.directory?(to_file)
555
- if $diff.relative
556
- to_file = File.expand_path(from_file, to_file)
557
- else
558
- to_file = File.expand_path(File.basename(from_file), to_file)
559
- end
560
- end
561
-
562
- diff_files(from_file, to_file)
563
- }
564
- end
565
- }
566
- end
567
-
568
- def diff_files(file1, file2)
569
- if file1.is_a?(Array)
570
- file2.is_a?(Array) and raise "cannot compare two sets of multiple files"
571
- file1.empty? and return 0
572
-
573
- call_diff('--to-file', file2, '--', file1)
574
- elsif file2.is_a?(Array)
575
- file1.empty? and return 0
576
-
577
- call_diff('--from-file', file1, '--', file2)
578
- else
579
- call_diff('--', file1, file2)
580
- end
581
- end
582
-
583
- def call_diff(*args)
584
- command_args = [DIFF_CMD, $diff.flags, args].flatten
585
- if $diff.colorize
586
- case $diff.format
587
- when :unified
588
- filter = method(:colorize_unified_diff)
589
- when :context
590
- filter = method(:colorize_context_diff)
591
- end
592
- end
593
- if filter
594
- require 'shellwords'
595
- filter.call(IO.popen(command_args.shelljoin, 'r'))
596
- else
597
- system(*command_args)
598
- end
599
- status = $? >> 8
600
- $status = status if $status < status
601
- return status
602
- end
603
-
604
- def diff_dirs(dir1, dir2)
605
- entries1 = diff_entries(dir1)
606
- entries2 = diff_entries(dir2)
607
-
608
- common = entries1 & entries2
609
- missing1 = entries2 - entries1
610
- missing2 = entries1 - entries2
611
-
612
- files = []
613
- common.each { |file|
614
- file1 = File.join(dir1, file)
615
- file2 = File.join(dir2, file)
616
- file1_is_dir = File.directory?(file1)
617
- file2_is_dir = File.directory?(file2)
618
- if file1_is_dir && file2_is_dir
619
- diff_dirs(file1, file2) if $diff.recursive
620
- elsif !file1_is_dir && !file2_is_dir
621
- files << file1
622
- else
623
- missing1 << file
624
- missing2 << file
625
- end
626
- }
627
- diff_files(files, dir2)
628
-
629
- [[dir1, missing2], [dir2, missing1]].each { |dir, missing|
630
- new_files = []
631
- missing.each { |entry|
632
- file = File.join(dir, entry)
633
-
634
- if $diff.new_file
635
- if File.directory?(file)
636
- if dir.equal?(dir1)
637
- diff_dirs(file, nil)
638
- else
639
- diff_dirs(nil, file)
640
- end
641
- else
642
- new_files << file
643
- end
644
- else
645
- printf "Only in %s: %s (%s)\n",
646
- dir, entry, File.directory?(file) ? 'directory' : 'file'
647
- $status = 1 if $status < 1
648
- end
649
- }
650
- if dir.equal?(dir1)
651
- diff_files(new_files, EMPTYFILE)
652
- else
653
- diff_files(EMPTYFILE, new_files)
654
- end
655
- }
656
- end
657
-
658
- def diff_entries(dir)
659
- return [] if dir.nil?
660
- return Dir.entries(dir).reject { |file| diff_exclude?(file) }
661
- rescue => e
662
- warn "#{dir}: #{e}"
663
- return []
664
- end
665
-
666
- def diff_exclude?(basename)
667
- return true if basename == '.' || basename == '..'
668
- return false if $diff.include.any? { |pat|
669
- File.fnmatch(pat, basename, File::FNM_DOTMATCH)
670
- }
671
- return true if $diff.exclude.any? { |pat|
672
- File.fnmatch(pat, basename, File::FNM_DOTMATCH)
673
- }
674
- return true if $diff.rsync_exclude && RSYNC_EXCLUDE_GLOBS.any? { |pat|
675
- File.fnmatch(pat, basename, File::FNM_DOTMATCH)
676
- }
677
- return true if $diff.fignore_exclude && FIGNORE_GLOBS.any? { |pat|
678
- File.fnmatch(pat, basename, File::FNM_DOTMATCH)
679
- }
680
- return false
681
- end
682
-
683
- def colorize_unified_diff(io)
684
- colors = $diff.colors
685
-
686
- state = :comment
687
- hunk_left = nil
688
- io.each_line { |line|
689
- case state
690
- when :comment
691
- case line
692
- when /^\+{3} /
693
- color = colors[:file1]
694
- when /^-{3} /
695
- color = colors[:file2]
696
- when /^@@ -[0-9]+(,([0-9]+))? \+[0-9]+(,([0-9]+))?/
697
- state = :hunk
698
- hunk_left = ($1 ? $2.to_i : 1) + ($3 ? $4.to_i : 1)
699
- line.sub!(/^(@@ .*? @@)( )?/) {
700
- $1 + ($2 ? colors[:off] + $2 + colors[:function] : '')
701
- }
702
- color = colors[:header]
703
- else
704
- color = colors[:comment]
705
- end
706
- when :hunk
707
- check = false
708
- case line
709
- when /^\+/
710
- color = colors[:new]
711
- hunk_left -= 1
712
- check = $diff.highlight_whitespace
713
- when /^-/
714
- color = colors[:old]
715
- hunk_left -= 1
716
- check = $diff.highlight_whitespace
717
- when /^ /
718
- color = colors[:unchanged]
719
- hunk_left -= 2
720
- else
721
- # error
722
- color = colors[:comment]
723
- end
724
- if check
725
- line.sub!(/([ \t]+)$/) {
726
- colors[:off] + colors[:whitespace] + $1
727
- }
728
- end
729
- if hunk_left <= 0
730
- state = :comment
731
- hunk_left = nil
732
- end
733
- end
734
-
735
- line.sub!(/^/, color)
736
- line.sub!(/$/, colors[:off])
737
-
738
- print line
739
- }
740
-
741
- io.close
742
- end
743
-
744
- def colorize_context_diff(io)
745
- colors = $diff.colors
746
-
747
- state = :comment
748
- hunk_part = nil
749
- io.each_line { |line|
750
- case state
751
- when :comment
752
- case line
753
- when /^\*{3} /
754
- color = colors[:file1]
755
- when /^-{3} /
756
- color = colors[:file2]
757
- when /^\*{15}/
758
- state = :hunk
759
- hunk_part = 0
760
- line.sub!(/^(\*{15})( )?/) {
761
- $1 + ($2 ? colors[:off] + $2 + colors[:function] : '')
762
- }
763
- color = colors[:header]
764
- end
765
- when :hunk
766
- case hunk_part
767
- when 0
768
- case line
769
- when /^\*{3} /
770
- hunk_part = 1
771
- color = colors[:header]
772
- else
773
- # error
774
- color = colors[:comment]
775
- end
776
- when 1, 2
777
- check = false
778
- case line
779
- when /^\-{3} /
780
- if hunk_part == 1
781
- hunk_part = 2
782
- color = colors[:header]
783
- else
784
- #error
785
- color = colors[:comment]
786
- end
787
- when /^\*{3} /, /^\*{15} /
788
- state = :comment
789
- redo
790
- when /^\+ /
791
- color = colors[:new]
792
- check = $diff.highlight_whitespace
793
- when /^- /
794
- color = colors[:old]
795
- check = $diff.highlight_whitespace
796
- when /^! /
797
- color = colors[:changed]
798
- check = $diff.highlight_whitespace
799
- when /^ /
800
- color = colors[:unchanged]
801
- else
802
- # error
803
- color = colors[:comment]
804
- end
805
- if check
806
- line.sub!(/^(...*)([ \t]+)$/) {
807
- $1 + colors[:off] + colors[:whitespace] + $2
808
- }
809
- end
810
- end
811
- end
812
-
813
- line.sub!(/^/, color)
814
- line.sub!(/$/, colors[:off])
815
-
816
- print line
817
- }
818
-
819
- io.close
3
+ begin
4
+ require 'di'
5
+ rescue LoadError
6
+ require 'rubygems'
7
+ require 'di'
820
8
  end
821
9
 
822
- main(ARGV) if $0 == __FILE__
10
+ main(ARGV)