TIVI-153: add as dependency for iputils
[profile/ivi/xmlto.git] / xmlto.in
1 #!@BASH@
2 #
3 # @PACKAGE@ - apply an XSL stylesheet to an XML document
4 # Copyright (C) 2001, 2002, 2003  Tim Waugh <twaugh@redhat.com>
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,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 # GNU 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, see <http://www.gnu.org/licenses/>.
18
19 # Utilities that we need that aren't everywhere
20 FIND=@FIND@     # This must be GNU find (need -maxdepth)
21 MKTEMP=@MKTEMP@ # See http://www.mktemp.org if missing on your system
22 BASH=@BASH@     # GNU bash, for running the format scripts
23 GETOPT=@GETOPT@ # a getopt that supports --longoptions
24 TAIL=@TAIL@     # a tail that supports -n (posix)
25
26 version () {
27   echo "@PACKAGE@ version @VERSION@"
28 }
29
30 usage () {
31   cat << EOF
32 usage: @PACKAGE@ [OPTION]... FORMAT XML
33 OPTIONs are:
34   -v              verbose output (-vv for very verbose)
35   -x stylesheet   use the specified stylesheet instead of choosing one
36   -m fragment     use the XSL fragment to customize the stylesheet
37   -o directory    put output in the specified directory instead of
38                   the current working directory
39   -p postprocopts pass option to postprocessor
40   --extensions    turn on stylesheet extensions for this tool chain
41   --searchpath    colon-separated list of fallback directories
42   --skip-validation
43                   do not attempt to validate the input before processing
44   --stringparam paramname=paramvalue
45                   pass a named parameter to the stylesheet from the
46                   command line
47   --noclean       temp files are not deleted automatically
48                   (good for diagnostics)
49   --noautosize    do not autodetect paper size via locales or paperconf
50   --with-fop      use fop for formatting (if fop available)
51   --with-dblatex  use dblatex for formatting (if dblatex available)
52
53 Available FORMATs depend on the type of the XML file (which is
54 determined automatically).
55 EOF
56   if [ -d "$FORMAT_DIR" ]
57   then
58     for source in $(${FIND} "$FORMAT_DIR" -maxdepth 1 -type d)
59     do
60       if [ "$source" = "$FORMAT_DIR" ]; then continue; fi
61
62       cat << EOF
63
64 For documents of type "$(basename "$source")":
65 EOF
66     ls "$source"
67     done
68   fi
69 }
70
71 # make_temp [-d] filenametag varname [message upon failure]
72 #
73 # Wrapper for 'varname=$(mktemp /tmp/xmlto-$filenametag.XXXXXX)'.
74 #  * Remembers the temporary file's name so it can be deleted on exit
75 #  * If the failure message is empty or missing, exits on failure
76 make_temp () {
77   local dirflag="" prefix="@PACKAGE@"
78   [ "$1" = "-d" ] && { dirflag="-d"; shift; }
79   [ -n "$1" ] && prefix="@PACKAGE@-$1"
80
81   if eval $2='$(${MKTEMP} $dirflag "${TMPDIR:-/tmp}/${prefix}.XXXXXX")'
82   then
83     eval 'CLEANFILES[$CLEANFILE_COUNT]="${'$2'}"'
84     CLEANFILE_COUNT=$(($CLEANFILE_COUNT + 1))
85     return 0
86   elif [ -z "$3" ]
87   then
88     echo >&2 "mktemp failed!"
89     exit 2
90   else
91     echo >&2 "mktemp failed. $3"
92     return 2
93   fi
94 }
95
96 # Allow FORMAT_DIR to be over-ridden, so that we can be
97 # run from the build directory.
98 prefix=@prefix@
99 : ${FORMAT_DIR=@datarootdir@/@PACKAGE@/format}
100 # This can be over-ridden, but really we should detect the source
101 # document type without needing any help.
102 : ${SOURCE_FORMAT=docbook}
103
104 # Get absolute pathnames for FORMAT_DIR and OUTPUT_DIR.
105 WD="$(pwd)"
106 if [ "x${FORMAT_DIR##/*}" != "x" ]
107 then
108         FORMAT_DIR="${PWD}/${FORMAT_DIR}"
109 fi
110 OUTPUT_DIR="$WD"
111
112 # This is an array of XSL fragments specified by the user.
113 declare -a XSL_MODS
114 XSL_MOD_COUNT=0
115
116 # List of files to remove after exit
117 declare -a CLEANFILES
118 CLEANFILE_COUNT=0
119 trap -- 'cd /; [ -z "${CLEANFILES[*]}" ] || rm -rf "${CLEANFILES[@]}"' EXIT
120
121 XSLTOPTS=
122 SEARCHPATH=
123 PAPERCONF_PATH=@PAPER_CONF@
124 LOCALE_PATH=@LOCALE@
125 XMLLINT_PATH=@XMLLINT@
126 XSLTPROC_PATH=@XSLTPROC@
127
128 # Try to setup papersize using libpaper first ...
129 if [ -n "`type -t $PAPERCONF_PATH`" ]
130 then
131   papername=`"$PAPERCONF_PATH" -n`
132   paperheight=`"$PAPERCONF_PATH" -mh | sed 's/ //g'`
133   paperwidth=`"$PAPERCONF_PATH" -mw | sed 's/ //g'`
134
135   if [ -n "$paperheight" -a -n "$paperwidth" ]
136   then
137     make_temp xsl papersizemod "Using default paper type." &&
138     cat << EOF > "$papersizemod"
139 <?xml version='1.0'?>
140 <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
141                 version='1.0'>
142 <xsl:param name="page.height">$paperheight</xsl:param>
143 <xsl:param name="page.width">$paperwidth</xsl:param>
144 <xsl:template name="root.messages">
145   <xsl:message>
146   <xsl:text>Making </xsl:text>
147   <xsl:value-of select="\$page.orientation"/>
148   <xsl:text> pages on $papername paper (</xsl:text>
149   <xsl:value-of select="\$page.width"/>
150   <xsl:text>x</xsl:text>
151   <xsl:value-of select="\$page.height"/>
152   <xsl:text>)</xsl:text>
153   </xsl:message>
154 </xsl:template>
155 </xsl:stylesheet>
156 EOF
157   fi
158
159 # ... or use magic paper size, based on LC_PAPER
160 elif [ -n "`type -t $LOCALE_PATH`" ]
161 then
162   # For paper sizes we know about, specify them.
163   h=$("$LOCALE_PATH" LC_PAPER 2>/dev/null | head -n 1)
164   if [ "$h" = "297" ]
165   then
166     papertype=A4
167   fi
168
169   if [ -n "$papertype" ]
170   then
171     make_temp xsl papersizemod "Using default paper type." &&
172     cat << EOF > "$papersizemod"
173 <?xml version='1.0'?>
174 <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
175                 version='1.0'>
176 <xsl:param name="paper.type" select="'$papertype'"/>
177 </xsl:stylesheet>
178 EOF
179   fi
180 fi
181
182 # Magic encoding, based on locale
183 if [ -n "`type -t $LOCALE_PATH`" ]
184 then
185   charmap=$("$LOCALE_PATH" charmap 2>/dev/null)
186
187   if [ -n "$charmap" ]
188   then
189     if make_temp xsl encodingmod "Using default output encoding."
190     then
191       cat << EOF > "$encodingmod"
192 <?xml version='1.0'?>
193 <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
194                 version='1.0'>
195 <xsl:param name="chunker.output.encoding" select="'$charmap'"/>
196 <xsl:param name="man.charmap.use.subset" select="'0'"/>
197 </xsl:stylesheet>
198 EOF
199       XSL_MODS[$XSL_MOD_COUNT]="$encodingmod"
200       XSL_MOD_COUNT=$(($XSL_MOD_COUNT + 1))
201     fi
202   fi
203 fi
204
205 # Make verbosity level uniformly available to called scripts
206 VERBOSE=0
207 export VERBOSE
208
209 # Disable network entities
210 XSLTOPTS="$XSLTOPTS --nonet"
211
212 # The names parameter for the XSLT stylesheet
213 XSLTPARAMS=""
214
215 # Enable XInclude
216 XSLTOPTS="$XSLTOPTS --xinclude"
217
218 SKIP_VALIDATION=0
219 EXTENSIONS=0
220 NO_AUTOSIZE=0
221
222 #actual possibilities DEFAULT(XSL-FO/passivetex), FOP and DBLATEX
223 USE_BACKEND=@BACKEND@
224
225
226 FOP_PATH=@FOP@
227 DBLATEX_PATH=@DBLATEX@
228
229 XMLTEX_PATH=@XMLTEX@
230 PDFXMLTEX_PATH=@PDFXMLTEX@
231
232 #check if we could use fop/dblatex backend as default(if not, use passivetex)
233 if [ x"$USE_BACKEND" = xFOP ] && [ -z "`type -t $FOP_PATH`" ]
234 then
235   echo >&2 "@PACKAGE@: Warning: fop not found or not executable."
236   echo >&2 "@PACKAGE@: Using default backend..."
237   USE_BACKEND=DEFAULT
238 fi
239 if [ x"$USE_BACKEND" = xDBLATEX ] && \
240    [ -z "`type -t $DBLATEX_PATH`" ]
241 then
242   echo >&2 "@PACKAGE@: Warning: dblatex not found or not executable."
243   echo >&2 "@PACKAGE@: Using default backend..."
244   USE_BACKEND=DEFAULT
245 fi
246
247 LINKS_PATH=@LINKS@
248 W3M_PATH=@W3M@
249 LYNX_PATH=@LYNX@
250 GCP_PATH=@GCP@
251
252 # Process any options
253 ARGS=$(${GETOPT} \
254         --longoptions=help,version,extensions,searchpath:,skip-validation,stringparam:,noclean,noautosize,with-fop,with-dblatex \
255         -n xmlto -- x:m:o:p:v "$@")
256 [ $? != 0 ] && { usage; exit 1; }
257 eval set -- "$ARGS"
258 while [ "$#" -gt "0" ]; do
259   case "$1" in
260   --help)
261         usage
262         exit 0
263         ;;
264   --version)
265         version
266         exit 0
267         ;;
268   -x)
269         case "$2" in
270         -)  make_temp stdin-xsl TMP_STYLESHEET
271             cat /dev/stdin > ${TMP_STYLESHEET}
272             STYLESHEET=${TMP_STYLESHEET} ;;
273         /*) STYLESHEET="$2" ;;
274          *) STYLESHEET="$PWD/$2" ;;
275         esac
276         shift 2
277         ;;
278   -m)
279         case "$2" in
280         /* | *:/*) XSL_MODS[$XSL_MOD_COUNT]="$2" ;;
281                 *) XSL_MODS[$XSL_MOD_COUNT]="$PWD/$2" ;;
282         esac
283         XSL_MOD_COUNT=$(($XSL_MOD_COUNT + 1))
284         shift 2
285         ;;
286   -o)
287         case "$2" in
288         /*) OUTPUT_DIR="$2" ;;
289          *) OUTPUT_DIR="$WD/$2" ;;
290         esac
291         shift 2
292         ;;
293   -p)
294         case $POSTARGS in
295         "") POSTARGS="$2" ;;
296         *) POSTPOSTARGS="$2" ;;
297         esac
298         shift 2
299         ;;
300   --extensions)
301         # Turn on extensions for whatever tools we are using.
302         # XSLTOPTS will be done later(once we will know which
303         # tool is used for processing)
304         EXTENSIONS=1
305         XSLTOPTS="$XSLTOPTS --param use.extensions '1'"
306         shift
307         ;;
308   -v)
309         : ${VERBOSE:-0}
310         VERBOSE=$((${VERBOSE}+1))
311         shift
312         ;;
313   --searchpath)
314         SEARCHPATH="$SEARCHPATH:$2"
315         SEARCHPATH="${SEARCHPATH#:}"
316         SEARCHPATH="${SEARCHPATH%:}"
317         shift 2
318         ;;
319   --skip-validation)
320         SKIP_VALIDATION=1
321         shift
322         ;;
323   --stringparam)
324         MYPARAM="$2"
325         XSLTPARAMS="$XSLTPARAMS --stringparam ${MYPARAM%=*}"
326         XSLTPARAMS="$XSLTPARAMS ${MYPARAM#*=}"
327         shift 2
328         ;;
329   --noclean)
330         trap -- 'cd /; [ -z "${CLEANFILES[*]}" ] || echo "${CLEANFILES[@]}"' EXIT
331         shift
332         ;;
333   --noautosize)
334   NO_AUTOSIZE=1
335   shift
336   ;;
337   --with-fop)
338         ##use fop instead of passivetex where possible
339         if [ -z "`type -t $FOP_PATH`" ]
340         then
341                 echo >&2 Warning: fop not found or not executable.
342                 echo >&2 Using default backend...
343         else
344                 USE_BACKEND="FOP"
345         fi
346         shift
347         ;;
348   --with-dblatex)
349         ##use dblatex instead of passivetex where possible
350         if [ -z "`type -t $DBLATEX_PATH`" ]
351         then
352                 echo >&2 Warning: dblatex not found or not executable.
353                 echo >&2 Using default backend...
354         else
355                 USE_BACKEND="DBLATEX"
356         fi
357         shift
358         ;;
359   --)
360         shift
361         break
362         ;;
363   esac
364 done
365
366 ##here we would decide which extensions should be active
367 if [ "$EXTENSIONS" -eq 1 ]
368 then
369   case "$USE_BACKEND" in
370   FOP)
371       #maybe fop1.extensions for latest fop, but keeping this one
372       XSLTOPTS="$XSLTOPTS --param fop.extensions '1'" ;;
373   DBLATEX)
374       ;;
375   DEFAULT)
376       XSLTOPTS="$XSLTOPTS --param passivetex.extensions '1'" ;;
377   esac
378 fi
379
380 if [ "$#" != "2" ]
381 then
382   usage
383   exit 1
384 fi
385
386 DEST_FORMAT="$1"
387 case "$2" in
388 /*) INPUT_FILE="$2" ;;
389  *) INPUT_FILE="$PWD/$2" ;;
390 esac
391
392 if [ -z "$DEST_FORMAT" -o -z "$INPUT_FILE" ]
393 then
394   usage
395   exit 1
396 fi
397
398 [ ! -e "$INPUT_FILE" ] && echo >&2 Input file "$INPUT_FILE" not found && \
399   exit 1
400
401 # Since we know DEST_FORMAT, we know whether or not to use $papersizemod.
402 case "$DEST_FORMAT" in
403   fo | pdf | ps | dvi)
404     if [ "$NO_AUTOSIZE" -eq 0 ] && [ -n "$papersizemod" ]
405     then
406       XSL_MODS[$XSL_MOD_COUNT]="$papersizemod"
407       XSL_MOD_COUNT=$(($XSL_MOD_COUNT + 1))
408     fi
409     ;;
410 esac
411
412
413 # Decide what source format this is.  Default to DocBook.
414 #rootel=$(head -n 4 "$INPUT_FILE" | tr -d '\n' | \
415 #     sed -e 's/^<?[^?>]*?>//g' -e 's/^<![^>]*>//g' -e 's/^<\([^ ]*\).*$/\1/')
416
417 # Seems reasonable fix the file command and teach it to identify the DTD/Schema but this is faster to write:
418 rootel=$(echo "xpath *" | "$XMLLINT_PATH" --shell "$INPUT_FILE" 2> /dev/null | head -n 3 |$TAIL -n 1 | cut -f 4 -d " " )
419
420 case $(echo $rootel) in
421   fo:root)
422         SOURCE_FORMAT="fo"
423         ;;
424   html)
425         SOURCE_FORMAT="xhtml1"
426         ;;
427 esac
428
429 [ "$VERBOSE" -ge 1 ] && \
430   echo >&2 "Source format: ${SOURCE_FORMAT} / root element: ${rootel} "
431
432 # If the destination format is an absolute pathname then it's a
433 # user-defined format script.  Otherwise it's one of ours.
434 case "$DEST_FORMAT" in
435 /*) FORMAT="$DEST_FORMAT" ;;
436  *) FORMAT="${FORMAT_DIR}/${SOURCE_FORMAT}/${DEST_FORMAT}" ;;
437 esac
438
439 [ "$VERBOSE" -ge 1 ] && echo >&2 "Format script: ${FORMAT}"
440
441 if [ ! -e "$FORMAT" ]
442 then
443   echo >&2 "I don't know how to convert ${SOURCE_FORMAT} into ${DEST_FORMAT}."
444   exit 1
445 fi
446
447 # Ask the format script what stylesheet to use.
448 XSLT_PROCESSOR="$XSLTPROC_PATH" # We only know about xsltproc right now.
449 export XSLT_PROCESSOR
450 export W3M_PATH
451 export GCP_PATH
452 export LINKS_PATH
453 export LYNX_PATH
454 export FOP_PATH
455 export DBLATEX_PATH
456 export XMLTEX_PATH
457 export PDFXMLTEX_PATH
458 export USE_BACKEND
459 if [ -z "$STYLESHEET" ]
460 then
461   STYLESHEET="$(${BASH} "$FORMAT" stylesheet)" || exit 1
462 fi
463
464 # We might need to create a temporary stylesheet if there are
465 # XSL fragments that need adding.
466 if [ "$XSL_MOD_COUNT" -gt "0" -a -n "$STYLESHEET" ]
467 then
468   REAL_STYLESHEET="$STYLESHEET"
469   [ "$VERBOSE" -ge 1 ] && echo >&2 "Real stylesheet: ${REAL_STYLESHEET}"
470   make_temp xsl STYLESHEET
471   cat << EOF > "$STYLESHEET"
472 <?xml version='1.0'?>
473 <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
474                 version='1.0'>
475 <xsl:import href="${REAL_STYLESHEET}"/>
476 EOF
477
478   i=0
479   while [ "$i" -lt "$XSL_MOD_COUNT" ]
480   do
481     cat << EOF >> "$STYLESHEET"
482 <xsl:include href="${XSL_MODS[$i]}"/>
483 EOF
484     i=$(($i + 1))
485   done
486
487   cat << EOF >> "$STYLESHEET"
488 </xsl:stylesheet>
489 EOF
490 fi
491
492 make_temp -d "" XSLT_PROCESSED_DIR
493 cd "$XSLT_PROCESSED_DIR"
494
495 # Validate the input
496 if [ "$SKIP_VALIDATION" -eq 0 ] && [ "$SOURCE_FORMAT" != "fo" ]
497 then
498   #do we have xmllint validation tool?
499   if [ -z "`type -t $XMLLINT_PATH`" ]
500   then
501     echo >&2 "@PACKAGE@: xmllint validation tool not found or not executable."
502     echo >&2 "@PACKAGE@: Skipping validation... " \
503              "Please make sure xmllint is installed."
504   else
505     VALIDATION="${XSLT_PROCESSED_DIR}/validation-errors"
506
507     [ "$VERBOSE" -ge 1 ] && \
508     echo >&2 \
509        "$XMLLINT_PATH --noout --nonet --xinclude --postvalid --noent \"$INPUT_FILE\""
510     "$XMLLINT_PATH" --noout --nonet --xinclude --postvalid --noent "$INPUT_FILE" 2>"${VALIDATION}"
511     xmllint_status=$?
512
513     if [ $xmllint_status -ne 0 ]
514     then
515       echo >&2 "@PACKAGE@: $INPUT_FILE does not validate (status ${xmllint_status})"
516       echo >&2 "@PACKAGE@: Fix document syntax or use --skip-validation option"
517       cat >&2 "${VALIDATION}"
518       exit $(($xmllint_status + 10))
519     fi
520     rm -f "${VALIDATION}"
521   fi
522 fi
523
524 if [ -z "${STYLESHEET}" ]
525 then
526   # No stylesheet: no XSL-T processing to do.
527   XSLT_PROCESSED="$INPUT_FILE"
528 else
529
530   #do we have xsltproc tool?
531   if [ -z "`type -t $XSLTPROC_PATH`" ]
532   then
533     echo >&2 "@PACKAGE@: Can't continue, xsltproc tool not found or not executable."
534     exit 3
535   fi
536
537   [ "$VERBOSE" -ge 1 ] && echo >&2 "Stylesheet: ${STYLESHEET}"
538   XSLT_PROCESSED="$XSLT_PROCESSED_DIR/$(basename "${INPUT_FILE%.*}").proc"
539
540   if [ "$VERBOSE" -gt 2 ]
541   then
542     XSLTOPTS="$XSLTOPTS -v"
543   fi
544
545   if [ -n "$SEARCHPATH" ]
546   then
547     XSLTWITHPATH=--path
548     XSLTPATH=$(echo "$SEARCHPATH" | tr : ' ')
549     XSLTSHOWPATH="$XSLTWITHPATH \"$XSLTPATH\""
550   fi
551
552   XSLTOPTS="$XSLTPARAMS $XSLTOPTS"
553   [ "$VERBOSE" -ge 1 ] && \
554    echo -e >&2 "$XSLTPROC_PATH ${XSLTOPTS} ${XSLTSHOWPATH}\\\\\n -o \"$XSLT_PROCESSED\" \\\\\n $STYLESHEET \\\\\n \"$INPUT_FILE\""
555
556   if [ -z "$XSLTWITHPATH" ]
557   then
558     "$XSLTPROC_PATH" $XSLTOPTS -o "$XSLT_PROCESSED" "$STYLESHEET" "$INPUT_FILE"
559   else
560     "$XSLTPROC_PATH" $XSLTOPTS $XSLTWITHPATH "$XSLTPATH" \
561       -o "$XSLT_PROCESSED" "$STYLESHEET" "$INPUT_FILE"
562   fi
563
564   if [ $? == 4 ]
565   then
566     XSLTOPTS="${XSLTOPTS} --catalogs"
567     [ "$VERBOSE" -ge 1 ] && \
568       echo >&2 "No XML Catalogs?  Trying again with --catalogs.."
569     "$XSLTPROC_PATH" $XSLTOPTS -o "$XSLT_PROCESSED" "$STYLESHEET" "$INPUT_FILE"
570   fi
571
572   if [ $? -gt 0 ]
573   then
574     exit $?
575   fi
576 fi
577
578 if [ ! -d "$OUTPUT_DIR" ]
579 then
580   [ "$VERBOSE" -ge 1 ] && echo >&2 "Creating output directory ${OUTPUT_DIR}"
581   mkdir -p "$OUTPUT_DIR"
582 fi
583
584 # Run the format script in post-process mode to finish off.
585 export OUTPUT_DIR
586 export POSTARGS
587 export POSTPOSTARGS
588 export XSLT_PROCESSED
589 export INPUT_FILE
590 export SEARCHPATH
591 if [ "$VERBOSE" -gt 2 ]
592 then
593   # Extremely verbose
594   BASH="${BASH} -x"
595 fi
596 ${BASH} "$FORMAT" post-process || exit 1