mizuho 0.9.10 → 0.9.11
Sign up to get free protection for your applications and to get access to all the features.
- data/README.markdown +18 -2
- data/asciidoc/BUGS +3 -6
- data/asciidoc/BUGS.txt +0 -3
- data/asciidoc/CHANGELOG +660 -397
- data/asciidoc/CHANGELOG.txt +237 -2
- data/asciidoc/INSTALL +14 -14
- data/asciidoc/MANIFEST +2 -0
- data/asciidoc/Makefile.in +9 -1
- data/asciidoc/README +2 -2
- data/asciidoc/a2x.py +101 -43
- data/asciidoc/asciidoc.conf +18 -11
- data/asciidoc/asciidoc.py +615 -260
- data/asciidoc/common.aap +2 -2
- data/asciidoc/configure +9 -9
- data/asciidoc/configure.ac +1 -1
- data/asciidoc/doc/a2x.1 +34 -4
- data/asciidoc/doc/a2x.1.txt +12 -0
- data/asciidoc/doc/article.pdf +0 -0
- data/asciidoc/doc/asciidoc.1 +73 -29
- data/asciidoc/doc/asciidoc.1.txt +56 -30
- data/asciidoc/doc/asciidoc.dict +23 -2
- data/asciidoc/doc/asciidoc.txt +468 -327
- data/asciidoc/doc/book.epub +0 -0
- data/asciidoc/doc/faq.txt +201 -25
- data/asciidoc/doc/latex-filter.pdf +0 -0
- data/asciidoc/doc/music-filter.pdf +0 -0
- data/asciidoc/doc/publishing-ebooks-with-asciidoc.txt +1 -1
- data/asciidoc/doc/source-highlight-filter.pdf +0 -0
- data/asciidoc/doc/source-highlight-filter.txt +48 -37
- data/asciidoc/docbook45.conf +4 -4
- data/asciidoc/examples/website/ASCIIMathML.js +938 -0
- data/asciidoc/examples/website/CHANGELOG.txt +3056 -0
- data/asciidoc/examples/website/INSTALL.txt +227 -0
- data/asciidoc/examples/website/LaTeXMathML.js +1223 -0
- data/asciidoc/examples/website/README-website.txt +29 -0
- data/asciidoc/examples/website/README.txt +35 -0
- data/asciidoc/examples/website/a2x.1.txt +358 -0
- data/asciidoc/examples/website/asciidoc-docbook-xsl.txt +65 -0
- data/asciidoc/examples/website/asciidoc-graphviz-sample.txt +170 -0
- data/asciidoc/examples/website/asciidoc.css +533 -0
- data/asciidoc/examples/website/asciidoc.js +189 -0
- data/asciidoc/examples/website/asciidocapi.txt +189 -0
- data/asciidoc/examples/website/asciimathml.txt +61 -0
- data/asciidoc/examples/website/build-website.sh +25 -0
- data/asciidoc/examples/website/customers.csv +18 -0
- data/asciidoc/examples/website/epub-notes.txt +210 -0
- data/asciidoc/examples/website/faq.txt +1298 -0
- data/asciidoc/examples/website/index.txt +502 -0
- data/asciidoc/examples/website/latex-backend.txt +192 -0
- data/asciidoc/examples/website/latex-bugs.txt +134 -0
- data/asciidoc/examples/website/latex-filter.txt +196 -0
- data/asciidoc/examples/website/latexmathml.txt +41 -0
- data/asciidoc/examples/website/layout1.conf +153 -0
- data/asciidoc/examples/website/layout1.css +65 -0
- data/asciidoc/examples/website/layout2.conf +153 -0
- data/asciidoc/examples/website/layout2.css +83 -0
- data/asciidoc/examples/website/main.aap +159 -0
- data/asciidoc/examples/website/manpage.txt +197 -0
- data/asciidoc/examples/website/music-filter.txt +148 -0
- data/asciidoc/examples/website/newlists.txt +40 -0
- data/asciidoc/examples/website/newtables.txt +743 -0
- data/asciidoc/examples/website/plugins.txt +91 -0
- data/asciidoc/examples/website/publishing-ebooks-with-asciidoc.txt +398 -0
- data/asciidoc/examples/website/slidy-example.txt +167 -0
- data/asciidoc/examples/website/slidy.txt +113 -0
- data/asciidoc/examples/website/source-highlight-filter.txt +239 -0
- data/asciidoc/examples/website/support.txt +5 -0
- data/asciidoc/examples/website/testasciidoc.txt +231 -0
- data/asciidoc/examples/website/userguide.txt +5991 -0
- data/asciidoc/examples/website/version83.txt +37 -0
- data/asciidoc/examples/website/xhtml11-quirks.css +43 -0
- data/asciidoc/filters/latex/latex2png.py +28 -12
- data/asciidoc/filters/music/music2png.py +22 -6
- data/asciidoc/filters/source/source-highlight-filter.conf +7 -5
- data/asciidoc/help.conf +147 -131
- data/asciidoc/html4.conf +1 -0
- data/asciidoc/html5.conf +37 -39
- data/asciidoc/javascripts/asciidoc.js +3 -3
- data/asciidoc/lang-de.conf +4 -0
- data/asciidoc/lang-es.conf +2 -0
- data/asciidoc/lang-fr.conf +1 -1
- data/asciidoc/lang-hu.conf +2 -0
- data/asciidoc/lang-it.conf +2 -0
- data/asciidoc/lang-nl.conf +5 -0
- data/asciidoc/lang-pt-BR.conf +2 -0
- data/asciidoc/lang-ru.conf +2 -3
- data/asciidoc/latex.conf +2 -2
- data/asciidoc/slidy.conf +4 -2
- data/asciidoc/stylesheets/asciidoc.css +29 -4
- data/asciidoc/stylesheets/docbook-xsl.css +12 -5
- data/asciidoc/stylesheets/toc2.css +1 -0
- data/asciidoc/stylesheets/xhtml11-quirks.css +1 -1
- data/asciidoc/tests/data/lang-de-man-test.txt +21 -0
- data/asciidoc/tests/data/lang-en-man-test.txt +21 -0
- data/asciidoc/tests/data/lang-es-man-test.txt +21 -0
- data/asciidoc/tests/data/lang-fr-man-test.txt +21 -0
- data/asciidoc/tests/data/lang-hu-man-test.txt +21 -0
- data/asciidoc/tests/data/lang-it-man-test.txt +21 -0
- data/asciidoc/tests/data/lang-it-test.txt +106 -0
- data/asciidoc/tests/data/lang-nl-man-test.txt +21 -0
- data/asciidoc/tests/data/lang-pt-BR-man-test.txt +21 -0
- data/asciidoc/tests/data/lang-ru-man-test.txt +21 -0
- data/asciidoc/tests/data/lang-uk-man-test.txt +21 -0
- data/asciidoc/tests/data/testcases.conf +10 -0
- data/asciidoc/tests/data/testcases.txt +40 -0
- data/asciidoc/tests/testasciidoc.conf +143 -17
- data/asciidoc/tests/testasciidoc.py +11 -2
- data/asciidoc/{stylesheets → themes/flask}/flask.css +0 -0
- data/asciidoc/{stylesheets → themes/volnitsky}/volnitsky.css +1 -1
- data/asciidoc/vim/ftdetect/asciidoc_filetype.vim +1 -1
- data/asciidoc/vim/syntax/asciidoc.vim +1 -1
- data/asciidoc/xhtml11-quirks.conf +2 -2
- data/asciidoc/xhtml11.conf +35 -37
- data/lib/mizuho.rb +1 -1
- data/lib/mizuho/generator.rb +3 -1
- data/source-highlight/darwin/source-highlight +0 -0
- data/templates/juvia.js +30 -5
- metadata +58 -9
- data/asciidoc/stylesheets/asciidoc-manpage.css +0 -18
- data/asciidoc/stylesheets/flask-manpage.css +0 -1
- data/asciidoc/stylesheets/volnitsky-manpage.css +0 -1
data/asciidoc/asciidoc.conf
CHANGED
@@ -15,6 +15,7 @@ newline=\r\n
|
|
15
15
|
backend-alias-html=xhtml11
|
16
16
|
backend-alias-docbook=docbook45
|
17
17
|
toclevels=2
|
18
|
+
toc-placement=auto
|
18
19
|
sectids=
|
19
20
|
iconsdir=./images/icons
|
20
21
|
encoding=UTF-8
|
@@ -25,6 +26,7 @@ encoding=UTF-8
|
|
25
26
|
# Uncomment to use deprecated quote attributes.
|
26
27
|
#deprecated-quotes=
|
27
28
|
empty=
|
29
|
+
sp=" "
|
28
30
|
# Attribute and AttributeList element patterns.
|
29
31
|
attributeentry-pattern=^:(?P<attrname>\w[^.]*?)(\.(?P<attrname2>.*?))?:(\s+(?P<attrvalue>.*))?$
|
30
32
|
attributelist-pattern=(?u)(^\[\[(?P<id>[\w_:][\w_:.-]*)(,(?P<reftext>.*?))?\]\]$)|(^\[(?P<attrlist>.*)\]$)
|
@@ -169,10 +171,10 @@ template::[paragraph-styles]
|
|
169
171
|
|
170
172
|
[paragraph-styles]
|
171
173
|
normal-style=template="paragraph"
|
172
|
-
verse-style=template="verseparagraph",posattrs=
|
173
|
-
quote-style=template="quoteparagraph",posattrs=
|
174
|
-
literal-style=template="literalparagraph",subs=
|
175
|
-
listing-style=template="listingparagraph",subs=
|
174
|
+
verse-style=template="verseparagraph",posattrs=("style","attribution","citetitle")
|
175
|
+
quote-style=template="quoteparagraph",posattrs=("style","attribution","citetitle")
|
176
|
+
literal-style=template="literalparagraph",subs=("verbatim",)
|
177
|
+
listing-style=template="listingparagraph",subs=("verbatim",)
|
176
178
|
NOTE-style=template="admonitionparagraph",name="note",caption="{note-caption}"
|
177
179
|
TIP-style=template="admonitionparagraph",name="tip",caption="{tip-caption}"
|
178
180
|
IMPORTANT-style=template="admonitionparagraph",name="important",caption="{important-caption}"
|
@@ -267,7 +269,7 @@ endif::no-inline-literal[]
|
|
267
269
|
# Block macros
|
268
270
|
#-------------
|
269
271
|
# Macros using default syntax.
|
270
|
-
(?u)^(?P<name>image|unfloat)::(?P<target>\S*?)(\[(?P<attrlist>.*?)\])$=#
|
272
|
+
(?u)^(?P<name>image|unfloat|toc)::(?P<target>\S*?)(\[(?P<attrlist>.*?)\])$=#
|
271
273
|
|
272
274
|
# Passthrough macros.
|
273
275
|
(?u)^(?P<name>pass)::(?P<subslist>\S*?)(\[(?P<passtext>.*?)\])$=#
|
@@ -276,8 +278,9 @@ endif::no-inline-literal[]
|
|
276
278
|
^<{3,}$=#pagebreak
|
277
279
|
^//(?P<passtext>[^/].*|)$=#comment[specialcharacters]
|
278
280
|
|
279
|
-
[unfloat-blockmacro]
|
280
281
|
# Implemented in HTML backends.
|
282
|
+
[unfloat-blockmacro]
|
283
|
+
[toc-blockmacro]
|
281
284
|
|
282
285
|
#-----------------
|
283
286
|
# Delimited blocks
|
@@ -285,6 +288,7 @@ endif::no-inline-literal[]
|
|
285
288
|
[blockdef-comment]
|
286
289
|
delimiter=^/{4,}$
|
287
290
|
options=skip
|
291
|
+
posattrs=style
|
288
292
|
|
289
293
|
[blockdef-sidebar]
|
290
294
|
delimiter=^\*{4,}$
|
@@ -309,7 +313,7 @@ template=passblock
|
|
309
313
|
# Default subs choosen for backward compatibility.
|
310
314
|
subs=attributes,macros
|
311
315
|
posattrs=style
|
312
|
-
pass-style=template="passblock",subs=
|
316
|
+
pass-style=template="passblock",subs=()
|
313
317
|
|
314
318
|
[blockdef-listing]
|
315
319
|
delimiter=^-{4,}$
|
@@ -510,12 +514,12 @@ posattrs=style
|
|
510
514
|
template=table
|
511
515
|
default-style=tags="default"
|
512
516
|
verse-style=tags="verse"
|
513
|
-
literal-style=tags="literal",subs=
|
517
|
+
literal-style=tags="literal",subs=("specialcharacters",)
|
514
518
|
emphasis-style=tags="emphasis"
|
515
519
|
strong-style=tags="strong"
|
516
520
|
monospaced-style=tags="monospaced"
|
517
521
|
header-style=tags="header"
|
518
|
-
asciidoc-style=tags="asciidoc",subs=
|
522
|
+
asciidoc-style=tags="asciidoc",subs=(),filter='"{python}" "{asciidoc-file}" -b {backend} {asciidoc-args}{lang? -a "lang={lang}@"}{icons? -a icons -a "iconsdir={iconsdir}"}{imagesdir? -a "imagesdir={imagesdir}"}{data-uri? -a data-uri} -a "indir={indir}"{trace? -a "trace={trace}"}{blockname? -a "blockname={blockname}"} -s -'
|
519
523
|
|
520
524
|
[tabledef-nested]
|
521
525
|
# Same as [tabledef-default] but with different delimiter and separator.
|
@@ -524,12 +528,12 @@ separator=((?<!\S)((?P<span>[\d.]+)(?P<op>[*+]))?(?P<align>[<\^>.]{,3})?(?P<styl
|
|
524
528
|
posattrs=style
|
525
529
|
template=table
|
526
530
|
verse-style=tags="verse"
|
527
|
-
literal-style=tags="literal",subs=
|
531
|
+
literal-style=tags="literal",subs=("specialcharacters",)
|
528
532
|
emphasis-style=tags="emphasis"
|
529
533
|
strong-style=tags="strong"
|
530
534
|
monospaced-style=tags="monospaced"
|
531
535
|
header-style=tags="header"
|
532
|
-
asciidoc-style=tags="asciidoc",subs=
|
536
|
+
asciidoc-style=tags="asciidoc",subs=(),filter='"{python}" "{asciidoc-file}" -b {backend} {asciidoc-args}{lang? -a "lang={lang}@"}{icons? -a icons -a "iconsdir={iconsdir}"}{imagesdir? -a "imagesdir={imagesdir}"}{data-uri? -a data-uri} -a "indir={indir}"{trace? -a "trace={trace}"}{blockname? -a "blockname={blockname}"} -s -'
|
533
537
|
|
534
538
|
#----------------------------------------
|
535
539
|
# Common block and macro markup templates
|
@@ -560,6 +564,9 @@ template::[pass-blockmacro]
|
|
560
564
|
|
|
561
565
|
template::[image-blockmacro]
|
562
566
|
|
567
|
+
[+docinfo]
|
568
|
+
# Blank section to suppress missing template warning.
|
569
|
+
|
563
570
|
#----------------------------------
|
564
571
|
# Default special section templates
|
565
572
|
#----------------------------------
|
data/asciidoc/asciidoc.py
CHANGED
@@ -6,12 +6,12 @@ Copyright (C) 2002-2010 Stuart Rackham. Free use of this software is granted
|
|
6
6
|
under the terms of the GNU General Public License (GPL).
|
7
7
|
"""
|
8
8
|
|
9
|
-
import sys, os, re, time, traceback, tempfile, subprocess, codecs, locale, unicodedata
|
9
|
+
import sys, os, re, time, traceback, tempfile, subprocess, codecs, locale, unicodedata, copy
|
10
10
|
|
11
11
|
### Used by asciidocapi.py ###
|
12
|
-
VERSION = '8.6.
|
12
|
+
VERSION = '8.6.7' # See CHANGLOG file for version history.
|
13
13
|
|
14
|
-
MIN_PYTHON_VERSION = 2.4
|
14
|
+
MIN_PYTHON_VERSION = '2.4' # Require this version of Python or better.
|
15
15
|
|
16
16
|
#---------------------------------------------------------------------------
|
17
17
|
# Program constants.
|
@@ -22,7 +22,7 @@ DEFAULT_DOCTYPE = 'article'
|
|
22
22
|
# definition subs entry.
|
23
23
|
SUBS_OPTIONS = ('specialcharacters','quotes','specialwords',
|
24
24
|
'replacements', 'attributes','macros','callouts','normal','verbatim',
|
25
|
-
'none','replacements2')
|
25
|
+
'none','replacements2','replacements3')
|
26
26
|
# Default value for unspecified subs and presubs configuration file entries.
|
27
27
|
SUBS_NORMAL = ('specialcharacters','quotes','attributes',
|
28
28
|
'specialwords','replacements','macros','replacements2')
|
@@ -133,7 +133,7 @@ class Trace(object):
|
|
133
133
|
"""
|
134
134
|
SUBS_NAMES = ('specialcharacters','quotes','specialwords',
|
135
135
|
'replacements', 'attributes','macros','callouts',
|
136
|
-
'replacements2')
|
136
|
+
'replacements2','replacements3')
|
137
137
|
def __init__(self):
|
138
138
|
self.name_re = '' # Regexp pattern to match trace names.
|
139
139
|
self.linenos = True
|
@@ -255,7 +255,7 @@ def safe():
|
|
255
255
|
return document.safe
|
256
256
|
|
257
257
|
def is_safe_file(fname, directory=None):
|
258
|
-
# A safe file must reside in directory
|
258
|
+
# A safe file must reside in 'directory' (defaults to the source
|
259
259
|
# file directory).
|
260
260
|
if directory is None:
|
261
261
|
if document.infile == '<stdin>':
|
@@ -273,15 +273,12 @@ def is_safe_file(fname, directory=None):
|
|
273
273
|
def safe_filename(fname, parentdir):
|
274
274
|
"""
|
275
275
|
Return file name which must reside in the parent file directory.
|
276
|
-
Return None if file is not
|
276
|
+
Return None if file is not safe.
|
277
277
|
"""
|
278
278
|
if not os.path.isabs(fname):
|
279
279
|
# Include files are relative to parent document
|
280
280
|
# directory.
|
281
281
|
fname = os.path.normpath(os.path.join(parentdir,fname))
|
282
|
-
if not os.path.isfile(fname):
|
283
|
-
message.warning('include file not found: %s' % fname)
|
284
|
-
return None
|
285
282
|
if not is_safe_file(fname, parentdir):
|
286
283
|
message.unsafe('include file: %s' % fname)
|
287
284
|
return None
|
@@ -319,16 +316,6 @@ def re_join(relist):
|
|
319
316
|
result = '('+result+')'
|
320
317
|
return result
|
321
318
|
|
322
|
-
def validate(value,rule,errmsg):
|
323
|
-
"""Validate value against rule expression. Throw EAsciiDoc exception with
|
324
|
-
errmsg if validation fails."""
|
325
|
-
try:
|
326
|
-
if not eval(rule.replace('$',str(value))):
|
327
|
-
raise EAsciiDoc,errmsg
|
328
|
-
except Exception:
|
329
|
-
raise EAsciiDoc,errmsg
|
330
|
-
return value
|
331
|
-
|
332
319
|
def lstrip_list(s):
|
333
320
|
"""
|
334
321
|
Return list with empty items from start of list removed.
|
@@ -386,6 +373,102 @@ def dovetail_tags(stag,content,etag):
|
|
386
373
|
include extraneous opening and closing line breaks."""
|
387
374
|
return dovetail(dovetail(stag,content), etag)
|
388
375
|
|
376
|
+
# The following functions are so we don't have to use the dangerous
|
377
|
+
# built-in eval() function.
|
378
|
+
if float(sys.version[:3]) >= 2.6 or sys.platform[:4] == 'java':
|
379
|
+
# Use AST module if CPython >= 2.6 or Jython.
|
380
|
+
import ast
|
381
|
+
from ast import literal_eval
|
382
|
+
|
383
|
+
def get_args(val):
|
384
|
+
d = {}
|
385
|
+
args = ast.parse("d(" + val + ")", mode='eval').body.args
|
386
|
+
i = 1
|
387
|
+
for arg in args:
|
388
|
+
if isinstance(arg, ast.Name):
|
389
|
+
d[str(i)] = literal_eval(arg.id)
|
390
|
+
else:
|
391
|
+
d[str(i)] = literal_eval(arg)
|
392
|
+
i += 1
|
393
|
+
return d
|
394
|
+
|
395
|
+
def get_kwargs(val):
|
396
|
+
d = {}
|
397
|
+
args = ast.parse("d(" + val + ")", mode='eval').body.keywords
|
398
|
+
for arg in args:
|
399
|
+
d[arg.arg] = literal_eval(arg.value)
|
400
|
+
return d
|
401
|
+
|
402
|
+
def parse_to_list(val):
|
403
|
+
values = ast.parse("[" + val + "]", mode='eval').body.elts
|
404
|
+
return [literal_eval(v) for v in values]
|
405
|
+
|
406
|
+
else: # Use deprecated CPython compiler module.
|
407
|
+
import compiler
|
408
|
+
from compiler.ast import Const, Dict, Expression, Name, Tuple, UnarySub, Keyword
|
409
|
+
|
410
|
+
# Code from:
|
411
|
+
# http://mail.python.org/pipermail/python-list/2009-September/1219992.html
|
412
|
+
# Modified to use compiler.ast.List as this module has a List
|
413
|
+
def literal_eval(node_or_string):
|
414
|
+
"""
|
415
|
+
Safely evaluate an expression node or a string containing a Python
|
416
|
+
expression. The string or node provided may only consist of the
|
417
|
+
following Python literal structures: strings, numbers, tuples,
|
418
|
+
lists, dicts, booleans, and None.
|
419
|
+
"""
|
420
|
+
_safe_names = {'None': None, 'True': True, 'False': False}
|
421
|
+
if isinstance(node_or_string, basestring):
|
422
|
+
node_or_string = compiler.parse(node_or_string, mode='eval')
|
423
|
+
if isinstance(node_or_string, Expression):
|
424
|
+
node_or_string = node_or_string.node
|
425
|
+
def _convert(node):
|
426
|
+
if isinstance(node, Const) and isinstance(node.value,
|
427
|
+
(basestring, int, float, long, complex)):
|
428
|
+
return node.value
|
429
|
+
elif isinstance(node, Tuple):
|
430
|
+
return tuple(map(_convert, node.nodes))
|
431
|
+
elif isinstance(node, compiler.ast.List):
|
432
|
+
return list(map(_convert, node.nodes))
|
433
|
+
elif isinstance(node, Dict):
|
434
|
+
return dict((_convert(k), _convert(v)) for k, v
|
435
|
+
in node.items)
|
436
|
+
elif isinstance(node, Name):
|
437
|
+
if node.name in _safe_names:
|
438
|
+
return _safe_names[node.name]
|
439
|
+
elif isinstance(node, UnarySub):
|
440
|
+
return -_convert(node.expr)
|
441
|
+
raise ValueError('malformed string')
|
442
|
+
return _convert(node_or_string)
|
443
|
+
|
444
|
+
def get_args(val):
|
445
|
+
d = {}
|
446
|
+
args = compiler.parse("d(" + val + ")", mode='eval').node.args
|
447
|
+
i = 1
|
448
|
+
for arg in args:
|
449
|
+
if isinstance(arg, Keyword):
|
450
|
+
break
|
451
|
+
d[str(i)] = literal_eval(arg)
|
452
|
+
i = i + 1
|
453
|
+
return d
|
454
|
+
|
455
|
+
def get_kwargs(val):
|
456
|
+
d = {}
|
457
|
+
args = compiler.parse("d(" + val + ")", mode='eval').node.args
|
458
|
+
i = 0
|
459
|
+
for arg in args:
|
460
|
+
if isinstance(arg, Keyword):
|
461
|
+
break
|
462
|
+
i += 1
|
463
|
+
args = args[i:]
|
464
|
+
for arg in args:
|
465
|
+
d[str(arg.name)] = literal_eval(arg.expr)
|
466
|
+
return d
|
467
|
+
|
468
|
+
def parse_to_list(val):
|
469
|
+
values = compiler.parse("[" + val + "]", mode='eval').node.asList()
|
470
|
+
return [literal_eval(v) for v in values]
|
471
|
+
|
389
472
|
def parse_attributes(attrs,dict):
|
390
473
|
"""Update a dictionary with name/value attributes from the attrs string.
|
391
474
|
The attrs string is a comma separated list of values and keyword name=value
|
@@ -414,9 +497,10 @@ def parse_attributes(attrs,dict):
|
|
414
497
|
dict['0'] = attrs
|
415
498
|
# Replace line separators with spaces so line spanning works.
|
416
499
|
s = re.sub(r'\s', ' ', attrs)
|
500
|
+
d = {}
|
417
501
|
try:
|
418
|
-
d
|
419
|
-
|
502
|
+
d.update(get_args(s))
|
503
|
+
d.update(get_kwargs(s))
|
420
504
|
for v in d.values():
|
421
505
|
if not (isinstance(v,str) or isinstance(v,int) or isinstance(v,float) or v is None):
|
422
506
|
raise Exception
|
@@ -426,7 +510,9 @@ def parse_attributes(attrs,dict):
|
|
426
510
|
s = map(lambda x: '"' + x.strip() + '"', s)
|
427
511
|
s = ','.join(s)
|
428
512
|
try:
|
429
|
-
d =
|
513
|
+
d = {}
|
514
|
+
d.update(get_args(s))
|
515
|
+
d.update(get_kwargs(s))
|
430
516
|
except Exception:
|
431
517
|
return # If there's a syntax error leave with {0}=attrs.
|
432
518
|
for k in d.keys(): # Drop any empty positional arguments.
|
@@ -444,7 +530,8 @@ def parse_named_attributes(s,attrs):
|
|
444
530
|
def f(**keywords): return keywords
|
445
531
|
|
446
532
|
try:
|
447
|
-
d =
|
533
|
+
d = {}
|
534
|
+
d = get_kwargs(s)
|
448
535
|
attrs.update(d)
|
449
536
|
return True
|
450
537
|
except Exception:
|
@@ -454,7 +541,7 @@ def parse_list(s):
|
|
454
541
|
"""Parse comma separated string of Python literals. Return a tuple of of
|
455
542
|
parsed values."""
|
456
543
|
try:
|
457
|
-
result =
|
544
|
+
result = tuple(parse_to_list(s))
|
458
545
|
except Exception:
|
459
546
|
raise EAsciiDoc,'malformed list: '+s
|
460
547
|
return result
|
@@ -712,15 +799,19 @@ def filter_lines(filter_cmd, lines, attrs={}):
|
|
712
799
|
message.warning('filter not found: %s' % cmd)
|
713
800
|
if found:
|
714
801
|
filter_cmd = '"' + found + '"' + mo.group('tail')
|
715
|
-
if
|
716
|
-
|
717
|
-
|
718
|
-
|
719
|
-
|
720
|
-
|
721
|
-
|
722
|
-
filter_cmd = 'ruby ' + filter_cmd
|
802
|
+
if found:
|
803
|
+
if cmd.endswith('.py'):
|
804
|
+
filter_cmd = '"%s" %s' % (document.attributes['python'],
|
805
|
+
filter_cmd)
|
806
|
+
elif cmd.endswith('.rb'):
|
807
|
+
filter_cmd = 'ruby ' + filter_cmd
|
808
|
+
|
723
809
|
message.verbose('filtering: ' + filter_cmd)
|
810
|
+
if os.name == 'nt':
|
811
|
+
# Remove redundant quoting -- this is not just
|
812
|
+
# cosmetic, unnecessary quoting appears to cause
|
813
|
+
# command line truncation.
|
814
|
+
filter_cmd = re.sub(r'"([^ ]+?)"', r'\1', filter_cmd)
|
724
815
|
try:
|
725
816
|
p = subprocess.Popen(filter_cmd, shell=True,
|
726
817
|
stdin=subprocess.PIPE, stdout=subprocess.PIPE)
|
@@ -791,14 +882,24 @@ def system(name, args, is_macro=False, attrs=None):
|
|
791
882
|
os.close(fd)
|
792
883
|
try:
|
793
884
|
cmd = args
|
794
|
-
cmd = cmd + (' > %s' % tmp)
|
885
|
+
cmd = cmd + (' > "%s"' % tmp)
|
795
886
|
if name == 'sys2':
|
796
887
|
cmd = cmd + ' 2>&1'
|
888
|
+
if os.name == 'nt':
|
889
|
+
# Remove redundant quoting -- this is not just
|
890
|
+
# cosmetic, unnecessary quoting appears to cause
|
891
|
+
# command line truncation.
|
892
|
+
cmd = re.sub(r'"([^ ]+?)"', r'\1', cmd)
|
893
|
+
message.verbose('shelling: %s' % cmd)
|
797
894
|
if os.system(cmd):
|
798
895
|
message.warning('%s: non-zero exit status' % syntax)
|
799
896
|
try:
|
800
897
|
if os.path.isfile(tmp):
|
801
|
-
|
898
|
+
f = open(tmp)
|
899
|
+
try:
|
900
|
+
lines = [s.rstrip() for s in f]
|
901
|
+
finally:
|
902
|
+
f.close()
|
802
903
|
else:
|
803
904
|
lines = []
|
804
905
|
except Exception:
|
@@ -867,7 +968,11 @@ def system(name, args, is_macro=False, attrs=None):
|
|
867
968
|
elif not is_safe_file(args):
|
868
969
|
message.unsafe(syntax)
|
869
970
|
else:
|
870
|
-
|
971
|
+
f = open(args)
|
972
|
+
try:
|
973
|
+
result = [s.rstrip() for s in f]
|
974
|
+
finally:
|
975
|
+
f.close()
|
871
976
|
if result:
|
872
977
|
result = subs_attrs(result)
|
873
978
|
result = separator.join(result)
|
@@ -1258,7 +1363,7 @@ class Lex:
|
|
1258
1363
|
result = subs_quotes(result)
|
1259
1364
|
elif o == 'specialwords':
|
1260
1365
|
result = config.subs_specialwords(result)
|
1261
|
-
elif o in ('replacements','replacements2'):
|
1366
|
+
elif o in ('replacements','replacements2','replacements3'):
|
1262
1367
|
result = config.subs_replacements(result,o)
|
1263
1368
|
elif o == 'macros':
|
1264
1369
|
result = macros.subs(result)
|
@@ -1349,7 +1454,10 @@ class Document(object):
|
|
1349
1454
|
self.attributes['asciidoc-version'] = VERSION
|
1350
1455
|
self.attributes['asciidoc-file'] = APP_FILE
|
1351
1456
|
self.attributes['asciidoc-dir'] = APP_DIR
|
1352
|
-
|
1457
|
+
if localapp():
|
1458
|
+
self.attributes['asciidoc-confdir'] = APP_DIR
|
1459
|
+
else:
|
1460
|
+
self.attributes['asciidoc-confdir'] = CONF_DIR
|
1353
1461
|
self.attributes['user-dir'] = USER_DIR
|
1354
1462
|
if config.verbose:
|
1355
1463
|
self.attributes['verbose'] = ''
|
@@ -1518,7 +1626,7 @@ class Document(object):
|
|
1518
1626
|
self.attributes['manpurpose'] = mo.group('manpurpose').strip()
|
1519
1627
|
names = [s.strip() for s in self.attributes['manname'].split(',')]
|
1520
1628
|
if len(names) > 9:
|
1521
|
-
message.warning('
|
1629
|
+
message.warning('too many manpage names')
|
1522
1630
|
for i,name in enumerate(names):
|
1523
1631
|
self.attributes['manname%d' % (i+1)] = name
|
1524
1632
|
if has_header:
|
@@ -1844,14 +1952,13 @@ class AttributeList:
|
|
1844
1952
|
reo = re.compile(r"^'.*'$")
|
1845
1953
|
for k,v in attrs.items():
|
1846
1954
|
if reo.match(str(v)):
|
1847
|
-
attrs[k] = Lex.subs_1(v[1:-1],
|
1955
|
+
attrs[k] = Lex.subs_1(v[1:-1], config.subsnormal)
|
1848
1956
|
@staticmethod
|
1849
1957
|
def style():
|
1850
1958
|
return AttributeList.attrs.get('style') or AttributeList.attrs.get('1')
|
1851
1959
|
@staticmethod
|
1852
|
-
def consume(d):
|
1853
|
-
"""Add attribute list to the dictionary 'd' and reset the
|
1854
|
-
list."""
|
1960
|
+
def consume(d={}):
|
1961
|
+
"""Add attribute list to the dictionary 'd' and reset the list."""
|
1855
1962
|
if AttributeList.attrs:
|
1856
1963
|
d.update(AttributeList.attrs)
|
1857
1964
|
AttributeList.attrs = {}
|
@@ -1890,7 +1997,7 @@ class BlockTitle:
|
|
1890
1997
|
message.warning('blank block title')
|
1891
1998
|
BlockTitle.title = s
|
1892
1999
|
@staticmethod
|
1893
|
-
def consume(d):
|
2000
|
+
def consume(d={}):
|
1894
2001
|
"""If there is a title add it to dictionary 'd' then reset title."""
|
1895
2002
|
if BlockTitle.title:
|
1896
2003
|
d['title'] = BlockTitle.title
|
@@ -2133,23 +2240,27 @@ class Section:
|
|
2133
2240
|
"""
|
2134
2241
|
# Replace non-alpha numeric characters in title with underscores and
|
2135
2242
|
# convert to lower case.
|
2136
|
-
|
2137
|
-
|
2243
|
+
base_id = re.sub(r'(?u)\W+', '_', char_decode(title)).strip('_').lower()
|
2244
|
+
if 'ascii-ids' in document.attributes:
|
2245
|
+
# Replace non-ASCII characters with ASCII equivalents.
|
2246
|
+
import unicodedata
|
2247
|
+
base_id = unicodedata.normalize('NFKD', base_id).encode('ascii','ignore')
|
2248
|
+
base_id = char_encode(base_id)
|
2138
2249
|
# Prefix the ID name with idprefix attribute or underscore if not
|
2139
2250
|
# defined. Prefix ensures the ID does not clash with existing IDs.
|
2140
2251
|
idprefix = document.attributes.get('idprefix','_')
|
2141
|
-
|
2252
|
+
base_id = idprefix + base_id
|
2142
2253
|
i = 1
|
2143
2254
|
while True:
|
2144
2255
|
if i == 1:
|
2145
|
-
|
2256
|
+
id = base_id
|
2146
2257
|
else:
|
2147
|
-
|
2148
|
-
if
|
2149
|
-
Section.ids.append(
|
2150
|
-
return
|
2258
|
+
id = '%s_%d' % (base_id, i)
|
2259
|
+
if id not in Section.ids:
|
2260
|
+
Section.ids.append(id)
|
2261
|
+
return id
|
2151
2262
|
else:
|
2152
|
-
|
2263
|
+
id = base_id
|
2153
2264
|
i += 1
|
2154
2265
|
@staticmethod
|
2155
2266
|
def set_id():
|
@@ -2212,12 +2323,15 @@ class Section:
|
|
2212
2323
|
message.error('empty section is not valid')
|
2213
2324
|
|
2214
2325
|
class AbstractBlock:
|
2326
|
+
|
2327
|
+
blocknames = [] # Global stack of names for push_blockname() and pop_blockname().
|
2328
|
+
|
2215
2329
|
def __init__(self):
|
2216
2330
|
# Configuration parameter names common to all blocks.
|
2217
2331
|
self.CONF_ENTRIES = ('delimiter','options','subs','presubs','postsubs',
|
2218
2332
|
'posattrs','style','.*-style','template','filter')
|
2219
2333
|
self.start = None # File reader cursor at start delimiter.
|
2220
|
-
self.
|
2334
|
+
self.defname=None # Configuration file block definition section name.
|
2221
2335
|
# Configuration parameters.
|
2222
2336
|
self.delimiter=None # Regular expression matching block delimiter.
|
2223
2337
|
self.delimiter_reo=None # Compiled delimiter.
|
@@ -2241,14 +2355,14 @@ class AbstractBlock:
|
|
2241
2355
|
# Leading delimiter match object.
|
2242
2356
|
self.mo=None
|
2243
2357
|
def short_name(self):
|
2244
|
-
""" Return the text following the
|
2245
|
-
i = self.
|
2358
|
+
""" Return the text following the first dash in the section name."""
|
2359
|
+
i = self.defname.find('-')
|
2246
2360
|
if i == -1:
|
2247
|
-
return self.
|
2361
|
+
return self.defname
|
2248
2362
|
else:
|
2249
|
-
return self.
|
2363
|
+
return self.defname[i+1:]
|
2250
2364
|
def error(self, msg, cursor=None, halt=False):
|
2251
|
-
message.error('[%s] %s' % (self.
|
2365
|
+
message.error('[%s] %s' % (self.defname,msg), cursor, halt)
|
2252
2366
|
def is_conf_entry(self,param):
|
2253
2367
|
"""Return True if param matches an allowed configuration file entry
|
2254
2368
|
name."""
|
@@ -2256,9 +2370,9 @@ class AbstractBlock:
|
|
2256
2370
|
if re.match('^'+s+'$',param):
|
2257
2371
|
return True
|
2258
2372
|
return False
|
2259
|
-
def load(self,
|
2373
|
+
def load(self,defname,entries):
|
2260
2374
|
"""Update block definition from section 'entries' dictionary."""
|
2261
|
-
self.
|
2375
|
+
self.defname = defname
|
2262
2376
|
self.update_parameters(entries, self, all=True)
|
2263
2377
|
def update_parameters(self, src, dst=None, all=False):
|
2264
2378
|
"""
|
@@ -2267,7 +2381,7 @@ class AbstractBlock:
|
|
2267
2381
|
If all is True then copy src entries that aren't parameter names.
|
2268
2382
|
"""
|
2269
2383
|
dst = dst or self.parameters
|
2270
|
-
msg = '[%s] malformed entry %%s: %%s' % self.
|
2384
|
+
msg = '[%s] malformed entry %%s: %%s' % self.defname
|
2271
2385
|
def copy(obj,k,v):
|
2272
2386
|
if isinstance(obj,dict):
|
2273
2387
|
obj[k] = v
|
@@ -2346,7 +2460,7 @@ class AbstractBlock:
|
|
2346
2460
|
def dump(self):
|
2347
2461
|
"""Write block definition to stdout."""
|
2348
2462
|
write = lambda s: sys.stdout.write('%s%s' % (s,writer.newline))
|
2349
|
-
write('['+self.
|
2463
|
+
write('['+self.defname+']')
|
2350
2464
|
if self.is_conf_entry('delimiter'):
|
2351
2465
|
write('delimiter='+self.delimiter)
|
2352
2466
|
if self.template:
|
@@ -2374,14 +2488,14 @@ class AbstractBlock:
|
|
2374
2488
|
def validate(self):
|
2375
2489
|
"""Validate block after the complete configuration has been loaded."""
|
2376
2490
|
if self.is_conf_entry('delimiter') and not self.delimiter:
|
2377
|
-
raise EAsciiDoc,'[%s] missing delimiter' % self.
|
2491
|
+
raise EAsciiDoc,'[%s] missing delimiter' % self.defname
|
2378
2492
|
if self.style:
|
2379
2493
|
if not is_name(self.style):
|
2380
2494
|
raise EAsciiDoc, 'illegal style name: %s' % self.style
|
2381
2495
|
if not self.style in self.styles:
|
2382
2496
|
if not isinstance(self,List): # Lists don't have templates.
|
2383
2497
|
message.warning('[%s] \'%s\' style not in %s' % (
|
2384
|
-
self.
|
2498
|
+
self.defname,self.style,self.styles.keys()))
|
2385
2499
|
# Check all styles for missing templates.
|
2386
2500
|
all_styles_have_template = True
|
2387
2501
|
for k,v in self.styles.items():
|
@@ -2403,7 +2517,7 @@ class AbstractBlock:
|
|
2403
2517
|
% self.template)
|
2404
2518
|
elif not all_styles_have_template:
|
2405
2519
|
if not isinstance(self,List): # Lists don't have templates.
|
2406
|
-
message.warning('missing styles templates: [%s]' % self.
|
2520
|
+
message.warning('missing styles templates: [%s]' % self.defname)
|
2407
2521
|
def isnext(self):
|
2408
2522
|
"""Check if this block is next in document reader."""
|
2409
2523
|
result = False
|
@@ -2423,9 +2537,31 @@ class AbstractBlock:
|
|
2423
2537
|
self.presubs = config.subsnormal
|
2424
2538
|
if reader.cursor:
|
2425
2539
|
self.start = reader.cursor[:]
|
2540
|
+
def push_blockname(self, blockname=None):
|
2541
|
+
'''
|
2542
|
+
On block entry set the 'blockname' attribute.
|
2543
|
+
Only applies to delimited blocks, lists and tables.
|
2544
|
+
'''
|
2545
|
+
if blockname is None:
|
2546
|
+
blockname = self.attributes.get('style', self.short_name()).lower()
|
2547
|
+
trace('push blockname', blockname)
|
2548
|
+
self.blocknames.append(blockname)
|
2549
|
+
document.attributes['blockname'] = blockname
|
2550
|
+
def pop_blockname(self):
|
2551
|
+
'''
|
2552
|
+
On block exits restore previous (parent) 'blockname' attribute or
|
2553
|
+
undefine it if we're no longer inside a block.
|
2554
|
+
'''
|
2555
|
+
assert len(self.blocknames) > 0
|
2556
|
+
blockname = self.blocknames.pop()
|
2557
|
+
trace('pop blockname', blockname)
|
2558
|
+
if len(self.blocknames) == 0:
|
2559
|
+
document.attributes['blockname'] = None
|
2560
|
+
else:
|
2561
|
+
document.attributes['blockname'] = self.blocknames[-1]
|
2426
2562
|
def merge_attributes(self,attrs,params=[]):
|
2427
2563
|
"""
|
2428
|
-
Use the current
|
2564
|
+
Use the current block's attribute list (attrs dictionary) to build a
|
2429
2565
|
dictionary of block processing parameters (self.parameters) and tag
|
2430
2566
|
substitution attributes (self.attributes).
|
2431
2567
|
|
@@ -2452,8 +2588,8 @@ class AbstractBlock:
|
|
2452
2588
|
def check_array_parameter(param):
|
2453
2589
|
# Check the parameter is a sequence type.
|
2454
2590
|
if not is_array(self.parameters[param]):
|
2455
|
-
message.error('malformed
|
2456
|
-
self.parameters[param])
|
2591
|
+
message.error('malformed %s parameter: %s' %
|
2592
|
+
(param, self.parameters[param]))
|
2457
2593
|
# Revert to default value.
|
2458
2594
|
self.parameters[param] = getattr(self,param)
|
2459
2595
|
|
@@ -2484,7 +2620,7 @@ class AbstractBlock:
|
|
2484
2620
|
style = self.style
|
2485
2621
|
# Lists have implicit styles and do their own style checks.
|
2486
2622
|
elif style not in self.styles and not isinstance(self,List):
|
2487
|
-
message.warning('missing style: [%s]: %s' % (self.
|
2623
|
+
message.warning('missing style: [%s]: %s' % (self.defname,style))
|
2488
2624
|
style = self.style
|
2489
2625
|
if style in self.styles:
|
2490
2626
|
self.attributes['style'] = style
|
@@ -2522,7 +2658,7 @@ class AbstractBlocks:
|
|
2522
2658
|
d = {}
|
2523
2659
|
parse_entries(sections.get(k,()),d)
|
2524
2660
|
for b in self.blocks:
|
2525
|
-
if b.
|
2661
|
+
if b.defname == k:
|
2526
2662
|
break
|
2527
2663
|
else:
|
2528
2664
|
b = self.BLOCK_TYPE()
|
@@ -2612,7 +2748,7 @@ class Paragraphs(AbstractBlocks):
|
|
2612
2748
|
AbstractBlocks.validate(self)
|
2613
2749
|
# Check we have a default paragraph definition, put it last in list.
|
2614
2750
|
for b in self.blocks:
|
2615
|
-
if b.
|
2751
|
+
if b.defname == 'paradef-default':
|
2616
2752
|
self.blocks.append(b)
|
2617
2753
|
self.default = b
|
2618
2754
|
self.blocks.remove(b)
|
@@ -2627,7 +2763,7 @@ class List(AbstractBlock):
|
|
2627
2763
|
AbstractBlock.__init__(self)
|
2628
2764
|
self.CONF_ENTRIES += ('type','tags')
|
2629
2765
|
self.PARAM_NAMES += ('tags',)
|
2630
|
-
#
|
2766
|
+
# listdef conf file parameters.
|
2631
2767
|
self.type=None
|
2632
2768
|
self.tags=None # Name of listtags-<tags> conf section.
|
2633
2769
|
# Calculated parameters.
|
@@ -2799,6 +2935,7 @@ class List(AbstractBlock):
|
|
2799
2935
|
BlockTitle.consume(attrs)
|
2800
2936
|
AttributeList.consume(attrs)
|
2801
2937
|
self.merge_attributes(attrs,['tags'])
|
2938
|
+
self.push_blockname()
|
2802
2939
|
if self.type in ('numbered','callout'):
|
2803
2940
|
self.number_style = self.attributes.get('style')
|
2804
2941
|
if self.number_style not in self.NUMBER_STYLES:
|
@@ -2833,7 +2970,7 @@ class List(AbstractBlock):
|
|
2833
2970
|
elif self.type == 'labeled':
|
2834
2971
|
self.translate_entry()
|
2835
2972
|
else:
|
2836
|
-
raise AssertionError,'illegal [%s] list type' % self.
|
2973
|
+
raise AssertionError,'illegal [%s] list type' % self.defname
|
2837
2974
|
if etag:
|
2838
2975
|
writer.write(etag,trace='list close')
|
2839
2976
|
if self.type == 'callout':
|
@@ -2842,6 +2979,7 @@ class List(AbstractBlock):
|
|
2842
2979
|
lists.open.pop()
|
2843
2980
|
if len(lists.open):
|
2844
2981
|
document.attributes['listindex'] = str(lists.open[-1].ordinal)
|
2982
|
+
self.pop_blockname()
|
2845
2983
|
|
2846
2984
|
class Lists(AbstractBlocks):
|
2847
2985
|
"""List of List objects."""
|
@@ -2889,7 +3027,7 @@ class Lists(AbstractBlocks):
|
|
2889
3027
|
for b in self.blocks:
|
2890
3028
|
# Check list has valid type.
|
2891
3029
|
if not b.type in Lists.TYPES:
|
2892
|
-
raise EAsciiDoc,'[%s] illegal type' % b.
|
3030
|
+
raise EAsciiDoc,'[%s] illegal type' % b.defname
|
2893
3031
|
b.validate()
|
2894
3032
|
def dump(self):
|
2895
3033
|
AbstractBlocks.dump(self)
|
@@ -2911,20 +3049,20 @@ class DelimitedBlock(AbstractBlock):
|
|
2911
3049
|
def translate(self):
|
2912
3050
|
AbstractBlock.translate(self)
|
2913
3051
|
reader.read() # Discard delimiter.
|
2914
|
-
attrs
|
2915
|
-
if
|
2916
|
-
BlockTitle.consume(
|
2917
|
-
AttributeList.consume(
|
2918
|
-
self.
|
3052
|
+
self.merge_attributes(AttributeList.attrs)
|
3053
|
+
if not 'skip' in self.parameters.options:
|
3054
|
+
BlockTitle.consume(self.attributes)
|
3055
|
+
AttributeList.consume()
|
3056
|
+
self.push_blockname()
|
2919
3057
|
options = self.parameters.options
|
2920
3058
|
if 'skip' in options:
|
2921
3059
|
reader.read_until(self.delimiter,same_file=True)
|
2922
|
-
elif safe() and self.
|
3060
|
+
elif safe() and self.defname == 'blockdef-backend':
|
2923
3061
|
message.unsafe('Backend Block')
|
2924
3062
|
reader.read_until(self.delimiter,same_file=True)
|
2925
3063
|
else:
|
2926
3064
|
template = self.parameters.template
|
2927
|
-
template = subs_attrs(template,
|
3065
|
+
template = subs_attrs(template,self.attributes)
|
2928
3066
|
name = self.short_name()+' block'
|
2929
3067
|
if 'sectionbody' in options:
|
2930
3068
|
# The body is treated like a section body.
|
@@ -2950,6 +3088,7 @@ class DelimitedBlock(AbstractBlock):
|
|
2950
3088
|
else:
|
2951
3089
|
delimiter = reader.read() # Discard delimiter line.
|
2952
3090
|
assert re.match(self.delimiter,delimiter)
|
3091
|
+
self.pop_blockname()
|
2953
3092
|
|
2954
3093
|
class DelimitedBlocks(AbstractBlocks):
|
2955
3094
|
"""List of delimited blocks."""
|
@@ -2979,12 +3118,19 @@ class Cell:
|
|
2979
3118
|
self.span, self.vspan = Table.parse_span_spec(span_spec)
|
2980
3119
|
self.halign, self.valign = Table.parse_align_spec(align_spec)
|
2981
3120
|
self.style = style
|
3121
|
+
self.reserved = False
|
2982
3122
|
def __repr__(self):
|
2983
3123
|
return '<Cell: %d.%d %s.%s %s "%s">' % (
|
2984
3124
|
self.span, self.vspan,
|
2985
3125
|
self.halign, self.valign,
|
2986
3126
|
self.style or '',
|
2987
3127
|
self.data)
|
3128
|
+
def clone_reserve(self):
|
3129
|
+
"""Return a clone of self to reserve vertically spanned cell."""
|
3130
|
+
result = copy.copy(self)
|
3131
|
+
result.vspan = 1
|
3132
|
+
result.reserved = True
|
3133
|
+
return result
|
2988
3134
|
|
2989
3135
|
class Table(AbstractBlock):
|
2990
3136
|
ALIGN = {'<':'left', '>':'right', '^':'center'}
|
@@ -3055,7 +3201,7 @@ class Table(AbstractBlock):
|
|
3055
3201
|
self.error('missing section: [tabletags-%s]' % t,halt=True)
|
3056
3202
|
if self.separator:
|
3057
3203
|
# Evaluate escape characters.
|
3058
|
-
self.separator =
|
3204
|
+
self.separator = literal_eval('"'+self.separator+'"')
|
3059
3205
|
#TODO: Move to class Tables
|
3060
3206
|
# Check global table parameters.
|
3061
3207
|
elif config.pagewidth is None:
|
@@ -3226,18 +3372,33 @@ class Table(AbstractBlock):
|
|
3226
3372
|
Parse the table source text into self.rows (a list of rows, each row
|
3227
3373
|
is a list of Cells.
|
3228
3374
|
"""
|
3229
|
-
reserved = {} #
|
3375
|
+
reserved = {} # Reserved cells generated by rowspans.
|
3230
3376
|
if self.parameters.format in ('psv','dsv'):
|
3377
|
+
colcount = len(self.columns)
|
3378
|
+
parsed_cells = self.parse_psv_dsv(text)
|
3231
3379
|
ri = 0 # Current row index 0..
|
3232
|
-
cells = self.parse_psv_dsv(text)
|
3233
|
-
row = []
|
3234
3380
|
ci = 0 # Column counter 0..colcount
|
3235
|
-
|
3236
|
-
|
3237
|
-
|
3238
|
-
|
3239
|
-
|
3240
|
-
|
3381
|
+
row = []
|
3382
|
+
i = 0
|
3383
|
+
while True:
|
3384
|
+
resv = reserved.get(ri) and reserved[ri].get(ci)
|
3385
|
+
if resv:
|
3386
|
+
# We have a cell generated by a previous row span so
|
3387
|
+
# process it before continuing with the current parsed
|
3388
|
+
# cell.
|
3389
|
+
cell = resv
|
3390
|
+
else:
|
3391
|
+
if i >= len(parsed_cells):
|
3392
|
+
break # No more parsed or reserved cells.
|
3393
|
+
cell = parsed_cells[i]
|
3394
|
+
i += 1
|
3395
|
+
if cell.vspan > 1:
|
3396
|
+
# Generate ensuing reserved cells spanned vertically by
|
3397
|
+
# the current cell.
|
3398
|
+
for j in range(1, cell.vspan):
|
3399
|
+
if not ri+j in reserved:
|
3400
|
+
reserved[ri+j] = {}
|
3401
|
+
reserved[ri+j][ci] = cell.clone_reserve()
|
3241
3402
|
ci += cell.span
|
3242
3403
|
if ci <= colcount:
|
3243
3404
|
row.append(cell)
|
@@ -3246,28 +3407,30 @@ class Table(AbstractBlock):
|
|
3246
3407
|
ri += 1
|
3247
3408
|
row = []
|
3248
3409
|
ci = 0
|
3249
|
-
if ci > colcount:
|
3250
|
-
message.warning('table row %d: span exceeds number of columns'
|
3251
|
-
% ri)
|
3252
3410
|
elif self.parameters.format == 'csv':
|
3253
3411
|
self.rows = self.parse_csv(text)
|
3254
3412
|
else:
|
3255
3413
|
assert True,'illegal table format'
|
3414
|
+
# Check for empty rows containing only reserved (spanned) cells.
|
3415
|
+
for ri,row in enumerate(self.rows):
|
3416
|
+
empty = True
|
3417
|
+
for cell in row:
|
3418
|
+
if not cell.reserved:
|
3419
|
+
empty = False
|
3420
|
+
break
|
3421
|
+
if empty:
|
3422
|
+
message.warning('table row %d: empty spanned row' % (ri+1))
|
3256
3423
|
# Check that all row spans match.
|
3257
3424
|
for ri,row in enumerate(self.rows):
|
3258
3425
|
row_span = 0
|
3259
3426
|
for cell in row:
|
3260
3427
|
row_span += cell.span
|
3261
|
-
row_span += reserved.get(ri,0)
|
3262
3428
|
if ri == 0:
|
3263
3429
|
header_span = row_span
|
3264
3430
|
if row_span < header_span:
|
3265
3431
|
message.warning('table row %d: does not span all columns' % (ri+1))
|
3266
3432
|
if row_span > header_span:
|
3267
3433
|
message.warning('table row %d: exceeds columns span' % (ri+1))
|
3268
|
-
# Check that now row spans exceed the number of rows.
|
3269
|
-
if len([x for x in reserved.keys() if x >= len(self.rows)]) > 0:
|
3270
|
-
message.warning('one or more cell spans exceed the available rows')
|
3271
3434
|
def subs_rows(self, rows, rowtype='body'):
|
3272
3435
|
"""
|
3273
3436
|
Return a string of output markup from a list of rows, each row
|
@@ -3295,6 +3458,10 @@ class Table(AbstractBlock):
|
|
3295
3458
|
result = []
|
3296
3459
|
i = 0
|
3297
3460
|
for cell in row:
|
3461
|
+
if cell.reserved:
|
3462
|
+
# Skip vertically spanned placeholders.
|
3463
|
+
i += cell.span
|
3464
|
+
continue
|
3298
3465
|
if i >= len(self.columns):
|
3299
3466
|
break # Skip cells outside the header width.
|
3300
3467
|
col = self.columns[i]
|
@@ -3435,8 +3602,9 @@ class Table(AbstractBlock):
|
|
3435
3602
|
delimiter = reader.read() # Discard closing delimiter.
|
3436
3603
|
assert re.match(self.delimiter,delimiter)
|
3437
3604
|
if len(text) == 0:
|
3438
|
-
message.warning('[%s] table is empty' % self.
|
3605
|
+
message.warning('[%s] table is empty' % self.defname)
|
3439
3606
|
return
|
3607
|
+
self.push_blockname('table')
|
3440
3608
|
cols = attrs.get('cols')
|
3441
3609
|
if not cols:
|
3442
3610
|
# Calculate column count from number of items in first line.
|
@@ -3484,6 +3652,7 @@ class Table(AbstractBlock):
|
|
3484
3652
|
if bodyrows:
|
3485
3653
|
table = table.replace('\x07bodyrows\x07', bodyrows, 1)
|
3486
3654
|
writer.write(table,trace='table')
|
3655
|
+
self.pop_blockname()
|
3487
3656
|
|
3488
3657
|
class Tables(AbstractBlocks):
|
3489
3658
|
"""List of tables."""
|
@@ -3520,7 +3689,7 @@ class Tables(AbstractBlocks):
|
|
3520
3689
|
AbstractBlocks.validate(self)
|
3521
3690
|
# Check we have a default table definition,
|
3522
3691
|
for i in range(len(self.blocks)):
|
3523
|
-
if self.blocks[i].
|
3692
|
+
if self.blocks[i].defname == 'tabledef-default':
|
3524
3693
|
default = self.blocks[i]
|
3525
3694
|
break
|
3526
3695
|
else:
|
@@ -3641,7 +3810,7 @@ class Macros:
|
|
3641
3810
|
if mo:
|
3642
3811
|
if m.name == name:
|
3643
3812
|
return mo
|
3644
|
-
if re.match(name,mo.group('name')):
|
3813
|
+
if re.match(name, mo.group('name')):
|
3645
3814
|
return mo
|
3646
3815
|
return None
|
3647
3816
|
def extract_passthroughs(self,text,prefix=''):
|
@@ -3977,8 +4146,12 @@ class Reader1:
|
|
3977
4146
|
del self.next[0]
|
3978
4147
|
result = self.cursor[2]
|
3979
4148
|
# Check for include macro.
|
3980
|
-
mo = macros.match('+',r'include[1]
|
4149
|
+
mo = macros.match('+',r'^include[1]?$',result)
|
3981
4150
|
if mo and not skip:
|
4151
|
+
# Parse include macro attributes.
|
4152
|
+
attrs = {}
|
4153
|
+
parse_attributes(mo.group('attrlist'),attrs)
|
4154
|
+
warnings = attrs.get('warnings', True)
|
3982
4155
|
# Don't process include macro once the maximum depth is reached.
|
3983
4156
|
if self.current_depth >= self.max_depth:
|
3984
4157
|
return result
|
@@ -3991,37 +4164,53 @@ class Reader1:
|
|
3991
4164
|
fname = safe_filename(fname, os.path.dirname(self.fname))
|
3992
4165
|
if not fname:
|
3993
4166
|
return Reader1.read(self) # Return next input line.
|
4167
|
+
if not os.path.isfile(fname):
|
4168
|
+
if warnings:
|
4169
|
+
message.warning('include file not found: %s' % fname)
|
4170
|
+
return Reader1.read(self) # Return next input line.
|
3994
4171
|
if mo.group('name') == 'include1':
|
3995
4172
|
if not config.dumping:
|
3996
|
-
|
3997
|
-
|
3998
|
-
|
3999
|
-
|
4173
|
+
if fname not in config.include1:
|
4174
|
+
message.verbose('include1: ' + fname, linenos=False)
|
4175
|
+
# Store the include file in memory for later
|
4176
|
+
# retrieval by the {include1:} system attribute.
|
4177
|
+
f = open(fname)
|
4178
|
+
try:
|
4179
|
+
config.include1[fname] = [
|
4180
|
+
s.rstrip() for s in f]
|
4181
|
+
finally:
|
4182
|
+
f.close()
|
4000
4183
|
return '{include1:%s}' % fname
|
4001
4184
|
else:
|
4002
4185
|
# This is a configuration dump, just pass the macro
|
4003
4186
|
# call through.
|
4004
4187
|
return result
|
4005
|
-
# Parse include macro attributes.
|
4006
|
-
attrs = {}
|
4007
|
-
parse_attributes(mo.group('attrlist'),attrs)
|
4008
4188
|
# Clone self and set as parent (self assumes the role of child).
|
4009
4189
|
parent = Reader1()
|
4010
4190
|
assign(parent,self)
|
4011
4191
|
self.parent = parent
|
4012
4192
|
# Set attributes in child.
|
4013
4193
|
if 'tabsize' in attrs:
|
4014
|
-
|
4015
|
-
|
4016
|
-
|
4194
|
+
try:
|
4195
|
+
val = int(attrs['tabsize'])
|
4196
|
+
if not val >= 0:
|
4197
|
+
raise ValueError, "not >= 0"
|
4198
|
+
self.tabsize = val
|
4199
|
+
except ValueError:
|
4200
|
+
raise EAsciiDoc, 'illegal include macro tabsize argument'
|
4017
4201
|
else:
|
4018
4202
|
self.tabsize = config.tabsize
|
4019
4203
|
if 'depth' in attrs:
|
4020
|
-
|
4021
|
-
|
4022
|
-
|
4023
|
-
|
4204
|
+
try:
|
4205
|
+
val = int(attrs['depth'])
|
4206
|
+
if not val >= 1:
|
4207
|
+
raise ValueError, "not >= 1"
|
4208
|
+
self.max_depth = self.current_depth + val
|
4209
|
+
except ValueError:
|
4210
|
+
raise EAsciiDoc, 'illegal include macro depth argument'
|
4211
|
+
|
4024
4212
|
# Process included file.
|
4213
|
+
message.verbose('include: ' + fname, linenos=False)
|
4025
4214
|
self.open(fname)
|
4026
4215
|
self.current_depth = self.current_depth + 1
|
4027
4216
|
result = Reader1.read(self)
|
@@ -4125,6 +4314,9 @@ class Reader(Reader1):
|
|
4125
4314
|
else:
|
4126
4315
|
self.skip = defined
|
4127
4316
|
elif name == 'ifeval':
|
4317
|
+
if safe():
|
4318
|
+
message.unsafe('ifeval invalid')
|
4319
|
+
raise EAsciiDoc,'ifeval invalid safe document'
|
4128
4320
|
if not attrlist:
|
4129
4321
|
raise EAsciiDoc,'missing ifeval condition: %s' % result
|
4130
4322
|
cond = False
|
@@ -4134,6 +4326,7 @@ class Reader(Reader1):
|
|
4134
4326
|
cond = eval(attrlist)
|
4135
4327
|
except Exception,e:
|
4136
4328
|
raise EAsciiDoc,'error evaluating ifeval condition: %s: %s' % (result, str(e))
|
4329
|
+
message.verbose('ifeval: %s: %r' % (attrlist, cond))
|
4137
4330
|
self.skip = not cond
|
4138
4331
|
if not attrlist or name == 'ifeval':
|
4139
4332
|
if self.skip:
|
@@ -4147,10 +4340,8 @@ class Reader(Reader1):
|
|
4147
4340
|
if mo:
|
4148
4341
|
action = mo.group('name')
|
4149
4342
|
cmd = mo.group('attrlist')
|
4150
|
-
|
4151
|
-
|
4152
|
-
self.cursor[2] = s # So we don't re-evaluate.
|
4153
|
-
result = s
|
4343
|
+
result = system(action, cmd, is_macro=True)
|
4344
|
+
self.cursor[2] = result # So we don't re-evaluate.
|
4154
4345
|
if result:
|
4155
4346
|
# Unescape escaped system macros.
|
4156
4347
|
if macros.match('+',r'\\eval|\\sys|\\sys2|\\ifdef|\\ifndef|\\endif|\\include|\\include1',result):
|
@@ -4312,7 +4503,7 @@ class Config:
|
|
4312
4503
|
ENTRIES_SECTIONS= ('tags','miscellaneous','attributes','specialcharacters',
|
4313
4504
|
'specialwords','macros','replacements','quotes','titles',
|
4314
4505
|
r'paradef-.+',r'listdef-.+',r'blockdef-.+',r'tabledef-.+',
|
4315
|
-
r'tabletags-.+',r'listtags-.+','
|
4506
|
+
r'tabletags-.+',r'listtags-.+','replacements[23]',
|
4316
4507
|
r'old_tabledef-.+')
|
4317
4508
|
def __init__(self):
|
4318
4509
|
self.sections = OrderedDict() # Keyed by section name containing
|
@@ -4336,6 +4527,7 @@ class Config:
|
|
4336
4527
|
self.replacements = OrderedDict() # Key is find pattern, value is
|
4337
4528
|
#replace pattern.
|
4338
4529
|
self.replacements2 = OrderedDict()
|
4530
|
+
self.replacements3 = OrderedDict()
|
4339
4531
|
self.specialsections = {} # Name is special section name pattern, value
|
4340
4532
|
# is corresponding section name.
|
4341
4533
|
self.quotes = OrderedDict() # Values contain corresponding tag name.
|
@@ -4345,6 +4537,7 @@ class Config:
|
|
4345
4537
|
self.loaded = [] # Loaded conf files.
|
4346
4538
|
self.include1 = {} # Holds include1::[] files for {include1:}.
|
4347
4539
|
self.dumping = False # True if asciidoc -c option specified.
|
4540
|
+
self.filters = [] # Filter names specified by --filter option.
|
4348
4541
|
|
4349
4542
|
def init(self, cmd):
|
4350
4543
|
"""
|
@@ -4352,8 +4545,9 @@ class Config:
|
|
4352
4545
|
directory.
|
4353
4546
|
cmd is the asciidoc command or asciidoc.py path.
|
4354
4547
|
"""
|
4355
|
-
if float(sys.version[:3]) < MIN_PYTHON_VERSION:
|
4356
|
-
message.stderr('FAILED: Python
|
4548
|
+
if float(sys.version[:3]) < float(MIN_PYTHON_VERSION):
|
4549
|
+
message.stderr('FAILED: Python %s or better required' %
|
4550
|
+
MIN_PYTHON_VERSION)
|
4357
4551
|
sys.exit(1)
|
4358
4552
|
if not os.path.exists(cmd):
|
4359
4553
|
message.stderr('FAILED: Missing asciidoc command: %s' % cmd)
|
@@ -4377,6 +4571,25 @@ class Config:
|
|
4377
4571
|
The 'exclude' list contains section names not to be loaded.
|
4378
4572
|
Return False if no file was found in any of the locations.
|
4379
4573
|
"""
|
4574
|
+
def update_section(section):
|
4575
|
+
""" Update section in sections with contents. """
|
4576
|
+
if section and contents:
|
4577
|
+
if section in sections and self.entries_section(section):
|
4578
|
+
if ''.join(contents):
|
4579
|
+
# Merge entries.
|
4580
|
+
sections[section] += contents
|
4581
|
+
else:
|
4582
|
+
del sections[section]
|
4583
|
+
else:
|
4584
|
+
if section.startswith('+'):
|
4585
|
+
# Append section.
|
4586
|
+
if section in sections:
|
4587
|
+
sections[section] += contents
|
4588
|
+
else:
|
4589
|
+
sections[section] = contents
|
4590
|
+
else:
|
4591
|
+
# Replace section.
|
4592
|
+
sections[section] = contents
|
4380
4593
|
if dir:
|
4381
4594
|
fname = os.path.join(dir, fname)
|
4382
4595
|
# Sliently skip missing configuration file.
|
@@ -4391,7 +4604,7 @@ class Config:
|
|
4391
4604
|
rdr.open(fname)
|
4392
4605
|
message.linenos = None
|
4393
4606
|
self.fname = fname
|
4394
|
-
reo = re.compile(r'(?u)^\[(?P<section
|
4607
|
+
reo = re.compile(r'(?u)^\[(?P<section>\+?[^\W\d][\w-]*)\]\s*$')
|
4395
4608
|
sections = OrderedDict()
|
4396
4609
|
section,contents = '',[]
|
4397
4610
|
while not rdr.eof():
|
@@ -4403,30 +4616,12 @@ class Config:
|
|
4403
4616
|
s = s.rstrip()
|
4404
4617
|
found = reo.findall(s)
|
4405
4618
|
if found:
|
4406
|
-
|
4407
|
-
if section in sections \
|
4408
|
-
and self.entries_section(section):
|
4409
|
-
if ''.join(contents):
|
4410
|
-
# Merge entries.
|
4411
|
-
sections[section] = sections[section] + contents
|
4412
|
-
else:
|
4413
|
-
del sections[section]
|
4414
|
-
else:
|
4415
|
-
sections[section] = contents
|
4619
|
+
update_section(section) # Store previous section.
|
4416
4620
|
section = found[0].lower()
|
4417
4621
|
contents = []
|
4418
4622
|
else:
|
4419
4623
|
contents.append(s)
|
4420
|
-
|
4421
|
-
if section in sections \
|
4422
|
-
and self.entries_section(section):
|
4423
|
-
if ''.join(contents):
|
4424
|
-
# Merge entries.
|
4425
|
-
sections[section] = sections[section] + contents
|
4426
|
-
else:
|
4427
|
-
del sections[section]
|
4428
|
-
else:
|
4429
|
-
sections[section] = contents
|
4624
|
+
update_section(section) # Store last section.
|
4430
4625
|
rdr.close()
|
4431
4626
|
if include:
|
4432
4627
|
for s in set(sections) - set(include):
|
@@ -4455,8 +4650,18 @@ class Config:
|
|
4455
4650
|
del sections[k][i]
|
4456
4651
|
elif not self.entries_section(k):
|
4457
4652
|
break
|
4458
|
-
#
|
4459
|
-
|
4653
|
+
# Update new sections.
|
4654
|
+
for k,v in sections.items():
|
4655
|
+
if k.startswith('+'):
|
4656
|
+
# Append section.
|
4657
|
+
k = k[1:]
|
4658
|
+
if k in self.sections:
|
4659
|
+
self.sections[k] += v
|
4660
|
+
else:
|
4661
|
+
self.sections[k] = v
|
4662
|
+
else:
|
4663
|
+
# Replace section.
|
4664
|
+
self.sections[k] = v
|
4460
4665
|
self.parse_tags()
|
4461
4666
|
# Internally [miscellaneous] section entries are just attributes.
|
4462
4667
|
d = {}
|
@@ -4475,6 +4680,7 @@ class Config:
|
|
4475
4680
|
self.parse_specialwords()
|
4476
4681
|
self.parse_replacements()
|
4477
4682
|
self.parse_replacements('replacements2')
|
4683
|
+
self.parse_replacements('replacements3')
|
4478
4684
|
self.parse_specialsections()
|
4479
4685
|
paragraphs.load(sections)
|
4480
4686
|
lists.load(sections)
|
@@ -4530,19 +4736,34 @@ class Config:
|
|
4530
4736
|
"""
|
4531
4737
|
Load the backend configuration files from dirs list.
|
4532
4738
|
If dirs not specified try all the well known locations.
|
4739
|
+
If a <backend>.conf file was found return it's full path name,
|
4740
|
+
if not found return None.
|
4533
4741
|
"""
|
4742
|
+
result = None
|
4534
4743
|
if dirs is None:
|
4535
4744
|
dirs = self.get_load_dirs()
|
4536
|
-
|
4537
|
-
|
4538
|
-
|
4539
|
-
|
4540
|
-
self.load_file(conf,d)
|
4745
|
+
conf = document.backend + '.conf'
|
4746
|
+
conf2 = document.backend + '-' + document.doctype + '.conf'
|
4747
|
+
# First search for filter backends.
|
4748
|
+
for d in [os.path.join(d, 'backends', document.backend) for d in dirs]:
|
4749
|
+
if self.load_file(conf,d):
|
4750
|
+
result = os.path.join(d, conf)
|
4751
|
+
self.load_file(conf2,d)
|
4752
|
+
if not result:
|
4753
|
+
# Search in the normal locations.
|
4754
|
+
for d in dirs:
|
4755
|
+
if self.load_file(conf,d):
|
4756
|
+
result = os.path.join(d, conf)
|
4757
|
+
self.load_file(conf2,d)
|
4758
|
+
return result
|
4541
4759
|
|
4542
4760
|
def load_filters(self, dirs=None):
|
4543
4761
|
"""
|
4544
4762
|
Load filter configuration files from 'filters' directory in dirs list.
|
4545
|
-
If dirs not specified try all the well known locations.
|
4763
|
+
If dirs not specified try all the well known locations. Suppress
|
4764
|
+
loading if a file named __noautoload__ is in same directory as the conf
|
4765
|
+
file unless the filter has been specified with the --filter
|
4766
|
+
command-line option (in which case it is loaded unconditionally).
|
4546
4767
|
"""
|
4547
4768
|
if dirs is None:
|
4548
4769
|
dirs = self.get_load_dirs()
|
@@ -4550,29 +4771,66 @@ class Config:
|
|
4550
4771
|
# Load filter .conf files.
|
4551
4772
|
filtersdir = os.path.join(d,'filters')
|
4552
4773
|
for dirpath,dirnames,filenames in os.walk(filtersdir):
|
4553
|
-
|
4554
|
-
|
4555
|
-
|
4774
|
+
subdirs = dirpath[len(filtersdir):].split(os.path.sep)
|
4775
|
+
# True if processing a filter specified by a --filter option.
|
4776
|
+
filter_opt = len(subdirs) > 1 and subdirs[1] in self.filters
|
4777
|
+
if '__noautoload__' not in filenames or filter_opt:
|
4778
|
+
for f in filenames:
|
4779
|
+
if re.match(r'^.+\.conf$',f):
|
4780
|
+
self.load_file(f,dirpath)
|
4781
|
+
|
4782
|
+
def find_config_dir(self, *dirnames):
|
4783
|
+
"""
|
4784
|
+
Return path of configuration directory.
|
4785
|
+
Try all the well known locations.
|
4786
|
+
Return None if directory not found.
|
4787
|
+
"""
|
4788
|
+
for d in [os.path.join(d, *dirnames) for d in self.get_load_dirs()]:
|
4789
|
+
if os.path.isdir(d):
|
4790
|
+
return d
|
4791
|
+
return None
|
4792
|
+
|
4793
|
+
def set_theme_attributes(self):
|
4794
|
+
theme = document.attributes.get('theme')
|
4795
|
+
if theme and 'themedir' not in document.attributes:
|
4796
|
+
themedir = self.find_config_dir('themes', theme)
|
4797
|
+
if themedir:
|
4798
|
+
document.attributes['themedir'] = themedir
|
4799
|
+
iconsdir = os.path.join(themedir, 'icons')
|
4800
|
+
if 'data-uri' in document.attributes and os.path.isdir(iconsdir):
|
4801
|
+
document.attributes['iconsdir'] = iconsdir
|
4802
|
+
else:
|
4803
|
+
message.warning('missing theme: %s' % theme, linenos=False)
|
4556
4804
|
|
4557
4805
|
def load_miscellaneous(self,d):
|
4558
4806
|
"""Set miscellaneous configuration entries from dictionary 'd'."""
|
4559
|
-
def
|
4807
|
+
def set_if_int_gt_zero(name, d):
|
4560
4808
|
if name in d:
|
4561
|
-
|
4562
|
-
|
4563
|
-
|
4564
|
-
|
4565
|
-
|
4566
|
-
|
4567
|
-
|
4568
|
-
|
4809
|
+
try:
|
4810
|
+
val = int(d[name])
|
4811
|
+
if not val > 0:
|
4812
|
+
raise ValueError, "not > 0"
|
4813
|
+
if val > 0:
|
4814
|
+
setattr(self, name, val)
|
4815
|
+
except ValueError:
|
4816
|
+
raise EAsciiDoc, 'illegal [miscellaneous] %s entry' % name
|
4817
|
+
set_if_int_gt_zero('tabsize', d)
|
4818
|
+
set_if_int_gt_zero('textwidth', d) # DEPRECATED: Old tables only.
|
4819
|
+
|
4569
4820
|
if 'pagewidth' in d:
|
4570
|
-
|
4571
|
-
|
4572
|
-
|
4821
|
+
try:
|
4822
|
+
val = float(d['pagewidth'])
|
4823
|
+
self.pagewidth = val
|
4824
|
+
except ValueError:
|
4825
|
+
raise EAsciiDoc, 'illegal [miscellaneous] pagewidth entry'
|
4826
|
+
|
4827
|
+
if 'pageunits' in d:
|
4828
|
+
self.pageunits = d['pageunits']
|
4829
|
+
if 'outfilesuffix' in d:
|
4830
|
+
self.outfilesuffix = d['outfilesuffix']
|
4573
4831
|
if 'newline' in d:
|
4574
4832
|
# Convert escape sequences to their character values.
|
4575
|
-
self.newline =
|
4833
|
+
self.newline = literal_eval('"'+d['newline']+'"')
|
4576
4834
|
if 'subsnormal' in d:
|
4577
4835
|
self.subsnormal = parse_options(d['subsnormal'],SUBS_OPTIONS,
|
4578
4836
|
'illegal [%s] %s: %s' %
|
@@ -4664,6 +4922,7 @@ class Config:
|
|
4664
4922
|
dump_section('specialwords',d)
|
4665
4923
|
dump_section('replacements',self.replacements)
|
4666
4924
|
dump_section('replacements2',self.replacements2)
|
4925
|
+
dump_section('replacements3',self.replacements3)
|
4667
4926
|
dump_section('specialsections',self.specialsections)
|
4668
4927
|
d = {}
|
4669
4928
|
for k,v in self.tags.items():
|
@@ -5060,8 +5319,13 @@ class Table_OLD(AbstractBlock):
|
|
5060
5319
|
if s == '':
|
5061
5320
|
c.rulerwidth = None
|
5062
5321
|
else:
|
5063
|
-
|
5064
|
-
|
5322
|
+
try:
|
5323
|
+
val = int(s)
|
5324
|
+
if not val > 0:
|
5325
|
+
raise ValueError, 'not > 0'
|
5326
|
+
c.rulerwidth = val
|
5327
|
+
except ValueError:
|
5328
|
+
raise EAsciiDoc, 'malformed ruler: bad width'
|
5065
5329
|
else: # Calculate column width from inter-fillchar intervals.
|
5066
5330
|
if not re.match(r'^'+fc+r'+$',s):
|
5067
5331
|
raise EAsciiDoc,'malformed ruler: illegal fillchars'
|
@@ -5130,7 +5394,7 @@ class Table_OLD(AbstractBlock):
|
|
5130
5394
|
if i == 0:
|
5131
5395
|
raise EAsciiDoc,'missing table rows'
|
5132
5396
|
if i >= len(rows):
|
5133
|
-
raise EAsciiDoc,'closing [%s] underline expected' % self.
|
5397
|
+
raise EAsciiDoc,'closing [%s] underline expected' % self.defname
|
5134
5398
|
return (join_lines_OLD(rows[:i]), rows[i+1:])
|
5135
5399
|
def parse_rows(self, rows, rtag, dtag):
|
5136
5400
|
"""Parse rows list using the row and data tags. Returns a substituted
|
@@ -5177,7 +5441,7 @@ class Table_OLD(AbstractBlock):
|
|
5177
5441
|
# and the output markup is HTML. It's also a bit dubious in that it
|
5178
5442
|
# assumes the user has not modified the shipped line break pattern.
|
5179
5443
|
subs = self.get_subs()[0]
|
5180
|
-
if '
|
5444
|
+
if 'replacements2' in subs:
|
5181
5445
|
# Insert line breaks in cell data.
|
5182
5446
|
d = re.sub(r'(?m)\n',r' +\n',d)
|
5183
5447
|
d = d.split('\n') # So writer.newline is written.
|
@@ -5224,7 +5488,7 @@ class Table_OLD(AbstractBlock):
|
|
5224
5488
|
"""Parse the list of source table rows. Each row item in the returned
|
5225
5489
|
list contains a list of cell data elements."""
|
5226
5490
|
separator = self.attributes.get('separator',':')
|
5227
|
-
separator =
|
5491
|
+
separator = literal_eval('"'+separator+'"')
|
5228
5492
|
if len(separator) != 1:
|
5229
5493
|
raise EAsciiDoc,'malformed dsv separator: %s' % separator
|
5230
5494
|
# TODO If separator is preceeded by an odd number of backslashes then
|
@@ -5234,7 +5498,7 @@ class Table_OLD(AbstractBlock):
|
|
5234
5498
|
# Skip blank lines
|
5235
5499
|
if row == '': continue
|
5236
5500
|
# Unescape escaped characters.
|
5237
|
-
row =
|
5501
|
+
row = literal_eval('"'+row.replace('"','\\"')+'"')
|
5238
5502
|
data = row.split(separator)
|
5239
5503
|
data = [s.strip() for s in data]
|
5240
5504
|
result.append(data)
|
@@ -5256,13 +5520,13 @@ class Table_OLD(AbstractBlock):
|
|
5256
5520
|
for k,v in attrs.items():
|
5257
5521
|
if k == 'format':
|
5258
5522
|
if v not in self.FORMATS:
|
5259
|
-
raise EAsciiDoc, 'illegal [%s] %s: %s' % (self.
|
5523
|
+
raise EAsciiDoc, 'illegal [%s] %s: %s' % (self.defname,k,v)
|
5260
5524
|
self.format = v
|
5261
5525
|
elif k == 'tablewidth':
|
5262
5526
|
try:
|
5263
5527
|
self.tablewidth = float(attrs['tablewidth'])
|
5264
5528
|
except Exception:
|
5265
|
-
raise EAsciiDoc, 'illegal [%s] %s: %s' % (self.
|
5529
|
+
raise EAsciiDoc, 'illegal [%s] %s: %s' % (self.defname,k,v)
|
5266
5530
|
self.merge_attributes(attrs)
|
5267
5531
|
# Parse table ruler.
|
5268
5532
|
ruler = reader.read()
|
@@ -5277,13 +5541,14 @@ class Table_OLD(AbstractBlock):
|
|
5277
5541
|
if line in ('',None):
|
5278
5542
|
break;
|
5279
5543
|
if line is None:
|
5280
|
-
raise EAsciiDoc,'closing [%s] underline expected' % self.
|
5544
|
+
raise EAsciiDoc,'closing [%s] underline expected' % self.defname
|
5281
5545
|
table.append(reader.read())
|
5282
5546
|
# EXPERIMENTAL: The number of lines in the table, requested by Benjamin Klum.
|
5283
5547
|
self.attributes['rows'] = str(len(table))
|
5284
5548
|
if self.check_msg: # Skip if table definition was marked invalid.
|
5285
|
-
message.warning('skipping %s table: %s' % (self.
|
5549
|
+
message.warning('skipping [%s] table: %s' % (self.defname,self.check_msg))
|
5286
5550
|
return
|
5551
|
+
self.push_blockname('table')
|
5287
5552
|
# Generate colwidths and colspecs.
|
5288
5553
|
self.build_colspecs()
|
5289
5554
|
# Generate headrows, footrows, bodyrows.
|
@@ -5321,6 +5586,7 @@ class Table_OLD(AbstractBlock):
|
|
5321
5586
|
table = table.replace('\x07footrows\x07', footrows, 1)
|
5322
5587
|
table = table.replace('\x07bodyrows\x07', bodyrows, 1)
|
5323
5588
|
writer.write(table,trace='table')
|
5589
|
+
self.pop_blockname()
|
5324
5590
|
|
5325
5591
|
class Tables_OLD(AbstractBlocks):
|
5326
5592
|
"""List of tables."""
|
@@ -5334,7 +5600,7 @@ class Tables_OLD(AbstractBlocks):
|
|
5334
5600
|
# Does not call AbstractBlocks.validate().
|
5335
5601
|
# Check we have a default table definition,
|
5336
5602
|
for i in range(len(self.blocks)):
|
5337
|
-
if self.blocks[i].
|
5603
|
+
if self.blocks[i].defname == 'old_tabledef-default':
|
5338
5604
|
default = self.blocks[i]
|
5339
5605
|
break
|
5340
5606
|
else:
|
@@ -5357,7 +5623,7 @@ class Tables_OLD(AbstractBlocks):
|
|
5357
5623
|
# Check all tables have valid fill character.
|
5358
5624
|
for b in self.blocks:
|
5359
5625
|
if not b.fillchar or len(b.fillchar) != 1:
|
5360
|
-
raise EAsciiDoc,'[%s] missing or illegal fillchar' % b.
|
5626
|
+
raise EAsciiDoc,'[%s] missing or illegal fillchar' % b.defname
|
5361
5627
|
# Build combined tables delimiter patterns and assign defaults.
|
5362
5628
|
delimiters = []
|
5363
5629
|
for b in self.blocks:
|
@@ -5383,13 +5649,13 @@ class Tables_OLD(AbstractBlocks):
|
|
5383
5649
|
b.validate()
|
5384
5650
|
if config.verbose:
|
5385
5651
|
if b.check_msg:
|
5386
|
-
message.warning('[%s] table definition: %s' % (b.
|
5652
|
+
message.warning('[%s] table definition: %s' % (b.defname,b.check_msg))
|
5387
5653
|
|
5388
5654
|
# End of deprecated old table classes.
|
5389
5655
|
#---------------------------------------------------------------------------
|
5390
5656
|
|
5391
5657
|
#---------------------------------------------------------------------------
|
5392
|
-
#
|
5658
|
+
# filter and theme plugin commands.
|
5393
5659
|
#---------------------------------------------------------------------------
|
5394
5660
|
import shutil, zipfile
|
5395
5661
|
|
@@ -5397,7 +5663,7 @@ def die(msg):
|
|
5397
5663
|
message.stderr(msg)
|
5398
5664
|
sys.exit(1)
|
5399
5665
|
|
5400
|
-
def
|
5666
|
+
def extract_zip(zip_file, destdir):
|
5401
5667
|
"""
|
5402
5668
|
Unzip Zip file to destination directory.
|
5403
5669
|
Throws exception if error occurs.
|
@@ -5414,7 +5680,14 @@ def unzip(zip_file, destdir):
|
|
5414
5680
|
outfile = os.path.join(directory, outfile)
|
5415
5681
|
perms = (zi.external_attr >> 16) & 0777
|
5416
5682
|
message.verbose('extracting: %s' % outfile)
|
5417
|
-
|
5683
|
+
flags = os.O_CREAT | os.O_WRONLY
|
5684
|
+
if sys.platform == 'win32':
|
5685
|
+
flags |= os.O_BINARY
|
5686
|
+
if perms == 0:
|
5687
|
+
# Zip files created under Windows do not include permissions.
|
5688
|
+
fh = os.open(outfile, flags)
|
5689
|
+
else:
|
5690
|
+
fh = os.open(outfile, flags, perms)
|
5418
5691
|
try:
|
5419
5692
|
os.write(fh, zipo.read(zi.filename))
|
5420
5693
|
finally:
|
@@ -5422,100 +5695,162 @@ def unzip(zip_file, destdir):
|
|
5422
5695
|
finally:
|
5423
5696
|
zipo.close()
|
5424
5697
|
|
5425
|
-
|
5698
|
+
def create_zip(zip_file, src, skip_hidden=False):
|
5426
5699
|
"""
|
5427
|
-
|
5700
|
+
Create Zip file. If src is a directory archive all contained files and
|
5701
|
+
subdirectories, if src is a file archive the src file.
|
5702
|
+
Files and directories names starting with . are skipped
|
5703
|
+
if skip_hidden is True.
|
5704
|
+
Throws exception if error occurs.
|
5428
5705
|
"""
|
5706
|
+
zipo = zipfile.ZipFile(zip_file, 'w')
|
5707
|
+
try:
|
5708
|
+
if os.path.isfile(src):
|
5709
|
+
arcname = os.path.basename(src)
|
5710
|
+
message.verbose('archiving: %s' % arcname)
|
5711
|
+
zipo.write(src, arcname, zipfile.ZIP_DEFLATED)
|
5712
|
+
elif os.path.isdir(src):
|
5713
|
+
srcdir = os.path.abspath(src)
|
5714
|
+
if srcdir[-1] != os.path.sep:
|
5715
|
+
srcdir += os.path.sep
|
5716
|
+
for root, dirs, files in os.walk(srcdir):
|
5717
|
+
arcroot = os.path.abspath(root)[len(srcdir):]
|
5718
|
+
if skip_hidden:
|
5719
|
+
for d in dirs[:]:
|
5720
|
+
if d.startswith('.'):
|
5721
|
+
message.verbose('skipping: %s' % os.path.join(arcroot, d))
|
5722
|
+
del dirs[dirs.index(d)]
|
5723
|
+
for f in files:
|
5724
|
+
filename = os.path.join(root,f)
|
5725
|
+
arcname = os.path.join(arcroot, f)
|
5726
|
+
if skip_hidden and f.startswith('.'):
|
5727
|
+
message.verbose('skipping: %s' % arcname)
|
5728
|
+
continue
|
5729
|
+
message.verbose('archiving: %s' % arcname)
|
5730
|
+
zipo.write(filename, arcname, zipfile.ZIP_DEFLATED)
|
5731
|
+
else:
|
5732
|
+
raise ValueError,'src must specify directory or file: %s' % src
|
5733
|
+
finally:
|
5734
|
+
zipo.close()
|
5735
|
+
|
5736
|
+
class Plugin:
|
5737
|
+
"""
|
5738
|
+
--filter and --theme option commands.
|
5739
|
+
"""
|
5740
|
+
CMDS = ('install','remove','list','build')
|
5741
|
+
|
5742
|
+
type = None # 'backend', 'filter' or 'theme'.
|
5429
5743
|
|
5430
5744
|
@staticmethod
|
5431
|
-
def
|
5745
|
+
def get_dir():
|
5432
5746
|
"""
|
5433
|
-
Return path
|
5434
|
-
user home not defined.
|
5747
|
+
Return plugins path (.asciidoc/filters or .asciidoc/themes) in user's
|
5748
|
+
home direcory or None if user home not defined.
|
5435
5749
|
"""
|
5436
5750
|
result = userdir()
|
5437
5751
|
if result:
|
5438
|
-
result = os.path.join(result,'.asciidoc','
|
5752
|
+
result = os.path.join(result, '.asciidoc', Plugin.type+'s')
|
5439
5753
|
return result
|
5440
5754
|
|
5441
5755
|
@staticmethod
|
5442
5756
|
def install(args):
|
5443
5757
|
"""
|
5444
|
-
Install
|
5445
|
-
args[0] is
|
5446
|
-
args[1] is optional destination
|
5758
|
+
Install plugin Zip file.
|
5759
|
+
args[0] is plugin zip file path.
|
5760
|
+
args[1] is optional destination plugins directory.
|
5447
5761
|
"""
|
5448
5762
|
if len(args) not in (1,2):
|
5449
|
-
die('invalid number of arguments:
|
5450
|
-
% ' '.join(args))
|
5763
|
+
die('invalid number of arguments: --%s install %s'
|
5764
|
+
% (Plugin.type, ' '.join(args)))
|
5451
5765
|
zip_file = args[0]
|
5452
5766
|
if not os.path.isfile(zip_file):
|
5453
5767
|
die('file not found: %s' % zip_file)
|
5454
5768
|
reo = re.match(r'^\w+',os.path.split(zip_file)[1])
|
5455
5769
|
if not reo:
|
5456
|
-
die('
|
5457
|
-
% zip_file)
|
5458
|
-
|
5770
|
+
die('file name does not start with legal %s name: %s'
|
5771
|
+
% (Plugin.type, zip_file))
|
5772
|
+
plugin_name = reo.group()
|
5459
5773
|
if len(args) == 2:
|
5460
|
-
|
5461
|
-
if not os.path.isdir(
|
5462
|
-
die('directory not found: %s' %
|
5774
|
+
plugins_dir = args[1]
|
5775
|
+
if not os.path.isdir(plugins_dir):
|
5776
|
+
die('directory not found: %s' % plugins_dir)
|
5463
5777
|
else:
|
5464
|
-
|
5465
|
-
if not
|
5778
|
+
plugins_dir = Plugin.get_dir()
|
5779
|
+
if not plugins_dir:
|
5466
5780
|
die('user home directory is not defined')
|
5467
|
-
|
5468
|
-
if os.path.exists(
|
5469
|
-
die('
|
5781
|
+
plugin_dir = os.path.join(plugins_dir, plugin_name)
|
5782
|
+
if os.path.exists(plugin_dir):
|
5783
|
+
die('%s is already installed: %s' % (Plugin.type, plugin_dir))
|
5470
5784
|
try:
|
5471
|
-
os.makedirs(
|
5785
|
+
os.makedirs(plugin_dir)
|
5472
5786
|
except Exception,e:
|
5473
|
-
die('failed to create
|
5787
|
+
die('failed to create %s directory: %s' % (Plugin.type, str(e)))
|
5474
5788
|
try:
|
5475
|
-
|
5789
|
+
extract_zip(zip_file, plugin_dir)
|
5476
5790
|
except Exception,e:
|
5477
|
-
|
5791
|
+
if os.path.isdir(plugin_dir):
|
5792
|
+
shutil.rmtree(plugin_dir)
|
5793
|
+
die('failed to extract %s: %s' % (Plugin.type, str(e)))
|
5478
5794
|
|
5479
5795
|
@staticmethod
|
5480
5796
|
def remove(args):
|
5481
5797
|
"""
|
5482
|
-
Delete
|
5483
|
-
args[0] is
|
5484
|
-
args[1] is optional
|
5798
|
+
Delete plugin directory.
|
5799
|
+
args[0] is plugin name.
|
5800
|
+
args[1] is optional plugin directory (defaults to ~/.asciidoc/<plugin_name>).
|
5485
5801
|
"""
|
5486
5802
|
if len(args) not in (1,2):
|
5487
|
-
die('invalid number of arguments:
|
5488
|
-
% ' '.join(args))
|
5489
|
-
|
5490
|
-
if not re.match(r'^\w+$',
|
5491
|
-
die('illegal
|
5803
|
+
die('invalid number of arguments: --%s remove %s'
|
5804
|
+
% (Plugin.type, ' '.join(args)))
|
5805
|
+
plugin_name = args[0]
|
5806
|
+
if not re.match(r'^\w+$',plugin_name):
|
5807
|
+
die('illegal %s name: %s' % (Plugin.type, plugin_name))
|
5492
5808
|
if len(args) == 2:
|
5493
5809
|
d = args[1]
|
5494
5810
|
if not os.path.isdir(d):
|
5495
5811
|
die('directory not found: %s' % d)
|
5496
5812
|
else:
|
5497
|
-
d =
|
5813
|
+
d = Plugin.get_dir()
|
5498
5814
|
if not d:
|
5499
5815
|
die('user directory is not defined')
|
5500
|
-
|
5501
|
-
if not os.path.isdir(
|
5502
|
-
die('cannot find
|
5816
|
+
plugin_dir = os.path.join(d, plugin_name)
|
5817
|
+
if not os.path.isdir(plugin_dir):
|
5818
|
+
die('cannot find %s: %s' % (Plugin.type, plugin_dir))
|
5503
5819
|
try:
|
5504
|
-
message.verbose('removing: %s' %
|
5505
|
-
shutil.rmtree(
|
5820
|
+
message.verbose('removing: %s' % plugin_dir)
|
5821
|
+
shutil.rmtree(plugin_dir)
|
5506
5822
|
except Exception,e:
|
5507
|
-
die('failed to delete
|
5823
|
+
die('failed to delete %s: %s' % (Plugin.type, str(e)))
|
5508
5824
|
|
5509
5825
|
@staticmethod
|
5510
|
-
def list():
|
5826
|
+
def list(args):
|
5511
5827
|
"""
|
5512
|
-
List all
|
5828
|
+
List all plugin directories (global and local).
|
5513
5829
|
"""
|
5514
|
-
for d in [os.path.join(d,'
|
5830
|
+
for d in [os.path.join(d, Plugin.type+'s') for d in config.get_load_dirs()]:
|
5515
5831
|
if os.path.isdir(d):
|
5516
5832
|
for f in os.walk(d).next()[1]:
|
5517
5833
|
message.stdout(os.path.join(d,f))
|
5518
5834
|
|
5835
|
+
@staticmethod
|
5836
|
+
def build(args):
|
5837
|
+
"""
|
5838
|
+
Create plugin Zip file.
|
5839
|
+
args[0] is Zip file name.
|
5840
|
+
args[1] is plugin directory.
|
5841
|
+
"""
|
5842
|
+
if len(args) != 2:
|
5843
|
+
die('invalid number of arguments: --%s build %s'
|
5844
|
+
% (Plugin.type, ' '.join(args)))
|
5845
|
+
zip_file = args[0]
|
5846
|
+
plugin_source = args[1]
|
5847
|
+
if not (os.path.isdir(plugin_source) or os.path.isfile(plugin_source)):
|
5848
|
+
die('plugin source not found: %s' % plugin_source)
|
5849
|
+
try:
|
5850
|
+
create_zip(zip_file, plugin_source, skip_hidden=True)
|
5851
|
+
except Exception,e:
|
5852
|
+
die('failed to create %s: %s' % (zip_file, str(e)))
|
5853
|
+
|
5519
5854
|
|
5520
5855
|
#---------------------------------------------------------------------------
|
5521
5856
|
# Application code.
|
@@ -5564,9 +5899,12 @@ def asciidoc(backend, doctype, confiles, infile, outfile, options):
|
|
5564
5899
|
if os.path.isfile(f):
|
5565
5900
|
config.load_file(f, include=include, exclude=exclude)
|
5566
5901
|
else:
|
5567
|
-
raise EAsciiDoc,'configuration file %s
|
5568
|
-
|
5902
|
+
raise EAsciiDoc,'missing configuration file: %s' % f
|
5569
5903
|
try:
|
5904
|
+
document.attributes['python'] = sys.executable
|
5905
|
+
for f in config.filters:
|
5906
|
+
if not config.find_config_dir('filters', f):
|
5907
|
+
raise EAsciiDoc,'missing filter: %s' % f
|
5570
5908
|
if doctype not in (None,'article','manpage','book'):
|
5571
5909
|
raise EAsciiDoc,'illegal document type'
|
5572
5910
|
# Set processing options.
|
@@ -5602,12 +5940,14 @@ def asciidoc(backend, doctype, confiles, infile, outfile, options):
|
|
5602
5940
|
has_header = document.parse_header(doctype,backend)
|
5603
5941
|
# doctype is now finalized.
|
5604
5942
|
document.attributes['doctype-'+document.doctype] = ''
|
5943
|
+
config.set_theme_attributes()
|
5605
5944
|
# Load backend configuration files.
|
5606
5945
|
if '-e' not in options:
|
5607
5946
|
f = document.backend + '.conf'
|
5608
|
-
|
5609
|
-
|
5610
|
-
|
5947
|
+
conffile = config.load_backend()
|
5948
|
+
if not conffile:
|
5949
|
+
raise EAsciiDoc,'missing backend conf file: %s' % f
|
5950
|
+
document.attributes['backend-confdir'] = os.path.dirname(conffile)
|
5611
5951
|
# backend is now known.
|
5612
5952
|
document.attributes['backend-'+document.backend] = ''
|
5613
5953
|
document.attributes[document.backend+'-'+document.doctype] = ''
|
@@ -5656,10 +5996,13 @@ def asciidoc(backend, doctype, confiles, infile, outfile, options):
|
|
5656
5996
|
# Document header attributes override conf file attributes.
|
5657
5997
|
document.attributes.update(AttributeEntry.attributes)
|
5658
5998
|
document.update_attributes()
|
5659
|
-
# Configuration is fully loaded
|
5999
|
+
# Configuration is fully loaded.
|
5660
6000
|
config.expand_all_templates()
|
5661
6001
|
# Check configuration for consistency.
|
5662
6002
|
config.validate()
|
6003
|
+
# Initialize top level block name.
|
6004
|
+
if document.attributes.get('blockname'):
|
6005
|
+
AbstractBlock.blocknames.append(document.attributes['blockname'])
|
5663
6006
|
paragraphs.initialize()
|
5664
6007
|
lists.initialize()
|
5665
6008
|
if config.dumping:
|
@@ -5765,7 +6108,7 @@ def execute(cmd,opts,args):
|
|
5765
6108
|
"""
|
5766
6109
|
config.init(cmd)
|
5767
6110
|
if len(args) > 1:
|
5768
|
-
usage('
|
6111
|
+
usage('Too many arguments')
|
5769
6112
|
sys.exit(1)
|
5770
6113
|
backend = None
|
5771
6114
|
doctype = None
|
@@ -5786,19 +6129,22 @@ def execute(cmd,opts,args):
|
|
5786
6129
|
sys.exit(0)
|
5787
6130
|
if o in ('-b','--backend'):
|
5788
6131
|
backend = v
|
5789
|
-
# config.cmd_attrs['backend'] = v
|
5790
6132
|
if o in ('-c','--dump-conf'):
|
5791
6133
|
options.append('-c')
|
5792
6134
|
if o in ('-d','--doctype'):
|
5793
6135
|
doctype = v
|
5794
|
-
# config.cmd_attrs['doctype'] = v
|
5795
6136
|
if o in ('-e','--no-conf'):
|
5796
6137
|
options.append('-e')
|
5797
6138
|
if o in ('-f','--conf-file'):
|
5798
6139
|
confiles.append(v)
|
6140
|
+
if o == '--filter':
|
6141
|
+
config.filters.append(v)
|
5799
6142
|
if o in ('-n','--section-numbers'):
|
5800
6143
|
o = '-a'
|
5801
6144
|
v = 'numbered'
|
6145
|
+
if o == '--theme':
|
6146
|
+
o = '-a'
|
6147
|
+
v = 'theme='+v
|
5802
6148
|
if o in ('-a','--attribute'):
|
5803
6149
|
e = parse_entry(v, allow_name_only=True)
|
5804
6150
|
if not e:
|
@@ -5828,9 +6174,6 @@ def execute(cmd,opts,args):
|
|
5828
6174
|
if len(args) == 0:
|
5829
6175
|
usage('No source file specified')
|
5830
6176
|
sys.exit(1)
|
5831
|
-
# if not backend:
|
5832
|
-
# usage('No --backend option specified')
|
5833
|
-
# sys.exit(1)
|
5834
6177
|
stdin,stdout = sys.stdin,sys.stdout
|
5835
6178
|
try:
|
5836
6179
|
infile = args[0]
|
@@ -5868,11 +6211,12 @@ if __name__ == '__main__':
|
|
5868
6211
|
['attribute=','backend=','conf-file=','doctype=','dump-conf',
|
5869
6212
|
'help','no-conf','no-header-footer','out-file=',
|
5870
6213
|
'section-numbers','verbose','version','safe','unsafe',
|
5871
|
-
'doctest','filter'])
|
6214
|
+
'doctest','filter=','theme='])
|
5872
6215
|
except getopt.GetoptError:
|
5873
6216
|
message.stderr('illegal command options')
|
5874
6217
|
sys.exit(1)
|
5875
|
-
|
6218
|
+
opt_names = [opt[0] for opt in opts]
|
6219
|
+
if '--doctest' in opt_names:
|
5876
6220
|
# Run module doctests.
|
5877
6221
|
import doctest
|
5878
6222
|
options = doctest.NORMALIZE_WHITESPACE + doctest.ELLIPSIS
|
@@ -5882,21 +6226,32 @@ if __name__ == '__main__':
|
|
5882
6226
|
sys.exit(0)
|
5883
6227
|
else:
|
5884
6228
|
sys.exit(1)
|
5885
|
-
|
6229
|
+
# Look for plugin management commands.
|
6230
|
+
count = 0
|
6231
|
+
for o,v in opts:
|
6232
|
+
if o in ('-b','--backend','--filter','--theme'):
|
6233
|
+
if o == '-b':
|
6234
|
+
o = '--backend'
|
6235
|
+
plugin = o[2:]
|
6236
|
+
cmd = v
|
6237
|
+
if cmd not in Plugin.CMDS:
|
6238
|
+
continue
|
6239
|
+
count += 1
|
6240
|
+
if count > 1:
|
6241
|
+
die('--backend, --filter and --theme options are mutually exclusive')
|
6242
|
+
if count == 1:
|
6243
|
+
# Execute plugin management commands.
|
6244
|
+
if not cmd:
|
6245
|
+
die('missing --%s command' % plugin)
|
6246
|
+
if cmd not in Plugin.CMDS:
|
6247
|
+
die('illegal --%s command: %s' % (plugin, cmd))
|
6248
|
+
Plugin.type = plugin
|
5886
6249
|
config.init(sys.argv[0])
|
5887
|
-
config.verbose = bool(set(['-v','--verbose']) & set(
|
5888
|
-
|
5889
|
-
|
5890
|
-
|
5891
|
-
|
5892
|
-
|
5893
|
-
|
5894
|
-
|
5895
|
-
Filter.list()
|
5896
|
-
else:
|
5897
|
-
die('illegal --filter command: %s' % args[0])
|
5898
|
-
sys.exit(0)
|
5899
|
-
try:
|
5900
|
-
execute(sys.argv[0],opts,args)
|
5901
|
-
except KeyboardInterrupt:
|
5902
|
-
sys.exit(1)
|
6250
|
+
config.verbose = bool(set(['-v','--verbose']) & set(opt_names))
|
6251
|
+
getattr(Plugin,cmd)(args)
|
6252
|
+
else:
|
6253
|
+
# Execute asciidoc.
|
6254
|
+
try:
|
6255
|
+
execute(sys.argv[0],opts,args)
|
6256
|
+
except KeyboardInterrupt:
|
6257
|
+
sys.exit(1)
|