18c2ecf20Sopenharmony_ci#!/usr/bin/gawk -f
28c2ecf20Sopenharmony_ci# SPDX-License-Identifier: GPL-2.0
38c2ecf20Sopenharmony_ci
48c2ecf20Sopenharmony_ci# Script to check sysctl documentation against source files
58c2ecf20Sopenharmony_ci#
68c2ecf20Sopenharmony_ci# Copyright (c) 2020 Stephen Kitt
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci# Example invocation:
98c2ecf20Sopenharmony_ci#	scripts/check-sysctl-docs -vtable="kernel" \
108c2ecf20Sopenharmony_ci#		Documentation/admin-guide/sysctl/kernel.rst \
118c2ecf20Sopenharmony_ci#		$(git grep -l register_sysctl_)
128c2ecf20Sopenharmony_ci#
138c2ecf20Sopenharmony_ci# Specify -vdebug=1 to see debugging information
148c2ecf20Sopenharmony_ci
158c2ecf20Sopenharmony_ciBEGIN {
168c2ecf20Sopenharmony_ci    if (!table) {
178c2ecf20Sopenharmony_ci	print "Please specify the table to look for using the table variable" > "/dev/stderr"
188c2ecf20Sopenharmony_ci	exit 1
198c2ecf20Sopenharmony_ci    }
208c2ecf20Sopenharmony_ci}
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci# The following globals are used:
238c2ecf20Sopenharmony_ci# children: maps ctl_table names and procnames to child ctl_table names
248c2ecf20Sopenharmony_ci# documented: maps documented entries (each key is an entry)
258c2ecf20Sopenharmony_ci# entries: maps ctl_table names and procnames to counts (so
268c2ecf20Sopenharmony_ci#          enumerating the subkeys for a given ctl_table lists its
278c2ecf20Sopenharmony_ci#          procnames)
288c2ecf20Sopenharmony_ci# files: maps procnames to source file names
298c2ecf20Sopenharmony_ci# paths: maps ctl_path names to paths
308c2ecf20Sopenharmony_ci# curpath: the name of the current ctl_path struct
318c2ecf20Sopenharmony_ci# curtable: the name of the current ctl_table struct
328c2ecf20Sopenharmony_ci# curentry: the name of the current proc entry (procname when parsing
338c2ecf20Sopenharmony_ci#           a ctl_table, constructed path when parsing a ctl_path)
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci
368c2ecf20Sopenharmony_ci# Remove punctuation from the given value
378c2ecf20Sopenharmony_cifunction trimpunct(value) {
388c2ecf20Sopenharmony_ci    while (value ~ /^["&]/) {
398c2ecf20Sopenharmony_ci	value = substr(value, 2)
408c2ecf20Sopenharmony_ci    }
418c2ecf20Sopenharmony_ci    while (value ~ /[]["&,}]$/) {
428c2ecf20Sopenharmony_ci	value = substr(value, 1, length(value) - 1)
438c2ecf20Sopenharmony_ci    }
448c2ecf20Sopenharmony_ci    return value
458c2ecf20Sopenharmony_ci}
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci# Print the information for the given entry
488c2ecf20Sopenharmony_cifunction printentry(entry) {
498c2ecf20Sopenharmony_ci    seen[entry]++
508c2ecf20Sopenharmony_ci    printf "* %s from %s", entry, file[entry]
518c2ecf20Sopenharmony_ci    if (documented[entry]) {
528c2ecf20Sopenharmony_ci	printf " (documented)"
538c2ecf20Sopenharmony_ci    }
548c2ecf20Sopenharmony_ci    print ""
558c2ecf20Sopenharmony_ci}
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_ci
588c2ecf20Sopenharmony_ci# Stage 1: build the list of documented entries
598c2ecf20Sopenharmony_ciFNR == NR && /^=+$/ {
608c2ecf20Sopenharmony_ci    if (prevline ~ /Documentation for/) {
618c2ecf20Sopenharmony_ci	# This is the main title
628c2ecf20Sopenharmony_ci	next
638c2ecf20Sopenharmony_ci    }
648c2ecf20Sopenharmony_ci
658c2ecf20Sopenharmony_ci    # The previous line is a section title, parse it
668c2ecf20Sopenharmony_ci    $0 = prevline
678c2ecf20Sopenharmony_ci    if (debug) print "Parsing " $0
688c2ecf20Sopenharmony_ci    inbrackets = 0
698c2ecf20Sopenharmony_ci    for (i = 1; i <= NF; i++) {
708c2ecf20Sopenharmony_ci	if (length($i) == 0) {
718c2ecf20Sopenharmony_ci	    continue
728c2ecf20Sopenharmony_ci	}
738c2ecf20Sopenharmony_ci	if (!inbrackets && substr($i, 1, 1) == "(") {
748c2ecf20Sopenharmony_ci	    inbrackets = 1
758c2ecf20Sopenharmony_ci	}
768c2ecf20Sopenharmony_ci	if (!inbrackets) {
778c2ecf20Sopenharmony_ci	    token = trimpunct($i)
788c2ecf20Sopenharmony_ci	    if (length(token) > 0 && token != "and") {
798c2ecf20Sopenharmony_ci		if (debug) print trimpunct($i)
808c2ecf20Sopenharmony_ci		documented[trimpunct($i)]++
818c2ecf20Sopenharmony_ci	    }
828c2ecf20Sopenharmony_ci	}
838c2ecf20Sopenharmony_ci	if (inbrackets && substr($i, length($i), 1) == ")") {
848c2ecf20Sopenharmony_ci	    inbrackets = 0
858c2ecf20Sopenharmony_ci	}
868c2ecf20Sopenharmony_ci    }
878c2ecf20Sopenharmony_ci}
888c2ecf20Sopenharmony_ci
898c2ecf20Sopenharmony_ciFNR == NR {
908c2ecf20Sopenharmony_ci    prevline = $0
918c2ecf20Sopenharmony_ci    next
928c2ecf20Sopenharmony_ci}
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_ci# Stage 2: process each file and find all sysctl tables
968c2ecf20Sopenharmony_ciBEGINFILE {
978c2ecf20Sopenharmony_ci    delete children
988c2ecf20Sopenharmony_ci    delete entries
998c2ecf20Sopenharmony_ci    delete paths
1008c2ecf20Sopenharmony_ci    curpath = ""
1018c2ecf20Sopenharmony_ci    curtable = ""
1028c2ecf20Sopenharmony_ci    curentry = ""
1038c2ecf20Sopenharmony_ci    if (debug) print "Processing file " FILENAME
1048c2ecf20Sopenharmony_ci}
1058c2ecf20Sopenharmony_ci
1068c2ecf20Sopenharmony_ci/^static struct ctl_path/ {
1078c2ecf20Sopenharmony_ci    match($0, /static struct ctl_path ([^][]+)/, tables)
1088c2ecf20Sopenharmony_ci    curpath = tables[1]
1098c2ecf20Sopenharmony_ci    if (debug) print "Processing path " curpath
1108c2ecf20Sopenharmony_ci}
1118c2ecf20Sopenharmony_ci
1128c2ecf20Sopenharmony_ci/^static struct ctl_table/ {
1138c2ecf20Sopenharmony_ci    match($0, /static struct ctl_table ([^][]+)/, tables)
1148c2ecf20Sopenharmony_ci    curtable = tables[1]
1158c2ecf20Sopenharmony_ci    if (debug) print "Processing table " curtable
1168c2ecf20Sopenharmony_ci}
1178c2ecf20Sopenharmony_ci
1188c2ecf20Sopenharmony_ci/^};$/ {
1198c2ecf20Sopenharmony_ci    curpath = ""
1208c2ecf20Sopenharmony_ci    curtable = ""
1218c2ecf20Sopenharmony_ci    curentry = ""
1228c2ecf20Sopenharmony_ci}
1238c2ecf20Sopenharmony_ci
1248c2ecf20Sopenharmony_cicurpath && /\.procname[\t ]*=[\t ]*".+"/ {
1258c2ecf20Sopenharmony_ci    match($0, /.procname[\t ]*=[\t ]*"([^"]+)"/, names)
1268c2ecf20Sopenharmony_ci    if (curentry) {
1278c2ecf20Sopenharmony_ci	curentry = curentry "/" names[1]
1288c2ecf20Sopenharmony_ci    } else {
1298c2ecf20Sopenharmony_ci	curentry = names[1]
1308c2ecf20Sopenharmony_ci    }
1318c2ecf20Sopenharmony_ci    if (debug) print "Setting path " curpath " to " curentry
1328c2ecf20Sopenharmony_ci    paths[curpath] = curentry
1338c2ecf20Sopenharmony_ci}
1348c2ecf20Sopenharmony_ci
1358c2ecf20Sopenharmony_cicurtable && /\.procname[\t ]*=[\t ]*".+"/ {
1368c2ecf20Sopenharmony_ci    match($0, /.procname[\t ]*=[\t ]*"([^"]+)"/, names)
1378c2ecf20Sopenharmony_ci    curentry = names[1]
1388c2ecf20Sopenharmony_ci    if (debug) print "Adding entry " curentry " to table " curtable
1398c2ecf20Sopenharmony_ci    entries[curtable][curentry]++
1408c2ecf20Sopenharmony_ci    file[curentry] = FILENAME
1418c2ecf20Sopenharmony_ci}
1428c2ecf20Sopenharmony_ci
1438c2ecf20Sopenharmony_ci/\.child[\t ]*=/ {
1448c2ecf20Sopenharmony_ci    child = trimpunct($NF)
1458c2ecf20Sopenharmony_ci    if (debug) print "Linking child " child " to table " curtable " entry " curentry
1468c2ecf20Sopenharmony_ci    children[curtable][curentry] = child
1478c2ecf20Sopenharmony_ci}
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci/register_sysctl_table\(.*\)/ {
1508c2ecf20Sopenharmony_ci    match($0, /register_sysctl_table\(([^)]+)\)/, tables)
1518c2ecf20Sopenharmony_ci    if (debug) print "Registering table " tables[1]
1528c2ecf20Sopenharmony_ci    if (children[tables[1]][table]) {
1538c2ecf20Sopenharmony_ci	for (entry in entries[children[tables[1]][table]]) {
1548c2ecf20Sopenharmony_ci	    printentry(entry)
1558c2ecf20Sopenharmony_ci	}
1568c2ecf20Sopenharmony_ci    }
1578c2ecf20Sopenharmony_ci}
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci/register_sysctl_paths\(.*\)/ {
1608c2ecf20Sopenharmony_ci    match($0, /register_sysctl_paths\(([^)]+), ([^)]+)\)/, tables)
1618c2ecf20Sopenharmony_ci    if (debug) print "Attaching table " tables[2] " to path " tables[1]
1628c2ecf20Sopenharmony_ci    if (paths[tables[1]] == table) {
1638c2ecf20Sopenharmony_ci	for (entry in entries[tables[2]]) {
1648c2ecf20Sopenharmony_ci	    printentry(entry)
1658c2ecf20Sopenharmony_ci	}
1668c2ecf20Sopenharmony_ci    }
1678c2ecf20Sopenharmony_ci    split(paths[tables[1]], components, "/")
1688c2ecf20Sopenharmony_ci    if (length(components) > 1 && components[1] == table) {
1698c2ecf20Sopenharmony_ci	# Count the first subdirectory as seen
1708c2ecf20Sopenharmony_ci	seen[components[2]]++
1718c2ecf20Sopenharmony_ci    }
1728c2ecf20Sopenharmony_ci}
1738c2ecf20Sopenharmony_ci
1748c2ecf20Sopenharmony_ci
1758c2ecf20Sopenharmony_ciEND {
1768c2ecf20Sopenharmony_ci    for (entry in documented) {
1778c2ecf20Sopenharmony_ci	if (!seen[entry]) {
1788c2ecf20Sopenharmony_ci	    print "No implementation for " entry
1798c2ecf20Sopenharmony_ci	}
1808c2ecf20Sopenharmony_ci    }
1818c2ecf20Sopenharmony_ci}
182