]> matita.cs.unibo.it Git - helm.git/blob - helm/software/share/texmf/unicode/discovermacro.pl
8b595b48f3d52b1c28b26bdeb78face81931ae97
[helm.git] / helm / software / share / texmf / unicode / discovermacro.pl
1 #!/usr/bin/perl -T
2
3 # The content of the following variable is configuration data:
4 @DiscoverMacro::datafile_path = ('program','.');
5
6 =head1 NAME
7
8 discovermacro.pl - Discover which LaTeX package contains a missing macro
9
10 =head1 SYNOPSIS
11
12 B<discovermacro.pl> \I<themissingmacro>
13
14 B<discovermacro.pl> I<mydocument.log>
15
16 B<discovermacro.pl>
17
18 =head1 DESCRIPTION
19
20 The ucs package uses many macros from many packages which have to be 
21 included with B<\usepackage> before the inclusion of F<ucs.sty>.
22
23 The missing macro can be given directly as parameter. Note that the
24 backslash must be included, so with most shells you either need to
25 quote it or write a double backslash (e. g. \\cyrc or '\cyrc').
26
27 An alternative way is to give a LaTeX log file as parameter in which
28 case the missing macros are parsed from it. 
29
30 If no argument is given, the newest LaTeX log file in the current
31 directory is used as default.
32
33 =head1 ALTERNATIVES
34
35 B<discovermacro.pl> uses the data file F<ltxmacrs.txt> which is human
36 readable, thus can be used directly.
37
38 F<http://www.unruh.de/DniQ/cgi/discovermacro.cgi> provides an online
39 version of this script.
40
41 =cut
42
43 use strict;
44
45 package DiscoverMacro::H;
46
47 sub cleanline($) {
48     my $line = shift;
49     chomp $line;
50     $line =~ s/^\s*//;
51     $line =~ s/^\#.*//;
52     return $line;
53 }
54
55
56 sub readline2($) {
57     my $file = shift;
58     my $line = <$file>;
59     while ($line =~ /\\\n?$/s) {
60         $line =~ s/\\\n?$//;
61         my $next = <$file>;
62         $next =~ s/^\s*//;
63         $line .= $next;
64     }
65     return $line;
66 }
67
68 sub escape($$) {
69     my ($format,$text) = @_;
70     if ($format eq 'text/plain') {
71         $text =~ s/\[\[\[.*?\]\]\]//g;
72     } elsif ($format eq 'text/html') {
73         $text =~ s{[<>&]|(\n[ \t]*)}{ my $x=$&;
74             if ($x =~ /^\n/) {
75                 $x=~s/\t/        /g;
76                 $x=~s/ /&nbsp;/g;
77                 "<br>$x"; 
78             } else {
79                 sprintf "&#%d;", ord $& }}egs;
80         $text =~ s{\[\[\[.+?\]\]\]}{
81             my ($t) = ($& =~ /\[\[\[(.+?)\]\]\]/);
82             my @t = split / /,$t;
83             my $r = '';
84             if ($t[0] eq 'NAME') {
85                 $r = "<a name=\"$t[1]\">";
86             } elsif ($t[0] eq 'HREF') {
87                 $r = "<a href=\"$t[1]\">";
88             } elsif ($t[0] eq '/NAME') {
89                 $r = "</a>";
90             } elsif ($t[0] eq '/HREF') {
91                 $r = "</a>";
92             } else {
93                 warn "escape($format,[[[$t[0]...]]])";
94             }
95         }ge;
96         return $text;
97     } elsif ($format eq 'identifier') {
98         $text =~ s/[^a-z0-9]/
99             sprintf "_%02X", ord $&/ge;
100     } else {
101         warn "Unknown format $format";
102         return escape('text/plain',$text);
103     }
104     return $text;
105 }
106
107 sub header($$) {
108     my ($format,$header) = @_;
109     $header = escape($format,$header);
110     if ($format eq 'text/plain') {
111         my $len = length $header;
112         return "\n$header\n".('=' x $len)."\n";
113     } elsif ($format eq 'text/html') {
114         return "<p><table border=1>\n<tr><th align=left colspan=2>$header</th></tr>\n";
115     } else {
116         warn "Unknown format $format";
117         return header('text/plain',$header);
118     }
119 }
120
121 sub footer($) {
122     my ($format) = @_;
123     if ($format eq 'text/plain') {
124         return "\n";
125     } elsif ($format eq 'text/html') {
126         return "</table></p>\n\n";
127     } else {
128         warn "Unknown format $format";
129         return footer('text/plain');
130     }
131 }
132
133 sub line($$) {
134     my ($format,$line) = @_;
135     $line = escape $format, $line;
136     if ($format eq 'text/plain') {
137         return "$line\n";
138     } elsif ($format eq 'text/html') {
139         return "<tr><td colspan=2>$line</td></tr>\n";
140     } else {
141         warn "Unknown format $format";
142         return line($format,$line);
143     }
144 }
145
146
147 sub twocol($$$) {
148     my ($format,$col1,$col2) = @_;
149     $col1 = escape $format, $col1;
150     $col2 = escape $format, $col2;
151     if ($format eq 'text/plain') {
152         return "$col1\t$col2\n";
153     } elsif ($format eq 'text/html') {
154         return "<tr><td>$col1&nbsp;&nbsp;&nbsp;</td><td>$col2</td></tr>\n";
155     } else {
156         warn "Unknown format $format";
157         return twocol('text/plain',$col1,$col2);
158     }
159 }
160
161
162 package DiscoverMacro::Feature;
163
164 use vars qw/$obj_count/;
165 $obj_count = 0;
166
167 sub new($$$) {
168     my ($proto,$db,$name) = @_;
169     $obj_count++;
170     my $class = ref($proto) || $proto;
171     my $self = {};
172     bless $self, $class;
173     $self->{name} = $name;
174     $self->{db} = $db;
175     $self->loadfeature();
176     return $self;
177 }
178
179 sub DESTROY {
180     $obj_count--;
181 }
182
183 sub getfontenc($) {
184     my $self = shift;
185     return $self->{fontencoding};
186 }
187
188 sub getlatexcmd($) {
189     $_[0]->{latexcmd};
190 }
191
192 sub getneeds($) {
193     my $self = shift;
194     return $self->{needs};
195 }
196
197 sub getpackages($) {
198     my $self = shift;
199     return $self->{packages};
200 }
201
202 sub getpackages_ascode($) {
203     my $self = shift;
204     return join '', map {
205         my $p = $_;
206         $p = "{$_}" unless $p =~ /\}$/;
207         "\\usepackage$p\n";
208     } @{$self->{packages}};
209 }
210
211
212 sub loadfeature($) {
213     my $self = shift;
214     my $pos = $self->{db}->getfeaturepos($self->{name});
215     unless (defined $pos) {
216         $self->{undefined} = 1;
217         return;
218     }
219     my $file = $self->{db}->getfileat($pos);
220     my $line;
221     my $lastfile = undef;
222     while (defined ($line = DiscoverMacro::H::readline2($file))) {
223         my $tlastfile;
224         $line = DiscoverMacro::H::cleanline $line;
225         next if $line eq '';
226         my @line = split ' ',$line;
227         if ($line[0] eq 'FONTENCODING') {
228             warn "Two fontencodings in feature $self->{name}"
229                 if defined $self->{fontencoding};
230             $self->{fontencoding} = $line[1];
231         } elsif ($line[0] eq 'CTAN') {
232             if (!defined $lastfile) {
233                 die "CTAN not preceded by FILE in feature $self->{name}"; }
234             my $ctan = $self->{ctan};
235             warn "Two CTAN locations defined for file $lastfile ".
236                 "in feature $self->{name}" if defined $$ctan{$lastfile};
237             $$ctan{$lastfile} = $line[1];
238             $self->{ctan} = $ctan;
239         } elsif ($line[0] eq 'LATEXCMD') {
240             warn "Two LATEXCMDs in feature $self->{name}"
241                 if defined $self->{latexcmd};
242             $self->{latexcmd} = $line[1];
243         } elsif ($line[0] eq 'NEEDS') {
244             warn "Two NEEDS lines in feature $self->{name}"
245                 if defined $self->{needs};
246             $self->{needs} = join ' ',@line[1..$#line];
247         } elsif ($line[0] eq 'END') {
248             warn "FEATURE $self->{name} ended by END $line[1]"
249                 if $line[1] ne 'FEATURE';
250             last;
251         } elsif ($line[0] eq 'COMMENT') {
252             my $comment = readblock($file,'COMMENT',1);
253             if (defined $self->{comment}) {
254                 warn "Two COMMENT sections in feature $self->{name}.".
255                     "Concatenating";
256                 $comment = "$self->{comment}$comment"; }
257             $self->{comment} = $comment;
258         } elsif ($line[0] eq 'INSTALL') {
259             my $install = readblock($file,'INSTALL',1);
260             if (defined $self->{install}) {
261                 warn "Two INSTALL sections in feature $self->{name}.".
262                     "Concatenating";
263                 $install = "$self->{install}$install"; }
264             $self->{install} = $install;
265         } elsif ($line[0] eq 'FILE') {
266             push @{$self->{files}}, $line[1];
267             $tlastfile = $line[1];
268         } elsif ($line[0] eq 'PACKAGE') {
269             push @{$self->{packages}}, $line[1];
270         } else {
271             warn "Unknown command in feature $self->{name}: $line[0]";
272         }
273         $lastfile = $tlastfile; $tlastfile = undef;
274     }
275 }
276
277 sub readblock($$$) {
278     my ($file,$blocktype,$raw) = @_;
279     my $line;
280     my $str = '';
281     while (defined ($line = $raw?<$file>:DiscoverMacro::H::readline2($file))) {
282         return $str if ($line =~ /^\s*END\s+\Q$blocktype\E\s*$/);
283         $line = DiscoverMacro::H::cleanline $line unless $raw;
284         #print "L:$line\n";
285         $str .= $line;
286     }
287     warn "EOF in $blocktype";
288     return $str;
289 }
290
291 sub isdefined($) {
292     my $self = shift;
293     return !$self->{undefined};
294 }
295
296 sub as_text($$) {
297     my ($self,$f) = @_;
298     my $link = "[[[NAME ".
299         DiscoverMacro::H::escape('identifier',$self->{name})."]]]";
300     my $text = DiscoverMacro::H::header
301         ($f,"${link}Feature: $self->{name}\[[[/NAME]]]");
302     if ($self->{undefined}) {
303         $text .= DiscoverMacro::H::line($f,"No information available");
304     }
305     if (defined $self->{fontencoding}) {
306         $text .= DiscoverMacro::H::twocol($f,"Fontencoding:",
307                                           $self->{fontencoding});
308     }
309     for my $i (@{$self->{packages}}) {
310         $text .= DiscoverMacro::H::twocol($f,"Package:",$i);
311     }
312     if (defined $self->{needs}) {
313         $text .= DiscoverMacro::H::twocol($f,"Needs:",$self->{needs});
314     }
315     for my $i (@{$self->{files}}) {
316         my $ctan = ${$self->{ctan}}{$i};
317         if (defined $ctan) {
318             $ctan = " (CTAN: [[[HREF http://www.ctan.org/tex-archive/".
319                 "$ctan]]]$ctan\[[[/HREF]]])";
320         } else { $ctan = ''; };
321         $text .= DiscoverMacro::H::twocol($f,"Needed file:",$i.$ctan);
322     }
323     if ($self->{comment}) {
324         $text .= DiscoverMacro::H::twocol($f,"Comment:",$self->{comment});
325     }
326     if ($self->{install}) {
327         $text .= DiscoverMacro::H::twocol($f,"Installation:",$self->{install});
328     }
329     $text .= DiscoverMacro::H::footer $f;
330     return $text;
331 }
332
333 package DiscoverMacro::Macro;
334
335 use Data::Dumper;
336
337 use vars qw/$obj_count/;
338 $obj_count = 0;
339
340 sub new($$$) {
341     my ($proto,$db,$macro) = @_;
342     my $class = ref($proto) || $proto;
343     my $self = {};
344     $obj_count++;
345     bless $self, $class;
346     $self->{macro} = $macro;
347     $self->{db} = $db;
348     $self->loadlist();
349     return $self;
350 }
351
352 sub DESTROY {
353     $obj_count--;
354 }
355
356 sub isdefined($) {
357     my $self = shift;
358     return !$self->{undefined};
359 }
360
361 sub getname($) {
362     my $self = shift;
363     return $self->{macro};
364 }
365
366 sub getlist($) {
367     my $self = shift;
368     return $self->{list};
369 }
370
371 sub loadlist($) {
372     my $self = shift;
373     my $macros = $self->{db}->{macros};
374     my $macro = $self->{macro};
375     my $list = $$macros{$macro};
376     unless (defined $list) {
377         my $regex = $self->{db}->{regexmacros};
378         while (my ($re,$l) = each %$regex) {
379             #print "RE: $re\n";
380             if ($macro =~ /$re/) {
381                 $self->{regex} = $re;
382                 $list = $l; last; }
383         }
384     }
385     unless (defined $list) {
386         $self->{undefined} = 1;
387         return;
388     }
389     $self->{list} = $list;
390     my %tlist;
391     my @list = grep { my $bad = $_ eq 'OR' || $_ eq 'AND' || $tlist{$_};
392                   $tlist{$_}=1; !$bad; } split ' ', $self->{list};
393     my %features = ();
394     for my $i (@list) {
395         $features{$i} = new DiscoverMacro::Feature($self->{db},$i)
396             unless defined $features{$i};
397     }
398     $self->{features} = \%features;
399 }
400
401 sub getfeature($) {
402     my ($self,$name) = @_;
403     return ${$self->{features}}{$name};
404 }
405
406 sub as_text($$) {
407     my ($self,$f) = @_;
408     my $text = '';
409     my $link = "[[[NAME ".
410         DiscoverMacro::H::escape('identifier',$self->{macro})."]]]";
411     $text .= DiscoverMacro::H::header
412         ($f,"${link}Macro: $self->{macro}\[[[/NAME]]]");
413     if ($self->{undefined}) {
414         $text .= DiscoverMacro::H::line($f,"Macro is unknown.");
415     } else {
416         my $list = $self->{list};
417         my $features = $self->{features};
418         $list =~ s{\S+}{
419             my $w = $&;
420             if ($$features{$w}) {
421                 $w = "[[[HREF #".DiscoverMacro::H::escape('identifier',$w).
422                     "]]]$w\[[[/HREF]]]";
423             }
424             $w;
425         }ge;
426         $text .= DiscoverMacro::H::twocol($f,"Available with:",$list);
427     }
428     $text .= DiscoverMacro::H::footer($f);
429     for my $feature (values %{$self->{features}}) {
430         $text .= $feature->as_text($f);
431     }
432     return $text;
433 }
434
435 package DiscoverMacro;
436
437 use IO::File;
438 use Data::Dumper;
439
440 use vars qw/$obj_count @datafile_path/;
441 $obj_count = 0;
442
443 sub new($) {
444     my ($proto) = @_;
445     my $class = ref($proto) || $proto;
446     my $self = {};
447     $obj_count++;
448     bless $self, $class;
449     $self->{missingmacros} = {};
450     $self->opendata();
451     return $self;
452 }
453
454 sub DESTROY {
455     $obj_count--;
456 }
457
458 sub skip_to_end($$$) {
459     my ($file,$type,$lnr) = @_;
460     my $line;
461     while (defined ($line = DiscoverMacro::H::readline2($file))) {
462         return $file->getpos 
463             if $line =~ /^\s*END\s*\Q$type\E\s*$/;
464     }
465     warn "Could not find end of $type-section started on line $lnr";
466     return undef;
467 }
468
469 sub lineinfo($) {
470     my $lnr = shift;
471     return "ltxmacrs.txt:$lnr: ";
472 }
473
474 sub opendata($) {
475     my $self = shift;
476     my $macros = {};
477     my $regexmacros = {};
478     my $features = {};
479     my $datafile = $self->{datafile};
480     unless (defined $datafile) {
481         for my $dir (@datafile_path) {
482             if ($dir eq 'program') {
483                 $datafile = $0; $datafile =~ s@[^/]*$@ltxmacrs.txt@;
484             } elsif (-d $dir) {
485                 $datafile = "$dir/ltxmacrs.txt";
486             } else {
487                 $datafile = "$dir";
488             }
489             if (-e $datafile) { last; }
490             else { $datafile = undef; }
491         }
492         die "Could not find data file ltxmacrs.txt in path ".
493             join ', ', map {
494                 ($_ eq 'program')?'script location':$_ } @datafile_path
495                     unless defined $datafile;
496         $self->{datafile} = $datafile;
497     }
498     my $data = new IO::File($datafile,"r") or
499         die "Could not open $datafile for reading";
500     my $line;
501     while (defined ($line = DiscoverMacro::H::readline2($data))) {
502         $line = DiscoverMacro::H::cleanline($line);
503         next if $line eq '';
504         my (@line) = split ' ', $line;
505         if ($line[0] eq 'MACRO') {
506 #           print Dumper(\@line);
507             warn lineinfo($.)."Macro $line[1] given twice\n"
508                 if defined $$macros{$line[1]};
509             $$macros{$line[1]} = join ' ', @line[2..$#line];
510         } elsif ($line[0] eq 'REGEX') {
511             warn lineinfo($.)."Regex macro $line[1] given twice\n"
512                 if defined $$regexmacros{$line[1]};
513             $$regexmacros{$line[1]} = join ' ', @line[2..$#line];
514         } elsif ($line[0] eq 'FEATURE') {
515             $$features{$line[1]} = $data->getpos;
516             skip_to_end($data,'FEATURE',$.);
517         } else {
518             warn lineinfo($.)."Unknown command '$line[0]'\n";
519         }
520     }
521     $self->{macros} = $macros;
522     $self->{regexmacros} = $regexmacros;
523     $self->{features} = $features;
524     $self->{datafile} = $data;
525
526 #    print Dumper($macros);
527 }
528
529 sub getmacro($$) {
530     my ($self,$macro) = @_;
531     return new DiscoverMacro::Macro($self,$macro);
532 }
533
534 sub getfeaturepos($$) {
535     my ($self,$feature) = @_;
536     my $features = $self->{features};
537     return $$features{$feature};
538 }
539
540 sub readlog($$;$) {
541     my ($self,$filename,$file) = @_;
542     my $found = 0;
543     if (!defined $file) {
544         $file = new IO::File($filename,"r")
545             or die "Could not open log file '$filename' for reading: $!";
546     }
547     my $line;
548     while (defined ($line = <$file>)) {
549         chomp $line;
550         if ($line =~ /^! Undefined control sequence\.$/) {
551             chomp ($line = <$file>);
552             #print "LINE: '$line'\n";
553             my $dots = '';
554             my ($macro) = ($line =~ /(\\.)$/);
555             ($dots,$macro) = 
556                 ($line =~ /(...)(\\[a-zA-Z@]+) ?$/) unless defined $macro;
557             ($dots,$macro) = 
558                 ($line =~ /(...)([a-zA-Z@]+) ?$/) unless defined $macro;
559             my $regex = 0;
560             if ($dots eq '...') {
561                 $regex = 1;
562                 $macro =~ s/^\\//;
563                 $macro = "\\\\[a-zA-Z@]*\Q$macro";
564             } else {
565                 $macro = "\\$macro" unless $macro =~ /^\\/;
566             }
567             unless (defined $macro) {
568                 warn "$filename:$.: Could not identify undefined control in:\n".
569                     "\t$line\n";
570                 next; };
571             #print "MACRO: '$macro'\n";
572             if ($regex) {
573                 $found = 0;
574                 for my $m (keys %{$self->{macros}}) {
575                     if ($m =~ /$macro/) {
576                         $self->addmacro($m);
577                         $found = 1; }
578                 }
579                 $self->addmacro("REGEX:$macro") unless $found;
580             } else {
581                 $self->addmacro($macro);
582             }
583             $found = 1;
584         } elsif ($line =~ /^! Package babel Error: You haven\'t defined the language (.+) yet.$/) {
585             my $lang = $1;
586             $self->addmacro("\\selectlanguage{$lang}");
587             $found = 1;
588         } elsif ($line =~ /^! LaTeX Error: Environment .* undefined\.$/) {
589             my ($env) =
590                 ($line =~ /^! LaTeX Error: Environment (.*) undefined\.$/);
591             unless (defined $env) {
592                 warn "$filename:$.: Could not identify environment in:\n".
593                     "\t$line\n" unless defined $env;
594                 next; };
595             $self->addmacro("\\begin{$env}");
596             $found = 1;
597         } elsif ($line =~ /^! LaTeX Error: Encoding scheme \`.*\' unknown\.$/) {
598             my ($fe) =
599                 ($line =~ /^! LaTeX Error: Encoding scheme \`(.*)\' unknown\.$/);
600             unless (defined $fe) {
601                 warn "$filename:$.: Could not identify fontencoding in:\n".
602                     "\t$line\n" unless defined $fe;
603                 next; };
604             $self->addmacro("\\fontencoding{$fe}");
605             $found = 1;
606         } elsif ($line =~ /^! LaTeX Error: Command .* (not provided|unavailable)/) {
607             my ($macro) =
608                 ($line =~ /^! LaTeX Error: Command (.*) (not provided|unavailable)/);
609             unless (defined $macro) {
610                 warn "$filename:$.: Could not identify macro in:\n".
611                     "\t$line\n" unless defined $macro;
612                 next; };
613             $macro = "\\$macro" unless $macro =~ /^\\/;
614             $self->addmacro("$macro");
615             $found = 1;
616         } elsif ($line =~ /^! Package ucs Error: Unknown .* tag '.*' \((.*)\)\.$/) {
617             my ($macro) =
618                 ($line =~ /^! Package ucs Error: Unknown .* tag '.*' \((.*)\)\.$/);
619             unless (defined $macro) {
620                 warn "$filename:$.: Could not identify macro in:\n".
621                     "\t$line\n";
622                 next; };
623             $macro = "\\$macro" unless $macro =~ /^\\/;
624             $self->addmacro("$macro");
625             $found = 1;
626         } elsif ($line =~ /^! Font .*=([^=]+) at .* not loadable: Metric \(TFM\)/) {
627             my ($tfm) = ($line =~ /^! Font .*=([^=]+) at .* not loadable: Metric \(TFM\)/);
628             unless (defined $tfm) {
629                 warn "$filename:$.: Could not identify TFM filename in:\n".
630                     "\t$line\n";
631                 next; };
632             print "XXX: $tfm\n";
633             $self->addmacro("$tfm.tfm");
634             $found = 1;
635         }
636     }
637     close $file;
638     return $found;
639 }
640
641 sub addmacro($$%) {
642     my ($self,$macro) = @_;
643     my $macros = $self->{missingmacros};
644     return $$macros{$macro} if $$macros{$macro};
645     my $res = $self->getmacro($macro);
646     $$macros{$macro} = $res if defined $res;
647     return $res;
648 }
649
650 sub getfileat($$) {
651     my ($self,$pos) = @_;
652     my $file = $self->{datafile};
653     $file->setpos($pos) or
654         die "Could not seek in datafile to pos $pos";
655     return $file;
656 }
657
658 sub getmissingmacros($) {
659     my $self = shift;
660     return $self->{missingmacros};
661 }
662
663 sub as_text($$) {
664     my ($self,$f) = @_;
665     my $macros = $self->{missingmacros};
666     my $str = '';
667     for my $m (keys %$macros) {
668         $str .= $$macros{$m}->as_text($f);
669     }
670     return $str;
671 }
672
673 sub close($) {
674     my $self = shift;
675     $self->{macros} = undef;
676     $self->{features} = undef;
677     $self->{missingmacros} = undef;
678     close $self->{datafile};
679 }
680
681 sub getfeature($$) {
682     my ($self,$feature) = @_;
683     return new DiscoverMacro::Feature($self,$feature);
684 }
685
686 sub scanformacro($%) {
687     my ($self,%args) = @_;
688     my $file = $args{file};
689     my $fh = new IO::File($file,"r") or
690         die "Could not open $file for reading: $!";
691     my $line;
692     # {\newenvironment}{\renewenvironment}{\newif}
693     my $newcommand = '\\\\newcommand|\\\\renewcommand|\\\\providecommand|'.
694         '\\\\DeclareRobustCommand|\\\\def';
695     my $newenvironment = '\\\\newenvironment|\\\\renewenvironment';
696     my @macros;
697     while (defined ($line = <$fh>)) {
698         chomp $line;
699         print "L: $line\n";
700         $line =~ s{($newcommand) \s* \{? (\\[a-zA-Z@]+) \}?}{
701             print "MACRO: $2\n";
702             my ($env) = ($2 =~ /^\\end(.*)$/);
703             if (defined $env) {
704                 print "ENV: $env\n"; }
705         }gxeo;
706         $line =~ s{($newenvironment) \s* \{ ([^\}]+) \} }{
707             print "ENV: $2\n";
708         }gxeo;
709     }
710     print Dumper [\@macros];
711 }
712
713
714 sub main(@) {
715     if (@_) {
716         for my $file (@_) {
717             my $obj = new DiscoverMacro;
718             if (-e $file) {
719                 $obj->readlog($file);
720             } else {
721                 $obj->addmacro($file);
722             }
723             print $obj->as_text('text/plain');
724         };
725     } else {
726         my ($file) = 
727             sort { my @a = stat $a; my @b = stat $b; $b[9] <=> $a[9] }
728         grep { $_ ne 'missfont.log' } (<*.log>);
729         die "No logfile found" if (!defined $file);
730         print "Using logfile $file.\n";
731         &main($file);
732     }
733 }
734
735 sub test() {
736     my $obj = new DiscoverMacro;
737     $obj->scanformacro(file => 'test.tex');
738     exit;
739 }
740
741 #test;
742
743 caller || &main(@ARGV);
744
745 1;