From: Jaroslav Kysela Date: Sun, 3 Apr 2016 11:08:41 +0000 (+0200) Subject: markdown doc: more work; added support for other doc pages and TOC X-Git-Tag: v4.2.1~757 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=8ed853b2412d55c8250a5d1811b7365e40fabba4;p=thirdparty%2Ftvheadend.git markdown doc: more work; added support for other doc pages and TOC --- diff --git a/.gitignore b/.gitignore index c262d6aac..59c2cf18b 100644 --- a/.gitignore +++ b/.gitignore @@ -3,6 +3,7 @@ build.* src/version.c src/tvh_locale_inc.c +src/docs_inc.[ch] src/webui/extjs-std.c src/webui/extjs-debug.c src/webui/extjs-tv-std.c diff --git a/Makefile b/Makefile index 0e7458616..4d91463f2 100644 --- a/Makefile +++ b/Makefile @@ -541,9 +541,11 @@ ALL-$(CONFIG_DVBSCAN) += check_dvb_scan # Documentation # -MD-CLASS = $(patsubst docs/class/%.md,%,$(wildcard docs/class/*.md)) SRCS-yes += src/docs.c I18N-C += src/docs_inc.c +I18N-DOCS = $(wildcard docs/markdown/*.md) $(wildcard docs/class/*.md) +MD-ROOT = $(patsubst docs/markdown/%.md,%,$(wildcard docs/markdown/*.md)) +MD-CLASS = $(patsubst docs/class/%.md,%,$(wildcard docs/class/*.md)) # # Internationalization @@ -662,18 +664,30 @@ $(BUILDDIR)/build.o: $(BUILDDIR)/build.c $(pCC) -c -o $@ $< # Documentation -$(BUILDDIR)/docs-timestamp: $(MD-FILES) support/doc/md_to_c.py +$(BUILDDIR)/docs-timestamp: $(I18N-DOCS) support/doc/md_to_c.py @-rm -f src/docs_inc.c + @for i in $(MD-ROOT); do \ + echo "Markdown: docs/markdown/$${i}.md"; \ + support/doc/md_to_c.py --in="docs/markdown/$${i}.md" \ + --name="tvh_doc_root_$${i}" >> src/docs_inc.c || exit 1; \ + done @for i in $(MD-CLASS); do \ - echo "Markdown class: $${i}"; \ + echo "Markdown: docs/class/$${i}.md"; \ support/doc/md_to_c.py --in="docs/class/$${i}.md" \ - --name="tvh_doc_$${i}_class" >> src/docs_inc.c; \ + --name="tvh_doc_$${i}_class" >> src/docs_inc.c || exit 1; \ + done + @printf "\n\nconst struct tvh_doc_page tvh_doc_markdown_pages[] = {\n" >> src/docs_inc.c + @for i in $(MD-ROOT); do \ + echo " { \"$${i}\", tvh_doc_root_$${i} }," >> src/docs_inc.c || exit 1; \ done + @echo " { NULL, NULL }," >> src/docs_inc.c || exit 1 + @echo "};" >> src/docs_inc.c @touch $@ src/docs_inc.c: $(BUILDDIR)/docs-timestamp +src/docs_inc.h: $(BUILDDIR)/docs-timestamp -$(BUILDDIR)/src/docs.o: src/docs_inc.c +$(BUILDDIR)/src/docs.o: src/docs_inc.c $(I18N-DOCS) support/doc/md_to_c.py # Internationalization .PHONY: intl diff --git a/docs/markdown/configure_tvheadend.md b/docs/markdown/configure_tvheadend.md new file mode 100644 index 000000000..029bb1195 --- /dev/null +++ b/docs/markdown/configure_tvheadend.md @@ -0,0 +1,150 @@ +##Configure Tvheadend + +This section gives a high-level overview of the steps needed to get Tvheadend +up and running. For more detailed information, please consult the rest of +this guide - much of it is arranged in the same order as the tabs on the +Tvheadend interface so you know where to look. + +You can also consult the in-application help text, which mirrors this guide +to a very great extent. + +###1. Ensure Tuners are Available for Use + +**Tvheadend web interface: _Configuration -> DVB Inputs -> TV Adapters_** + +On this tab, you'll see a tree structure, with the Linux device list at the +top level (e.g. `/dev/dvb/adapter0`) + +Individual tuners are then the next level down (e.g. `DiBcom 7000PC : DVB-T #0`) + +Click on each tuner that you want Tvheadend to use, and ensure "Enabled" +is checked in the 'Parameters' list + +If anything is obviously wrong at this point, you probably have a +driver/firmware error which you'll need to resolve before going any further. + +###2. Set up Relevant Network(s) + +**Tvheadend web interface: _Configuration -> DVB Inputs -> Networks_** + +Create a network of the appropriate type here. You can have multiple networks +of the same type as necessary, e.g. to have two DVB-T networks defined, +one with HD muxes, one without. + +The creation process allows you to select from a series of pre-defined mux +lists for common DVB sources. These are maintained outside of Tvheadend, and +are downloaded from [linuxtv](http://git.linuxtv.org/cgit.cgi/dtv-scan-tables.git/) - +but they do go out of date as broadcasters move services around and national +authorities change entire pieces of spectrum. As such, you should try the +pre-defined values, but you may need to add muxes manually. + +**TODO: Critical configuration items at this stage: ........** + +###3. Associate the Network with the Respective Tuner(s) + +**Tvheadend web interface: _Configuration -> DVB Inputs -> TV Adapters_** + +Associate each of your tuners with the correct network through _Parameters -> Basic Settings_. + +This can be as simple or as complex as necessary. You may simply have, for +example, a single DVB-S2 network defined and then associate this with all +DVB-S2 tuners. Or, you might have multiple networks defined - different +satellites, different encoding. So, as further examples, you might define +and then associate an HD DVB-T2 (e.g. H.264) network with HD tuners, while +having a separate SD network associated with an independent SD (e.g. MPEG-2) +tuner. + +**TODO: Critical configuration items at this stage: ........** + +At this point, your tuners now know what networks to use: one network can +appear on multiple tuners (many-to-one), and one tuner can have multiple +networks. + +### 4. If Necessary, Manually Add Muxes + +**Tvheadend web interface: _Configuration -> DVB Inputs -> Muxes_** + +Ideally, this is where you'll see a list of the pre-populated muxes as created +when you set up your initial network. However, should there be any issues, +this is where you can manually add missing muxes. You only really need to +worry about this if the pre-defined list didn't work (e.g. because of +out-of-date data as broadcasters re-arrange their services or because automatic +detection hasn't successfully found all the muxes over time. + +If you do need to add something manually, you'll need to search the Internet +for details of the appropriate transmitter and settings: satellites tend not +to change much and are universal over a large area, but terrestrial muxes +are typically very localised and you'll need to know which specific transmitter +you're listening to. + +Good sources of transmitter/mux information include: + +* [KingofSat](http://en.kingofsat.net) for all European satellite information + +* [ukfree.tv](http://www.ukfree.tv/maps/freeview) for UK DVB-T transmitters + +* [Interactive EU DVB-T map](http://www.dvbtmap.eu/mapmux.html) for primarily +central and northern Europe + +> other major sources....? +##NOTE: TODO: TEXT REQUIRED + +You can also use [dvbscan](http://www.linuxtv.org/wiki/index.php/Dvbscan) to +force a scan and effectively ask your tuner what it can see. + +**TODO: Critical configuration items at this stage: ........** + +### 5. Scan for Services + +**Tvheadend web interface: _Configuration -> DVB Inputs -> Services_** + +This is where the services will appear as your tuners tune to the muxes based +on the network you told them to look on. Again, remember what's happening: +Tvheadend is telling your tuner hardware (via the drivers) to sequentially +tune to each mux it knows about, and then see what 'programmes' it can see +on that mux, each of which is identified by a series of unique identifiers +that describe the audio stream(s), the video stream(s), the subtitle stream(s) +and language(s), and so on. + +(For the technically-minded, these unique identifiers - the elementary streams +- are referred to as 'packet identifiers' or 'PIDs'). + +> To force a scan ... +##NOTE: TODO: TEXT REQUIRED + +### 6. Map Services to Channels + +Once scanning for services is complete, you need to map the services to +channels so your client can actually request them (i.e. so you can watch +or record). You can do this in two places: + +**Tvheadend web interface: _Configuration -> DVB Inputs -> Services_** + +Press the "Map All" button. Note the resultant dialog box that allows you +to exclude some services from the mapping: this is covered in more detail +later in this guide. + +**Tvheadend web interface: _Configuration -> Channel/EPG -> Channels_** + +Press the "Map Services" button. Again, you can exclude services as you +can for 'Map All', above. + +> Any explanation on how a channel can map to multiple services <......> + +> Anything about using bouquets +##NOTE: TODO: TEXT REQUIRED + +### 7. Watch TV + +That's it - you're done. You should now have a working basic Tvheadend +installation with channels mapped and ready for use! + +As required, you may now wish to look into: + +* Setting up different EPGs (inc. localised character sets and timing offsets) +* Setting up channel icons +* Setting up recording profiles +* Setting up streaming profiles (including transcoding) +* Arranging your channels into groups (channel tags) +* Setting up softcams for descrambling +* Setting up access control rules for different client types/permission levels diff --git a/docs/markdown/index.md b/docs/markdown/index.md new file mode 100644 index 000000000..600d4c25f --- /dev/null +++ b/docs/markdown/index.md @@ -0,0 +1,19 @@ +#Tvheadend 4.2 User Guide + +![Tvheadend Logo](images/logobig.png) + +#Purpose + +This document is intended to give you a high-level overview of how to set +up Tvheadend for the first time. It does not aim to provide a complete description +of every step or answer every question: more details are available on the +tvheadend [wiki](https://tvheadend.org/projects/tvheadend/wiki). + +As you set things up, consult the on-line (web interface) help as well. +Tvheadend includes copies of many of these pages in the application, which +is easier to find when you're wondering what to do next. + +If you get really stuck, there's the [forum](https://tvheadend.org/projects/tvheadend/boards) +and IRC (*#hts* on *freenode*) - [Kiwi IRC](https://kiwiirc.com/) is a good web +client if you don't already have an IRC client installed, please [Freenode](https://webchat.freenode.net/) +has one of their own. diff --git a/docs/markdown/toc.md b/docs/markdown/toc.md new file mode 100644 index 000000000..bb0edb5dc --- /dev/null +++ b/docs/markdown/toc.md @@ -0,0 +1,19 @@ +####Table of Contents + +Introduction +* Intro1 + +Getting Started +* [Configure Tvheadend](configure_tvheadend) + +Web Interface Guide + +* Access Configuration + + - [Users](class/access) + - [Passwords](class/passwd) + +Appendices + +[About](index) + diff --git a/src/docs.c b/src/docs.c index 1138f8592..3a7ac9a12 100644 --- a/src/docs.c +++ b/src/docs.c @@ -1,5 +1,5 @@ #include -//#include "tvh_locale.h" +#include "docs.h" #define LANGPREF "\xff" #define N_(s) s diff --git a/src/webui/doc_md.c b/src/webui/doc_md.c index 22d20ab39..2947839bf 100644 --- a/src/webui/doc_md.c +++ b/src/webui/doc_md.c @@ -20,6 +20,7 @@ #include "config.h" #include "webui.h" #include "http.h" +#include "docs.h" /* */ static int @@ -201,12 +202,35 @@ http_markdown_class(http_connection_t *hc, const char *clazz) return 0; } +/** + * + */ +static int +http_markdown_page(http_connection_t *hc, const struct tvh_doc_page *page) +{ + const char **doc = page->strings; + const char *lang = hc->hc_access->aa_lang_ui; + htsbuf_queue_t *hq = &hc->hc_reply; + + if (doc == NULL) + return HTTP_STATUS_NOT_FOUND; + for (; *doc; doc++) { + if (*doc[0] == '\xff') { + htsbuf_append_str(hq, tvh_gettext_lang(lang, *doc + 1)); + } else { + htsbuf_append_str(hq, *doc); + } + } + return 0; +} + /** * Handle requests for markdown export. */ int page_markdown(http_connection_t *hc, const char *remain, void *opaque) { + const struct tvh_doc_page *page; char *components[2]; int nc, r; @@ -221,9 +245,18 @@ page_markdown(http_connection_t *hc, const char *remain, void *opaque) r = http_markdown_classes(hc); else if (nc == 2 && !strcmp(components[0], "class")) r = http_markdown_class(hc, components[1]); - else + else if (nc == 1) { + for (page = tvh_doc_markdown_pages; page->name; page++) + if (!strcmp(page->name, components[0])) { + r = http_markdown_page(hc, page); + goto done; + } r = HTTP_STATUS_BAD_REQUEST; + } else { + r = HTTP_STATUS_BAD_REQUEST; + } +done: if (r == 0) http_output_content(hc, "text/markdown"); diff --git a/src/webui/extjs.c b/src/webui/extjs.c index d691be6b2..3fc8f6bdb 100644 --- a/src/webui/extjs.c +++ b/src/webui/extjs.c @@ -196,7 +196,7 @@ page_about(http_connection_t *hc, const char *remain, void *opaque) htsbuf_qprintf(hq, "
\n\
HTS Tvheadend %s
\n\

© 2006 - 2016 Andreas \303\226man, Jaroslav Kysela, Adam Sutton, et al.

\n\ -

\n\ +

\n\

https://tvheadend.org

\n", tvheadend_version); diff --git a/src/webui/static/app/ext.css b/src/webui/static/app/ext.css index 46b5e7b09..0a262dd20 100644 --- a/src/webui/static/app/ext.css +++ b/src/webui/static/app/ext.css @@ -736,29 +736,29 @@ /* Table styles for webUI help */ .hts-doc-text table, .hts-doc-text th, .hts-doc-text td { - font-size: inherit; - border: 1px solid #98c0f4; - margin: 20px + font-size: inherit; + border: 1px solid #98c0f4; + margin: 20px } .hts-doc-text td,.hts-doc-text th { - padding: 5px + padding: 5px } .hts-doc-text table { - border-collapse: collapse + border-collapse: collapse } .hts-doc-text th { - text-align: center; - height: 2em; - background-color: #d9e8fb; - color: black; - font-weight: bold + text-align: center; + height: 2em; + background-color: #d9e8fb; + color: black; + font-weight: bold } .hts-doc-text tr:nth-child(even) { - background-color: #F2F7FE + background-color: #F2F7FE } /* Additional general text styles for webUI help */ @@ -768,33 +768,32 @@ padding: 5px; } - .hts-doc-text p { - margin-top: 1em; - margin-bottom: 1em; - margin-left: 1em + margin-top: 1em; + margin-bottom: 1em; + margin-left: 1em } .hts-doc-text h2, h3, h4 { - font-size: larger + font-size: larger } .hts-doc-text h4 { - color: #2163A6; - margin-top: 15px + color: #2163A6; + margin-top: 15px } .hts-doc-text strong { - font-weight: bold + font-weight: bold } .hts-doc-text em { - font-style: italic + font-style: italic } .hts-doc-text td img { - display: inline; - margin: -5px auto + display: inline; + margin: -5px auto } /* List styles for webUI help */ @@ -815,13 +814,54 @@ } .hts-doc-text ul { - list-style-type:disc; - padding-left:3em + list-style-type:disc; + padding-left:3em } .hts-doc-text ul li { - padding-top: 0px; - padding-bottom: 0px + padding-top: 0px; + padding-bottom: 0px +} + +/* Styles for help TOC */ + +.hts-doc-toc { + width: 200px; + padding: 5px 5px 5px 5px; + float: right; +} + +.hts-doc-toc a { + cursor: pointer; +} + +.hts-doc-toc p { + margin: 3px 0 1px 5px; +} + +.hts-doc-toc li { + padding-top: 5px; + padding-bottom: 5px; +} + +.hts-doc-toc ul { + list-style-type: disc; + padding-left: 2em; +} + +.hts-doc-toc ul ul { + list-style-type: circle; + padding-left: 1em; +} + +.hts-doc-toc ul ul ul { + list-style-type: square; + padding-left: 1em; +} + +.hts-doc-toc ul li { + padding-top: 0px; + padding-bottom: 0px } /* Additional image and caption styles for webUI help */ diff --git a/src/webui/static/app/micromarkdown.js b/src/webui/static/app/micromarkdown.js index 54e7bc020..92ef85f07 100644 --- a/src/webui/static/app/micromarkdown.js +++ b/src/webui/static/app/micromarkdown.js @@ -51,7 +51,7 @@ var micromarkdown = { url2: /[ \t\n]([a-zA-Z]{2,16}:\/\/[a-zA-Z0-9@:%_\+.~#?&=]{2,256}.[a-z]{2,4}\b(\/[\-a-zA-Z0-9@:%_\+.~#?&\/\/=]*)?)[ \t\n]/g }, codeblocks: {}, - parse: function (str, strict) { + parse: function (str, strict, linkfcn) { 'use strict'; var line, nstatus = 0, status, cel, calign, indent, helper, helper1, helper2, count, repstr, stra, trashgc = [], @@ -207,9 +207,17 @@ var micromarkdown = { /* links */ while ((stra = micromarkdown.regexobject.links.exec(str)) !== null) { if (stra[0].substr(0, 1) === '!') { - str = str.replace(stra[0], '' + stra[1] + '\n'); + var src = stra[2]; + if (linkfcn && src.indexOf('://') === -1) + src = linkfcn('src', src); + str = str.replace(stra[0], '' + stra[1] + '\n'); } else { - str = str.replace(stra[0], '' + stra[1] + '\n'); + var href = stra[2]; + if (linkfcn && href.indexOf('://') === -1) + href = linkfcn('href', href); + else if (href) + href = 'href="' + href + '"' + str = str.replace(stra[0], '' + stra[1] + '\n'); } } while ((stra = micromarkdown.regexobject.mail.exec(str)) !== null) { @@ -402,11 +410,13 @@ var micromarkdown = { if ((str.indexOf('/') !== -1) && (strict !== true)) { urlTemp = str.split('/'); if (urlTemp[1].length === 0) { - urlTemp = urlTemp[2].split('.'); + if (urlTemp[2].indexOf('.') !== -1) + urlTemp = urlTemp[2].split('.'); } else { - urlTemp = urlTemp[0].split('.'); + if (urlTemp[0].indexOf('.') !== -1) + urlTemp = urlTemp[0].split('.'); } - return 'class="mmd_' + urlTemp[urlTemp.length - 2].replace(/[^\w\d]/g, '') + urlTemp[urlTemp.length - 1] + '" '; + return 'class="tvh_mmd_' + urlTemp[urlTemp.length - 2].replace(/[^\w\d]/g, '') + urlTemp[urlTemp.length - 1] + '" '; } return ''; } diff --git a/src/webui/static/app/tvheadend.js b/src/webui/static/app/tvheadend.js index 71148e886..0d342659d 100644 --- a/src/webui/static/app/tvheadend.js +++ b/src/webui/static/app/tvheadend.js @@ -7,6 +7,7 @@ tvheadend.uilevel = 'expert'; tvheadend.uilevel_nochange = false; tvheadend.quicktips = true; tvheadend.wizard = null; +tvheadend.docs_toc = null; tvheadend.cookieProvider = new Ext.state.CookieProvider({ // 7 days from now @@ -87,42 +88,75 @@ tvheadend.help = function(title, pagename) { items: [content] }); win.show(); - } }); }; tvheadend.mdhelp = function(pagename) { - Ext.Ajax.request({ - url: 'markdown/' + pagename, - success: function(result, request) { - var text = result.responseText; - var title = text.split('\n')[0].split('#'); - - if (title) - title = title[title.length-1]; - - text = '
' + micromarkdown.parse(text) + '
'; + var linkfcn = function(type, href) { + if (type == 'src') { + if (href.substring(0, 7) == 'images/') + return 'static/img' + href.substring(6); + return href; + } else { + return 'page="' + href + '"'; + } + } - var content = new Ext.Panel({ - autoScroll: true, - border: false, - layout: 'fit', - html: text - }); + var fcn = function(result) { + var mdtext = result.responseText; + var title = mdtext.split('\n')[0].split('#'); + + if (title) + title = title[title.length-1]; + + var text = '
'; + if (tvheadend.docs_toc) + text += '
' + tvheadend.docs_toc + '
'; + text += '
' + micromarkdown.parse(mdtext, 0, linkfcn) + '
'; + text += '
'; - var win = new Ext.Window({ - title: _('Help for') + ' ' + title, - iconCls: 'help', - layout: 'fit', - width: 900, - height: 400, - constrainHeader: true, - items: [content] - }); - win.show(); + var content = new Ext.Panel({ + autoScroll: true, + border: false, + layout: 'fit', + html: text + }); + var win = new Ext.Window({ + title: _('Help for') + ' ' + title, + iconCls: 'help', + layout: 'fit', + width: 900, + height: 400, + constrainHeader: true, + items: [content], + listeners: { + render: function(win) { + win.body.on('click', function(e, dom) { + tvheadend.mdhelp(dom.getAttribute('page')); + }); + } + }, + }); + win.show(); + } + + Ext.Ajax.request({ + url: 'markdown/' + pagename, + success: function(result) { + if (!tvheadend.docs_toc) { + Ext.Ajax.request({ + url: 'markdown/toc', + success: function(result_toc) { + tvheadend.docs_toc = micromarkdown.parse(result_toc.responseText, 0, linkfcn); + fcn(result); + } + }); + } else { + fcn(result); + } } }); }; diff --git a/docs/docresources/tvheadendlogo.png b/src/webui/static/img/logobig.png similarity index 100% rename from docs/docresources/tvheadendlogo.png rename to src/webui/static/img/logobig.png diff --git a/support/doc/md_to_c.py b/support/doc/md_to_c.py index 802b61936..25593b834 100755 --- a/support/doc/md_to_c.py +++ b/support/doc/md_to_c.py @@ -18,11 +18,17 @@ import sys from textwrap import wrap from mistune import Markdown, Renderer +HUMAN=False +DEBUG=False + NOLANG=[ '.', ',' ] +def debug(str): + sys.stderr.write('DEBUG: ' + str + '\n') + class Object: pass @@ -30,12 +36,18 @@ class Object: class TVH_C_Renderer(Renderer): def get_nolang(self, text): - return '"' + text + '",\n' + if HUMAN: + return text + else: + return '_' + str(len(text)) + ':' + text def get_lang(self, text): if text in NOLANG: return self.get_nolang(text) - return 'LANGPREF N_("' + text + '"),\n' + if HUMAN: + return text + else: + return 'x' + str(len(text)) + ':' + text def get_block(self, text): type = text[0] @@ -47,53 +59,60 @@ class TVH_C_Renderer(Renderer): return (text[p+1+l:], type, t) def newline(self): - return self.get_nolang('\n') + if DEBUG: debug('newline') + return '\n' def text(self, text): if not text: return '' - pre = '' - post = '' - if ord(text[0]) <= ord(' '): - pre = self.get_nolang(' ') - if ord(text[-1]) <= ord(' '): - post = self.get_nolang(' ') + if DEBUG: debug('text: ' + repr(text)) text = text.replace('\n', ' ') - text = ' \\\n'.join(wrap(text, 74)) - return pre + self.get_lang(text) + post + return self.get_lang(text) def linebreak(self): - return self.get_nolang('\\n') + if DEBUG: debug('linebreak') + return '\n' def hrule(self): - return self.get_nolang('---\\n') + if DEBUG: debug('hrule') + return self.get_nolang('---') + '\n' def header(self, text, level, raw=None): - return self.get_nolang('#'*(level+1)) + \ - text + \ - self.get_nolang('\\n\\n') + if DEBUG: debug('header[%d]: ' % level + repr(text)) + return '\n' + self.get_nolang('#'*(level+1)) + text + '\n' def paragraph(self, text): - return text + self.get_nolang('\\n\\n') + if DEBUG: debug('paragraph: ' + repr(text)) + return '\n' + text + '\n' def list(self, text, ordered=True): - r = '' + r = '\n' while text: text, type, t = self.get_block(text) + if DEBUG: debug('list[' + type + ']: ' + repr(t)) if type == 'l': - r += self.get_nolang((ordered and ('# ' + t) or ('* ' + t)) + '\n') + r += self.get_nolang(ordered and '# ' or '* ') + t return r def list_item(self, text): - return self.get_nolang('l' + str(len(text)) + ':') + text + while text[0] == '\n': + text = text[1:] + if DEBUG: debug('list item: ' + repr(text)) + a = text.split('\n') + text = a[0] + '\n' + for t in a[1:]: + if t: + text += self.get_nolang(' ') + t + '\n' + return 'l' + str(len(text)) + ':' + text def block_code(self, code, lang=None): - return self.get_nolang('```no-highlight\n') + code + self.get_nolang('\n```\n') + return self.get_nolang('```no-highlight') + '\n' + \ + code + '\n' + self.get_nolang('```') + '\n' def block_quote(self, text): r = '' for line in text.splitlines(): - r += self.get_nolang((line and '> ' or '')) + line + self.get_nolang('\n') + r += self.get_nolang((line and '> ' or '')) + line + '\n' return r def block_html(self, text): @@ -103,6 +122,7 @@ class TVH_C_Renderer(Renderer): fatal('Inline HTML not allowed: ' + repr(text)) def _emphasis(self, text, pref): + if DEBUG: debug('emphasis[' + pref + ']: ' + repr(text)) return self.get_nolang(pref) + text + self.get_nolang(pref + ' ') def emphasis(self, text): @@ -115,7 +135,7 @@ class TVH_C_Renderer(Renderer): return self._emphasis(text, '~~') def codespan(self, text): - return self.get_nolang('`') + text + self.get_nolang('`') + return self.get_nolang('`' + text + '`') def autolink(self, link, is_email=False): return self.get_nolang('<') + link + self.get_nolang('>') @@ -225,30 +245,64 @@ class TVH_C_Renderer(Renderer): return self.get_nolang('[^' + str(index) + ']') def footnote_item(self, key, text): - r = self.get_nolang('[^' + str(index) + ']:\n') + r = self.get_nolang('[^' + str(index) + ']:' + '\n') for l in text.split('\n'): - r += self.get_nolang(' ') + l.lstrip().rstrip() + self.get_nolang('\n') + r += self.get_nolang(' ') + self.get_lang(l.lstrip().rstrip()) + '\n' return r def footnotes(self, text): - return text + text = text.replace('\n', ' ') + return self.get_lang(text.lstrip().rstrip()) + '\n' # # # def optimize(text): - lines = text.splitlines() + r = '' - prev = '' - for line in lines: - if prev.startswith('"') and line.startswith('"'): - prev = prev[:-2] + line[1:] - continue - elif prev: - r += prev + '\n' - prev = line - return r + (prev and (prev + '\n') or '') + x = '' + n = '' + + def repl(t): + return t.replace('"', '\\"') + + def nolang(t): + return '"' + repl(t) + '",\n' + + def lang(t): + return 'LANGPREF N_("' + repl(x) + '"),\n' + + text = text.lstrip().rstrip() + while text.find('\n\n\n') >= 0: + text = text.replace('\n\n\n', '\n\n') + if HUMAN: + return text + + for text in text.splitlines(): + while text: + type = text[0] + p = text.find(':') + if p <= 0: + fatal('wrong text entry: ' + repr(text)) + break + l = int(text[1:p]) + t = text[p+1:p+1+l] + if type == 'x': + if n: r += nolang(n) + n = '' + x += t + elif type == '_': + if x: r += lang(x) + x = '' + n += t + text = text[p+l+1:] + if x: r += lang(x) + x = '' + n += '\\n' + if n: r += nolang(n) + if x: r += lang(x) + return r # # @@ -273,6 +327,8 @@ def argv_get(what): # # +HUMAN=argv_get('human') +DEBUG=argv_get('debug') input = argv_get('in') if not input: fatal('Specify input file.') @@ -289,4 +345,7 @@ md = Markdown(renderer) text = md(text) text = optimize(text) -print('const char *' + name + '[] = {\n' + text + '\nNULL\n};\n'); +if not HUMAN: + print('const char *' + name + '[] = {\n' + text + '\nNULL\n};\n'); +else: + print(text)