6180d0f75987a423eb8928c745e7060e04774eee
[conv.git] / conv.pl
1 #!/usr/bin/perl
2 use Getopt::Std;
3 use File::Basename;
4 use strict;
5 use warnings;
6
7 my %rules;
8 my %vars;
9 my %opts = (
10     j => 1
11 );
12
13 getopts('vnsj:f:o:i:', \%opts);
14
15 my $dirout;
16 if (-d $opts{o}) {
17     # output is a directory
18     $dirout = 1;
19 } else {
20     # output is a single file, imply -i <ext> unless -i has also been specified
21     if ($opts{o} =~ /\.(\w+)$/) {
22         $opts{i} = $1 unless exists $opts{i};
23     } else {
24         print "Specified -o <file>, but '$opts{o}' has no extension.\n";
25         exit 1;
26     }
27 }
28
29 print "Using $opts{j} processors\n" if $opts{v};
30
31 my $var_re = qr/[A-Za-z_][A-Za-z0-9_]*/;
32
33 sub convert {
34     my ($base, $iext, $oext) = @_;
35     my $cmd = $rules{$oext}{$iext};
36     my $infile = "$base.$iext";
37     $infile =~ s/"/\\"/g;
38     $cmd =~ s!\$\<!"$infile"!g;
39     my $outfile;
40     if ($opts{o}) {
41         if ($dirout) {
42             # Strip leading path from base so we can stick the file in
43             # another dir.
44             $base = basename($base);
45             $outfile = "$opts{o}/$base.$oext";
46         } else {
47             $outfile = $opts{o};
48         }
49     } else {
50         $outfile = "$base.$oext";
51     }
52     $outfile =~ s/"/\\"/g;
53     $cmd =~ s!\$\@!"$outfile"!g;
54     $cmd = expand($cmd);
55     print "$cmd\n";
56     return if $opts{n};
57     my $pid = fork();
58     return $pid if $pid;
59
60     exec($cmd);
61 }
62
63 sub expand {
64     local $_ = shift;
65     s/\$($var_re)/(exists $vars{$1} ? $vars{$1} : '$' . $1)/eg;
66     return $_;
67 }
68
69 open CONF, ($opts{f} || 'Convfile') or die "Could not open Convfile";
70 while (<CONF>) {
71     s/#.*$//;
72     s/^\s+//;
73     s/\s+$//;
74     next if /^$/;
75
76     if (/^([\w.]+)\s*=>\s*([\w.]+)\s*:\s*(.*)$/) {
77         $rules{$2}{$1} = $3;
78     } elsif (/^($var_re)\s*=\s*(.*)$/) {
79         $vars{$1} = expand($2);
80     } else {
81         warn "Error in Convfile line $.: $_\n";
82     }
83 }
84 close CONF;
85
86 my @queue;
87 my @files;
88
89 # If we specify reading from stdin, do this.
90 if (exists $opts{s}) {
91     while (<STDIN>) {
92         next if /^#/;
93         chomp;
94
95         push @files, $_;
96     }
97 } else {
98     @files = @ARGV;
99 }
100
101 # Reversed semantics. Files are input to be converted to the specified extension
102 if ($opts{i}) {
103     unless (exists $rules{$opts{i}}) {
104         print <<EOD;
105 You have specified -i <ext>, but the Convfile has no rule to convert to
106 extension '$opts{i}'
107 EOD
108         exit 1;
109     }
110     foreach (@files) {
111         unless (-e $_) {
112             print "$_ does not exist.\n";
113             next;
114         }
115
116         if (/^(.+)\.(\w+)$/) {
117             my $base = $1;
118             my $iext = $2;
119             print "Considering $base.$iext\n" if $opts{v};
120             if (exists $rules{$opts{i}}{$iext}) {
121                 print "Using $base.$iext\n" if $opts{v};
122                 push @queue, [$base, $iext, $opts{i}];
123             }
124         } else {
125             print "I don't see an extension on $_, so I don't know what to do with it.\n";
126         }
127     }
128 }
129
130 # Make semantics, files specified are desired output files
131 else {
132     # Do something fun (convert *.foo into a list of files that would match if they existed)
133     my @tmpfiles;
134     foreach (@files) {
135         if (/^\*\.(\w+)$/) {
136             my $extsub = $1;
137             push @tmpfiles,
138             map { s/\.(\w+)$/\.$extsub/; $_ } 
139                 grep { /^[^.]/ }
140                 <*>;
141         } else {
142             push @tmpfiles, $_;
143         }
144     }
145     @files = @tmpfiles;
146     
147     # I like to call this the TOWER OF POWER
148     for my $ofile (@files) {
149         my $ofound = 0;
150         for my $oext (keys %rules) {
151             my $oext_re = $oext;
152             $oext_re =~ s/\./\\./g;
153             if ($ofile =~ /^(.*)\.$oext_re$/) {
154                 my $base = $1;
155                 my $ifound = 0;
156                 for my $iext (keys %{$rules{$oext}}) {
157                     print "Considering $base.$iext\n" if $opts{v};
158                     if (-e "$base.$iext") {
159                         print "Using $base.$iext\n" if $opts{v};
160                         push @queue, [$base, $iext, $oext];
161                         $ifound = 1;
162                         last;
163                     }
164                 }
165                 unless ($ifound) {
166                     print "No suitable input file found for $ofile\n";
167                 }
168                 $ofound = 1;
169                 last;
170             }
171         }
172         unless ($ofound) {
173             print "No rule to make $ofile\n";
174         }
175     }
176 }
177
178 print scalar @queue, " items to process\n" if $opts{v};
179
180 my $procs = 0;
181 foreach my $chunk (@queue) {
182     convert(@$chunk);
183     $procs++;
184     if ($procs == $opts{j}) {
185         wait;
186         $procs--;
187     }
188 }
189 wait while $procs--;
190
191 # vim:ts=4 sts=4 sw=4 expandtab