xref: /plan9/sys/lib/man/checkman.awk (revision 9a747e4fd48b9f4522c70c07e8f882a15030f964)
1# Usage: awk -f checkman.awk man?/*.?
2#
3# Checks:
4#	- .TH is first line, and has proper name section number
5#	- sections are in order NAME, SYNOPSIS, DESCRIPTION, EXAMPLES,
6#		FILES, SOURCE, SEE ALSO, DIAGNOSTICS, BUGS
7#	- there's a manual page for each cross-referenced page
8
9BEGIN {
10
11#	.SH sections should come in the following order
12
13	Weight["NAME"] = 1
14	Weight["SYNOPSIS"] = 2
15	Weight["DESCRIPTION"] = 4
16	Weight["EXAMPLE"] = 8
17	Weight["EXAMPLES"] = 16
18	Weight["FILES"] = 32
19	Weight["SOURCE"] = 64
20	Weight["SEE ALSO"] = 128
21	Weight["DIAGNOSTICS"] = 256
22	Weight["SYSTEM CALLS"] = 512
23	Weight["BUGS"] = 1024
24
25	Skipdirs["X11"] = 1
26	Skipdirs["ape"] = 1
27	Skipdirs["aux"] = 1
28	Skipdirs["aviation"] = 1
29	Skipdirs["c++"] = 1
30	Skipdirs["fb"] = 1
31	Skipdirs["pub"] = 1
32	Skipdirs["games"] = 1
33	Skipdirs["gnu"] = 1
34	Skipdirs["lml"] = 1
35	Skipdirs["type1"] = 1
36	Skipdirs["service.alt"] = 1
37
38	Omitted["411"] = 1
39	Omitted["Kill"] = 1
40	Omitted["cb"] = 1
41	Omitted["edmail"] = 1
42	Omitted["mousereset"] = 1
43	Omitted["postalias"] = 1
44	Omitted["mksacfs"] = 1
45	Omitted["sacfs"] = 1
46	Omitted["stock"] = 1
47	Omitted["eg"] = 1
48	Omitted["i"] = 1
49	Omitted["netlib_find"] = 1
50	Omitted["uuencode"] = 1
51	Omitted["uudecode"] = 1
52	Omitted["P"] = 1
53	Omitted["charon"] = 1
54	Omitted["tcp17032"] = 1
55	Omitted["tcp17033"] = 1
56	Omitted["tcp666"] = 1
57	Omitted["tcp667"] = 1
58	Omitted["tcp7330"] = 1
59	Omitted["tcp22"] = 1
60	Omitted["tcp79"] = 1
61	Omitted["tcp1723"] = 1
62	Omitted["pump"] = 1
63	Omitted["allmail"] = 1
64
65	Omittedlib["brk_"] = 1
66	Omittedlib["creadimage"] = 1
67	Omittedlib["main"] = 1
68	Omittedlib["opasstokey"] = 1
69	Omittedlib["oseek"] = 1
70	Omittedlib["sysr1"] = 1
71}
72
73FNR==1	{
74		n = length(FILENAME)
75		seclen = 0
76		if (substr(FILENAME, 2, 1) == "/")
77			seclen = 1
78		else if (substr(FILENAME, 3, 1) == "/")
79			seclen = 2
80		if(seclen == 0)
81			print "FILENAME", FILENAME, "not of form [0-9][0-9]?/*"
82		else if(!(substr(FILENAME, seclen+2, n-seclen-1) ~ /^[A-Z]+(.html)?$/)){
83			section = substr(FILENAME, 1, seclen)
84			name = substr(FILENAME, seclen+2, n-seclen-1)
85			if($1 != ".TH" || NF != 3)
86				print "First line of", FILENAME, "not a proper .TH"
87			else if($2 != toupper(name) || substr($3, 1, seclen) != section){
88				if($2!="INTRO" || name!="0intro")
89					print ".TH of", FILENAME, "doesn't match filename"
90			}else
91				Pages[section "/" $2] = 1
92		}
93		Sh = 0
94	}
95
96$1 == ".SH" {
97		if(inex)
98			print "Unterminated .EX in", FILENAME, ":", $0
99		inex = 0;
100		if (substr($2, 1, 1) == "\"") {
101			if (NF == 2) {
102				print "Unneeded quote in", FILENAME, ":", $0
103				$2 = substr($2, 2, length($2)-2)
104			} else if (NF == 3) {
105				$2 = substr($2, 2) substr($3, 1, length($3)-1)
106				NF = 2
107			}
108		}
109		if(Sh == 0 && $2 != "NAME")
110			print FILENAME, "has no .SH NAME"
111		w = Weight[$2]
112		if (w) {
113			if (w < Sh)
114				print "Heading", $2, "out of order in", FILENAME
115			Sh += w
116		}
117}
118
119$1 == ".EX" {
120		if(inex)
121			print "Nested .EX in", FILENAME, ":", $0
122		inex = 1
123}
124
125$1 == ".EE" {
126		if(!inex)
127			print "Bad .EE in", FILENAME, ":", $0
128		inex = 0;
129}
130
131$1 == ".TF" {
132		smallspace = 1
133}
134
135$1 == ".PD" || $1 == ".SH" || $1 == ".SS" || $1 == ".TH" {
136		smallspace = 0
137}
138
139$1 == ".RE" {
140		lastre = 1
141}
142
143$1 == ".PP" {
144		if(smallspace && !lastre)
145			print "Possible missing .PD at " FILENAME ":" FNR
146		smallspace = 0
147}
148
149$1 != ".RE" {
150		lastre = 0
151}
152
153$0 ~ /^\.[A-Z].*\([1-9]\)/ {
154		if ($1 == ".IR" && $3 ~ /\([0-9]\)/) {
155			name = $2
156			section = $3
157		}else if ($1 == ".RI" && $2 == "(" && $4 ~ /\([0-9]\)/) {
158			name = $3
159			section = $4
160		}else if ($1 == ".IR" && $3 ~ /9.\([0-9]\)/) {
161			name = $2
162			section = "9"
163		}else if ($1 == ".RI" && $2 == "(" && $4 ~ /9.\([0-9]\)/) {
164			name = $3
165			section = "9"
166		} else {
167			print "Possible bad cross-reference format in", FILENAME ":" FNR
168			print $0
169			next
170		}
171		gsub(/[^0-9]/, "", section)
172		Refs[section "/" toupper(name)]++
173}
174
175END {
176	print "Checking Cross-Referenced Pages"
177	for (i in Refs) {
178		if (!(i in Pages)){
179			split(tolower(i), a, "/")
180			print "grep -n " a[2] ".*" a[1] " ?/* # Need " tolower(i)
181		}
182	}
183	print ""
184	print "Checking commands"
185	getindex("/sys/man/1")
186	getindex("/sys/man/4")
187	getindex("/sys/man/7")
188	getindex("/sys/man/8")
189	getbinlist("/386/bin")
190	getbinlist("/rc/bin")
191	for (i in List) {
192		if (!(i in Index) && !(i in Omitted))
193			print "Need", i, "(in " List[i] ")"
194	}
195	print ""
196	for (i in List) {
197		if (!(i in Index) && (i in Omitted))
198			print "Omit", i, "(in " List[i] ")"
199	}
200	clearindex()
201	clearlist()
202	print ""
203	print "Checking libraries"
204	getindex("/sys/man/2")
205	getnmlist("/386/lib/lib9p.a")
206	getnmlist("/386/lib/libauth.a")
207	getnmlist("/386/lib/libauthsrv.a")
208	getnmlist("/386/lib/libbin.a")
209	getnmlist("/386/lib/libbio.a")
210	getnmlist("/386/lib/libc.a")
211	getnmlist("/386/lib/libcontrol.a")
212	getnmlist("/386/lib/libdisk.a")
213	getnmlist("/386/lib/libdraw.a")
214	getnmlist("/386/lib/libflate.a")
215	getnmlist("/386/lib/libframe.a")
216	getnmlist("/386/lib/libgeometry.a")
217	getnmlist("/386/lib/libhtml.a")
218	getnmlist("/386/lib/libhttpd.a")
219	getnmlist("/386/lib/libip.a")
220	getnmlist("/386/lib/libmach.a")
221	getnmlist("/386/lib/libmemdraw.a")
222	getnmlist("/386/lib/libmemlayer.a")
223	getnmlist("/386/lib/libmp.a")
224	getnmlist("/386/lib/libndb.a")
225	getnmlist("/386/lib/libplumb.a")
226	getnmlist("/386/lib/libregexp.a")
227	getnmlist("/386/lib/libsec.a")
228	getnmlist("/386/lib/libstdio.a")
229	getnmlist("/386/lib/libString.a")
230	getnmlist("/386/lib/libthread.a")
231	for (i in List) {
232		if (!(i in Index) && !(i in Omittedlib))
233			print "Need", i, "(in " List[i] ")"
234	}
235	print ""
236	for (i in List) {
237		if (!(i in Index) && (i in Omittedlib))
238			print "Omit", i, "(in " List[i] ")"
239	}
240}
241
242func getindex(dir,    fname)
243{
244	fname = dir "/INDEX"
245	while ((getline < fname) > 0)
246		Index[$1] = dir
247	close(fname)
248}
249
250func getbinlist(dir,    cmd, subdirs, nsd)
251{
252	cmd = "ls -p -l " dir
253	nsd = 0
254	while (cmd | getline) {
255		if ($1 ~ /^d/) {
256			if (!($10 in Skipdirs))
257				subdirs[++nsd] = $10
258		} else if ($10 !~ "^_")
259			List[$10] = dir
260	}
261	for ( ; nsd > 0 ; nsd--)
262		getbinlist(dir "/" subdirs[nsd])
263	close(cmd)
264}
265
266func getnmlist(lib,    cmd)
267{
268	cmd = "nm -g -h " lib
269	while (cmd | getline) {
270		if (($1 == "T" || $1 == "L") && $2 !~ "^_")
271			List[$2] = lib
272	}
273	close(cmd)
274}
275
276func clearindex(    i)
277{
278	for (i in Index)
279		delete Index[i]
280}
281
282func clearlist(    i)
283{
284	for (i in List)
285		delete List[i]
286}
287