xref: /plan9-contrib/sys/lib/man/checkman.awk (revision 219b2ee8daee37f4aad58d63f21287faa8e4ffdc)
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
26FNR==1	{
27		n = length(FILENAME)
28		seclen = 0
29		if (substr(FILENAME, 2, 1) == "/")
30			seclen = 1
31		else if (substr(FILENAME, 3, 1) == "/")
32			seclen = 2
33		if(seclen == 0)
34			print "FILENAME", FILENAME, "not of form [0-9][0-9]?/*"
35		else if(!(substr(FILENAME, seclen+2, n-seclen-1) ~ /^[A-Z]+$/)){
36			section = substr(FILENAME, 1, seclen)
37			name = substr(FILENAME, seclen+2, n-seclen-1)
38			if($1 != ".TH" || NF != 3)
39				print "First line of", FILENAME, "not a proper .TH"
40			else if($2 != toupper(name) || substr($3, 1, seclen) != section){
41				if($2!="INTRO" || name!="0intro")
42					print ".TH of", FILENAME, "doesn't match filename"
43			}else
44				Pages[section "/" $2] = 1
45		}
46		Sh = 0
47	}
48
49$1 == ".SH" {
50		if(inex)
51			print "Unterminated .EX in", FILENAME, ":", $0
52		inex = 0;
53		if (substr($2, 1, 1) == "\"") {
54			if (NF == 2) {
55				print "Unneeded quote in", FILENAME, ":", $0
56				$2 = substr($2, 2, length($2)-2)
57			} else if (NF == 3) {
58				$2 = substr($2, 2) substr($3, 1, length($3)-1)
59				NF = 2
60			}
61		}
62		w = Weight[$2]
63		if (w) {
64			if (w < Sh)
65				print "Heading", $2, "out of order in", FILENAME
66			Sh += w
67		}
68}
69
70$1 == ".EX" {
71		if(inex)
72			print "Nested .EX in", FILENAME, ":", $0
73		inex = 1
74}
75
76$1 == ".EE" {
77		if(!inex)
78			print "Bad .EE in", FILENAME, ":", $0
79		inex = 0;
80}
81
82$0 ~ /^\..*\([0-9]\)/ {
83		if ($1 == ".IR" && $3 ~ /\([0-9]\)/) {
84			name = $2
85			section = $3
86		}else if ($1 == ".RI" && $2 == "(" && $4 ~ /\([0-9]\)/) {
87			name = $3
88			section = $4
89		}else if ($1 == ".IR" && $3 ~ /9.\([0-9]\)/) {
90			name = $2
91			section = "9"
92		}else if ($1 == ".RI" && $2 == "(" && $4 ~ /9.\([0-9]\)/) {
93			name = $3
94			section = "9"
95		} else {
96			print "Possible bad cross-reference format in", FILENAME
97			print $0
98			next
99		}
100		gsub(/[^0-9]/, "", section)
101		Refs[section "/" toupper(name)]++
102}
103
104END {
105	print "Checking Cross-Referenced Pages"
106	for (i in Refs) {
107		if (!(i in Pages))
108			print "Need", tolower(i)
109	}
110	print ""
111	print "Checking commands"
112	getindex("/sys/man/1")
113	getindex("/sys/man/4")
114	getindex("/sys/man/7")
115	getindex("/sys/man/8")
116	getindex("/sys/man/9")
117	getindex("/sys/man/10")
118	Skipdirs["X11"] = 1
119	Skipdirs["ape"] = 1
120	Skipdirs["aux"] = 1
121	Skipdirs["c++"] = 1
122	Skipdirs["help"] = 1
123	Skipdirs["lbp"] = 1
124	Skipdirs["m"] = 1
125	Skipdirs["scsi"] = 1
126	getbinlist("/mips/bin")
127	getbinlist("/rc/bin")
128	for (i in List) {
129		if (!(i in Index))
130			print "Need", i, "(in " List[i] ")"
131	}
132	clearindex()
133	clearlist()
134	print ""
135	print "Checking libraries"
136	getindex("/sys/man/2")
137	getindex9("/sys/man/9")
138	getnmlist("/mips/lib/libauth.a")
139	getnmlist("/mips/lib/libbio.a")
140	getnmlist("/mips/lib/libc.a")
141	getnmlist("/mips/lib/libfb.a")
142	getnmlist("/mips/lib/libframe.a")
143	getnmlist("/mips/lib/libg.a")
144	getnmlist("/mips/lib/libip.a")
145	getnmlist("/mips/lib/liblayer.a")
146	getnmlist("/mips/lib/libmach.a")
147	getnmlist("/mips/lib/libndb.a")
148	getnmlist("/mips/lib/libregexp.a")
149	getnmlist("/mips/lib/libstdio.a")
150	getnmlist("/mips/lib/libpanel.a")
151	for (i in List) {
152		if (!(i in Index))
153			print "Need", i, "(in " List[i] ")"
154	}
155}
156
157func getindex(dir,    fname)
158{
159	fname = dir "/INDEX"
160	while ((getline < fname) > 0)
161		Index[$1] = dir
162	close(fname)
163}
164
165func getindex9(dir,    fname)
166{
167	fname = dir "/INDEX"
168	while ((getline < fname) > 0)
169		if($2 ~ "(getflags|picopen|getcmap)")
170			Index[$1] = dir
171	close(fname)
172}
173
174func getbinlist(dir,    cmd, subdirs, nsd)
175{
176	cmd = "ls -p -l " dir
177	nsd = 0
178	while (cmd | getline) {
179		if ($1 ~ /^d/) {
180			if (!($10 in Skipdirs))
181				subdirs[++nsd] = $10
182		} else
183			List[$10] = dir
184	}
185	for ( ; nsd > 0 ; nsd--)
186		getbinlist(dir "/" subdirs[nsd])
187	close(cmd)
188}
189
190func getnmlist(lib,    cmd)
191{
192	cmd = "nm -g -h " lib
193	while (cmd | getline) {
194		if (($1 == "T" || $1 == "L") && $2 !~ "^_")
195			List[$2] = lib
196	}
197	close(cmd)
198}
199
200func clearindex(    i)
201{
202	for (i in Index)
203		delete Index[i]
204}
205
206func clearlist(    i)
207{
208	for (i in List)
209		delete List[i]
210}
211