Fix for segfault in top_name stuff.
[monky] / check_docs.py
1 #!/usr/bin/python
2 #
3 # This script will check the documentation consistency against the code.  It
4 # doesn't check the actual accuracy of the documentation, it just ensures that
5 # everything is documented and that nothing which doesn't exist in Conky
6 # appears in the documentation.
7 #
8 # This script also updates the vim and nano syntax files so it doesn't have to
9 # be done manually.
10 #
11 # Requires the ElementTree Python module for the sorting stuff, see:
12 # http://effbot.org/zone/element-index.htm
13 #
14 # You should also install htmltidy, but it's not necessary.
15 #
16
17 import os.path
18 import re
19 import sys
20 import mmap
21 from datetime import *
22
23 file_names = dict()
24 file_names["text_objects"]    = "src/text_object.h"
25 file_names["conky"]           = "src/conky.c"
26 file_names["vim_syntax"]      = "extras/vim/syntax/conkyrc.vim"
27 file_names["nano_syntax"]     = "extras/nano/conky.nanorc"
28 file_names["variables"]       = "doc/variables.xml"
29 file_names["config_settings"] = "doc/config_settings.xml"
30 file_names["lua"]             = "doc/lua.xml"
31 file_names["docs"]            = "doc/docs.xml"
32 file_names["command_options"] = "doc/command_options.xml"
33
34 for fn in file_names.values():
35         if not os.path.exists(fn) or not os.path.isfile(fn):
36                 print "'%s' doesn't exist, or isn't a file" % (fn)
37                 exit(1)
38
39 print 'sorting/tidying docs...'
40
41 # sort the docs by variable/config setting
42 import string
43 import xml.etree.ElementTree as ET
44
45 vars_xml = ET.parse(file_names['variables'])
46 config_xml = ET.parse(file_names['config_settings'])
47
48 getkey = lambda x: x.findtext('term/command/option')
49
50 vars = vars_xml.getroot()
51 vars[:] = sorted(vars, key=getkey)
52
53 configs = config_xml.getroot()
54 configs[:] = sorted(configs, key=getkey)
55
56 vars_xml.write(file_names['variables'])
57 config_xml.write(file_names['config_settings'])
58
59 def tidy(file):
60         command = ['tidy', '-qim', '-xml', '-utf8', '--indent-spaces', '4']
61         os.system('%s %s 2>/dev/null' % (string.join(command), file))
62
63 tidy(file_names['variables'])
64 tidy(file_names['config_settings'])
65 tidy(file_names['lua'])
66 tidy(file_names['command_options'])
67
68 #
69 # Do all the objects first
70 #
71
72 objects = []
73
74 file = open(file_names["text_objects"], "r")
75 exp = re.compile("\s*OBJ_(\w*).*")
76 while file:
77         line = file.readline()
78         if len(line) == 0:
79                 break
80         res = exp.match(line)
81         if res:
82                 obj = res.group(1)
83                 if not re.match("color\d", obj) and obj != "text":
84                         # ignore colourN stuff
85                         objects.append(res.group(1))
86 file.close()
87 print 'counted %i text objects' % len(objects)
88
89 doc_objects = []
90 exp = re.compile("\s*<command><option>(\w*)</option></command>.*")
91 print "checking docs -> objs consistency (in %s)" % (file_names["text_objects"])
92 for var in vars:
93         term = getkey(var)
94         doc_objects.append(term)
95         if ['templaten', 'colorn'].count(doc_objects[len(doc_objects) - 1].lower()):
96                 # ignore these
97                 continue
98         if doc_objects[len(doc_objects) - 1] not in objects:
99                 print "   '%s' is documented, but doesn't seem to be an object" % (doc_objects[len(doc_objects) - 1])
100 print "done\n"
101
102 print "checking objs -> docs consistency (in %s)" % (file_names["variables"])
103 for obj in objects:
104         if obj not in doc_objects:
105                 print "   '%s' seems to be undocumented" % (obj)
106 print "done\n"
107
108 #
109 # Now we'll do config settings
110 #
111
112 config_entries = []
113
114 file = open(file_names["conky"], "r")
115 exp1 = re.compile('\s*CONF\("(\w*)".*')
116 exp2 = re.compile('\s*CONF2\("(\w*)".*')
117 exp3 = re.compile('\s*CONF3\("(\w*)".*')
118 while file:
119         line = file.readline()
120         if len(line) == 0:
121                 break
122         res = exp1.match(line)
123         if not res:
124                 res = exp2.match(line)
125         if not res:
126                 res = exp3.match(line)
127         if res:
128                 conf = res.group(1)
129                 if re.match("color\d", conf):
130                         conf = "colorN"
131                 if config_entries.count(conf) == 0:
132                         config_entries.append(conf)
133 file.close()
134 print 'counted %i config settings' % len(config_entries)
135
136 doc_configs = []
137 print "checking docs -> configs consistency (in %s)" % (file_names["conky"])
138 for config in configs:
139         term = getkey(config)
140         doc_configs.append(term)
141         if ['text', 'templaten'].count(doc_configs[len(doc_configs) - 1].lower()):
142                 # ignore these
143                 continue
144         if doc_configs[len(doc_configs) - 1] not in config_entries:
145                 print "   '%s' is documented, but doesn't seem to be a config setting" % (doc_configs[len(doc_configs) - 1])
146 print "done\n"
147
148 print "checking configs -> docs consistency (in %s)" % (file_names["config_settings"])
149 for obj in config_entries:
150         if obj != "text" and obj != "template" and obj not in doc_configs:
151                 print "   '%s' seems to be undocumented" % (obj)
152 print "done\n"
153
154
155
156 # Cheat and add the colour/template stuff.
157
158 for i in range(0, 10):
159         objects.append("color" + str(i))
160         config_entries.append("color" + str(i))
161         objects.append("template" + str(i))
162         config_entries.append("template" + str(i))
163
164 # Finally, sort everything.
165 objects.sort()
166 config_entries.sort()
167
168 #
169 # Update nano syntax stuff
170 #
171
172 print "updating nano syntax...",
173 sys.stdout.flush()
174 file = open(file_names["nano_syntax"], "rw+")
175 lines = []
176 while file:
177         line = file.readline()
178         if len(line) == 0:
179                 break
180         lines.append(line)
181
182 # find the line we want to update
183 for line in lines:
184         if re.match("color green ", line):
185                 idx = lines.index(line)
186                 lines.pop(idx) # remove old line
187                 line = 'color green "\<('
188                 for obj in config_entries:
189                         line += "%s|" % (obj)
190                 line = line[:len(line) - 1]
191                 line += ')\>"\n'
192                 lines.insert(idx, line)
193         if re.match("color brightblue ", line):
194                 idx = lines.index(line)
195                 lines.pop(idx) # remove old line
196                 line = 'color brightblue "\<('
197                 for obj in objects:
198                         line += "%s|" % (obj)
199                 line = line[:len(line) - 1]
200                 line += ')\>"\n'
201                 lines.insert(idx, line)
202                 break # want to ignore everything after this line
203 file.truncate(0)
204 file.seek(0)
205 file.writelines(lines)
206 file.close()
207 print "done."
208
209 #
210 # Update vim syntax stuff
211 #
212
213 print "updating vim syntax...",
214 sys.stdout.flush()
215 file = open(file_names["vim_syntax"], "rw+")
216 lines = []
217 while file:
218         line = file.readline()
219         if len(line) == 0:
220                 break
221         lines.append(line)
222
223 # find the line we want to update
224 for line in lines:
225         if re.match("syn keyword ConkyrcSetting ", line):
226                 idx = lines.index(line)
227                 lines.pop(idx) # remove old line
228                 line = 'syn keyword ConkyrcSetting '
229                 for obj in config_entries:
230                         line += "%s " % (obj)
231                 line = line[:len(line) - 1]
232                 line += '\n'
233                 lines.insert(idx, line)
234         if re.match("syn keyword ConkyrcVarName contained nextgroup=ConkyrcNumber,ConkyrcColour skipwhite ", line):
235                 idx = lines.index(line)
236                 lines.pop(idx) # remove old line
237                 line = 'syn keyword ConkyrcVarName contained nextgroup=ConkyrcNumber,ConkyrcColour skipwhite '
238                 for obj in objects:
239                         line += "%s " % (obj)
240                 line = line[:len(line) - 1]
241                 line += '\n'
242                 lines.insert(idx, line)
243                 break # want to ignore everything after this line
244 file.truncate(0)
245 file.seek(0)
246 file.writelines(lines)
247 file.close()
248
249 # lastly, update the date in docs.xml
250 file = open(file_names["docs"], 'r+')
251 map = mmap.mmap(file.fileno(), os.path.getsize(file_names["docs"]))
252 d = map.find("<date>")
253 d += 6 # skip over first date stuff
254 map[d:d+10] = datetime.now().strftime("%F")
255 map.close()
256 file.close()
257
258 print "done."