Declare exsl as an extension element prefix for CSS
[projectmallard:mallard-site-tool.git] / mallard-site-tool.in
1 #!/bin/sh
2 # -*- indent-tabs-mode: nil -*-
3 # mallard-site-tool  Build web sites from collections of Mallard documents
4 # Copyright (C) 2006-2010 Shaun McCance <shaunm@gnome.org>
5 #
6 # This program is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; either version 2 of the License, or
9 # (at your option) any later version.
10 #
11 # This program is distributed in the hope that it will be useful, but
12 # WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 # General Public License for more details.
15 #
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
19
20 progname=`echo "$0" | sed 's%^.*/%%'`
21
22 PROGRAM=mallard-site-tool
23 VERSION=@VERSION@
24 prefix=@prefix@
25 datarootdir=@datarootdir@
26 datadir=@datadir@
27
28 xsl_path="@XSL_PATH@"
29 xsl_mal_cache="@XSL_MAL_CACHE@"
30
31 # This is important to make sure string manipulation is handled
32 # byte-by-byte.
33 export LANG=C
34
35 XSL_CACHE='
36 <xsl:stylesheet
37   xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
38   xmlns:cache="http://projectmallard.org/cache/1.0/"
39   xmlns:site="http://projectmallard.org/site/1.0/"
40   xmlns:mal="http://projectmallard.org/1.0/"
41   version="1.0">
42 <xsl:import href="'"$xsl_mal_cache"'"/>
43 <xsl:template name="mal.cache.id">
44   <xsl:param name="node" select="."/>
45   <xsl:param name="node_in"/>
46   <xsl:choose>
47     <xsl:when test="not($node/@id)"/>
48     <xsl:when test="$node/self::mal:page">
49       <xsl:attribute name="id">
50         <xsl:value-of select="$node_in/@site:dir"/>
51         <xsl:value-of select="$node/@id"/>
52       </xsl:attribute>
53     </xsl:when>
54     <xsl:otherwise>
55       <xsl:attribute name="id">
56         <xsl:value-of select="$node_in/@site:dir"/>
57         <xsl:value-of select="ancestor::mal:page[1]/@id"/>
58         <xsl:text>#</xsl:text>
59         <xsl:value-of select="@id"/>
60       </xsl:attribute>
61     </xsl:otherwise>
62   </xsl:choose>
63   <xsl:copy-of select="$node_in/@site:dir"/>
64 </xsl:template>
65 <xsl:template name="mal.cache.xref">
66   <xsl:param name="node" select="."/>
67   <xsl:param name="node_in"/>
68   <xsl:attribute name="xref">
69     <xsl:choose>
70       <xsl:when test="starts-with($node/@xref, '\''/'\'')">
71         <xsl:value-of select="$node/@xref"/>
72       </xsl:when>
73       <xsl:when test="starts-with($node/@xref, '\''#'\'')">
74         <xsl:value-of select="$node/@xref"/>
75       </xsl:when>
76       <xsl:otherwise>
77         <xsl:value-of select="$node_in/@site:dir"/>
78         <xsl:value-of select="$node/@xref"/>
79       </xsl:otherwise>
80     </xsl:choose>
81   </xsl:attribute>
82 </xsl:template>
83 </xsl:stylesheet>'
84
85 XSL_MALLARD_MEDIA='
86 <xsl:stylesheet
87   xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
88   xmlns:mal="http://projectmallard.org/1.0/"
89   version="1.0">
90 <xsl:output method="text" encoding="utf-8"/>
91 <xsl:template match="/">
92   <xsl:for-each select="//mal:media">
93     <xsl:value-of select="@src"/>
94     <xsl:text>&#x000A;</xsl:text>
95   </xsl:for-each>
96 </xsl:template>
97 </xsl:stylesheet>'
98
99 XSL_GET_ROOT='
100 <xsl:stylesheet
101   xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
102   xmlns:site="http://projectmallard.org/site/1.0/"
103   version="1.0">
104 <xsl:output method="text" encoding="utf-8"/>
105 <xsl:template match="/site:site">
106 <xsl:value-of select="site:link[@type='\'site-root\'']/@href"/>
107 </xsl:template>
108 </xsl:stylesheet>'
109
110 XSL_GET_CUSTOM='
111 <xsl:stylesheet
112   xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
113   xmlns:site="http://projectmallard.org/site/1.0/"
114   version="1.0">
115 <xsl:output method="text" encoding="utf-8"/>
116 <xsl:template match="/site:site">
117 <xsl:value-of select="site:link[@type='\'site-custom-xsl\'']/@href"/>
118 </xsl:template>
119 </xsl:stylesheet>'
120
121 error() {
122     echo "$progname: $1" 1>&2;
123     exit 1;
124 }
125
126 print_help() {
127     cat <<EOF
128 Usage: $progname <COMMAND> [OPTIONS] FILES...
129 Process a Mallard site.
130
131 COMMAND is one of:
132   cache        generate a Mallard site cache file
133   css          generate the CSS files
134   media        copy media to the output directory
135   html         convert the site to HTML
136   help         display this help and exit
137 EOF
138 }
139
140 print_help_html() {
141     format="$1"
142     upformat=`echo $format | tr a-z A-Z`
143     cat <<EOF
144 Usage: $progname $format [OPTIONS] FILE
145 Convert FILE into $upformat.
146
147   -o, --output=DIR                the output directory
148   -s, --site=SITE                 base name of the site file
149 EOF
150 }
151
152 print_help_cache() {
153     cat <<EOF
154 Usage: $progname cache [OPTIONS]
155 Create a Mallard site cache file.
156
157   -s, --site=SITE                 base name of the site file
158 EOF
159 }
160
161 print_help_css() {
162     cat <<EOF
163 Usage: $progname css [OPTIONS]
164 Create the CSS files for a site.
165
166   -o, --output=DIR                the output directory
167   -s, --site=SITE                 base name of the site file
168 EOF
169 }
170
171 print_help_media() {
172     cat <<EOF
173 Usage: $progname media [OPTIONS]
174 Copy media to the output directory.
175
176   -o, --output=DIR                the output directory
177   -s, --site=SITE                 base name of the site file
178 EOF
179 }
180
181 echo_verbose() {
182     if [ "x$site_verbose" = "x1" ]; then
183         echo "$1"
184     fi
185 }
186
187 mkdir_p() {
188     __dir__='';
189     echo $1 | sed -e 's/\//\n/g' | while read d; do
190         __dir__="$__dir__$d/"
191         if [ ! -d "$__dir__" ]; then
192             mkdir "$__dir__" || error "Could not create directory"
193         fi
194     done || exit 1;
195 }
196
197 urlencode() {
198     echo "$1" | awk --posix '
199 BEGIN {
200   for (i = 1; i <= 256; i++) {
201     bytes[sprintf("%c", i)] = i;
202   }
203 }
204 {
205   for (i = 1; i <= length($0); i++) {
206     c = substr($0, i, 1);
207     if (c ~ /[a-zA-Z0-9:_\.\-\/]/)
208       printf("%s", c);
209     else
210       printf("%%%02X", bytes[c]);
211   }
212 }
213 '
214 }
215
216 locate_site_file() {
217     site_directory=`pwd`
218     while [ "x${site_directory}" != "x" -a ! -f "${site_directory}/${site_site}.site" ]; do
219         if [ "x${site_directory}" = "x/" ]; then
220             error "Could not locate ${site_site}.site"
221         else
222             site_directory=`dirname "$site_directory"`
223         fi
224     done
225     site_directory=$( (cd "$site_directory" && pwd) )
226     site_file="${site_directory}/${site_site}.site"
227     if [ ! -f "$site_file" ]; then
228         error "Could not locate ${site_site}.site"
229     fi
230 }
231
232 create_cache() {
233     longopts='
234       -lsite:
235       -lverbose
236       -lhelp
237     ';
238     options=`getopt -qn$progname $longopts -- s:vh "$@"`
239     if [ "$?" != "0" ]; then print_help_cache 1>&2; exit 1; fi
240     eval set -- "$options";
241     while [ "$1" != "--" ]; do
242         case "$1" in
243             -s | --site)
244                 site_site="$2";;
245             -v | --verbose)
246                 site_verbose=1;;
247             -h | --help)
248                 print_help_cache
249                 exit 0;;
250             --)
251                 print_help_cache 1>&2
252                 exit 1;;
253         esac
254         shift
255     done
256     shift
257     locate_site_file
258     site_cache="${site_directory}/${site_site}.cache"
259     echo_verbose "Creating ${site_directory}/${site_site}.cache.in"
260     (
261         echo '<cache:cache xmlns:cache="http://projectmallard.org/cache/1.0/"'
262         echo '             xmlns:site="http://projectmallard.org/site/1.0/"'
263         echo '             xmlns="http://projectmallard.org/1.0/">'
264         find "$site_directory" -name '*.page' | while read page; do
265             page_esc=$(urlencode "$page" | sed -e 's/\&/\&amp;/g' -e 's/</\&lt;/g' -e "s/'/\&apos;/g")
266                   page_dir=$(urlencode $(dirname $(echo "$page" | cut -c${#site_directory}- | cut -c2-)))
267                   if [ "x${page_dir}" != "x/" ]; then
268                       page_dir="$page_dir/"
269                   fi
270                   echo "<page cache:href='file://$page_esc' site:dir='${page_dir}'/>"
271         done
272         echo '</cache:cache>'
273     ) > "${site_cache}.in"
274     echo_verbose "Creating ${site_directory}/${site_site}.cache"
275     echo "$XSL_CACHE" | xsltproc --xinclude - "${site_cache}.in" | xmllint --format - > "$site_cache"
276 }
277
278 create_css_xsl() {
279     echo '
280 <xsl:stylesheet
281   xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
282   xmlns:exsl="http://exslt.org/common"
283   xmlns:cache="http://projectmallard.org/cache/1.0/"
284   xmlns:mal="http://projectmallard.org/1.0/"
285   extension-element-prefixes="exsl"
286   version="1.0">
287 <xsl:import href="'"$xsl_path"'/gettext/gettext.xsl"/>
288 <xsl:import href="'"$xsl_path"'/common/color.xsl"/>
289 <xsl:import href="'"$xsl_path"'/common/icons.xsl"/>
290 <xsl:import href="'"$xsl_path"'/common/html.xsl"/>
291 <xsl:import href="'"$xsl_path"'/mallard/html/mal2html-page.xsl"/>'
292 if [ "x$site_custom" != "x" ]; then
293     echo "<xsl:include href='${site_directory}/${site_custom}'/>"
294 fi;
295 echo '
296 <xsl:output method="text"/>
297 <xsl:template match="/">
298   <xsl:for-each select="/cache:cache/mal:page[
299       (@xml:lang and not(@xml:lang = preceding-sibling::mal:page/@xml:lang)) or
300       (not(@xml:lang) and not(preceding-sibling::mal:page[not(@xml:lang)])) ]">
301     <xsl:variable name="locale">
302       <xsl:choose>
303         <xsl:when test="@xml:lang">
304           <xsl:value-of select="@xml:lang"/>
305         </xsl:when>
306         <xsl:otherwise>
307           <xsl:text>C</xsl:text>
308         </xsl:otherwise>
309       </xsl:choose>
310     </xsl:variable>
311     <xsl:message>
312      <xsl:value-of select="$locale"/>
313     </xsl:message>
314     <exsl:document href="{$locale}.css" method="text">
315       <xsl:for-each select="document(@cache:href)">
316         <xsl:call-template name="html.css.content"/>
317       </xsl:for-each>
318     </exsl:document>
319   </xsl:for-each>
320 </xsl:template>'
321     echo '</xsl:stylesheet>'
322 }
323
324 create_css() {
325     longopts='
326       -lsite:
327       -loutput:
328       -lverbose
329       -lhelp
330     ';
331     options=`getopt -qn$progname $longopts -- s:o:vh "$@"`
332     if [ "$?" != "0" ]; then print_help_css 1>&2; exit 1; fi
333     eval set -- "$options";
334     while [ "$1" != "--" ]; do
335         case "$1" in
336             -s | --site)
337                 site_site="$2";;
338             -o | --output)
339                 site_output_dir=$( (cd "$2" && pwd) );;
340             -v | --verbose)
341                 site_verbose=1;;
342             -h | --help)
343                 print_help_css
344                 exit 0;;
345             --)
346                 print_help_css 1>&2
347                 exit 1;;
348         esac
349         shift
350     done
351     shift
352     locate_site_file
353     if [ "x$site_output_dir" = "x" ]; then
354         site_output_dir="$site_directory"
355     fi
356     site_root=`echo "$XSL_GET_ROOT" | xsltproc - "$site_file"`
357     site_custom=`echo "$XSL_GET_CUSTOM" | xsltproc - "$site_file"`
358     create_css_xsl | \
359         xsltproc -o "${site_output_dir}/" - "${site_directory}/${site_site}.cache" 2>&1 | \
360         while read lc; do
361         if [ "x$site_verbose" = "x1" ]; then
362             echo "Creating ${site_output_dir}/${lc}.css"
363         fi
364     done
365 }
366
367 copy_media() {
368     longopts='
369       -lsite:
370       -loutput:
371       -lverbose
372       -lhelp
373     ';
374     options=`getopt -qn$progname $longopts -- s:o:vh "$@"`
375     if [ "$?" != "0" ]; then print_help_media 1>&2; exit 1; fi
376     eval set -- "$options";
377     while [ "$1" != "--" ]; do
378         case "$1" in
379             -s | --site)
380                 site_site="$2";;
381             -o | --output)
382                 site_output_dir=$( (cd "$2" && pwd) );;
383             -v | --verbose)
384                 site_verbose=1;;
385             -h | --help)
386                 print_help_media
387                 exit 0;;
388             --)
389                 print_help_media 1>&2
390                 exit 1;;
391         esac
392         shift
393     done
394     shift
395     locate_site_file
396     if [ "x$site_output_dir" = "x" ]; then
397         error "No output directory given"
398     fi
399     for page in $(find $(pwd) -name '*.page'); do
400               page_dir=$(dirname $(echo "$page" | cut -c${#site_directory}- | cut -c2-))/
401         page_base=$(basename "$page" .page)
402         page_outdir="$site_output_dir$page_dir"
403         mkdir_p $(dirname "$page_output")
404         echo "$XSL_MALLARD_MEDIA" | xsltproc --xinclude - "$page" | while read media; do
405             media_output="$page_outdir$media"
406             echo_verbose "Creating $media_output"
407             cp ".$page_dir$media" "$media_output"
408         done
409     done
410 }
411
412 copy_icons() {
413     if [ "x$doc_copy_icons" = "x1" ]; then
414         if [ "x$doc_icons_admon_path" = "x" ]; then
415             p="$pkgdatadir/icons/hicolor/"
416             if [ "x$doc_icons_admon_size" != "x" ]; then
417                 doc_icons_admon_path="${p}${doc_icons_admon_size}x${doc_icons_admon_size}/status"
418             else
419                 doc_icons_admon_path="${p}48x48/status"
420             fi
421             unset p
422         fi
423         for doc_icon in $(echo $doc_icons_to_copy | tr ' ' '\n' | grep '^admon-' | sort | uniq); do
424             doc_icon_file="${doc_icon}.png"
425             cmd="cp \"$doc_icons_admon_path/$doc_icon_file\" \"$doc_outdir/$doc_icon_file\""
426             echo_verbose "$cmd"
427             eval "$cmd"
428         done
429     fi
430 }
431
432 list_icons() {
433     while [ "$#" != "0" ]; do
434         if [ -d "$1" ]; then
435             list_icons "$1/"*.page
436         else
437             echo "$XSL_ICONS" | xsltproc --nonet --xinclude - "$1"
438         fi
439         shift
440     done | sort | uniq
441 }
442
443 list_media() {
444     if [ "x$(echo "$1" | sed -e 's/.*\.//')" = "xpage" -o -d "$1" ]; then
445         XSL_MEDIA="$XSL_MALLARD_MEDIA"
446     else
447         XSL_MEDIA="$XSL_DOCBOOK_MEDIA"
448     fi
449     while [ "$#" != "0" ]; do
450         if [ -d "$1" ]; then
451             list_media "$1/"*.page
452         else
453             doc_indir=$(dirname "$1")
454             if [ "$doc_indir" = "." ]; then
455                 doc_indir=""
456             else
457                 doc_indir="$doc_indir/"
458             fi
459             echo "$XSL_MEDIA" | xsltproc --nonet --xinclude - "$1" \
460                 | while read doc_media; do
461                 echo "$doc_indir$doc_media"
462             done
463         fi
464         shift
465     done | sort | uniq
466 }
467
468 convert_html_xsl() {
469     echo '
470 <xsl:stylesheet
471   xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
472   xmlns:site="http://projectmallard.org/site/1.0/"
473   version="1.0">
474 <xsl:import href="'${datadir}/mallard-site-tool/site2html.xsl'"/>'
475     if [ "x$site_custom" != "x" ]; then
476         echo "<xsl:include href='${site_directory}/${site_custom}'/>"
477     fi;
478     echo '</xsl:stylesheet>'
479 }
480
481 convert_html() {
482     site_format="$1"
483     shift
484     longopts='
485       -lsite:
486       -loutput:
487       -lverbose
488       -lhelp
489     ';
490     options=`getopt -qn$progname $longopts -- s:o:vh "$@"`
491     if [ "$?" != "0" ]; then print_help_html 1>&2; exit 1; fi
492     eval set -- "$options";
493     while [ "$1" != "--" ]; do
494         case "$1" in
495             -s | --site)
496                 site_site="$2";;
497             -o | --output)
498                 site_output_dir=$( (cd "$2" && pwd) );;
499             -v | --verbose)
500                 site_verbose=1;;
501             -h | --help)
502                 print_help_cache
503                 exit 0;;
504             --)
505                 print_help_cache 1>&2
506                 exit 1;;
507         esac
508         shift
509     done
510     shift
511     locate_site_file
512     if [ "x$site_output_dir" = "x" ]; then
513         site_output_dir="$site_directory"
514     fi
515     site_cache="${site_directory}/${site_site}.cache"
516     site_root=`echo "$XSL_GET_ROOT" | xsltproc - "$site_file"`
517     site_custom=`echo "$XSL_GET_CUSTOM" | xsltproc - "$site_file"`
518     if [ "x$site_root" = "x" ]; then
519         site_root="file://"$(urlencode "$site_directory")
520     fi
521
522     for page in $(find $(pwd) -name '*.page'); do
523         page_dir=$(dirname $(echo "$page" | cut -c${#site_directory}- | cut -c2-))/
524         page_base=$(basename "$page" .page)
525         page_output="$site_output_dir$page_dir$page_base.$site_format"
526         mkdir_p $(dirname "$page_output")
527         echo_verbose "Creating $page_output"
528         convert_html_xsl | xsltproc \
529             --xinclude \
530             -o "$page_output" \
531             --stringparam mal.site.dir $(echo "$page_dir" | sed -e 's/\/\//\//') \
532             --stringparam mal.site.root "$site_root" \
533             --stringparam mal.cache.file "$site_cache" \
534             - "$page"
535     done
536 }
537
538 site_site="index"
539
540 command="$1";
541 if [ "x$command" = "x" ]; then
542     print_help 1>&2;
543     exit 1;
544 fi;
545 shift;
546 if [ "x$command" = "xhelp" -o "x$command" = "x--help" -o "x$command" = "x-h" ]; then
547     print_help;
548     exit 0;
549 elif [ "x$command" = "xhtml" ]; then
550     convert_html "$command" $@;
551 elif [ "x$command" = "xcache" ]; then
552     create_cache $@;
553 elif [ "x$command" = "xcss" ]; then
554     create_css $@;
555 elif [ "x$command" = "xmedia" ]; then
556     copy_media $@;
557 else
558     print_help 1>&2;
559     exit 1;
560 fi;
561