#!/usr/local/bin/perl
if ($] < 5.001)
{
    print STDERR
	"Error: Perl version $] is not capable of running this program.\n";
    print STDERR
	"       You need at least Perl 5.001 (preferrably 5.002 or above).\n";
    exit(-1);
}
elsif ($] == 5.001)
{
    print STDERR
	"Warning: Perl $] has memory limits on scalar (string) size.\n";
    print STDERR
       "         You may not be able to successfully run large input files.\n";
}
($C, 
 $D, 
 $E, 
 $L, 
 $T, 
 $V, 
 $outfile, 
 $scale_factor, 
 $graphfile, 
 $infile
 ) = &parse_cmd_line();
print STDERR "Processing file...\n";
($LOC, $CCD_Vg, $CCD_Vgp, @CCD_list) = &process_file($infile);
$stack_item_size = 7; # 7 elements in an item
print STDERR "Verifying conditional logic...\n";
&verify_cclogic($infile, $stack_item_size, @CCD_list);
if (defined ($graphfile))
{
    print STDERR "Preprocessing for image...\n";
    ($max_x, $max_y)
	= &assign_position($stack_item_size, \@CCD_list);
    print STDERR "Generating image...\n";
    &gen_image($graphfile,
	       $infile,
	       $stack_item_size,
	       $max_x,
	       $max_y,
	       $LOC,
	       $scale_factor,
	       \@CCD_list
	       );
}
if (($C == 1) || ($D == 1) || ($E == 1) || ($L == 1) || ($T == 1) || ($V == 1))
{
    print STDERR "Generating report...\n";
    &gen_report($C, 
		$D, 
		$E, 
		$L, 
		$T,
		$V,
		$outfile, 
		$infile, 
		$LOC, 
		$CCD_Vg, 
		$CCD_Vgp, 
		$stack_item_size,
		\@CCD_list);
}
else
{
    print STDERR "No summary report generated because no report options are activated.\n";
}
print STDERR "Done!\n";
sub parse_cmd_line
{
    my ($C) = 1; # print complete logic listing
    my ($D) = 0; # don't print directive summary
    my ($E) = 0; # don't print expression summary
    my ($L) = 1; # print total lines of conditional code
    my ($T) = 1; # print total lines of code
    my ($V) = 1; # print V(g) and V'(g)
    my ($outfile);
    my ($scale_factor);
    my ($graphfile);
    my ($infile);
    my ($curr_arg);
    my ($i, $letters);
    my $opts = "CcDdEeLlTtVv";
    if ($#ARGV == -1) # if (no args)
    {
	print STDERR "No parameters specified.\n";
	print STDERR "Type \"$0 -h\" for command usage.\n";
	exit(-1);
    }
    for ($curr_arg = 0; $curr_arg <= $#ARGV; $curr_arg++)
    {
	if ($ARGV[$curr_arg] =~ /^-f$/)
	{
	    unless ($curr_arg < $#ARGV)
	    {
		print STDERR "<infile> expected at end of command line.\n";
		print STDERR "Type \"$0 -h\" for command usage.\n";
		exit(-1);
	    }
	    $curr_arg++;
	    $infile = $ARGV[$curr_arg];
	}
	elsif ($ARGV[$curr_arg] =~ /^-g(.*)$/)
	{
	    $scale_factor = $1;
	    if ($scale_factor eq "")
	    {
		$scale_factor = 'p';
	    }
	    unless ($curr_arg < $#ARGV)
	    {
		print STDERR "<graphfile> expected at end of command line.\n";
		print STDERR "Type \"$0 -h\" for command usage.\n";
		exit(-1);
	    }
	    $curr_arg++;
	    $graphfile = $ARGV[$curr_arg];
	}
	elsif ($ARGV[$curr_arg] =~ /^-o$/)
	{
	    unless ($curr_arg < $#ARGV)
	    {
		print STDERR "<outfile> expected at end of command line.\n";
		print STDERR "Type \"$0 -h\" for command usage.\n";
		exit(-1);
	    }
	    $curr_arg++;
	    $outfile = $ARGV[$curr_arg];
	}
	elsif ($ARGV[$curr_arg] =~ /^-(h|help|\?)$/)
	{
	    if ($#ARGV == 0)
	    {
		&print_usage();
		exit(0);
	    }
	    else
	    {
		print STDERR "Help must be specified as the only parameter.\n";
		print STDERR "Type \"$0 -h\" for command usage.\n";
		exit(-1);
	    }
	}
	elsif ($ARGV[$curr_arg] =~ /^-(.*)/)
	{
	    $letters = $1;
	    for ($i = 0; $i < length($letters); $i++)
	    {
		if ($letters =~ /^[$opts]{$i}C/)
		{
		    $C = 1;
		}
		elsif ($letters =~ /^[$opts]{$i}c/)
		{
		    $C = 0;
		}
		elsif ($letters =~ /^[$opts]{$i}D/)
		{
		    $D = 1;
		}
		elsif ($letters =~ /^[$opts]{$i}d/)
		{
		    $D = 0;
		}
		elsif ($letters =~ /^[$opts]{$i}E/)
		{
		    $E = 1;
		}
		elsif ($letters =~ /^[$opts]{$i}e/)
		{
		    $E = 0;
		}
		elsif ($letters =~ /^[$opts]{$i}L/)
		{
		    $L = 1;
		}
		elsif ($letters =~ /^[$opts]{$i}l/)
		{
		    $L = 0;
		}
		elsif ($letters =~ /^[$opts]{$i}T/)
		{
		    $T = 1;
		}
		elsif ($letters =~ /^[$opts]{$i}t/)
		{
		    $T = 0;
		}
		elsif ($letters =~ /^[$opts]{$i}V/)
		{
		    $V = 1;
		}
		elsif ($letters =~ /^[$opts]{$i}v/)
		{
		    $V = 0;
		}
		elsif ($letters =~ /^[$opts]{$i}T/)
		{
		    $T = 1;
		}
		elsif ($letters =~ /^[$opts]{$i}t/)
		{
		    $T = 0;
		}
		else
		{
		    $letters =~ /^[$opts]{$i}(.)/;
		    print STDERR "Invalid or out of context flag \"$1\".\n";
		    print STDERR "Type \"$0 -h\" for command usage.\n";
		    exit(-1);
		}
	    }
	}
	else
	{
	    print STDERR "Invalid or out of order command line parameter.\n";
	    print STDERR "Type \"$0 -h\" for command usage.\n";
	    exit(-1);
	}
    }
    unless (defined($infile))
    {
	print STDERR "No input file specified.\n";
	print STDERR "Type \"$0 -h\" for command usage.\n";
	exit(-1);
    }
    unless (-e $infile)
    {
	print STDERR "Invalid or non-existant input file \"$infile\".\n";
	print STDERR "Type \"$0 -h\" for command usage.\n";
	exit(-1);
    }
    if (defined ($scale_factor))
    {
	unless ($scale_factor =~ /^(p|1|2|4|8|16)$/)
	{
	    print STDERR "Invalid graph scale factor \"$scale_factor\".\n";
	    print STDERR "Type \"$0 -h\" for command usage.\n";
	    exit(-1);
	}
    }
    if (defined($outfile) && defined($graphfile))
    {
	if ($outfile eq $graphfile)
	{
	    print STDERR 
		"Warning: Same filename specified for report and graph.\n";
	}
    }
    return ($C, $D, $E, $L, $T, $V, 
	    $outfile, $scale_factor, $graphfile, $infile);
}
sub print_usage
{
    my (@path, $progname);
    (@path) = split(/\//, $0);
    $progname = $path[$#path];
    print STDERR "$progname usage:\n";
    print STDERR "             $progname <option>* -f infile\n";
    print STDERR "             $progname -h | -help | -?\n";
    print STDERR "\n";
    print STDERR "                 option ::=  -{CcDdEeLlTtVv}+\n";
    print STDERR "                           | -g[p|1|2|4|8|16] <graphfile>\n";
    print STDERR "                           | -o <outfile>\n";
}
sub gen_report
{
    my $num_items;
    my $list_index;
    my %ccds;
    my %exprs;
    my $CCD_LOC;
    my($C, $D, $E, $L, $T, $V, $outfile, $infile, 
       $LOC, $CCD_Vg, $CCD_Vgp, $size, $CCD_list) = @_;
    if (defined($outfile)){
	open(OUT,">$outfile")||
	    die"Couldn't open $outfile.\n";
	select(OUT);
    }
    printf("\nSummary Report for %s :\n\n",$infile);
    printf("Analysis Options Specified:\n\n");
    if($C){
	printf("C : Display Comprehensive Conditional Logic Listing\n");
    }
    if($D){
	printf("D : Display Conditional Directive Summary\n");
    }
    if($E){
	printf("E : Display Conditional Expression Summary\n");
    }
    if($L){
	printf("L : Display Total Lines of Code\n");
    }
    if($T){
	printf("T : Display Total Lines of Conditional Compilation Logic\n");
    }
    if($V){
	printf("V : Display V(g) and V'(g) for Conditional Logic\n");
    }
    printf("\nCode Analysis Summary:\n\n");
    if ($L){
	printf("\tTotal Lines of Code:\t\t%6d\n",$LOC);
    }
    if ($T){
	$CCD_LOC = ($#$CCD_list + 1) / $size;
	printf("\tTotal Lines of CC Logic:\t%6d\n",$CCD_LOC);
    }
    if ($V){
	printf("\tTotal V(g) for CCDs:\t\t%6d\n",$CCD_Vg);
	printf("\tTotal V'(g) for CCDs:\t\t%6d\n",$CCD_Vgp);
    }
    if ($D){
	printf("\nDirective Use Summary:\n\n");
	$num_items = size_stack($size,@$CCD_list);
	$ccds{"#if"} = 0;
	$ccds{"#ifdef"} = 0;
	$ccds{"#ifndef"} = 0;
	$ccds{"#else"} = 0;
	$ccds{"#elif"} = 0;
	$ccds{"#endif"} = 0;
	for($list_index = 0;$list_index < $num_items; $list_index++){
	    $_ = $$CCD_list[$list_index * $size + 0];
	    if ((/\w+/)&&(defined($_))){
		$ccds{$_} += 1;
	    }
	}
	printf("\t%5d\t#if\n",$ccds{"#if"});
	printf("\t%5d\t#ifdef\n",$ccds{"#ifdef"});
	printf("\t%5d\t#ifndef\n",$ccds{"#ifndef"});
	printf("\t%5d\t#else\n",$ccds{"#else"});
	printf("\t%5d\t#elif\n",$ccds{"#elif"});
	printf("\t%5d\t#endif\n",$ccds{"#endif"});
    }	
    if ($E){
	printf("\nExpression Use Summary:\n\n");
	$num_items = size_stack($size,@$CCD_list);
	for($list_index = 0;$list_index < $num_items; $list_index++){
	    $_ = $$CCD_list[$list_index * $size + 1];
	    if ((/\w+/)&&(defined($_))){
		if (!defined($exprs{$_})){
		    $exprs{$_} = 0;
		}
		$exprs{$_} += 1;		
	    }
	}
	@keys = keys(%exprs);
	@keys = sort(@keys);
	foreach (@keys){
	    printf("\t%5d\t%s\n",$exprs{$_},$_);
	}	    
    }
    if ($C){
	printf("\nComplete Conditional Logic Listing:\n\n");
	printf("Line#\tCCD\t\tExpression\n");
	printf( "----------------------------------------------\n");
	$num_items = size_stack($size,@$CCD_list);
	for($list_index = 0;$list_index < $num_items; $list_index++){
	    $lin = $$CCD_list[$list_index * $size + 2];
	    $_ = $$CCD_list[$list_index * $size + 0];
	    if ((/\w+/)&&(defined($_))){
		printf("%5d\t%s\t\t",$lin,$_);
	    }
	    $_ = $$CCD_list[$list_index * $size + 1];
	    if ((/\w+/)&&(defined($_))){
		printf("%s\n",$_);
	    }
	    else{
		printf("\n");
	    }
	}
    }
    printf("\n*** End of Analysis Summary ***\n");
    close(OUT);
}
sub scan_comment
{
    my($Line, $filename);
    (*IN, $Line, $filename) = @_;
    my $Comment;
    my $Rest;
    while (($Line !~ /\*\//s)&&($Line)){
	$Line = <IN>;
	if (!defined($Line))
	{
	    print STDERR "Error in $filename: reached end of file"
		. " without reaching end of comment.\n";
	}
    }
    ($Comment,$Rest) = split(/\*\//,$Line, 2); # split on "*/"
    return ($Rest);
}
sub scan_tick
{
    my ($line, $filename);
    (*IN, $line, $filename) = @_;
    my $quote;
    my $rest;
    my ($end_of_quote) = 0;
    my ($before_separator, $separator);
    $quote = "";
    until ($end_of_quote)
    {
	if ($line =~ /(\\\\|\\\'|\'|\\\s*$)/)
	{
	    $separator = $1;
	    if ($separator =~ /\\\\/)
	    {
		($before_separator, $line) = split(/\\\\/, $line, 2);
		$quote = $quote . $before_separator . $separator;
	    }
	    elsif ($separator =~ /\\\'/) # matched (\')
	    {
		($before_separator, $line) = split(/\\\'/, $line, 2);
		$quote = $quote . $before_separator . $separator;
	    }
	    elsif ($separator =~ /\'/) # matched (')
	    {
		$end_of_quote = 1;
		($before_separator, $rest) = split(/\'/, $line, 2);
		$quote = $quote . $before_separator . $separator;
	    }
	    else #elsif ($separator =~ /\\\s*$/) # matched (\)
	    {
		chop $line;
		$quote = $quote . $line;
		$line = <IN>;
		if (!defined($line))
		{
		    print STDERR "Error in $filename: reached end of file"
			. " without reaching end of single quoted string.\n";
		    exit(-1);
		}
	    }
	}
        else
	{
	    $quote = $quote . $line;
	    $line = <IN>;
	    if (!defined($line))
	    {
		print STDERR "Error in $filename: reached end of file"
		    . " without reaching end of single quoted string.\n";
		exit(-1);
	    }
	}
    }
    return ($quote, $rest);
}
sub scan_quote
{
    my ($line, $filename);
    (*IN, $line, $filename) = @_;
    my $quote;
    my $rest;
    my ($end_of_quote) = 0;
    my ($before_separator, $separator);
    $quote = "";
    until ($end_of_quote)
    {
	if ($line =~ /(\\\\|\\\"|\"|\\\s*$)/)
	{
	    $separator = $1;
	    if ($separator =~ /\\\\/)
	    {
		($before_separator, $line) = split(/\\\\/, $line, 2);
		$quote = $quote . $before_separator . $separator;
	    }
	    elsif ($separator =~ /\\\"/) # matched (\")
	    {
		($before_separator, $line) = split(/\\\"/, $line, 2);
		$quote = $quote . $before_separator . $separator;
	    }
	    elsif ($separator =~ /\"/) # matched (")
	    {
		$end_of_quote = 1;
		($before_separator, $rest) = split(/\"/, $line, 2);
		$quote = $quote . $before_separator . $separator;
	    }
	    else #elsif ($separator =~ /\\\s*$/) # matched (\)
	    {
		chop $line;
		$quote = $quote . $line;
		$line = <IN>;
		if (!defined($line))
		{
		    print STDERR "Error in $filename: reached end of file"
			. " without reaching end of double quoted string.\n";
		    exit(-1);
		}
	    }
	}
        else
	{
	    $quote = $quote . $line;
	    $line = <IN>;
	    if (!defined($line))
	    {
		print STDERR "Error in $filename: reached end of file"
		    . " without reaching end of double quoted string.\n";
		exit(-1);
	    }
	}
    }
    return ($quote, $rest);
}
sub process_file
{
    my($infile) = @_;
    my($has_directive) = 0;
    my($line_num_for_directive);
    my($line_text, $curr_text, $processed_line, $processed_wo_quotes, 
       $inside_quote);
    my($eol) = 0;
    my($pre_separator, $post_separator, $separator);
    my(@output);
    my($directive, $expression);
    my($curr_CCD_Vg) = 1;
    my($curr_CCD_Vgp) = 1;
    (open(INFILE, "$infile")) ||
	die "process_file(): can't open \"$infile\".";
    select(INFILE);
    $| = 1;
    select(STDOUT);
    while ($line_text = <INFILE>)
    {
	$processed_line = "";
	$processed_wo_quotes = "";
	$has_directive = 0;
	if ($line_text =~ (/^\s*\#\s*if[^A-Za-z]?/)
	    || ($line_text =~ /^\s*\#\s*else[^A-Za-z]?/)
	    || ($line_text =~ /^\s*\#\s*elif[^A-Za-z]?/)
	    || ($line_text =~ /^\s*\#\s*ifdef[^A-Za-z]?/)
	    || ($line_text =~ /^\s*\#\s*ifndef[^A-Za-z]?/)
	    || ($line_text =~ /^\s*\#\s*endif[^A-Za-z]?/))
	{
	    $has_directive = 1;
	    $line_num_for_directive = $.;
	}
	$eol = 0;
	$curr_text = $line_text;
	until ($eol)
	{
	    if ($curr_text =~ /(\'|\"|\/\/|\/\*|\\\s*$)/)
	    {
		$separator = $1;
		if ($separator =~ /^\'/) # found a quote (')
		{
		    ($pre_separator, $post_separator) = split(/\'/, 
							      $curr_text, 
							      2);
		    $processed_wo_quotes = $processed_line . $pre_separator;
		    $processed_line = $processed_line . $pre_separator . "\'";
		    ($inside_quote, $curr_text) = &scan_tick(*INFILE,
							      $post_separator,
							      $infile);
		    $processed_line = $processed_line . $inside_quote;
		}
		elsif ($separator =~ /^\"/) # found a quote (")
		{
		    ($pre_separator, $post_separator) = split(/\"/, 
							      $curr_text, 
							      2);
		    $processed_wo_quotes = $processed_line . $pre_separator;
		    $processed_line = $processed_line . $pre_separator . "\"";
		    ($inside_quote, $curr_text) = &scan_quote(*INFILE, 
							      $post_separator,
							      $infile);
		    $processed_line = $processed_line . $inside_quote;
		}
		elsif ($separator =~ /\/\//) # found a comment (//)
		{
		    $eol = 1;
		    ($pre_separator, $post_separator) = split(/\/\//, 
							      $curr_text, 
							      2);
		    $processed_line = $processed_line . $pre_separator;
		    $processed_wo_quotes = $processed_wo_quotes 
			. $pre_separator;
		}
		elsif ($separator =~ /\/\*/) # found a comment (/*)
		{
		    ($pre_separator, $post_separator) = split(/\/\*/, 
							      $curr_text, 
							      2);
		    $processed_line = $processed_line . $pre_separator;
		    $processed_wo_quotes = $processed_wo_quotes 
			. $pre_separator;
		    ($curr_text) = &scan_comment(*INFILE,
						 $post_separator,
						 $infile);
		}
		else # ($separator =~ /\\/) # found a \ continuation (\)
		{
		    $curr_text =~ /(.*)\\\s*$/;
		    ($processed_line) = $processed_line . $1;
		    ($processed_wo_quotes) = $processed_wo_quotes . $1;
		    $curr_text = <INFILE>;
		    if (!defined($curr_text))
		    {
			print STDERR "Error in $infile: reached end of file"
			    . " without reaching end of backslash continued"
				. " conditional directive.\n";
			exit(-1);
		    }
		}
	    }
	    else
	    {
		$eol = 1;
		if ($curr_text =~ /.*\n$/s)
		{
		    chop($curr_text);
		}
		$processed_line = $processed_line . $curr_text;
		$processed_wo_quotes = $processed_wo_quotes . $curr_text;
	    }
	}
	if ($has_directive)
	{
	    if ($processed_wo_quotes =~ /^\s*\#\s*(elif|if)/)
	    {
		$curr_CCD_Vg += 1;
		$curr_CCD_Vgp += 1;
	    }
	    $processed_line =~ 
		/^\s*\#\s*(ifndef|ifdef|if|endif|else|elif)([^\0]*)/;
	    $directive = "\#" . $1;
	    $expression = $2;
	    unless (defined($expression))
	    {
		$expression = "";
	    }
	    ($curr_CCD_Vgp) += &vgprime($expression);
	    if ($expression =~ /^\s*(\S.*\S|\S)\s*$/)
	    {
		$expression = $1;
	    }
	    (@output) = (@output,
			 $directive,
			 $expression,
			 $line_num_for_directive,
			 1,
			 -1,
			 -1,
			 0
			 );
	}
    }
    $LOC = $.;
    close(INFILE);
    return ($LOC, $curr_CCD_Vg, $curr_CCD_Vgp, @output);
}
sub size_stack{
    my ($Data_Size,@Stack) = @_;
    $Size = (($#Stack + 1)/$Data_Size);
    return ($Size);
}
sub verify_cclogic{
    my $IF = "#if";
    my $IFD = "#ifdef";
    my $IFND = "#ifndef";
    my $END = "#endif";
    my $ELS = "#else";
    my $ELI = "#elif";
    my $errmsg;
    my $nest_level;
    my @sublist = ();
    my ($fname,$stack_item_size,@stack) = @_;
    my $else_flag;
    $else_flag = 0;
    $nest_level = 0;
    while($#stack != -1){ #While there are lines of CC logic on the stack
	@sublist = splice(@stack,0,$stack_item_size);	    
	$_ = $sublist[0];
	if (/$IF|$IFD|$IFND/){
	    $else_flag = 0;
	    $nest_level++;
	}
	elsif (/$END/){
	    $else_flag = 0;
	    if ($nest_level == 0){
		$errmsg = "\nFatal Error - Unmatched \#endif at line $sublist[2]\n\n";
		die ($errmsg);
	    }
	    $nest_level--;
	}
	elsif (/$ELS/){
	    if ($nest_level == 0){
		$errmsg = "\nFatal Error - #else statement outside any #if block at line " 
		    . $sublist[2] . "\n\n";
		die ($errmsg);
	    }
	    $else_flag = 1;
	}
	elsif (/$ELI/){
	    if ($nest_level == 0){
		$errmsg = "\nFatal Error - \#elif statement outside any \#if block at line " 
		    . $sublist[2] . "\n\n";
		die ($errmsg);
	    }
	    if ($else_flag){
		$errmsg = "\nFatal Error - \#elif following an \#else at line " 
		    . $sublist[2] . "\n\n";
		die ($errmsg);
	    }
	    $else_flag = 0;
	}
    }
    if ($nest_level != 0){
	$errmsg = "\nFatal Error - Missing \#endif\n\n";
	die ($errmsg);
    }
}
sub vgprime{
    my $AND = "&&";
    my $OR = "||";
    my ($expression) = @_;
    if (!defined($expression)){
	return (0);
    }
    else{
	$expression =~ s/\&\&/junk $AND junk/g;
	$expression =~ s/\|\|/junk $OR junk/g;
	@sub_expressions = split (/\&\&|\|\|/, $expression);
	if ($#sub_expressions < 0){
	    return(0);
	}
	else{
	    return ($#sub_expressions);
	}
    }
}
sub gen_image
{
    my ($graphfile, $infile, 
	$item_size, $max_x, $max_y, $file_loc, $scale_factor,
	$CCD_list) = @_;
    my ($curr_item) = 0;
    my ($xpages, $ypages);
    my ($xscale, $yscale);
    my (%page_block);
    my ($top_loc);
    my ($junk1, $junk2, $junk3);
    if ($scale_factor eq 'p')
    {
	$xpages = 1;
	$ypages = 1;
        $xscale = 64 / ($max_x + 3);
        $yscale = 80 / ($max_y + 2);
        $scale_factor = $xscale;
        if ($scale_factor > $yscale)
        {
            $scale_factor = $yscale;
        }
    }
    else
    {
	$xpages = int (($max_x + 2) / (64 / $scale_factor)) + 1;
	$ypages = int (($max_y + 1) / (80 / $scale_factor)) + 1;
    }
    %page_block = &PS_create_page_block($infile, $xpages, $ypages);
    if ($#$CCD_list == -1)
    {
	$top_loc = $file_loc . " LOC";
    }
    else
    {
	$top_loc = "";
    }
	&PS_draw_top_line(\%page_block,
			  $scale_factor,
			  $top_loc,
			  1,
			  0, 
			  0
			  );
    &draw_block(\%page_block,
		$scale_factor,
		$xpages,
		0,
		$item_size,
		$curr_item,
		$max_y,
		$file_loc,
		0,
		0,
		$CCD_list
		);
    &PS_draw_bottom_line(\%page_block,
			 $scale_factor,
			 1, 
			 0, 
			 $max_y + 1
			 );
    &PS_create_file($graphfile,
		    $scale_factor / 4,
		    $xpages,
		    $ypages,
		    \%page_block
		    );
}
sub draw_block
{
    my ($page_block, $scale_factor, $xpages, $depth, $item_size, $curr_item, 
	$max_y, $file_loc, $curr_x, $curr_y, $CCD_list) = @_;
    my($loc, $i, $j);
    my($has_no_else);
    my($temp_x, $temp_y);
    my($top_line_item, $line_loc);
    unless (($curr_item * $item_size < $#$CCD_list)
	    &&
	    (($$CCD_list[$curr_item * $item_size]) =~ /\#if/))
    {
	return ($curr_item, -1, -1);
    }
    while ((($curr_item + 1) * $item_size < $#$CCD_list)
	   && ($$CCD_list[$curr_item * $item_size + 5]
	       <= $$CCD_list[($curr_item + 1) * $item_size + 5])
	   && ($$CCD_list[$curr_item * $item_size + 5] == $depth))
    {
	$has_no_else = 1;
	while ((($curr_item * $item_size) < $#$CCD_list)
	       && ((($$CCD_list[$curr_item * $item_size]) !~ /\#endif/)
		   || ($$CCD_list[$curr_item * $item_size + 5] 
		       != $depth)))
	{
	    $top_line_item = $curr_item;
	    $curr_x = $$CCD_list[$curr_item * $item_size + 3] + 1;
	    $curr_y = $$CCD_list[$curr_item * $item_size + 4] + 2;
	    if ($$CCD_list[$curr_item * $item_size] =~ /\#if/)
	    {
		if ($curr_item == 0)
		{
		    $loc = ($$CCD_list[$curr_item * $item_size + 2] - 1) 
			. " LOC";
		}
		elsif ($$CCD_list[$curr_item * $item_size + 5]
		       > $$CCD_list[($curr_item - 1) * $item_size + 5])
		{
		    $loc = ($$CCD_list[$curr_item * $item_size + 2]
			    - $$CCD_list[($curr_item - 1) * $item_size + 2] - 1)
			. " LOC";
		}
		else
		{
		    $loc = "";
		}
		&PS_insert_if($page_block,
			      $scale_factor,
			      $xpages,
			      $$CCD_list[$curr_item * $item_size],
			      $$CCD_list[$curr_item * $item_size + 1],
			      $$CCD_list[$curr_item * $item_size + 2],
			      $loc,
			      0,
			      $$CCD_list[$curr_item * $item_size 
					+ 6],
			      $$CCD_list[$curr_item * $item_size 
					+ 4]
			      );
	    }
	    elsif ($$CCD_list[$curr_item * $item_size] =~ /\#elif/)
	    {
		&PS_insert_if($page_block,
			      $scale_factor,
			      $xpages,
			      $$CCD_list[$curr_item * $item_size],
			      $$CCD_list[$curr_item * $item_size + 1],
			      $$CCD_list[$curr_item * $item_size + 2],
			      "",
			      1,
			      $$CCD_list[$curr_item * $item_size 
					 + 6],
			      $$CCD_list[$curr_item * $item_size 
					 + 4]
			      );
	    }
	    elsif ($$CCD_list[$curr_item * $item_size] =~ /\#else/)
	    {
		&PS_insert_else($page_block,
				      $scale_factor,
				      $$CCD_list[$curr_item * $item_size + 2],
				      $$CCD_list[$curr_item * $item_size 
						+ 6],
				      $$CCD_list[$curr_item * $item_size
						+ 4]
				      );
		$has_no_else = 0;
	    }
	    if ($curr_item * $item_size < $#$CCD_list)
	    {
		($curr_item, 
		 $temp_x, $temp_y) = &draw_block($page_block,
						 $scale_factor,
						 $xpages,
						 $depth + 1,
						 $item_size,
						 ++$curr_item,
						 $max_y,
						 $file_loc,
						 $curr_x,
						 $curr_y,
						 $CCD_list
						 );
		if ($temp_x != -1)
		{
		    $curr_x = $temp_x;
		}
		if ($temp_y != -1)
		{
		    $curr_y = $temp_y;
		}
	    }
	    if (($temp_x == -1) && ($temp_y == -1))
	    {
		$line_loc = $$CCD_list[($top_line_item + 1) * $item_size + 2]
		    - $$CCD_list[$top_line_item * $item_size + 2] - 1
			. " LOC";
	    }
	    else
	    {
		$line_loc = "";
	    }
	    &PS_draw_top_line($page_block,
				    $scale_factor,
				    $line_loc,
				    $$CCD_list[$top_line_item * $item_size 
					      + 3]
				    -
				    $$CCD_list[$top_line_item * $item_size
					      + 6],
				    $$CCD_list[$top_line_item * $item_size 
					      + 6]
				    + 1,
				    $$CCD_list[$top_line_item * $item_size 
					      + 4]
				    );
	    for ($i = $curr_item;
		 ($i * $item_size < $#$CCD_list)
		 && (($$CCD_list[$i * $item_size] !~ /\#endif/)
		     || ($$CCD_list[$i * $item_size + 5] 
			 != $depth));
		 $i++)
	    {
	    }
	    if (($i * $item_size < $#$CCD_list)
		&&
		(($$CCD_list[$i * $item_size + 4] - $curr_y + 1)
		 > 0))
	    {
		&PS_draw_vert_line($page_block,
					 $scale_factor,
					 $$CCD_list[$i * $item_size + 4]
					 - $curr_y + 1,
					 0,
					 $curr_x - 1,
					 $curr_y - 1
					 );
	    }
	    if ($i * $item_size < $#$CCD_list)
	    {
		&PS_draw_bottom_line($page_block,
					   $scale_factor,
					   $curr_x - $$CCD_list[$i * $item_size
							       + 6] - 1,
					   $curr_x - 1,
					   $$CCD_list[$i * $item_size 
						     + 4]
					   );
	    }
	} # matches inner while loop
	if ($i * $item_size < $#$CCD_list)
	{
	    if (($curr_item + 1) * $item_size < $#$CCD_list)
	    {
		&PS_insert_endif($page_block,
				       $scale_factor,
				       $$CCD_list[$curr_item * $item_size + 2],
				       $$CCD_list[($curr_item + 1) * $item_size
						 + 2] 
				       - $$CCD_list[$curr_item * $item_size 
						   + 2] - 1,
				       $has_no_else,
				       $$CCD_list[$curr_item * $item_size + 6],
				       $$CCD_list[$curr_item * $item_size + 4]
				       );
	    }
	    else
	    {
		&PS_insert_endif($page_block,
				       $scale_factor,
				       $$CCD_list[$curr_item * $item_size + 2],
				       $file_loc
				       - $$CCD_list[$curr_item * $item_size 
						   + 2],
				       $has_no_else,
				       $$CCD_list[$curr_item * $item_size + 6],
				       $$CCD_list[$curr_item * $item_size + 4]
				       );
	    }
	    if ($has_no_else)
	    {
		for ($j = $curr_item - 1; 
		     ($j >= 0) 
		     && ($$CCD_list[$j * $item_size + 5]
			 != $$CCD_list[$curr_item * $item_size + 5]
			 );
		     $j--)
		{
		}
		&PS_draw_vert_line($page_block,
					 $scale_factor,
					 $$CCD_list[$curr_item * $item_size
						    + 4]
					 - $$CCD_list[$j * $item_size + 4] - 1,
					 1,
					 $$CCD_list[$j * $item_size + 6],
					 $$CCD_list[$j * $item_size + 4] + 1
					 );
	    }
	    $curr_x = $$CCD_list[$curr_item * $item_size + 6] + 1;
	    $curr_y = $$CCD_list[$curr_item * $item_size + 4] + 2;
	}
	$curr_item++;
    } # matches outer while loop
    return ($curr_item, $curr_x, $curr_y);
}
sub assign_position{
    my ($data_size, $stack) = @_;
    my (@sublist) = ();
    my ($i, $j,
	$stack_size,
	$max_lx, $max_x, $max_y,
	$next_element);
    ($max_lx) = &logical_x_position(-1,
				    $data_size,
				    $stack);
    if ($#$stack == -1)
    {
	$max_y = 0;
    }
    else
    {
	($max_y, $next_element) = &y_position(1,
					      0,
					      $data_size,
					      $stack);
    }
    ($max_x) = &physical_x_position(0, 
				    0, 
				    $data_size,
				    $stack);
    return($max_x, $max_y);
}
sub y_position{
    my ($current_y, $current_lx, $data_size, $stack) = @_;
    my ($max_y, $block_max_y) = 1;
    my ($i);
    my (@new_stack) = ();
    for($i = 0; $i < $#$stack + 1; $i = $i + $data_size){
	if($current_y > $max_y){
	    $max_y = $current_y;
	}
	if($$stack[$i + 5] == $current_lx){
	    if($$stack[$i] !~ m/\#endif/){
		$$stack[$i + 4] = $current_y;
		if($$stack[$i + $data_size + 5] == ($current_lx + 1)){
		    @new_stack = splice(@$stack, 0, $i + $data_size);
		    ($block_max_y, 
		     $i) = y_position($current_y + 1, 
					  $current_lx + 1, 
					  $data_size, 
					  $stack);
		    if($block_max_y > $max_y){
			$max_y = $block_max_y;
		    }
		    $i = @new_stack + $i - $data_size;
		    @$stack = (@new_stack, @$stack);
		}
		$current_y++;
	    }
	    else{
		if($max_y > $current_y){
		    $current_y = $max_y;
		}
		$$stack[$i + 4] = $current_y;
		$current_y++;
	    }
	}
	elsif($$stack[$i + 5] < $current_lx){
	    return($max_y, $i);
	}
    }
    return($max_y, $i);
}
sub logical_x_position{
    my ($current_x, $data_size, $stack) = @_;
    my ($i, $max_x) = (0, 0, 0, 0);
    for($i = 0; $i < @$stack; $i += $data_size){
	if($$stack[$i] =~ m/\#endif/){
	    $$stack[$i + 5] = $current_x;
	    $current_x--;
	}
	elsif($$stack[$i] =~ m/\#elif|\#else/){
	    $$stack[$i + 5] = $current_x;
	}
	else{
	    $current_x++;
	    $$stack[$i + 5] = $current_x;
	}
	if($current_x > $max_x){
	    $max_x = $current_x;
	}
    }
    return($max_x);
}
sub physical_x_position{
    my ($current_p_x, $current_l_x, $data_size, $stack) = @_;
    my ($i, $j, $k, $max_x, $block_x);
    my (@block, @block_nodes, @rest) = ();
    $max_x = $block_x = 0;
    for($i = 0; $i < $#$stack + 1; $i += $data_size){
	$block_x = $current_p_x;
	@block_nodes = ();
	until(($$stack[$i] =~ m/\#endif/) 
	      && ($$stack[$i + 5] == $current_l_x)){
	    if($$stack[$i + 5] == $current_l_x){
		@block_nodes = (@block_nodes, $i);
	    }
	    $i += $data_size;
	}
	$$stack[$i + 6] = $current_p_x;
	$k = $i;
	for($j = $k - $data_size; 
	    $j >= $block_nodes[0]; 
	    $j -= $data_size){
	    if($$stack[$j] !~ m/\#endif/){
		$$stack[$j + 3] = $block_x + 1;
	    }
	    if($$stack[$j + 5] == $current_l_x){
		$$stack[$j + 6] = $current_p_x;
		@rest = splice(@$stack, $k);
		@block = splice(@$stack, $j + $data_size);
		if($k > $j + $data_size){
		    ($block_x) = &physical_x_position($block_x + 1, 
							      $current_l_x + 1,
							      $data_size, 
							      \@block);
		}
		elsif($$stack[$j] !~ m/\#endif/){
		    $block_x++;
		}
		@$stack = (@$stack, @block, @rest);
		$k = $j;
	    }
	}
	if($block_x > $max_x){
	    $max_x = $block_x;
	}
    }
    return($max_x);
}
sub PS_create_page_block
{
    my ($filename, $xpages, $ypages) = @_;
    my (%page_block) = "";
    my ($i, $j, $page);
    my ($page_template) = "%%Page: %PAGE% %PAGE%
%%BeginPageSetup
%%EndPageSetup
gsave
  (%FILENAME% \(%XPAGE%, %YPAGE%\)) PageNum
  gsave
    %XPAGE% %YPAGE% XYPage
  grestore
  showpage
grestore
%%PageTrailer
";
    $page = 1;
    for ($i = 0; $i < $ypages; $i++)
    {
	for ($j = 0; $j < $xpages; $j++)
	{
	    $_ = $page_template;
	    $_ =~ s/\%PAGE\%/$page/g;
	    $_ =~ s/\%XPAGE\%/$j/g;
	    $_ =~ s/\%YPAGE\%/$i/g;
	    $_ =~ s/\%FILENAME\%/$filename/g;
	    $page_block{$j, $i} = $_;
	    $page++;
	}
    }
    return %page_block;
}
sub PS_create_file
{
    my ($PSfilename, $scale_factor, $xpages, $ypages, $page_block) = @_;
    my ($i, $j);
    my ($before_pages) = "%!PS-Adobe-3.0
%%EndComments
%%BeginProlog
%------------- Procedure Definitions -------------
% sets font to bold
% float(point size) BF
/BF
{
  dup 
  /fontsize exch def
  /Helvetica-Bold findfont 
  exch scalefont % uses stack operand
  setfont
}
def
% draws a decision diamond for #if, #ifdef, and #ifndef
%
% str(#CCD)
% str(expression)
% int(line num)
% int(top loc)
% int(bottom loc)
% int(dotted top)
% int(x coord)
% int(y coord)
% DrawIf
/DrawIf
{
  gsave
    newpath
    square neg exch square exch translate % move to upper left corner of square
    % draw diamond and lines
    gsave
      0.5 square -0.25 square moveto
      0.25 square -0.25 square rlineto
      -0.25 square -0.25 square rlineto
      -0.25 square 0.25 square rlineto
      closepath
      stroke
    grestore
    gsave
      1 eq
      {
	[1 1] 1 setdash	
      }
      if
      0.5 square 0 square moveto
      0 square -0.25 square rlineto
      stroke
    grestore
    gsave
      0.75 square -0.5 square moveto
      0.25 square 0 square rlineto
      stroke
    grestore
    gsave
      [1 1] 0 setdash
      0.5 square -0.75 square moveto
      0 square -0.25 square rlineto
      stroke
    grestore
    % write lower LOC
    gsave
      0.53 square -0.82 square moveto
      show
    grestore
    % write upper LOC
    gsave
      0.53 square -0.18 square moveto
      show
    grestore
    % write line number
    gsave
      0.21 square -0.525 square moveto
      dup stringwidth pop neg 0 rmoveto show
    grestore
    %write expression
    gsave
      0.78 square -0.42 square moveto
      show
    grestore
    % write #if in center of diamond
    gsave
      0.5 square -0.5 square moveto
      dup stringwidth pop neg 2 div -0.025 square rmoveto
      show
    grestore
  grestore
}
bind def
% draws a triangle for #else
%
% str(line num)
% int(x coord)
% int(y coord)
% DrawElse
/DrawElse
{
  gsave
    newpath
    square neg exch square exch translate % move to upper left corner of square
    % draw triangle and lines
    gsave 
      0.375 square -0.125 square moveto
      0 square -0.5 square rlineto
      0.5 square 0 square rlineto
      closepath
      stroke
    grestore
    gsave
      0.5 square 0 square moveto
      [1 1] 1 setdash
      0 square -0.25 square rlineto
      stroke
    grestore
    gsave
      0.75 square -0.5 square moveto
      0.25 square 0 square rlineto
      stroke
    grestore
    % write line number
    gsave
      0.21 square -0.525 square moveto
      dup stringwidth pop neg 0 rmoveto show
    grestore
    % write #else in center of diamond
    gsave
      (\\#else) 
      0.435 square -0.525 square moveto
      show
    grestore
  grestore
} 
bind def
% draws a triangle for #endif
%
% str(line num)
% str(LOC)
% int(line flag) 1 if the line is to be drawn
% int(x coord)
% int(y coord)
% DrawEndif
/DrawEndif
{
  gsave
    newpath
    square neg exch square exch translate % move to upper left corner of square
    % draw circle and lines
    gsave 
      0.5 square -0.5 square 0.18 square 0 360 arc
      stroke
    grestore
    gsave
      1 eq
      {
	0.5 square 0 square moveto
	[1 1] 1 setdash
	0 square -0.32 square rlineto
	stroke
      }
      if
    grestore
    gsave
      0.68 square -0.5 square moveto
      0.32 square 0 square rlineto
      stroke
    grestore
    gsave
      0.5 square -0.68 square moveto
      0 square -0.32 square rlineto
      stroke
    grestore
    % write lower LOC
    gsave
      0.53 square -0.82 square moveto
      show
    grestore
    % write line number
    gsave
      0.21 square -0.525 square moveto
      dup stringwidth pop neg 0 rmoveto show
    grestore
    % write #endif in center of diamond
    gsave
      0.35 square -0.525 square moveto
      (\\#endif)
      show
    grestore
  grestore
} 
bind def
% Draws a line from left to right, plus the corner and LOC
%
% string(LOC)
% int(length)
% int(x start)
% int(y start)
% DrawTopLine
/DrawTopLine
{
  gsave
    square neg exch square exch translate % move to upper left corner of square
    dup % copy length for later use
    gsave
      0 square -0.5 square moveto
      0.5 sub square 0 square rlineto
      0 square -0.5 square rlineto
      stroke
    grestore
    gsave
      0.47 sub square -0.65 square moveto
      show
    grestore
  grestore
}
bind def
% Draws a line from right to left, plus the corner
%
% int(length)
% int(x start)
% int(y start)
% DrawBottomLine
/DrawBottomLine
{
  gsave
    square neg exch square exch translate % move to upper left corner of square
    gsave
      0.5 square 0 square moveto
      0 square -0.5 square rlineto
      0.5 sub neg square 0 square rlineto
      stroke
    grestore
  grestore
}
bind def
% Draws a line from top to bottom
%
% int(length)
% int(is dotted)
% int(x start)
% int(y start)
% DrawVertLine
/DrawVertLine
{
  gsave
    square neg exch square exch translate % move to upper left corner of square
    gsave
      1 eq
      {
	[1 1] 1 setdash
      }
      if
      0.5 square 0 square moveto
      square neg 0 square exch rlineto
      stroke
    grestore
  grestore
}
bind def
%
% miscellaneous defs
%
/XPt {576} bind def
/YPt {720} bind def
/square {36 mul} bind def
/ScaleFactor {%SCALE%} bind def
/Scale {ScaleFactor div} bind def
/XPage {XPt Scale mul} bind def
/YPage {YPt Scale mul} bind def
/XYPage 
{
  % set clipping path for this page
  newpath
  -1 square 0 moveto
  XPt Scale 0 rlineto
  0 YPt neg Scale rlineto
  XPt neg Scale 0 rlineto
  closepath clip
  0.7 setgray
  clippath stroke
  0 setgray
  % translate to this page
  YPage exch XPage neg exch translate
}
bind def
% prints page number on page
%
% str(page number)
% PageNum
/PageNum
{
  gsave
    1 ScaleFactor div dup scale
    /Helvetica-Bold findfont 15 scalefont setfont
    newpath
    -0.95 square ScaleFactor mul
    6
    moveto
    show
  grestore
}
bind def
% End of procedure definitions
%EndProlog
%%BeginSetup
/Helvetica-Bold findfont 0.1 square scalefont setfont
20 72 11 mul 36 sub translate % move origin to top left of graph
ScaleFactor ScaleFactor scale
1 square 0 translate
%%EndSetup
";
    my ($after_pages) = "%%Trailer
%%EOF
";
    open (PSOUT, ">$PSfilename")
	|| die "Fatal Error - Couldn't open $PSfilename for writing.\n";
    $before_pages =~ s/\%SCALE\%/$scale_factor/g;
    print PSOUT "$before_pages";
    for ($j = 0; $j < $ypages; $j++)
    {
      for ($i = 0; $i < $xpages; $i++)
      {
        print PSOUT "$$page_block{$i, $j}";
      }
    }
    print PSOUT "$after_pages";
    close PSOUT;
}
sub PS_insert_if
{
    my ($page_block, $scale, $xpages,
       $ccd, $expression, $line_num, $loc, $dotted_top, 
       $xpos, $ypos) = @_;
    my ($xpage_start, $xpage_end, $ypage, $i);
    my ($junk);
    $expression =~ s/\\/\\\\/g;
    $expression =~ s/\)/\\\)/g;
    $expression =~ s/\(/\\\(/g;
    if ($scale eq 'p') # scaled to one page
    {
	$xpage_start = 0;
	$xpage_end = 0;
	$ypage = 0;
    }
    else # must be 1, 2, 4, 8, or 16
    {
	$xpage_start = (($xpos) / (64 / $scale));
	($xpage_start, $junk) = split(/\./, $xpage_start, 2);
	$xpage_end = (($xpos + 1) / (64 / $scale));
	($xpage_end, $junk) = split(/\./, $xpage_end, 2);
	$xpage_end += 1;
	$ypage = (($ypos) / (80 / $scale));
	($ypage, $junk) = split(/\./, $ypage, 2);
    }
    for ($i = $xpage_start; ($i <= $xpage_end) && ($i < $xpages); $i++)
    {
	$$page_block{$i, $ypage} 
	=~ s/$i $ypage XYPage/$i $ypage XYPage\n  \(\\$ccd\) \($expression\) \($line_num\) \($loc\) \(\) $dotted_top $xpos $ypos DrawIf/;
    }
}
sub PS_insert_else
{
    my ($page_block, $scale, 
     $line_num, $xpos, $ypos) = @_;
    my ($xpage_start, $xpage_end, $ypage, $i);
    my ($junk);
    if ($scale eq 'p') # scaled to one page
    {
	$xpage_start = 0;
	$xpage_end = 0;
	$ypage = 0;
    }
    else # must be 1, 2, 4, 8, or 16
    {
	$xpage_start = (($xpos) / (64 / $scale));
	($xpage_start, $junk) = split(/\./, $xpage_start, 2);
	$xpage_end = (($xpos + 1) / (64 / $scale));
	($xpage_end, $junk) = split(/\./, $xpage_end, 2);
	$ypage = (($ypos) / (80 / $scale));
	($ypage, $junk) = split(/\./, $ypage, 2);
    }
    for ($i = $xpage_start; $i <= $xpage_end; $i++)
    {
	$$page_block{$i, $ypage} =~ s/$i $ypage XYPage/$i $ypage XYPage\n  \($line_num\) $xpos $ypos DrawElse/;
    }
}
sub PS_insert_endif
{
    my ($page_block, $scale, 
	$line_num, $loc, $draw_above_line, $xpos, $ypos) = @_;
    my ($xpage_start, $xpage_end, $ypage, $i);
    my ($junk);
    if ($scale eq 'p') # scaled to one page
    {
	$xpage_start = 0;
	$xpage_end = 0;
	$ypage = 0;
    }
    else # must be 1, 2, 4, 8, or 16
    {
	$xpage_start = (($xpos) / (64 / $scale));
	($xpage_start, $junk) = split(/\./, $xpage_start, 2);
	$xpage_end = (($xpos + 1) / (64 / $scale));
	($xpage_end, $junk) = split(/\./, $xpage_end, 2);
	$ypage = (($ypos) / (80 / $scale));
	($ypage, $junk) = split(/\./, $ypage, 2);
    }
    for ($i = $xpage_start; $i <= $xpage_end; $i++)
    {
	$$page_block{$i, $ypage} 
	=~ s/$i $ypage XYPage/$i $ypage XYPage\n  \($line_num\) \($loc LOC\) $draw_above_line $xpos $ypos DrawEndif/;
    }
}
sub PS_draw_top_line
{
    my ($page_block, $scale,
	$loc, $length, $xstart, $ystart) = @_;
    my ($xpage_start, $xpage_end, $ypage, $i);
    my ($junk);
    if ($scale eq 'p') # scaled to one page
    {
	$xpage_start = 0;
	$xpage_end = 0;
	$ypage = 0;
    }
    else # must be 1, 2, 4, 8, or 16
    {
	$xpage_start = (($xstart + 1) / (64 / $scale));
	($xpage_start, $junk) = split(/\./, $xpage_start, 2);
	$xpage_end = (($xstart + $length + 2) / (64 / $scale));
	($xpage_end, $junk) = split(/\./, $xpage_end, 2);
	$ypage = (($ystart) / (80 / $scale));
	($ypage, $junk) = split(/\./, $ypage, 2);
    }
    for ($i = $xpage_start; $i <= $xpage_end; $i++)
    {
	$$page_block{$i, $ypage}
	=~ s/$i $ypage XYPage/$i $ypage XYPage\n  \($loc\) $length $xstart $ystart DrawTopLine/;
    }
}
sub PS_draw_bottom_line
{
    my ($page_block, $scale, 
	$length, $xstart, $ystart) = @_;
    my ($xpage_start, $xpage_end, $ypage, $i);
    my ($junk);
    if ($scale eq 'p') # scaled to one page
    {
	$xpage_start = 0;
	$xpage_end = 0;
	$ypage = 0;
    }
    else # must be 1, 2, 4, 8, or 16
    {
	$xpage_start = (($xstart + 1) / (64 / $scale));
	($xpage_start, $junk) = split(/\./, $xpage_start, 2);
	$xpage_end = (($xstart - $length + 1) / (64 / $scale));
	($xpage_end, $junk) = split(/\./, $xpage_end, 2);
	$ypage = (($ystart) / (80 / $scale));
	($ypage, $junk) = split(/\./, $ypage, 2);
    }
    for ($i = $xpage_end; $i <= $xpage_start; $i++)
    {
	$$page_block{$i, $ypage}
	=~ s/$i $ypage XYPage/$i $ypage XYPage\n  $length $xstart $ystart DrawBottomLine/;
    }
}
sub PS_draw_vert_line
{
    my ($page_block, $scale,
	$length, $dotted, $xstart, $ystart) = @_;
    my ($xpage, $ypage_start, $ypage_end, $i);
    my ($junk);
    if ($scale eq 'p') # scaled to one page
    {
	$xpage = 0;
	$ypage_start = 0;
	$ypage_end = 0;
    }
    else # must be 1, 2, 4, 8, or 16
    {
	$xpage = (($xstart + 1) / (64 / $scale));
	($xpage, $junk) = split(/\./, $xpage, 2);
	$ypage_start = (($ystart) / (80 / $scale));
	($ypage_start, $junk) = split(/\./, $ypage_start, 2);
	$ypage_end = (($ystart + $length + 1) / (80 / $scale));
	($ypage_end, $junk) = split(/\./, $ypage_end, 2);
    }
    for ($i = $ypage_start; $i <= $ypage_end; $i++)
    {
	$$page_block{$xpage, $i}
	=~ s/$xpage $i XYPage/$xpage $i XYPage\n  $length $dotted $xstart $ystart DrawVertLine/;
    }
}
