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
# 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
$(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
--- /dev/null
+##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
--- /dev/null
+#Tvheadend 4.2 User Guide
+
+
+
+#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.
--- /dev/null
+####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)
+
#include <unistd.h>
-//#include "tvh_locale.h"
+#include "docs.h"
#define LANGPREF "\xff"
#define N_(s) s
#include "config.h"
#include "webui.h"
#include "http.h"
+#include "docs.h"
/* */
static int
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;
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");
htsbuf_qprintf(hq, "<center>\n\
<div class=\"about-title\">HTS Tvheadend %s</div>\n\
<p>© 2006 - 2016 Andreas \303\226man, Jaroslav Kysela, Adam Sutton, et al.</p>\n\
-<p><img src=\"docresources/tvheadendlogo.png\"></p>\n\
+<p><img src=\"static/img/logobig.png\"></p>\n\
<p><a href=\"https://tvheadend.org\">https://tvheadend.org</a></p>\n",
tvheadend_version);
/* 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 */
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 */
}
.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 */
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 = [],
/* links */
while ((stra = micromarkdown.regexobject.links.exec(str)) !== null) {
if (stra[0].substr(0, 1) === '!') {
- str = str.replace(stra[0], '<img src="' + stra[2] + '" alt="' + stra[1] + '" title="' + stra[1] + '" />\n');
+ var src = stra[2];
+ if (linkfcn && src.indexOf('://') === -1)
+ src = linkfcn('src', src);
+ str = str.replace(stra[0], '<img src="' + src + '" alt="' + stra[1] + '" title="' + stra[1] + '" />\n');
} else {
- str = str.replace(stra[0], '<a ' + micromarkdown.mmdCSSclass(stra[2], strict) + 'href="' + stra[2] + '">' + stra[1] + '</a>\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], '<a ' + micromarkdown.mmdCSSclass(stra[2], strict) + href + '>' + stra[1] + '</a>\n');
}
}
while ((stra = micromarkdown.regexobject.mail.exec(str)) !== null) {
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 '';
}
tvheadend.uilevel_nochange = false;
tvheadend.quicktips = true;
tvheadend.wizard = null;
+tvheadend.docs_toc = null;
tvheadend.cookieProvider = new Ext.state.CookieProvider({
// 7 days from now
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 = '<div class="hts-doc-text">' + micromarkdown.parse(text) + '</div>';
+ 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 = '<div>';
+ if (tvheadend.docs_toc)
+ text += '<div class="hts-doc-toc">' + tvheadend.docs_toc + '</div>';
+ text += '<div class="hts-doc-text">' + micromarkdown.parse(mdtext, 0, linkfcn) + '</div>';
+ text += '</div>';
- 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);
+ }
}
});
};
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
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]
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):
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):
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('>')
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
#
#
#
#
+HUMAN=argv_get('human')
+DEBUG=argv_get('debug')
input = argv_get('in')
if not input:
fatal('Specify input file.')
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)