Add built local::lib
[catagits/Gitalist.git] / local-lib5 / bin / ttree
1 #!/usr/bin/perl -w
2
3 eval 'exec /usr/bin/perl -w -S $0 ${1+"$@"}'
4     if 0; # not running under some shell
5 #========================================================================
6 #
7 # ttree
8 #
9 # DESCRIPTION
10 #   Script for processing all directory trees containing templates.
11 #   Template files are processed and the output directed to the 
12 #   relvant file in an output tree.  The timestamps of the source and
13 #   destination files can then be examined for future invocations 
14 #   to process only those files that have changed.  In other words,
15 #   it's a lot like 'make' for templates.
16 #
17 # AUTHOR
18 #   Andy Wardley   <abw@wardley.org>
19 #
20 # COPYRIGHT
21 #   Copyright (C) 1996-2003 Andy Wardley.  All Rights Reserved.
22 #   Copyright (C) 1998-2003 Canon Research Centre Europe Ltd.
23 #
24 #   This module is free software; you can redistribute it and/or
25 #   modify it under the same terms as Perl itself.
26 #
27 #------------------------------------------------------------------------
28 #
29 # $Id: ttree 1196 2009-04-07 13:34:14Z abw $
30 #
31 #========================================================================
32
33 use strict;
34 use Template;
35 use AppConfig qw( :expand );
36 use File::Copy;
37 use File::Path;
38 use File::Spec;
39 use File::Basename;
40 use Text::ParseWords qw(quotewords);
41
42 my $NAME     = "ttree";
43 my $VERSION  = 2.90;
44 my $HOME     = $ENV{ HOME } || '';
45 my $RCFILE   = $ENV{"\U${NAME}rc"} || "$HOME/.${NAME}rc";
46 my $TTMODULE = 'Template';
47
48 #------------------------------------------------------------------------
49 # configuration options
50 #------------------------------------------------------------------------
51
52 # offer create a sample config file if it doesn't exist, unless a '-f'
53 # has been specified on the command line
54 unless (-f $RCFILE or grep(/^(-f|-h|--help)$/, @ARGV) ) {
55     print("Do you want me to create a sample '.ttreerc' file for you?\n",
56       "(file: $RCFILE)   [y/n]: ");
57     my $y = <STDIN>;
58     if ($y =~ /^y(es)?/i) {
59         write_config($RCFILE);
60         exit(0);
61     }
62 }
63
64 # read configuration file and command line arguments - I need to remember 
65 # to fix varlist() and varhash() in AppConfig to make this nicer...
66 my $config   = read_config($RCFILE);
67 my $dryrun   = $config->nothing;
68 my $verbose  = $config->verbose || $dryrun;
69 my $colour   = $config->colour;
70 my $summary  = $config->summary;
71 my $recurse  = $config->recurse;
72 my $preserve = $config->preserve;
73 my $all      = $config->all;
74 my $libdir   = $config->lib;
75 my $ignore   = $config->ignore;
76 my $copy     = $config->copy;
77 my $accept   = $config->accept;
78 my $absolute = $config->absolute;
79 my $relative = $config->relative;
80 my $suffix   = $config->suffix;
81 my $binmode  = $config->binmode;
82 my $depends  = $config->depend;
83 my $depsfile = $config->depend_file;
84 my ($n_proc, $n_unmod, $n_skip, $n_copy, $n_mkdir) = (0) x 5;
85
86 my $srcdir   = $config->src
87     || die "Source directory not set (-s)\n";
88 my $destdir  = $config->dest
89     || die "Destination directory not set (-d)\n";
90 die "Source and destination directories may not be the same:\n  $srcdir\n"
91     if $srcdir eq $destdir;
92
93 # unshift any perl5lib directories onto front of INC
94 unshift(@INC, @{ $config->perl5lib });
95
96 # get all template_* options from the config and fold keys to UPPER CASE
97 my %ttopts   = $config->varlist('^template_', 1);
98 my $ttmodule = delete($ttopts{ module });
99 my $ucttopts = {
100     map { my $v = $ttopts{ $_ }; defined $v ? (uc $_, $v) : () }
101     keys %ttopts,
102 };
103
104 # get all template variable definitions
105 my $replace = $config->get('define');
106
107 # now create complete parameter hash for creating template processor
108 my $ttopts   = {
109     %$ucttopts,
110     RELATIVE     => $relative,
111     ABSOLUTE     => $absolute,
112     INCLUDE_PATH => [ $srcdir, @$libdir ],
113     OUTPUT_PATH  => $destdir,
114 };
115
116 # load custom template module 
117 if ($ttmodule) {
118     my $ttpkg = $ttmodule;
119     $ttpkg =~ s[::][/]g;
120     $ttpkg .= '.pm';
121     require $ttpkg;
122 }
123 else {
124     $ttmodule = $TTMODULE;
125 }
126
127
128 #------------------------------------------------------------------------
129 # inter-file dependencies
130 #------------------------------------------------------------------------
131
132 if ($depsfile or $depends) {
133     $depends = dependencies($depsfile, $depends);
134
135 else {
136     $depends = { };
137 }
138
139 my $global_deps = $depends->{'*'} || [ ];
140
141 # add any PRE_PROCESS, etc., templates as global dependencies
142 foreach my $ttopt (qw( PRE_PROCESS POST_PROCESS PROCESS WRAPPER )) {
143     my $deps = $ucttopts->{ $ttopt } || next;
144     my @deps = ref $deps eq 'ARRAY' ? (@$deps) : ($deps);
145     next unless @deps;
146     push(@$global_deps, @deps);
147 }
148
149 # remove any duplicates
150 $global_deps = { map { ($_ => 1) } @$global_deps };
151 $global_deps = [ keys %$global_deps ];
152
153 # update $depends hash or delete it if there are no dependencies
154 if (@$global_deps) {
155     $depends->{'*'} = $global_deps;
156 }
157 else {
158     delete $depends->{'*'};
159     $global_deps = undef;
160 }
161 $depends = undef
162     unless keys %$depends;
163
164 my $DEP_DEBUG = $config->depend_debug();
165
166
167 #------------------------------------------------------------------------
168 # pre-amble
169 #------------------------------------------------------------------------
170
171 if ($colour) {
172     no strict 'refs';
173     *red    = \&_red;
174     *green  = \&_green;
175     *yellow = \&_yellow;
176     *blue   = \&_blue;
177 }
178 else {
179     no strict 'refs';
180     *red    = \&_white;
181     *green  = \&_white;
182     *yellow = \&_white;
183     *blue   = \&_white;
184 }
185
186 if ($verbose) {
187     local $" = ', ';
188
189
190     print "$NAME $VERSION (Template Toolkit version $Template::VERSION)\n\n";
191
192     my $sfx = join(', ', map { "$_ => $suffix->{$_}" } keys %$suffix);
193
194     print("      Source: $srcdir\n",
195           " Destination: $destdir\n",
196           "Include Path: [ @$libdir ]\n",
197           "      Ignore: [ @$ignore ]\n",
198           "        Copy: [ @$copy ]\n",
199           "      Accept: [ @$accept ]\n",
200           "      Suffix: [ $sfx ]\n");
201     print("      Module: $ttmodule ", $ttmodule->module_version(), "\n")
202         unless $ttmodule eq $TTMODULE;
203
204     if ($depends && $DEP_DEBUG) {
205         print "Dependencies:\n";
206         foreach my $key ('*', grep { !/\*/ } keys %$depends) {
207             printf( "    %-16s %s\n", $key, 
208                     join(', ', @{ $depends->{ $key } }) ) 
209                 if defined $depends->{ $key };
210
211         }
212     }
213     print "\n" if $verbose > 1;
214     print red("NOTE: dry run, doing nothing...\n")
215         if $dryrun;
216 }
217
218 #------------------------------------------------------------------------
219 # main processing loop
220 #------------------------------------------------------------------------
221
222 my $template = $ttmodule->new($ttopts)
223     || die $ttmodule->error();
224
225 if (@ARGV) {
226     # explicitly process files specified on command lines 
227     foreach my $file (@ARGV) {
228         my $path = $srcdir ? File::Spec->catfile($srcdir, $file) : $file;
229         if ( -d $path ) {
230             process_tree($file);
231         }
232         else {
233             process_file($file, $path, force => 1);
234         }
235     }
236 }
237 else {
238     # implicitly process all file in source directory
239     process_tree();
240 }
241
242 if ($summary || $verbose) {
243     my $format  = "%13d %s %s\n";
244     print "\n" if $verbose > 1;
245     print(
246         "     Summary: ",
247         $dryrun ? red("This was a dry run.  Nothing was actually done\n") : "\n",
248         green(sprintf($format, $n_proc,  $n_proc  == 1 ? 'file' : 'files', 'processed')),
249         green(sprintf($format, $n_copy,  $n_copy  == 1 ? 'file' : 'files', 'copied')),
250         green(sprintf($format, $n_mkdir, $n_mkdir == 1 ? 'directory' : 'directories', 'created')),
251         yellow(sprintf($format, $n_unmod, $n_unmod == 1 ? 'file' : 'files', 'skipped (not modified)')),
252         yellow(sprintf($format, $n_skip,  $n_skip  == 1 ? 'file' : 'files', 'skipped (ignored)'))
253     );
254 }
255
256 exit(0);
257
258
259 #========================================================================
260 # END 
261 #========================================================================
262
263
264 #------------------------------------------------------------------------
265 # process_tree($dir)
266 #
267 # Walks the directory tree starting at $dir or the current directory
268 # if unspecified, processing files as found.
269 #------------------------------------------------------------------------
270
271 sub process_tree {
272     my $dir = shift;
273     my ($file, $path, $abspath, $check);
274     my $target;
275     local *DIR;
276
277     my $absdir = join('/', $srcdir ? $srcdir : (), defined $dir ? $dir : ());
278     $absdir ||= '.';
279
280     opendir(DIR, $absdir) || do { warn "$absdir: $!\n"; return undef; };
281
282     FILE: while (defined ($file = readdir(DIR))) {
283         next if $file eq '.' || $file eq '..';
284         $path = defined $dir ? "$dir/$file" : $file;
285         $abspath = "$absdir/$file";
286         
287         next unless -e $abspath;
288
289         # check against ignore list
290         foreach $check (@$ignore) {
291             if ($path =~ /$check/) {
292                 printf yellow("  - %-32s (ignored, matches /$check/)\n"), $path
293                     if $verbose > 1;
294                 $n_skip++;
295                 next FILE;
296             }
297         }
298
299         # check against acceptance list
300         if (@$accept) {
301             unless ((-d $abspath && $recurse) || grep { $path =~ /$_/ } @$accept) {
302                 printf yellow("  - %-32s (not accepted)\n"), $path
303                     if $verbose > 1;
304                 $n_skip++;
305                 next FILE;
306             }
307         }
308
309         if (-d $abspath) {
310             if ($recurse) {
311                 my ($uid, $gid, $mode);
312                 
313                 (undef, undef, $mode, undef, $uid, $gid, undef, undef,
314                  undef, undef, undef, undef, undef)  = stat($abspath);
315                 
316                 # create target directory if required
317                 $target = "$destdir/$path";
318                 unless (-d $target || $dryrun) {
319                     mkpath($target, $verbose, $mode) or 
320                         die red("Could not mkpath ($target): $!\n");
321
322                     # commented out by abw on 2000/12/04 - seems to raise a warning?
323                     # chown($uid, $gid, $target) || warn "chown($target): $!\n";
324
325                     $n_mkdir++;
326                     printf green("  + %-32s (created target directory)\n"), $path
327                         if $verbose;
328                 }
329                 # recurse into directory
330                 process_tree($path);
331             }
332             else {
333                 $n_skip++;
334                 printf yellow("  - %-32s (directory, not recursing)\n"), $path
335                     if $verbose > 1;
336             }
337         }
338         else {
339             process_file($path, $abspath);
340         }
341     }
342     closedir(DIR);
343 }
344     
345
346 #------------------------------------------------------------------------
347 # process_file()
348 #
349 # File filtering and processing sub-routine called by process_tree()
350 #------------------------------------------------------------------------
351
352 sub process_file {
353     my ($file, $absfile, %options) = @_;
354     my ($dest, $destfile, $filename, $check, 
355         $srctime, $desttime, $mode, $uid, $gid);
356     my ($old_suffix, $new_suffix);
357     my $is_dep = 0;
358     my $copy_file = 0;
359
360     $absfile ||= $file;
361     $filename = basename($file);
362     $destfile = $file;
363     
364     # look for any relevant suffix mapping
365     if (%$suffix) {
366         if ($filename =~ m/\.(.+)$/) {
367             $old_suffix = $1;
368             if ($new_suffix = $suffix->{ $old_suffix }) {
369                 $destfile =~ s/$old_suffix$/$new_suffix/;
370             }
371         }
372     }
373     $dest = $destdir ? "$destdir/$destfile" : $destfile;
374                    
375 #    print "proc $file => $dest\n";
376     
377     # check against copy list
378     foreach my $copy_pattern (@$copy) {
379         if ($filename =~ /$copy_pattern/) {
380             $copy_file = 1;
381             $check = $copy_pattern;
382             last;
383         }
384     }
385
386     # stat the source file unconditionally, so we can preserve
387     # mode and ownership
388     ( undef, undef, $mode, undef, $uid, $gid, undef, 
389       undef, undef, $srctime, undef, undef, undef ) = stat($absfile);
390     
391     # test modification time of existing destination file
392     if (! $all && ! $options{ force } && -f $dest) {
393         $desttime = ( stat($dest) )[9];
394
395         if (defined $depends and not $copy_file) {
396             my $deptime  = depend_time($file, $depends);
397             if (defined $deptime && ($srctime < $deptime)) {
398                 $srctime = $deptime;
399                 $is_dep = 1;
400             }
401         }
402     
403         if ($desttime >= $srctime) {
404             printf yellow("  - %-32s (not modified)\n"), $file
405                 if $verbose > 1;
406             $n_unmod++;
407             return;
408         }
409     }
410     
411     # check against copy list
412     if ($copy_file) {
413         $n_copy++;
414         unless ($dryrun) {
415             copy($absfile, $dest) or die red("Could not copy ($absfile to $dest) : $!\n");
416
417             if ($preserve) {
418                 chown($uid, $gid, $dest) || warn red("chown($dest): $!\n");
419                 chmod($mode, $dest) || warn red("chmod($dest): $!\n");
420             }
421         }
422
423         printf green("  > %-32s (copied, matches /$check/)\n"), $file
424             if $verbose;
425
426         return;
427     }
428
429     $n_proc++;
430     
431     if ($verbose) {
432         printf(green("  + %-32s"), $file);
433         print(green(" (changed suffix to $new_suffix)")) if $new_suffix;
434         print "\n";
435     }
436
437     # process file
438     unless ($dryrun) {
439         $template->process($file, $replace, $destfile,
440             $binmode ? {binmode => $binmode} : {})
441             || print(red("  ! "), $template->error(), "\n");
442
443         if ($preserve) {
444             chown($uid, $gid, $dest) || warn red("chown($dest): $!\n");
445             chmod($mode, $dest) || warn red("chmod($dest): $!\n");
446         }
447     }
448 }
449
450
451 #------------------------------------------------------------------------
452 # dependencies($file, $depends)
453
454 # Read the dependencies from $file, if defined, and merge in with 
455 # those passed in as the hash array $depends, if defined.
456 #------------------------------------------------------------------------
457
458 sub dependencies {
459     my ($file, $depend) = @_;
460     my %depends = ();
461
462     if (defined $file) {
463         my ($fh, $text, $line);
464         open $fh, $file or die "Can't open $file, $!";
465         local $/ = undef;
466         $text = <$fh>;
467         close($fh);
468         $text =~ s[\\\n][]mg;
469         
470         foreach $line (split("\n", $text)) {
471             next if $line =~ /^\s*(#|$)/;
472             chomp $line;
473             my ($file, @files) = quotewords('\s*:\s*', 0, $line);
474             $file =~ s/^\s+//;
475             @files = grep(defined, quotewords('(,|\s)\s*', 0, @files));
476             $depends{$file} = \@files;
477         }
478     }
479
480     if (defined $depend) {
481         foreach my $key (keys %$depend) {
482             $depends{$key} = [ quotewords(',', 0, $depend->{$key}) ];
483         }
484     }
485
486     return \%depends;
487 }
488
489
490
491 #------------------------------------------------------------------------
492 # depend_time($file, \%depends)
493 #
494 # Returns the mtime of the most recent in @files.
495 #------------------------------------------------------------------------
496
497 sub depend_time {
498     my ($file, $depends) = @_;
499     my ($deps, $absfile, $modtime);
500     my $maxtime = 0;
501     my @pending = ($file);
502     my @files;
503     my %seen;
504
505     # push any global dependencies onto the pending list
506     if ($deps = $depends->{'*'}) {
507         push(@pending, @$deps);
508     }
509
510     print "    # checking dependencies for $file...\n"
511         if $DEP_DEBUG;
512
513     # iterate through the list of pending files
514     while (@pending) {
515         $file = shift @pending;
516         next if $seen{ $file }++;
517
518         if (File::Spec->file_name_is_absolute($file) && -f $file) {
519             $modtime = (stat($file))[9];
520             print "    #   $file [$modtime]\n"
521                 if $DEP_DEBUG;
522         }
523         else {
524             $modtime = 0;
525             foreach my $dir ($srcdir, @$libdir) {
526                 $absfile = File::Spec->catfile($dir, $file);
527                 if (-f $absfile) {
528                     $modtime = (stat($absfile))[9];
529                     print "    #   $absfile [$modtime]\n"
530                         if $DEP_DEBUG;
531                     last;
532                 }
533             }
534         }
535         $maxtime = $modtime
536             if $modtime > $maxtime;
537
538         if ($deps = $depends->{ $file }) {
539             push(@pending, @$deps);
540             print "    #     depends on ", join(', ', @$deps), "\n"
541                 if $DEP_DEBUG;
542         }
543     }
544
545     return $maxtime;
546 }
547
548
549 #------------------------------------------------------------------------
550 # read_config($file)
551 #
552 # Handles reading of config file and/or command line arguments.
553 #------------------------------------------------------------------------
554
555 sub read_config {
556     my $file    = shift;
557     my $verbose = 0;
558     my $verbinc = sub {
559         my ($state, $var, $value) = @_;
560         $state->{ VARIABLE }->{ verbose } = $value ? ++$verbose : --$verbose;
561     };
562     my $config  = AppConfig->new(
563         { 
564             ERROR  => sub { die(@_, "\ntry `$NAME --help'\n") }
565         }, 
566         'help|h'      => { ACTION => \&help },
567         'src|s=s'     => { EXPAND => EXPAND_ALL },
568         'dest|d=s'    => { EXPAND => EXPAND_ALL },
569         'lib|l=s@'    => { EXPAND => EXPAND_ALL },
570         'cfg|c=s'     => { EXPAND => EXPAND_ALL, DEFAULT => '.' },
571         'verbose|v'   => { DEFAULT => 0, ACTION => $verbinc },
572         'recurse|r'   => { DEFAULT => 0 },
573         'nothing|n'   => { DEFAULT => 0 },
574         'preserve|p'  => { DEFAULT => 0 },
575         'absolute'    => { DEFAULT => 0 },
576         'relative'    => { DEFAULT => 0 },
577         'colour|color'=> { DEFAULT => 0 },
578         'summary'     => { DEFAULT => 0 },
579         'all|a'       => { DEFAULT => 0 },
580         'define=s%',
581         'suffix=s%',
582         'binmode=s',
583         'ignore=s@',
584         'copy=s@',
585         'accept=s@',
586         'depend=s%',
587         'depend_debug|depdbg',
588         'depend_file|depfile=s' => { EXPAND => EXPAND_ALL },
589         'template_module|module=s',
590         'template_anycase|anycase',
591         'template_encoding|encoding=s',
592         'template_eval_perl|eval_perl',
593         'template_load_perl|load_perl',
594         'template_interpolate|interpolate',
595         'template_pre_chomp|pre_chomp|prechomp',
596         'template_post_chomp|post_chomp|postchomp',
597         'template_trim|trim',
598         'template_pre_process|pre_process|preprocess=s@',
599         'template_post_process|post_process|postprocess=s@',
600         'template_process|process=s',
601         'template_wrapper|wrapper=s',
602         'template_recursion|recursion',
603         'template_expose_blocks|expose_blocks',
604         'template_default|default=s',
605         'template_error|error=s',
606         'template_debug|debug=s',
607         'template_start_tag|start_tag|starttag=s',
608         'template_end_tag|end_tag|endtag=s',
609         'template_tag_style|tag_style|tagstyle=s',
610         'template_compile_ext|compile_ext=s',
611         'template_compile_dir|compile_dir=s' => { EXPAND => EXPAND_ALL },
612         'template_plugin_base|plugin_base|pluginbase=s@' => { EXPAND => EXPAND_ALL },
613         'perl5lib|perllib=s@' => { EXPAND => EXPAND_ALL },
614     );
615
616     # add the 'file' option now that we have a $config object that we 
617     # can reference in a closure
618     $config->define(
619         'file|f=s@' => { 
620             EXPAND => EXPAND_ALL, 
621             ACTION => sub { 
622                 my ($state, $item, $file) = @_;
623                 $file = $state->cfg . "/$file" 
624                     unless $file =~ /^[\.\/]|(?:\w:)/;
625                 $config->file($file) }  
626         }
627     );
628
629     # process main config file, then command line args
630     $config->file($file) if -f $file;
631     $config->args();
632
633     $config;
634 }
635
636
637 sub ANSI_escape {
638     my $attr = shift;
639     my $text = join('', @_);
640     return join("\n",
641         map {
642             # look for an existing escape start sequence and add new
643             # attribute to it, otherwise add escape start/end sequences
644             s/ \e \[ ([1-9][\d;]*) m/\e[$1;${attr}m/gx
645                 ? $_
646                 : "\e[${attr}m" . $_ . "\e[0m";
647         }
648         split(/\n/, $text, -1)   # -1 prevents it from ignoring trailing fields
649     );
650 }
651
652 sub _red(@)    { ANSI_escape(31, @_) }
653 sub _green(@)  { ANSI_escape(32, @_) }
654 sub _yellow(@) { ANSI_escape(33, @_) }
655 sub _blue(@)   { ANSI_escape(34, @_) }
656 sub _white(@)  { @_ }                   # nullop
657
658
659 #------------------------------------------------------------------------
660 # write_config($file)
661 #
662 # Writes a sample configuration file to the filename specified.
663 #------------------------------------------------------------------------
664
665 sub write_config {
666     my $file = shift;
667
668     open(CONFIG, ">$file") || die "failed to create $file: $!\n";
669     print(CONFIG <<END_OF_CONFIG);
670 #------------------------------------------------------------------------
671 # sample .ttreerc file created automatically by $NAME version $VERSION
672 #
673 # This file originally written to $file
674 #
675 # For more information on the contents of this configuration file, see
676
677 #     perldoc ttree
678 #     ttree -h
679 #
680 #------------------------------------------------------------------------
681
682 # The most flexible way to use ttree is to create a separate directory 
683 # for configuration files and simply use the .ttreerc to tell ttree where
684 # it is.  
685 #
686 #     cfg = /path/to/ttree/config/directory
687
688 # print summary of what's going on 
689 verbose 
690
691 # recurse into any sub-directories and process files
692 recurse
693
694 # regexen of things that aren't templates and should be ignored
695 ignore = \\b(CVS|RCS)\\b
696 ignore = ^#
697
698 # ditto for things that should be copied rather than processed.
699 copy = \\.png\$ 
700 copy = \\.gif\$ 
701
702 # by default, everything not ignored or copied is accepted; add 'accept'
703 # lines if you want to filter further. e.g.
704 #
705 #    accept = \\.html\$
706 #    accept = \\.tt2\$
707
708 # options to rewrite files suffixes (htm => html, tt2 => html)
709 #
710 #    suffix htm=html
711 #    suffix tt2=html
712
713 # options to define dependencies between templates
714 #
715 #    depend *=header,footer,menu
716 #    depend index.html=mainpage,sidebar
717 #    depend menu=menuitem,menubar
718
719
720 #------------------------------------------------------------------------
721 # The following options usually relate to a particular project so 
722 # you'll probably want to put them in a separate configuration file 
723 # in the directory specified by the 'cfg' option and then invoke tree 
724 # using '-f' to tell it which configuration you want to use.
725 # However, there's nothing to stop you from adding default 'src',
726 # 'dest' or 'lib' options in the .ttreerc.  The 'src' and 'dest' options
727 # can be re-defined in another configuration file, but be aware that 'lib' 
728 # options accumulate so any 'lib' options defined in the .ttreerc will
729 # be applied every time you run ttree.
730 #------------------------------------------------------------------------
731 # # directory containing source page templates
732 # src = /path/to/your/source/page/templates
733 #
734 # # directory where output files should be written
735 # dest = /path/to/your/html/output/directory
736
737 # # additional directories of library templates
738 # lib = /first/path/to/your/library/templates
739 # lib = /second/path/to/your/library/templates
740
741 END_OF_CONFIG
742
743     close(CONFIG);
744     print "$file created.  Please edit accordingly and re-run $NAME\n"; 
745 }
746
747
748 #------------------------------------------------------------------------
749 # help()
750 #
751 # Prints help message and exits.
752 #------------------------------------------------------------------------
753
754 sub help {
755     print<<END_OF_HELP;
756 $NAME $VERSION (Template Toolkit version $Template::VERSION)
757
758 usage: $NAME [options] [files]
759
760 Options:
761    -a      (--all)          Process all files, regardless of modification
762    -r      (--recurse)      Recurse into sub-directories
763    -p      (--preserve)     Preserve file ownership and permission
764    -n      (--nothing)      Do nothing, just print summary (enables -v)
765    -v      (--verbose)      Verbose mode. Use twice for more verbosity: -v -v
766    -h      (--help)         This help
767    -s DIR  (--src=DIR)      Source directory
768    -d DIR  (--dest=DIR)     Destination directory
769    -c DIR  (--cfg=DIR)      Location of configuration files
770    -l DIR  (--lib=DIR)      Library directory (INCLUDE_PATH)  (multiple)
771    -f FILE (--file=FILE)    Read named configuration file     (multiple)
772
773 Display options:
774    --colour / --color       Enable colo(u)rful verbose output.
775    --summary                Show processing summary.
776
777 File search specifications (all may appear multiple times):
778    --ignore=REGEX           Ignore files matching REGEX
779    --copy=REGEX             Copy files matching REGEX
780    --accept=REGEX           Process only files matching REGEX 
781
782 File Dependencies Options:
783    --depend foo=bar,baz     Specify that 'foo' depends on 'bar' and 'baz'.
784    --depend_file FILE       Read file dependancies from FILE.
785    --depend_debug           Enable debugging for dependencies
786
787 File suffix rewriting (may appear multiple times)
788    --suffix old=new         Change any '.old' suffix to '.new'
789
790 File encoding options
791    --binmode=value          Set binary mode of output files
792    --encoding=value         Set encoding of input files
793
794 Additional options to set Template Toolkit configuration items:
795    --define var=value       Define template variable
796    --interpolate            Interpolate '\$var' references in text
797    --anycase                Accept directive keywords in any case.
798    --pre_chomp              Chomp leading whitespace 
799    --post_chomp             Chomp trailing whitespace
800    --trim                   Trim blank lines around template blocks
801    --eval_perl              Evaluate [% PERL %] ... [% END %] code blocks
802    --load_perl              Load regular Perl modules via USE directive
803    --absolute               Enable the ABSOLUTE option
804    --relative               Enable the RELATIVE option
805    --pre_process=TEMPLATE   Process TEMPLATE before each main template
806    --post_process=TEMPLATE  Process TEMPLATE after each main template
807    --process=TEMPLATE       Process TEMPLATE instead of main template
808    --wrapper=TEMPLATE       Process TEMPLATE wrapper around main template
809    --default=TEMPLATE       Use TEMPLATE as default
810    --error=TEMPLATE         Use TEMPLATE to handle errors
811    --debug=STRING           Set TT DEBUG option to STRING
812    --start_tag=STRING       STRING defines start of directive tag
813    --end_tag=STRING         STRING defined end of directive tag
814    --tag_style=STYLE        Use pre-defined tag STYLE    
815    --plugin_base=PACKAGE    Base PACKAGE for plugins            
816    --compile_ext=STRING     File extension for compiled template files
817    --compile_dir=DIR        Directory for compiled template files
818    --perl5lib=DIR           Specify additional Perl library directories
819    --template_module=MODULE Specify alternate Template module
820
821 See 'perldoc ttree' for further information.  
822
823 END_OF_HELP
824
825     exit(0);
826 }
827
828 __END__
829
830
831 #------------------------------------------------------------------------
832 # IMPORTANT NOTE
833 #   This documentation is generated automatically from source
834 #   templates.  Any changes you make here may be lost.
835
836 #   The 'docsrc' documentation source bundle is available for download
837 #   from http://www.template-toolkit.org/docs.html and contains all
838 #   the source templates, XML files, scripts, etc., from which the
839 #   documentation for the Template Toolkit is built.
840 #------------------------------------------------------------------------
841
842 =head1 NAME
843
844 Template::Tools::ttree - Process entire directory trees of templates
845
846 =head1 SYNOPSIS
847
848     ttree [options] [files]
849
850 =head1 DESCRIPTION
851
852 The F<ttree> script is used to process entire directory trees containing
853 template files.  The resulting output from processing each file is then 
854 written to a corresponding file in a destination directory.  The script
855 compares the modification times of source and destination files (where
856 they already exist) and processes only those files that have been modified.
857 In other words, it is the equivalent of 'make' for the Template Toolkit.
858
859 It supports a number of options which can be used to configure
860 behaviour, define locations and set Template Toolkit options.  The
861 script first reads the F<.ttreerc> configuration file in the HOME
862 directory, or an alternative file specified in the TTREERC environment
863 variable.  Then, it processes any command line arguments, including
864 any additional configuration files specified via the C<-f> (file)
865 option.
866
867 =head2 The F<.ttreerc> Configuration File
868
869 When you run F<ttree> for the first time it will ask you if you want
870 it to create a F<.ttreerc> file for you.  This will be created in your
871 home directory.
872
873     $ ttree
874     Do you want me to create a sample '.ttreerc' file for you?
875     (file: /home/abw/.ttreerc)   [y/n]: y
876     /home/abw/.ttreerc created.  Please edit accordingly and re-run ttree
877
878 The purpose of this file is to set any I<global> configuration options
879 that you want applied I<every> time F<ttree> is run.  For example, you
880 can use the C<ignore> and C<copy> option to provide regular expressions
881 that specify which files should be ignored and which should be copied 
882 rather than being processed as templates.  You may also want to set 
883 flags like C<verbose> and C<recurse> according to your preference.
884
885 A minimal F<.ttreerc>:
886
887     # ignore these files
888     ignore = \b(CVS|RCS)\b
889     ignore = ^#
890     ignore = ~$
891
892     # copy these files
893     copy   = \.(gif|png|jpg|pdf)$ 
894
895     # recurse into directories
896     recurse
897
898     # provide info about what's going on
899     verbose
900
901 In most cases, you'll want to create a different F<ttree> configuration 
902 file for each project you're working on.  The C<cfg> option allows you
903 to specify a directory where F<ttree> can find further configuration 
904 files.
905
906     cfg = /home/abw/.ttree
907
908 The C<-f> command line option can be used to specify which configuration
909 file should be used.  You can specify a filename using an absolute or 
910 relative path:
911
912     $ ttree -f /home/abw/web/example/etc/ttree.cfg
913     $ ttree -f ./etc/ttree.cfg
914     $ ttree -f ../etc/ttree.cfg
915
916 If the configuration file does not begin with C</> or C<.> or something
917 that looks like a MS-DOS absolute path (e.g. C<C:\\etc\\ttree.cfg>) then
918 F<ttree> will look for it in the directory specified by the C<cfg> option.
919
920     $ ttree -f test1          # /home/abw/.ttree/test1
921
922 The C<cfg> option can only be used in the F<.ttreerc> file.  All the
923 other options can be used in the F<.ttreerc> or any other F<ttree>
924 configuration file.  They can all also be specified as command line
925 options.
926
927 Remember that F<.ttreerc> is always processed I<before> any
928 configuration file specified with the C<-f> option.  Certain options
929 like C<lib> can be used any number of times and accumulate their values.
930
931 For example, consider the following configuration files:
932
933 F</home/abw/.ttreerc>:
934
935     cfg = /home/abw/.ttree
936     lib = /usr/local/tt2/templates
937
938 F</home/abw/.ttree/myconfig>:
939
940     lib = /home/abw/web/example/templates/lib
941
942 When F<ttree> is invoked as follows:
943
944     $ ttree -f myconfig
945
946 the C<lib> option will be set to the following directories:
947
948     /usr/local/tt2/templates
949     /home/abw/web/example/templates/lib
950
951 Any templates located under F</usr/local/tt2/templates> will be used
952 in preference to those located under
953 F</home/abw/web/example/templates/lib>.  This may be what you want,
954 but then again, it might not.  For this reason, it is good practice to
955 keep the F<.ttreerc> as simple as possible and use different
956 configuration files for each F<ttree> project.
957
958 =head2 Directory Options
959
960 The C<src> option is used to define the directory containing the
961 source templates to be processed.  It can be provided as a command
962 line option or in a configuration file as shown here:
963
964     src = /home/abw/web/example/templates/src
965
966 Each template in this directory typically corresponds to a single
967 web page or other document. 
968
969 The C<dest> option is used to specify the destination directory for the
970 generated output.
971
972     dest = /home/abw/web/example/html
973
974 The C<lib> option is used to define one or more directories containing
975 additional library templates.  These templates are not documents in
976 their own right and typically comprise of smaller, modular components
977 like headers, footers and menus that are incorporated into pages templates.
978
979     lib = /home/abw/web/example/templates/lib
980     lib = /usr/local/tt2/templates
981
982 The C<lib> option can be used repeatedly to add further directories to
983 the search path.
984
985 A list of templates can be passed to F<ttree> as command line arguments.
986
987     $ ttree foo.html bar.html
988
989 It looks for these templates in the C<src> directory and processes them
990 through the Template Toolkit, using any additional template components
991 from the C<lib> directories.  The generated output is then written to 
992 the corresponding file in the C<dest> directory.
993
994 If F<ttree> is invoked without explicitly specifying any templates
995 to be processed then it will process every file in the C<src> directory.
996 If the C<-r> (recurse) option is set then it will additionally iterate
997 down through sub-directories and process and other template files it finds
998 therein.
999
1000     $ ttree -r
1001
1002 If a template has been processed previously, F<ttree> will compare the
1003 modification times of the source and destination files.  If the source
1004 template (or one it is dependant on) has not been modified more
1005 recently than the generated output file then F<ttree> will not process
1006 it.  The F<-a> (all) option can be used to force F<ttree> to process
1007 all files regardless of modification time.
1008
1009     $ tree -a
1010
1011 Any templates explicitly named as command line argument are always
1012 processed and the modification time checking is bypassed.
1013
1014 =head2 File Options
1015
1016 The C<ignore>, C<copy> and C<accept> options are used to specify Perl
1017 regexen to filter file names.  Files that match any of the C<ignore>
1018 options will not be processed.  Remaining files that match any of the
1019 C<copy> regexen will be copied to the destination directory.  Remaining
1020 files that then match any of the C<accept> criteria are then processed
1021 via the Template Toolkit.  If no C<accept> parameter is specified then 
1022 all files will be accepted for processing if not already copied or
1023 ignored.
1024
1025     # ignore these files
1026     ignore = \b(CVS|RCS)\b
1027     ignore = ^#
1028     ignore = ~$
1029
1030     # copy these files
1031     copy   = \.(gif|png|jpg|pdf)$ 
1032
1033     # accept only .tt2 templates
1034     accept = \.tt2$
1035
1036 The C<suffix> option is used to define mappings between the file
1037 extensions for source templates and the generated output files.  The
1038 following example specifies that source templates with a C<.tt2>
1039 suffix should be output as C<.html> files:
1040
1041     suffix tt2=html
1042
1043 Or on the command line, 
1044
1045     --suffix tt2=html
1046
1047 You can provide any number of different suffix mappings by repeating 
1048 this option.
1049
1050 The C<binmode> option is used to set the encoding of the output file.
1051 For example use C<--binmode=:utf8> to set the output format to unicode.
1052
1053 =head2 Template Dependencies
1054
1055 The C<depend> and C<depend_file> options allow you to specify
1056 how any given template file depends on another file or group of files. 
1057 The C<depend> option is used to express a single dependency.
1058
1059   $ ttree --depend foo=bar,baz
1060
1061 This command line example shows the C<--depend> option being used to
1062 specify that the F<foo> file is dependant on the F<bar> and F<baz>
1063 templates.  This option can be used many time on the command line:
1064
1065   $ ttree --depend foo=bar,baz --depend crash=bang,wallop
1066
1067 or in a configuration file:
1068
1069   depend foo=bar,baz
1070   depend crash=bang,wallop
1071
1072 The file appearing on the left of the C<=> is specified relative to
1073 the C<src> or C<lib> directories.  The file(s) appearing on the right
1074 can be specified relative to any of these directories or as absolute
1075 file paths.
1076
1077 For example:
1078
1079   $ ttree --depend foo=bar,/tmp/baz
1080
1081 To define a dependency that applies to all files, use C<*> on the 
1082 left of the C<=>.
1083
1084   $ ttree --depend *=header,footer
1085
1086 or in a configuration file:
1087
1088   depend *=header,footer
1089
1090 Any templates that are defined in the C<pre_process>, C<post_process>,
1091 C<process> or C<wrapper> options will automatically be added to the
1092 list of global dependencies that apply to all templates.
1093
1094 The C<depend_file> option can be used to specify a file that contains
1095 dependency information.  
1096
1097     $ ttree --depend_file=/home/abw/web/example/etc/ttree.dep
1098
1099 Here is an example of a dependency file:
1100
1101    # This is a comment. It is ignored.
1102   
1103    index.html: header footer menubar 
1104   
1105    header: titlebar hotlinks
1106   
1107    menubar: menuitem
1108   
1109    # spanning multiple lines with the backslash
1110    another.html: header footer menubar \
1111    sidebar searchform
1112
1113 Lines beginning with the C<#> character are comments and are ignored.
1114 Blank lines are also ignored.  All other lines should provide a
1115 filename followed by a colon and then a list of dependant files
1116 separated by whitespace, commas or both.  Whitespace around the colon
1117 is also optional.  Lines ending in the C<\> character are continued
1118 onto the following line.
1119
1120 Files that contain spaces can be quoted. That is only necessary
1121 for files after the colon (':'). The file before the colon may be
1122 quoted if it contains a colon. 
1123
1124 As with the command line options, the C<*> character can be used
1125 as a wildcard to specify a dependency for all templates.
1126
1127     * : config,header
1128
1129 =head2 Template Toolkit Options
1130
1131 F<ttree> also provides access to the usual range of Template Toolkit
1132 options.  For example, the C<--pre_chomp> and C<--post_chomp> F<ttree>
1133 options correspond to the C<PRE_CHOMP> and C<POST_CHOMP> options.
1134
1135 Run C<ttree -h> for a summary of the options available.
1136
1137 =head1 AUTHORS
1138
1139 Andy Wardley E<lt>abw@andywardley.comE<gt>
1140
1141 L<http://www.andywardley.com/|http://www.andywardley.com/>
1142
1143 With contributions from Dylan William Hardison (support for
1144 dependencies), Bryce Harrington (C<absolute> and C<relative> options),
1145 Mark Anderson (C<suffix> and C<debug> options), Harald Joerg and Leon
1146 Brocard who gets everywhere, it seems.
1147
1148 =head1 VERSION
1149
1150 2.68, distributed as part of the
1151 Template Toolkit version 2.19, released on 27 April 2007.
1152
1153 =head1 COPYRIGHT
1154
1155   Copyright (C) 1996-2007 Andy Wardley.  All Rights Reserved.
1156
1157
1158 This module is free software; you can redistribute it and/or
1159 modify it under the same terms as Perl itself.
1160
1161 =head1 SEE ALSO
1162
1163 L<tpage|Template::Tools::tpage>
1164