checkman.awk (12137B)
1 # Usage: cd $PLAN9; awk -f dist/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 9 BEGIN { 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["CVS"] = 1 26 27 # allow references to pages provded 28 # by the underlying Unix system 29 Omitman["awk(1)"] = 1 30 Omitman["bash(1)"] = 1 31 Omitman["chmod(1)"] = 1 32 Omitman["compress(1)"] = 1 33 Omitman["cp(1)"] = 1 34 Omitman["egrep(1)"] = 1 35 Omitman["gs(1)"] = 1 36 Omitman["gv(1)"] = 1 37 Omitman["lex(1)"] = 1 38 Omitman["lp(1)"] = 1 39 Omitman["lpr(1)"] = 1 40 Omitman["mail(1)"] = 1 41 Omitman["make(1)"] = 1 42 Omitman["nm(1)"] = 1 43 Omitman["prof(1)"] = 1 44 Omitman["pwd(1)"] = 1 45 Omitman["qiv(1)"] = 1 46 Omitman["sftp(1)"] = 1 47 Omitman["sh(1)"] = 1 48 Omitman["ssh(1)"] = 1 49 Omitman["stty(1)"] = 1 50 Omitman["tex(1)"] = 1 51 Omitman["unutf(1)"] = 1 52 Omitman["vnc(1)"] = 1 53 Omitman["xterm(1)"] = 1 54 55 Omitman["access(2)"] = 1 56 Omitman["brk(2)"] = 1 57 Omitman["chdir(2)"] = 1 58 Omitman["close(2)"] = 1 59 Omitman["connect(2)"] = 1 60 Omitman["fork(2)"] = 1 61 Omitman["gethostname(2)"] = 1 62 Omitman["getpid(2)"] = 1 63 Omitman["getuid(2)"] = 1 64 Omitman["open(2)"] = 1 65 Omitman["pipe(2)"] = 1 66 Omitman["ptrace(2)"] = 1 67 Omitman["rmdir(2)"] = 1 68 Omitman["send(2)"] = 1 69 Omitman["signal(2)"] = 1 70 Omitman["sigprocmask(2)"] = 1 71 Omitman["socketpair(2)"] = 1 72 Omitman["unlink(2)"] = 1 73 74 Omitman["abort(3)"] = 1 75 Omitman["assert(3)"] = 1 76 Omitman["fprintf(3)"] = 1 77 Omitman["fscanf(3)"] = 1 78 Omitman["fopen(3)"] = 1 79 Omitman["isalpha(3)"] = 1 80 Omitman["malloc(3)"] = 1 81 Omitman["perror(3)"] = 1 82 Omitman["remove(3)"] = 1 83 Omitman["sin(3)"] = 1 84 Omitman["strerror(3)"] = 1 85 86 Omitman["core(5)"] = 1 87 Omitman["passwd(5)"] = 1 88 89 Omitman["signal(7)"] = 1 90 91 Omitman["cron(8)"] = 1 92 Omitman["mount(8)"] = 1 93 94 # don't need documentation for these in bin 95 Omitted[".cvsignore"] = 1 96 Omitted["Getdir"] = 1 97 Omitted["Irc"] = 1 98 Omitted["Juke"] = 1 99 Omitted["ajuke"] = 1 100 Omitted["goodmk"] = 1 101 Omitted["jukefmt"] = 1 102 Omitted["jukeget"] = 1 103 Omitted["jukeindex"] = 1 104 Omitted["jukeinfo"] = 1 105 Omitted["jukeplay"] = 1 106 Omitted["jukeput"] = 1 107 Omitted["jukesearch"] = 1 108 Omitted["jukesongfile"] = 1 109 Omitted["m4ainfo"] = 1 110 Omitted["mp3info"] = 1 111 Omitted["notes"] = 1 112 Omitted["tcolors"] = 1 113 Omitted["tref"] = 1 114 Omitted["unutf"] = 1 115 Omitted["volume"] = 1 116 Omitted["vtdump"] = 1 117 Omitted["netfilelib.rc"] = 1 118 119 # not for users 120 Omittedlib["creadimage"] = 1 121 Omittedlib["pixelbits"] = 1 122 Omittedlib["bouncemouse"] = 1 123 Omittedlib["main"] = 1 # in libthread 124 125 Omittedlib["opasstokey"] = 1 # in libauthsrv 126 127 # functions provided for -lthread_db 128 Omittedlib["ps_get_thread_area"] = 1 129 Omittedlib["ps_getpid"] = 1 130 Omittedlib["ps_lcontinue"] = 1 131 Omittedlib["ps_lgetfpregs"] = 1 132 Omittedlib["ps_lgetregs"] = 1 133 Omittedlib["ps_lsetfpregs"] = 1 134 Omittedlib["ps_lsetregs"] = 1 135 Omittedlib["ps_lstop"] = 1 136 Omittedlib["ps_pcontinue"] = 1 137 Omittedlib["ps_pdread"] = 1 138 Omittedlib["ps_pdwrite"] = 1 139 Omittedlib["ps_pglobal_lookup"] = 1 140 Omittedlib["ps_pstop"] = 1 141 Omittedlib["ps_ptread"] = 1 142 Omittedlib["ps_ptwrite"] = 1 143 144 # libmach includes a small dwarf and elf library 145 Omittedlib["corecmdfreebsd386"] = 1 146 Omittedlib["corecmdlinux386"] = 1 147 Omittedlib["coreregsfreebsd386"] = 1 148 Omittedlib["coreregslinux386"] = 1 149 Omittedlib["coreregsmachopower"] = 1 150 Omittedlib["crackelf"] = 1 151 Omittedlib["crackmacho"] = 1 152 Omittedlib["dwarfaddrtounit"] = 1 153 Omittedlib["dwarfclose"] = 1 154 Omittedlib["dwarfenum"] = 1 155 Omittedlib["dwarfenumunit"] = 1 156 Omittedlib["dwarfget1"] = 1 157 Omittedlib["dwarfget128"] = 1 158 Omittedlib["dwarfget128s"] = 1 159 Omittedlib["dwarfget2"] = 1 160 Omittedlib["dwarfget4"] = 1 161 Omittedlib["dwarfget8"] = 1 162 Omittedlib["dwarfgetabbrev"] = 1 163 Omittedlib["dwarfgetaddr"] = 1 164 Omittedlib["dwarfgetn"] = 1 165 Omittedlib["dwarfgetnref"] = 1 166 Omittedlib["dwarfgetstring"] = 1 167 Omittedlib["dwarflookupfn"] = 1 168 Omittedlib["dwarflookupname"] = 1 169 Omittedlib["dwarflookupnameinunit"] = 1 170 Omittedlib["dwarflookupsubname"] = 1 171 Omittedlib["dwarflookuptag"] = 1 172 Omittedlib["dwarfnextsym"] = 1 173 Omittedlib["dwarfnextsymat"] = 1 174 Omittedlib["dwarfopen"] = 1 175 Omittedlib["dwarfpctoline"] = 1 176 Omittedlib["dwarfseeksym"] = 1 177 Omittedlib["dwarfskip"] = 1 178 Omittedlib["dwarfunwind"] = 1 179 Omittedlib["elfclose"] = 1 180 Omittedlib["elfdl386mapdl"] = 1 181 Omittedlib["elfinit"] = 1 182 Omittedlib["elfmachine"] = 1 183 Omittedlib["elfmap"] = 1 184 Omittedlib["elfopen"] = 1 185 Omittedlib["elfsection"] = 1 186 Omittedlib["elfsym"] = 1 187 Omittedlib["elfsymlookup"] = 1 188 Omittedlib["elftype"] = 1 189 Omittedlib["machoclose"] = 1 190 Omittedlib["machoinit"] = 1 191 Omittedlib["machoopen"] = 1 192 Omittedlib["stabsym"] = 1 193 Omittedlib["symdwarf"] = 1 194 Omittedlib["symelf"] = 1 195 Omittedlib["symmacho"] = 1 196 Omittedlib["symstabs"] = 1 197 Omittedlib["elfcorelinux386"] = 1 198 Omittedlib["linux2ureg386"] = 1 199 Omittedlib["ureg2linux386"] = 1 200 Omittedlib["coreregs"] = 1 # haven't documented mach yet 201 Omittedlib["regdesc"] = 1 202 203 Omittedlib["auth_attr"] = 1 # not happy about this 204 205 Omittedlib["ndbnew"] = 1 # private to library 206 Omittedlib["ndbsetval"] = 1 207 208 Renamelib["chanalt"] = "alt" 209 Renamelib["channbrecv"] = "nbrecv" 210 Renamelib["channbrecvp"] = "nbrecvp" 211 Renamelib["channbrecvul"] = "nbrecvul" 212 Renamelib["channbsend"] = "nbsend" 213 Renamelib["channbsendp"] = "nbsendp" 214 Renamelib["channbsendul"] = "nbsendul" 215 Renamelib["chanrecv"] = "recv" 216 Renamelib["chanrecvp"] = "recvp" 217 Renamelib["chanrecvul"] = "recvul" 218 Renamelib["chansend"] = "send" 219 Renamelib["chansendp"] = "sendp" 220 Renamelib["chansendul"] = "sendul" 221 Renamelib["threadyield"] = "yield" 222 223 Renamelib["fmtcharstod"] = "charstod" 224 Renamelib["fmtstrtod"] = "strtod" 225 226 Renamelib["regcomp9"] = "regcomp" 227 Renamelib["regcomplit9"] = "regcomplit" 228 Renamelib["regcompnl9"] = "regcompnl" 229 Renamelib["regerror9"] = "regerror" 230 Renamelib["regexec9"] = "regexec" 231 Renamelib["regsub9"] = "regsub" 232 Renamelib["rregexec9"] = "rregexec" 233 Renamelib["rregsub9"] = "rregsub" 234 235 lastline = "XXX"; 236 lastfile = FILENAME; 237 } 238 239 func getnmlist(lib, cmd) 240 { 241 cmd = "nm -g " lib 242 while (cmd | getline) { 243 if (($2 == "T" || $2 == "L") && $3 !~ "^_"){ 244 sym = $3 245 sub("^p9", "", sym) 246 if(sym in Renamelib) 247 List[Renamelib[sym]] = lib " as " sym 248 else 249 List[sym] = lib 250 } 251 } 252 close(cmd) 253 } 254 255 256 func getindex(dir, fname) 257 { 258 fname = dir "/INDEX" 259 while ((getline < fname) > 0) 260 Index[$1] = dir 261 close(fname) 262 } 263 264 func getbinlist(dir, cmd, subdirs, nsd) 265 { 266 cmd = "ls -p -l " dir 267 nsd = 0 268 while (cmd | getline) { 269 if ($1 ~ /^d/) { 270 if (!($10 in Skipdirs)) 271 subdirs[++nsd] = $10 272 } else if ($10 !~ "^_") 273 List[$10] = dir 274 } 275 for ( ; nsd > 0 ; nsd--) 276 getbinlist(dir "/" subdirs[nsd]) 277 close(cmd) 278 } 279 280 func clearindex( i) 281 { 282 for (i in Index) 283 delete Index[i] 284 } 285 286 func clearlist( i) 287 { 288 for (i in List) 289 delete List[i] 290 } 291 292 293 FNR==1 { 294 if(lastline == ""){ 295 # screws up troff headers 296 print lastfile ":$ is a blank line" 297 } 298 299 n = length(FILENAME) 300 nam = FILENAME 301 if(nam ~ /\.html$/) 302 next 303 if(nam !~ /^man\/man(.*)\/(.*)\.(.*)$/){ 304 print "nam", nam, "not of form [0-9][0-9]?/*" 305 next 306 } 307 nam = substr(nam, 8) 308 gsub("[/.]", " ", nam); 309 n = split(nam, a) 310 sec = a[1] 311 name = a[2] 312 section = a[3] 313 if($1 != ".TH" || NF != 3) 314 print "First line of", FILENAME, "not a proper .TH" 315 else if(($2 != toupper(name) || substr($3, 1, length(sec)) != sec || $3 != toupper(section)) \ 316 && ($2!="INTRO" || name!="0intro") \ 317 && (name !~ /^9/ || $2!=toupper(substr(name, 2)))){ 318 print ".TH of", FILENAME, "doesn't match filename" 319 }else 320 Pages[tolower($2) "(" tolower($3) ")"] = 1 321 Sh = 0 322 } 323 324 { lastline=$0; lastfile=FILENAME; } 325 326 $1 == ".SH" { 327 if(inex) 328 print "Unterminated .EX in", FILENAME, ":", $0 329 inex = 0; 330 if (substr($2, 1, 1) == "\"") { 331 if (NF == 2) { 332 print "Unneeded quote in", FILENAME, ":", $0 333 $2 = substr($2, 2, length($2)-2) 334 } else if (NF == 3) { 335 $2 = substr($2, 2) substr($3, 1, length($3)-1) 336 NF = 2 337 } 338 } 339 if(Sh == 0 && $2 != "NAME") 340 print FILENAME, "has no .SH NAME" 341 w = Weight[$2] 342 if (w) { 343 if (w < Sh) 344 print "Heading", $2, "out of order in", FILENAME 345 Sh += w 346 } 347 sh = $2 348 } 349 350 $1 == ".EX" { 351 if(inex) 352 print "Nested .EX in", FILENAME ":" FNR, ":", $0 353 inex = 1 354 } 355 356 $1 == ".EE" { 357 if(!inex) 358 print "Bad .EE in", FILENAME ":" FNR ":", $0 359 inex = 0; 360 } 361 362 $1 == ".TF" { 363 smallspace = 1 364 } 365 366 $1 == ".PD" || $1 == ".SH" || $1 == ".SS" || $1 == ".TH" { 367 smallspace = 0 368 } 369 370 $1 == ".RE" { 371 lastre = 1 372 } 373 374 $1 == ".PP" { 375 if(smallspace && !lastre) 376 print "Possible missing .PD at " FILENAME ":" FNR 377 smallspace = 0 378 } 379 380 $1 != ".RE" { 381 lastre = 0 382 } 383 384 sh == "BUGS" && $1 == ".br" { 385 print FILENAME ":" FNR ": .br in BUGS" 386 } 387 388 sh == "SOURCE" && $1 ~ /^\\\*9\// { 389 s = ENVIRON["PLAN9"] substr($1, 4) 390 Sources[s] = 1 391 } 392 393 sh == "SOURCE" && $2 ~ /^\\\*9\// { 394 s = ENVIRON["PLAN9"] substr($2, 4) 395 Sources[s] = 1 396 } 397 398 sh == "SOURCE" && $1 ~ /^\// { 399 Sources[$1] = 1 400 } 401 402 sh == "SOURCE" && $2 ~ /^\// { 403 Sources[$2] = 1 404 } 405 406 $0 ~ /^\.[A-Z].*\([1-9]\)/ { 407 if ($1 == ".IR" && $3 ~ /\([0-9]\)/) { 408 name = $2 409 section = $3 410 }else if ($1 == ".RI" && $2 == "(" && $4 ~ /\([0-9]\)/) { 411 name = $3 412 section = $4 413 }else if ($1 == ".IR" && $3 ~ /9.\([0-9]\)/) { 414 name = $2 415 section = "9" 416 }else if ($1 == ".RI" && $2 == "(" && $4 ~ /9.\([0-9]\)/) { 417 name = $3 418 section = "9" 419 } else { 420 if ($1 == ".HR" && $3 == "\"Section") 421 next; 422 print "Possible bad cross-reference format in", FILENAME ":" FNR 423 print $0 424 next 425 } 426 gsub(/[^0-9]/, "", section) 427 Refs[toupper(name) "(" section ")"]++ 428 } 429 430 END { 431 if(lastline == ""){ 432 print lastfile ":$ is a blank line" 433 } 434 435 print "Checking Source References" 436 cmd = "xargs -n 100 ls -d 2>&1 >/dev/null | sed 's/^ls: / /; s/: .*//'" 437 for (i in Sources) { 438 print i |cmd 439 } 440 close(cmd) 441 print "" 442 print "Checking Cross-Referenced Pages" 443 for (i in Refs) { 444 if (!(tolower(i) in Pages) && !(tolower(i) in Omitman)){ 445 b = tolower(i) 446 gsub("\\(", " \\(", b) 447 gsub("\\)", "\\)", b) 448 split(tolower(i), a, "/") 449 print "egrep -in '^\\.IR.*" b "' $PLAN9/man/man*/* # Need " tolower(i) |"sort" 450 } 451 } 452 close("sort") 453 print "" 454 print "Checking commands" 455 getindex("man/man1") 456 getindex("man/man4") 457 getindex("man/man7") 458 getindex("man/man8") 459 getbinlist("bin") 460 for (i in List) { 461 if (!(i in Index) && !(i in Omitted)) 462 print "Need", i, "(in " List[i] ")" |"sort" 463 } 464 close("sort") 465 print "" 466 for (i in List) { 467 if (!(i in Index) && (i in Omitted)) 468 print "Omit", i, "(in " List[i] ")" |"sort" 469 } 470 close("sort") 471 clearindex() 472 clearlist() 473 print "" 474 print "Checking libraries" 475 getindex("man/man3") 476 getnmlist("lib/lib9.a") 477 getnmlist("lib/lib9p.a") 478 getnmlist("lib/lib9pclient.a") 479 getnmlist("lib/libString.a") 480 getnmlist("lib/libauth.a") 481 getnmlist("lib/libauthsrv.a") 482 getnmlist("lib/libbin.a") 483 getnmlist("lib/libbio.a") 484 getnmlist("lib/libcomplete.a") 485 # getnmlist("lib/libcontrol.a") 486 getnmlist("lib/libdisk.a") 487 getnmlist("lib/libdraw.a") 488 getnmlist("lib/libflate.a") 489 getnmlist("lib/libframe.a") 490 getnmlist("lib/libgeometry.a") 491 getnmlist("lib/libhtml.a") 492 # getnmlist("lib/libhttpd.a") 493 getnmlist("lib/libip.a") 494 getnmlist("lib/libmach.a") 495 # getnmlist("lib/libmemdraw.a") 496 # getnmlist("lib/libmemlayer.a") 497 getnmlist("lib/libmp.a") 498 getnmlist("lib/libmux.a") 499 getnmlist("lib/libndb.a") 500 getnmlist("lib/libplumb.a") 501 getnmlist("lib/libregexp9.a") 502 getnmlist("lib/libsec.a") 503 getnmlist("lib/libthread.a") 504 # getnmlist("lib/libventi.a") 505 for (i in List) { 506 if (!(i in Index) && !(i in Omittedlib)) 507 print "Need", List[i], i |"sort" 508 # print "Need", i, "(in " List[i] ")" |"sort" 509 } 510 close("sort") 511 print "" 512 for (i in List) { 513 if (!(i in Index) && (i in Omittedlib)) 514 print "Omit", List[i], i |"sort" 515 # print "Omit", i, "(in " List[i] ")" |"sort" 516 } 517 close("sort") 518 } 519