#!/usr/bin/perl -w use strict; use Getopt::Long; use File::Basename; my($pdfbasepath) = $ENV{'HOME'} . "/public_html/PDFDOCS/"; my($datafilename); my($verbose) = 0; my($fromdate, $uptodate) = (0, 999999); my($mincool)=0; my($maxmonth); (undef,undef,undef,undef,undef,$maxmonth,undef,undef,undef) = localtime(time); $maxmonth += 1901; # now it's the next year $maxmonth *= 100; # now it has two zeros $maxmonth += 12; # now it's yyyymm GetOptions('datafilename=s' => \$datafilename, 'verbose+' => \$verbose, 'mincool=i' => \$mincool, 'from=i' => \$fromdate, 'upto=i' => \$uptodate); die "$0: Incorrect commandline args. Usage: $0 --data filename [--verbose] [--mincool number] [--from yyyymm] [--upto yyyymm] By default, all entries are taken. If you specify an integer mincool, then entries with inferior coolness are skipped. If you specify either a starting or an ending date (or both), then entries that don't fit are screened out. Say '--verbose' (or -v) multiple times for more verbosity." if (!defined $datafilename); # datafilename could be ans-res.hrb or ../ans-res.hrb or ~ajayshah/PERSONAL/RESUME/ans-res.hrb my($basename) = &basename($datafilename, ".hrb"); print "Parsed commandline -- datafile $datafilename -> basename $basename verbosity $verbose mincool $mincool from $fromdate upto $uptodate\n\n" if ($verbose>2); my($name, $url, $email, $bibfile, $bst, @order); my(%subjects, %types); my(@entries); my(%usedpdf); for (`ls $pdfbasepath`) { chop $_; $usedpdf{$_}=0; } # Let me explain the data structures. # # The hashes 'subjects' and 'types' are just shortname to fullname mappings. # E.g. you might have $subjects{'hpc'} = 'High performance computing'. # and you might have $types{'pub'} = 'Publications'. # # The ORDER in which the sections appear in the generated resume is # supplied as part of basics - it is in @order. # # The @entries table stores the actual information. It is indexed by # the entry number, so the entries in the .hrb file come into this as # elem 0, 1, 2, ... # The columns in this file are hash table entries, where the following # information is stored: # date, from, upto, cool, content, type, pdf, key, subject $/="\n\n"; my($i); # just a scratch counter my($n)=0; open(F, $datafilename) or die "$0: Data file $datafilename not found.\n"; print "Opened $datafilename...\n" if ($verbose > 0); while () { s/\#.*?\n//gm; # deal with comments (ungreedy) s/\#.*$//m; # comment that ends the file :-) s/\n/ /gm; # remove carriage returns s/\s+/ /g; # remove surplus whitespace next if /^\s*$/; # Do we have nothing after all? my(@f) = split /;/; # If !nothing, it has to be ';' delimited die "$0: Error in $datafilename -- malformed paragraph at " . join(" ", @f) . "\n" if ($#f == 0); my($taken)=0; if ($f[0] eq "basics") { print "Processing basics...\n" if ($verbose > 2); for ($i=1; $i<$#f; $i++) { # These are strings of the form 'key = value' my(@words) = split(/=/, $f[$i], 2); $words[0] =~ s/^\s+//; $words[0] =~ s/\s+$//; $words[1] =~ s/^\s+//; $words[1] =~ s/\s+$//; die "$0: Error in $datafilename -- Illegal entry : $f[$i]\n" if ((length($words[0]) eq 0) || (length($words[1]) eq 0)); my($entrytaken)=0; if ($words[0] eq 'name') { $name = $words[1]; $entrytaken=1; } if ($words[0] eq 'url') { $url = $words[1]; $entrytaken=1; } if ($words[0] eq 'email') { $email = $words[1]; $entrytaken=1; } if ($words[0] eq 'bibfile') { $bibfile = $words[1]; $entrytaken=1; } if ($words[0] eq 'bst') { $bst = $words[1]; $entrytaken=1; } if ($words[0] eq 'order') { @order = split /,/, $words[1]; my($j); for ($j=0; $j<=$#order; $j++) { $order[$j] =~ s/^\s+//; $order[$j] =~ s/\s$//; } $entrytaken=1; } die "$0: Dunno what you mean by $f[$i]\n" if (!$entrytaken); } $taken=1; } if ($f[0] eq "mappings") { print "Processing mappings...\n" if ($verbose > 2); my($i); for ($i=1; $i<$#f; $i++) { # These are strings of the form 'subject risk -> Risk management' # 0 1 2 3 my(@words) = split(' ', $f[$i], 4); die "$0: Error in $datafilename -- Illegal mappings string : $f[$i]\n" if ((($words[0] ne 'type') && ($words[0] ne 'subject')) || ($words[2] ne '->') || (length($words[3]) eq 0)); if ($words[0] eq 'subject') { $subjects{$words[1]} = $words[3]; } if ($words[0] eq 'type') { $types{$words[1]} = $words[3]; } } $taken=1; } if ($f[0] =~ /date/) { print "Processing data...\n" if ($verbose > 2); my($i); for ($i=0; $i<$#f; $i++) { # These are strings of the form 'key = value' # 0 1 my(@words) = split(/=/, $f[$i], 2); $words[0] =~ s/^\s+//; $words[0] =~ s/\s+$//; $words[1] =~ s/^\s+//; $words[1] =~ s/\s+$//; die "$0: Error in $datafilename -- Illegal entry : $f[$i]\n" if ((length($words[0]) eq 0) || (length($words[1]) eq 0)); my($entrytaken)=0; if ($words[0] eq 'date') { my($dateditem, $daterangeitem, $date, $from, $upto); &processdate($words[1], \$dateditem, \$daterangeitem, \$date, \$from, \$upto); die "$0: You can't be both dated and daterange (at $f[$i])\n" if ($dateditem && $daterangeitem); if ($dateditem) { $entries[$n]{'date'} = $date; } else { $entries[$n]{'from'} = $from; $entries[$n]{'upto'} = $upto; } $entrytaken=1; } if ($words[0] eq 'cool') { $entries[$n]{'cool'} = $words[1]; $entrytaken=1; } if ($words[0] eq 'content') { $entries[$n]{'content'} = $words[1]; $entrytaken=1; } if ($words[0] eq 'type') { die "$0: Type $words[1] was not defined earlier.\n" if (!defined $types{$words[1]}); $entries[$n]{'type'} = $words[1]; $entrytaken=1; } if ($words[0] eq 'pdf') { $entries[$n]{'pdf'} = $words[1]; $entrytaken=1; die "$0: File " . " " . $entries[$n]{'pdf'} . " is required but is missing.\n" if (! -f $pdfbasepath . '/' . $entries[$n]{'pdf'}); $usedpdf{$words[1]} = 1; } if ($words[0] eq 'key') { $entries[$n]{'key'} = $words[1]; $entrytaken=1; } if ($words[0] eq 'subject') { die "$0: Subject $words[1] was not defined earlier.\n" if (!defined $subjects{$words[1]}); $entries[$n]{'subject'} = $words[1]; $entrytaken=1; } die "$0: I did not know what to do with $f[$i]\n" if (!$entrytaken); } $taken=1; $entries[$n]{'taken'}=0; $n++; } die "$0: I just don't know what to do with " . join(" ", @f) . "\n" if (!$taken); } print "Finished reading $datafilename.\n" if ($verbose > 0); close(F); # Warn him about PDF files not used for (keys %usedpdf) { print "Your pdf file $_ exists but is not used.\n" if ($usedpdf{$_} == 0); } # Validation rules for basics. die "$0: You did not tell me the bst file.\n" if (!defined $bst); die "$0: You did not tell me the bib file.\n" if (!defined $bibfile); die "$0: You did not tell me your name.\n" if (!defined $name); die "$0: You did not tell me your URL.\n" if (!defined $url); die "$0: You did not tell me your email address.\n" if (!defined $email); if ($verbose >= 2) { print "The basics:\n"; print " name = $name url = $url email = $email bibfile = $bibfile bst = $bst order = ", join(",", @order), "\n\n"; # Throw in a sanity check here. for (@order) { die "$0: Don't know what you mean by $_ (in 'order').\n" if ((!defined $subjects{$_}) && (!defined $types{$_})); } print "The subjects mapping table is:\n"; for (keys %subjects) { print " $_ -> $subjects{$_}\n"; } print "\nThe types mapping table is:\n"; for (keys %types) { print " $_ -> $types{$_}\n"; } print "\nThe table of $n entries is:\n"; my($i); for ($i=0; $i<$n; $i++) { print "$i. "; if (defined $entries[$i]{'date'}) { print 'date: ', $entries[$i]{'date'}, "; "; } if (defined $entries[$i]{'from'}) { print 'from: ', $entries[$i]{'from'}, "; "; } if (defined $entries[$i]{'upto'}) { print 'upto: ', $entries[$i]{'upto'}, "; "; } if (defined $entries[$i]{'cool'}) { print 'cool: ', $entries[$i]{'cool'}, "; "; } if (defined $entries[$i]{'content'}) { print 'content: ', $entries[$i]{'content'}, "; "; } if (defined $entries[$i]{'type'}) { print 'type: ', $entries[$i]{'type'}, "; "; } if (defined $entries[$i]{'pdf'}) { print 'pdf: ', $entries[$i]{'pdf'}, "; "; } if (defined $entries[$i]{'key'}) { print 'key: ', $entries[$i]{'key'}, "; "; } if (defined $entries[$i]{'subject'}) { print 'subject: ', $entries[$i]{'subject'}, "; "; } print "\n"; } } # Now we get on with resume generation... print "Starting to write ./$basename.tex...\n" if ($verbose>0); &cleantex(); open(F, ">$basename.tex") or die "$0: Could not write ./$basename.tex\n"; print F <<'EOF'; \documentclass[11pt,a4paper]{article} \usepackage{parskip} \usepackage{bibentry} \input resume-config % ------------------------------------------------------------------ % Put username on front page nicely % Arg 1 - your name (e.g. 'Ajay Shah') % Arg 2 - homepage URL (e.g. 'http://www.igidr.ac.in/\string~ajayshah') % just saying blah/~ajayshah won't work! % Arg 3 - your email address \newcommand{\username}[3]{ \hfill\begin{tabular}{r} \textbf{\LARGE \myhref{#2}{#1}} \\ \hline \texttt{#3} \\ \texttt{#2} \\ \end{tabular} } % ------------------------------------------------------------------ % ------------------------------------------------------------------ % Setup new style for section/subsection titles % Stolen from `The Latex Companion' \makeatletter \renewcommand\section{\@startsection {section}{1}{\z@}% {-3.5ex \@plus -1ex \@minus -.2ex}% {2.3ex \@plus.2ex}% {\normalfont\large\bfseries\sffamily}} \renewcommand\subsection{\@startsection{subsection}{2}{\z@}% {-3.25ex\@plus -1ex \@minus -.2ex}% {1.5ex \@plus .2ex}% {\normalfont\large\sffamily}} \renewcommand\subsubsection{\@startsection{subsubsection}{3}{\z@}% {-3.25ex\@plus -1ex \@minus -.2ex}% {1.5ex \@plus .2ex}% {\normalfont\sffamily}} \makeatother % ------------------------------------------------------------------ \begin{document} \sloppy EOF printf F "\\bibliographystyle\{%s\} \\nobibliography\{%s\} \\username\{%s\} \{%s\} \{%s\}\n", $bst, $bibfile, $name, $url, $email; my($key); for $key (@order) { my($utilised)=0; # Create the resume content for $key... # Is it a type or is it a subject? if (defined $types{$key}) { # First check - do we have any content, or is this section blank? my($count)=0; for ($i=0; $i<$n; $i++) { $count++ if ((defined $entries[$i]{'type'}) && ($entries[$i]{'type'} eq $key) && ($entries[$i]{'taken'} == 0)); } if ($count > 0) { printf F "\n\n\n\\section*{%s}\n", $types{$key}; for ($i=0; $i<$n; $i++) { if ((defined $entries[$i]{'type'}) && ($entries[$i]{'type'} eq $key) && ($entries[$i]{'taken'} == 0)) { &emit_tex(\*F, \@entries, $i, $fromdate, $uptodate, $mincool); $entries[$i]{'taken'}=1; } } } else { printf F "\n\n\n%% Skipped %s since there was nothing\n", $key; } $utilised=1; } if (defined $subjects{$key}) { # First check - do we have any content, or is this section blank? my($count)=0; for ($i=0; $i<$n; $i++) { $count++ if ((defined $entries[$i]{'subject'}) && ($entries[$i]{'subject'} eq $key) && ($entries[$i]{'taken'} == 0)); } if ($count > 0) { printf F "\n\n\n\\section*{%s}\n", $subjects{$key}; for ($i=0; $i<$n; $i++) { if ((defined $entries[$i]{'subject'}) && ($entries[$i]{'subject'} eq $key) && ($entries[$i]{'taken'} == 0)) { &emit_tex(\*F, \@entries, $i, $fromdate, $uptodate, $mincool); $entries[$i]{'taken'}=1; } } } else { printf F "\n\n\n%% Skipped %s since there was nothing\n", $key; } $utilised=1; } die "$0: You have \"$key\" in `order', which is neither a subject nor a type.\n" if (!$utilised); } print F <<'EOF'; \end{document} EOF print "Finished making ./$basename.tex\n" if ($verbose>0); close(F); sleep(5); # Time to put ink on paper... # PDF -- print "\n\n\n\nProducing pdf...\n"; open(F, ">resume-config.tex") or die "$0: Could not write ./resume-config.tex\n"; print F <<'EOF'; % Switch into Times Roman for use with pdflatex % \usepackage{times} % Zap out the cute macros used in resume-config-html \newcommand{\pdffile}[1]{~} \newcommand{\myhref}[2]{#2} EOF close(F); system("pdflatex $basename.tex && bibtex $basename && pdflatex $basename.tex && pdflatex $basename.tex"); &cleantex(); # Text -- print "\n\n\n\nProducing text...\n"; open(F, ">resume-config.tex") or die "$0: Could not write ./resume-config.tex\n"; print F <<'EOF'; % Use tex4ht if you want text \usepackage[html]{tex4ht} \newcommand{\myhref}[2]{#2} \newcommand{\pdffile}[1]{~} EOF close(F); system("latex $basename.tex && bibtex $basename && latex $basename.tex && latex $basename.tex && tex4ht $basename.tex && lynx -dump $basename.html > $basename.text"); &cleantex(); system("rm resume-config.tex"); # HTML -- print "\n\n\n\nProducing HTML...\n"; open(F, ">resume-config.tex") or die "$0: Could not write ./resume-config.tex\n"; print F <<'EOF'; % Use tex4ht if you want HTML \usepackage[html,2]{tex4ht} % myhref: % Usage: \myhref{URL}{string} % In the HTML file, it comes out as string % In the TeX file, it comes out as string. \newcommand{\myhref}[2]{\Link[#1]{}{}#2\EndLink} % pdffile: % Usage: \pdffile{a.pdf} % In the HTML file, it comes out as [pdf] % In the TeX file, it is zapped out. \newcommand{\pdffile}[1]{\Link[PDFDOCS/#1]{}{}[pdf]\EndLink} % FYI - \pdffile{r.pdf}{s} is the same as % \htmlembellish{PDFDOCS/r.pdf}{[pdf]} % or \pdffilemyanchor{r.pdf}{[pdf]} EOF close(F); system("latex $basename.tex && bibtex $basename && latex $basename.tex && latex $basename.tex && tex4ht $basename.tex"); &cleantex(); # Time to generate one file per subject my($subject); for $subject (keys %subjects) { open(F, ">$subject.tex") or die; print F <<'EOF'; \documentclass[11pt,a4paper]{article} \usepackage{parskip} \usepackage{bibentry} \input resume-config % ------------------------------------------------------------------ % Setup new style for section/subsection titles % Stolen from `The Latex Companion' \makeatletter \renewcommand\section{\@startsection {section}{1}{\z@}% {-3.5ex \@plus -1ex \@minus -.2ex}% {2.3ex \@plus.2ex}% {\normalfont\large\bfseries\sffamily}} \renewcommand\subsection{\@startsection{subsection}{2}{\z@}% {-3.25ex\@plus -1ex \@minus -.2ex}% {1.5ex \@plus .2ex}% {\normalfont\large\sffamily}} \renewcommand\subsubsection{\@startsection{subsubsection}{3}{\z@}% {-3.25ex\@plus -1ex \@minus -.2ex}% {1.5ex \@plus .2ex}% {\normalfont\sffamily}} \makeatother % ------------------------------------------------------------------ \begin{document} \sloppy EOF printf F "\\bibliographystyle\{%s\} \\nobibliography\{%s\}\n", $bst, $bibfile; printf F "\\section*{Work in %s}\n", $subjects{$subject}; for ($i=0; $i<$n; $i++) { if ((defined $entries[$i]{'subject'}) && ($entries[$i]{'subject'} eq $subject)) { &emit_tex(\*F, \@entries, $i, 0, 999999, $mincool); } } printf F "\n\\vspace*{1cm}\n\n"; printf F "\\HCode{
Return to %s's home page\n
\n
Generated using hrbuilder.
\n}\n", $url, $name; printf F "\\end{document}\n"; close(F); print "Finished making ./$subject.tex\n" if ($verbose>0); # HTML -- print "\nProducing HTML...\n"; open(F, ">resume-config.tex") or die "$0: Could not write ./resume-config.tex\n"; print F <<'EOF'; % Use tex4ht if you want HTML \usepackage[html]{tex4ht} % myhref: % Usage: \myhref{URL}{string} % In the HTML file, it comes out as string % In the TeX file, it comes out as string. \newcommand{\myhref}[2]{\Link[#1]{}{}#2\EndLink} % pdffile: % Usage: \pdffile{a.pdf} % In the HTML file, it comes out as [pdf] % In the TeX file, it is zapped out. \newcommand{\pdffile}[1]{\Link[PDFDOCS/#1]{}{}[pdf]\EndLink} % FYI - \pdffile{r.pdf}{s} is the same as % \htmlembellish{PDFDOCS/r.pdf}{[pdf]} % or \pdffilemyanchor{r.pdf}{[pdf]} EOF close(F); system("latex $subject.tex && bibtex $subject && latex $subject.tex && latex $subject.tex && tex4ht $subject.tex"); &cleantex(); } # END OF MAIN PROGRAM # Function cleantex sub cleantex { system("rm -f *.{aux,dvi,log,ps,bbl,blg,toc,out,ent,fff,ttt}"); } # Function processdate # Inputs # a string like yyyymm, yyyymm- or yyyymm-yyyymm # Outputs # if it's just a date, then dateditem=1 and date is set. # if it's a daterange then daterangeitem=1 and fromdate,uptodate are set. # if it's yyyymm- then uptodate is set as 999999. sub processdate { my($thestring, $pdateditem, $pdaterangeitem, $pdate, $pfrom, $pupto) = @_; if ($thestring =~ /\-/) { $$pdaterangeitem=1; $$pdateditem=0; my(@f) = split /\-/, $thestring; $$pfrom = $f[0]; if (defined $f[1]) { $$pupto = $f[1]; } else { $$pupto = 999999; } die "$0: Illegal month range in $thestring" if ($$pupto < $$pfrom); for ($$pupto, $$pfrom) { if ($_ != 999999) { die "$0: Error in date $_.\n" if (($_ < 0) || ($_ > $maxmonth)); } } } else { $$pdaterangeitem=0; $$pdateditem=1; $$pdate=$thestring; die "$0: Error in date $thestring.\n" if (($$pdate != 999999) && (($$pdate < 0) || ($$pdate > $maxmonth))); } } # Function emit_tex # Inputs # a file handle # the vector of entries # the entry number where we have to write .tex. # limiting from date # limiting upto date # amount of mincool required. # Outputs # the file handle is written into. # We variously utilise -- # date fields: date, from, upto # coolness: cool # classifiers: key, subject # The goods: {key, pdf} or {content} sub emit_tex { my($fh, $pentries, $i, $fromdate, $uptodate, $mincool) = @_; # Test for mincool return 0 if ((defined $pentries->[$i]{'cool'}) && ($pentries->[$i]{'cool'} < $mincool)); # Test if the date fits. return 0 if ((defined $pentries->[$i]{'date'}) && (($pentries->[$i]{'date'} < $fromdate) || ($pentries->[$i]{'date'} > $uptodate))); return 0 if ((defined $pentries->[$i]{'from'}) && ($pentries->[$i]{'from'} > $uptodate)); return 0 if ((defined $pentries->[$i]{'upto'}) && ($pentries->[$i]{'upto'} < $fromdate)); # Emit tex. print $fh "\n% Item $i\n"; if (defined $pentries->[$i]{'key'}) { # it's a pub. # In this case, we definitely do a \bibentry, but we'll also # take the contents if it's there. printf $fh "\\bibentry\{%s\} ", $pentries->[$i]{'key'}; if (defined $pentries->[$i]{'content'}) { printf $fh $pentries->[$i]{'content'}; } } else { # In this case, we bloody well need a content. if (!defined $pentries->[$i]{'content'}) { die "$0: Content not defined for elem $i\n"; } else { printf $fh $pentries->[$i]{'content'}; } } if (defined $pentries->[$i]{'pdf'}) { # he gave us a PDF file printf $fh " \\pdffile\{%s\}", $pentries->[$i]{'pdf'}; } printf $fh "\n"; }