Eskil

Check-in [d1d44e3aed]
Login

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

Overview
Comment:Merge from trunk
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | thread
Files: files | file ages | folders
SHA1: d1d44e3aedd340c84b95f711eb1d4408827dbe97
User & Date: peter 2016-08-03 17:45:11.795
Context
2016-08-06
18:59
Handle vfs in threads Leaf check-in: a36a8fc470 user: peter tags: thread
2016-08-03
17:45
Merge from trunk check-in: d1d44e3aed user: peter tags: thread
17:44
Added ability to transfer mounts to another thread. check-in: e650f1067c user: peter tags: thread
2016-07-29
22:03
Include plugin options in plugin dialog. check-in: c9e28d0c53 user: peter tags: trunk
Changes
Unified Diff Ignore Whitespace Patch
Changes to Changes.










1
2
3
4
5
6
7










2016-07-01
 When displaying a patch, detect chunks marked with ##.
 E.g. svn diff lists changed properties like that.

2016-06-10
 Reorganised code for option descriptions.

>
>
>
>
>
>
>
>
>
>







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
2016-07-29
 Include plugin command line options in command line help.
 Include plugin options in plugin dialog.

2016-07-29
 Corrected right side numbering when parsing patch. [288be8f321]

2016-07-06
 Support negative revisions with GIT. Added log view for GIT.

2016-07-01
 When displaying a patch, detect chunks marked with ##.
 E.g. svn diff lists changed properties like that.

2016-06-10
 Reorganised code for option descriptions.

Changes to htdocs/changes.wiki.
1
2
3
4

5
6
7

8
9
10
11
12
13
14
<title>Changes</title>

Upcoming changes (not yet released):


  *  Word parse now consistenty uses non-space as word char.
  *  New [./table.wiki | table] view, activated by -table, when comparing tables.
  *  Mercurial support for Directory Diff, Commit, Revert and Log.

  *  Printed PDF is now compressed.
  *  Printed PDF from patch view adds page break between files.
  *  Plugins can define command line options they accept.
  *  Plugins can read ::argv to know the given command line.
  *  New plugin for binary files
  *  New plugin for CSV files
  *  Added option -sep, to set a separator that makes input be interpreted




>
|


>







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<title>Changes</title>

Upcoming changes (not yet released):

  *  Corrected right side numbering when parsing patch.
  *  Word parse now consistently uses non-space as word char.
  *  New [./table.wiki | table] view, activated by -table, when comparing tables.
  *  Mercurial support for Directory Diff, Commit, Revert and Log.
  *  GIT support for negative revisions and log view.
  *  Printed PDF is now compressed.
  *  Printed PDF from patch view adds page break between files.
  *  Plugins can define command line options they accept.
  *  Plugins can read ::argv to know the given command line.
  *  New plugin for binary files
  *  New plugin for CSV files
  *  Added option -sep, to set a separator that makes input be interpreted
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
Changes in v2.7 (2015-03-09):

  *  Directory Diff support for GIT, Fossil and Subversion.
     Directly browse and compare two revisions.
  *  Plugins in Directory Diff.
  *  Added option -printFont to select font for PDF generation.
     Default font changed to a True Type font.
     Use "-printFont Courier" to fall back on PDF builtin.
  *  Mac supported

Changes in v2.6.7 (2014-11-13):

  *  Fixed Directory Diff that stopped working in 2.6.6

Changes in v2.6.6 (2014-10-27):







|







24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
Changes in v2.7 (2015-03-09):

  *  Directory Diff support for GIT, Fossil and Subversion.
     Directly browse and compare two revisions.
  *  Plugins in Directory Diff.
  *  Added option -printFont to select font for PDF generation.
     Default font changed to a True Type font.
     Use "-printFont Courier" to fall back on PDF built-in.
  *  Mac supported

Changes in v2.6.7 (2014-11-13):

  *  Fixed Directory Diff that stopped working in 2.6.6

Changes in v2.6.6 (2014-10-27):
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
  *  Support negative revisions with Fossil.
  *  Added -nocdiff command line flag for debug.
  *  Fixed a bug where alignment was not properly shown in output.
  *  Fixed out-of-stack crash.

Changes in v2.6.1 (2011-11-01):

  *  Eskil [http://eskil.tcl.tk|rehosted] and changed to use [http://www.fossil-scm.org|Fossil].
  *  Fixed directory diff appearance on Windows.
  *  Fixed bug where copy button in directory diff picked the wrong file.
  *  Fixed bug where plugins were not found in VFS.

Changes in v2.6 (2011-10-30):

  *  Support commit in Git and Fossil.
  *  Support commit, list of files and revisions with -review.
  *  Added Paste Patch command.
  *  New -pluginlist option. New GUI for plugin selection.
  *  Added three-way merge.
  *  Autodetect line endings in ancestor file to select merge output.
  *  Fully rewritten directory diff with new design.
  *  Set alignment with drag & drop.

Changes in v2.5 (2011-04-01):

  *  Requires Tcl 8.5.
  *  Plugins: Added dump, better documentation.







|











|







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
  *  Support negative revisions with Fossil.
  *  Added -nocdiff command line flag for debug.
  *  Fixed a bug where alignment was not properly shown in output.
  *  Fixed out-of-stack crash.

Changes in v2.6.1 (2011-11-01):

  *  Eskil [http://eskil.tcl.tk|re-hosted] and changed to use [http://www.fossil-scm.org|Fossil].
  *  Fixed directory diff appearance on Windows.
  *  Fixed bug where copy button in directory diff picked the wrong file.
  *  Fixed bug where plugins were not found in VFS.

Changes in v2.6 (2011-10-30):

  *  Support commit in Git and Fossil.
  *  Support commit, list of files and revisions with -review.
  *  Added Paste Patch command.
  *  New -pluginlist option. New GUI for plugin selection.
  *  Added three-way merge.
  *  Auto-detect line endings in ancestor file to select merge output.
  *  Fully rewritten directory diff with new design.
  *  Set alignment with drag & drop.

Changes in v2.5 (2011-04-01):

  *  Requires Tcl 8.5.
  *  Plugins: Added dump, better documentation.
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138

  *  Added experimental -nonewline command option.
  *  Added -close command option.
  *  Added experimental PDF print.
  *  Added dirdiff preferences and filters.
  *  Smarter save in merge. FR 2957
  *  Added commit button for CVS. FR 2780
  *  Bug fixes include: Kits are mounted readonly, fixed dir diff window menu,
     improved patch file parsing.

Changes in v2.1 (2006-06-02):

  *  Added -preprocess command line option.
  *  Added -foreach command line option.
  *  Added -context command line option.







|







126
127
128
129
130
131
132
133
134
135
136
137
138
139
140

  *  Added experimental -nonewline command option.
  *  Added -close command option.
  *  Added experimental PDF print.
  *  Added dirdiff preferences and filters.
  *  Smarter save in merge. FR 2957
  *  Added commit button for CVS. FR 2780
  *  Bug fixes include: Kits are mounted read-only, fixed dir diff window menu,
     improved patch file parsing.

Changes in v2.1 (2006-06-02):

  *  Added -preprocess command line option.
  *  Added -foreach command line option.
  *  Added -context command line option.
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
  *  Added context options for "Diffs only" mode.
  *  Rewrote how "Separate Diff" and "Align" works.  The latter now only happens after a "Redo Diff".
  *  Added scroll map and some more context menu options in Directory Diff.

Changes in v2.0.2 (2004-05-03):

  *  Fixed a bug in ClearCase support.
  *  Improved enscipt usage in print command.
  *  Added "mark file" in dirdiff context menu.

Changes in v2.0.1 (2004-02-10):

  *  Added preference for width and height.
  *  Added Tools menu to directory diff window.
  *  Made it simpler to save a conflict in the same file.

First public release v2.0 (2004-01-30):







|









169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
  *  Added context options for "Diffs only" mode.
  *  Rewrote how "Separate Diff" and "Align" works.  The latter now only happens after a "Redo Diff".
  *  Added scroll map and some more context menu options in Directory Diff.

Changes in v2.0.2 (2004-05-03):

  *  Fixed a bug in ClearCase support.
  *  Improved enscript usage in print command.
  *  Added "mark file" in dirdiff context menu.

Changes in v2.0.1 (2004-02-10):

  *  Added preference for width and height.
  *  Added Tools menu to directory diff window.
  *  Made it simpler to save a conflict in the same file.

First public release v2.0 (2004-01-30):
Changes to htdocs/plugins.wiki.
20
21
22
23
24
25
26


27
28
29
30
31
32
33
  *  -plugin plugin     : Use plugin
  *  -plugininfo info   : Pass info to plugin (plugin specific)
  *  -plugindump plugin : Dump plugin source to stdout
  *  -pluginlist        : List known plugins
  *  -pluginallow       : Allow full access privilege for a plugin.

A plugin may further define command line options that it accepts.



<h1>General Format</h1>

A plugin is a Tcl script file that must start with the verbatim sequence
"##Eskil Plugin :". A plugin is sourced and used in its own safe
interpreter and thus have free access to its own global space. Hookup
points are defined by declaring specifically named procedures as specified







>
>







20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
  *  -plugin plugin     : Use plugin
  *  -plugininfo info   : Pass info to plugin (plugin specific)
  *  -plugindump plugin : Dump plugin source to stdout
  *  -pluginlist        : List known plugins
  *  -pluginallow       : Allow full access privilege for a plugin.

A plugin may further define command line options that it accepts.
A way to see the plugin's options is to do:
<pre>eskil -plugin &lt;plg&gt; -help</pre>

<h1>General Format</h1>

A plugin is a Tcl script file that must start with the verbatim sequence
"##Eskil Plugin :". A plugin is sourced and used in its own safe
interpreter and thus have free access to its own global space. Hookup
points are defined by declaring specifically named procedures as specified
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
<h2>Additional options</h2>
A plugin can declare command line options that should be accepted by Eskil.
They will be passed on to the plugin through the ::argv list.
If the initial "##Eskil" line is followed by comments formatted as below,
it adds options. Any empty line will end parsing for such lines.

A line like "## Option -<option>" declares an option that takes a value and
a line lile "## Flag -<option>" declares an option without value. The rest of
the line after the option name is ignored and can be used for comments.



<h1>File plugin</h1>

To process the files being compared, the following procedure should be
defined in the plugin file:

<pre>proc PreProcess {side chi cho} {...}</pre>

The arguments given to PrePrecess are:

  *  side : left or right, indicating which file is being handled
  *  chi : An input channel for reading the original file
  *  cho : An output channel for writing the processed file

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

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

If Eskil is run with Tcl 8.6 or newer, PreProcess is run as a couroutine
and may yield. The left and right side will then be called alternately
until they return. This allows a plugin to take both sides into account
for decisions if needed.

<h1>Directory plugin</h1>

To be used for file comparison in a directory diff, the following procedure







|
|
>
>








|













|







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
<h2>Additional options</h2>
A plugin can declare command line options that should be accepted by Eskil.
They will be passed on to the plugin through the ::argv list.
If the initial "##Eskil" line is followed by comments formatted as below,
it adds options. Any empty line will end parsing for such lines.

A line like "## Option -<option>" declares an option that takes a value and
a line like "## Flag -<option>" declares an option without value. The rest of
the line after the option name is functionally ignored and can be used for
comments. It is included in command line help, so the rest should preferably
be formatted as " : Explanation" if used.

<h1>File plugin</h1>

To process the files being compared, the following procedure should be
defined in the plugin file:

<pre>proc PreProcess {side chi cho} {...}</pre>

The arguments given to PreProcess are:

  *  side : left or right, indicating which file is being handled
  *  chi : An input channel for reading the original file
  *  cho : An output channel for writing the processed file

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

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

If Eskil is run with Tcl 8.6 or newer, PreProcess is run as a coroutine
and may yield. The left and right side will then be called alternately
until they return. This allows a plugin to take both sides into account
for decisions if needed.

<h1>Directory plugin</h1>

To be used for file comparison in a directory diff, the following procedure
Changes to htdocs/revision.wiki.
1
2
3
4
5
6
7
8
9
10
11
12
<title>Revision Control Support</title>

<h1>Introduction</h1>

Eskil can compare versions in meny revision control systems.
Currently RCS, CVS, Git, Fossil, Mercurial, Bazaar, Subversion, Perforce and ClearCase are supported (some features are not implemented for all systems).

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

By default the local file is compared against the latest checked in version.




|







1
2
3
4
5
6
7
8
9
10
11
12
<title>Revision Control Support</title>

<h1>Introduction</h1>

Eskil can compare versions in many revision control systems.
Currently RCS, CVS, Git, Fossil, Mercurial, Bazaar, Subversion, Perforce and ClearCase are supported (some features are not implemented for all systems).

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

By default the local file is compared against the latest checked in version.
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
<pre>git config --global diff.tool eskil</pre>
<pre>git config --global difftool.eskil.cmd 'eskil $LOCAL $REMOTE'</pre>

<h2>Fossil</h2>

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

Additionaly, if a revision is zero or a negative integer, the log is searched backwards
for earlier versions. E.g. -1 gives the second to last version. The search
follows the current branch from the current version.

To use Eskil for conflict resolution these settings can be used.

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








|







102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
<pre>git config --global diff.tool eskil</pre>
<pre>git config --global difftool.eskil.cmd 'eskil $LOCAL $REMOTE'</pre>

<h2>Fossil</h2>

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

Additionally, if a revision is zero or a negative integer, the log is searched backwards
for earlier versions. E.g. -1 gives the second to last version. The search
follows the current branch from the current version.

To use Eskil for conflict resolution these settings can be used.

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

Changes to htdocs/table.wiki.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<title>Table diff</title>
<h1>Table diff</h1>

Eskil can compare tables in comma/tab separated text files and display
them like a table.

<verbatim>
eskil -table apa1.csv apa2.csv
</verbatim>

Eskil will try to autodetect the separator but you can also give it
using -sep. Example for tab separation:

<verbatim>
eskil -table -sep '\t' apa1.csv apa2.csv
</verbatim>

Eskil has a built in plugin, csv, than can preprocess table files. This example clears the "Short" and "Long" columns before comparison:










|







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<title>Table diff</title>
<h1>Table diff</h1>

Eskil can compare tables in comma/tab separated text files and display
them like a table.

<verbatim>
eskil -table apa1.csv apa2.csv
</verbatim>

Eskil will try to auto-detect the separator but you can also give it
using -sep. Example for tab separation:

<verbatim>
eskil -table -sep '\t' apa1.csv apa2.csv
</verbatim>

Eskil has a built in plugin, csv, than can preprocess table files. This example clears the "Short" and "Long" columns before comparison:
Changes to plugins/backslash.tcl.
1
2



3
4
5
6
7
8
9
10
11
12
13
14
15
16
##Eskil Plugin : Compare with backslash-newline removed




# 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 replaces any backslash-newline with space, thus
# ignoring restructured lines.

# 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 trim 0
    while {[gets $chi line] >= 0} {

|
>
>
>

|


<
<
<







1
2
3
4
5
6
7
8
9



10
11
12
13
14
15
16
##Eskil Plugin : Compare with backslash-newline removed
#
# This plugin replaces any backslash-newline with space, thus
# ignoring restructured lines.

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




# 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 trim 0
    while {[gets $chi line] >= 0} {
Changes to plugins/binary.tcl.
1
2
3





4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
##Eskil Plugin : Compare binary files, in hex
## Option -binsep : A set of chars to be used as "newline"






# 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

# A plugin may declare command line options that should be allowed through
# to ::argv

# This plugin converts files to hex to be able to compare binary files.
# A set of chars can be defined to be used as "newline". Default "0 10 13".
# Example usage:
# eskil -plugin binary -binsep "0 10 13 32" f1 f2

# 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 delimitL [list 0 10 13]
    if {[llength $::Info] > 0} {


|
>
>
>
>
>

|





<
<
<
<
<







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15





16
17
18
19
20
21
22
##Eskil Plugin : Compare binary files, in hex
## Option -binsep : A set of chars to be used as "newline"
#
# This plugin converts files to hex to be able to compare binary files.
# A set of chars can be defined to be used as "newline". Default "0 10 13".
# Example usage:
# eskil -plugin binary -binsep "0 10 13 32" f1 f2

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

# A plugin may declare command line options that should be allowed through
# to ::argv






# 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 delimitL [list 0 10 13]
    if {[llength $::Info] > 0} {
Changes to plugins/csv.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
##Eskil Plugin : Compare comma separated value (CSV) files
## Option -csvignore : A list of columns to ignore
## Option -csvkey    : A list of columns to sort on before comparison
## Flag   -csvheader : First line is a header line defining names of columns






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

# A plugin may declare command line options that should be allowed through
# to ::argv

# This plugin compares CSV files with some preprocessing available
# Example usage:
# eskil -plugin csv -csvignore "head3 head5" -csvkey head2 -sep , \
#       examples/dir*/csv1.txt

# 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} {
    # Look for parameters in command line
    set opts(-sep) ","




>
>
>
>
>








<
<
<
<
<







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





18
19
20
21
22
23
24
##Eskil Plugin : Compare comma separated value (CSV) files
## Option -csvignore : A list of columns to ignore
## Option -csvkey    : A list of columns to sort on before comparison
## Flag   -csvheader : First line is a header line defining names of columns
#
# This plugin compares CSV files with some preprocessing available
# Example usage:
# eskil -plugin csv -csvignore "head3 head5" -csvkey head2 -sep , \
#       examples/dir*/csv1.txt

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

# A plugin may declare command line options that should be allowed through
# to ::argv






# 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} {
    # Look for parameters in command line
    set opts(-sep) ","
Changes to plugins/grep.tcl.
1
2
3




4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
##Eskil Plugin : Compare after filtering lines
## Option -grepre : Regexp to filter on





# 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

# A plugin may declare command line options that should be allowed through
# to ::argv

# This plugin only compares lines that match a pattern.
# Example usage:
# eskil -plugin grep -grepre "<t>" f1 f2

# 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} {
    if {[catch {llength $::Info}]} {
        puts $cho "Grep plugin needs -plugininfo parameter to be a list"


|
>
>
>
>

|





<
<
<
<







1
2
3
4
5
6
7
8
9
10
11
12
13
14




15
16
17
18
19
20
21
##Eskil Plugin : Compare after filtering lines
## Option -grepre : Regexp to filter on
#
# This plugin only compares lines that match a regexp pattern.
# Example usage:
# eskil -plugin grep -grepre "<t>" f1 f2

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

# A plugin may declare command line options that should be allowed through
# to ::argv





# 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} {
    if {[catch {llength $::Info}]} {
        puts $cho "Grep plugin needs -plugininfo parameter to be a list"
Changes to plugins/keyword.tcl.
1
2



3
4
5
6
7
8
9
10
11
12
13
14
15
16
##Eskil Plugin : Ignore $Keywords$




# 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 ignores keywords like $Revision$, both in file diff
# and in directory diff

# 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} {
    while {1} {
        # Read data in large chunks for speed

|
>
>
>

|


<
<
<







1
2
3
4
5
6
7
8
9



10
11
12
13
14
15
16
##Eskil Plugin : Ignore $Keywords$
#
# This plugin ignores keywords like $Revision$, both in file diff
# and in directory diff

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




# 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} {
    while {1} {
        # Read data in large chunks for speed
Changes to plugins/nocase.tcl.
1
2



3
4
5
6
7
8
9
10
11
12
13
14
15
16
##Eskil Plugin : Case insensitive matching




# 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 implements case insensitive matching, similar to the
# -nocase flag.

# 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} {
    while {1} {
        # Read data in large chunks for speed

|
>
>
>

|


<
<
<







1
2
3
4
5
6
7
8
9



10
11
12
13
14
15
16
##Eskil Plugin : Case insensitive matching
#
# This plugin implements case insensitive matching, similar to the
# -nocase flag.

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




# 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} {
    while {1} {
        # Read data in large chunks for speed
Changes to plugins/pdf.tcl.
1
2
3
4
5
6
7
8
9
10




11
12
13
14
15
16
17
##Eskil Plugin : Compare text from PDF files. (needs pdftotext)

# 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 runs input through the external tool pdftotext.
# Thus it must be run together with the -pluginallow flag.
# Anything given in -plugininfo is passed as parameters to pdftotext.





# 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} {
    if {[info commands exec] eq ""} {
        puts $cho "PDF plugin must be run with privilege to be able\

|
<
<
<
<




>
>
>
>







1
2




3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
##Eskil Plugin : Compare text from PDF files. (needs pdftotext)
#




# This plugin runs input through the external tool pdftotext.
# Thus it must be run together with the -pluginallow flag.
# Anything given in -plugininfo is passed as parameters to pdftotext.

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

# 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} {
    if {[info commands exec] eq ""} {
        puts $cho "PDF plugin must be run with privilege to be able\
Changes to plugins/sort.tcl.
1
2


3
4
5
6
7
8
9
10
11
12
13
14
15
##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 files after sorting the lines in each side

# 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

|
>
>

|


<
<







1
2
3
4
5
6
7
8


9
10
11
12
13
14
15
##Eskil Plugin : Compare files after sorting lines
#
# This plugin compares files after sorting the lines in each side

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



# 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
Changes to plugins/swap.tcl.
1
2



3
4
5
6
7
8
9
10
11
12
13
14
15
16
##Eskil Plugin : Swap sides of contents




# 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 swaps data between files. A fairly useless thing.
# This is to test and exemplify how to use yield in a plugin.

# 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} {
    if {[info commands yield] eq ""} {
        puts $cho "Swap plugin must be run with Tcl 8.6 or newer"

|
>
>
>

|


<
<
<







1
2
3
4
5
6
7
8
9



10
11
12
13
14
15
16
##Eskil Plugin : Swap sides of contents
#
# This plugin swaps data between files. A fairly useless thing.
# This is to test and exemplify how to use yield in a plugin.

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




# 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} {
    if {[info commands yield] eq ""} {
        puts $cho "Swap plugin must be run with Tcl 8.6 or newer"
Changes to plugins/words.tcl.
1
2


3
4
5
6
7
8
9
10
11
12
13
14
15
##Eskil Plugin : Compare set of words



# 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} {
    while {[gets $chi line] >= 0} {
        foreach word [regexp -all -inline {\w+} $line] {

|
>
>

|


<
<







1
2
3
4
5
6
7
8


9
10
11
12
13
14
15
##Eskil Plugin : Compare set of words
#
# This plugin compares the set of words in files.

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



# 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} {
    while {[gets $chi line] >= 0} {
        foreach word [regexp -all -inline {\w+} $line] {
Changes to src/eskil.tcl.
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
        # We are in a -u style diff
        if {$state eq "both"} {
            if {![regexp {^[\s+-]} $line]} continue
            set sig [string trim [string index $line 0]]
            set str [string range $line 1 end]
            if {$sig eq ""} {
                lappend leftLines [list $leftLine "" $str]
                lappend rightLines [list $leftLine "" $str]
                incr leftLine
                incr rightLine
            } elseif {$sig eq "-"} {
                lappend leftLines [list $leftLine "-" $str]
                incr leftLine
            } else {
                lappend rightLines [list $leftLine "+" $str]
                incr rightLine
            }
            continue
        }
    }
    if {$state != "none"} {
        displayOnePatch $top $leftLines $rightLines $leftLine $rightLine







|






|







1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
1257
1258
        # We are in a -u style diff
        if {$state eq "both"} {
            if {![regexp {^[\s+-]} $line]} continue
            set sig [string trim [string index $line 0]]
            set str [string range $line 1 end]
            if {$sig eq ""} {
                lappend leftLines [list $leftLine "" $str]
                lappend rightLines [list $rightLine "" $str]
                incr leftLine
                incr rightLine
            } elseif {$sig eq "-"} {
                lappend leftLines [list $leftLine "-" $str]
                incr leftLine
            } else {
                lappend rightLines [list $rightLine "+" $str]
                incr rightLine
            }
            continue
        }
    }
    if {$state != "none"} {
        displayOnePatch $top $leftLines $rightLines $leftLine $rightLine
Changes to src/plugin.tcl.
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
    set dirs [list . ./plugins]
    lappend dirs [file join $::eskil(thisDir) .. ..]
    lappend dirs [file join $::eskil(thisDir) .. .. plugins]
    lappend dirs [file join $::eskil(thisDir) .. plugins]
    return $dirs
}

# Locate plugin source
proc LocatePlugin {plugin} {

    set src ""
    set dirs [PluginSearchPath]

    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
            set ch [open $file r]
            set data [read $ch 20]
            close $ch
            if {[string match "##Eskil Plugin*" $data]} {
                set src $file
                break
            }
        }
        if {$src ne ""} break
    }



















    return $src
}

proc createPluginInterp {plugin info allow pinfoName} {
    upvar 1 $pinfoName pinfo
    set src [LocatePlugin $plugin]


    if {$src eq ""} {
        return ""
    }

    # Create interpreter and load source
    if {$allow} {







|

>
|















|



|

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




|
>







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
    set dirs [list . ./plugins]
    lappend dirs [file join $::eskil(thisDir) .. ..]
    lappend dirs [file join $::eskil(thisDir) .. .. plugins]
    lappend dirs [file join $::eskil(thisDir) .. plugins]
    return $dirs
}

# Locate plugin source and extract some info
proc LocatePlugin {plugin} {
    set res [dict create name "" src "" opts ""]
    set fSrc ""
    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
            set ch [open $file r]
            set data [read $ch 20]
            close $ch
            if {[string match "##Eskil Plugin*" $data]} {
                set fSrc $file
                break
            }
        }
        if {$fSrc ne ""} break
    }
    if {$fSrc ne ""} {
        dict set res name $plugin
        dict set res src  $fSrc
        # Look for declarations of command line options
        set ch [open $fSrc r]
        while {[gets $ch line] >= 0} {
            # Only look until empty line
            if {[string trim $line] eq ""} break
            if {[regexp {^\#\# Option\s+(\S+)(.*)} $line -> name rest]} {
                # structure is name flag doc
                dict lappend res opts $name 0 [string trim $rest " :"]
            }
            if {[regexp {^\#\# Flag\s+(\S+)(.*)} $line -> name rest]} {
                dict lappend res opts $name 1 [string trim $rest " :"]
            }
        }
        close $ch
    }

    return $res
}

proc createPluginInterp {plugin info allow pinfoName} {
    upvar 1 $pinfoName pinfo
    set res [LocatePlugin $plugin]
    set src [dict get $res src]

    if {$src eq ""} {
        return ""
    }

    # Create interpreter and load source
    if {$allow} {
94
95
96
97
98
99
100
101
102

103
104
105
106
107
108









109
110
111
112
113
114
115
    if {[$pi eval info proc FileCompare] ne ""} {
        dict set pinfo dir 1
    }

    return $pi
}

proc printPlugin {plugin} {
    set src [LocatePlugin $plugin]

    if {$src eq ""} {
        printPlugins
        return
    }
    set ch [open $src]
    puts -nonewline [read $ch]









    close $ch
}

proc listPlugins {} {
    set dirs [PluginSearchPath]
    set result {}








|
|
>





|
>
>
>
>
>
>
>
>
>







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
    if {[$pi eval info proc FileCompare] ne ""} {
        dict set pinfo dir 1
    }

    return $pi
}

proc printPlugin {plugin {short 0}} {
    set res [LocatePlugin $plugin]
    set src [dict get $res src]
    if {$src eq ""} {
        printPlugins
        return
    }
    set ch [open $src]
    set lines [split [read $ch] \n]
    foreach line $lines {
        set line [string trim $line]
        if {$short} {
            if {![string match "#*" $line]} {
                break
            }
        }
        puts $line
    }
    close $ch
}

proc listPlugins {} {
    set dirs [PluginSearchPath]
    set result {}

170
171
172
173
174
175
176




177
178
179
180
181
182
183
184
    }
}

proc preparePlugin {top} {
    disallowEdit $top
    set allow [dict get $::eskil($top,pluginpinfo) allow]
    # Pass ::argv to plugin




    $::eskil($top,plugin) eval [list set ::argv $::eskil(argv)]
    # Pass ::Pref to plugin
    $::eskil($top,plugin) eval [list array set ::Pref [array get ::Pref]]
    # Pass File info to plugin
    $::eskil($top,plugin) eval [list set ::File(left)  $::eskil($top,leftFile)]
    $::eskil($top,plugin) eval [list set ::File(right) $::eskil($top,rightFile)]

    set out1 [tmpFile]







>
>
>
>
|







201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
    }
}

proc preparePlugin {top} {
    disallowEdit $top
    set allow [dict get $::eskil($top,pluginpinfo) allow]
    # Pass ::argv to plugin
    set pArgv $::eskil(argv)
    if {[info exists ::eskil($top,pluginargv)]} {
        lappend pArgv {*}$::eskil($top,pluginargv)
    }
    $::eskil($top,plugin) eval [list set ::argv $pArgv]
    # Pass ::Pref to plugin
    $::eskil($top,plugin) eval [list array set ::Pref [array get ::Pref]]
    # Pass File info to plugin
    $::eskil($top,plugin) eval [list set ::File(left)  $::eskil($top,leftFile)]
    $::eskil($top,plugin) eval [list set ::File(right) $::eskil($top,rightFile)]

    set out1 [tmpFile]
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
    }
    if {![info exists ::eskil($top,pluginallow)]} {
        set ::eskil($top,pluginallow) 0
    }
    set ::eskil($top,edit,pluginname) $::eskil($top,pluginname)
    set ::eskil($top,edit,plugininfo) $::eskil($top,plugininfo)
    set ::eskil($top,edit,pluginallow) $::eskil($top,pluginallow)




    set t 0
    foreach {plugin info} $plugins {
        set descr [dict get $info descr]
        if {$dirdiff && ![dict get $info dir]} continue
        ttk::radiobutton $wt.rb$t -variable ::eskil($top,edit,pluginname) \
                -value $plugin -text $plugin
        ttk::label $wt.l$t -text $descr -anchor w
        grid $wt.rb$t $wt.l$t -sticky we -padx 3 -pady 3
        incr t
    }
    ttk::radiobutton $wt.rb$t -variable ::eskil($top,edit,pluginname) \
            -value "" -text "No Plugin"



    grid $wt.rb$t -sticky we -padx 3 -pady 3





    ttk::label $wt.li -text "Info" -anchor w
    addBalloon $wt.li "Info passed to plugin. Plugin specific."
    ttk::entry $wt.ei -textvariable ::eskil($top,edit,plugininfo)
    grid $wt.li $wt.ei -sticky we -padx 3 -pady 3

    ttk::checkbutton $wt.cb -text "Privilege" \
            -variable ::eskil($top,edit,pluginallow)
    addBalloon $wt.cb "Run plugin with raised privileges"
    grid $wt.cb - -sticky w -padx 3 -pady 3







    ttk::frame $wt.fb -padding 3
    ttk::button $wt.fb.b1 -text "Ok"   \
            -command [list EditPrefPluginsOk $top $wt]
    ttk::button $wt.fb.b2 -text "Show" \
            -command "ShowPlugin $wt \$::eskil($top,edit,pluginname)"
    addBalloon $wt.fb.b2 "Show plugin source code."
    ttk::button $wt.fb.b3 -text "Cancel" -command [list destroy $wt]
    set ::widgets($top,prefPluginsOk) $wt.fb.b1

    grid $wt.fb.b1 x $wt.fb.b2 x $wt.fb.b3 -sticky we
    grid columnconfigure $wt.fb {0 2 4} -uniform a
    grid columnconfigure $wt.fb {1 3} -weight 1




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







































proc EditPrefPluginsOk {top wt} {

    destroy $wt

    set ::eskil($top,pluginname) $::eskil($top,edit,pluginname)
    set ::eskil($top,plugininfo) $::eskil($top,edit,plugininfo)
    set ::eskil($top,pluginallow) $::eskil($top,edit,pluginallow)
    if {$::eskil($top,pluginname) ne ""} {
        set pinterp [createPluginInterp $::eskil($top,pluginname) \
                             $::eskil($top,plugininfo) \
                             $::eskil($top,pluginallow) pinfo]
    } else {
        set pinterp ""
        set pinfo ""
    }
    set ::eskil($top,plugin) $pinterp
    set ::eskil($top,pluginpinfo) $pinfo














}

# Put Tcl code in a text widget, with some syntax highlighting
proc TextViewTcl {t data} {
    $t tag configure comment -foreground "#b22222"
    foreach line [split $data \n] {
        if {[regexp {^\s*#} $line]} {
            $t insert end $line\n comment
        } elseif {[regexp {^(.*;\s*)(#.*)$} $line -> pre post]} {
            $t insert end $pre
            $t insert end $post\n comment
        } else {
            $t insert end $line\n
        }
    }
}

# Show plugin source
proc ShowPlugin {parent plugin} {
    set src [LocatePlugin $plugin]

    if {$src eq ""} return
    set ch [open $src]
    set data [read $ch]
    close $ch
    set wt $parent.plugin
    if {[winfo exists $wt]} {
        wm deiconify $wt







>
>
>
>







|




>
>
>
|
>
>
>
>




|




|

>
>
>
>
>
>


|
|
|
<







>
>
>
|
|


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













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



















|
>







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
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
    }
    if {![info exists ::eskil($top,pluginallow)]} {
        set ::eskil($top,pluginallow) 0
    }
    set ::eskil($top,edit,pluginname) $::eskil($top,pluginname)
    set ::eskil($top,edit,plugininfo) $::eskil($top,plugininfo)
    set ::eskil($top,edit,pluginallow) $::eskil($top,pluginallow)

    ttk::labelframe $wt.lfs -text "Select"
    grid columnconfigure $wt.lfs 1 -weight 1

    set t 0
    foreach {plugin info} $plugins {
        set descr [dict get $info descr]
        if {$dirdiff && ![dict get $info dir]} continue
        ttk::radiobutton $wt.rb$t -variable ::eskil($top,edit,pluginname) \
                -value $plugin -text $plugin
        ttk::label $wt.l$t -text $descr -anchor w
        grid $wt.rb$t $wt.l$t -in $wt.lfs -sticky we -padx 3 -pady 3
        incr t
    }
    ttk::radiobutton $wt.rb$t -variable ::eskil($top,edit,pluginname) \
            -value "" -text "No Plugin"
    ttk::button $wt.bs -text "Show" \
            -command "ShowPlugin $wt \$::eskil($top,edit,pluginname)"
    addBalloon $wt.bs "Show plugin source code."
    grid $wt.rb$t $wt.bs -in $wt.lfs -sticky we -padx 3 -pady 3
    grid $wt.bs -sticky e

    ttk::labelframe $wt.lfgc -text "Generic Configuration"
    grid columnconfigure $wt.lfgc 1 -weight 1

    ttk::label $wt.li -text "Info" -anchor w
    addBalloon $wt.li "Info passed to plugin. Plugin specific."
    ttk::entry $wt.ei -textvariable ::eskil($top,edit,plugininfo)
    grid $wt.li $wt.ei -in $wt.lfgc -sticky we -padx 3 -pady 3

    ttk::checkbutton $wt.cb -text "Privilege" \
            -variable ::eskil($top,edit,pluginallow)
    addBalloon $wt.cb "Run plugin with raised privileges"
    grid $wt.cb -  -in $wt.lfgc -sticky w -padx 3 -pady 3

    ttk::labelframe $wt.lfsc -text "Specific Configuration"
    set ::widgets($top,prefPluginsSpec) $wt.lfsc
    trace add variable ::eskil($top,edit,pluginname) write \
            [list UpdateSpecificPluginConf $top]
    UpdateSpecificPluginConf $top
    
    ttk::frame $wt.fb -padding 3
    ttk::button $wt.fb.b1 -text "Ok"   \
            -command [list EditPrefPluginsOk $top $wt 0]
    ttk::button $wt.fb.b2 -text "Apply" \
            -command [list EditPrefPluginsOk $top $wt 1]

    ttk::button $wt.fb.b3 -text "Cancel" -command [list destroy $wt]
    set ::widgets($top,prefPluginsOk) $wt.fb.b1

    grid $wt.fb.b1 x $wt.fb.b2 x $wt.fb.b3 -sticky we
    grid columnconfigure $wt.fb {0 2 4} -uniform a
    grid columnconfigure $wt.fb {1 3} -weight 1

    grid $wt.lfs  -sticky we -padx 3 -pady 3
    grid $wt.lfgc -sticky we -padx 3 -pady 3
    grid $wt.lfsc -sticky we -padx 3 -pady 3
    grid $wt.fb   -sticky we -padx 3 -pady 3
    grid columnconfigure $wt 0 -weight 1
}

proc UpdateSpecificPluginConf {top args} {
    set w $::widgets($top,prefPluginsSpec)
    # If the dialog is closed w might not exist
    if {![winfo exists $w]} return
    eval destroy [winfo children $w]

    set arg $::eskil($top,edit,pluginname)
    set pOpts {}
    if {$arg ne ""} {
        set res [LocatePlugin $arg]
        set pOpts [dict get $res opts]
    }
    # Look for declarations of command line options
    set n 0
    set ::eskil($top,edit,opts) $pOpts
    foreach {name flag doc} $pOpts {
        ttk::label $w.l$n -text $name
        addBalloon $w.l$n $doc
        grid $w.l$n -sticky "w" -padx 3 -pady 3
        if {$flag} {
            ttk::checkbutton $w.s$n -text "On" \
                    -variable ::eskil($top,edit,$name)
            grid $w.s$n -row $n -column 1 -sticky "w" -padx 3 -pady 3
        } else {
            ttk::entry $w.s$n \
                    -textvariable ::eskil($top,edit,$name)
            grid $w.s$n -row $n -column 1 -sticky we -padx 3 -pady 3
        }
        incr n
    }
    grid columnconfigure $w 1 -weight 1
    if {$n == 0} {
        ttk::label $w.l -text "No specific configuration"
        grid $w.l -sticky "w" -padx 3 -pady 3
        return
    }
}

proc EditPrefPluginsOk {top wt apply} {
    if {!$apply} {
        destroy $wt
    }
    set ::eskil($top,pluginname) $::eskil($top,edit,pluginname)
    set ::eskil($top,plugininfo) $::eskil($top,edit,plugininfo)
    set ::eskil($top,pluginallow) $::eskil($top,edit,pluginallow)
    if {$::eskil($top,pluginname) ne ""} {
        set pinterp [createPluginInterp $::eskil($top,pluginname) \
                             $::eskil($top,plugininfo) \
                             $::eskil($top,pluginallow) pinfo]
    } else {
        set pinterp ""
        set pinfo ""
    }
    set ::eskil($top,plugin) $pinterp
    set ::eskil($top,pluginpinfo) $pinfo
    set ::eskil($top,pluginargv) {}
    foreach {name flag doc} $::eskil($top,edit,opts) {
        if {$flag} {
            if {[info exists ::eskil($top,edit,$name)] && \
                        $::eskil($top,edit,$name)} {
                lappend ::eskil($top,pluginargv) $name
            }
        } else {
            if {[info exists ::eskil($top,edit,$name)] && \
                        $::eskil($top,edit,$name) ne ""} {
                lappend ::eskil($top,pluginargv) $name $::eskil($top,edit,$name)
            }
        }
    }
}

# Put Tcl code in a text widget, with some syntax highlighting
proc TextViewTcl {t data} {
    $t tag configure comment -foreground "#b22222"
    foreach line [split $data \n] {
        if {[regexp {^\s*#} $line]} {
            $t insert end $line\n comment
        } elseif {[regexp {^(.*;\s*)(#.*)$} $line -> pre post]} {
            $t insert end $pre
            $t insert end $post\n comment
        } else {
            $t insert end $line\n
        }
    }
}

# Show plugin source
proc ShowPlugin {parent plugin} {
    set res [LocatePlugin $plugin]
    set src [dict get $res src]
    if {$src eq ""} return
    set ch [open $src]
    set data [read $ch]
    close $ch
    set wt $parent.plugin
    if {[winfo exists $wt]} {
        wm deiconify $wt
Changes to src/rev.tcl.
652
653
654
655
656
657
658





























659
660
661
662
663
664
665
            if {[regexp {r(\d+)} $line -> rev]} {
                lappend revs $rev
            }
        }
    }
    return $revs
}






























# Return revision list of a FOSSIL file
proc eskil::rev::FOSSIL::GetRevList {filename} {
    # Keep on current branch
    set x [exec fossil branch list]
    if { ! [regexp -line {^\* (.*)$} $x -> branch]} {
        set branch ""







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







652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
            if {[regexp {r(\d+)} $line -> rev]} {
                lappend revs $rev
            }
        }
    }
    return $revs
}

# Return revision list of a GIT file
proc eskil::rev::GIT::GetRevList {filename} {
    set old ""
    set cmd [list exec git log --first-parent --oneline -n 50]
    if {$filename eq ""} {
        # Nothing
    } else {
        set old [pwd]
        cd [file dirname $filename]
        lappend cmd [file nativename [file tail $filename]]
    }
    if {[catch {eval $cmd} res]} {
        # What to do here?
        set revs [list HEAD]
    } else {
        set lines [split $res \n]
        set revs {}
        foreach line $lines {
            if {[regexp {^(\w+)} $line -> rev]} {
                lappend revs $rev
            }
        }
    }
    if {$old ne ""} {
        cd $old
    }
    return $revs
}

# Return revision list of a FOSSIL file
proc eskil::rev::FOSSIL::GetRevList {filename} {
    # Keep on current branch
    set x [exec fossil branch list]
    if { ! [regexp -line {^\* (.*)$} $x -> branch]} {
        set branch ""
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
        # RCS does not support tree versions
        return {}
    }
    return $revs
}

# Figure out GIT revision from arguments

proc eskil::rev::GIT::ParseRevs {filename revs} {
    set result ""
    foreach rev $revs {
        switch -glob -- $rev {
            0 - _ { # _ is a common shortcut for current
                lappend result HEAD
            }



            HEAD - master - * { # Let anything through for now

                lappend result $rev

            }
        }


    }
    return $result
}

# Figure out FOSSIL revision from arguments
proc eskil::rev::FOSSIL::ParseRevs {filename revs} {
    set result ""







>



<
|
|
|
>
>
>
|
>
|
>


>
>







753
754
755
756
757
758
759
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
        # RCS does not support tree versions
        return {}
    }
    return $revs
}

# Figure out GIT revision from arguments
# The resulting rev should work with 'git show <rev>:filename'
proc eskil::rev::GIT::ParseRevs {filename revs} {
    set result ""
    foreach rev $revs {

        # Special cases that shortcuts to GIT special names
        if {$rev eq "_" || $rev eq "0"} {set rev HEAD}

        if {[string is integer -strict $rev] && $rev < 0} {
            # A negative integer rev is a relative rev
            set revList [eskil::rev::GIT::GetRevList $filename]

            set rev [lindex $revList [- $rev]]
            if {$rev eq ""} {
                set rev [lindex $revs end]
            }
        }
        # Let anything else through
        lappend result $rev
    }
    return $result
}

# Figure out FOSSIL revision from arguments
proc eskil::rev::FOSSIL::ParseRevs {filename revs} {
    set result ""
1241
1242
1243
1244
1245
1246
1247















1248
1249
1250
1251
1252
1253
1254
proc eskil::rev::SVN::viewLog {top filename revs} {
    set cmd [list exec svn log]
    if {[llength $revs] > 1} {
        lappend cmd -r [join $revs ":"]
    } else {
        lappend cmd -r HEAD:[lindex $revs 0]
    }















    lappend cmd $filename
    if {[catch {eval $cmd} result]} {
        #return
    }
    ViewLog $top $filename $result
}








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







1277
1278
1279
1280
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
proc eskil::rev::SVN::viewLog {top filename revs} {
    set cmd [list exec svn log]
    if {[llength $revs] > 1} {
        lappend cmd -r [join $revs ":"]
    } else {
        lappend cmd -r HEAD:[lindex $revs 0]
    }
    lappend cmd $filename
    if {[catch {eval $cmd} result]} {
        #return
    }
    ViewLog $top $filename $result
}

# View log between displayed versions
proc eskil::rev::GIT::viewLog {top filename revs} {
    set cmd [list exec git log]
    if {[llength $revs] > 1} {
        lappend cmd [join $revs ".."]
    } else {
        lappend cmd [lindex $revs 0]..
    }
    lappend cmd $filename
    if {[catch {eval $cmd} result]} {
        #return
    }
    ViewLog $top $filename $result
}

Changes to src/startup.tcl.
377
378
379
380
381
382
383




384
385
386
387
388
389
390
                set chunk [string range $d 0 $ci-1]
                set d [string trim [string range $d $ci end]]
            }
            puts "$outName$chunk"
            set outName [format %*s $indent ""]
        }
    }




}

#####################################
# Option/flag handling helpers
#####################################
# Validators
proc optValidatePdfColor {opt arg} {







>
>
>
>







377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
                set chunk [string range $d 0 $ci-1]
                set d [string trim [string range $d $ci end]]
            }
            puts "$outName$chunk"
            set outName [format %*s $indent ""]
        }
    }
    if {$::eskil(opts,src) ne ""} {
        puts ""
        printPlugin $::eskil(opts,src) 1
    }
}

#####################################
# Option/flag handling helpers
#####################################
# Validators
proc optValidatePdfColor {opt arg} {
423
424
425
426
427
428
429
430

431
432
433
434
435
436
437
438
439
440
441

442
443
444
445
446
447
448
449
450
451
452


453
454
455
456
457
458
459
460
461
462

463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
        puts [join [lsort -dictionary [pdf4tcl::getPaperSizeList]] \n]
        exit
    }
}
proc optValidatePlugin {opt arg} {
    # We must start up completely to check a plugin
    Init
    set src [LocatePlugin $arg]

    if {$src eq ""} {
        puts "Bad plugin: $arg"
        printPlugins
        exit
    }
    # Look for declarations of command line options
    set ch [open $src r]
    while {[gets $ch line] >= 0} {
        # Only look until empty line
        if {[string trim $line] eq ""} break
        if {[regexp {^\#\# Option\s+(\S+)} $line -> name]} {

            addOpt $name
        }
        if {[regexp {^\#\# Flag\s+(\S+)} $line -> name]} {
            addFlags $name
        }
    }
    close $ch
}
# Option database setup
proc initOpts {} {
    set ::eskil(opts) {}


    set ::eskil(defoptinfo) {
        flag 0
        given 0
        multi 0
        type ""
        validator ""
        filter ""
        sideeffect ""
        shortdescr ""
        longdescr ""

    } 
}
# Add a command line flag that do not take a value
proc addFlags {args} {
    foreach name $args {
        dict set ::eskil(opts) $name 0
        dict set ::eskil(opts,info) $name $::eskil(defoptinfo)
        dict set ::eskil(opts,info) $name flag  1
    }
}
# Document a flag
proc docFlag {name short {long {}}} {
    dict set ::eskil(opts,info) $name shortdescr $short
    dict set ::eskil(opts,info) $name longdescr $long
}

# Flag that affects Pref
proc addPrefFlag {name elem {value 1}} {







|
>






|
<
<
|
|
>


|
|
|
<
<




>
>










>










|







427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442


443
444
445
446
447
448
449
450


451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
        puts [join [lsort -dictionary [pdf4tcl::getPaperSizeList]] \n]
        exit
    }
}
proc optValidatePlugin {opt arg} {
    # We must start up completely to check a plugin
    Init
    set res [LocatePlugin $arg]
    set src [dict get $res src]
    if {$src eq ""} {
        puts "Bad plugin: $arg"
        printPlugins
        exit
    }
    # Look for declarations of command line options
    foreach {name flag doc} [dict get $res opts] {


        if {$flag} {
            addFlags $name
        } else {
            addOpt $name
        }
        docFlag $name "Plugin $arg : $doc"
        addSource $name $arg
    }


}
# Option database setup
proc initOpts {} {
    set ::eskil(opts) {}
    set ::eskil(opts,info) {}
    set ::eskil(opts,src) ""
    set ::eskil(defoptinfo) {
        flag 0
        given 0
        multi 0
        type ""
        validator ""
        filter ""
        sideeffect ""
        shortdescr ""
        longdescr ""
        source ""
    } 
}
# Add a command line flag that do not take a value
proc addFlags {args} {
    foreach name $args {
        dict set ::eskil(opts) $name 0
        dict set ::eskil(opts,info) $name $::eskil(defoptinfo)
        dict set ::eskil(opts,info) $name flag  1
    }
}
# Document a flag or option
proc docFlag {name short {long {}}} {
    dict set ::eskil(opts,info) $name shortdescr $short
    dict set ::eskil(opts,info) $name longdescr $long
}

# Flag that affects Pref
proc addPrefFlag {name elem {value 1}} {
511
512
513
514
515
516
517





518
519
520
521
522
523
524
proc addValidator {name cmd} {
    dict set ::eskil(opts,info) $name validator $cmd
}
# Add a filter command prefix to an Opt
proc addFilter {name cmd} {
    dict set ::eskil(opts,info) $name filter $cmd
}





# Add a sideeffect to an Opt
##nagelfar syntax addSideEffect x c
proc addSideEffect {name script} {
    dict set ::eskil(opts,info) $name sideeffect $script
}
# Add a command line option that takes a value and stores in local opts
proc addOptsOpt {name elem {validator ""}} {







>
>
>
>
>







516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
proc addValidator {name cmd} {
    dict set ::eskil(opts,info) $name validator $cmd
}
# Add a filter command prefix to an Opt
proc addFilter {name cmd} {
    dict set ::eskil(opts,info) $name filter $cmd
}
# Add a source reference to an Opt
proc addSource {name src} {
    set ::eskil(opts,src) $src
    dict set ::eskil(opts,info) $name source $src
}
# Add a sideeffect to an Opt
##nagelfar syntax addSideEffect x c
proc addSideEffect {name script} {
    dict set ::eskil(opts,info) $name sideeffect $script
}
# Add a command line option that takes a value and stores in local opts
proc addOptsOpt {name elem {validator ""}} {
Changes to tests/blocks.test.
1
2
3
4
5
6
7
8
9
10
# Tests for comparing blocks.                               -*- tcl -*-
#----------------------------------------------------------------------
# $Revision$
#----------------------------------------------------------------------

test blocks-1.1 {
    Change-block parsing
} -body {
    set b1 [list "Apa 1" "Bepa 1" "Cepa 1"]
    set b2 [list "Apa 2" "Bepa 2" "Cepa 2"]
<
|
|








1
2
3
4
5
6
7
8
9

#------------------------------------------------------------*- tcl -*-
# Tests for comparing blocks.
#----------------------------------------------------------------------

test blocks-1.1 {
    Change-block parsing
} -body {
    set b1 [list "Apa 1" "Bepa 1" "Cepa 1"]
    set b2 [list "Apa 2" "Bepa 2" "Cepa 2"]
Changes to tests/dir.test.
1
2
3

4
5
6
7
8
9
10
# Tests for comparing directories.                          -*- tcl -*-
#----------------------------------------------------------------------
# $Revision$

#----------------------------------------------------------------------

set ::Pref(dir,ignorekey) 0
set ::Pref(dir,comparelevel) 1
set ::eskil(.dirdiff,plugin) ""

proc testCompareFiles {text1 text2 {sametime 0}} {
<
|
<
>








1

2
3
4
5
6
7
8
9

#------------------------------------------------------------*- tcl -*-

# Tests for comparing directories.
#----------------------------------------------------------------------

set ::Pref(dir,ignorekey) 0
set ::Pref(dir,comparelevel) 1
set ::eskil(.dirdiff,plugin) ""

proc testCompareFiles {text1 text2 {sametime 0}} {
Changes to tests/gui.test.
1
2
3
4
5
6
7
8
9
10
11
#------------------------------------------------------------*- tcl -*-
# Tests for GUI
#----------------------------------------------------------------------
# $Revision$
#----------------------------------------------------------------------

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

proc XauthSecure {} {


<
<







1
2


3
4
5
6
7
8
9
#------------------------------------------------------------*- tcl -*-
# Tests for GUI


#----------------------------------------------------------------------

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

proc XauthSecure {} {
Changes to tests/patch.test.
1
2
3

4
5
6
7
8
9
10
11
12
13







14





15
16
17
18
19
20
21
22

23



24
25
26
27


















28
29
30
31
32
33
34
# Tests for patch file parsingunctions                      -*- tcl -*-
#----------------------------------------------------------------------
# $Revision$

#----------------------------------------------------------------------

# Overload exec during these tests
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}} {
    incr ::_patchfiles
}
stub insertLine {top n line text {tag {equal}} {linetag {}}} {
}
stub addChange {top n tag line1 n1 line2 n2} {}

proc _PatchInit {} {

    set ::_patchfiles 0




}
test patch-1.1 {
    Xxx


















} -body {
    _PatchInit
    set ::testpatch {
Index: vhdl/tb/tb_system.bhv
===================================================================
--- vhdl/tb/tb_system.bhv	(revision 320)
+++ vhdl/tb/tb_system.bhv	(working copy)
<
|
<
>










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

|






>
|
>
>
>
|
|


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








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

#------------------------------------------------------------*- tcl -*-

# Tests for patch file parsing functions
#----------------------------------------------------------------------

# Overload exec during these tests
set ::eskil(gurka,patchFile) ""
set ::eskil(gurka,patchData) ""
stub update args {}
stub getFullPatch {top} {
    return $::testpatch
}
stub displayOnePatch {top leftLines rightLines leftLine rightLine} {
    # Line per patch
    lappend ::_patchfiles(pll) $leftLine
    lappend ::_patchfiles(prl) $rightLine
    # All lines
    foreach l $leftLines {
        lassign $l lline lmode lstr
        lappend ::_patchfiles(ll) $lline
    }
    foreach l $rightLines {
        lassign $l lline lmode lstr
        lappend ::_patchfiles(rl) $lline
    }
}
stub emptyLine {top n {highlight 1}} {
    incr ::_patchfiles(e)
}
stub insertLine {top n line text {tag {equal}} {linetag {}}} {
}
stub addChange {top n tag line1 n1 line2 n2} {}

proc _PatchInit {} {
    set ::_patchfiles(e) 0
    set ::_patchfiles(pll) {}
    set ::_patchfiles(prl) {}
    set ::_patchfiles(ll) {}
    set ::_patchfiles(rl) {}
}

test patch-1.1 {
    Xxx
} -body {
    _PatchInit
    set ::testpatch [string trim {
--- foo.txt	2016-07-10 21:53:36.671932638 -0700
+++ bar.txt	2016-07-10 21:53:54.739860205 -0700
@@ -1 +1,2 @@
+0
 1
@@ -5 +8,9 @@
+0
 1
    }]
    displayPatch gurka
    concat $_patchfiles(ll) $_patchfiles(rl)
} -result {1 5 1 2 8 9}

test patch-1.2 {
    Xxx
} -body {
    _PatchInit
    set ::testpatch {
Index: vhdl/tb/tb_system.bhv
===================================================================
--- vhdl/tb/tb_system.bhv	(revision 320)
+++ vhdl/tb/tb_system.bhv	(working copy)
129
130
131
132
133
134
135
136
137
+                      variable TcDFHeader     : out TcDFH_T);
   
   procedure TcAddrCalc(PresentAddr : in integer;
                        AccWidth    : in DynamicSize_T;

    }
    displayPatch gurka
    set ::_patchfiles
} -result {6}







|

162
163
164
165
166
167
168
169
170
+                      variable TcDFHeader     : out TcDFH_T);
   
   procedure TcAddrCalc(PresentAddr : in integer;
                        AccWidth    : in DynamicSize_T;

    }
    displayPatch gurka
    set ::_patchfiles(e)
} -result {6}
Changes to tests/print.test.
1
2
3
4
5
6
7
8
9
10
# Tests for printing.                                       -*- tcl -*-
#----------------------------------------------------------------------
# $Revision$
#----------------------------------------------------------------------

test print-1.1 {Tab expansion} {FixTextBlock "a\fp\fa" 1.0} {apa}
test print-1.2 {Tab expansion} {FixTextBlock "\tapa"   1.0} {        apa}
test print-1.3 {Tab expansion} {FixTextBlock "\tapa"   1.1} {       apa}
test print-1.4 {Tab expansion} {FixTextBlock "\tapa"   1.7} { apa}
test print-1.5 {Tab expansion} {FixTextBlock "\tapa"   1.8} {        apa}
<
|
|








1
2
3
4
5
6
7
8
9

#------------------------------------------------------------*- tcl -*-
# Tests for printing.
#----------------------------------------------------------------------

test print-1.1 {Tab expansion} {FixTextBlock "a\fp\fa" 1.0} {apa}
test print-1.2 {Tab expansion} {FixTextBlock "\tapa"   1.0} {        apa}
test print-1.3 {Tab expansion} {FixTextBlock "\tapa"   1.1} {       apa}
test print-1.4 {Tab expansion} {FixTextBlock "\tapa"   1.7} { apa}
test print-1.5 {Tab expansion} {FixTextBlock "\tapa"   1.8} {        apa}
Changes to tests/procs.test.
1
2
3

4
5
6
7
8
9
10
# Tests for comparing misc procedures.                      -*- tcl -*-
#----------------------------------------------------------------------
# $Revision$

#----------------------------------------------------------------------

test procs-1.1 {
    Linit
} -body {
    # Make sure argument ordering is same as lindex
    lindex [Linit x 3 4 5] 2 3 4
<
|
<
>








1

2
3
4
5
6
7
8
9

#------------------------------------------------------------*- tcl -*-

# Tests for comparing misc procedures.
#----------------------------------------------------------------------

test procs-1.1 {
    Linit
} -body {
    # Make sure argument ordering is same as lindex
    lindex [Linit x 3 4 5] 2 3 4
Changes to tests/rev.test.
1
2
3
4
5
6
7
8
9
10
# Tests for revision control functions                      -*- tcl -*-
#----------------------------------------------------------------------
# $Revision$
#----------------------------------------------------------------------

# Overload exec during these tests
stub exec {args} {
    set cmd [lindex $args 0]
    switch -- $cmd {
        cleartool {
<
|
|








1
2
3
4
5
6
7
8
9

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

# Overload exec during these tests
stub exec {args} {
    set cmd [lindex $args 0]
    switch -- $cmd {
        cleartool {