scc-log2html(1)



NAME


	scc-log2html - convert a SCC-logbook to HTML-format

RELEASE


	scc	1.23.185

SYNOPSIS


	scc-log2html [ -h|--help ] [ -s|--standalone ] <name>

DESCRIPTION


	This program reads a SCC-logbook from stdin and converts it to HTML-
	format on stdout. Refer to scc(4) for a detailed description of 
	SCC-logbook. An empty logbook results in an empty html-file.

	The html-file consists of a table with entries for each run of scc.
	For reported differences in the logbook, the menu contains an URL
	pointing to the corresponding report.

	The logbook contains DIV-tags with the following nested classes:
	    - SCC_LOG
	        - SCC_LOG_NAV
	        - SCC_LOG_SUMMARY
	        - SCC_LOG_STATS
	        - SCC_LOG_ENTRY
	The sylesheet style.css in /var/opt/scc/data controls the markup.

OPTIONS


	-h|--help       Display the syntax and exit.
	-s|--standalone Embed the stylesheet in the html-data and do not
	                show "externel" links to snapshot and index.html.
	                Use this option before transferring the html
	                version of the logbook to a user.

ARGUMENTS


	<name>		name of the host to convert the SCC-snapshot for

DIAGNOSTICS


	This program writes the following message to stderr:

	Syntax error, use: scc-log2html [ -h|--help ] [ -s|--standalone ] <name>
	A syntax error was detected and reported.

	scc-log2html: insufficient permissions to write in data directory: <data_dir>
	To run this program, log in as the owner of the directory.

RETURN VALUE


	Upon completion, the program returns one of the following values:

		0 successful completion
		1 Syntax error
		2 Runtime error

EXTERNAL INFLUENCES


	Use environment variable SCC_DATA to specify an alternative directory
	for the SCC data files. Should be an absolute path.

COPYRIGHT


	scc-log2html is free software under the terms of the GNU General Public 
	License. Copyright (C) 2001-2004 Open Challenge B.V.,
	2004-2005 OpenEyeT Professional Services, 2005-2015 QNH.

FILES


	/var/opt/scc/data/style.css - stylesheet

SEE ALSO


	scc(1), scc-cmp(1), scc-collect(1), scc-log(1), scc-log2html(1),
	scc-plugin(1), scc-snap2html(1), scc(4), scc(5)

VERSION


	$Revision: 5917 $

)	;;
	echo "${ProgName}: alternative SCC_DATA (${SCC_DATA}) should be an absolute path" >&2
xit 2;;
ac

Perform the security settings before calling any program.
TH=/sbin:/usr/sbin:/usr/bin:/bin;	export PATH

port TMPDIR=${SCC_TMP}
port TMP=${SCC_TMP}

port SHELL=/bin/sh

[ ! -w ${SCC_DATA} ]
en
cho "${ProgName}: insufficient permissions to write in data directory: ${SCC_DATA}" >&2
xit 2


${SCC_TMP}

ask 077

D_LINE="${ProgName} [ -h|--help ] [ -s|--standalone ] <name>"
NTAX_ERROR="Syntax error, use: ${CMD_LINE}"

andalone=""
ile [ $# -gt 0 ]

ase "${1}" in
s|--standalone)	standalone="yes"
		shift 1;;
h|--help)		echo "${CMD_LINE}"
		exit 0;;
*)			echo "${SYNTAX_ERROR}" >&2
		exit 1;;
)			break;;
sac
ne

[ $# -ne 1 ]
en
cho "${SYNTAX_ERROR}" >&2
xit 1


${SCC_BIN}/scc_modules/scc_utils

-r ${SCC_CONF}/scc-localize ] && . ${SCC_CONF}/scc-localize

ndom="$(get_RANDOM)"
port TMP_FILE=${SCC_TMP}/scc_log_html_$$_${random}

ap 'rm -f ${TMP_FILE}' 0
ap "exit 2" 1 2 3 15

Replace special HTML-characters in input.
d	-e 's/&/\&amp;/g'	\
e 's/</\&lt;/g'	\
e 's/>/\&gt;/g'	\
e "s/'/\&#39;/g"	\
e 's/"/\&quot;/g' >${TMP_FILE}

[ ! -s ${TMP_FILE} ]
en
xit 0


In standalone mode and with only one run, we do not generate statistics.
ngle_run=""
[ "${standalone}" ]
en
irst_run_time="$(head -n 1 ${TMP_FILE} | sed -e 's/:[^0-9].*//')"
ast_run_time="$(tail -n 1 ${TMP_FILE} | sed -e 's/:[^0-9].*//')"
f [ "${first_run_time}" = "${last_run_time}" ]
hen
single_run="yes"
i


BLE_TAG="<TABLE CLASS=SCC SUMMARY=\"Summary of scc-runs\" BORDER CELLSPACING=1 CELLPADDING=4>"
P_NAME="<A NAME=cfg_top></A>"
P_URL="<A HREF=\"#cfg_top\">Top</A>&nbsp;&nbsp;&nbsp;&nbsp;"

ho '<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">'
ho "<HTML lang=\"en\">"

ho "<HEAD>"
[ -z "${standalone}" ]
en
cho '<LINK HREF="style.css" REL="stylesheet" TYPE="text/css">'
se
f [ -f "${SCC_DATA}/style.css" ]
hen
echo '<style TYPE="text/css">'
cat "${SCC_DATA}/style.css"
echo "</style>"
i

ho "<TITLE>Logbook: $1</TITLE>"
ho "</HEAD>"

ho "<BODY>"
ho "<DIV class=SCC_LOG>";
ho "${TOP_NAME}"

ho "<H1>Logbook: ${1}</H1>"

DO NOT CHANGE THE FOLLOWING HTML-CODE WITHOUT CONSULTING scc.cgi IN scc-srv
[ -z "${single_run}" ]
en
cho "<DIV class=SCC_LOG_NAV>";

cho "<H2>"
f [ -z "${standalone}" ]
hen
echo "	<A HREF=\"index.html\">Home</A>&nbsp;&nbsp;&nbsp;&nbsp;"
echo "	<A HREF=\"scc.${1}.html\">Configuration</A>&nbsp;&nbsp;&nbsp;&nbsp;"
i
cho '	<A HREF="#cfg_statistics">Statistics</A>'
cho "</H2>"

cho "</DIV><!-- class=SCC_LOG_NAV -->";

cho "<H3>Summary of runs of SCC</H3>"
se
cho "<H2>Run of SCC</H2>"


Build a table with URL's to reported differences in the logbook.
Use the date/time of the run as the ID for HREF and NAME.
Sort the data reverse to show the most recent change at the top of the table.
Check the code in scc-log for the specific layout of the logbook.
k -F: '/:result::/	{
		stat_cnt_runs++;
		if ( length( prev_res ) )
		{
			# restart or identical, no number of differences.
			print prev_res ":" ":" prev_runtime ":" prev_remark;
			prev_remark="";
			prev_runtime="";
		}
		prev_res=$0;
	}
:remark::/	{
		# A remark can contain ":", syntax of a line is: <date>:<time>:remark::<remark>
		prev_remark = $5;
		for ( i = 6; i <= NF; i++ )
		{
			prev_remark = sprintf( "%s: %s", prev_remark, $i );
		}
	}
:runtime::/	{
		# Use a separate counter as older versions of SCC did not record the runtime.
		stat_runtime_cnt++;
		stat_runtime_total+=$NF;
		# The min/max urls from the statistics table refer to the entire table with the runs: prefix = "back"
		if ( $NF > stat_runtime_max )
		{
			stat_runtime_max = $NF;
			runtime_max_date = $1;
			runtime_max_time = $2;
		}
		if ( $NF < stat_runtime_min || stat_runtime_min == 0 )
		{
			stat_runtime_min = $NF;
			runtime_min_date = $1;
			runtime_min_time = $2;
		}
		prev_runtime=$NF;
	}
:count::/	{
		stat_change_cnt++;
		stat_change_total+=$NF;
		# The min/max urls from the statistics table refer to the changes: prefix = "log"
		if ( $NF > stat_change_max )
		{
			stat_change_max = $NF;
			change_max_date = $1;
			change_max_time = $2;
		}
		if ( $NF < stat_change_min || stat_change_min == 0 )
		{
			stat_change_min = $NF;
			change_min_date = $1;
			change_min_time = $2;
		}
		# changes detected, report
		print prev_res ":" $NF ":" prev_runtime ":" prev_remark;
		prev_res=""
		prev_remark=""
	}
D			{
		# unreported results?
		if ( length( prev_res ) )
		{
			print prev_res ":" ":" prev_runtime ":" prev_remark;
		}
		if ( stat_cnt_runs == 0 )
		{
			stat_cnt_runs=1;
			stat_runtime_cnt=1;
		}

		# Prefix the statistics lines with 0 to put them at the end of the data after the reverse sort we are going to perform:
		printf( "0:9:runs total        : %d\n", stat_cnt_runs );
		printf( "0:8:runs with changes : %d\n", stat_change_cnt );
		printf( "0:7:runs perc. changes: %d\n", ( 100 * stat_change_cnt ) / stat_cnt_runs );
		printf( "0:6:change count min. : %d:log:%s:%s\n", stat_change_min, change_min_date, change_min_time );
		printf( "0:5:change count max. : %d:log:%s:%s\n", stat_change_max, change_max_date, change_max_time );
		if ( stat_change_cnt == 0 )
		{
			stat_change_cnt = 1;
		}
		printf( "0:4:change count average: %d\n", stat_change_total / stat_change_cnt );
		printf( "0:3:runtime min.        : %d:back:%s:%s\n", stat_runtime_min, runtime_min_date, runtime_min_time );
		printf( "0:2:runtime max.        : %d:back:%s:%s\n", stat_runtime_max, runtime_max_date, runtime_max_time );
		printf( "0:1:runtime average     : %d\n", stat_runtime_total / stat_runtime_cnt );
	}' ${TMP_FILE}		|
rt -r						|
k -F":" '{
# We produce both the table with all runs and the table with the statistics. 
# Start with the table with all runs.
# The variable u is not initialized in a BEGIN-clause.
# Therefore we check for the first record.
if ( NR == 1 )
{
	print "<DIV class=SCC_LOG_SUMMARY>";
	print u;
	print "<THEAD>";
	print "	<TR class=Odd>";
	print "		<TH>Date</TH>";
	print "		<TH>Time</TH>";
	print "		<TH>Runtime</TH>";
	print "		<TH>Result</TH>";
	print "		<TH>Count</TH>";
	print "		<TH>Remark</TH>";
	print "	</TR>";
	print "</THEAD>";

	print "<TBODY>";
	tr_c = "Even";
}

0:/	{
# Process the statistics:
# Format of the input data is:
#0:<order>:<label>:<value>[:<prefix>:<date>:<time>]
if ( statistics == 0 )		# No heading yet?
{
	print "</TBODY>";
	print "</TABLE>";
	print "</DIV><!-- class=SCC_LOG_SUMMARY -->";
	statistics=1;		# Heading done!

	if ( length( single_run ) )
	{
		next;
	}
	print "<HR>";
	print "<DIV class=SCC_LOG_STATS>";
	print "<A NAME=cfg_statistics></A>"
	printf( "<H3>%sStatistics</H3>\n", t );

	print u;
	print "<THEAD>";
	print "	<TR class=Odd>";
	print "		<TH>Category</TH>";
	print "		<TH>Value</TH>";
	print "		<TH>Date</TH>";
	print "	</TR>";
	print "</THEAD>";

	print "<TBODY>";
	s_tr="Even";
}

if ( length( single_run ) )
{
	next;
}

print "<TR class=" s_tr ">";
if ( s_tr != "Even" )
{
	s_tr = "Even";
}
else
{
	s_tr = "Odd";
}
print "	<TD class=Even>" $3 "</TD>";									# Category
printf( "	<TD class=Odd align=right>%s</TD>\n", $4 );						# Value
if ( length( $5 ) > 0 )
{
	printf( "	<TD class=Even><A HREF=\"#%s_%s_%s\">%s %s</A></TD>\n", $5, $6, $7, $6, $7 );	# Date
}
else
{
	printf( "	<TD class=Even>&nbsp;</TD>\n" );						# No date
}
print "</TR>";
next;

{

Show data of a run.


Format of the input is:

<date>:<time>:result::different:<count>:<runtime>:<remark>
f ( $5 == "different" )

print "<TR class=" tr_c "_Emp>";

lse

print "<TR class=" tr_c ">";


f ( tr_c != "Even" )

tr_c = "Even";

lse

tr_c = "Odd";


rintf( "	<TD class=Even><A NAME=\"back_%s_%s\"></A>%s</TD>\n", $1, $2, $1 );	# Date
rint "	<TD class=Odd>" $2 "</TD>";	# Time
f ( length( $7 ) > 0 )			# Runtime available?

printf( "	<TD class=Even align=right>%s</TD>\n", $7 );

lse

print "	<TD class=Even>&nbsp;</TD>";

f ( $5 == "different" )		# Result

printf( "	<TD class=Odd_Emp><A HREF=\"#log_%s_%s\" TITLE=\"%s %s\">%s</A></TD>\n", $1, $2, $1, $2, $5 );

lse

printf( "	<TD class=Odd>%s</TD>\n", $5 );

f ( length( $6 ) > 0 )			# Count available?

printf( "	<TD class=Even align=right>%s</TD>\n", $6 );

lse

print "	<TD class=Even>&nbsp;</TD>";

emark = $8;				# Possibly contains ":"
or ( i = 9; i <= NF; i++ )

remark = sprintf( "%s: %s", remark, $i );

f ( length( remark ) > 0 )

printf( "	<TD class=Odd_Emp align=left>%s</TD>\n", remark );

lse

print "	<TD class=Odd>&nbsp;</TD>";

rint "</TR>";
}
D	{
if ( length( single_run ) == 0 )
{
	print "</TBODY>";
	print "</TABLE>";
	print "</DIV><!-- class=SCC_LOG_STATS -->";
}
' single_run="${single_run}" t="${TOP_URL}" u="${TABLE_TAG}"

When sorting the logdata, we want to preserve the order of the data within each single run.
As we sort descending, we use <total_line_nr> - <current_record_nr>.
First determine the total_line_nr:
tal_cnt="$(wc -l <${TMP_FILE})"

Now the table is present, we show the data.
Use the date/time of the run as the ID for HREF and NAME.
k -F:	'{
# Sort the log file, most recent run first, 
# within the run keep the data in the order of the log file.
log_date=$1;
log_time=$2;
gsub( "-", "", log_date );
gsub( /\./, "", log_time );
printf( "%s:%s:%010.10d:%s\n", log_date, log_time, total - NR, $0 );
' total="${total_cnt}" ${TMP_FILE}		|
rt -r							|
d -e 's/^[0-9]*:[0-9]*:[0-9]*://'			|
k -F":"	'BEGIN	{ first_log_entry = 1; }
:result::different$/	{
	if ( show_data )
	{
		print "</PRE>";		# end of previously showed data
	}
	show_data=1;			# indicate that we are going to show differences
	prev_class="";			# reset class-identifier for this run
	print "<HR>";
	if ( first_log_entry )
	{
		print "<DIV class=SCC_LOG_ENTRY>";
		first_log_entry=0;
	}
	printf( "<H3>%s<A HREF=\"#back_%s_%s\" TITLE=\"%s %s\">Runs</A>&nbsp;&nbsp;&nbsp;&nbsp;", u, $1, $2, $1, $2 );
	printf( "<A NAME=\"log_%s_%s\">Differences at: %s %s</A></H3>\n", $1, $2, $1, $2 );
	print "<PRE>";
	next;
}
:result::identical$|:result::.re.start$/	{
	if ( show_data )
	{
		print "</PRE>";		# end of previously showed data
	}
	show_data=0;			# the are no differences: do not show data
	next;
}
:data::/	{
	if ( show_data )
	{
		# Do not show the classification on each line, only once in bold format.
		change=$5;		# old/new or ctx for unchanged data with context diff
		# $6 is empty to separate changed data from the classification
		class=$7;
		for ( i = 8; length( $i ) > 0; i++ )
		{
			class=sprintf( "%s - %s", class, $i );
		}
		if ( class != prev_class )
		{
			print "</PRE>";
			print "<H4>" class "</H4>";
			print "<PRE>";
			prev_class=class;
		}
		if ( change == "ctx" )
		{
			printf( "<span class=\"%s\">	", change );
		}
		else
		{
			printf( "<span class=\"%s\">%s:	", change, change );
		}
		for ( i++; i < NF; i++ )
		{
			printf( "%s:", $i );
		}
		print $i "</span>";
	}
	next;
}
{
	# No data, classifications:
	# possible classifications are: count, remark, previous date and time
	if ( show_data )
	{
		printf( "%-30.30s: %s\n", $3, $5 );
	}
	next;
}
D	{
if ( show_data )
{
	print "</PRE>";		# end of previously showed data
}
print "<HR>";
if ( first_log_entry == 0 )	# Have any log-entries been printed?
{
	print "</DIV><!-- class=SCC_LOG_ENTRY -->";	# Mark the end of all log-entries.
}
' u="${TOP_URL}"

-f ${TMP_FILE}

ho "<P>Generated by SCC version 1.23.185 (&copy; <A HREF=\"http://www.qnh.eu\">QNH</A>) on $(date)</P>"

ho "</DIV><!-- class=SCC_LOG -->";
ho "</BODY>"
ho "</HTML>"

it 0