checkman.awk 6.1 KB

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