#!/bin/sh

echo This script takes a minute to run.  Be patient. 1>&2

# pad stdin to multiple of 120 lines
pad()
{
	awk '{print} END{for(; NR%120!=0; NR++) print ""}'
}

# create formatted (numbered) files
mkdir -p fmt
rm fmt/*
cp README fmt
files=`grep -v '^#' runoff.list | awk '{print $1}'`
n=99
for i in $files
do
	perl -e '$n='$n';' -e ' 
		$n = int(($n+49)/50)*50 - 1;

		@lines = <>;
		foreach (@lines) {
			chomp;
			s/\s+$//;
			if(length() >= 75){
				print "$ARGV[0]:$.: line too long";
			}
		}
		@outlines = ();
		$nextout = 0;
		for($i=0; $i<@lines; ){
			# Skip leading blank lines.
			$i++ while $i<@lines && $lines[$i] =~ /^$/;
			last if $i>=@lines;

			# If the rest of the file fits, use the whole thing.
			if(@lines <= $i+50){
				$breakbefore = @lines;
			}else{
				# Find a good next page break;
				# Hope for end of function.
				# but settle for a blank line (but not first blank line
				# in function, which comes after variable declarations).
				$breakbefore = $i;
				$lastblank = $i;
				$sawbrace = 0;
				$breaksize = 15;  # 15 lines to get to function
				for($j=$i; $j<$i+50 && $j < @lines; $j++){
					if($lines[$j] =~ /PAGEBREAK:\s*([0-9]+)/){
						$breaksize = int($2);
						$breakbefore = $j;
						$lines[$j] = "";
					}
					if($lines[$j] =~ /^}$/){
						$breakbefore = $j+1;
					}
					if($lines[$j] =~ /^{$/){
						$sawbrace = 1;
					}
					if($lines[$j] =~ /^$/){
						if($sawbrace){
							$sawbrace = 0;
						}else{
							$lastblank = $j;
						}
					}
				}
				if($j<@lines && $lines[$j] =~ /^$/){
					$lastblank = $j;
				}
				
				# If we are not putting enough on a page, try a blank line.
				if($breakbefore - $i < 50 - $breaksize && $lastblank > $breakbefore && $lastblank >= $i+50 - 5){
					$breakbefore = $lastblank;
					$breaksize = 5;  # only 5 lines to get to blank line
				}

				# If we are not putting enough on a page, force a full page.
				if($breakbefore - $i < 50 - $breaksize && $breakbefore != @lines){
					$breakbefore = $i + 50;
					$breakbefore = @lines if @lines < $breakbefore;
				}

				if($breakbefore < $i+2){
					$breakbefore = $i+2;
				}
			}

			# Emit the page.
			$i50 = $i + 50;
			for(; $i<$breakbefore; $i++){
				printf "%04d %s\n", ++$n, $lines[$i];
			}

			# Finish page
			for($j=$i; $j<$i50; $j++){
				printf "%04d \n", ++$n;
			}
		}
	' $i >fmt/$i

	nn=`tail -1 fmt/$i | sed 's/ .*//; s/^0*//'`
	if [ "x$nn" != x ]; then
		n=$nn
	fi
done

# create table of contents
pr -e8 -t runoff.list | awk '
/^[a-z]/ {
	s=$0
	f="fmt/"$1
	getline<f
	close(f)
	n=$1
	printf("%02d %s\n", n/100, s);
	next
}
{
	print
}' >fmt/toc

# make definition list
cd fmt
perl -e '
	while(<>) {
		chomp;

		s!//.*!!;
		s!/\*([^*]|[*][^/])*\*/!!g;
		s!\s! !g;
		s! +$!!;

		# look for declarations like char* x;
		if (/^[0-9]+ typedef .* u(int|short|long|char);/) {
			next;
		}
		if (/^[0-9]+ extern/) {
			next;
		}
		if (/^[0-9]+ struct [a-zA-Z0-9_]+;/) {
			next;
		}
		if (/\(/) {
			next;
		}

		if (/^([0-9]+) (((static|struct|extern|union|enum) +)*([A-Za-z0-9_]+))( .*)? +([A-Za-z_][A-Za-z0-9_]*)[,;]/) {
		
			print "$1 $7\n"
		}
		
		elsif (/^([0-9]+) #define +([A-za-z0-9_]+) +?\(.*/) {
			print "$1 $2\n"
		}
		
		elsif (/^([0-9]+) #define +([A-Za-z0-9_]+) +([^ ]+)$/) {
			print "$1 $2 $3\n";
		}
		
		elsif (/^([0-9]+) #define +([A-Za-z0-9_]+)/) {
			print "$1 $2\n";
		}
		
		elsif(/^([0-9]+) (enum|struct|union) +([A-Za-z0-9_]+) +{/){ 
			print "$1 $3\n";
		}
		# TODO: enum members
	}
' $files >defs

perl -n -e 'print if s/^([0-9]+ [a-zA-Z0-9_]+)\(.*$/\1/;' $files |
	egrep -v ' (usage|main|if|for)$' >>defs
(
>s.defs

# make reference list
for i in `awk '{print $2}' defs | sort -fu`
do
	defs=`egrep '^[0-9]+ '$i'( |$)' defs | awk '{print $1}'`
	echo $i $defs >>s.defs
	uses=`egrep -h '([^a-zA-Z_0-9])'$i'($|[^a-zA-Z_0-9])' $files | awk '{print $1}'`
	echo $i $defs
	echo $uses |fmt -24 | sed 's/^/    /'
done
) >refs

# build defs list
awk '
{
	printf("%04d %s\n", $2, $1);
	for(i=3; i<=NF; i++)
		printf("%04d    \" \n", $i);
}
' s.defs > t.defs

# format the whole thing
(
	pr -l60 -e8 README
	pr -l60 -h "table of contents" -e8 -2 toc
	pr -l60 -h "definitions" -2 t.defs | pad
	pr -l60 -h "cross-references" -2 refs | pad 
	for i in $files
	do
		cat $i | pr -l60 -e8 -h "xv6/$i"
	done
) | mpage -m50t50b -o -bLetter -T -t -2 -FCourier -L60 >all.ps
grep Pages: all.ps

# if we have the nice font, use it
nicefont=~rsc/plan9/sys/lib/postscript/font/LucidaSans-Typewriter83
if [ -f $nicefont ]
then
	(sed 1q all.ps; cat $nicefont; sed '1d; s/Courier/LucidaSans-Typewriter83/' all.ps) >allf.ps
else
	cp all.ps allf.ps
fi
ps2pdf allf.ps ../xv6.pdf