Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 | #!/usr/bin/gawk -f # SPDX-License-Identifier: GPL-2.0 # Script to check sysctl documentation against source files # # Copyright (c) 2020 Stephen Kitt # Example invocation: # scripts/check-sysctl-docs -vtable="kernel" \ # Documentation/admin-guide/sysctl/kernel.rst \ # $(git grep -l register_sysctl_) # # Specify -vdebug=1 to see debugging information BEGIN { if (!table) { print "Please specify the table to look for using the table variable" > "/dev/stderr" exit 1 } } # The following globals are used: # children: maps ctl_table names and procnames to child ctl_table names # documented: maps documented entries (each key is an entry) # entries: maps ctl_table names and procnames to counts (so # enumerating the subkeys for a given ctl_table lists its # procnames) # files: maps procnames to source file names # paths: maps ctl_path names to paths # curpath: the name of the current ctl_path struct # curtable: the name of the current ctl_table struct # curentry: the name of the current proc entry (procname when parsing # a ctl_table, constructed path when parsing a ctl_path) # Remove punctuation from the given value function trimpunct(value) { while (value ~ /^["&]/) { value = substr(value, 2) } while (value ~ /[]["&,}]$/) { value = substr(value, 1, length(value) - 1) } return value } # Print the information for the given entry function printentry(entry) { seen[entry]++ printf "* %s from %s", entry, file[entry] if (documented[entry]) { printf " (documented)" } print "" } # Stage 1: build the list of documented entries FNR == NR && /^=+$/ { if (prevline ~ /Documentation for/) { # This is the main title next } # The previous line is a section title, parse it $0 = prevline if (debug) print "Parsing " $0 inbrackets = 0 for (i = 1; i <= NF; i++) { if (length($i) == 0) { continue } if (!inbrackets && substr($i, 1, 1) == "(") { inbrackets = 1 } if (!inbrackets) { token = trimpunct($i) if (length(token) > 0 && token != "and") { if (debug) print trimpunct($i) documented[trimpunct($i)]++ } } if (inbrackets && substr($i, length($i), 1) == ")") { inbrackets = 0 } } } FNR == NR { prevline = $0 next } # Stage 2: process each file and find all sysctl tables BEGINFILE { delete children delete entries delete paths curpath = "" curtable = "" curentry = "" if (debug) print "Processing file " FILENAME } /^static struct ctl_path/ { match($0, /static struct ctl_path ([^][]+)/, tables) curpath = tables[1] if (debug) print "Processing path " curpath } /^static struct ctl_table/ { match($0, /static struct ctl_table ([^][]+)/, tables) curtable = tables[1] if (debug) print "Processing table " curtable } /^};$/ { curpath = "" curtable = "" curentry = "" } curpath && /\.procname[\t ]*=[\t ]*".+"/ { match($0, /.procname[\t ]*=[\t ]*"([^"]+)"/, names) if (curentry) { curentry = curentry "/" names[1] } else { curentry = names[1] } if (debug) print "Setting path " curpath " to " curentry paths[curpath] = curentry } curtable && /\.procname[\t ]*=[\t ]*".+"/ { match($0, /.procname[\t ]*=[\t ]*"([^"]+)"/, names) curentry = names[1] if (debug) print "Adding entry " curentry " to table " curtable entries[curtable][curentry]++ file[curentry] = FILENAME } /\.child[\t ]*=/ { child = trimpunct($NF) if (debug) print "Linking child " child " to table " curtable " entry " curentry children[curtable][curentry] = child } /register_sysctl_table\(.*\)/ { match($0, /register_sysctl_table\(([^)]+)\)/, tables) if (debug) print "Registering table " tables[1] if (children[tables[1]][table]) { for (entry in entries[children[tables[1]][table]]) { printentry(entry) } } } /register_sysctl_paths\(.*\)/ { match($0, /register_sysctl_paths\(([^)]+), ([^)]+)\)/, tables) if (debug) print "Attaching table " tables[2] " to path " tables[1] if (paths[tables[1]] == table) { for (entry in entries[tables[2]]) { printentry(entry) } } split(paths[tables[1]], components, "/") if (length(components) > 1 && components[1] == table) { # Count the first subdirectory as seen seen[components[2]]++ } } END { for (entry in documented) { if (!seen[entry]) { print "No implementation for " entry } } } |