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