#!/bin/sh ######################################################################### # # FILE : diff2html # DATE : 2.8.2000 # AUTHOR : Patrick Meier / patrick.meier@gmx.net # DESCRIPTION : convert diff output to HTML # # Original version written 4/20/2000 by D.Singer # OK, Georg, here's a Copylite: # # Copyright (c) 2000 by Daniel E. Singer. All rights reserved. # Permission is granted to reproduce and distribute this program # with the following conditions: # 1) This copyright notice and the author identification below # must be left intact in the program and in any copies. # 2) Any modifications to the program must be clearly identified # in the source file. # # Written by Daniel E. Singer, , 4/2000 # # Program availability: # ftp://ftp.cs.duke.edu/pub/des/scripts/ # ######################################################################### PROG=`basename "$0"` USAGE=" Usage: $PROG [-nostibw] [-c #] file1 file2 $PROG -h " DFLT_TRUNC=80 # default value for -t SUMMARY=" \`$PROG' runs \`diff', and formats the output with HTML so that two files can be easily compared side by side; one file can be specified as \`-' for standard input; Options: -n show line numbers; -c number of columns (text width) to use for each file; -o show lines in one column, instead of side by side; -s do not show lines that are the same; -t truncate lines over specified width, default $DFLT_TRUNC; -h help, print this message and exit; These are standard \`diff' options that will be passed to \`diff': -i ignore case of letters; -b ignore trail blanks (spaces and tabs), and treat other strings of blanks as equivalent; -w ignore all white space (spaces and tabs); Exit status: 0 differences found, html produced; 1 files are identical, or -h (help) option selected; 2 \`$PROG' option syntax error; 3 \`diff' error, run \`diff' directly for more information; These variables can be set in the environment to override default colors; use HTML-style (name or RGB) specifications: COLOR_P Page background (bg); COLOR_T Text color; COLOR_D bg for text to Delete (diff "d" text); COLOR_A bg for text to Add (diff "a" text); COLOR_CL bg for text to Change (diff "c" text), left (file1); COLOR_CR bg for text to Change (diff "c" text), right (file2); COLOR_S bg for text that is the Same in both files; COLOR_E bg for Empty, area with no corresponding text; " DIFF='diff' # diff command DIFF_OPTS=' -D "$DSTR"' # options to pass to DIFF DSTR='_DV_' # used by diff for the #ifdef's DSTAT= # status returned by DIFF AWK='awk' EXPAND='expand' TMP_FILE="/tmp/$PROG.$$" trap 'rm -f "$TMP_FILE"; exit' 0 1 2 3 15 # # system dependencies # SYS=`uname -sr` case "$SYS" in 'SunOS '*) AWK='/usr/bin/nawk' esac # # colors to use for various shading; # NOTE: these can be set from the environment; # : ${COLOR_P:="#FFFFFF"} # page background : ${COLOR_T:="#000000"} # page background : ${COLOR_D:="#FFFFE0"} # delete (diff "d" text) : ${COLOR_A:="#E0FFE0"} # add (diff "a" text) : ${COLOR_CL:="#FFFFE0"} # change (diff "c" text), left (file1) : ${COLOR_CR:="#E0FFE0"} # change (diff "c" text), right (file2) : ${COLOR_S:="#F8F8F8"} # same (text that is the same in both files) : ${COLOR_E:="#F0F0F0"} # empty (area with no corresponding text WRAP_MARK='\\»' # show wrapped lines DO_NUMS=0 # -n opt ONE_COL=0 # -o opt COLS=0 # -c opt DO_SAME=1 # -s opt DO_TRUNC= # -t opt TRUNC=0 # -t opt SYNTAX="^G$PROG: option syntax error." syntax_error() { echo "$SYNTAX" >&2 echo "$USAGE" >&2 exit 2 } arg_syntax_check() { [ "$1" -lt 1 ] && syntax_error } while [ "$#" -gt 0 ]; do case "$1" in # options without argument -) break ;; -n) DO_NUMS=1 ;; -o) ONE_COL=1 ;; -s) DO_SAME=0 ;; -t) DO_TRUNC=1 TRUNC="$DFLT_TRUNC" ;; -i) DIFF_OPTS="$DIFF_OPTS -i" ;; -b) DIFF_OPTS="$DIFF_OPTS -b" ;; -w) DIFF_OPTS="$DIFF_OPTS -w" ;; -h) echo "$USAGE$SUMMARY" exit 1 ;; # options with arguments -c) shift arg_syntax_check "$#" COLS="$1" case "$COLS" in [1-9]|[1-9][0-9]|[1-9][0-9][0-9]) ;; *) syntax_error esac ;; # ... --) shift break ;; # unknown option -?) syntax_error ;; # compound option -??*) # break up a compound option NEW_OPTS=`$AWK -v "OPT_STR=$1" 'BEGIN { LEN = length(OPT_STR); NEW_OPTS = ""; STATUS = 0; for (POS=2; POS+0 <= LEN; ++POS) { OPT = substr(OPT_STR,POS,1); if (OPT !~ /[a-zA-Z0-9_]/) STATUS = 1; NEW_OPTS = NEW_OPTS " -" OPT; } print NEW_OPTS; exit STATUS; }' <&-` || { syntax_error } shift set -- $NEW_OPTS ${1:+"$@"} continue ;; # end of options, just command arguments left *) break esac shift done # # need 2 file arguments # [ $# != 2 ] && syntax_error # # adjust truncation # [ "$DO_TRUNC" -a "$COLS" -gt 0 ] && TRUNC="$COLS" # # run DIFF, collect its output, and exit if there were any errors # or if the files are identical # eval "$DIFF$DIFF_OPTS \"\$@\" > \"$TMP_FILE\"" DSTAT="$?" [ "$DSTAT" = 0 ] && { echo "$PROG: the files are identical." >&2 exit 1 } [ "$DSTAT" -gt 1 ] && exit 3 # # expand tabs to spaces # $EXPAND "$TMP_FILE" | # # now do all the hideous stuff # $AWK \ -v "DSTR=$DSTR" \ -v "FILE1=$1" \ -v "FILE2=$2" \ -v "COLS=$COLS" \ -v "TRUNC=$TRUNC" \ -v "DO_NUMS=$DO_NUMS" \ -v "DO_SAME=$DO_SAME" \ -v "ONE_COL=$ONE_COL" \ -v "WRAP_MARK=$WRAP_MARK" \ -v "COLOR_P=$COLOR_P" \ -v "COLOR_T=$COLOR_T" \ -v "COLOR_D=$COLOR_D" \ -v "COLOR_A=$COLOR_A" \ -v "COLOR_CL=$COLOR_CL" \ -v "COLOR_CR=$COLOR_CR" \ -v "COLOR_S=$COLOR_S" \ -v "COLOR_E=$COLOR_E" \ 'BEGIN { # these are produced by "diff -D"; # some versions of diff do it a little differently DEF_IF = "#ifdef " DSTR; DEF_IFN = "#ifndef " DSTR; DEF_ELSE = "#else " DSTR; DEF_ELSE2 = "#else /* " DSTR " */"; DEF_ENDIF = "#endif " DSTR; DEF_ENDIF2 = "#endif /* " DSTR " */"; IN_DEF = 0; SCNT = DCNT[0] = DCNT[1] = 0; LINENO[0] = LINENO[1] = 0; if (COLS && !TRUNC) for (I=1; I+0 <= COLS; ++I) COLPAT = COLPAT "."; FILE1 = fix_file(FILE1); FILE2 = fix_file(FILE2); COLSPAN = (ONE_COL) ? 1 : 2; # # HTML header info # print ""; print ""; print "diff view of " FILE1 " and " FILE2 ""; print ""; print ""; print "
"; print "
"; print ""; print ""; if (ONE_COL) { print ""; } else { print ""; print ""; } } # MAIN { # # parse the input, print out groups of lines when necessary # if (!IN_DEF) { if ($0 == DEF_IF) { do_same_lines(); IN_DEF = 1; DEF_TYPE = 1; next; } if ($0 == DEF_IFN) { do_same_lines(); IN_DEF = 1; DEF_TYPE = 0; next; } } if (IN_DEF) { if ($0 == DEF_ELSE || $0 == DEF_ELSE2) { DEF_TYPE = !DEF_TYPE; next; } if ($0 == DEF_ENDIF || $0 == DEF_ENDIF2) { do_diff_lines(); IN_DEF = 0; next; } DEF_LINES[DEF_TYPE,++DCNT[DEF_TYPE]] = fix_line($0); next; } S_LINES[++SCNT] = fix_line($0); } END { # take care of stragglers do_same_lines(); # # finish up HTML # print ""; print "
diff view
" FILE1 "  »  " FILE2 "
" FILE1 "" FILE2 "
end of diff view
"; print "
"; print "
"; print ""; print ""; exit; } # print out blocks of lines that are the same func do_same_lines() { if (!SCNT) return; print ""; JMAX = ONE_COL ? 0 : 1; for (J=0; J+0 <= JMAX; ++J) { printf("%s","
");
		if (DO_SAME) {
			for (I=1; I+0 <= SCNT; ++I) {
				if (DO_NUMS) {
					if (ONE_COL) {
						++LINENO[0];
						++LINENO[1];
						if (LINENO[0] != LINENO[1])
							_NSTR = LINENO[0] "." LINENO[1];
						else
							_NSTR = LINENO[0];
						printf("%s. ",_NSTR);
						_NLEN = length(_NSTR) + 2;
					}
					else {
						_NSTR = ++LINENO[J];
						printf("%s. ",_NSTR);
						_NLEN = length(_NSTR) + 2;
					}
					NMAX = _NLEN;
				}
				else if (COLS && !TRUNC) {
					printf("  ");
					_NLEN = 2;
				}
				printf("%s",pad_line(S_LINES[I],_NLEN));
				if (I != SCNT)
					printf("\n");
			}
		}
		else {
			if (ONE_COL) {
				LINENO[0] += SCNT;
				LINENO[1] += SCNT;
			}
			else
				LINENO[J] += SCNT;
			#printf("%s"," ");
			printf("%s","...");
		}
		print "";
	}
	print "";
	SCNT = 0;
  }
  # print out blocks of lines where there are differences
  func do_diff_lines() {
	print "";
	DID_DATA = 0;
	for (J=0; J+0 <= 1; ++J) {
		_DCNT = DCNT[J];
		if (_DCNT) {
			if (ONE_COL && DID_DATA)
				print "";
			if (J)
				_COLOR = DCNT[!J] ? COLOR_CR : COLOR_A;
			else
				_COLOR = DCNT[!J] ? COLOR_CL : COLOR_D;
			printf("%s","
");
			for (I=1; I+0 <= _DCNT; ++I) {
				if (DO_NUMS) {
					_NSTR = ++LINENO[J];
					_NLEN = length(_NSTR) + 2;
					if (ONE_COL && NMAX+0 > _NLEN)
						_NLEN = NMAX;
					# line number positioning
					_LPSTR = "";
					_WIDTH = "";
					if (ONE_COL) {
						if (!J)
							_LPSTR = "-";
						_WIDTH = _NLEN - 1;
					}
					printf("%" _LPSTR _WIDTH "s ",_NSTR ".");
				}
				else if (COLS && !TRUNC) {
					printf("  ");
					_NLEN = 2;
				}
				printf("%s",pad_line(DEF_LINES[J,I],_NLEN));
				if (I != SCNT)
					printf("\n");
			}
			print "";
			DID_DATA = 1;
		}
		else {
			if (!ONE_COL)
				print " ";
		}
	}
	print "";
	DCNT[0] = DCNT[1] = 0;
  }
  # if needed, add start-of-line padding to the components of a line
  func pad_line(_L,_N) {
	if (!COLS)
		return(_L);
	_P = substr("          ",1,_N-2) WRAP_MARK " ";
	gsub(/\n/,("\n" _P),_L);
	return(_L);
  }
  # make various adjustments to an input line
  func fix_line(_L) {
	if (_L == "")
		return(" ");
	# truncate or do wrapping
	if (TRUNC) {
		_L = substr(_L,1,COLS);
	}
	else if (COLS) {
		gsub(COLPAT,"&\n",_L);
		sub(/\n$/,"",_L);
	}
	# replace certain characters that HTML is funny about
	gsub("&","\\&",_L);
	gsub("<","\\<",_L);
	gsub(">","\\>",_L);
	return(_L);
  }
  # adjust a file name if needed
  func fix_file(_F) {
	if (_F == "" || _F == "-")
		return("<STDIN>");
	return(_F);
}'

exit 0