Pipe unescaping is done in the right place.
[artemus.git] / waste / artemus
blobf7181702c7659488e5c745befbb41f6012385430
1 #!/usr/bin/perl
4 # artemus - HTML (and other things) Preprocessor
6 # Copyright (C) 2000/2002 Angel Ortega <angel@triptico.com>
8 # This program is free software; you can redistribute it and/or
9 # modify it under the terms of the GNU General Public License
10 # as published by the Free Software Foundation; either version 2
11 # of the License, or (at your option) any later version.
13 # This program is distributed in the hope that it will be useful,
14 # but WITHOUT ANY WARRANTY; without even the implied warranty of
15 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 # GNU General Public License for more details.
18 # You should have received a copy of the GNU General Public License
19 # along with this program; if not, write to the Free Software
20 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
22 # http://www.triptico.com
25 use locale;
26 use Getopt::Long;
28 # id
29 $VERSION="3.0beta1";
30 $artemus_id="Artemus $VERSION";
32 # substitution variables
33 %vars=();
35 # substitution functions
36 %funcs=();
38 # source and destination files
39 $src="";
40 $dst="";
42 # config file
43 $config_file="artemus.conf";
45 # local configuration file
46 $local_config_file="local-artemus.conf";
48 # paragraph separator
49 $para_sep="";
51 # use cr/lf instead of lf
52 $use_cr_lf=0;
54 # append instead of overwrite output file
55 $append=0;
57 # send files using ftp
58 $use_ftp=0;
60 # build site map
61 $site_map=0;
63 # create makefile template
64 $makefile_template=0;
66 # quiet flag
67 $quiet=0;
69 # inverse substitutions
70 $inverse_config_file="artemus-inv.conf";
71 %inv_vars=();
73 # include path
74 $include_path="";
76 # include debugging info
77 $debug=0;
79 # list of unresolved symbols
80 @unresolved=();
82 #####################################################################
85 usage() if(!GetOptions( "i|input=s" => \$src,
86 "o|output=s" => \$dst,
87 "c|conf=s" => \$config_file,
88 "p|paragraph=s" => \$para_sep,
89 "m|msdos" => \$use_cr_lf,
90 "a|append" => \$append,
91 "v|version-only" => \$version_only,
92 "f|ftp" => \$use_ftp,
93 "k|makefile" => \$makefile_template,
94 "s|site-map" => \$site_map,
95 "q|quiet" => \$quiet,
96 "l|include-path=s" => \$include_path,
97 "d|debug" => \$debug,
98 "h|help" => \$help) or $help);
100 $dst=">-" if $dst eq "-";
102 version_only() if $version_only;
104 make_makefile() if $makefile_template;
106 build_site_map(@ARGV) if $site_map;
108 if(!$use_ftp)
110 usage() unless $src;
111 usage() unless $dst;
114 # read the configuration files
115 read_config($config_file) if -f $config_file;
116 read_config($local_config_file) if -f $local_config_file;
117 read_config($inverse_config_file,1) if -f $inverse_config_file;
119 ftp_send() if $use_ftp;
121 open F, $src or die "can't open '$src'";
123 # get all the file
124 #undef $/;
125 $data=join("",<F>);
126 close F;
128 ###########################################
130 # Artemus main processing function
132 ###########################################
133 # -*- Mode: Perl
136 sub artemus
138 my ($data,%opts)=@_;
139 my ($vars,$inv_vars,$funcs);
140 my ($unresolved,$res);
142 # abort flag not set
143 $artemus_abort=0;
145 # make hashes comfortable
146 $vars=$opts{'vars'};
147 $inv_vars=$opts{'inv-vars'};
148 $funcs=$opts{'funcs'};
149 $unresolved=$opts{'unresolved'};
151 # special values:
152 # {-\n}, substitutes as \n
153 $vars->{"\\n"}="\n";
155 # special functions
156 $funcs->{"localtime"}=sub { scalar(localtime) };
157 $funcs->{"if"}=sub { $_[0] ? return($_[1]) : return("") };
158 $funcs->{"ifelse"}=sub { $_[0] ? return($_[1]) : return($_[2]) };
160 # if defined, substitute the paragraphs
161 # with the paragraph separator
162 if($opts{'paragraph-separator'})
164 $data =~ s/\n\n/\n$opts{'paragraph-separator'}\n/g;
167 # concat special variables BEGIN & END
168 $data = $vars->{"\\BEGIN"} . $data . $vars->{"\\END"};
170 # inverse substitutions
171 for my $i (keys(%$inv_vars))
173 next if $inv_vars->{$i} =~ /\$/;
174 next if $i =~ /^\-/;
175 $data =~ s/\b($i)\b/\{\-$1\}/g;
178 # start counting resolutions
179 $res=0;
181 # main function, variable and include substitutions
182 while($data =~ /{-([^}{]*)}/s)
184 my ($found)=$1;
185 my ($key,@params,$text,$n);
187 ($key,@params)=split(/\|/,$found);
189 # exclude dangerous keys
190 unless($key =~ /^[-\\\w_ \.]+$/)
192 $text=$key;
195 # is it a variable?
196 elsif(defined $vars->{$key})
198 $text=$vars->{$key};
200 for($n=0;$text =~ /\$$n/;$n++)
202 $text =~ s/\$$n/$params[$n]/g;
206 # is it a function?
207 elsif(defined $funcs->{$key})
209 my ($func);
211 $func=$funcs->{$key};
212 $text=&$func(@params);
214 # functions can abort further execution
215 last if $artemus_abort;
218 # is it an include?
219 elsif($opts{'include-path'} and
220 open (INC, "$opts{'include-path'}/$key"))
222 $text=join("",<INC>);
223 close INC;
225 for($n=0;$text =~ /\$$n/;$n++)
227 $text =~ s/\$$n/$params[$n]/g;
231 unless(defined $text)
233 # print STDERR "unresolved: '$found'\n" if not $quiet;
234 push(@$unresolved,$found);
235 $text=$found;
238 # include debug information, if necessary
239 $text="<!-- artemus: $key ($res) -->$text<!-- artemus: /$key ($res) -->" if $opts{'debug'};
241 # make the substitution
242 $data =~ s/{-\Q$found\E}/$text/;
244 # count
245 $res++;
248 # finally, convert end of lines if necessary
249 $data =~ s/\n/\r\n/g if($opts{'use-cr-lf'});
251 return($data);
256 if($dst =~ /.html$/)
258 $data="<meta name=\"generator\" content=\"$artemus_id\">\n".$data;
259 $data=sprintf("<!-- Date: %s -->\n",scalar(localtime)).$data;
260 $data="<!-- Built with $artemus_id Angel Ortega 2000/2002 -->\n".$data;
263 # special functions
264 $funcs{"filesize"}=sub { -s $_[0] };
265 $funcs{"shell"}=sub { $_=`$_[0]`; chop; return $_ };
267 $data=artemus($data, "paragraph-separator" => $para_sep,
268 "vars" => \%vars,
269 "inv-vars" => \%inv_vars,
270 "funcs" => \%funcs,
271 "include-path" => $include_path,
272 "use-cr-lf" => $use_cr_lf,
273 "debug" => $debug,
274 "unresolved" => \@unresolved
277 # save file
279 open F, ($append?">":"").">$dst" or die "can't write '$dst'";
280 print F $data;
281 close F;
283 # dump errors
284 unless($quiet)
286 foreach my $e (@unresolved)
288 print STDERR "Unresolved: '$e'\n";
293 exit(0);
295 # #####################################################################
297 sub read_config
299 my ($conf,$inverse)=@_;
300 local (*F);
302 # read config file
303 unless(open F, $conf)
305 if($quiet)
306 { return }
307 else
308 { die "'$conf' bad config file"; }
311 while(<F>)
313 my ($key,$val);
315 chomp($_);
317 unless(/^#/ or /^$/)
319 ($key,$val)=split("=",$_,2);
321 if($val =~ s/^\|//)
323 $val=`$val`;
324 chop($val);
326 elsif($val eq "<<EOF")
328 # 'document here' construction
329 $val="";
331 while(<F>)
333 last if /^EOF/;
335 $val.=$_;
338 elsif($key eq "\\INCLUDE")
340 read_config($val);
341 next;
344 $vars{$key}=$val;
345 $inv_vars{$key}=$val if($inverse);
349 close F;
353 sub usage
355 print("$artemus_id - HTML (and other things) Preprocessor\n");
356 print("Copyright (C) 2000/2002 Angel Ortega <angel\@triptico.com>\n\n");
358 print("Usage:\n");
359 print(" artemus -i|--input={input file} -o|--output={output file}\n");
360 print(" [-c|--conf={config file}]\n");
361 print(" [-l|--include-path={path to includes}]\n");
362 print(" [-p|--paragraph={paragraph_separator}]\n");
363 print(" [-q|--quiet]\n");
364 print(" [-m|--msdos] [-a|--append] [-d|--debug]\n\n");
365 print(" or\n\n");
366 print(" artemus -f|--ftp {files to ftp...}\n\n");
367 print(" or\n\n");
368 print(" artemus -k|--makefile\n");
369 print(" or\n\n");
370 print(" artemus -s|--site-map\n");
372 exit(1);
376 sub version_only
378 print("$VERSION\n");
379 exit(0);
383 sub ftp_send
384 # send files using ftp
386 my ($ftp);
388 require Net::FTP;
390 if(scalar(@ARGV)==0)
392 print "Nothing to send.\n";
393 exit(0);
396 print "Connecting to $vars{'ftp.host'}...\n";
397 $ftp=Net::FTP->new($vars{'ftp.host'});
398 print "OK\n";
400 print "Logging in as $vars{'ftp.user'}...\n";
401 $ftp->login($vars{'ftp.user'},$vars{'ftp.passwd'})
402 or die "ftp login error";
403 print "OK\n";
405 if(defined($vars{'ftp.dir'}))
407 print "Chdir $vars{'ftp.dir'}...\n";
408 $ftp->cwd($vars{'ftp.dir'});
409 print "OK\n";
412 $ftp->binary();
414 foreach my $f (@ARGV)
416 print "Sending $f...\n";
417 $ftp->put($f,$f);
418 print "OK\n";
421 print "Done.\n";
422 $ftp->quit;
424 exit(0);
428 sub make_makefile
429 # makes a makefile template
431 print <<"EOF";
433 # Makefile template created by $artemus_id
434 # for HTML projects (GNU Make)
436 # artemus (C) 2000/2002 Angel Ortega <angel\@triptico.com>
438 # Use:
439 # make -- to build everything
440 # make file.html -- to build just file.html from file.artemus
441 # make clean -- to delete every rebuildable thing
442 # make ftp -- to upload using ftp
445 # Fill DEST with a list of the .html files created by artemus
446 # Use relative paths (e.g. misc/contact.html) if there are
447 # subdirectories. This paths will be the same when uploading;
448 # subdirs on ftp server must exist
450 DEST= index.html [MODIFY HERE]
452 # Add \$(DEST) to INET plus any other file/directory than will
453 # be uploaded but not built by this makefile.
454 # e.g.: download/* or images/*
456 INET=\$(DEST) images/* [MODIFY HERE]
458 # directories for the site map
459 DIRS= [MODIFY HERE]
461 # default target
462 .PHONY: all
463 all: \$(DEST)
465 # default rule. Artemus sources use the .artemus extension.
466 # Change if you use another
467 %.html: %.artemus
468 artemus -i \$< -o \$@
470 # site map. File sitemap/index.artemus must be included
471 # in DEST in order to be correctly remade.
472 map:
473 artemus --site-map \$(DIRS) > sitemap/index.artemus
474 \$(MAKE)
476 # The usual clean
477 .PHONY: clean
478 clean:
479 -rm \$(DEST) unresolved
481 # Last upload timestamp
482 .PHONY: ftp
483 ftp: ftpstamp
485 # if any file is newer than the last upload,
486 # send it and recreate timestamp
487 ftpstamp: \$(INET)
488 artemus --ftp \$?
489 touch ftpstamp
493 exit(0);
497 sub build_site_map
498 # builds a site map from current directory to stdout
500 my (@dirs)=@_;
502 # special artemus token site-map-head
503 print "{-site-map-head}\n\n";
504 print "<ul>\n";
506 foreach my $dir (@dirs)
508 my ($cnt,@l);
510 open F, "find $dir -name \"*.html\" -follow|" or next;
512 while(<F>)
514 chop;
515 s/^\.\///;
516 push(@l,$_) unless /index.html$/;
519 close F;
521 @l=sort(@l);
523 # the (supposed) index.html is put the first
524 unshift(@l,"$dir/index.html");
526 # travel the list
527 $cnt=0;
528 foreach my $i (@l)
530 my ($size,$file,$title);
532 $size=-s $i;
534 # slurps all
535 open F, $i or next;
536 $file=join("",<F>);
537 close F;
539 if ($file =~ /<title>([^<]*)/i)
541 $title=$1;
543 else
545 $title=$i;
548 print "<li><a href='../$i'>$title</a> [$i] $size bytes\n";
549 print "<ul>\n" unless $cnt;
551 $cnt++;
553 print "</ul>\n";
556 print "</ul>\n";
558 # special artemus token site-map-foot
559 print "{-site-map-foot}\n";
561 exit(0);