ssic: Set __SSIC__ SSI variable.
[ssic.git] / bin / ssic
1 #!/usr/bin/perl
2
3 =head1 NAME
4
5 ssic - Server Side Includes Compiler
6
7 =cut
8
9 use strict;
10 use warnings;
11
12 use Getopt::Long;
13 use CGI::SSI;
14
15 my $VERSION;
16 $VERSION = '1.0.0';
17
18 sub main
19 {
20         my %opts;
21         my $ssi;
22         my $input;
23         my $output;
24
25         $SIG{'__WARN__'} = \&warning;
26
27         Getopt::Long::Configure("no_ignore_case", "bundling", "gnu_compat",
28                 "no_getopt_compat");
29         if (not GetOptions(\%opts,
30                         "o=s",
31                         "D=s%",
32                         "I=s",
33                         "h|help",
34                         "V|version",
35                 )) {
36                 usage(*STDERR);
37                 exit(4);
38         }
39
40         if (exists($opts{'h'})) {
41                 help(*STDOUT);
42                 exit(0);
43         }
44         if (exists($opts{'V'})) {
45                 version(*STDOUT);
46                 exit(0);
47         }
48
49         if ($#ARGV lt 0) {
50                 error(4, "No input files\n");
51         }
52
53         $ssi = init_compiler($opts{'D'}, $opts{'I'});
54
55         if (exists($opts{'o'})) {
56                 if ($#ARGV gt 0) {
57                         error(4, "Cannot specify -o with multiple files\n");
58                 }
59                 compile($ssi, $ARGV[0], $opts{'o'});
60         } else {
61                 for $input (@ARGV) {
62                         $output = $input;
63                         $output =~ s/\.[^.]+$/.html/;
64                         compile($ssi, $input, $output);
65                 }
66         }
67
68         undef $ssi;
69 }
70
71 sub usage
72 {
73         my ($fh) = @_;
74
75         printf($fh "Usage: %s [-o <output>] <input> ...\n", $0);
76 }
77
78 sub help
79 {
80         my ($fh) = @_;
81
82         usage($fh);
83         print("Options:\n");
84         print("  -o <output>        Place the output into <output>\n");
85         print("  -I <directory>     Set the document root to <directory>\n");
86         print("  -D <name>=<value>  Set the variable <name> to <value>\n");
87         print("  -h, --help         Display this information\n");
88         print("  -V, --version      Display compiler version information\n");
89 }
90
91 sub version
92 {
93         my ($fh) = @_;
94
95         printf("ssic %s\n", $VERSION);
96         print("Copyright (C) 2013 Patrick \"P. J.\" McDermott\n");
97         print("License GPLv3+: GNU GPL version 3 or later " .
98                 "<http://gnu.org/licenses/gpl.html>.\n");
99         print("This is free software: you are free to change and " .
100                 "redistribute it.\n");
101         print("There is NO WARRANTY, to the extent permitted by law.\n");
102 }
103
104 sub warning
105 {
106         my ($fmt, @args) = @_;
107
108         printf(STDERR "ssic: Warning: " . $fmt, @args);
109 }
110
111 sub error
112 {
113         my ($status, $fmt, @args) = @_;
114
115         printf(STDERR "ssic: Error: " . $fmt, @args);
116         exit($status);
117 }
118
119 sub init_compiler
120 {
121         my ($vars, $root) = @_;
122         my $ssi;
123         my $var_name;
124         my $var_value;
125
126         %ENV = (
127                 "DOCUMENT_ROOT" => $root,
128         );
129
130         $CGI::SSI::DEBUG = 0;
131         $ssi = CGI::SSI->new();
132
133         $ssi->set("__SSIC__" => 1);
134         $ssi->set("DOCUMENT_ROOT" => $root);
135
136         while (($var_name, $var_value) = each(%{$vars})) {
137                 $ssi->set($var_name => $var_value);
138         }
139
140         return $ssi;
141 }
142
143 sub compile
144 {
145         my ($ssi, $input, $output) = @_;
146         my $input_fh;
147         my $input_abs;
148         my $output_fh;
149
150         if ($input eq $output and $input ne "-") {
151                 warning("Input and output files are equal\n");
152         }
153
154         if ($input eq "-") {
155                 $input_fh = *STDIN;
156                 $input_abs = File::Spec->rel2abs(".");
157         } else {
158                 if (not open($input_fh, "<", $input)) {
159                         error(4, "%s: %s\n", $input, $!);
160                 }
161                 $input_abs = File::Spec->rel2abs($input);
162         }
163         if ($output eq "-") {
164                 $output_fh = *STDOUT;
165         } else {
166                 if (not open($output_fh, ">", $output . "~")) {
167                         error(4, "%s: %s\n", $output . "~", $!);
168                 }
169         }
170
171         # CGI::SSI uses SCRIPT_FILENAME to determine the value of LAST_MODIFIED.
172         $ENV{"DOCUMENT_NAME"} = $input;
173         $ENV{"DOCUMENT_URI"} = $input;
174         $ENV{"SCRIPT_FILENAME"} = $input_abs;
175
176         # Reset config tags to default values.
177         $ssi->config("errmsg",
178                 "[an error occurred while processing this directive]");
179         $ssi->config("sizefmt", "abbrev");
180         $ssi->config("timefmt", undef);
181
182         $ssi->set("DOCUMENT_NAME" => $input);
183         $ssi->set("DOCUMENT_URI" => $input);
184
185         print($output_fh $ssi->process(<$input_fh>));
186
187         if ($input ne "-") {
188                 close($input_fh);
189         }
190         if ($output ne "-") {
191                 close($output_fh);
192                 if (not rename($output . "~", $output)) {
193                         error(4, "%s: %s\n", $output, $!);
194                 }
195         }
196 }
197
198 main();
199
200 __END__
201
202 =head1 SYNOPSIS
203
204 B<ssic>
205 [B<-o> I<output>]
206 [B<-I> I<directory>] 
207 [B<-D> I<name>=I<value> ...]
208 I<input> ...
209
210 =head1 DESCRIPTION
211
212 B<ssic> processes HTML documents with SSI directives formatted as SGML comments.
213 It can be used to process documents without an HTTP server for local browsing or
214 to generate static HTML documents to be efficiently served by an HTTP server.
215 Documents could even be preprocessed, e.g. by a Markdown processor, before being
216 parsed with ssic.
217
218 =head1 OPTIONS
219
220 =over 4
221
222 =item B<-o> I<output>
223
224 Place the output into I<output>.
225
226 =item B<-I> I<directory>
227
228 Set the document root to I<directory>.
229
230 =item B<-D> I<name>=I<value>
231
232 Set the variable I<name> to I<value>.
233
234 =item B<-h>, B<--help>
235
236 Display help information.
237
238 =item B<-V>, B<--version>
239
240 Display compiler version information.
241
242 =back
243
244 =head1 COPYRIGHT
245
246 Copyright (C) 2013  Patrick "P. J." McDermott
247
248 This program is free software: you can redistribute it and/or modify
249 it under the terms of the GNU General Public License as published by
250 the Free Software Foundation, either version 3 of the License, or
251 (at your option) any later version.
252
253 This program is distributed in the hope that it will be useful,
254 but WITHOUT ANY WARRANTY; without even the implied warranty of
255 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
256 GNU General Public License for more details.
257
258 You should have received a copy of the GNU General Public License
259 along with this program.  If not, see <http://www.gnu.org/licenses/>.
260
261 =head1 SEE ALSO
262
263 L<CGI::SSI(3)>
264
265 NCSA HTTPd SSI documentation:
266 L<http://web.archive.org/web/19971210170837/http://hoohoo.ncsa.uiuc.edu/docs/tutorials/includes.html>
267
268 Apache HTTPd mod_include documentation:
269 L<http://httpd.apache.org/docs/current/mod/mod_include.html>
270
271 =cut