162306a36Sopenharmony_ci#!/usr/bin/gawk -f
262306a36Sopenharmony_ci# SPDX-License-Identifier: GPL-2.0
362306a36Sopenharmony_ci
462306a36Sopenharmony_ci# Script to check sysctl documentation against source files
562306a36Sopenharmony_ci#
662306a36Sopenharmony_ci# Copyright (c) 2020 Stephen Kitt
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci# Example invocation:
962306a36Sopenharmony_ci#	scripts/check-sysctl-docs -vtable="kernel" \
1062306a36Sopenharmony_ci#		Documentation/admin-guide/sysctl/kernel.rst \
1162306a36Sopenharmony_ci#		$(git grep -l register_sysctl_)
1262306a36Sopenharmony_ci#
1362306a36Sopenharmony_ci# Specify -vdebug=1 to see debugging information
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ciBEGIN {
1662306a36Sopenharmony_ci    if (!table) {
1762306a36Sopenharmony_ci	print "Please specify the table to look for using the table variable" > "/dev/stderr"
1862306a36Sopenharmony_ci	exit 1
1962306a36Sopenharmony_ci    }
2062306a36Sopenharmony_ci}
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci# The following globals are used:
2362306a36Sopenharmony_ci# children: maps ctl_table names and procnames to child ctl_table names
2462306a36Sopenharmony_ci# documented: maps documented entries (each key is an entry)
2562306a36Sopenharmony_ci# entries: maps ctl_table names and procnames to counts (so
2662306a36Sopenharmony_ci#          enumerating the subkeys for a given ctl_table lists its
2762306a36Sopenharmony_ci#          procnames)
2862306a36Sopenharmony_ci# files: maps procnames to source file names
2962306a36Sopenharmony_ci# paths: maps ctl_path names to paths
3062306a36Sopenharmony_ci# curpath: the name of the current ctl_path struct
3162306a36Sopenharmony_ci# curtable: the name of the current ctl_table struct
3262306a36Sopenharmony_ci# curentry: the name of the current proc entry (procname when parsing
3362306a36Sopenharmony_ci#           a ctl_table, constructed path when parsing a ctl_path)
3462306a36Sopenharmony_ci
3562306a36Sopenharmony_ci
3662306a36Sopenharmony_ci# Remove punctuation from the given value
3762306a36Sopenharmony_cifunction trimpunct(value) {
3862306a36Sopenharmony_ci    while (value ~ /^["&]/) {
3962306a36Sopenharmony_ci	value = substr(value, 2)
4062306a36Sopenharmony_ci    }
4162306a36Sopenharmony_ci    while (value ~ /[]["&,}]$/) {
4262306a36Sopenharmony_ci	value = substr(value, 1, length(value) - 1)
4362306a36Sopenharmony_ci    }
4462306a36Sopenharmony_ci    return value
4562306a36Sopenharmony_ci}
4662306a36Sopenharmony_ci
4762306a36Sopenharmony_ci# Print the information for the given entry
4862306a36Sopenharmony_cifunction printentry(entry) {
4962306a36Sopenharmony_ci    seen[entry]++
5062306a36Sopenharmony_ci    printf "* %s from %s", entry, file[entry]
5162306a36Sopenharmony_ci    if (documented[entry]) {
5262306a36Sopenharmony_ci	printf " (documented)"
5362306a36Sopenharmony_ci    }
5462306a36Sopenharmony_ci    print ""
5562306a36Sopenharmony_ci}
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci# Stage 1: build the list of documented entries
5962306a36Sopenharmony_ciFNR == NR && /^=+$/ {
6062306a36Sopenharmony_ci    if (prevline ~ /Documentation for/) {
6162306a36Sopenharmony_ci	# This is the main title
6262306a36Sopenharmony_ci	next
6362306a36Sopenharmony_ci    }
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci    # The previous line is a section title, parse it
6662306a36Sopenharmony_ci    $0 = prevline
6762306a36Sopenharmony_ci    if (debug) print "Parsing " $0
6862306a36Sopenharmony_ci    inbrackets = 0
6962306a36Sopenharmony_ci    for (i = 1; i <= NF; i++) {
7062306a36Sopenharmony_ci	if (length($i) == 0) {
7162306a36Sopenharmony_ci	    continue
7262306a36Sopenharmony_ci	}
7362306a36Sopenharmony_ci	if (!inbrackets && substr($i, 1, 1) == "(") {
7462306a36Sopenharmony_ci	    inbrackets = 1
7562306a36Sopenharmony_ci	}
7662306a36Sopenharmony_ci	if (!inbrackets) {
7762306a36Sopenharmony_ci	    token = trimpunct($i)
7862306a36Sopenharmony_ci	    if (length(token) > 0 && token != "and") {
7962306a36Sopenharmony_ci		if (debug) print trimpunct($i)
8062306a36Sopenharmony_ci		documented[trimpunct($i)]++
8162306a36Sopenharmony_ci	    }
8262306a36Sopenharmony_ci	}
8362306a36Sopenharmony_ci	if (inbrackets && substr($i, length($i), 1) == ")") {
8462306a36Sopenharmony_ci	    inbrackets = 0
8562306a36Sopenharmony_ci	}
8662306a36Sopenharmony_ci    }
8762306a36Sopenharmony_ci}
8862306a36Sopenharmony_ci
8962306a36Sopenharmony_ciFNR == NR {
9062306a36Sopenharmony_ci    prevline = $0
9162306a36Sopenharmony_ci    next
9262306a36Sopenharmony_ci}
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci# Stage 2: process each file and find all sysctl tables
9662306a36Sopenharmony_ciBEGINFILE {
9762306a36Sopenharmony_ci    delete children
9862306a36Sopenharmony_ci    delete entries
9962306a36Sopenharmony_ci    delete paths
10062306a36Sopenharmony_ci    curpath = ""
10162306a36Sopenharmony_ci    curtable = ""
10262306a36Sopenharmony_ci    curentry = ""
10362306a36Sopenharmony_ci    if (debug) print "Processing file " FILENAME
10462306a36Sopenharmony_ci}
10562306a36Sopenharmony_ci
10662306a36Sopenharmony_ci/^static struct ctl_path/ {
10762306a36Sopenharmony_ci    match($0, /static struct ctl_path ([^][]+)/, tables)
10862306a36Sopenharmony_ci    curpath = tables[1]
10962306a36Sopenharmony_ci    if (debug) print "Processing path " curpath
11062306a36Sopenharmony_ci}
11162306a36Sopenharmony_ci
11262306a36Sopenharmony_ci/^static struct ctl_table/ {
11362306a36Sopenharmony_ci    match($0, /static struct ctl_table ([^][]+)/, tables)
11462306a36Sopenharmony_ci    curtable = tables[1]
11562306a36Sopenharmony_ci    if (debug) print "Processing table " curtable
11662306a36Sopenharmony_ci}
11762306a36Sopenharmony_ci
11862306a36Sopenharmony_ci/^};$/ {
11962306a36Sopenharmony_ci    curpath = ""
12062306a36Sopenharmony_ci    curtable = ""
12162306a36Sopenharmony_ci    curentry = ""
12262306a36Sopenharmony_ci}
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_cicurpath && /\.procname[\t ]*=[\t ]*".+"/ {
12562306a36Sopenharmony_ci    match($0, /.procname[\t ]*=[\t ]*"([^"]+)"/, names)
12662306a36Sopenharmony_ci    if (curentry) {
12762306a36Sopenharmony_ci	curentry = curentry "/" names[1]
12862306a36Sopenharmony_ci    } else {
12962306a36Sopenharmony_ci	curentry = names[1]
13062306a36Sopenharmony_ci    }
13162306a36Sopenharmony_ci    if (debug) print "Setting path " curpath " to " curentry
13262306a36Sopenharmony_ci    paths[curpath] = curentry
13362306a36Sopenharmony_ci}
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_cicurtable && /\.procname[\t ]*=[\t ]*".+"/ {
13662306a36Sopenharmony_ci    match($0, /.procname[\t ]*=[\t ]*"([^"]+)"/, names)
13762306a36Sopenharmony_ci    curentry = names[1]
13862306a36Sopenharmony_ci    if (debug) print "Adding entry " curentry " to table " curtable
13962306a36Sopenharmony_ci    entries[curtable][curentry]++
14062306a36Sopenharmony_ci    file[curentry] = FILENAME
14162306a36Sopenharmony_ci}
14262306a36Sopenharmony_ci
14362306a36Sopenharmony_ci/\.child[\t ]*=/ {
14462306a36Sopenharmony_ci    child = trimpunct($NF)
14562306a36Sopenharmony_ci    if (debug) print "Linking child " child " to table " curtable " entry " curentry
14662306a36Sopenharmony_ci    children[curtable][curentry] = child
14762306a36Sopenharmony_ci}
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ciEND {
15062306a36Sopenharmony_ci    for (entry in documented) {
15162306a36Sopenharmony_ci	if (!seen[entry]) {
15262306a36Sopenharmony_ci	    print "No implementation for " entry
15362306a36Sopenharmony_ci	}
15462306a36Sopenharmony_ci    }
15562306a36Sopenharmony_ci}
156