#!/usr/bin/env perl ### ### (C) 2005 by Marco Danelutto (marcod@di.unipi.it) ### (C) 2007 by Francesco Nidito (nids@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; $SIG{"HUP"} = \&rescan_files; use Getopt::Std; getopts("s:m:c:hpf"); # Usage ... if($opt_h) { printusage(); 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) { # punish the user... print "Main file should be supplied (with or without extension)\n\n"; # ...but teach him what to do! printusage(); exit 1; } # mainfile without extension if($opt_m =~ /(.*)\.tex/) { $mainfile = $1; } else { $mainfile = $opt_m; } # mainfile to compile $mainfiletex = $mainfile.".tex"; # look for include files ... @files = (); createfileslist($mainfile); # 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; #### prints the usage sub printusage { 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"; } #### throws away the list and create it back sub createfileslist { # if it's not the first time let's recompile (maybe some file has changed) if( @files > 0 ) { print "---> Recompiling $mainfiletex \n"; recompile($mainfiletex); citations($mainfile); generate_warning($mainfile); } @files = (); #cleans up the files list push( @files, $mainfiletex ); parsefile($_[0]); # 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; } } #### recursively enters the refered files and provides a list of them sub parsefile { my $file = shift; my $FILETEX; open $FILETEX, "cat $file.tex|" or die "Cannot open $file.tex\n"; local $_; while(<$FILETEX>) { ### process tex files included with an \input directive if($_ !~ m/^\s*%/) { if($_ =~ m/\\(input|include){([^}]+)}/) { $inpfile = $2; $inpfiletex = $2.".tex"; # we do not want to observe files twice! my $seen = grep {/$inpfiletex/} @files; if( 0 == $seen ){ print "---> Includes $inpfiletex\n"; @files = (@files,$inpfiletex); parsefile($inpfile) } } ### process ps or pdf file included with an \includegraphics directive elsif($_ =~ m/\\includegraphics([^{}]*){([^}]+)}/) { $inpfile = $2; # we do not want to observe files twice! my $seen = grep {/$inpfile/} @files; if( 0 == $seen ){ print "---> Includegraphics $inpfile\n"; @files = (@files,$inpfile); } } } } close $FILETEX; }; #### 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); } ### handles the rescan of the files sub rescan_files { createfileslist($mainfile); } ### 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