/mk/help/help.awk

https://github.com/mend0za/pkgsrc-2011Q3 · AWK · 188 lines · 118 code · 33 blank · 37 comment · 58 complexity · 79741dd23f862dd58e470fad46d1163b MD5 · raw file

  1. # $NetBSD: help.awk,v 1.26 2009/05/15 21:10:31 rillig Exp $
  2. #
  3. # This program extracts the inline documentation from *.mk files.
  4. #
  5. # usage: env TOPIC="topic" awk help.awk file...
  6. #
  7. BEGIN {
  8. no = 0; yes = 1; always = 1;
  9. topic = ENVIRON["TOPIC"];
  10. uctopic = toupper(topic);
  11. lctopic = tolower(topic);
  12. found_anything = no; # has some help text been found at all?
  13. last_fname = "";
  14. ignore_this_line = no;
  15. ignore_next_empty_line = no;
  16. ignore_this_section = no;
  17. delete lines; # the collected lines
  18. nlines = 0; # the number of lines collected so far
  19. delete keywords; # the keywords for this paragraph
  20. delete all_keywords; # all keywords that appear anywhere
  21. comment_lines = 0; # the number of comment lines so far
  22. print_noncomment_lines = yes; # for make targets, this isn't useful
  23. print_index = (topic == ":index");
  24. # whether to print only the list of keywords
  25. }
  26. # Help topics are separated by either completely empty lines or by the
  27. # end of a file or by the end of all files. When there have been enough
  28. # comment lines, the topic is considered worth printing.
  29. #
  30. function end_of_topic() {
  31. if (comment_lines <= 2 || ignore_this_section) {
  32. cleanup();
  33. return;
  34. }
  35. for (k in keywords)
  36. all_keywords[k]++;
  37. relevant = (topic in keywords || lctopic in keywords || uctopic in keywords || topic == ":all");
  38. if (relevant && !print_index) {
  39. if (found_anything)
  40. print "";
  41. found_anything = yes;
  42. kw = "";
  43. for (i in keywords)
  44. kw = kw " " i;
  45. print "===> "last_fname " (keywords:" kw "):";
  46. for (i = 0; i < nlines; i++) {
  47. if (print_noncomment_lines || (lines[i] ~ /^#/))
  48. print lines[i];
  49. }
  50. }
  51. cleanup();
  52. }
  53. function cleanup() {
  54. ignore_next_empty_line = yes;
  55. delete lines;
  56. nlines = 0;
  57. delete keywords;
  58. comment_lines = 0;
  59. print_noncomment_lines = yes;
  60. ignore_this_section = no;
  61. }
  62. always {
  63. ignore_this_line = (ignore_next_empty_line && $0 == "#") || $0 == "";
  64. ignore_next_empty_line = no;
  65. }
  66. # There is no need to print the RCS Id, since the full pathname
  67. # is prefixed to the file contents.
  68. /^#.*\$.*\$$/ {
  69. ignore_this_line = yes;
  70. ignore_next_empty_line = yes;
  71. }
  72. # The lines containing the keywords should also not appear in
  73. # the output for now. This decision is not final since it may
  74. # be helpful for the user to know by which keywords a topic
  75. # can be reached.
  76. ($1 == "#" && $2 == "Keywords:") {
  77. for (i = 3; i <= NF; i++) {
  78. w = ($i == toupper($i)) ? tolower($i) : $i;
  79. sub(/,$/, "", w);
  80. keywords[w] = yes;
  81. }
  82. ignore_this_line = yes;
  83. ignore_next_empty_line = yes;
  84. }
  85. ($0 == "#") {
  86. ignore_next_empty_line = no;
  87. }
  88. $1 == "#" && $2 == "Copyright" {
  89. ignore_this_section = yes;
  90. }
  91. # Don't show the user the definition of make targets, since they are
  92. # usually not interesting enough. This allows the comments to reach
  93. # until the line directly above the target definition.
  94. #
  95. $1 ~ /:$/ && $2 == ".PHONY" {
  96. end_of_topic();
  97. }
  98. (!ignore_this_line) {
  99. lines[nlines++] = $0;
  100. }
  101. # Check whether the current line contains a keyword. Such a keyword must
  102. # be all-lowercase (make targets) or all-uppercase (variable names).
  103. # Everything else is assumed to belong to the explaining text.
  104. #
  105. NF >= 1 && !/^[\t.]/ && !/^#*$/ {
  106. w = ($1 ~ /^\#[A-Z]/) ? substr($1, 2) : ($1 == "#") ? $2 : $1;
  107. # Reduce FOO.<param> and FOO.${param} to FOO.
  108. sub(/\.[<$].*[>}]$/, "", w);
  109. if (w ~ /\+=$/) {
  110. # Appending to a variable is usually not a definition.
  111. } else if (w != toupper(w) && w != tolower(w)) {
  112. # Words in mixed case are not taken as keywords. If you
  113. # want them anyway, list them in a "Keywords:" line.
  114. } else if (w !~ /^[A-Za-z][-0-9A-Z_a-z]*[0-9A-Za-z](:|\?=|=)?$/) {
  115. # Keywords must consist only of letters, digits, hyphens
  116. # and underscores; except for some trailing type specifier.
  117. } else if (w == tolower(w) && !(w ~ /:$/)) {
  118. # Lower-case words (often make targets) must be followed
  119. # by a colon to be recognized as keywords.
  120. } else if (w == toupper(w) && w ~ /:$/) {
  121. # Upper-case words ending with a colon are probably not
  122. # make targets, so ignore them. Common cases are tags
  123. # like FIXME and TODO.
  124. } else {
  125. sub(/^#[ \t]*/, "", w);
  126. sub(/(:|\?=|=)$/, "", w);
  127. sub(/:$/, "", w);
  128. if (w != "") {
  129. keywords[w] = yes;
  130. }
  131. }
  132. }
  133. # Don't print the implementation of make targets.
  134. $1 ~ /:$/ {
  135. print_noncomment_lines = no;
  136. }
  137. $1 == "#" {
  138. comment_lines++;
  139. }
  140. /^$/ || last_fname != FILENAME {
  141. end_of_topic();
  142. }
  143. always {
  144. last_fname = FILENAME;
  145. }
  146. END {
  147. end_of_topic();
  148. if (print_index) {
  149. for (k in all_keywords) {
  150. print all_keywords[k] "\t" k;
  151. }
  152. } else if (!found_anything) {
  153. print "No help found for "topic".";
  154. }
  155. }