xref: /illumos-gate/usr/src/tools/scripts/wdiff.pl (revision 7a1306a70fee0e017a445bde1dcfd1997f691cf4)
1#!/usr/bin/perl
2#
3# CDDL HEADER START
4#
5# The contents of this file are subject to the terms of the
6# Common Development and Distribution License, Version 1.0 only
7# (the "License").  You may not use this file except in compliance
8# with the License.
9#
10# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
11# or http://www.opensolaris.org/os/licensing.
12# See the License for the specific language governing permissions
13# and limitations under the License.
14#
15# When distributing Covered Code, include this CDDL HEADER in each
16# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
17# If applicable, add the following below this CDDL HEADER, with the
18# fields enclosed by brackets "[]" replaced with your own identifying
19# information: Portions Copyright [yyyy] [name of copyright owner]
20#
21# CDDL HEADER END
22#
23#
24# ident	"%Z%%M%	%I%	%E% SMI"
25#
26# Copyright 2004 Sun Microsystems, Inc.  All rights reserved.
27# Use is subject to license terms.
28#
29# Make a dynamic HTML page for the unified diffs between two (C) files.
30#
31
32use Getopt::Std;
33
34$diffword = "wdiff";
35$context = 10;
36
37getopt('t');
38
39if ($#ARGV + 1 == 1) {
40	$diffword = $ARGV[0];
41	open DIFF, "<&STDIN";
42	$ARGV[0] = '-';
43	$ARGV[1] = '-';
44} elsif ($#ARGV + 1 == 2) {
45	open DIFF, "diff -D $diffword $ARGV[0] $ARGV[1] | expand |";
46} else {
47	print "Usage: $0 [-t title] file1 file2\n";
48	exit 2;
49}
50
51$title = $opt_t ? $opt_t : "Differences between $ARGV[0] and $ARGV[1]";
52
53$indiff = 0;
54$line1 = 0;
55$line2 = 0;
56@pretext = ();		# Speculative pretext buffer (code)
57$posttext = 0;		# Lines of posttext to print
58$nelided = 0;		# Number of elided chunks
59$endfunc = 0;		# Seen end of function?
60$inelided = 0;		# Elided section open?
61$elided_lines = 0;
62
63print <<END;
64<html>
65  <head>
66    <title>$title</title>
67    <style type='text/css'>
68      pre	{ margin: 0; border: 2px solid white }
69
70      .subtracted { color: red }
71      .added	{ color: blue }
72
73      .elided	{ border: 2px solid; cursor: n-resize; padding: 1px }
74
75      .hidebar	{ background-color: #cccccc; border: 1px solid black;
76      		  text-align: center;
77		  border-collapse: separate; border-spacing: 0px; }
78
79      .hidebar .active { border: 2px black outset; cursor: n-resize }
80
81      .cmdbox	{ position: fixed; top: 0; right: 0; border: solid 1px black;
82      		  background-color: white; text-align: center }
83
84      .cmdbox td { border: outset #808080; cursor: default; padding: 3px 4px; }
85
86    </style>
87    <script>
88      function show_n_hide_dir(id_to_show, id_to_hide, dir) {
89	      var elt_to_show = document.getElementById(id_to_show);
90	      var elt_to_hide = document.getElementById(id_to_hide);
91	      // When we're opening up, we need to make the bottoms of the
92	      // elements appear to be the same.  So our invariant should be
93	      // elt.offsetBottom - window.scrollY.
94	      var preinvar = elt_to_hide.offsetHeight - window.scrollY;
95	      elt_to_show.style.setProperty('display', '', '');
96	      elt_to_hide.style.setProperty('display', 'none', '');
97	      if (dir == 'up') {
98		      var postinvar = elt_to_show.offsetHeight - window.scrollY;
99		      window.scrollBy(0, postinvar - preinvar);
100	      }
101      }
102
103      function handle_click(e) {
104	      var eh = e.target;
105	      var es = document.getElementById("hb-" + e.target.id);
106	      eh.style.setProperty('display', 'none', '');
107	      es.style.setProperty('display', '', '');
108	      /* Scroll so new element is at cursor. */
109	      window.scroll(0, es.offsetTop + (es.offsetHeight / 2)
110	          - e.clientY);
111      }
112
113      function stripsearch(str) {
114	q = str.indexOf("?");
115	if (q != -1)
116	  str = str.substr(0, q);
117	return (str);
118      }
119
120      function split() {
121        page = stripsearch(location.href);
122	halfway = window.scrollY + window.innerHeight / 2 - 5;
123	document.write('<frameset rows="50%,*">' +
124	  '<frame src="' + page + "?" + window.scrollY + '" />' +
125	  '<frame src="' + page + "?" + halfway + '" />' +
126	  '</frameset>');
127	document.close();
128      }
129
130      function closeframe() {
131	page = stripsearch(location.href);
132
133	otherf = window.parent.frames[0];
134	if (otherf == window)
135	  otherf = window.parent.frames[1];
136
137	parent.location.href = page + "?" + otherf.scrollY;
138      }
139
140    </script>
141  </head>
142  <body>
143    <table class='cmdbox'>
144      <tr>
145        <td onclick='split()'>Split</td>
146	<td id='close' onclick='closeframe()'>Close</td>
147      </tr>
148      <tr><td colspan=2 onclick='open_or_close_all(1)'>Expand all</td></tr>
149      <tr><td colspan=2 onclick='open_or_close_all(0)'>Collapse all</td></tr>
150    </table>
151    <script type='text/javascript'>
152      if (window == top)
153        document.getElementById('close').style.setProperty('display', 'none', '');
154    </script>
155END
156
157print "<pre><span class='subtracted'>          --- $ARGV[0]
158</span><span class='added'>          +++ $ARGV[1]
159</span>";
160
161sub begin_elided {
162	++$nelided;
163	# onclick handler assigned at bottom
164	print "<pre id='elided$nelided' class='elided' style='display: none'>";
165	$inelided = 1;
166	$elided_lines = 0;
167}
168
169sub end_elided {
170	print "</pre>\n";
171
172	print <<END;
173<table id='hb-elided$nelided' class='hidebar'>
174  <tr>
175    <td class='active' onclick='show_n_hide_dir("elided$nelided", "hb-elided$nelided", "down")'>
176      Click to open down</td>
177    <td style='width: 5em'>$elided_lines lines</td>
178    <td class='active' onclick='show_n_hide_dir("elided$nelided", "hb-elided$nelided", "up")'>
179      Click to open up</td>
180  </tr>
181<table>
182END
183	$inelided = 0;
184}
185
186while (<DIFF>) {
187	chomp;
188
189	# Change detection
190	$previndiff = $indiff;
191
192	if (!$indiff) {
193		if (/^#ifdef $diffword$/) {
194			$indiff = 1;
195		} elsif (/^#ifndef $diffword$/) {
196			$indiff = -1;
197		}
198	} else {
199		if (/^#else \/\* $diffword \*\/$/) {
200			$indiff = -$indiff;
201			print "</span>";
202			printf "<span class='%s'>",
203			    ($indiff > 0 ? "added" : "subtracted");
204			next;
205		} elsif (/^#endif \/\* (! )?$diffword \*\/$/) {
206			$indiff = 0;
207			$posttext = $context;
208			print "</span>";
209			next;
210		}
211	}
212
213	if (!$previndiff && $indiff) {
214		# Beginning of a change: If we have an elided section open,
215		# end it.  Print the pretext and continue.
216
217		if ($inelided) {
218			end_elided;
219			print "<pre>";
220		}
221
222		print @pretext;
223		@pretext = ();
224		$endfunc = 0;
225		$infunc = -1;
226
227		printf "<span class='%s'>",
228		    ($indiff > 0 ? "added" : "subtracted");
229		next;
230	}
231
232	# Line of code
233
234	# Quote for HTML
235	s/&/&amp;/g;
236	s/</&lt;/g;
237	s/>/&gt;/g;
238
239	# Format the line according to $indiff, and print it or put it into
240	# a buffer.
241	if ($indiff == -1) {
242		++$line1;
243		printf "%4d %4s -%s\n", $line1, "", $_;
244	} elsif ($indiff == 0) {
245		++$line1;
246		++$line2;
247
248		$str = sprintf "%4d %4d  %s\n", $line1, $line2, $_;
249
250		if ($posttext > 0) {
251			print $str;
252			--$posttext;
253		} else {
254			push @pretext, $str;
255			if ($#pretext + 1 > $context) {
256				$str = shift @pretext;
257
258				if (!$inelided) {
259					print "</pre>\n";
260					begin_elided;
261				}
262
263				++$elided_lines;
264
265				print $str;
266			}
267		}
268	} elsif ($indiff == 1) {
269		++$line2;
270		printf "%4s %4d +%s\n", "", $line2, $_;
271	}
272}
273
274print @pretext;
275
276if ($inelided) {
277	$elided_lines += @pretext;
278	end_elided;
279} else {
280	print "    </pre>\n";
281}
282
283print "<pre id='linerefpre'><span id='lineref'>", ' ' x (4 + 1 + 4 + 2 + 80),
284    "</span></pre>\n";
285
286print <<END;
287    <script>
288      /* Assign event handlers and widths. */
289      var w = document.getElementById('lineref').offsetWidth;
290      for (var i = 1; i <= $nelided; ++i) {
291	      var e = document.getElementById("elided" + i);
292	      e.onclick = handle_click;
293	      e.style.setProperty('width', w, '');
294
295	      e = document.getElementById("hb-elided" + i);
296	      e.style.setProperty('width', w, '');
297      }
298
299      /* Hide our line size reference. */
300      document.getElementById('linerefpre').style.setProperty('display', 'none', '');
301
302      /* Scroll as indicated. */
303      str = location.search;
304      s = str.substring(1, str.length);
305      if (s > 0)
306        window.scroll(0, s);
307
308      function open_or_close_all(open) {
309	      for (var i = 1; i <= $nelided; ++i) {
310		      var e = document.getElementById("hb-elided" + i);
311		      e.style.setProperty("display", open ? "none" : "", "");
312
313		      e = document.getElementById("elided" + i);
314		      e.style.setProperty("display", open ? "" : "none", "");
315	      }
316      }
317    </script>
318  </body>
319</html>
320END
321