#!/usr/bin/python3 # # Generate documentation for how this machine works by # parsing our bash scripts! import cgi, re import markdown from modgrammar import * def generate_documentation(): print("""
Here’s how you can build your own mail server from scratch. This document is generated automatically from our setup script.
" + cgi.escape(content) + "
\n" return "" class EditConf(Grammar): grammar = ( L('tools/editconf.py '), FILENAME, SPACE, OPTIONAL((LIST_OF( L("-w") | L("-s") | L("-c ';'"), sep=SPACE, ), SPACE)), REST_OF_LINE, OPTIONAL(SPACE), EOL ) def value(self): conffile = self[1] options = [""] mode = 1 for c in self[4].string: if mode == 1 and c in (" ", "\t") and options[-1] != "": # new word options.append("") elif mode < 0: # escaped character options[-1] += c mode = -mode elif c == "\\": # escape next character mode = -mode elif mode == 1 and c == '"': mode = 2 elif mode == 2 and c == '"': mode = 1 else: options[-1] += c if options[-1] == "": options.pop(-1) return "" + cgi.escape(self.string.strip()) + "
" + "\n".join(cgi.escape(s) for s in options) + "
" + cgi.escape(cmd) + "
replace
" + cgi.escape(self[3].string.replace(".*", ". . .")) + "
with
" + cgi.escape(self[5].string.replace("\\n", "\n").replace("\\t", "\t")) + "
\n" class AptGet(Grammar): grammar = (ZERO_OR_MORE(SPACE), L("apt_install "), REST_OF_LINE, EOL) def value(self): return shell_line("apt-get install -y " + re.sub(r"\s+", " ", self[2].string)) class UfwAllow(Grammar): grammar = (ZERO_OR_MORE(SPACE), L("ufw_allow "), REST_OF_LINE, EOL) def value(self): return shell_line("ufw allow " + self[2].string) class RestartService(Grammar): grammar = (ZERO_OR_MORE(SPACE), L("restart_service "), REST_OF_LINE, EOL) def value(self): return shell_line("service " + self[2].string + " restart") class OtherLine(Grammar): grammar = (REST_OF_LINE, EOL) def value(self): if self.string.strip() == "": return "" return "" + cgi.escape(wrap_lines(bash.strip())) + "
\n" class BashElement(Grammar): grammar = Comment | Source | CatEOF | SuppressedLine | HideOutput | EditConf | CaptureOutput | SedReplace | AptGet | UfwAllow | RestartService | OtherLine def value(self): return self[0].value() class BashScript(Grammar): grammar = (OPTIONAL(HashBang), REPEAT(BashElement)) def value(self): return [line.value() for line in self[1]] @staticmethod def parse(fn): if fn in ("setup/functions.sh", "/etc/mailinabox.conf"): return "" parser = BashScript.parser() string = open(fn).read() string = re.sub(r"\s*\\\n\s*", " ", string) string = re.sub(".* #NODOC\n", "", string) string = re.sub("\n\s*if .*|\n\s*fi|\n\s*else|\n\s*elif .*", "", string) string = re.sub("hide_output ", "", string) result = parser.parse_string(string) v = "\n" % ("https://github.com/mail-in-a-box/mailinabox/tree/master/" + fn, fn) v += "".join(result.value()) v = v.replace("\n" + cgi.escape(self.string.rstrip()) + "
", "") v = re.sub("([\w\W]*?)", lambda m : "" + strip_indent(m.group(1)) + "", v) v = re.sub(r"\$?PRIMARY_HOSTNAME", "box.yourdomain.com", v) v = re.sub(r"\$?STORAGE_ROOT", "/path/to/user-data
", v) v = v.replace("`pwd`", "/path/to/mailinabox
") return v def wrap_lines(text, cols=60): ret = "" words = re.split("(\s+)", text) linelen = 0 for w in words: if linelen + len(w) > cols-1: ret += " \\\n" ret += " " linelen = 0 if linelen == 0 and w.strip() == "": continue ret += w linelen += len(w) return ret if __name__ == '__main__': generate_documentation()