Add support for highlighting of multi-line C preprocessor statements.
[enscript.git] / scripts / diffpp.in
1 #!@PERLPROG@
2 # -*- perl -*-
3 #
4 # Pretty-print diff outputs with GNU enscript.
5 # Copyright (c) 1996-1998 Markku Rossi
6 #
7 # Author: Markku Rossi <mtr@iki.fi>
8 #
9
10 #
11 # This file is part of GNU enscript.
12 #
13 # This program is free software; you can redistribute it and/or modify
14 # it under the terms of the GNU General Public License as published by
15 # the Free Software Foundation; either version 2, or (at your option)
16 # any later version.
17 #
18 # This program is distributed in the hope that it will be useful,
19 # but WITHOUT ANY WARRANTY; without even the implied warranty of
20 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21 # GNU General Public License for more details.
22 #
23 # You should have received a copy of the GNU General Public License
24 # along with this program; see the file COPYING.  If not, write to
25 # the Free Software Foundation, 51 Franklin Street, Fifth Floor,
26 # Boston, MA 02110-1301, USA.
27 #
28
29 #
30 # Original idea by Trent Fisher <trent@informix.com>
31 # Thanks to:
32 #  - Tero Kivinen <kivinen@iki.fi> for the first prototype
33 #  - Tait Cyrus <tait.cyrus@mci.com> for fixes and cleanups
34 #
35
36 $file = shift(@ARGV);
37 $add_gray = ".80";
38 $del_gray = ".60";
39
40 $program = $0;
41 $program =~ s/.*\///g;
42
43 sub usage {
44     warn "Usage: $program ORIGINAL_FILE < DIFF\n\n";
45     warn "Program reads a diff file from its standard input and annotates
46 ORIGINAL_FILE to show the changes made to the file.  The easiest way to use
47 this program is to use it as an input filter for GNU enscript:
48
49   \$ enscript -G2re --filter='rcsdiff %s | diffpp %s' *.c *.h
50   \$ enscript -G2re --filter='diff %s~ %s | diffpp %s' *.c *.h
51
52 ";
53 }
54
55 if (!defined($file) || defined($ARGV[0])) {
56     &usage;
57     exit 1;
58 }
59
60 if ($file eq "--help") {
61     &usage;
62     exit 0;
63 }
64
65 if ($file eq "--version") {
66     warn "diffpp 1.0\n";
67     exit 0;
68 }
69
70 # Read in original file into internal array.
71 open(FP, $file) || die "$program: couldn't open file `$file' for input: $!\n";
72 @orig_file = <FP>;
73 close(FP);
74 $[ = 1;
75 $orig_line_num = 1;
76 $orig_num_lines = @orig_file;
77
78 # Now read in file of diffs into internal array.
79 @diffs = <STDIN>;
80 $diff_line_num = 1;
81 $diff_num_lines = @diffs;
82
83 while ($diff_line_num <= $diff_num_lines) {
84     $_ = $diffs[$diff_line_num];
85     if (/a/) {
86         do_add($_);
87     } elsif (/d/) {
88         do_delete($_);
89     } elsif (/c/) {
90         do_change($_);
91     }
92 }
93
94 while ($orig_line_num <= $orig_num_lines) {
95     print $orig_file[$orig_line_num++];
96 }
97
98 # Handle new/added lines
99 #
100 # Formats used:
101 #       #a#
102 #       #a#,#
103 sub do_add {
104     ($line) = @_;
105     if ($line =~ /(\d+)a(\d+),(\d+)/) {
106         $insert_at_line = $1;
107         $num_new_lines = $3 - $2 + 1;
108     } elsif ($line =~ /(\d+)a(\d+)/) {
109         $insert_at_line = $1;
110         $num_new_lines = 1;
111     }
112     &skip_to_line($insert_at_line);
113     printf("\000shade{$add_gray}");
114     &mark_to_line($num_new_lines, "+");
115     printf("\000shade{1.0}");
116 }
117
118 # Handle deleted/removed lines
119 #
120 # Formats used:
121 #       #d#
122 #       #,#d#
123 sub do_delete {
124     ($line) = @_;
125     if ($line =~ /(\d+),(\d+)d(\d+)/) {
126         $num_del_lines = $2 - $1 + 1;
127     } elsif ($line =~ /(\d+)d(\d+)/) {
128         $num_del_lines = 1;
129     }
130     $del_from = $1;
131     &skip_to_line($del_from - 1);
132     printf("\000shade{$del_gray}");
133     &mark_to_line($num_del_lines, "-");
134     printf("\000shade{1.0}");
135     $orig_line_num += $num_del_lines;
136 }
137
138 # Handle changed/modified lines
139 #
140 # Formats used:
141 #       #,#c#,#
142 #       #,#c#
143 #       #c#,#
144 #       #c#
145 sub do_change {
146     ($line) = @_;
147     if ($line =~ /(\d+),(\d+)c(\d+),(\d+)/) {
148         $num_old_lines = $2 - $1 + 1;
149         $num_new_lines = $4 - $3 + 1;
150         $change_at_line = $1;
151     } elsif ($line =~ /(\d+),(\d+)c(\d+)/) {
152         $num_old_lines = $2 - $1 + 1;
153         $num_new_lines = 1;
154         $change_at_line = $1;
155     } elsif ($line =~ /(\d+)c(\d+),(\d+)/) {
156         $num_old_lines = 1;
157         $num_new_lines = $3 - $2 + 1;
158         $change_at_line = $1;
159     } elsif ($line =~ /(\d+)c(\d+)/) {
160         $num_old_lines = 1;
161         $num_new_lines = 1;
162         $change_at_line = $1;
163     }
164     # Mark old lines
165     &skip_to_line($change_at_line - 1);
166     $orig_line_num += $num_old_lines;   # skip over changed lines
167     printf("\000shade{$del_gray}");
168     &mark_to_line($num_old_lines, "-");
169     printf("\000shade{1.0}");
170     # Mark new lines
171     printf("\000shade{$add_gray}");
172     &mark_to_line($num_new_lines, "+");
173     printf("\000shade{1.0}");
174 }
175
176 sub skip_to_line {
177     ($line) = @_;
178
179     while ($orig_line_num <= $line) {
180         print $orig_file[$orig_line_num];
181         $orig_line_num++;
182     }
183 }
184
185 sub mark_to_line {
186     ($num_lines, $marker) = @_;
187
188     $diff_line_num++;           # skip over diff command
189     while ($num_lines > 0) {
190         $diff_line = substr($diffs[$diff_line_num++],3);
191         print "\000ps{gsave -5 0 rmoveto ($marker) show grestore}";
192         print $diff_line;
193         $num_lines--;
194     }
195 }