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