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