bla
[hutstaender.git] / boobies.py
blob02e0aeb60b0c1862109412fc3daebd49ed4204cf
1 #!/usr/bin/python
2 # Copyright (C) 2012 Roman Zimbelmann <hut@lavabit.com>
3 # This software is distributed under the terms of the GNU GPL version 3.
4 """
5 boobies.py is a html preprocessor for pre-generated static websites.
7 It searches your html files for commands in the form of <!-- COMMAND [ARGS] -->
8 and evaluates them IN-PLACE. NESTING COMMANDS IS NOT SUPPORTED.
9 The following commands exist:
11 run:
12 <!-- run ls -l -->
13 <!-- /run -->
15 This will execute "ls -l" in the shell and insert the output into the block
17 get:
18 You can write this in index.html:
20 <!-- head -->
21 <head><title>boobies</title></head>
22 <!-- /head -->
24 and this in another file:
26 <!-- get head from index.html -->
27 foo
28 <!-- /get -->
30 and foo will be replaced by <head><title>boobies</title></head>.
32 relative:
33 <!-- relative -->
34 <a href="about.html">about</a>
35 <!-- /relative -->
37 If you use this in a "get"-block, it will fix URLs with relative paths.
38 """
40 import os.path
41 import re
42 import sys
43 from subprocess import Popen, PIPE, call
45 PATTERN_GET = r'(<!--\s*get\s+(\w+)\s+from\s+([^\s]+)\s*-->\n?)(.*?)(<!--\s*/get\s*-->)'
46 PATTERN_RUN = r'(<!--\s*run\s+(.*?)\s*-->(\n?))(.*?)(<!--\s*/run\s*-->)'
47 PATTERN_RELATIVE = r'(<!--\s*relative\s*-->\n?)(.*?)(<!--\s*/relative\s*-->)'
48 PATTERN_GETURL = r'href="([^"]+?\.[^"]+?)"'
49 PATTERN_GETKEYWORD = r'<!--\s*{0}\s*-->\n?(.*?)<!--\s*/{0}\s*-->'
51 def main():
52 if '-h' in sys.argv or '--help' in sys.argv or len(sys.argv) <= 1:
53 return sys.stdout.write("usage: boobies.py [--dry] [-h|--help] files...\n")
55 for filename in [s for s in sys.argv[1:] if not s.startswith('-')]:
56 tmpname = filename + ".__new"
58 f = open(filename, 'r')
59 content = f.read()
60 f.close()
61 processed_content = process(content, filename)
62 if processed_content == content:
63 print("unchanged: %s" % filename)
64 continue
66 tmp = open(tmpname, "w")
67 tmp.write(processed_content)
68 tmp.close()
69 call(['diff', '-u', filename, tmpname])
71 if '--dry' in sys.argv:
72 os.unlink(tmpname)
73 else:
74 print("writing: %s" % filename)
75 os.rename(tmpname, filename)
77 def process(content, filename):
78 global PATH
79 PATH = os.path.dirname(filename)
80 content = re.sub(PATTERN_GET, _command_get, content, 0, re.S)
81 content = re.sub(PATTERN_RUN, _command_run, content, 0, re.S)
82 return content
85 def _command_get(m):
86 filename = m.group(3)
87 keyword = m.group(2)
88 fallback = m.group(4)
90 f = open(filename, 'r')
91 content = f.read()
92 f.close()
93 match = re.search(PATTERN_GETKEYWORD.format(keyword), content, re.DOTALL)
94 if match is None:
95 sys.stderr.write("Could not find keyword {0} in {1}\n".format(keyword, filename))
96 result = fallback
97 else:
98 result = re.sub(PATTERN_RELATIVE, _command_relative, match.group(1), 0, re.S)
100 return m.group(1) + result + m.group(5)
103 def _command_relative(m):
104 return m.group(1) + re.sub(PATTERN_GETURL,
105 lambda match: 'href="' + os.path.relpath(match.group(1), PATH) + '"',
106 m.group(2)) + m.group(3)
109 def _command_run(m):
110 output = Popen(m.group(2), shell=True, stdout=PIPE).communicate()[0]
111 if m.group(3) != "\n":
112 output = output.strip()
113 return m.group(1) + output.decode('utf-8') + m.group(5)
116 if __name__ == '__main__':
117 main()