155 lines · 3.7 KB · Text
RawDownload
#!/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 valuefunction 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 entryfunction printentry(entry) {    seen[entry]++    printf "* %s from %s", entry, file[entry]    if (documented[entry]) {	printf " (documented)"    }    print ""}  # Stage 1: build the list of documented entriesFNR == 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 tablesBEGINFILE {    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} END {    for (entry in documented) {	if (!seen[entry]) {	    print "No implementation for " entry	}    }}