| Server IP : 85.112.90.236 / Your IP : 192.168.1.26 Web Server : Apache System : Linux 85-112-90-236.cprapid.com 6.12.0-211.7.3.el10_2.x86_64 #1 SMP PREEMPT_DYNAMIC Tue May 19 12:46:58 EDT 2026 x86_64 User : ftechme ( 1002) PHP Version : 8.2.32 Disable Function : exec,passthru,shell_exec,system MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : OFF | Sudo : ON | Pkexec : ON Directory : /proc/self/root/usr/bin/ |
Upload File : |
#!/usr/bin/python3
#
# yelp-build
# Copyright (C) 2010-2020 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.
import configparser
import os
import sys
import shutil
import subprocess
import tempfile
import urllib.parse
import uuid
import lxml.etree
import lxml.ElementInclude
XSL_DB2HTML = '/usr/share/yelp-xsl/xslt/docbook/html/db2html.xsl'
XSL_DB2XHTML = '/usr/share/yelp-xsl/xslt/docbook/html/db2xhtml.xsl'
XSL_MALCACHE = '/usr/share/yelp-xsl/xslt/mallard/cache/mal-cache.xsl'
XSL_MAL2HTML = '/usr/share/yelp-xsl/xslt/mallard/html/mal2html.xsl'
XSL_MAL2XHTML = '/usr/share/yelp-xsl/xslt/mallard/html/mal2xhtml.xsl'
XSL_MAL_OPF='/usr/share/yelp-tools/xslt/mal-opf.xsl'
XSL_MAL_NCX='/usr/share/yelp-tools/xslt/mal-ncx.xsl'
YELP_JS_DIR = '/usr/share/yelp-xsl/js'
XSLCOMMON = ('''
<xsl:variable name="yelp.internal.datadir" select="'{intdatadir}'"/>
<xsl:param name="html.css.root" select="$yelp.internal.datadir"/>
<xsl:param name="html.js.root" select="$yelp.internal.datadir"/>
{includes}
<xsl:template name="html.css">
<xsl:param name="node" select="."/>
<xsl:variable name="yelp.locale">
<xsl:choose>
<xsl:when test="$node/@xml:lang != ''">
<xsl:value-of select="$node/@xml:lang"/>
</xsl:when>
<xsl:when test="$node/@lang != ''">
<xsl:value-of select="$node/@lang"/>
</xsl:when>
<xsl:otherwise>
<xsl:text>C</xsl:text>
</xsl:otherwise>
</xsl:choose>
</xsl:variable>
<exsl:document href="{{$yelp.internal.datadir}}{{$yelp.locale}}.css" method="text">
<xsl:call-template name="html.css.content">
<xsl:with-param name="node" select="$node"/>
<xsl:with-param name="direction">
<xsl:call-template name="l10n.direction">
<xsl:with-param name="lang" select="$yelp.locale"/>
</xsl:call-template>
</xsl:with-param>
</xsl:call-template>
</exsl:document>
<link rel="stylesheet" type="text/css" href="{{$html.css.root}}{{$yelp.locale}}.css"/>
</xsl:template>
<xsl:template name="html.js.script">
<xsl:param name="node" select="."/>
<exsl:document href="{{$yelp.internal.datadir}}yelp.js" method="text">
<xsl:call-template name="html.js.content">
<xsl:with-param name="node" select="$node"/>
</xsl:call-template>
</exsl:document>
<script type="text/javascript" src="{{$html.js.root}}yelp.js"/>
</xsl:template>
''')
DB2HTML = ('''
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:exsl="http://exslt.org/common"
xmlns="http://www.w3.org/1999/xhtml"
extension-element-prefixes="exsl"
version="1.0">
<xsl:import href="file://{xslfile}"/>
'''
+ XSLCOMMON +
'''
</xsl:stylesheet>
''')
MAL2HTML = ('''
<xsl:stylesheet
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:mal="http://projectmallard.org/1.0/"
xmlns:cache="http://projectmallard.org/cache/1.0/"
xmlns:exsl="http://exslt.org/common"
xmlns="http://www.w3.org/1999/xhtml"
exclude-result-prefixes="mal cache"
extension-element-prefixes="exsl"
version="1.0">
<xsl:import href="file://{xslfile}"/>
<xsl:param name="mal.cache.file" select="'file://{cachefile}'"/>
'''
+ XSLCOMMON +
'''
<xsl:template match="/">
<xsl:for-each select="cache:cache/mal:page | cache:cache/mal:stack">
<xsl:variable name="href" select="@cache:href"/>
<xsl:for-each select="document(@cache:href)">
<xsl:for-each select="mal:page | mal:stack/mal:page">
<xsl:call-template name="html.output"/>
</xsl:for-each>
</xsl:for-each>
</xsl:for-each>
</xsl:template>
</xsl:stylesheet>
''')
class InputFile:
def __init__(self, filepath, filename, sitedir=None):
self.filepath = filepath
self.filename = filename
self.absfile = os.path.join(filepath, filename)
self.absdir = os.path.dirname(self.absfile)
self.sitedir = sitedir or ''
self.sitefilename = self.sitedir + self.filename
class PathResolver(lxml.etree.Resolver):
def __init__(self, srcdir, path):
if srcdir.endswith('/'):
self.srcdir = srcdir
else:
self.srcdir = srcdir + '/'
self.path = path
def resolve(self, uri, id, context):
if os.path.exists(uri):
return self.resolve_filename(uri, context)
if uri.startswith(self.srcdir):
ref = uri[len(self.srcdir):]
else:
ref = uri
for p in self.path:
tryfile = os.path.join(p, ref)
if os.path.exists(tryfile):
return self.resolve_filename(tryfile, context)
return None
class Builder:
name = None
desc = None
blurb = None
formats = []
arguments = []
postblurb = None
config = None
def __init__(self, yelpbuild):
self.yelpbuild = yelpbuild
self.options = {}
self.fileargs = []
self.tmpdir = None
def __del__(self):
if self.tmpdir is not None:
shutil.rmtree(self.tmpdir)
self.tmpdir = None
def parse_args(self, args):
while len(args) > 0:
argdef = None
if args[0].startswith('--'):
for arg_ in self.arguments:
if args[0] == '--' + arg_[0]:
argdef = arg_
break
if argdef is None:
self.print_help()
return 1
elif args[0].startswith('-'):
for arg_ in self.arguments:
if args[0] == arg_[1]:
argdef = arg_
break
if argdef is None:
self.print_help()
return 1
if argdef is not None:
takesarg = (argdef[2] is not None)
if takesarg:
if len(args) < 2:
self.print_help()
return 1
self.options.setdefault(argdef[0], [])
self.options[argdef[0]].append(args[1])
args = args[2:]
else:
self.options[argdef[0]] = True
args = args[1:]
else:
self.fileargs.append(args[0])
args = args[1:]
cfgfile = None
if len(self.fileargs) > 0:
cfgfile = os.path.join(os.path.dirname(self.fileargs[0]), '.yelp-tools.cfg')
if not os.path.exists(cfgfile):
cfgfile = None
if cfgfile is None:
cfgfile = os.path.join(os.getcwd(), '.yelp-tools.cfg')
if os.path.exists(cfgfile):
self.config = configparser.ConfigParser()
try:
self.config.read(cfgfile)
except Exception as e:
print(e, file=sys.stderr)
sys.exit(1)
return 0
def get_option_bool(self, arg):
if arg in self.options:
return self.options[arg] == True
if self.config is not None:
val = self.config.get('build:' + self.name, arg, fallback=None)
if val is not None:
return (val == 'true')
val = self.config.get('build', arg, fallback=None)
if val is not None:
return (val == 'true')
val = self.config.get('default', arg, fallback=None)
if val is not None:
return (val == 'true')
return False
def get_option_str(self, arg):
if arg in self.options:
if isinstance(self.options[arg], list):
return self.options[arg][-1]
if self.config is not None:
val = self.config.get('build:' + self.name, arg, fallback=None)
if val is not None:
return val
val = self.config.get('build', arg, fallback=None)
if val is not None:
return val
val = self.config.get('default', arg, fallback=None)
if val is not None:
return val
return None
def get_option_list(self, arg):
if arg in self.options:
if isinstance(self.options[arg], list):
ret = []
for opt in self.options[arg]:
ret.extend(opt.replace(',', ' ').split())
return ret
if self.config is not None:
val = self.config.get('build:' + self.name, arg, fallback=None)
if val is not None:
return val.replace(',', ' ').split()
val = self.config.get('build', arg, fallback=None)
if val is not None:
return val.replace(',', ' ').split()
val = self.config.get('default', arg, fallback=None)
if val is not None:
return val.replace(',', ' ').split()
return None
def get_xml(self, infile, path):
parser = lxml.etree.XMLParser()
parser.resolvers.add(PathResolver(os.path.realpath(infile.absdir), path))
tree = lxml.etree.parse(infile.absfile, parser=parser)
def pathloader(href, parse, encoding=None):
usefile = os.path.join(infile.absdir, href)
if not os.path.exists(href):
usefile = None
if usefile is None:
absdir = infile.absdir
if not absdir.endswith('/'):
absdir = absdir + '/'
ref = href
if ref.startswith(absdir):
ref = ref[len(absdir):]
for p in path:
tryfile = os.path.join(p, ref)
if os.path.exists(tryfile):
usefile = tryfile
break
if usefile is not None:
if parse == 'xml':
return lxml.etree.parse(usefile, parser=parser).getroot()
elif parse == 'text':
return open(usefile).read()
return None
lxml.ElementInclude.include(tree, loader=pathloader)
return tree
def iter_files(self, sitedir=None):
issite = self.get_option_bool('site')
if len(self.fileargs) == 0:
self.fileargs.append('.')
for filearg in self.fileargs:
if os.path.isdir(filearg):
if issite:
for infile in self.iter_site(filearg, '/'):
yield infile
else:
for fname in os.listdir(filearg):
if fname.endswith('.page'):
yield InputFile(filearg, fname)
else:
if issite:
# FIXME: should do some normalization here, I guess.
# It's hard to get this perfect without a defined start dir
yield InputFile(os.getcwd(), filearg, '/' + os.path.dirname(filearg))
else:
yield InputFile(os.getcwd(), filearg)
def iter_site(self, filepath, sitedir):
for fname in os.listdir(filepath):
newpath = os.path.join(filepath, fname)
if os.path.isdir(newpath):
# FIXME https://github.com/projectmallard/pintail/issues/36
if fname == '__pintail__':
continue
for infile in self.iter_site(newpath, sitedir + fname + '/'):
yield infile
elif fname.endswith('.page'):
yield InputFile(filepath, fname, sitedir)
def create_tmpdir(self):
if self.tmpdir is None:
self.tmpdir = tempfile.mkdtemp()
def print_help(self):
print('Usage: yelp-build ' + self.name + ' [OPTIONS] [FILES]')
print('Formats: ' + ' '.join(self.formats) + '\n')
#FIXME: prettify names of formats
if self.blurb is not None:
print(self.blurb + '\n')
print('Options:')
maxarglen = 2
args = []
for arg in self.arguments:
argkey = '--' + arg[0]
if arg[1] is not None:
argkey = arg[1] + ', ' + argkey
if arg[2] is not None:
argkey = argkey + ' ' + arg[2]
args.append((argkey, arg[3]))
for arg in args:
maxarglen = max(maxarglen, len(arg[0]) + 1)
for arg in args:
print(' ' + (arg[0]).ljust(maxarglen) + ' ' + arg[1])
if self.postblurb is not None:
print(self.postblurb)
def main(self, args):
pass
class CacheBuilder (Builder):
name = 'cache'
desc = 'Convert a Mallard cache file'
blurb = ('Create a Mallard cache file from the page files FILES.\n' +
'If FILES contains directories, all .page files in those\n' +
'directories will be used.')
formats = ['mallard']
arguments = [
('help', '-h', None, 'Show this help and exit'),
('output', '-o', 'OUT', 'Output files in the directory OUT'),
('path', '-p', 'PATH', 'Extra directories to search for files'),
('site', '-s', None, 'Treat pages as belonging to a Mallard site')
]
def build_cache_in(self, filename):
with open(filename, 'w') as cachein:
print('<cache:cache xmlns:cache="http://projectmallard.org/cache/1.0/"' +
' xmlns:site="http://projectmallard.org/site/1.0/"'
' xmlns="http://projectmallard.org/1.0/">',
file=cachein)
for infile in self.iter_files():
if infile.filename.endswith('.page'):
page = '<page'
elif infile.filename.endswith('.stack'):
page = '<stack'
else:
continue
page += ' cache:href="file://' + urllib.parse.quote(os.path.realpath(infile.absfile)) + '"'
if self.get_option_bool('site'):
page += ' site:dir="' + infile.sitedir + '"'
page += '/>'
print(page, file=cachein)
print('</cache:cache>', file=cachein)
def main(self, args, output=None, path=None):
if self.parse_args(args) != 0:
return 1
if 'help' in self.options:
self.print_help()
return 0
retcode = 0
self.create_tmpdir()
cacheinfile = os.path.join(self.tmpdir, 'index.cache.in')
self.build_cache_in(cacheinfile)
if output is None:
output = self.get_option_str('output')
if output is None:
output = 'index.cache'
if path is None:
path = self.get_option_list('path')
if path is None:
path = ':'
else:
path = ':'.join(path)
retcode = subprocess.call(['xsltproc', '--xinclude', '-o', output,
'--path', path,
XSL_MALCACHE, cacheinfile])
return retcode
class XhtmlBuilder (Builder):
name = 'xhtml'
desc = 'Convert input files to XHTML'
blurb = ('Create XHTML output from the input files FILES.\n' +
'FILES can be DocBook files, Mallard page files,\n' +
'or directories containing Mallard page files.')
formats = ['docbook4', 'docbook5', 'mallard']
arguments = [
('help', '-h', None, 'Show this help and exit'),
('cache', '-c', 'CACHE', 'Use the existing Mallard cache CACHE'),
('output', '-o', 'OUT', 'Output files in the directory OUT'),
('xsl', '-x', 'CUSTOM', 'Import the custom XSLT file CUSTOM'),
('path', '-p', 'PATH', 'Extra directories to search for files'),
('ignore', '-i', None, 'Ignore missing media files')
]
def __init__(self, yelpbuild, xhtml=True, epub=False):
super().__init__(yelpbuild)
self.mal2html = None
self.db2html = None
self.xhtml = xhtml
self.epub = epub
if self.epub:
self.intdatadir = 'yelp'
else:
self.intdatadir = ''
self.cacheinfile = None
def build_mallard_all(self, cache=None, output=None, xsl=None, path=None):
if self.mal2html is not None:
# We build all the pages on the first call, because it's faster
return 0
if path is None:
path = self.get_option_list('path')
self.create_tmpdir()
if cache is None:
cachefile = self.get_option_str('cache')
else:
cachefile = cache
cachebuilder = CacheBuilder(self.yelpbuild)
if cachefile is None:
cachefile = os.path.join(self.tmpdir, 'index.cache')
retcode = cachebuilder.main(self.fileargs, output=cachefile, path=path)
if retcode != 0:
return retcode
self.cacheinfile = cachefile
else:
cachefile = os.path.realpath(cachefile)
self.cacheinfile = os.path.join(self.tmpdir, 'index.cache.in')
cachebuilder.parse_args(self.fileargs)
cachebuilder.build_cache_in(self.cacheinfile)
self.mal2html = os.path.join(self.tmpdir, 'mal2html.xsl')
with open(self.mal2html, 'w') as xslout:
if self.xhtml:
xslfile = XSL_MAL2XHTML
else:
xslfile = XSL_MAL2HTML
includes = ''
if xsl is None:
customxsl = self.get_option_str('xsl')
else:
customxsl = xsl
if customxsl is not None:
customxsl = urllib.parse.quote(os.path.realpath(customxsl))
includes += '<xsl:include href="file://' + customxsl + '"/>'
if self.epub:
includes += '''<xsl:param name="mal.if.target" select="'target:epub target:html target:xhtml'"/>'''
includes += '''<xsl:template mode="html.header.mode" match="mal:page"/>'''
includes += '''<xsl:template mode="html.footer.mode" match="mal:page"/>'''
xslout.write(MAL2HTML.format(xslfile=xslfile,
cachefile=cachefile,
includes=includes,
intdatadir=self.intdatadir))
if output is None:
output = self.get_option_str('output')
if output is None:
output = os.getcwd()
else:
if not os.path.isdir(output):
print('Output must be a directory', file=sys.stderr)
return 1
if not output.endswith('/'):
# xsltproc is picky about this
output = output + '/'
if path is None:
pathstr = ':'
else:
pathstr = ':'.join(path)
retcode = subprocess.call(['xsltproc', '--xinclude', '-o', output,
'--path', pathstr,
'--stringparam', 'mal.cache.file', cachefile,
self.mal2html, self.cacheinfile])
return retcode
def build_docbook(self, infile, output=None, xsl=None, path=None):
if self.db2html is None:
self.create_tmpdir()
self.db2html = os.path.join(self.tmpdir, 'db2html.xsl')
with open(self.db2html, 'w') as xslout:
if self.xhtml:
xslfile = XSL_DB2XHTML
else:
xslfile = XSL_DB2HTML
includes = ''
if xsl is not None:
customxsl = xsl
else:
customxsl = self.get_option_str('xsl')
if customxsl is not None:
customxsl = urllib.parse.quote(os.path.realpath(customxsl))
includes += '<xsl:include href="file://' + customxsl + '"/>'
xslout.write(DB2HTML.format(xslfile=xslfile,
includes=includes,
intdatadir=self.intdatadir))
if output is None:
output = self.get_option_str('output')
if output is None:
output = os.getcwd()
else:
if not os.path.isdir(output):
print('Output must be a directory', file=sys.stderr)
return 1
if not output.endswith('/'):
# xsltproc is picky about this
output = output + '/'
if path is None:
path = self.get_option_list('path')
if path is None:
pathstr = ':'
else:
pathstr = ':'.join(path)
retcode = subprocess.call(['xsltproc', '--xinclude', '-o', output,
'--path', pathstr,
self.db2html, infile.absfile])
return retcode
def main(self, args, cache=None, output=None, xsl=None, path=None, ignore=None):
if self.parse_args(args) != 0:
return 1
if 'help' in self.options:
self.print_help()
return 0
if path is None:
pathopt = self.get_option_list('path')
else:
pathopt = path
path = []
if pathopt is not None:
for p in pathopt:
path.extend(p.split(':'))
if output is None:
output = self.get_option_str('output')
srcs = {}
for infile in self.iter_files():
if infile.filename.endswith('.page') or infile.filename.endswith('.stack'):
retcode = self.build_mallard_all(cache=cache, output=output, xsl=xsl, path=path)
if retcode != 0:
return retcode
if output is not None:
tree = self.get_xml(infile, path)
if tree is None:
return 1
for el in tree.xpath('//*[@src]'):
src = el.get('src')
srcs.setdefault(src, [])
orig = os.path.join(os.path.realpath(infile.absdir), src)
if orig not in srcs[src]:
srcs[src].append(orig)
elif infile.filename.endswith('.docbook') or infile.filename.endswith('.xml'):
retcode = self.build_docbook(infile, output=output, xsl=xsl, path=path)
if retcode != 0:
return retcode
if output is not None:
tree = self.get_xml(infile, path)
if tree is None:
return 1
for el in tree.xpath('//*[@fileref]'):
src = el.get('fileref')
srcs.setdefault(src, [])
orig = os.path.join(os.path.realpath(infile.absdir), src)
if orig not in srcs[src]:
srcs[src].append(orig)
else:
print('Error: No builder for ' + infile.filename)
return 1
if ignore is None:
ignore = self.get_option_bool('ignore')
tocopy = {}
for src in srcs:
useorig = None
for orig in srcs[src]:
if os.path.exists(orig):
if useorig is None:
useorig = orig
else:
print('Warning: Multiple sources for ' + src + '. Using first.',
file=sys.stderr)
if useorig is None:
for p in path:
tryorig = os.path.join(p, src)
if os.path.exists(tryorig):
useorig = tryorig
break
if useorig is None:
if ignore:
print('Warning: No source found for ' + src, file=sys.stderr)
else:
print('Error: No source found for ' + src, file=sys.stderr)
return 1
if useorig is not None:
destfile = os.path.join(output, src)
destdir = os.path.dirname(destfile)
os.makedirs(destdir, exist_ok=True)
shutil.copyfile(useorig, destfile)
if output is None:
shutil.copyfile(os.path.join(YELP_JS_DIR, 'highlight.pack.js'),
os.path.join(self.intdatadir, 'highlight.pack.js'))
else:
shutil.copyfile(os.path.join(YELP_JS_DIR, 'highlight.pack.js'),
os.path.join(output, self.intdatadir, 'highlight.pack.js'))
return 0
class HtmlBuilder (Builder):
name = 'html'
desc = 'Convert input files to HTML'
blurb = ('Create HTML output from the input files FILES.\n' +
'FILES can be DocBook files, Mallard page files,\n' +
'or directories containing Mallard page files.')
formats = ['docbook4', 'docbook5', 'mallard']
arguments = [
('help', '-h', None, 'Show this help and exit'),
('cache', '-c', 'CACHE', 'Use the existing Mallard cache CACHE'),
('output', '-o', 'OUT', 'Output files in the directory OUT'),
('xsl', '-x', 'CUSTOM', 'Import the custom XSLT file CUSTOM'),
('path', '-p', 'PATH', 'Extra directories to search for files'),
('ignore', '-i', None, 'Ignore missing media files')
]
def __init__(self, yelpbuild):
super().__init__(yelpbuild)
self.xhtmlbuilder = XhtmlBuilder(yelpbuild, xhtml=False)
def main(self, args):
if self.parse_args(args) != 0:
return 1
if 'help' in self.options:
self.print_help()
return 0
return self.xhtmlbuilder.main(args)
class EpubBuilder (Builder):
name = 'epub'
desc = 'Create an EPUB file for Mallard'
blurb = ('Create an EPUB file from the Mallard page files FILES')
formats = ['mallard']
arguments = [
('help', '-h', None, 'Show this help and exit'),
('cache', '-c', 'CACHE', 'Use the existing Mallard cache CACHE'),
('output', '-o', 'OUT', 'Output files in the directory OUT'),
('xsl', '-x', 'CUSTOM', 'Import the custom XSLT file CUSTOM'),
('path', '-p', 'PATH', 'Extra directories to search for files'),
('ignore', '-i', None, 'Ignore missing media files'),
('nozip', None, None, 'Do not zip the output directory')
]
def __init__(self, yelpbuild):
super().__init__(yelpbuild)
def main(self, args):
if self.parse_args(args) != 0:
return 1
if 'help' in self.options:
self.print_help()
return 0
output = self.get_option_str('output')
nozip = self.get_option_bool('nozip')
if nozip:
if output is None:
output = 'EPUB'
if os.path.isfile(output):
print('Error: Output must be a directory', file=sys.stderr)
sys.exit(1)
epubdir = output
else:
self.create_tmpdir()
if output is None:
output = 'index.epub'
if os.path.isdir(output):
print('Error: Output must be a file', file=sys.stderr)
sys.exit(1)
epubdir = os.path.join(self.tmpdir, 'EPUB')
os.makedirs(epubdir, exist_ok=True)
os.makedirs(os.path.join(epubdir, 'OPS', 'yelp'), exist_ok=True)
xhtmlbuilder = XhtmlBuilder(self.yelpbuild, epub=True)
retcode = xhtmlbuilder.main(self.fileargs,
cache=self.get_option_str('cache'),
output=os.path.join(epubdir, 'OPS'),
xsl=self.get_option_str('xsl'),
path=self.get_option_list('path'),
ignore=self.get_option_bool('ignore'))
if retcode != 0:
return retcode
with open(os.path.join(epubdir, 'mimetype'), 'w') as fd:
fd.write('application/epub+zip\n')
os.makedirs(os.path.join(epubdir, 'META-INF'), exist_ok=True)
with open(os.path.join(epubdir, 'META-INF', 'container.xml'), 'w') as fd:
fd.write('<?xml version="1.0" encoding="UTF-8"?>')
fd.write('<container version="1.0" xmlns="urn:oasis:names:tc:opendocument:xmlns:container">')
fd.write('<rootfiles>')
fd.write('<rootfile full-path="OPS/opf.opf" media-type="application/oebps-package+xml"/>')
fd.write('</rootfiles>')
fd.write('</container>\n')
path = self.get_option_list('path')
if path is None:
pathstr = ':'
else:
pathstr = ':'.join(path)
epubid = str(uuid.uuid4())
opfdata = ''
for fname in os.listdir(os.path.join(epubdir, 'OPS', 'yelp')):
opfdata += ' OPS/yelp/' + urllib.parse.quote(fname)
retcode = subprocess.call(['xsltproc', '--xinclude',
'-o', os.path.join(epubdir, 'OPS', 'opf.opf'),
'--path', pathstr,
'--stringparam', 'opf.id', epubid,
'--stringparam', 'opf.data', opfdata,
XSL_MAL_OPF, xhtmlbuilder.cacheinfile])
if retcode != 0:
return retcode
retcode = subprocess.call(['xsltproc', '--xinclude',
'-o', os.path.join(epubdir, 'OPS', 'ncx.ncx'),
'--path', pathstr,
'--stringparam', 'ncx.id', epubid,
XSL_MAL_NCX, xhtmlbuilder.cacheinfile])
if retcode != 0:
return retcode
if not nozip:
retcode = subprocess.call(['zip', '-q', '-r', os.path.realpath(output),
'mimetype', 'META-INF', 'OPS'],
cwd=os.path.realpath(epubdir))
if retcode != 0:
return retcode
return 0
class YelpBuild:
def __init__(self):
pass
def main(self):
if len(sys.argv) < 2:
self.print_usage()
return 1
builder = None
for cls in Builder.__subclasses__():
if sys.argv[1] == cls.name:
builder = cls(self)
if builder is None:
print('Unrecognized command: ' + sys.argv[1], file=sys.stderr)
return 1
return builder.main(sys.argv[2:])
def print_usage(self):
print('Usage: yelp-builder <COMMAND> [OPTIONS] [FILES]')
namelen = 2
builders = []
for cls in sorted(Builder.__subclasses__(), key=(lambda cls: cls.name or '')):
namelen = max(namelen, len(cls.name) + 2)
builders.append(cls)
print('\nCommands:')
for cls in builders:
print(' ' + cls.name.ljust(namelen) + cls.desc)
if __name__ == '__main__':
try:
sys.exit(YelpBuild().main())
except KeyboardInterrupt:
sys.exit(1)