Eskil

Check-in [93bf0308fc]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:Merge branch 'master' into tablelist
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | table-list
Files: files | file ages | folders
SHA1: 93bf0308fcef3b2f683658856d31da39f8a8d2b6
User & Date: peter.spjuth@gmail.com 2011-05-03 21:37:47
Context
2011-05-03
22:35
Use images from tablelist check-in: 133c0fd5bc user: peter.spjuth@gmail.com tags: table-list
21:37
Merge branch 'master' into tablelist check-in: 93bf0308fc user: peter.spjuth@gmail.com tags: table-list
21:16
Added GUI for selecting ancestor Closed-Leaf check-in: 973d18ceb6 user: peter.spjuth@gmail.com tags: trunk
2010-11-07
20:20
Merge branch 'master' into tablelist check-in: a2c892dc70 user: peter.spjuth@gmail.com tags: table-list
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to Changes.







































1
2
3
4
5
6
7






































2010-11-07
 Added tkdnd support. [FR 5125]

2010-11-07
 Autodetect line endings in conflict file.
 Allow line ending selection in merge save. [FR 5160]
 Added menu bar to merge window.
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
2011-04-30
 Improved three-way merge.
 Highlight conflicts and navigate directly between conflicts.
 Include status for each merge chunk to see the descision made.
 Added Goto menu in merge window to get fewer toolbar buttons.
 Autodetect line endings in ancestor file to select merge output.

2011-04-28
 Code cleanup to get clean Nagelfar run

2011-04-28
 Added three-way merge. Cmd line options -a and -fine.

2011-04-24
 Added basic GUI for plugin selection.

2011-04-22
 Merging did not work properly if alignement was used. [Bug 9925]
 
2011-04-11
 Support files and revisions with -review in Fossil.
 Support revisions with -review in Git.
 New DiffUtil has a fallback to pure Tcl LCS.

2011-04-05
 Added -pluginlist option.
 Added Paste Patch command in Tools.

2011-04-04
 Support list of files with -review.
 Support commit from -review.
 Support commit in Git and Fossil.

Release 2.5

2011-03-31
 Added support for Fossil revision control.

2010-11-07
 Added tkdnd support. [FR 5125]

2010-11-07
 Autodetect line endings in conflict file.
 Allow line ending selection in merge save. [FR 5160]
 Added menu bar to merge window.

Changes to Eskil.html.

24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42

43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
..
66
67
68
69
70
71
72














73
74
75
76
77
78
79
but still found Tcl annoying at the time.)<br>
I have used it a lot during the years and lately it has received enough
polish to grant a public release.
<p>
Pronunciation: The E is short, like in "set", the rest is like "skill".
<p>
Any feedback, good or bad, can be sent to
&lt;peter <i>dot</i> spjuth <i>at</i> space <i>dot</i> se&gt;
<p>
It is similar but unrelated to <a href="http://www.accurev.com/free/tkdiff">TkDiff</a>.

<a name="EskilFeatures"></a><h3>Features</h3>

<ul>
<li>Highlights changes within a line.</li>
<li>Matches similar lines within a changed block to better show changed
lines that are adjacent to added/removed lines.</li>
<li>Directory diff.</li>
<li>CVS/RCS/ClearCase/GIT/SVN/BZR/HG diff. Conflict merge.</li>

<li>View patch file.</li>
<li>Print to PDF or postscript.</li>
<li>"Clip diff"</li>
<li>Plugins for preprocessing files.</li> 
<li>Alignment and block diff functions for tricky diffs.</li>
<li>Edit and Save file from diff window.</li>
<li><a href="http://wiki.tcl.tk/starkit">Starkit</a> browsing.</li>
</ul>

<a name="EskilDownload"></a><h3>Download</h3>

Version 2.4:<br>
Is available from the
<a href="https://developer.berlios.de/projects/eskil/">Project page</a>
both as a Starkit and as Starpacks for Windows, Linux and Solaris.
<p>
The license for the application source is GPL but the bundled packages
are under the same license as Tcl.

................................................................................

<img src="eskil/eskil1.png">
<p>
A "zoom" feature for long lines.<p>
<img src="eskil/eskil2.png"><br>

<a name="EskilChanges"></a><h3>Changes</h3>














Changes in v2.4 (2009-01-08):<br>
<ul>
 <li> Completely redesigned directory diff.</li>
 <li> Added a log viewer in revision mode.</li>
 <li> Added color option for unchanged text.</li>
 <li> Plugins support.</li> 
 <li> Support for Subversion, Mercurial and Bazaar.</li>







|

|








|
>
|
|









|







 







>
>
>
>
>
>
>
>
>
>
>
>
>
>







24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
..
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
but still found Tcl annoying at the time.)<br>
I have used it a lot during the years and lately it has received enough
polish to grant a public release.
<p>
Pronunciation: The E is short, like in "set", the rest is like "skill".
<p>
Any feedback, good or bad, can be sent to
&lt;peter <i>dot</i> spjuth <i>at</i> gmail <i>dot</i> com&gt;
<p>
It is similar but unrelated to <a href="http://wiki.tcl.tk/tkdiff">TkDiff</a>.

<a name="EskilFeatures"></a><h3>Features</h3>

<ul>
<li>Highlights changes within a line.</li>
<li>Matches similar lines within a changed block to better show changed
lines that are adjacent to added/removed lines.</li>
<li>Directory diff.</li>
<li>CVS/RCS/ClearCase/GIT/SVN/BZR/HG/Perforce/Fossil diff. Conflict merge.</li>
<li>Commit changes directly from Eskil.</li>
<li>View patch, from file or clipboard.</li>
<li>Print to PDF.</li>
<li>"Clip diff"</li>
<li>Plugins for preprocessing files.</li> 
<li>Alignment and block diff functions for tricky diffs.</li>
<li>Edit and Save file from diff window.</li>
<li><a href="http://wiki.tcl.tk/starkit">Starkit</a> browsing.</li>
</ul>

<a name="EskilDownload"></a><h3>Download</h3>

Version 2.5:<br>
Is available from the
<a href="https://developer.berlios.de/projects/eskil/">Project page</a>
both as a Starkit and as Starpacks for Windows, Linux and Solaris.
<p>
The license for the application source is GPL but the bundled packages
are under the same license as Tcl.

................................................................................

<img src="eskil/eskil1.png">
<p>
A "zoom" feature for long lines.<p>
<img src="eskil/eskil2.png"><br>

<a name="EskilChanges"></a><h3>Changes</h3>
Changes in v2.5 (2011-04-01):<br>
<ul>
 <li> Requires Tcl 8.5.</li>
 <li> Plugins: Added dump, better documentation.</li>
 <li> Dir diff: Added step down.</li>
 <li> Dir diff: Redesigned to display less.</li>
 <li> Support for Perforce and Fossil.</li>
 <li> Allow zero lines of context.</li>
 <li> Detect and display annotation in patch view.</li>
 <li> Select colors for PDF print. Command line options for PDF.</li>
 <li> Removed support for Postscript output.</li>
 <li> Support File Drop with TkDnd.</li>
 <li> Handle line endings in conflict and merge.</li>
</ul>
Changes in v2.4 (2009-01-08):<br>
<ul>
 <li> Completely redesigned directory diff.</li>
 <li> Added a log viewer in revision mode.</li>
 <li> Added color option for unchanged text.</li>
 <li> Plugins support.</li> 
 <li> Support for Subversion, Mercurial and Bazaar.</li>

Changes to Makefile.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
..
65
66
67
68
69
70
71






72
73
74
75
76
77
78
79
80
81
82
83


84
85
86
87
88
89
90
91
92
93
94
95


96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
#----------------------------------------------------------------------
# Make file for Eskil
#----------------------------------------------------------------------
# $Revision$
#----------------------------------------------------------------------

VERSION = 241

# Path to the TclKits used for creating StarPacks.
TCLKIT = /home/peter/tclkit/v85
TCLKIT_LINUX   = $(TCLKIT)/tclkit-8.5.8
TCLKIT_SOLARIS = $(TCLKIT)/tclkit-solaris-sparc
TCLKIT_WIN     = $(TCLKIT)/tclkit-win32.upx.exe

# Path to the libraries used
STYLE      = /home/peter/src/packages/style
GRIFFIN    = /home/peter/tclkit/griffin.vfs/lib/griffin
TEXTSEARCH = /home/peter/src/textsearch
DIFFUTIL   = /home/peter/src/DiffUtilTcl/lib.vfs/DiffUtil
WCB        = /home/peter/src/packages/wcb3.0
PDF4TCL    = /home/peter/src/pdf4tcl/trunk/pkg
SNIT       = /home/peter/tcl/tcllib/modules/snit
TWAPI      = /home/peter/src/twapi
TKDND      = /home/peter/tkdnd/lib/tkdnd1.0
#DIFFUTIL   = /home/peter/src/DiffUtil/tcl


# Tools
NAGELFAR    = nagelfar

all: setup

SRCFILES = src/clip.tcl src/dirdiff.tcl src/help.tcl src/map.tcl \
	   src/print.tcl src/registry.tcl src/rev.tcl src/eskil.tcl \
	   src/compare.tcl src/merge.tcl src/printobj.tcl src/plugin.tcl

#----------------------------------------------------------------
# Setup symbolic links from the VFS to the real files
#----------------------------------------------------------------

eskil.vfs/src/eskil.tcl:
................................................................................
	cd eskil.vfs/lib ; mkdir snit
	cd eskil.vfs/lib/snit ; ln -s $(SNIT)/pkgIndex.tcl
	cd eskil.vfs/lib/snit ; ln -s $(SNIT)/snit.tcl
	cd eskil.vfs/lib/snit ; ln -s $(SNIT)/snit2.tcl
	cd eskil.vfs/lib/snit ; ln -s $(SNIT)/main2.tcl
	cd eskil.vfs/lib/snit ; ln -s $(SNIT)/main1.tcl
	cd eskil.vfs/lib/snit ; ln -s $(SNIT)/validate.tcl







links: eskil.vfs/src/eskil.tcl \
	eskil.vfs/examples\
	eskil.vfs/doc\
	eskil.vfs/plugins\
	eskil.vfs/COPYING\
	eskil.vfs/lib/griffin\
	eskil.vfs/lib/style\
	eskil.vfs/lib/textsearch\
	eskil.vfs/lib/diffutil\
	eskil.vfs/lib/pdf4tcl\
	eskil.vfs/lib/snit\


	eskil.vfs/lib/tkdnd\
	eskil.vfs/lib/wcb

setup: links

#----------------------------------------------------------------
# Testing
#----------------------------------------------------------------

spell:
	@cat doc/*.txt | ispell -d british -l | sort -u



# Create a common "header" file for all source files.
eskil_h.syntax: $(SRCFILES) src/eskil.syntax
	@echo Creating syntax header file...
	@$(NAGELFAR) -header eskil_h.syntax $(SRCFILES)

check: eskil_h.syntax
	@echo Checking...
	@for i in $(SRCFILES); do $(NAGELFAR) -quiet eskil_h.syntax $$i ; done

test:
	@./tests/all.tcl

#----------------------------------------------------------------
# Coverage
#----------------------------------------------------------------



<
<

|













|

|
|
|
>






|
|







 







>
>
>
>
>
>












>
>












>
>



|



|







1
2
3


4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
..
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
#----------------------------------------------------------------------
# Make file for Eskil
#----------------------------------------------------------------------



VERSION = 26

# Path to the TclKits used for creating StarPacks.
TCLKIT = /home/peter/tclkit/v85
TCLKIT_LINUX   = $(TCLKIT)/tclkit-8.5.8
TCLKIT_SOLARIS = $(TCLKIT)/tclkit-solaris-sparc
TCLKIT_WIN     = $(TCLKIT)/tclkit-win32.upx.exe

# Path to the libraries used
STYLE      = /home/peter/src/packages/style
GRIFFIN    = /home/peter/tclkit/griffin.vfs/lib/griffin
TEXTSEARCH = /home/peter/src/textsearch
DIFFUTIL   = /home/peter/src/DiffUtilTcl/lib.vfs/DiffUtil
WCB        = /home/peter/src/packages/wcb3.0
PDF4TCL    = /home/peter/src/pdf4tcl/pkg
SNIT       = /home/peter/tcl/tcllib/modules/snit
STRUCT     = /home/peter/tcl/tcllib/modules/struct
CMDLINE    = /home/peter/tcl/tcllib/modules/cmdline
TWAPI      = /home/peter/src/packages/twapi
TKDND      = /home/peter/src/packages/tkdnd/lib/tkdnd1.0

# Tools
NAGELFAR    = nagelfar

all: setup

SRCFILES = src/eskil.tcl src/clip.tcl src/dirdiff.tcl src/help.tcl src/map.tcl \
	   src/print.tcl src/registry.tcl src/rev.tcl \
	   src/compare.tcl src/merge.tcl src/printobj.tcl src/plugin.tcl

#----------------------------------------------------------------
# Setup symbolic links from the VFS to the real files
#----------------------------------------------------------------

eskil.vfs/src/eskil.tcl:
................................................................................
	cd eskil.vfs/lib ; mkdir snit
	cd eskil.vfs/lib/snit ; ln -s $(SNIT)/pkgIndex.tcl
	cd eskil.vfs/lib/snit ; ln -s $(SNIT)/snit.tcl
	cd eskil.vfs/lib/snit ; ln -s $(SNIT)/snit2.tcl
	cd eskil.vfs/lib/snit ; ln -s $(SNIT)/main2.tcl
	cd eskil.vfs/lib/snit ; ln -s $(SNIT)/main1.tcl
	cd eskil.vfs/lib/snit ; ln -s $(SNIT)/validate.tcl
eskil.vfs/lib/struct:
	cd eskil.vfs/lib ; mkdir struct
	cd eskil.vfs/lib/struct ; ln -s $(STRUCT)/pkgIndex.tcl
	cd eskil.vfs/lib/struct ; ln -s $(STRUCT)/list.tcl
eskil.vfs/lib/cmdline:
	cd eskil.vfs/lib ; ln -s $(CMDLINE) cmdline

links: eskil.vfs/src/eskil.tcl \
	eskil.vfs/examples\
	eskil.vfs/doc\
	eskil.vfs/plugins\
	eskil.vfs/COPYING\
	eskil.vfs/lib/griffin\
	eskil.vfs/lib/style\
	eskil.vfs/lib/textsearch\
	eskil.vfs/lib/diffutil\
	eskil.vfs/lib/pdf4tcl\
	eskil.vfs/lib/snit\
	eskil.vfs/lib/struct\
	eskil.vfs/lib/cmdline\
	eskil.vfs/lib/tkdnd\
	eskil.vfs/lib/wcb

setup: links

#----------------------------------------------------------------
# Testing
#----------------------------------------------------------------

spell:
	@cat doc/*.txt | ispell -d british -l | sort -u

NAGELFARFLAGS = -s syntaxdb86.tcl -filter "*Non constant definition*" -quiet

# Create a common "header" file for all source files.
eskil_h.syntax: $(SRCFILES) src/eskil.syntax
	@echo Creating syntax header file...
	@$(NAGELFAR) $(NAGELFARFLAGS) -header eskil_h.syntax $(SRCFILES)

check: eskil_h.syntax
	@echo Checking...
	@for i in $(SRCFILES); do $(NAGELFAR)  $(NAGELFARFLAGS) eskil_h.syntax $$i ; done

test:
	@./tests/all.tcl

#----------------------------------------------------------------
# Coverage
#----------------------------------------------------------------

Changes to doc/cmdline.txt.

13
14
15
16
17
18
19


20
21
22
23
24
25
26
..
32
33
34
35
36
37
38


39
40
41
42
43


44

45
46
47

48
49
50
51
52
53
54
55
56
57
58
59
60



61
62
63
64
65
66

  -nodiff     : Normally, if there are enough information on the
                command line to run diff, Eskil will do so unless
                this option is specified.
  -dir        : Start in directory diff mode. Ignores other args.
  -clip       : Start in clip diff mode. Ignores other args.
  -patch      : View patch file.


  -context <n>: Show only differences, with <n> lines of context.
  -foreach    : Open one diff window per file listed.
  -close      : Close windows with no changes.

  -noparse    : Eskil can perform analysis of changed blocks to
  -line       : improve display. See online help for details.
  -smallblock : The default. Do block analysis on small blocks.
................................................................................

  -noignore   : Don't ignore any whitespace.
  -b          : Ignore space changes. Default.
  -w          : Ignore all spaces.
  -nocase     : Ignore case changes.
  -nodigit    : Ignore digit changes.
  -nokeyword  : In directory diff, ignore $ Keywords: $



  -prefix <str> : Care mainly about words starting with "str".
  -preprocess <pair> : TBW

  -r <ver>    : Version info for version control mode.




  -conflict   : Treat file as a merge conflict file and enter merge
                mode.
  -o <file>   : Specify merge result output file.


  -browse     : Automatically bring up file dialog after starting.
  -server     : Set up Eskil to be controllable from the outside.

  -print <file>          : Generate PDF and exit.
  -printCharsPerLine <n> : Adapt font size for this line length and wrap. (80)
  -printPaper <paper>    : Select paper size (a4)
  -printHeaderSize <n>   : Font size for page header (10)
  -printColorChange <RGB> : Color for change   (1.0 0.7 0.7)
  -printColorOld <RGB>    : Color for old text (0.7 1.0 0.7)
  -printColorNew <RGB     : Color for new text (0.8 0.8 1.0)

  -plugin <name> : Preprocess files using plugin.




  -limit <lines> : Do not process more than <lines> lines.}

To list all options matching a prefix, run 'eskil --query prefix'.
In tcsh use this line to get option completion:
complete eskil 'C/-/`eskil --query -`/'







>
>







 







>
>





>
>

>



>












|
>
>
>

|




13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
..
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77

  -nodiff     : Normally, if there are enough information on the
                command line to run diff, Eskil will do so unless
                this option is specified.
  -dir        : Start in directory diff mode. Ignores other args.
  -clip       : Start in clip diff mode. Ignores other args.
  -patch      : View patch file.
  -           : Read patch file from standard input, to allow pipes.
  -review     : View revision control tree as a patch.
  -context <n>: Show only differences, with <n> lines of context.
  -foreach    : Open one diff window per file listed.
  -close      : Close windows with no changes.

  -noparse    : Eskil can perform analysis of changed blocks to
  -line       : improve display. See online help for details.
  -smallblock : The default. Do block analysis on small blocks.
................................................................................

  -noignore   : Don't ignore any whitespace.
  -b          : Ignore space changes. Default.
  -w          : Ignore all spaces.
  -nocase     : Ignore case changes.
  -nodigit    : Ignore digit changes.
  -nokeyword  : In directory diff, ignore $ Keywords: $
  -nonewline  : Try to ignore newline changes.
  -nonewline+ : Try to ignore newline changes, and don't display.

  -prefix <str> : Care mainly about words starting with "str".
  -preprocess <pair> : TBW

  -r <ver>    : Version info for version control mode.
  -cvs        : Detect CVS first, if multiple version systems are used.
  -svn        : Detect SVN first, if multiple version systems are used.

  -a <file>   : Give anscestor file for three way merge.
  -conflict   : Treat file as a merge conflict file and enter merge
                mode.
  -o <file>   : Specify merge result output file.
  -fine       : Use fine grained chunks. Useful for merging.

  -browse     : Automatically bring up file dialog after starting.
  -server     : Set up Eskil to be controllable from the outside.

  -print <file>          : Generate PDF and exit.
  -printCharsPerLine <n> : Adapt font size for this line length and wrap. (80)
  -printPaper <paper>    : Select paper size (a4)
  -printHeaderSize <n>   : Font size for page header (10)
  -printColorChange <RGB> : Color for change   (1.0 0.7 0.7)
  -printColorOld <RGB>    : Color for old text (0.7 1.0 0.7)
  -printColorNew <RGB     : Color for new text (0.8 0.8 1.0)

  -plugin <name>       : Preprocess files using plugin.
  -plugininfo <info>   : Pass info to plugin (plugin specific)
  -pluginlist          : List known plugins
  -plugindump <plugin> : Dump plugin source to stdout

  -limit <lines> : Do not process more than <lines> lines.

To list all options matching a prefix, run 'eskil --query prefix'.
In tcsh use this line to get option completion:
complete eskil 'C/-/`eskil --query -`/'

Changes to doc/eskil.txt.

1
2
3
4
5
6
7

8
9
10
11
12
13
14
15
16
17
18

19
20
21
22
23
24
25
..
29
30
31
32
33
34
35

36
37
38
39
40
41
42
43
44
45
46
47
48

49
50
51
52
53
54
55


56
57
58
59
60
61
62
..
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
<b><ul>Commands</ul></b>

<b>File Menu</b>
  Redo Diff       \t: Run diff again on the same files.
  Open Both       \t: Select two files, run diff.
  Open Left File  \t: Select a file for left window, run diff
  Open Right File \t: Select a file for right window, run diff

  Open Conflict File\t: Select a file containing conflicts such as from
                    \t  a CVS merge.
  Open Patch File \t: Display a patch file created by diff -c or diff -u.
  Revision Diff   \t: Select one revision controlled file.
  Print PDF       \t: Print diff to PDF file.
  Close           \t: Close this window.
  Quit            \t: Guess

<b>Options Menu</b>
  Font   \t: Select font and fontsize for the two main text windows
  Ignore \t: Diff options for ignoring e.g. whitespace

  Parse  \t: Additional parsing made by Eskil to improve the display.
         \t  See examples below.
         \t  Nothing: No parsing made.
         \t  Lines  : When there is a changed block with the same number
         \t           of lines in both right and left files, Eskil
         \t           compares corresponding lines and tries to highlight
         \t           only the part that has been changed.
................................................................................
         \t           version do not parse big blocks to avoid long runs.
         \t  The Char and Word options selects if the line parsing should
         \t  highlight full words only, or check single characters.
         \t  Mark last  : Last change of a line is underlined
  Colours\t: Choose highlight colours.
  Context\t: You can select that only differing lines shall be displayed,
         \t  and how many surrounding lines are shown.

  Save default\t: Save current option settings in ~/.eskilrc

<b>Search Menu</b>
  Find      \t: Search dialog
  Find next \t: Repeat search
  Find prev \t: Repeat search backwards

<b>Tools Menu</b>
  New Diff Window \t : Open another diff window.
  Directory Diff  \t : Window for comparing directories
  Clip Diff       \t : Gives two areas where you can paste text and diff them.
  Merge           \t : Perform a two way merge.
  Edit Mode       \t : (new edit mode, to be described)

  Clear Align     \t : Clear alignment info for this diff.  The effect does not
                  \t   show until you do "Redo Diff".

Diff Options Field\t: Any text written here will be passed to diff.
Rev 1/2 Fields    \t: In Revision mode, these field are selects versions to be
                  \t  compared.



Prev Diff Button\t: Scrolls to the previous differing block, or to the top
                \t  if there are no more diffs.
Next Diff Button\t: Scrolls to the next differing block, or to the bottom
                \t  if there are no more diffs.

Equal sign\t: Above the vertical scrollbar, a "=" will appear if the files
          \t  are equal. While the external diff executes a "*" is shown
................................................................................

You can, for each difference, select which version you want to appear in the output file.  The buttons "LR", "L", "R" and "RL" select the lines from the left file, right file or both files.
"Prev" and "Next" buttons moves between differences.
"All L" and "All R" buttons select "L" or "R" for all differences.
"Pure" ...
"Save" saves the merge result to a file.

On the keyboard, Up and Down keys means the same as "Prev" and "Next".  Left and Right keys selects "L" and "R".

When saved it is the contents of the text widget that is saved which means you can hand edit there if you are careful.  E.g. you can't use cursor keys, but you can type text, delete text and copy/paste with mouse assistance.

<b><ul>Examples of effects of parse options.</ul></b>

Below are two example files, and five different results when using different options with those files.

<example>Left file: \tRight file:
<pre>NET '/I$1/N$1454' IC2-15 IC5-7  \tNET '/I$1/N$1454' IC1-4 IC2-15 IC5-2 IC5-7







>











>







 







>













>



<



>
>







 







|

|







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
..
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55

56
57
58
59
60
61
62
63
64
65
66
67
..
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
<b><ul>Commands</ul></b>

<b>File Menu</b>
  Redo Diff       \t: Run diff again on the same files.
  Open Both       \t: Select two files, run diff.
  Open Left File  \t: Select a file for left window, run diff
  Open Right File \t: Select a file for right window, run diff
  Open Ancestor File\t: Select ancestor for threeway diff, rerun and merge
  Open Conflict File\t: Select a file containing conflicts such as from
                    \t  a CVS merge.
  Open Patch File \t: Display a patch file created by diff -c or diff -u.
  Revision Diff   \t: Select one revision controlled file.
  Print PDF       \t: Print diff to PDF file.
  Close           \t: Close this window.
  Quit            \t: Guess

<b>Options Menu</b>
  Font   \t: Select font and fontsize for the two main text windows
  Ignore \t: Diff options for ignoring e.g. whitespace
  Preprocess\t: Options for making string substitution before comparing.
  Parse  \t: Additional parsing made by Eskil to improve the display.
         \t  See examples below.
         \t  Nothing: No parsing made.
         \t  Lines  : When there is a changed block with the same number
         \t           of lines in both right and left files, Eskil
         \t           compares corresponding lines and tries to highlight
         \t           only the part that has been changed.
................................................................................
         \t           version do not parse big blocks to avoid long runs.
         \t  The Char and Word options selects if the line parsing should
         \t  highlight full words only, or check single characters.
         \t  Mark last  : Last change of a line is underlined
  Colours\t: Choose highlight colours.
  Context\t: You can select that only differing lines shall be displayed,
         \t  and how many surrounding lines are shown.
  Toolbar\t: Show/hide toolbar
  Save default\t: Save current option settings in ~/.eskilrc

<b>Search Menu</b>
  Find      \t: Search dialog
  Find next \t: Repeat search
  Find prev \t: Repeat search backwards

<b>Tools Menu</b>
  New Diff Window \t : Open another diff window.
  Directory Diff  \t : Window for comparing directories
  Clip Diff       \t : Gives two areas where you can paste text and diff them.
  Merge           \t : Perform a two way merge.
  Edit Mode       \t : (new edit mode, to be described)
  Paste Patch     \t : Display a patch from clipboard.
  Clear Align     \t : Clear alignment info for this diff.  The effect does not
                  \t   show until you do "Redo Diff".


Rev 1/2 Fields    \t: In Revision mode, these field are selects versions to be
                  \t  compared.

Log Button      \t: Show revision control log between shown files.
Commit Button   \t: Commit shown differences to version control.
Prev Diff Button\t: Scrolls to the previous differing block, or to the top
                \t  if there are no more diffs.
Next Diff Button\t: Scrolls to the next differing block, or to the bottom
                \t  if there are no more diffs.

Equal sign\t: Above the vertical scrollbar, a "=" will appear if the files
          \t  are equal. While the external diff executes a "*" is shown
................................................................................

You can, for each difference, select which version you want to appear in the output file.  The buttons "LR", "L", "R" and "RL" select the lines from the left file, right file or both files.
"Prev" and "Next" buttons moves between differences.
"All L" and "All R" buttons select "L" or "R" for all differences.
"Pure" ...
"Save" saves the merge result to a file.

On the keyboard, Up and Down keys means the same as "Prev" and "Next".  Left and Right keys selects "L" and "R". Shift-Up and Shift-Down jumps 10 changes, or if there are conflicts jumps to next conflict.

When saved it is the contents of the text widget that is saved which means you can hand edit there if you are careful.  Press Escape to bring focus out of the text and make cursor keys navigate between changes.

<b><ul>Examples of effects of parse options.</ul></b>

Below are two example files, and five different results when using different options with those files.

<example>Left file: \tRight file:
<pre>NET '/I$1/N$1454' IC2-15 IC5-7  \tNET '/I$1/N$1454' IC1-4 IC2-15 IC5-2 IC5-7

Changes to doc/plugins.txt.

1
2
3
4
5
6
7

8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
Eskil provides a plugin system where a plugin can preprocess data
before being compared and displayed.

The command line options for plugins are:
  -plugin plugin     : Use plugin
  -plugininfo info   : Pass info to plugin (plugin specific)
  -plugindump plugin : Dump plugin source to stdout


A plugin is a Tcl script that must follow a specific format.
Dump one of the included plugins to see what it looks like.
The plugin is executed in a safe interpreter and thus cannot do any
damage.

A plugin is set up with these global variables filled in:
::WhoAmI  : The name of the plugin
::Info    : The contents of -plugininfo parameter
::Pref    : A copy if Eskil's internal preferences array.

Example plugins are included in the kit.
Just give a bad plugin name to the options and you will get a list
of available plugins.

A plugin may give a result that has a line-by-line correspondence to
the original, in which case the preprocessed data is used for comparing
while the original is used for displaying.  The main plugin procedure
returns 0 to signify this case.

If the plugin procedure returns 1, the processed data is used also for
displaying.

When searching for a plugin, files "plugin" and "plugin.tcl" will
match. The search path is current directory, "plugins" directory,
the directory where Eskil is installed, "plugins" directory where
Eskil is installed, and also the internal "plugins" wrapped into Eskil.







>












<
<









|



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20


21
22
23
24
25
26
27
28
29
30
31
32
33
Eskil provides a plugin system where a plugin can preprocess data
before being compared and displayed.

The command line options for plugins are:
  -plugin plugin     : Use plugin
  -plugininfo info   : Pass info to plugin (plugin specific)
  -plugindump plugin : Dump plugin source to stdout
  -pluginlist        : List known plugins

A plugin is a Tcl script that must follow a specific format.
Dump one of the included plugins to see what it looks like.
The plugin is executed in a safe interpreter and thus cannot do any
damage.

A plugin is set up with these global variables filled in:
::WhoAmI  : The name of the plugin
::Info    : The contents of -plugininfo parameter
::Pref    : A copy if Eskil's internal preferences array.

Example plugins are included in the kit.



A plugin may give a result that has a line-by-line correspondence to
the original, in which case the preprocessed data is used for comparing
while the original is used for displaying.  The main plugin procedure
returns 0 to signify this case.

If the plugin procedure returns 1, the processed data is used also for
displaying.

When searching for a plugin "x", files "x" and "x.tcl" will
match. The search path is current directory, "plugins" directory,
the directory where Eskil is installed, "plugins" directory where
Eskil is installed, and also the internal "plugins" wrapped into Eskil.

Changes to doc/revision.txt.

1
2
3
4
5
6
7
8
9
..
31
32
33
34
35
36
37
38
39
40



41
42
43
44
45
46
47
..
48
49
50
51
52
53
54




55
56
57
58
59
60
61
Eskil can compare versions in revision control systems.
Currently RCS, CVS, Git, Mercurial, Bazaar, Subversion and ClearCase
are supported.

If you specify only one file on the command line to Eskil, it will automatically detect if the file is under revision control and enter revision control mode.

By default the local file is compared against the latest checked in version.  This is for the common case when you just want to know what you have changed before checking in.

You can use the -r option to select which versions to compare.  It works like it does in "cvs diff".  Examples:
................................................................................
<pre>hg diff | eskil -</pre>
<pre>git diff -p --diff-filter=M master | eskil -</pre>

<ul>View all changes</ul>

If the command line option -review is used. Eskil will generate a patch
for the current tree and display it as in patch mode.
In a Mercurial directory, these are equivalent:
<pre>eskil -preview</pre>
<pre>hg diff | eskil -</pre>




<ul>RCS/CVS</ul>

For RCS and CVS the arguments to -r are standard version numbers just like to their -r options.  RCS style -r\u003crev\u003e is allowed.
If a revision is an integer, it is added to the last number in the current version, thus giving relative versions.  E.g. -1 gives the second to last version.

<ul>Subversion</ul>
................................................................................

For Subversion the arguments to -r are standard version numbers just like its -r option.  If a revision is a negative integer, the log is searched backwards for earlier versions. E.g. -1 gives the second to last version.

<ul>Git</ul>

For Git -r <rev> is passed to show, as in "git show <rev>:<file>".





<ul>Mercurial</ul>

For Mercurial -r works as in "hg cat -r".

<ul>Bazaar</ul>

For Bazaar -r works as in "bzr cat -r".

|







 







|


>
>
>







 







>
>
>
>







1
2
3
4
5
6
7
8
9
..
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
..
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
Eskil can compare versions in revision control systems.
Currently RCS, CVS, Git, Fossil, Mercurial, Bazaar, Subversion, Perforce and ClearCase
are supported.

If you specify only one file on the command line to Eskil, it will automatically detect if the file is under revision control and enter revision control mode.

By default the local file is compared against the latest checked in version.  This is for the common case when you just want to know what you have changed before checking in.

You can use the -r option to select which versions to compare.  It works like it does in "cvs diff".  Examples:
................................................................................
<pre>hg diff | eskil -</pre>
<pre>git diff -p --diff-filter=M master | eskil -</pre>

<ul>View all changes</ul>

If the command line option -review is used. Eskil will generate a patch
for the current tree and display it as in patch mode.
E.g. in a Mercurial directory, these show the same thing:
<pre>eskil -preview</pre>
<pre>hg diff | eskil -</pre>

If file names are given after -review, only the listed files are included. If supported,
the Commit button will be enabled allowing the viewed differences to be committed.

<ul>RCS/CVS</ul>

For RCS and CVS the arguments to -r are standard version numbers just like to their -r options.  RCS style -r\u003crev\u003e is allowed.
If a revision is an integer, it is added to the last number in the current version, thus giving relative versions.  E.g. -1 gives the second to last version.

<ul>Subversion</ul>
................................................................................

For Subversion the arguments to -r are standard version numbers just like its -r option.  If a revision is a negative integer, the log is searched backwards for earlier versions. E.g. -1 gives the second to last version.

<ul>Git</ul>

For Git -r <rev> is passed to show, as in "git show <rev>:<file>".

<ul>Fossil</ul>

For Fossil -r <rev> is passed to finfo, as in "fossil finfo -p <file> -r <rev>".

<ul>Mercurial</ul>

For Mercurial -r works as in "hg cat -r".

<ul>Bazaar</ul>

For Bazaar -r works as in "bzr cat -r".

Changes to doc/tutorial.txt.

35
36
37
38
39
40
41








42
43
44
45
46
47
48
Now those lines are remembered, and you could add more alignment pairs before you proceed with regenerating the view.

<bullet>\u2022\tSelect menu File->Redo Diff.</bullet>

Scroll down and see that the lines are next to each other.  Also note that their line numbers are underlined.
You can clear the alignment information in the right-click menu over a line or by selecting the menu Tools->Clear Align.









<b>Regular expression preprocessing</b>

Double click on enum.c to bring up the diff.
[write something here to explain the problem and the goal]
[add reference to re_syntax and regsub manuals]
<bullet>
\u2022\tSelect menu Options->Preprocess.







>
>
>
>
>
>
>
>







35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
Now those lines are remembered, and you could add more alignment pairs before you proceed with regenerating the view.

<bullet>\u2022\tSelect menu File->Redo Diff.</bullet>

Scroll down and see that the lines are next to each other.  Also note that their line numbers are underlined.
You can clear the alignment information in the right-click menu over a line or by selecting the menu Tools->Clear Align.

<b>Three way merge</b>
<bullet>
\u2022\tDouble click on merge.txt to bring up the diff.
\u2022\tSelect menu File->Open Ancestor File.
\u2022\tSelect file mergeanc.txt
</bullet>
The merge window will appear with most changes merged.  Conflicts are marked with gray, and a row of asterisks in the status bar.  Conflicts are resolved to the right initially.  Navigate between conflicts using shift-up/down keys. Select side with left/right keys.

<b>Regular expression preprocessing</b>

Double click on enum.c to bring up the diff.
[write something here to explain the problem and the goal]
[add reference to re_syntax and regsub manuals]
<bullet>
\u2022\tSelect menu Options->Preprocess.

Added examples/dir1/merge.txt.

















































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
0
1
Added left
x
2
3
4
Added right
5
6
7
Changed same
8a
9
10
Changed different
12a
13
14
Deleted left
17
18
Deleted right
19
20
21
Left: Add+change Right: change
xx
22a
23
24
Left: change Right add+change
25a
26
27
Left: Delete  Right: change
29
30
Left: change  Right: delete
31a
32
33
Added same
xxx
34
35
36
Deleted same
38
39
Changed adjacent
40a
41
42
Left: Add+change Right: change same
xxxx
43a
44
45
Left: change same Right: add+change
46a
47
48
Left: change Right: deleted block
49
50a
51
52
Added different
xxxxx
53
54
55
Left mixed change
57x
59
60
Right mixed change
61
62
63
64
65
Both added multiple
xx1
xx2
66
67

Added examples/dir1/mergeanc.txt.













































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
0
1
Added left
2
3
4
Added right
5
6
7
Changed same
8
9
10
Changed different
12
13
14
Deleted left
16
17
18
Deleted right
19
20
21
Left: Add+change Right: change
22
23
24
Left: change Right add+change
25
26
27
Left: Delete  Right: change
28
29
30
Left: change  Right: delete
31
32
33
Added same
34
35
36
Deleted same
37
38
39
Changed adjacent
40
41
42
Left: Add+change Right: change same
43
44
45
Left: change same Right: add+change
46
47
48
Left: change Right: deleted block
49
50
51
52
Added different
53
54
55
Left mixed change
56
57
58
59
60
Right mixed change
61
62
63
64
65
Both added multiple
66
67

Added examples/dir2/merge.txt.













































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
0
1
Added left
2
3
4
Added right
y
5
6
7
Changed same
8a
9
10
Changed different
12b
13
14
Deleted left
16
17
18
Deleted right
20
21
Left: Add+change Right: change
22b
23
24
Left: change Right add+change
yy
25b
26
27
Left: Delete  Right: change
28a
29
30
Left: change  Right: delete
32
33
Added same
xxx
34
35
36
Deleted same
38
39
Changed adjacent
40
41a
42
Left: Add+change Right: change same
43a
44
45
Left: change same Right: add+change
yyyy
46a
47
48
Left: change Right: deleted block
52
Added different
yyyyy
53
54
55
Left mixed change
56
57
58
59
60
Right mixed change
61x
63x
64
65
Both added multiple
yy1
yy2
66
67

Changes to src/clip.tcl.

129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
...
192
193
194
195
196
197
198

199
}

proc makeClipDiffWin {} {
    set top .clipdiff
    if {[winfo exists $top] && [winfo toplevel $top] eq $top} {
        raise $top
        focus -force $top
        return
    }
    destroy $top
    toplevel $top
    lappend ::diff(diffWindows) $top

    wm title $top "Clip Diff"
    wm protocol $top WM_DELETE_WINDOW "cleanupAndExit $top"
    set t1 [Scroll both \
            text $top.t1 -width 60 -height 35 -font myfont]
    set t2 [Scroll both \
            text $top.t2 -width 60 -height 35 -font myfont]
................................................................................
    }

    grid $top.f    -       -sticky we
    grid $top.t1   $top.t2 -sticky news
    grid $top.t2 -padx {2 0}
    grid rowconfigure    $top 1     -weight 1
    grid columnconfigure $top {0 1} -weight 1

}







|



|







 







>

129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
...
192
193
194
195
196
197
198
199
200
}

proc makeClipDiffWin {} {
    set top .clipdiff
    if {[winfo exists $top] && [winfo toplevel $top] eq $top} {
        raise $top
        focus -force $top
        return $top
    }
    destroy $top
    toplevel $top
    eskilRegisterToplevel $top

    wm title $top "Clip Diff"
    wm protocol $top WM_DELETE_WINDOW "cleanupAndExit $top"
    set t1 [Scroll both \
            text $top.t1 -width 60 -height 35 -font myfont]
    set t2 [Scroll both \
            text $top.t2 -width 60 -height 35 -font myfont]
................................................................................
    }

    grid $top.f    -       -sticky we
    grid $top.t1   $top.t2 -sticky news
    grid $top.t2 -padx {2 0}
    grid rowconfigure    $top 1     -weight 1
    grid columnconfigure $top {0 1} -weight 1
    return $top
}

Changes to src/dirdiff.tcl.

434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
...
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
...
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
....
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
....
1123
1124
1125
1126
1127
1128
1129

1130
                set dst $lf
            } elseif {$lp ne ""} {
                set dst [file join $lp [file tail $src]]
            } else {
                return
            }
        } else {
            error "Bad to argument to CopyFile: $to"
        }

        if {[file exists $dst]} {
            if {[tk_messageBox -icon question -title "Overwrite file?" -message \
                    "Copy\n$src\noverwriting\n$dst ?" -type yesno] eq "yes"} {
                file copy -force $src $dst
                # FIXA: update file info in tree too
................................................................................
snit::widget DirDiff {
    hulltype toplevel
    widgetclass Toplevel
    component tree
    variable statusVar

    constructor {args} {
        lappend ::diff(diffWindows) $win
        wm title $win "Eskil Dir"
        wm protocol $win WM_DELETE_WINDOW [list cleanupAndExit $win]

        install tree using DirCompareTree $win.dc \
                -leftdirvariable ::dirdiff(leftDir) \
                -rightdirvariable ::dirdiff(rightDir) \
                -statusvar [myvar statusVar]
................................................................................
        
        $win.m add cascade -label "Help" -underline 0 -menu $win.m.help
        menu $win.m.help
        $win.m.help add command -label "Tutorial" -command makeTutorialWin \
                -underline 0
        $win.m.help add command -label "About" -command makeAboutWin -underline 0
        
        if {$::debug} {
            $win.m add cascade -label "Debug" -menu $win.m.md -underline 0
            menu $win.m.md
            if {$::tcl_platform(platform) eq "windows"} {
                $win.m.md add checkbutton -label "Console" -variable consolestate \
                        -onvalue show -offvalue hide -command {console $consolestate}
                $win.m.md add separator
            }
................................................................................
            -command "ApplyDirDiffPref ; destroy $top"
    ttk::button $fb.ap -width 10 -text "Apply" -command ApplyDirDiffPref
    ttk::button $fb.ca -width 10 -text "Cancel" -command "destroy $top"
    grid $fb.ok $fb.ap $fb.ca -padx 3 -pady 3
    grid columnconfigure $fb {0 1 2} -uniform a -weight 1

    pack $fb -side bottom -fill x
    pack $check $opts $filter -side top -fill x
}

# Experimental...
#preprocess filter pa namnen sa man kan jamfora bibliotek
#med andrade namn.
proc makeRegSubWin {} {
    set top .ddregsub
................................................................................
        set ::dirdiff(leftDir) ""
    }
    if {![info exists ::dirdiff(rightDir)]} {
        set ::dirdiff(rightDir) ""
    }
    destroy .dirdiff
    DirDiff .dirdiff

}







|







 







|







 







|







 







|







 







>

434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
...
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
...
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
....
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
....
1123
1124
1125
1126
1127
1128
1129
1130
1131
                set dst $lf
            } elseif {$lp ne ""} {
                set dst [file join $lp [file tail $src]]
            } else {
                return
            }
        } else {
            error "Bad from argument to CopyFile: $from"
        }

        if {[file exists $dst]} {
            if {[tk_messageBox -icon question -title "Overwrite file?" -message \
                    "Copy\n$src\noverwriting\n$dst ?" -type yesno] eq "yes"} {
                file copy -force $src $dst
                # FIXA: update file info in tree too
................................................................................
snit::widget DirDiff {
    hulltype toplevel
    widgetclass Toplevel
    component tree
    variable statusVar

    constructor {args} {
        eskilRegisterToplevel $win
        wm title $win "Eskil Dir"
        wm protocol $win WM_DELETE_WINDOW [list cleanupAndExit $win]

        install tree using DirCompareTree $win.dc \
                -leftdirvariable ::dirdiff(leftDir) \
                -rightdirvariable ::dirdiff(rightDir) \
                -statusvar [myvar statusVar]
................................................................................
        
        $win.m add cascade -label "Help" -underline 0 -menu $win.m.help
        menu $win.m.help
        $win.m.help add command -label "Tutorial" -command makeTutorialWin \
                -underline 0
        $win.m.help add command -label "About" -command makeAboutWin -underline 0
        
        if {$::eskil(debug)} {
            $win.m add cascade -label "Debug" -menu $win.m.md -underline 0
            menu $win.m.md
            if {$::tcl_platform(platform) eq "windows"} {
                $win.m.md add checkbutton -label "Console" -variable consolestate \
                        -onvalue show -offvalue hide -command {console $consolestate}
                $win.m.md add separator
            }
................................................................................
            -command "ApplyDirDiffPref ; destroy $top"
    ttk::button $fb.ap -width 10 -text "Apply" -command ApplyDirDiffPref
    ttk::button $fb.ca -width 10 -text "Cancel" -command "destroy $top"
    grid $fb.ok $fb.ap $fb.ca -padx 3 -pady 3
    grid columnconfigure $fb {0 1 2} -uniform a -weight 1

    pack $fb -side bottom -fill x
    pack $check $opts $filter -side "top" -fill x
}

# Experimental...
#preprocess filter pa namnen sa man kan jamfora bibliotek
#med andrade namn.
proc makeRegSubWin {} {
    set top .ddregsub
................................................................................
        set ::dirdiff(leftDir) ""
    }
    if {![info exists ::dirdiff(rightDir)]} {
        set ::dirdiff(rightDir) ""
    }
    destroy .dirdiff
    DirDiff .dirdiff
    return .dirdiff
}

Changes to src/eskil.syntax.

1
2
3
4
5

6
7
8
9
10
11
12
13
14
15


































































































##nagelfar syntax textSearch::searchMenu x
##nagelfar syntax textSearch::enableSearch x x*
##nagelfar syntax DiffUtil::LocateDiffExe x
###nagelfar syntax DiffUtil::diffStrings o* x x
##nagelfar syntax dde s x

##nagelfar syntax safeLoad x n
##nagelfar syntax helpWin x x
##nagelfar syntax commonYScroll x x*
##nagelfar syntax locateEditor n
##nagelfar syntax locateTmp n
##nagelfar syntax wcb::cancel 0
##nagelfar syntax wcb::callback 4
##nagelfar syntax vfs::mk4::Mount r 2
##nagelfar syntax vfs::unmount 1
##nagelfar syntax ttk::entryX x p*







































































































>









|
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
##nagelfar syntax textSearch::searchMenu x
##nagelfar syntax textSearch::enableSearch x x*
##nagelfar syntax DiffUtil::LocateDiffExe x
###nagelfar syntax DiffUtil::diffStrings o* x x
##nagelfar syntax dde s x
##nagelfar syntax dnd s x*
##nagelfar syntax safeLoad x n
##nagelfar syntax helpWin x x
##nagelfar syntax commonYScroll x x*
##nagelfar syntax locateEditor n
##nagelfar syntax locateTmp n
##nagelfar syntax wcb::cancel 0
##nagelfar syntax wcb::callback 4
##nagelfar syntax vfs::mk4::Mount r 2
##nagelfar syntax vfs::unmount 1
###nagelfar syntax ttk::entryX x p*
##nagelfar syntax ::tk::GetSelection x x
##nagelfar syntax ::tk::ScrollButton2Down x x x
##nagelfar syntax console x
##nagelfar syntax registry x x x
##nagelfar syntax pdf4tcl::getPaperSize x
##nagelfar syntax pdf4tcl::getPaperSizeList
##nagelfar syntax twapi::get_foreground_window
##nagelfar syntax twapi::get_window_coordinates x
##nagelfar syntax twapi::get_window_at_location x x
##nagelfar syntax twapi::set_focus x
##nagelfar syntax twapi::send_keys x
##nagelfar syntax twapi::get_window_coordinates x

# Operators
##nagelfar syntax + x*
##nagelfar syntax - x x*
##nagelfar syntax * x*
##nagelfar syntax / x x*

# This is the generic definitions needed for Snit.

##nagelfar syntax _stdclass_snit s x*
##nagelfar subcmd _stdclass_snit destroy configurelist configure
##nagelfar syntax _stdclass_snit\ destroy 0
##nagelfar syntax _stdclass_snit\ configurelist x
##nagelfar syntax _stdclass_snit\ configure x*

##nagelfar syntax snit::type do=_stdclass_snit cn
##nagelfar syntax snit::type::method dm
##nagelfar syntax snit::type::constructor cv
##nagelfar syntax snit::type::destructor cl
##nagelfar syntax snit::type::option x p*
##nagelfar syntax snit::type::component x
##nagelfar syntax snit::type::delegate x*
##nagelfar syntax snit::type::install s x*

##nagelfar syntax snit::widgetadaptor do=_stdclass_snit cn
##nagelfar syntax snit::widgetadaptor::method dm
##nagelfar syntax snit::widgetadaptor::constructor cv
##nagelfar syntax snit::widgetadaptor::destructor cl
##nagelfar syntax snit::widgetadaptor::delegate x*
##nagelfar syntax snit::widgetadaptor::installhull x*
##nagelfar syntax snit::widgetadaptor::from l x*
##nagelfar syntax snit::widgetadaptor::component x
##nagelfar syntax snit::widgetadaptor::install s x*
##nagelfar syntax snit::widgetadaptor::option x p*

##nagelfar syntax snit::widget do=_stdclass_snit cn
##nagelfar syntax snit::widget::method dm
##nagelfar syntax snit::widget::constructor cv
##nagelfar syntax snit::widget::destructor cl
##nagelfar syntax snit::widget::delegate x*
##nagelfar syntax snit::widget::installhull x*
##nagelfar syntax snit::widget::from l x*
##nagelfar syntax snit::widget::hulltype x
##nagelfar syntax snit::widget::widgetclass x
##nagelfar syntax snit::widget::myvar l
##nagelfar syntax snit::widget::mymethod x x*
##nagelfar return snit::widget::myvar varName
##nagelfar syntax snit::widget::component x
##nagelfar syntax snit::widget::install s x*
##nagelfar syntax snit::widget::option x p*

# This is the annotation needed for this object definition

##nagelfar syntax eskilprint dc=_obj,eskilprint p*
##nagelfar option eskilprint -file -cpl -cpln -headsize -headleft -headright -headnpages -margin -paper
##nagelfar return eskilprint _obj,eskilprint
##nagelfar subcmd+ _obj,eskilprint text newLine

##nagelfar implicitvar snit::type::eskilprint self\ _obj,eskilprint width height pdf hoy fontsize linesize nlines ox1 ox2 oy page options

# This is the annotation needed for this object definition

##nagelfar syntax DirDiff dc=_obj,DirDiff p*
###nagelfar option DirDiff
##nagelfar return DirDiff _obj,DirDiff
##nagelfar subcmd+ _obj,DirDiff text newLine

##nagelfar implicitvar snit::widget::DirDiff self\ _obj,DirDiff statusVar hull win self tree

# This is the annotation needed for this object definition

##nagelfar syntax DirCompareTree dc=_obj,DirCompareTree p*
##nagelfar option DirCompareTree -leftdirvariable -rightdirvariable -statusvar
##nagelfar return DirCompareTree _obj,DirCompareTree
##nagelfar subcmd+ _obj,DirCompareTree text newLine

##nagelfar implicitvar snit::widget::DirCompareTree self\ _obj,DirCompareTree hull win self tree hsb vsb options AfterId PauseBgProcessing IdleQueue IdleQueueArr leftMark rightMark leftDir rightDir

# This is the annotation needed for this object definition

##nagelfar syntax ttk::entryX dc=_obj,ttk::entryX p*
##nagelfar option ttk::entryX -width -textvariable -style
##nagelfar return ttk::entryX _obj,ttk::entryX
##nagelfar subcmd+ _obj,ttk::entryX text newLine

##nagelfar implicitvar snit::widgetadaptor::ttk::entryX self\ _obj,ttk::entryX hull win self  options

Changes to src/eskil.tcl.

1
2
3
4
5
6
7
8
9
10
11
12
13
..
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55

56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
...
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
...
192
193
194
195
196
197
198

199
200
201
202
203
204
205
...
224
225
226
227
228
229
230






231
232
233
234
235
236
237
238

239
240
241
242
243
244




245
246
247
248
249
250
251
252
...
356
357
358
359
360
361
362
363

364
365
366
367
368
369
370
...
436
437
438
439
440
441
442
443
444
445
446
447
448
449


450
451
452
453
454
455
456
457
458
459







460
461
462
463
464






































465
466
467
468
469
470
471
472





473
474
475
476
477
478
479
...
480
481
482
483
484
485
486





487
488
489
490
491
492
493
494
495
496
497





498
499
500
501
502
503
504
505





506
507
508





509


510
511
512
513
514
515
516
...
619
620
621
622
623
624
625

626
627
628
629
630
631
632
...
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
...
659
660
661
662
663
664
665

666
667
668
669
670

671
672
673
674
675
676
677
...
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
...
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
...
920
921
922
923
924
925
926

927



928
929
930
931
932
933
934
....
1149
1150
1151
1152
1153
1154
1155
















1156
1157
1158
1159
1160
1161
1162
....
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
....
1270
1271
1272
1273
1274
1275
1276




1277
1278
1279
1280
1281
1282
1283
....
1293
1294
1295
1296
1297
1298
1299
1300
1301
1302
1303
1304
1305
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
....
1385
1386
1387
1388
1389
1390
1391




1392
1393
1394
1395
1396
1397
1398
....
1900
1901
1902
1903
1904
1905
1906



















1907
1908
1909
1910
1911
1912
1913
....
1916
1917
1918
1919
1920
1921
1922







1923
1924
1925
1926
1927
1928
1929
....
1934
1935
1936
1937
1938
1939
1940

1941
1942
1943
















1944
1945
1946
1947
1948
1949
1950
....
2260
2261
2262
2263
2264
2265
2266
2267
2268
2269
2270
2271
2272
2273
2274

2275
2276
2277
2278
2279
2280
2281
....
2390
2391
2392
2393
2394
2395
2396

2397
2398
2399
2400
2401
2402
2403
....
2429
2430
2431
2432
2433
2434
2435
2436
2437
2438
2439
2440
2441
2442
2443
2444
2445
2446
2447
2448
2449
2450
....
2462
2463
2464
2465
2466
2467
2468

2469
2470
2471
2472
2473
2474
2475
....
2513
2514
2515
2516
2517
2518
2519
2520
2521
2522
2523
2524
2525
2526
2527
2528
2529
2530
2531
2532
2533
2534
2535
....
2538
2539
2540
2541
2542
2543
2544
2545
2546
2547
2548
2549
2550
2551
2552
....
2558
2559
2560
2561
2562
2563
2564
2565
2566
2567
2568
2569
2570
2571
2572
2573
2574
2575
2576
2577


2578
2579
2580
2581
2582
2583
2584
....
2593
2594
2595
2596
2597
2598
2599


2600
2601
2602
2603
2604
2605
2606
....
2643
2644
2645
2646
2647
2648
2649


2650
2651
2652
2653
2654
2655
2656
....
2681
2682
2683
2684
2685
2686
2687


2688
2689
2690
2691
2692
2693
2694
....
2831
2832
2833
2834
2835
2836
2837
2838
2839
2840
2841
2842
2843
2844
2845
2846
2847
2848
2849
2850
2851
2852
2853
2854
2855
....
3360
3361
3362
3363
3364
3365
3366

3367
3368
3369

3370
3371
3372
3373
3374






3375
3376

3377

3378
3379
3380
3381
3382
3383
3384
....
3398
3399
3400
3401
3402
3403
3404
3405




3406
3407
3408
3409
3410
3411
3412
3413
3414
3415
3416
3417
3418
3419
3420
3421
3422
3423
3424
3425
3426
3427
3428
3429
3430
3431
3432
3433
....
3448
3449
3450
3451
3452
3453
3454

3455
3456
3457
3458
3459


3460
3461
3462
3463
3464
3465
3466
....
3559
3560
3561
3562
3563
3564
3565
3566
3567
3568
3569
3570


3571
3572
3573
3574
3575
3576
3577
....
3606
3607
3608
3609
3610
3611
3612


3613
3614
3615
3616
3617
3618
3619
....
3623
3624
3625
3626
3627
3628
3629






3630
3631
3632
3633
3634
3635
3636
3637
3638
3639
3640
....
3647
3648
3649
3650
3651
3652
3653




3654
3655
3656
3657
3658
3659
3660
3661
3662
3663
3664
3665


3666
3667
3668
3669
3670
3671
3672
3673
3674
3675
3676
3677
3678
3679
3680
3681
3682
3683
3684
3685
3686
3687
3688
3689
3690
3691
3692
3693
3694
3695
3696
3697
3698
3699
3700
3701
3702
3703
3704
3705
3706
3707
3708
3709
3710
3711
3712
3713
3714











3715
3716
3717
3718
3719
3720
3721
3722
3723
3724
3725
3726
3727
3728
3729
3730
3731
3732
....
3759
3760
3761
3762
3763
3764
3765

3766
3767
3768
3769
3770
3771
3772
3773
3774
3775
3776
3777
3778
3779
3780
3781
3782
3783
....
3789
3790
3791
3792
3793
3794
3795
3796
3797
3798
3799
3800
3801
3802
3803
3804
3805
3806
3807
3808
3809
3810
....
3818
3819
3820
3821
3822
3823
3824

3825
3826
3827
3828
3829
3830
3831
....
3889
3890
3891
3892
3893
3894
3895

3896
3897
3898
3899
3900
3901
3902
....
3940
3941
3942
3943
3944
3945
3946
3947
3948




3949
3950
3951
3952
3953
3954
3955
3956
3957
3958
3959
3960
3961
3962
3963
3964
3965
3966
3967
3968
3969
#!/bin/sh
#---------------------------------------------------------- -*- tcl -*-
#
#  Eskil, a Graphical frontend to diff
#
#  Copyright (c) 1998-2010, Peter Spjuth  (peter.spjuth@gmail.com)
#
#  Usage
#             Do 'eskil' for interactive mode
#             Do 'eskil --help' for command line usage
#
#  This program is free software; you can redistribute it and/or modify
#  it under the terms of the GNU General Public License as published by
................................................................................
#
#  You should have received a copy of the GNU General Public License
#  along with this program; see the file COPYING.  If not, write to
#  the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
#  Boston, MA 02111-1307, USA.
#
#----------------------------------------------------------------------
# $Revision$
#----------------------------------------------------------------------
# the next line restarts using tclsh \
exec tclsh "$0" "$@"

package require Tcl 8.5

# Stop Tk from meddling with the command line by copying it first.
set ::eskil(argv) $::argv
set ::eskil(argc) $::argc
set ::argv {}
set ::argc 0

set debug 1
set diffver "Version 2.4+ 2010-11-07"
set ::thisScript [file join [pwd] [info script]]

namespace import tcl::mathop::+
namespace import tcl::mathop::-
namespace import tcl::mathop::*
namespace import tcl::mathop::/

# Do initalisations for needed packages and globals.
# This is not run until needed to speed up command line error reporting.
proc Init {} {
    package require Tk 8.4
    catch {package require textSearch}
    package require wcb


    if {[catch {package require psballoon}]} {
        # Add a dummy if it does not exist.
        proc addBalloon {args} {}
    } else {
        namespace import -force psballoon::addBalloon
    }

    set ::thisDir [file dirname $::thisScript]

    # Follow any link
    set tmplink $::thisScript
    while {[file type $tmplink] eq "link"} {
        set tmplink [file readlink $tmplink]
        set tmplink [file normalize [file join $::thisDir $tmplink]]
        set ::thisDir [file dirname $tmplink]
    }

    # Get all other source files
    source $::thisDir/clip.tcl
    source $::thisDir/compare.tcl
    source $::thisDir/map.tcl
    source $::thisDir/merge.tcl
    source $::thisDir/registry.tcl
    source $::thisDir/dirdiff.tcl
    source $::thisDir/help.tcl
    source $::thisDir/plugin.tcl
    source $::thisDir/printobj.tcl
    source $::thisDir/print.tcl
    source $::thisDir/rev.tcl

    set ::util(diffexe) diff

    # Diff functionality is in the DiffUtil package.
    package require DiffUtil
    # Help DiffUtil to find a diff executable, if needed
    catch {DiffUtil::LocateDiffExe $::thisScript}

    # Figure out a place to store temporary files.
    locateTmp ::diff(tmpdir)

    if {$::tcl_platform(platform) eq "windows"} {
        # Locate CVS if it is in c:/bin
        if {[auto_execok cvs] eq "" && [file exists "c:/bin/cvs.exe"]} {
................................................................................
    }

    interp alias {} toplevel {} ttk::toplevel
}

# Debug function to be able to reread the source even when wrapped in a kit.
proc EskilRereadSource {} {
    set this $::thisScript

    # FIXA: Better detection of starkit?
    # Maybe look at ::starkit::topdir ?

    #if {[info exists ::starkit::topdir]} {
    #    puts "Topdir: $::starkit::topdir"
    #}
................................................................................
    puts "Resourcing $this"
    uplevel \#0 [list source $this]
}

# This function is called when a toplevel is closed.
# If it is the last remaining toplevel, the application quits.
# If top = "all" it means quit.

proc cleanupAndExit {top} {
    # A security thing to make sure we can exit.
    set cont 0
    if {[catch {
        if {$top != "all"} {
            set i [lsearch $::diff(diffWindows) $top]
            if {$i >= 0} {
................................................................................
                (This is a bug)\nTerminating application." -type ok
    }
    if {$cont} return

    clearTmp
    exit
}







# Format a line number
proc myFormL {lineNo} {
    if {![string is integer -strict $lineNo]} {return "$lineNo\n"}
      return [format "%3d: \n" $lineNo]
}

# Get a name for a temporary file

proc tmpFile {} {
    if {[info exists ::tmpcnt]} {
        incr ::tmpcnt
    } else {
        set ::tmpcnt 0
    }




    set name [file join $::diff(tmpdir) "tmpd[pid]a$::tmpcnt"]
    lappend ::tmpfiles $name
    return $name
}

# Delete temporary files
proc clearTmp {args} {
    if {![info exists ::tmpfiles]} {
................................................................................
    incr doingLine2
}

# Detect if only newlines has changed within the block, e.g.
# when rearranging newlines.
# Rearranging newlines in comment blocks usually leads to
# words moving across "*", ignore * too.
# Returns 0 if the block in not handled here, non-zero if the block is done.

proc ParseBlocksAcrossNewline {top block1 block2} {
    global doingLine1 doingLine2

    set map {{ } {} \t {}}
    set RE {\n\s*\*?|\s}
    set equal 0
    set visible [expr {$::eskil(ignorenewline) == 1}]
................................................................................
        return [expr {($n1 > $n2 ? $n1 : $n2) + 1}]
    } else {
        return [expr {-($n1 > $n2 ? $n1 : $n2)}]
    }
}

# Insert two blocks of lines in the compare windows.
# Returns number of lines used to display the blocks
# Negative if the block should be viewed as equal
proc insertMatchingBlocks {top block1 block2} {
    global doingLine1 doingLine2

    # A large block may take time.  Give a small warning.
    if {[llength $block1] * [llength $block2] > 1000} {


        set ::widgets($top,eqLabel) "!"
        #puts "Eskil warning: Analyzing a large block. ($size1 $size2)"
        update idletasks
    }

    # Detect if only newlines has changed within the block, e.g.
    # when rearranging newlines.
    if {$::eskil(ignorenewline)} {
        set res [ParseBlocksAcrossNewline $top $block1 $block2]
        if {$res != 0} {







            return $res
        }
    }

    set apa [compareBlocks $block1 $block2]







































    set t1 0
    set t2 0
    foreach c $apa {
        if {$c eq "c"} {
            set textline1 [lindex $block1 $t1]
            set textline2 [lindex $block2 $t2]
            insertMatchingLines $top $textline1 $textline2





            incr t1
            incr t2
        } elseif {$c eq "C"} {
	    # This is two lines that the block matching considered
	    # too different to use line parsing on them.
	    # Marked the whole line as deleted/inserted
            set textline1 [lindex $block1 $t1]
................................................................................
            set textline2 [lindex $block2 $t2]
            $::widgets($top,wLine1) insert end [myFormL $doingLine1] \
                    "hl$::HighLightCount change"
            $::widgets($top,wDiff1) insert end "$textline1\n" new1
            $::widgets($top,wLine2) insert end [myFormL $doingLine2] \
                    "hl$::HighLightCount change"
            $::widgets($top,wDiff2) insert end "$textline2\n" new2





            incr doingLine1
            incr doingLine2
            incr t1
            incr t2
        } elseif {$c eq "d"} {
            set bepa [lindex $block1 $t1]
            $::widgets($top,wLine1) insert end [myFormL $doingLine1] \
                    "hl$::HighLightCount change"
            $::widgets($top,wDiff1) insert end "$bepa\n" new1
            emptyLine $top 2
            incr doingLine1





            incr t1
        } elseif {$c eq "a"} {
            set bepa [lindex $block2 $t2]
            $::widgets($top,wLine2) insert end [myFormL $doingLine2] \
                    "hl$::HighLightCount change"
            $::widgets($top,wDiff2) insert end "$bepa\n" new2
            emptyLine $top 1
            incr doingLine2





            incr t2
        }
    }





    return [llength $apa]


}

# Process one of the change/add/delete blocks reported by diff.
#  ch1 is a file channel for the left file
#  ch2 is a file channel for the right file
#  n1/n2 is the number of lines involved
#  line1/line2 says on what lines this block starts
................................................................................
            gets $ch2 textline2
            insertMatchingLines $top $textline1 $textline2
        }
        if {$::diff(filter) != "" &&  $::diff(filterflag)} {
            addMapLines $top $n1
        } else {
            addChange $top $n1 change $line1 $n1 $line2 $n2

        }
    } else {
        if {$n1 != 0 && $n2 != 0 && $Pref(parse) >= 2 && \
                ($n1 * $n2 < 1000 || $Pref(parse) == 3)} {
            # Full block parsing
            set block1 {}
            for {set t 0} {$t < $n1} {incr t} {
................................................................................
                lappend block1 $apa
            }
            set block2 {}
            for {set t 0} {$t < $n2} {incr t} {
                gets $ch2 apa
                lappend block2 $apa
            }
            set apa [insertMatchingBlocks $top $block1 $block2]
            if {$apa >= 0} {
                addChange $top $apa change $line1 $n1 $line2 $n2
            } else {
                addMapLines $top [- $apa]
                # In this case, a change is not visible
                return 1
            }
        } else {
            # No extra parsing at all.
            for {set t 0} {$t < $n1} {incr t} {
                gets $ch1 apa
                insertLine $top 1 $doingLine1 $apa $tag1
                incr doingLine1
            }
................................................................................
                incr doingLine2
            }
            if {$n1 <= $n2} {
                for {set t $n1} {$t < $n2} {incr t} {
                    emptyLine $top 1
                }
                addChange $top $n2 $tag2 $line1 $n1 $line2 $n2

            } elseif {$n2 < $n1} {
                for {set t $n2} {$t < $n1} {incr t} {
                    emptyLine $top 2
                }
                addChange $top $n1 $tag1 $line1 $n1 $line2 $n2

            }
        }
    }
    # Empty return value
    return
}

................................................................................
            }
            continue
        }
        # No change block anymore. If one just ended, display it.
        if {[llength $lblock] > 0 || [llength $rblock] > 0} {
            set ::doingLine1 $lblockl
            set ::doingLine2 $rblockl
            addMapLines $top [insertMatchingBlocks $top $lblock $rblock]
            set lblock {}
            set rblock {}
        }
        if {$lmode == "" && $rmode == ""} {
            insertLine $top 1 $lline $lstr
            insertLine $top 2 $rline $rstr
            incr leftc
................................................................................
            continue
        }
    }
    # If the patch ended with a change block, display it.
    if {[llength $lblock] > 0 || [llength $rblock] > 0} {
        set ::doingLine1 $lblockl
        set ::doingLine2 $rblockl
        addMapLines $top [insertMatchingBlocks $top $lblock $rblock]
        set lblock {}
        set rblock {}
    }
}

# Read a patch file and display it
proc displayPatch {top} {
................................................................................
    global Pref

    set ::diff($top,leftLabel) "Patch $::diff($top,patchFile): old"
    set ::diff($top,rightLabel) "Patch $::diff($top,patchFile): new"
    update idletasks

    if {$::diff($top,patchFile) eq ""} {

        set data [getFullPatch $top]



    } elseif {$::diff($top,patchFile) eq "-"} {
        set data [read stdin]
    } else {
        set ch [open $::diff($top,patchFile) r]
        set data [read $ch]
        close $ch
    }
................................................................................

    # Restore view
    foreach item {wLine1 wDiff1 wLine2 wDiff2} {
        set w $::widgets($top,$item)
        seeText $w $first.0 $last.0
    }
}

















# Main diff function.
proc doDiff {top} {
    global Pref
    global doingLine1 doingLine2

    if {$::diff($top,mode) eq "" && ($::diff($top,leftOK) == 0 || $::diff($top,rightOK) == 0)} {
................................................................................
        $::widgets($top,wLine2) see 1.0
        normalCursor $top
        return
    } else {
        prepareFiles $top
    }

    set tail1 [file tail $::diff($top,rightLabel)]
    set tail2 [file tail $::diff($top,leftLabel)]
    if {$::diff($top,mode) ne "" || $tail1 eq $tail2} {
        if {$::diff($top,mode) eq "rev"} {
            set tail1 [file tail $::diff($top,RevFile)]
        } elseif {$::diff($top,mode) eq "conflict"} {
            set tail1 [file tail $::diff($top,conflictFile)]
        }
        wm title $top "Eskil: $tail1"
    } else {
        wm title $top "Eskil: $tail2 vs $tail1"
    }

    # Run diff and parse the result.
    set opts $Pref(ignore)
    if {$Pref(nocase)} {lappend opts -nocase}
    if {$Pref(noempty)} {lappend opts -noempty}
    if {[info exists ::diff($top,aligns)] && \
            [llength $::diff($top,aligns)] > 0} {
................................................................................
            return
        }
    } else {
        set ::widgets($top,eqLabel) " "
    }
    # Update the equal label immediately for better feedback
    update idletasks





    set firstview 1

    set ch1 [open $::diff($top,leftFile)]
    set ch2 [open $::diff($top,rightFile)]
    set doingLine1 1
    set doingLine2 1
................................................................................
            incr doingLine2
        }
    }

    set t 0
    foreach i $diffres {
        lassign $i line1 n1 line2 n2
        set notvisible [doText $top $ch1 $ch2 $n1 $n2 $line1 $line2]
        if {$::diff($top,limitlines) && \
                ($::diff($top,mapMax) > $::diff($top,limitlines))} {
            break
        }
        if {$notvisible != 1} {
            bindHighlight $top
            incr ::HighLightCount
        }

        # Get one update when the screen has been filled.
        # Show the first diff.
        if {$firstview && $::diff($top,mapMax) > 100} {
            set firstview 0
            showDiff $top 0
            update idletasks
        }
        if {0 && [incr t] >= 10} {
	    update idletasks
	    $::widgets($top,wLine2) see end
	    update idletasks
            set t 0
        }
    }

    # If there is a range, just display the range
    if {[llength $range] != 0} {
        lassign $range start1 end1 start2 end2
    } else {
        set end1 0
................................................................................
        set ::widgets($top,eqLabel) " "
    }

    cleanupFiles $top
    if {$::diff($top,mode) eq "conflict"} {
        if {$::widgets($top,eqLabel) != "="} {
            makeMergeWin $top




        }
    }
    if {$::diff($top,printFile) ne ""} {
        after idle "doPrint $top 1 ; cleanupAndExit all"
    }
}

................................................................................
        set ::diff($top,rightFile) $apa
        set ::diff($top,rightLabel) $apa
        set ::diff($top,rightOK) 1
        return 1
    }
    return 0
}




















proc openLeft {top} {
    if {[doOpenLeft $top]} {
        set ::diff($top,mode) ""
        set ::diff($top,mergeFile) ""
        doDiff $top
    }
................................................................................
proc openRight {top} {
    if {[doOpenRight $top]} {
        set ::diff($top,mode) ""
        set ::diff($top,mergeFile) ""
        doDiff $top
    }
}








proc openConflict {top} {
    global Pref
    if {[doOpenRight $top]} {
        startConflictDiff $top $::diff($top,rightFile)
        set ::diff($top,mergeFile) ""
        doDiff $top
................................................................................
    global Pref
    if {[doOpenLeft $top]} {
        set ::diff($top,mode) "patch"
        set Pref(ignore) " "
        set Pref(nocase) 0
        set Pref(noempty) 0 
        set ::diff($top,patchFile) $::diff($top,leftFile)

        doDiff $top
    }
}

















proc openRev {top} {
    if {[doOpenRight $top]} {
        set rev [detectRevSystem $::diff($top,rightFile)]
        if {$rev eq ""} {
            tk_messageBox -icon error -title "Eskil Error" -message \
                    "Could not figure out which revison control system\
................................................................................
    if {!$tmp1 && $tmp2} {.lpm delete last}

    set ::diff($top,nopopup) 1
    tk_popup .lpm $X $Y
    after idle [list after 1 [list set ::diff($top,nopopup) 0]]
}

proc bindHighlight {top} {
    set tag hl$::HighLightCount
    foreach n {1 2} {
        $::widgets($top,wLine$n) tag bind $tag <ButtonPress-3> \
                "hlPopup $top $n $::HighLightCount %X %Y %x %y ; break"
        $::widgets($top,wLine$n) tag bind $tag <ButtonPress-1> \
                "hlSelect $top $::HighLightCount"
    }

}

#########
# Zooming
#########

proc zoomRow {w X Y x y} {
................................................................................
proc applyColor {} {
    global dirdiff Pref

    foreach top $::diff(diffWindows) {
        if {$top eq ".clipdiff"} continue
        if {$top != ".dirdiff"} {
            foreach item {wLine1 wDiff1 wLine2 wDiff2} {

                set w $::widgets($top,$item)

                $w tag configure equal -foreground $Pref(colorequal) \
                        -background $Pref(bgequal)
                $w tag configure new1 -foreground $Pref(colornew1) \
                        -background $Pref(bgnew1)
                $w tag configure change -foreground $Pref(colorchange) \
................................................................................
    # Do not scroll if focus is in a text window.
    # This is for scroll bindings in the toplevel.
    if {[winfo class [focus]] != "Text"} {
        $::widgets($top,wDiff1) yview scroll $n $what
    }
}

# Experiment using snit
lappend ::auto_path [file dirname [file dirname $::thisScript]]/lib
#puts $::auto_path
if {[catch {package require snit}]} {
    namespace eval snit {
        proc widgetadaptor {args} {}
    }
}
# Emulate a label that:
# 1 : Displays the right part of the text if there isn't enough room
# 2 : Justfify text to the left if there is enough room.
# 3 : Does not try to allocate space according to its contents
proc fileLabel {w args} {
    ttk::entryX $w -style TLabel
    $w configure {*}$args
................................................................................
# Fill in default data for a diff window
proc initDiffData {top} {
    set ::diff($top,leftOK) 0
    set ::diff($top,rightOK) 0
    set ::diff($top,mode) ""
    set ::diff($top,printFile) ""
    set ::diff($top,mergeFile) ""

    set ::diff($top,conflictFile) ""
    set ::diff($top,limitlines) 0
    set ::diff($top,plugin) ""
}

# Create a new diff window and diff two files
proc newDiff {file1 file2 {range {}}} {
................................................................................
}

# A thing to easily get to debug mode
proc backDoor {a} {
    append ::eskil(backdoor) $a
    set ::eskil(backdoor) [string range $::eskil(backdoor) end-9 end]
    if {$::eskil(backdoor) eq "PeterDebug"} {
        set ::debug 1
        catch {console show}
        set ::eskil(backdoor) ""
    }
}

# Build the main window
proc makeDiffWin {{top {}}} {
    global Pref tcl_platform debug

    if {$top != "" && [winfo exists $top] && [winfo toplevel $top] eq $top} {
        # Reuse the old window
        destroy {*}[winfo children $top]
    } else {
        # Locate a free toplevel name
        if {[info exists ::diff(topDiffCnt)]} {
................................................................................
            set t 0
        }
        while {[winfo exists .diff$t]} {
            incr t
        }
        set top .diff$t
        toplevel $top
        lappend ::diff(diffWindows) $top
    }

    wm title $top "Eskil:"
    wm protocol $top WM_DELETE_WINDOW [list cleanupAndExit $top]

    ttk::frame $top.f
    grid $top.f -row 0 -columnspan 4 -sticky nws
................................................................................
    menu $top.m
    $top configure -menu $top.m

    $top.m add cascade -label "File" -underline 0 -menu $top.m.mf
    menu $top.m.mf
    $top.m.mf add command -label "Redo Diff" -underline 5 \
            -command [list redoDiff $top] -state disabled
    if {$debug == 1} {
        $top.m.mf entryconfigure "Redo Diff" -state normal
    }
    $top.m.mf add separator
    $top.m.mf add command -label "Open Both..." -underline 0 \
            -command [list openBoth $top 0]
    $top.m.mf add command -label "Open Both (forget)..." \
            -command [list openBoth $top 1]
    $top.m.mf add command -label "Open Left File..." \
            -command [list openLeft $top]
    $top.m.mf add command -label "Open Right File..." \
            -command [list openRight $top]
    $top.m.mf add separator


    $top.m.mf add command -label "Open Conflict File..." \
            -command [list openConflict $top]
    $top.m.mf add command -label "Open Patch File..." \
            -command [list openPatch $top]
    $top.m.mf add command -label "Revision Diff..." -underline 0 \
            -command [list openRev $top]
    $top.m.mf add separator
................................................................................

    $top.m add cascade -label "Options" -underline 0 -menu $top.m.mo
    menu $top.m.mo
    $top.m.mo add cascade -label "Font" -underline 0 -menu $top.m.mo.f
    $top.m.mo add cascade -label "Ignore" -underline 0 -menu $top.m.mo.i
    $top.m.mo add command -label "Preprocess..." -underline 0 \
            -command [list EditPrefRegsub $top]


    $top.m.mo add cascade -label "Parse" -underline 1 -menu $top.m.mo.p
    $top.m.mo add command -label "Colours..." -underline 0 -command makePrefWin
    $top.m.mo add cascade -label "Context" -underline 1 -menu $top.m.mo.c
    $top.m.mo add separator
    $top.m.mo add checkbutton -label "Toolbar" -variable ::Pref(toolbar)
    $top.m.mo add separator
    $top.m.mo add command -label "Save default" \
................................................................................
    $top.m.mo.p add radiobutton -label "Blocks" -variable Pref(parse) -value 3
    $top.m.mo.p add separator
    $top.m.mo.p add radiobutton -label "Characters" \
            -variable Pref(lineparsewords) -value "0"
    $top.m.mo.p add radiobutton -label "Words" \
            -variable Pref(lineparsewords) -value "1"
    $top.m.mo.p add separator


    $top.m.mo.p add checkbutton -label "Mark last" -variable Pref(marklast)

    menu $top.m.mo.c
    $top.m.mo.c add radiobutton -label "Show all lines" \
            -variable ::Pref(context) -value -1
    $top.m.mo.c add radiobutton -label "Show only diffs" \
            -variable ::Pref(context) -value 0
................................................................................
            -command makeDirDiffWin
    $top.m.mt add command -label "Clip Diff" -underline 0 \
            -command makeClipDiffWin
    $top.m.mt add command -label "Merge" -underline 0 \
            -command [list makeMergeWin $top] -state disabled
    $top.m.mt add command -label "Edit Mode" -underline 0 \
            -command [list allowEdit $top] -state disabled


    $top.m.mt add command -label "Clear Align" \
            -command [list clearAlign $top] -state disabled
    set ::widgets($top,enableAlignCmd) [list \
            $top.m.mt entryconfigure "Clear Align" -state normal]
    set ::widgets($top,disableAlignCmd) [list \
            $top.m.mt entryconfigure "Clear Align" -state disabled]

................................................................................
    grid $top.ls -sticky ""

    bind $top <Key-Up>    [list scrollText $top -1 u]
    bind $top <Key-Down>  [list scrollText $top  1 u]
    bind $top <Key-Prior> [list scrollText $top -1 pa]
    bind $top <Key-Next>  [list scrollText $top  1 pa]
    bind $top <Key-Escape> [list focus $top]
    if {$debug == 0} {
        bind $top <Key> "backDoor %A"
    }

    pack $top.bfn -in $top.f -side right -padx {3 6}
    pack $top.bfp $top.bcm $top.blg \
            $top.er2 $top.lr2 $top.er1 $top.lr1 \
            -in $top.f -side right -padx 3
    pack $top.bfn $top.bfp $top.bcm -ipadx 15

    if {$debug == 1} {
        $top.m add cascade -label "Debug" -menu $top.m.md -underline 0
        menu $top.m.md
        if {$tcl_platform(platform) eq "windows"} {
            $top.m.md add checkbutton -label "Console" -variable consolestate \
                    -onvalue show -offvalue hide \
                    -command {console $consolestate}
            $top.m.md add separator
................................................................................
  -prefix <str> : Care mainly about words starting with "str".
  -preprocess <pair> : TBW

  -r <ver>    : Version info for version control mode.
  -cvs        : Detect CVS first, if multiple version systems are used.
  -svn        : Detect SVN first, if multiple version systems are used.


  -conflict   : Treat file as a merge conflict file and enter merge
                mode.
  -o <file>   : Specify merge result output file.


  -browse     : Automatically bring up file dialog after starting.
  -server     : Set up Eskil to be controllable from the outside.

  -print <file> : Generate pdf and exit.







  -plugin <plugin>     : Use plugin

  -plugininfo <info>   : Pass info to plugin (plugin specific)

  -plugindump <plugin> : Dump plugin source to stdout

  -limit <lines> : Do not process more than <lines> lines.

To list all options matching a prefix, run 'eskil --query prefix'.
In tcsh use this line to get option completion:
complete eskil 'C/-/`eskil --query -`/'}
................................................................................
    }
    if {$fail} {
        puts "Argument $opt must be a list of RBG values from 0.0 to 1.0"
        exit
    }
}

# Go through all command line arguments




proc parseCommandLine {} {
    global dirdiff Pref

    set ::eskil(autoclose) 0
    set ::eskil(ignorenewline) 0

    if {$::eskil(argc) == 0} {
        Init
        makeDiffWin
        return
    }

    set allOpts {
        -w --help -help -b -noignore -i -nocase -nodigit -nokeyword -prefix
        -noparse -line -smallblock -block -char -word -limit -nodiff -dir
        -clip -patch -browse -conflict -print
        -printHeaderSize -printCharsPerLine -printPaper
        -printColorChange -printColorOld -printColorNew
        -server -o -r -context -cvs -svn -review
        -foreach -preprocess -close -nonewline -plugin -plugininfo
        -plugindump
    }

    # If the first option is "--query", use it to ask about options.
    if {$::eskil(argc) == 2 && [lindex $::eskil(argv) 0] == "--query"} {
        set arg [lindex $::eskil(argv) 1]
        if {[lsearch -exact $allOpts $arg] < 0} {
            set match [lsearch -glob -all -inline $allOpts $arg*]
................................................................................
    set dopatch 0
    set doreview 0
    set foreach 0
    set preferedRev "GIT"
    set plugin ""
    set plugininfo ""
    set plugindump ""


    foreach arg $::eskil(argv) {
        if {$nextArg != ""} {
            if {$nextArg eq "mergeFile"} {
                set opts(mergeFile) [file join [pwd] $arg]


            } elseif {$nextArg eq "printFile"} {
                set opts(printFile) [file join [pwd] $arg]
            } elseif {$nextArg eq "printHeaderSize"} {
                if {![string is double -strict $arg] || $arg <= 0} {
                    puts "Argument -printHeaderSize must be a positive number"
                    exit
                }
................................................................................
        } elseif {$arg eq "-nokeyword"} {
            set Pref(dir,ignorekey) 1
        } elseif {$arg eq "-prefix"} {
            set nextArg prefix
        } elseif {$arg eq "-preprocess"} {
            set nextArg preprocess
        } elseif {$arg eq "-plugin"} {
            set nextArg plugin
        } elseif {$arg eq "-plugininfo"} {
            set nextArg plugininfo
        } elseif {$arg eq "-plugindump"} {
            set nextArg plugindump


        } elseif {$arg eq "-context"} {
            set nextArg context
        } elseif {$arg eq "-noparse"} {
            set Pref(parse) 0
        } elseif {$arg eq "-line"} {
            set Pref(parse) 1
        } elseif {$arg eq "-smallblock"} {
................................................................................
            set ::eskil(ignorenewline) 1
        } elseif {$arg eq "-nonewline+"} {
            set ::eskil(ignorenewline) 2
        } elseif {$arg eq "-close"} {
            set ::eskil(autoclose) 1
        } elseif {$arg eq "-conflict"} {
            set opts(mode) "conflict"


        } elseif {$arg eq "-print" || $arg eq "-printpdf"} {
            set nextArg printFile
        } elseif {$arg in {-printHeaderSize -printCharsPerLine -printPaper \
                -printColorChange -printColorOld -printColorNew}} {
            set nextArg [string range $arg 1 end]
        } elseif {$arg eq "-server"} {
            if {$::tcl_platform(platform) eq "windows"} {
................................................................................
                }
            } else {
                package require Tk
                tk appname Eskil
            }
        } elseif {$arg eq "-o"} {
            set nextArg mergeFile






        } elseif {$arg eq "-r"} {
            set nextArg revision
        } elseif {$arg eq "-debug"} {
            set ::debug 1
        } elseif {$arg eq "-svn"} {
            set preferedRev "SVN"
        } elseif {$arg eq "-cvs"} {
            set preferedRev "CVS"
        } elseif {$arg eq "-"} {
            # Allow "-" for stdin patch processing
            lappend files "-"
................................................................................
                lappend files $apa
            }
        }
    }

    Init





    if {$plugindump ne ""} {
        printPlugin $plugindump
        exit
    }
    if {$plugin ne ""} {
        set pinterp [createPluginInterp $plugin $plugininfo]
        if {$pinterp eq ""} {
            puts "Bad plugin: $plugin"
            printPlugins
            exit
        }
        set opts(plugin) $pinterp


    }

    # Do we start in clip diff mode?
    if {$doclip} {
        makeClipDiffWin
        return
    }

    # Figure out if we start in a diff or dirdiff window.
    set len [llength $files]

    if {$len == 0 && $dodir} {
        set dirdiff(leftDir) [pwd]
        set dirdiff(rightDir) [pwd]
        makeDirDiffWin
        return
    }
    if {$len == 1} {
        set fullname [lindex $files 0]
        if {[FileIsDirectory $fullname 1]} {
            set dirdiff(leftDir) $fullname
            set dirdiff(rightDir) $dirdiff(leftDir)
            makeDirDiffWin
            return
        }
    } elseif {$len >= 2} {
        set fullname1 [lindex $files 0]
        set fullname2 [lindex $files 1]
        if {[FileIsDirectory $fullname1 1] && [FileIsDirectory $fullname2 1]} {
            set dirdiff(leftDir) $fullname1
            set dirdiff(rightDir) $fullname2
            makeDirDiffWin
            return
        }
    }

    # Ok, we have a normal diff
    makeDiffWin
    update
    set top [lindex $::diff(diffWindows) end]
    # Copy the previously collected options
    foreach {item val} [array get opts] {
        set ::diff($top,$item) $val
    }

    # It is preferable to see the end if the rev string is too long
    $::widgets($top,rev1) xview end
    $::widgets($top,rev2) xview end












    if {$len == 1 || $foreach} {
        set ReturnAfterLoop 0
        set first 1
        foreach file $files {
            if {$first} {
                set first 0
            } else {
                # Create new window for other files
                makeDiffWin
                update
                set top [lindex $::diff(diffWindows) end]
                # Copy the previously collected options
                foreach {item val} [array get opts] {
                    set ::diff($top,$item) $val
                }
                # It is preferable to see the end if the rev string is too long
                $::widgets($top,rev1) xview end
                $::widgets($top,rev2) xview end
................................................................................
            set ::diff($top,leftLabel) $fullname
            set ::diff($top,leftOK) 1
            if {$dopatch                                 || \
                    [regexp {\.(diff|patch)$} $fullname] || \
                    $fullname eq "-"} {
                set ::diff($top,mode) "patch"
                set ::diff($top,patchFile) $fullname

                set autobrowse 0
                if {$noautodiff} {
                    enableRedo $top
                } else {
                    after idle [list doDiff $top]
                }
                set ReturnAfterLoop 1
                continue
            }
        }
        if {$ReturnAfterLoop} return
    } elseif {$len >= 2} {
        set fullname [file join [pwd] [lindex $files 0]]
        set fulldir [file dirname $fullname]
        set ::diff($top,leftDir) $fulldir
        set ::diff($top,leftFile) $fullname
        set ::diff($top,leftLabel) $fullname
        set ::diff($top,leftOK) 1
................................................................................
        set ::diff($top,rightOK) 1
        if {$noautodiff} {
            enableRedo $top
        } else {
            after idle [list doDiff $top]
        }
    }
    if {$doreview} {
        set rev [detectRevSystem "" $preferedRev]
        set ::diff($top,modetype) $rev
        set ::diff($top,mode) "patch"
        set ::diff($top,patchFile) ""
        after idle [list doDiff $top]
        return
    }
    if {$autobrowse && (!$::diff($top,leftOK) || !$::diff($top,rightOK))} {
        if {!$::diff($top,leftOK) && !$::diff($top,rightOK)} {
            openBoth $top 0
        } elseif {!$::diff($top,leftOK)} {
            openLeft $top
        } elseif {!$::diff($top,rightOK)} {
            openRight $top
................................................................................
                set fullname $::diff($top,leftFile)
                set ::diff($top,leftOK) 0
                startRevMode $top "CVS" $fullname
                after idle [list doDiff $top]
            }
        }
    }

}

# Save options to file ~/.eskilrc
proc saveOptions {top} {
    global Pref

    # Check if the window size has changed
................................................................................
    set Pref(colornew1) darkgreen
    set Pref(colornew2) blue
    set Pref(bgequal) ""
    set Pref(bgchange) \#ffe0e0
    set Pref(bgnew1) \#a0ffa0
    set Pref(bgnew2) \#e0e0ff
    set Pref(context) -1

    set Pref(marklast) 1
    set Pref(linewidth) 80
    set Pref(lines) 60
    set Pref(editor) ""
    set Pref(regsub) {}
    set Pref(toolbar) 0
    set Pref(wideMap) 0 ;# Not settable in GUI yet
................................................................................
    }
    unset Pref(onlydiffs)

    # Set up reactions to some Pref settings
    if {![info exists ::widgets(toolbars)]} {
        set ::widgets(toolbars) {}
    }
    # FIXA: Move to procs, handle destroyed windows.
    trace add variable ::Pref(toolbar) write "




        foreach __ \$::widgets(toolbars) {
            if {\$::Pref(toolbar)} {
                grid configure \$__
            } else {
                grid remove \$__
            }
        }
    \# "
}

# Global code is only run the first time to be able to reread source
if {![info exists gurkmeja]} {
    set gurkmeja 1

    package require pstools
    namespace import -force pstools::*
    getOptions
    if {![info exists ::eskil_testsuite]} {
        parseCommandLine
    }
}





|







 







<
<











|
|
|












>








|


|


|
|



|
|
|
|
|
|
|
|
|
|
|






|







 







|







 







>







 







>
>
>
>
>
>








>
|





>
>
>
>
|







 







|
>







 







<
<
|



|
>
>




|





>
>
>
>
>
>
>
|




>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>








>
>
>
>
>







 







>
>
>
>
>











>
>
>
>
>








>
>
>
>
>



>
>
>
>
>
|
>
>







 







>







 







|
<
<
<
<
<
<
<







 







>





>







 







|







 







|







 







>
|
>
>
>







 







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







 







<
<
<
<
<
<
<
<
|
<
<
<







 







>
>
>
>







 







|




<
<
<
<








<
<
<
<
<
<







 







>
>
>
>







 







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







 







>
>
>
>
>
>
>







 







>



>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







 







|







>







 







>







 







<
<
<
<
<
<
<
<







 







>







 







|







|







 







|







 







|












>
>







 







>
>







 







>
>







 







>
>







 







|









|







 







>



>




|
>
>
>
>
>
>

<
>

>







 







|
>
>
>
>








|
<








|

|







 







>





>
>







 







|

|

|
>
>







 







>
>







 







>
>
>
>
>
>



|







 







>
>
>
>












>
>




|
<








|
<






|
<







|
<




|

<









>
>
>
>
>
>
>
>
>
>
>








|

<







 







>










|







 







<
<
<
<
<
<
<
<







 







>







 







>







 







<
|
>
>
>
>
|
|
|
|
|
|
|
<



|
|








1
2
3
4
5
6
7
8
9
10
11
12
13
..
21
22
23
24
25
26
27


28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
...
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
...
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
...
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
...
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
...
448
449
450
451
452
453
454


455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
...
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
...
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
...
719
720
721
722
723
724
725
726







727
728
729
730
731
732
733
...
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
...
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
...
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
....
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
....
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
1259
1260
1261
1262
....
1298
1299
1300
1301
1302
1303
1304








1305



1306
1307
1308
1309
1310
1311
1312
....
1359
1360
1361
1362
1363
1364
1365
1366
1367
1368
1369
1370
1371
1372
1373
1374
1375
1376
....
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397




1398
1399
1400
1401
1402
1403
1404
1405






1406
1407
1408
1409
1410
1411
1412
....
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485
....
1987
1988
1989
1990
1991
1992
1993
1994
1995
1996
1997
1998
1999
2000
2001
2002
2003
2004
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
....
2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041
2042
....
2047
2048
2049
2050
2051
2052
2053
2054
2055
2056
2057
2058
2059
2060
2061
2062
2063
2064
2065
2066
2067
2068
2069
2070
2071
2072
2073
2074
2075
2076
2077
2078
2079
2080
....
2390
2391
2392
2393
2394
2395
2396
2397
2398
2399
2400
2401
2402
2403
2404
2405
2406
2407
2408
2409
2410
2411
2412
....
2521
2522
2523
2524
2525
2526
2527
2528
2529
2530
2531
2532
2533
2534
2535
....
2561
2562
2563
2564
2565
2566
2567








2568
2569
2570
2571
2572
2573
2574
....
2586
2587
2588
2589
2590
2591
2592
2593
2594
2595
2596
2597
2598
2599
2600
....
2638
2639
2640
2641
2642
2643
2644
2645
2646
2647
2648
2649
2650
2651
2652
2653
2654
2655
2656
2657
2658
2659
2660
....
2663
2664
2665
2666
2667
2668
2669
2670
2671
2672
2673
2674
2675
2676
2677
....
2683
2684
2685
2686
2687
2688
2689
2690
2691
2692
2693
2694
2695
2696
2697
2698
2699
2700
2701
2702
2703
2704
2705
2706
2707
2708
2709
2710
2711
....
2720
2721
2722
2723
2724
2725
2726
2727
2728
2729
2730
2731
2732
2733
2734
2735
....
2772
2773
2774
2775
2776
2777
2778
2779
2780
2781
2782
2783
2784
2785
2786
2787
....
2812
2813
2814
2815
2816
2817
2818
2819
2820
2821
2822
2823
2824
2825
2826
2827
....
2964
2965
2966
2967
2968
2969
2970
2971
2972
2973
2974
2975
2976
2977
2978
2979
2980
2981
2982
2983
2984
2985
2986
2987
2988
....
3493
3494
3495
3496
3497
3498
3499
3500
3501
3502
3503
3504
3505
3506
3507
3508
3509
3510
3511
3512
3513
3514
3515
3516

3517
3518
3519
3520
3521
3522
3523
3524
3525
3526
....
3540
3541
3542
3543
3544
3545
3546
3547
3548
3549
3550
3551
3552
3553
3554
3555
3556
3557
3558
3559
3560

3561
3562
3563
3564
3565
3566
3567
3568
3569
3570
3571
3572
3573
3574
3575
3576
3577
3578
....
3593
3594
3595
3596
3597
3598
3599
3600
3601
3602
3603
3604
3605
3606
3607
3608
3609
3610
3611
3612
3613
3614
....
3707
3708
3709
3710
3711
3712
3713
3714
3715
3716
3717
3718
3719
3720
3721
3722
3723
3724
3725
3726
3727
....
3756
3757
3758
3759
3760
3761
3762
3763
3764
3765
3766
3767
3768
3769
3770
3771
....
3775
3776
3777
3778
3779
3780
3781
3782
3783
3784
3785
3786
3787
3788
3789
3790
3791
3792
3793
3794
3795
3796
3797
3798
....
3805
3806
3807
3808
3809
3810
3811
3812
3813
3814
3815
3816
3817
3818
3819
3820
3821
3822
3823
3824
3825
3826
3827
3828
3829
3830
3831
3832
3833
3834

3835
3836
3837
3838
3839
3840
3841
3842
3843

3844
3845
3846
3847
3848
3849
3850

3851
3852
3853
3854
3855
3856
3857
3858

3859
3860
3861
3862
3863
3864

3865
3866
3867
3868
3869
3870
3871
3872
3873
3874
3875
3876
3877
3878
3879
3880
3881
3882
3883
3884
3885
3886
3887
3888
3889
3890
3891
3892
3893
3894

3895
3896
3897
3898
3899
3900
3901
....
3928
3929
3930
3931
3932
3933
3934
3935
3936
3937
3938
3939
3940
3941
3942
3943
3944
3945
3946
3947
3948
3949
3950
3951
3952
3953
....
3959
3960
3961
3962
3963
3964
3965








3966
3967
3968
3969
3970
3971
3972
....
3980
3981
3982
3983
3984
3985
3986
3987
3988
3989
3990
3991
3992
3993
3994
....
4052
4053
4054
4055
4056
4057
4058
4059
4060
4061
4062
4063
4064
4065
4066
....
4104
4105
4106
4107
4108
4109
4110

4111
4112
4113
4114
4115
4116
4117
4118
4119
4120
4121
4122

4123
4124
4125
4126
4127
4128
4129
4130
4131
4132
4133
4134
4135
#!/bin/sh
#---------------------------------------------------------- -*- tcl -*-
#
#  Eskil, a Graphical frontend to diff
#
#  Copyright (c) 1998-2011, Peter Spjuth  (peter.spjuth@gmail.com)
#
#  Usage
#             Do 'eskil' for interactive mode
#             Do 'eskil --help' for command line usage
#
#  This program is free software; you can redistribute it and/or modify
#  it under the terms of the GNU General Public License as published by
................................................................................
#
#  You should have received a copy of the GNU General Public License
#  along with this program; see the file COPYING.  If not, write to
#  the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
#  Boston, MA 02111-1307, USA.
#
#----------------------------------------------------------------------


# the next line restarts using tclsh \
exec tclsh "$0" "$@"

package require Tcl 8.5

# Stop Tk from meddling with the command line by copying it first.
set ::eskil(argv) $::argv
set ::eskil(argc) $::argc
set ::argv {}
set ::argc 0

set ::eskil(debug) 0
set ::eskil(diffver) "Version 2.5+ 2011-04-30"
set ::eskil(thisScript) [file join [pwd] [info script]]

namespace import tcl::mathop::+
namespace import tcl::mathop::-
namespace import tcl::mathop::*
namespace import tcl::mathop::/

# Do initalisations for needed packages and globals.
# This is not run until needed to speed up command line error reporting.
proc Init {} {
    package require Tk 8.4
    catch {package require textSearch}
    package require wcb
    package require snit

    if {[catch {package require psballoon}]} {
        # Add a dummy if it does not exist.
        proc addBalloon {args} {}
    } else {
        namespace import -force psballoon::addBalloon
    }

    set ::eskil(thisDir) [file dirname $::eskil(thisScript)]

    # Follow any link
    set tmplink $::eskil(thisScript)
    while {[file type $tmplink] eq "link"} {
        set tmplink [file readlink $tmplink]
        set tmplink [file normalize [file join $::eskil(thisDir) $tmplink]]
        set ::eskil(thisDir) [file dirname $tmplink]
    }

    # Get all other source files
    source $::eskil(thisDir)/clip.tcl
    source $::eskil(thisDir)/compare.tcl
    source $::eskil(thisDir)/map.tcl
    source $::eskil(thisDir)/merge.tcl
    source $::eskil(thisDir)/registry.tcl
    source $::eskil(thisDir)/dirdiff.tcl
    source $::eskil(thisDir)/help.tcl
    source $::eskil(thisDir)/plugin.tcl
    source $::eskil(thisDir)/printobj.tcl
    source $::eskil(thisDir)/print.tcl
    source $::eskil(thisDir)/rev.tcl

    set ::util(diffexe) diff

    # Diff functionality is in the DiffUtil package.
    package require DiffUtil
    # Help DiffUtil to find a diff executable, if needed
    catch {DiffUtil::LocateDiffExe $::eskil(thisScript)}

    # Figure out a place to store temporary files.
    locateTmp ::diff(tmpdir)

    if {$::tcl_platform(platform) eq "windows"} {
        # Locate CVS if it is in c:/bin
        if {[auto_execok cvs] eq "" && [file exists "c:/bin/cvs.exe"]} {
................................................................................
    }

    interp alias {} toplevel {} ttk::toplevel
}

# Debug function to be able to reread the source even when wrapped in a kit.
proc EskilRereadSource {} {
    set this $::eskil(thisScript)

    # FIXA: Better detection of starkit?
    # Maybe look at ::starkit::topdir ?

    #if {[info exists ::starkit::topdir]} {
    #    puts "Topdir: $::starkit::topdir"
    #}
................................................................................
    puts "Resourcing $this"
    uplevel \#0 [list source $this]
}

# This function is called when a toplevel is closed.
# If it is the last remaining toplevel, the application quits.
# If top = "all" it means quit.
# If eskil is embedded, this should be used to close an eskil toplevel.
proc cleanupAndExit {top} {
    # A security thing to make sure we can exit.
    set cont 0
    if {[catch {
        if {$top != "all"} {
            set i [lsearch $::diff(diffWindows) $top]
            if {$i >= 0} {
................................................................................
                (This is a bug)\nTerminating application." -type ok
    }
    if {$cont} return

    clearTmp
    exit
}

# If embedding, tell eskil about any other toplevel, then
# cleanupAndExit can be used to get rid of it.
proc eskilRegisterToplevel {top} {
    lappend ::diff(diffWindows) $top
}

# Format a line number
proc myFormL {lineNo} {
    if {![string is integer -strict $lineNo]} {return "$lineNo\n"}
      return [format "%3d: \n" $lineNo]
}

# Get a name for a temporary file
# A tail can be given to make the file more recognisable.
proc tmpFile {{tail {}}} {
    if {[info exists ::tmpcnt]} {
        incr ::tmpcnt
    } else {
        set ::tmpcnt 0
    }
    set name "tmpd[pid]a$::tmpcnt"
    if {$tail ne ""} {
        append name " [file tail $tail]"
    }
    set name [file join $::diff(tmpdir) $name]
    lappend ::tmpfiles $name
    return $name
}

# Delete temporary files
proc clearTmp {args} {
    if {![info exists ::tmpfiles]} {
................................................................................
    incr doingLine2
}

# Detect if only newlines has changed within the block, e.g.
# when rearranging newlines.
# Rearranging newlines in comment blocks usually leads to
# words moving across "*", ignore * too.
# Returns 0 if the block in not handled here, non-zero if the block is done,
# negative if the block is considered not a change.
proc ParseBlocksAcrossNewline {top block1 block2} {
    global doingLine1 doingLine2

    set map {{ } {} \t {}}
    set RE {\n\s*\*?|\s}
    set equal 0
    set visible [expr {$::eskil(ignorenewline) == 1}]
................................................................................
        return [expr {($n1 > $n2 ? $n1 : $n2) + 1}]
    } else {
        return [expr {-($n1 > $n2 ? $n1 : $n2)}]
    }
}

# Insert two blocks of lines in the compare windows.


proc insertMatchingBlocks {top block1 block2 line1 line2 details} {
    global doingLine1 doingLine2

    # A large block may take time.  Give a small warning.
    set n1 [llength $block1]
    set n2 [llength $block2]
    if {$n1 * $n2 > 1000} {
        set ::widgets($top,eqLabel) "!"
        #puts "Eskil warning: Analyzing a large block. ($size1 $size2)"
        update idletasks
    }
    
    # Detect if only newlines has changed within the block, e.g.
    # when rearranging newlines.
    if {$::eskil(ignorenewline)} {
        set res [ParseBlocksAcrossNewline $top $block1 $block2]
        if {$res != 0} {
            # FIXA: move this to ParseBlocksAcrossNewline ?
            if {$res > 0 && $details} {
                addChange $top $res change $line1 $n1 $line2 $n2
                nextHighlight $top
            } else {
                addMapLines $top [expr {abs($res)}]
            }
            return
        }
    }

    set apa [compareBlocks $block1 $block2]
    # Fine grained changes means that each line is considered its own
    # chunk. This is used for merging better to avoid the same decision
    # for an entire block.
    set finegrain [expr {$::Pref(finegrainchunks) && $details}]

    if {$finegrain && $::diff($top,ancestorFile) ne ""} {
        # Avoid fine grain depending on relation to ancestor
        set leftChange 0
        set leftChangeOrAdd 0
        for {set t $line1} {$t < $line1 + $n1} {incr t} {
            if {[info exists ::diff($top,ancestorLeft,$t)]} {
                set leftChangeOrAdd 1
                if {$::diff($top,ancestorLeft,$t) eq "c"} {
                    set leftChange 1
                    break
                }
            }
        }
        set rightChange 0
        set rightChangeOrAdd 0
        for {set t $line2} {$t < $line2 + $n2} {incr t} {
            if {[info exists ::diff($top,ancestorRight,$t)]} {
                set rightChangeOrAdd 1
                if {$::diff($top,ancestorRight,$t) eq "c"} {
                    set rightChange 1
                    break
                }
            }
        }
        # Avoid fine grain if either side has no changes against ancestor
        if {!$leftChangeOrAdd || !$rightChangeOrAdd} {
            set finegrain 0
        }
        # Avoid fine grain if both sides have at most additions
        if {!$leftChange && !$rightChange} {
            set finegrain 0
        }
    }

    set t1 0
    set t2 0
    foreach c $apa {
        if {$c eq "c"} {
            set textline1 [lindex $block1 $t1]
            set textline2 [lindex $block2 $t2]
            insertMatchingLines $top $textline1 $textline2
            if {$finegrain} {
                addChange $top 1 change [expr {$line1 + $t1}] 1 \
                        [expr {$line2 + $t2}] 1
                nextHighlight $top
            }
            incr t1
            incr t2
        } elseif {$c eq "C"} {
	    # This is two lines that the block matching considered
	    # too different to use line parsing on them.
	    # Marked the whole line as deleted/inserted
            set textline1 [lindex $block1 $t1]
................................................................................
            set textline2 [lindex $block2 $t2]
            $::widgets($top,wLine1) insert end [myFormL $doingLine1] \
                    "hl$::HighLightCount change"
            $::widgets($top,wDiff1) insert end "$textline1\n" new1
            $::widgets($top,wLine2) insert end [myFormL $doingLine2] \
                    "hl$::HighLightCount change"
            $::widgets($top,wDiff2) insert end "$textline2\n" new2
            if {$finegrain} {
                addChange $top 1 change [expr {$line1 + $t1}] 1 \
                        [expr {$line2 + $t2}] 1
                nextHighlight $top
            }
            incr doingLine1
            incr doingLine2
            incr t1
            incr t2
        } elseif {$c eq "d"} {
            set bepa [lindex $block1 $t1]
            $::widgets($top,wLine1) insert end [myFormL $doingLine1] \
                    "hl$::HighLightCount change"
            $::widgets($top,wDiff1) insert end "$bepa\n" new1
            emptyLine $top 2
            incr doingLine1
            if {$finegrain} {
                addChange $top 1 new1 [expr {$line1 + $t1}] 1 \
                        [expr {$line2 + $t2}] 0
                nextHighlight $top
            }
            incr t1
        } elseif {$c eq "a"} {
            set bepa [lindex $block2 $t2]
            $::widgets($top,wLine2) insert end [myFormL $doingLine2] \
                    "hl$::HighLightCount change"
            $::widgets($top,wDiff2) insert end "$bepa\n" new2
            emptyLine $top 1
            incr doingLine2
            if {$finegrain} {
                addChange $top 1 new2 [expr {$line1 + $t1}] 0 \
                        [expr {$line2 + $t2}] 1
                nextHighlight $top
            }
            incr t2
        }
    }
    if {!$finegrain} {
        if {$details} {
            addChange $top [llength $apa] change $line1 $n1 $line2 $n2
            nextHighlight $top
        } else {
            addMapLines $top [llength $apa]
        }
    }
}

# Process one of the change/add/delete blocks reported by diff.
#  ch1 is a file channel for the left file
#  ch2 is a file channel for the right file
#  n1/n2 is the number of lines involved
#  line1/line2 says on what lines this block starts
................................................................................
            gets $ch2 textline2
            insertMatchingLines $top $textline1 $textline2
        }
        if {$::diff(filter) != "" &&  $::diff(filterflag)} {
            addMapLines $top $n1
        } else {
            addChange $top $n1 change $line1 $n1 $line2 $n2
            nextHighlight $top
        }
    } else {
        if {$n1 != 0 && $n2 != 0 && $Pref(parse) >= 2 && \
                ($n1 * $n2 < 1000 || $Pref(parse) == 3)} {
            # Full block parsing
            set block1 {}
            for {set t 0} {$t < $n1} {incr t} {
................................................................................
                lappend block1 $apa
            }
            set block2 {}
            for {set t 0} {$t < $n2} {incr t} {
                gets $ch2 apa
                lappend block2 $apa
            }
            insertMatchingBlocks $top $block1 $block2 $line1 $line2 1







        } else {
            # No extra parsing at all.
            for {set t 0} {$t < $n1} {incr t} {
                gets $ch1 apa
                insertLine $top 1 $doingLine1 $apa $tag1
                incr doingLine1
            }
................................................................................
                incr doingLine2
            }
            if {$n1 <= $n2} {
                for {set t $n1} {$t < $n2} {incr t} {
                    emptyLine $top 1
                }
                addChange $top $n2 $tag2 $line1 $n1 $line2 $n2
                nextHighlight $top
            } elseif {$n2 < $n1} {
                for {set t $n2} {$t < $n1} {incr t} {
                    emptyLine $top 2
                }
                addChange $top $n1 $tag1 $line1 $n1 $line2 $n2
                nextHighlight $top
            }
        }
    }
    # Empty return value
    return
}

................................................................................
            }
            continue
        }
        # No change block anymore. If one just ended, display it.
        if {[llength $lblock] > 0 || [llength $rblock] > 0} {
            set ::doingLine1 $lblockl
            set ::doingLine2 $rblockl
            insertMatchingBlocks $top $lblock $rblock $lblockl $rblockl 0
            set lblock {}
            set rblock {}
        }
        if {$lmode == "" && $rmode == ""} {
            insertLine $top 1 $lline $lstr
            insertLine $top 2 $rline $rstr
            incr leftc
................................................................................
            continue
        }
    }
    # If the patch ended with a change block, display it.
    if {[llength $lblock] > 0 || [llength $rblock] > 0} {
        set ::doingLine1 $lblockl
        set ::doingLine2 $rblockl
        insertMatchingBlocks $top $lblock $rblock $lblockl $rblockl 0
        set lblock {}
        set rblock {}
    }
}

# Read a patch file and display it
proc displayPatch {top} {
................................................................................
    global Pref

    set ::diff($top,leftLabel) "Patch $::diff($top,patchFile): old"
    set ::diff($top,rightLabel) "Patch $::diff($top,patchFile): new"
    update idletasks

    if {$::diff($top,patchFile) eq ""} {
        if {$::diff($top,patchData) eq ""} {
            set data [getFullPatch $top]
        } else {
            set data $::diff($top,patchData)
        }
    } elseif {$::diff($top,patchFile) eq "-"} {
        set data [read stdin]
    } else {
        set ch [open $::diff($top,patchFile) r]
        set data [read $ch]
        close $ch
    }
................................................................................

    # Restore view
    foreach item {wLine1 wDiff1 wLine2 wDiff2} {
        set w $::widgets($top,$item)
        seeText $w $first.0 $last.0
    }
}

# Make an appropriate tail for a window title, depending on mode and files.
proc TitleTail {top} {
    set tail1 [file tail $::diff($top,rightLabel)]
    set tail2 [file tail $::diff($top,leftLabel)]
    if {$::diff($top,mode) ne "" || $tail1 eq $tail2} {
        if {$::diff($top,mode) eq "rev"} {
            set tail1 [file tail $::diff($top,RevFile)]
        } elseif {$::diff($top,mode) eq "conflict"} {
            set tail1 [file tail $::diff($top,conflictFile)]
        }
        return $tail1
    } else {
        return "$tail2 vs $tail1"
    }
}

# Main diff function.
proc doDiff {top} {
    global Pref
    global doingLine1 doingLine2

    if {$::diff($top,mode) eq "" && ($::diff($top,leftOK) == 0 || $::diff($top,rightOK) == 0)} {
................................................................................
        $::widgets($top,wLine2) see 1.0
        normalCursor $top
        return
    } else {
        prepareFiles $top
    }









    wm title $top "Eskil: [TitleTail $top]"




    # Run diff and parse the result.
    set opts $Pref(ignore)
    if {$Pref(nocase)} {lappend opts -nocase}
    if {$Pref(noempty)} {lappend opts -noempty}
    if {[info exists ::diff($top,aligns)] && \
            [llength $::diff($top,aligns)] > 0} {
................................................................................
            return
        }
    } else {
        set ::widgets($top,eqLabel) " "
    }
    # Update the equal label immediately for better feedback
    update idletasks

    if {$::diff($top,ancestorFile) ne ""} {
        collectAncestorInfo $top $dFile1 $dFile2 $opts
    }

    set firstview 1

    set ch1 [open $::diff($top,leftFile)]
    set ch2 [open $::diff($top,rightFile)]
    set doingLine1 1
    set doingLine2 1
................................................................................
            incr doingLine2
        }
    }

    set t 0
    foreach i $diffres {
        lassign $i line1 n1 line2 n2
        doText $top $ch1 $ch2 $n1 $n2 $line1 $line2
        if {$::diff($top,limitlines) && \
                ($::diff($top,mapMax) > $::diff($top,limitlines))} {
            break
        }





        # Get one update when the screen has been filled.
        # Show the first diff.
        if {$firstview && $::diff($top,mapMax) > 100} {
            set firstview 0
            showDiff $top 0
            update idletasks
        }






    }

    # If there is a range, just display the range
    if {[llength $range] != 0} {
        lassign $range start1 end1 start2 end2
    } else {
        set end1 0
................................................................................
        set ::widgets($top,eqLabel) " "
    }

    cleanupFiles $top
    if {$::diff($top,mode) eq "conflict"} {
        if {$::widgets($top,eqLabel) != "="} {
            makeMergeWin $top
        }
    } elseif {$::diff($top,ancestorFile) ne ""} {
        if {$::widgets($top,eqLabel) != "="} {
            makeMergeWin $top
        }
    }
    if {$::diff($top,printFile) ne ""} {
        after idle "doPrint $top 1 ; cleanupAndExit all"
    }
}

................................................................................
        set ::diff($top,rightFile) $apa
        set ::diff($top,rightLabel) $apa
        set ::diff($top,rightOK) 1
        return 1
    }
    return 0
}

proc doOpenAncestor {top} {
    if {$::diff($top,ancestorFile) ne ""} {
        set initDir [file dirname $::diff($top,ancestorFile)]
    } elseif {[info exists ::diff($top,leftDir)]} {
        set initDir $::diff($top,leftDir)
    } elseif {[info exists ::diff($top,rightDir)]} {
        set initDir $::diff($top,rightDir)
    } else {
        set initDir [pwd]
    }
    set apa [myOpenFile -title "Select ancestor file" -initialdir $initDir \
            -parent $top]
    if {$apa != ""} {
        set ::diff($top,ancestorFile) $apa
        return 1
    }
    return 0
}

proc openLeft {top} {
    if {[doOpenLeft $top]} {
        set ::diff($top,mode) ""
        set ::diff($top,mergeFile) ""
        doDiff $top
    }
................................................................................
proc openRight {top} {
    if {[doOpenRight $top]} {
        set ::diff($top,mode) ""
        set ::diff($top,mergeFile) ""
        doDiff $top
    }
}

proc openAncestor {top} {
    if {[doOpenAncestor $top]} {
        # Redo diff with ancestor
        doDiff $top
    }
}

proc openConflict {top} {
    global Pref
    if {[doOpenRight $top]} {
        startConflictDiff $top $::diff($top,rightFile)
        set ::diff($top,mergeFile) ""
        doDiff $top
................................................................................
    global Pref
    if {[doOpenLeft $top]} {
        set ::diff($top,mode) "patch"
        set Pref(ignore) " "
        set Pref(nocase) 0
        set Pref(noempty) 0 
        set ::diff($top,patchFile) $::diff($top,leftFile)
        set ::diff($top,patchData) ""
        doDiff $top
    }
}

# Get data from clipboard and display as a patch.
proc doPastePatch {top} {
    if {[catch {::tk::GetSelection $top CLIPBOARD} sel]} {
        tk_messageBox -icon error -title "Eskil Error" -parent $top \
                -message "Could not retreive clipboard" -type ok
        return
    }
    set ::diff($top,mode) "patch"
    set ::Pref(ignore) " "
    set ::Pref(nocase) 0
    set ::Pref(noempty) 0 
    set ::diff($top,patchFile) ""
    set ::diff($top,patchData) $sel
    doDiff $top
}

proc openRev {top} {
    if {[doOpenRight $top]} {
        set rev [detectRevSystem $::diff($top,rightFile)]
        if {$rev eq ""} {
            tk_messageBox -icon error -title "Eskil Error" -message \
                    "Could not figure out which revison control system\
................................................................................
    if {!$tmp1 && $tmp2} {.lpm delete last}

    set ::diff($top,nopopup) 1
    tk_popup .lpm $X $Y
    after idle [list after 1 [list set ::diff($top,nopopup) 0]]
}

proc nextHighlight {top} {
    set tag hl$::HighLightCount
    foreach n {1 2} {
        $::widgets($top,wLine$n) tag bind $tag <ButtonPress-3> \
                "hlPopup $top $n $::HighLightCount %X %Y %x %y ; break"
        $::widgets($top,wLine$n) tag bind $tag <ButtonPress-1> \
                "hlSelect $top $::HighLightCount"
    }
    incr ::HighLightCount
}

#########
# Zooming
#########

proc zoomRow {w X Y x y} {
................................................................................
proc applyColor {} {
    global dirdiff Pref

    foreach top $::diff(diffWindows) {
        if {$top eq ".clipdiff"} continue
        if {$top != ".dirdiff"} {
            foreach item {wLine1 wDiff1 wLine2 wDiff2} {
                if {![info exists ::widgets($top,$item)]} continue
                set w $::widgets($top,$item)

                $w tag configure equal -foreground $Pref(colorequal) \
                        -background $Pref(bgequal)
                $w tag configure new1 -foreground $Pref(colornew1) \
                        -background $Pref(bgnew1)
                $w tag configure change -foreground $Pref(colorchange) \
................................................................................
    # Do not scroll if focus is in a text window.
    # This is for scroll bindings in the toplevel.
    if {[winfo class [focus]] != "Text"} {
        $::widgets($top,wDiff1) yview scroll $n $what
    }
}









# Emulate a label that:
# 1 : Displays the right part of the text if there isn't enough room
# 2 : Justfify text to the left if there is enough room.
# 3 : Does not try to allocate space according to its contents
proc fileLabel {w args} {
    ttk::entryX $w -style TLabel
    $w configure {*}$args
................................................................................
# Fill in default data for a diff window
proc initDiffData {top} {
    set ::diff($top,leftOK) 0
    set ::diff($top,rightOK) 0
    set ::diff($top,mode) ""
    set ::diff($top,printFile) ""
    set ::diff($top,mergeFile) ""
    set ::diff($top,ancestorFile) ""
    set ::diff($top,conflictFile) ""
    set ::diff($top,limitlines) 0
    set ::diff($top,plugin) ""
}

# Create a new diff window and diff two files
proc newDiff {file1 file2 {range {}}} {
................................................................................
}

# A thing to easily get to debug mode
proc backDoor {a} {
    append ::eskil(backdoor) $a
    set ::eskil(backdoor) [string range $::eskil(backdoor) end-9 end]
    if {$::eskil(backdoor) eq "PeterDebug"} {
        set ::eskil(debug) 1
        catch {console show}
        set ::eskil(backdoor) ""
    }
}

# Build the main window
proc makeDiffWin {{top {}}} {
    global Pref tcl_platform

    if {$top != "" && [winfo exists $top] && [winfo toplevel $top] eq $top} {
        # Reuse the old window
        destroy {*}[winfo children $top]
    } else {
        # Locate a free toplevel name
        if {[info exists ::diff(topDiffCnt)]} {
................................................................................
            set t 0
        }
        while {[winfo exists .diff$t]} {
            incr t
        }
        set top .diff$t
        toplevel $top
        eskilRegisterToplevel $top
    }

    wm title $top "Eskil:"
    wm protocol $top WM_DELETE_WINDOW [list cleanupAndExit $top]

    ttk::frame $top.f
    grid $top.f -row 0 -columnspan 4 -sticky nws
................................................................................
    menu $top.m
    $top configure -menu $top.m

    $top.m add cascade -label "File" -underline 0 -menu $top.m.mf
    menu $top.m.mf
    $top.m.mf add command -label "Redo Diff" -underline 5 \
            -command [list redoDiff $top] -state disabled
    if {$::eskil(debug) == 1} {
        $top.m.mf entryconfigure "Redo Diff" -state normal
    }
    $top.m.mf add separator
    $top.m.mf add command -label "Open Both..." -underline 0 \
            -command [list openBoth $top 0]
    $top.m.mf add command -label "Open Both (forget)..." \
            -command [list openBoth $top 1]
    $top.m.mf add command -label "Open Left File..." \
            -command [list openLeft $top]
    $top.m.mf add command -label "Open Right File..." \
            -command [list openRight $top]
    $top.m.mf add separator
    $top.m.mf add command -label "Open Ancestor File..." \
            -command [list openAncestor $top]
    $top.m.mf add command -label "Open Conflict File..." \
            -command [list openConflict $top]
    $top.m.mf add command -label "Open Patch File..." \
            -command [list openPatch $top]
    $top.m.mf add command -label "Revision Diff..." -underline 0 \
            -command [list openRev $top]
    $top.m.mf add separator
................................................................................

    $top.m add cascade -label "Options" -underline 0 -menu $top.m.mo
    menu $top.m.mo
    $top.m.mo add cascade -label "Font" -underline 0 -menu $top.m.mo.f
    $top.m.mo add cascade -label "Ignore" -underline 0 -menu $top.m.mo.i
    $top.m.mo add command -label "Preprocess..." -underline 0 \
            -command [list EditPrefRegsub $top]
    $top.m.mo add command -label "Plugins..." -underline 1 \
            -command [list EditPrefPlugins $top]
    $top.m.mo add cascade -label "Parse" -underline 1 -menu $top.m.mo.p
    $top.m.mo add command -label "Colours..." -underline 0 -command makePrefWin
    $top.m.mo add cascade -label "Context" -underline 1 -menu $top.m.mo.c
    $top.m.mo add separator
    $top.m.mo add checkbutton -label "Toolbar" -variable ::Pref(toolbar)
    $top.m.mo add separator
    $top.m.mo add command -label "Save default" \
................................................................................
    $top.m.mo.p add radiobutton -label "Blocks" -variable Pref(parse) -value 3
    $top.m.mo.p add separator
    $top.m.mo.p add radiobutton -label "Characters" \
            -variable Pref(lineparsewords) -value "0"
    $top.m.mo.p add radiobutton -label "Words" \
            -variable Pref(lineparsewords) -value "1"
    $top.m.mo.p add separator
    $top.m.mo.p add checkbutton -label "Fine chunks" -variable Pref(finegrainchunks)
    $top.m.mo.p add separator
    $top.m.mo.p add checkbutton -label "Mark last" -variable Pref(marklast)

    menu $top.m.mo.c
    $top.m.mo.c add radiobutton -label "Show all lines" \
            -variable ::Pref(context) -value -1
    $top.m.mo.c add radiobutton -label "Show only diffs" \
            -variable ::Pref(context) -value 0
................................................................................
            -command makeDirDiffWin
    $top.m.mt add command -label "Clip Diff" -underline 0 \
            -command makeClipDiffWin
    $top.m.mt add command -label "Merge" -underline 0 \
            -command [list makeMergeWin $top] -state disabled
    $top.m.mt add command -label "Edit Mode" -underline 0 \
            -command [list allowEdit $top] -state disabled
    $top.m.mt add command -label "Paste Patch" -underline 0 \
            -command [list doPastePatch $top]
    $top.m.mt add command -label "Clear Align" \
            -command [list clearAlign $top] -state disabled
    set ::widgets($top,enableAlignCmd) [list \
            $top.m.mt entryconfigure "Clear Align" -state normal]
    set ::widgets($top,disableAlignCmd) [list \
            $top.m.mt entryconfigure "Clear Align" -state disabled]

................................................................................
    grid $top.ls -sticky ""

    bind $top <Key-Up>    [list scrollText $top -1 u]
    bind $top <Key-Down>  [list scrollText $top  1 u]
    bind $top <Key-Prior> [list scrollText $top -1 pa]
    bind $top <Key-Next>  [list scrollText $top  1 pa]
    bind $top <Key-Escape> [list focus $top]
    if {$::eskil(debug) == 0} {
        bind $top <Key> "backDoor %A"
    }

    pack $top.bfn -in $top.f -side right -padx {3 6}
    pack $top.bfp $top.bcm $top.blg \
            $top.er2 $top.lr2 $top.er1 $top.lr1 \
            -in $top.f -side right -padx 3
    pack $top.bfn $top.bfp $top.bcm -ipadx 15

    if {$::eskil(debug) == 1} {
        $top.m add cascade -label "Debug" -menu $top.m.md -underline 0
        menu $top.m.md
        if {$tcl_platform(platform) eq "windows"} {
            $top.m.md add checkbutton -label "Console" -variable consolestate \
                    -onvalue show -offvalue hide \
                    -command {console $consolestate}
            $top.m.md add separator
................................................................................
  -prefix <str> : Care mainly about words starting with "str".
  -preprocess <pair> : TBW

  -r <ver>    : Version info for version control mode.
  -cvs        : Detect CVS first, if multiple version systems are used.
  -svn        : Detect SVN first, if multiple version systems are used.

  -a <file>   : Give anscestor file for three way merge.
  -conflict   : Treat file as a merge conflict file and enter merge
                mode.
  -o <file>   : Specify merge result output file.
  -fine       : Use fine grained chunks. Useful for merging.

  -browse     : Automatically bring up file dialog after starting.
  -server     : Set up Eskil to be controllable from the outside.

  -print <file>          : Generate PDF and exit.
  -printCharsPerLine <n> : Adapt font size for this line length and wrap. (80)
  -printPaper <paper>    : Select paper size (a4)
  -printHeaderSize <n>   : Font size for page header (10)
  -printColorChange <RGB> : Color for change   (1.0 0.7 0.7)
  -printColorOld <RGB>    : Color for old text (0.7 1.0 0.7)
  -printColorNew <RGB     : Color for new text (0.8 0.8 1.0)


  -plugin <name>       : Preprocess files using plugin.
  -plugininfo <info>   : Pass info to plugin (plugin specific)
  -pluginlist          : List known plugins
  -plugindump <plugin> : Dump plugin source to stdout

  -limit <lines> : Do not process more than <lines> lines.

To list all options matching a prefix, run 'eskil --query prefix'.
In tcsh use this line to get option completion:
complete eskil 'C/-/`eskil --query -`/'}
................................................................................
    }
    if {$fail} {
        puts "Argument $opt must be a list of RBG values from 0.0 to 1.0"
        exit
    }
}

# Go through all command line arguments and start the appropriate
# diff window.
# Returns the created toplevel.
# This can be used as an entry point if embedding eskil.
# In that case fill in ::eskil(argv) and ::eskil(argc) before calling.
proc parseCommandLine {} {
    global dirdiff Pref

    set ::eskil(autoclose) 0
    set ::eskil(ignorenewline) 0

    if {$::eskil(argc) == 0} {
        Init
        return [makeDiffWin]

    }

    set allOpts {
        -w --help -help -b -noignore -i -nocase -nodigit -nokeyword -prefix
        -noparse -line -smallblock -block -char -word -limit -nodiff -dir
        -clip -patch -browse -conflict -print
        -printHeaderSize -printCharsPerLine -printPaper
        -printColorChange -printColorOld -printColorNew
        -server -o -a -fine -r -context -cvs -svn -review
        -foreach -preprocess -close -nonewline -plugin -plugininfo
        -plugindump -pluginlist
    }

    # If the first option is "--query", use it to ask about options.
    if {$::eskil(argc) == 2 && [lindex $::eskil(argv) 0] == "--query"} {
        set arg [lindex $::eskil(argv) 1]
        if {[lsearch -exact $allOpts $arg] < 0} {
            set match [lsearch -glob -all -inline $allOpts $arg*]
................................................................................
    set dopatch 0
    set doreview 0
    set foreach 0
    set preferedRev "GIT"
    set plugin ""
    set plugininfo ""
    set plugindump ""
    set pluginlist 0

    foreach arg $::eskil(argv) {
        if {$nextArg != ""} {
            if {$nextArg eq "mergeFile"} {
                set opts(mergeFile) [file join [pwd] $arg]
            } elseif {$nextArg eq "ancestorFile"} {
                set opts(ancestorFile) [file join [pwd] $arg]
            } elseif {$nextArg eq "printFile"} {
                set opts(printFile) [file join [pwd] $arg]
            } elseif {$nextArg eq "printHeaderSize"} {
                if {![string is double -strict $arg] || $arg <= 0} {
                    puts "Argument -printHeaderSize must be a positive number"
                    exit
                }
................................................................................
        } elseif {$arg eq "-nokeyword"} {
            set Pref(dir,ignorekey) 1
        } elseif {$arg eq "-prefix"} {
            set nextArg prefix
        } elseif {$arg eq "-preprocess"} {
            set nextArg preprocess
        } elseif {$arg eq "-plugin"} {
            set nextArg "plugin"
        } elseif {$arg eq "-plugininfo"} {
            set nextArg "plugininfo"
        } elseif {$arg eq "-plugindump"} {
            set nextArg "plugindump"
        } elseif {$arg eq "-pluginlist"} {
            set pluginlist 1
        } elseif {$arg eq "-context"} {
            set nextArg context
        } elseif {$arg eq "-noparse"} {
            set Pref(parse) 0
        } elseif {$arg eq "-line"} {
            set Pref(parse) 1
        } elseif {$arg eq "-smallblock"} {
................................................................................
            set ::eskil(ignorenewline) 1
        } elseif {$arg eq "-nonewline+"} {
            set ::eskil(ignorenewline) 2
        } elseif {$arg eq "-close"} {
            set ::eskil(autoclose) 1
        } elseif {$arg eq "-conflict"} {
            set opts(mode) "conflict"
            # Conflict implies foreach
            set foreach 1
        } elseif {$arg eq "-print" || $arg eq "-printpdf"} {
            set nextArg printFile
        } elseif {$arg in {-printHeaderSize -printCharsPerLine -printPaper \
                -printColorChange -printColorOld -printColorNew}} {
            set nextArg [string range $arg 1 end]
        } elseif {$arg eq "-server"} {
            if {$::tcl_platform(platform) eq "windows"} {
................................................................................
                }
            } else {
                package require Tk
                tk appname Eskil
            }
        } elseif {$arg eq "-o"} {
            set nextArg mergeFile
        } elseif {$arg eq "-a"} {
            set nextArg ancestorFile
            # Default is no ignore on three-way merge
            set Pref(ignore) " "
        } elseif {$arg eq "-fine"} {
            set Pref(finegrainchunks) 1
        } elseif {$arg eq "-r"} {
            set nextArg revision
        } elseif {$arg eq "-debug"} {
            set ::eskil(debug) 1
        } elseif {$arg eq "-svn"} {
            set preferedRev "SVN"
        } elseif {$arg eq "-cvs"} {
            set preferedRev "CVS"
        } elseif {$arg eq "-"} {
            # Allow "-" for stdin patch processing
            lappend files "-"
................................................................................
                lappend files $apa
            }
        }
    }

    Init

    if {$pluginlist} {
        printPlugins
        exit
    }
    if {$plugindump ne ""} {
        printPlugin $plugindump
        exit
    }
    if {$plugin ne ""} {
        set pinterp [createPluginInterp $plugin $plugininfo]
        if {$pinterp eq ""} {
            puts "Bad plugin: $plugin"
            printPlugins
            exit
        }
        set opts(plugin) $pinterp
        set opts(pluginname) $plugin
        set opts(plugininfo) $plugininfo
    }

    # Do we start in clip diff mode?
    if {$doclip} {
        return [makeClipDiffWin]

    }

    # Figure out if we start in a diff or dirdiff window.
    set len [llength $files]

    if {$len == 0 && $dodir} {
        set dirdiff(leftDir) [pwd]
        set dirdiff(rightDir) [pwd]
        return [makeDirDiffWin]

    }
    if {$len == 1} {
        set fullname [lindex $files 0]
        if {[FileIsDirectory $fullname 1]} {
            set dirdiff(leftDir) $fullname
            set dirdiff(rightDir) $dirdiff(leftDir)
            return [makeDirDiffWin]

        }
    } elseif {$len >= 2} {
        set fullname1 [lindex $files 0]
        set fullname2 [lindex $files 1]
        if {[FileIsDirectory $fullname1 1] && [FileIsDirectory $fullname2 1]} {
            set dirdiff(leftDir) $fullname1
            set dirdiff(rightDir) $fullname2
            return [makeDirDiffWin]

        }
    }

    # Ok, we have a normal diff
    set top [makeDiffWin]
    update

    # Copy the previously collected options
    foreach {item val} [array get opts] {
        set ::diff($top,$item) $val
    }

    # It is preferable to see the end if the rev string is too long
    $::widgets($top,rev1) xview end
    $::widgets($top,rev2) xview end

    if {$doreview} {
        set rev [detectRevSystem "" $preferedRev]
        set ::diff($top,modetype) $rev
        set ::diff($top,mode) "patch"
        set ::diff($top,patchFile) ""
        set ::diff($top,patchData) ""
        set ::diff($top,reviewFiles) $files
        set ::Pref(toolbar) 1
        after idle [list doDiff $top]
        return $top
    }
    if {$len == 1 || $foreach} {
        set ReturnAfterLoop 0
        set first 1
        foreach file $files {
            if {$first} {
                set first 0
            } else {
                # Create new window for other files
                set top [makeDiffWin]
                update

                # Copy the previously collected options
                foreach {item val} [array get opts] {
                    set ::diff($top,$item) $val
                }
                # It is preferable to see the end if the rev string is too long
                $::widgets($top,rev1) xview end
                $::widgets($top,rev2) xview end
................................................................................
            set ::diff($top,leftLabel) $fullname
            set ::diff($top,leftOK) 1
            if {$dopatch                                 || \
                    [regexp {\.(diff|patch)$} $fullname] || \
                    $fullname eq "-"} {
                set ::diff($top,mode) "patch"
                set ::diff($top,patchFile) $fullname
                set ::diff($top,patchData) ""
                set autobrowse 0
                if {$noautodiff} {
                    enableRedo $top
                } else {
                    after idle [list doDiff $top]
                }
                set ReturnAfterLoop 1
                continue
            }
        }
        if {$ReturnAfterLoop} {return $top}
    } elseif {$len >= 2} {
        set fullname [file join [pwd] [lindex $files 0]]
        set fulldir [file dirname $fullname]
        set ::diff($top,leftDir) $fulldir
        set ::diff($top,leftFile) $fullname
        set ::diff($top,leftLabel) $fullname
        set ::diff($top,leftOK) 1
................................................................................
        set ::diff($top,rightOK) 1
        if {$noautodiff} {
            enableRedo $top
        } else {
            after idle [list doDiff $top]
        }
    }








    if {$autobrowse && (!$::diff($top,leftOK) || !$::diff($top,rightOK))} {
        if {!$::diff($top,leftOK) && !$::diff($top,rightOK)} {
            openBoth $top 0
        } elseif {!$::diff($top,leftOK)} {
            openLeft $top
        } elseif {!$::diff($top,rightOK)} {
            openRight $top
................................................................................
                set fullname $::diff($top,leftFile)
                set ::diff($top,leftOK) 0
                startRevMode $top "CVS" $fullname
                after idle [list doDiff $top]
            }
        }
    }
    return $top
}

# Save options to file ~/.eskilrc
proc saveOptions {top} {
    global Pref

    # Check if the window size has changed
................................................................................
    set Pref(colornew1) darkgreen
    set Pref(colornew2) blue
    set Pref(bgequal) ""
    set Pref(bgchange) \#ffe0e0
    set Pref(bgnew1) \#a0ffa0
    set Pref(bgnew2) \#e0e0ff
    set Pref(context) -1
    set Pref(finegrainchunks) 0
    set Pref(marklast) 1
    set Pref(linewidth) 80
    set Pref(lines) 60
    set Pref(editor) ""
    set Pref(regsub) {}
    set Pref(toolbar) 0
    set Pref(wideMap) 0 ;# Not settable in GUI yet
................................................................................
    }
    unset Pref(onlydiffs)

    # Set up reactions to some Pref settings
    if {![info exists ::widgets(toolbars)]} {
        set ::widgets(toolbars) {}
    }

    trace add variable ::Pref(toolbar) write TraceToolbar
}

proc TraceToolbar {args} {
    # FIXA: Handle destroyed windows ?
    foreach __ $::widgets(toolbars) {
        if {$::Pref(toolbar)} {
            grid configure $__
        } else {
            grid remove $__
        }
    }

}

# Global code is only run the first time to be able to reread source
if {![info exists ::eskil(gurkmeja)]} {
    set ::eskil(gurkmeja) 1

    package require pstools
    namespace import -force pstools::*
    getOptions
    if {![info exists ::eskil_testsuite]} {
        parseCommandLine
    }
}

Changes to src/help.tcl.

21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
..
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
...
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
...
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
#----------------------------------------------------------------------
# $Revision$
#----------------------------------------------------------------------

# Silly experiment...
proc makeNuisance {top {str {Hi there!}}} {
    if {[lsearch [image names] nuisance] < 0} {
        set file [file join $::thisDir .. Nuisance.gif]
        if {![file exists $file]} return
        image create photo nuisance -file $file
    }

    destroy $top.nui
    toplevel $top.nui
    wm transient $top.nui $top
................................................................................
    pack $w.b -side bottom -pady 2
    pack $w.f -side top -expand y -fill both -padx 2 -pady 2
    focus $w
    return $w.f
}

proc makeAboutWin {} {
    global diffver

    set w [helpWin .ab "About Eskil"]

    set bg [ttk::style configure . -background]
    text $w.t -width 45 -height 11 -wrap none -relief flat \
            -background $bg
    pack $w.t -side top -expand y -fill both

    $w.t insert end "A graphical frontend to diff\n\n"
    $w.t insert end "$diffver\n\n"
    $w.t insert end "Made by Peter Spjuth\n"
    $w.t insert end "E-Mail: peter.spjuth@gmail.com\n"
    $w.t insert end "\nURL: http://eskil.berlios.de\n"
    $w.t insert end "\nTcl version: [info patchlevel]\n"

    set du [package provide DiffUtil]
    if {[info procs DiffUtil::LocateDiffExe] ne ""} {
        append du " (tcl)"
    } else {
        append du " (c)"
    }
    $w.t insert end "DiffUtil version: $du\n"
    $w.t insert end "\nCredits:\n"
    $w.t insert end "Ideas for scrollbar map and merge function\n"
    $w.t insert end "taken from TkDiff"

    set last [lindex [split [$w.t index end] "."] 0]
    $w.t configure -height $last
................................................................................
        }
    }
}

proc makeHelpWin {} {
    global Pref

    set doc [file join $::thisDir .. doc/eskil.txt]
    if {![file exists $doc]} return

    set w [helpWin .he "Eskil Help"]
    set t [Scroll y text $w.t -width 85 -height 35]
    pack $w.t -side top -expand 1 -fill both

    configureDocWin $t
................................................................................
proc makeDocWin {fileName} {
    set w [helpWin .doc "Eskil Help"]
    set t [Scroll y text $w.t -width 80 -height 25]
    pack $w.t -side top -expand 1 -fill both

    configureDocWin $t

    if {![file exists $::thisDir/../doc/$fileName]} {
        $t insert end "ERROR: Could not find doc file "
        $t insert end \"$fileName\"
        return
    }
    insertTaggedText $t $::thisDir/../doc/$fileName

    #focus $t
    $t configure -state disabled
}

proc makeTutorialWin {} {
    global Pref

    set doc [file join $::thisDir .. doc/tutorial.txt]
    if {![file exists $doc]} return

    if {[catch {cd [file join $::thisDir .. examples]}]} {
        tk_messageBox -icon error -title "Eskil Error" -message \
                "Could not locate examples directory." \
                -type ok
        return
    }
    #set ::diff(tutorial) 1








|







 







<
<








|





|
<
<
<
|
|







 







|







 







|




|








|


|







21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
..
68
69
70
71
72
73
74


75
76
77
78
79
80
81
82
83
84
85
86
87
88
89



90
91
92
93
94
95
96
97
98
...
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
...
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
#----------------------------------------------------------------------
# $Revision$
#----------------------------------------------------------------------

# Silly experiment...
proc makeNuisance {top {str {Hi there!}}} {
    if {[lsearch [image names] nuisance] < 0} {
        set file [file join $::eskil(thisDir) .. Nuisance.gif]
        if {![file exists $file]} return
        image create photo nuisance -file $file
    }

    destroy $top.nui
    toplevel $top.nui
    wm transient $top.nui $top
................................................................................
    pack $w.b -side bottom -pady 2
    pack $w.f -side top -expand y -fill both -padx 2 -pady 2
    focus $w
    return $w.f
}

proc makeAboutWin {} {


    set w [helpWin .ab "About Eskil"]

    set bg [ttk::style configure . -background]
    text $w.t -width 45 -height 11 -wrap none -relief flat \
            -background $bg
    pack $w.t -side top -expand y -fill both

    $w.t insert end "A graphical frontend to diff\n\n"
    $w.t insert end "$::eskil(diffver)\n\n"
    $w.t insert end "Made by Peter Spjuth\n"
    $w.t insert end "E-Mail: peter.spjuth@gmail.com\n"
    $w.t insert end "\nURL: http://eskil.berlios.de\n"
    $w.t insert end "\nTcl version: [info patchlevel]\n"

    set du $::DiffUtil::version



    append du " ($::DiffUtil::implementation)"

    $w.t insert end "DiffUtil version: $du\n"
    $w.t insert end "\nCredits:\n"
    $w.t insert end "Ideas for scrollbar map and merge function\n"
    $w.t insert end "taken from TkDiff"

    set last [lindex [split [$w.t index end] "."] 0]
    $w.t configure -height $last
................................................................................
        }
    }
}

proc makeHelpWin {} {
    global Pref

    set doc [file join $::eskil(thisDir) .. doc/eskil.txt]
    if {![file exists $doc]} return

    set w [helpWin .he "Eskil Help"]
    set t [Scroll y text $w.t -width 85 -height 35]
    pack $w.t -side top -expand 1 -fill both

    configureDocWin $t
................................................................................
proc makeDocWin {fileName} {
    set w [helpWin .doc "Eskil Help"]
    set t [Scroll y text $w.t -width 80 -height 25]
    pack $w.t -side top -expand 1 -fill both

    configureDocWin $t

    if {![file exists $::eskil(thisDir)/../doc/$fileName]} {
        $t insert end "ERROR: Could not find doc file "
        $t insert end \"$fileName\"
        return
    }
    insertTaggedText $t $::eskil(thisDir)/../doc/$fileName

    #focus $t
    $t configure -state disabled
}

proc makeTutorialWin {} {
    global Pref

    set doc [file join $::eskil(thisDir) .. doc/tutorial.txt]
    if {![file exists $doc]} return

    if {[catch {cd [file join $::eskil(thisDir) .. examples]}]} {
        tk_messageBox -icon error -title "Eskil Error" -message \
                "Could not locate examples directory." \
                -type ok
        return
    }
    #set ::diff(tutorial) 1

Changes to src/merge.tcl.

1
2
3
4
5
6
7
8
9
10
11
..
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

31
32
33
34
35
36
37
..
67
68
69
70
71
72
73
74






75
76
77
78
79
80
81
..
97
98
99
100
101
102
103

104
105
106
107
108
109




110









111
112
113
114
115
116
117











118
119
120
121
122
123
124
125



126
127
128
129
130
131
132
133
134
135




















136
137
138
139
140
141
142
143
144
145



146
147
148
149
150
151
152
...
176
177
178
179
180
181
182









183
184
185
186
187
188
189
190
191





192
193
194
195
196
197
198
...
278
279
280
281
282
283
284

285
286
287
288
289
290
291
...
292
293
294
295
296
297
298

299
300
301
302
303
304
305
306
307
...
316
317
318
319
320
321
322













323
324
325
326
327
328
329
...
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
...
360
361
362
363
364
365
366



367
368
369
370
371
372
373
374
375
376
377
378


379
380
381
382
383
384
385
386
387
388
389
390
391

392
393
394
395
396
397







































































































































































#----------------------------------------------------------------------
#  Eskil, Merge function
#
#  Copyright (c) 1998-2007, Peter Spjuth  (peter.spjuth@gmail.com)
#
#  This program is free software; you can redistribute it and/or modify
#  it under the terms of the GNU General Public License as published by
#  the Free Software Foundation; either version 2 of the License, or
#  (at your option) any later version.
#
#  This program is distributed in the hope that it will be useful,
................................................................................
#
#  You should have received a copy of the GNU General Public License
#  along with this program; see the file COPYING.  If not, write to
#  the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
#  Boston, MA 02111-1307, USA.
#
#----------------------------------------------------------------------
# $Revision$
#----------------------------------------------------------------------

# Get all data from the files to merge
proc collectMergeData {top} {
    global diff

    set diff($top,leftMergeData) {}
    set diff($top,rightMergeData) {}


    if {![info exists ::diff($top,changes)]} {
        set ::diff($top,changes) {}
    }

    prepareFiles $top

................................................................................
        for {set t 0} {$t < $n2} {incr t} {
            gets $ch2 apa
            append data2 $apa\n
            incr doingLine2
        }
        lappend diff($top,leftMergeData) $data1
        lappend diff($top,rightMergeData) $data2
        set diff($top,mergeSelection,$changeNo) 2






        incr changeNo
    }
    set data1 {}
    set data2 {}
    while {[gets $ch1 apa] != -1} {
        append data1 $apa\n
        incr doingLine1
................................................................................
proc fillMergeWindow {top} {
    global diff

    set w $top.merge.t
    $w delete 1.0 end
    set marks {}
    set t 0

    foreach {commLeft diffLeft} $diff($top,leftMergeData) \
            {commRight diffRight} $diff($top,rightMergeData) {
        $w insert end $commRight
        if {![info exists diff($top,mergeSelection,$t)]} continue
        $w mark set merges$t insert
        $w mark gravity merges$t left




        $w insert end $diffRight merge$t









        lappend marks mergee$t [$w index insert]
        set diff($top,mergeSelection,$t) 2
        incr t
    }
    foreach {mark index} $marks {
        $w mark set $mark $index
    }











    set diff($top,curMerge) 0
    set diff($top,curMergeSel) 2
    $w tag configure merge0 -foreground red
    showDiff $top 0
    update
    # If there is any diff, show the first
    if {$t > 0} {
        seeText $w merges0 mergee0



    }
}

# Move to and highlight another diff.
proc nextMerge {top delta} {
    global diff

    set w $top.merge.t
    $w tag configure merge$diff($top,curMerge) -foreground ""





















    set diff($top,curMerge) [expr {$diff($top,curMerge) + $delta}]
    if {$diff($top,curMerge) < 0} {set diff($top,curMerge) 0}
    if {$diff($top,curMerge) >= ([llength $diff($top,leftMergeData)] / 2)} {
        set diff($top,curMerge) \
                [expr {[llength $diff($top,leftMergeData)] / 2 - 1}]
    }
    set diff($top,curMergeSel) $diff($top,mergeSelection,$diff($top,curMerge))
    $w tag configure merge$diff($top,curMerge) -foreground red
    showDiff $top $diff($top,curMerge)
    seeText $w merges$diff($top,curMerge) mergee$diff($top,curMerge)



}

# Select a merge setting for all diffs.
proc selectMergeAll {top new} {
    global diff
    set end [expr {[llength $diff($top,leftMergeData)] / 2}]
    for {set t 0} {$t < $end} {incr t} {
................................................................................

    set diff($top,mergeSelection,$no) $new

    set i [expr {$no * 2 + 1}]
    set diffLeft [lindex $diff($top,leftMergeData) $i]
    set diffRight [lindex $diff($top,rightMergeData) $i]










    if {$diff($top,mergeSelection,$no) == 12} {
        $w insert merges$no $diffLeft$diffRight merge$no
    } elseif {$diff($top,mergeSelection,$no) == 21} {
        $w insert merges$no $diffRight$diffLeft merge$no
    } elseif {$diff($top,mergeSelection,$no) == 1} {
        $w insert merges$no $diffLeft merge$no
    } elseif {$diff($top,mergeSelection,$no) == 2} {
        $w insert merges$no $diffRight merge$no
    }





}

# Save the merge result.
proc saveMerge {top} {
    set w $top.merge.t

    if {$::diff($top,mergeFile) eq "" && $::diff($top,mode) eq "conflict"} {
................................................................................
    set diff($top,leftMergeData) {}
    set diff($top,rightMergeData) {}
    array unset diff $top,mergeSelection,*
}

# Create a window to display merge result.
proc makeMergeWin {top} {

    if {![info exists ::diff($top,mergetranslation)]} {
        if {$::tcl_platform(platform) eq "windows"} {
            set ::diff($top,mergetranslation) crlf
        } else {
            set ::diff($top,mergetranslation) lf
        }
    }
................................................................................

    set w $top.merge
    if {![winfo exists $w]} {
        toplevel $w
    } else {
        destroy {*}[winfo children $w]
    }


    wm title $w "Merge result"

    menu $w.m
    $w configure -menu $w.m
    $w.m add cascade -label "File" -underline 0 -menu $w.m.mf
    menu $w.m.mf
    $w.m.mf add command -label "Save" -underline 0 -command "saveMerge $top"
    $w.m.mf add separator
................................................................................
    $w.m.ms add radiobutton -label "Right" -underline 0 -value 2  \
            -variable diff($top,curMergeSel) -command "selectMerge $top"
    $w.m.ms add radiobutton -label "Right+Left"         -value 21 \
            -variable diff($top,curMergeSel) -command "selectMerge $top"
    $w.m.ms add separator
    $w.m.ms add command -label "All Left"  -command "selectMergeAll $top 1"
    $w.m.ms add command -label "All Right" -command "selectMergeAll $top 2"














    $w.m add cascade -label "Config" -underline 0 -menu $w.m.mc
    menu $w.m.mc
    $w.m.mc add radiobutton -label "Line end LF"   -value lf   -variable diff($top,mergetranslation)
    $w.m.mc add radiobutton -label "Line end CRLF" -value crlf -variable diff($top,mergetranslation)
    if {$::diff($top,mode) eq "conflict"} {
        $w.m.mc add separator
................................................................................
            -command "selectMerge $top"
    ttk::radiobutton $w.f.rb4 -text "RL" -value 21 \
            -variable diff($top,curMergeSel) \
            -command "selectMerge $top"
    bind $w <Key-Left>  "focus $w; set diff($top,curMergeSel) 1; selectMerge $top"
    bind $w <Key-Right> "focus $w; set diff($top,curMergeSel) 2; selectMerge $top"

    ttk::button $w.f.bl -text "All L" -command "selectMergeAll $top 1"
    ttk::button $w.f.br -text "All R" -command "selectMergeAll $top 2"
    
    ttk::button $w.f.b1 -text "Prev" -command "nextMerge $top -1"
    ttk::button $w.f.b2 -text "Next" -command "nextMerge $top 1"
    bind $w <Key-Down> "focus $w ; nextMerge $top 1"
    bind $w <Key-Up>   "focus $w ; nextMerge $top -1"
    bind $w <Shift-Key-Down> "focus $w ; nextMerge $top 10"
    bind $w <Shift-Key-Up>   "focus $w ; nextMerge $top -10"
................................................................................

    ttk::button $w.f.bs -text "Save" -command "saveMerge $top"
    ttk::button $w.f.bq -text "Close" -command "closeMerge $top"
    wm protocol $w WM_DELETE_WINDOW "closeMerge $top"

    grid $w.f.rb1 $w.f.rb2 $w.f.rb3 $w.f.rb4 x $w.f.b1 $w.f.b2 x \
            $w.f.bl $w.f.br x $w.f.bs $w.f.bq -sticky we -padx 1



    grid columnconfigure $w.f {4 7 10} -minsize 10
    grid columnconfigure $w.f 10 -weight 1
    grid columnconfigure $w.f {0 1 2 3} -uniform a
    grid columnconfigure $w.f {5 6 8 9 11 12} -uniform b
    #grid columnconfigure $w.f {11 13 14} -uniform c

    text $w.t -width 80 -height 20 -xscrollcommand "$w.sbx set" \
            -yscrollcommand "$w.sby set" -font myfont
    scrollbar $w.sbx -orient horizontal -command "$w.t xview"
    scrollbar $w.sby -orient vertical   -command "$w.t yview"

    bind $w.t <Key-Escape> [list focus $w]



    # Prevent toplevel bindings on keys to fire while in the text widget.
    bindtags $w.t [list Text $w.t $w all]
    bind $w.t <Key-Left>  "break"
    bind $w.t <Key-Right> "break"
    bind $w.t <Key-Down>  "break"
    bind $w.t <Key-Up>    "break"
    bind $w.t <Shift-Key-Down> "break"
    bind $w.t <Shift-Key-Up>   "break"

    grid $w.f   -      -sticky news -row 0
    grid $w.t   $w.sby -sticky news
    grid $w.sbx x      -sticky we

    grid columnconfigure $w 0 -weight 1
    grid rowconfigure $w 1 -weight 1

    collectMergeData $top
    fillMergeWindow $top
}










































































































































































|







 







<
<







>







 







|
>
>
>
>
>
>







 







>






>
>
>
>
|
>
>
>
>
>
>
>
>
>

<





>
>
>
>
>
>
>
>
>
>
>
|
|
|
|



|
>
>
>










>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>


|
|
<





>
>
>







 







>
>
>
>
>
>
>
>
>









>
>
>
>
>







 







>







 







>

|







 







>
>
>
>
>
>
>
>
>
>
>
>
>







 







|
|







 







>
>
>












>
>













>



<


>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
..
15
16
17
18
19
20
21


22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
..
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
...
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130

131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191

192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
...
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
...
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
...
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
...
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
...
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
...
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483

484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
#----------------------------------------------------------------------
#  Eskil, Merge function
#
#  Copyright (c) 1998-2011, Peter Spjuth  (peter.spjuth@gmail.com)
#
#  This program is free software; you can redistribute it and/or modify
#  it under the terms of the GNU General Public License as published by
#  the Free Software Foundation; either version 2 of the License, or
#  (at your option) any later version.
#
#  This program is distributed in the hope that it will be useful,
................................................................................
#
#  You should have received a copy of the GNU General Public License
#  along with this program; see the file COPYING.  If not, write to
#  the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
#  Boston, MA 02111-1307, USA.
#
#----------------------------------------------------------------------



# Get all data from the files to merge
proc collectMergeData {top} {
    global diff

    set diff($top,leftMergeData) {}
    set diff($top,rightMergeData) {}
    set diff($top,mergeSelection,AnyConflict) 0

    if {![info exists ::diff($top,changes)]} {
        set ::diff($top,changes) {}
    }

    prepareFiles $top

................................................................................
        for {set t 0} {$t < $n2} {incr t} {
            gets $ch2 apa
            append data2 $apa\n
            incr doingLine2
        }
        lappend diff($top,leftMergeData) $data1
        lappend diff($top,rightMergeData) $data2
        set diff($top,mergeSelection,$changeNo) \
                [WhichSide $top $line1 $n1 $line2 $n2 conflict comment]
        set diff($top,mergeSelection,Conflict,$changeNo) $conflict
        set diff($top,mergeSelection,Comment,$changeNo) $comment
        if {$conflict} {
            set diff($top,mergeSelection,AnyConflict) 1
        }
        incr changeNo
    }
    set data1 {}
    set data2 {}
    while {[gets $ch1 apa] != -1} {
        append data1 $apa\n
        incr doingLine1
................................................................................
proc fillMergeWindow {top} {
    global diff

    set w $top.merge.t
    $w delete 1.0 end
    set marks {}
    set t 0
    set firstConflict -1
    foreach {commLeft diffLeft} $diff($top,leftMergeData) \
            {commRight diffRight} $diff($top,rightMergeData) {
        $w insert end $commRight
        if {![info exists diff($top,mergeSelection,$t)]} continue
        $w mark set merges$t insert
        $w mark gravity merges$t left
        switch $diff($top,mergeSelection,$t) {
            1 { $w insert end $diffLeft merge$t }
            2 { $w insert end $diffRight merge$t }
            12 { $w insert end $diffLeft merge$t 
                $w insert end $diffRight merge$t }
            21 { $w insert end $diffRight merge$t
                $w insert end $diffLeft merge$t  }
        }
        if {$diff($top,mergeSelection,Conflict,$t)} {
            $w tag configure merge$t -background grey
            if {$firstConflict == -1} {
                set firstConflict $t
            }
        }
        lappend marks mergee$t [$w index insert]

        incr t
    }
    foreach {mark index} $marks {
        $w mark set $mark $index
    }
    # Add fences to simplify some handling later
    $w mark set mergee-2 1.0
    $w mark set mergee-1 1.0
    $w mark set merges$t end
    $w mark set merges[expr {$t + 1}] end

    set showFirst 0
    if {$firstConflict != -1} {
        set showFirst $firstConflict
    }

    set diff($top,curMerge) $showFirst
    set diff($top,curMergeSel) $diff($top,mergeSelection,$showFirst)
    $w tag configure merge$showFirst -foreground red
    showDiff $top $showFirst
    update
    # If there is any diff, show the first
    if {$t > 0} {
        seeText $w merges$showFirst mergee$showFirst
        # Show status for first chunk
        set diff($top,mergeStatus) \
                $diff($top,mergeSelection,Comment,$showFirst)
    }
}

# Move to and highlight another diff.
proc nextMerge {top delta} {
    global diff

    set w $top.merge.t
    $w tag configure merge$diff($top,curMerge) -foreground ""

    set last [expr {[llength $diff($top,leftMergeData)] / 2 - 1}]

    if {$delta == -1000} {
        # Search backward for conflict
        for {set t [expr {$diff($top,curMerge) - 1}]} {$t >= 0} {incr t -1} {
            if {$diff($top,mergeSelection,Conflict,$t)} {
                set delta [expr {$t - $diff($top,curMerge)}]
                break
            }
        }
    } elseif {$delta == 1000} {
        # Search forward for conflict
        for {set t [expr {$diff($top,curMerge) + 1}]} {$t <= $last} {incr t} {
            if {$diff($top,mergeSelection,Conflict,$t)} {
                set delta [expr {$t - $diff($top,curMerge)}]
                break
            }
        }
    }

    set diff($top,curMerge) [expr {$diff($top,curMerge) + $delta}]
    if {$diff($top,curMerge) < 0} {set diff($top,curMerge) 0}
    if {$diff($top,curMerge) > $last} {
        set diff($top,curMerge) $last

    }
    set diff($top,curMergeSel) $diff($top,mergeSelection,$diff($top,curMerge))
    $w tag configure merge$diff($top,curMerge) -foreground red
    showDiff $top $diff($top,curMerge)
    seeText $w merges$diff($top,curMerge) mergee$diff($top,curMerge)

    set diff($top,mergeStatus) \
            $diff($top,mergeSelection,Comment,$diff($top,curMerge))
}

# Select a merge setting for all diffs.
proc selectMergeAll {top new} {
    global diff
    set end [expr {[llength $diff($top,leftMergeData)] / 2}]
    for {set t 0} {$t < $end} {incr t} {
................................................................................

    set diff($top,mergeSelection,$no) $new

    set i [expr {$no * 2 + 1}]
    set diffLeft [lindex $diff($top,leftMergeData) $i]
    set diffRight [lindex $diff($top,rightMergeData) $i]

    # Temporarily switch surrounding marks
    # Two steps are enough since there can't be consecutive empty areas
    # The one before and/or the one after the one being switch might
    # be empty.
    $w mark gravity mergee[expr {$no - 2}] left
    $w mark gravity mergee[expr {$no - 1}] left
    $w mark gravity merges[expr {$no + 1}] right
    $w mark gravity merges[expr {$no + 2}] right

    if {$diff($top,mergeSelection,$no) == 12} {
        $w insert merges$no $diffLeft$diffRight merge$no
    } elseif {$diff($top,mergeSelection,$no) == 21} {
        $w insert merges$no $diffRight$diffLeft merge$no
    } elseif {$diff($top,mergeSelection,$no) == 1} {
        $w insert merges$no $diffLeft merge$no
    } elseif {$diff($top,mergeSelection,$no) == 2} {
        $w insert merges$no $diffRight merge$no
    }
    # Switch back surrounding marks
    $w mark gravity mergee[expr {$no - 2}] right
    $w mark gravity mergee[expr {$no - 1}] right
    $w mark gravity merges[expr {$no + 1}] left
    $w mark gravity merges[expr {$no + 2}] left
}

# Save the merge result.
proc saveMerge {top} {
    set w $top.merge.t

    if {$::diff($top,mergeFile) eq "" && $::diff($top,mode) eq "conflict"} {
................................................................................
    set diff($top,leftMergeData) {}
    set diff($top,rightMergeData) {}
    array unset diff $top,mergeSelection,*
}

# Create a window to display merge result.
proc makeMergeWin {top} {
    collectMergeData $top
    if {![info exists ::diff($top,mergetranslation)]} {
        if {$::tcl_platform(platform) eq "windows"} {
            set ::diff($top,mergetranslation) crlf
        } else {
            set ::diff($top,mergetranslation) lf
        }
    }
................................................................................

    set w $top.merge
    if {![winfo exists $w]} {
        toplevel $w
    } else {
        destroy {*}[winfo children $w]
    }
    set anyC $::diff($top,mergeSelection,AnyConflict)

    wm title $w "Merge result: [TitleTail $top]"

    menu $w.m
    $w configure -menu $w.m
    $w.m add cascade -label "File" -underline 0 -menu $w.m.mf
    menu $w.m.mf
    $w.m.mf add command -label "Save" -underline 0 -command "saveMerge $top"
    $w.m.mf add separator
................................................................................
    $w.m.ms add radiobutton -label "Right" -underline 0 -value 2  \
            -variable diff($top,curMergeSel) -command "selectMerge $top"
    $w.m.ms add radiobutton -label "Right+Left"         -value 21 \
            -variable diff($top,curMergeSel) -command "selectMerge $top"
    $w.m.ms add separator
    $w.m.ms add command -label "All Left"  -command "selectMergeAll $top 1"
    $w.m.ms add command -label "All Right" -command "selectMergeAll $top 2"

    $w.m add cascade -label "Goto" -underline 0 -menu $w.m.mg
    menu $w.m.mg
    $w.m.mg add command -accelerator "Up" -label "Previous" -command "nextMerge $top -1"
    $w.m.mg add command -accelerator "Down" -label "Next" -command "nextMerge $top 1"
    if {$anyC} {
        $w.m.mg add command -accelerator "Shift-Up" -label "Previous Conflict" -command "nextMerge $top -1000"
        $w.m.mg add command -accelerator "Shift-Down" -label "Next Conflict" -command "nextMerge $top 1000"
    } else {
        $w.m.mg add command -accelerator "Shift-Up" -label "Previous 10" -command "nextMerge $top -10"
        $w.m.mg add command -accelerator "Shift-Down" -label "Next 10" -command "nextMerge $top 10"
    }
    

    $w.m add cascade -label "Config" -underline 0 -menu $w.m.mc
    menu $w.m.mc
    $w.m.mc add radiobutton -label "Line end LF"   -value lf   -variable diff($top,mergetranslation)
    $w.m.mc add radiobutton -label "Line end CRLF" -value crlf -variable diff($top,mergetranslation)
    if {$::diff($top,mode) eq "conflict"} {
        $w.m.mc add separator
................................................................................
            -command "selectMerge $top"
    ttk::radiobutton $w.f.rb4 -text "RL" -value 21 \
            -variable diff($top,curMergeSel) \
            -command "selectMerge $top"
    bind $w <Key-Left>  "focus $w; set diff($top,curMergeSel) 1; selectMerge $top"
    bind $w <Key-Right> "focus $w; set diff($top,curMergeSel) 2; selectMerge $top"

    ttk::button $w.f.bl -text "Prev C" -command "nextMerge $top -1000"
    ttk::button $w.f.br -text "Next C" -command "nextMerge $top 1000"
    
    ttk::button $w.f.b1 -text "Prev" -command "nextMerge $top -1"
    ttk::button $w.f.b2 -text "Next" -command "nextMerge $top 1"
    bind $w <Key-Down> "focus $w ; nextMerge $top 1"
    bind $w <Key-Up>   "focus $w ; nextMerge $top -1"
    bind $w <Shift-Key-Down> "focus $w ; nextMerge $top 10"
    bind $w <Shift-Key-Up>   "focus $w ; nextMerge $top -10"
................................................................................

    ttk::button $w.f.bs -text "Save" -command "saveMerge $top"
    ttk::button $w.f.bq -text "Close" -command "closeMerge $top"
    wm protocol $w WM_DELETE_WINDOW "closeMerge $top"

    grid $w.f.rb1 $w.f.rb2 $w.f.rb3 $w.f.rb4 x $w.f.b1 $w.f.b2 x \
            $w.f.bl $w.f.br x $w.f.bs $w.f.bq -sticky we -padx 1
    if {!$anyC} {
        grid forget $w.f.bl $w.f.br
    }
    grid columnconfigure $w.f {4 7 10} -minsize 10
    grid columnconfigure $w.f 10 -weight 1
    grid columnconfigure $w.f {0 1 2 3} -uniform a
    grid columnconfigure $w.f {5 6 8 9 11 12} -uniform b
    #grid columnconfigure $w.f {11 13 14} -uniform c

    text $w.t -width 80 -height 20 -xscrollcommand "$w.sbx set" \
            -yscrollcommand "$w.sby set" -font myfont
    scrollbar $w.sbx -orient horizontal -command "$w.t xview"
    scrollbar $w.sby -orient vertical   -command "$w.t yview"

    bind $w.t <Key-Escape> [list focus $w]

    ttk::label $w.ls -textvariable ::diff($top,mergeStatus)

    # Prevent toplevel bindings on keys to fire while in the text widget.
    bindtags $w.t [list Text $w.t $w all]
    bind $w.t <Key-Left>  "break"
    bind $w.t <Key-Right> "break"
    bind $w.t <Key-Down>  "break"
    bind $w.t <Key-Up>    "break"
    bind $w.t <Shift-Key-Down> "break"
    bind $w.t <Shift-Key-Up>   "break"

    grid $w.f   -      -sticky news -row 0
    grid $w.t   $w.sby -sticky news
    grid $w.sbx x      -sticky we
    grid $w.ls  -      -sticky we
    grid columnconfigure $w 0 -weight 1
    grid rowconfigure $w 1 -weight 1


    fillMergeWindow $top
}

# Compare each file against an ancestor file for three-way merge
proc collectAncestorInfo {top dFile1 dFile2 opts} {
    if {![info exists ::diff($top,mergetranslation)]} {
        # Try to autodetect line endings in ancestor file
        set ch [open $::diff($top,ancestorFile) rb]
        set data [read $ch 10000]
        close $ch
        if {[string first \r\n $data] >= 0} {
            set ::diff($top,mergetranslation) crlf
        } else {
            set ::diff($top,mergetranslation) lf
        }
    }
    array unset ::diff $top,ancestorLeft,*
    array unset ::diff $top,ancestorRight,*
    set differrA1 [catch {DiffUtil::diffFiles {*}$opts \
            $::diff($top,ancestorFile) $dFile1} diffresA1]
    set differrA2 [catch {DiffUtil::diffFiles {*}$opts \
            $::diff($top,ancestorFile) $dFile2} diffresA2]
    if {$differrA1 != 0 || $differrA2 != 0} {
        puts $diffresA1
        puts $diffresA2
        return
    }
    foreach i $diffresA1 {
        lassign $i line1 n1 line2 n2
        if {$n1 == 0} {
            # Added lines
            for {set t $line2} {$t < $line2 + $n2} {incr t} {
                set ::diff($top,ancestorLeft,$t) a
            }
        } elseif {$n2 == 0} {
            # Deleted lines
            # Mark the following line
            set ::diff($top,ancestorLeft,d$line2) d
        } else {
            # Changed lines
            for {set t $line2} {$t < $line2 + $n2} {incr t} {
                set ::diff($top,ancestorLeft,$t) c
            }
        }
    }            
    foreach i $diffresA2 {
        lassign $i line1 n1 line2 n2
        if {$n1 == 0} {
            # Added lines
            for {set t $line2} {$t < $line2 + $n2} {incr t} {
                set ::diff($top,ancestorRight,$t) a
            }
        } elseif {$n2 == 0} {
            # Deleted lines
            # Mark the following line
            set ::diff($top,ancestorRight,d$line2) d
        } else {
            # Changed lines
            for {set t $line2} {$t < $line2 + $n2} {incr t} {
                set ::diff($top,ancestorRight,$t) c
            }
        }
    }
    #parray ::diff $top,ancestor*
}

# Use ancestor info to select which side to use in a merge chunk
##nagelfar syntax WhichSide x x x x x n n
proc WhichSide {top line1 n1 line2 n2 conflictName commentName} {
    upvar 1 $conflictName conflict $commentName comment
    set conflict 0
    set comment ""
    if {$::diff($top,ancestorFile) eq ""} {
        # No ancestor info, just select right side
        return 2
    }
    if {$n1 == 0} {
        # Only to the right
        set delLeft [info exists ::diff($top,ancestorLeft,d$line1)]
        # Inserted to right : Keep right side
        if {!$delLeft} {
            set comment "Right: Add"
            return 2
        }

        for {set t $line2} {$t < $line2 + $n2} {incr t} {
            if {[info exists ::diff($top,ancestorRight,$t)]} {
                set right($::diff($top,ancestorRight,$t)) 1
            }
        }
        # Deleted to left   : Keep left side
        if {[array size right] == 0} {
            set comment "Left: Delete"
            return 1
        }
        # Deleted to left and changed to the right : ?? (right for now)
        # FIXA
        set comment "*** Left: Delete, Right: Change"
        set conflict 1
        return 2
    } elseif {$n2 == 0} {
        # Only to the left, this can be:
        set delRight [info exists ::diff($top,ancestorRight,d$line2)]
        # Inserted to left : Keep left side
        if {!$delRight} {
            set comment "Left: Add"
            return 1
        }

        for {set t $line1} {$t < $line1 + $n1} {incr t} {
            if {[info exists ::diff($top,ancestorLeft,$t)]} {
                set left($::diff($top,ancestorLeft,$t)) 1
            }
        }
        # Deleted to right : Keep right side
        if {[array size left] == 0} {
            set comment "Right: Delete"
            return 2
        }
        # Deleted to right and changed to the left : ?? (right for now)
        # FIXA
        set comment "*** Left: Change, Right: Delete"
        set conflict 1
        return 2
    } else {
        # Changed on both sides

        # Collect left side info
        for {set t $line1} {$t < $line1 + $n1} {incr t} {
            if {[info exists ::diff($top,ancestorLeft,$t)]} {
                set left($::diff($top,ancestorLeft,$t)) 1
            }
        }

        # No changes against ancestor on left side means it is just
        # changed to the right : Keep right
        if {[array size left] == 0} {
            set comment "Right: Change"
            return 2
        }

        # Collect right side info
        for {set t $line2} {$t < $line2 + $n2} {incr t} {
            if {[info exists ::diff($top,ancestorRight,$t)]} {
                set right($::diff($top,ancestorRight,$t)) 1
            }
        }

        # No changes against ancestor on right side means it is just
        # changed to the left : Keep left
        if {[array size right] == 0} {
            set comment "Left: Change"
            return 1
        }

        if {[info exists left(a)] && ![info exists left(c)] && \
                [info exists right(a)] && ![info exists right(c)]} {
            # Pure add on both sides, keep both
            set comment "*** Left: Add, Right: Add"
            set conflict 1
            return 12
        }
        # Changed in both, right for now
        # FIXA
        set comment "*** Left: Change, Right: Change"
        set conflict 1
        return 2
    }
}

Changes to src/plugin.tcl.

20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
..
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103

104
105
106
107
108
109
110
111
112













113
114
115
116
117
118
119
...
164
165
166
167
168
169
170






























































#
#----------------------------------------------------------------------
# $Revision$
#----------------------------------------------------------------------

proc PluginSearchPath {} {
    set dirs [list . ./plugins]
    lappend dirs [file join $::thisDir .. ..]
    lappend dirs [file join $::thisDir .. .. plugins]
    lappend dirs [file join $::thisDir .. plugins]
    return $dirs
}

# Locate plugin source
proc LocatePlugin {plugin} {
    set src ""
    set dirs [PluginSearchPath]
................................................................................
        return
    }
    set ch [open $src]
    puts -nonewline [read $ch]
    close $ch
}

proc printPlugins {} {
    set dirs [PluginSearchPath]

    foreach dir $dirs {
        set files [glob -nocomplain [file join $dir *.tcl]]
        foreach file $files {
            set file [file normalize $file]
            if {[info exists done($file)]} continue
            if {![file exists $file]} continue
            if {![file isfile $file]} continue
            if {![file readable $file]} continue

            set done($file) 1
            set ch [open $file r]
            set data [read $ch 100]
            close $ch
            if {[regexp {^\#\#Eskil Plugin :(.*?)(\n|$)} $data -> descr]} {
                puts "Plugin \"[file rootname [file tail $file]]\" : $descr"
            }
        }
    }













}

proc preparePlugin {top} {
    #FIXA: plugin miffo
    disallowEdit $top
    $::diff($top,plugin) eval [list array set ::Pref [array get ::Pref]]
    set out1 [tmpFile]
................................................................................
    if {[info exists ::diff($top,rightFileBak)]} {
        set ::diff($top,rightFile) $::diff($top,rightFileBak)
    }
    unset -nocomplain \
            ::diff($top,leftFileBak) ::diff($top,rightFileBak) \
            ::diff($top,leftFileDiff) ::diff($top,rightFileDiff)
}





































































|
|
|







 







|










>


|


|



>
>
>
>
>
>
>
>
>
>
>
>
>







 







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
..
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
...
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
#
#----------------------------------------------------------------------
# $Revision$
#----------------------------------------------------------------------

proc PluginSearchPath {} {
    set dirs [list . ./plugins]
    lappend dirs [file join $::eskil(thisDir) .. ..]
    lappend dirs [file join $::eskil(thisDir) .. .. plugins]
    lappend dirs [file join $::eskil(thisDir) .. plugins]
    return $dirs
}

# Locate plugin source
proc LocatePlugin {plugin} {
    set src ""
    set dirs [PluginSearchPath]
................................................................................
        return
    }
    set ch [open $src]
    puts -nonewline [read $ch]
    close $ch
}

proc listPlugins {} {
    set dirs [PluginSearchPath]

    foreach dir $dirs {
        set files [glob -nocomplain [file join $dir *.tcl]]
        foreach file $files {
            set file [file normalize $file]
            if {[info exists done($file)]} continue
            if {![file exists $file]} continue
            if {![file isfile $file]} continue
            if {![file readable $file]} continue

            set done($file) 1
            set ch [open $file r]
            set data [read $ch 200]
            close $ch
            if {[regexp {^\#\#Eskil Plugin :(.*?)(\n|$)} $data -> descr]} {
                set result([file rootname [file tail $file]]) $descr
            }
        }
    }
    return [array get result]
}

proc printPlugins {} {
    set plugins [listPlugins]
    if {[llength $plugins] == 0} {
        puts "No plugins found."
        return
    }
    puts "Available plugins:"
    foreach {plugin descr} $plugins {
        puts "Plugin \"$plugin\" : $descr"
    }
}

proc preparePlugin {top} {
    #FIXA: plugin miffo
    disallowEdit $top
    $::diff($top,plugin) eval [list array set ::Pref [array get ::Pref]]
    set out1 [tmpFile]
................................................................................
    if {[info exists ::diff($top,rightFileBak)]} {
        set ::diff($top,rightFile) $::diff($top,rightFileBak)
    }
    unset -nocomplain \
            ::diff($top,leftFileBak) ::diff($top,rightFileBak) \
            ::diff($top,leftFileDiff) ::diff($top,rightFileDiff)
}

# GUI for plugin selection
proc EditPrefPlugins {top} {
    set w $top.prefplugin

    # Create window
    destroy $w
    toplevel $w -padx 3 -pady 3
    ttk::frame $w._bg
    place $w._bg -x 0 -y 0 -relwidth 1.0 -relheight 1.0 -border outside
    wm title $w "Preferences: Plugins"

    set plugins [listPlugins]
    if {[llength $plugins] == 0} {
        grid [ttk::label $w.l -text "No plugins found."] - -padx 3 -pady 3
    }
    if {![info exists ::diff($top,pluginname)]} {
        set ::diff($top,pluginname) ""
    }
    if {![info exists ::diff($top,plugininfo)]} {
        set ::diff($top,plugininfo) ""
    }
    set ::diff($top,edit,pluginname) $::diff($top,pluginname) 
    set ::diff($top,edit,plugininfo) $::diff($top,plugininfo)
    set t 0
    foreach {plugin descr} $plugins {
        ttk::radiobutton $w.rb$t -variable ::diff($top,edit,pluginname) -value $plugin -text $plugin
        ttk::label $w.l$t -text $descr -anchor "w"
        grid $w.rb$t $w.l$t -sticky we -padx 3 -pady 3
        incr t
    }
    ttk::radiobutton $w.rb$t -variable ::diff($top,edit,pluginname) -value "" -text "No Plugin"
    grid $w.rb$t -sticky we -padx 3 -pady 3

    ttk::label $w.li -text "Info" -anchor "w"
    ttk::entry $w.ei -textvariable ::diff($top,edit,plugininfo)
    grid $w.li $w.ei -sticky we -padx 3 -pady 3

    ttk::frame $w.fb -padding 3
    ttk::button $w.fb.b1 -text "Ok"     -command [list EditPrefPluginsOk $top $w]
    ttk::button $w.fb.b2 -text "Cancel" -command [list destroy $w]
    set ::widgets($top,prefPluginsOk) $w.fb.b1

    grid $w.fb.b1 x $w.fb.b2 -sticky we
    grid columnconfigure $w.fb {0 2} -uniform a
    grid columnconfigure $w.fb 1 -weight 1

    grid $w.fb - -sticky we
    grid columnconfigure $w 1 -weight 1
}

proc EditPrefPluginsOk {top w} {
    destroy $w
    set ::diff($top,pluginname) $::diff($top,edit,pluginname) 
    set ::diff($top,plugininfo) $::diff($top,edit,plugininfo)
    if {$::diff($top,pluginname) ne ""} {
        set pinterp [createPluginInterp $::diff($top,pluginname) $::diff($top,plugininfo)]
    } else {
        set pinterp ""
    }
    set ::diff($top,plugin) $pinterp
}

Changes to src/registry.tcl.

52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
..
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
    grid $l.new1 $l.new2 -     -sticky "w" -padx 4 -pady 4
    grid $l.delete - $l.change -sticky "w" -padx 4 -pady 4
    grid $l.change -sticky "e"
    grid columnconfigure $l 2 -weight 1
}

proc makeRegistryWin {} {
    global thisScript

    # Locate executable for this program
    set exe [info nameofexecutable]
    if {[regexp {^(.*wish)\d+\.exe$} $exe -> pre]} {
        set alt $pre.exe
        if {[file exists $alt]} {
            set a [tk_messageBox -icon question -title "Which Wish" -message \
                    "Would you prefer to use the executable\n\
................................................................................
    } else {
        if {[regexp {wish\d+\.exe} $exe]} {
            set exe [file join [file dirname $exe] wish.exe]
            if {[file exists $exe]} {
                set myexe [list $exe]
            }
        }
        set myexe [list $exe $thisScript]
    }

    set valbase {}
    foreach item $myexe {
        lappend valbase \"[file nativename $item]\"
    }
    set valbase [join $valbase]







<
<







 







|







52
53
54
55
56
57
58


59
60
61
62
63
64
65
..
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
    grid $l.new1 $l.new2 -     -sticky "w" -padx 4 -pady 4
    grid $l.delete - $l.change -sticky "w" -padx 4 -pady 4
    grid $l.change -sticky "e"
    grid columnconfigure $l 2 -weight 1
}

proc makeRegistryWin {} {


    # Locate executable for this program
    set exe [info nameofexecutable]
    if {[regexp {^(.*wish)\d+\.exe$} $exe -> pre]} {
        set alt $pre.exe
        if {[file exists $alt]} {
            set a [tk_messageBox -icon question -title "Which Wish" -message \
                    "Would you prefer to use the executable\n\
................................................................................
    } else {
        if {[regexp {wish\d+\.exe} $exe]} {
            set exe [file join [file dirname $exe] wish.exe]
            if {[file exists $exe]} {
                set myexe [list $exe]
            }
        }
        set myexe [list $exe $::eskil(thisScript)]
    }

    set valbase {}
    foreach item $myexe {
        lappend valbase \"[file nativename $item]\"
    }
    set valbase [join $valbase]

Changes to src/rev.tcl.

43
44
45
46
47
48
49
50
51
52
53
54

55
56
57
58
59

60
61
62
63
64
65
66
67
68
69

70
71
72
73
74
75
76
...
174
175
176
177
178
179
180


















181
182
183
184
185
186
187
...
211
212
213
214
215
216
217
218
219
220
221
222
223

224
225
226
227
228
229
230
...
259
260
261
262
263
264
265
266
267
268
269
270

271
272
273
274
275
276
277
...
300
301
302
303
304
305
306
307

308
309
310
311
312
313
314
...
341
342
343
344
345
346
347
348

349
350
351
352
353
354
355
...
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
...
381
382
383
384
385
386
387



388
389
390
391
392
393
394
...
405
406
407
408
409
410
411
412
413
414





415



416
417
418




















































419
420
421
422
423
424
425
...
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
...
537
538
539
540
541
542
543
544




545






546
547
548
549
550
551
552
...
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706







707
708
709
710
711
712
713
714







715
716
717
718
719


































720
721
722
723
724
725
726
...
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
...
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
...
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894





895
896
897
898
899
900
901
902
903
904
905
906
907
908



909

910
911
912
913
914
915
916
...
920
921
922
923
924
925
926






927
928
929
930
931
932
933
934
...
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957

# eskil::rev::XXX::get {filename outfile rev}
#
# Get a revision of a file and place it in outfile.
# rev is in any format understood by this system, and
# should be retrieved from ParseRevs

# eskil::rev::XXX::getPatch {revs}
#
# Get a patch of the file tree, between the revisions given.
# revs is in any format understood by this system, and
# should be retrieved from ParseRevs


# eskil::rev::XXX::commitFile {top filename}
#
# If implemented, enables the commit feature when comparing an edited
# file agains latest check in.


# eskil::rev::XXX::viewLog {top filename revs}
#
# If implemented, enables the log feature when comparing revisions.
# View log between displayed versions

namespace eval eskil::rev::CVS {}
namespace eval eskil::rev::RCS {}
namespace eval eskil::rev::CT {}
namespace eval eskil::rev::GIT {}

namespace eval eskil::rev::SVN {}
namespace eval eskil::rev::HG {}
namespace eval eskil::rev::BZR {}
namespace eval eskil::rev::P4 {}

proc eskil::rev::CVS::detect {file} {
    if {$file eq ""} {
................................................................................
        [file isdirectory [file join $dir .. .. .git]]} {
        if {[auto_execok git] ne ""} {
            return 1
        }
    }
    return 0
}



















proc eskil::rev::P4::detect {file} {
    if {[auto_execok icmp4] != ""} {
        if {[catch {exec csh -c "icmp4 have $file"} p4have]} { return 0 }
	if {[lindex $p4have 1] eq "-"} { return 1 }
    }
    return 0
................................................................................

    if {$old != ""} {
        cd $old
    }
}

# Get a CVS patch
proc eskil::rev::CVS::getPatch {revs} {
    if {$::Pref(context) > 0} {
        set context $::Pref(context)
    } else {
        set context 5
    }

    set cmd [list exec cvs diff -U $context]
    foreach rev $revs {
        lappend cmd -r $rev
    }

    if {[catch {eval $cmd} res]} {
        if {![string match "*=========*" $res]} {
................................................................................

    if {$old != ""} {
        cd $old
    }
}

# Get a SVN patch
proc eskil::rev::SVN::getPatch {revs} {
    set cmd [list exec svn diff]
    foreach rev $revs {
        lappend cmd -r $rev
    }


    if {[catch {eval $cmd} res]} {
        tk_messageBox -icon error -title "SVN error" -message $res
        return ""
    }
    return $res
}
................................................................................

    if {$old != ""} {
        cd $old
    }
}

# Get a HG patch
proc eskil::rev::HG::getPatch {revs} {

    set cmd [list exec hg diff]
    foreach rev $revs {
        lappend cmd -r $rev
    }

    if {[catch {eval $cmd} res]} {
        tk_messageBox -icon error -title "HG error" -message $res
................................................................................

    if {$old != ""} {
        cd $old
    }
}

# Get a BZR patch
proc eskil::rev::BZR::getPatch {revs} {

    set cmd [list exec bzr diff]
    if {[llength $revs] == 2} {
        lappend cmd -r [lindex $revs 0]..[lindex $revs 1]
    } elseif {[llength $revs] == 1} {
        lappend cmd -r [lindex $revs 0]
    }

................................................................................
# Get an RCS revision
proc eskil::rev::RCS::get {filename outfile {rev {}}} {
    catch {exec co -p$rev [file nativename $filename] \
            > $outfile}
}

# Get a RCS patch
proc eskil::rev::RCS::getPatch {revs} {
    # Not supported yet.
    return ""
}

# Get a GIT revision
# No support for revisions yet
proc eskil::rev::GIT::get {filename outfile rev} {
................................................................................
    set dir [file dirname $filename]
    set tail [file tail $filename]
    # Locate the top directory
    while {![file isdirectory $dir/.git]} {
        set thisdir [file tail $dir]
        set dir [file dirname $dir]
        set tail [file join $thisdir $tail]



    }
    cd $dir
    catch {exec git show $rev:$tail > $outfile}
    cd $old
    # example: git show HEAD^^^:apa
}

................................................................................
    }
    cd $dir
    catch {exec git add $tail}
    cd $old
}

# Get a GIT patch
proc eskil::rev::GIT::getPatch {revs} {
    set cmd [list exec git diff]
    # No rev support yet









    if {[catch {eval $cmd} res]} {
        tk_messageBox -icon error -title "GIT error" -message $res
        return ""




















































    }
    return $res
}

# Get a ClearCase revision
proc eskil::rev::CT::get {filename outfile rev} {
    set filerev [file nativename $filename@@$rev]
................................................................................
    if {[catch {exec cleartool get -to $outfile $filerev} msg]} {
        tk_messageBox -icon error -title "Cleartool error" -message $msg
        return
    }
}

# Get a CT patch
proc eskil::rev::CT::getPatch {revs} {
    # Not supported yet
    return ""
}

# Get a P4 revision
proc eskil::rev::P4::get {filename outfile rev} {
    set dir [file dirname $filename]
................................................................................
    foreach rev $revs {
        switch -glob -- $rev {
            HEAD - master - * { # Let anything through for now
                lappend result $rev
            }
        }
    }
    if {[llength $result] == 0} {




        set result [list HEAD]






    }
    return $result
}

# Figure out HG revision from arguments
proc eskil::rev::HG::ParseRevs {filename revs} {
    set result ""
................................................................................
    if {$revs == ""} { set revs -1 }
    foreach rev $revs {
        if {[string is digit $rev]} {
            lappend result $rev
        } else {
            if {[catch {exec csh -c "icmp4 files $filename"} res]} {
                tk_messageBox -icon error \
                        -message "Failed p4 files filename: $thisrev"
                exit
            }
            regexp {\#(\d+)} [file tail $res] -> res
            if {$rev != ""} { incr res $rev }
            lappend result $res
        }
    }
    return $result
}

# Check in CVS controlled file
proc eskil::rev::CVS::commitFile {top filename} {







    set logmsg [LogDialog $top $filename]
    if {$logmsg ne ""} {
        catch {exec cvs -q commit -m $logmsg $filename}
    }
}

# Check in SVN controlled file
proc eskil::rev::SVN::commitFile {top filename} {







    set logmsg [LogDialog $top $filename]
    if {$logmsg ne ""} {
        catch {exec svn -q commit -m $logmsg $filename}
    }
}



































# View log between displayed versions
proc eskil::rev::CVS::viewLog {top filename revs} {
    set cmd [list exec cvs -q log -N]
    if {[llength $revs] > 1} {
        lappend cmd -r[join $revs ":"]
    } else {
................................................................................
        if {![file exists $file]} { return "" }

        if {[info exists cache($file)]} {
            return $cache($file)
        }
    }
    
    set searchlist [list $preference GIT HG BZR P4]
    foreach ns [namespace children eskil::rev] {
        lappend searchlist [namespace tail $ns]
    }
    foreach rev $searchlist {
        set result [eskil::rev::${rev}::detect $file]
        if {$result} {
            set cache($file) $rev
................................................................................
    set ::diff($top,rightFile) $file
    set ::diff($top,rightOK) 1
    set ::diff($top,leftLabel) $rev
    set ::diff($top,leftOK) 0
    set ::Pref(toolbar) 1
}

# Prepare for RCS/CVS/CT diff. Checkout copies of the versions needed.
proc prepareRev {top} {
    global Pref

    $::widgets($top,commit) configure -state disabled
    $::widgets($top,log)    configure -state disabled

    set type $::diff($top,modetype)
................................................................................
            $::widgets($top,log) configure -state normal
        }
    }
    # Make sure labels are updated before processing starts
    update idletasks
}

# Clean up after a RCS/CVS/CT diff.
proc cleanupRev {top} {
    global Pref

    clearTmp $::diff($top,rightFile) $::diff($top,leftFile)
    set ::diff($top,rightFile) $::diff($top,RevFile)
    set ::diff($top,leftFile) $::diff($top,RevFile)
}

proc revCommit {top} {
    if {[$::widgets($top,commit) cget -state] eq "disabled"} return
    set type $::diff($top,modetype)





    eskil::rev::${type}::commitFile $top $::diff($top,RevFile)
}

proc revLog {top} {
    if {[$::widgets($top,log) cget -state] eq "disabled"} return
    set type $::diff($top,modetype)
    eskil::rev::${type}::viewLog $top $::diff($top,RevFile) \
            $::diff($top,RevRevs)
}

# Get a complete tree patch from this system.
proc getFullPatch {top} {
    global Pref




    set type $::diff($top,modetype)


    set revs {}

    # Search for revision options
    if {$::diff($top,doptrev1) != ""} {
        lappend revs $::diff($top,doptrev1)
    }
................................................................................

    set revs [eskil::rev::${type}::ParseRevs "" $revs]
    set revlabels {}
    foreach rev $revs {
        lappend revlabels [GetLastTwoPath $rev]
    }







    return [eskil::rev::${type}::getPatch $revs]
}

##############################################################################
# Utilities
##############################################################################

# Get the last two elements in a file path
................................................................................
        return $last
    } else {
        return [file join $penultimate $last]
    }
}

# Dialog for log message
proc LogDialog {top filename {clean 0}} {
    set w $top.logmsg
    destroy  $w
    toplevel $w -padx 3 -pady 3
    wm title $w "Commit log message for [file tail $filename]"

    set ::diff($top,logdialogok) 0

    text $w.t -width 70 -height 10
    if {!$clean && [info exists ::diff(logdialog)]} {
        $w.t insert end $::diff(logdialog)
        $w.t tag add sel 1.0 end-1c







|




>

|

|
|
>










>







 







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







 







|





>







 







|




>







 







|
>







 







|
>







 







|







 







>
>
>







 







|
|
|
>
>
>
>
>
|
>
>
>



>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







 







|







 







|
>
>
>
>
|
>
>
>
>
>
>







 







|











|
>
>
>
>
>
>
>
|

|




|
>
>
>
>
>
>
>
|

|


>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







 







|







 







|







 







|











>
>
>
>
>
|













>
>
>

>







 







>
>
>
>
>
>
|







 







|



|







43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
...
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
...
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
...
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
...
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
...
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
...
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
...
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
...
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
...
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
...
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
...
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
...
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
...
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
....
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
....
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
....
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118

# eskil::rev::XXX::get {filename outfile rev}
#
# Get a revision of a file and place it in outfile.
# rev is in any format understood by this system, and
# should be retrieved from ParseRevs

# eskil::rev::XXX::getPatch {revs {files {}}}
#
# Get a patch of the file tree, between the revisions given.
# revs is in any format understood by this system, and
# should be retrieved from ParseRevs
# An optional list of files that should be included can be given.

# eskil::rev::XXX::commitFile {top args}
#
# If implemented, enables the commit feature when comparing edited
# file(s) agains latest check in.
# If no files are given, all edited files are committed.

# eskil::rev::XXX::viewLog {top filename revs}
#
# If implemented, enables the log feature when comparing revisions.
# View log between displayed versions

namespace eval eskil::rev::CVS {}
namespace eval eskil::rev::RCS {}
namespace eval eskil::rev::CT {}
namespace eval eskil::rev::GIT {}
namespace eval eskil::rev::FOSSIL {}
namespace eval eskil::rev::SVN {}
namespace eval eskil::rev::HG {}
namespace eval eskil::rev::BZR {}
namespace eval eskil::rev::P4 {}

proc eskil::rev::CVS::detect {file} {
    if {$file eq ""} {
................................................................................
        [file isdirectory [file join $dir .. .. .git]]} {
        if {[auto_execok git] ne ""} {
            return 1
        }
    }
    return 0
}

proc eskil::rev::FOSSIL::detect {file} {
    if {$file eq ""} {
        set dir [pwd]
    } else {
        set dir [file dirname $file]
    }
    # Fossil, detect three steps down. Could be improved. FIXA
    if {[file exists [file join $dir _FOSSIL_]] ||
        [file exists [file join $dir .. _FOSSIL_]] ||
        [file exists [file join $dir .. .. _FOSSIL_]] ||
        [file exists [file join $dir .. .. .. _FOSSIL_]]} {
        if {[auto_execok fossil] ne ""} {
            return 1
        }
    }
    return 0
}

proc eskil::rev::P4::detect {file} {
    if {[auto_execok icmp4] != ""} {
        if {[catch {exec csh -c "icmp4 have $file"} p4have]} { return 0 }
	if {[lindex $p4have 1] eq "-"} { return 1 }
    }
    return 0
................................................................................

    if {$old != ""} {
        cd $old
    }
}

# Get a CVS patch
proc eskil::rev::CVS::getPatch {revs {files {}}} {
    if {$::Pref(context) > 0} {
        set context $::Pref(context)
    } else {
        set context 5
    }
    # TODO: support files
    set cmd [list exec cvs diff -U $context]
    foreach rev $revs {
        lappend cmd -r $rev
    }

    if {[catch {eval $cmd} res]} {
        if {![string match "*=========*" $res]} {
................................................................................

    if {$old != ""} {
        cd $old
    }
}

# Get a SVN patch
proc eskil::rev::SVN::getPatch {revs {files {}}} {
    set cmd [list exec svn diff]
    foreach rev $revs {
        lappend cmd -r $rev
    }
    lappend cmd {*}$files

    if {[catch {eval $cmd} res]} {
        tk_messageBox -icon error -title "SVN error" -message $res
        return ""
    }
    return $res
}
................................................................................

    if {$old != ""} {
        cd $old
    }
}

# Get a HG patch
proc eskil::rev::HG::getPatch {revs {files {}}} {
    # TODO: support files
    set cmd [list exec hg diff]
    foreach rev $revs {
        lappend cmd -r $rev
    }

    if {[catch {eval $cmd} res]} {
        tk_messageBox -icon error -title "HG error" -message $res
................................................................................

    if {$old != ""} {
        cd $old
    }
}

# Get a BZR patch
proc eskil::rev::BZR::getPatch {revs {files {}}} {
    # TODO: support files
    set cmd [list exec bzr diff]
    if {[llength $revs] == 2} {
        lappend cmd -r [lindex $revs 0]..[lindex $revs 1]
    } elseif {[llength $revs] == 1} {
        lappend cmd -r [lindex $revs 0]
    }

................................................................................
# Get an RCS revision
proc eskil::rev::RCS::get {filename outfile {rev {}}} {
    catch {exec co -p$rev [file nativename $filename] \
            > $outfile}
}

# Get a RCS patch
proc eskil::rev::RCS::getPatch {revs {files {}}} {
    # Not supported yet.
    return ""
}

# Get a GIT revision
# No support for revisions yet
proc eskil::rev::GIT::get {filename outfile rev} {
................................................................................
    set dir [file dirname $filename]
    set tail [file tail $filename]
    # Locate the top directory
    while {![file isdirectory $dir/.git]} {
        set thisdir [file tail $dir]
        set dir [file dirname $dir]
        set tail [file join $thisdir $tail]
    }
    if {$rev eq ""} {
        set rev HEAD
    }
    cd $dir
    catch {exec git show $rev:$tail > $outfile}
    cd $old
    # example: git show HEAD^^^:apa
}

................................................................................
    }
    cd $dir
    catch {exec git add $tail}
    cd $old
}

# Get a GIT patch
proc eskil::rev::GIT::getPatch {revs {files {}}} {
    set cmd [list exec git diff -p]
    if {[llength $revs] == 0} {
        # Always default to HEAD to see changes regardless of index
        lappend cmd HEAD
    } else {
        foreach rev $revs {
            lappend cmd $rev
        }
    }
    lappend cmd "--" {*}$files

    if {[catch {eval $cmd} res]} {
        tk_messageBox -icon error -title "GIT error" -message $res
        return ""
    }
    return $res
}

# Get a FOSSIL revision
# No support for revisions yet
proc eskil::rev::FOSSIL::get {filename outfile rev} {
    set old [pwd]
    set dir [file dirname $filename]
    set tail [file tail $filename]
    # Locate the top directory
    while {![file exists $dir/_FOSSIL_]} {
        set thisdir [file tail $dir]
        set dir [file dirname $dir]
        set tail [file join $thisdir $tail]
    }
    cd $dir
    if {$rev eq "HEAD" || $rev eq ""} {
        catch {exec fossil finfo -p $tail > $outfile}
    } else {
        catch {exec fossil finfo -p $tail -r $rev > $outfile}
    }
    cd $old
}

# Get a FOSSIL patch
proc eskil::rev::FOSSIL::getPatch {revs {files {}}} {
    set cmd [list exec fossil diff]

    if {[llength $revs] >= 1} {
        lappend cmd --from [lindex $revs 0]
    }
    if {[llength $revs] >= 2} {
        lappend cmd --to [lindex $revs 1]
    }
    if {[llength $files] > 0} {
        # Fossil diff only handles one file at a time.
        set res ""
        foreach file $files {
            set fcmd $cmd
            lappend fcmd $file
            if {[catch {eval $cmd} fres]} {
                tk_messageBox -icon error -title "FOSSIL error" -message $fres
                return ""
            }
            append res $fres
        }
    } else {
        if {[catch {eval $cmd} res]} {
            tk_messageBox -icon error -title "FOSSIL error" -message $res
            return ""
        }
    }
    return $res
}

# Get a ClearCase revision
proc eskil::rev::CT::get {filename outfile rev} {
    set filerev [file nativename $filename@@$rev]
................................................................................
    if {[catch {exec cleartool get -to $outfile $filerev} msg]} {
        tk_messageBox -icon error -title "Cleartool error" -message $msg
        return
    }
}

# Get a CT patch
proc eskil::rev::CT::getPatch {revs {files {}}} {
    # Not supported yet
    return ""
}

# Get a P4 revision
proc eskil::rev::P4::get {filename outfile rev} {
    set dir [file dirname $filename]
................................................................................
    foreach rev $revs {
        switch -glob -- $rev {
            HEAD - master - * { # Let anything through for now
                lappend result $rev
            }
        }
    }
    return $result
}

# Figure out FOSSIL revision from arguments
proc eskil::rev::FOSSIL::ParseRevs {filename revs} {
    set result ""
    foreach rev $revs {
        switch -glob -- $rev {
            HEAD - master - * { # Let anything through for now FIXA
                lappend result $rev
            }
        }
    }
    return $result
}

# Figure out HG revision from arguments
proc eskil::rev::HG::ParseRevs {filename revs} {
    set result ""
................................................................................
    if {$revs == ""} { set revs -1 }
    foreach rev $revs {
        if {[string is digit $rev]} {
            lappend result $rev
        } else {
            if {[catch {exec csh -c "icmp4 files $filename"} res]} {
                tk_messageBox -icon error \
                        -message "Failed p4 files filename: $rev"
                exit
            }
            regexp {\#(\d+)} [file tail $res] -> res
            if {$rev != ""} { incr res $rev }
            lappend result $res
        }
    }
    return $result
}

# Check in CVS controlled file
proc eskil::rev::CVS::commitFile {top args} {
    if {[llength $args] == 0} {
        set target all
    } elseif {[llength $args] == 1} {
        set target [file tail [lindex $args 0]]
    } else {
        set target "[file tail [lindex $args 0]] ..."
    }        
    set logmsg [LogDialog $top $target]
    if {$logmsg ne ""} {
        catch {exec cvs -q commit -m $logmsg {*}$args}
    }
}

# Check in SVN controlled file
proc eskil::rev::SVN::commitFile {top args} {
    if {[llength $args] == 0} {
        set target all
    } elseif {[llength $args] == 1} {
        set target [file tail [lindex $args 0]]
    } else {
        set target "[file tail [lindex $args 0]] ..."
    }        
    set logmsg [LogDialog $top $target]
    if {$logmsg ne ""} {
        catch {exec svn -q commit -m $logmsg {*}$args}
    }
}

# Check in GIT controlled file
proc eskil::rev::GIT::commitFile {top args} {
    if {[llength $args] == 0} {
        set target all
    } elseif {[llength $args] == 1} {
        set target [file tail [lindex $args 0]]
    } else {
        set target "[file tail [lindex $args 0]] ..."
    }        
    set logmsg [LogDialog $top $target]
    if {$logmsg eq ""} return

    if {[llength $args] == 0} {
        catch {exec git commit -a -m $logmsg}
    } else {
        catch {exec git commit -m $logmsg {*}$args}
    }
}

# Check in Fossil controlled file
proc eskil::rev::FOSSIL::commitFile {top args} {
    if {[llength $args] == 0} {
        set target all
    } elseif {[llength $args] == 1} {
        set target [file tail [lindex $args 0]]
    } else {
        set target "[file tail [lindex $args 0]] ..."
    }        
    set logmsg [LogDialog $top $target]
    if {$logmsg eq ""} return

    catch {exec fossil commit -m $logmsg {*}$args}
}

# View log between displayed versions
proc eskil::rev::CVS::viewLog {top filename revs} {
    set cmd [list exec cvs -q log -N]
    if {[llength $revs] > 1} {
        lappend cmd -r[join $revs ":"]
    } else {
................................................................................
        if {![file exists $file]} { return "" }

        if {[info exists cache($file)]} {
            return $cache($file)
        }
    }
    
    set searchlist [list $preference GIT FOSSIL HG BZR P4]
    foreach ns [namespace children eskil::rev] {
        lappend searchlist [namespace tail $ns]
    }
    foreach rev $searchlist {
        set result [eskil::rev::${rev}::detect $file]
        if {$result} {
            set cache($file) $rev
................................................................................
    set ::diff($top,rightFile) $file
    set ::diff($top,rightOK) 1
    set ::diff($top,leftLabel) $rev
    set ::diff($top,leftOK) 0
    set ::Pref(toolbar) 1
}

# Prepare for revision diff. Checkout copies of the versions needed.
proc prepareRev {top} {
    global Pref

    $::widgets($top,commit) configure -state disabled
    $::widgets($top,log)    configure -state disabled

    set type $::diff($top,modetype)
................................................................................
            $::widgets($top,log) configure -state normal
        }
    }
    # Make sure labels are updated before processing starts
    update idletasks
}

# Clean up after a revision diff.
proc cleanupRev {top} {
    global Pref

    clearTmp $::diff($top,rightFile) $::diff($top,leftFile)
    set ::diff($top,rightFile) $::diff($top,RevFile)
    set ::diff($top,leftFile) $::diff($top,RevFile)
}

proc revCommit {top} {
    if {[$::widgets($top,commit) cget -state] eq "disabled"} return
    set type $::diff($top,modetype)
    if {$::diff($top,mode) eq "patch"} {
        set files $::diff($top,reviewFiles)
    } else {
        set files [list $::diff($top,RevFile)]
    }
    eskil::rev::${type}::commitFile $top {*}$files
}

proc revLog {top} {
    if {[$::widgets($top,log) cget -state] eq "disabled"} return
    set type $::diff($top,modetype)
    eskil::rev::${type}::viewLog $top $::diff($top,RevFile) \
            $::diff($top,RevRevs)
}

# Get a complete tree patch from this system.
proc getFullPatch {top} {
    global Pref

    $::widgets($top,commit) configure -state disabled
    $::widgets($top,log)    configure -state disabled

    set type $::diff($top,modetype)
    set files $::diff($top,reviewFiles)

    set revs {}

    # Search for revision options
    if {$::diff($top,doptrev1) != ""} {
        lappend revs $::diff($top,doptrev1)
    }
................................................................................

    set revs [eskil::rev::${type}::ParseRevs "" $revs]
    set revlabels {}
    foreach rev $revs {
        lappend revlabels [GetLastTwoPath $rev]
    }

    if {[llength $revs] == 0} {
        if {[info commands eskil::rev::${type}::commitFile] ne ""} {
            $::widgets($top,commit) configure -state normal
        }
    }

    return [eskil::rev::${type}::getPatch $revs $files]
}

##############################################################################
# Utilities
##############################################################################

# Get the last two elements in a file path
................................................................................
        return $last
    } else {
        return [file join $penultimate $last]
    }
}

# Dialog for log message
proc LogDialog {top target {clean 0}} {
    set w $top.logmsg
    destroy  $w
    toplevel $w -padx 3 -pady 3
    wm title $w "Commit log message for $target"

    set ::diff($top,logdialogok) 0

    text $w.t -width 70 -height 10
    if {!$clean && [info exists ::diff(logdialog)]} {
        $w.t insert end $::diff(logdialog)
        $w.t tag add sel 1.0 end-1c

Added tests/ancestor.txt.













































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
0
1
Added left
2
3
4
Added right
5
6
7
Changed same
8
9
10
Changed different
12
13
14
Deleted left
16
17
18
Deleted right
19
20
21
Left: Add+change Right: change
22
23
24
Left: change Right add+change
25
26
27
Left: Delete  Right: change
28
29
30
Left: change  Right: delete
31
32
33
Added same
34
35
36
Deleted same
37
38
39
Changed adjacent
40
41
42
Left: Add+change Right: change same
43
44
45
Left: change same Right: add+change
46
47
48
Left: change Right: deleted block
49
50
51
52
Added different
53
54
55
Left mixed change
56
57
58
59
60
Right mixed change
61
62
63
64
65
Both added multiple
66
67

Added tests/gui.test.





























































































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
#------------------------------------------------------------*- tcl -*-
# Tests for GUI
#----------------------------------------------------------------------
# $Revision$
#----------------------------------------------------------------------

lappend ::auto_path /home/peter/src/TkTest
package require TkTest
wm withdraw .

proc XauthSecure {} {
    global tcl_platform

    if {[string compare unix $tcl_platform(platform)]} {
	# This makes no sense outside of Unix
	return
    }
    set hosts [exec xhost]
    # the first line is info only
    foreach host [lrange [split $hosts \n] 1 end] {
	exec xhost -$host
    }
    exec xhost -
}
XauthSecure

proc RestartClient {args} {
    set ::clientfile ./eskil.kit
    #if {[file exists ${::clientfile}_i]} {
    #    set ::clientfile ${::clientfile}_i
    #}

    if {![catch {send -async Eskil exit}]} {
        update
        after 500
    }

    set slavepid [exec $::clientfile -server {*}$args &]

    after 1000
    while {[catch {tktest::init Eskil}]} {
        after 500
    }
    tktest::cmd wm geometry . +10+10

    #set files [tktest::widget -class Listbox -pos 1 -eval "get 0 end"]
    #if {($mode == 0 && $files ne "_testfile_") || \
    #        ($mode != 0 && $files ne "")} {
    #    exec kill $slavepid
    #    puts "Another nagelfar is running. Can't do GUI tests."
    #    exit
    #}
}

proc DeleteFileToTest {file} {
    set w [tktest::widget -class Listbox -pos 1]
    set cont [tktest::cmd $w get 0 end]
    set i [lsearch $cont $file]
    if {$i < 0} return

    tktest::cmd $w see $i
    set coord [tktest::coord $w bbox $i]
    tktest::mouse left $coord
    tktest::key Delete
}

proc AddFileToTest {file {dialog 0}} {
    # Dialog = 1 : use gui
    # Dialog = 2 ; use gui+cancel
    if {$dialog != 0} {
        tktest::waitFocus press Add 1
        if {$dialog != 1} {
            tktest::press Cancel
        } else {
            set e [tktest::widget -class Entry]
            tktest::cmd $e insert end $file
            tktest::press Open
        }
    }
    if {$dialog != 1} {
        tktest::cmd fileDropFile $file
    }
}

proc ResetFilters {} {
    # FIXA: there should be a GUI way of doing this.
    tktest::cmd set ::Nagelfar(filter) {}
}

# Get all lines from the text widget, as a list
proc GetTextLines {} {
    set str [tktest::widget -class Text -eval "get 1.0 end"]
    return [split [string trim $str] \n]
}

test gui-1.1 {Run 3-way merge} -setup {
    set f1 [tcltest::makeFile {} _test1]
} -body {
    RestartClient -fine -a tests/ancestor.txt tests/left.txt tests/right.txt -o $f1
    tktest::waitFocus press Save
    tktest::waitFocus press No
    tktest::press Close
    catch {exec diff $f1 tests/merge.txt}
} -cleanup {
    tcltest::removeFile {} _test1
} -result {0}

catch {tktest::menu File Quit}
catch {send -async Eskil exit}
update

Added tests/left.txt.

















































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
0
1
Added left
x
2
3
4
Added right
5
6
7
Changed same
8a
9
10
Changed different
12a
13
14
Deleted left
17
18
Deleted right
19
20
21
Left: Add+change Right: change
xx
22a
23
24
Left: change Right add+change
25a
26
27
Left: Delete  Right: change
29
30
Left: change  Right: delete
31a
32
33
Added same
xxx
34
35
36
Deleted same
38
39
Changed adjacent
40a
41
42
Left: Add+change Right: change same
xxxx
43a
44
45
Left: change same Right: add+change
46a
47
48
Left: change Right: deleted block
49
50a
51
52
Added different
xxxxx
53
54
55
Left mixed change
57x
59
60
Right mixed change
61
62
63
64
65
Both added multiple
xx1
xx2
66
67

Added tests/merge.txt.



















































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
0
1
Added left
x
2
3
4
Added right
y
5
6
7
Changed same
8a
9
10
Changed different
12b
13
14
Deleted left
17
18
Deleted right
20
21
Left: Add+change Right: change
xx
22b
23
24
Left: change Right add+change
yy
25b
26
27
Left: Delete  Right: change
28a
29
30
Left: change  Right: delete
32
33
Added same
xxx
34
35
36
Deleted same
38
39
Changed adjacent
40a
41a
42
Left: Add+change Right: change same
xxxx
43a
44
45
Left: change same Right: add+change
yyyy
46a
47
48
Left: change Right: deleted block
52
Added different
xxxxx
yyyyy
53
54
55
Left mixed change
57x
59
60
Right mixed change
61x
63x
64
65
Both added multiple
xx1
xx2
yy1
yy2
66
67

Changes to tests/patch.test.

1
2
3
4
5
6
7

8
9
10
11
12
13
14
# Tests for patch file parsingunctions                      -*- tcl -*-
#----------------------------------------------------------------------
# $Revision$
#----------------------------------------------------------------------

# Overload exec during these tests
set ::diff(gurka,patchFile) ""

stub update args {}
stub getFullPatch {top} {
    return $::testpatch
}
stub displayOnePatch {top leftLines rightLines leftLine rightLine} {
}
stub emptyLine {top n {highlight 1}} {







>







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# Tests for patch file parsingunctions                      -*- tcl -*-
#----------------------------------------------------------------------
# $Revision$
#----------------------------------------------------------------------

# Overload exec during these tests
set ::diff(gurka,patchFile) ""
set ::diff(gurka,patchData) ""
stub update args {}
stub getFullPatch {top} {
    return $::testpatch
}
stub displayOnePatch {top leftLines rightLines leftLine rightLine} {
}
stub emptyLine {top n {highlight 1}} {

Changes to tests/rev.test.

277
278
279
280
281
282
283


















284
285
286
287
288
289
290
291
292
293
294
        file delete .bzr
    }
    set res
} -cleanup {
    tcltest::removeFile {} _rev2_11
} -result {BZR}



















test rev-3.1 {
    Subversion revisions
} -body {
    eskil::rev::SVN::ParseRevs filename -1
} -result {157}

test rev-3.2 {
    Subversion revisions
} -body {
    eskil::rev::SVN::ParseRevs filename -2
} -result {115}







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>











277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
        file delete .bzr
    }
    set res
} -cleanup {
    tcltest::removeFile {} _rev2_11
} -result {BZR}

test rev-2.12 {
    Detecting, Fossil
} -body {
    set f [tcltest::makeFile {} _rev2_12]
    set ::auto_execs(fossil) "x"
    set apa [file exists _FOSSIL_]
    if {!$apa} {
        file mkdir _FOSSIL_
    }
    set res [detectRevSystem $f]
    if {!$apa} {
        file delete _FOSSIL_
    }
    set res
} -cleanup {
    tcltest::removeFile {} _rev2_12
} -result {FOSSIL}

test rev-3.1 {
    Subversion revisions
} -body {
    eskil::rev::SVN::ParseRevs filename -1
} -result {157}

test rev-3.2 {
    Subversion revisions
} -body {
    eskil::rev::SVN::ParseRevs filename -2
} -result {115}

Added tests/right.txt.













































































































































































>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
0
1
Added left
2
3
4
Added right
y
5
6
7
Changed same
8a
9
10
Changed different
12b
13
14
Deleted left
16
17
18
Deleted right
20
21
Left: Add+change Right: change
22b
23
24
Left: change Right add+change
yy
25b
26
27
Left: Delete  Right: change
28a
29
30
Left: change  Right: delete
32
33
Added same
xxx
34
35
36
Deleted same
38
39
Changed adjacent
40
41a
42
Left: Add+change Right: change same
43a
44
45
Left: change same Right: add+change
yyyy
46a
47
48
Left: change Right: deleted block
52
Added different
yyyyy
53
54
55
Left mixed change
56
57
58
59
60
Right mixed change
61x
63x
64
65
Both added multiple
yy1
yy2
66
67