#!/bin/sh
# yelp-build
# Copyright (C) 2010 Shaun McCance <shaunm@gnome.org>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.

xsl_mal_cache='/usr/share/yelp-xsl/xslt/mallard/cache/mal-cache.xsl'
xsl_db2html='/usr/share/yelp-xsl/xslt/docbook/html/db2html.xsl'
xsl_db2xhtml='/usr/share/yelp-xsl/xslt/docbook/html/db2xhtml.xsl'
xsl_mal2html='/usr/share/yelp-xsl/xslt/mallard/html/mal2html.xsl'
xsl_mal2xhtml='/usr/share/yelp-xsl/xslt/mallard/html/mal2xhtml.xsl'
yelp_icon_dir='/usr/share/yelp-xsl/icons/hicolor/24x24/status'
yelp_js_dir='/usr/share/yelp-xsl/js'

mkdir_p () {
    if [ ! -d "$1" ]; then
        mkdir_p `dirname "$1"`
        mkdir "$1"
    fi
}

urlencode () {
    echo "$1" | LANG=C awk '
BEGIN {
  for (i = 1; i <= 255; i++) chars[sprintf("%c", i)] = i;
}
{
  ret = "";
  for (i = 1; i <= length($0); i++) {
    c = substr($0, i, 1);
    if (c ~ /[\/a-zA-Z0-9._-]/)
      ret = ret c;
    else
      ret = ret sprintf("%%%X%X", int(chars[c] / 16), chars[c] % 16);
  }
  print ret;
}'
}

urldecode () {
    echo "$1" | LANG=C awk '
BEGIN {
  for(i = 0; i < 10; i++) hex[i] = i;
  hex["A"] = hex["a"] = 10;
  hex["B"] = hex["b"] = 11;
  hex["C"] = hex["c"] = 12;
  hex["D"] = hex["d"] = 13;
  hex["E"] = hex["e"] = 14;
  hex["F"] = hex["f"] = 15;
}
{
  ret = "";
  for (i = 1; i <= length($0); i++) {
    c = substr($0, i, 1);
    if (c == "+") {
      ret = ret " ";
    }
    else if (c == "%") {
      c = sprintf("%c", hex[substr($0, i + 1, 1)] * 16 + hex[substr($0, i + 2, 1)]);
      ret = ret c;
      i += 2;
    }
    else {
      ret = ret c;
    }
  }
  print ret;
}'
}

yelp_usage () {
    (
        echo "Usage: yelp-build <COMMAND> [OPTIONS] [FILES]"
        echo ""
        echo "Commands:"
        echo "  cache         Create a Mallard cache file"
        echo "  html          Convert input files to HTML"
        echo "  xhtml         Convert input files to XHTML"
    ) 1>&2
}
yelp_usage_cache () {
    (
        echo "Usage: yelp-build cache <FILES>"
        echo ""
        echo "  Create a Mallard cache file from the page files FILES."
        echo "  If FILES contains directories, all .page files in those"
        echo "  directories will be used."
    ) 1>&2
}
yelp_usage_html () {
    (
        echo "Usage: yelp-build <html|xhtml> [OPTIONS] <FILES>"
        echo ""
        echo "  Create HTML or XHTML from the input files FILES."
        echo "  FILES can be DocBook files, Mallard page files,"
        echo "  or directories containing Mallard page files."
        echo ""
        echo "Options:"
        echo "  -c CACHE      Use the existing Mallard cache CACHE"
        echo "  -o OUT        Output files in the directory OUT"
        echo "  -x CUSTOM     Import the custom XSLT file CUSTOM"
    ) 1>&2
}

if [ $# = 0 ]; then
    yelp_usage
    exit 1
fi

yelp_cache_ls () {
    fbase=`basename "$1"`
    fdir=`dirname "$1"`
    fdir=`(cd "$fdir" && pwd)`
    echo '<page cache:href="file://'`urlencode "$fdir/$fbase"`'"/>'
}
yelp_cache () {
    cache_out="index.cache"
    while [ "$#" != "0" ]; do
        case "$1" in
            "-o")
                shift
                cache_out="$1"
                shift
                ;;
            *)
                break
                ;;
        esac
    done
    if [ "$#" = "0" -o "x$1" = "x--help" ]; then
        yelp_usage_cache
        exit 1
    fi
    (
        echo '<cache:cache xmlns:cache="http://projectmallard.org/cache/1.0/"'
        echo '  xmlns="http://projectmallard.org/1.0/">'
        for page in "$@"; do
            if [ -d "$page" ]; then
                for sub in "$page"/*.page; do
                    yelp_cache_ls "$sub"
                done
            else
                yelp_cache_ls "$page"
            fi
        done
        echo '</cache:cache>'
    ) | xsltproc --xinclude -o "$cache_out" "$xsl_mal_cache" -
}

yelp_html_xsl_common () {
    if [ "x$html_custom" != "x" ]; then
        echo '<xsl:include href="file://'`urlencode "$html_custom"`'"/>'
    fi
    echo '<xsl:variable name="yelp.locale">'
    echo ' <xsl:choose>'
    echo '  <xsl:when test="$l10n.locale != '"''"'">'
    echo '   <xsl:value-of select="$l10n.locale"/>'
    echo '  </xsl:when>'
    echo '  <xsl:otherwise>'
    echo '   <xsl:text>C</xsl:text>'
    echo '  </xsl:otherwise>'
    echo ' </xsl:choose>'
    echo '</xsl:variable>'
    echo '<xsl:template name="html.css">'
    echo ' <xsl:param name="node" select="."/>'
    echo ' <exsl:document href="{$yelp.locale}.css" method="text">'
    echo '  <xsl:call-template name="html.css.content">'
    echo '   <xsl:with-param name="node" select="$node"/>'
    echo '  </xsl:call-template>'
    echo ' </exsl:document>'
    echo ' <link rel="stylesheet" type="text/css" href="{$html.css.root}{$yelp.locale}.css"/>'
    echo '</xsl:template>'
    echo '<xsl:template name="html.js">'
    echo ' <xsl:param name="node" select="."/>'
    echo ' <xsl:call-template name="html.js.jquery">'
    echo '  <xsl:with-param name="node" select="$node"/>'
    echo ' </xsl:call-template>'
    echo ' <exsl:document href="yelp.js" method="text">'
    echo '  <xsl:call-template name="html.js.content">'
    echo '   <xsl:with-param name="node" select="$node"/>'
    echo '  </xsl:call-template>'
    echo ' </exsl:document>'
    echo ' <script type="text/javascript" language="javascript" src="{$html.js.root}yelp.js"/>'
    echo '</xsl:template>'
}
yelp_html_db2html () {
    for xml in "$@"; do
        xmldir=`dirname "$xml"`
        xmldir=`(cd "$xmldir" && pwd)`
        # Output HTML
        (
            echo '<xsl:stylesheet'
            echo ' xmlns:xsl="http://www.w3.org/1999/XSL/Transform"'
            echo ' xmlns:exsl="http://exslt.org/common"'
            echo ' extension-element-prefixes="exsl"'
            echo ' version="1.0">'
            if [ "x$is_xhtml" = "x1" ]; then
                xsl='file://'`urlencode "$xsl_db2xhtml"`
            else
                xsl='file://'`urlencode "$xsl_db2html"`
            fi
            echo '<xsl:import href="'"$xsl"'"/>'
            yelp_html_xsl_common
            echo '</xsl:stylesheet>'
        ) | xsltproc --xinclude -o "$html_out"/ - "$xml"
        # Copy media
        if [ "x$xmldir" != "x$html_out" ]; then
            (
                echo '<xsl:stylesheet'
                echo ' xmlns:xsl="http://www.w3.org/1999/XSL/Transform"'
                echo ' xmlns:db="http://docbook.org/ns/docbook"'
                echo ' exclude-result-prefixes="db"'
                echo ' version="1.0">'
                echo '<xsl:output method="text"/>'
                echo '<xsl:template match="/">'
                echo ' <xsl:for-each select="'
                echo '   //audiodata | //imagedata | //videodata |'
                echo '   //db:audiodata | //db:imagedata | //db:videodata">'
                echo '  <xsl:value-of select="concat(@fileref, '"'&#x000A;'"')"/>'
                echo ' </xsl:for-each>'
                echo '</xsl:template>'
                echo '</xsl:stylesheet>'
            ) | xsltproc --xinclude - "$xml" | while read media; do
                mfile=`urldecode "$media"`
                minput="$xmldir/$mfile"
                moutput="$html_out/$mfile"
                mkdir_p `dirname "$moutput"`
                cp "$minput" "$moutput"
            done
        fi
        # Copy icons
        (
            echo '<xsl:stylesheet'
            echo ' xmlns:xsl="http://www.w3.org/1999/XSL/Transform"'
            echo ' xmlns:db="http://docbook.org/ns/docbook"'
            echo ' exclude-result-prefixes="db"'
            echo ' version="1.0">'
            echo '<xsl:output method="text"/>'
            echo '<xsl:template match="/">'
            echo ' <xsl:for-each select="(//caution|//warning|//db:caution|//db:warning)[1]">'
            echo '  <xsl:text>yelp-note-warning&#x000A;</xsl:text>'
            echo ' </xsl:for-each>'
            echo ' <xsl:for-each select="(//important|//db:important)[1]">'
            echo '  <xsl:text>yelp-note-important&#x000A;</xsl:text>'
            echo ' </xsl:for-each>'
            echo ' <xsl:for-each select="(//tip|//db:tip)[1]">'
            echo '  <xsl:text>yelp-note-tip&#x000A;</xsl:text>'
            echo ' </xsl:for-each>'
            echo ' <xsl:for-each select="(//note|//db:note)[@role='"'bug'"'][1]">'
            echo '  <xsl:text>yelp-note-bug&#x000A;</xsl:text>'
            echo ' </xsl:for-each>'
            echo ' <xsl:for-each select="(//note|//db:note)[not(@role) or @role!='"'bug'"'][1]">'
            echo '  <xsl:text>yelp-note&#x000A;</xsl:text>'
            echo ' </xsl:for-each>'
            echo '</xsl:template>'
            echo '</xsl:stylesheet>'
        ) | xsltproc --xinclude - "$xml" | sort | uniq | while read icon; do
            cp "${yelp_icon_dir}/${icon}.png" "$html_out"
        done;
        # Copy jQuery
        cp "${yelp_js_dir}/jquery.js" "$html_out"
        cp "${yelp_js_dir}/jquery.syntax.js" "$html_out"
        (
            echo '<xsl:stylesheet'
            echo ' xmlns:xsl="http://www.w3.org/1999/XSL/Transform"'
            echo ' xmlns:db="http://docbook.org/ns/docbook"'
            echo ' xmlns:html="http://www.w3.org/1999/xhtml"'
            echo ' xmlns:exsl="http://exslt.org/common"'
            echo ' exclude-result-prefixes="db"'
            echo ' extension-element-prefixes="exsl"'
            echo ' version="1.0">'
            xsl='file://'`urlencode "$xsl_db2xhtml"`
            echo '<xsl:import href="'"$xsl"'"/>'
            echo '<xsl:output method="text"/>'
            echo '<xsl:template match="/">'
            echo ' <xsl:for-each select="//programlisting[@language] | //db:programlisting[@language]">'
            echo '  <xsl:variable name="out">'
            echo '   <xsl:call-template name="db2html.pre"/>'
            echo '  </xsl:variable>'
            echo '  <xsl:value-of select="exsl:node-set($out)/*/html:pre[last()]/@class"/>'
            echo '  <xsl:text>&#x000A;</xsl:text>'
            echo ' </xsl:for-each>'
            echo '</xsl:template>'
            echo '</xsl:stylesheet>'
        ) | xsltproc --xinclude - "$xml" | sort | uniq \
            | grep '^contents syntax ' | sed -e 's/^contents syntax brush-/brush./' \
            | while read js; do
            cp "${yelp_js_dir}/jquery.syntax.${js}.js" "$html_out"
        done
    done
}

yelp_html_page2html () {
    xml="$1"
    xmldir=`dirname "$xml"`
    xmldir=`(cd "$xmldir" && pwd)`
    # Output HTML
    (
        echo '<xsl:stylesheet'
        echo ' xmlns:xsl="http://www.w3.org/1999/XSL/Transform"'
        echo ' xmlns:exsl="http://exslt.org/common"'
        echo ' extension-element-prefixes="exsl"'
        echo ' version="1.0">'
        if [ "x$is_xhtml" = "x1" ]; then
            xsl='file://'`urlencode "$xsl_mal2xhtml"`
        else
            xsl='file://'`urlencode "$xsl_mal2html"`
        fi
        echo '<xsl:import href="'"$xsl"'"/>'
        yelp_html_xsl_common
        html_cache_url='file://'`urlencode "$html_cache_file"`
        echo '<xsl:param name="mal.cache.file" select="'"'$html_cache_url'"'"/>'
        echo '</xsl:stylesheet>'
    ) | xsltproc --xinclude -o "$html_out"/ - "$xml"
    # Copy media
    if [ "x$xmldir" != "x$html_out" ]; then
        (
            echo '<xsl:stylesheet'
            echo ' xmlns:xsl="http://www.w3.org/1999/XSL/Transform"'
            echo ' xmlns:mal="http://projectmallard.org/1.0/"'
            echo ' exclude-result-prefixes="mal"'
            echo ' version="1.0">'
            echo '<xsl:output method="text"/>'
            echo '<xsl:template match="/">'
            echo ' <xsl:for-each select="//mal:media">'
            echo '  <xsl:value-of select="concat(@src, '"'&#x000A;'"')"/>'
            echo ' </xsl:for-each>'
            echo '</xsl:template>'
            echo '</xsl:stylesheet>'
        ) | xsltproc --xinclude - "$xml" | while read media; do
            mfile=`urldecode "$media"`
            minput="$xmldir/$mfile"
            moutput="$html_out/$mfile"
            mkdir_p `dirname "$moutput"`
            cp "$minput" "$moutput"
        done
    fi
    # Copy icons
    (
        echo '<xsl:stylesheet'
        echo ' xmlns:xsl="http://www.w3.org/1999/XSL/Transform"'
        echo ' xmlns:mal="http://projectmallard.org/1.0/"'
        echo ' exclude-result-prefixes="mal"'
        echo ' version="1.0">'
        echo '<xsl:output method="text"/>'
        echo '<xsl:template match="/">'
        echo ' <xsl:for-each select="//mal:note">'
        echo '  <xsl:variable name="style" select="concat('"'"' '"'"', @style, '"'"' '"'"')"/>'
        echo '  <xsl:choose>'
        echo '   <xsl:when test="contains($style, '"'"' advanced '"'"')">'
        echo '    <xsl:text>yelp-note-advanced&#x000A;</xsl:text>'
        echo '   </xsl:when>'
        echo '   <xsl:when test="contains($style, '"'"' bug '"'"')">'
        echo '    <xsl:text>yelp-note-bug&#x000A;</xsl:text>'
        echo '   </xsl:when>'
        echo '   <xsl:when test="contains($style, '"'"' important '"'"')">'
        echo '    <xsl:text>yelp-note-important&#x000A;</xsl:text>'
        echo '   </xsl:when>'
        echo '   <xsl:when test="contains($style, '"'"' sidebar '"'"')"/>'
        echo '   <xsl:when test="contains($style, '"'"' tip '"'"')">'
        echo '    <xsl:text>yelp-note-tip&#x000A;</xsl:text>'
        echo '   </xsl:when>'
        echo '   <xsl:when test="contains($style, '"'"' warning '"'"')">'
        echo '    <xsl:text>yelp-note-warning&#x000A;</xsl:text>'
        echo '   </xsl:when>'
        echo '   <xsl:otherwise>'
        echo '    <xsl:text>yelp-note&#x000A;</xsl:text>'
        echo '   </xsl:otherwise>'
        echo '  </xsl:choose>'
        echo ' </xsl:for-each>'
        echo '</xsl:template>'
        echo '</xsl:stylesheet>'
    ) | xsltproc --xinclude - "$xml" | sort | uniq | while read icon; do
        cp "${yelp_icon_dir}/${icon}.png" "$html_out"
    done;
    # Copy jQuery
    cp "${yelp_js_dir}/jquery.js" "$html_out"
    cp "${yelp_js_dir}/jquery.syntax.js" "$html_out"
    (
        echo '<xsl:stylesheet'
        echo ' xmlns:xsl="http://www.w3.org/1999/XSL/Transform"'
        echo ' xmlns:mal="http://projectmallard.org/1.0/"'
        echo ' xmlns:html="http://www.w3.org/1999/xhtml"'
        echo ' xmlns:exsl="http://exslt.org/common"'
        echo ' exclude-result-prefixes="mal"'
        echo ' extension-element-prefixes="exsl"'
        echo ' version="1.0">'
        xsl='file://'`urlencode "$xsl_mal2xhtml"`
        echo '<xsl:import href="'"$xsl"'"/>'
        echo '<xsl:output method="text"/>'
        echo '<xsl:template match="/">'
        echo ' <xsl:for-each select="//mal:code[@mime]">'
        echo '  <xsl:variable name="out">'
        echo '   <xsl:call-template name="mal2html.pre"/>'
        echo '  </xsl:variable>'
        echo '  <xsl:value-of select="exsl:node-set($out)/*/html:pre[last()]/@class"/>'
        echo '  <xsl:text>&#x000A;</xsl:text>'
        echo ' </xsl:for-each>'
        echo '</xsl:template>'
        echo '</xsl:stylesheet>'
    ) | xsltproc --xinclude - "$xml" | sort | uniq \
        | grep '^contents syntax ' | sed -e 's/^contents syntax brush-/brush./' \
        | while read js; do
        cp "${yelp_js_dir}/jquery.syntax.${js}.js" "$html_out"
    done
}

yelp_html_mal2html () {
    if [ "x$html_cache_file" != "x" ]; then
        html_cache_file=`(cd $(dirname "$html_cache_file") && pwd)`/`basename "$html_cache_file"`
    else
        html_cache_file_is_tmp="yes"
        html_cache_file=`mktemp`
        yelp_cache -o "$html_cache_file" "$@"
    fi
    for xml in "$@"; do
        if [ -d "$xml" ]; then
            for page in "$xml"/*.page; do
                yelp_html_page2html "$page"
            done
        else
            yelp_html_page2html "$xml"
        fi
    done
    if [ "x$html_cache_file_is_tmp" = "xyes" ]; then
        rm "$html_cache_file"
    fi
}

yelp_html () {
    html_out="."
    while [ "$#" != "0" ]; do
        case "$1" in
            "-c")
                shift
                html_cache_file="$1"
                shift
                ;;
            "-o")
                shift
                html_out="$1"
                shift
                ;;
            "-x")
                shift
                html_custom="$1"
                shift
                ;;
            *)
                break
                ;;
        esac
    done
    if [ ! -d "$html_out" ]; then
        echo "Error: output must be a directory." 1>&2
        exit 1
    fi
    html_out=`(cd "$html_out" && pwd)`
    if [ "x$html_custom" != "x" ]; then
        html_custom_dir=`dirname "$html_custom"`
        html_custom_dir=`(cd "$html_custom_dir" && pwd)`
        html_custom="$html_custom_dir"/`basename "$html_custom"`
    fi
    if [ "$#" = "0" -o "x$1" = "x--help" ]; then
        yelp_usage_html
        exit 1
    fi
    ext=`echo "$1" | sed -e 's/.*\.//'`
    if [ "x$ext" = "xxml" -o "x$ext" = "xdocbook" ]; then
        yelp_html_db2html "$@"
    else
        yelp_html_mal2html "$@"
    fi
}

cmd="$1"
shift
case "x$cmd" in
    "xcache")
        yelp_cache "$@"
        ;;
    "xhtml")
        is_xhtml=0
        yelp_html "$@"
        ;;
    "xxhtml")
        is_xhtml=1
        yelp_html "$@"
        ;;
    *)
        yelp_usage
        ;;
esac
