#!/usr/bin/perl

###
### (C) 2005 by Marco Danelutto (marcod@di.unipi.it)
###
### This program is free software; you can redistribute it and/or          
### modify it under the terms of the GNU Library General Public License    
### as published by the Free Software Foundation; either version 2         
### of the License, or (at your option) any later version.                 
###                                                                        
### This program is distributed in the hope that it will be useful,        
### but WITHOUT ANY WARRANTY; without even the implied warranty of         
### MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the          
### GNU Library General Public License for more details.                   

###
### mktex: re-compiles a latex file as soon as it has been modified
### refreshes the xdvi display to show the new contents
### also run bibtex, if there are unresolved cite commands
### 

# number of seconds to sleep before iterating
$SECITER = 1;
# default command to recompile
$TEXCMD = "latex";
# default command to bibtex
$bibcmd = "bibtex";
# default editor (if needed) 
$EDITORCMD = "emacs";

###########################################################################
############### DO NOT MODIFY BELOW THIS LINE #############################
###########################################################################

# the arguments are the files that have to be observed 
%filetimes = ();

# install signal handler to control finalization 
$SIG{"INT"} = \&close_up;

use Getopt::Std;
getopts("s:m:c:hpf");

# Usage ... 
if($opt_h) {
    print "Usage is:\nmktex -m mainFileName [-c texcommand] [-s seconds] [-p] [-f]\n";
    print "\t-c cmd\trun cmd instead of latex (e.g. -c pdflatef) \n";
    print "\t-s sec\twait sec seconds before starting a new loop iteration\n";
    print "\t-p\tgenerate postscript when program terminates (via ^C)\n";
    print "\t-f\tgenerate PDF when program terminates (via ^C)\n";
    exit;
}

# the name of the latex command 
if($opt_c) {
    $texcmd = $opt_c;
} else {
    $texcmd = $TEXCMD;
}

# how much seconds before iterating again ...
if($opt_s) {
    $secondi = $opt_s;
} else {
    $secondi = $SECITER;
}

# check for main file param 
unless($opt_m) {
    die "Main file should be supplied (with or without extension)\n";
}

# mainfile without extension
if($opt_m =~ /(.*)\.tex/) {
    $mainfile = $1;
} else {
    $mainfile = $opt_m;
}

# mainfile to compile
$mainfiletex = $mainfile.".tex";

# look for include files ... 
@files = ($mainfiletex);
open FILETEX, "cat $mainfile.tex|" 
    or die "Cannot open $mainfile.tex\n";
while(<FILETEX>) {
    ### process tex files included with an \input directive
    if($_ =~ /([^%]*)\\input{([^}]*)(.*)/) {
	$inpfile = $2.".tex";
	print "---> Includes $inpfile\n";
	@files = (@files,$inpfile);
    }
    ### process ps or pdf file included with an \includegraphics directive
    if($_ =~ /([^%]*)\\includegraphics{([^{]*){(.*)}(.*)/) {
	$inpfile = $2;
	print "---> Includegraphics $inpfile\n";
	@files = (@files,$inpfile);
    }
}
close FILETEX;

# get initial times ... 
foreach $f (@files) {
    print "---> Observing file: $f\t";
    ### gather initial stats ...
    $wrtime = (stat($f))[9];
    print "last modified at $wrtime\n";
    $filetimes{$f} = $wrtime;
}

# get the pid of the xdvi process (must be already there ...) 
$pidxdvi = getxdvipid($mainfile);
print "---> Updating xdvi view pid = $pidxdvi\n";

# main cycle to watch for modifications
while(true) {
    $recomp = 0;
    foreach $f (@files) {
	$t = (stat($f))[9];
	if($t != $filetimes{$f}) {
	    print "---> Recompile (due to $f)\n";
	    $filetimes{$f
} = $t;  # adjust the modified file time
	    $recomp = 1;
	}
    }
    if($recomp == 1) {
	print "---> Recompiling $mainfiletex \n";
	recompile($mainfiletex);
	citations($mainfile);
	generate_warning($mainfile);
	$recomp = 0;
    } else {
	### print "---> No need to recompile\n";
    }
    sleep $secondi;
}
exit;

#### sub to analyze log for unresolved citations
sub citations {
    print "---> Looking for unresolved citations ...\n";
    $file = $_[0];
    open FD, "cat $file.log|" or die "cannot analyze $file.log\n";
    $bibbe = 0;
    while(<FD>) {
	if(/LaTeX Warning: Citation (.*) on page (.*)/) {
	    print "---> Citation $1 undefined -> running bibtex\n";
	    $bibbe = 1;
	}
    }
    close(FD);
    if($bibbe==1) {
	bibtex($file);
	### after bitexing file, compile it another 2 times to get the labels fixed
	recompile($file);
	recompile($file);
    }
    print "---> (unresolved citations) Done!\n";
}

#### subroutine to refresh xdvi preview
sub refresh {
    my $pidx = $_[0];
    print "---> KILL(USR1,$pidx)\n";
    kill(USR1,$pidx);
    return;
}

##### subroutine to recompile
sub recompile {
    print "---> Recompiling ... \n";
    my $file = $_[0]; ## file to recompile
    if(($pid=fork) == 0) {
	exec "$texcmd $file";
	print "---> Error while compiling $file with $texcmd";
	## continue in case of errors
    } else {
	waitpid($pid,0);
	## when finisehd, update screen view sending a SIGUSR1 to the xdvi process
	print "---> sending SIGUSR1 to $pidxdvi\n";
	### kill(USR1,$pidxdvi);
	refresh($pidxdvi);
    }
    print "---> (recompiling) Done!\n";
    return;
}

#### subroutine to bibtex 
sub bibtex {
    print "---> Running bibtex ... \n";
    my $file = $_[0];
    if(($pid=fork) == 0) {
	exec "$bibcmd $file";
	print "---> Error while compiling $file with $bibcmd\n";
	## continue in case of errors
    } else {
	waitpid($pid,0);
    }
    print "---> (bibtex) Done!\n";
}

### subroutine to fetch xdvi pid
sub getxdvipid {
    print "---> Getting xdvi pid ... \n";
    my $pid = 0;
    my $file = $_[0];
    my $xdvi = "xdvi";
    open SYS, "uname|" 
	or die "cannot uname\n";
    $line = <SYS>;
    chop $line;
    print "---> executing on $line\n";
    close SYS;
    if($line eq "Darwin") {
	print "---> changing xdvi exec name to xdvi.bin (Darwin)\n";
	$xdvi = "xdvi.bin";
    }
    open FD,"ps x|" 
	or die "Cannot ps x\n";
    while(<FD>) {
	### print "---> PID: $_";
	if(/(\s*)([0-9]*)(.*)$xdvi(.*)$file(.*)/) {
	    print "---> PID matched: $_\n";
	    $pid = $2;
	    #last;
	}
    }
    close(FD);
    if($pid == 0) {
	if(($pid = fork) == 0) {
	    exec "xdvi $file";
	} else {  
            # must recompute the $pid in case it is a child process of exec'ed process
	    # actually, must wait that the exec succeeded, to view its pid ... therefore
	    sleep 1;
	    # now go on ... 
	    open FD,"ps x|" 
		or die "Cannot ps x\n";
	    while(<FD>) {
		### print "---> PID: $_";
		if(/(\s*)([0-9]*)(.*)$xdvi(.*)$file(.*)/) {
		    print "---> PID matched: $_\n";
		    $pid = $2;
		    last;
		}
	    }
	    close(FD);
	    
	}
    }
    print "---> (getting xdvi pid) Done!\n";
    return $pid;
}

### subroutine to handle program termination, depending on the flags passed, 
### generates pdf or ps out of the dvi and shows the warning in the .log file

sub close_up {
    print "---> TERMINATION ... \n";
    if($opt_f) {
	generate_pdf($mainfile);
    }
    if($opt_p) {
	generate_ps($mainfile);
    }
    generate_warning($mainfile);
    exit(0);
}

### generate pdf out of dvi
sub generate_pdf {
    print "---> GENERATING PDF ... \n";
    my $file = $_[0];
    system("dvipdf $file");
    return;
}

### generate ps out of dvi
sub generate_ps {
    print "---> GENERATING PS ... \n";
    my $file = $_[0];
    system("dvips $file.dvi -o");
    return;
}

### displays warnings in the log file
sub generate_warnings {
    print "---> GENERATING WARNINGS ... \n";
    print "=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=\n";    
    my $file = $_[0];
    system("cat $file.log | grep arni");
    print "=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=\n";
    return;
}

sub generate_warning {
    print "---> GENERATING WARNINGS ... \n";

    my $file = $_[0];
    open FW, "cat $file.log|";
    my $first_border = 1;
    my $last_border = 0;
    while(<FW>) {
	if(/(.*)arning(.*)/) {
	    my $line = $_;
	    if($first_border == 1) {
		print "=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=\n";
	    }
	    print "=*=  $line";
	    $first_border = 0;
	    $last_border = 1;
	}
    }
    if($last_border == 1) {
	print "=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=*=\n";
    }
    return;
}

### fine lavori 
