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