di 0.1.8 → 0.1.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.
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)