#!/bin/sh

# pdvi v1.03b	Public Domain.	NIDE lab @ Nara-WU.

# ϲ:
#   xdviȻѴĤ褦˺äDVIӥ塼
#   MacPortsTexLivexdviܸбǤʤxdviʤȤƺä
#   ϡޤDVIեPDFxpdfǱȤ
#   xdvi˻Ѵ뤿ˡDVIե뤬줿PDFե⹹
#   xpdfɽեå夹褦ˤƤ

# ˡ:
#   pdvi [-rpemx] DVIե̾
#   -rץ󤬤ȡDVI줿ݤ˥եˡLabel(s) may have
#   changed.פΥåäƤɽΥեå򤹤롣ǥեȤǤ
#   DVIƤ⤳ΥåɽΥեåϤʤ
#   -pץ󤬤ȡDVIPDFdvipdfmxǤʤdvips+ps2pdfǹԤ
#   -eץ󤬤ȡxpdf夨evinceȤ
#   -mץ󤬤ȡxpdf夨mupdfȤ
#   -xץ󤬤xpdfȤ(-e, -m, -xΤꤷʤ
#   xpdf, evince, mupdfνõƺǽ˸ĤäΤȤ)

# 󥹥ȡˡ:
#   ưɬפʤΡפ򥤥󥹥ȡ
#   ʤPATH̤äǥ쥯ȥ֤

# ưɬפʤ:
#   TeX켰 xpdf pdfinfo dvipdfmx perl mktemp
#   -pץȤdvipsps2pdf
#   -eץȤevince -xץȤmupdf

# ȯηа:
#   MacOSXMacPortsǤpTeXtexliveϾͤΤξʤ
#   (ԤpxdviܸбԤxdvikϺΤȤܸб)
#   ΥѥåȤξͤΤᡢԤ󥹥ȡǤʤ
#   ˤʤä xdvikܸбǤ뤳Ȥ˺äƺäΤ

# ޤή:
#   ǥ쥯ȥꡢDVIեPDFƤ
#   xpdfǤ(äxpdfɬ)
#   DVIեƻ뤷줿PDF⹹xpdfեå

usage(){
	cat <<-EOF >&2
		Usage: `basename "$0"` [-rpemx] DVIfile
		  -r: force refresh even if recompile may be required
		  -p: use ps2pdf instead of dvipdfmx
		  -e: use evince instead of xpdf
		  -m: use mupdf instead of xpdf
		  -x: use xpdf
	EOF
	exit 1
}

# ץ
FORCE_REFRESH=0; USE_PS2PDF=0
OPTERR=0
VIEWER_KIND=	# empty, or one of xpdf, evince, mupdf
while getopts rpemx OPTC; do
	case "$OPTC" in
	r)	FORCE_REFRESH=1;;
	p)	USE_PS2PDF=1;;
	x)	VIEWER_KIND=xpdf;;
	e)	VIEWER_KIND=evince;;
	m)	VIEWER_KIND=mupdf;;
	*)	usage;;
	esac
done
shift `expr $OPTIND - 1`
# ץΤȤDVIե1Ĥ
if [ $# -ne 1 ]; then
	usage
fi

case "$VIEWER_KIND" in '')
	for i in xpdf evince mupdf; do
		[ -x "`which "$i" 2>/dev/null`" ] || continue
		VIEWER_KIND="$i"; break
	done
	case "$VIEWER_KIND" in '')
		echo 'None of xpdf, evince, or mupdf is found' >&2; exit 1;;
	esac
	;;
esac

DVI="$1"
# ѿͤ.dviפǽäƤʤղ
case "$DVI" in
*.dvi)
	true;;
*)
	DVI="$DVI.dvi";;
esac
# ꤵ줿DVIե뤬ʤä齪λ
if [ ! -e "$DVI" ]; then
	echo "$DVI" does not exist >&2; exit 1
fi
# ץȸʤʤȤݾڤ褦
case "$DVI" in
/*)	:;;
*)	DVI="./$DVI";;
esac

# ǥ쥯ȥλϾõ褦ˤƤ
XPDFPID= # xpdfεưϤPID򤳤ѿ
trap '' HUP INT QUIT PIPE TERM
TMPD=`mktemp -d "${PDVITMP-/tmp}"/.pdviXXXXXXXX` || exit 1
trap '
	STATUS=$?
	trap 0
	rm -rf "$TMPD" &
	# XPDFPIDʤ顢ưxpdfλ
	if [ x"$XPDFPID" != x ]; then
		case "$VIEWER_KIND" in
		mupdf|evince)
			kill $XPDFPID;;
			# pdfcloseϻȤʤDebian stretchǤμ¸
			# ȤΤˤʤʤȽǤ줿
		*)	xpdf -remote "$PDF" -quit &:;;
		esac
	fi
	exit $STATUS
' EXIT HUP INT QUIT PIPE TERM

# DVIեιƻ뤹뤿ᡢॹפե$STAMPݻ
STAMP="$TMPD"/stamp
touch -r "$DVI" "$STAMP"

# ǥ쥯ȥ˺PDFΥե̾
PDFBASE="`basename "$DVI" .dvi`"
PDF="$TMPD/$PDFBASE.pdf"
NEWPDF="$TMPD/_$PDFBASE.pdf"	# PDF򹹿ο
WHITEPDF="$TMPD/,$PDFBASE.pdf"	# PDFݻΥե̾
ERR="$TMPD"/err	# dvipdfmxΥ顼åݻ

# dvipdfmx򵯤ؿ dvipdfmxԤɤ֤ͤɬפ
case $USE_PS2PDF in
1) # dvipsΥ顼ªʤФʤʤΤǡdvipdfϻȤʤ
	dvi_to_pdf(){
		{
			dvips -f "$1" 2>"$ERR," && rm "$ERR,"
		} | ps2pdf - "$2" 2>"$ERR" && [ ! -e "$ERR," ] || {
			cat "$ERR," "$ERR" >&2 2>/dev/null; return 1
		}
	};;
*)
	dvi_to_pdf(){
		dvipdfmx -o "$2" "$1" 2>"$ERR" || {
			cat "$ERR" >&2; return 1
		}	
	};;
esac

# ꤷPDFƱڡƱڡPDF
# äȤˡ뤫
whitepdf(){ # Usage: whitepdf origPDF newPDF
	pdfinfo -- "$1" | perl -ne '
		s/(.*?)\s*:(.*)/join(" ", split(" ", "$1: $2"))/e; # Ĵ

		($w, $h) = ($1, $2), next if /^Page size: (?#
			)([-+e\d.]+) ?x ?([-+e\d.]+) ?pts\b/i;
		$p = $1 if /^Pages?: (\d+)$/i;
		$r = ($1 == 90 || $1 == 270) if /^Page rot: (\d+)$/i;
		END{
			exit(1) if !defined($w) || !defined($p);
			($w, $h) = ($h, $w) if $r;

			print "$w $h $p\n";
		}
	' | {
		read w h p
		echo '%!PS
			1 1 '$p' {showpage} for
		' |ps2pdf -dDEVICEWIDTHPOINTS=$w -dDEVICEHEIGHTPOINTS=$h - "$2"
	}
}

# PDFեǥ쥯ȥ˺
dvi_to_pdf "$DVI" "$PDF" || exit 1

# xpdfХå饦ɤǵưPIDϿƤ
case "$VIEWER_KIND" in
mupdf)	mupdf -- "$PDF" &:;;
evince)	evince -- "$PDF" &:;;
*)	xpdf -remote "$PDF" -- "$PDF" 2>/dev/null &:;;
esac
XPDFPID=$!

while true; do
	# PDFХå饦ɤǺ
	whitepdf "$PDF" "$WHITEPDF" & WPDFPID=$!

	# DVIեѹ̵ͭƻ
	PERLSCR='
		sub dvi_ok{ # DVIե"\337"x4(i.e.񤭹߽λ)
			open(IN, "<", $_[0]) || return 0;
			binmode(IN); # ǰΤ
			seek(IN, -4, 2);
			my($a);
			read(IN, $a, 4);
			close(IN);
			return $a eq "\337" x 4;
		}
		sub strhead{ # 1Ƭ2Ȱפ
			return substr($_[0], 0, length($_[1])) eq $_[1];
		}
		sub log_reqs_rerun{ # ե˽Υå뤫
			open(IN, "<", $_[0]) || return 0;
			seek(IN, -1000, 2); # 500ä٤
			my($f);
			<IN>;
			while(<IN>){
				$f = 1, last if strhead($_,
				  "LaTeX Warning: Label(s) may have changed.");
				last if strhead($_,
					"Here is how much of TeX'\''s memory");
			}
			close(IN);
			return $f;
		}

		# ޥɥ饤󤫤顢եå嶯̵ͭ(-rץ)
		# xpdfPIDॹץե̾DVIե
		# ̾ե̾
		($force, $xpdfpid, $stamp, $dvi) = @ARGV;
		($log = $dvi) =~ s/\.dvi$/\.log/;

		# ॹץեι
		$stamp_mtime = (stat($stamp))[9];
		# ॹץե뤬äƤ1֤
		exit(1) if !defined($stamp_mtime);

		for(;;){
			# xpdfλƤ0֤
			exit(0) if kill(0, $xpdfpid) == 0;

			# DVIեι
			my($dvi_mtime) = (stat($dvi))[9];
			# DVIե뤬äƤ1֤
			exit(1) if !defined($dvi_mtime);

			# ॹץեŤʤäƤ
			# (i.e. DVIե뤬Ƥ)
			# DVIեؤν񤭹ߤλƤС
			# DVIեΥॹפ򥿥ॹץե
			# ݻξǡRerun׵ᤵƤʤ
			# -1(ºݤˤ255)֤ƽλ
			if($stamp_mtime < $dvi_mtime && dvi_ok($dvi)){
				utime($dvi_mtime, $dvi_mtime, $stamp);
				exit(-1) if $force || !log_reqs_rerun($log);
				$stamp_mtime = $dvi_mtime;
			}

			# 0.2Ԥ
			select(undef, undef, undef, 0.2);
		}
		# 1sleepʤɤΥץʤƺѤ褦
		# ʬperlǽ
	' perl -e '
		eval($ENV{"PERLSCR"});
		warn($@), exit(1) if $@;
	' $FORCE_REFRESH $XPDFPID "$STAMP" "$DVI"
	# ľܡperl -e 'ץȡ'פȽ񤫤ʤΤϡpsνϤ
	# ߤäȤʤʤΤɤᡣwarnʬSyntax errorθ
	case $? in # 嵭perlͤ򸫤
	0)	# xpdfλƤ롣XPDFPID򥯥ꥢƽλ
		XPDFPID=; exit 0;;	
	1)	# ॹץե뤫DVIե뤬ä줿1ǽλ
		exit 1;;
	esac
	# DVIե뤬졢Υॹפ$STAMPݻ줿

	# öPDFǺؤ
	wait $WPDFPID 2>/dev/null
	mv "$WHITEPDF" "$PDF"
	case "$VIEWER_KIND" in
	mupdf)	kill -HUP $XPDFPID;;
	evince)	:;;
	*)	xpdf -remote "$PDF" -reload;;
	esac
	# DVIPDFԤ1ǽλ
	dvi_to_pdf "$DVI" "$NEWPDF" || {
		# âDVI֤δ֤˹˹Ƥ³
		[ "$DVI" -nt "$STAMP" ] && continue
		exit 1
	}
	mv "$NEWPDF" "$PDF"
	# xpdfɤɽ򹹿
	# evinceä˲⤷ʤƤPDFѤмưŪɽ򹹿
	case "$VIEWER_KIND" in
	mupdf)	kill -HUP $XPDFPID;;
	evince)	:;;
	*)	xpdf -remote "$PDF" -reload;;
	esac
done
