Merge tag 'drm-next-2024-03-22' of https://gitlab.freedesktop.org/drm/kernel
[sfrench/cifs-2.6.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 # documented: maps documented entries (each key is an entry)
24 # entries: maps ctl_table names and procnames to counts (so
25 #          enumerating the subkeys for a given ctl_table lists its
26 #          procnames)
27 # curtable: the name of the current ctl_table struct
28 # curentry: the name of the current proc entry (procname when parsing
29 #           a ctl_table, constructed path when parsing a ctl_path)
30
31
32 # Remove punctuation from the given value
33 function trimpunct(value) {
34     while (value ~ /^["&]/) {
35         value = substr(value, 2)
36     }
37     while (value ~ /[]["&,}]$/) {
38         value = substr(value, 1, length(value) - 1)
39     }
40     return value
41 }
42
43 # Print the information for the given entry
44 function printentry(entry) {
45     seen[entry]++
46     printf "* %s from %s", entry, file[entry]
47     if (documented[entry]) {
48         printf " (documented)"
49     }
50     print ""
51 }
52
53
54 # Stage 1: build the list of documented entries
55 FNR == NR && /^=+$/ {
56     if (prevline ~ /Documentation for/) {
57         # This is the main title
58         next
59     }
60
61     # The previous line is a section title, parse it
62     $0 = prevline
63     if (debug) print "Parsing " $0
64     inbrackets = 0
65     for (i = 1; i <= NF; i++) {
66         if (length($i) == 0) {
67             continue
68         }
69         if (!inbrackets && substr($i, 1, 1) == "(") {
70             inbrackets = 1
71         }
72         if (!inbrackets) {
73             token = trimpunct($i)
74             if (length(token) > 0 && token != "and") {
75                 if (debug) print trimpunct($i)
76                 documented[trimpunct($i)]++
77             }
78         }
79         if (inbrackets && substr($i, length($i), 1) == ")") {
80             inbrackets = 0
81         }
82     }
83 }
84
85 FNR == NR {
86     prevline = $0
87     next
88 }
89
90
91 # Stage 2: process each file and find all sysctl tables
92 BEGINFILE {
93     delete entries
94     curtable = ""
95     curentry = ""
96     delete vars
97     if (debug) print "Processing file " FILENAME
98 }
99
100 /^static( const)? struct ctl_table/ {
101     match($0, /static( const)? struct ctl_table ([^][]+)/, tables)
102     curtable = tables[2]
103     if (debug) print "Processing table " curtable
104 }
105
106 /^};$/ {
107     curtable = ""
108     curentry = ""
109     delete vars
110 }
111
112 curtable && /\.procname[\t ]*=[\t ]*".+"/ {
113     match($0, /.procname[\t ]*=[\t ]*"([^"]+)"/, names)
114     curentry = names[1]
115     if (debug) print "Adding entry " curentry " to table " curtable
116     entries[curtable][curentry]++
117     file[curentry] = FILENAME
118 }
119
120 /register_sysctl.*/ {
121     match($0, /register_sysctl(|_init|_sz)\("([^"]+)" *, *([^,)]+)/, tables)
122     if (debug) print "Registering table " tables[3] " at " tables[2]
123     if (tables[2] == table) {
124         for (entry in entries[tables[3]]) {
125             printentry(entry)
126         }
127     }
128 }
129
130 /kmemdup.*/ {
131     match($0, /([^ \t]+) *= *kmemdup\(([^,]+) *,/, names)
132     if (debug) print "Found variable " names[1] " for table " names[2]
133     if (names[2] in entries) {
134         vars[names[1]] = names[2]
135     }
136 }
137
138 /__register_sysctl_table.*/ {
139     match($0, /__register_sysctl_table\([^,]+, *"([^"]+)" *, *([^,]+)/, tables)
140     if (debug) print "Registering variable table " tables[2] " at " tables[1]
141     if (tables[1] == table && tables[2] in vars) {
142         for (entry in entries[vars[tables[2]]]) {
143             printentry(entry)
144         }
145     }
146 }
147
148 END {
149     for (entry in documented) {
150         if (!seen[entry]) {
151             print "No implementation for " entry
152         }
153     }
154 }