Eskil

Changes On Branch version-2.6.1
Login

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

Changes In Branch trunk Through [d859cf57c8] Excluding Merge-Ins

This is equivalent to a diff from 1cdf7d5e95 to d859cf57c8

2011-11-03
00:25
Use relative links to repo check-in: c38d2982b8 user: peter tags: trunk
2011-11-01
22:16
Bumped revision to 2.6.1 check-in: d859cf57c8 user: peter tags: trunk, version-2.6.1
22:12
Use a custom Toolbutton layout to get a small toolbutton in directory diff check-in: 00878f4ffb user: peter tags: trunk
2011-05-09
00:08
Minor correction to clear syntax warning. check-in: ddfc1ceec8 user: peter.spjuth@gmail.com tags: trunk
2011-05-08
22:49
Documented tablelist transition Closed-Leaf check-in: 1cdf7d5e95 user: peter.spjuth@gmail.com tags: table-list
2011-05-07
00:37
Handle links in directory diff. Changed buttons to use images in directory diff. check-in: eb61cb3ca6 user: peter.spjuth@gmail.com tags: table-list

Changes to Changes.


































1
2
3
4
5
6
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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







Release 2.6.1

2011-11-01
  Fixed bug where copy button in directory diff picked the wrong file.
  Use a custom Toolbutton layout to get a small toolbutton in directory diff.

Release 2.6

2011-10-27
  Rebuilt rev-detection to handle any dir depth.
  Detect .fos as fossil indicator.

2011-10-15
  Added Show in plugin dialog. Added sort plugin. [FR 3735]

2011-10-15
 Added procedure editor to debug menu.

2011-10-15
 Corrected search of plugins to find them in VFS. [Bug 18395]

2011-10-05
 Respect block parse setting when showing a patch. [Bug 18147]

2011-10-04
 Fall back to Tcl-dialog when accessing a vfs. [Bug 18371]

2011-05-09
 Use mouse dragging to set alignment.

2011-05-09
 Rewritten directory diff to use tablelist.
 Redesigned appearance of directory diff.

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.

Deleted Eskil.html.
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
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





















































































































































































-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
  <title>Eskil - A graphical frontend to Diff</title>
      
  <meta http-equiv="content-type"
 content="text/html; charset=ISO-8859-1">
</head>
<body>
<a href="http://developer.berlios.de" title="BerliOS Developer"> <img src="http://developer.berlios.de/bslogo.php?group_id=6563" width="124px" height="32px" border="0" alt="BerliOS Developer Logo"></a>
<p>
<h3>Bug tracker and mailing list can be found at the
<b><a href="https://developer.berlios.de/projects/eskil/">Project page</a>
</b></h3><p> 
<h1>Eskil: A graphical frontend to Diff</h1>
<a href="#EskilDownload">Download</a>
<a href="#EskilFeatures">Features</a>
<a href="#EskilScreenshots">Screenshots</a>
<a href="#EskilChanges">Changes</a>
<p>
<h3>About Eskil</h3>
The first major application I wrote in Tcl/Tk was this tool (somewhere around
1997).  Writing it is what made me learn and enjoy Tcl. (I already liked Tk
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.

<p>More information about <a href="http://wiki.tcl.tk/starkit">Starkits</a>
and <a href="http://wiki.tcl.tk/starpack">Starpacks</a>.

<a name="EskilScreenshots"></a><h3>Screenshots</h3>

<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>
 <li> Support commit in Subversion.</li>
 <li> Added -review for displaying all changes in a tree.</li>
 <li> Support command line "-" to read a patch from std input.</li>
</ul>
Changes in v2.3 (2007-12-05):<br>
<ul>
 <li> Added -printpdf command line option.</li>
 <li> Fixed line numbering in PDF with big line numbers.</li>
 <li> Started on GIT support.</li>
 <li> Anything on the command line is checked for a starkit.
      Kits are mounted and treated as directories.</li>
</ul>
Changes in v2.2 (2007-04-05):<br>
<ul>
 <li> Added experimental -nonewline command option.</li>
 <li> Added -close command option.</li>
 <li> Added experimental PDF print.</li>
 <li> Added dirdiff preferences and filters.</li>
 <li> Smarter save in merge. [FR 2957]</li>
 <li> Added commit button for CVS. [FR 2780]</li>
 <li> Bug fixes include: Kits are mounted readonly,
      fixed dir diff window menu,
      improved patch file parsing.</li>
</ul>
<br>
Changes in v2.1 (2006-06-02):<br>
<ul>
  <li>  Added -preprocess command line option.</li>
  <li>  Added -foreach command line option.</li>
  <li>  Added -context command line option.</li>
  <li>  Handle starkits as directories in dir diff.</li>
  <li>  Support relative -r with CVS.</li>
</ul>
<br>
Changes in v2.0.7 (2004-12-14):<br>
<ul>
  <li>  Added regsub preprocessing option.</li>
  <li>  Added -prefix command line option.</li>
  <li>  Improved merge window.</li>
  <li>  Added ignore keyword option to directory diff.</li>
</ul>
<br>
Changes in v2.0.6 (2004-10-19):<br>
<ul>
  <li>  Added Ignore Digit option.</li>
  <li>  Fixed bug in -r for ClearCase.</li>
  <li>  Edit Mode made more robust.</li>
</ul>
<br>
Changes in v2.0.5 (2004-08-20):<br>
<ul>
  <li>  Option -r can now be used with ClearCase diff.</li>
  <li>  Edit Mode allows simple editing in the diff display and saving.</li>
</ul>
<br>
Changes in v2.0.4 (2004-06-17):<br>
<ul>
  <li>  Added ignore case option.</li>
  <li>  Improved alignment function.</li>
</ul>
<br>
Changes in v2.0.3 (2004-05-26):<br>
<ul>
  <li>  Added context options for "Diffs only" mode.</li>
  <li>  Rewrote how "Separate Diff" and "Align" works.  The latter now only happens after a "Redo Diff".</li>
  <li>  Added scroll map and some more context menu options in Directory Diff.</li>
</ul>
<br>
Changes in v2.0.2 (2004-05-03):<br>
<ul>
  <li>  Fixed a bug in ClearCase support.</li>
  <li>  Improved enscipt usage in print command.</li>
  <li>  Added "mark file" in dirdiff context menu.</li>
</ul>
<br>
Changes in v2.0.1 (2004-02-10):<br>
<ul>
  <li>  Added preference for width and height.</li>
  <li>  Added Tools menu to directory diff window.</li>
  <li>  Made it simpler to save a conflict in the same file.</li>
</ul>
<br>
First public release v2.0 (2004-01-30):<br>
<br>

</body>
</html>
Changes to Makefile.
1
2
3
4
5

6
7
8
9
10
11
12
1
2
3
4

5
6
7
8
9
10
11
12




-
+







#----------------------------------------------------------------------
# Make file for Eskil
#----------------------------------------------------------------------

VERSION = 26
VERSION = 261

# 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

26
27
28
29
30
31
32
33

34
35
36
37
38
39
40
26
27
28
29
30
31
32

33
34
35
36
37
38
39
40







-
+








# 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/print.tcl src/registry.tcl src/rev.tcl src/debug.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:
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
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







-
+
+
+
+






-
+





-
-
+
+







	@$(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
	@./tests/all.tcl $(TESTFLAGS)

run:
	$(TCLKIT_LINUX) eskil.vfs/main.tcl

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

# Source files for code coverage
COVFILES = src/rev.tcl src/eskil.tcl
COVFILES = eskil.vfs/main.tcl eskil.vfs/src/rev.tcl eskil.vfs/src/eskil.tcl eskil.vfs/src/merge.tcl
IFILES   = $(COVFILES:.tcl=.tcl_i)
LOGFILES = $(COVFILES:.tcl=.tcl_log)
MFILES   = $(COVFILES:.tcl=.tcl_m)

# Instrument source file for code coverage
%.tcl_i: %.tcl
	@$(NAGELFAR) -instrument $<
%.tcl_i: %.tcl eskil_h.syntax
	@$(NAGELFAR) -instrument eskil_h.syntax $<

# Target to prepare for code coverage run. Makes sure log file is clear.
instrument: $(IFILES)
	@rm -f $(LOGFILES)

# Run tests to create log file.
testcover $(LOGFILES): $(IFILES)
Changes to doc/revision.txt.
29
30
31
32
33
34
35
36

37
38
39
40
41
42
43
29
30
31
32
33
34
35

36
37
38
39
40
41
42
43







-
+








Eskil can read a patch from standard input, thus allowing display from any patch generating command. 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
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.
51
52
53
54
55
56
57





58
59
60


61
62
63
64
65
66
67
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74







+
+
+
+
+



+
+








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>".

<pre>git config --global merge.tool eskil</pre>
<pre>git config --global mergetool.eskil.cmd 'eskil -fine -a $BASE -o $MERGED $REMOTE $LOCAL'</pre>
<pre>git config --global diff.tool eskil</pre>
<pre>git config --global difftool.eskil.cmd 'eskil $LOCAL $REMOTE'</pre>

<ul>Fossil</ul>

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

<pre>fossil settings gmerge-command 'eskil -fine -a "%baseline" "%merge" "%original" -o "%output"' -global</pre>

<ul>Mercurial</ul>

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

<ul>Bazaar</ul>

Deleted eskil.vfs/COPYING.
1

-
../COPYING
Deleted eskil.vfs/doc.
1

-
../doc
Deleted eskil.vfs/examples.
1

-
../examples
Changes to eskil.vfs/lib/psballoon/psballoon.tcl.
66
67
68
69
70
71
72

73



74
75
76
77
78
79
80
66
67
68
69
70
71
72
73

74
75
76
77
78
79
80
81
82
83







+
-
+
+
+







    set balloon(created) 0
    set balloon(pending) 0
}

proc psballoon::createBalloon {w mx my} {
    variable balloon
    if {$balloon(created) == 0} {
        # Figure out widget's font
        set font [$w cget -font]
        if {[catch {set font [$w cget -font]}]} {
            set font [ttk::style lookup [winfo class $w] -font]
        }
        set ww [winfo width $w]
        set ih [winfo height $w]
        set ix 0
        set iy 0
        set create 1
        set msg $balloon(msg,$w)
        if {$msg == ""} {
Deleted eskil.vfs/plugins.
1

-
../plugins
Deleted eskil.vfs/src/clip.tcl.
1

-
../../src/clip.tcl
Deleted eskil.vfs/src/compare.tcl.
1

-
../../src/compare.tcl
Deleted eskil.vfs/src/dirdiff.tcl.
1

-
../../src/dirdiff.tcl
Deleted eskil.vfs/src/eskil.tcl.
1

-
../../src/eskil.tcl
Deleted eskil.vfs/src/help.tcl.
1

-
../../src/help.tcl
Deleted eskil.vfs/src/map.tcl.
1

-
../../src/map.tcl
Deleted eskil.vfs/src/merge.tcl.
1

-
../../src/merge.tcl
Deleted eskil.vfs/src/plugin.tcl.
1

-
../../src/plugin.tcl
Deleted eskil.vfs/src/print.tcl.
1

-
../../src/print.tcl
Deleted eskil.vfs/src/printobj.tcl.
1

-
../../src/printobj.tcl
Deleted eskil.vfs/src/registry.tcl.
1

-
../../src/registry.tcl
Deleted eskil.vfs/src/rev.tcl.
1

-
../../src/rev.tcl
Added htdocs/changes.html.








































































































































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
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
  <title>Eskil - A graphical frontend to Diff</title>
      
  <meta http-equiv="content-type"
 content="text/html; charset=ISO-8859-1">
</head>
<body>
<h1><a href="http://eskil.tcl.tk/">Eskil</a>: A graphical frontend to Diff</h1>
<a href="download.html">Download</a>
<a href="index.html#EskilFeatures">Features</a>
<a href="index.html#EskilScreenshots">Screenshots</a>
Changes
<a href="/fossil/timeline">Fossil Repository</a>
<a href="/fossil/reportlist">Bug Tracker</a>
<h3>Changes</h3>
Changes in v2.6 (2011-10-30):<br>
<ul>
 <li> Support commit in Git and Fossil.</li>
 <li> Support commit, list of files and revisions with -review.</li>
 <li> Added Paste Patch command.</li>
 <li> New -pluginlist option. New GUI for plugin selection.</li>
 <li> Added three-way merge.</li>
 <li> Autodetect line endings in ancestor file to select merge output.</li>
 <li> Fully rewritten directory diff with new design.</li>
 <li> Set alignment with drag & drop.</li>
</ul>
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>
 <li> Support commit in Subversion.</li>
 <li> Added -review for displaying all changes in a tree.</li>
 <li> Support command line "-" to read a patch from std input.</li>
</ul>
Changes in v2.3 (2007-12-05):<br>
<ul>
 <li> Added -printpdf command line option.</li>
 <li> Fixed line numbering in PDF with big line numbers.</li>
 <li> Started on GIT support.</li>
 <li> Anything on the command line is checked for a starkit.
      Kits are mounted and treated as directories.</li>
</ul>
Changes in v2.2 (2007-04-05):<br>
<ul>
 <li> Added experimental -nonewline command option.</li>
 <li> Added -close command option.</li>
 <li> Added experimental PDF print.</li>
 <li> Added dirdiff preferences and filters.</li>
 <li> Smarter save in merge. [FR 2957]</li>
 <li> Added commit button for CVS. [FR 2780]</li>
 <li> Bug fixes include: Kits are mounted readonly,
      fixed dir diff window menu,
      improved patch file parsing.</li>
</ul>
<br>
Changes in v2.1 (2006-06-02):<br>
<ul>
  <li>  Added -preprocess command line option.</li>
  <li>  Added -foreach command line option.</li>
  <li>  Added -context command line option.</li>
  <li>  Handle starkits as directories in dir diff.</li>
  <li>  Support relative -r with CVS.</li>
</ul>
<br>
Changes in v2.0.7 (2004-12-14):<br>
<ul>
  <li>  Added regsub preprocessing option.</li>
  <li>  Added -prefix command line option.</li>
  <li>  Improved merge window.</li>
  <li>  Added ignore keyword option to directory diff.</li>
</ul>
<br>
Changes in v2.0.6 (2004-10-19):<br>
<ul>
  <li>  Added Ignore Digit option.</li>
  <li>  Fixed bug in -r for ClearCase.</li>
  <li>  Edit Mode made more robust.</li>
</ul>
<br>
Changes in v2.0.5 (2004-08-20):<br>
<ul>
  <li>  Option -r can now be used with ClearCase diff.</li>
  <li>  Edit Mode allows simple editing in the diff display and saving.</li>
</ul>
<br>
Changes in v2.0.4 (2004-06-17):<br>
<ul>
  <li>  Added ignore case option.</li>
  <li>  Improved alignment function.</li>
</ul>
<br>
Changes in v2.0.3 (2004-05-26):<br>
<ul>
  <li>  Added context options for "Diffs only" mode.</li>
  <li>  Rewrote how "Separate Diff" and "Align" works.  The latter now only happens after a "Redo Diff".</li>
  <li>  Added scroll map and some more context menu options in Directory Diff.</li>
</ul>
<br>
Changes in v2.0.2 (2004-05-03):<br>
<ul>
  <li>  Fixed a bug in ClearCase support.</li>
  <li>  Improved enscipt usage in print command.</li>
  <li>  Added "mark file" in dirdiff context menu.</li>
</ul>
<br>
Changes in v2.0.1 (2004-02-10):<br>
<ul>
  <li>  Added preference for width and height.</li>
  <li>  Added Tools menu to directory diff window.</li>
  <li>  Made it simpler to save a conflict in the same file.</li>
</ul>
<br>
First public release v2.0 (2004-01-30):<br>
<br>

</body>
</html>
Added htdocs/download.html.


































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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
  <title>Eskil - A graphical frontend to Diff</title>
      
  <meta http-equiv="content-type"
 content="text/html; charset=ISO-8859-1">
</head>
<body>
<h1><a href="http://eskil.tcl.tk/">Eskil</a>: A graphical frontend to Diff</h1>
Download
<a href="index.html#EskilFeatures">Features</a>
<a href="index.html#EskilScreenshots">Screenshots</a>
<a href="changes.html">Changes</a>
<a href="/fossil/timeline">Fossil Repository</a>
<a href="/fossil/reportlist">Bug Tracker</a>
<p>
<h3>Download</h3>

Download page is currently being reorganised, not files here yet.<br>

Version 2.5:<br>
Is available from the
<a href="/fossil/timeline">Fossil Repository</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.

<p>More information about <a href="http://wiki.tcl.tk/starkit">Starkits</a>
and <a href="http://wiki.tcl.tk/starpack">Starpacks</a>.

</body>
</html>
Added htdocs/eskil1.png.

cannot compute difference between binary files

Added htdocs/eskil2.png.

cannot compute difference between binary files

Added htdocs/eskil3.png.

cannot compute difference between binary files

Added htdocs/index.html.





























































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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
  <title>Eskil - A graphical frontend to Diff</title>
      
  <meta http-equiv="content-type"
 content="text/html; charset=ISO-8859-1">
</head>
<body>
<h1><a href="http://eskil.tcl.tk/">Eskil</a>: A graphical frontend to Diff</h1>
<a href="download.html">Download</a>
<a href="#EskilFeatures">Features</a>
<a href="#EskilScreenshots">Screenshots</a>
<a href="changes.html">Changes</a>
<a href="/fossil/timeline">Fossil Repository</a>
<a href="/fossil/reportlist">Bug Tracker</a>
<p>
<h3>About Eskil</h3>
The first major application I wrote in Tcl/Tk was this tool (somewhere around
1997).  Writing it is what made me learn and enjoy Tcl. (I already liked Tk
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>Recursive directory diff.</li>
<li>CVS/RCS/ClearCase/GIT/SVN/BZR/HG/Perforce/Fossil diff.</li>
<li>Conflict merge and three-way 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="EskilScreenshots"></a><h3>Screenshots</h3>

<img src="eskil1.png">
<p>
A "zoom" feature for long lines.<p>
<img src="eskil2.png"><br>
<p>Directory Diff.<p>
<img src="eskil3.png"><br>

</body>
</html>
Added mergetest-fossil.sh.




















1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
#!/bin/sh
# Build up a merge conflict in fossil
fossil init apa.fossil
mkdir apa_fossil
cd apa_fossil
fossil open ../apa.fossil
fossil settings gmerge-command 'eskil -fine -a "%baseline" "%merge" "%original" -o "%output"'
cp ../tests/ancestor.txt a.txt
fossil add a.txt
fossil commit -m a
fossil branch new b trunk
fossil update b
cp ../tests/right.txt a.txt
fossil commit -m r
fossil update trunk
cp ../tests/left.txt  a.txt
fossil commit -m l
fossil update b
#fossil merge trunk
#fossil commit -m "Merge from trunk"
Added mergetest-git.sh.


















1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
#!/bin/sh
# Build up a merge conflict in git
mkdir apa_git
cd apa_git
git init
cp ../tests/ancestor.txt a.txt
git add a.txt
git commit -m a
git checkout -b b
cp ../tests/right.txt a.txt
git commit -am r
git checkout master
cp ../tests/left.txt  a.txt
git commit -am l
git checkout b
#git merge master
#git mergetool
#git commit -am m
Added plugins/sort.tcl.






























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
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
##Eskil Plugin : Compare files after sorting lines

# Example file for a plugin.
# A plugin must start exactly like this one.
# The text after : is the summary you can get at the command line

# This plugin compares the set of words in files.

# A plugin must define this procedure to do the job.
# side: left or right
# chi:  An input channel for reading the original file.
# cho:  An output channel for writing the processed file.
proc PreProcess {side chi cho} {
    set data [read $chi]
    set endingNewLine 0
    if {[string index $data end] eq "\n"} {
        set data [string range $data 0 end-1]
        set endingNewLine 1
    }
    set lines [split $data \n]
    # Allow sort parameters in info
    set lines [lsort -dictionary {*}$::Info $lines]
    puts -nonewline $cho [join $lines \n]
    if {$endingNewLine} {
        puts $cho ""
    }
    # Signal that the file after processing should be used both
    # for comparison and for displaying.
    return 1
}
Changes to src/clip.tcl.
29
30
31
32
33
34
35
36

37
38
39
40
41
42

43
44
45
46
47
48
49
29
30
31
32
33
34
35

36
37
38
39
40
41

42
43
44
45
46
47
48
49







-
+





-
+







}

proc DoClipDiff {} {
    set f1 [tmpFile]
    set f2 [tmpFile]

    set ch [open $f1 w]
    set data1 [$::diff(wClip1) get 1.0 end]
    set data1 [$::eskil(wClip1) get 1.0 end]
    set data1 [ClipClean $data1]
    puts $ch $data1
    close $ch

    set ch [open $f2 w]
    set data2 [$::diff(wClip2) get 1.0 end]
    set data2 [$::eskil(wClip2) get 1.0 end]
    set data2 [ClipClean $data2]
    puts $ch $data2
    close $ch

    #set line1 [split $data1 \n]
    #set len1  [llength $line1]
    #set line2 [split $data2 \n]
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
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







-
+











-
+







        $::widgets($top,wLine1) configure -height 1
        $t2 configure -height $lines2
        $::widgets($top,wLine2) configure -height 1
    }
}

proc ArmCatch {} {
    if {$::diff(armcatch)} {
    if {$::eskil(armcatch)} {
        bind .clipdiff <FocusOut> {
            if {[string equal %W .clipdiff]} {
                after 50 CatchFromWin
            }
        }
    } else {
        bind .clipdiff <FocusOut> {}
    }
}

proc CatchFromWin {} {
    set ::diff(armcatch) 0
    set ::eskil(armcatch) 0
    ArmCatch
    set win [twapi::get_foreground_window]
    if {$win eq ""} {
        #puts "No fg window"
        return
    }
    #puts "Locating windows"
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
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







-
-
+
+





-
+



-
+







        twapi::send_keys ^(ac)
        after 50 "set ::CatchFromWinWait 1" ; vwait ::CatchFromWinWait
        lassign [twapi::get_window_coordinates $win] x1 y1 x2 y2
        if {[catch {clipboard get} text]} continue
        if {$text eq ""} continue
        lappend capturedData [list $x1 $text]
    }
    $::diff(wClip1) delete 1.0 end
    $::diff(wClip2) delete 1.0 end
    $::eskil(wClip1) delete 1.0 end
    $::eskil(wClip2) delete 1.0 end
    if {[llength $capturedData] == 0} return
    # Set it up left-to-right
    set capturedData [lsort -index 0 -integer $capturedData]
    if {[llength $capturedData] >= 1} {
        set text [lindex $capturedData 0 1]
        $::diff(wClip1) insert end $text
        $::eskil(wClip1) insert end $text
    }
    if {[llength $capturedData] >= 2} {
        set text [lindex $capturedData 1 1]
        $::diff(wClip2) insert end $text
        $::eskil(wClip2) insert end $text
        after idle DoClipDiff
    }
}

proc makeClipDiffWin {} {
    set top .clipdiff
    if {[winfo exists $top] && [winfo toplevel $top] eq $top} {
142
143
144
145
146
147
148
149
150


151
152
153
154
155
156
157
142
143
144
145
146
147
148


149
150
151
152
153
154
155
156
157







-
-
+
+







    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]

    set ::diff(wClip1) $t1
    set ::diff(wClip2) $t2
    set ::eskil(wClip1) $t1
    set ::eskil(wClip2) $t2

    bind $t1 <Control-o> [list focus $t2]
    bind $t2 <Control-o> [list focus $t1]

    ttk::frame $top.f
    menubutton $top.f.mf -menu $top.f.mf.m -text "File" -underline 0
    menu $top.f.mf.m
181
182
183
184
185
186
187
188

189
190
191
192
193
194
195
196
197
198
199
200
181
182
183
184
185
186
187

188
189
190
191
192
193
194
195
196
197
198
199
200







-
+












            -padx 4 -pady 2 -sticky "w"
    grid $top.f.mf -sticky nw -pady 0 -padx 0
    grid columnconfigure $top.f {0 3 5 8} -weight 1
    grid columnconfigure $top.f 8 -minsize [winfo reqwidth $top.f.mf]

    if {![catch {package require twapi}]} {
        ttk::checkbutton $top.f.b6 -text "Capture" -command ArmCatch \
                -underline 0 -variable ::diff(armcatch)
                -underline 0 -variable ::eskil(armcatch)
        bind $top <Alt-c> [list $top.f.b6 invoke]
        #raise $top.f.b6
        place $top.f.b6 -anchor e -relx 1.0 -rely 0.5
    }

    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
}
Added src/debug.tcl.






































































































































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
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
# debug.tcl
#
#    Helpers for debugging.
#
#
proc testme {x y z} {
    puts "In Testme $x $y $z"
    list [lindex $x 0] [lindex $y 0]
}

proc debugTRenter {cmd op} {
    set fr [info frame -2]
    puts "Line [dict get $fr line] Enter: '$cmd'"
}
proc debugTRenterstep {cmd op} {
    set fr [info frame -2]
    #puts "$fr"
    puts "Line [dict get $fr line] Enterstep: '$cmd'"
}
proc debugTRleave {cmd core res op} {
    puts "Leave: '$res'"
}
proc debugTRleavestep {cmd code res op} {
    puts "Leavestep: '$res'"
}

proc debugTR {cmd} {
    trace add execution $cmd enter debugTRenter
    trace add execution $cmd leave debugTRleave
    trace add execution $cmd enterstep debugTRenterstep
    trace add execution $cmd leavestep debugTRleavestep
}

proc DebugMenu {m} {
    $m add cascade -label "Debug" -menu $m.debug -underline 0
    menu $m.debug

    if {$::tcl_platform(platform) eq "windows"} {
        $m.debug add checkbutton -label "Console" -variable consolestate \
                -onvalue show -offvalue hide -command {console $consolestate} \
                -underline 0
        $m.debug add separator
    }

    $m.debug add command -label "Edit" -command ProcEditor -underline 0
    return $m.debug
}

proc ProcEditorUpdate {a k} {
    # Only update on keys generating characters
    if {$a eq ""} return
    #puts "Key '$a' '$k'"
    set p [info procs $::ProcEditor(proc)]
    if {$p eq "" && $::ProcEditor(proc) ne ""} {
        # Try prefix matching
        set p [info procs $::ProcEditor(proc)*]
    }
    set p [lsort -dictionary $p]
    $::ProcEditor(procW) configure -values $p
    # Keep the first
    set p [lindex $p 0]

    if {$p eq ""} {
        set ::ProcEditor(args) ""
        $::ProcEditor(bodyW) delete 1.0 end
        return
    }
    if {$p ne $::ProcEditor(proc)} {
        after idle [list set ::ProcEditor(proc) $p]
        after idle [list $::ProcEditor(procW) selection range insert end]
    }

    after idle ProcEditorSelected
}

proc ProcEditorSelected {} {
    set p [info procs $::ProcEditor(proc)]
    if {$p eq ""} {
        set ::ProcEditor(args) ""
        $::ProcEditor(bodyW) delete 1.0 end
        return
    }
    set arglist {}
    foreach i [info args $p] {
        if {[info default $p $i value]} {
            lappend arglist [list $i $value]
        } else {
            lappend arglist [list $i]
        }
    }
    set body [info body $p]

    set ::ProcEditor(args) $arglist
    $::ProcEditor(bodyW) delete 1.0 end
    $::ProcEditor(bodyW) insert end $body
}

proc ProcEditorRedefine {} {
    set body [$::ProcEditor(bodyW) get 1.0 end]
    set body [string trimright $body]

    ##nagelfar ignore Non constant argument to proc
    proc $::ProcEditor(proc) $::ProcEditor(args) $body
}

proc ProcEditor {} {
    set top .proceditor
    destroy $top
    toplevel $top -padx 3 -pady 3
    ttk::frame $top.bgf
    place $top.bgf -x 0 -y 0 -relwidth 1.0 -relheight 1.0 -anchor nw
    wm title $top "Proc Editor"

    ttk::label $top.l1 -text "Proc" -anchor w
    ttk::combobox $top.e1 -textvariable ::ProcEditor(proc) -values ""
    set ::ProcEditor(procW) $top.e1
    bind $top.e1 <KeyRelease> {ProcEditorUpdate %A %K}
    bind $top.e1 <<ComboboxSelected>> ProcEditorSelected
    #trace add variable ::ProcEditor(proc) write "ProcEditorUpdate"
    ttk::label $top.l2 -text "Args" -anchor w
    ttk::entry $top.e2 -textvariable ::ProcEditor(args)
    set ::ProcEditor(bodyW) [text $top.t -yscrollcommand "$top.sby set"]
    ttk::scrollbar $top.sby -orient vertical -command "$top.t yview"

    ttk::button $top.b -text "Redefine" -command ProcEditorRedefine

    grid $top.l1 $top.e1 -        -padx 3 -pady 3 -sticky we
    grid $top.l2 $top.e2 -        -padx 3 -pady 3 -sticky we
    grid $top.t  -       $top.sby -padx 3 -pady 3 -sticky news
    grid $top.b  -       -        -padx 3 -pady 3

    grid columnconfigure $top 1 -weight 1
    grid rowconfigure    $top 2 -weight 1
}
Changes to src/dirdiff.tcl.
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
18
19
20
21
22
23
24


25
26
27
28
29
30
31







-
-







#  the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
#  Boston, MA 02111-1307, USA.
#
#----------------------------------------------------------------------
# $Revision$
#----------------------------------------------------------------------

package require tablelist_tile

# Compare file names
proc FStrCmp {s1 s2} {
    # Equality is based on platform's standard
    # Order is dictionary order

    # Exact equal is equal regardless of platform.
    if {$s1 eq $s2} {
265
266
267
268
269
270
271
272
273
274
275
276
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
263
264
265
266
267
268
269

270
271
272
273
274
275
276
277
278
279
280
281
282
283











284
285
286
287
288
289
290







-














-
-
-
-
-
-
-
-
-
-
-







    variable ScheduledRestart 0
    variable IdleQueue {}
    variable IdleQueueArr
    variable leftMark ""
    variable rightMark ""
    variable leftDir ""
    variable rightDir ""
    variable img

    constructor {args} {
        variable color
        install tree using tablelist::tablelist $win.tree -height 20 \
                -movablecolumns no -setgrid no -showseparators yes \
                -expandcommand [mymethod expandCmd] \
                -collapsecommand [mymethod collapseCmd] \
                -fullseparators yes -selectmode none \
                -columns {0 "Structure" 0 Size 0 Date 0 Copy 0 Size 0 Date}
        install vsb using scrollbar $win.vsb -orient vertical \
                -command "$tree yview"
        install hsb using scrollbar $win.hsb -orient horizontal \
                -command "$tree xview"

        # Use demo images from Tablelist
        set dir $::eskil(thisDir)/../lib/tablelist/demos
        set img(clsd) [image create photo -file [file join $dir clsdFolder.gif]]
        set img(open) [image create photo -file [file join $dir openFolder.gif]]
        set img(file) [image create photo -file [file join $dir file.gif]]
        # Local images
        set dir $::eskil(thisDir)/images
        set img(link) [image create photo -file [file join $dir link.gif]]
        set img(left) [image create photo -file [file join $dir arrow_left.gif]]
        set img(right) [image create photo -file [file join $dir arrow_right.gif]]

        set AfterId ""
        set IdleQueue {}

        $tree configure -yscrollcommand "$vsb set" -xscrollcommand "$hsb set"

        $tree columnconfigure 0 -name structure
        $tree columnconfigure 1 -name leftsize -align right
393
394
395
396
397
398
399
400

401
402
403
404
405
406
407
408
409
410
411

412
413
414
415
416

417
418
419
420
421
422
423
379
380
381
382
383
384
385

386
387
388
389
390
391
392
393
394
395
396

397
398
399
400
401

402
403
404
405
406
407
408
409







-
+










-
+




-
+







        set d1 [file tail $leftDir]
        set d2 [file tail $rightDir]
        if {$d1 eq $d2} {
            $tree cellconfigure $topIndex,structure -text $d1
        } else {
            $tree cellconfigure $topIndex,structure -text "$d1 vs $d2"
        }
        $tree cellconfigure $topIndex,structure -image $img(open)
        $tree cellconfigure $topIndex,structure -image $::img(open)
        $tree rowattrib $topIndex type directory
        $self SetNodeStatus $topIndex empty
        $tree rowattrib $topIndex leftfull $leftDir             
        $tree rowattrib $topIndex rightfull $rightDir            

        $self UpdateDirNode $topIndex
    }

    method expandCmd {tbl row} {
        if {[$tree childcount $row] != 0} {
            $tree cellconfigure $row,0 -image $img(open)
            $tree cellconfigure $row,0 -image $::img(open)
        }
    }

    method collapseCmd {tbl row} {
        $tree cellconfigure $row,0 -image $img(clsd)
        $tree cellconfigure $row,0 -image $::img(clsd)
    }

    # Format a time stamp for display
    proc FormatDate {date} {
        clock format $date -format "%Y-%m-%d %H:%M:%S"
    }

774
775
776
777
778
779
780
781

782
783

784
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
760
761
762
763
764
765
766

767
768

769
770
771
772
773
774
775
776
777
778
779
780

781
782
783
784
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







-
+

-
+











-
+















+






-
+

-
-
-
-
+
+
+
+







        set id [$tree insertchild $node end $values]
        $tree rowattrib $id type $type
        $tree rowattrib $id status unknown
        $tree rowattrib $id leftfull $df1
        $tree rowattrib $id rightfull $df2
        if {$type ne "directory"} {
            if {$type eq "link"} {
                $tree cellconfigure $id,structure -image $img(link)
                $tree cellconfigure $id,structure -image $::img(link)
            } else {
                $tree cellconfigure $id,structure -image $img(file)
                $tree cellconfigure $id,structure -image $::img(file)
                $tree cellconfigure $id,command -window [mymethod addCmdCol]
            }
        }

        if {$type eq "directory"} {
            ## Make it so that this node is openable
            $tree collapse $id
            #$tree insertchild $id end dummy ;# a dummy
            $tree cellconfigure $id,structure -text $name/
            $self SetNodeStatus $id empty
            $self AddNodeToIdle $id
            $tree cellconfigure $id,structure -image $img(clsd)
            $tree cellconfigure $id,structure -image $::img(clsd)
        } elseif {$size1 == $size2 && \
                $time1 == $time2} {
            $self SetNodeStatus $id equal
        } elseif {$size1 == ""} {
            $self SetNodeStatus $id new
        } elseif {$size2 == ""} {
            $self SetNodeStatus $id old
        } else {
            $self SetNodeStatus $id unknown
            $self AddNodeToIdle $id
        }
        return [$tree rowattrib $id status]
    }

    method addCmdCol {tbl row col w} {
        set key [$tree getfullkeys $row]
        set status [$tree rowattrib $row status]
        set type   [$tree rowattrib $row type]
        set lf [$tree rowattrib $row leftfull]
        set rf [$tree rowattrib $row rightfull]
        set bg [$tbl cget -background]
        ttk::style configure Apa.TFrame -background $bg
        ttk::style configure Apa.Toolbutton -background $bg
        ttk::style configure Apa.My.Toolbutton -background $bg
        ttk::frame $w -style Apa.TFrame
        ttk::button $w.bl -image $img(left) -style Apa.Toolbutton \
                -command [mymethod CopyFile $row right]
        ttk::button $w.br -image $img(right) -style Apa.Toolbutton \
                -command [mymethod CopyFile $row left]
        ttk::button $w.bl -image $::img(left) -style Apa.My.Toolbutton \
                -command [mymethod CopyFile $key right]
        ttk::button $w.br -image $::img(right) -style Apa.My.Toolbutton \
                -command [mymethod CopyFile $key left]
        pack $w.bl $w.br -side left -fill y
        if {$lf eq ""} {
            $w.br configure -state disabled
        }
        if {$rf eq ""} {
            $w.bl configure -state disabled
        }
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
906
907
908
909
910
911
912








913
914
915
916
917
918
919







-
-
-
-
-
-
-
-







    variable statusVar

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

        set dir $::eskil(thisDir)/images
        set img(open) [image create photo -file [file join $dir folderopen1.gif]]
        set img(up) [image create photo -file [file join $dir arrow_up.gif]]
        set h [image height $img(up)]
        set w [image width $img(up)]
        set img(upup) [image create photo -height $h -width [expr {2 * $w}]]
        $img(upup) copy $img(up) -to 0 0 [expr {2 * $w - 1}] [expr {$h - 1}]

        install tree using DirCompareTree $win.dc \
                -leftdirvariable ::dirdiff(leftDir) \
                -rightdirvariable ::dirdiff(rightDir) \
                -statusvar [myvar statusVar]

        ttk::frame $win.fe1
        ttk::frame $win.fe2
999
1000
1001
1002
1003
1004
1005
1006

1007

1008
1009
1010
1011
1012
1013
1014
1015



1016
1017

1018
1019
1020
1021



1022
1023

1024
1025
1026
1027
1028
1029
1030
978
979
980
981
982
983
984

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







-
+

+






-
-
+
+
+


+


-
-
+
+
+


+







            }
            $win.m.md add command -label "Reread Source" -underline 0 \
                    -command {EskilRereadSource}
            $win.m.md add separator
            $win.m.md add command -label "Redraw Window" -command {makeDirDiffWin 1}
        }
        
        ttk::button $win.bu -image $img(upup) -command [mymethod UpDir] \
        ttk::button $win.bu -image $::img(upup) -command [mymethod UpDir] \
                -underline 0
        addBalloon $win.bu "Up in both."
        bind $win <Alt-u> "$win.bu invoke"
        
        #catch {font delete myfont}
        #font create myfont -family $Pref(fontfamily) -size $Pref(fontsize)

        ttk::entryX $win.e1 -textvariable dirdiff(leftDir) -width 30
        ttk::button $win.bu1 -image $img(up) -command [mymethod UpDir 1]
        ttk::button $win.bb1 -image $img(open) \
        ttk::button $win.bu1 -image $::img(up) -command [mymethod UpDir 1]
        addBalloon $win.bu1 "Up in left."
        ttk::button $win.bb1 -image $::img(browse) \
                -command "[list BrowseDir dirdiff(leftDir) $win.e1]
                          [mymethod DoDirCompare]"
        addBalloon $win.bb1 "Browse left."
        after 50 [list after idle [list $win.e1 xview end]]
        ttk::entryX $win.e2 -textvariable dirdiff(rightDir) -width 30
        ttk::button $win.bu2 -image $img(up) -command [mymethod UpDir 2]
        ttk::button $win.bb2 -image $img(open) \
        ttk::button $win.bu2 -image $::img(up) -command [mymethod UpDir 2]
        addBalloon $win.bu2 "Up in right."
        ttk::button $win.bb2 -image $::img(browse) \
                -command "[list BrowseDir dirdiff(rightDir) $win.e2]
                          [mymethod DoDirCompare]"
        addBalloon $win.bb2 "Browse right."
        after 50 [list after idle [list $win.e2 xview end]]
        bind $win.e1 <Return> [mymethod DoDirCompare]
        bind $win.e2 <Return> [mymethod DoDirCompare]

        ttk::label $win.sl -anchor w -textvariable [myvar statusVar]
        
        pack $win.bb1 $win.bu1 -in $win.fe1 -side left -pady 1 -ipadx 10
Changes to src/eskil.tcl.
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
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







-
+














+



















-
-
-
-
-
-
-
-
-
-
-
-
+
-







-
+







# 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-05-06"
set ::eskil(diffver) "Version 2.6.1 2011-11-01"
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
    package require tablelist_tile

    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

    InitReSource
    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)
    locateTmp ::eskil(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"]} {
            set ::env(PATH) "$::env(PATH);c:\\bin"
            auto_reset
        }
159
160
161
162
163
164
165










































166
167
168
169
170
171
172
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







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+







            } else {
                $hull xview {*}$args
            }
        }
    }

    interp alias {} toplevel {} ttk::toplevel

    # Use demo images from Tablelist
    set dir $::eskil(thisDir)/../lib/tablelist/demos
    if {[catch {
        set ::img(clsd) [image create photo -file [file join $dir clsdFolder.gif]]
        set ::img(open) [image create photo -file [file join $dir openFolder.gif]]
        set ::img(file) [image create photo -file [file join $dir file.gif]]
    }]} then {
        set ::img(clsd) ""
        set ::img(open) ""
        set ::img(file) ""
    }
    # Local images
    set dir $::eskil(thisDir)/images
    set ::img(link) [image create photo -file [file join $dir link.gif]]
    set ::img(left) [image create photo -file [file join $dir arrow_left.gif]]
    set ::img(right) [image create photo -file [file join $dir arrow_right.gif]]
    set ::img(browse) [image create photo -file [file join $dir folderopen1.gif]]
    set ::img(up) [image create photo -file [file join $dir arrow_up.gif]]
    # Create a double up arrow
    set ih [image height $::img(up)]
    set iw [image width $::img(up)]
    set ::img(upup) [image create photo -height $ih -width [expr {2 * $iw}]]
    $::img(upup) copy $::img(up) -to 0 0 [expr {2 * $iw - 1}] [expr {$ih - 1}]

    EskilThemeInit
}

proc InitReSource {} {
    # 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
    source $::eskil(thisDir)/debug.tcl
}

# 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?
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
247
248
249
250
251
252
253
254
255
256

257
258
259
260
261
262
263
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
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262

263
264

265
266
267
268
269
270
271
272

273
274
275

276
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
313
314

315
316
317
318
319
320
321
322







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+











-
+

-
+







-
+


-
+

















-
+




















-
+







            if {[file readable $src]} {
                set this $src
            }
        }
    }
    puts "Resourcing $this"
    uplevel \#0 [list source $this]
    # Get all other source files
    InitReSource
}

# Initialize Ttk style settings
proc EskilThemeInit {} {
    # Import the 'default' theme border element.
    catch { ttk::style element create plain.border from default }
    catch { ttk::style element create plain.padding from default }
    catch { ttk::style element create plain.label from default }

    # Create a new style using the imported element.
    ttk::style layout My.Toolbutton {
        My.Toolbutton.plain.border -sticky nswe -children {
            My.Toolbutton.padding -sticky nswe -children {
                My.Toolbutton.label -sticky nswe
            }
        }
    }
    # Configure  our new style.
    ttk::style configure My.Toolbutton {*}[ttk::style configure Toolbutton] \
        -padding {1 1}
    ttk::style map My.Toolbutton {*}[ttk::style map Toolbutton] \
        -relief {disabled flat selected sunken pressed sunken active raised}     
    # Re-do if the user changes theme.
    if {[lsearch -exact [bind . <<ThemeChanged>>] EskilThemeInit] == -1} {
        bind . <<ThemeChanged>> +EskilThemeInit
    }
}

# 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]
            set i [lsearch $::eskil(diffWindows) $top]
            if {$i >= 0} {
                set ::diff(diffWindows) [lreplace $::diff(diffWindows) $i $i]
                set ::eskil(diffWindows) [lreplace $::eskil(diffWindows) $i $i]
            }
            set i [lsearch $::widgets(toolbars) $top.f]
            if {$i >= 0} {
                set ::widgets(toolbars) [lreplace $::widgets(toolbars) $i $i]
            }

            destroy $top
            array unset ::diff $top,*
            array unset ::eskil $top,*

            # Any windows remaining?
            if {[llength $::diff(diffWindows)] > 0} {
            if {[llength $::eskil(diffWindows)] > 0} {
                set cont 1
            }
        }
    } errMsg]} {
        tk_messageBox -icon error -title "Eskil Error" -message \
                "An error occured in the close process.\n$errMsg\n\
                (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
    lappend ::eskil(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]
    set name [file join $::eskil(tmpdir) $name]
    lappend ::tmpfiles $name
    return $name
}

# Delete temporary files
proc clearTmp {args} {
    if {![info exists ::tmpfiles]} {
304
305
306
307
308
309
310
311
312


313
314
315
316
317

318
319
320

321
322
323
324
325
326
327
363
364
365
366
367
368
369


370
371
372
373
374
375

376
377
378

379
380
381
382
383
384
385
386







-
-
+
+




-
+


-
+








# Insert one line in each text widget.
# Mark them as changed, and optionally parse them.
proc insertMatchingLines {top line1 line2} {
    global doingLine1 doingLine2 Pref

    # FIXA: fully implement filter
    if {$::diff(filter) != ""} {
        if {[regexp $::diff(filter) $line1]} {
    if {$::eskil(filter) != ""} {
        if {[regexp $::eskil(filter) $line1]} {
            insertLine $top 1 $doingLine1 $line1
            insertLine $top 2 $doingLine2 $line2
            incr doingLine1
            incr doingLine2
            set ::diff(filterflag) 1
            set ::eskil(filterflag) 1
            return
        }
        set ::diff(filterflag) 0
        set ::eskil(filterflag) 0
    }

    if {$Pref(parse) != 0} {
        set opts $Pref(ignore)
        if {$Pref(nocase)} {lappend opts -nocase}
        if {$Pref(lineparsewords)} {lappend opts -words}
        set res [DiffUtil::diffStrings {*}$opts $line1 $line2]
448
449
450
451
452
453
454


































455
456

457
458
459
460

461











462
463
464
465
466
467
468
469
470
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







+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+

-
+

-


+
-
+
+
+
+
+
+
+
+
+
+
+

-







        return [expr {($n1 > $n2 ? $n1 : $n2) + 1}]
    } else {
        return [expr {-($n1 > $n2 ? $n1 : $n2)}]
    }
}

# Insert two blocks of lines in the compare windows.
# No extra parsing at all.
proc insertMatchingBlocksNoParse {top block1 block2 line1 line2 details} {
    global doingLine1 doingLine2

    set n1 [llength $block1]
    set n2 [llength $block2]
    # Is this a change block, a delete block or an insert block?
    if {$n1 == 0} {set tag2 new2} else {set tag2 change}
    if {$n2 == 0} {set tag1 new1} else {set tag1 change}

    foreach line $block1 {
        insertLine $top 1 $doingLine1 $line $tag1
        incr doingLine1
    }
    foreach line $block2 {
        insertLine $top 2 $doingLine2 $line $tag2
        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
    }
}

# Insert two blocks of lines in the compare windows.
proc insertMatchingBlocks {top block1 block2 line1 line2 details} {
    global doingLine1 doingLine2
    global doingLine1 doingLine2 Pref

    # A large block may take time.  Give a small warning.
    set n1 [llength $block1]
    set n2 [llength $block2]

    if {$n1 * $n2 > 1000} {
    set large [expr {$n1 * $n2 > 1000}]

    if {$n1 == 0 || $n2 == 0 || $Pref(parse) < 2 || \
            ($large && $Pref(parse) < 3)} {
        # No extra parsing at all.
        insertMatchingBlocksNoParse $top $block1 $block2 $line1 $line2 $details
        return
    }

    # A large block may take time.  Give a small warning.
    if {$large} {
        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]
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
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







-
+




-
+

-
+








-
+

-
+








    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 ""} {
    if {$finegrain && $::eskil($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)]} {
            if {[info exists ::eskil($top,ancestorLeft,$t)]} {
                set leftChangeOrAdd 1
                if {$::diff($top,ancestorLeft,$t) eq "c"} {
                if {$::eskil($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)]} {
            if {[info exists ::eskil($top,ancestorRight,$t)]} {
                set rightChangeOrAdd 1
                if {$::diff($top,ancestorRight,$t) eq "c"} {
                if {$::eskil($top,ancestorRight,$t) eq "c"} {
                    set rightChange 1
                    break
                }
            }
        }
        # Avoid fine grain if either side has no changes against ancestor
        if {!$leftChangeOrAdd || !$rightChangeOrAdd} {
607
608
609
610
611
612
613
614
615


616
617
618
619
620
621
622
709
710
711
712
713
714
715


716
717
718
719
720
721
722
723
724







-
-
+
+







        # All blocks have been processed. Continue until end of file.
        # If "show all" is not on, just display a couple of context lines.
        set limit -1
        if {$Pref(context) >= 0} {
            set limit $Pref(context)
        }
	# Consider any total limit on displayed lines.
        if {$::diff($top,limitlines)} {
            set limit [expr {$::diff($top,limitlines) - $::diff($top,mapMax)}]
        if {$::eskil($top,limitlines)} {
            set limit [expr {$::eskil($top,limitlines) - $::eskil($top,mapMax)}]
            if {$limit < 0} {
                set limit 0
            }
        }
        if {$limit >= 0} {disallowEdit $top}

        set t 0
635
636
637
638
639
640
641
642

643
644
645
646
647
648
649
737
738
739
740
741
742
743

744
745
746
747
748
749
750
751







-
+







            incr doingLine1
            incr t
            if {$limit >= 0 && $t >= $limit} break
        }
        return
    }

    # Is this a change block, a delete block or a insert block?
    # Is this a change block, a delete block or an insert block?
    if {$n1 == 0} {set tag2 new2} else {set tag2 change}
    if {$n2 == 0} {set tag1 new1} else {set tag1 change}

    # Display all equal lines before next diff
    # If only diff is on, only skip a section if the blank
    # line replaces at least 3 lines.
    set limit -1
672
673
674
675
676
677
678
679
680


681
682
683
684
685
686
687
774
775
776
777
778
779
780


781
782
783
784
785
786
787
788
789







-
-
+
+







                emptyLine $top 2 0
                addMapLines $top 1
            }
        }
        incr doingLine1
        incr doingLine2
        incr t
        if {$::diff($top,limitlines) && \
                ($::diff($top,mapMax) > $::diff($top,limitlines))} {
        if {$::eskil($top,limitlines) && \
                ($::eskil($top,mapMax) > $::eskil($top,limitlines))} {
            return
        }
    }
    # This should not happen unless something is wrong...
    if {$doingLine2 != $line2} {
        disallowEdit $top
        $::widgets($top,wDiff1) insert end \
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












727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
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







-
+






-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-







        # If block parsing is turned off, only do line parsing for
        # blocks of equal size.
        for {set t 0} {$t < $n1} {incr t} {
            gets $ch1 textline1
            gets $ch2 textline2
            insertMatchingLines $top $textline1 $textline2
        }
        if {$::diff(filter) != "" &&  $::diff(filterflag)} {
        if {$::eskil(filter) != "" &&  $::eskil(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} {
                gets $ch1 apa
                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
        # Collect blocks
        set block1 {}
        for {set t 0} {$t < $n1} {incr t} {
            gets $ch1 apa
            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
            }
            for {set t 0} {$t < $n2} {incr t} {
                gets $ch2 apa
                insertLine $top 2 $doingLine2 $apa $tag2
                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
}

proc enableRedo {top} {
    $top.m.mf entryconfigure "Redo Diff" -state normal
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
873
874
875
876

877
878
879
880


881
882
883
884
885
886
887
888
889
890



891
892
893
894
895
896
897
866
867
868
869
870
871
872








873
874
875
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
917
918
919
920
921
922
923
924
925
926
927
928

929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949

950
951
952


953
954
955
956
957
958
959
960
961



962
963
964
965
966
967
968
969
970
971







-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+











-
+

-
+








-
-
+
+

-
-
-
+
+
+

-
+


















-
+




















-
+


-
-
+
+







-
-
-
+
+
+







}

#####################################
# Special cases.  Conflict/patch
#####################################

proc startConflictDiff {top file} {
    set ::diff($top,mode) "conflict"
    set ::diff($top,modetype) ""
    set ::diff($top,conflictFile) $file
    set ::diff($top,rightDir) [file dirname $file]
    set ::diff($top,rightOK) 1
    set ::diff($top,rightLabel) $file
    set ::diff($top,leftLabel) $file
    set ::diff($top,leftOK) 0
    set ::eskil($top,mode) "conflict"
    set ::eskil($top,modetype) ""
    set ::eskil($top,conflictFile) $file
    set ::eskil($top,rightDir) [file dirname $file]
    set ::eskil($top,rightOK) 1
    set ::eskil($top,rightLabel) $file
    set ::eskil($top,leftLabel) $file
    set ::eskil($top,leftOK) 0

    # Turn off ignore
    set ::Pref(ignore) " "
    set ::Pref(nocase) 0
    set ::Pref(noempty) 0

    # Try to autodetect line endings in file
    set ch [open $file rb]
    set data [read $ch 10000]
    close $ch
    if {[string first \r\n $data] >= 0} {
        set ::diff($top,mergetranslation) crlf
        set ::eskil($top,mergetranslation) crlf
    } else {
        set ::diff($top,mergetranslation) lf
        set ::eskil($top,mergetranslation) lf
    }
}

# Read a conflict file and extract the two versions.
proc prepareConflict {top} {
    global Pref

    disallowEdit $top
    set ::diff($top,leftFile) [tmpFile]
    set ::diff($top,rightFile) [tmpFile]
    set ::eskil($top,leftFile) [tmpFile]
    set ::eskil($top,rightFile) [tmpFile]

    set ch1 [open $::diff($top,leftFile) w]
    set ch2 [open $::diff($top,rightFile) w]
    set ch [open $::diff($top,conflictFile) r]
    set ch1 [open $::eskil($top,leftFile) w]
    set ch2 [open $::eskil($top,rightFile) w]
    set ch [open $::eskil($top,conflictFile) r]

    set ::diff($top,conflictDiff) {}
    set ::eskil($top,conflictDiff) {}
    set leftLine 1
    set rightLine 1
    set state both
    set rightName ""
    set leftName ""
    while {[gets $ch line] != -1} {
        if {[string match <<<<<<* $line]} {
            set state right
            regexp {<*\s*(.*)} $line -> rightName
            set start2 $rightLine
        } elseif {[string match ======* $line] && $state eq "right"} {
            set state left
            set end2 [expr {$rightLine - 1}]
            set start1 $leftLine
        } elseif {[string match >>>>>>* $line] && $state eq "left"} {
            set state both
            regexp {>*\s*(.*)} $line -> leftName
            set end1 [expr {$leftLine - 1}]
            lappend ::diff($top,conflictDiff) [list \
            lappend ::eskil($top,conflictDiff) [list \
                    $start1 [expr {$end1 - $start1 + 1}] \
                    $start2 [expr {$end2 - $start2 + 1}]]
        } elseif {$state eq "both"} {
            puts $ch1 $line
            puts $ch2 $line
            incr leftLine
            incr rightLine
        } elseif {$state eq "left"} {
            puts $ch1 $line
            incr leftLine
        } else {
            puts $ch2 $line
            incr rightLine
        }
    }
    close $ch
    close $ch1
    close $ch2

    if {$leftName eq "" && $rightName eq ""} {
        set leftName "No Conflict: [file tail $::diff($top,conflictFile)]"
        set leftName "No Conflict: [file tail $::eskil($top,conflictFile)]"
        set rightName $leftName
    }
    set ::diff($top,leftLabel) $leftName
    set ::diff($top,rightLabel) $rightName
    set ::eskil($top,leftLabel) $leftName
    set ::eskil($top,rightLabel) $rightName
    update idletasks
}

# Clean up after a conflict diff.
proc cleanupConflict {top} {
    global Pref

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

# Display one chunk from a patch file
proc displayOnePatch {top leftLines rightLines leftLine rightLine} {
    emptyLine $top 1
    emptyLine $top 2

995
996
997
998
999
1000
1001
1002
1003


1004
1005
1006
1007


1008
1009
1010

1011
1012

1013
1014
1015

1016
1017
1018
1019
1020
1021
1022
1069
1070
1071
1072
1073
1074
1075


1076
1077
1078
1079


1080
1081
1082
1083

1084
1085

1086
1087
1088

1089
1090
1091
1092
1093
1094
1095
1096







-
-
+
+


-
-
+
+


-
+

-
+


-
+







    }
}

# 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"
    set ::eskil($top,leftLabel) "Patch $::eskil($top,patchFile): old"
    set ::eskil($top,rightLabel) "Patch $::eskil($top,patchFile): new"
    update idletasks

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

    set style ""
    set divider "-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-"

1178
1179
1180
1181
1182
1183
1184
1185
1186


1187
1188
1189


1190
1191

1192
1193

1194
1195

1196
1197
1198
1199
1200
1201

1202
1203
1204
1205
1206
1207
1208

1209
1210
1211
1212
1213
1214
1215
1252
1253
1254
1255
1256
1257
1258


1259
1260
1261


1262
1263
1264

1265
1266

1267
1268

1269
1270
1271
1272
1273
1274

1275
1276
1277
1278
1279
1280
1281

1282
1283
1284
1285
1286
1287
1288
1289







-
-
+
+

-
-
+
+

-
+

-
+

-
+





-
+






-
+








#####################################
# Main diff
#####################################

# Prepare for a diff by creating needed temporary files
proc prepareFiles {top} {
    set ::diff($top,cleanup) {}
    if {$::diff($top,mode) eq "rev"} {
    set ::eskil($top,cleanup) {}
    if {$::eskil($top,mode) eq "rev"} {
        prepareRev $top
        lappend ::diff($top,cleanup) "rev"
    } elseif {$::diff($top,mode) eq "conflict"} {
        lappend ::eskil($top,cleanup) "rev"
    } elseif {$::eskil($top,mode) eq "conflict"} {
        prepareConflict $top
        lappend ::diff($top,cleanup) "conflict"
        lappend ::eskil($top,cleanup) "conflict"
    }
    if {$::diff($top,plugin) ne ""} {
    if {$::eskil($top,plugin) ne ""} {
        preparePlugin $top
        set ::diff($top,cleanup) "plugin $::diff($top,cleanup)"
        set ::eskil($top,cleanup) "plugin $::eskil($top,cleanup)"
    }
}

# Clean up after a diff
proc cleanupFiles {top} {
    foreach keyword $::diff($top,cleanup) {
    foreach keyword $::eskil($top,cleanup) {
        switch $keyword {
            "rev"      {cleanupRev      $top}
            "conflict" {cleanupConflict $top}
            "plugin"   {cleanupPlugin   $top}
        }
    }
    set ::diff($top,cleanup) {}
    set ::eskil($top,cleanup) {}
}

# Redo Diff command
proc redoDiff {top} {
    # Note what rows are being displayed
    set w $::widgets($top,wDiff1)

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

1263
1264
1265
1266
1267
1268
1269
1310
1311
1312
1313
1314
1315
1316







1317
1318
1319
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335

1336
1337
1338
1339
1340
1341
1342
1343







-
-
-
-
-
-
-
+
+
+
+
+
+
+












-
+







        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)]
    set tail1 [file tail $::eskil($top,rightLabel)]
    set tail2 [file tail $::eskil($top,leftLabel)]
    if {$::eskil($top,mode) ne "" || $tail1 eq $tail2} {
        if {$::eskil($top,mode) eq "rev"} {
            set tail1 [file tail $::eskil($top,RevFile)]
        } elseif {$::eskil($top,mode) eq "conflict"} {
            set tail1 [file tail $::eskil($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)} {
    if {$::eskil($top,mode) eq "" && ($::eskil($top,leftOK) == 0 || $::eskil($top,rightOK) == 0)} {
        disableRedo $top
        return
    } else {
        enableRedo $top
    }

    busyCursor $top
1281
1282
1283
1284
1285
1286
1287
1288

1289
1290
1291
1292
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


1330
1331

1332
1333
1334


1335
1336

1337
1338
1339
1340
1341
1342
1343
1344
1345
1346


1347
1348
1349
1350
1351
1352
1353
1354
1355
1356
1357
1358
1359
1360
1361
1362
1363
1364
1365
1366
1367

1368
1369
1370
1371
1372
1373
1374


1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395


1396
1397
1398
1399
1400
1401

1402
1403
1404
1405
1406
1407
1408
1355
1356
1357
1358
1359
1360
1361

1362
1363
1364
1365
1366
1367
1368
1369
1370

1371
1372
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384



1385
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
1413
1414
1415
1416
1417
1418


1419
1420
1421
1422
1423
1424
1425
1426
1427
1428
1429
1430
1431
1432
1433
1434
1435
1436
1437
1438
1439
1440

1441
1442
1443
1444
1445
1446


1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
1459
1460
1461
1462
1463
1464
1465
1466
1467


1468
1469
1470
1471
1472
1473
1474

1475
1476
1477
1478
1479
1480
1481
1482







-
+








-
+













-
-
-
+
+
+


-
-
-
+
+
+









-
-
+
+

-
+

-
-
+
+

-
+








-
-
+
+




















-
+





-
-
+
+



















-
-
+
+





-
+







    # Display a star during diff execution, to know when the internal
    # processing starts, and when the label is "valid".
    set ::widgets($top,eqLabel) "*"

    wm title $top "Eskil:"
    update idletasks

    if {$::diff($top,mode) eq "patch"} {
    if {$::eskil($top,mode) eq "patch"} {
        disallowEdit $top
        displayPatch $top
        drawMap $top -1
        foreach item {wLine1 wLine2} {
            set w $::widgets($top,$item)
            $w configure -state disabled
        }
        update idletasks
        wm title $top "Eskil: [file tail $::diff($top,patchFile)]"
        wm title $top "Eskil: [file tail $::eskil($top,patchFile)]"
        $::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} {
        lappend opts -align $::diff($top,aligns)
    if {[info exists ::eskil($top,aligns)] && \
            [llength $::eskil($top,aligns)] > 0} {
        lappend opts -align $::eskil($top,aligns)
    }
    set range {}
    if {[info exists ::diff($top,range)] && \
            [llength $::diff($top,range)] == 4} {
        set range $::diff($top,range)
    if {[info exists ::eskil($top,range)] && \
            [llength $::eskil($top,range)] == 4} {
        set range $::eskil($top,range)
        lappend opts -range $range
    }
    if {[llength $Pref(regsub)] > 0} {
        lappend opts -regsub $Pref(regsub)
    }
    # Apply nodigit after preprocess
    if {$Pref(nodigit)} {lappend opts -nodigit}

    # If a special file for diffing is present, use it.
    if {[info exists ::diff($top,leftFileDiff)]} {
        set dFile1 $::diff($top,leftFileDiff)
    if {[info exists ::eskil($top,leftFileDiff)]} {
        set dFile1 $::eskil($top,leftFileDiff)
    } else {
        set dFile1 $::diff($top,leftFile)
        set dFile1 $::eskil($top,leftFile)
    }
    if {[info exists ::diff($top,rightFileDiff)]} {
        set dFile2 $::diff($top,rightFileDiff)
    if {[info exists ::eskil($top,rightFileDiff)]} {
        set dFile2 $::eskil($top,rightFileDiff)
    } else {
        set dFile2 $::diff($top,rightFile)
        set dFile2 $::eskil($top,rightFile)
    }

    set differr [catch {DiffUtil::diffFiles {*}$opts \
            $dFile1 $dFile2} diffres]

    # In conflict mode we can use the diff information collected when
    # parsing the conflict file. This makes sure the blocks in the conflict
    # file become change-blocks during merge.
    if {$::diff($top,mode) eq "conflict" && $::diff($top,modetype) eq "Pure"} {
        set diffres $::diff($top,conflictDiff)
    if {$::eskil($top,mode) eq "conflict" && $::eskil($top,modetype) eq "Pure"} {
        set diffres $::eskil($top,conflictDiff)
    }

    if {$differr != 0} {
        $::widgets($top,wDiff1) insert end $diffres
        normalCursor $top
        return
    }
    if {[llength $diffres] == 0} {
        set ::widgets($top,eqLabel) "="
        # Automatically close if equal
        if {$::eskil(autoclose)} {
            after idle cleanupAndExit $top
            return
        }
    } else {
        set ::widgets($top,eqLabel) " "
    }
    # Update the equal label immediately for better feedback
    update idletasks

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

    set firstview 1

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

    # If there is a range, skip lines up to the range
    if {[llength $range] != 0} {
        disallowEdit $top
        lassign $range start1 end1 start2 end2
        while {$doingLine1 < $start1 && [gets $ch1 line] >= 0} {
            incr doingLine1
        }
        while {$doingLine2 < $start2 && [gets $ch2 line] >= 0} {
            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))} {
        if {$::eskil($top,limitlines) && \
                ($::eskil($top,mapMax) > $::eskil($top,limitlines))} {
            break
        }

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

    # If there is a range, just display the range
1435
1436
1437
1438
1439
1440
1441
1442
1443
1444



1445
1446
1447
1448
1449
1450
1451
1509
1510
1511
1512
1513
1514
1515



1516
1517
1518
1519
1520
1521
1522
1523
1524
1525







-
-
-
+
+
+







    close $ch2

    # We can turn off editing in the text windows after everything
    # is displayed.
    noEdit $top

    # Mark aligned lines
    if {[info exists ::diff($top,aligns)] && \
            [llength $::diff($top,aligns)] > 0} {
        foreach {align1 align2} $::diff($top,aligns) {
    if {[info exists ::eskil($top,aligns)] && \
            [llength $::eskil($top,aligns)] > 0} {
        foreach {align1 align2} $::eskil($top,aligns) {
            set i [$::widgets($top,wLine1) search -regexp "\\m$align1\\M" 1.0]
            if {$i != ""} {
                $::widgets($top,wLine1) tag add align \
                        "$i linestart" "$i lineend"
            }
            set i [$::widgets($top,wLine2) search -regexp "\\m$align2\\M" 1.0]
            if {$i != ""} {
1465
1466
1467
1468
1469
1470
1471
1472

1473
1474
1475
1476

1477
1478
1479
1480
1481

1482
1483
1484
1485
1486
1487
1488
1489
1490
1491
1492
1493
1494
1495
1496
1497

1498
1499
1500
1501
1502
1503
1504
1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519



1520
1521

1522
1523
1524
1525
1526
1527
1528





1529
1530

1531
1532

1533
1534
1535
1536
1537
1538
1539
1540
1541

1542
1543
1544

1545
1546
1547
1548
1549
1550
1551
1539
1540
1541
1542
1543
1544
1545

1546
1547
1548
1549

1550
1551
1552
1553
1554

1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566
1567
1568
1569
1570

1571
1572
1573
1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590



1591
1592
1593
1594

1595
1596
1597





1598
1599
1600
1601
1602
1603

1604
1605

1606
1607
1608
1609
1610
1611
1612
1613
1614

1615
1616
1617

1618
1619
1620
1621
1622
1623
1624
1625







-
+



-
+




-
+















-
+



















-
-
-
+
+
+

-
+


-
-
-
-
-
+
+
+
+
+

-
+

-
+








-
+


-
+







    normalCursor $top
    showDiff $top 0
    if {$::widgets($top,eqLabel) eq "!"} {
        set ::widgets($top,eqLabel) " "
    }

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

# This is the entrypoint to do a diff via DDE or Send
proc remoteDiff {file1 file2} {
    newDiff $file1 $file2
}

#####################################
# Highlight and navigation stuff
#####################################

# Scroll windows to next/previous diff
proc findDiff {top delta} {
    showDiff $top [expr {$::diff($top,currHighLight) + $delta}]
    showDiff $top [expr {$::eskil($top,currHighLight) + $delta}]
}

# Scroll a text window to view a certain range, and possibly some
# lines before and after.
proc seeText {w si ei} {
    $w see $ei
    $w see $si
    $w see $si-5lines
    $w see $ei+5lines
    if {[llength [$w bbox $si]] == 0} {
        $w yview $si-5lines
    }
    if {[llength [$w bbox $ei]] == 0} {
        $w yview $si
    }
}

# Highlight a diff
proc highLightChange {top n} {
    if {[info exists ::diff($top,currHighLight)] && \
            $::diff($top,currHighLight) >= 0} {
        $::widgets($top,wLine1) tag configure hl$::diff($top,currHighLight) \
    if {[info exists ::eskil($top,currHighLight)] && \
            $::eskil($top,currHighLight) >= 0} {
        $::widgets($top,wLine1) tag configure hl$::eskil($top,currHighLight) \
                -background {}
        $::widgets($top,wLine2) tag configure hl$::diff($top,currHighLight) \
        $::widgets($top,wLine2) tag configure hl$::eskil($top,currHighLight) \
                -background {}
    }
    set ::diff($top,currHighLight) $n
    if {$::diff($top,currHighLight) < 0} {
        set ::diff($top,currHighLight) -1
    } elseif {$::diff($top,currHighLight) >= [llength $::diff($top,changes)]} {
        set ::diff($top,currHighLight) [llength $::diff($top,changes)]
    set ::eskil($top,currHighLight) $n
    if {$::eskil($top,currHighLight) < 0} {
        set ::eskil($top,currHighLight) -1
    } elseif {$::eskil($top,currHighLight) >= [llength $::eskil($top,changes)]} {
        set ::eskil($top,currHighLight) [llength $::eskil($top,changes)]
    } else {
        $::widgets($top,wLine1) tag configure hl$::diff($top,currHighLight) \
        $::widgets($top,wLine1) tag configure hl$::eskil($top,currHighLight) \
                -background yellow
        $::widgets($top,wLine2) tag configure hl$::diff($top,currHighLight) \
        $::widgets($top,wLine2) tag configure hl$::eskil($top,currHighLight) \
                -background yellow
    }
}

# Highlight a diff and scroll windows to it.
proc showDiff {top num} {
    highLightChange $top $num

    set change [lindex $::diff($top,changes) $::diff($top,currHighLight)]
    set change [lindex $::eskil($top,changes) $::eskil($top,currHighLight)]
    set line1 [lindex $change 0]

    if {$::diff($top,currHighLight) < 0} {
    if {$::eskil($top,currHighLight) < 0} {
        set line1 1.0
        set line2 1.0
    } elseif {$line1 eq ""} {
        set line1 end
        set line2 end
    } else {
        set line2 [expr {$line1 + [lindex $change 1]}]
1565
1566
1567
1568
1569
1570
1571
1572
1573


1574
1575
1576
1577
1578
1579
1580
1581
1582
1583
1584
1585
1586

1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600

1601
1602
1603
1604
1605
1606
1607

1608
1609
1610
1611

1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630

1631
1632
1633
1634

1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652

1653
1654
1655
1656
1657
1658
1659


1660
1661
1662
1663


1664
1665
1666
1667
1668
1669
1670
1671

1672
1673
1674

1675
1676

1677
1678
1679
1680
1681
1682
1683
1684

1685
1686

1687
1688
1689
1690
1691
1692
1693
1694
1695

1696
1697
1698
1699
1700
1701
1702
1703
1704

1705
1706
1707
1708
1709
1710
1711
1639
1640
1641
1642
1643
1644
1645


1646
1647
1648
1649
1650
1651
1652
1653
1654
1655
1656
1657
1658
1659

1660
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673

1674
1675
1676
1677
1678
1679
1680

1681
1682
1683
1684

1685
1686
1687
1688
1689
1690
1691
1692
1693
1694
1695
1696
1697
1698
1699
1700
1701
1702
1703

1704
1705
1706
1707

1708
1709
1710
1711
1712
1713
1714
1715
1716
1717
1718
1719
1720
1721
1722
1723
1724
1725

1726
1727
1728
1729
1730
1731


1732
1733
1734
1735


1736
1737
1738
1739
1740
1741
1742
1743
1744

1745
1746
1747

1748
1749

1750
1751
1752
1753
1754
1755
1756
1757

1758
1759

1760
1761
1762
1763
1764
1765
1766
1767
1768

1769
1770
1771
1772
1773
1774
1775
1776
1777

1778
1779
1780
1781
1782
1783
1784
1785







-
-
+
+












-
+













-
+






-
+



-
+


















-
+



-
+

















-
+





-
-
+
+


-
-
+
+







-
+


-
+

-
+







-
+

-
+








-
+








-
+







#####################################

# FIXA: Use snit to adapt text widget instead of using wcb
# include seeText in such a snidget.

# Clear Editing state
proc resetEdit {top} {
    set ::diff($top,leftEdit) 0
    set ::diff($top,rightEdit) 0
    set ::eskil($top,leftEdit) 0
    set ::eskil($top,rightEdit) 0
    $top.m.mt entryconfigure "Edit Mode" -state normal

    resetEditW $::widgets($top,wDiff1)
    resetEditW $::widgets($top,wDiff2)
}

# Clear Editing state for a Text widget
proc resetEditW {w} {
    $w tag configure padding -background {}
    $w edit reset
    $w configure -undo 0

    set ::diff($w,allowChange) all
    set ::eskil($w,allowChange) all

    wcb::callback $w before insert {}
    wcb::callback $w before delete {}
}

# Do not allow any editing
proc noEdit {top} {
    noEditW $::widgets($top,wDiff1)
    noEditW $::widgets($top,wDiff2)
}

# Do not allow any editing in a Text widget
proc noEditW {w} {
    set ::diff($w,allowChange) none
    set ::eskil($w,allowChange) none

    wcb::callback $w before insert [list TextInterceptInsert $w]
    wcb::callback $w before delete [list TextInterceptDelete $w]
}

proc TextInterceptInsert {w ow index str args} {
    if {$::diff($w,allowChange) eq "none"} {
    if {$::eskil($w,allowChange) eq "none"} {
        wcb::cancel
        return
    }
    if {$::diff($w,allowChange) eq "all"} return
    if {$::eskil($w,allowChange) eq "all"} return

    #wcb::cancel - Cancel a widget command
    #wcb::replace - Replace arguments of a widget command with new ones

    # Disallow all new lines
    if {[string first "\n" $str] >= 0} {
        wcb::cancel
        return
    }
    foreach {tag str2} $args {
        if {[string first "\n" $str2] >= 0} {
            wcb::cancel
            return
        }
    }
}

proc TextInterceptDelete {w ow from {to {}}} {
    if {$::diff($w,allowChange) eq "none"} {
    if {$::eskil($w,allowChange) eq "none"} {
        wcb::cancel
        return
    }
    if {$::diff($w,allowChange) eq "all"} return
    if {$::eskil($w,allowChange) eq "all"} return

    if {$to eq ""} {
        set to $from+1char
    }
    set text [$ow get $from $to]
    # Disallow all new lines
    if {[string first "\n" $text] >= 0} {
        wcb::cancel
        return
    }
}

# Turn on editing for a Text widget
proc turnOnEdit {w} {
    $w tag configure padding -background \#f0f0f0
    $w configure -undo 1

    set ::diff($w,allowChange) line
    set ::eskil($w,allowChange) line
}

# Turn on editing on sides where it has not been disallowed
proc allowEdit {top} {
    $top.m.mt entryconfigure "Edit Mode" -state disable
    if {$::diff($top,leftEdit) == 0} {
        set ::diff($top,leftEdit) 1
    if {$::eskil($top,leftEdit) == 0} {
        set ::eskil($top,leftEdit) 1
        turnOnEdit $::widgets($top,wDiff1)
    }
    if {$::diff($top,rightEdit) == 0} {
        set ::diff($top,rightEdit) 1
    if {$::eskil($top,rightEdit) == 0} {
        set ::eskil($top,rightEdit) 1
        turnOnEdit $::widgets($top,wDiff2)
    }
}

# Turn off editing on sides that do not correspond to a file
proc disallowEdit {top {side 0}} {
    if {$side == 0 || $side == 1} {
        set ::diff($top,leftEdit) -1
        set ::eskil($top,leftEdit) -1
    }
    if {$side == 0 || $side == 2} {
        set ::diff($top,rightEdit) -1
        set ::eskil($top,rightEdit) -1
    }
    if {$::diff($top,leftEdit) == -1 && $::diff($top,rightEdit) == -1} {
    if {$::eskil($top,leftEdit) == -1 && $::eskil($top,rightEdit) == -1} {
        $top.m.mt entryconfigure "Edit Mode" -state disabled
    }
}

# Ask if editing is allowed on a side
proc mayEdit {top side} {
    if {$side == 1} {
        return [expr {$::diff($top,leftEdit) == 1}]
        return [expr {$::eskil($top,leftEdit) == 1}]
    } else {
        return [expr {$::diff($top,rightEdit) == 1}]
        return [expr {$::eskil($top,rightEdit) == 1}]
    }
}

# Start an undo block in a bunch of text widgets
proc startUndoBlock {args} {
    foreach w $args {
        $w configure -autoseparators 0
        # Open up editing for copy functions
        set ::diff($w,allowChange) all
        set ::eskil($w,allowChange) all
    }
}

# End an undo block in a bunch of text widgets
proc endUndoBlock {args} {
    foreach w $args {
        $w configure -autoseparators 1
        $w edit separator
        set ::diff($w,allowChange) line
        set ::eskil($w,allowChange) line
    }
}

# Copy a block
proc copyBlock {top from first last} {
    set to [expr {3 - $from}]

1857
1858
1859
1860
1861
1862
1863
1864
1865


1866
1867
1868


1869
1870
1871
1872
1873
1874
1875
1931
1932
1933
1934
1935
1936
1937


1938
1939
1940


1941
1942
1943
1944
1945
1946
1947
1948
1949







-
-
+
+

-
-
+
+







    $m add command -label "Save File" -command [list saveFile $top $n]

    return 0
}

proc saveFile {top side} {
    if {$side == 1} {
        if {!$::diff($top,leftEdit)} return
        set fileName $::diff($top,leftFile)
        if {!$::eskil($top,leftEdit)} return
        set fileName $::eskil($top,leftFile)
    } else {
        if {!$::diff($top,rightEdit)} return
        set fileName $::diff($top,rightFile)
        if {!$::eskil($top,rightEdit)} return
        set fileName $::eskil($top,rightFile)
    }

    set w $::widgets($top,wDiff$side)

    # Confirm dialog
    set apa [tk_messageBox -parent $top -icon question \
            -title "Overwrite file" -type yesnocancel -message \
1932
1933
1934
1935
1936
1937
1938







1939

1940
1941

1942
1943
1944
1945
1946
1947
1948
1949
1950
1951
1952
1953
1954
1955
1956
1957




1958
1959
1960
1961
1962
1963
1964
1965
1966
1967
1968




1969
1970
1971
1972
1973
1974
1975
1976
1977
1978




1979
1980
1981
1982
1983
1984
1985
1986
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
2020
2021
2022
2023
2024
2025


2026
2027
2028
2029
2030
2031
2032
2033
2034
2035
2036
2037
2038
2039
2040
2041


2042
2043
2044
2045
2046
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
2081

2082
2083
2084
2085


2086
2087
2088
2089
2090
2091
2092
2093
2094


2095
2096
2097
2098
2099
2100
2101
2102
2103
2104
2105
2106
2107
2108
2109
2110
2111
2112
2113
2114
2115
2116
2117
2118
2119
2120






2121
2122
2123
2124
2125
2126
2127
2128






2129
2130

2131
2132
2133
2134
2135
2136
2137
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019

2020
2021

2022
2023
2024
2025
2026
2027
2028
2029
2030
2031
2032
2033
2034




2035
2036
2037
2038
2039
2040
2041
2042
2043
2044
2045




2046
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
2081
2082
2083
2084
2085
2086
2087
2088

2089
2090
2091
2092
2093
2094
2095
2096


2097
2098
2099
2100
2101
2102
2103
2104


2105
2106
2107
2108
2109
2110
2111
2112
2113
2114
2115
2116
2117
2118
2119
2120


2121
2122
2123
2124
2125
2126
2127
2128
2129

2130
2131
2132
2133


2134
2135
2136
2137
2138
2139
2140
2141
2142
2143
2144
2145
2146

2147
2148
2149
2150


2151
2152
2153
2154
2155
2156
2157

2158
2159
2160
2161

2162
2163
2164


2165
2166
2167
2168
2169
2170
2171
2172
2173


2174
2175
2176
2177
2178
2179
2180
2181
2182
2183
2184
2185
2186
2187
2188
2189
2190
2191
2192
2193
2194
2195






2196
2197
2198
2199
2200
2201
2202
2203






2204
2205
2206
2207
2208
2209
2210

2211
2212
2213
2214
2215
2216
2217
2218







+
+
+
+
+
+
+
-
+

-
+












-
-
-
-
+
+
+
+







-
-
-
-
+
+
+
+






-
-
-
-
+
+
+
+







-
-
-
-
+
+
+
+






-
-
-
-
-
-
+
+
+
+
+
+






-
+







-
-
+
+






-
-
+
+














-
-
+
+







-
+



-
-
+
+











-
+



-
-
+
+





-
+



-
+


-
-
+
+







-
-
+
+




















-
-
-
-
-
-
+
+
+
+
+
+


-
-
-
-
-
-
+
+
+
+
+
+

-
+







        }
    }
    return [file isdirectory $file]
}

# A wrapper for tk_getOpenFile
proc myOpenFile {args} {
    array set opts $args
    set isVfs 0
    if {[info exists opts(-initialdir)]} {
        if {[string match tclvfs* [file system $opts(-initialdir)]]} {
            set isVfs 1
        }
    }
    # When in tutorial mode, make sure the Tcl file dialog is used
    # When in a vfs, make sure the Tcl file dialog is used
    # to be able to access the files in a starkit.
    if {[info exists ::diff(tutorial)] && $::diff(tutorial)} {
    if {$isVfs} {
        # Only do this if tk_getOpenFile is not a proc.
        if {[info procs tk_getOpenFile] eq ""} {
            # If there is any problem, call the real one
            if {![catch {set res [::tk::dialog::file:: open {*}$args]}]} {
                return $res
            }
        }
    }
    return [tk_getOpenFile {*}$args]
}

proc doOpenLeft {top {forget 0}} {
    if {!$forget && [info exists ::diff($top,leftDir)]} {
        set initDir $::diff($top,leftDir)
    } elseif {[info exists ::diff($top,rightDir)]} {
        set initDir $::diff($top,rightDir)
    if {!$forget && [info exists ::eskil($top,leftDir)]} {
        set initDir $::eskil($top,leftDir)
    } elseif {[info exists ::eskil($top,rightDir)]} {
        set initDir $::eskil($top,rightDir)
    } else {
        set initDir [pwd]
    }

    set apa [myOpenFile -title "Select left file" -initialdir $initDir \
            -parent $top]
    if {$apa != ""} {
        set ::diff($top,leftDir) [file dirname $apa]
        set ::diff($top,leftFile) $apa
        set ::diff($top,leftLabel) $apa
        set ::diff($top,leftOK) 1
        set ::eskil($top,leftDir) [file dirname $apa]
        set ::eskil($top,leftFile) $apa
        set ::eskil($top,leftLabel) $apa
        set ::eskil($top,leftOK) 1
        return 1
    }
    return 0
}

proc doOpenRight {top {forget 0}} {
    if {!$forget && [info exists ::diff($top,rightDir)]} {
        set initDir $::diff($top,rightDir)
    } elseif {[info exists ::diff($top,leftDir)]} {
        set initDir $::diff($top,leftDir)
    if {!$forget && [info exists ::eskil($top,rightDir)]} {
        set initDir $::eskil($top,rightDir)
    } elseif {[info exists ::eskil($top,leftDir)]} {
        set initDir $::eskil($top,leftDir)
    } else {
        set initDir [pwd]
    }

    set apa [myOpenFile -title "Select right file" -initialdir $initDir \
            -parent $top]
    if {$apa != ""} {
        set ::diff($top,rightDir) [file dirname $apa]
        set ::diff($top,rightFile) $apa
        set ::diff($top,rightLabel) $apa
        set ::diff($top,rightOK) 1
        set ::eskil($top,rightDir) [file dirname $apa]
        set ::eskil($top,rightFile) $apa
        set ::eskil($top,rightLabel) $apa
        set ::eskil($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)
    if {$::eskil($top,ancestorFile) ne ""} {
        set initDir [file dirname $::eskil($top,ancestorFile)]
    } elseif {[info exists ::eskil($top,leftDir)]} {
        set initDir $::eskil($top,leftDir)
    } elseif {[info exists ::eskil($top,rightDir)]} {
        set initDir $::eskil($top,rightDir)
    } else {
        set initDir [pwd]
    }
    set apa [myOpenFile -title "Select ancestor file" -initialdir $initDir \
            -parent $top]
    if {$apa != ""} {
        set ::diff($top,ancestorFile) $apa
        set ::eskil($top,ancestorFile) $apa
        return 1
    }
    return 0
}

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

proc openRight {top} {
    if {[doOpenRight $top]} {
        set ::diff($top,mode) ""
        set ::diff($top,mergeFile) ""
        set ::eskil($top,mode) ""
        set ::eskil($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) ""
        startConflictDiff $top $::eskil($top,rightFile)
        set ::eskil($top,mergeFile) ""
        doDiff $top
    }
}

proc openPatch {top} {
    global Pref
    if {[doOpenLeft $top]} {
        set ::diff($top,mode) "patch"
        set ::eskil($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) ""
        set ::eskil($top,patchFile) $::eskil($top,leftFile)
        set ::eskil($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 ::eskil($top,mode) "patch"
    set ::Pref(ignore) " "
    set ::Pref(nocase) 0
    set ::Pref(noempty) 0 
    set ::diff($top,patchFile) ""
    set ::diff($top,patchData) $sel
    set ::eskil($top,patchFile) ""
    set ::eskil($top,patchData) $sel
    doDiff $top
}

proc openRev {top} {
    if {[doOpenRight $top]} {
        set rev [detectRevSystem $::diff($top,rightFile)]
        set rev [detectRevSystem $::eskil($top,rightFile)]
        if {$rev eq ""} {
            tk_messageBox -icon error -title "Eskil Error" -message \
                    "Could not figure out which revison control system\
                    \"$::diff($top,rightFile)\" is under." -type ok
                    \"$::eskil($top,rightFile)\" is under." -type ok
            return
        }
        startRevMode $top $rev $::diff($top,rightFile)
        set ::diff($top,mergeFile) ""
        startRevMode $top $rev $::eskil($top,rightFile)
        set ::eskil($top,mergeFile) ""
        doDiff $top
    }
}

proc openBoth {top forget} {
    if {[doOpenLeft $top]} {
        if {[doOpenRight $top $forget]} {
            set ::diff($top,mode) ""
            set ::diff($top,mergeFile) ""
            set ::eskil($top,mode) ""
            set ::eskil($top,mergeFile) ""
            doDiff $top
        }
    }
}

# File drop using TkDnd
proc fileDrop {top side files} {
    # FIXA: Maybe single drop during rev mode should stay in rev mode?
    # Dropping two files mean set both
    if {[llength $files] >= 2} {
        set leftFile [lindex $files 0]
        set rightFile [lindex $files 1]
    } elseif {$side eq "left"} {
        set leftFile [lindex $files 0]
        set rightFile ""
    } else {
        set leftFile ""
        set rightFile [lindex $files 0]
    }
    if {$leftFile ne ""} {
        set ::diff($top,leftDir) [file dirname $leftFile]
        set ::diff($top,leftFile) $leftFile
        set ::diff($top,leftLabel) $leftFile
        set ::diff($top,leftOK) 1
        set ::diff($top,mode) ""
        set ::diff($top,mergeFile) ""
        set ::eskil($top,leftDir) [file dirname $leftFile]
        set ::eskil($top,leftFile) $leftFile
        set ::eskil($top,leftLabel) $leftFile
        set ::eskil($top,leftOK) 1
        set ::eskil($top,mode) ""
        set ::eskil($top,mergeFile) ""
    }
    if {$rightFile ne ""} {
        set ::diff($top,rightDir) [file dirname $rightFile]
        set ::diff($top,rightFile) $rightFile
        set ::diff($top,rightLabel) $rightFile
        set ::diff($top,rightOK) 1
        set ::diff($top,mode) ""
        set ::diff($top,mergeFile) ""
        set ::eskil($top,rightDir) [file dirname $rightFile]
        set ::eskil($top,rightFile) $rightFile
        set ::eskil($top,rightLabel) $rightFile
        set ::eskil($top,rightOK) 1
        set ::eskil($top,mode) ""
        set ::eskil($top,mergeFile) ""
    }
    if {$::diff($top,leftOK) && $::diff($top,rightOK)} {
    if {$::eskil($top,leftOK) && $::eskil($top,rightOK)} {
        doDiff $top
    }
}

#####################################
# GUI stuff
#####################################
2195
2196
2197
2198
2199
2200
2201
2202

2203
2204
2205
2206

2207
2208
2209

2210
2211
2212
2213
2214
2215
2216
2217

2218
2219
2220







2221
2222
2223
2224
2225


2226
2227

2228
2229

2230
2231
2232
2233

2234
2235
2236
2237
2238

2239
2240
2241
2242


2243
2244
2245

2246
2247
2248
2249
2250
2251
2252
2253
2254
2255
2256

2257
2258
2259
2260
2261
2262
2263
2264

2265
2266
2267

2268
2269
2270
2271


2272
2273
2274
2275
2276
2277
2278
2279
2280
2281
2282
2283
2284
2285
















































































































2286
2287
2288
2289
2290
2291
2292
2293
2294
2295
2296

2297
2298
2299
2300
2301
2302
2303

2304
2305
2306

2307
2308
2309
2310
2311
2312
2313
2314
2315
2316
2317
2318
2319

2320
2321
2322


2323
2324
2325


2326
2327
2328
2329
2330

2331
2332
2333

2334
2335
2336
2337
2338
2339


2340
2341
2342
2343
2344

2345
2346
2347
2348
2349
2350
2351
2352
2353
2354
2355
2356
2357
2358

2359
2360
2361
2362
2363
2364
2365
2366
2367

2368
2369

2370
2371
2372
2373
2374
2375
2376
2377
2378

2379
2380
2381
2382
2383
2384
2385
2386
2387
2388
2389
2390
2391
2392

2393
2394

2395
2396
2397
2398
2399
2400
2401
2276
2277
2278
2279
2280
2281
2282

2283
2284
2285
2286

2287
2288
2289

2290
2291
2292
2293
2294
2295
2296
2297

2298
2299
2300
2301
2302
2303
2304
2305
2306
2307
2308
2309
2310
2311


2312
2313
2314

2315


2316
2317
2318
2319

2320
2321

2322
2323

2324
2325
2326


2327
2328


2329
2330
2331
2332
2333
2334
2335
2336
2337
2338
2339
2340
2341
2342
2343
2344
2345
2346
2347
2348
2349

2350
2351
2352

2353
2354
2355


2356
2357
2358
2359
2360
2361
2362
2363
2364
2365
2366
2367
2368
2369
2370
2371
2372
2373
2374
2375
2376
2377
2378
2379
2380
2381
2382
2383
2384
2385
2386
2387
2388
2389
2390
2391
2392
2393
2394
2395
2396
2397
2398
2399
2400
2401
2402
2403
2404
2405
2406
2407
2408
2409
2410
2411
2412
2413
2414
2415
2416
2417
2418
2419
2420
2421
2422
2423
2424
2425
2426
2427
2428
2429
2430
2431
2432
2433
2434
2435
2436
2437
2438
2439
2440
2441
2442
2443
2444
2445
2446
2447
2448
2449
2450
2451
2452
2453
2454
2455
2456
2457
2458
2459
2460
2461
2462
2463
2464
2465
2466
2467
2468
2469
2470
2471
2472
2473
2474
2475
2476
2477
2478
2479
2480
2481
2482
2483
2484
2485
2486
2487
2488
2489
2490
2491
2492
2493

2494
2495
2496
2497
2498
2499
2500

2501
2502
2503

2504
2505
2506
2507
2508
2509
2510
2511
2512
2513
2514
2515
2516

2517
2518


2519
2520
2521


2522
2523
2524
2525
2526
2527

2528
2529
2530

2531
2532
2533
2534
2535


2536
2537
2538
2539
2540
2541

2542
2543
2544
2545
2546
2547
2548
2549
2550
2551
2552
2553
2554
2555

2556
2557
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
2585
2586
2587
2588
2589

2590
2591

2592
2593
2594
2595
2596
2597
2598
2599







-
+



-
+


-
+







-
+



+
+
+
+
+
+
+



-
-
+
+

-
+
-
-
+



-
+

-


-
+


-
-
+
+
-
-

+











+







-
+


-
+


-
-
+
+














+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+










-
+






-
+


-
+












-
+

-
-
+
+

-
-
+
+




-
+


-
+




-
-
+
+




-
+













-
+








-
+

-
+








-
+













-
+

-
+







proc disableAlign {top} {
    eval $::widgets($top,disableAlignCmd)
}

# Remove one or all alignment pairs
proc clearAlign {top {leftline {}}} {
    if {$leftline == ""} {
        set ::diff($top,aligns) {}
        set ::eskil($top,aligns) {}
    } else {
        set i 0
        while 1 {
            set i [lsearch -integer -start $i $::diff($top,aligns) $leftline]
            set i [lsearch -integer -start $i $::eskil($top,aligns) $leftline]
            if {$i < 0} break
            if {($i % 2) == 0} {
                set ::diff($top,aligns) [lreplace $::diff($top,aligns) \
                set ::eskil($top,aligns) [lreplace $::eskil($top,aligns) \
                        $i [+ $i 1]]
                break
            }
            incr i
        }
    }

    if {[llength $::diff($top,aligns)] == 0} {
    if {[llength $::eskil($top,aligns)] == 0} {
        disableAlign $top
    }
}

proc NoMarkAlign {top} {
    unset -nocomplain ::eskil($top,align1)
    unset -nocomplain ::eskil($top,align2)
    unset -nocomplain ::eskil($top,aligntext1)
    unset -nocomplain ::eskil($top,aligntext2)
}

# Mark a line as aligned.
proc markAlign {top n line text} {
    set ::diff($top,align$n) $line
    set ::diff($top,aligntext$n) $text
    set ::eskil($top,align$n) $line
    set ::eskil($top,aligntext$n) $text

    if {[info exists ::diff($top,align1)] && [info exists ::diff($top,align2)]} {
    if {[info exists ::eskil($top,align1)] && [info exists ::eskil($top,align2)]} {
        set level 2
        if {![string equal $::diff($top,aligntext1) $::diff($top,aligntext2)]} {
        if {![string equal $::eskil($top,aligntext1) $::eskil($top,aligntext2)]} {
            set apa [tk_messageBox -icon question -title "Align" -type yesno \
                    -message "Those lines are not equal.\nReally align them?"]
            if {$apa != "yes"} {
                return
                return 0
            }
            set level 3
        }

        lappend ::diff($top,aligns) $::diff($top,align1) $::diff($top,align2)
        lappend ::eskil($top,aligns) $::eskil($top,align1) $::eskil($top,align2)
        enableAlign $top

        unset ::diff($top,align1)
        unset ::diff($top,align2)
        NoMarkAlign $top
        return 1
        unset ::diff($top,aligntext1)
        unset ::diff($top,aligntext2)
    }
    return 0
}

# Called by popup menus over row numbers to add command for alignment.
# Returns 1 if nothing was added.
proc alignMenu {m top n x y} {
    # Get the row that was clicked
    set w $::widgets($top,wLine$n)
    set index [$w index @$x,$y]
    set row [lindex [split $index "."] 0]

    set data [$w get $row.0 $row.end]
    # Must be a line number
    if {![regexp {\d+} $data line]} {
        return 1
    }
    set text [$::widgets($top,wDiff$n) get $row.0 $row.end]

    set other [expr {$n == 1 ? 2 : 1}]
    set cmd [list markAlign $top $n $line $text]
    if {![info exists ::diff($top,align$other)]} {
    if {![info exists ::eskil($top,align$other)]} {
        set label "Mark line for alignment"
    } else {
        set label "Align with line $::diff($top,align$other) on other side"
        set label "Align with line $::eskil($top,align$other) on other side"
    }

    if {[info exists ::diff($top,aligns)]} {
        foreach {align1 align2} $::diff($top,aligns) {
    if {[info exists ::eskil($top,aligns)]} {
        foreach {align1 align2} $::eskil($top,aligns) {
            if {$n == 1 && $line == $align1} {
                set label "Remove alignment with line $align2"
                set cmd [list clearAlign $top $align1]
            } elseif {$n == 2 && $line == $align2} {
                set label "Remove alignment with line $align1"
                set cmd [list clearAlign $top $align1]
            }
        }
    }

    $m add command -label $label -command $cmd

    return 0
}

# Set up bindings to allow setting alignment using drag
proc SetupAlignDrag {top left right} {
    bind $left <ButtonPress-1> [list startAlignDrag $top 1 %x %y %X %Y]\;break
    bind $left <B1-Motion> [list motionAlignDrag $top 1 0 %x %y %X %Y]\;break
    bind $left <Shift-B1-Motion> [list motionAlignDrag $top 1 1 %x %y %X %Y]\;break
    bind $left <ButtonRelease-1> [list endAlignDrag $top 1 %x %y %X %Y]\;break
    bind $left <B1-Leave> break
    bind $right <ButtonPress-1> [list startAlignDrag $top 2 %x %y %X %Y]\;break
    bind $right <B1-Motion> [list motionAlignDrag $top 2 0 %x %y %X %Y]\;break
    bind $right <Shift-B1-Motion> [list motionAlignDrag $top 2 1 %x %y %X %Y]\;break
    bind $right <ButtonRelease-1> [list endAlignDrag $top 2 %x %y %X %Y]\;break
    bind $right <B1-Leave> break
}

# Button has been pressed over line window
proc startAlignDrag {top n x y X Y} {
    # Get the row that was clicked
    set w $::widgets($top,wLine$n)
    set index [$w index @$x,$y]
    set row [lindex [split $index "."] 0]

    set data [$w get $row.0 $row.end]
    set ::eskil($top,alignDrag,state) none
    # Must be a line number
    if {![regexp {\d+} $data line]} {
        return 1
    }
    # Set up information about start of drag
    set text [$::widgets($top,wDiff$n) get $row.0 $row.end]
    set other [expr {$n == 1 ? 2 : 1}]
    set ::eskil($top,alignDrag,X) $X
    set ::eskil($top,alignDrag,Y) $Y
    set ::eskil($top,alignDrag,from) $n
    set ::eskil($top,alignDrag,line$n) $line
    set ::eskil($top,alignDrag,text$n) $text
    set ::eskil($top,alignDrag,line$other) "?"
    set ::eskil($top,alignDrag,state) press
}

# Mouse moves with button down
proc motionAlignDrag {top n shift x y X Y} {
    if {$::eskil($top,alignDrag,state) eq "press"} {
        # Have we moved enough to call it dragging?
        set dX [expr {abs($X - $::eskil($top,alignDrag,X))}]
        set dY [expr {abs($Y - $::eskil($top,alignDrag,Y))}]
        if {$dX + $dY > 3} {
            # Start a drag action
            set w $top.alignDrag
            destroy $w
            toplevel $w
            wm overrideredirect $w 1
            label $w.l -borderwidth 1 -relief solid -justify left
            pack $w.l
            set ::eskil($top,alignDrag,W) $w
            set ::eskil($top,alignDrag,state) "drag"
        }
    }
    if {$::eskil($top,alignDrag,state) eq "drag"} {
        set w $::eskil($top,alignDrag,W)
        # Move drag label with cursor
        wm geometry $w +[expr {$X + 1}]+[expr {$Y + 1}]
        
        set n $::eskil($top,alignDrag,from)
        set other [expr {$n == 1 ? 2 : 1}]
        set w2 $::widgets($top,wLine$other)
        # Are we over the other line window?
        if {[winfo containing $X $Y] eq $w2} {
            set x [expr {$X - [winfo rootx $w2]}]
            set y [expr {$Y - [winfo rooty $w2]}]
            set index [$w2 index @$x,$y]
            set row [lindex [split $index "."] 0]
            set data [$w2 get $row.0 $row.end]
            if {![regexp {\d+} $data line]} {
                set ::eskil($top,alignDrag,line$other) "?"
            } else {
                set ::eskil($top,alignDrag,line$other) $line
                set text [$::widgets($top,wDiff$other) get $row.0 $row.end]
                set ::eskil($top,alignDrag,text$other) $text
            }
        } else {
            set ::eskil($top,alignDrag,line$other) "?"
        }
        set txt "Align Left $::eskil($top,alignDrag,line1)"
        append txt "\nwith Right $::eskil($top,alignDrag,line2)"
        set ::eskil($top,alignDrag,shift) $shift
        if {$shift} {
            append txt "\nAnd Redo Diff"
        }
        $w.l configure -text $txt
    }
}

# Button has been released
proc endAlignDrag {top n x y X Y} {
    if {$::eskil($top,alignDrag,state) eq "drag"} {
        destroy $::eskil($top,alignDrag,W)
        # Are both line numbers valid? I.e. is this a full align operation?
        if {$::eskil($top,alignDrag,line1) ne "?" && \
                $::eskil($top,alignDrag,line2) ne "?"} {
            NoMarkAlign $top
            markAlign $top 1 $::eskil($top,alignDrag,line1) \
                    $::eskil($top,alignDrag,text1)
            set marked [markAlign $top 2 $::eskil($top,alignDrag,line2) \
                    $::eskil($top,alignDrag,text2)]
            if {$::eskil($top,alignDrag,shift) && $marked} {
                redoDiff $top
            }
        }
    }
    set ::eskil($top,alignDrag,state) none
}

###################
# Diff highlighting
###################

proc hlSelect {top hl} {
    highLightChange $top $hl
}

proc hlSeparate {top n hl} {
    set ::diff($top,separate$n) $hl
    set ::eskil($top,separate$n) $hl
    set wd $::widgets($top,wDiff$n)
    set wl $::widgets($top,wLine$n)

    if {$hl eq ""} {
        set range [$wd tag ranges sel]
    } else {
        set range [$wl tag ranges hl$::diff($top,separate$n)]
        set range [$wl tag ranges hl$::eskil($top,separate$n)]
    }
    set text [$wd get {*}$range]
    set ::diff($top,separatetext$n) $text
    set ::eskil($top,separatetext$n) $text

    # Get the lines involved in the display
    set from [lindex $range 0]
    set to   [lindex $range 1]
    lassign [split $from "."] froml fromi
    lassign [split $to   "."] tol   toi
    if {$toi == 0} {incr tol -1}
    # Get the corresponding lines in the file
    set t [$wl get $froml.0 $tol.end]
    set lines [lsort -integer [regexp -all -inline {\d+} $t]]
    set froml [lindex $lines 0]
    set tol [lindex $lines end]
    set ::diff($top,separatelines$n) [list $froml $tol]
    set ::eskil($top,separatelines$n) [list $froml $tol]

    if {[info exists ::diff($top,separate1)] && \
            [info exists ::diff($top,separate2)]} {
    if {[info exists ::eskil($top,separate1)] && \
            [info exists ::eskil($top,separate2)]} {
        if {1} {
            cloneDiff $top [concat $::diff($top,separatelines1) \
                    $::diff($top,separatelines2)]
            cloneDiff $top [concat $::eskil($top,separatelines1) \
                    $::eskil($top,separatelines2)]
        } else {
            set f1 [tmpFile]
            set f2 [tmpFile]
            set ch [open $f1 w]
            puts $ch $::diff($top,separatetext1)
            puts $ch $::eskil($top,separatetext1)
            close $ch
            set ch [open $f2 w]
            puts $ch $::diff($top,separatetext2)
            puts $ch $::eskil($top,separatetext2)
            close $ch

            newDiff $f1 $f2
        }
        unset ::diff($top,separate1)
        unset ::diff($top,separate2)
        unset ::eskil($top,separate1)
        unset ::eskil($top,separate2)
    }
}

proc hlPopup {top n hl X Y x y} {
    if {[info exists ::diff($top,nopopup)] && $::diff($top,nopopup)} return
    if {[info exists ::eskil($top,nopopup)] && $::eskil($top,nopopup)} return
    destroy .lpm
    menu .lpm

    if {![editMenu .lpm $top $n $hl $x $y]} {
        .lpm add separator
    }

    if {$hl != ""} {
        .lpm add command -label "Select" \
                -command [list hlSelect $top $hl]
    }

    set other [expr {$n == 1 ? 2 : 1}]
    if {![info exists ::diff($top,separate$other)]} {
    if {![info exists ::eskil($top,separate$other)]} {
        set label "Mark for Separate Diff"
    } else {
        set label "Separate Diff"
    }

    .lpm add command -label $label -command [list hlSeparate $top $n $hl]
    alignMenu .lpm $top $n $x $y

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

    return
}

# This is called when right clicking over the line numbers which are not
# marked for changes
proc rowPopup {w X Y x y} {
    set top [winfo toplevel $w]
    if {[info exists ::diff($top,nopopup)] && $::diff($top,nopopup)} return
    if {[info exists ::eskil($top,nopopup)] && $::eskil($top,nopopup)} return
    destroy .lpm
    menu .lpm

    regexp {(\d+)\D*$} $w -> n
    set tmp1 [editMenu  .lpm $top $n "" $x $y]
    if {!$tmp1} {.lpm add separator}
    set tmp2 [alignMenu .lpm $top $n $x $y]
    if {$tmp1 && $tmp2} {
        # Nothing in the menu
        return
    }
    if {!$tmp1 && $tmp2} {.lpm delete last}

    set ::diff($top,nopopup) 1
    set ::eskil($top,nopopup) 1
    tk_popup .lpm $X $Y
    after idle [list after 1 [list set ::diff($top,nopopup) 0]]
    after idle [list after 1 [list set ::eskil($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"
2517
2518
2519
2520
2521
2522
2523
2524

2525
2526
2527
2528
2529
2530
2531
2715
2716
2717
2718
2719
2720
2721

2722
2723
2724
2725
2726
2727
2728
2729







-
+







    font configure myfont -size $Pref(fontsize) -family $Pref(fontfamily)
}

# Change color settings
proc applyColor {} {
    global dirdiff Pref

    foreach top $::diff(diffWindows) {
    foreach top $::eskil(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) \
2581
2582
2583
2584
2585
2586
2587
2588
2589
2590
2591
2592
2593
2594
2595
2596









2597
2598
2599
2600
2601
2602
2603
2604
2605
2606
2607
2608
2609
2610
2611
2612
2613










2614
2615
2616
2617
2618
2619
2620
2621
2622
2623
2624
2625
2626
2627

2628
2629

2630
2631
2632

2633
2634
2635
2636
2637
2638
2639
2779
2780
2781
2782
2783
2784
2785









2786
2787
2788
2789
2790
2791
2792
2793
2794
2795
2796
2797
2798
2799
2800
2801










2802
2803
2804
2805
2806
2807
2808
2809
2810
2811
2812
2813
2814
2815
2816
2817
2818
2819
2820
2821
2822
2823
2824

2825
2826

2827
2828
2829

2830
2831
2832
2833
2834
2835
2836
2837







-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+







-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+













-
+

-
+


-
+







	uplevel \#0 "trace variable $var w \
		{after idle {$w xview end} ;#}"
    }
}

# 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) ""
    set ::eskil($top,leftOK) 0
    set ::eskil($top,rightOK) 0
    set ::eskil($top,mode) ""
    set ::eskil($top,printFile) ""
    set ::eskil($top,mergeFile) ""
    set ::eskil($top,ancestorFile) ""
    set ::eskil($top,conflictFile) ""
    set ::eskil($top,limitlines) 0
    set ::eskil($top,plugin) ""
}

# Create a new diff window and diff two files
proc newDiff {file1 file2 {range {}}} {
    set top [makeDiffWin]
    update

    set ::diff($top,leftDir) [file dirname $file1]
    set ::diff($top,leftFile) $file1
    set ::diff($top,leftLabel) $file1
    set ::diff($top,leftOK) 1
    set ::diff($top,rightDir) [file dirname $file2]
    set ::diff($top,rightFile) $file2
    set ::diff($top,rightLabel) $file2
    set ::diff($top,rightOK) 1
    set ::diff($top,mode) ""
    set ::diff($top,range) $range
    set ::eskil($top,leftDir) [file dirname $file1]
    set ::eskil($top,leftFile) $file1
    set ::eskil($top,leftLabel) $file1
    set ::eskil($top,leftOK) 1
    set ::eskil($top,rightDir) [file dirname $file2]
    set ::eskil($top,rightFile) $file2
    set ::eskil($top,rightLabel) $file2
    set ::eskil($top,rightOK) 1
    set ::eskil($top,mode) ""
    set ::eskil($top,range) $range
    wm deiconify $top
    raise $top
    update
    doDiff $top
    return $top
}


# Create a new diff window equal to another, except for possibly a range
proc cloneDiff {other {range {}}} {
    set top [makeDiffWin]
    update

    foreach item [array names ::diff $other,*] {
    foreach item [array names ::eskil $other,*] {
        regsub {^[^,]*,} $item {} item
        set ::diff($top,$item) $::diff($other,$item)
        set ::eskil($top,$item) $::eskil($other,$item)
    }
    if {[llength $range] != 0} {
        set ::diff($top,range) $range
        set ::eskil($top,range) $range
    }
    wm deiconify $top
    raise $top
    update
    doDiff $top
}

2653
2654
2655
2656
2657
2658
2659
2660
2661


2662
2663
2664
2665
2666
2667
2668
2851
2852
2853
2854
2855
2856
2857


2858
2859
2860
2861
2862
2863
2864
2865
2866







-
-
+
+







    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 $::diff(topDiffCnt)
        if {[info exists ::eskil(topDiffCnt)]} {
            set t $::eskil(topDiffCnt)
        } else {
            set t 0
        }
        while {[winfo exists .diff$t]} {
            incr t
        }
        set top .diff$t
2844
2845
2846
2847
2848
2849
2850
2851

2852
2853
2854
2855
2856
2857

2858
2859
2860
2861
2862
2863
2864
3042
3043
3044
3045
3046
3047
3048

3049
3050
3051
3052
3053
3054

3055
3056
3057
3058
3059
3060
3061
3062







-
+





-
+







                -command [list makeDocWin $file] -underline 0
    }
    $top.m.help add separator
    $top.m.help add command -label "About" -command makeAboutWin -underline 0

    ttk::label $top.lr1 -text "Rev 1"
    addBalloon $top.lr1 "Revision number for version diff."
    ttk::entryX $top.er1 -width 12 -textvariable diff($top,doptrev1)
    ttk::entryX $top.er1 -width 12 -textvariable ::eskil($top,doptrev1)
    set ::widgets($top,rev1) $top.er1
    bind $top.er1 <Key-Return> [list redoDiff $top]

    ttk::label $top.lr2 -text "Rev 2"
    addBalloon $top.lr2 "Revision number for version diff."
    ttk::entryX $top.er2 -width 12 -textvariable diff($top,doptrev2)
    ttk::entryX $top.er2 -width 12 -textvariable ::eskil($top,doptrev2)
    set ::widgets($top,rev2) $top.er2
    bind $top.er2 <Key-Return> [list redoDiff $top]

    ttk::button $top.bcm -text Commit -command [list revCommit $top] \
            -state disabled -underline 0
    set ::widgets($top,commit) $top.bcm
    ttk::button $top.blg -text Log -command [list revLog $top] \
2874
2875
2876
2877
2878
2879
2880
2881
2882


2883
2884
2885
2886
2887
2888
2889
3072
3073
3074
3075
3076
3077
3078


3079
3080
3081
3082
3083
3084
3085
3086
3087







-
-
+
+







    bind $top <Alt-p> [list findDiff $top -1]
    bind $top <Alt-c> [list revCommit $top]
    bind $top <Alt-l> [list revLog $top]

    catch {font delete myfont}
    font create myfont -family $Pref(fontfamily) -size $Pref(fontsize)

    fileLabel $top.l1 -textvariable diff($top,leftLabel)
    fileLabel $top.l2 -textvariable diff($top,rightLabel)
    fileLabel $top.l1 -textvariable ::eskil($top,leftLabel)
    fileLabel $top.l2 -textvariable ::eskil($top,rightLabel)

    ttk::frame $top.ft1 -borderwidth 2 -relief sunken
    text $top.ft1.tl -height $Pref(lines) -width 5 -wrap none \
            -font myfont -borderwidth 0 -padx 0 -highlightthickness 0 \
            -takefocus 0
    text $top.ft1.tt -height $Pref(lines) -width $Pref(linewidth) -wrap none \
            -xscrollcommand [list $top.sbx1 set] \
2950
2951
2952
2953
2954
2955
2956

2957
2958
2959
2960
2961
2962
2963
3148
3149
3150
3151
3152
3153
3154
3155
3156
3157
3158
3159
3160
3161
3162







+







        bind $w <ButtonPress-3> "zoomRow %W %X %Y %x %y"
        bind $w <ButtonRelease-3> "unzoomRow %W"
    }
    foreach w [list $top.ft1.tl $top.ft2.tl] {
        $w tag configure align -underline 1
        bind $w <ButtonPress-3> "rowPopup %W %X %Y %x %y"
    }
    SetupAlignDrag $top $top.ft1.tl $top.ft2.tl

    grid $top.l1   $top.le -        $top.l2   -row 1 -sticky news
    grid $top.ft1  $map    $top.sby $top.ft2  -row 2 -sticky news
    grid $top.sbx1 $top.ls -        $top.sbx2 -row 3 -sticky news
    grid columnconfigure $top {0 3} -weight 1
    grid rowconfigure $top 2 -weight 1
    grid $map -pady [expr {[winfo reqwidth $top.sby] - 2}]
2975
2976
2977
2978
2979
2980
2981
2982
2983

2984
2985
2986
2987
2988
2989
2990

2991
2992
2993
2994
2995
2996
2997




2998
2999
3000


3001
3002
3003


3004
3005
3006
3007
3008




3009
3010
3011
3012
3013
3014
3015
3174
3175
3176
3177
3178
3179
3180


3181







3182
3183
3184
3185




3186
3187
3188
3189
3190


3191
3192
3193


3194
3195
3196




3197
3198
3199
3200
3201
3202
3203
3204
3205
3206
3207







-
-
+
-
-
-
-
-
-
-
+



-
-
-
-
+
+
+
+

-
-
+
+

-
-
+
+

-
-
-
-
+
+
+
+







    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
        set dMenu [DebugMenu $top.m]
        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
        }
        $top.m.md add checkbutton -label "Wrap" -variable wrapstate \
        $dMenu add checkbutton -label "Wrap" -variable wrapstate \
                -onvalue char -offvalue none -command \
                "$top.ft1.tt configure -wrap \$wrapstate ;\
                $top.ft2.tt configure -wrap \$wrapstate"
        $top.m.md add command -label "Date Filter" \
                -command {set ::diff(filter) {^Date}}
        $top.m.md add separator
        $top.m.md add command -label "Reread Source" -underline 0 \
        $dMenu add command -label "Date Filter" \
                -command {set ::eskil(filter) {^Date}}
        $dMenu add separator
        $dMenu add command -label "Reread Source" -underline 0 \
                -command {EskilRereadSource}
        $top.m.md add separator
        $top.m.md add command -label "Redraw Window" \
        $dMenu add separator
        $dMenu add command -label "Redraw Window" \
                -command [list makeDiffWin $top]
        $top.m.md add separator
        $top.m.md add command -label "Normal Cursor" \
        $dMenu add separator
        $dMenu add command -label "Normal Cursor" \
                -command [list normalCursor $top]
        $top.m.md add separator
        $top.m.md add command -label "Evalstats" -command {evalstats}
        $top.m.md add command -label "_stats" -command {parray _stats}
        $top.m.md add command -label "Nuisance" -command [list makeNuisance \
        $dMenu add separator
        $dMenu add command -label "Evalstats" -command {evalstats}
        $dMenu add command -label "_stats" -command {parray _stats}
        $dMenu add command -label "Nuisance" -command [list makeNuisance \
                $top "It looks like you are trying out the debug menu."]
    }

    initDiffData $top
    return $top
}

3171
3172
3173
3174
3175
3176
3177
3178

3179
3180
3181
3182
3183
3184
3185
3363
3364
3365
3366
3367
3368
3369

3370
3371
3372
3373
3374
3375
3376
3377







-
+







        font configure tmpfont -size $TmpPref(fontsize)
    }
}

proc UpdateFontBox {lb} {
    $lb delete 0 end
    foreach {f fixed} $::FontCache {
        if {$fixed || !$::diff(fixedfont)} {
        if {$fixed || !$::eskil(fixedfont)} {
            $lb insert end $f
            if {[string equal -nocase $f $::Pref(fontfamily)]} {
                $lb selection set end
                $lb see end
            }
        }
    }
3210
3211
3212
3213
3214
3215
3216
3217
3218


3219
3220
3221
3222
3223
3224
3225
3402
3403
3404
3405
3406
3407
3408


3409
3410
3411
3412
3413
3414
3415
3416
3417







-
-
+
+







    ttk::labelframe .fo.ls -text "Size" -padding 3
    spinbox .fo.ls.sp -from 1 -to 30 -increment 1 -width 3 -state readonly \
            -textvariable TmpPref(fontsize) -command [list exampleFont $lb]
    pack .fo.ls.sp -fill both -expand 1

    ttk::label .fo.le -text "Example\n0Ooi1Il" -anchor w -font tmpfont \
            -width 1 -justify left
    if {![info exists ::diff(fixedfont)]} {set ::diff(fixedfont) 1}
    ttk::checkbutton .fo.cb -text "Fixed" -variable ::diff(fixedfont) \
    if {![info exists ::eskil(fixedfont)]} {set ::eskil(fixedfont) 1}
    ttk::checkbutton .fo.cb -text "Fixed" -variable ::eskil(fixedfont) \
            -command [list UpdateFontBox $lb]
    ttk::button .fo.bo -text "Ok"    -command "applyFont $lb ; destroy .fo"
    ttk::button .fo.ba -text "Apply" -command "applyFont $lb"
    ttk::button .fo.bc -text "Close" -command "destroy .fo"

    if {![info exists FontCache]} {
        set fam [lsort -dictionary [font families]]
3250
3251
3252
3253
3254
3255
3256
3257

3258
3259
3260
3261
3262



3263
3264
3265
3266
3267
3268
3269
3270
3271
3272
3273
3274
3275


3276
3277
3278
3279
3280


3281
3282
3283
3284
3285



3286
3287
3288
3289
3290

3291
3292
3293
3294
3295
3296
3297

3298
3299
3300
3301
3302
3303
3304
3305


3306
3307
3308
3309
3310
3311
3312
3313
3314
3315

3316
3317

3318
3319
3320
3321
3322
3323
3324
3325

3326
3327

3328
3329
3330
3331
3332
3333
3334
3335
3336
3337
3338
3339
3340
3341
3342
3343
3344
3345
3346
3347
3348


3349
3350

3351
3352
3353
3354
3355

3356
3357

3358
3359
3360

3361
3362

3363
3364
3365
3366
3367
3368
3369
3442
3443
3444
3445
3446
3447
3448

3449
3450
3451



3452
3453
3454
3455
3456
3457
3458
3459
3460
3461
3462
3463
3464
3465


3466
3467
3468
3469
3470


3471
3472
3473
3474



3475
3476
3477
3478
3479
3480
3481

3482
3483
3484
3485
3486
3487
3488

3489
3490
3491
3492
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
3527
3528
3529
3530
3531
3532
3533
3534
3535
3536
3537
3538


3539
3540
3541

3542
3543
3544
3545
3546

3547
3548

3549
3550
3551

3552
3553

3554
3555
3556
3557
3558
3559
3560
3561







-
+


-
-
-
+
+
+











-
-
+
+



-
-
+
+


-
-
-
+
+
+




-
+






-
+






-
-
+
+









-
+

-
+







-
+

-
+



















-
-
+
+

-
+




-
+

-
+


-
+

-
+







}

###########################
# Editor for ::Pref(regsub)
###########################

proc EditPrefRegsubOk {top w} {
    set exa $::diff($top,prefregexa)
    set exa $::eskil($top,prefregexa)

    set result {}
    for {set t 1} {[info exists ::diff($top,prefregexp$t)]} {incr t} {
        set RE $::diff($top,prefregexp$t)
        set Sub $::diff($top,prefregsub$t)
    for {set t 1} {[info exists ::eskil($top,prefregexp$t)]} {incr t} {
        set RE $::eskil($top,prefregexp$t)
        set Sub $::eskil($top,prefregsub$t)
        if {$RE eq ""} continue

        if {[catch {regsub -all -- $RE $exa $Sub _} err]} {
            return
        }
        lappend result $RE $Sub
    }

    set ::Pref(regsub) $result
    destroy $w

    array unset ::diff $top,prefregexp*
    array unset ::diff $top,prefregsub*
    array unset ::eskil $top,prefregexp*
    array unset ::eskil $top,prefregsub*
}

proc EditPrefRegsubUpdate {top args} {
    set exa $::diff($top,prefregexa)
    set exa2 $::diff($top,prefregexa2)
    set exa $::eskil($top,prefregexa)
    set exa2 $::eskil($top,prefregexa2)
    set ok $::widgets($top,prefRegsubOk)

    for {set t 1} {[info exists ::diff($top,prefregexp$t)]} {incr t} {
        set RE $::diff($top,prefregexp$t)
        set Sub $::diff($top,prefregsub$t)
    for {set t 1} {[info exists ::eskil($top,prefregexp$t)]} {incr t} {
        set RE $::eskil($top,prefregexp$t)
        set Sub $::eskil($top,prefregsub$t)

        if {$RE eq ""} continue

        if {[catch {regsub -all -- $RE $exa $Sub result} err]} {
            set ::diff($top,prefregresult) "$t ERROR: $err"
            set ::eskil($top,prefregresult) "$t ERROR: $err"
            $ok configure -state disabled
            return
        } else {
            set exa $result
        }
        if {[catch {regsub -all -- $RE $exa2 $Sub result} err]} {
            set ::diff($top,prefregresult2) "$t ERROR: $err"
            set ::eskil($top,prefregresult2) "$t ERROR: $err"
            $ok configure -state disabled
            return
        } else {
            set exa2 $result
        }
    }
    set ::diff($top,prefregresult2) $exa2
    set ::diff($top,prefregresult) $exa
    set ::eskil($top,prefregresult2) $exa2
    set ::eskil($top,prefregresult) $exa
    $ok configure -state normal
}

proc AddPrefRegsub {top parent} {
    for {set t 1} {[winfo exists $parent.fr$t]} {incr t} {
        #Empty
    }
    set w [ttk::frame $parent.fr$t -borderwidth 2 -relief groove -padding 3]
    ttk::label $w.l1 -text "Regexp:" -anchor "w"
    ttk::entryX $w.e1 -textvariable ::diff($top,prefregexp$t) -width 60
    ttk::entryX $w.e1 -textvariable ::eskil($top,prefregexp$t) -width 60
    ttk::label $w.l2 -text "Subst:" -anchor "w"
    ttk::entryX $w.e2 -textvariable ::diff($top,prefregsub$t)
    ttk::entryX $w.e2 -textvariable ::eskil($top,prefregsub$t)

    grid $w.l1 $w.e1 -sticky we -padx 3 -pady 3
    grid $w.l2 $w.e2 -sticky we -padx 3 -pady 3
    grid columnconfigure $w 1 -weight 1

    pack $w -side "top" -fill x -padx 3 -pady 3

    trace add variable ::diff($top,prefregexp$t) write \
    trace add variable ::eskil($top,prefregexp$t) write \
            [list EditPrefRegsubUpdate $top]
    trace add variable ::diff($top,prefregsub$t) write \
    trace add variable ::eskil($top,prefregsub$t) write \
            [list EditPrefRegsubUpdate $top]
}

# Editor for ::Pref(regsub)
proc EditPrefRegsub {top} {
    set w $top.prefregsub

    if {[winfo exists $w] && [winfo toplevel $w] eq $w} {
        wm deiconify $w
        raise $w
        focus $w
    } else {
        toplevel $w -padx 3 -pady 3
        wm title $w "Preferences: Preprocess"
    }

    ttk::button $w.b -text "Add" -command [list AddPrefRegsub $top $w]

    # Result example part
    if {![info exists ::diff($top,prefregexa)]} {
        set ::diff($top,prefregexa) \
    if {![info exists ::eskil($top,prefregexa)]} {
        set ::eskil($top,prefregexa) \
                "An example TextString FOR_REGSUB /* Comment */"
        set ::diff($top,prefregexa2) \
        set ::eskil($top,prefregexa2) \
                "An example TextString FOR_REGSUB /* Comment */"
    }
    ttk::labelframe $w.res -text "Preprocessing result" -padding 3
    ttk::label $w.res.l3 -text "Example 1:" -anchor "w"
    ttk::entryX $w.res.e3 -textvariable ::diff($top,prefregexa) -width 60
    ttk::entryX $w.res.e3 -textvariable ::eskil($top,prefregexa) -width 60
    ttk::label $w.res.l4 -text "Result 1:" -anchor "w"
    ttk::label $w.res.e4 -textvariable ::diff($top,prefregresult) \
    ttk::label $w.res.e4 -textvariable ::eskil($top,prefregresult) \
            -anchor "w" -width 10
    ttk::label $w.res.l5 -text "Example 2:" -anchor "w"
    ttk::entryX $w.res.e5 -textvariable ::diff($top,prefregexa2)
    ttk::entryX $w.res.e5 -textvariable ::eskil($top,prefregexa2)
    ttk::label $w.res.l6 -text "Result 2:" -anchor "w"
    ttk::label $w.res.e6 -textvariable ::diff($top,prefregresult2) \
    ttk::label $w.res.e6 -textvariable ::eskil($top,prefregresult2) \
            -anchor "w" -width 10

    grid $w.res.l3 $w.res.e3 -sticky we -padx 3 -pady 3
    grid $w.res.l4 $w.res.e4 -sticky we -padx 3 -pady 3
    grid $w.res.l5 $w.res.e5 -sticky we -padx 3 -pady 3
    grid $w.res.l6 $w.res.e6 -sticky we -padx 3 -pady 3
    grid columnconfigure $w.res 1 -weight 1
3384
3385
3386
3387
3388
3389
3390
3391
3392


3393
3394
3395
3396
3397
3398

3399
3400

3401
3402
3403
3404
3405
3406
3407
3576
3577
3578
3579
3580
3581
3582


3583
3584
3585
3586
3587
3588
3589

3590
3591

3592
3593
3594
3595
3596
3597
3598
3599







-
-
+
+





-
+

-
+








    # Fill in existing or an empty line
    if {[llength $::Pref(regsub)] == 0} {
        AddPrefRegsub $top $w
    } else {
        set t 1
        foreach {RE Sub} $::Pref(regsub) {
            set ::diff($top,prefregexp$t) $RE
            set ::diff($top,prefregsub$t) $Sub
            set ::eskil($top,prefregexp$t) $RE
            set ::eskil($top,prefregsub$t) $Sub
            AddPrefRegsub $top $w
            incr t
        }
    }

    trace add variable ::diff($top,prefregexa) write \
    trace add variable ::eskil($top,prefregexa) write \
            [list EditPrefRegsubUpdate $top]
    trace add variable ::diff($top,prefregexa2) write \
    trace add variable ::eskil($top,prefregexa2) write \
            [list EditPrefRegsubUpdate $top]
    EditPrefRegsubUpdate $top
}

proc defaultGuiOptions {} {
    catch {package require griffin}

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
3902
3903
3904
3905

3906
3907
3908
3909
3910
3911
3912
3913
3914
3915
3916
3917
3918
3919
3920
3921
3922
3923
3924
3925
3926
3927
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




3954
3955
3956
3957
3958
3959




3960
3961
3962
3963
3964
3965
3966
3967


3968
3969

3970
3971

3972
3973
3974
3975

3976
3977
3978
3979
3980
3981


3982
3983
3984
3985
3986
3987
3988
4052
4053
4054
4055
4056
4057
4058

4059
4060
4061
4062
4063
4064
4065
4066
4067





4068
4069
4070
4071
4072
4073
4074
4075
4076
4077
4078
4079
4080
4081
4082
4083
4084
4085
4086
4087
4088

4089
4090
4091
4092
4093
4094
4095
4096

4097
4098
4099
4100
4101
4102
4103
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
4136
4137
4138
4139
4140
4141




4142
4143
4144
4145
4146
4147




4148
4149
4150
4151
4152
4153
4154
4155
4156
4157


4158
4159
4160

4161
4162

4163
4164
4165
4166

4167
4168
4169
4170
4171


4172
4173
4174
4175
4176
4177
4178
4179
4180







-
+








-
-
-
-
-
+
+
+
+
+
















-
+







-
+




















-
-
-
-
+
+
+
+



-
-
-
+
+
+














-
-
-
-
+
+
+
+


-
-
-
-
+
+
+
+






-
-
+
+

-
+

-
+



-
+




-
-
+
+







    }

    # 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
        set ::eskil($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 ::eskil($top,modetype) $rev
        set ::eskil($top,mode) "patch"
        set ::eskil($top,patchFile) ""
        set ::eskil($top,patchData) ""
        set ::eskil($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
                    set ::eskil($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 fullname $file
            set fulldir [file dirname $fullname]
            if {$::diff($top,mode) eq "conflict"} {
            if {$::eskil($top,mode) eq "conflict"} {
                startConflictDiff $top $fullname
                after idle [list doDiff $top]
                set ReturnAfterLoop 1
                continue
            }
            if {!$autobrowse && !$dopatch} {
                # Check for revision control
                set rev [detectRevSystem $fullname $preferedRev]
                if {$rev ne ""} {
                    startRevMode $top $rev $fullname
                    if {$noautodiff} {
                        enableRedo $top
                    } else {
                        after idle [list doDiff $top]
                    }
                    set ReturnAfterLoop 1
                    continue
                }
            }
            # No revision control. Is it a patch file?
            set ::diff($top,leftDir) $fulldir
            set ::diff($top,leftFile) $fullname
            set ::diff($top,leftLabel) $fullname
            set ::diff($top,leftOK) 1
            set ::eskil($top,leftDir) $fulldir
            set ::eskil($top,leftFile) $fullname
            set ::eskil($top,leftLabel) $fullname
            set ::eskil($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 ::eskil($top,mode) "patch"
                set ::eskil($top,patchFile) $fullname
                set ::eskil($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 ::eskil($top,leftDir) $fulldir
        set ::eskil($top,leftFile) $fullname
        set ::eskil($top,leftLabel) $fullname
        set ::eskil($top,leftOK) 1
        set fullname [file join [pwd] [lindex $files 1]]
        set fulldir [file dirname $fullname]
        set ::diff($top,rightDir) $fulldir
        set ::diff($top,rightFile) $fullname
        set ::diff($top,rightLabel) $fullname
        set ::diff($top,rightOK) 1
        set ::eskil($top,rightDir) $fulldir
        set ::eskil($top,rightFile) $fullname
        set ::eskil($top,rightLabel) $fullname
        set ::eskil($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)} {
    if {$autobrowse && (!$::eskil($top,leftOK) || !$::eskil($top,rightOK))} {
        if {!$::eskil($top,leftOK) && !$::eskil($top,rightOK)} {
            openBoth $top 0
        } elseif {!$::diff($top,leftOK)} {
        } elseif {!$::eskil($top,leftOK)} {
            openLeft $top
        } elseif {!$::diff($top,rightOK)} {
        } elseif {!$::eskil($top,rightOK)} {
            openRight $top
        }
        # If we cancel the second file and detect CVS, ask about it.
        if {$::diff($top,leftOK) && !$::diff($top,rightOK) && \
        if {$::eskil($top,leftOK) && !$::eskil($top,rightOK) && \
                [llength [glob -nocomplain [file join $fulldir CVS]]]} {

            if {[tk_messageBox -title Diff -icon question \
                    -message "Do CVS diff?" -type yesno] eq "yes"} {
                set fullname $::diff($top,leftFile)
                set ::diff($top,leftOK) 0
                set fullname $::eskil($top,leftFile)
                set ::eskil($top,leftOK) 0
                startRevMode $top "CVS" $fullname
                after idle [list doDiff $top]
            }
        }
    }
    return $top
}
4084
4085
4086
4087
4088
4089
4090
4091

4092
4093
4094
4095
4096
4097
4098
4276
4277
4278
4279
4280
4281
4282

4283
4284
4285
4286
4287
4288
4289
4290







-
+








    # Store default preferences, to filter saved preferences
    array set ::DefaultPref [array get Pref]

    # Backward compatibilty option
    set Pref(onlydiffs) -1

    set ::diff(filter) ""
    set ::eskil(filter) ""

    if {![info exists ::eskil_testsuite] && [file exists "~/.eskilrc"]} {
        safeLoad "~/.eskilrc" Pref
    }

    if {$Pref(editor) ne ""} {
        set ::util(editor) $Pref(editor)
Changes to src/help.tcl.
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
214
215
216
217
218
219
220

221
222
223
224
225
226
227







-








    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

    # Start up a dirdiff in the examples directory
    set ::dirdiff(leftDir) [file join [pwd] dir1]
    set ::dirdiff(rightDir) [file join [pwd] dir2]
    makeDirDiffWin

    set w [helpWin .ht "Eskil Tutorial"]
Changes to src/map.tcl.
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
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







-
-
+
+





-
+


-
+



-
+









-
-
+
+








-
+

-
+



-
+







    bind $w <Configure> [list drawMap $top %h]
    bind $w <Button-2>  [list ThumbMap $top %y]

    return $w
}

proc clearMap {top} {
    set ::diff($top,changes) {}
    set ::diff($top,mapMax) 0
    set ::eskil($top,changes) {}
    set ::eskil($top,mapMax) 0
    drawMap $top -1
}

proc addChange {top n tag line1 n1 line2 n2} {
    if {$tag ne ""} {
        lappend ::diff($top,changes) [list $::diff($top,mapMax) $n \
        lappend ::eskil($top,changes) [list $::eskil($top,mapMax) $n \
                $tag $line1 $n1 $line2 $n2]
    }
    incr ::diff($top,mapMax) $n
    incr ::eskil($top,mapMax) $n
}

proc addMapLines {top n} {
    incr ::diff($top,mapMax) $n
    incr ::eskil($top,mapMax) $n
}

proc drawMap {top newh} {
    global Pref

    set oldh [map$top cget -height]
    if {$oldh == $newh} return

    map$top blank
    if {![info exists ::diff($top,changes)] || \
	    [llength $::diff($top,changes)] == 0} return
    if {![info exists ::eskil($top,changes)] || \
	    [llength $::eskil($top,changes)] == 0} return

    set w [winfo width $top.c_map]
    set h [winfo height $top.c_map]
    set x2 [expr {$w - ($Pref(wideMap) ? 5 : 1)}]
    if {$x2 < 0} { set x2 0 }
    map$top configure -width $w -height $h
    incr h -1
    set y0 0
    foreach change $::diff($top,changes) {
    foreach change $::eskil($top,changes) {
	lassign $change start length type
	set y1 [expr {$start * $h / $::diff($top,mapMax) + 1}]
	set y1 [expr {$start * $h / $::eskil($top,mapMax) + 1}]
	if {!$y0} { set y0 $y1 } ;# Record first occurance
	if {$y1 < 1} {set y1 1}
	if {$y1 > $h} {set y1 $h}
	set y2 [expr {($start + $length) * $h / $::diff($top,mapMax) + 1}]
	set y2 [expr {($start + $length) * $h / $::eskil($top,mapMax) + 1}]
	if {$y2 < 1} {set y2 1}
	if {$y2 <= $y1} {set y2 [expr {$y1 + 1}]}
	if {$y2 > $h} {set y2 $h}
	incr y2
	map$top put $Pref(color$type) -to 1 $y1 $x2 $y2
    }
    if {$Pref(wideMap)} {
Changes to src/merge.tcl.
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
115
116

117
118
119
120
121
122
123
124

125
126
127
128
129
130
131
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
115

116
117
118
119
120
121
122
123

124
125
126
127
128
129
130
131







-
+

-
-
-
+
+
+

-
-
+
+




-
-
+
+



-
+













-
-
+
+













-
-
-
+
+
+

-
-
+
+

-
+













-
-
+
+









-
+






-
-
+
+

-
+


-
+







-
+







#  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
    global eskil

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

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

    prepareFiles $top

    set ch1 [open $::diff($top,leftFile) r]
    set ch2 [open $::diff($top,rightFile) r]
    set ch1 [open $eskil($top,leftFile) r]
    set ch2 [open $eskil($top,rightFile) r]
    set doingLine1 1
    set doingLine2 1
    set changeNo 0
    foreach change $::diff($top,changes) {
    foreach change $eskil($top,changes) {
        lassign $change start length type line1 n1 line2 n2
        set data1 {}
        set data2 {}
        while {$doingLine1 < $line1} {
            gets $ch1 apa
            append data1 $apa\n
            incr doingLine1
        }
        while {$doingLine2 < $line2} {
            gets $ch2 apa
            append data2 $apa\n
            incr doingLine2
        }
        lappend diff($top,leftMergeData) $data1
        lappend diff($top,rightMergeData) $data2
        lappend eskil($top,leftMergeData) $data1
        lappend eskil($top,rightMergeData) $data2

        set data1 {}
        set data2 {}
        for {set t 0} {$t < $n1} {incr t} {
            gets $ch1 apa
            append data1 $apa\n
            incr doingLine1
        }
        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) \
        lappend eskil($top,leftMergeData) $data1
        lappend eskil($top,rightMergeData) $data2
        set eskil($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
        set eskil($top,mergeSelection,Conflict,$changeNo) $conflict
        set eskil($top,mergeSelection,Comment,$changeNo) $comment
        if {$conflict} {
            set diff($top,mergeSelection,AnyConflict) 1
            set eskil($top,mergeSelection,AnyConflict) 1
        }
        incr changeNo
    }
    set data1 {}
    set data2 {}
    while {[gets $ch1 apa] != -1} {
        append data1 $apa\n
        incr doingLine1
    }
    while {[gets $ch2 apa] != -1} {
        append data2 $apa\n
        incr doingLine2
    }
    lappend diff($top,leftMergeData) $data1
    lappend diff($top,rightMergeData) $data2
    lappend eskil($top,leftMergeData) $data1
    lappend eskil($top,rightMergeData) $data2

    close $ch1
    close $ch2

    cleanupFiles $top
}

# Fill up the merge window with the initial version of merged files.
proc fillMergeWindow {top} {
    global diff
    global eskil

    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) {
    foreach {commLeft diffLeft} $eskil($top,leftMergeData) \
            {commRight diffRight} $eskil($top,rightMergeData) {
        $w insert end $commRight
        if {![info exists diff($top,mergeSelection,$t)]} continue
        if {![info exists eskil($top,mergeSelection,$t)]} continue
        $w mark set merges$t insert
        $w mark gravity merges$t left
        switch $diff($top,mergeSelection,$t) {
        switch $eskil($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)} {
        if {$eskil($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
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
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

247
248

249
250

251
252

253
254
255
256
257
258
259
260
261
262
263
264
265
266

267
268
269
270
271

272
273
274

275
276
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
313
314
315
316

317
318
319
320
321


322
323
324
325
326
327

328
329
330

331
332
333

334
335
336
337

338
339
340
341
342
343

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
398
399
400
401
402
403
404
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
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
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

247
248

249
250

251
252

253
254
255
256
257
258
259
260
261
262
263
264
265
266

267
268
269
270
271

272
273
274

275
276
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
313
314
315
316

317
318
319
320


321
322
323
324
325
326
327

328
329
330

331
332
333

334
335
336
337

338
339
340
341
342
343

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
398
399
400
401
402
403
404
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







-
-
+
+







-
-
+
+





-
+


-
+

-
+



-
-
-
+
+
+





-
-
-
+
+
+





-
-
-
-
+
+
+
+

-
-
-
-
+
+
+
+

-
-
+
+




-
-
+
+
+



-
+

-
+




-
+


-
-
+
+




-
+





-
+


-
-
+
+










-
+

-
+

-
+

-
+













-
+




-
+


-
+



-
-
+
+

-
+

-
-
+
+

-
+









-
+

-
+




-
+

-
-
-
-
+
+
+
+







-
+



-
-
+
+





-
+


-
+


-
+



-
+





-
+


-
-
-
+
+
+





-
+

-
+

-
+









-
+














-
+

-
+

-
+

-
+



















-
-
-
+
+
+

-
+






-
+


-
+


-
+


-
+

-
-
+
+







    $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)
    set eskil($top,curMerge) $showFirst
    set eskil($top,curMergeSel) $eskil($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)
        set eskil($top,mergeStatus) \
                $eskil($top,mergeSelection,Comment,$showFirst)
    }
}

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

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

    set last [expr {[llength $diff($top,leftMergeData)] / 2 - 1}]
    set last [expr {[llength $eskil($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)}]
        for {set t [expr {$eskil($top,curMerge) - 1}]} {$t >= 0} {incr t -1} {
            if {$eskil($top,mergeSelection,Conflict,$t)} {
                set delta [expr {$t - $eskil($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)}]
        for {set t [expr {$eskil($top,curMerge) + 1}]} {$t <= $last} {incr t} {
            if {$eskil($top,mergeSelection,Conflict,$t)} {
                set delta [expr {$t - $eskil($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 eskil($top,curMerge) [expr {$eskil($top,curMerge) + $delta}]
    if {$eskil($top,curMerge) < 0} {set eskil($top,curMerge) 0}
    if {$eskil($top,curMerge) > $last} {
        set eskil($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 eskil($top,curMergeSel) $eskil($top,mergeSelection,$eskil($top,curMerge))
    $w tag configure merge$eskil($top,curMerge) -foreground red
    showDiff $top $eskil($top,curMerge)
    seeText $w merges$eskil($top,curMerge) mergee$eskil($top,curMerge)

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

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

    set end [expr {[llength $eskil($top,leftMergeData)] / 2}]
    for {set t 0} {$t < $end} {incr t} {
        selectMerge2 $top $t $new
    }
    set diff($top,curMergeSel) $new
    set eskil($top,curMergeSel) $new
    set w $top.merge.t
    seeText $w merges$diff($top,curMerge) mergee$diff($top,curMerge)
    seeText $w merges$eskil($top,curMerge) mergee$eskil($top,curMerge)
}

# Change merge setting fo current diff.
proc selectMerge {top} {
    global diff
    global eskil

    set w $top.merge.t
    selectMerge2 $top $diff($top,curMerge) $diff($top,curMergeSel)
    seeText $w merges$diff($top,curMerge) mergee$diff($top,curMerge)
    selectMerge2 $top $eskil($top,curMerge) $eskil($top,curMergeSel)
    seeText $w merges$eskil($top,curMerge) mergee$eskil($top,curMerge)
}

# Change merge setting for a diff.
proc selectMerge2 {top no new} {
    global diff
    global eskil

    set w $top.merge.t
    # Delete current string
    $w delete merges$no mergee$no

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

    set i [expr {$no * 2 + 1}]
    set diffLeft [lindex $diff($top,leftMergeData) $i]
    set diffRight [lindex $diff($top,rightMergeData) $i]
    set diffLeft [lindex $eskil($top,leftMergeData) $i]
    set diffRight [lindex $eskil($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} {
    if {$eskil($top,mergeSelection,$no) == 12} {
        $w insert merges$no $diffLeft$diffRight merge$no
    } elseif {$diff($top,mergeSelection,$no) == 21} {
    } elseif {$eskil($top,mergeSelection,$no) == 21} {
        $w insert merges$no $diffRight$diffLeft merge$no
    } elseif {$diff($top,mergeSelection,$no) == 1} {
    } elseif {$eskil($top,mergeSelection,$no) == 1} {
        $w insert merges$no $diffLeft merge$no
    } elseif {$diff($top,mergeSelection,$no) == 2} {
    } elseif {$eskil($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"} {
    if {$::eskil($top,mergeFile) eq "" && $::eskil($top,mode) eq "conflict"} {
        set apa [tk_messageBox -parent $top.merge -icon question \
                -title "Save merge file" -type yesno -message \
                "Do you want to overwrite the original conflict file?"]
        if {$apa == "yes"} {
            set ::diff($top,mergeFile) $::diff($top,conflictFile)
            set ::eskil($top,mergeFile) $::eskil($top,conflictFile)
        }
    }
    if {$::diff($top,mergeFile) eq ""} {
    if {$::eskil($top,mergeFile) eq ""} {
        # Ask user which file
        set buttons {}
        set text "Overwrite file or Browse?"
        if {[file exists $::diff($top,leftFile)] && \
                $::diff($top,leftFile) eq $::diff($top,leftLabel)} {
        if {[file exists $::eskil($top,leftFile)] && \
                $::eskil($top,leftFile) eq $::eskil($top,leftLabel)} {
            lappend buttons Left
            append text "\nLeft: $::diff($top,leftFile)"
            append text "\nLeft: $::eskil($top,leftFile)"
        }
        if {[file exists $::diff($top,rightFile)] && \
                $::diff($top,rightFile) eq $::diff($top,rightLabel)} {
        if {[file exists $::eskil($top,rightFile)] && \
                $::eskil($top,rightFile) eq $::eskil($top,rightLabel)} {
            lappend buttons Right
            append text "\nRight: $::diff($top,rightFile)"
            append text "\nRight: $::eskil($top,rightFile)"
        }
        lappend buttons Browse Cancel
        if {[llength $buttons] > 2} {
            set apa [tk_dialog .savemerge "Save merge file" \
                    $text \
                    questhead -1 {*}$buttons]
            if {$apa < 0} return
            set apa [lindex $buttons $apa]
            if {$apa eq "Left"} {
                set ::diff($top,mergeFile) $::diff($top,leftFile)
                set ::eskil($top,mergeFile) $::eskil($top,leftFile)
            } elseif {$apa eq "Right"} {
                set ::diff($top,mergeFile) $::diff($top,rightFile)
                set ::eskil($top,mergeFile) $::eskil($top,rightFile)
            } elseif {$apa eq "Cancel"} {
                return
            }
        }
        if {$::diff($top,mergeFile) eq ""} {
        if {$::eskil($top,mergeFile) eq ""} {
            # Browse
            if {[info exists ::diff($top,rightDir)]} {
                set initDir $::diff($top,rightDir)
            } elseif {[info exists ::diff($top,leftDir)]} {
                set initDir $::diff($top,leftDir)
            if {[info exists ::eskil($top,rightDir)]} {
                set initDir $::eskil($top,rightDir)
            } elseif {[info exists ::eskil($top,leftDir)]} {
                set initDir $::eskil($top,leftDir)
            } else {
                set initDir [pwd]
            }

            set apa [tk_getSaveFile -title "Save merge file" -initialdir $initDir \
                    -parent $top.merge]
            if {$apa eq ""} return
            set ::diff($top,mergeFile) $apa
            set ::eskil($top,mergeFile) $apa
        }
    }

    set ch [open $::diff($top,mergeFile) "w"]
    fconfigure $ch -translation $::diff($top,mergetranslation)
    set ch [open $::eskil($top,mergeFile) "w"]
    fconfigure $ch -translation $::eskil($top,mergetranslation)
    puts -nonewline $ch [$w get 1.0 end-1char]
    close $ch

    # Detect if this is a GIT merge, and possibly add it to the index
    # after save (i.e. git add file)
    if {[detectRevSystem $::diff($top,mergeFile)] eq "GIT"} {
    if {[detectRevSystem $::eskil($top,mergeFile)] eq "GIT"} {
        set apa [tk_messageBox -parent $top.merge -icon info -type yesno \
                -title "Diff" \
                -message "Saved merge to file $::diff($top,mergeFile).\nAdd\
                -message "Saved merge to file $::eskil($top,mergeFile).\nAdd\
                it to GIT index?"]
        if {$apa eq "yes"} {
            eskil::rev::GIT::add $::diff($top,mergeFile)
            eskil::rev::GIT::add $::eskil($top,mergeFile)
        }
    } else {
        tk_messageBox -parent $top.merge -icon info -type ok -title "Diff" \
                -message "Saved merge to file $::diff($top,mergeFile)."
                -message "Saved merge to file $::eskil($top,mergeFile)."
    }
}

# Close merge window and clean up.
proc closeMerge {top} {
    global diff
    global eskil

    destroy $top.merge
    set diff($top,leftMergeData) {}
    set diff($top,rightMergeData) {}
    array unset diff $top,mergeSelection,*
    set eskil($top,leftMergeData) {}
    set eskil($top,rightMergeData) {}
    array unset eskil $top,mergeSelection,*
}

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

    set w $top.merge
    if {![winfo exists $w]} {
        toplevel $w
    } else {
        destroy {*}[winfo children $w]
    }
    set anyC $::diff($top,mergeSelection,AnyConflict)
    set anyC $::eskil($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.mf add command -label "Close" -underline 0 -command "closeMerge $top"

    $w.m add cascade -label "Select" -underline 0 -menu $w.m.ms
    menu $w.m.ms
    $w.m.ms add radiobutton -label "Left+Right"         -value 12 \
            -variable diff($top,curMergeSel) -command "selectMerge $top"
            -variable ::eskil($top,curMergeSel) -command "selectMerge $top"
    $w.m.ms add radiobutton -label "Left" -underline 0  -value 1  \
            -variable diff($top,curMergeSel) -command "selectMerge $top"
            -variable ::eskil($top,curMergeSel) -command "selectMerge $top"
    $w.m.ms add radiobutton -label "Right" -underline 0 -value 2  \
            -variable diff($top,curMergeSel) -command "selectMerge $top"
            -variable ::eskil($top,curMergeSel) -command "selectMerge $top"
    $w.m.ms add radiobutton -label "Right+Left"         -value 21 \
            -variable diff($top,curMergeSel) -command "selectMerge $top"
            -variable ::eskil($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 radiobutton -label "Line end LF"   -value lf   -variable ::eskil($top,mergetranslation)
    $w.m.mc add radiobutton -label "Line end CRLF" -value crlf -variable ::eskil($top,mergetranslation)
    if {$::eskil($top,mode) eq "conflict"} {
        $w.m.mc add separator
        $w.m.mc add checkbutton -label "Pure" -variable diff($top,modetype) \
        $w.m.mc add checkbutton -label "Pure" -variable ::eskil($top,modetype) \
                -onvalue "Pure" -offvalue "" -command {doDiff}
    }

    ttk::frame $w.f

    ttk::radiobutton $w.f.rb1 -text "LR" -value 12 \
            -variable diff($top,curMergeSel) \
            -variable ::eskil($top,curMergeSel) \
            -command "selectMerge $top"
    ttk::radiobutton $w.f.rb2 -text "L"  -value 1 \
            -variable diff($top,curMergeSel) \
            -variable ::eskil($top,curMergeSel) \
            -command "selectMerge $top"
    ttk::radiobutton $w.f.rb3 -text "R"  -value 2 \
            -variable diff($top,curMergeSel) \
            -variable ::eskil($top,curMergeSel) \
            -command "selectMerge $top"
    ttk::radiobutton $w.f.rb4 -text "RL" -value 21 \
            -variable diff($top,curMergeSel) \
            -variable ::eskil($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"
    bind $w <Key-Left>  "focus $w; set ::eskil($top,curMergeSel) 1; selectMerge $top"
    bind $w <Key-Right> "focus $w; set ::eskil($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"
459
460
461
462
463
464
465
466

467
468
469
470
471
472
473
460
461
462
463
464
465
466

467
468
469
470
471
472
473
474







-
+







    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)
    ttk::label $w.ls -textvariable ::eskil($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"
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
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







-
+

-
+



-
+

-
+


-
-
+
+

-
+

-
+










-
+




-
+



-
+








-
+




-
+



-
+












-
+





-
+







-
-
+
+














-
+







-
-
+
+

















-
-
+
+












-
-
+
+







    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)]} {
    if {![info exists ::eskil($top,mergetranslation)]} {
        # Try to autodetect line endings in ancestor file
        set ch [open $::diff($top,ancestorFile) rb]
        set ch [open $::eskil($top,ancestorFile) rb]
        set data [read $ch 10000]
        close $ch
        if {[string first \r\n $data] >= 0} {
            set ::diff($top,mergetranslation) crlf
            set ::eskil($top,mergetranslation) crlf
        } else {
            set ::diff($top,mergetranslation) lf
            set ::eskil($top,mergetranslation) lf
        }
    }
    array unset ::diff $top,ancestorLeft,*
    array unset ::diff $top,ancestorRight,*
    array unset ::eskil $top,ancestorLeft,*
    array unset ::eskil $top,ancestorRight,*
    set differrA1 [catch {DiffUtil::diffFiles {*}$opts \
            $::diff($top,ancestorFile) $dFile1} diffresA1]
            $::eskil($top,ancestorFile) $dFile1} diffresA1]
    set differrA2 [catch {DiffUtil::diffFiles {*}$opts \
            $::diff($top,ancestorFile) $dFile2} diffresA2]
            $::eskil($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
                set ::eskil($top,ancestorLeft,$t) a
            }
        } elseif {$n2 == 0} {
            # Deleted lines
            # Mark the following line
            set ::diff($top,ancestorLeft,d$line2) d
            set ::eskil($top,ancestorLeft,d$line2) d
        } else {
            # Changed lines
            for {set t $line2} {$t < $line2 + $n2} {incr t} {
                set ::diff($top,ancestorLeft,$t) c
                set ::eskil($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
                set ::eskil($top,ancestorRight,$t) a
            }
        } elseif {$n2 == 0} {
            # Deleted lines
            # Mark the following line
            set ::diff($top,ancestorRight,d$line2) d
            set ::eskil($top,ancestorRight,d$line2) d
        } else {
            # Changed lines
            for {set t $line2} {$t < $line2 + $n2} {incr t} {
                set ::diff($top,ancestorRight,$t) c
                set ::eskil($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 ""} {
    if {$::eskil($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)]
        set delLeft [info exists ::eskil($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
            if {[info exists ::eskil($top,ancestorRight,$t)]} {
                set right($::eskil($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)]
        set delRight [info exists ::eskil($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
            if {[info exists ::eskil($top,ancestorLeft,$t)]} {
                set left($::eskil($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
            if {[info exists ::eskil($top,ancestorLeft,$t)]} {
                set left($::eskil($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
            if {[info exists ::eskil($top,ancestorRight,$t)]} {
                set right($::eskil($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"
Changes to src/plugin.tcl.
32
33
34
35
36
37
38

39
40
41
42
43
44
45
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46







+








# Locate plugin source
proc LocatePlugin {plugin} {
    set src ""
    set dirs [PluginSearchPath]

    foreach dir $dirs {
        set dir [file normalize $dir]
        set files {}
        lappend files [file join $dir $plugin]
        lappend files [file join $dir $plugin.tcl]
        foreach file $files {
            if {![file exists   $file]} continue
            if {![file isfile   $file]} continue
            if {![file readable $file]} continue
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
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
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

























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
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


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
247
248
249
250
251
252

253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279







+

















-
+
+
+
+
+

















-
+



-
+

-
-
-
-
-
+
+
+
+
+



-
+

-
-
-
-
-
+
+
+
+
+






-
-
+
+

-
-
+
+


-
-
+
+

-
-
+
+




-
-
+
+

-
-
+
+


-
-
+
+

















-
-
+
+

-
-
+
+

-
-
+
+


-
+




-
+



-
+




-
+
+
+


-
-
-
+
+
+







-
-
-
-
+
+
+
+



-
+

+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
    close $ch
}

proc listPlugins {} {
    set dirs [PluginSearchPath]

    foreach dir $dirs {
        set dir [file normalize $dir]
        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]
    set resultSort {}
    foreach elem [lsort -dictionary [array names result]] {
        lappend resultSort $elem $result($elem)
    }
    return $resultSort
}

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]]
    $::eskil($top,plugin) eval [list array set ::Pref [array get ::Pref]]
    set out1 [tmpFile]
    set out2 [tmpFile]

    set chi [open $::diff($top,leftFile) r]
    set chi [open $::eskil($top,leftFile) r]
    set cho [open $out1 w]
    interp share {} $chi $::diff($top,plugin)
    interp share {} $cho $::diff($top,plugin)
    set usenew1 [$::diff($top,plugin) eval [list PreProcess left $chi $cho]]
    $::diff($top,plugin) invokehidden close $chi
    $::diff($top,plugin) invokehidden close $cho
    interp share {} $chi $::eskil($top,plugin)
    interp share {} $cho $::eskil($top,plugin)
    set usenew1 [$::eskil($top,plugin) eval [list PreProcess left $chi $cho]]
    $::eskil($top,plugin) invokehidden close $chi
    $::eskil($top,plugin) invokehidden close $cho
    close $chi
    close $cho

    set chi [open $::diff($top,rightFile) r]
    set chi [open $::eskil($top,rightFile) r]
    set cho [open $out2 w]
    interp share {} $chi $::diff($top,plugin)
    interp share {} $cho $::diff($top,plugin)
    set usenew2 [$::diff($top,plugin) eval [list PreProcess right $chi $cho]]
    $::diff($top,plugin) invokehidden close $chi
    $::diff($top,plugin) invokehidden close $cho
    interp share {} $chi $::eskil($top,plugin)
    interp share {} $cho $::eskil($top,plugin)
    set usenew2 [$::eskil($top,plugin) eval [list PreProcess right $chi $cho]]
    $::eskil($top,plugin) invokehidden close $chi
    $::eskil($top,plugin) invokehidden close $cho
    close $chi
    close $cho

    if {$usenew1} {
        # The file after processing should be used both
        # for comparison and for displaying.
        set ::diff($top,leftFileBak) $::diff($top,leftFile)
        set ::diff($top,leftFile) $out1
        set ::eskil($top,leftFileBak) $::eskil($top,leftFile)
        set ::eskil($top,leftFile) $out1
    } else {
        set ::diff($top,leftFileDiff) $out1
        #set ::diff($top,leftLabel) "$::diff($top,RevFile) $tag"
        set ::eskil($top,leftFileDiff) $out1
        #set ::eskil($top,leftLabel) "$::eskil($top,RevFile) $tag"
    }
    if {$usenew2} {
        set ::diff($top,rightFileBak) $::diff($top,rightFile)
        set ::diff($top,rightFile) $out2
        set ::eskil($top,rightFileBak) $::eskil($top,rightFile)
        set ::eskil($top,rightFile) $out2
    } else {
        set ::diff($top,rightFileDiff) $out2
        #set ::diff($top,rightLabel) $::diff($top,RevFile)
        set ::eskil($top,rightFileDiff) $out2
        #set ::eskil($top,rightLabel) $::eskil($top,RevFile)
    }
}

proc cleanupPlugin {top} {
    if {[info exists ::diff($top,leftFileBak)]} {
        set ::diff($top,leftFile) $::diff($top,leftFileBak)
    if {[info exists ::eskil($top,leftFileBak)]} {
        set ::eskil($top,leftFile) $::eskil($top,leftFileBak)
    }
    if {[info exists ::diff($top,rightFileBak)]} {
        set ::diff($top,rightFile) $::diff($top,rightFileBak)
    if {[info exists ::eskil($top,rightFileBak)]} {
        set ::eskil($top,rightFile) $::eskil($top,rightFileBak)
    }
    unset -nocomplain \
            ::diff($top,leftFileBak) ::diff($top,rightFileBak) \
            ::diff($top,leftFileDiff) ::diff($top,rightFileDiff)
            ::eskil($top,leftFileBak) ::eskil($top,rightFileBak) \
            ::eskil($top,leftFileDiff) ::eskil($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 ::eskil($top,pluginname)]} {
        set ::eskil($top,pluginname) ""
    }
    if {![info exists ::diff($top,plugininfo)]} {
        set ::diff($top,plugininfo) ""
    if {![info exists ::eskil($top,plugininfo)]} {
        set ::eskil($top,plugininfo) ""
    }
    set ::diff($top,edit,pluginname) $::diff($top,pluginname) 
    set ::diff($top,edit,plugininfo) $::diff($top,plugininfo)
    set ::eskil($top,edit,pluginname) $::eskil($top,pluginname) 
    set ::eskil($top,edit,plugininfo) $::eskil($top,plugininfo)
    set t 0
    foreach {plugin descr} $plugins {
        ttk::radiobutton $w.rb$t -variable ::diff($top,edit,pluginname) -value $plugin -text $plugin
        ttk::radiobutton $w.rb$t -variable ::eskil($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"
    ttk::radiobutton $w.rb$t -variable ::eskil($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)
    ttk::entry $w.ei -textvariable ::eskil($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]
    ttk::button $w.fb.b2 -text "Show" \
            -command "ShowPlugin $w \$::eskil($top,edit,pluginname)"
    ttk::button $w.fb.b3 -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.b1 x $w.fb.b2 x $w.fb.b3 -sticky we
    grid columnconfigure $w.fb {0 2 4} -uniform a
    grid columnconfigure $w.fb {1 3} -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)]
    set ::eskil($top,pluginname) $::eskil($top,edit,pluginname) 
    set ::eskil($top,plugininfo) $::eskil($top,edit,plugininfo)
    if {$::eskil($top,pluginname) ne ""} {
        set pinterp [createPluginInterp $::eskil($top,pluginname) $::eskil($top,plugininfo)]
    } else {
        set pinterp ""
    }
    set ::diff($top,plugin) $pinterp
    set ::eskil($top,plugin) $pinterp
}

proc ShowPlugin {parent plugin} {
    set src [LocatePlugin $plugin]
    if {$src eq ""} return
    set ch [open $src]
    set data [read $ch]
    close $ch
    set w $parent.plugin
    if {[winfo exists $w]} {
        wm deiconify $w
    } else {
        toplevel $w -padx 3 -pady 3
    }
    destroy {*}[winfo children $w]
    ttk::frame $w._bg
    place $w._bg -x 0 -y 0 -relwidth 1.0 -relheight 1.0 -border outside
    wm title $w "Plugin: $plugin"

    set t [Scroll both text $w.t -width 80 -height 20 -font myfont]
    pack $w.t -fill both -expand 1
    bind $t <Control-a> "[list $t tag add sel 1.0 end];break"

    $t insert end $data
}

Changes to src/print.tcl.
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
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







-
-
+
+




-
-
+
+



-
-
+
+







    # Finished

    normalCursor $top
}

proc PdfPrint {top cpl cpln wraplines1 wraplines2} {

    if {$::diff($top,printFile) != ""} {
        set pdfFile $::diff($top,printFile)
    if {$::eskil($top,printFile) != ""} {
        set pdfFile $::eskil($top,printFile)
    } else {
        set pdfFile ~/eskil.pdf
    }

    if {![regexp {^(.*)( \(.*?\))$} $::diff($top,leftLabel) -> lfile lrest]} {
        set lfile $::diff($top,leftLabel)
    if {![regexp {^(.*)( \(.*?\))$} $::eskil($top,leftLabel) -> lfile lrest]} {
        set lfile $::eskil($top,leftLabel)
        set lrest ""
    }
    set lfile [file tail $lfile]$lrest
    if {![regexp {^(.*)( \(.*?\))$} $::diff($top,rightLabel) -> rfile rrest]} {
        set rfile $::diff($top,rightLabel)
    if {![regexp {^(.*)( \(.*?\))$} $::eskil($top,rightLabel) -> rfile rrest]} {
        set rfile $::eskil($top,rightLabel)
        set rrest ""
    }
    set rfile [file tail $rfile]$rrest

    set pdf [eskilprint %AUTO% -file $pdfFile -cpl $cpl -cpln $cpln \
                     -headleft $lfile -headright $rfile -headsize 10]
    set linesPerPage [$pdf getNLines]
289
290
291
292
293
294
295
296
297


298
299
300
301
302
303

304
305
306

307
308
309
310

311
312
313
314
315
316

317
318
319
320
321
322
323
289
290
291
292
293
294
295


296
297
298
299
300
301
302

303
304
305

306
307
308
309

310
311
312
313
314
315

316
317
318
319
320
321
322
323







-
-
+
+





-
+


-
+



-
+





-
+







    $pdf endPrint
}

# Count the length of a line during a text dump
proc AccumulateMax {key value index} {
    set index [lindex [split $index "."] 1]
    set len [expr {[string length $value] + $index - 1}]
    if {$len > $::diff(currentCharsPerLine)} {
        set ::diff(currentCharsPerLine) $len
    if {$len > $::eskil(currentCharsPerLine)} {
        set ::eskil(currentCharsPerLine) $len
    }
}

# Count the longest line length in the current display
proc CountCharsPerLine {top} {
    set ::diff(currentCharsPerLine) 0
    set ::eskil(currentCharsPerLine) 0
    $::widgets($top,wDiff1) dump -text -command AccumulateMax 1.0 end
    $::widgets($top,wDiff2) dump -text -command AccumulateMax 1.0 end
    return $::diff(currentCharsPerLine)
    return $::eskil(currentCharsPerLine)
}

proc BrowsePrintFileName {top entry} {
    set prev $::diff($top,printFile)
    set prev $::eskil($top,printFile)
    set dir [file dirname $prev]

    set apa [tk_getSaveFile -initialdir $dir -initialfile [file tail $prev] \
                     -parent [winfo toplevel $entry] -title "PDF file"]
    if {$apa ne ""} {
        set ::diff($top,printFile) $apa
        set ::eskil($top,printFile) $apa
        $entry xview end
    }
}

# Create a print dialog for PDF.
proc doPrint {top {quiet 0}} {
    if {$quiet} {
401
402
403
404
405
406
407
408

409
410
411
412
413


414
415
416
417
418
419
420
401
402
403
404
405
406
407

408
409
410
411


412
413
414
415
416
417
418
419
420







-
+



-
-
+
+







    grid .pr.cf.l1 .pr.cf.s1r .pr.cf.s1g .pr.cf.s1b -sticky w -padx 3 -pady 3
    grid .pr.cf.l2 .pr.cf.s2r .pr.cf.s2g .pr.cf.s2b -sticky w -padx 3 -pady 3
    grid .pr.cf.l3 .pr.cf.s3r .pr.cf.s3g .pr.cf.s3b -sticky w -padx 3 -pady 3

    # File

    ttk::label .pr.fnl -anchor w -text "File name"
    ttk::entryX .pr.fne -textvariable ::diff($top,printFile) -width 30
    ttk::entryX .pr.fne -textvariable ::eskil($top,printFile) -width 30
    ttk::button .pr.fnb -text Browse \
            -command [list BrowsePrintFileName $top .pr.fne]

    if {$::diff($top,printFile) eq ""} {
        set ::diff($top,printFile) "~/eskil.pdf"
    if {$::eskil($top,printFile) eq ""} {
        set ::eskil($top,printFile) "~/eskil.pdf"
    }

    ttk::frame .pr.fb
    ttk::button .pr.b1 -text "Print to File" \
            -command "destroy .pr; update; PrintDiffs $top"
    ttk::button .pr.b2 -text "Cancel" -command {destroy .pr}
    pack .pr.b1 -in .pr.fb -side left  -padx 3 -pady 3 -ipadx 5
Changes to src/rev.tcl.
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
100
101
102
103
104
105
106

107








108
109
110
111
112
113
114
115

116








117
118
119
120
121
122
123







-
+
-
-
-
-
-
-
-
-








-
+
-
-
-
-
-
-
-
-







            return 1
        }
    }
    return 0
}

proc eskil::rev::HG::detect {file} {
    if {$file eq ""} {
    if {[SearchUpwardsFromFile $file .hg]} {
        set dir [pwd]
    } else {
        set dir [file dirname $file]
    }
    # HG, detect two steps down. Could be improved. FIXA
    if {[file isdirectory [file join $dir .hg]] ||
        [file isdirectory [file join $dir .. .hg]] ||
        [file isdirectory [file join $dir .. .. .hg]]} {
        if {[auto_execok hg] ne ""} {
            return 1
        }
    }
    return 0
}

proc eskil::rev::BZR::detect {file} {
    if {$file eq ""} {
    if {[SearchUpwardsFromFile $file .bzr]} {
        set dir [pwd]
    } else {
        set dir [file dirname $file]
    }
    # HG, detect two steps down. Could be improved. FIXA
    if {[file isdirectory [file join $dir .bzr]] ||
        [file isdirectory [file join $dir .. .bzr]] ||
        [file isdirectory [file join $dir .. .. .bzr]]} {
        if {[auto_execok bzr] ne ""} {
            return 1
        }
    }
    return 0
}

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
146
147
148
149
150
151
152

153








154
155
156
157
158
159
160
161

162









163
164
165
166
167
168
169







-
+
-
-
-
-
-
-
-
-








-
+
-
-
-
-
-
-
-
-
-







        }
        cd $old
    }
    return 0
}

proc eskil::rev::GIT::detect {file} {
    if {$file eq ""} {
    if {[SearchUpwardsFromFile $file .git]} {
        set dir [pwd]
    } else {
        set dir [file dirname $file]
    }
    # Git, detect two steps down. Could be improved. FIXA
    if {[file isdirectory [file join $dir .git]] ||
        [file isdirectory [file join $dir .. .git]] ||
        [file isdirectory [file join $dir .. .. .git]]} {
        if {[auto_execok git] ne ""} {
            return 1
        }
    }
    return 0
}

proc eskil::rev::FOSSIL::detect {file} {
    if {$file eq ""} {
    if {[SearchUpwardsFromFile $file _FOSSIL_ .fos]} {
        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
}

939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954









955
956
957
958
959
960
961
962
963
964
965

966
967
968
969
970
971


972
973
974


975
976
977

978
979
980
981
982

983
984
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






1019
1020
1021
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
1072
1073


1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093























1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113

1114
1115
1116
1117


1118
1119
1120
1121
1122
1123
1124


1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140



1141
1142
1143
1144
1145
1146
1147
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
935
936


937
938
939


940
941
942
943

944
945
946
947
948

949
950
951
952
953
954
955
956
957
958
959
960




961
962
963
964
965

966
967
968
969
970
971
972
973
974
975
976


977
978
979






980
981
982
983
984
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
1019
1020
1021
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
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102

1103
1104
1105


1106
1107
1108
1109
1110
1111
1112


1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127



1128
1129
1130
1131
1132
1133
1134
1135
1136
1137







-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+










-
+




-
-
+
+

-
-
+
+


-
+




-
+











-
-
-
-
+
+
+
+

-
+










-
-
+
+

-
-
-
-
-
-
+
+
+
+
+
+














-
-
-
+
+
+




-
-
-
+
+
+

-
+






-
-
-
+
+
+









-
-
+
+




-
-
+
+

-
-
+
+




















+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+



















-
+


-
-
+
+





-
-
+
+













-
-
-
+
+
+







    }
    return
}

# Initialise revision control mode
# The file name should be an absolute normalized path.
proc startRevMode {top rev file} {
    set ::diff($top,mode) "rev"
    set ::diff($top,modetype) $rev
    set ::diff($top,rightDir) [file dirname $file]
    set ::diff($top,RevFile) $file
    set ::diff($top,rightLabel) $file
    set ::diff($top,rightFile) $file
    set ::diff($top,rightOK) 1
    set ::diff($top,leftLabel) $rev
    set ::diff($top,leftOK) 0
    set ::eskil($top,mode) "rev"
    set ::eskil($top,modetype) $rev
    set ::eskil($top,rightDir) [file dirname $file]
    set ::eskil($top,RevFile) $file
    set ::eskil($top,rightLabel) $file
    set ::eskil($top,rightFile) $file
    set ::eskil($top,rightOK) 1
    set ::eskil($top,leftLabel) $rev
    set ::eskil($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)
    set type $::eskil($top,modetype)

    set revs {}

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

    set revs [eskil::rev::${type}::ParseRevs $::diff($top,RevFile) $revs]
    set revs [eskil::rev::${type}::ParseRevs $::eskil($top,RevFile) $revs]
    set revlabels {}
    foreach rev $revs {
        lappend revlabels [GetLastTwoPath $rev]
    }
    set ::diff($top,RevRevs) $revs
    set ::eskil($top,RevRevs) $revs

    if {[llength $revs] < 2} {
        # Compare local file with specified version.
        disallowEdit $top 1
        if {[llength $revs] == 0} {
            set r ""
            set tag "($type)"
        } else {
            set r [lindex $revs 0]
            set tag "($type [lindex $revlabels 0])"
        }
        set ::diff($top,leftFile) [tmpFile]
        set ::diff($top,leftLabel) "$::diff($top,RevFile) $tag"
        set ::diff($top,rightLabel) $::diff($top,RevFile)
        set ::diff($top,rightFile) $::diff($top,RevFile)
        set ::eskil($top,leftFile) [tmpFile]
        set ::eskil($top,leftLabel) "$::eskil($top,RevFile) $tag"
        set ::eskil($top,rightLabel) $::eskil($top,RevFile)
        set ::eskil($top,rightFile) $::eskil($top,RevFile)

        eskil::rev::${type}::get $::diff($top,RevFile) $::diff($top,leftFile) $r
        eskil::rev::${type}::get $::eskil($top,RevFile) $::eskil($top,leftFile) $r
        if {[llength $revs] == 0} {
            if {[info commands eskil::rev::${type}::commitFile] ne ""} {
                $::widgets($top,commit) configure -state normal
            }
        }
    } else {
        # Compare the two specified versions.
        disallowEdit $top
        set r1 [lindex $revs 0]
        set r2 [lindex $revs 1]
        set ::diff($top,leftFile)  [tmpFile]
        set ::diff($top,rightFile) [tmpFile]
        set ::eskil($top,leftFile)  [tmpFile]
        set ::eskil($top,rightFile) [tmpFile]

        set ::diff($top,leftLabel) \
                "$::diff($top,RevFile) ($type [lindex $revlabels 0])"
        set ::diff($top,rightLabel) \
                "$::diff($top,RevFile) ($type [lindex $revlabels 1])"
        eskil::rev::${type}::get $::diff($top,RevFile) $::diff($top,leftFile) $r1
        eskil::rev::${type}::get $::diff($top,RevFile) $::diff($top,rightFile) $r2
        set ::eskil($top,leftLabel) \
                "$::eskil($top,RevFile) ($type [lindex $revlabels 0])"
        set ::eskil($top,rightLabel) \
                "$::eskil($top,RevFile) ($type [lindex $revlabels 1])"
        eskil::rev::${type}::get $::eskil($top,RevFile) $::eskil($top,leftFile) $r1
        eskil::rev::${type}::get $::eskil($top,RevFile) $::eskil($top,rightFile) $r2
    }
    if {[llength $revs] > 0} {
        if {[info commands eskil::rev::${type}::viewLog] ne ""} {
            $::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)
    clearTmp $::eskil($top,rightFile) $::eskil($top,leftFile)
    set ::eskil($top,rightFile) $::eskil($top,RevFile)
    set ::eskil($top,leftFile) $::eskil($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)
    set type $::eskil($top,modetype)
    if {$::eskil($top,mode) eq "patch"} {
        set files $::eskil($top,reviewFiles)
    } else {
        set files [list $::diff($top,RevFile)]
        set files [list $::eskil($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)
    set type $::eskil($top,modetype)
    eskil::rev::${type}::viewLog $top $::eskil($top,RevFile) \
            $::eskil($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 type $::eskil($top,modetype)
    set files $::eskil($top,reviewFiles)

    set revs {}

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

    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
##############################################################################

# Search upwards the directory structure for a file
proc SearchUpwardsFromFile {file args} {
    if {$file eq ""} {
        set dir [pwd]
    } elseif {[file isdirectory $file]} {
        set dir $file
    } else {
        set dir [file dirname $file]
    }
    while {[file readable $dir] && [file isdirectory $dir]} {
        foreach candidate $args {
            if {[file exists [file join $dir $candidate]]} {
                return 1
            }
        }
        set parent [file dirname $dir]
        # Make sure to stop if we reach a dead end
        if {$parent eq $dir} break
        set dir $parent
    }
    return 0
}

# Get the last two elements in a file path
proc GetLastTwoPath {path} {
    set last [file tail $path]
    set penultimate [file tail [file dirname $path]]
    if {$penultimate eq "."} {
        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
    set ::eskil($top,logdialogok) 0

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

    ttk::button $w.ok -width 10 -text "Commit" -underline 1 \
            -command "set ::diff($top,logdialogok) 1 ; \
                      set ::diff(logdialog) \[$w.t get 1.0 end\] ; \
            -command "set ::eskil($top,logdialogok) 1 ; \
                      set ::eskil(logdialog) \[$w.t get 1.0 end\] ; \
                      destroy $w"
    ttk::button $w.ca -width 10 -text "Cancel" -command "destroy $w" \
            -underline 0
    bind $w <Alt-o> [list $w.ok invoke]\;break
    bind $w <Alt-c> [list destroy $w]\;break
    bind $w <Key-Escape> [list destroy $w]\;break

    grid $w.t  - -sticky news -padx 3 -pady 3
    grid $w.ok $w.ca -padx 3 -pady 3
    tkwait visibility $w
    focus -force $w.t
    tkwait window $w

    if {$::diff($top,logdialogok)} {
        set res [string trim $::diff(logdialog)]
        set ::diff(logdialog) $res
    if {$::eskil($top,logdialogok)} {
        set res [string trim $::eskil(logdialog)]
        set ::eskil(logdialog) $res
        if {$res eq ""} {
            set res "No Log"
        }
    } else {
        set res ""
    }
    return $res
Changes to tests/all.tcl.
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
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







+
+
+
+






-
+

-
+

-
+







lappend auto_path eskil.vfs/lib

package require tcltest 2.2
namespace import tcltest::*
tcltest::configure -verbose "body error"
#testConstraint knownbug 1
#tcltest::configure -match print-*

if {$argc > 0} {
    eval tcltest::configure $argv
}

package require Tk
wm withdraw .

set ::eskil_testsuite 1

if {[file exists src/eskil.tcl_i]} {
if {[file exists eskil.vfs/src/eskil.tcl_i]} {
    puts "Running with code coverage"
    source src/eskil.tcl_i
    source eskil.vfs/src/eskil.tcl_i
} else {
    source src/eskil.tcl
    source eskil.vfs/src/eskil.tcl
}
Init

# Helpers to temporarily stub things out
set ::stubs {}
proc stub {name argv body} {
    if {[info commands _stub_$name] eq ""} {
43
44
45
46
47
48
49



50
51
52
53
54
55
56
57
58
59
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66







+
+
+










    foreach name $::stubs {
        rename $name {}
        rename _stub_$name $name
    }
    set ::stubs {}
}

proc ExecEskil {args} {
    return [exec ./eskil.kit {*}$args]
}

puts "Running Tests"

foreach test [glob -nocomplain $testDir/*.test] {
    source $test
    clearstub
}
tcltest::cleanupTests 1

exit
Changes to tests/gui.test.
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
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







+
+
+
+
+
+
+
-
+









-
+







	exec xhost -$host
    }
    exec xhost -
}
XauthSecure

proc RestartClient {args} {
    if {[file exists eskil.vfs/main.tcl_i]} {
        puts "Starting gui instrumented"
        set src eskil.vfs/main.tcl_i
    } else {
        set src eskil.vfs/main.tcl
    }
    set cmd [list tclkit $src]
    set ::clientfile ./eskil.kit
    #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 &]
    set slavepid [exec {*}$cmd -server {*}$args &]

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

Changes to tests/patch.test.
1
2
3
4
5
6
7
8


9
10
11
12
13
14
15
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) ""
set ::eskil(gurka,patchFile) ""
set ::eskil(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/print.test.
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
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







-
+













-
+



-
+



-
+



-
+



-
+



-
+

    puts $ch1 "Diffline2.1"
    puts $ch2 "Diffline2.2"
    puts $ch1 $data
    puts $ch2 $data
    close $ch1
    close $ch2
} -body {
    set res [exec ./eskil.kit -context 5 -printpdf $f3 $f1 $f2]
    set res [ExecEskil -context 5 -printpdf $f3 $f1 $f2]
    puts $res
    set ch [open $f3 r]
    set data [read $ch]
    close $ch
    # Check that line numbers take up 7 chars
    string match "*(    3: )*(24690: )*" $data
} -cleanup {
    tcltest::removeFile {} _test1
    tcltest::removeFile {} _test2
    tcltest::removeFile {} _test3
} -result {1}

test print-3.1 {Pdf, cmd line} -body {
    set res [exec ./eskil.kit -printHeaderSize x]
    set res [ExecEskil -printHeaderSize x]
} -result {Argument -printHeaderSize must be a positive number}

test print-3.2 {Pdf, cmd line} -body {
    set res [exec ./eskil.kit -printCharsPerLine -5]
    set res [ExecEskil -printCharsPerLine -5]
} -result {Argument -printCharsPerLine must be a positive number}

test print-3.3 {Pdf, cmd line} -body {
    set res [exec ./eskil.kit -printPaper qx]
    set res [ExecEskil -printPaper qx]
} -match glob -result {Argument -printPaper must be a valid paper size*}

test print-3.4 {Pdf, cmd line} -body {
    set res [exec ./eskil.kit -printColorChange x]
    set res [ExecEskil -printColorChange x]
} -result {Argument -printColorChange must be a list of RBG values from 0.0 to 1.0}

test print-3.5 {Pdf, cmd line} -body {
    set res [exec ./eskil.kit -printColorOld "0 1 2"]
    set res [ExecEskil -printColorOld "0 1 2"]
} -result {Argument -printColorOld must be a list of RBG values from 0.0 to 1.0}

test print-3.6 {Pdf, cmd line} -body {
    set res [exec ./eskil.kit -printColorNew "0 -1 0.5"]
    set res [ExecEskil -printColorNew "0 -1 0.5"]
} -result {Argument -printColorNew must be a list of RBG values from 0.0 to 1.0}
Changes to tests/rev.test.
1
2
3
4
5
6
7
8
9
10
11
12
13
14

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

14
15
16
17
18
19
20
21













-
+







# Tests for revision control functions                      -*- tcl -*-
#----------------------------------------------------------------------
# $Revision$
#----------------------------------------------------------------------

# Overload exec during these tests
stub exec {args} {
    set cmd [lindex $args 0]
    switch -- $cmd {
        cleartool {
            # cleartool lshistory -short $filename
            # cleartool pwv -s
            # cleartool get -to $outfile $filerev
            # cleartool ls $::diff($top,RevFile)
            # cleartool ls $::eskil($top,RevFile)
            if {[lindex $args 1] eq "lshistory"} {
                return [join [list x@/Apa/Bepa/12 x@/Apa/Cepa/2 x@/Apa/22 x@/Apa] \n]
            }
            if {[lindex $args 1] eq "pwv"} {
                return $::ct_pwv
            }
            if {[lindex $args 1] eq "ls"} {