| # Usage: cd $PLAN9; awk -f dist/checkman.awk man?/*.? |
| # |
| # Checks: |
| # - .TH is first line, and has proper name section number |
| # - sections are in order NAME, SYNOPSIS, DESCRIPTION, EXAMPLES, |
| # FILES, SOURCE, SEE ALSO, DIAGNOSTICS, BUGS |
| # - there's a manual page for each cross-referenced page |
| |
| BEGIN { |
| |
| # .SH sections should come in the following order |
| |
| Weight["NAME"] = 1 |
| Weight["SYNOPSIS"] = 2 |
| Weight["DESCRIPTION"] = 4 |
| Weight["EXAMPLE"] = 8 |
| Weight["EXAMPLES"] = 16 |
| Weight["FILES"] = 32 |
| Weight["SOURCE"] = 64 |
| Weight["SEE ALSO"] = 128 |
| Weight["DIAGNOSTICS"] = 256 |
| Weight["SYSTEM CALLS"] = 512 |
| Weight["BUGS"] = 1024 |
| |
| Skipdirs["CVS"] = 1 |
| |
| # allow references to pages provded |
| # by the underlying Unix system |
| Omitman["awk(1)"] = 1 |
| Omitman["bash(1)"] = 1 |
| Omitman["chmod(1)"] = 1 |
| Omitman["compress(1)"] = 1 |
| Omitman["cp(1)"] = 1 |
| Omitman["egrep(1)"] = 1 |
| Omitman["gs(1)"] = 1 |
| Omitman["gv(1)"] = 1 |
| Omitman["lex(1)"] = 1 |
| Omitman["lp(1)"] = 1 |
| Omitman["lpr(1)"] = 1 |
| Omitman["mail(1)"] = 1 |
| Omitman["make(1)"] = 1 |
| Omitman["nm(1)"] = 1 |
| Omitman["prof(1)"] = 1 |
| Omitman["pwd(1)"] = 1 |
| Omitman["qiv(1)"] = 1 |
| Omitman["sftp(1)"] = 1 |
| Omitman["sh(1)"] = 1 |
| Omitman["ssh(1)"] = 1 |
| Omitman["stty(1)"] = 1 |
| Omitman["tex(1)"] = 1 |
| Omitman["unutf(1)"] = 1 |
| Omitman["vnc(1)"] = 1 |
| Omitman["xterm(1)"] = 1 |
| |
| Omitman["access(2)"] = 1 |
| Omitman["brk(2)"] = 1 |
| Omitman["chdir(2)"] = 1 |
| Omitman["close(2)"] = 1 |
| Omitman["connect(2)"] = 1 |
| Omitman["fork(2)"] = 1 |
| Omitman["gethostname(2)"] = 1 |
| Omitman["getpid(2)"] = 1 |
| Omitman["getuid(2)"] = 1 |
| Omitman["open(2)"] = 1 |
| Omitman["pipe(2)"] = 1 |
| Omitman["ptrace(2)"] = 1 |
| Omitman["rmdir(2)"] = 1 |
| Omitman["send(2)"] = 1 |
| Omitman["signal(2)"] = 1 |
| Omitman["sigprocmask(2)"] = 1 |
| Omitman["socketpair(2)"] = 1 |
| Omitman["unlink(2)"] = 1 |
| |
| Omitman["abort(3)"] = 1 |
| Omitman["assert(3)"] = 1 |
| Omitman["fprintf(3)"] = 1 |
| Omitman["fscanf(3)"] = 1 |
| Omitman["fopen(3)"] = 1 |
| Omitman["isalpha(3)"] = 1 |
| Omitman["malloc(3)"] = 1 |
| Omitman["perror(3)"] = 1 |
| Omitman["remove(3)"] = 1 |
| Omitman["sin(3)"] = 1 |
| Omitman["strerror(3)"] = 1 |
| |
| Omitman["core(5)"] = 1 |
| Omitman["passwd(5)"] = 1 |
| |
| Omitman["signal(7)"] = 1 |
| |
| Omitman["cron(8)"] = 1 |
| Omitman["mount(8)"] = 1 |
| |
| # don't need documentation for these in bin |
| Omitted[".cvsignore"] = 1 |
| Omitted["Getdir"] = 1 |
| Omitted["Irc"] = 1 |
| Omitted["Juke"] = 1 |
| Omitted["ajuke"] = 1 |
| Omitted["goodmk"] = 1 |
| Omitted["jukefmt"] = 1 |
| Omitted["jukeget"] = 1 |
| Omitted["jukeindex"] = 1 |
| Omitted["jukeinfo"] = 1 |
| Omitted["jukeplay"] = 1 |
| Omitted["jukeput"] = 1 |
| Omitted["jukesearch"] = 1 |
| Omitted["jukesongfile"] = 1 |
| Omitted["m4ainfo"] = 1 |
| Omitted["mp3info"] = 1 |
| Omitted["notes"] = 1 |
| Omitted["tcolors"] = 1 |
| Omitted["tref"] = 1 |
| Omitted["unutf"] = 1 |
| Omitted["volume"] = 1 |
| Omitted["vtdump"] = 1 |
| Omitted["netfilelib.rc"] = 1 |
| |
| # not for users |
| Omittedlib["creadimage"] = 1 |
| Omittedlib["pixelbits"] = 1 |
| Omittedlib["bouncemouse"] = 1 |
| Omittedlib["main"] = 1 # in libthread |
| |
| Omittedlib["opasstokey"] = 1 # in libauthsrv |
| |
| # functions provided for -lthread_db |
| Omittedlib["ps_get_thread_area"] = 1 |
| Omittedlib["ps_getpid"] = 1 |
| Omittedlib["ps_lcontinue"] = 1 |
| Omittedlib["ps_lgetfpregs"] = 1 |
| Omittedlib["ps_lgetregs"] = 1 |
| Omittedlib["ps_lsetfpregs"] = 1 |
| Omittedlib["ps_lsetregs"] = 1 |
| Omittedlib["ps_lstop"] = 1 |
| Omittedlib["ps_pcontinue"] = 1 |
| Omittedlib["ps_pdread"] = 1 |
| Omittedlib["ps_pdwrite"] = 1 |
| Omittedlib["ps_pglobal_lookup"] = 1 |
| Omittedlib["ps_pstop"] = 1 |
| Omittedlib["ps_ptread"] = 1 |
| Omittedlib["ps_ptwrite"] = 1 |
| |
| # libmach includes a small dwarf and elf library |
| Omittedlib["corecmdfreebsd386"] = 1 |
| Omittedlib["corecmdlinux386"] = 1 |
| Omittedlib["coreregsfreebsd386"] = 1 |
| Omittedlib["coreregslinux386"] = 1 |
| Omittedlib["coreregsmachopower"] = 1 |
| Omittedlib["crackelf"] = 1 |
| Omittedlib["crackmacho"] = 1 |
| Omittedlib["dwarfaddrtounit"] = 1 |
| Omittedlib["dwarfclose"] = 1 |
| Omittedlib["dwarfenum"] = 1 |
| Omittedlib["dwarfenumunit"] = 1 |
| Omittedlib["dwarfget1"] = 1 |
| Omittedlib["dwarfget128"] = 1 |
| Omittedlib["dwarfget128s"] = 1 |
| Omittedlib["dwarfget2"] = 1 |
| Omittedlib["dwarfget4"] = 1 |
| Omittedlib["dwarfget8"] = 1 |
| Omittedlib["dwarfgetabbrev"] = 1 |
| Omittedlib["dwarfgetaddr"] = 1 |
| Omittedlib["dwarfgetn"] = 1 |
| Omittedlib["dwarfgetnref"] = 1 |
| Omittedlib["dwarfgetstring"] = 1 |
| Omittedlib["dwarflookupfn"] = 1 |
| Omittedlib["dwarflookupname"] = 1 |
| Omittedlib["dwarflookupnameinunit"] = 1 |
| Omittedlib["dwarflookupsubname"] = 1 |
| Omittedlib["dwarflookuptag"] = 1 |
| Omittedlib["dwarfnextsym"] = 1 |
| Omittedlib["dwarfnextsymat"] = 1 |
| Omittedlib["dwarfopen"] = 1 |
| Omittedlib["dwarfpctoline"] = 1 |
| Omittedlib["dwarfseeksym"] = 1 |
| Omittedlib["dwarfskip"] = 1 |
| Omittedlib["dwarfunwind"] = 1 |
| Omittedlib["elfclose"] = 1 |
| Omittedlib["elfdl386mapdl"] = 1 |
| Omittedlib["elfinit"] = 1 |
| Omittedlib["elfmachine"] = 1 |
| Omittedlib["elfmap"] = 1 |
| Omittedlib["elfopen"] = 1 |
| Omittedlib["elfsection"] = 1 |
| Omittedlib["elfsym"] = 1 |
| Omittedlib["elfsymlookup"] = 1 |
| Omittedlib["elftype"] = 1 |
| Omittedlib["machoclose"] = 1 |
| Omittedlib["machoinit"] = 1 |
| Omittedlib["machoopen"] = 1 |
| Omittedlib["stabsym"] = 1 |
| Omittedlib["symdwarf"] = 1 |
| Omittedlib["symelf"] = 1 |
| Omittedlib["symmacho"] = 1 |
| Omittedlib["symstabs"] = 1 |
| Omittedlib["elfcorelinux386"] = 1 |
| Omittedlib["linux2ureg386"] = 1 |
| Omittedlib["ureg2linux386"] = 1 |
| Omittedlib["coreregs"] = 1 # haven't documented mach yet |
| Omittedlib["regdesc"] = 1 |
| |
| Omittedlib["auth_attr"] = 1 # not happy about this |
| |
| Omittedlib["ndbnew"] = 1 # private to library |
| Omittedlib["ndbsetval"] = 1 |
| |
| Renamelib["chanalt"] = "alt" |
| Renamelib["channbrecv"] = "nbrecv" |
| Renamelib["channbrecvp"] = "nbrecvp" |
| Renamelib["channbrecvul"] = "nbrecvul" |
| Renamelib["channbsend"] = "nbsend" |
| Renamelib["channbsendp"] = "nbsendp" |
| Renamelib["channbsendul"] = "nbsendul" |
| Renamelib["chanrecv"] = "recv" |
| Renamelib["chanrecvp"] = "recvp" |
| Renamelib["chanrecvul"] = "recvul" |
| Renamelib["chansend"] = "send" |
| Renamelib["chansendp"] = "sendp" |
| Renamelib["chansendul"] = "sendul" |
| Renamelib["threadyield"] = "yield" |
| |
| Renamelib["fmtcharstod"] = "charstod" |
| Renamelib["fmtstrtod"] = "strtod" |
| |
| Renamelib["regcomp9"] = "regcomp" |
| Renamelib["regcomplit9"] = "regcomplit" |
| Renamelib["regcompnl9"] = "regcompnl" |
| Renamelib["regerror9"] = "regerror" |
| Renamelib["regexec9"] = "regexec" |
| Renamelib["regsub9"] = "regsub" |
| Renamelib["rregexec9"] = "rregexec" |
| Renamelib["rregsub9"] = "rregsub" |
| |
| lastline = "XXX"; |
| lastfile = FILENAME; |
| } |
| |
| func getnmlist(lib, cmd) |
| { |
| cmd = "nm -g " lib |
| while (cmd | getline) { |
| if (($2 == "T" || $2 == "L") && $3 !~ "^_"){ |
| sym = $3 |
| sub("^p9", "", sym) |
| if(sym in Renamelib) |
| List[Renamelib[sym]] = lib " as " sym |
| else |
| List[sym] = lib |
| } |
| } |
| close(cmd) |
| } |
| |
| |
| func getindex(dir, fname) |
| { |
| fname = dir "/INDEX" |
| while ((getline < fname) > 0) |
| Index[$1] = dir |
| close(fname) |
| } |
| |
| func getbinlist(dir, cmd, subdirs, nsd) |
| { |
| cmd = "ls -p -l " dir |
| nsd = 0 |
| while (cmd | getline) { |
| if ($1 ~ /^d/) { |
| if (!($10 in Skipdirs)) |
| subdirs[++nsd] = $10 |
| } else if ($10 !~ "^_") |
| List[$10] = dir |
| } |
| for ( ; nsd > 0 ; nsd--) |
| getbinlist(dir "/" subdirs[nsd]) |
| close(cmd) |
| } |
| |
| func clearindex( i) |
| { |
| for (i in Index) |
| delete Index[i] |
| } |
| |
| func clearlist( i) |
| { |
| for (i in List) |
| delete List[i] |
| } |
| |
| |
| FNR==1 { |
| if(lastline == ""){ |
| # screws up troff headers |
| print lastfile ":$ is a blank line" |
| } |
| |
| n = length(FILENAME) |
| nam = FILENAME |
| if(nam ~ /\.html$/) |
| next |
| if(nam !~ /^man\/man(.*)\/(.*)\.(.*)$/){ |
| print "nam", nam, "not of form [0-9][0-9]?/*" |
| next |
| } |
| nam = substr(nam, 8) |
| gsub("[/.]", " ", nam); |
| n = split(nam, a) |
| sec = a[1] |
| name = a[2] |
| section = a[3] |
| if($1 != ".TH" || NF != 3) |
| print "First line of", FILENAME, "not a proper .TH" |
| else if(($2 != toupper(name) || substr($3, 1, length(sec)) != sec || $3 != toupper(section)) \ |
| && ($2!="INTRO" || name!="0intro") \ |
| && (name !~ /^9/ || $2!=toupper(substr(name, 2)))){ |
| print ".TH of", FILENAME, "doesn't match filename" |
| }else |
| Pages[tolower($2) "(" tolower($3) ")"] = 1 |
| Sh = 0 |
| } |
| |
| { lastline=$0; lastfile=FILENAME; } |
| |
| $1 == ".SH" { |
| if(inex) |
| print "Unterminated .EX in", FILENAME, ":", $0 |
| inex = 0; |
| if (substr($2, 1, 1) == "\"") { |
| if (NF == 2) { |
| print "Unneeded quote in", FILENAME, ":", $0 |
| $2 = substr($2, 2, length($2)-2) |
| } else if (NF == 3) { |
| $2 = substr($2, 2) substr($3, 1, length($3)-1) |
| NF = 2 |
| } |
| } |
| if(Sh == 0 && $2 != "NAME") |
| print FILENAME, "has no .SH NAME" |
| w = Weight[$2] |
| if (w) { |
| if (w < Sh) |
| print "Heading", $2, "out of order in", FILENAME |
| Sh += w |
| } |
| sh = $2 |
| } |
| |
| $1 == ".EX" { |
| if(inex) |
| print "Nested .EX in", FILENAME ":" FNR, ":", $0 |
| inex = 1 |
| } |
| |
| $1 == ".EE" { |
| if(!inex) |
| print "Bad .EE in", FILENAME ":" FNR ":", $0 |
| inex = 0; |
| } |
| |
| $1 == ".TF" { |
| smallspace = 1 |
| } |
| |
| $1 == ".PD" || $1 == ".SH" || $1 == ".SS" || $1 == ".TH" { |
| smallspace = 0 |
| } |
| |
| $1 == ".RE" { |
| lastre = 1 |
| } |
| |
| $1 == ".PP" { |
| if(smallspace && !lastre) |
| print "Possible missing .PD at " FILENAME ":" FNR |
| smallspace = 0 |
| } |
| |
| $1 != ".RE" { |
| lastre = 0 |
| } |
| |
| sh == "BUGS" && $1 == ".br" { |
| print FILENAME ":" FNR ": .br in BUGS" |
| } |
| |
| sh == "SOURCE" && $1 ~ /^\\\*9\// { |
| s = ENVIRON["PLAN9"] substr($1, 4) |
| Sources[s] = 1 |
| } |
| |
| sh == "SOURCE" && $2 ~ /^\\\*9\// { |
| s = ENVIRON["PLAN9"] substr($2, 4) |
| Sources[s] = 1 |
| } |
| |
| sh == "SOURCE" && $1 ~ /^\// { |
| Sources[$1] = 1 |
| } |
| |
| sh == "SOURCE" && $2 ~ /^\// { |
| Sources[$2] = 1 |
| } |
| |
| $0 ~ /^\.[A-Z].*\([1-9]\)/ { |
| if ($1 == ".IR" && $3 ~ /\([0-9]\)/) { |
| name = $2 |
| section = $3 |
| }else if ($1 == ".RI" && $2 == "(" && $4 ~ /\([0-9]\)/) { |
| name = $3 |
| section = $4 |
| }else if ($1 == ".IR" && $3 ~ /9.\([0-9]\)/) { |
| name = $2 |
| section = "9" |
| }else if ($1 == ".RI" && $2 == "(" && $4 ~ /9.\([0-9]\)/) { |
| name = $3 |
| section = "9" |
| } else { |
| if ($1 == ".HR" && $3 == "\"Section") |
| next; |
| print "Possible bad cross-reference format in", FILENAME ":" FNR |
| print $0 |
| next |
| } |
| gsub(/[^0-9]/, "", section) |
| Refs[toupper(name) "(" section ")"]++ |
| } |
| |
| END { |
| if(lastline == ""){ |
| print lastfile ":$ is a blank line" |
| } |
| |
| print "Checking Source References" |
| cmd = "xargs -n 100 ls -d 2>&1 >/dev/null | sed 's/^ls: / /; s/: .*//'" |
| for (i in Sources) { |
| print i |cmd |
| } |
| close(cmd) |
| print "" |
| print "Checking Cross-Referenced Pages" |
| for (i in Refs) { |
| if (!(tolower(i) in Pages) && !(tolower(i) in Omitman)){ |
| b = tolower(i) |
| gsub("\\(", " \\(", b) |
| gsub("\\)", "\\)", b) |
| split(tolower(i), a, "/") |
| print "egrep -in '^\\.IR.*" b "' $PLAN9/man/man*/* # Need " tolower(i) |"sort" |
| } |
| } |
| close("sort") |
| print "" |
| print "Checking commands" |
| getindex("man/man1") |
| getindex("man/man4") |
| getindex("man/man7") |
| getindex("man/man8") |
| getbinlist("bin") |
| for (i in List) { |
| if (!(i in Index) && !(i in Omitted)) |
| print "Need", i, "(in " List[i] ")" |"sort" |
| } |
| close("sort") |
| print "" |
| for (i in List) { |
| if (!(i in Index) && (i in Omitted)) |
| print "Omit", i, "(in " List[i] ")" |"sort" |
| } |
| close("sort") |
| clearindex() |
| clearlist() |
| print "" |
| print "Checking libraries" |
| getindex("man/man3") |
| getnmlist("lib/lib9.a") |
| getnmlist("lib/lib9p.a") |
| getnmlist("lib/lib9pclient.a") |
| getnmlist("lib/libString.a") |
| getnmlist("lib/libauth.a") |
| getnmlist("lib/libauthsrv.a") |
| getnmlist("lib/libbin.a") |
| getnmlist("lib/libbio.a") |
| getnmlist("lib/libcomplete.a") |
| # getnmlist("lib/libcontrol.a") |
| getnmlist("lib/libdisk.a") |
| getnmlist("lib/libdraw.a") |
| getnmlist("lib/libflate.a") |
| getnmlist("lib/libframe.a") |
| getnmlist("lib/libgeometry.a") |
| getnmlist("lib/libhtml.a") |
| # getnmlist("lib/libhttpd.a") |
| getnmlist("lib/libip.a") |
| getnmlist("lib/libmach.a") |
| # getnmlist("lib/libmemdraw.a") |
| # getnmlist("lib/libmemlayer.a") |
| getnmlist("lib/libmp.a") |
| getnmlist("lib/libmux.a") |
| getnmlist("lib/libndb.a") |
| getnmlist("lib/libplumb.a") |
| getnmlist("lib/libregexp9.a") |
| getnmlist("lib/libsec.a") |
| getnmlist("lib/libthread.a") |
| # getnmlist("lib/libventi.a") |
| for (i in List) { |
| if (!(i in Index) && !(i in Omittedlib)) |
| print "Need", List[i], i |"sort" |
| # print "Need", i, "(in " List[i] ")" |"sort" |
| } |
| close("sort") |
| print "" |
| for (i in List) { |
| if (!(i in Index) && (i in Omittedlib)) |
| print "Omit", List[i], i |"sort" |
| # print "Omit", i, "(in " List[i] ")" |"sort" |
| } |
| close("sort") |
| } |
| |