txt2tags.py (txt2tags-3.6) | : | txt2tags.py (txt2tags-3.7) | ||
---|---|---|---|---|
skipping to change at line 82 | skipping to change at line 82 | |||
import collections | import collections | |||
import getopt | import getopt | |||
import io | import io | |||
import os | import os | |||
import re | import re | |||
import sys | import sys | |||
############################################################################## | ############################################################################## | |||
# Program information | # Program information | |||
my_url = "http://txt2tags.org" | my_url = "https://txt2tags.org" | |||
my_name = "txt2tags" | my_name = "txt2tags" | |||
my_email = "jendrikseipp@gmail.com" | my_email = "jendrikseipp@gmail.com" | |||
__version__ = "3.6" | __version__ = "3.7" | |||
# FLAGS : the conversion related flags , may be used in %!options | # FLAGS : the conversion related flags , may be used in %!options | |||
# OPTIONS : the conversion related options, may be used in %!options | # OPTIONS : the conversion related options, may be used in %!options | |||
# ACTIONS : the other behavior modifiers, valid on command line only | # ACTIONS : the other behavior modifiers, valid on command line only | |||
# SETTINGS: global miscellaneous settings, valid on RC file only | ||||
# NO_TARGET: actions that don't require a target specification | # NO_TARGET: actions that don't require a target specification | |||
# NO_MULTI_INPUT: actions that don't accept more than one input file | # NO_MULTI_INPUT: actions that don't accept more than one input file | |||
# CONFIG_KEYWORDS: the valid %!key:val keywords | # CONFIG_KEYWORDS: the valid %!key:val keywords | |||
# | # | |||
# FLAGS and OPTIONS are configs that affect the converted document. | # FLAGS and OPTIONS are configs that affect the converted document. | |||
# They usually have also a --no-<option> to turn them OFF. | # They usually have also a --no-<option> to turn them OFF. | |||
# | # | |||
# ACTIONS are needed because when handling multiple input files, strange | # ACTIONS are needed because when handling multiple input files, strange | |||
# behavior may occur. There is no --no-<action>. | # behavior may occur. There is no --no-<action>. | |||
# Options --version and --help inside %!options are odd. | # Options --version and --help inside %!options are odd. | |||
FLAGS = { | FLAGS = { | |||
"headers": 1, | "headers": 1, | |||
"enum-title": 0, | "enum-title": 0, | |||
"mask-email": 0, | ||||
"toc-only": 0, | ||||
"toc": 0, | "toc": 0, | |||
"rc": 1, | "rc": 1, | |||
"quiet": 0, | "quiet": 0, | |||
"slides": 0, | "slides": 0, | |||
} | } | |||
OPTIONS = { | OPTIONS = { | |||
"target": "", | "target": "", | |||
"toc-level": 3, | ||||
"style": "", | "style": "", | |||
"infile": "", | "infile": "", | |||
"outfile": "", | "outfile": "", | |||
"config-file": "", | "config-file": "", | |||
"lang": "", | "lang": "", | |||
} | } | |||
ACTIONS = { | ACTIONS = { | |||
"help": 0, | "help": 0, | |||
"version": 0, | "version": 0, | |||
"verbose": 0, | "verbose": 0, | |||
"debug": 0, | "debug": 0, | |||
"targets": 0, | "targets": 0, | |||
} | } | |||
SETTINGS = {} # for future use | NO_TARGET = ["help", "version", "targets"] | |||
NO_TARGET = ["help", "version", "toc-only", "targets"] | ||||
CONFIG_KEYWORDS = ["target", "style", "options", "preproc", "postproc"] | CONFIG_KEYWORDS = ["target", "style", "options", "preproc", "postproc"] | |||
TARGET_NAMES = { | TARGET_NAMES = { | |||
"html": "HTML page", | "html": "HTML page", | |||
"sgml": "SGML document", | "sgml": "SGML document", | |||
"dbk": "DocBook document", | "dbk": "DocBook document", | |||
"tex": "LaTeX document", | "tex": "LaTeX document", | |||
"lout": "Lout document", | "lout": "Lout document", | |||
"man": "UNIX Manual page", | "man": "UNIX Manual page", | |||
"mgp": "MagicPoint presentation", | "mgp": "MagicPoint presentation", | |||
skipping to change at line 182 | skipping to change at line 177 | |||
MODULEIN = MODULEOUT = "-module-" | MODULEIN = MODULEOUT = "-module-" | |||
ESCCHAR = "\x00" | ESCCHAR = "\x00" | |||
SEPARATOR = "\x01" | SEPARATOR = "\x01" | |||
LISTNAMES = {"-": "list", "+": "numlist", ":": "deflist"} | LISTNAMES = {"-": "list", "+": "numlist", ":": "deflist"} | |||
VERSIONSTR = "{} version {} <{}>".format(my_name, __version__, my_url) | VERSIONSTR = "{} version {} <{}>".format(my_name, __version__, my_url) | |||
USAGE = "\n".join( | USAGE = "\n".join( | |||
[ | [ | |||
"", | "", | |||
"Usage: %s [OPTIONS] [infile.t2t ...]" % my_name, | "Usage: %s [OPTIONS] infile.t2t" % my_name, | |||
"", | "", | |||
" --targets print a list of all the available targets and exi t", | " --targets list available targets and exit", | |||
" -t, --target=TYPE set target document type. currently supported:", | " -t, --target=TYPE set target document type. currently supported:", | |||
" %s," % ", ".join(TARGETS[:9]), | " %s" % ", ".join(TARGETS), | |||
" %s" % ", ".join(TARGETS[9:]), | ||||
" -i, --infile=FILE set FILE as the input file name ('-' for STDIN)", | " -i, --infile=FILE set FILE as the input file name ('-' for STDIN)", | |||
" -o, --outfile=FILE set FILE as the output file name ('-' for STDOUT) ", | " -o, --outfile=FILE set FILE as the output file name ('-' for STDOUT) ", | |||
" --toc add an automatic Table of Contents to the output" | " --toc add a table of contents to the output", | |||
, | " -n, --enum-title enumerate all titles as 1, 1.1, 1.1.1, etc.", | |||
" --toc-level=N set maximum TOC level (depth) to N", | " --style=FILE use FILE as the document style (e.g., a CSS file) | |||
" --toc-only print the Table of Contents and exit", | ", | |||
" -n, --enum-title enumerate all titles as 1, 1.1, 1.1.1, etc", | " -H, --no-headers omit header and footer from output", | |||
" --style=FILE use FILE as the document style (like HTML CSS)", | ||||
" -H, --no-headers suppress header and footer from the output", | ||||
" --mask-email hide email from spam robots. x@y.z turns <x (a) y | ||||
z>", | ||||
" -C, --config-file=F read configuration from file F", | " -C, --config-file=F read configuration from file F", | |||
" -q, --quiet quiet mode, suppress all output (except errors)", | " -q, --quiet suppress all output (except errors)", | |||
" -v, --verbose print informative messages during conversion", | " -v, --verbose print informative messages during conversion", | |||
" -h, --help print this help information and exit", | " -h, --help print this help text and exit", | |||
" -V, --version print program version and exit", | " -V, --version print program version and exit", | |||
"", | "", | |||
"Turn OFF options:", | "Turn off options:", | |||
" --no-enum-title, --no-headers, --no-infile,", | " --no-enum-title, --headers, --no-quiet,", | |||
" --no-mask-email, --no-outfile, --no-quiet, --no-rc, --no-slides,", | " --no-rc, --no-style, --no-toc", | |||
" --no-style, --no-targets, --no-toc, --no-toc-only", | ||||
"", | "", | |||
"Example:", | "Example:", | |||
" {} -t html --toc {}".format(my_name, "file.t2t"), | " {} -t html --toc {}".format(my_name, "file.t2t"), | |||
"", | "", | |||
"By default, converted output is saved to 'infile.<target>'.", | "By default, converted output is saved to 'infile.<target>'.", | |||
"Use --outfile to force an output file name.", | "Use --outfile to force an output file name.", | |||
"If input file is '-', reads from STDIN.", | "If input file is '-', read from STDIN.", | |||
"If output file is '-', dumps output to STDOUT.", | "If output file is '-', dump output to STDOUT.", | |||
"", | "", | |||
my_url, | my_url, | |||
"", | "", | |||
] | ] | |||
) | ) | |||
############################################################################## | ############################################################################## | |||
# Here is all the target's templates | # Here is all the target's templates | |||
# You may edit them to fit your needs | # You may edit them to fit your needs | |||
skipping to change at line 244 | skipping to change at line 234 | |||
%(HEADER2)s | %(HEADER2)s | |||
%(HEADER3)s | %(HEADER3)s | |||
""", | """, | |||
"sgml": """\ | "sgml": """\ | |||
<!doctype linuxdoc system> | <!doctype linuxdoc system> | |||
<article> | <article> | |||
<title>%(HEADER1)s | <title>%(HEADER1)s | |||
<author>%(HEADER2)s | <author>%(HEADER2)s | |||
<date>%(HEADER3)s | <date>%(HEADER3)s | |||
""", | """, | |||
# HTML5 reference code: | ||||
# https://github.com/h5bp/html5-boilerplate/blob/master/index.html | ||||
# https://github.com/murtaugh/HTML5-Reset/blob/master/index.html | ||||
"html": """\ | "html": """\ | |||
<!DOCTYPE html> | <!DOCTYPE html> | |||
<html> | <html> | |||
<head> | <head> | |||
<meta charset="%(ENCODING)s"> | <meta charset="%(ENCODING)s"> | |||
<title>%(HEADER1)s</title> | <title>%(HEADER1)s</title> | |||
<meta name="generator" content="http://txt2tags.org"> | <meta name="generator" content="https://txt2tags.org"> | |||
<link rel="stylesheet" href="%(STYLE)s"> | <link rel="stylesheet" href="%(STYLE)s"> | |||
<style> | <style type="text/css"> | |||
body{background-color:#fff;color:#000;} | blockquote{margin: 1em 2em; border-left: 2px solid #999; | |||
font-style: oblique; padding-left: 1em;} | ||||
blockquote:first-letter{margin: .2em .1em .1em 0; font-size: 160%%; font-weight: | ||||
bold;} | ||||
blockquote:first-line{font-weight: bold;} | ||||
body{font-family: sans-serif;} | ||||
hr{background-color:#000;border:0;color:#000;} | hr{background-color:#000;border:0;color:#000;} | |||
hr.heavy{height:5px;} | hr.heavy{height:2px;} | |||
hr.light{height:1px;} | hr.light{height:1px;} | |||
img{border:0;display:block;} | img{border:0;display:block;} | |||
img.right{margin:0 0 0 auto;} | img.right{margin:0 0 0 auto;} | |||
img.center{border:0;margin:0 auto;} | img.center{border:0;margin:0 auto;} | |||
table th,table td{padding:4px;} | table{border-collapse: collapse;} | |||
.center,header{text-align:center;} | table th,table td{padding: 3px 7px 2px 7px;} | |||
table.center {margin-left:auto; margin-right:auto;} | table th{background-color: lightgrey;} | |||
table.center{margin-left:auto; margin-right:auto;} | ||||
.center{text-align:center;} | ||||
.right{text-align:right;} | .right{text-align:right;} | |||
.left{text-align:left;} | .left{text-align:left;} | |||
.tableborder,.tableborder td,.tableborder th{border:1px solid #000;} | .tableborder,.tableborder td,.tableborder th{border:1px solid #000;} | |||
.underline{text-decoration:underline;} | .underline{text-decoration:underline;} | |||
</style> | </style> | |||
</head> | </head> | |||
<body> | <body> | |||
<header> | <header> | |||
<hgroup> | <hgroup> | |||
<h1>%(HEADER1)s</h1> | <h1>%(HEADER1)s</h1> | |||
skipping to change at line 2066 | skipping to change at line 2059 | |||
ret.append(["all", "infile", infile]) | ret.append(["all", "infile", infile]) | |||
# Apply 'ignore' and 'filter_' rules (filter_ is stronger) | # Apply 'ignore' and 'filter_' rules (filter_ is stronger) | |||
if ignore or filter_: | if ignore or filter_: | |||
filtered = [] | filtered = [] | |||
for target, name, value in ret: | for target, name, value in ret: | |||
if (filter_ and name in filter_) or (ignore and name not in igno re): | if (filter_ and name in filter_) or (ignore and name not in igno re): | |||
filtered.append([target, name, value]) | filtered.append([target, name, value]) | |||
ret = filtered[:] | ret = filtered[:] | |||
# Add the original command line string as 'realcmdline' | ||||
ret.append(["all", "realcmdline", cmdline]) | ||||
return ret | return ret | |||
############################################################################## | ############################################################################## | |||
class SourceDocument: | class SourceDocument: | |||
""" | """ | |||
SourceDocument class - scan document structure, extract data | SourceDocument class - scan document structure, extract data | |||
It knows about full files. It reads a file and identify all | It knows about full files. It reads a file and identify all | |||
the areas beginning (Head,Conf,Body). With this info it can | the areas beginning (Head,Conf,Body). With this info it can | |||
skipping to change at line 2229 | skipping to change at line 2219 | |||
This class is the configuration master. It knows how to handle | This class is the configuration master. It knows how to handle | |||
the RAW and PARSED config format. It also performs the sanity | the RAW and PARSED config format. It also performs the sanity | |||
checking for a given configuration. | checking for a given configuration. | |||
DATA: | DATA: | |||
self.raw - Stores the config on the RAW format | self.raw - Stores the config on the RAW format | |||
self.parsed - Stores the config on the PARSED format | self.parsed - Stores the config on the PARSED format | |||
self.defaults - Stores the default values for all keys | self.defaults - Stores the default values for all keys | |||
self.off - Stores the OFF values for all keys | self.off - Stores the OFF values for all keys | |||
self.multi - List of keys which can have multiple values | self.multi - List of keys which can have multiple values | |||
self.numeric - List of keys which value must be a number | ||||
self.incremental - List of keys which are incremental | self.incremental - List of keys which are incremental | |||
RAW FORMAT: | RAW FORMAT: | |||
The RAW format is a list of lists, being each mother list item | The RAW format is a list of lists, being each mother list item | |||
a full configuration entry. Any entry is a 3 item list, on | a full configuration entry. Any entry is a 3 item list, on | |||
the following format: [ TARGET, KEY, VALUE ] | the following format: [ TARGET, KEY, VALUE ] | |||
Being a list, the order is preserved, so it's easy to use | Being a list, the order is preserved, so it's easy to use | |||
different kinds of configs, as CONF area and command line, | different kinds of configs, as CONF area and command line, | |||
respecting the precedence. | respecting the precedence. | |||
The special target 'all' is used when no specific target was | The special target 'all' is used when no specific target was | |||
skipping to change at line 2268 | skipping to change at line 2257 | |||
{'enum-title': 1, 'headers': 0} | {'enum-title': 1, 'headers': 0} | |||
""" | """ | |||
def __init__(self, raw=None, target=""): | def __init__(self, raw=None, target=""): | |||
self.raw = raw or [] | self.raw = raw or [] | |||
self.target = target | self.target = target | |||
self.parsed = {} | self.parsed = {} | |||
self.dft_options = OPTIONS.copy() | self.dft_options = OPTIONS.copy() | |||
self.dft_flags = FLAGS.copy() | self.dft_flags = FLAGS.copy() | |||
self.dft_actions = ACTIONS.copy() | self.dft_actions = ACTIONS.copy() | |||
self.dft_settings = SETTINGS.copy() | ||||
self.defaults = self._get_defaults() | self.defaults = self._get_defaults() | |||
self.off = self._get_off() | self.off = self._get_off() | |||
self.incremental = ["verbose"] | self.incremental = ["verbose"] | |||
self.numeric = ["toc-level"] | ||||
self.multi = ["infile", "preproc", "postproc", "options", "style"] | self.multi = ["infile", "preproc", "postproc", "options", "style"] | |||
def _get_defaults(self): | def _get_defaults(self): | |||
"Get the default values for all config/options/flags" | "Get the default values for all config/options/flags" | |||
empty = {} | empty = {} | |||
for kw in CONFIG_KEYWORDS: | for kw in CONFIG_KEYWORDS: | |||
empty[kw] = "" | empty[kw] = "" | |||
empty.update(self.dft_options) | empty.update(self.dft_options) | |||
empty.update(self.dft_flags) | empty.update(self.dft_flags) | |||
empty.update(self.dft_actions) | empty.update(self.dft_actions) | |||
empty.update(self.dft_settings) | ||||
empty["realcmdline"] = "" # internal use only | ||||
empty["sourcefile"] = "" # internal use only | empty["sourcefile"] = "" # internal use only | |||
return empty | return empty | |||
def _get_off(self): | def _get_off(self): | |||
"Turns OFF all the config/options/flags" | "Turns OFF all the config/options/flags" | |||
off = {} | off = {} | |||
for key in self.defaults.keys(): | for key in self.defaults.keys(): | |||
kind = type(self.defaults[key]) | kind = type(self.defaults[key]) | |||
if kind == int: | if kind == int: | |||
off[key] = 0 | off[key] = 0 | |||
skipping to change at line 2406 | skipping to change at line 2391 | |||
"No target specified (try --help)." | "No target specified (try --help)." | |||
+ "\n\n" | + "\n\n" | |||
+ "Please select a target using the -t option or the %!target co mmand." | + "Please select a target using the -t option or the %!target co mmand." | |||
+ "\n" | + "\n" | |||
+ "Example:" | + "Example:" | |||
+ " {} -t html {}".format(my_name, "file.t2t") | + " {} -t html {}".format(my_name, "file.t2t") | |||
+ "\n\n" | + "\n\n" | |||
+ "Run 'txt2tags --targets' to see all available targets." | + "Run 'txt2tags --targets' to see all available targets." | |||
) | ) | |||
# And of course, an infile also | # And of course, an infile also | |||
# TODO#1: It seems that this checking is never reached | if "infile" not in config: | |||
if not config.get("infile"): | ||||
Error("Missing input file (try --help)") | Error("Missing input file (try --help)") | |||
# Is the target valid? | # Is the target valid? | |||
if not TARGETS.count(target): | if not TARGETS.count(target): | |||
Error( | Error( | |||
"Invalid target '%s'" % target | "Invalid target '%s'" % target | |||
+ "\n\n" | + "\n\n" | |||
+ "Run 'txt2tags --targets' to see all the available targets." | + "Run 'txt2tags --targets' to see all the available targets." | |||
) | ) | |||
# Ensure all keys are present | # Ensure all keys are present | |||
empty = self.defaults.copy() | empty = self.defaults.copy() | |||
empty.update(config) | empty.update(config) | |||
config = empty.copy() | config = empty.copy() | |||
# Check integers options | ||||
for key in config.keys(): | ||||
if key in self.numeric: | ||||
try: | ||||
config[key] = int(config[key]) | ||||
except ValueError: | ||||
Error("--%s value must be a number" % key) | ||||
# --toc-only is stronger than others | ||||
if config["toc-only"]: | ||||
config["headers"] = 0 | ||||
config["toc"] = 0 | ||||
config["outfile"] = config["outfile"] or STDOUT | ||||
# Restore target | # Restore target | |||
config["target"] = target | config["target"] = target | |||
# Set output file name | # Set output file name | |||
config["outfile"] = self.get_outfile_name(config) | config["outfile"] = self.get_outfile_name(config) | |||
# Checking suicide | # Checking suicide | |||
if os.path.abspath(config["sourcefile"]) == os.path.abspath( | if os.path.abspath(config["sourcefile"]) == os.path.abspath( | |||
config["outfile"] | config["outfile"] | |||
) and config["outfile"] not in [STDOUT, MODULEOUT]: | ) and config["outfile"] not in [STDOUT, MODULEOUT]: | |||
Error("Input and Output files are the same: %s" % config["outfile"]) | Error("Input and Output files are the same: %s" % config["outfile"]) | |||
return config | return config | |||
skipping to change at line 2896 | skipping to change at line 2868 | |||
self.count[i] = 0 | self.count[i] = 0 | |||
# Compose count id from hierarchy | # Compose count id from hierarchy | |||
for i in range(self.level): | for i in range(self.level): | |||
count_id = "%s%d." % (count_id, self.count[i + 1]) | count_id = "%s%d." % (count_id, self.count[i + 1]) | |||
self.count_id = count_id | self.count_id = count_id | |||
def _set_label(self): | def _set_label(self): | |||
"Compose and save title label, used by anchors." | "Compose and save title label, used by anchors." | |||
# Remove invalid chars from label set by user | # Remove invalid chars from label set by user | |||
self.label = re.sub("[^A-Za-z0-9_-]", "", self.label or "") | self.label = re.sub("[^A-Za-z0-9_-]", "", self.label or "") | |||
# Generate name as 15 first :alnum: chars | ||||
# TODO how to translate safely accented chars to plain? | ||||
# self.label = re.sub('[^A-Za-z0-9]', '', self.txt)[:15] | ||||
# 'tocN' label - sequential count, ignoring 'toc-level' | ||||
# self.label = self.anchor_prefix + str(len(self.toc)+1) | ||||
def _get_tagged_anchor(self): | def _get_tagged_anchor(self): | |||
"Return anchor if user defined a label, or TOC is on." | "Return anchor if user defined a label, or TOC is on." | |||
ret = "" | ret = "" | |||
label = self.label | label = self.label | |||
if CONF["toc"] and self.level <= CONF["toc-level"]: | if CONF["toc"]: | |||
# This count is needed bcos self.toc stores all | ||||
# titles, regardless of the 'toc-level' setting, | ||||
# so we can't use self.toc length to number anchors | ||||
self.anchor_count += 1 | self.anchor_count += 1 | |||
# Autonumber label (if needed) | # Autonumber label (if needed) | |||
label = label or "{}{}".format(self.anchor_prefix, self.anchor_count ) | label = label or "{}{}".format(self.anchor_prefix, self.anchor_count ) | |||
if label and TAGS["anchor"]: | if label and TAGS["anchor"]: | |||
ret = regex["x"].sub(label, TAGS["anchor"]) | ret = regex["x"].sub(label, TAGS["anchor"]) | |||
return ret | return ret | |||
def _get_full_title_text(self): | def _get_full_title_text(self): | |||
"Returns the full title contents, already escaped." | "Returns the full title contents, already escaped." | |||
ret = self.txt | ret = self.txt | |||
skipping to change at line 2961 | skipping to change at line 2925 | |||
ret.append("") # blank line before | ret.append("") # blank line before | |||
ret.append(tagged) | ret.append(tagged) | |||
# Get the right letter count for UTF | # Get the right letter count for UTF | |||
if isinstance(full_title, bytes): | if isinstance(full_title, bytes): | |||
full_title = full_title.decode(ENCODING) | full_title = full_title.decode(ENCODING) | |||
ret.append(regex["x"].sub("=" * len(full_title), self.tag)) | ret.append(regex["x"].sub("=" * len(full_title), self.tag)) | |||
else: | else: | |||
ret.append(tagged) | ret.append(tagged) | |||
return ret | return ret | |||
def dump_marked_toc(self, max_level=99): | def dump_marked_toc(self): | |||
"Dumps all toc itens as a valid t2t-marked list" | "Dumps all toc itens as a valid t2t-marked list" | |||
ret = [] | ret = [] | |||
toc_count = 1 | toc_count = 1 | |||
for level, count_id, txt, label in self.toc: | for level, count_id, txt, label in self.toc: | |||
if level > max_level: | ||||
continue # ignore | ||||
indent = " " * level | indent = " " * level | |||
id_txt = ("{} {}".format(count_id, txt)).lstrip() | id_txt = ("{} {}".format(count_id, txt)).lstrip() | |||
label = label or self.anchor_prefix + repr(toc_count) | label = label or self.anchor_prefix + repr(toc_count) | |||
toc_count += 1 | toc_count += 1 | |||
# TOC will have crosslinks to anchors | # TOC will have crosslinks to anchors | |||
if TAGS["anchor"]: | if TAGS["anchor"]: | |||
if CONF["enum-title"] and level == 1: | if CONF["enum-title"] and level == 1: | |||
# 1. [Foo #anchor] is more readable than [1. Foo #anchor] in level 1. | # 1. [Foo #anchor] is more readable than [1. Foo #anchor] in level 1. | |||
# This is a stoled idea from Windows .CHM help files. | # This is an idea stolen from Windows .CHM help files. | |||
tocitem = '{}+ [""{}"" #{}]'.format(indent, txt, label) | tocitem = '{}+ [""{}"" #{}]'.format(indent, txt, label) | |||
else: | else: | |||
tocitem = '{}- [""{}"" #{}]'.format(indent, id_txt, label) | tocitem = '{}- [""{}"" #{}]'.format(indent, id_txt, label) | |||
# TOC will be plain text (no links) | # TOC will be plain text (no links) | |||
else: | else: | |||
if TARGET in ["txt", "man"]: | if TARGET in ["txt", "man"]: | |||
# For these, the list is not necessary, just dump the text | # For these, the list is not necessary, just dump the text | |||
tocitem = '{}""{}""'.format(indent, id_txt) | tocitem = '{}""{}""'.format(indent, id_txt) | |||
else: | else: | |||
skipping to change at line 3776 | skipping to change at line 3738 | |||
return result | return result | |||
############################################################################## | ############################################################################## | |||
def listTargets(): | def listTargets(): | |||
"""List available targets.""" | """List available targets.""" | |||
for target, name in sorted(TARGET_NAMES.items()): | for target, name in sorted(TARGET_NAMES.items()): | |||
print("{:8}{}".format(target, name)) | print("{:8}{}".format(target, name)) | |||
def dumpConfig(source_raw, parsed_config): | ||||
onoff = {1: "ON", 0: "OFF"} | ||||
data = [ | ||||
("RC file", RC_RAW), | ||||
("source document", source_raw), | ||||
("command line", CMDLINE_RAW), | ||||
] | ||||
# First show all RAW data found | ||||
for label, cfg in data: | ||||
print("RAW config for %s" % label) | ||||
for target, key, val in cfg: | ||||
target = "(%s)" % target | ||||
key = dotted_spaces("%-14s" % key) | ||||
val = val or "ON" | ||||
print(" {:<8} {}: {}".format(target, key, val)) | ||||
print() | ||||
# Then the parsed results of all of them | ||||
print("Full PARSED config") | ||||
keys = list(parsed_config.keys()) | ||||
keys.sort() # sorted | ||||
for key in keys: | ||||
val = parsed_config[key] | ||||
# Filters are the last | ||||
if key == "preproc" or key == "postproc": | ||||
continue | ||||
# Flag beautifier | ||||
if key in list(FLAGS.keys()) or key in list(ACTIONS.keys()): | ||||
val = onoff.get(val) or val | ||||
# List beautifier | ||||
if isinstance(val, list): | ||||
if key == "options": | ||||
sep = " " | ||||
else: | ||||
sep = ", " | ||||
val = sep.join(val) | ||||
print("{:>25}: {}".format(dotted_spaces("%-14s" % key), val)) | ||||
print() | ||||
print("Active filters") | ||||
for filter_ in ["preproc", "postproc"]: | ||||
for rule in parsed_config.get(filter_) or []: | ||||
print( | ||||
"%25s: %s -> %s" | ||||
% (dotted_spaces("%-14s" % filter_), rule[0], rule[1]) | ||||
) | ||||
def get_file_body(file_): | def get_file_body(file_): | |||
"Returns all the document BODY lines" | "Returns all the document BODY lines" | |||
return process_source_file(file_, noconf=1)[1][2] | return process_source_file(file_, noconf=1)[1][2] | |||
def finish_him(outlist, config): | def finish_him(outlist, config): | |||
"Writing output to screen or file" | "Writing output to screen or file" | |||
outfile = config["outfile"] | outfile = config["outfile"] | |||
outlist = unmaskEscapeChar(outlist) | outlist = unmaskEscapeChar(outlist) | |||
outlist = expandLineBreaks(outlist) | outlist = expandLineBreaks(outlist) | |||
skipping to change at line 3859 | skipping to change at line 3776 | |||
for line in outlist: | for line in outlist: | |||
print(line) | print(line) | |||
else: | else: | |||
Message("Saving results to the output file", 1) | Message("Saving results to the output file", 1) | |||
Savefile(outfile, outlist) | Savefile(outfile, outlist) | |||
if not QUIET: | if not QUIET: | |||
print("{} wrote {}".format(my_name, outfile)) | print("{} wrote {}".format(my_name, outfile)) | |||
def toc_tagger(toc, config): | def toc_tagger(toc, config): | |||
"Returns the tagged TOC, as a single tag or a tagged list" | "Returns the tagged TOC, as a single tag or a tagged list" | |||
ret = [] | if not config["toc"]: | |||
return [] | ||||
elif TAGS["TOC"]: | ||||
# Our TOC list is not needed, the target already knows how to do a TOC | ||||
ret = [TAGS["TOC"]] | ||||
# Convert the TOC list (t2t-marked) to the target's list format | # Convert the TOC list (t2t-marked) to the target's list format | |||
if config["toc-only"] or (config["toc"] and not TAGS["TOC"]): | else: | |||
fakeconf = config.copy() | fakeconf = config.copy() | |||
fakeconf["headers"] = 0 | fakeconf["headers"] = 0 | |||
fakeconf["toc-only"] = 0 | ||||
fakeconf["mask-email"] = 0 | ||||
fakeconf["preproc"] = [] | fakeconf["preproc"] = [] | |||
fakeconf["postproc"] = [] | fakeconf["postproc"] = [] | |||
ret, _ = convert(toc, fakeconf) | ret, _ = convert(toc, fakeconf) | |||
set_global_config(config) # restore config | set_global_config(config) # restore config | |||
# Our TOC list is not needed, the target already knows how to do a TOC | ||||
elif config["toc"] and TAGS["TOC"]: | ||||
ret = [TAGS["TOC"]] | ||||
return ret | return ret | |||
def toc_formatter(toc, config): | def toc_formatter(toc, config): | |||
"Formats TOC for automatic placement between headers and body" | "Formats TOC for automatic placement between headers and body" | |||
if config["toc-only"]: | ||||
return toc # no formatting needed | ||||
if not config["toc"]: | if not config["toc"]: | |||
return [] # TOC disabled | return [] # TOC disabled | |||
ret = toc | ret = toc | |||
# TOC open/close tags (if any) | # TOC open/close tags (if any) | |||
if TAGS["tocOpen"]: | if TAGS["tocOpen"]: | |||
ret.insert(0, TAGS["tocOpen"]) | ret.insert(0, TAGS["tocOpen"]) | |||
if TAGS["tocClose"]: | if TAGS["tocClose"]: | |||
ret.append(TAGS["tocClose"]) | ret.append(TAGS["tocClose"]) | |||
skipping to change at line 4120 | skipping to change at line 4034 | |||
guessurl = "http://" + url | guessurl = "http://" + url | |||
else: | else: | |||
guessurl = "ftp://" + url | guessurl = "ftp://" + url | |||
# Not link aware targets -> protocol is useless | # Not link aware targets -> protocol is useless | |||
if not rules["linkable"]: | if not rules["linkable"]: | |||
guessurl = "" | guessurl = "" | |||
# Simple link (not guessed) | # Simple link (not guessed) | |||
if not label and not guessurl: | if not label and not guessurl: | |||
if CONF["mask-email"] and linktype == "email": | # Just add link data to tag | |||
# Do the email mask feature (no TAGs, just text) | tag = TAGS[linktype] | |||
url = url.replace("@", " (a) ") | ret = regex["x"].sub(url, tag) | |||
url = url.replace(".", " ") | ||||
url = "<%s>" % url | ||||
if rules["linkable"]: | ||||
url = doEscape(target, url) | ||||
ret = url | ||||
else: | ||||
# Just add link data to tag | ||||
tag = TAGS[linktype] | ||||
ret = regex["x"].sub(url, tag) | ||||
# Named link or guessed simple link | # Named link or guessed simple link | |||
else: | else: | |||
# Adjusts for guessed link | # Adjusts for guessed link | |||
if not label: | if not label: | |||
label = url # no protocol | label = url # no protocol | |||
if guessurl: | if guessurl: | |||
url = guessurl # with protocol | url = guessurl # with protocol | |||
# Image inside link! | # Image inside link! | |||
if image_re.match(label): | if image_re.match(label): | |||
if rules["imglinkable"]: # get image tag | if rules["imglinkable"]: # get image tag | |||
label = parse_images(label) | label = parse_images(label) | |||
else: | else: | |||
# img@link !supported | # img@link !supported | |||
label = "(%s)" % image_re.match(label).group(1) | label = "(%s)" % image_re.match(label).group(1) | |||
skipping to change at line 4571 | skipping to change at line 4476 | |||
targ, key, val = ConfigLines().parse_line(line, None, target) | targ, key, val = ConfigLines().parse_line(line, None, target) | |||
if key: | if key: | |||
Debug("Found config '{}', value '{}'".format(key, val), 1, linen r) | Debug("Found config '{}', value '{}'".format(key, val), 1, linen r) | |||
else: | else: | |||
Debug("Bogus Special Line", 1, linenr) | Debug("Bogus Special Line", 1, linenr) | |||
# %!include command | # %!include command | |||
if key == "include": | if key == "include": | |||
incpath = os.path.dirname(CONF["sourcefile"]) | incpath = os.path.dirname(CONF["sourcefile"]) | |||
incfile = val | incfile = val | |||
err = "A file cannot include itself (loop!)" | err = "A file cannot include itself (loop!)" | |||
if CONF["sourcefile"] == incfile: | if CONF["sourcefile"] == incfile: | |||
Error("{}: {}".format(err, incfile)) | Error("{}: {}".format(err, incfile)) | |||
inctype, inclines = get_include_contents(incfile, incpath) | inctype, inclines = get_include_contents(incfile, incpath) | |||
# Verb, raw and tagged are easy | # Verb, raw and tagged are easy | |||
if inctype != "t2t": | if inctype != "t2t": | |||
ret.extend(BLOCK.blockin(inctype)) | ret.extend(BLOCK.blockin(inctype)) | |||
skipping to change at line 4840 | skipping to change at line 4744 | |||
# Maybe close some opened title area? | # Maybe close some opened title area? | |||
if rules["titleblocks"]: | if rules["titleblocks"]: | |||
ret.extend(TITLE.close_all()) | ret.extend(TITLE.close_all()) | |||
# Maybe a major tag to enclose body? (like DIV for CSS) | # Maybe a major tag to enclose body? (like DIV for CSS) | |||
if TAGS["bodyOpen"]: | if TAGS["bodyOpen"]: | |||
ret.insert(0, TAGS["bodyOpen"]) | ret.insert(0, TAGS["bodyOpen"]) | |||
if TAGS["bodyClose"]: | if TAGS["bodyClose"]: | |||
ret.append(TAGS["bodyClose"]) | ret.append(TAGS["bodyClose"]) | |||
if CONF["toc-only"]: | marked_toc = TITLE.dump_marked_toc() | |||
ret = [] | ||||
marked_toc = TITLE.dump_marked_toc(CONF["toc-level"]) | ||||
return ret, marked_toc | return ret, marked_toc | |||
def exec_command_line(user_cmdline=None): | def exec_command_line(user_cmdline=None): | |||
global CMDLINE_RAW, RC_RAW, DEBUG, VERBOSE, QUIET, Error | global CMDLINE_RAW, RC_RAW, DEBUG, VERBOSE, QUIET, Error | |||
# Extract command line data | # Extract command line data | |||
cmdline_data = user_cmdline or sys.argv[1:] | cmdline_data = user_cmdline or sys.argv[1:] | |||
CMDLINE_RAW = CommandLine().get_raw_config(cmdline_data, relative=True) | CMDLINE_RAW = CommandLine().get_raw_config(cmdline_data, relative=True) | |||
cmdline_parsed = ConfigMaster(CMDLINE_RAW).parse() | cmdline_parsed = ConfigMaster(CMDLINE_RAW).parse() | |||
End of changes. 41 change blocks. | ||||
145 lines changed or deleted | 47 lines changed or added |