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