summaryrefslogtreecommitdiff
path: root/scripts
diff options
context:
space:
mode:
Diffstat (limited to 'scripts')
-rw-r--r--scripts/Makefile.build97
-rw-r--r--scripts/Makefile.compiler2
-rw-r--r--scripts/Makefile.gcc-plugins32
-rw-r--r--scripts/Makefile.kcov9
-rw-r--r--scripts/Makefile.kstack_erase21
-rw-r--r--scripts/Makefile.lib87
-rwxr-xr-xscripts/check-sysctl-docs184
-rwxr-xr-xscripts/checkpatch.pl82
-rwxr-xr-xscripts/checktransupdate.py38
-rw-r--r--scripts/coccinelle/misc/secs_to_jiffies.cocci49
-rw-r--r--scripts/const_structs.checkpatch1
-rwxr-xr-xscripts/crypto/gen-hash-testvecs.py147
-rwxr-xr-xscripts/extract-vmlinux13
-rw-r--r--scripts/gcc-plugins/Kconfig10
-rw-r--r--scripts/gcc-plugins/gcc-common.h77
-rw-r--r--scripts/gcc-plugins/randomize_layout_plugin.c22
-rw-r--r--scripts/gcc-plugins/sancov_plugin.c134
-rw-r--r--scripts/gcc-plugins/stackleak_plugin.c52
-rw-r--r--scripts/gcc-plugins/structleak_plugin.c257
-rw-r--r--scripts/gdb/linux/constants.py.in19
-rw-r--r--scripts/gdb/linux/cpus.py4
-rw-r--r--scripts/gdb/linux/interrupts.py16
-rw-r--r--scripts/gdb/linux/mapletree.py252
-rw-r--r--scripts/gdb/linux/symbols.py64
-rw-r--r--scripts/gdb/linux/utils.py22
-rw-r--r--scripts/gdb/linux/vfs.py2
-rw-r--r--scripts/gdb/linux/xarray.py28
-rw-r--r--scripts/gendwarfksyms/cache.c2
-rw-r--r--scripts/gendwarfksyms/die.c4
-rw-r--r--scripts/gendwarfksyms/dwarf.c16
-rw-r--r--scripts/gendwarfksyms/examples/kabi.h21
-rw-r--r--scripts/gendwarfksyms/examples/kabi_ex.c7
-rw-r--r--scripts/gendwarfksyms/examples/kabi_ex.h101
-rw-r--r--scripts/gendwarfksyms/gendwarfksyms.h16
-rw-r--r--scripts/gendwarfksyms/kabi.c145
-rw-r--r--scripts/gendwarfksyms/symbols.c2
-rw-r--r--scripts/gendwarfksyms/types.c212
-rwxr-xr-xscripts/generate_rust_analyzer.py13
-rw-r--r--scripts/generate_rust_target.rs4
-rw-r--r--scripts/genksyms/genksyms.c27
-rwxr-xr-xscripts/git-resolve.sh201
-rw-r--r--scripts/kconfig/conf.c2
-rw-r--r--scripts/kconfig/confdata.c2
-rw-r--r--scripts/kconfig/expr.h11
-rwxr-xr-xscripts/kconfig/gconf-cfg.sh11
-rw-r--r--scripts/kconfig/gconf.c1687
-rw-r--r--scripts/kconfig/gconf.ui (renamed from scripts/kconfig/gconf.glade)361
-rw-r--r--scripts/kconfig/lkc.h4
-rw-r--r--scripts/kconfig/lxdialog/inputbox.c6
-rw-r--r--scripts/kconfig/lxdialog/menubox.c2
-rw-r--r--scripts/kconfig/lxdialog/util.c3
-rw-r--r--scripts/kconfig/menu.c99
-rw-r--r--scripts/kconfig/nconf.c2
-rw-r--r--scripts/kconfig/nconf.gui.c1
-rw-r--r--scripts/kconfig/parser.y12
-rw-r--r--scripts/kconfig/qconf.cc36
-rw-r--r--scripts/kconfig/qconf.h1
-rw-r--r--scripts/kconfig/symbol.c4
-rwxr-xr-xscripts/kernel-doc.py10
-rw-r--r--scripts/lib/kdoc/kdoc_files.py4
-rw-r--r--scripts/lib/kdoc/kdoc_item.py42
-rw-r--r--scripts/lib/kdoc/kdoc_output.py172
-rw-r--r--scripts/lib/kdoc/kdoc_parser.py858
-rw-r--r--scripts/lib/kdoc/kdoc_re.py7
-rwxr-xr-xscripts/min-tool-version.sh6
-rwxr-xr-xscripts/misc-check77
-rw-r--r--scripts/mod/modpost.c56
-rw-r--r--scripts/module.lds.S5
-rwxr-xr-xscripts/recordmcount.pl2
-rw-r--r--scripts/rustdoc_test_builder.rs8
-rw-r--r--scripts/rustdoc_test_gen.rs49
-rw-r--r--scripts/spelling.txt3
-rwxr-xr-xscripts/sphinx-pre-install6
-rw-r--r--scripts/syscall.tbl2
-rwxr-xr-xscripts/tags.sh2
-rwxr-xr-xscripts/test_doc_build.py513
-rwxr-xr-xscripts/ver_linux2
77 files changed, 3858 insertions, 2702 deletions
diff --git a/scripts/Makefile.build b/scripts/Makefile.build
index 884dc86ce04e..d0ee33a487be 100644
--- a/scripts/Makefile.build
+++ b/scripts/Makefile.build
@@ -37,6 +37,90 @@ include $(srctree)/scripts/Makefile.compiler
include $(kbuild-file)
include $(srctree)/scripts/Makefile.lib
+# flags that take effect in current and sub directories
+KBUILD_AFLAGS += $(subdir-asflags-y)
+KBUILD_CFLAGS += $(subdir-ccflags-y)
+KBUILD_RUSTFLAGS += $(subdir-rustflags-y)
+
+# Figure out what we need to build from the various variables
+# ===========================================================================
+
+# When an object is listed to be built compiled-in and modular,
+# only build the compiled-in version
+obj-m := $(filter-out $(obj-y),$(obj-m))
+
+# Libraries are always collected in one lib file.
+# Filter out objects already built-in
+lib-y := $(filter-out $(obj-y), $(sort $(lib-y) $(lib-m)))
+
+# Subdirectories we need to descend into
+subdir-ym := $(sort $(subdir-y) $(subdir-m) \
+ $(patsubst %/,%, $(filter %/, $(obj-y) $(obj-m))))
+
+# Handle objects in subdirs:
+# - If we encounter foo/ in $(obj-y), replace it by foo/built-in.a and
+# foo/modules.order
+# - If we encounter foo/ in $(obj-m), replace it by foo/modules.order
+#
+# Generate modules.order to determine modorder. Unfortunately, we don't have
+# information about ordering between -y and -m subdirs. Just put -y's first.
+
+ifdef need-modorder
+obj-m := $(patsubst %/,%/modules.order, $(filter %/, $(obj-y)) $(obj-m))
+else
+obj-m := $(filter-out %/, $(obj-m))
+endif
+
+ifdef need-builtin
+obj-y := $(patsubst %/, %/built-in.a, $(obj-y))
+else
+obj-y := $(filter-out %/, $(obj-y))
+endif
+
+# Expand $(foo-objs) $(foo-y) etc. by replacing their individuals
+suffix-search = $(strip $(foreach s, $3, $($(1:%$(strip $2)=%$s))))
+# List composite targets that are constructed by combining other targets
+multi-search = $(sort $(foreach m, $1, $(if $(call suffix-search, $m, $2, $3 -), $m)))
+# List primitive targets that are compiled from source files
+real-search = $(foreach m, $1, $(if $(call suffix-search, $m, $2, $3 -), $(call suffix-search, $m, $2, $3), $m))
+
+# If $(foo-objs), $(foo-y), $(foo-m), or $(foo-) exists, foo.o is a composite object
+multi-obj-y := $(call multi-search, $(obj-y), .o, -objs -y)
+multi-obj-m := $(call multi-search, $(obj-m), .o, -objs -y -m)
+multi-obj-ym := $(multi-obj-y) $(multi-obj-m)
+
+# Replace multi-part objects by their individual parts,
+# including built-in.a from subdirectories
+real-obj-y := $(call real-search, $(obj-y), .o, -objs -y)
+real-obj-m := $(call real-search, $(obj-m), .o, -objs -y -m)
+
+always-y += $(always-m)
+
+# hostprogs-always-y += foo
+# ... is a shorthand for
+# hostprogs += foo
+# always-y += foo
+hostprogs += $(hostprogs-always-y) $(hostprogs-always-m)
+always-y += $(hostprogs-always-y) $(hostprogs-always-m)
+
+# userprogs-always-y is likewise.
+userprogs += $(userprogs-always-y) $(userprogs-always-m)
+always-y += $(userprogs-always-y) $(userprogs-always-m)
+
+# Add subdir path
+
+ifneq ($(obj),.)
+extra-y := $(addprefix $(obj)/, $(extra-y))
+always-y := $(addprefix $(obj)/, $(always-y))
+targets := $(addprefix $(obj)/, $(targets))
+obj-m := $(addprefix $(obj)/, $(obj-m))
+lib-y := $(addprefix $(obj)/, $(lib-y))
+real-obj-y := $(addprefix $(obj)/, $(real-obj-y))
+real-obj-m := $(addprefix $(obj)/, $(real-obj-m))
+multi-obj-m := $(addprefix $(obj)/, $(multi-obj-m))
+subdir-ym := $(addprefix $(obj)/, $(subdir-ym))
+endif
+
ifndef obj
$(warning kbuild: Makefile.build is included improperly)
endif
@@ -222,7 +306,18 @@ $(obj)/%.lst: $(obj)/%.c FORCE
# Compile Rust sources (.rs)
# ---------------------------------------------------------------------------
-rust_allowed_features := asm_const,asm_goto,arbitrary_self_types,lint_reasons,raw_ref_op
+# The features in this list are the ones allowed for non-`rust/` code.
+#
+# - Stable since Rust 1.81.0: `feature(lint_reasons)`.
+# - Stable since Rust 1.82.0: `feature(asm_const)`,
+# `feature(offset_of_nested)`, `feature(raw_ref_op)`.
+# - Stable since Rust 1.87.0: `feature(asm_goto)`.
+# - Expected to become stable: `feature(arbitrary_self_types)`.
+# - To be determined: `feature(used_with_arg)`.
+#
+# Please see https://github.com/Rust-for-Linux/linux/issues/2 for details on
+# the unstable features in use.
+rust_allowed_features := asm_const,asm_goto,arbitrary_self_types,lint_reasons,offset_of_nested,raw_ref_op,used_with_arg
# `--out-dir` is required to avoid temporaries being created by `rustc` in the
# current working directory, which may be not accessible in the out-of-tree
diff --git a/scripts/Makefile.compiler b/scripts/Makefile.compiler
index 65cfa72e376b..ef91910de265 100644
--- a/scripts/Makefile.compiler
+++ b/scripts/Makefile.compiler
@@ -60,7 +60,7 @@ cc-option-yn = $(if $(call cc-option,$1),y,n)
cc-disable-warning = $(call cc-option,-Wno-$(strip $1))
# gcc-min-version
-# Usage: cflags-$(call gcc-min-version, 70100) += -foo
+# Usage: cflags-$(call gcc-min-version, 110100) += -foo
gcc-min-version = $(call test-ge, $(CONFIG_GCC_VERSION), $1)
# clang-min-version
diff --git a/scripts/Makefile.gcc-plugins b/scripts/Makefile.gcc-plugins
index e50dc931be49..b0e1423b09c2 100644
--- a/scripts/Makefile.gcc-plugins
+++ b/scripts/Makefile.gcc-plugins
@@ -8,34 +8,6 @@ ifdef CONFIG_GCC_PLUGIN_LATENT_ENTROPY
endif
export DISABLE_LATENT_ENTROPY_PLUGIN
-gcc-plugin-$(CONFIG_GCC_PLUGIN_STRUCTLEAK) += structleak_plugin.so
-gcc-plugin-cflags-$(CONFIG_GCC_PLUGIN_STRUCTLEAK_VERBOSE) \
- += -fplugin-arg-structleak_plugin-verbose
-gcc-plugin-cflags-$(CONFIG_GCC_PLUGIN_STRUCTLEAK_BYREF) \
- += -fplugin-arg-structleak_plugin-byref
-gcc-plugin-cflags-$(CONFIG_GCC_PLUGIN_STRUCTLEAK_BYREF_ALL) \
- += -fplugin-arg-structleak_plugin-byref-all
-ifdef CONFIG_GCC_PLUGIN_STRUCTLEAK
- DISABLE_STRUCTLEAK_PLUGIN += -fplugin-arg-structleak_plugin-disable
-endif
-export DISABLE_STRUCTLEAK_PLUGIN
-gcc-plugin-cflags-$(CONFIG_GCC_PLUGIN_STRUCTLEAK) \
- += -DSTRUCTLEAK_PLUGIN
-
-gcc-plugin-$(CONFIG_GCC_PLUGIN_STACKLEAK) += stackleak_plugin.so
-gcc-plugin-cflags-$(CONFIG_GCC_PLUGIN_STACKLEAK) \
- += -DSTACKLEAK_PLUGIN
-gcc-plugin-cflags-$(CONFIG_GCC_PLUGIN_STACKLEAK) \
- += -fplugin-arg-stackleak_plugin-track-min-size=$(CONFIG_STACKLEAK_TRACK_MIN_SIZE)
-gcc-plugin-cflags-$(CONFIG_GCC_PLUGIN_STACKLEAK) \
- += -fplugin-arg-stackleak_plugin-arch=$(SRCARCH)
-gcc-plugin-cflags-$(CONFIG_GCC_PLUGIN_STACKLEAK_VERBOSE) \
- += -fplugin-arg-stackleak_plugin-verbose
-ifdef CONFIG_GCC_PLUGIN_STACKLEAK
- DISABLE_STACKLEAK_PLUGIN += -fplugin-arg-stackleak_plugin-disable
-endif
-export DISABLE_STACKLEAK_PLUGIN
-
# All the plugin CFLAGS are collected here in case a build target needs to
# filter them out of the KBUILD_CFLAGS.
GCC_PLUGINS_CFLAGS := $(strip $(addprefix -fplugin=$(objtree)/scripts/gcc-plugins/, $(gcc-plugin-y)) $(gcc-plugin-cflags-y)) -DGCC_PLUGINS
@@ -46,10 +18,10 @@ KBUILD_CFLAGS += $(GCC_PLUGINS_CFLAGS)
# Some plugins are enabled outside of this Makefile, but they still need to
# be included in GCC_PLUGIN so they can get built.
-gcc-plugin-external-$(CONFIG_GCC_PLUGIN_SANCOV) \
- += sancov_plugin.so
gcc-plugin-external-$(CONFIG_GCC_PLUGIN_RANDSTRUCT) \
+= randomize_layout_plugin.so
+gcc-plugin-external-$(CONFIG_GCC_PLUGIN_STACKLEAK) \
+ += stackleak_plugin.so
# All enabled GCC plugins are collected here for building in
# scripts/gcc-scripts/Makefile.
diff --git a/scripts/Makefile.kcov b/scripts/Makefile.kcov
index 67e8cfe3474b..78305a84ba9d 100644
--- a/scripts/Makefile.kcov
+++ b/scripts/Makefile.kcov
@@ -1,6 +1,11 @@
# SPDX-License-Identifier: GPL-2.0-only
-kcov-flags-$(CONFIG_CC_HAS_SANCOV_TRACE_PC) += -fsanitize-coverage=trace-pc
+kcov-flags-y += -fsanitize-coverage=trace-pc
kcov-flags-$(CONFIG_KCOV_ENABLE_COMPARISONS) += -fsanitize-coverage=trace-cmp
-kcov-flags-$(CONFIG_GCC_PLUGIN_SANCOV) += -fplugin=$(objtree)/scripts/gcc-plugins/sancov_plugin.so
+
+kcov-rflags-y += -Cpasses=sancov-module
+kcov-rflags-y += -Cllvm-args=-sanitizer-coverage-level=3
+kcov-rflags-y += -Cllvm-args=-sanitizer-coverage-trace-pc
+kcov-rflags-$(CONFIG_KCOV_ENABLE_COMPARISONS) += -Cllvm-args=-sanitizer-coverage-trace-compares
export CFLAGS_KCOV := $(kcov-flags-y)
+export RUSTFLAGS_KCOV := $(kcov-rflags-y)
diff --git a/scripts/Makefile.kstack_erase b/scripts/Makefile.kstack_erase
new file mode 100644
index 000000000000..ee7e4ef7b892
--- /dev/null
+++ b/scripts/Makefile.kstack_erase
@@ -0,0 +1,21 @@
+# SPDX-License-Identifier: GPL-2.0
+
+ifdef CONFIG_GCC_PLUGIN_STACKLEAK
+kstack-erase-cflags-y += -fplugin=$(objtree)/scripts/gcc-plugins/stackleak_plugin.so
+kstack-erase-cflags-y += -fplugin-arg-stackleak_plugin-track-min-size=$(CONFIG_KSTACK_ERASE_TRACK_MIN_SIZE)
+kstack-erase-cflags-y += -fplugin-arg-stackleak_plugin-arch=$(SRCARCH)
+kstack-erase-cflags-$(CONFIG_GCC_PLUGIN_STACKLEAK_VERBOSE) += -fplugin-arg-stackleak_plugin-verbose
+DISABLE_KSTACK_ERASE := -fplugin-arg-stackleak_plugin-disable
+endif
+
+ifdef CONFIG_CC_IS_CLANG
+kstack-erase-cflags-y += -fsanitize-coverage=stack-depth
+kstack-erase-cflags-y += -fsanitize-coverage-stack-depth-callback-min=$(CONFIG_KSTACK_ERASE_TRACK_MIN_SIZE)
+DISABLE_KSTACK_ERASE := -fno-sanitize-coverage=stack-depth
+endif
+
+KSTACK_ERASE_CFLAGS := $(kstack-erase-cflags-y)
+
+export KSTACK_ERASE_CFLAGS DISABLE_KSTACK_ERASE
+
+KBUILD_CFLAGS += $(KSTACK_ERASE_CFLAGS)
diff --git a/scripts/Makefile.lib b/scripts/Makefile.lib
index 6fc2a82ee3bb..1d581ba5df66 100644
--- a/scripts/Makefile.lib
+++ b/scripts/Makefile.lib
@@ -1,89 +1,5 @@
# SPDX-License-Identifier: GPL-2.0
-# flags that take effect in current and sub directories
-KBUILD_AFLAGS += $(subdir-asflags-y)
-KBUILD_CFLAGS += $(subdir-ccflags-y)
-KBUILD_RUSTFLAGS += $(subdir-rustflags-y)
-
-# Figure out what we need to build from the various variables
-# ===========================================================================
-
-# When an object is listed to be built compiled-in and modular,
-# only build the compiled-in version
-obj-m := $(filter-out $(obj-y),$(obj-m))
-
-# Libraries are always collected in one lib file.
-# Filter out objects already built-in
-lib-y := $(filter-out $(obj-y), $(sort $(lib-y) $(lib-m)))
-
-# Subdirectories we need to descend into
-subdir-ym := $(sort $(subdir-y) $(subdir-m) \
- $(patsubst %/,%, $(filter %/, $(obj-y) $(obj-m))))
-
-# Handle objects in subdirs:
-# - If we encounter foo/ in $(obj-y), replace it by foo/built-in.a and
-# foo/modules.order
-# - If we encounter foo/ in $(obj-m), replace it by foo/modules.order
-#
-# Generate modules.order to determine modorder. Unfortunately, we don't have
-# information about ordering between -y and -m subdirs. Just put -y's first.
-
-ifdef need-modorder
-obj-m := $(patsubst %/,%/modules.order, $(filter %/, $(obj-y)) $(obj-m))
-else
-obj-m := $(filter-out %/, $(obj-m))
-endif
-
-ifdef need-builtin
-obj-y := $(patsubst %/, %/built-in.a, $(obj-y))
-else
-obj-y := $(filter-out %/, $(obj-y))
-endif
-
-# Expand $(foo-objs) $(foo-y) etc. by replacing their individuals
-suffix-search = $(strip $(foreach s, $3, $($(1:%$(strip $2)=%$s))))
-# List composite targets that are constructed by combining other targets
-multi-search = $(sort $(foreach m, $1, $(if $(call suffix-search, $m, $2, $3 -), $m)))
-# List primitive targets that are compiled from source files
-real-search = $(foreach m, $1, $(if $(call suffix-search, $m, $2, $3 -), $(call suffix-search, $m, $2, $3), $m))
-
-# If $(foo-objs), $(foo-y), $(foo-m), or $(foo-) exists, foo.o is a composite object
-multi-obj-y := $(call multi-search, $(obj-y), .o, -objs -y)
-multi-obj-m := $(call multi-search, $(obj-m), .o, -objs -y -m)
-multi-obj-ym := $(multi-obj-y) $(multi-obj-m)
-
-# Replace multi-part objects by their individual parts,
-# including built-in.a from subdirectories
-real-obj-y := $(call real-search, $(obj-y), .o, -objs -y)
-real-obj-m := $(call real-search, $(obj-m), .o, -objs -y -m)
-
-always-y += $(always-m)
-
-# hostprogs-always-y += foo
-# ... is a shorthand for
-# hostprogs += foo
-# always-y += foo
-hostprogs += $(hostprogs-always-y) $(hostprogs-always-m)
-always-y += $(hostprogs-always-y) $(hostprogs-always-m)
-
-# userprogs-always-y is likewise.
-userprogs += $(userprogs-always-y) $(userprogs-always-m)
-always-y += $(userprogs-always-y) $(userprogs-always-m)
-
-# Add subdir path
-
-ifneq ($(obj),.)
-extra-y := $(addprefix $(obj)/,$(extra-y))
-always-y := $(addprefix $(obj)/,$(always-y))
-targets := $(addprefix $(obj)/,$(targets))
-obj-m := $(addprefix $(obj)/,$(obj-m))
-lib-y := $(addprefix $(obj)/,$(lib-y))
-real-obj-y := $(addprefix $(obj)/,$(real-obj-y))
-real-obj-m := $(addprefix $(obj)/,$(real-obj-m))
-multi-obj-m := $(addprefix $(obj)/, $(multi-obj-m))
-subdir-ym := $(addprefix $(obj)/,$(subdir-ym))
-endif
-
# Finds the multi-part object the current object will be linked into.
# If the object belongs to two or more multi-part objects, list them all.
modname-multi = $(sort $(foreach m,$(multi-obj-ym),\
@@ -169,6 +85,9 @@ ifeq ($(CONFIG_KCOV),y)
_c_flags += $(if $(patsubst n%,, \
$(KCOV_INSTRUMENT_$(target-stem).o)$(KCOV_INSTRUMENT)$(if $(is-kernel-object),$(CONFIG_KCOV_INSTRUMENT_ALL))), \
$(CFLAGS_KCOV))
+_rust_flags += $(if $(patsubst n%,, \
+ $(KCOV_INSTRUMENT_$(target-stem).o)$(KCOV_INSTRUMENT)$(if $(is-kernel-object),$(CONFIG_KCOV_INSTRUMENT_ALL))), \
+ $(RUSTFLAGS_KCOV))
endif
#
diff --git a/scripts/check-sysctl-docs b/scripts/check-sysctl-docs
index 20274c63e745..910fd8a9a268 100755
--- a/scripts/check-sysctl-docs
+++ b/scripts/check-sysctl-docs
@@ -1,4 +1,4 @@
-#!/usr/bin/gawk -f
+#!/usr/bin/env -S gawk -f
# SPDX-License-Identifier: GPL-2.0
# Script to check sysctl documentation against source files
@@ -13,10 +13,22 @@
# Specify -vdebug=1 to see debugging information
BEGIN {
- if (!table) {
+ if (!table) {
print "Please specify the table to look for using the table variable" > "/dev/stderr"
exit 1
- }
+ }
+
+ # Documentation title skiplist
+ skiplist[0] = "^Documentation for"
+ skiplist[1] = "Network core options$"
+ skiplist[2] = "POSIX message queues filesystem$"
+ skiplist[3] = "Configuration options"
+ skiplist[4] = ". /proc/sys/fs"
+ skiplist[5] = "^Introduction$"
+ skiplist[6] = "^seccomp$"
+ skiplist[7] = "^pty$"
+ skiplist[8] = "^firmware_config$"
+ skiplist[9] = "^random$"
}
# The following globals are used:
@@ -31,124 +43,132 @@ BEGIN {
# Remove punctuation from the given value
function trimpunct(value) {
- while (value ~ /^["&]/) {
- value = substr(value, 2)
- }
- while (value ~ /[]["&,}]$/) {
- value = substr(value, 1, length(value) - 1)
- }
- return value
+ while (value ~ /^["&]/) {
+ value = substr(value, 2)
+ }
+ while (value ~ /[]["&,}]$/) {
+ value = substr(value, 1, length(value) - 1)
+ }
+ return value
}
# Print the information for the given entry
function printentry(entry) {
- seen[entry]++
- printf "* %s from %s", entry, file[entry]
- if (documented[entry]) {
- printf " (documented)"
- }
- print ""
+ seen[entry]++
+ printf "* %s from %s", entry, file[entry]
+ if (documented[entry]) {
+ printf " (documented)"
+ }
+ print ""
}
# Stage 1: build the list of documented entries
FNR == 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)]++
- }
+ for (i in skiplist) {
+ if (prevline ~ skiplist[i]) {
+ next
+ }
}
- if (inbrackets && substr($i, length($i), 1) == ")") {
- inbrackets = 0
+
+ # 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
+ prevline = $0
+ next
}
# Stage 2: process each file and find all sysctl tables
BEGINFILE {
- delete entries
- curtable = ""
- curentry = ""
- delete vars
- if (debug) print "Processing file " FILENAME
+ delete entries
+ curtable = ""
+ curentry = ""
+ delete vars
+ if (debug) print "Processing file " FILENAME
}
/^static( const)? struct ctl_table/ {
- match($0, /static( const)? struct ctl_table ([^][]+)/, tables)
- curtable = tables[2]
- if (debug) print "Processing table " curtable
+ match($0, /static( const)? struct ctl_table ([^][]+)/, tables)
+ curtable = tables[2]
+ if (debug) print "Processing table " curtable
}
/^};$/ {
- curtable = ""
- curentry = ""
- delete vars
+ curtable = ""
+ curentry = ""
+ delete vars
}
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
+ match($0, /.procname[\t ]*=[\t ]*"([^"]+)"/, names)
+ curentry = names[1]
+ if (debug) print "Adding entry " curentry " to table " curtable
+ entries[curtable][curentry]++
+ file[curentry] = FILENAME
+}
+
+curtable && /UCOUNT_ENTRY.*/ {
+ match($0, /UCOUNT_ENTRY\("([^"]+)"\)/, names)
+ curentry = names[1]
+ if (debug) print "Adding entry " curentry " to table " curtable
+ entries[curtable][curentry]++
+ file[curentry] = FILENAME
}
/register_sysctl.*/ {
- match($0, /register_sysctl(|_init|_sz)\("([^"]+)" *, *([^,)]+)/, tables)
- if (debug) print "Registering table " tables[3] " at " tables[2]
- if (tables[2] == table) {
- for (entry in entries[tables[3]]) {
- printentry(entry)
- }
- }
+ match($0, /register_sysctl(|_init|_sz)\("([^"]+)" *, *([^,)]+)/, tables)
+ if (debug) print "Registering table " tables[3] " at " tables[2]
+ if (tables[2] == table) {
+ for (entry in entries[tables[3]]) {
+ printentry(entry)
+ }
+ }
}
/kmemdup.*/ {
- match($0, /([^ \t]+) *= *kmemdup\(([^,]+) *,/, names)
- if (debug) print "Found variable " names[1] " for table " names[2]
- if (names[2] in entries) {
- vars[names[1]] = names[2]
- }
+ match($0, /([^ \t]+) *= *kmemdup\(([^,]+) *,/, names)
+ if (debug) print "Found variable " names[1] " for table " names[2]
+ if (names[2] in entries) {
+ vars[names[1]] = names[2]
+ }
}
/__register_sysctl_table.*/ {
- match($0, /__register_sysctl_table\([^,]+, *"([^"]+)" *, *([^,]+)/, tables)
- if (debug) print "Registering variable table " tables[2] " at " tables[1]
- if (tables[1] == table && tables[2] in vars) {
- for (entry in entries[vars[tables[2]]]) {
- printentry(entry)
- }
- }
+ match($0, /__register_sysctl_table\([^,]+, *"([^"]+)" *, *([^,]+)/, tables)
+ if (debug) print "Registering variable table " tables[2] " at " tables[1]
+ if (tables[1] == table && tables[2] in vars) {
+ for (entry in entries[vars[tables[2]]]) {
+ printentry(entry)
+ }
+ }
}
END {
- for (entry in documented) {
- if (!seen[entry]) {
- print "No implementation for " entry
+ for (entry in documented) {
+ if (!seen[entry])
+ print "No implementation for " entry
}
- }
}
diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl
index 966813c2573c..e722dd6fa8ef 100755
--- a/scripts/checkpatch.pl
+++ b/scripts/checkpatch.pl
@@ -151,6 +151,24 @@ EOM
exit($exitcode);
}
+my $DO_WHILE_0_ADVICE = q{
+ do {} while (0) advice is over-stated in a few situations:
+
+ The more obvious case is macros, like MODULE_PARM_DESC, invoked at
+ file-scope, where C disallows code (it must be in functions). See
+ $exceptions if you have one to add by name.
+
+ More troublesome is declarative macros used at top of new scope,
+ like DECLARE_PER_CPU. These might just compile with a do-while-0
+ wrapper, but would be incorrect. Most of these are handled by
+ detecting struct,union,etc declaration primitives in $exceptions.
+
+ Theres also macros called inside an if (block), which "return" an
+ expression. These cannot do-while, and need a ({}) wrapper.
+
+ Enjoy this qualification while we work to improve our heuristics.
+};
+
sub uniq {
my %seen;
return grep { !$seen{$_}++ } @_;
@@ -667,6 +685,9 @@ our $tracing_logging_tags = qr{(?xi:
[\.\!:\s]*
)};
+# Device ID types like found in include/linux/mod_devicetable.h.
+our $dev_id_types = qr{\b[a-z]\w*_device_id\b};
+
sub edit_distance_min {
my (@arr) = @_;
my $len = scalar @arr;
@@ -839,8 +860,6 @@ our %deprecated_apis = (
"kunmap" => "kunmap_local",
"kmap_atomic" => "kmap_local_page",
"kunmap_atomic" => "kunmap_local",
- "srcu_read_lock_lite" => "srcu_read_lock_fast",
- "srcu_read_unlock_lite" => "srcu_read_unlock_fast",
);
#Create a search pattern for all these strings to speed up a loop below
@@ -3484,9 +3503,10 @@ sub process {
# Check for various typo / spelling mistakes
if (defined($misspellings) &&
($in_commit_log || $line =~ /^(?:\+|Subject:)/i)) {
- while ($rawline =~ /(?:^|[^\w\-'`])($misspellings)(?:[^\w\-'`]|$)/gi) {
+ my $rawline_utf8 = decode("utf8", $rawline);
+ while ($rawline_utf8 =~ /(?:^|[^\w\-'`])($misspellings)(?:[^\w\-'`]|$)/gi) {
my $typo = $1;
- my $blank = copy_spacing($rawline);
+ my $blank = copy_spacing($rawline_utf8);
my $ptr = substr($blank, 0, $-[1]) . "^" x length($typo);
my $hereptr = "$hereline$ptr\n";
my $typo_fix = $spelling_fix{lc($typo)};
@@ -3723,6 +3743,18 @@ sub process {
}
}
+# Check for RGMII phy-mode with delay on PCB
+ if ($realfile =~ /\.(dts|dtsi|dtso)$/ &&
+ $line =~ /^\+\s*(phy-mode|phy-connection-type)\s*=\s*"/ &&
+ !ctx_has_comment($first_line, $linenr)) {
+ my $prop = $1;
+ my $mode = get_quoted_string($line, $rawline);
+ if ($mode =~ /^"rgmii(?:|-rxid|-txid)"$/) {
+ WARN("UNCOMMENTED_RGMII_MODE",
+ "$prop $mode without comment -- delays on the PCB should be described, otherwise use \"rgmii-id\"\n" . $herecurr);
+ }
+ }
+
# check for using SPDX license tag at beginning of files
if ($realline == $checklicenseline) {
if ($rawline =~ /^[ \+]\s*\#\!\s*\//) {
@@ -5885,9 +5917,9 @@ sub process {
}
}
-# multi-statement macros should be enclosed in a do while loop, grab the
-# first statement and ensure its the whole macro if its not enclosed
-# in a known good container
+# Usually multi-statement macros should be enclosed in a do {} while
+# (0) loop. Grab the first statement and ensure its the whole macro
+# if its not enclosed in a known good container
if ($realfile !~ m@/vmlinux.lds.h$@ &&
$line =~ /^.\s*\#\s*define\s*$Ident(\()?/) {
my $ln = $linenr;
@@ -5940,10 +5972,13 @@ sub process {
my $exceptions = qr{
$Declare|
+ # named exceptions
module_param_named|
MODULE_PARM_DESC|
DECLARE_PER_CPU|
DEFINE_PER_CPU|
+ static_assert|
+ # declaration primitives
__typeof__\(|
union|
struct|
@@ -5978,11 +6013,11 @@ sub process {
ERROR("MULTISTATEMENT_MACRO_USE_DO_WHILE",
"Macros starting with if should be enclosed by a do - while loop to avoid possible if/else logic defects\n" . "$herectx");
} elsif ($dstat =~ /;/) {
- ERROR("MULTISTATEMENT_MACRO_USE_DO_WHILE",
- "Macros with multiple statements should be enclosed in a do - while loop\n" . "$herectx");
+ WARN("MULTISTATEMENT_MACRO_USE_DO_WHILE",
+ "Non-declarative macros with multiple statements should be enclosed in a do - while loop\n" . "$herectx\nBUT SEE:\n$DO_WHILE_0_ADVICE");
} else {
ERROR("COMPLEX_MACRO",
- "Macros with complex values should be enclosed in parentheses\n" . "$herectx");
+ "Macros with complex values should be enclosed in parentheses\n" . "$herectx\nBUT SEE:\n$DO_WHILE_0_ADVICE");
}
}
@@ -6026,7 +6061,7 @@ sub process {
}
# check if this is an unused argument
- if ($define_stmt !~ /\b$arg\b/) {
+ if ($define_stmt !~ /\b$arg\b/ && $define_stmt) {
WARN("MACRO_ARG_UNUSED",
"Argument '$arg' is not used in function-like macro\n" . "$herectx");
}
@@ -7657,6 +7692,31 @@ sub process {
WARN("DUPLICATED_SYSCTL_CONST",
"duplicated sysctl range checking value '$1', consider using the shared one in include/linux/sysctl.h\n" . $herecurr);
}
+
+# Check that *_device_id tables have sentinel entries.
+ if (defined $stat && $line =~ /struct\s+$dev_id_types\s+\w+\s*\[\s*\]\s*=\s*\{/) {
+ my $stripped = $stat;
+
+ # Strip diff line prefixes.
+ $stripped =~ s/(^|\n)./$1/g;
+ # Line continuations.
+ $stripped =~ s/\\\n/\n/g;
+ # Strip whitespace, empty strings, zeroes, and commas.
+ $stripped =~ s/""//g;
+ $stripped =~ s/0x0//g;
+ $stripped =~ s/[\s$;,0]//g;
+ # Strip field assignments.
+ $stripped =~ s/\.$Ident=//g;
+
+ if (!(substr($stripped, -4) eq "{}};" ||
+ substr($stripped, -6) eq "{{}}};" ||
+ $stripped =~ /ISAPNP_DEVICE_SINGLE_END}};$/ ||
+ $stripped =~ /ISAPNP_CARD_END}};$/ ||
+ $stripped =~ /NULL};$/ ||
+ $stripped =~ /PCMCIA_DEVICE_NULL};$/)) {
+ ERROR("MISSING_SENTINEL", "missing sentinel in ID array\n" . "$here\n$stat\n");
+ }
+ }
}
# If we have no input at all, then there is nothing to report on
diff --git a/scripts/checktransupdate.py b/scripts/checktransupdate.py
index 578c3fecfdfd..e39529e46c3d 100755
--- a/scripts/checktransupdate.py
+++ b/scripts/checktransupdate.py
@@ -24,6 +24,7 @@ commit 42fb9cfd5b18 ("Documentation: dev-tools: Add link to RV docs")
"""
import os
+import re
import time
import logging
from argparse import ArgumentParser, ArgumentTypeError, BooleanOptionalAction
@@ -69,6 +70,38 @@ def get_origin_from_trans(origin_path, t_from_head):
return o_from_t
+def get_origin_from_trans_smartly(origin_path, t_from_head):
+ """Get the latest origin commit from the formatted translation commit:
+ (1) update to commit HASH (TITLE)
+ (2) Update the translation through commit HASH (TITLE)
+ """
+ # catch flag for 12-bit commit hash
+ HASH = r'([0-9a-f]{12})'
+ # pattern 1: contains "update to commit HASH"
+ pat_update_to = re.compile(rf'update to commit {HASH}')
+ # pattern 2: contains "Update the translation through commit HASH"
+ pat_update_translation = re.compile(rf'Update the translation through commit {HASH}')
+
+ origin_commit_hash = None
+ for line in t_from_head["message"]:
+ # check if the line matches the first pattern
+ match = pat_update_to.search(line)
+ if match:
+ origin_commit_hash = match.group(1)
+ break
+ # check if the line matches the second pattern
+ match = pat_update_translation.search(line)
+ if match:
+ origin_commit_hash = match.group(1)
+ break
+ if origin_commit_hash is None:
+ return None
+ o_from_t = get_latest_commit_from(origin_path, origin_commit_hash)
+ if o_from_t is not None:
+ logging.debug("tracked origin commit id: %s", o_from_t["hash"])
+ return o_from_t
+
+
def get_commits_count_between(opath, commit1, commit2):
"""Get the commits count between two commits for the specified file"""
command = f"git log --pretty=format:%H {commit1}...{commit2} -- {opath}"
@@ -108,7 +141,10 @@ def check_per_file(file_path):
logging.error("Cannot find the latest commit for %s", file_path)
return
- o_from_t = get_origin_from_trans(opath, t_from_head)
+ o_from_t = get_origin_from_trans_smartly(opath, t_from_head)
+ # notice, o_from_t from get_*_smartly() is always more accurate than from get_*()
+ if o_from_t is None:
+ o_from_t = get_origin_from_trans(opath, t_from_head)
if o_from_t is None:
logging.error("Error: Cannot find the latest origin commit for %s", file_path)
diff --git a/scripts/coccinelle/misc/secs_to_jiffies.cocci b/scripts/coccinelle/misc/secs_to_jiffies.cocci
index 416f348174ca..f3241ce75a7b 100644
--- a/scripts/coccinelle/misc/secs_to_jiffies.cocci
+++ b/scripts/coccinelle/misc/secs_to_jiffies.cocci
@@ -7,26 +7,65 @@
// Confidence: High
// Copyright: (C) 2024 Easwar Hariharan, Microsoft
// Keywords: secs, seconds, jiffies
-//
+// Options: --include-headers
virtual patch
+virtual report
+virtual context
-@depends on patch@ constant C; @@
+@pconst depends on patch@ constant C; @@
- msecs_to_jiffies(C * 1000)
+ secs_to_jiffies(C)
-@depends on patch@ constant C; @@
+@pconstms depends on patch@ constant C; @@
- msecs_to_jiffies(C * MSEC_PER_SEC)
+ secs_to_jiffies(C)
-@depends on patch@ expression E; @@
+@pexpr depends on patch@ expression E; @@
- msecs_to_jiffies(E * 1000)
+ secs_to_jiffies(E)
-@depends on patch@ expression E; @@
+@pexprms depends on patch@ expression E; @@
- msecs_to_jiffies(E * MSEC_PER_SEC)
+ secs_to_jiffies(E)
+
+@r depends on report && !patch@
+constant C;
+expression E;
+position p;
+@@
+
+(
+ msecs_to_jiffies(C@p * 1000)
+|
+ msecs_to_jiffies(C@p * MSEC_PER_SEC)
+|
+ msecs_to_jiffies(E@p * 1000)
+|
+ msecs_to_jiffies(E@p * MSEC_PER_SEC)
+)
+
+@c depends on context && !patch@
+constant C;
+expression E;
+@@
+
+(
+* msecs_to_jiffies(C * 1000)
+|
+* msecs_to_jiffies(C * MSEC_PER_SEC)
+|
+* msecs_to_jiffies(E * 1000)
+|
+* msecs_to_jiffies(E * MSEC_PER_SEC)
+)
+
+@script:python depends on report@
+p << r.p;
+@@
+
+coccilib.report.print_report(p[0], "WARNING opportunity for secs_to_jiffies()")
diff --git a/scripts/const_structs.checkpatch b/scripts/const_structs.checkpatch
index e8609a03c3d8..6eb94fddc338 100644
--- a/scripts/const_structs.checkpatch
+++ b/scripts/const_structs.checkpatch
@@ -1,6 +1,7 @@
acpi_dock_ops
address_space_operations
backlight_ops
+bin_attribute
block_device_operations
bus_type
clk_ops
diff --git a/scripts/crypto/gen-hash-testvecs.py b/scripts/crypto/gen-hash-testvecs.py
new file mode 100755
index 000000000000..4ac927d40cf5
--- /dev/null
+++ b/scripts/crypto/gen-hash-testvecs.py
@@ -0,0 +1,147 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# Script that generates test vectors for the given cryptographic hash function.
+#
+# Copyright 2025 Google LLC
+
+import hashlib
+import hmac
+import sys
+
+DATA_LENS = [0, 1, 2, 3, 16, 32, 48, 49, 63, 64, 65, 127, 128, 129, 256, 511,
+ 513, 1000, 3333, 4096, 4128, 4160, 4224, 16384]
+
+# Generate the given number of random bytes, using the length itself as the seed
+# for a simple linear congruential generator (LCG). The C test code uses the
+# same LCG with the same seeding strategy to reconstruct the data, ensuring
+# reproducibility without explicitly storing the data in the test vectors.
+def rand_bytes(length):
+ seed = length
+ out = []
+ for _ in range(length):
+ seed = (seed * 25214903917 + 11) % 2**48
+ out.append((seed >> 16) % 256)
+ return bytes(out)
+
+POLY1305_KEY_SIZE = 32
+
+# A straightforward, unoptimized implementation of Poly1305.
+# Reference: https://cr.yp.to/mac/poly1305-20050329.pdf
+class Poly1305:
+ def __init__(self, key):
+ assert len(key) == POLY1305_KEY_SIZE
+ self.h = 0
+ rclamp = 0x0ffffffc0ffffffc0ffffffc0fffffff
+ self.r = int.from_bytes(key[:16], byteorder='little') & rclamp
+ self.s = int.from_bytes(key[16:], byteorder='little')
+
+ # Note: this supports partial blocks only at the end.
+ def update(self, data):
+ for i in range(0, len(data), 16):
+ chunk = data[i:i+16]
+ c = int.from_bytes(chunk, byteorder='little') + 2**(8 * len(chunk))
+ self.h = ((self.h + c) * self.r) % (2**130 - 5)
+ return self
+
+ # Note: gen_additional_poly1305_testvecs() relies on this being
+ # nondestructive, i.e. not changing any field of self.
+ def digest(self):
+ m = (self.h + self.s) % 2**128
+ return m.to_bytes(16, byteorder='little')
+
+def hash_init(alg):
+ if alg == 'poly1305':
+ # Use a fixed random key here, to present Poly1305 as an unkeyed hash.
+ # This allows all the test cases for unkeyed hashes to work on Poly1305.
+ return Poly1305(rand_bytes(POLY1305_KEY_SIZE))
+ return hashlib.new(alg)
+
+def hash_update(ctx, data):
+ ctx.update(data)
+
+def hash_final(ctx):
+ return ctx.digest()
+
+def compute_hash(alg, data):
+ ctx = hash_init(alg)
+ hash_update(ctx, data)
+ return hash_final(ctx)
+
+def print_bytes(prefix, value, bytes_per_line):
+ for i in range(0, len(value), bytes_per_line):
+ line = prefix + ''.join(f'0x{b:02x}, ' for b in value[i:i+bytes_per_line])
+ print(f'{line.rstrip()}')
+
+def print_static_u8_array_definition(name, value):
+ print('')
+ print(f'static const u8 {name} = {{')
+ print_bytes('\t', value, 8)
+ print('};')
+
+def print_c_struct_u8_array_field(name, value):
+ print(f'\t\t.{name} = {{')
+ print_bytes('\t\t\t', value, 8)
+ print('\t\t},')
+
+def gen_unkeyed_testvecs(alg):
+ print('')
+ print('static const struct {')
+ print('\tsize_t data_len;')
+ print(f'\tu8 digest[{alg.upper()}_DIGEST_SIZE];')
+ print('} hash_testvecs[] = {')
+ for data_len in DATA_LENS:
+ data = rand_bytes(data_len)
+ print('\t{')
+ print(f'\t\t.data_len = {data_len},')
+ print_c_struct_u8_array_field('digest', compute_hash(alg, data))
+ print('\t},')
+ print('};')
+
+ data = rand_bytes(4096)
+ ctx = hash_init(alg)
+ for data_len in range(len(data) + 1):
+ hash_update(ctx, compute_hash(alg, data[:data_len]))
+ print_static_u8_array_definition(
+ f'hash_testvec_consolidated[{alg.upper()}_DIGEST_SIZE]',
+ hash_final(ctx))
+
+def gen_hmac_testvecs(alg):
+ ctx = hmac.new(rand_bytes(32), digestmod=alg)
+ data = rand_bytes(4096)
+ for data_len in range(len(data) + 1):
+ ctx.update(data[:data_len])
+ key_len = data_len % 293
+ key = rand_bytes(key_len)
+ mac = hmac.digest(key, data[:data_len], alg)
+ ctx.update(mac)
+ print_static_u8_array_definition(
+ f'hmac_testvec_consolidated[{alg.upper()}_DIGEST_SIZE]',
+ ctx.digest())
+
+def gen_additional_poly1305_testvecs():
+ key = b'\xff' * POLY1305_KEY_SIZE
+ data = b''
+ ctx = Poly1305(key)
+ for _ in range(32):
+ for j in range(0, 4097, 16):
+ ctx.update(b'\xff' * j)
+ data += ctx.digest()
+ print_static_u8_array_definition(
+ 'poly1305_allones_macofmacs[POLY1305_DIGEST_SIZE]',
+ Poly1305(key).update(data).digest())
+
+if len(sys.argv) != 2:
+ sys.stderr.write('Usage: gen-hash-testvecs.py ALGORITHM\n')
+ sys.stderr.write('ALGORITHM may be any supported by Python hashlib, or poly1305.\n')
+ sys.stderr.write('Example: gen-hash-testvecs.py sha512\n')
+ sys.exit(1)
+
+alg = sys.argv[1]
+print('/* SPDX-License-Identifier: GPL-2.0-or-later */')
+print(f'/* This file was generated by: {sys.argv[0]} {" ".join(sys.argv[1:])} */')
+gen_unkeyed_testvecs(alg)
+if alg == 'poly1305':
+ gen_additional_poly1305_testvecs()
+else:
+ gen_hmac_testvecs(alg)
diff --git a/scripts/extract-vmlinux b/scripts/extract-vmlinux
index 8995cd304e6e..189956b5a5c8 100755
--- a/scripts/extract-vmlinux
+++ b/scripts/extract-vmlinux
@@ -12,13 +12,12 @@
check_vmlinux()
{
- # Use readelf to check if it's a valid ELF
- # TODO: find a better to way to check that it's really vmlinux
- # and not just an elf
- readelf -h $1 > /dev/null 2>&1 || return 1
-
- cat $1
- exit 0
+ if file "$1" | grep -q 'Linux kernel.*boot executable' ||
+ readelf -h "$1" > /dev/null 2>&1
+ then
+ cat "$1"
+ exit 0
+ fi
}
try_decompress()
diff --git a/scripts/gcc-plugins/Kconfig b/scripts/gcc-plugins/Kconfig
index 231f4a20d617..6b34ba19358d 100644
--- a/scripts/gcc-plugins/Kconfig
+++ b/scripts/gcc-plugins/Kconfig
@@ -19,16 +19,6 @@ menuconfig GCC_PLUGINS
if GCC_PLUGINS
-config GCC_PLUGIN_SANCOV
- bool
- # Plugin can be removed once the kernel only supports GCC 6+
- depends on !CC_HAS_SANCOV_TRACE_PC
- help
- This plugin inserts a __sanitizer_cov_trace_pc() call at the start of
- basic blocks. It supports all gcc versions with plugin support (from
- gcc-4.5 on). It is based on the commit "Add fuzzing coverage support"
- by Dmitry Vyukov <dvyukov@google.com>.
-
config GCC_PLUGIN_LATENT_ENTROPY
bool "Generate some entropy during boot and runtime"
help
diff --git a/scripts/gcc-plugins/gcc-common.h b/scripts/gcc-plugins/gcc-common.h
index 3222c1070444..6cb6d1051815 100644
--- a/scripts/gcc-plugins/gcc-common.h
+++ b/scripts/gcc-plugins/gcc-common.h
@@ -3,11 +3,7 @@
#define GCC_COMMON_H_INCLUDED
#include "bversion.h"
-#if BUILDING_GCC_VERSION >= 6000
#include "gcc-plugin.h"
-#else
-#include "plugin.h"
-#endif
#include "plugin-version.h"
#include "config.h"
#include "system.h"
@@ -39,9 +35,7 @@
#include "hash-map.h"
-#if BUILDING_GCC_VERSION >= 7000
#include "memmodel.h"
-#endif
#include "emit-rtl.h"
#include "debug.h"
#include "target.h"
@@ -74,9 +68,7 @@
#include "context.h"
#include "tree-ssa-alias.h"
#include "tree-ssa.h"
-#if BUILDING_GCC_VERSION >= 7000
#include "tree-vrp.h"
-#endif
#include "tree-ssanames.h"
#include "print-tree.h"
#include "tree-eh.h"
@@ -123,6 +115,38 @@ static inline tree build_const_char_string(int len, const char *str)
return cstr;
}
+static inline void __add_type_attr(tree type, const char *attr, tree args)
+{
+ tree oldattr;
+
+ if (type == NULL_TREE)
+ return;
+ oldattr = lookup_attribute(attr, TYPE_ATTRIBUTES(type));
+ if (oldattr != NULL_TREE) {
+ gcc_assert(TREE_VALUE(oldattr) == args || TREE_VALUE(TREE_VALUE(oldattr)) == TREE_VALUE(args));
+ return;
+ }
+
+ TYPE_ATTRIBUTES(type) = copy_list(TYPE_ATTRIBUTES(type));
+ TYPE_ATTRIBUTES(type) = tree_cons(get_identifier(attr), args, TYPE_ATTRIBUTES(type));
+}
+
+static inline void add_type_attr(tree type, const char *attr, tree args)
+{
+ tree main_variant = TYPE_MAIN_VARIANT(type);
+
+ __add_type_attr(TYPE_CANONICAL(type), attr, args);
+ __add_type_attr(TYPE_CANONICAL(main_variant), attr, args);
+ __add_type_attr(main_variant, attr, args);
+
+ for (type = TYPE_NEXT_VARIANT(main_variant); type; type = TYPE_NEXT_VARIANT(type)) {
+ if (!lookup_attribute(attr, TYPE_ATTRIBUTES(type)))
+ TYPE_ATTRIBUTES(type) = TYPE_ATTRIBUTES(main_variant);
+
+ __add_type_attr(TYPE_CANONICAL(type), attr, args);
+ }
+}
+
#define PASS_INFO(NAME, REF, ID, POS) \
struct register_pass_info NAME##_pass_info = { \
.pass = make_##NAME##_pass(), \
@@ -149,16 +173,6 @@ static inline opt_pass *get_pass_for_id(int id)
return g->get_passes()->get_pass_for_id(id);
}
-#if BUILDING_GCC_VERSION < 6000
-/* gimple related */
-template <>
-template <>
-inline bool is_a_helper<const gassign *>::test(const_gimple gs)
-{
- return gs->code == GIMPLE_ASSIGN;
-}
-#endif
-
#define TODO_verify_ssa TODO_verify_il
#define TODO_verify_flow TODO_verify_il
#define TODO_verify_stmts TODO_verify_il
@@ -181,7 +195,6 @@ static inline const char *get_decl_section_name(const_tree decl)
#define varpool_get_node(decl) varpool_node::get(decl)
#define dump_varpool_node(file, node) (node)->dump(file)
-#if BUILDING_GCC_VERSION >= 8000
#define cgraph_create_edge(caller, callee, call_stmt, count, freq) \
(caller)->create_edge((callee), (call_stmt), (count))
@@ -189,15 +202,6 @@ static inline const char *get_decl_section_name(const_tree decl)
old_call_stmt, call_stmt, count, freq, reason) \
(caller)->create_edge_including_clones((callee), \
(old_call_stmt), (call_stmt), (count), (reason))
-#else
-#define cgraph_create_edge(caller, callee, call_stmt, count, freq) \
- (caller)->create_edge((callee), (call_stmt), (count), (freq))
-
-#define cgraph_create_edge_including_clones(caller, callee, \
- old_call_stmt, call_stmt, count, freq, reason) \
- (caller)->create_edge_including_clones((callee), \
- (old_call_stmt), (call_stmt), (count), (freq), (reason))
-#endif
typedef struct cgraph_node *cgraph_node_ptr;
typedef struct cgraph_edge *cgraph_edge_p;
@@ -293,14 +297,12 @@ static inline void cgraph_call_edge_duplication_hooks(cgraph_edge *cs1, cgraph_e
symtab->call_edge_duplication_hooks(cs1, cs2);
}
-#if BUILDING_GCC_VERSION >= 6000
typedef gimple *gimple_ptr;
typedef const gimple *const_gimple_ptr;
#define gimple gimple_ptr
#define const_gimple const_gimple_ptr
#undef CONST_CAST_GIMPLE
#define CONST_CAST_GIMPLE(X) CONST_CAST(gimple, (X))
-#endif
/* gimple related */
static inline gimple gimple_build_assign_with_ops(enum tree_code subcode, tree lhs, tree op1, tree op2 MEM_STAT_DECL)
@@ -400,15 +402,7 @@ static inline void ipa_remove_stmt_references(symtab_node *referring_node, gimpl
referring_node->remove_stmt_references(stmt);
}
-#if BUILDING_GCC_VERSION < 6000
-#define get_inner_reference(exp, pbitsize, pbitpos, poffset, pmode, punsignedp, preversep, pvolatilep, keep_aligning) \
- get_inner_reference(exp, pbitsize, pbitpos, poffset, pmode, punsignedp, pvolatilep, keep_aligning)
-#define gen_rtx_set(ARG0, ARG1) gen_rtx_SET(VOIDmode, (ARG0), (ARG1))
-#endif
-
-#if BUILDING_GCC_VERSION >= 6000
#define gen_rtx_set(ARG0, ARG1) gen_rtx_SET((ARG0), (ARG1))
-#endif
#ifdef __cplusplus
static inline void debug_tree(const_tree t)
@@ -425,15 +419,8 @@ static inline void debug_gimple_stmt(const_gimple s)
#define debug_gimple_stmt(s) debug_gimple_stmt(CONST_CAST_GIMPLE(s))
#endif
-#if BUILDING_GCC_VERSION >= 7000
#define get_inner_reference(exp, pbitsize, pbitpos, poffset, pmode, punsignedp, preversep, pvolatilep, keep_aligning) \
get_inner_reference(exp, pbitsize, pbitpos, poffset, pmode, punsignedp, preversep, pvolatilep)
-#endif
-
-#if BUILDING_GCC_VERSION < 7000
-#define SET_DECL_ALIGN(decl, align) DECL_ALIGN(decl) = (align)
-#define SET_DECL_MODE(decl, mode) DECL_MODE(decl) = (mode)
-#endif
#if BUILDING_GCC_VERSION >= 14000
#define last_stmt(x) last_nondebug_stmt(x)
diff --git a/scripts/gcc-plugins/randomize_layout_plugin.c b/scripts/gcc-plugins/randomize_layout_plugin.c
index 971a1908a8cc..ff65a4f87f24 100644
--- a/scripts/gcc-plugins/randomize_layout_plugin.c
+++ b/scripts/gcc-plugins/randomize_layout_plugin.c
@@ -73,6 +73,9 @@ static tree handle_randomize_layout_attr(tree *node, tree name, tree args, int f
if (TYPE_P(*node)) {
type = *node;
+ } else if (TREE_CODE(*node) == FIELD_DECL) {
+ *no_add_attrs = false;
+ return NULL_TREE;
} else {
gcc_assert(TREE_CODE(*node) == TYPE_DECL);
type = TREE_TYPE(*node);
@@ -348,15 +351,14 @@ static int relayout_struct(tree type)
TREE_CHAIN(newtree[i]) = newtree[i+1];
TREE_CHAIN(newtree[num_fields - 1]) = NULL_TREE;
+ add_type_attr(type, "randomize_performed", NULL_TREE);
+ add_type_attr(type, "designated_init", NULL_TREE);
+ if (has_flexarray)
+ add_type_attr(type, "has_flexarray", NULL_TREE);
+
main_variant = TYPE_MAIN_VARIANT(type);
- for (variant = main_variant; variant; variant = TYPE_NEXT_VARIANT(variant)) {
+ for (variant = main_variant; variant; variant = TYPE_NEXT_VARIANT(variant))
TYPE_FIELDS(variant) = newtree[0];
- TYPE_ATTRIBUTES(variant) = copy_list(TYPE_ATTRIBUTES(variant));
- TYPE_ATTRIBUTES(variant) = tree_cons(get_identifier("randomize_performed"), NULL_TREE, TYPE_ATTRIBUTES(variant));
- TYPE_ATTRIBUTES(variant) = tree_cons(get_identifier("designated_init"), NULL_TREE, TYPE_ATTRIBUTES(variant));
- if (has_flexarray)
- TYPE_ATTRIBUTES(type) = tree_cons(get_identifier("has_flexarray"), NULL_TREE, TYPE_ATTRIBUTES(type));
- }
/*
* force a re-layout of the main variant
@@ -424,10 +426,8 @@ static void randomize_type(tree type)
if (lookup_attribute("randomize_layout", TYPE_ATTRIBUTES(TYPE_MAIN_VARIANT(type))) || is_pure_ops_struct(type))
relayout_struct(type);
- for (variant = TYPE_MAIN_VARIANT(type); variant; variant = TYPE_NEXT_VARIANT(variant)) {
- TYPE_ATTRIBUTES(type) = copy_list(TYPE_ATTRIBUTES(type));
- TYPE_ATTRIBUTES(type) = tree_cons(get_identifier("randomize_considered"), NULL_TREE, TYPE_ATTRIBUTES(type));
- }
+ add_type_attr(type, "randomize_considered", NULL_TREE);
+
#ifdef __DEBUG_PLUGIN
fprintf(stderr, "Marking randomize_considered on struct %s\n", ORIG_TYPE_NAME(type));
#ifdef __DEBUG_VERBOSE
diff --git a/scripts/gcc-plugins/sancov_plugin.c b/scripts/gcc-plugins/sancov_plugin.c
deleted file mode 100644
index b76cb9c42cec..000000000000
--- a/scripts/gcc-plugins/sancov_plugin.c
+++ /dev/null
@@ -1,134 +0,0 @@
-/*
- * Copyright 2011-2016 by Emese Revfy <re.emese@gmail.com>
- * Licensed under the GPL v2, or (at your option) v3
- *
- * Homepage:
- * https://github.com/ephox-gcc-plugins/sancov
- *
- * This plugin inserts a __sanitizer_cov_trace_pc() call at the start of basic blocks.
- * It supports all gcc versions with plugin support (from gcc-4.5 on).
- * It is based on the commit "Add fuzzing coverage support" by Dmitry Vyukov <dvyukov@google.com>.
- *
- * You can read about it more here:
- * https://gcc.gnu.org/viewcvs/gcc?limit_changes=0&view=revision&revision=231296
- * https://lwn.net/Articles/674854/
- * https://github.com/google/syzkaller
- * https://lwn.net/Articles/677764/
- *
- * Usage:
- * make run
- */
-
-#include "gcc-common.h"
-
-__visible int plugin_is_GPL_compatible;
-
-tree sancov_fndecl;
-
-static struct plugin_info sancov_plugin_info = {
- .version = PLUGIN_VERSION,
- .help = "sancov plugin\n",
-};
-
-static unsigned int sancov_execute(void)
-{
- basic_block bb;
-
- /* Remove this line when this plugin and kcov will be in the kernel.
- if (!strcmp(DECL_NAME_POINTER(current_function_decl), DECL_NAME_POINTER(sancov_fndecl)))
- return 0;
- */
-
- FOR_EACH_BB_FN(bb, cfun) {
- const_gimple stmt;
- gcall *gcall;
- gimple_stmt_iterator gsi = gsi_after_labels(bb);
-
- if (gsi_end_p(gsi))
- continue;
-
- stmt = gsi_stmt(gsi);
- gcall = as_a_gcall(gimple_build_call(sancov_fndecl, 0));
- gimple_set_location(gcall, gimple_location(stmt));
- gsi_insert_before(&gsi, gcall, GSI_SAME_STMT);
- }
- return 0;
-}
-
-#define PASS_NAME sancov
-
-#define NO_GATE
-#define TODO_FLAGS_FINISH TODO_dump_func | TODO_verify_stmts | TODO_update_ssa_no_phi | TODO_verify_flow
-
-#include "gcc-generate-gimple-pass.h"
-
-static void sancov_start_unit(void __unused *gcc_data, void __unused *user_data)
-{
- tree leaf_attr, nothrow_attr;
- tree BT_FN_VOID = build_function_type_list(void_type_node, NULL_TREE);
-
- sancov_fndecl = build_fn_decl("__sanitizer_cov_trace_pc", BT_FN_VOID);
-
- DECL_ASSEMBLER_NAME(sancov_fndecl);
- TREE_PUBLIC(sancov_fndecl) = 1;
- DECL_EXTERNAL(sancov_fndecl) = 1;
- DECL_ARTIFICIAL(sancov_fndecl) = 1;
- DECL_PRESERVE_P(sancov_fndecl) = 1;
- DECL_UNINLINABLE(sancov_fndecl) = 1;
- TREE_USED(sancov_fndecl) = 1;
-
- nothrow_attr = tree_cons(get_identifier("nothrow"), NULL, NULL);
- decl_attributes(&sancov_fndecl, nothrow_attr, 0);
- gcc_assert(TREE_NOTHROW(sancov_fndecl));
- leaf_attr = tree_cons(get_identifier("leaf"), NULL, NULL);
- decl_attributes(&sancov_fndecl, leaf_attr, 0);
-}
-
-__visible int plugin_init(struct plugin_name_args *plugin_info, struct plugin_gcc_version *version)
-{
- int i;
- const char * const plugin_name = plugin_info->base_name;
- const int argc = plugin_info->argc;
- const struct plugin_argument * const argv = plugin_info->argv;
- bool enable = true;
-
- static const struct ggc_root_tab gt_ggc_r_gt_sancov[] = {
- {
- .base = &sancov_fndecl,
- .nelt = 1,
- .stride = sizeof(sancov_fndecl),
- .cb = &gt_ggc_mx_tree_node,
- .pchw = &gt_pch_nx_tree_node
- },
- LAST_GGC_ROOT_TAB
- };
-
- /* BBs can be split afterwards?? */
- PASS_INFO(sancov, "asan", 0, PASS_POS_INSERT_BEFORE);
-
- if (!plugin_default_version_check(version, &gcc_version)) {
- error(G_("incompatible gcc/plugin versions"));
- return 1;
- }
-
- for (i = 0; i < argc; ++i) {
- if (!strcmp(argv[i].key, "no-sancov")) {
- enable = false;
- continue;
- }
- error(G_("unknown option '-fplugin-arg-%s-%s'"), plugin_name, argv[i].key);
- }
-
- register_callback(plugin_name, PLUGIN_INFO, NULL, &sancov_plugin_info);
-
- if (!enable)
- return 0;
-
-#if BUILDING_GCC_VERSION < 6000
- register_callback(plugin_name, PLUGIN_START_UNIT, &sancov_start_unit, NULL);
- register_callback(plugin_name, PLUGIN_REGISTER_GGC_ROOTS, NULL, (void *)&gt_ggc_r_gt_sancov);
- register_callback(plugin_name, PLUGIN_PASS_MANAGER_SETUP, NULL, &sancov_pass_info);
-#endif
-
- return 0;
-}
diff --git a/scripts/gcc-plugins/stackleak_plugin.c b/scripts/gcc-plugins/stackleak_plugin.c
index d20c47d21ad8..e486488c867d 100644
--- a/scripts/gcc-plugins/stackleak_plugin.c
+++ b/scripts/gcc-plugins/stackleak_plugin.c
@@ -9,7 +9,7 @@
* any of the gcc libraries
*
* This gcc plugin is needed for tracking the lowest border of the kernel stack.
- * It instruments the kernel code inserting stackleak_track_stack() calls:
+ * It instruments the kernel code inserting __sanitizer_cov_stack_depth() calls:
* - after alloca();
* - for the functions with a stack frame size greater than or equal
* to the "track-min-size" plugin parameter.
@@ -33,7 +33,7 @@ __visible int plugin_is_GPL_compatible;
static int track_frame_size = -1;
static bool build_for_x86 = false;
-static const char track_function[] = "stackleak_track_stack";
+static const char track_function[] = "__sanitizer_cov_stack_depth";
static bool disable = false;
static bool verbose = false;
@@ -58,7 +58,7 @@ static void add_stack_tracking_gcall(gimple_stmt_iterator *gsi, bool after)
cgraph_node_ptr node;
basic_block bb;
- /* Insert calling stackleak_track_stack() */
+ /* Insert calling __sanitizer_cov_stack_depth() */
stmt = gimple_build_call(track_function_decl, 0);
gimple_call = as_a_gcall(stmt);
if (after)
@@ -120,12 +120,12 @@ static void add_stack_tracking_gasm(gimple_stmt_iterator *gsi, bool after)
gcc_assert(build_for_x86);
/*
- * Insert calling stackleak_track_stack() in asm:
- * asm volatile("call stackleak_track_stack"
+ * Insert calling __sanitizer_cov_stack_depth() in asm:
+ * asm volatile("call __sanitizer_cov_stack_depth"
* :: "r" (current_stack_pointer))
* Use ASM_CALL_CONSTRAINT trick from arch/x86/include/asm/asm.h.
* This constraint is taken into account during gcc shrink-wrapping
- * optimization. It is needed to be sure that stackleak_track_stack()
+ * optimization. It is needed to be sure that __sanitizer_cov_stack_depth()
* call is inserted after the prologue of the containing function,
* when the stack frame is prepared.
*/
@@ -137,7 +137,7 @@ static void add_stack_tracking_gasm(gimple_stmt_iterator *gsi, bool after)
input = build_tree_list(NULL_TREE, build_const_char_string(2, "r"));
input = chainon(NULL_TREE, build_tree_list(input, sp_decl));
vec_safe_push(inputs, input);
- asm_call = gimple_build_asm_vec("call stackleak_track_stack",
+ asm_call = gimple_build_asm_vec("call __sanitizer_cov_stack_depth",
inputs, NULL, NULL, NULL);
gimple_asm_set_volatile(asm_call, true);
if (after)
@@ -151,11 +151,11 @@ static void add_stack_tracking(gimple_stmt_iterator *gsi, bool after)
{
/*
* The 'no_caller_saved_registers' attribute is used for
- * stackleak_track_stack(). If the compiler supports this attribute for
- * the target arch, we can add calling stackleak_track_stack() in asm.
+ * __sanitizer_cov_stack_depth(). If the compiler supports this attribute for
+ * the target arch, we can add calling __sanitizer_cov_stack_depth() in asm.
* That improves performance: we avoid useless operations with the
* caller-saved registers in the functions from which we will remove
- * stackleak_track_stack() call during the stackleak_cleanup pass.
+ * __sanitizer_cov_stack_depth() call during the stackleak_cleanup pass.
*/
if (lookup_attribute_spec(get_identifier("no_caller_saved_registers")))
add_stack_tracking_gasm(gsi, after);
@@ -165,7 +165,7 @@ static void add_stack_tracking(gimple_stmt_iterator *gsi, bool after)
/*
* Work with the GIMPLE representation of the code. Insert the
- * stackleak_track_stack() call after alloca() and into the beginning
+ * __sanitizer_cov_stack_depth() call after alloca() and into the beginning
* of the function if it is not instrumented.
*/
static unsigned int stackleak_instrument_execute(void)
@@ -205,7 +205,7 @@ static unsigned int stackleak_instrument_execute(void)
DECL_NAME_POINTER(current_function_decl));
}
- /* Insert stackleak_track_stack() call after alloca() */
+ /* Insert __sanitizer_cov_stack_depth() call after alloca() */
add_stack_tracking(&gsi, true);
if (bb == entry_bb)
prologue_instrumented = true;
@@ -241,7 +241,7 @@ static unsigned int stackleak_instrument_execute(void)
return 0;
}
- /* Insert stackleak_track_stack() call at the function beginning */
+ /* Insert __sanitizer_cov_stack_depth() call at the function beginning */
bb = entry_bb;
if (!single_pred_p(bb)) {
/* gcc_assert(bb_loop_depth(bb) ||
@@ -270,15 +270,15 @@ static void remove_stack_tracking_gcall(void)
rtx_insn *insn, *next;
/*
- * Find stackleak_track_stack() calls. Loop through the chain of insns,
+ * Find __sanitizer_cov_stack_depth() calls. Loop through the chain of insns,
* which is an RTL representation of the code for a function.
*
* The example of a matching insn:
- * (call_insn 8 4 10 2 (call (mem (symbol_ref ("stackleak_track_stack")
- * [flags 0x41] <function_decl 0x7f7cd3302a80 stackleak_track_stack>)
- * [0 stackleak_track_stack S1 A8]) (0)) 675 {*call} (expr_list
- * (symbol_ref ("stackleak_track_stack") [flags 0x41] <function_decl
- * 0x7f7cd3302a80 stackleak_track_stack>) (expr_list (0) (nil))) (nil))
+ * (call_insn 8 4 10 2 (call (mem (symbol_ref ("__sanitizer_cov_stack_depth")
+ * [flags 0x41] <function_decl 0x7f7cd3302a80 __sanitizer_cov_stack_depth>)
+ * [0 __sanitizer_cov_stack_depth S1 A8]) (0)) 675 {*call} (expr_list
+ * (symbol_ref ("__sanitizer_cov_stack_depth") [flags 0x41] <function_decl
+ * 0x7f7cd3302a80 __sanitizer_cov_stack_depth>) (expr_list (0) (nil))) (nil))
*/
for (insn = get_insns(); insn; insn = next) {
rtx body;
@@ -318,7 +318,7 @@ static void remove_stack_tracking_gcall(void)
if (SYMBOL_REF_DECL(body) != track_function_decl)
continue;
- /* Delete the stackleak_track_stack() call */
+ /* Delete the __sanitizer_cov_stack_depth() call */
delete_insn_and_edges(insn);
#if BUILDING_GCC_VERSION < 8000
if (GET_CODE(next) == NOTE &&
@@ -340,12 +340,12 @@ static bool remove_stack_tracking_gasm(void)
gcc_assert(build_for_x86);
/*
- * Find stackleak_track_stack() asm calls. Loop through the chain of
+ * Find __sanitizer_cov_stack_depth() asm calls. Loop through the chain of
* insns, which is an RTL representation of the code for a function.
*
* The example of a matching insn:
* (insn 11 5 12 2 (parallel [ (asm_operands/v
- * ("call stackleak_track_stack") ("") 0
+ * ("call __sanitizer_cov_stack_depth") ("") 0
* [ (reg/v:DI 7 sp [ current_stack_pointer ]) ]
* [ (asm_input:DI ("r")) ] [])
* (clobber (reg:CC 17 flags)) ]) -1 (nil))
@@ -375,7 +375,7 @@ static bool remove_stack_tracking_gasm(void)
continue;
if (strcmp(ASM_OPERANDS_TEMPLATE(body),
- "call stackleak_track_stack")) {
+ "call __sanitizer_cov_stack_depth")) {
continue;
}
@@ -389,7 +389,7 @@ static bool remove_stack_tracking_gasm(void)
/*
* Work with the RTL representation of the code.
- * Remove the unneeded stackleak_track_stack() calls from the functions
+ * Remove the unneeded __sanitizer_cov_stack_depth() calls from the functions
* which don't call alloca() and don't have a large enough stack frame size.
*/
static unsigned int stackleak_cleanup_execute(void)
@@ -474,13 +474,13 @@ static bool stackleak_gate(void)
return track_frame_size >= 0;
}
-/* Build the function declaration for stackleak_track_stack() */
+/* Build the function declaration for __sanitizer_cov_stack_depth() */
static void stackleak_start_unit(void *gcc_data __unused,
void *user_data __unused)
{
tree fntype;
- /* void stackleak_track_stack(void) */
+ /* void __sanitizer_cov_stack_depth(void) */
fntype = build_function_type_list(void_type_node, NULL_TREE);
track_function_decl = build_fn_decl(track_function, fntype);
DECL_ASSEMBLER_NAME(track_function_decl); /* for LTO */
diff --git a/scripts/gcc-plugins/structleak_plugin.c b/scripts/gcc-plugins/structleak_plugin.c
deleted file mode 100644
index d8c744233832..000000000000
--- a/scripts/gcc-plugins/structleak_plugin.c
+++ /dev/null
@@ -1,257 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-only
-/*
- * Copyright 2013-2017 by PaX Team <pageexec@freemail.hu>
- *
- * Note: the choice of the license means that the compilation process is
- * NOT 'eligible' as defined by gcc's library exception to the GPL v3,
- * but for the kernel it doesn't matter since it doesn't link against
- * any of the gcc libraries
- *
- * gcc plugin to forcibly initialize certain local variables that could
- * otherwise leak kernel stack to userland if they aren't properly initialized
- * by later code
- *
- * Homepage: https://pax.grsecurity.net/
- *
- * Options:
- * -fplugin-arg-structleak_plugin-disable
- * -fplugin-arg-structleak_plugin-verbose
- * -fplugin-arg-structleak_plugin-byref
- * -fplugin-arg-structleak_plugin-byref-all
- *
- * Usage:
- * $ # for 4.5/4.6/C based 4.7
- * $ gcc -I`gcc -print-file-name=plugin`/include -I`gcc -print-file-name=plugin`/include/c-family -fPIC -shared -O2 -o structleak_plugin.so structleak_plugin.c
- * $ # for C++ based 4.7/4.8+
- * $ g++ -I`g++ -print-file-name=plugin`/include -I`g++ -print-file-name=plugin`/include/c-family -fPIC -shared -O2 -o structleak_plugin.so structleak_plugin.c
- * $ gcc -fplugin=./structleak_plugin.so test.c -O2
- *
- * TODO: eliminate redundant initializers
- */
-
-#include "gcc-common.h"
-
-/* unused C type flag in all versions 4.5-6 */
-#define TYPE_USERSPACE(TYPE) TYPE_LANG_FLAG_5(TYPE)
-
-__visible int plugin_is_GPL_compatible;
-
-static struct plugin_info structleak_plugin_info = {
- .version = PLUGIN_VERSION,
- .help = "disable\tdo not activate plugin\n"
- "byref\tinit structs passed by reference\n"
- "byref-all\tinit anything passed by reference\n"
- "verbose\tprint all initialized variables\n",
-};
-
-#define BYREF_STRUCT 1
-#define BYREF_ALL 2
-
-static bool verbose;
-static int byref;
-
-static tree handle_user_attribute(tree *node, tree name, tree args, int flags, bool *no_add_attrs)
-{
- *no_add_attrs = true;
-
- /* check for types? for now accept everything linux has to offer */
- if (TREE_CODE(*node) != FIELD_DECL)
- return NULL_TREE;
-
- *no_add_attrs = false;
- return NULL_TREE;
-}
-
-static struct attribute_spec user_attr = { };
-
-static void register_attributes(void *event_data, void *data)
-{
- user_attr.name = "user";
- user_attr.handler = handle_user_attribute;
- user_attr.affects_type_identity = true;
-
- register_attribute(&user_attr);
-}
-
-static tree get_field_type(tree field)
-{
- return strip_array_types(TREE_TYPE(field));
-}
-
-static bool is_userspace_type(tree type)
-{
- tree field;
-
- for (field = TYPE_FIELDS(type); field; field = TREE_CHAIN(field)) {
- tree fieldtype = get_field_type(field);
- enum tree_code code = TREE_CODE(fieldtype);
-
- if (code == RECORD_TYPE || code == UNION_TYPE)
- if (is_userspace_type(fieldtype))
- return true;
-
- if (lookup_attribute("user", DECL_ATTRIBUTES(field)))
- return true;
- }
- return false;
-}
-
-static void finish_type(void *event_data, void *data)
-{
- tree type = (tree)event_data;
-
- if (type == NULL_TREE || type == error_mark_node)
- return;
-
- if (TREE_CODE(type) == ENUMERAL_TYPE)
- return;
-
- if (TYPE_USERSPACE(type))
- return;
-
- if (is_userspace_type(type))
- TYPE_USERSPACE(type) = 1;
-}
-
-static void initialize(tree var)
-{
- basic_block bb;
- gimple_stmt_iterator gsi;
- tree initializer;
- gimple init_stmt;
- tree type;
-
- /* this is the original entry bb before the forced split */
- bb = single_succ(ENTRY_BLOCK_PTR_FOR_FN(cfun));
-
- /* first check if variable is already initialized, warn otherwise */
- for (gsi = gsi_start_bb(bb); !gsi_end_p(gsi); gsi_next(&gsi)) {
- gimple stmt = gsi_stmt(gsi);
- tree rhs1;
-
- /* we're looking for an assignment of a single rhs... */
- if (!gimple_assign_single_p(stmt))
- continue;
- rhs1 = gimple_assign_rhs1(stmt);
- /* ... of a non-clobbering expression... */
- if (TREE_CLOBBER_P(rhs1))
- continue;
- /* ... to our variable... */
- if (gimple_get_lhs(stmt) != var)
- continue;
- /* if it's an initializer then we're good */
- if (TREE_CODE(rhs1) == CONSTRUCTOR)
- return;
- }
-
- /* these aren't the 0days you're looking for */
- if (verbose)
- inform(DECL_SOURCE_LOCATION(var),
- "%s variable will be forcibly initialized",
- (byref && TREE_ADDRESSABLE(var)) ? "byref"
- : "userspace");
-
- /* build the initializer expression */
- type = TREE_TYPE(var);
- if (AGGREGATE_TYPE_P(type))
- initializer = build_constructor(type, NULL);
- else
- initializer = fold_convert(type, integer_zero_node);
-
- /* build the initializer stmt */
- init_stmt = gimple_build_assign(var, initializer);
- gsi = gsi_after_labels(single_succ(ENTRY_BLOCK_PTR_FOR_FN(cfun)));
- gsi_insert_before(&gsi, init_stmt, GSI_NEW_STMT);
- update_stmt(init_stmt);
-}
-
-static unsigned int structleak_execute(void)
-{
- basic_block bb;
- tree var;
- unsigned int i;
-
- /* split the first bb where we can put the forced initializers */
- gcc_assert(single_succ_p(ENTRY_BLOCK_PTR_FOR_FN(cfun)));
- bb = single_succ(ENTRY_BLOCK_PTR_FOR_FN(cfun));
- if (!single_pred_p(bb)) {
- split_edge(single_succ_edge(ENTRY_BLOCK_PTR_FOR_FN(cfun)));
- gcc_assert(single_succ_p(ENTRY_BLOCK_PTR_FOR_FN(cfun)));
- }
-
- /* enumerate all local variables and forcibly initialize our targets */
- FOR_EACH_LOCAL_DECL(cfun, i, var) {
- tree type = TREE_TYPE(var);
-
- gcc_assert(DECL_P(var));
- if (!auto_var_in_fn_p(var, current_function_decl))
- continue;
-
- /* only care about structure types unless byref-all */
- if (byref != BYREF_ALL && TREE_CODE(type) != RECORD_TYPE && TREE_CODE(type) != UNION_TYPE)
- continue;
-
- /* if the type is of interest, examine the variable */
- if (TYPE_USERSPACE(type) ||
- (byref && TREE_ADDRESSABLE(var)))
- initialize(var);
- }
-
- return 0;
-}
-
-#define PASS_NAME structleak
-#define NO_GATE
-#define PROPERTIES_REQUIRED PROP_cfg
-#define TODO_FLAGS_FINISH TODO_verify_il | TODO_verify_ssa | TODO_verify_stmts | TODO_dump_func | TODO_remove_unused_locals | TODO_update_ssa | TODO_ggc_collect | TODO_verify_flow
-#include "gcc-generate-gimple-pass.h"
-
-__visible int plugin_init(struct plugin_name_args *plugin_info, struct plugin_gcc_version *version)
-{
- int i;
- const char * const plugin_name = plugin_info->base_name;
- const int argc = plugin_info->argc;
- const struct plugin_argument * const argv = plugin_info->argv;
- bool enable = true;
-
- PASS_INFO(structleak, "early_optimizations", 1, PASS_POS_INSERT_BEFORE);
-
- if (!plugin_default_version_check(version, &gcc_version)) {
- error(G_("incompatible gcc/plugin versions"));
- return 1;
- }
-
- if (strncmp(lang_hooks.name, "GNU C", 5) && !strncmp(lang_hooks.name, "GNU C+", 6)) {
- inform(UNKNOWN_LOCATION, G_("%s supports C only, not %s"), plugin_name, lang_hooks.name);
- enable = false;
- }
-
- for (i = 0; i < argc; ++i) {
- if (!strcmp(argv[i].key, "disable")) {
- enable = false;
- continue;
- }
- if (!strcmp(argv[i].key, "verbose")) {
- verbose = true;
- continue;
- }
- if (!strcmp(argv[i].key, "byref")) {
- byref = BYREF_STRUCT;
- continue;
- }
- if (!strcmp(argv[i].key, "byref-all")) {
- byref = BYREF_ALL;
- continue;
- }
- error(G_("unknown option '-fplugin-arg-%s-%s'"), plugin_name, argv[i].key);
- }
-
- register_callback(plugin_name, PLUGIN_INFO, NULL, &structleak_plugin_info);
- if (enable) {
- register_callback(plugin_name, PLUGIN_PASS_MANAGER_SETUP, NULL, &structleak_pass_info);
- register_callback(plugin_name, PLUGIN_FINISH_TYPE, finish_type, NULL);
- }
- register_callback(plugin_name, PLUGIN_ATTRIBUTES, register_attributes, NULL);
-
- return 0;
-}
diff --git a/scripts/gdb/linux/constants.py.in b/scripts/gdb/linux/constants.py.in
index fd6bd69c5096..c3886739a028 100644
--- a/scripts/gdb/linux/constants.py.in
+++ b/scripts/gdb/linux/constants.py.in
@@ -20,6 +20,7 @@
#include <linux/of_fdt.h>
#include <linux/page_ext.h>
#include <linux/radix-tree.h>
+#include <linux/maple_tree.h>
#include <linux/slab.h>
#include <linux/threads.h>
#include <linux/vmalloc.h>
@@ -73,12 +74,12 @@ if IS_BUILTIN(CONFIG_MODULES):
LX_GDBPARSED(MOD_RO_AFTER_INIT)
/* linux/mount.h */
-LX_VALUE(MNT_NOSUID)
-LX_VALUE(MNT_NODEV)
-LX_VALUE(MNT_NOEXEC)
-LX_VALUE(MNT_NOATIME)
-LX_VALUE(MNT_NODIRATIME)
-LX_VALUE(MNT_RELATIME)
+LX_GDBPARSED(MNT_NOSUID)
+LX_GDBPARSED(MNT_NODEV)
+LX_GDBPARSED(MNT_NOEXEC)
+LX_GDBPARSED(MNT_NOATIME)
+LX_GDBPARSED(MNT_NODIRATIME)
+LX_GDBPARSED(MNT_RELATIME)
/* linux/threads.h */
LX_VALUE(NR_CPUS)
@@ -93,6 +94,12 @@ LX_GDBPARSED(RADIX_TREE_MAP_SIZE)
LX_GDBPARSED(RADIX_TREE_MAP_SHIFT)
LX_GDBPARSED(RADIX_TREE_MAP_MASK)
+/* linux/maple_tree.h */
+LX_VALUE(MAPLE_NODE_SLOTS)
+LX_VALUE(MAPLE_RANGE64_SLOTS)
+LX_VALUE(MAPLE_ARANGE64_SLOTS)
+LX_GDBPARSED(MAPLE_NODE_MASK)
+
/* linux/vmalloc.h */
LX_VALUE(VM_IOREMAP)
LX_VALUE(VM_ALLOC)
diff --git a/scripts/gdb/linux/cpus.py b/scripts/gdb/linux/cpus.py
index f506965ea759..6edf4ef61636 100644
--- a/scripts/gdb/linux/cpus.py
+++ b/scripts/gdb/linux/cpus.py
@@ -141,7 +141,7 @@ LxCpus()
class PerCpu(gdb.Function):
"""Return per-cpu variable.
-$lx_per_cpu("VAR"[, CPU]): Return the per-cpu variable called VAR for the
+$lx_per_cpu(VAR[, CPU]): Return the per-cpu variable called VAR for the
given CPU number. If CPU is omitted, the CPU of the current context is used.
Note that VAR has to be quoted as string."""
@@ -158,7 +158,7 @@ PerCpu()
class PerCpuPtr(gdb.Function):
"""Return per-cpu pointer.
-$lx_per_cpu_ptr("VAR"[, CPU]): Return the per-cpu pointer called VAR for the
+$lx_per_cpu_ptr(VAR[, CPU]): Return the per-cpu pointer called VAR for the
given CPU number. If CPU is omitted, the CPU of the current context is used.
Note that VAR has to be quoted as string."""
diff --git a/scripts/gdb/linux/interrupts.py b/scripts/gdb/linux/interrupts.py
index 616a5f26377a..f4f715a8f0e3 100644
--- a/scripts/gdb/linux/interrupts.py
+++ b/scripts/gdb/linux/interrupts.py
@@ -7,7 +7,7 @@ import gdb
from linux import constants
from linux import cpus
from linux import utils
-from linux import radixtree
+from linux import mapletree
irq_desc_type = utils.CachedType("struct irq_desc")
@@ -23,12 +23,12 @@ def irqd_is_level(desc):
def show_irq_desc(prec, irq):
text = ""
- desc = radixtree.lookup(gdb.parse_and_eval("&irq_desc_tree"), irq)
+ desc = mapletree.mtree_load(gdb.parse_and_eval("&sparse_irqs"), irq)
if desc is None:
return text
- desc = desc.cast(irq_desc_type.get_type())
- if desc is None:
+ desc = desc.cast(irq_desc_type.get_type().pointer())
+ if desc == 0:
return text
if irq_settings_is_hidden(desc):
@@ -110,7 +110,7 @@ def x86_show_mce(prec, var, pfx, desc):
pvar = gdb.parse_and_eval(var)
text = "%*s: " % (prec, pfx)
for cpu in cpus.each_online_cpu():
- text += "%10u " % (cpus.per_cpu(pvar, cpu))
+ text += "%10u " % (cpus.per_cpu(pvar, cpu).dereference())
text += " %s\n" % (desc)
return text
@@ -142,7 +142,7 @@ def x86_show_interupts(prec):
if constants.LX_CONFIG_X86_MCE:
text += x86_show_mce(prec, "&mce_exception_count", "MCE", "Machine check exceptions")
- text == x86_show_mce(prec, "&mce_poll_count", "MCP", "Machine check polls")
+ text += x86_show_mce(prec, "&mce_poll_count", "MCP", "Machine check polls")
text += show_irq_err_count(prec)
@@ -221,8 +221,8 @@ class LxInterruptList(gdb.Command):
gdb.write("CPU%-8d" % cpu)
gdb.write("\n")
- if utils.gdb_eval_or_none("&irq_desc_tree") is None:
- return
+ if utils.gdb_eval_or_none("&sparse_irqs") is None:
+ raise gdb.GdbError("Unable to find the sparse IRQ tree, is CONFIG_SPARSE_IRQ enabled?")
for irq in range(nr_irqs):
gdb.write(show_irq_desc(prec, irq))
diff --git a/scripts/gdb/linux/mapletree.py b/scripts/gdb/linux/mapletree.py
new file mode 100644
index 000000000000..d52d51c0a03f
--- /dev/null
+++ b/scripts/gdb/linux/mapletree.py
@@ -0,0 +1,252 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Maple tree helpers
+#
+# Copyright (c) 2025 Broadcom
+#
+# Authors:
+# Florian Fainelli <florian.fainelli@broadcom.com>
+
+import gdb
+
+from linux import utils
+from linux import constants
+from linux import xarray
+
+maple_tree_root_type = utils.CachedType("struct maple_tree")
+maple_node_type = utils.CachedType("struct maple_node")
+maple_enode_type = utils.CachedType("void")
+
+maple_dense = 0
+maple_leaf_64 = 1
+maple_range_64 = 2
+maple_arange_64 = 3
+
+class Mas(object):
+ ma_active = 0
+ ma_start = 1
+ ma_root = 2
+ ma_none = 3
+ ma_pause = 4
+ ma_overflow = 5
+ ma_underflow = 6
+ ma_error = 7
+
+ def __init__(self, mt, first, end):
+ if mt.type == maple_tree_root_type.get_type().pointer():
+ self.tree = mt.dereference()
+ elif mt.type != maple_tree_root_type.get_type():
+ raise gdb.GdbError("must be {} not {}"
+ .format(maple_tree_root_type.get_type().pointer(), mt.type))
+ self.tree = mt
+ self.index = first
+ self.last = end
+ self.node = None
+ self.status = self.ma_start
+ self.min = 0
+ self.max = -1
+
+ def is_start(self):
+ # mas_is_start()
+ return self.status == self.ma_start
+
+ def is_ptr(self):
+ # mas_is_ptr()
+ return self.status == self.ma_root
+
+ def is_none(self):
+ # mas_is_none()
+ return self.status == self.ma_none
+
+ def root(self):
+ # mas_root()
+ return self.tree['ma_root'].cast(maple_enode_type.get_type().pointer())
+
+ def start(self):
+ # mas_start()
+ if self.is_start() is False:
+ return None
+
+ self.min = 0
+ self.max = ~0
+
+ while True:
+ self.depth = 0
+ root = self.root()
+ if xarray.xa_is_node(root):
+ self.depth = 0
+ self.status = self.ma_active
+ self.node = mte_safe_root(root)
+ self.offset = 0
+ if mte_dead_node(self.node) is True:
+ continue
+
+ return None
+
+ self.node = None
+ # Empty tree
+ if root is None:
+ self.status = self.ma_none
+ self.offset = constants.LX_MAPLE_NODE_SLOTS
+ return None
+
+ # Single entry tree
+ self.status = self.ma_root
+ self.offset = constants.LX_MAPLE_NODE_SLOTS
+
+ if self.index != 0:
+ return None
+
+ return root
+
+ return None
+
+ def reset(self):
+ # mas_reset()
+ self.status = self.ma_start
+ self.node = None
+
+def mte_safe_root(node):
+ if node.type != maple_enode_type.get_type().pointer():
+ raise gdb.GdbError("{} must be {} not {}"
+ .format(mte_safe_root.__name__, maple_enode_type.get_type().pointer(), node.type))
+ ulong_type = utils.get_ulong_type()
+ indirect_ptr = node.cast(ulong_type) & ~0x2
+ val = indirect_ptr.cast(maple_enode_type.get_type().pointer())
+ return val
+
+def mte_node_type(entry):
+ ulong_type = utils.get_ulong_type()
+ val = None
+ if entry.type == maple_enode_type.get_type().pointer():
+ val = entry.cast(ulong_type)
+ elif entry.type == ulong_type:
+ val = entry
+ else:
+ raise gdb.GdbError("{} must be {} not {}"
+ .format(mte_node_type.__name__, maple_enode_type.get_type().pointer(), entry.type))
+ return (val >> 0x3) & 0xf
+
+def ma_dead_node(node):
+ if node.type != maple_node_type.get_type().pointer():
+ raise gdb.GdbError("{} must be {} not {}"
+ .format(ma_dead_node.__name__, maple_node_type.get_type().pointer(), node.type))
+ ulong_type = utils.get_ulong_type()
+ parent = node['parent']
+ indirect_ptr = node['parent'].cast(ulong_type) & ~constants.LX_MAPLE_NODE_MASK
+ return indirect_ptr == node
+
+def mte_to_node(enode):
+ ulong_type = utils.get_ulong_type()
+ if enode.type == maple_enode_type.get_type().pointer():
+ indirect_ptr = enode.cast(ulong_type)
+ elif enode.type == ulong_type:
+ indirect_ptr = enode
+ else:
+ raise gdb.GdbError("{} must be {} not {}"
+ .format(mte_to_node.__name__, maple_enode_type.get_type().pointer(), enode.type))
+ indirect_ptr = indirect_ptr & ~constants.LX_MAPLE_NODE_MASK
+ return indirect_ptr.cast(maple_node_type.get_type().pointer())
+
+def mte_dead_node(enode):
+ if enode.type != maple_enode_type.get_type().pointer():
+ raise gdb.GdbError("{} must be {} not {}"
+ .format(mte_dead_node.__name__, maple_enode_type.get_type().pointer(), enode.type))
+ node = mte_to_node(enode)
+ return ma_dead_node(node)
+
+def ma_is_leaf(tp):
+ result = tp < maple_range_64
+ return tp < maple_range_64
+
+def mt_pivots(t):
+ if t == maple_dense:
+ return 0
+ elif t == maple_leaf_64 or t == maple_range_64:
+ return constants.LX_MAPLE_RANGE64_SLOTS - 1
+ elif t == maple_arange_64:
+ return constants.LX_MAPLE_ARANGE64_SLOTS - 1
+
+def ma_pivots(node, t):
+ if node.type != maple_node_type.get_type().pointer():
+ raise gdb.GdbError("{}: must be {} not {}"
+ .format(ma_pivots.__name__, maple_node_type.get_type().pointer(), node.type))
+ if t == maple_arange_64:
+ return node['ma64']['pivot']
+ elif t == maple_leaf_64 or t == maple_range_64:
+ return node['mr64']['pivot']
+ else:
+ return None
+
+def ma_slots(node, tp):
+ if node.type != maple_node_type.get_type().pointer():
+ raise gdb.GdbError("{}: must be {} not {}"
+ .format(ma_slots.__name__, maple_node_type.get_type().pointer(), node.type))
+ if tp == maple_arange_64:
+ return node['ma64']['slot']
+ elif tp == maple_range_64 or tp == maple_leaf_64:
+ return node['mr64']['slot']
+ elif tp == maple_dense:
+ return node['slot']
+ else:
+ return None
+
+def mt_slot(mt, slots, offset):
+ ulong_type = utils.get_ulong_type()
+ return slots[offset].cast(ulong_type)
+
+def mtree_lookup_walk(mas):
+ ulong_type = utils.get_ulong_type()
+ n = mas.node
+
+ while True:
+ node = mte_to_node(n)
+ tp = mte_node_type(n)
+ pivots = ma_pivots(node, tp)
+ end = mt_pivots(tp)
+ offset = 0
+ while True:
+ if pivots[offset] >= mas.index:
+ break
+ if offset >= end:
+ break
+ offset += 1
+
+ slots = ma_slots(node, tp)
+ n = mt_slot(mas.tree, slots, offset)
+ if ma_dead_node(node) is True:
+ mas.reset()
+ return None
+ break
+
+ if ma_is_leaf(tp) is True:
+ break
+
+ return n
+
+def mtree_load(mt, index):
+ ulong_type = utils.get_ulong_type()
+ # MT_STATE(...)
+ mas = Mas(mt, index, index)
+ entry = None
+
+ while True:
+ entry = mas.start()
+ if mas.is_none():
+ return None
+
+ if mas.is_ptr():
+ if index != 0:
+ entry = None
+ return entry
+
+ entry = mtree_lookup_walk(mas)
+ if entry is None and mas.is_start():
+ continue
+ else:
+ break
+
+ if xarray.xa_is_zero(entry):
+ return None
+
+ return entry
diff --git a/scripts/gdb/linux/symbols.py b/scripts/gdb/linux/symbols.py
index b255177301e9..6edb99221675 100644
--- a/scripts/gdb/linux/symbols.py
+++ b/scripts/gdb/linux/symbols.py
@@ -38,19 +38,13 @@ if hasattr(gdb, 'Breakpoint'):
# Disable pagination while reporting symbol (re-)loading.
# The console input is blocked in this context so that we would
# get stuck waiting for the user to acknowledge paged output.
- show_pagination = gdb.execute("show pagination", to_string=True)
- pagination = show_pagination.endswith("on.\n")
- gdb.execute("set pagination off")
-
- if module_name in cmd.loaded_modules:
- gdb.write("refreshing all symbols to reload module "
- "'{0}'\n".format(module_name))
- cmd.load_all_symbols()
- else:
- cmd.load_module_symbols(module)
-
- # restore pagination state
- gdb.execute("set pagination %s" % ("on" if pagination else "off"))
+ with utils.pagination_off():
+ if module_name in cmd.loaded_modules:
+ gdb.write("refreshing all symbols to reload module "
+ "'{0}'\n".format(module_name))
+ cmd.load_all_symbols()
+ else:
+ cmd.load_module_symbols(module)
return False
@@ -60,6 +54,18 @@ def get_vmcore_s390():
vmcore_info = 0x0e0c
paddr_vmcoreinfo_note = gdb.parse_and_eval("*(unsigned long long *)" +
hex(vmcore_info))
+ if paddr_vmcoreinfo_note == 0 or paddr_vmcoreinfo_note & 1:
+ # In the early boot case, extract vm_layout.kaslr_offset from the
+ # vmlinux image in physical memory.
+ if paddr_vmcoreinfo_note == 0:
+ kaslr_offset_phys = 0
+ else:
+ kaslr_offset_phys = paddr_vmcoreinfo_note - 1
+ with utils.pagination_off():
+ gdb.execute("symbol-file {0} -o {1}".format(
+ utils.get_vmlinux(), hex(kaslr_offset_phys)))
+ kaslr_offset = gdb.parse_and_eval("vm_layout.kaslr_offset")
+ return "KERNELOFFSET=" + hex(kaslr_offset)[2:]
inferior = gdb.selected_inferior()
elf_note = inferior.read_memory(paddr_vmcoreinfo_note, 12)
n_namesz, n_descsz, n_type = struct.unpack(">III", elf_note)
@@ -78,6 +84,30 @@ def get_kerneloffset():
return None
+def is_in_s390_decompressor():
+ # DAT is always off in decompressor. Use this as an indicator.
+ # Note that in the kernel, DAT can be off during kexec() or restart.
+ # Accept this imprecision in order to avoid complicating things.
+ # It is unlikely that someone will run lx-symbols at these points.
+ pswm = int(gdb.parse_and_eval("$pswm"))
+ return (pswm & 0x0400000000000000) == 0
+
+
+def skip_decompressor():
+ if utils.is_target_arch("s390"):
+ if is_in_s390_decompressor():
+ # The address of the jump_to_kernel function is statically placed
+ # into svc_old_psw.addr (see ipl_data.c); read it from there. DAT
+ # is off, so we do not need to care about lowcore relocation.
+ svc_old_pswa = 0x148
+ jump_to_kernel = int(gdb.parse_and_eval("*(unsigned long long *)" +
+ hex(svc_old_pswa)))
+ gdb.execute("tbreak *" + hex(jump_to_kernel))
+ gdb.execute("continue")
+ while is_in_s390_decompressor():
+ gdb.execute("stepi")
+
+
class LxSymbols(gdb.Command):
"""(Re-)load symbols of Linux kernel and currently loaded modules.
@@ -178,11 +208,7 @@ lx-symbols command."""
saved_states.append({'breakpoint': bp, 'enabled': bp.enabled})
# drop all current symbols and reload vmlinux
- orig_vmlinux = 'vmlinux'
- for obj in gdb.objfiles():
- if (obj.filename.endswith('vmlinux') or
- obj.filename.endswith('vmlinux.debug')):
- orig_vmlinux = obj.filename
+ orig_vmlinux = utils.get_vmlinux()
gdb.execute("symbol-file", to_string=True)
kerneloffset = get_kerneloffset()
if kerneloffset is None:
@@ -202,6 +228,8 @@ lx-symbols command."""
saved_state['breakpoint'].enabled = saved_state['enabled']
def invoke(self, arg, from_tty):
+ skip_decompressor()
+
self.module_paths = [os.path.abspath(os.path.expanduser(p))
for p in arg.split()]
self.module_paths.append(os.getcwd())
diff --git a/scripts/gdb/linux/utils.py b/scripts/gdb/linux/utils.py
index 03ebdccf5f69..e11f6f67961a 100644
--- a/scripts/gdb/linux/utils.py
+++ b/scripts/gdb/linux/utils.py
@@ -200,7 +200,7 @@ def get_gdbserver_type():
def probe_kgdb():
try:
- thread_info = gdb.execute("info thread 2", to_string=True)
+ thread_info = gdb.execute("info thread 1", to_string=True)
return "shadowCPU" in thread_info
except gdb.error:
return False
@@ -251,3 +251,23 @@ def parse_vmcore(s):
else:
kerneloffset = int(match.group(1), 16)
return VmCore(kerneloffset=kerneloffset)
+
+
+def get_vmlinux():
+ vmlinux = 'vmlinux'
+ for obj in gdb.objfiles():
+ if (obj.filename.endswith('vmlinux') or
+ obj.filename.endswith('vmlinux.debug')):
+ vmlinux = obj.filename
+ return vmlinux
+
+
+@contextlib.contextmanager
+def pagination_off():
+ show_pagination = gdb.execute("show pagination", to_string=True)
+ pagination = show_pagination.endswith("on.\n")
+ gdb.execute("set pagination off")
+ try:
+ yield
+ finally:
+ gdb.execute("set pagination %s" % ("on" if pagination else "off"))
diff --git a/scripts/gdb/linux/vfs.py b/scripts/gdb/linux/vfs.py
index c77b9ce75f6d..9e921b645a68 100644
--- a/scripts/gdb/linux/vfs.py
+++ b/scripts/gdb/linux/vfs.py
@@ -22,7 +22,7 @@ def dentry_name(d):
if parent == d or parent == 0:
return ""
p = dentry_name(d['d_parent']) + "/"
- return p + d['d_iname'].string()
+ return p + d['d_name']['name'].string()
class DentryName(gdb.Function):
"""Return string of the full path of a dentry.
diff --git a/scripts/gdb/linux/xarray.py b/scripts/gdb/linux/xarray.py
new file mode 100644
index 000000000000..f4477b5def75
--- /dev/null
+++ b/scripts/gdb/linux/xarray.py
@@ -0,0 +1,28 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# Xarray helpers
+#
+# Copyright (c) 2025 Broadcom
+#
+# Authors:
+# Florian Fainelli <florian.fainelli@broadcom.com>
+
+import gdb
+
+from linux import utils
+from linux import constants
+
+def xa_is_internal(entry):
+ ulong_type = utils.get_ulong_type()
+ return ((entry.cast(ulong_type) & 3) == 2)
+
+def xa_mk_internal(v):
+ return ((v << 2) | 2)
+
+def xa_is_zero(entry):
+ ulong_type = utils.get_ulong_type()
+ return entry.cast(ulong_type) == xa_mk_internal(257)
+
+def xa_is_node(entry):
+ ulong_type = utils.get_ulong_type()
+ return xa_is_internal(entry) and (entry.cast(ulong_type) > 4096)
diff --git a/scripts/gendwarfksyms/cache.c b/scripts/gendwarfksyms/cache.c
index c9c19b86a686..1c640db93db3 100644
--- a/scripts/gendwarfksyms/cache.c
+++ b/scripts/gendwarfksyms/cache.c
@@ -15,7 +15,7 @@ void cache_set(struct cache *cache, unsigned long key, int value)
{
struct cache_item *ci;
- ci = xmalloc(sizeof(struct cache_item));
+ ci = xmalloc(sizeof(*ci));
ci->key = key;
ci->value = value;
hash_add(cache->cache, &ci->hash, hash_32(key));
diff --git a/scripts/gendwarfksyms/die.c b/scripts/gendwarfksyms/die.c
index 6183bbbe7b54..052f7a3f975a 100644
--- a/scripts/gendwarfksyms/die.c
+++ b/scripts/gendwarfksyms/die.c
@@ -33,7 +33,7 @@ static struct die *create_die(Dwarf_Die *die, enum die_state state)
{
struct die *cd;
- cd = xmalloc(sizeof(struct die));
+ cd = xmalloc(sizeof(*cd));
init_die(cd);
cd->addr = (uintptr_t)die->addr;
@@ -123,7 +123,7 @@ static struct die_fragment *append_item(struct die *cd)
{
struct die_fragment *df;
- df = xmalloc(sizeof(struct die_fragment));
+ df = xmalloc(sizeof(*df));
df->type = FRAGMENT_EMPTY;
list_add_tail(&df->list, &cd->fragments);
return df;
diff --git a/scripts/gendwarfksyms/dwarf.c b/scripts/gendwarfksyms/dwarf.c
index eed247d8abfc..3538a7d9cb07 100644
--- a/scripts/gendwarfksyms/dwarf.c
+++ b/scripts/gendwarfksyms/dwarf.c
@@ -228,12 +228,24 @@ static void process_fqn(struct die *cache, Dwarf_Die *die)
DEFINE_PROCESS_UDATA_ATTRIBUTE(accessibility)
DEFINE_PROCESS_UDATA_ATTRIBUTE(alignment)
DEFINE_PROCESS_UDATA_ATTRIBUTE(bit_size)
-DEFINE_PROCESS_UDATA_ATTRIBUTE(byte_size)
DEFINE_PROCESS_UDATA_ATTRIBUTE(encoding)
DEFINE_PROCESS_UDATA_ATTRIBUTE(data_bit_offset)
DEFINE_PROCESS_UDATA_ATTRIBUTE(data_member_location)
DEFINE_PROCESS_UDATA_ATTRIBUTE(discr_value)
+static void process_byte_size_attr(struct die *cache, Dwarf_Die *die)
+{
+ Dwarf_Word value;
+ unsigned long override;
+
+ if (get_udata_attr(die, DW_AT_byte_size, &value)) {
+ if (stable && kabi_get_byte_size(cache->fqn, &override))
+ value = override;
+
+ process_fmt(cache, " byte_size(%" PRIu64 ")", value);
+ }
+}
+
/* Match functions -- die_match_callback_t */
#define DEFINE_MATCH(type) \
static bool match_##type##_type(Dwarf_Die *die) \
@@ -622,7 +634,7 @@ static int get_union_kabi_status(Dwarf_Die *die, Dwarf_Die *placeholder,
* Note that the user of this feature is responsible for ensuring
* that the structure actually remains ABI compatible.
*/
- memset(&state.kabi, 0, sizeof(struct kabi_state));
+ memset(&state.kabi, 0, sizeof(state.kabi));
res = checkp(process_die_container(&state, NULL, die,
check_union_member_kabi_status,
diff --git a/scripts/gendwarfksyms/examples/kabi.h b/scripts/gendwarfksyms/examples/kabi.h
index 97a5669b083d..170733a3fba4 100644
--- a/scripts/gendwarfksyms/examples/kabi.h
+++ b/scripts/gendwarfksyms/examples/kabi.h
@@ -37,11 +37,14 @@
#define __stringify(x...) __stringify_1(x)
#endif
-#define __KABI_RULE(hint, target, value) \
+#define ___KABI_RULE(hint, target, value) \
static const char __PASTE(__gendwarfksyms_rule_, \
__COUNTER__)[] __used __aligned(1) \
__section(".discard.gendwarfksyms.kabi_rules") = \
- "1\0" #hint "\0" #target "\0" #value
+ "1\0" #hint "\0" target "\0" value
+
+#define __KABI_RULE(hint, target, value) \
+ ___KABI_RULE(hint, #target, #value)
#define __KABI_NORMAL_SIZE_ALIGN(_orig, _new) \
union { \
@@ -90,6 +93,20 @@
__KABI_RULE(enumerator_value, fqn field, value)
/*
+ * KABI_BYTE_SIZE(fqn, value)
+ * Set the byte_size attribute for the struct/union/enum fqn to
+ * value bytes.
+ */
+#define KABI_BYTE_SIZE(fqn, value) __KABI_RULE(byte_size, fqn, value)
+
+/*
+ * KABI_TYPE_STRING(type, str)
+ * For the given type, override the type string used in symtypes
+ * output and version calculation with str.
+ */
+#define KABI_TYPE_STRING(type, str) ___KABI_RULE(type_string, type, str)
+
+/*
* KABI_RESERVE
* Reserve some "padding" in a structure for use by LTS backports.
* This is normally placed at the end of a structure.
diff --git a/scripts/gendwarfksyms/examples/kabi_ex.c b/scripts/gendwarfksyms/examples/kabi_ex.c
index 0b7ffd830541..1f799eb7c756 100644
--- a/scripts/gendwarfksyms/examples/kabi_ex.c
+++ b/scripts/gendwarfksyms/examples/kabi_ex.c
@@ -28,3 +28,10 @@ struct ex2c ex2c;
struct ex3a ex3a;
struct ex3b ex3b;
struct ex3c ex3c;
+
+struct ex4a ex4a;
+
+struct ex5a ex5a;
+struct ex5b ex5b;
+
+int ex6a;
diff --git a/scripts/gendwarfksyms/examples/kabi_ex.h b/scripts/gendwarfksyms/examples/kabi_ex.h
index 1736e0f65208..785b211d9c58 100644
--- a/scripts/gendwarfksyms/examples/kabi_ex.h
+++ b/scripts/gendwarfksyms/examples/kabi_ex.h
@@ -21,6 +21,12 @@
* ./gendwarfksyms --stable --dump-dies \
* examples/kabi_ex.o 2>&1 >/dev/null | \
* FileCheck examples/kabi_ex.h --check-prefix=STABLE
+
+ * $ nm examples/kabi_ex.o | awk '{ print $NF }' | \
+ * ./gendwarfksyms --stable --dump-versions \
+ * examples/kabi_ex.o 2>&1 >/dev/null | \
+ * sort | \
+ * FileCheck examples/kabi_ex.h --check-prefix=VERSIONS
*/
#ifndef __KABI_EX_H__
@@ -170,7 +176,7 @@ struct ex2a {
/*
* STABLE: variable structure_type ex2a {
* STABLE-NEXT: member base_type int byte_size(4) encoding(5) a data_member_location(0) ,
- * STABLE-NEXT: member base_type [[ULONG:long unsigned int|unsigned long]] byte_size(8) encoding(7) b data_member_location(8)
+ * STABLE-NEXT: member base_type [[ULONG]] byte_size(8) encoding(7) b data_member_location(8)
* STABLE-NEXT: member base_type int byte_size(4) encoding(5) c data_member_location(16) ,
* STABLE-NEXT: member base_type [[ULONG]] byte_size(8) encoding(7) d data_member_location(24)
* STABLE-NEXT: } byte_size(32)
@@ -227,7 +233,7 @@ struct ex3a {
/*
* STABLE: variable structure_type ex3a {
- * STABLE-NEXT: member base_type [[ULONG:long unsigned int|unsigned long]] byte_size(8) encoding(7) a data_member_location(0)
+ * STABLE-NEXT: member base_type [[ULONG]] byte_size(8) encoding(7) a data_member_location(0)
* STABLE-NEXT: member base_type [[ULONG]] byte_size(8) encoding(7) unused data_member_location(8)
* STABLE-NEXT: } byte_size(16)
*/
@@ -260,4 +266,95 @@ _Static_assert(sizeof(struct ex3a) == sizeof(struct ex3c), "ex3a size doesn't ma
* STABLE-NEXT: } byte_size(16)
*/
+/*
+ * Example: An ignored field added to an end of a partially opaque struct,
+ * while keeping the byte_size attribute unchanged.
+ */
+
+struct ex4a {
+ unsigned long a;
+ KABI_IGNORE(0, unsigned long b);
+};
+
+/*
+ * This may be safe if the structure allocation is managed by the core kernel
+ * and the layout remains unchanged except for appended new members.
+ */
+KABI_BYTE_SIZE(ex4a, 8);
+
+/*
+ * STABLE: variable structure_type ex4a {
+ * STABLE-NEXT: member base_type [[ULONG]] byte_size(8) encoding(7) a data_member_location(0)
+ * STABLE-NEXT: } byte_size(8)
+ */
+
+/*
+ * Example: A type string override.
+ */
+
+struct ex5a {
+ unsigned long a;
+};
+
+/*
+ * This may be safe if the structure is fully opaque to modules, even though
+ * its definition has inadvertently become part of the ABI.
+ */
+KABI_TYPE_STRING(
+ "s#ex5a",
+ "structure_type ex5a { member pointer_type { s#ex4a } byte_size(8) p data_member_location(0) } byte_size(8)");
+
+/*
+ * Make sure the fully expanded type string includes ex4a.
+ *
+ * VERSIONS: ex5a variable structure_type ex5a {
+ * VERSIONS-SAME: member pointer_type {
+ * VERSIONS-SAME: structure_type ex4a {
+ * VERSIONS-SAME: member base_type [[ULONG:long unsigned int|unsigned long]] byte_size(8) encoding(7) a data_member_location(0)
+ * VERSIONS-SAME: } byte_size(8)
+ * VERSIONS-SAME: } byte_size(8) p data_member_location(0)
+ * VERSIONS-SAME: } byte_size(8)
+ */
+
+/*
+ * Example: A type string definition for a non-existent type.
+ */
+
+struct ex5b {
+ unsigned long a;
+};
+
+/* Replace the type string for struct ex5b */
+KABI_TYPE_STRING(
+ "s#ex5b",
+ "structure_type ex5b { member pointer_type { s#ex5c } byte_size(8) p data_member_location(0) } byte_size(8)");
+
+/* Define a type string for a non-existent struct ex5c */
+KABI_TYPE_STRING(
+ "s#ex5c",
+ "structure_type ex5c { member base_type int byte_size(4) encoding(5) n data_member_location(0) } byte_size(8)");
+
+/*
+ * Make sure the fully expanded type string includes the definition for ex5c.
+ *
+ * VERSIONS: ex5b variable structure_type ex5b {
+ * VERSIONS-SAME: member pointer_type {
+ * VERSIONS-SAME: structure_type ex5c {
+ * VERSIONS-SAME: member base_type int byte_size(4) encoding(5) n data_member_location(0)
+ * VERSIONS-SAME: } byte_size(8)
+ * VERSIONS-SAME: } byte_size(8) p data_member_location(0)
+ * VERSIONS-SAME: } byte_size(8)
+ */
+
+/*
+ * Example: A type string override for a symbol.
+ */
+
+KABI_TYPE_STRING("ex6a", "variable s#ex5c");
+
+/*
+ * VERSIONS: ex6a variable structure_type ex5c {
+ * VERSIONS-SAME: member base_type int byte_size(4) encoding(5) n data_member_location(0)
+ * VERSIONS-SAME: } byte_size(8)
+ */
#endif /* __KABI_EX_H__ */
diff --git a/scripts/gendwarfksyms/gendwarfksyms.h b/scripts/gendwarfksyms/gendwarfksyms.h
index 2feec168bf73..d9c06d2cb1df 100644
--- a/scripts/gendwarfksyms/gendwarfksyms.h
+++ b/scripts/gendwarfksyms/gendwarfksyms.h
@@ -216,24 +216,14 @@ int cache_get(struct cache *cache, unsigned long key);
void cache_init(struct cache *cache);
void cache_free(struct cache *cache);
-static inline void __cache_mark_expanded(struct cache *cache, uintptr_t addr)
-{
- cache_set(cache, addr, 1);
-}
-
-static inline bool __cache_was_expanded(struct cache *cache, uintptr_t addr)
-{
- return cache_get(cache, addr) == 1;
-}
-
static inline void cache_mark_expanded(struct cache *cache, void *addr)
{
- __cache_mark_expanded(cache, (uintptr_t)addr);
+ cache_set(cache, (unsigned long)addr, 1);
}
static inline bool cache_was_expanded(struct cache *cache, void *addr)
{
- return __cache_was_expanded(cache, (uintptr_t)addr);
+ return cache_get(cache, (unsigned long)addr) == 1;
}
/*
@@ -287,10 +277,12 @@ void generate_symtypes_and_versions(FILE *file);
* kabi.c
*/
+bool kabi_get_byte_size(const char *fqn, unsigned long *value);
bool kabi_is_enumerator_ignored(const char *fqn, const char *field);
bool kabi_get_enumerator_value(const char *fqn, const char *field,
unsigned long *value);
bool kabi_is_declonly(const char *fqn);
+bool kabi_get_type_string(const char *type, const char **str);
void kabi_read_rules(int fd);
void kabi_free(void);
diff --git a/scripts/gendwarfksyms/kabi.c b/scripts/gendwarfksyms/kabi.c
index 66f01fcd1607..e3c2a3ccf51a 100644
--- a/scripts/gendwarfksyms/kabi.c
+++ b/scripts/gendwarfksyms/kabi.c
@@ -54,11 +54,27 @@
*/
#define KABI_RULE_TAG_ENUMERATOR_VALUE "enumerator_value"
+/*
+ * Rule: byte_size
+ * - For the fqn_field in the target field, set the byte_size
+ * attribute to the value in the value field.
+ */
+#define KABI_RULE_TAG_BYTE_SIZE "byte_size"
+
+/*
+ * Rule: type_string
+ * - For the type reference in the fqn field, use the type string
+ * in the value field.
+ */
+#define KABI_RULE_TAG_TYPE_STRING "type_string"
+
enum kabi_rule_type {
KABI_RULE_TYPE_UNKNOWN,
KABI_RULE_TYPE_DECLONLY,
KABI_RULE_TYPE_ENUMERATOR_IGNORE,
KABI_RULE_TYPE_ENUMERATOR_VALUE,
+ KABI_RULE_TYPE_BYTE_SIZE,
+ KABI_RULE_TYPE_TYPE_STRING,
};
#define RULE_HASH_BITS 7
@@ -127,6 +143,14 @@ void kabi_read_rules(int fd)
.type = KABI_RULE_TYPE_ENUMERATOR_VALUE,
.tag = KABI_RULE_TAG_ENUMERATOR_VALUE,
},
+ {
+ .type = KABI_RULE_TYPE_BYTE_SIZE,
+ .tag = KABI_RULE_TAG_BYTE_SIZE,
+ },
+ {
+ .type = KABI_RULE_TYPE_TYPE_STRING,
+ .tag = KABI_RULE_TAG_TYPE_STRING,
+ },
};
if (!stable)
@@ -204,7 +228,7 @@ void kabi_read_rules(int fd)
if (type == KABI_RULE_TYPE_UNKNOWN)
error("unsupported kABI rule type: '%s'", field);
- rule = xmalloc(sizeof(struct rule));
+ rule = xmalloc(sizeof(*rule));
rule->type = type;
rule->target = xstrdup(get_rule_field(&rule_str, &left));
@@ -222,33 +246,55 @@ void kabi_read_rules(int fd)
check(elf_end(elf));
}
-bool kabi_is_declonly(const char *fqn)
+static char *get_enumerator_target(const char *fqn, const char *field)
+{
+ char *target = NULL;
+
+ if (asprintf(&target, "%s %s", fqn, field) < 0)
+ error("asprintf failed for '%s %s'", fqn, field);
+
+ return target;
+}
+
+static struct rule *find_rule(enum kabi_rule_type type, const char *target)
{
struct rule *rule;
if (!stable)
- return false;
- if (!fqn || !*fqn)
- return false;
+ return NULL;
+ if (!target || !*target)
+ return NULL;
hash_for_each_possible(rules, rule, hash,
- rule_values_hash(KABI_RULE_TYPE_DECLONLY, fqn)) {
- if (rule->type == KABI_RULE_TYPE_DECLONLY &&
- !strcmp(fqn, rule->target))
- return true;
+ rule_values_hash(type, target)) {
+ if (rule->type == type && !strcmp(target, rule->target))
+ return rule;
}
- return false;
+ return NULL;
}
-static char *get_enumerator_target(const char *fqn, const char *field)
+static struct rule *find_enumerator_rule(enum kabi_rule_type type,
+ const char *fqn, const char *field)
{
- char *target = NULL;
+ struct rule *rule;
+ char *target;
- if (asprintf(&target, "%s %s", fqn, field) < 0)
- error("asprintf failed for '%s %s'", fqn, field);
+ if (!stable)
+ return NULL;
+ if (!fqn || !*fqn || !field || !*field)
+ return NULL;
- return target;
+ target = get_enumerator_target(fqn, field);
+ rule = find_rule(type, target);
+
+ free(target);
+ return rule;
+}
+
+bool kabi_is_declonly(const char *fqn)
+{
+ return !!find_rule(KABI_RULE_TYPE_DECLONLY, fqn);
}
static unsigned long get_ulong_value(const char *value)
@@ -267,58 +313,49 @@ static unsigned long get_ulong_value(const char *value)
bool kabi_is_enumerator_ignored(const char *fqn, const char *field)
{
- bool match = false;
- struct rule *rule;
- char *target;
-
- if (!stable)
- return false;
- if (!fqn || !*fqn || !field || !*field)
- return false;
+ return !!find_enumerator_rule(KABI_RULE_TYPE_ENUMERATOR_IGNORE, fqn,
+ field);
+}
- target = get_enumerator_target(fqn, field);
+bool kabi_get_enumerator_value(const char *fqn, const char *field,
+ unsigned long *value)
+{
+ struct rule *rule;
- hash_for_each_possible(
- rules, rule, hash,
- rule_values_hash(KABI_RULE_TYPE_ENUMERATOR_IGNORE, target)) {
- if (rule->type == KABI_RULE_TYPE_ENUMERATOR_IGNORE &&
- !strcmp(target, rule->target)) {
- match = true;
- break;
- }
+ rule = find_enumerator_rule(KABI_RULE_TYPE_ENUMERATOR_VALUE, fqn,
+ field);
+ if (rule) {
+ *value = get_ulong_value(rule->value);
+ return true;
}
- free(target);
- return match;
+ return false;
}
-bool kabi_get_enumerator_value(const char *fqn, const char *field,
- unsigned long *value)
+bool kabi_get_byte_size(const char *fqn, unsigned long *value)
{
- bool match = false;
struct rule *rule;
- char *target;
- if (!stable)
- return false;
- if (!fqn || !*fqn || !field || !*field)
- return false;
+ rule = find_rule(KABI_RULE_TYPE_BYTE_SIZE, fqn);
+ if (rule) {
+ *value = get_ulong_value(rule->value);
+ return true;
+ }
- target = get_enumerator_target(fqn, field);
+ return false;
+}
- hash_for_each_possible(rules, rule, hash,
- rule_values_hash(KABI_RULE_TYPE_ENUMERATOR_VALUE,
- target)) {
- if (rule->type == KABI_RULE_TYPE_ENUMERATOR_VALUE &&
- !strcmp(target, rule->target)) {
- *value = get_ulong_value(rule->value);
- match = true;
- break;
- }
+bool kabi_get_type_string(const char *type, const char **str)
+{
+ struct rule *rule;
+
+ rule = find_rule(KABI_RULE_TYPE_TYPE_STRING, type);
+ if (rule) {
+ *str = rule->value;
+ return true;
}
- free(target);
- return match;
+ return false;
}
void kabi_free(void)
diff --git a/scripts/gendwarfksyms/symbols.c b/scripts/gendwarfksyms/symbols.c
index 327f87389c34..35ed594f0749 100644
--- a/scripts/gendwarfksyms/symbols.c
+++ b/scripts/gendwarfksyms/symbols.c
@@ -146,7 +146,7 @@ void symbol_read_exports(FILE *file)
continue;
}
- sym = xcalloc(1, sizeof(struct symbol));
+ sym = xcalloc(1, sizeof(*sym));
sym->name = name;
sym->addr.section = SHN_UNDEF;
sym->state = SYMBOL_UNPROCESSED;
diff --git a/scripts/gendwarfksyms/types.c b/scripts/gendwarfksyms/types.c
index 6f37289104ff..9c3b053bf061 100644
--- a/scripts/gendwarfksyms/types.c
+++ b/scripts/gendwarfksyms/types.c
@@ -6,6 +6,8 @@
#define _GNU_SOURCE
#include <inttypes.h>
#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
#include <zlib.h>
#include "gendwarfksyms.h"
@@ -43,7 +45,7 @@ static int type_list_append(struct list_head *list, const char *s, void *owned)
if (!s)
return 0;
- entry = xmalloc(sizeof(struct type_list_entry));
+ entry = xmalloc(sizeof(*entry));
entry->str = s;
entry->owned = owned;
list_add_tail(&entry->list, list);
@@ -100,7 +102,7 @@ static void type_expansion_append(struct type_expansion *type, const char *s,
#define TYPE_HASH_BITS 12
static HASHTABLE_DEFINE(type_map, 1 << TYPE_HASH_BITS);
-static int type_map_get(const char *name, struct type_expansion **res)
+static int __type_map_get(const char *name, struct type_expansion **res)
{
struct type_expansion *e;
@@ -114,12 +116,13 @@ static int type_map_get(const char *name, struct type_expansion **res)
return -1;
}
-static void type_map_add(const char *name, struct type_expansion *type)
+static struct type_expansion *type_map_add(const char *name,
+ struct type_expansion *type)
{
struct type_expansion *e;
- if (type_map_get(name, &e)) {
- e = xmalloc(sizeof(struct type_expansion));
+ if (__type_map_get(name, &e)) {
+ e = xmalloc(sizeof(*e));
type_expansion_init(e);
e->name = xstrdup(name);
@@ -130,7 +133,7 @@ static void type_map_add(const char *name, struct type_expansion *type)
} else {
/* Use the longest available expansion */
if (type->len <= e->len)
- return;
+ return e;
type_list_free(&e->expanded);
@@ -148,22 +151,71 @@ static void type_map_add(const char *name, struct type_expansion *type)
type_list_write(&e->expanded, stderr);
checkp(fputs("\n", stderr));
}
+
+ return e;
+}
+
+static void type_parse(const char *name, const char *str,
+ struct type_expansion *type);
+
+static int type_map_get(const char *name, struct type_expansion **res)
+{
+ struct type_expansion type;
+ const char *override;
+
+ if (!__type_map_get(name, res))
+ return 0;
+
+ /*
+ * If die_map didn't contain a type, we might still have
+ * a type_string kABI rule that defines it.
+ */
+ if (stable && kabi_get_type_string(name, &override)) {
+ type_expansion_init(&type);
+ type_parse(name, override, &type);
+ *res = type_map_add(name, &type);
+ type_expansion_free(&type);
+ return 0;
+ }
+
+ return -1;
+}
+
+static int cmp_expansion_name(const void *p1, const void *p2)
+{
+ struct type_expansion *const *e1 = p1;
+ struct type_expansion *const *e2 = p2;
+
+ return strcmp((*e1)->name, (*e2)->name);
}
static void type_map_write(FILE *file)
{
struct type_expansion *e;
struct hlist_node *tmp;
+ struct type_expansion **es;
+ size_t count = 0;
+ size_t i = 0;
if (!file)
return;
- hash_for_each_safe(type_map, e, tmp, hash) {
- checkp(fputs(e->name, file));
+ hash_for_each_safe(type_map, e, tmp, hash)
+ ++count;
+ es = xmalloc(count * sizeof(*es));
+ hash_for_each_safe(type_map, e, tmp, hash)
+ es[i++] = e;
+
+ qsort(es, count, sizeof(*es), cmp_expansion_name);
+
+ for (i = 0; i < count; ++i) {
+ checkp(fputs(es[i]->name, file));
checkp(fputs(" ", file));
- type_list_write(&e->expanded, file);
+ type_list_write(&es[i]->expanded, file);
checkp(fputs("\n", file));
}
+
+ free(es);
}
static void type_map_free(void)
@@ -267,15 +319,18 @@ static char *get_type_name(struct die *cache)
return name;
}
-static void __calculate_version(struct version *version, struct list_head *list)
+static void __calculate_version(struct version *version,
+ struct type_expansion *type)
{
struct type_list_entry *entry;
struct type_expansion *e;
/* Calculate a CRC over an expanded type string */
- list_for_each_entry(entry, list, list) {
+ list_for_each_entry(entry, &type->expanded, list) {
if (is_type_prefix(entry->str)) {
- check(type_map_get(entry->str, &e));
+ if (type_map_get(entry->str, &e))
+ error("unknown type reference to '%s' when expanding '%s'",
+ entry->str, type->name);
/*
* It's sufficient to expand each type reference just
@@ -285,7 +340,7 @@ static void __calculate_version(struct version *version, struct list_head *list)
version_add(version, entry->str);
} else {
cache_mark_expanded(&expansion_cache, e);
- __calculate_version(version, &e->expanded);
+ __calculate_version(version, e);
}
} else {
version_add(version, entry->str);
@@ -293,44 +348,19 @@ static void __calculate_version(struct version *version, struct list_head *list)
}
}
-static void calculate_version(struct version *version, struct list_head *list)
+static void calculate_version(struct version *version,
+ struct type_expansion *type)
{
version_init(version);
- __calculate_version(version, list);
+ __calculate_version(version, type);
cache_free(&expansion_cache);
}
-static void __type_expand(struct die *cache, struct type_expansion *type,
- bool recursive);
-
-static void type_expand_child(struct die *cache, struct type_expansion *type,
- bool recursive)
-{
- struct type_expansion child;
- char *name;
-
- name = get_type_name(cache);
- if (!name) {
- __type_expand(cache, type, recursive);
- return;
- }
-
- if (recursive && !__cache_was_expanded(&expansion_cache, cache->addr)) {
- __cache_mark_expanded(&expansion_cache, cache->addr);
- type_expansion_init(&child);
- __type_expand(cache, &child, true);
- type_map_add(name, &child);
- type_expansion_free(&child);
- }
-
- type_expansion_append(type, name, name);
-}
-
-static void __type_expand(struct die *cache, struct type_expansion *type,
- bool recursive)
+static void __type_expand(struct die *cache, struct type_expansion *type)
{
struct die_fragment *df;
struct die *child;
+ char *name;
list_for_each_entry(df, &cache->fragments, list) {
switch (df->type) {
@@ -346,7 +376,12 @@ static void __type_expand(struct die *cache, struct type_expansion *type,
error("unknown child: %" PRIxPTR,
df->data.addr);
- type_expand_child(child, type, recursive);
+ name = get_type_name(child);
+ if (name)
+ type_expansion_append(type, name, name);
+ else
+ __type_expand(child, type);
+
break;
case FRAGMENT_LINEBREAK:
/*
@@ -364,12 +399,85 @@ static void __type_expand(struct die *cache, struct type_expansion *type,
}
}
-static void type_expand(struct die *cache, struct type_expansion *type,
- bool recursive)
+static void type_expand(const char *name, struct die *cache,
+ struct type_expansion *type)
{
+ const char *override;
+
type_expansion_init(type);
- __type_expand(cache, type, recursive);
- cache_free(&expansion_cache);
+
+ if (stable && kabi_get_type_string(name, &override))
+ type_parse(name, override, type);
+ else
+ __type_expand(cache, type);
+}
+
+static void type_parse(const char *name, const char *str,
+ struct type_expansion *type)
+{
+ char *fragment;
+ size_t start = 0;
+ size_t end;
+ size_t pos;
+
+ if (!*str)
+ error("empty type string override for '%s'", name);
+
+ for (pos = 0; str[pos]; ++pos) {
+ bool empty;
+ char marker = ' ';
+
+ if (!is_type_prefix(&str[pos]))
+ continue;
+
+ end = pos + 2;
+
+ /*
+ * Find the end of the type reference. If the type name contains
+ * spaces, it must be in single quotes.
+ */
+ if (str[end] == '\'') {
+ marker = '\'';
+ ++end;
+ }
+ while (str[end] && str[end] != marker)
+ ++end;
+
+ /* Check that we have a non-empty type name */
+ if (marker == '\'') {
+ if (str[end] != marker)
+ error("incomplete %c# type reference for '%s' (string : '%s')",
+ str[pos], name, str);
+ empty = end == pos + 3;
+ ++end;
+ } else {
+ empty = end == pos + 2;
+ }
+ if (empty)
+ error("empty %c# type name for '%s' (string: '%s')",
+ str[pos], name, str);
+
+ /* Append the part of the string before the type reference */
+ if (pos > start) {
+ fragment = xstrndup(&str[start], pos - start);
+ type_expansion_append(type, fragment, fragment);
+ }
+
+ /*
+ * Append the type reference -- note that if the reference
+ * is invalid, i.e. points to a non-existent type, we will
+ * print out an error when calculating versions.
+ */
+ fragment = xstrndup(&str[pos], end - pos);
+ type_expansion_append(type, fragment, fragment);
+
+ start = end;
+ pos = end - 1;
+ }
+
+ /* Append the rest of the type string, if there's any left */
+ if (str[start])
+ type_expansion_append(type, &str[start], NULL);
}
static void expand_type(struct die *cache, void *arg)
@@ -399,9 +507,9 @@ static void expand_type(struct die *cache, void *arg)
return;
debug("%s", name);
- type_expand(cache, &type, true);
- type_map_add(name, &type);
+ type_expand(name, cache, &type);
+ type_map_add(name, &type);
type_expansion_free(&type);
free(name);
}
@@ -423,11 +531,11 @@ static void expand_symbol(struct symbol *sym, void *arg)
if (__die_map_get(sym->die_addr, DIE_SYMBOL, &cache))
return; /* We'll warn about missing CRCs later. */
- type_expand(cache, &type, false);
+ type_expand(sym->name, cache, &type);
/* If the symbol already has a version, don't calculate it again. */
if (sym->state != SYMBOL_PROCESSED) {
- calculate_version(&version, &type.expanded);
+ calculate_version(&version, &type);
symbol_set_crc(sym, version.crc);
debug("%s = %lx", sym->name, version.crc);
diff --git a/scripts/generate_rust_analyzer.py b/scripts/generate_rust_analyzer.py
index fe663dd0c43b..7c3ea2b55041 100755
--- a/scripts/generate_rust_analyzer.py
+++ b/scripts/generate_rust_analyzer.py
@@ -19,7 +19,7 @@ def args_crates_cfgs(cfgs):
return crates_cfgs
-def generate_crates(srctree, objtree, sysroot_src, external_src, cfgs):
+def generate_crates(srctree, objtree, sysroot_src, external_src, cfgs, core_edition):
# Generate the configuration list.
cfg = []
with open(objtree / "include" / "generated" / "rustc_cfg") as fd:
@@ -35,7 +35,7 @@ def generate_crates(srctree, objtree, sysroot_src, external_src, cfgs):
crates_indexes = {}
crates_cfgs = args_crates_cfgs(cfgs)
- def append_crate(display_name, root_module, deps, cfg=[], is_workspace_member=True, is_proc_macro=False):
+ def append_crate(display_name, root_module, deps, cfg=[], is_workspace_member=True, is_proc_macro=False, edition="2021"):
crate = {
"display_name": display_name,
"root_module": str(root_module),
@@ -43,7 +43,7 @@ def generate_crates(srctree, objtree, sysroot_src, external_src, cfgs):
"is_proc_macro": is_proc_macro,
"deps": [{"crate": crates_indexes[dep], "name": dep} for dep in deps],
"cfg": cfg,
- "edition": "2021",
+ "edition": edition,
"env": {
"RUST_MODFILE": "This is only for rust-analyzer"
}
@@ -61,6 +61,7 @@ def generate_crates(srctree, objtree, sysroot_src, external_src, cfgs):
display_name,
deps,
cfg=[],
+ edition="2021",
):
append_crate(
display_name,
@@ -68,12 +69,13 @@ def generate_crates(srctree, objtree, sysroot_src, external_src, cfgs):
deps,
cfg,
is_workspace_member=False,
+ edition=edition,
)
# NB: sysroot crates reexport items from one another so setting up our transitive dependencies
# here is important for ensuring that rust-analyzer can resolve symbols. The sources of truth
# for this dependency graph are `(sysroot_src / crate / "Cargo.toml" for crate in crates)`.
- append_sysroot_crate("core", [], cfg=crates_cfgs.get("core", []))
+ append_sysroot_crate("core", [], cfg=crates_cfgs.get("core", []), edition=core_edition)
append_sysroot_crate("alloc", ["core"])
append_sysroot_crate("std", ["alloc", "core"])
append_sysroot_crate("proc_macro", ["core", "std"])
@@ -177,6 +179,7 @@ def main():
parser = argparse.ArgumentParser()
parser.add_argument('--verbose', '-v', action='store_true')
parser.add_argument('--cfgs', action='append', default=[])
+ parser.add_argument("core_edition")
parser.add_argument("srctree", type=pathlib.Path)
parser.add_argument("objtree", type=pathlib.Path)
parser.add_argument("sysroot", type=pathlib.Path)
@@ -193,7 +196,7 @@ def main():
assert args.sysroot in args.sysroot_src.parents
rust_project = {
- "crates": generate_crates(args.srctree, args.objtree, args.sysroot_src, args.exttree, args.cfgs),
+ "crates": generate_crates(args.srctree, args.objtree, args.sysroot_src, args.exttree, args.cfgs, args.core_edition),
"sysroot": str(args.sysroot),
}
diff --git a/scripts/generate_rust_target.rs b/scripts/generate_rust_target.rs
index 8667d0ae3c82..39c82908ff3a 100644
--- a/scripts/generate_rust_target.rs
+++ b/scripts/generate_rust_target.rs
@@ -209,7 +209,7 @@ fn main() {
// target feature of the same name plus the other two target features in
// `clang/lib/Driver/ToolChains/Arch/X86.cpp`. These should be eventually enabled via
// `-Ctarget-feature` when `rustc` starts recognizing them (or via a new dedicated
- // flag); see https://github.com/rust-lang/rust/issues/116852.
+ // flag); see <https://github.com/rust-lang/rust/issues/116852>.
features += ",+retpoline-external-thunk";
features += ",+retpoline-indirect-branches";
features += ",+retpoline-indirect-calls";
@@ -218,7 +218,7 @@ fn main() {
// The kernel uses `-mharden-sls=all`, which Clang maps to both these target features in
// `clang/lib/Driver/ToolChains/Arch/X86.cpp`. These should be eventually enabled via
// `-Ctarget-feature` when `rustc` starts recognizing them (or via a new dedicated
- // flag); see https://github.com/rust-lang/rust/issues/116851.
+ // flag); see <https://github.com/rust-lang/rust/issues/116851>.
features += ",+harden-sls-ijmp";
features += ",+harden-sls-ret";
}
diff --git a/scripts/genksyms/genksyms.c b/scripts/genksyms/genksyms.c
index 8b0d7ac73dbb..83e48670c2fc 100644
--- a/scripts/genksyms/genksyms.c
+++ b/scripts/genksyms/genksyms.c
@@ -181,13 +181,9 @@ static int is_unknown_symbol(struct symbol *sym)
strcmp(defn->string, "{") == 0);
}
-static struct symbol *__add_symbol(const char *name, enum symbol_type type,
- struct string_list *defn, int is_extern,
- int is_reference)
+static struct string_list *process_enum(const char *name, enum symbol_type type,
+ struct string_list *defn)
{
- unsigned long h;
- struct symbol *sym;
- enum symbol_status status = STATUS_UNCHANGED;
/* The parser adds symbols in the order their declaration completes,
* so it is safe to store the value of the previous enum constant in
* a static variable.
@@ -216,7 +212,7 @@ static struct symbol *__add_symbol(const char *name, enum symbol_type type,
defn = mk_node(buf);
}
}
- } else if (type == SYM_ENUM) {
+ } else {
free_list(last_enum_expr, NULL);
last_enum_expr = NULL;
enum_counter = 0;
@@ -225,6 +221,23 @@ static struct symbol *__add_symbol(const char *name, enum symbol_type type,
return NULL;
}
+ return defn;
+}
+
+static struct symbol *__add_symbol(const char *name, enum symbol_type type,
+ struct string_list *defn, int is_extern,
+ int is_reference)
+{
+ unsigned long h;
+ struct symbol *sym;
+ enum symbol_status status = STATUS_UNCHANGED;
+
+ if ((type == SYM_ENUM_CONST || type == SYM_ENUM) && !is_reference) {
+ defn = process_enum(name, type, defn);
+ if (defn == NULL)
+ return NULL;
+ }
+
h = crc32(name);
hash_for_each_possible(symbol_hashtable, sym, hnode, h) {
if (map_to_ns(sym->type) != map_to_ns(type) ||
diff --git a/scripts/git-resolve.sh b/scripts/git-resolve.sh
new file mode 100755
index 000000000000..e9b5940c0f28
--- /dev/null
+++ b/scripts/git-resolve.sh
@@ -0,0 +1,201 @@
+#!/bin/bash
+# SPDX-License-Identifier: GPL-2.0
+# (c) 2025, Sasha Levin <sashal@kernel.org>
+
+usage() {
+ echo "Usage: $(basename "$0") [--selftest] [--force] <commit-id> [commit-subject]"
+ echo "Resolves a short git commit ID to its full SHA-1 hash, particularly useful for fixing references in commit messages."
+ echo ""
+ echo "Arguments:"
+ echo " --selftest Run self-tests"
+ echo " --force Try to find commit by subject if ID lookup fails"
+ echo " commit-id Short git commit ID to resolve"
+ echo " commit-subject Optional commit subject to help resolve between multiple matches"
+ exit 1
+}
+
+# Convert subject with ellipsis to grep pattern
+convert_to_grep_pattern() {
+ local subject="$1"
+ # First escape ALL regex special characters
+ local escaped_subject
+ escaped_subject=$(printf '%s\n' "$subject" | sed 's/[[\.*^$()+?{}|]/\\&/g')
+ # Also escape colons, parentheses, and hyphens as they are special in our context
+ escaped_subject=$(echo "$escaped_subject" | sed 's/[:-]/\\&/g')
+ # Then convert escaped ... sequence to .*?
+ escaped_subject=$(echo "$escaped_subject" | sed 's/\\\.\\\.\\\./.*?/g')
+ echo "^${escaped_subject}$"
+}
+
+git_resolve_commit() {
+ local force=0
+ if [ "$1" = "--force" ]; then
+ force=1
+ shift
+ fi
+
+ # Split input into commit ID and subject
+ local input="$*"
+ local commit_id="${input%% *}"
+ local subject=""
+
+ # Extract subject if present (everything after the first space)
+ if [[ "$input" == *" "* ]]; then
+ subject="${input#* }"
+ # Strip the ("...") quotes if present
+ subject="${subject#*(\"}"
+ subject="${subject%\")*}"
+ fi
+
+ # Get all possible matching commit IDs
+ local matches
+ readarray -t matches < <(git rev-parse --disambiguate="$commit_id" 2>/dev/null)
+
+ # Return immediately if we have exactly one match
+ if [ ${#matches[@]} -eq 1 ]; then
+ echo "${matches[0]}"
+ return 0
+ fi
+
+ # If no matches and not in force mode, return failure
+ if [ ${#matches[@]} -eq 0 ] && [ $force -eq 0 ]; then
+ return 1
+ fi
+
+ # If we have a subject, try to find a match with that subject
+ if [ -n "$subject" ]; then
+ # Convert subject with possible ellipsis to grep pattern
+ local grep_pattern
+ grep_pattern=$(convert_to_grep_pattern "$subject")
+
+ # In force mode with no ID matches, use git log --grep directly
+ if [ ${#matches[@]} -eq 0 ] && [ $force -eq 1 ]; then
+ # Use git log to search, but filter to ensure subject matches exactly
+ local match
+ match=$(git log --format="%H %s" --grep="$grep_pattern" --perl-regexp -10 | \
+ while read -r hash subject; do
+ if echo "$subject" | grep -qP "$grep_pattern"; then
+ echo "$hash"
+ break
+ fi
+ done)
+ if [ -n "$match" ]; then
+ echo "$match"
+ return 0
+ fi
+ else
+ # Normal subject matching for existing matches
+ for match in "${matches[@]}"; do
+ if git log -1 --format="%s" "$match" | grep -qP "$grep_pattern"; then
+ echo "$match"
+ return 0
+ fi
+ done
+ fi
+ fi
+
+ # No match found
+ return 1
+}
+
+run_selftest() {
+ local test_cases=(
+ '00250b5 ("MAINTAINERS: add new Rockchip SoC list")'
+ '0037727 ("KVM: selftests: Convert xen_shinfo_test away from VCPU_ID")'
+ 'ffef737 ("net/tls: Fix skb memory leak when running kTLS traffic")'
+ 'd3d7 ("cifs: Improve guard for excluding $LXDEV xattr")'
+ 'dbef ("Rename .data.once to .data..once to fix resetting WARN*_ONCE")'
+ '12345678' # Non-existent commit
+ '12345 ("I'\''m a dummy commit")' # Valid prefix but wrong subject
+ '--force 99999999 ("net/tls: Fix skb memory leak when running kTLS traffic")' # Force mode with non-existent ID but valid subject
+ '83be ("firmware: ... auto-update: fix poll_complete() ... errors")' # Wildcard test
+ '--force 999999999999 ("firmware: ... auto-update: fix poll_complete() ... errors")' # Force mode wildcard test
+ )
+
+ local expected=(
+ "00250b529313d6262bb0ebbd6bdf0a88c809f6f0"
+ "0037727b3989c3fe1929c89a9a1dfe289ad86f58"
+ "ffef737fd0372ca462b5be3e7a592a8929a82752"
+ "d3d797e326533794c3f707ce1761da7a8895458c"
+ "dbefa1f31a91670c9e7dac9b559625336206466f"
+ "" # Expect empty output for non-existent commit
+ "" # Expect empty output for wrong subject
+ "ffef737fd0372ca462b5be3e7a592a8929a82752" # Should find commit by subject in force mode
+ "83beece5aff75879bdfc6df8ba84ea88fd93050e" # Wildcard test
+ "83beece5aff75879bdfc6df8ba84ea88fd93050e" # Force mode wildcard test
+ )
+
+ local expected_exit_codes=(
+ 0
+ 0
+ 0
+ 0
+ 0
+ 1 # Expect failure for non-existent commit
+ 1 # Expect failure for wrong subject
+ 0 # Should succeed in force mode
+ 0 # Should succeed with wildcard
+ 0 # Should succeed with force mode and wildcard
+ )
+
+ local failed=0
+
+ echo "Running self-tests..."
+ for i in "${!test_cases[@]}"; do
+ # Capture both output and exit code
+ local result
+ result=$(git_resolve_commit ${test_cases[$i]}) # Removed quotes to allow --force to be parsed
+ local exit_code=$?
+
+ # Check both output and exit code
+ if [ "$result" != "${expected[$i]}" ] || [ $exit_code != ${expected_exit_codes[$i]} ]; then
+ echo "Test case $((i+1)) FAILED"
+ echo "Input: ${test_cases[$i]}"
+ echo "Expected output: '${expected[$i]}'"
+ echo "Got output: '$result'"
+ echo "Expected exit code: ${expected_exit_codes[$i]}"
+ echo "Got exit code: $exit_code"
+ failed=1
+ else
+ echo "Test case $((i+1)) PASSED"
+ fi
+ done
+
+ if [ $failed -eq 0 ]; then
+ echo "All tests passed!"
+ exit 0
+ else
+ echo "Some tests failed!"
+ exit 1
+ fi
+}
+
+# Check for selftest
+if [ "$1" = "--selftest" ]; then
+ run_selftest
+ exit $?
+fi
+
+# Handle --force flag
+force=""
+if [ "$1" = "--force" ]; then
+ force="--force"
+ shift
+fi
+
+# Verify arguments
+if [ $# -eq 0 ]; then
+ usage
+fi
+
+# Skip validation in force mode
+if [ -z "$force" ]; then
+ # Validate that the first argument matches at least one git commit
+ if [ "$(git rev-parse --disambiguate="$1" 2>/dev/null | wc -l)" -eq 0 ]; then
+ echo "Error: '$1' does not match any git commit"
+ exit 1
+ fi
+fi
+
+git_resolve_commit $force "$@"
+exit $?
diff --git a/scripts/kconfig/conf.c b/scripts/kconfig/conf.c
index 8abe57041955..a7b44cd8ae14 100644
--- a/scripts/kconfig/conf.c
+++ b/scripts/kconfig/conf.c
@@ -594,7 +594,7 @@ static void check_conf(struct menu *menu)
default:
if (!conf_cnt++)
printf("*\n* Restart config...\n*\n");
- rootEntry = menu_get_parent_menu(menu);
+ rootEntry = menu_get_menu_or_parent_menu(menu);
conf(rootEntry);
break;
}
diff --git a/scripts/kconfig/confdata.c b/scripts/kconfig/confdata.c
index ac95661a1c9d..9599a0408862 100644
--- a/scripts/kconfig/confdata.c
+++ b/scripts/kconfig/confdata.c
@@ -77,7 +77,7 @@ static bool is_same(const char *file1, const char *file2)
if (map2 == MAP_FAILED)
goto close2;
- if (bcmp(map1, map2, st1.st_size))
+ if (memcmp(map1, map2, st1.st_size))
goto close2;
ret = true;
diff --git a/scripts/kconfig/expr.h b/scripts/kconfig/expr.h
index 21578dcd4292..fe2231e0e6a4 100644
--- a/scripts/kconfig/expr.h
+++ b/scripts/kconfig/expr.h
@@ -205,15 +205,26 @@ struct property {
for (st = sym->prop; st; st = st->next) \
if (st->text)
+enum menu_type {
+ M_CHOICE, // "choice"
+ M_COMMENT, // "comment"
+ M_IF, // "if"
+ M_MENU, // "mainmenu", "menu", "menuconfig"
+ M_NORMAL, // others, i.e., "config"
+};
+
/*
* Represents a node in the menu tree, as seen in e.g. menuconfig (though used
* for all front ends). Each symbol, menu, etc. defined in the Kconfig files
* gets a node. A symbol defined in multiple locations gets one node at each
* location.
*
+ * @type: type of the menu entry
* @choice_members: list of choice members with priority.
*/
struct menu {
+ enum menu_type type;
+
/* The next menu node at the same level */
struct menu *next;
diff --git a/scripts/kconfig/gconf-cfg.sh b/scripts/kconfig/gconf-cfg.sh
index fc954c0538fa..856c692f480c 100755
--- a/scripts/kconfig/gconf-cfg.sh
+++ b/scripts/kconfig/gconf-cfg.sh
@@ -6,7 +6,7 @@ set -eu
cflags=$1
libs=$2
-PKG="gtk+-2.0 gmodule-2.0 libglade-2.0"
+PKG=gtk+-3.0
if [ -z "$(command -v ${HOSTPKG_CONFIG})" ]; then
echo >&2 "*"
@@ -18,18 +18,11 @@ fi
if ! ${HOSTPKG_CONFIG} --exists $PKG; then
echo >&2 "*"
echo >&2 "* Unable to find the GTK+ installation. Please make sure that"
- echo >&2 "* the GTK+ 2.0 development package is correctly installed."
+ echo >&2 "* the GTK 3 development package is correctly installed."
echo >&2 "* You need $PKG"
echo >&2 "*"
exit 1
fi
-if ! ${HOSTPKG_CONFIG} --atleast-version=2.0.0 gtk+-2.0; then
- echo >&2 "*"
- echo >&2 "* GTK+ is present but version >= 2.0.0 is required."
- echo >&2 "*"
- exit 1
-fi
-
${HOSTPKG_CONFIG} --cflags ${PKG} > ${cflags}
${HOSTPKG_CONFIG} --libs ${PKG} > ${libs}
diff --git a/scripts/kconfig/gconf.c b/scripts/kconfig/gconf.c
index c0f46f189060..8b164ccfa008 100644
--- a/scripts/kconfig/gconf.c
+++ b/scripts/kconfig/gconf.c
@@ -7,10 +7,7 @@
#include "lkc.h"
#include "images.h"
-#include <glade/glade.h>
#include <gtk/gtk.h>
-#include <glib.h>
-#include <gdk/gdkkeysyms.h>
#include <stdio.h>
#include <string.h>
@@ -18,7 +15,7 @@
#include <unistd.h>
#include <time.h>
-enum {
+enum view_mode {
SINGLE_VIEW, SPLIT_VIEW, FULL_VIEW
};
@@ -30,29 +27,24 @@ static gint view_mode = FULL_VIEW;
static gboolean show_name = TRUE;
static gboolean show_range = TRUE;
static gboolean show_value = TRUE;
-static gboolean resizeable = FALSE;
static int opt_mode = OPT_NORMAL;
-GtkWidget *main_wnd = NULL;
-GtkWidget *tree1_w = NULL; // left frame
-GtkWidget *tree2_w = NULL; // right frame
-GtkWidget *text_w = NULL;
-GtkWidget *hpaned = NULL;
-GtkWidget *vpaned = NULL;
-GtkWidget *back_btn = NULL;
-GtkWidget *save_btn = NULL;
-GtkWidget *save_menu_item = NULL;
+static GtkWidget *main_wnd;
+static GtkWidget *tree1_w; // left frame
+static GtkWidget *tree2_w; // right frame
+static GtkWidget *text_w;
+static GtkWidget *hpaned;
+static GtkWidget *vpaned;
+static GtkWidget *back_btn, *save_btn, *single_btn, *split_btn, *full_btn;
+static GtkWidget *save_menu_item;
-GtkTextTag *tag1, *tag2;
-GdkColor color;
+static GtkTextTag *tag1, *tag2;
-GtkTreeStore *tree1, *tree2, *tree;
-GtkTreeModel *model1, *model2;
-static GtkTreeIter *parents[256];
-static gint indent;
+static GtkTreeStore *tree1, *tree2;
+static GdkPixbuf *pix_menu;
-static struct menu *current; // current node for SINGLE view
-static struct menu *browsed; // browsed node for SPLIT view
+static struct menu *browsed; // browsed menu for SINGLE/SPLIT view
+static struct menu *selected; // selected entry
enum {
COL_OPTION, COL_NAME, COL_NO, COL_MOD, COL_YES, COL_VALUE,
@@ -61,28 +53,8 @@ enum {
COL_NUMBER
};
-static void display_list(void);
-static void display_tree(struct menu *menu);
-static void display_tree_part(void);
-static void update_tree(struct menu *src, GtkTreeIter * dst);
-
-static void replace_button_icon(GladeXML *xml, GdkDrawable *window,
- GtkStyle *style, gchar *btn_name, gchar **xpm)
-{
- GdkPixmap *pixmap;
- GdkBitmap *mask;
- GtkToolButton *button;
- GtkWidget *image;
-
- pixmap = gdk_pixmap_create_from_xpm_d(window, &mask,
- &style->bg[GTK_STATE_NORMAL],
- xpm);
-
- button = GTK_TOOL_BUTTON(glade_xml_get_widget(xml, btn_name));
- image = gtk_image_new_from_pixmap(pixmap, mask);
- gtk_widget_show(image);
- gtk_tool_button_set_icon_widget(button, image);
-}
+static void display_tree(GtkTreeStore *store, struct menu *menu);
+static void recreate_tree(void);
static void conf_changed(bool dirty)
{
@@ -90,465 +62,373 @@ static void conf_changed(bool dirty)
gtk_widget_set_sensitive(save_menu_item, dirty);
}
-/* Main Window Initialization */
-static void init_main_window(const gchar *glade_file)
+/* Utility Functions */
+
+static void text_insert_msg(const char *title, const char *msg)
{
- GladeXML *xml;
- GtkWidget *widget;
- GtkTextBuffer *txtbuf;
- GtkStyle *style;
+ GtkTextBuffer *buffer;
+ GtkTextIter start, end;
- xml = glade_xml_new(glade_file, "window1", NULL);
- if (!xml)
- g_error("GUI loading failed !\n");
- glade_xml_signal_autoconnect(xml);
+ buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_w));
+ gtk_text_buffer_get_bounds(buffer, &start, &end);
+ gtk_text_buffer_delete(buffer, &start, &end);
+ gtk_text_view_set_left_margin(GTK_TEXT_VIEW(text_w), 15);
- main_wnd = glade_xml_get_widget(xml, "window1");
- hpaned = glade_xml_get_widget(xml, "hpaned1");
- vpaned = glade_xml_get_widget(xml, "vpaned1");
- tree1_w = glade_xml_get_widget(xml, "treeview1");
- tree2_w = glade_xml_get_widget(xml, "treeview2");
- text_w = glade_xml_get_widget(xml, "textview3");
+ gtk_text_buffer_get_end_iter(buffer, &end);
+ gtk_text_buffer_insert_with_tags(buffer, &end, title, -1, tag1,
+ NULL);
+ gtk_text_buffer_insert_at_cursor(buffer, "\n\n", 2);
+ gtk_text_buffer_get_end_iter(buffer, &end);
+ gtk_text_buffer_insert_with_tags(buffer, &end, msg, -1, tag2,
+ NULL);
+}
- back_btn = glade_xml_get_widget(xml, "button1");
- gtk_widget_set_sensitive(back_btn, FALSE);
+static void text_insert_help(struct menu *menu)
+{
+ struct gstr help = str_new();
- widget = glade_xml_get_widget(xml, "show_name1");
- gtk_check_menu_item_set_active((GtkCheckMenuItem *) widget,
- show_name);
+ menu_get_ext_help(menu, &help);
+ text_insert_msg(menu_get_prompt(menu), str_get(&help));
+ str_free(&help);
+}
- widget = glade_xml_get_widget(xml, "show_range1");
- gtk_check_menu_item_set_active((GtkCheckMenuItem *) widget,
- show_range);
+static void _select_menu(GtkTreeView *view, GtkTreeModel *model,
+ GtkTreeIter *parent, struct menu *match)
+{
+ GtkTreeIter iter;
+ gboolean valid;
- widget = glade_xml_get_widget(xml, "show_data1");
- gtk_check_menu_item_set_active((GtkCheckMenuItem *) widget,
- show_value);
+ valid = gtk_tree_model_iter_children(model, &iter, parent);
+ while (valid) {
+ struct menu *menu;
- save_btn = glade_xml_get_widget(xml, "button3");
- save_menu_item = glade_xml_get_widget(xml, "save1");
- conf_set_changed_callback(conf_changed);
+ gtk_tree_model_get(model, &iter, COL_MENU, &menu, -1);
- style = gtk_widget_get_style(main_wnd);
- widget = glade_xml_get_widget(xml, "toolbar1");
+ if (menu == match) {
+ GtkTreeSelection *selection;
+ GtkTreePath *path;
- replace_button_icon(xml, main_wnd->window, style,
- "button4", (gchar **) xpm_single_view);
- replace_button_icon(xml, main_wnd->window, style,
- "button5", (gchar **) xpm_split_view);
- replace_button_icon(xml, main_wnd->window, style,
- "button6", (gchar **) xpm_tree_view);
+ /*
+ * Expand parents to reflect the selection, and
+ * scroll down to it.
+ */
+ path = gtk_tree_model_get_path(model, &iter);
+ gtk_tree_view_expand_to_path(view, path);
+ gtk_tree_view_scroll_to_cell(view, path, NULL, TRUE,
+ 0.5, 0.0);
+ gtk_tree_path_free(path);
- txtbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_w));
- tag1 = gtk_text_buffer_create_tag(txtbuf, "mytag1",
- "foreground", "red",
- "weight", PANGO_WEIGHT_BOLD,
- NULL);
- tag2 = gtk_text_buffer_create_tag(txtbuf, "mytag2",
- /*"style", PANGO_STYLE_OBLIQUE, */
- NULL);
+ selection = gtk_tree_view_get_selection(view);
+ gtk_tree_selection_select_iter(selection, &iter);
- gtk_window_set_title(GTK_WINDOW(main_wnd), rootmenu.prompt->text);
+ text_insert_help(menu);
+ }
+
+ _select_menu(view, model, &iter, match);
- gtk_widget_show(main_wnd);
+ valid = gtk_tree_model_iter_next(model, &iter);
+ }
}
-static void init_tree_model(void)
+static void select_menu(GtkTreeView *view, struct menu *match)
{
- gint i;
-
- tree = tree2 = gtk_tree_store_new(COL_NUMBER,
- G_TYPE_STRING, G_TYPE_STRING,
- G_TYPE_STRING, G_TYPE_STRING,
- G_TYPE_STRING, G_TYPE_STRING,
- G_TYPE_POINTER, GDK_TYPE_COLOR,
- G_TYPE_BOOLEAN, GDK_TYPE_PIXBUF,
- G_TYPE_BOOLEAN, G_TYPE_BOOLEAN,
- G_TYPE_BOOLEAN, G_TYPE_BOOLEAN,
- G_TYPE_BOOLEAN);
- model2 = GTK_TREE_MODEL(tree2);
-
- for (parents[0] = NULL, i = 1; i < 256; i++)
- parents[i] = (GtkTreeIter *) g_malloc(sizeof(GtkTreeIter));
-
- tree1 = gtk_tree_store_new(COL_NUMBER,
- G_TYPE_STRING, G_TYPE_STRING,
- G_TYPE_STRING, G_TYPE_STRING,
- G_TYPE_STRING, G_TYPE_STRING,
- G_TYPE_POINTER, GDK_TYPE_COLOR,
- G_TYPE_BOOLEAN, GDK_TYPE_PIXBUF,
- G_TYPE_BOOLEAN, G_TYPE_BOOLEAN,
- G_TYPE_BOOLEAN, G_TYPE_BOOLEAN,
- G_TYPE_BOOLEAN);
- model1 = GTK_TREE_MODEL(tree1);
+ _select_menu(view, gtk_tree_view_get_model(view), NULL, match);
}
-static void init_left_tree(void)
+static void _update_row_visibility(GtkTreeView *view)
{
- GtkTreeView *view = GTK_TREE_VIEW(tree1_w);
- GtkCellRenderer *renderer;
- GtkTreeSelection *sel;
- GtkTreeViewColumn *column;
-
- gtk_tree_view_set_model(view, model1);
- gtk_tree_view_set_headers_visible(view, TRUE);
- gtk_tree_view_set_rules_hint(view, TRUE);
-
- column = gtk_tree_view_column_new();
- gtk_tree_view_append_column(view, column);
- gtk_tree_view_column_set_title(column, "Options");
-
- renderer = gtk_cell_renderer_toggle_new();
- gtk_tree_view_column_pack_start(GTK_TREE_VIEW_COLUMN(column),
- renderer, FALSE);
- gtk_tree_view_column_set_attributes(GTK_TREE_VIEW_COLUMN(column),
- renderer,
- "active", COL_BTNACT,
- "inconsistent", COL_BTNINC,
- "visible", COL_BTNVIS,
- "radio", COL_BTNRAD, NULL);
- renderer = gtk_cell_renderer_text_new();
- gtk_tree_view_column_pack_start(GTK_TREE_VIEW_COLUMN(column),
- renderer, FALSE);
- gtk_tree_view_column_set_attributes(GTK_TREE_VIEW_COLUMN(column),
- renderer,
- "text", COL_OPTION,
- "foreground-gdk",
- COL_COLOR, NULL);
+ GtkTreeModelFilter *filter = GTK_TREE_MODEL_FILTER(gtk_tree_view_get_model(view));
- sel = gtk_tree_view_get_selection(view);
- gtk_tree_selection_set_mode(sel, GTK_SELECTION_SINGLE);
- gtk_widget_realize(tree1_w);
+ gtk_tree_model_filter_refilter(filter);
}
-static void renderer_edited(GtkCellRendererText * cell,
- const gchar * path_string,
- const gchar * new_text, gpointer user_data);
+static void update_row_visibility(void)
+{
+ if (view_mode == SPLIT_VIEW)
+ _update_row_visibility(GTK_TREE_VIEW(tree1_w));
+ _update_row_visibility(GTK_TREE_VIEW(tree2_w));
+}
-static void init_right_tree(void)
+static void set_node(GtkTreeStore *tree, GtkTreeIter *node, struct menu *menu)
{
- GtkTreeView *view = GTK_TREE_VIEW(tree2_w);
- GtkCellRenderer *renderer;
- GtkTreeSelection *sel;
- GtkTreeViewColumn *column;
- gint i;
+ struct symbol *sym = menu->sym;
+ tristate val;
+ gchar *option;
+ const gchar *_no = "";
+ const gchar *_mod = "";
+ const gchar *_yes = "";
+ const gchar *value = "";
+ GdkRGBA color;
+ gboolean editable = FALSE;
+ gboolean btnvis = FALSE;
+
+ option = g_strdup_printf("%s %s %s %s",
+ menu->type == M_COMMENT ? "***" : "",
+ menu_get_prompt(menu),
+ menu->type == M_COMMENT ? "***" : "",
+ sym && !sym_has_value(sym) ? "(NEW)" : "");
+
+ gdk_rgba_parse(&color, menu_is_visible(menu) ? "Black" : "DarkGray");
- gtk_tree_view_set_model(view, model2);
- gtk_tree_view_set_headers_visible(view, TRUE);
- gtk_tree_view_set_rules_hint(view, TRUE);
+ if (!sym)
+ goto set;
- column = gtk_tree_view_column_new();
- gtk_tree_view_append_column(view, column);
- gtk_tree_view_column_set_title(column, "Options");
+ sym_calc_value(sym);
- renderer = gtk_cell_renderer_pixbuf_new();
- gtk_tree_view_column_pack_start(GTK_TREE_VIEW_COLUMN(column),
- renderer, FALSE);
- gtk_tree_view_column_set_attributes(GTK_TREE_VIEW_COLUMN(column),
- renderer,
- "pixbuf", COL_PIXBUF,
- "visible", COL_PIXVIS, NULL);
- renderer = gtk_cell_renderer_toggle_new();
- gtk_tree_view_column_pack_start(GTK_TREE_VIEW_COLUMN(column),
- renderer, FALSE);
- gtk_tree_view_column_set_attributes(GTK_TREE_VIEW_COLUMN(column),
- renderer,
- "active", COL_BTNACT,
- "inconsistent", COL_BTNINC,
- "visible", COL_BTNVIS,
- "radio", COL_BTNRAD, NULL);
- renderer = gtk_cell_renderer_text_new();
- gtk_tree_view_column_pack_start(GTK_TREE_VIEW_COLUMN(column),
- renderer, FALSE);
- gtk_tree_view_column_set_attributes(GTK_TREE_VIEW_COLUMN(column),
- renderer,
- "text", COL_OPTION,
- "foreground-gdk",
- COL_COLOR, NULL);
+ if (menu->type == M_CHOICE) { // parse children to get a final value
+ struct symbol *def_sym = sym_calc_choice(menu);
+ struct menu *def_menu = NULL;
- renderer = gtk_cell_renderer_text_new();
- gtk_tree_view_insert_column_with_attributes(view, -1,
- "Name", renderer,
- "text", COL_NAME,
- "foreground-gdk",
- COL_COLOR, NULL);
- renderer = gtk_cell_renderer_text_new();
- gtk_tree_view_insert_column_with_attributes(view, -1,
- "N", renderer,
- "text", COL_NO,
- "foreground-gdk",
- COL_COLOR, NULL);
- renderer = gtk_cell_renderer_text_new();
- gtk_tree_view_insert_column_with_attributes(view, -1,
- "M", renderer,
- "text", COL_MOD,
- "foreground-gdk",
- COL_COLOR, NULL);
- renderer = gtk_cell_renderer_text_new();
- gtk_tree_view_insert_column_with_attributes(view, -1,
- "Y", renderer,
- "text", COL_YES,
- "foreground-gdk",
- COL_COLOR, NULL);
- renderer = gtk_cell_renderer_text_new();
- gtk_tree_view_insert_column_with_attributes(view, -1,
- "Value", renderer,
- "text", COL_VALUE,
- "editable",
- COL_EDIT,
- "foreground-gdk",
- COL_COLOR, NULL);
- g_signal_connect(G_OBJECT(renderer), "edited",
- G_CALLBACK(renderer_edited), NULL);
-
- column = gtk_tree_view_get_column(view, COL_NAME);
- gtk_tree_view_column_set_visible(column, show_name);
- column = gtk_tree_view_get_column(view, COL_NO);
- gtk_tree_view_column_set_visible(column, show_range);
- column = gtk_tree_view_get_column(view, COL_MOD);
- gtk_tree_view_column_set_visible(column, show_range);
- column = gtk_tree_view_get_column(view, COL_YES);
- gtk_tree_view_column_set_visible(column, show_range);
- column = gtk_tree_view_get_column(view, COL_VALUE);
- gtk_tree_view_column_set_visible(column, show_value);
-
- if (resizeable) {
- for (i = 0; i < COL_VALUE; i++) {
- column = gtk_tree_view_get_column(view, i);
- gtk_tree_view_column_set_resizable(column, TRUE);
+ for (struct menu *child = menu->list; child; child = child->next) {
+ if (menu_is_visible(child) && child->sym == def_sym)
+ def_menu = child;
}
- }
- sel = gtk_tree_view_get_selection(view);
- gtk_tree_selection_set_mode(sel, GTK_SELECTION_SINGLE);
-}
+ if (def_menu)
+ value = menu_get_prompt(def_menu);
+ goto set;
+ }
-/* Utility Functions */
+ switch (sym_get_type(sym)) {
+ case S_BOOLEAN:
+ case S_TRISTATE:
+ btnvis = TRUE;
-static void text_insert_help(struct menu *menu)
-{
- GtkTextBuffer *buffer;
- GtkTextIter start, end;
- const char *prompt = menu_get_prompt(menu);
- struct gstr help = str_new();
+ val = sym_get_tristate_value(sym);
+ switch (val) {
+ case no:
+ _no = "N";
+ value = "N";
+ break;
+ case mod:
+ _mod = "M";
+ value = "M";
+ break;
+ case yes:
+ _yes = "Y";
+ value = "Y";
+ break;
+ }
- menu_get_ext_help(menu, &help);
+ if (val != no && sym_tristate_within_range(sym, no))
+ _no = "_";
+ if (val != mod && sym_tristate_within_range(sym, mod))
+ _mod = "_";
+ if (val != yes && sym_tristate_within_range(sym, yes))
+ _yes = "_";
+ break;
+ default:
+ value = sym_get_string_value(sym);
+ editable = TRUE;
+ break;
+ }
- buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_w));
- gtk_text_buffer_get_bounds(buffer, &start, &end);
- gtk_text_buffer_delete(buffer, &start, &end);
- gtk_text_view_set_left_margin(GTK_TEXT_VIEW(text_w), 15);
+set:
+ gtk_tree_store_set(tree, node,
+ COL_OPTION, option,
+ COL_NAME, sym ? sym->name : "",
+ COL_NO, _no,
+ COL_MOD, _mod,
+ COL_YES, _yes,
+ COL_VALUE, value,
+ COL_MENU, (gpointer) menu,
+ COL_COLOR, &color,
+ COL_EDIT, editable,
+ COL_PIXBUF, pix_menu,
+ COL_PIXVIS, view_mode == SINGLE_VIEW && menu->type == M_MENU,
+ COL_BTNVIS, btnvis,
+ COL_BTNACT, _yes[0] == 'Y',
+ COL_BTNINC, _mod[0] == 'M',
+ COL_BTNRAD, sym && sym_is_choice_value(sym),
+ -1);
- gtk_text_buffer_get_end_iter(buffer, &end);
- gtk_text_buffer_insert_with_tags(buffer, &end, prompt, -1, tag1,
- NULL);
- gtk_text_buffer_insert_at_cursor(buffer, "\n\n", 2);
- gtk_text_buffer_get_end_iter(buffer, &end);
- gtk_text_buffer_insert_with_tags(buffer, &end, str_get(&help), -1, tag2,
- NULL);
- str_free(&help);
+ g_free(option);
}
-
-static void text_insert_msg(const char *title, const char *message)
+static void _update_tree(GtkTreeStore *store, GtkTreeIter *parent)
{
- GtkTextBuffer *buffer;
- GtkTextIter start, end;
- const char *msg = message;
+ GtkTreeModel *model = GTK_TREE_MODEL(store);
+ GtkTreeIter iter;
+ gboolean valid;
- buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_w));
- gtk_text_buffer_get_bounds(buffer, &start, &end);
- gtk_text_buffer_delete(buffer, &start, &end);
- gtk_text_view_set_left_margin(GTK_TEXT_VIEW(text_w), 15);
+ valid = gtk_tree_model_iter_children(model, &iter, parent);
+ while (valid) {
+ struct menu *menu;
- gtk_text_buffer_get_end_iter(buffer, &end);
- gtk_text_buffer_insert_with_tags(buffer, &end, title, -1, tag1,
- NULL);
- gtk_text_buffer_insert_at_cursor(buffer, "\n\n", 2);
- gtk_text_buffer_get_end_iter(buffer, &end);
- gtk_text_buffer_insert_with_tags(buffer, &end, msg, -1, tag2,
- NULL);
-}
+ gtk_tree_model_get(model, &iter, COL_MENU, &menu, -1);
+ if (menu)
+ set_node(store, &iter, menu);
-/* Main Windows Callbacks */
+ _update_tree(store, &iter);
-void on_save_activate(GtkMenuItem * menuitem, gpointer user_data);
-gboolean on_window1_delete_event(GtkWidget * widget, GdkEvent * event,
- gpointer user_data)
+ valid = gtk_tree_model_iter_next(model, &iter);
+ }
+}
+
+static void update_tree(GtkTreeStore *store)
{
- GtkWidget *dialog, *label;
- gint result;
+ _update_tree(store, NULL);
+ update_row_visibility();
+}
- if (!conf_get_changed())
- return FALSE;
+static void update_trees(void)
+{
+ if (view_mode == SPLIT_VIEW)
+ update_tree(tree1);
+ update_tree(tree2);
+}
- dialog = gtk_dialog_new_with_buttons("Warning !",
- GTK_WINDOW(main_wnd),
- (GtkDialogFlags)
- (GTK_DIALOG_MODAL |
- GTK_DIALOG_DESTROY_WITH_PARENT),
- GTK_STOCK_OK,
- GTK_RESPONSE_YES,
- GTK_STOCK_NO,
- GTK_RESPONSE_NO,
- GTK_STOCK_CANCEL,
- GTK_RESPONSE_CANCEL, NULL);
- gtk_dialog_set_default_response(GTK_DIALOG(dialog),
- GTK_RESPONSE_CANCEL);
+static void set_view_mode(enum view_mode mode)
+{
+ view_mode = mode;
- label = gtk_label_new("\nSave configuration ?\n");
- gtk_container_add(GTK_CONTAINER(GTK_DIALOG(dialog)->vbox), label);
- gtk_widget_show(label);
+ if (mode == SPLIT_VIEW) { // two panes
+ gint w;
- result = gtk_dialog_run(GTK_DIALOG(dialog));
- switch (result) {
- case GTK_RESPONSE_YES:
- on_save_activate(NULL, NULL);
- return FALSE;
- case GTK_RESPONSE_NO:
- return FALSE;
- case GTK_RESPONSE_CANCEL:
- case GTK_RESPONSE_DELETE_EVENT:
- default:
- gtk_widget_destroy(dialog);
- return TRUE;
+ gtk_widget_show(tree1_w);
+ gtk_window_get_default_size(GTK_WINDOW(main_wnd), &w, NULL);
+ gtk_paned_set_position(GTK_PANED(hpaned), w / 2);
+ } else {
+ gtk_widget_hide(tree1_w);
+ gtk_paned_set_position(GTK_PANED(hpaned), 0);
}
- return FALSE;
-}
+ gtk_widget_set_sensitive(single_btn, TRUE);
+ gtk_widget_set_sensitive(split_btn, TRUE);
+ gtk_widget_set_sensitive(full_btn, TRUE);
+ switch (mode) {
+ case SINGLE_VIEW:
+ if (selected)
+ browsed = menu_get_parent_menu(selected) ?: &rootmenu;
+ else
+ browsed = &rootmenu;
+ recreate_tree();
+ text_insert_msg("", "");
+ select_menu(GTK_TREE_VIEW(tree2_w), selected);
+ gtk_widget_set_sensitive(single_btn, FALSE);
+ break;
+ case SPLIT_VIEW:
+ browsed = selected;
+ while (browsed && !(browsed->flags & MENU_ROOT))
+ browsed = browsed->parent;
+ gtk_tree_store_clear(tree1);
+ display_tree(tree1, &rootmenu);
+ gtk_tree_view_expand_all(GTK_TREE_VIEW(tree1_w));
+ gtk_tree_store_clear(tree2);
+ if (browsed)
+ display_tree(tree2, browsed);
+ text_insert_msg("", "");
+ select_menu(GTK_TREE_VIEW(tree1_w), browsed);
+ select_menu(GTK_TREE_VIEW(tree2_w), selected);
+ gtk_widget_set_sensitive(split_btn, FALSE);
+ break;
+ case FULL_VIEW:
+ gtk_tree_store_clear(tree2);
+ display_tree(tree2, &rootmenu);
+ text_insert_msg("", "");
+ select_menu(GTK_TREE_VIEW(tree2_w), selected);
+ gtk_widget_set_sensitive(full_btn, FALSE);
+ break;
+ }
-void on_window1_destroy(GtkObject * object, gpointer user_data)
-{
- gtk_main_quit();
+ gtk_widget_set_sensitive(back_btn,
+ mode == SINGLE_VIEW && browsed != &rootmenu);
}
+/* Menu & Toolbar Callbacks */
-void
-on_window1_size_request(GtkWidget * widget,
- GtkRequisition * requisition, gpointer user_data)
+static void on_load1_activate(GtkMenuItem *menuitem, gpointer user_data)
{
- static gint old_h;
- gint w, h;
-
- if (widget->window == NULL)
- gtk_window_get_default_size(GTK_WINDOW(main_wnd), &w, &h);
- else
- gdk_window_get_size(widget->window, &w, &h);
-
- if (h == old_h)
- return;
- old_h = h;
-
- gtk_paned_set_position(GTK_PANED(vpaned), 2 * h / 3);
-}
+ GtkWidget *dialog;
+ GtkFileChooser *chooser;
+ gint res;
+ dialog = gtk_file_chooser_dialog_new("Load file...",
+ GTK_WINDOW(user_data),
+ GTK_FILE_CHOOSER_ACTION_OPEN,
+ "_Cancel", GTK_RESPONSE_CANCEL,
+ "_Open", GTK_RESPONSE_ACCEPT,
+ NULL);
-/* Menu & Toolbar Callbacks */
+ chooser = GTK_FILE_CHOOSER(dialog);
+ gtk_file_chooser_set_filename(chooser, conf_get_configname());
+ res = gtk_dialog_run(GTK_DIALOG(dialog));
+ if (res == GTK_RESPONSE_ACCEPT) {
+ char *filename;
-static void
-load_filename(GtkFileSelection * file_selector, gpointer user_data)
-{
- const gchar *fn;
+ filename = gtk_file_chooser_get_filename(chooser);
- fn = gtk_file_selection_get_filename(GTK_FILE_SELECTION
- (user_data));
+ if (conf_read(filename))
+ text_insert_msg("Error",
+ "Unable to load configuration!");
+ else
+ update_trees();
- if (conf_read(fn))
- text_insert_msg("Error", "Unable to load configuration !");
- else
- display_tree_part();
-}
+ g_free(filename);
+ }
-void on_load1_activate(GtkMenuItem * menuitem, gpointer user_data)
-{
- GtkWidget *fs;
-
- fs = gtk_file_selection_new("Load file...");
- g_signal_connect(GTK_OBJECT(GTK_FILE_SELECTION(fs)->ok_button),
- "clicked",
- G_CALLBACK(load_filename), (gpointer) fs);
- g_signal_connect_swapped(GTK_OBJECT
- (GTK_FILE_SELECTION(fs)->ok_button),
- "clicked", G_CALLBACK(gtk_widget_destroy),
- (gpointer) fs);
- g_signal_connect_swapped(GTK_OBJECT
- (GTK_FILE_SELECTION(fs)->cancel_button),
- "clicked", G_CALLBACK(gtk_widget_destroy),
- (gpointer) fs);
- gtk_widget_show(fs);
+ gtk_widget_destroy(GTK_WIDGET(dialog));
}
-
-void on_save_activate(GtkMenuItem * menuitem, gpointer user_data)
+static void on_save_activate(GtkMenuItem *menuitem, gpointer user_data)
{
if (conf_write(NULL))
text_insert_msg("Error", "Unable to save configuration !");
conf_write_autoconf(0);
}
-
-static void
-store_filename(GtkFileSelection * file_selector, gpointer user_data)
+static void on_save_as1_activate(GtkMenuItem *menuitem, gpointer user_data)
{
- const gchar *fn;
+ GtkWidget *dialog;
+ GtkFileChooser *chooser;
+ gint res;
- fn = gtk_file_selection_get_filename(GTK_FILE_SELECTION
- (user_data));
+ dialog = gtk_file_chooser_dialog_new("Save file as...",
+ GTK_WINDOW(user_data),
+ GTK_FILE_CHOOSER_ACTION_SAVE,
+ "_Cancel", GTK_RESPONSE_CANCEL,
+ "_Save", GTK_RESPONSE_ACCEPT,
+ NULL);
- if (conf_write(fn))
- text_insert_msg("Error", "Unable to save configuration !");
+ chooser = GTK_FILE_CHOOSER(dialog);
+ gtk_file_chooser_set_filename(chooser, conf_get_configname());
- gtk_widget_destroy(GTK_WIDGET(user_data));
-}
+ res = gtk_dialog_run(GTK_DIALOG(dialog));
+ if (res == GTK_RESPONSE_ACCEPT) {
+ char *filename;
-void on_save_as1_activate(GtkMenuItem * menuitem, gpointer user_data)
-{
- GtkWidget *fs;
-
- fs = gtk_file_selection_new("Save file as...");
- g_signal_connect(GTK_OBJECT(GTK_FILE_SELECTION(fs)->ok_button),
- "clicked",
- G_CALLBACK(store_filename), (gpointer) fs);
- g_signal_connect_swapped(GTK_OBJECT
- (GTK_FILE_SELECTION(fs)->ok_button),
- "clicked", G_CALLBACK(gtk_widget_destroy),
- (gpointer) fs);
- g_signal_connect_swapped(GTK_OBJECT
- (GTK_FILE_SELECTION(fs)->cancel_button),
- "clicked", G_CALLBACK(gtk_widget_destroy),
- (gpointer) fs);
- gtk_widget_show(fs);
-}
+ filename = gtk_file_chooser_get_filename(chooser);
+ if (conf_write(filename))
+ text_insert_msg("Error",
+ "Unable to save configuration !");
-void on_quit1_activate(GtkMenuItem * menuitem, gpointer user_data)
-{
- if (!on_window1_delete_event(NULL, NULL, NULL))
- gtk_widget_destroy(GTK_WIDGET(main_wnd));
-}
+ g_free(filename);
+ }
+ gtk_widget_destroy(dialog);
+}
-void on_show_name1_activate(GtkMenuItem * menuitem, gpointer user_data)
+static void on_show_name1_activate(GtkMenuItem *menuitem, gpointer user_data)
{
GtkTreeViewColumn *col;
- show_name = GTK_CHECK_MENU_ITEM(menuitem)->active;
+ show_name = gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(menuitem));
col = gtk_tree_view_get_column(GTK_TREE_VIEW(tree2_w), COL_NAME);
if (col)
gtk_tree_view_column_set_visible(col, show_name);
}
-
-void on_show_range1_activate(GtkMenuItem * menuitem, gpointer user_data)
+static void on_show_range1_activate(GtkMenuItem *menuitem, gpointer user_data)
{
GtkTreeViewColumn *col;
- show_range = GTK_CHECK_MENU_ITEM(menuitem)->active;
+ show_range = gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(menuitem));
col = gtk_tree_view_get_column(GTK_TREE_VIEW(tree2_w), COL_NO);
if (col)
gtk_tree_view_column_set_visible(col, show_range);
@@ -561,46 +441,38 @@ void on_show_range1_activate(GtkMenuItem * menuitem, gpointer user_data)
}
-
-void on_show_data1_activate(GtkMenuItem * menuitem, gpointer user_data)
+static void on_show_data1_activate(GtkMenuItem *menuitem, gpointer user_data)
{
GtkTreeViewColumn *col;
- show_value = GTK_CHECK_MENU_ITEM(menuitem)->active;
+ show_value = gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(menuitem));
col = gtk_tree_view_get_column(GTK_TREE_VIEW(tree2_w), COL_VALUE);
if (col)
gtk_tree_view_column_set_visible(col, show_value);
}
-
-void
-on_set_option_mode1_activate(GtkMenuItem *menuitem, gpointer user_data)
+static void on_set_option_mode1_activate(GtkMenuItem *menuitem,
+ gpointer user_data)
{
opt_mode = OPT_NORMAL;
- gtk_tree_store_clear(tree2);
- display_tree(&rootmenu); /* instead of update_tree to speed-up */
+ update_row_visibility();
}
-
-void
-on_set_option_mode2_activate(GtkMenuItem *menuitem, gpointer user_data)
+static void on_set_option_mode2_activate(GtkMenuItem *menuitem,
+ gpointer user_data)
{
opt_mode = OPT_ALL;
- gtk_tree_store_clear(tree2);
- display_tree(&rootmenu); /* instead of update_tree to speed-up */
+ update_row_visibility();
}
-
-void
-on_set_option_mode3_activate(GtkMenuItem *menuitem, gpointer user_data)
+static void on_set_option_mode3_activate(GtkMenuItem *menuitem,
+ gpointer user_data)
{
opt_mode = OPT_PROMPT;
- gtk_tree_store_clear(tree2);
- display_tree(&rootmenu); /* instead of update_tree to speed-up */
+ update_row_visibility();
}
-
-void on_introduction1_activate(GtkMenuItem * menuitem, gpointer user_data)
+static void on_introduction1_activate(GtkMenuItem *menuitem, gpointer user_data)
{
GtkWidget *dialog;
const gchar *intro_text =
@@ -621,14 +493,11 @@ void on_introduction1_activate(GtkMenuItem * menuitem, gpointer user_data)
GTK_DIALOG_DESTROY_WITH_PARENT,
GTK_MESSAGE_INFO,
GTK_BUTTONS_CLOSE, "%s", intro_text);
- g_signal_connect_swapped(GTK_OBJECT(dialog), "response",
- G_CALLBACK(gtk_widget_destroy),
- GTK_OBJECT(dialog));
- gtk_widget_show_all(dialog);
+ gtk_dialog_run(GTK_DIALOG(dialog));
+ gtk_widget_destroy(dialog);
}
-
-void on_about1_activate(GtkMenuItem * menuitem, gpointer user_data)
+static void on_about1_activate(GtkMenuItem *menuitem, gpointer user_data)
{
GtkWidget *dialog;
const gchar *about_text =
@@ -638,15 +507,16 @@ void on_about1_activate(GtkMenuItem * menuitem, gpointer user_data)
dialog = gtk_message_dialog_new(GTK_WINDOW(main_wnd),
GTK_DIALOG_DESTROY_WITH_PARENT,
GTK_MESSAGE_INFO,
- GTK_BUTTONS_CLOSE, "%s", about_text);
- g_signal_connect_swapped(GTK_OBJECT(dialog), "response",
- G_CALLBACK(gtk_widget_destroy),
- GTK_OBJECT(dialog));
- gtk_widget_show_all(dialog);
+ GTK_BUTTONS_CLOSE, "%s\nGTK version: %d.%d.%d",
+ about_text,
+ gtk_get_major_version(),
+ gtk_get_minor_version(),
+ gtk_get_micro_version());
+ gtk_dialog_run(GTK_DIALOG(dialog));
+ gtk_widget_destroy(dialog);
}
-
-void on_license1_activate(GtkMenuItem * menuitem, gpointer user_data)
+static void on_license1_activate(GtkMenuItem *menuitem, gpointer user_data)
{
GtkWidget *dialog;
const gchar *license_text =
@@ -658,81 +528,127 @@ void on_license1_activate(GtkMenuItem * menuitem, gpointer user_data)
GTK_DIALOG_DESTROY_WITH_PARENT,
GTK_MESSAGE_INFO,
GTK_BUTTONS_CLOSE, "%s", license_text);
- g_signal_connect_swapped(GTK_OBJECT(dialog), "response",
- G_CALLBACK(gtk_widget_destroy),
- GTK_OBJECT(dialog));
- gtk_widget_show_all(dialog);
+ gtk_dialog_run(GTK_DIALOG(dialog));
+ gtk_widget_destroy(dialog);
}
-
-void on_back_clicked(GtkButton * button, gpointer user_data)
+/* toolbar handlers */
+static void on_back_clicked(GtkButton *button, gpointer user_data)
{
- enum prop_type ptype;
+ browsed = menu_get_parent_menu(browsed) ?: &rootmenu;
- current = current->parent;
- ptype = current->prompt ? current->prompt->type : P_UNKNOWN;
- if (ptype != P_MENU)
- current = current->parent;
- display_tree_part();
+ recreate_tree();
- if (current == &rootmenu)
+ if (browsed == &rootmenu)
gtk_widget_set_sensitive(back_btn, FALSE);
}
-
-void on_load_clicked(GtkButton * button, gpointer user_data)
+static void on_load_clicked(GtkButton *button, gpointer user_data)
{
on_load1_activate(NULL, user_data);
}
-
-void on_single_clicked(GtkButton * button, gpointer user_data)
+static void on_save_clicked(GtkButton *button, gpointer user_data)
{
- view_mode = SINGLE_VIEW;
- gtk_widget_hide(tree1_w);
- current = &rootmenu;
- display_tree_part();
+ on_save_activate(NULL, user_data);
}
+static void on_single_clicked(GtkButton *button, gpointer user_data)
+{
+ set_view_mode(SINGLE_VIEW);
+}
-void on_split_clicked(GtkButton * button, gpointer user_data)
+static void on_split_clicked(GtkButton *button, gpointer user_data)
{
- gint w, h;
- view_mode = SPLIT_VIEW;
- gtk_widget_show(tree1_w);
- gtk_window_get_default_size(GTK_WINDOW(main_wnd), &w, &h);
- gtk_paned_set_position(GTK_PANED(hpaned), w / 2);
- if (tree2)
- gtk_tree_store_clear(tree2);
- display_list();
+ set_view_mode(SPLIT_VIEW);
+}
- /* Disable back btn, like in full mode. */
- gtk_widget_set_sensitive(back_btn, FALSE);
+static void on_full_clicked(GtkButton *button, gpointer user_data)
+{
+ set_view_mode(FULL_VIEW);
}
+static void on_collapse_clicked(GtkButton *button, gpointer user_data)
+{
+ gtk_tree_view_collapse_all(GTK_TREE_VIEW(tree2_w));
+}
-void on_full_clicked(GtkButton * button, gpointer user_data)
+static void on_expand_clicked(GtkButton *button, gpointer user_data)
{
- view_mode = FULL_VIEW;
- gtk_widget_hide(tree1_w);
- if (tree2)
- gtk_tree_store_clear(tree2);
- display_tree(&rootmenu);
- gtk_widget_set_sensitive(back_btn, FALSE);
+ gtk_tree_view_expand_all(GTK_TREE_VIEW(tree2_w));
}
+/* Main Windows Callbacks */
-void on_collapse_clicked(GtkButton * button, gpointer user_data)
+static void on_window1_destroy(GtkWidget *widget, gpointer user_data)
{
- gtk_tree_view_collapse_all(GTK_TREE_VIEW(tree2_w));
+ gtk_main_quit();
}
+static gboolean on_window1_configure(GtkWidget *self,
+ GdkEventConfigure *event,
+ gpointer user_data)
+{
+ gtk_paned_set_position(GTK_PANED(vpaned), 2 * event->height / 3);
+ return FALSE;
+}
-void on_expand_clicked(GtkButton * button, gpointer user_data)
+static gboolean on_window1_delete_event(GtkWidget *widget, GdkEvent *event,
+ gpointer user_data)
{
- gtk_tree_view_expand_all(GTK_TREE_VIEW(tree2_w));
+ GtkWidget *dialog, *label, *content_area;
+ gint result;
+ gint ret = FALSE;
+
+ if (!conf_get_changed())
+ return FALSE;
+
+ dialog = gtk_dialog_new_with_buttons("Warning !",
+ GTK_WINDOW(main_wnd),
+ (GtkDialogFlags)
+ (GTK_DIALOG_MODAL |
+ GTK_DIALOG_DESTROY_WITH_PARENT),
+ "_OK",
+ GTK_RESPONSE_YES,
+ "_No",
+ GTK_RESPONSE_NO,
+ "_Cancel",
+ GTK_RESPONSE_CANCEL, NULL);
+ gtk_dialog_set_default_response(GTK_DIALOG(dialog),
+ GTK_RESPONSE_CANCEL);
+
+ label = gtk_label_new("\nSave configuration ?\n");
+ content_area = gtk_dialog_get_content_area(GTK_DIALOG(dialog));
+ gtk_container_add(GTK_CONTAINER(content_area), label);
+ gtk_widget_show(label);
+
+ result = gtk_dialog_run(GTK_DIALOG(dialog));
+ switch (result) {
+ case GTK_RESPONSE_YES:
+ on_save_activate(NULL, NULL);
+ break;
+ case GTK_RESPONSE_NO:
+ break;
+ case GTK_RESPONSE_CANCEL:
+ case GTK_RESPONSE_DELETE_EVENT:
+ default:
+ ret = TRUE;
+ break;
+ }
+
+ gtk_widget_destroy(dialog);
+
+ if (!ret)
+ g_object_unref(pix_menu);
+
+ return ret;
}
+static void on_quit1_activate(GtkMenuItem *menuitem, gpointer user_data)
+{
+ if (!on_window1_delete_event(NULL, NULL, NULL))
+ gtk_widget_destroy(GTK_WIDGET(main_wnd));
+}
/* CTree Callbacks */
@@ -741,25 +657,28 @@ static void renderer_edited(GtkCellRendererText * cell,
const gchar * path_string,
const gchar * new_text, gpointer user_data)
{
+ GtkTreeView *view = GTK_TREE_VIEW(user_data);
+ GtkTreeModel *model = gtk_tree_view_get_model(view);
GtkTreePath *path = gtk_tree_path_new_from_string(path_string);
GtkTreeIter iter;
const char *old_def, *new_def;
struct menu *menu;
struct symbol *sym;
- if (!gtk_tree_model_get_iter(model2, &iter, path))
- return;
+ if (!gtk_tree_model_get_iter(model, &iter, path))
+ goto free;
- gtk_tree_model_get(model2, &iter, COL_MENU, &menu, -1);
+ gtk_tree_model_get(model, &iter, COL_MENU, &menu, -1);
sym = menu->sym;
- gtk_tree_model_get(model2, &iter, COL_VALUE, &old_def, -1);
+ gtk_tree_model_get(model, &iter, COL_VALUE, &old_def, -1);
new_def = new_text;
sym_set_string_value(sym, new_def);
- update_tree(&rootmenu, NULL);
+ update_trees();
+free:
gtk_tree_path_free(path);
}
@@ -787,14 +706,7 @@ static void change_sym_value(struct menu *menu, gint col)
if (!sym_tristate_within_range(sym, newval))
newval = yes;
sym_set_tristate_value(sym, newval);
- if (view_mode == FULL_VIEW)
- update_tree(&rootmenu, NULL);
- else if (view_mode == SPLIT_VIEW) {
- update_tree(browsed, NULL);
- display_list();
- }
- else if (view_mode == SINGLE_VIEW)
- display_tree_part(); //fixme: keep exp/coll
+ update_trees();
break;
case S_INT:
case S_HEX:
@@ -810,14 +722,7 @@ static void toggle_sym_value(struct menu *menu)
return;
sym_toggle_tristate_value(menu->sym);
- if (view_mode == FULL_VIEW)
- update_tree(&rootmenu, NULL);
- else if (view_mode == SPLIT_VIEW) {
- update_tree(browsed, NULL);
- display_list();
- }
- else if (view_mode == SINGLE_VIEW)
- display_tree_part(); //fixme: keep exp/coll
+ update_trees();
}
static gint column2index(GtkTreeViewColumn * column)
@@ -837,43 +742,39 @@ static gint column2index(GtkTreeViewColumn * column)
/* User click: update choice (full) or goes down (single) */
-gboolean
-on_treeview2_button_press_event(GtkWidget * widget,
- GdkEventButton * event, gpointer user_data)
+static gboolean on_treeview2_button_press_event(GtkWidget *widget,
+ GdkEventButton *event,
+ gpointer user_data)
{
GtkTreeView *view = GTK_TREE_VIEW(widget);
+ GtkTreeModel *model = gtk_tree_view_get_model(view);
GtkTreePath *path;
GtkTreeViewColumn *column;
GtkTreeIter iter;
struct menu *menu;
gint col;
-
-#if GTK_CHECK_VERSION(2,1,4) // bug in ctree with earlier version of GTK
gint tx = (gint) event->x;
gint ty = (gint) event->y;
- gint cx, cy;
- gtk_tree_view_get_path_at_pos(view, tx, ty, &path, &column, &cx,
- &cy);
-#else
- gtk_tree_view_get_cursor(view, &path, &column);
-#endif
+ gtk_tree_view_get_path_at_pos(view, tx, ty, &path, &column, NULL, NULL);
if (path == NULL)
return FALSE;
- if (!gtk_tree_model_get_iter(model2, &iter, path))
+ if (!gtk_tree_model_get_iter(model, &iter, path))
return FALSE;
- gtk_tree_model_get(model2, &iter, COL_MENU, &menu, -1);
+ gtk_tree_model_get(model, &iter, COL_MENU, &menu, -1);
+
+ selected = menu;
col = column2index(column);
if (event->type == GDK_2BUTTON_PRESS) {
enum prop_type ptype;
ptype = menu->prompt ? menu->prompt->type : P_UNKNOWN;
- if (ptype == P_MENU && view_mode != FULL_VIEW && col == COL_OPTION) {
+ if (ptype == P_MENU && view_mode == SINGLE_VIEW && col == COL_OPTION) {
// goes down into menu
- current = menu;
- display_tree_part();
+ browsed = menu;
+ recreate_tree();
gtk_widget_set_sensitive(back_btn, TRUE);
} else if (col == COL_OPTION) {
toggle_sym_value(menu);
@@ -894,35 +795,31 @@ on_treeview2_button_press_event(GtkWidget * widget,
}
/* Key pressed: update choice */
-gboolean
-on_treeview2_key_press_event(GtkWidget * widget,
- GdkEventKey * event, gpointer user_data)
+static gboolean on_treeview2_key_press_event(GtkWidget *widget,
+ GdkEventKey *event,
+ gpointer user_data)
{
GtkTreeView *view = GTK_TREE_VIEW(widget);
+ GtkTreeModel *model = gtk_tree_view_get_model(view);
GtkTreePath *path;
- GtkTreeViewColumn *column;
GtkTreeIter iter;
struct menu *menu;
gint col;
- gtk_tree_view_get_cursor(view, &path, &column);
+ gtk_tree_view_get_cursor(view, &path, NULL);
if (path == NULL)
return FALSE;
- if (event->keyval == GDK_space) {
+ if (event->keyval == GDK_KEY_space) {
if (gtk_tree_view_row_expanded(view, path))
gtk_tree_view_collapse_row(view, path);
else
gtk_tree_view_expand_row(view, path, FALSE);
return TRUE;
}
- if (event->keyval == GDK_KP_Enter) {
- }
- if (widget == tree1_w)
- return FALSE;
- gtk_tree_model_get_iter(model2, &iter, path);
- gtk_tree_model_get(model2, &iter, COL_MENU, &menu, -1);
+ gtk_tree_model_get_iter(model, &iter, path);
+ gtk_tree_model_get(model, &iter, COL_MENU, &menu, -1);
if (!strcasecmp(event->string, "n"))
col = COL_NO;
@@ -939,448 +836,449 @@ on_treeview2_key_press_event(GtkWidget * widget,
/* Row selection changed: update help */
-void
-on_treeview2_cursor_changed(GtkTreeView * treeview, gpointer user_data)
+static void on_treeview2_cursor_changed(GtkTreeView *treeview,
+ gpointer user_data)
{
+ GtkTreeModel *model = gtk_tree_view_get_model(treeview);
GtkTreeSelection *selection;
GtkTreeIter iter;
struct menu *menu;
selection = gtk_tree_view_get_selection(treeview);
- if (gtk_tree_selection_get_selected(selection, &model2, &iter)) {
- gtk_tree_model_get(model2, &iter, COL_MENU, &menu, -1);
+ if (gtk_tree_selection_get_selected(selection, &model, &iter)) {
+ gtk_tree_model_get(model, &iter, COL_MENU, &menu, -1);
text_insert_help(menu);
}
}
/* User click: display sub-tree in the right frame. */
-gboolean
-on_treeview1_button_press_event(GtkWidget * widget,
- GdkEventButton * event, gpointer user_data)
+static gboolean on_treeview1_button_press_event(GtkWidget *widget,
+ GdkEventButton *event,
+ gpointer user_data)
{
GtkTreeView *view = GTK_TREE_VIEW(widget);
+ GtkTreeModel *model = gtk_tree_view_get_model(view);
GtkTreePath *path;
- GtkTreeViewColumn *column;
GtkTreeIter iter;
struct menu *menu;
-
gint tx = (gint) event->x;
gint ty = (gint) event->y;
- gint cx, cy;
- gtk_tree_view_get_path_at_pos(view, tx, ty, &path, &column, &cx,
- &cy);
+ gtk_tree_view_get_path_at_pos(view, tx, ty, &path, NULL, NULL, NULL);
if (path == NULL)
return FALSE;
- gtk_tree_model_get_iter(model1, &iter, path);
- gtk_tree_model_get(model1, &iter, COL_MENU, &menu, -1);
+ gtk_tree_model_get_iter(model, &iter, path);
+ gtk_tree_model_get(model, &iter, COL_MENU, &menu, -1);
- if (event->type == GDK_2BUTTON_PRESS) {
+ if (event->type == GDK_2BUTTON_PRESS)
toggle_sym_value(menu);
- current = menu;
- display_tree_part();
- } else {
+
+ selected = menu;
+
+ if (menu->type == M_MENU) {
browsed = menu;
- display_tree_part();
+ recreate_tree();
}
- gtk_widget_realize(tree2_w);
gtk_tree_view_set_cursor(view, path, NULL, FALSE);
gtk_widget_grab_focus(tree2_w);
return FALSE;
}
-
-/* Fill a row of strings */
-static gchar **fill_row(struct menu *menu)
+/* Display the whole tree (single/split/full view) */
+static void _display_tree(GtkTreeStore *tree, struct menu *menu,
+ GtkTreeIter *parent)
{
- static gchar *row[COL_NUMBER];
- struct symbol *sym = menu->sym;
- const char *def;
- int stype;
- tristate val;
- enum prop_type ptype;
- int i;
-
- for (i = COL_OPTION; i <= COL_COLOR; i++)
- g_free(row[i]);
- bzero(row, sizeof(row));
-
- ptype = menu->prompt ? menu->prompt->type : P_UNKNOWN;
-
- row[COL_OPTION] =
- g_strdup_printf("%s %s %s %s",
- ptype == P_COMMENT ? "***" : "",
- menu_get_prompt(menu),
- ptype == P_COMMENT ? "***" : "",
- sym && !sym_has_value(sym) ? "(NEW)" : "");
-
- if (opt_mode == OPT_ALL && !menu_is_visible(menu))
- row[COL_COLOR] = g_strdup("DarkGray");
- else if (opt_mode == OPT_PROMPT &&
- menu_has_prompt(menu) && !menu_is_visible(menu))
- row[COL_COLOR] = g_strdup("DarkGray");
- else
- row[COL_COLOR] = g_strdup("Black");
-
- switch (ptype) {
- case P_MENU:
- row[COL_PIXBUF] = (gchar *) xpm_menu;
- if (view_mode == SINGLE_VIEW)
- row[COL_PIXVIS] = GINT_TO_POINTER(TRUE);
- row[COL_BTNVIS] = GINT_TO_POINTER(FALSE);
- break;
- case P_COMMENT:
- row[COL_PIXBUF] = (gchar *) xpm_void;
- row[COL_PIXVIS] = GINT_TO_POINTER(FALSE);
- row[COL_BTNVIS] = GINT_TO_POINTER(FALSE);
- break;
- default:
- row[COL_PIXBUF] = (gchar *) xpm_void;
- row[COL_PIXVIS] = GINT_TO_POINTER(FALSE);
- row[COL_BTNVIS] = GINT_TO_POINTER(TRUE);
- break;
- }
-
- if (!sym)
- return row;
- row[COL_NAME] = g_strdup(sym->name);
-
- sym_calc_value(sym);
- menu->flags &= ~MENU_CHANGED;
-
- if (sym_is_choice(sym)) { // parse childs for getting final value
- struct menu *child;
- struct symbol *def_sym = sym_calc_choice(menu);
- struct menu *def_menu = NULL;
+ struct menu *child;
+ GtkTreeIter iter;
- for (child = menu->list; child; child = child->next) {
- if (menu_is_visible(child)
- && child->sym == def_sym)
- def_menu = child;
- }
+ for (child = menu->list; child; child = child->next) {
+ /*
+ * REVISIT:
+ * menu_finalize() creates empty "if" entries.
+ * Do not confuse gtk_tree_model_get(), which would otherwise
+ * return "if" menu entry.
+ */
+ if (child->type == M_IF)
+ continue;
- if (def_menu)
- row[COL_VALUE] =
- g_strdup(menu_get_prompt(def_menu));
+ if ((view_mode == SPLIT_VIEW)
+ && !(child->flags & MENU_ROOT) && (tree == tree1))
+ continue;
- row[COL_BTNVIS] = GINT_TO_POINTER(FALSE);
- return row;
- }
- if (sym_is_choice_value(sym))
- row[COL_BTNRAD] = GINT_TO_POINTER(TRUE);
+ if ((view_mode == SPLIT_VIEW) && (child->flags & MENU_ROOT)
+ && (tree == tree2))
+ continue;
- stype = sym_get_type(sym);
- switch (stype) {
- case S_BOOLEAN:
- case S_TRISTATE:
- val = sym_get_tristate_value(sym);
- switch (val) {
- case no:
- row[COL_NO] = g_strdup("N");
- row[COL_VALUE] = g_strdup("N");
- row[COL_BTNACT] = GINT_TO_POINTER(FALSE);
- row[COL_BTNINC] = GINT_TO_POINTER(FALSE);
- break;
- case mod:
- row[COL_MOD] = g_strdup("M");
- row[COL_VALUE] = g_strdup("M");
- row[COL_BTNINC] = GINT_TO_POINTER(TRUE);
- break;
- case yes:
- row[COL_YES] = g_strdup("Y");
- row[COL_VALUE] = g_strdup("Y");
- row[COL_BTNACT] = GINT_TO_POINTER(TRUE);
- row[COL_BTNINC] = GINT_TO_POINTER(FALSE);
- break;
- }
+ gtk_tree_store_append(tree, &iter, parent);
+ set_node(tree, &iter, child);
- if (val != no && sym_tristate_within_range(sym, no))
- row[COL_NO] = g_strdup("_");
- if (val != mod && sym_tristate_within_range(sym, mod))
- row[COL_MOD] = g_strdup("_");
- if (val != yes && sym_tristate_within_range(sym, yes))
- row[COL_YES] = g_strdup("_");
- break;
- case S_INT:
- case S_HEX:
- case S_STRING:
- def = sym_get_string_value(sym);
- row[COL_VALUE] = g_strdup(def);
- row[COL_EDIT] = GINT_TO_POINTER(TRUE);
- row[COL_BTNVIS] = GINT_TO_POINTER(FALSE);
- break;
+ if (view_mode != SINGLE_VIEW || child->type != M_MENU)
+ _display_tree(tree, child, &iter);
}
+}
- return row;
+static void display_tree(GtkTreeStore *store, struct menu *menu)
+{
+ _display_tree(store, menu, NULL);
}
+/* Recreate the tree store starting at 'browsed' node */
+static void recreate_tree(void)
+{
+ gtk_tree_store_clear(tree2);
+ display_tree(tree2, browsed);
+ gtk_tree_view_expand_all(GTK_TREE_VIEW(tree2_w));
+}
-/* Set the node content with a row of strings */
-static void set_node(GtkTreeIter * node, struct menu *menu, gchar ** row)
+static void fixup_rootmenu(struct menu *menu)
{
- GdkColor color;
- gboolean success;
- GdkPixbuf *pix;
+ struct menu *child;
+ static int menu_cnt = 0;
- pix = gdk_pixbuf_new_from_xpm_data((const char **)
- row[COL_PIXBUF]);
+ menu->flags |= MENU_ROOT;
+ for (child = menu->list; child; child = child->next) {
+ if (child->prompt && child->prompt->type == P_MENU) {
+ menu_cnt++;
+ fixup_rootmenu(child);
+ menu_cnt--;
+ } else if (!menu_cnt)
+ fixup_rootmenu(child);
+ }
+}
- gdk_color_parse(row[COL_COLOR], &color);
- gdk_colormap_alloc_colors(gdk_colormap_get_system(), &color, 1,
- FALSE, FALSE, &success);
+/* Main Window Initialization */
+static void replace_button_icon(GtkWidget *widget, const char * const xpm[])
+{
+ GdkPixbuf *pixbuf;
+ GtkWidget *image;
- gtk_tree_store_set(tree, node,
- COL_OPTION, row[COL_OPTION],
- COL_NAME, row[COL_NAME],
- COL_NO, row[COL_NO],
- COL_MOD, row[COL_MOD],
- COL_YES, row[COL_YES],
- COL_VALUE, row[COL_VALUE],
- COL_MENU, (gpointer) menu,
- COL_COLOR, &color,
- COL_EDIT, GPOINTER_TO_INT(row[COL_EDIT]),
- COL_PIXBUF, pix,
- COL_PIXVIS, GPOINTER_TO_INT(row[COL_PIXVIS]),
- COL_BTNVIS, GPOINTER_TO_INT(row[COL_BTNVIS]),
- COL_BTNACT, GPOINTER_TO_INT(row[COL_BTNACT]),
- COL_BTNINC, GPOINTER_TO_INT(row[COL_BTNINC]),
- COL_BTNRAD, GPOINTER_TO_INT(row[COL_BTNRAD]),
- -1);
+ pixbuf = gdk_pixbuf_new_from_xpm_data((const char **)xpm);
+ image = gtk_image_new_from_pixbuf(pixbuf);
+ g_object_unref(pixbuf);
- g_object_unref(pix);
+ gtk_widget_show(image);
+ gtk_tool_button_set_icon_widget(GTK_TOOL_BUTTON(widget), image);
}
-
-/* Add a node to the tree */
-static void place_node(struct menu *menu, char **row)
+static void init_main_window(const gchar *glade_file)
{
- GtkTreeIter *parent = parents[indent - 1];
- GtkTreeIter *node = parents[indent];
+ GtkBuilder *builder;
+ GtkWidget *widget;
+ GtkTextBuffer *txtbuf;
- gtk_tree_store_append(tree, node, parent);
- set_node(node, menu, row);
-}
+ builder = gtk_builder_new_from_file(glade_file);
+ if (!builder)
+ g_error("GUI loading failed !\n");
+ main_wnd = GTK_WIDGET(gtk_builder_get_object(builder, "window1"));
+ g_signal_connect(main_wnd, "destroy",
+ G_CALLBACK(on_window1_destroy), NULL);
+ g_signal_connect(main_wnd, "configure-event",
+ G_CALLBACK(on_window1_configure), NULL);
+ g_signal_connect(main_wnd, "delete-event",
+ G_CALLBACK(on_window1_delete_event), NULL);
+
+ hpaned = GTK_WIDGET(gtk_builder_get_object(builder, "hpaned1"));
+ vpaned = GTK_WIDGET(gtk_builder_get_object(builder, "vpaned1"));
+ tree1_w = GTK_WIDGET(gtk_builder_get_object(builder, "treeview1"));
+ g_signal_connect(tree1_w, "cursor-changed",
+ G_CALLBACK(on_treeview2_cursor_changed), NULL);
+ g_signal_connect(tree1_w, "button-press-event",
+ G_CALLBACK(on_treeview1_button_press_event), NULL);
+ g_signal_connect(tree1_w, "key-press-event",
+ G_CALLBACK(on_treeview2_key_press_event), NULL);
+
+ tree2_w = GTK_WIDGET(gtk_builder_get_object(builder, "treeview2"));
+ g_signal_connect(tree2_w, "cursor-changed",
+ G_CALLBACK(on_treeview2_cursor_changed), NULL);
+ g_signal_connect(tree2_w, "button-press-event",
+ G_CALLBACK(on_treeview2_button_press_event), NULL);
+ g_signal_connect(tree2_w, "key-press-event",
+ G_CALLBACK(on_treeview2_key_press_event), NULL);
+
+ text_w = GTK_WIDGET(gtk_builder_get_object(builder, "textview3"));
+
+ /* menubar */
+ widget = GTK_WIDGET(gtk_builder_get_object(builder, "load1"));
+ g_signal_connect(widget, "activate",
+ G_CALLBACK(on_load1_activate), NULL);
+
+ save_menu_item = GTK_WIDGET(gtk_builder_get_object(builder, "save1"));
+ g_signal_connect(save_menu_item, "activate",
+ G_CALLBACK(on_save_activate), NULL);
+
+ widget = GTK_WIDGET(gtk_builder_get_object(builder, "save_as1"));
+ g_signal_connect(widget, "activate",
+ G_CALLBACK(on_save_as1_activate), NULL);
+
+ widget = GTK_WIDGET(gtk_builder_get_object(builder, "quit1"));
+ g_signal_connect(widget, "activate",
+ G_CALLBACK(on_quit1_activate), NULL);
+
+ widget = GTK_WIDGET(gtk_builder_get_object(builder, "show_name1"));
+ g_signal_connect(widget, "activate",
+ G_CALLBACK(on_show_name1_activate), NULL);
+ gtk_check_menu_item_set_active((GtkCheckMenuItem *) widget,
+ show_name);
-/* Find a node in the GTK+ tree */
-static GtkTreeIter found;
+ widget = GTK_WIDGET(gtk_builder_get_object(builder, "show_range1"));
+ g_signal_connect(widget, "activate",
+ G_CALLBACK(on_show_range1_activate), NULL);
+ gtk_check_menu_item_set_active((GtkCheckMenuItem *) widget,
+ show_range);
-/*
- * Find a menu in the GtkTree starting at parent.
- */
-static GtkTreeIter *gtktree_iter_find_node(GtkTreeIter *parent,
- struct menu *tofind)
-{
- GtkTreeIter iter;
- GtkTreeIter *child = &iter;
- gboolean valid;
- GtkTreeIter *ret;
+ widget = GTK_WIDGET(gtk_builder_get_object(builder, "show_data1"));
+ g_signal_connect(widget, "activate",
+ G_CALLBACK(on_show_data1_activate), NULL);
+ gtk_check_menu_item_set_active((GtkCheckMenuItem *) widget,
+ show_value);
- valid = gtk_tree_model_iter_children(model2, child, parent);
- while (valid) {
- struct menu *menu;
+ widget = GTK_WIDGET(gtk_builder_get_object(builder, "set_option_mode1"));
+ g_signal_connect(widget, "activate",
+ G_CALLBACK(on_set_option_mode1_activate), NULL);
- gtk_tree_model_get(model2, child, 6, &menu, -1);
+ widget = GTK_WIDGET(gtk_builder_get_object(builder, "set_option_mode2"));
+ g_signal_connect(widget, "activate",
+ G_CALLBACK(on_set_option_mode2_activate), NULL);
- if (menu == tofind) {
- memcpy(&found, child, sizeof(GtkTreeIter));
- return &found;
- }
+ widget = GTK_WIDGET(gtk_builder_get_object(builder, "set_option_mode3"));
+ g_signal_connect(widget, "activate",
+ G_CALLBACK(on_set_option_mode3_activate), NULL);
- ret = gtktree_iter_find_node(child, tofind);
- if (ret)
- return ret;
+ widget = GTK_WIDGET(gtk_builder_get_object(builder, "introduction1"));
+ g_signal_connect(widget, "activate",
+ G_CALLBACK(on_introduction1_activate), NULL);
- valid = gtk_tree_model_iter_next(model2, child);
- }
+ widget = GTK_WIDGET(gtk_builder_get_object(builder, "about1"));
+ g_signal_connect(widget, "activate",
+ G_CALLBACK(on_about1_activate), NULL);
- return NULL;
-}
+ widget = GTK_WIDGET(gtk_builder_get_object(builder, "license1"));
+ g_signal_connect(widget, "activate",
+ G_CALLBACK(on_license1_activate), NULL);
+ /* toolbar */
+ back_btn = GTK_WIDGET(gtk_builder_get_object(builder, "button1"));
+ g_signal_connect(back_btn, "clicked",
+ G_CALLBACK(on_back_clicked), NULL);
+ gtk_widget_set_sensitive(back_btn, FALSE);
-/*
- * Update the tree by adding/removing entries
- * Does not change other nodes
- */
-static void update_tree(struct menu *src, GtkTreeIter * dst)
-{
- struct menu *child1;
- GtkTreeIter iter, tmp;
- GtkTreeIter *child2 = &iter;
- gboolean valid;
- GtkTreeIter *sibling;
- struct symbol *sym;
- struct menu *menu1, *menu2;
+ widget = GTK_WIDGET(gtk_builder_get_object(builder, "button2"));
+ g_signal_connect(widget, "clicked",
+ G_CALLBACK(on_load_clicked), NULL);
- if (src == &rootmenu)
- indent = 1;
+ save_btn = GTK_WIDGET(gtk_builder_get_object(builder, "button3"));
+ g_signal_connect(save_btn, "clicked",
+ G_CALLBACK(on_save_clicked), NULL);
- valid = gtk_tree_model_iter_children(model2, child2, dst);
- for (child1 = src->list; child1; child1 = child1->next) {
+ single_btn = GTK_WIDGET(gtk_builder_get_object(builder, "button4"));
+ g_signal_connect(single_btn, "clicked",
+ G_CALLBACK(on_single_clicked), NULL);
+ replace_button_icon(single_btn, xpm_single_view);
- sym = child1->sym;
+ split_btn = GTK_WIDGET(gtk_builder_get_object(builder, "button5"));
+ g_signal_connect(split_btn, "clicked",
+ G_CALLBACK(on_split_clicked), NULL);
+ replace_button_icon(split_btn, xpm_split_view);
- reparse:
- menu1 = child1;
- if (valid)
- gtk_tree_model_get(model2, child2, COL_MENU,
- &menu2, -1);
- else
- menu2 = NULL; // force adding of a first child
-
- if ((opt_mode == OPT_NORMAL && !menu_is_visible(child1)) ||
- (opt_mode == OPT_PROMPT && !menu_has_prompt(child1)) ||
- (opt_mode == OPT_ALL && !menu_get_prompt(child1))) {
-
- /* remove node */
- if (gtktree_iter_find_node(dst, menu1) != NULL) {
- memcpy(&tmp, child2, sizeof(GtkTreeIter));
- valid = gtk_tree_model_iter_next(model2,
- child2);
- gtk_tree_store_remove(tree2, &tmp);
- if (!valid)
- return; /* next parent */
- else
- goto reparse; /* next child */
- } else
- continue;
- }
+ full_btn = GTK_WIDGET(gtk_builder_get_object(builder, "button6"));
+ g_signal_connect(full_btn, "clicked",
+ G_CALLBACK(on_full_clicked), NULL);
+ replace_button_icon(full_btn, xpm_tree_view);
- if (menu1 != menu2) {
- if (gtktree_iter_find_node(dst, menu1) == NULL) { // add node
- if (!valid && !menu2)
- sibling = NULL;
- else
- sibling = child2;
- gtk_tree_store_insert_before(tree2,
- child2,
- dst, sibling);
- set_node(child2, menu1, fill_row(menu1));
- if (menu2 == NULL)
- valid = TRUE;
- } else { // remove node
- memcpy(&tmp, child2, sizeof(GtkTreeIter));
- valid = gtk_tree_model_iter_next(model2,
- child2);
- gtk_tree_store_remove(tree2, &tmp);
- if (!valid)
- return; // next parent
- else
- goto reparse; // next child
- }
- } else if (sym && (child1->flags & MENU_CHANGED)) {
- set_node(child2, menu1, fill_row(menu1));
- }
+ widget = GTK_WIDGET(gtk_builder_get_object(builder, "button7"));
+ g_signal_connect(widget, "clicked",
+ G_CALLBACK(on_collapse_clicked), NULL);
- indent++;
- update_tree(child1, child2);
- indent--;
+ widget = GTK_WIDGET(gtk_builder_get_object(builder, "button8"));
+ g_signal_connect(widget, "clicked",
+ G_CALLBACK(on_expand_clicked), NULL);
- valid = gtk_tree_model_iter_next(model2, child2);
- }
-}
+ txtbuf = gtk_text_view_get_buffer(GTK_TEXT_VIEW(text_w));
+ tag1 = gtk_text_buffer_create_tag(txtbuf, "mytag1",
+ "foreground", "red",
+ "weight", PANGO_WEIGHT_BOLD,
+ NULL);
+ tag2 = gtk_text_buffer_create_tag(txtbuf, "mytag2",
+ /*"style", PANGO_STYLE_OBLIQUE, */
+ NULL);
+ gtk_window_set_title(GTK_WINDOW(main_wnd), rootmenu.prompt->text);
-/* Display the whole tree (single/split/full view) */
-static void display_tree(struct menu *menu)
+ gtk_widget_show_all(main_wnd);
+
+ g_object_unref(builder);
+
+ conf_set_changed_callback(conf_changed);
+}
+
+static gboolean visible_func(GtkTreeModel *model, GtkTreeIter *iter,
+ gpointer data)
{
- struct property *prop;
- struct menu *child;
- enum prop_type ptype;
+ struct menu *menu;
- if (menu == &rootmenu) {
- indent = 1;
- current = &rootmenu;
- }
+ gtk_tree_model_get(model, iter, COL_MENU, &menu, -1);
- for (child = menu->list; child; child = child->next) {
- prop = child->prompt;
- ptype = prop ? prop->type : P_UNKNOWN;
+ if (!menu)
+ return FALSE;
- menu->flags &= ~MENU_CHANGED;
+ return menu_is_visible(menu) || opt_mode == OPT_ALL ||
+ (opt_mode == OPT_PROMPT && menu_has_prompt(menu));
+}
- if ((view_mode == SPLIT_VIEW)
- && !(child->flags & MENU_ROOT) && (tree == tree1))
- continue;
+static void init_left_tree(void)
+{
+ GtkTreeView *view = GTK_TREE_VIEW(tree1_w);
+ GtkCellRenderer *renderer;
+ GtkTreeSelection *sel;
+ GtkTreeViewColumn *column;
+ GtkTreeModel *filter;
- if ((view_mode == SPLIT_VIEW) && (child->flags & MENU_ROOT)
- && (tree == tree2))
- continue;
+ tree1 = gtk_tree_store_new(COL_NUMBER,
+ G_TYPE_STRING, G_TYPE_STRING,
+ G_TYPE_STRING, G_TYPE_STRING,
+ G_TYPE_STRING, G_TYPE_STRING,
+ G_TYPE_POINTER, GDK_TYPE_RGBA,
+ G_TYPE_BOOLEAN, GDK_TYPE_PIXBUF,
+ G_TYPE_BOOLEAN, G_TYPE_BOOLEAN,
+ G_TYPE_BOOLEAN, G_TYPE_BOOLEAN,
+ G_TYPE_BOOLEAN);
- if ((opt_mode == OPT_NORMAL && menu_is_visible(child)) ||
- (opt_mode == OPT_PROMPT && menu_has_prompt(child)) ||
- (opt_mode == OPT_ALL && menu_get_prompt(child)))
- place_node(child, fill_row(child));
+ filter = gtk_tree_model_filter_new(GTK_TREE_MODEL(tree1), NULL);
- if ((view_mode != FULL_VIEW) && (ptype == P_MENU)
- && (tree == tree2))
- continue;
-/*
- if (((menu != &rootmenu) && !(menu->flags & MENU_ROOT))
- || (view_mode == FULL_VIEW)
- || (view_mode == SPLIT_VIEW))*/
+ gtk_tree_model_filter_set_visible_func(GTK_TREE_MODEL_FILTER(filter),
+ visible_func, NULL, NULL);
+ gtk_tree_view_set_model(view, filter);
- /* Change paned position if the view is not in 'split mode' */
- if (view_mode == SINGLE_VIEW || view_mode == FULL_VIEW) {
- gtk_paned_set_position(GTK_PANED(hpaned), 0);
- }
+ column = gtk_tree_view_column_new();
+ gtk_tree_view_append_column(view, column);
+ gtk_tree_view_column_set_title(column, "Options");
- if (((view_mode == SINGLE_VIEW) && (menu->flags & MENU_ROOT))
- || (view_mode == FULL_VIEW)
- || (view_mode == SPLIT_VIEW)) {
- indent++;
- display_tree(child);
- indent--;
- }
- }
-}
+ renderer = gtk_cell_renderer_toggle_new();
+ gtk_tree_view_column_pack_start(GTK_TREE_VIEW_COLUMN(column),
+ renderer, FALSE);
+ gtk_tree_view_column_set_attributes(GTK_TREE_VIEW_COLUMN(column),
+ renderer,
+ "active", COL_BTNACT,
+ "inconsistent", COL_BTNINC,
+ "visible", COL_BTNVIS,
+ "radio", COL_BTNRAD, NULL);
+ renderer = gtk_cell_renderer_text_new();
+ gtk_tree_view_column_pack_start(GTK_TREE_VIEW_COLUMN(column),
+ renderer, FALSE);
+ gtk_tree_view_column_set_attributes(GTK_TREE_VIEW_COLUMN(column),
+ renderer,
+ "text", COL_OPTION,
+ "foreground-rgba",
+ COL_COLOR, NULL);
-/* Display a part of the tree starting at current node (single/split view) */
-static void display_tree_part(void)
-{
- if (tree2)
- gtk_tree_store_clear(tree2);
- if (view_mode == SINGLE_VIEW)
- display_tree(current);
- else if (view_mode == SPLIT_VIEW)
- display_tree(browsed);
- else if (view_mode == FULL_VIEW)
- display_tree(&rootmenu);
- gtk_tree_view_expand_all(GTK_TREE_VIEW(tree2_w));
+ sel = gtk_tree_view_get_selection(view);
+ gtk_tree_selection_set_mode(sel, GTK_SELECTION_SINGLE);
}
-/* Display the list in the left frame (split view) */
-static void display_list(void)
+static void init_right_tree(void)
{
- if (tree1)
- gtk_tree_store_clear(tree1);
+ GtkTreeView *view = GTK_TREE_VIEW(tree2_w);
+ GtkCellRenderer *renderer;
+ GtkTreeSelection *sel;
+ GtkTreeViewColumn *column;
+ GtkTreeModel *filter;
+ gint i;
- tree = tree1;
- display_tree(&rootmenu);
- gtk_tree_view_expand_all(GTK_TREE_VIEW(tree1_w));
- tree = tree2;
-}
+ tree2 = gtk_tree_store_new(COL_NUMBER,
+ G_TYPE_STRING, G_TYPE_STRING,
+ G_TYPE_STRING, G_TYPE_STRING,
+ G_TYPE_STRING, G_TYPE_STRING,
+ G_TYPE_POINTER, GDK_TYPE_RGBA,
+ G_TYPE_BOOLEAN, GDK_TYPE_PIXBUF,
+ G_TYPE_BOOLEAN, G_TYPE_BOOLEAN,
+ G_TYPE_BOOLEAN, G_TYPE_BOOLEAN,
+ G_TYPE_BOOLEAN);
-static void fixup_rootmenu(struct menu *menu)
-{
- struct menu *child;
- static int menu_cnt = 0;
+ filter = gtk_tree_model_filter_new(GTK_TREE_MODEL(tree2), NULL);
- menu->flags |= MENU_ROOT;
- for (child = menu->list; child; child = child->next) {
- if (child->prompt && child->prompt->type == P_MENU) {
- menu_cnt++;
- fixup_rootmenu(child);
- menu_cnt--;
- } else if (!menu_cnt)
- fixup_rootmenu(child);
+ gtk_tree_model_filter_set_visible_func(GTK_TREE_MODEL_FILTER(filter),
+ visible_func, NULL, NULL);
+ gtk_tree_view_set_model(view, filter);
+
+ column = gtk_tree_view_column_new();
+ gtk_tree_view_append_column(view, column);
+ gtk_tree_view_column_set_title(column, "Options");
+
+ renderer = gtk_cell_renderer_pixbuf_new();
+ gtk_tree_view_column_pack_start(GTK_TREE_VIEW_COLUMN(column),
+ renderer, FALSE);
+ gtk_tree_view_column_set_attributes(GTK_TREE_VIEW_COLUMN(column),
+ renderer,
+ "pixbuf", COL_PIXBUF,
+ "visible", COL_PIXVIS, NULL);
+ renderer = gtk_cell_renderer_toggle_new();
+ gtk_tree_view_column_pack_start(GTK_TREE_VIEW_COLUMN(column),
+ renderer, FALSE);
+ gtk_tree_view_column_set_attributes(GTK_TREE_VIEW_COLUMN(column),
+ renderer,
+ "active", COL_BTNACT,
+ "inconsistent", COL_BTNINC,
+ "visible", COL_BTNVIS,
+ "radio", COL_BTNRAD, NULL);
+ renderer = gtk_cell_renderer_text_new();
+ gtk_tree_view_column_pack_start(GTK_TREE_VIEW_COLUMN(column),
+ renderer, FALSE);
+ gtk_tree_view_column_set_attributes(GTK_TREE_VIEW_COLUMN(column),
+ renderer,
+ "text", COL_OPTION,
+ "foreground-rgba",
+ COL_COLOR, NULL);
+
+ renderer = gtk_cell_renderer_text_new();
+ gtk_tree_view_insert_column_with_attributes(view, -1,
+ "Name", renderer,
+ "text", COL_NAME,
+ "foreground-rgba",
+ COL_COLOR, NULL);
+ renderer = gtk_cell_renderer_text_new();
+ gtk_tree_view_insert_column_with_attributes(view, -1,
+ "N", renderer,
+ "text", COL_NO,
+ "foreground-rgba",
+ COL_COLOR, NULL);
+ renderer = gtk_cell_renderer_text_new();
+ gtk_tree_view_insert_column_with_attributes(view, -1,
+ "M", renderer,
+ "text", COL_MOD,
+ "foreground-rgba",
+ COL_COLOR, NULL);
+ renderer = gtk_cell_renderer_text_new();
+ gtk_tree_view_insert_column_with_attributes(view, -1,
+ "Y", renderer,
+ "text", COL_YES,
+ "foreground-rgba",
+ COL_COLOR, NULL);
+ renderer = gtk_cell_renderer_text_new();
+ gtk_tree_view_insert_column_with_attributes(view, -1,
+ "Value", renderer,
+ "text", COL_VALUE,
+ "editable",
+ COL_EDIT,
+ "foreground-rgba",
+ COL_COLOR, NULL);
+ g_signal_connect(G_OBJECT(renderer), "edited",
+ G_CALLBACK(renderer_edited), tree2_w);
+
+ pix_menu = gdk_pixbuf_new_from_xpm_data((const char **)xpm_menu);
+
+ for (i = 0; i < COL_VALUE; i++) {
+ column = gtk_tree_view_get_column(view, i);
+ gtk_tree_view_column_set_resizable(column, TRUE);
}
-}
+ sel = gtk_tree_view_get_selection(view);
+ gtk_tree_selection_set_mode(sel, GTK_SELECTION_SINGLE);
+}
/* Main */
int main(int ac, char *av[])
@@ -1390,18 +1288,16 @@ int main(int ac, char *av[])
gchar *glade_file;
/* GTK stuffs */
- gtk_set_locale();
gtk_init(&ac, &av);
- glade_init();
/* Determine GUI path */
env = getenv(SRCTREE);
if (env)
- glade_file = g_strconcat(env, "/scripts/kconfig/gconf.glade", NULL);
+ glade_file = g_strconcat(env, "/scripts/kconfig/gconf.ui", NULL);
else if (av[0][0] == '/')
- glade_file = g_strconcat(av[0], ".glade", NULL);
+ glade_file = g_strconcat(av[0], ".ui", NULL);
else
- glade_file = g_strconcat(g_get_current_dir(), "/", av[0], ".glade", NULL);
+ glade_file = g_strconcat(g_get_current_dir(), "/", av[0], ".ui", NULL);
/* Conf stuffs */
if (ac > 1 && av[1][0] == '-') {
@@ -1426,23 +1322,12 @@ int main(int ac, char *av[])
/* Load the interface and connect signals */
init_main_window(glade_file);
- init_tree_model();
init_left_tree();
init_right_tree();
conf_read(NULL);
- switch (view_mode) {
- case SINGLE_VIEW:
- display_tree_part();
- break;
- case SPLIT_VIEW:
- display_list();
- break;
- case FULL_VIEW:
- display_tree(&rootmenu);
- break;
- }
+ set_view_mode(view_mode);
gtk_main();
diff --git a/scripts/kconfig/gconf.glade b/scripts/kconfig/gconf.ui
index aa483cb32755..ab4431255fa7 100644
--- a/scripts/kconfig/gconf.glade
+++ b/scripts/kconfig/gconf.ui
@@ -1,8 +1,8 @@
<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*-->
-<glade-interface>
+<interface>
-<widget class="GtkWindow" id="window1">
+<object class="GtkWindow" id="window1">
<property name="visible">True</property>
<property name="title" translatable="yes">Gtk Kernel Configurator</property>
<property name="type">GTK_WINDOW_TOPLEVEL</property>
@@ -17,295 +17,196 @@
<property name="skip_pager_hint">False</property>
<property name="type_hint">GDK_WINDOW_TYPE_HINT_NORMAL</property>
<property name="gravity">GDK_GRAVITY_NORTH_WEST</property>
- <signal name="destroy" handler="on_window1_destroy" object="window1"/>
- <signal name="size_request" handler="on_window1_size_request" object="vpaned1" last_modification_time="Fri, 11 Jan 2002 16:17:11 GMT"/>
- <signal name="delete_event" handler="on_window1_delete_event" object="window1" last_modification_time="Sun, 09 Mar 2003 19:42:46 GMT"/>
<child>
- <widget class="GtkVBox" id="vbox1">
+ <object class="GtkBox" id="vbox1">
+ <property name="orientation">vertical</property>
<property name="visible">True</property>
<property name="homogeneous">False</property>
<property name="spacing">0</property>
<child>
- <widget class="GtkMenuBar" id="menubar1">
+ <object class="GtkMenuBar" id="menubar1">
<property name="visible">True</property>
<child>
- <widget class="GtkMenuItem" id="file1">
+ <object class="GtkMenuItem" id="file1">
<property name="visible">True</property>
<property name="label" translatable="yes">_File</property>
<property name="use_underline">True</property>
- <child>
- <widget class="GtkMenu" id="file1_menu">
+ <child type="submenu">
+ <object class="GtkMenu" id="file1_menu">
<child>
- <widget class="GtkImageMenuItem" id="load1">
+ <object class="GtkMenuItem" id="load1">
<property name="visible">True</property>
- <property name="tooltip" translatable="yes">Load a config file</property>
+ <property name="tooltip-text" translatable="yes">Load a config file</property>
<property name="label" translatable="yes">_Load</property>
<property name="use_underline">True</property>
- <signal name="activate" handler="on_load1_activate"/>
<accelerator key="L" modifiers="GDK_CONTROL_MASK" signal="activate"/>
-
- <child internal-child="image">
- <widget class="GtkImage" id="image39">
- <property name="visible">True</property>
- <property name="stock">gtk-open</property>
- <property name="icon_size">1</property>
- <property name="xalign">0.5</property>
- <property name="yalign">0.5</property>
- <property name="xpad">0</property>
- <property name="ypad">0</property>
- </widget>
- </child>
- </widget>
+ </object>
</child>
<child>
- <widget class="GtkImageMenuItem" id="save1">
+ <object class="GtkMenuItem" id="save1">
<property name="visible">True</property>
- <property name="tooltip" translatable="yes">Save the config in .config</property>
+ <property name="tooltip-text" translatable="yes">Save the config in .config</property>
<property name="label" translatable="yes">_Save</property>
<property name="use_underline">True</property>
- <signal name="activate" handler="on_save_activate"/>
<accelerator key="S" modifiers="GDK_CONTROL_MASK" signal="activate"/>
-
- <child internal-child="image">
- <widget class="GtkImage" id="image40">
- <property name="visible">True</property>
- <property name="stock">gtk-save</property>
- <property name="icon_size">1</property>
- <property name="xalign">0.5</property>
- <property name="yalign">0.5</property>
- <property name="xpad">0</property>
- <property name="ypad">0</property>
- </widget>
- </child>
- </widget>
+ </object>
</child>
<child>
- <widget class="GtkImageMenuItem" id="save_as1">
+ <object class="GtkMenuItem" id="save_as1">
<property name="visible">True</property>
- <property name="tooltip" translatable="yes">Save the config in a file</property>
+ <property name="tooltip-text" translatable="yes">Save the config in a file</property>
<property name="label" translatable="yes">Save _as</property>
<property name="use_underline">True</property>
- <signal name="activate" handler="on_save_as1_activate"/>
-
- <child internal-child="image">
- <widget class="GtkImage" id="image41">
- <property name="visible">True</property>
- <property name="stock">gtk-save-as</property>
- <property name="icon_size">1</property>
- <property name="xalign">0.5</property>
- <property name="yalign">0.5</property>
- <property name="xpad">0</property>
- <property name="ypad">0</property>
- </widget>
- </child>
- </widget>
+ </object>
</child>
<child>
- <widget class="GtkSeparatorMenuItem" id="separator1">
+ <object class="GtkSeparatorMenuItem" id="separator1">
<property name="visible">True</property>
- </widget>
+ </object>
</child>
<child>
- <widget class="GtkImageMenuItem" id="quit1">
+ <object class="GtkMenuItem" id="quit1">
<property name="visible">True</property>
<property name="label" translatable="yes">_Quit</property>
<property name="use_underline">True</property>
- <signal name="activate" handler="on_quit1_activate"/>
<accelerator key="Q" modifiers="GDK_CONTROL_MASK" signal="activate"/>
-
- <child internal-child="image">
- <widget class="GtkImage" id="image42">
- <property name="visible">True</property>
- <property name="stock">gtk-quit</property>
- <property name="icon_size">1</property>
- <property name="xalign">0.5</property>
- <property name="yalign">0.5</property>
- <property name="xpad">0</property>
- <property name="ypad">0</property>
- </widget>
- </child>
- </widget>
+ </object>
</child>
- </widget>
+ </object>
</child>
- </widget>
+ </object>
</child>
<child>
- <widget class="GtkMenuItem" id="options1">
+ <object class="GtkMenuItem" id="options1">
<property name="visible">True</property>
<property name="label" translatable="yes">_Options</property>
<property name="use_underline">True</property>
- <child>
- <widget class="GtkMenu" id="options1_menu">
+ <child type="submenu">
+ <object class="GtkMenu" id="options1_menu">
<child>
- <widget class="GtkCheckMenuItem" id="show_name1">
+ <object class="GtkCheckMenuItem" id="show_name1">
<property name="visible">True</property>
- <property name="tooltip" translatable="yes">Show name</property>
+ <property name="tooltip-text" translatable="yes">Show name</property>
<property name="label" translatable="yes">Show _name</property>
<property name="use_underline">True</property>
<property name="active">False</property>
- <signal name="activate" handler="on_show_name1_activate"/>
- </widget>
+ </object>
</child>
<child>
- <widget class="GtkCheckMenuItem" id="show_range1">
+ <object class="GtkCheckMenuItem" id="show_range1">
<property name="visible">True</property>
- <property name="tooltip" translatable="yes">Show range (Y/M/N)</property>
+ <property name="tooltip-text" translatable="yes">Show range (Y/M/N)</property>
<property name="label" translatable="yes">Show _range</property>
<property name="use_underline">True</property>
<property name="active">False</property>
- <signal name="activate" handler="on_show_range1_activate"/>
- </widget>
+ </object>
</child>
<child>
- <widget class="GtkCheckMenuItem" id="show_data1">
+ <object class="GtkCheckMenuItem" id="show_data1">
<property name="visible">True</property>
- <property name="tooltip" translatable="yes">Show value of the option</property>
+ <property name="tooltip-text" translatable="yes">Show value of the option</property>
<property name="label" translatable="yes">Show _data</property>
<property name="use_underline">True</property>
<property name="active">False</property>
- <signal name="activate" handler="on_show_data1_activate"/>
- </widget>
+ </object>
</child>
<child>
- <widget class="GtkSeparatorMenuItem" id="separator2">
+ <object class="GtkSeparatorMenuItem" id="separator2">
<property name="visible">True</property>
- </widget>
+ </object>
</child>
<child>
- <widget class="GtkRadioMenuItem" id="set_option_mode1">
+ <object class="GtkRadioMenuItem" id="set_option_mode1">
<property name="visible">True</property>
- <property name="tooltip" translatable="yes">Show normal options</property>
+ <property name="tooltip-text" translatable="yes">Show normal options</property>
<property name="label" translatable="yes">Show normal options</property>
<property name="use_underline">True</property>
<property name="active">True</property>
- <signal name="activate" handler="on_set_option_mode1_activate"/>
- </widget>
+ </object>
</child>
<child>
- <widget class="GtkRadioMenuItem" id="set_option_mode2">
+ <object class="GtkRadioMenuItem" id="set_option_mode2">
<property name="visible">True</property>
- <property name="tooltip" translatable="yes">Show all options</property>
+ <property name="tooltip-text" translatable="yes">Show all options</property>
<property name="label" translatable="yes">Show all _options</property>
<property name="use_underline">True</property>
<property name="active">False</property>
<property name="group">set_option_mode1</property>
- <signal name="activate" handler="on_set_option_mode2_activate"/>
- </widget>
+ </object>
</child>
<child>
- <widget class="GtkRadioMenuItem" id="set_option_mode3">
+ <object class="GtkRadioMenuItem" id="set_option_mode3">
<property name="visible">True</property>
- <property name="tooltip" translatable="yes">Show all options with prompts</property>
+ <property name="tooltip-text" translatable="yes">Show all options with prompts</property>
<property name="label" translatable="yes">Show all prompt options</property>
<property name="use_underline">True</property>
<property name="active">False</property>
<property name="group">set_option_mode1</property>
- <signal name="activate" handler="on_set_option_mode3_activate"/>
- </widget>
+ </object>
</child>
- </widget>
+ </object>
</child>
- </widget>
+ </object>
</child>
<child>
- <widget class="GtkMenuItem" id="help1">
+ <object class="GtkMenuItem" id="help1">
<property name="visible">True</property>
<property name="label" translatable="yes">_Help</property>
<property name="use_underline">True</property>
- <child>
- <widget class="GtkMenu" id="help1_menu">
+ <child type="submenu">
+ <object class="GtkMenu" id="help1_menu">
<child>
- <widget class="GtkImageMenuItem" id="introduction1">
+ <object class="GtkMenuItem" id="introduction1">
<property name="visible">True</property>
<property name="label" translatable="yes">_Introduction</property>
<property name="use_underline">True</property>
- <signal name="activate" handler="on_introduction1_activate" last_modification_time="Fri, 15 Nov 2002 20:26:30 GMT"/>
<accelerator key="I" modifiers="GDK_CONTROL_MASK" signal="activate"/>
-
- <child internal-child="image">
- <widget class="GtkImage" id="image43">
- <property name="visible">True</property>
- <property name="stock">gtk-dialog-question</property>
- <property name="icon_size">1</property>
- <property name="xalign">0.5</property>
- <property name="yalign">0.5</property>
- <property name="xpad">0</property>
- <property name="ypad">0</property>
- </widget>
- </child>
- </widget>
+ </object>
</child>
<child>
- <widget class="GtkImageMenuItem" id="about1">
+ <object class="GtkMenuItem" id="about1">
<property name="visible">True</property>
<property name="label" translatable="yes">_About</property>
<property name="use_underline">True</property>
- <signal name="activate" handler="on_about1_activate" last_modification_time="Fri, 15 Nov 2002 20:26:30 GMT"/>
<accelerator key="A" modifiers="GDK_CONTROL_MASK" signal="activate"/>
-
- <child internal-child="image">
- <widget class="GtkImage" id="image44">
- <property name="visible">True</property>
- <property name="stock">gtk-properties</property>
- <property name="icon_size">1</property>
- <property name="xalign">0.5</property>
- <property name="yalign">0.5</property>
- <property name="xpad">0</property>
- <property name="ypad">0</property>
- </widget>
- </child>
- </widget>
+ </object>
</child>
<child>
- <widget class="GtkImageMenuItem" id="license1">
+ <object class="GtkMenuItem" id="license1">
<property name="visible">True</property>
<property name="label" translatable="yes">_License</property>
<property name="use_underline">True</property>
- <signal name="activate" handler="on_license1_activate" last_modification_time="Fri, 15 Nov 2002 20:26:30 GMT"/>
-
- <child internal-child="image">
- <widget class="GtkImage" id="image45">
- <property name="visible">True</property>
- <property name="stock">gtk-justify-fill</property>
- <property name="icon_size">1</property>
- <property name="xalign">0.5</property>
- <property name="yalign">0.5</property>
- <property name="xpad">0</property>
- <property name="ypad">0</property>
- </widget>
- </child>
- </widget>
+ </object>
</child>
- </widget>
+ </object>
</child>
- </widget>
+ </object>
</child>
- </widget>
+ </object>
<packing>
<property name="padding">0</property>
<property name="expand">False</property>
@@ -314,32 +215,23 @@
</child>
<child>
- <widget class="GtkHandleBox" id="handlebox1">
- <property name="visible">True</property>
- <property name="shadow_type">GTK_SHADOW_OUT</property>
- <property name="handle_position">GTK_POS_LEFT</property>
- <property name="snap_edge">GTK_POS_TOP</property>
-
- <child>
- <widget class="GtkToolbar" id="toolbar1">
+ <object class="GtkToolbar" id="toolbar1">
<property name="visible">True</property>
<property name="orientation">GTK_ORIENTATION_HORIZONTAL</property>
<property name="toolbar_style">GTK_TOOLBAR_BOTH</property>
- <property name="tooltips">True</property>
<property name="show_arrow">True</property>
<child>
- <widget class="GtkToolButton" id="button1">
+ <object class="GtkToolButton" id="button1">
<property name="visible">True</property>
- <property name="tooltip" translatable="yes">Goes up of one level (single view)</property>
+ <property name="tooltip-text" translatable="yes">Goes up one level (single view)</property>
<property name="label" translatable="yes">Back</property>
<property name="use_underline">True</property>
<property name="stock_id">gtk-undo</property>
<property name="visible_horizontal">True</property>
<property name="visible_vertical">True</property>
<property name="is_important">False</property>
- <signal name="clicked" handler="on_back_clicked"/>
- </widget>
+ </object>
<packing>
<property name="expand">False</property>
<property name="homogeneous">True</property>
@@ -347,18 +239,18 @@
</child>
<child>
- <widget class="GtkToolItem" id="toolitem1">
+ <object class="GtkToolItem" id="toolitem1">
<property name="visible">True</property>
<property name="visible_horizontal">True</property>
<property name="visible_vertical">True</property>
<property name="is_important">False</property>
<child>
- <widget class="GtkVSeparator" id="vseparator1">
+ <object class="GtkVSeparator" id="vseparator1">
<property name="visible">True</property>
- </widget>
+ </object>
</child>
- </widget>
+ </object>
<packing>
<property name="expand">False</property>
<property name="homogeneous">False</property>
@@ -366,17 +258,16 @@
</child>
<child>
- <widget class="GtkToolButton" id="button2">
+ <object class="GtkToolButton" id="button2">
<property name="visible">True</property>
- <property name="tooltip" translatable="yes">Load a config file</property>
+ <property name="tooltip-text" translatable="yes">Load a config file</property>
<property name="label" translatable="yes">Load</property>
<property name="use_underline">True</property>
<property name="stock_id">gtk-open</property>
<property name="visible_horizontal">True</property>
<property name="visible_vertical">True</property>
<property name="is_important">False</property>
- <signal name="clicked" handler="on_load_clicked"/>
- </widget>
+ </object>
<packing>
<property name="expand">False</property>
<property name="homogeneous">True</property>
@@ -384,17 +275,16 @@
</child>
<child>
- <widget class="GtkToolButton" id="button3">
+ <object class="GtkToolButton" id="button3">
<property name="visible">True</property>
- <property name="tooltip" translatable="yes">Save a config file</property>
+ <property name="tooltip-text" translatable="yes">Save a config file</property>
<property name="label" translatable="yes">Save</property>
<property name="use_underline">True</property>
<property name="stock_id">gtk-save</property>
<property name="visible_horizontal">True</property>
<property name="visible_vertical">True</property>
<property name="is_important">False</property>
- <signal name="clicked" handler="on_save_activate"/>
- </widget>
+ </object>
<packing>
<property name="expand">False</property>
<property name="homogeneous">True</property>
@@ -402,18 +292,18 @@
</child>
<child>
- <widget class="GtkToolItem" id="toolitem2">
+ <object class="GtkToolItem" id="toolitem2">
<property name="visible">True</property>
<property name="visible_horizontal">True</property>
<property name="visible_vertical">True</property>
<property name="is_important">False</property>
<child>
- <widget class="GtkVSeparator" id="vseparator2">
+ <object class="GtkVSeparator" id="vseparator2">
<property name="visible">True</property>
- </widget>
+ </object>
</child>
- </widget>
+ </object>
<packing>
<property name="expand">False</property>
<property name="homogeneous">False</property>
@@ -421,17 +311,16 @@
</child>
<child>
- <widget class="GtkToolButton" id="button4">
+ <object class="GtkToolButton" id="button4">
<property name="visible">True</property>
- <property name="tooltip" translatable="yes">Single view</property>
+ <property name="tooltip-text" translatable="yes">Single view</property>
<property name="label" translatable="yes">Single</property>
<property name="use_underline">True</property>
<property name="stock_id">gtk-missing-image</property>
<property name="visible_horizontal">True</property>
<property name="visible_vertical">True</property>
<property name="is_important">False</property>
- <signal name="clicked" handler="on_single_clicked" last_modification_time="Sun, 12 Jan 2003 14:28:39 GMT"/>
- </widget>
+ </object>
<packing>
<property name="expand">False</property>
<property name="homogeneous">True</property>
@@ -439,17 +328,16 @@
</child>
<child>
- <widget class="GtkToolButton" id="button5">
+ <object class="GtkToolButton" id="button5">
<property name="visible">True</property>
- <property name="tooltip" translatable="yes">Split view</property>
+ <property name="tooltip-text" translatable="yes">Split view</property>
<property name="label" translatable="yes">Split</property>
<property name="use_underline">True</property>
<property name="stock_id">gtk-missing-image</property>
<property name="visible_horizontal">True</property>
<property name="visible_vertical">True</property>
<property name="is_important">False</property>
- <signal name="clicked" handler="on_split_clicked" last_modification_time="Sun, 12 Jan 2003 14:28:45 GMT"/>
- </widget>
+ </object>
<packing>
<property name="expand">False</property>
<property name="homogeneous">True</property>
@@ -457,17 +345,16 @@
</child>
<child>
- <widget class="GtkToolButton" id="button6">
+ <object class="GtkToolButton" id="button6">
<property name="visible">True</property>
- <property name="tooltip" translatable="yes">Full view</property>
+ <property name="tooltip-text" translatable="yes">Full view</property>
<property name="label" translatable="yes">Full</property>
<property name="use_underline">True</property>
<property name="stock_id">gtk-missing-image</property>
<property name="visible_horizontal">True</property>
<property name="visible_vertical">True</property>
<property name="is_important">False</property>
- <signal name="clicked" handler="on_full_clicked" last_modification_time="Sun, 12 Jan 2003 14:28:50 GMT"/>
- </widget>
+ </object>
<packing>
<property name="expand">False</property>
<property name="homogeneous">True</property>
@@ -475,18 +362,18 @@
</child>
<child>
- <widget class="GtkToolItem" id="toolitem3">
+ <object class="GtkToolItem" id="toolitem3">
<property name="visible">True</property>
<property name="visible_horizontal">True</property>
<property name="visible_vertical">True</property>
<property name="is_important">False</property>
<child>
- <widget class="GtkVSeparator" id="vseparator3">
+ <object class="GtkVSeparator" id="vseparator3">
<property name="visible">True</property>
- </widget>
+ </object>
</child>
- </widget>
+ </object>
<packing>
<property name="expand">False</property>
<property name="homogeneous">False</property>
@@ -494,17 +381,16 @@
</child>
<child>
- <widget class="GtkToolButton" id="button7">
+ <object class="GtkToolButton" id="button7">
<property name="visible">True</property>
- <property name="tooltip" translatable="yes">Collapse the whole tree in the right frame</property>
+ <property name="tooltip-text" translatable="yes">Collapse the whole tree in the right frame</property>
<property name="label" translatable="yes">Collapse</property>
<property name="use_underline">True</property>
<property name="stock_id">gtk-remove</property>
<property name="visible_horizontal">True</property>
<property name="visible_vertical">True</property>
<property name="is_important">False</property>
- <signal name="clicked" handler="on_collapse_clicked"/>
- </widget>
+ </object>
<packing>
<property name="expand">False</property>
<property name="homogeneous">True</property>
@@ -512,25 +398,22 @@
</child>
<child>
- <widget class="GtkToolButton" id="button8">
+ <object class="GtkToolButton" id="button8">
<property name="visible">True</property>
- <property name="tooltip" translatable="yes">Expand the whole tree in the right frame</property>
+ <property name="tooltip-text" translatable="yes">Expand the whole tree in the right frame</property>
<property name="label" translatable="yes">Expand</property>
<property name="use_underline">True</property>
<property name="stock_id">gtk-add</property>
<property name="visible_horizontal">True</property>
<property name="visible_vertical">True</property>
<property name="is_important">False</property>
- <signal name="clicked" handler="on_expand_clicked"/>
- </widget>
+ </object>
<packing>
<property name="expand">False</property>
<property name="homogeneous">True</property>
</packing>
</child>
- </widget>
- </child>
- </widget>
+ </object>
<packing>
<property name="padding">0</property>
<property name="expand">False</property>
@@ -539,14 +422,13 @@
</child>
<child>
- <widget class="GtkHPaned" id="hpaned1">
+ <object class="GtkPaned" id="hpaned1">
<property name="width_request">1</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
- <property name="position">0</property>
<child>
- <widget class="GtkScrolledWindow" id="scrolledwindow1">
+ <object class="GtkScrolledWindow" id="scrolledwindow1">
<property name="visible">True</property>
<property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
<property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
@@ -554,19 +436,16 @@
<property name="window_placement">GTK_CORNER_TOP_LEFT</property>
<child>
- <widget class="GtkTreeView" id="treeview1">
+ <object class="GtkTreeView" id="treeview1">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="headers_visible">True</property>
<property name="rules_hint">False</property>
<property name="reorderable">False</property>
<property name="enable_search">False</property>
- <signal name="cursor_changed" handler="on_treeview2_cursor_changed" last_modification_time="Sun, 12 Jan 2003 15:58:22 GMT"/>
- <signal name="button_press_event" handler="on_treeview1_button_press_event" last_modification_time="Sun, 12 Jan 2003 16:03:52 GMT"/>
- <signal name="key_press_event" handler="on_treeview2_key_press_event" last_modification_time="Sun, 12 Jan 2003 16:11:44 GMT"/>
- </widget>
+ </object>
</child>
- </widget>
+ </object>
<packing>
<property name="shrink">True</property>
<property name="resize">False</property>
@@ -574,13 +453,13 @@
</child>
<child>
- <widget class="GtkVPaned" id="vpaned1">
+ <object class="GtkPaned" id="vpaned1">
+ <property name="orientation">vertical</property>
<property name="visible">True</property>
<property name="can_focus">True</property>
- <property name="position">0</property>
<child>
- <widget class="GtkScrolledWindow" id="scrolledwindow2">
+ <object class="GtkScrolledWindow" id="scrolledwindow2">
<property name="visible">True</property>
<property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
<property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
@@ -588,7 +467,7 @@
<property name="window_placement">GTK_CORNER_TOP_LEFT</property>
<child>
- <widget class="GtkTreeView" id="treeview2">
+ <object class="GtkTreeView" id="treeview2">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="has_focus">True</property>
@@ -596,12 +475,9 @@
<property name="rules_hint">False</property>
<property name="reorderable">False</property>
<property name="enable_search">False</property>
- <signal name="cursor_changed" handler="on_treeview2_cursor_changed" last_modification_time="Sun, 12 Jan 2003 15:57:55 GMT"/>
- <signal name="button_press_event" handler="on_treeview2_button_press_event" last_modification_time="Sun, 12 Jan 2003 15:57:58 GMT"/>
- <signal name="key_press_event" handler="on_treeview2_key_press_event" last_modification_time="Sun, 12 Jan 2003 15:58:01 GMT"/>
- </widget>
+ </object>
</child>
- </widget>
+ </object>
<packing>
<property name="shrink">True</property>
<property name="resize">False</property>
@@ -609,7 +485,7 @@
</child>
<child>
- <widget class="GtkScrolledWindow" id="scrolledwindow3">
+ <object class="GtkScrolledWindow" id="scrolledwindow3">
<property name="visible">True</property>
<property name="hscrollbar_policy">GTK_POLICY_NEVER</property>
<property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
@@ -617,7 +493,7 @@
<property name="window_placement">GTK_CORNER_TOP_LEFT</property>
<child>
- <widget class="GtkTextView" id="textview3">
+ <object class="GtkTextView" id="textview3">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="editable">False</property>
@@ -632,30 +508,29 @@
<property name="left_margin">0</property>
<property name="right_margin">0</property>
<property name="indent">0</property>
- <property name="text" translatable="yes">Sorry, no help available for this option yet.</property>
- </widget>
+ </object>
</child>
- </widget>
+ </object>
<packing>
<property name="shrink">True</property>
<property name="resize">True</property>
</packing>
</child>
- </widget>
+ </object>
<packing>
<property name="shrink">True</property>
<property name="resize">True</property>
</packing>
</child>
- </widget>
+ </object>
<packing>
<property name="padding">0</property>
<property name="expand">True</property>
<property name="fill">True</property>
</packing>
</child>
- </widget>
+ </object>
</child>
-</widget>
+</object>
-</glade-interface>
+</interface>
diff --git a/scripts/kconfig/lkc.h b/scripts/kconfig/lkc.h
index b8ebc3094a23..56548efc14d7 100644
--- a/scripts/kconfig/lkc.h
+++ b/scripts/kconfig/lkc.h
@@ -81,7 +81,7 @@ void _menu_init(void);
void menu_warn(const struct menu *menu, const char *fmt, ...);
struct menu *menu_add_menu(void);
void menu_end_menu(void);
-void menu_add_entry(struct symbol *sym);
+void menu_add_entry(struct symbol *sym, enum menu_type type);
void menu_add_dep(struct expr *dep);
void menu_add_visibility(struct expr *dep);
struct property *menu_add_prompt(enum prop_type type, const char *prompt,
@@ -98,9 +98,11 @@ bool menu_is_visible(struct menu *menu);
bool menu_has_prompt(const struct menu *menu);
const char *menu_get_prompt(const struct menu *menu);
struct menu *menu_get_parent_menu(struct menu *menu);
+struct menu *menu_get_menu_or_parent_menu(struct menu *menu);
int get_jump_key_char(void);
struct gstr get_relations_str(struct symbol **sym_arr, struct list_head *head);
void menu_get_ext_help(struct menu *menu, struct gstr *help);
+void menu_dump(void);
/* symbol.c */
void sym_clear_all_valid(void);
diff --git a/scripts/kconfig/lxdialog/inputbox.c b/scripts/kconfig/lxdialog/inputbox.c
index 3c6e24b20f5b..5e4a131724f2 100644
--- a/scripts/kconfig/lxdialog/inputbox.c
+++ b/scripts/kconfig/lxdialog/inputbox.c
@@ -39,8 +39,10 @@ int dialog_inputbox(const char *title, const char *prompt, int height, int width
if (!init)
instr[0] = '\0';
- else
- strcpy(instr, init);
+ else {
+ strncpy(instr, init, sizeof(dialog_input_result) - 1);
+ instr[sizeof(dialog_input_result) - 1] = '\0';
+ }
do_resize:
if (getmaxy(stdscr) <= (height - INPUTBOX_HEIGHT_MIN))
diff --git a/scripts/kconfig/lxdialog/menubox.c b/scripts/kconfig/lxdialog/menubox.c
index 6e6244df0c56..d4c19b7beebb 100644
--- a/scripts/kconfig/lxdialog/menubox.c
+++ b/scripts/kconfig/lxdialog/menubox.c
@@ -264,7 +264,7 @@ do_resize:
if (key < 256 && isalpha(key))
key = tolower(key);
- if (strchr("ynmh", key))
+ if (strchr("ynmh ", key))
i = max_choice;
else {
for (i = choice + 1; i < max_choice; i++) {
diff --git a/scripts/kconfig/lxdialog/util.c b/scripts/kconfig/lxdialog/util.c
index 964139c87fcb..b34000beb294 100644
--- a/scripts/kconfig/lxdialog/util.c
+++ b/scripts/kconfig/lxdialog/util.c
@@ -345,8 +345,7 @@ void print_autowrap(WINDOW * win, const char *prompt, int width, int y, int x)
int prompt_len, room, wlen;
char tempstr[MAX_LEN + 1], *word, *sp, *sp2, *newline_separator = 0;
- strcpy(tempstr, prompt);
-
+ snprintf(tempstr, sizeof(tempstr), "%s", prompt);
prompt_len = strlen(tempstr);
if (prompt_len <= width - x * 2) { /* If prompt is short */
diff --git a/scripts/kconfig/menu.c b/scripts/kconfig/menu.c
index 6587ac86d0d5..0f1a6513987c 100644
--- a/scripts/kconfig/menu.c
+++ b/scripts/kconfig/menu.c
@@ -15,7 +15,7 @@
static const char nohelp_text[] = "There is no help available for this option.";
-struct menu rootmenu;
+struct menu rootmenu = { .type = M_MENU };
static struct menu **last_entry_ptr;
/**
@@ -65,12 +65,13 @@ void _menu_init(void)
last_entry_ptr = &rootmenu.list;
}
-void menu_add_entry(struct symbol *sym)
+void menu_add_entry(struct symbol *sym, enum menu_type type)
{
struct menu *menu;
menu = xmalloc(sizeof(*menu));
memset(menu, 0, sizeof(*menu));
+ menu->type = type;
menu->sym = sym;
menu->parent = current_menu;
menu->filename = cur_filename;
@@ -574,8 +575,28 @@ const char *menu_get_prompt(const struct menu *menu)
return NULL;
}
+/**
+ * menu_get_parent_menu - return the parent menu or NULL
+ * @menu: pointer to the menu
+ * return: the parent menu, or NULL if there is no parent.
+ */
struct menu *menu_get_parent_menu(struct menu *menu)
{
+ for (menu = menu->parent; menu; menu = menu->parent)
+ if (menu->type == M_MENU)
+ return menu;
+
+ return NULL;
+}
+
+/**
+ * menu_get_menu_or_parent_menu - return the parent menu or the menu itself
+ * @menu: pointer to the menu
+ * return: the parent menu. If the given argument is already a menu, return
+ * itself.
+ */
+struct menu *menu_get_menu_or_parent_menu(struct menu *menu)
+{
enum prop_type type;
for (; menu != &rootmenu; menu = menu->parent) {
@@ -767,3 +788,77 @@ void menu_get_ext_help(struct menu *menu, struct gstr *help)
if (sym)
get_symbol_str(help, sym, NULL);
}
+
+/**
+ * menu_dump - dump all menu entries in a tree-like format
+ */
+void menu_dump(void)
+{
+ struct menu *menu = &rootmenu;
+ unsigned long long bits = 0;
+ int indent = 0;
+
+ while (menu) {
+
+ for (int i = indent - 1; i >= 0; i--) {
+ if (bits & (1ULL << i)) {
+ if (i > 0)
+ printf("| ");
+ else
+ printf("|-- ");
+ } else {
+ if (i > 0)
+ printf(" ");
+ else
+ printf("`-- ");
+ }
+ }
+
+ switch (menu->type) {
+ case M_CHOICE:
+ printf("choice \"%s\"\n", menu->prompt->text);
+ break;
+ case M_COMMENT:
+ printf("comment \"%s\"\n", menu->prompt->text);
+ break;
+ case M_IF:
+ printf("if\n");
+ break;
+ case M_MENU:
+ printf("menu \"%s\"", menu->prompt->text);
+ if (!menu->sym) {
+ printf("\n");
+ break;
+ }
+ printf(" + ");
+ /* fallthrough */
+ case M_NORMAL:
+ printf("symbol %s\n", menu->sym->name);
+ break;
+ }
+ if (menu->list) {
+ bits <<= 1;
+ menu = menu->list;
+ if (menu->next)
+ bits |= 1;
+ else
+ bits &= ~1;
+ indent++;
+ continue;
+ }
+
+ while (menu && !menu->next) {
+ menu = menu->parent;
+ bits >>= 1;
+ indent--;
+ }
+
+ if (menu) {
+ menu = menu->next;
+ if (menu->next)
+ bits |= 1;
+ else
+ bits &= ~1;
+ }
+ }
+}
diff --git a/scripts/kconfig/nconf.c b/scripts/kconfig/nconf.c
index c0b2dabf6c89..ae1fe5f60327 100644
--- a/scripts/kconfig/nconf.c
+++ b/scripts/kconfig/nconf.c
@@ -593,6 +593,8 @@ static void item_add_str(const char *fmt, ...)
tmp_str,
sizeof(k_menu_items[index].str));
+ k_menu_items[index].str[sizeof(k_menu_items[index].str) - 1] = '\0';
+
free_item(curses_menu_items[index]);
curses_menu_items[index] = new_item(
k_menu_items[index].str,
diff --git a/scripts/kconfig/nconf.gui.c b/scripts/kconfig/nconf.gui.c
index 4bfdf8ac2a9a..7206437e784a 100644
--- a/scripts/kconfig/nconf.gui.c
+++ b/scripts/kconfig/nconf.gui.c
@@ -359,6 +359,7 @@ int dialog_inputbox(WINDOW *main_window,
x = (columns-win_cols)/2;
strncpy(result, init, *result_len);
+ result[*result_len - 1] = '\0';
/* create the windows */
win = newwin(win_lines, win_cols, y, x);
diff --git a/scripts/kconfig/parser.y b/scripts/kconfig/parser.y
index 68372d3ff325..e9c3c664e925 100644
--- a/scripts/kconfig/parser.y
+++ b/scripts/kconfig/parser.y
@@ -139,7 +139,7 @@ stmt_list_in_choice:
config_entry_start: T_CONFIG nonconst_symbol T_EOL
{
- menu_add_entry($2);
+ menu_add_entry($2, M_NORMAL);
printd(DEBUG_PARSE, "%s:%d:config %s\n", cur_filename, cur_lineno, $2->name);
};
@@ -173,7 +173,7 @@ config_stmt: config_entry_start config_option_list
menuconfig_entry_start: T_MENUCONFIG nonconst_symbol T_EOL
{
- menu_add_entry($2);
+ menu_add_entry($2, M_MENU);
printd(DEBUG_PARSE, "%s:%d:menuconfig %s\n", cur_filename, cur_lineno, $2->name);
};
@@ -246,7 +246,7 @@ choice: T_CHOICE T_EOL
{
struct symbol *sym = sym_lookup(NULL, 0);
- menu_add_entry(sym);
+ menu_add_entry(sym, M_CHOICE);
menu_set_type(S_BOOLEAN);
INIT_LIST_HEAD(&current_entry->choice_members);
@@ -315,7 +315,7 @@ default:
if_entry: T_IF expr T_EOL
{
printd(DEBUG_PARSE, "%s:%d:if\n", cur_filename, cur_lineno);
- menu_add_entry(NULL);
+ menu_add_entry(NULL, M_IF);
menu_add_dep($2);
$$ = menu_add_menu();
};
@@ -338,7 +338,7 @@ if_stmt_in_choice: if_entry stmt_list_in_choice if_end
menu: T_MENU T_WORD_QUOTE T_EOL
{
- menu_add_entry(NULL);
+ menu_add_entry(NULL, M_MENU);
menu_add_prompt(P_MENU, $2, NULL);
printd(DEBUG_PARSE, "%s:%d:menu\n", cur_filename, cur_lineno);
};
@@ -376,7 +376,7 @@ source_stmt: T_SOURCE T_WORD_QUOTE T_EOL
comment: T_COMMENT T_WORD_QUOTE T_EOL
{
- menu_add_entry(NULL);
+ menu_add_entry(NULL, M_COMMENT);
menu_add_prompt(P_COMMENT, $2, NULL);
printd(DEBUG_PARSE, "%s:%d:comment\n", cur_filename, cur_lineno);
};
diff --git a/scripts/kconfig/qconf.cc b/scripts/kconfig/qconf.cc
index eaa465b0ccf9..f8992db1870a 100644
--- a/scripts/kconfig/qconf.cc
+++ b/scripts/kconfig/qconf.cc
@@ -37,6 +37,12 @@ QAction *ConfigMainWindow::saveAction;
ConfigSettings::ConfigSettings()
: QSettings("kernel.org", "qconf")
{
+ beginGroup("/kconfig/qconf");
+}
+
+ConfigSettings::~ConfigSettings()
+{
+ endGroup();
}
/**
@@ -92,7 +98,6 @@ void ConfigItem::updateMenu(void)
{
ConfigList* list;
struct symbol* sym;
- struct property *prop;
QString prompt;
int type;
tristate expr;
@@ -105,11 +110,10 @@ void ConfigItem::updateMenu(void)
}
sym = menu->sym;
- prop = menu->prompt;
prompt = menu_get_prompt(menu);
- if (prop) switch (prop->type) {
- case P_MENU:
+ switch (menu->type) {
+ case M_MENU:
if (list->mode == singleMode) {
/* a menuconfig entry is displayed differently
* depending whether it's at the view root or a child.
@@ -123,10 +127,16 @@ void ConfigItem::updateMenu(void)
setIcon(promptColIdx, QIcon());
}
goto set_prompt;
- case P_COMMENT:
+ case M_COMMENT:
setIcon(promptColIdx, QIcon());
prompt = "*** " + prompt + " ***";
goto set_prompt;
+ case M_CHOICE:
+ setIcon(promptColIdx, QIcon());
+ sym = sym_calc_choice(menu);
+ if (sym)
+ setText(dataColIdx, sym->name);
+ goto set_prompt;
default:
;
}
@@ -188,7 +198,11 @@ void ConfigItem::testUpdateMenu(void)
if (!menu)
return;
- sym_calc_value(menu->sym);
+ if (menu->type == M_CHOICE)
+ sym_calc_choice(menu);
+ else
+ sym_calc_value(menu->sym);
+
if (menu->flags & MENU_CHANGED) {
/* the menu entry changed, so update all list items */
menu->flags &= ~MENU_CHANGED;
@@ -478,7 +492,7 @@ void ConfigList::updateListAllForAll()
while (it.hasNext()) {
ConfigList *list = it.next();
- list->updateList();
+ list->updateListAll();
}
}
@@ -569,7 +583,7 @@ void ConfigList::setParentMenu(void)
oldroot = rootEntry;
if (rootEntry == &rootmenu)
return;
- setRootMenu(menu_get_parent_menu(rootEntry->parent));
+ setRootMenu(menu_get_menu_or_parent_menu(rootEntry->parent));
QTreeWidgetItemIterator it(this);
while (*it) {
@@ -1532,7 +1546,7 @@ void ConfigMainWindow::setMenuLink(struct menu *menu)
switch (configList->mode) {
case singleMode:
list = configList;
- parent = menu_get_parent_menu(menu);
+ parent = menu_get_menu_or_parent_menu(menu);
if (!parent)
return;
list->setRootMenu(parent);
@@ -1543,7 +1557,7 @@ void ConfigMainWindow::setMenuLink(struct menu *menu)
configList->clearSelection();
list = configList;
} else {
- parent = menu_get_parent_menu(menu->parent);
+ parent = menu_get_menu_or_parent_menu(menu->parent);
if (!parent)
return;
@@ -1821,7 +1835,6 @@ int main(int ac, char** av)
configApp = new QApplication(ac, av);
configSettings = new ConfigSettings();
- configSettings->beginGroup("/kconfig/qconf");
v = new ConfigMainWindow();
//zconfdump(stdout);
@@ -1829,7 +1842,6 @@ int main(int ac, char** av)
v->show();
configApp->exec();
- configSettings->endGroup();
delete configSettings;
delete v;
delete configApp;
diff --git a/scripts/kconfig/qconf.h b/scripts/kconfig/qconf.h
index 62ab3286d04f..ab4e51f12914 100644
--- a/scripts/kconfig/qconf.h
+++ b/scripts/kconfig/qconf.h
@@ -24,6 +24,7 @@ class ConfigMainWindow;
class ConfigSettings : public QSettings {
public:
ConfigSettings();
+ ~ConfigSettings(void);
QList<int> readSizes(const QString& key, bool *ok);
bool writeSizes(const QString& key, const QList<int>& value);
};
diff --git a/scripts/kconfig/symbol.c b/scripts/kconfig/symbol.c
index d57f8cbba291..26ab10c0fd76 100644
--- a/scripts/kconfig/symbol.c
+++ b/scripts/kconfig/symbol.c
@@ -195,6 +195,10 @@ static void sym_set_changed(struct symbol *sym)
list_for_each_entry(menu, &sym->menus, link)
menu->flags |= MENU_CHANGED;
+
+ menu = sym_get_choice_menu(sym);
+ if (menu)
+ menu->flags |= MENU_CHANGED;
}
static void sym_set_all_changed(void)
diff --git a/scripts/kernel-doc.py b/scripts/kernel-doc.py
index 12ae66f40bd7..fc3d46ef519f 100755
--- a/scripts/kernel-doc.py
+++ b/scripts/kernel-doc.py
@@ -271,6 +271,16 @@ def main():
logger.addHandler(handler)
+ python_ver = sys.version_info[:2]
+ if python_ver < (3,6):
+ logger.warning("Python 3.6 or later is required by kernel-doc")
+
+ # Return 0 here to avoid breaking compilation
+ sys.exit(0)
+
+ if python_ver < (3,7):
+ logger.warning("Python 3.7 or later is required for correct results")
+
if args.man:
out_style = ManFormat(modulename=args.modulename)
elif args.none:
diff --git a/scripts/lib/kdoc/kdoc_files.py b/scripts/lib/kdoc/kdoc_files.py
index 9be4a64df71d..9e09b45b02fa 100644
--- a/scripts/lib/kdoc/kdoc_files.py
+++ b/scripts/lib/kdoc/kdoc_files.py
@@ -275,8 +275,8 @@ class KernelFiles():
self.config.log.warning("No kernel-doc for file %s", fname)
continue
- for name, arg in self.results[fname]:
- m = self.out_msg(fname, name, arg)
+ for arg in self.results[fname]:
+ m = self.out_msg(fname, arg.name, arg)
if m is None:
ln = arg.get("ln", 0)
diff --git a/scripts/lib/kdoc/kdoc_item.py b/scripts/lib/kdoc/kdoc_item.py
new file mode 100644
index 000000000000..b3b225764550
--- /dev/null
+++ b/scripts/lib/kdoc/kdoc_item.py
@@ -0,0 +1,42 @@
+# SPDX-License-Identifier: GPL-2.0
+#
+# A class that will, eventually, encapsulate all of the parsed data that we
+# then pass into the output modules.
+#
+
+class KdocItem:
+ def __init__(self, name, type, start_line, **other_stuff):
+ self.name = name
+ self.type = type
+ self.declaration_start_line = start_line
+ self.sections = {}
+ self.sections_start_lines = {}
+ self.parameterlist = []
+ self.parameterdesc_start_lines = []
+ self.parameterdescs = {}
+ self.parametertypes = {}
+ #
+ # Just save everything else into our own dict so that the output
+ # side can grab it directly as before. As we move things into more
+ # structured data, this will, hopefully, fade away.
+ #
+ self.other_stuff = other_stuff
+
+ def get(self, key, default = None):
+ return self.other_stuff.get(key, default)
+
+ def __getitem__(self, key):
+ return self.get(key)
+
+ #
+ # Tracking of section and parameter information.
+ #
+ def set_sections(self, sections, start_lines):
+ self.sections = sections
+ self.section_start_lines = start_lines
+
+ def set_params(self, names, descs, types, starts):
+ self.parameterlist = names
+ self.parameterdescs = descs
+ self.parametertypes = types
+ self.parameterdesc_start_lines = starts
diff --git a/scripts/lib/kdoc/kdoc_output.py b/scripts/lib/kdoc/kdoc_output.py
index 86102e628d91..ea8914537ba0 100644
--- a/scripts/lib/kdoc/kdoc_output.py
+++ b/scripts/lib/kdoc/kdoc_output.py
@@ -124,9 +124,7 @@ class OutputFormat:
Output warnings for identifiers that will be displayed.
"""
- warnings = args.get('warnings', [])
-
- for log_msg in warnings:
+ for log_msg in args.warnings:
self.config.warning(log_msg)
def check_doc(self, name, args):
@@ -184,7 +182,7 @@ class OutputFormat:
self.data = ""
- dtype = args.get('type', "")
+ dtype = args.type
if dtype == "doc":
self.out_doc(fname, name, args)
@@ -338,12 +336,7 @@ class RestFormat(OutputFormat):
starts by putting out the name of the doc section itself, but that
tends to duplicate a header already in the template file.
"""
-
- sectionlist = args.get('sectionlist', [])
- sections = args.get('sections', {})
- section_start_lines = args.get('section_start_lines', {})
-
- for section in sectionlist:
+ for section, text in args.sections.items():
# Skip sections that are in the nosymbol_table
if section in self.nosymbol:
continue
@@ -355,8 +348,8 @@ class RestFormat(OutputFormat):
else:
self.data += f'{self.lineprefix}**{section}**\n\n'
- self.print_lineno(section_start_lines.get(section, 0))
- self.output_highlight(sections[section])
+ self.print_lineno(args.section_start_lines.get(section, 0))
+ self.output_highlight(text)
self.data += "\n"
self.data += "\n"
@@ -372,24 +365,19 @@ class RestFormat(OutputFormat):
func_macro = args.get('func_macro', False)
if func_macro:
- signature = args['function']
+ signature = name
else:
if args.get('functiontype'):
signature = args['functiontype'] + " "
- signature += args['function'] + " ("
-
- parameterlist = args.get('parameterlist', [])
- parameterdescs = args.get('parameterdescs', {})
- parameterdesc_start_lines = args.get('parameterdesc_start_lines', {})
-
- ln = args.get('declaration_start_line', 0)
+ signature += name + " ("
+ ln = args.declaration_start_line
count = 0
- for parameter in parameterlist:
+ for parameter in args.parameterlist:
if count != 0:
signature += ", "
count += 1
- dtype = args['parametertypes'].get(parameter, "")
+ dtype = args.parametertypes.get(parameter, "")
if function_pointer.search(dtype):
signature += function_pointer.group(1) + parameter + function_pointer.group(3)
@@ -401,7 +389,7 @@ class RestFormat(OutputFormat):
self.print_lineno(ln)
if args.get('typedef') or not args.get('functiontype'):
- self.data += f".. c:macro:: {args['function']}\n\n"
+ self.data += f".. c:macro:: {name}\n\n"
if args.get('typedef'):
self.data += " **Typedef**: "
@@ -424,26 +412,26 @@ class RestFormat(OutputFormat):
# function prototypes apart
self.lineprefix = " "
- if parameterlist:
+ if args.parameterlist:
self.data += ".. container:: kernelindent\n\n"
self.data += f"{self.lineprefix}**Parameters**\n\n"
- for parameter in parameterlist:
+ for parameter in args.parameterlist:
parameter_name = KernRe(r'\[.*').sub('', parameter)
- dtype = args['parametertypes'].get(parameter, "")
+ dtype = args.parametertypes.get(parameter, "")
if dtype:
self.data += f"{self.lineprefix}``{dtype}``\n"
else:
self.data += f"{self.lineprefix}``{parameter}``\n"
- self.print_lineno(parameterdesc_start_lines.get(parameter_name, 0))
+ self.print_lineno(args.parameterdesc_start_lines.get(parameter_name, 0))
self.lineprefix = " "
- if parameter_name in parameterdescs and \
- parameterdescs[parameter_name] != KernelDoc.undescribed:
+ if parameter_name in args.parameterdescs and \
+ args.parameterdescs[parameter_name] != KernelDoc.undescribed:
- self.output_highlight(parameterdescs[parameter_name])
+ self.output_highlight(args.parameterdescs[parameter_name])
self.data += "\n"
else:
self.data += f"{self.lineprefix}*undescribed*\n\n"
@@ -455,10 +443,7 @@ class RestFormat(OutputFormat):
def out_enum(self, fname, name, args):
oldprefix = self.lineprefix
- name = args.get('enum', '')
- parameterlist = args.get('parameterlist', [])
- parameterdescs = args.get('parameterdescs', {})
- ln = args.get('declaration_start_line', 0)
+ ln = args.declaration_start_line
self.data += f"\n\n.. c:enum:: {name}\n\n"
@@ -472,11 +457,11 @@ class RestFormat(OutputFormat):
self.lineprefix = outer + " "
self.data += f"{outer}**Constants**\n\n"
- for parameter in parameterlist:
+ for parameter in args.parameterlist:
self.data += f"{outer}``{parameter}``\n"
- if parameterdescs.get(parameter, '') != KernelDoc.undescribed:
- self.output_highlight(parameterdescs[parameter])
+ if args.parameterdescs.get(parameter, '') != KernelDoc.undescribed:
+ self.output_highlight(args.parameterdescs[parameter])
else:
self.data += f"{self.lineprefix}*undescribed*\n\n"
self.data += "\n"
@@ -487,8 +472,7 @@ class RestFormat(OutputFormat):
def out_typedef(self, fname, name, args):
oldprefix = self.lineprefix
- name = args.get('typedef', '')
- ln = args.get('declaration_start_line', 0)
+ ln = args.declaration_start_line
self.data += f"\n\n.. c:type:: {name}\n\n"
@@ -504,15 +488,10 @@ class RestFormat(OutputFormat):
def out_struct(self, fname, name, args):
- name = args.get('struct', "")
purpose = args.get('purpose', "")
declaration = args.get('definition', "")
- dtype = args.get('type', "struct")
- ln = args.get('declaration_start_line', 0)
-
- parameterlist = args.get('parameterlist', [])
- parameterdescs = args.get('parameterdescs', {})
- parameterdesc_start_lines = args.get('parameterdesc_start_lines', {})
+ dtype = args.type
+ ln = args.declaration_start_line
self.data += f"\n\n.. c:{dtype}:: {name}\n\n"
@@ -536,21 +515,21 @@ class RestFormat(OutputFormat):
self.lineprefix = " "
self.data += f"{self.lineprefix}**Members**\n\n"
- for parameter in parameterlist:
+ for parameter in args.parameterlist:
if not parameter or parameter.startswith("#"):
continue
parameter_name = parameter.split("[", maxsplit=1)[0]
- if parameterdescs.get(parameter_name) == KernelDoc.undescribed:
+ if args.parameterdescs.get(parameter_name) == KernelDoc.undescribed:
continue
- self.print_lineno(parameterdesc_start_lines.get(parameter_name, 0))
+ self.print_lineno(args.parameterdesc_start_lines.get(parameter_name, 0))
self.data += f"{self.lineprefix}``{parameter}``\n"
self.lineprefix = " "
- self.output_highlight(parameterdescs[parameter_name])
+ self.output_highlight(args.parameterdescs[parameter_name])
self.lineprefix = " "
self.data += "\n"
@@ -636,46 +615,38 @@ class ManFormat(OutputFormat):
self.data += line + "\n"
def out_doc(self, fname, name, args):
- sectionlist = args.get('sectionlist', [])
- sections = args.get('sections', {})
-
if not self.check_doc(name, args):
return
self.data += f'.TH "{self.modulename}" 9 "{self.modulename}" "{self.man_date}" "API Manual" LINUX' + "\n"
- for section in sectionlist:
+ for section, text in args.sections.items():
self.data += f'.SH "{section}"' + "\n"
- self.output_highlight(sections.get(section))
+ self.output_highlight(text)
def out_function(self, fname, name, args):
"""output function in man"""
- parameterlist = args.get('parameterlist', [])
- parameterdescs = args.get('parameterdescs', {})
- sectionlist = args.get('sectionlist', [])
- sections = args.get('sections', {})
-
- self.data += f'.TH "{args["function"]}" 9 "{args["function"]}" "{self.man_date}" "Kernel Hacker\'s Manual" LINUX' + "\n"
+ self.data += f'.TH "{name}" 9 "{name}" "{self.man_date}" "Kernel Hacker\'s Manual" LINUX' + "\n"
self.data += ".SH NAME\n"
- self.data += f"{args['function']} \\- {args['purpose']}\n"
+ self.data += f"{name} \\- {args['purpose']}\n"
self.data += ".SH SYNOPSIS\n"
if args.get('functiontype', ''):
- self.data += f'.B "{args["functiontype"]}" {args["function"]}' + "\n"
+ self.data += f'.B "{args["functiontype"]}" {name}' + "\n"
else:
- self.data += f'.B "{args["function"]}' + "\n"
+ self.data += f'.B "{name}' + "\n"
count = 0
parenth = "("
post = ","
- for parameter in parameterlist:
- if count == len(parameterlist) - 1:
+ for parameter in args.parameterlist:
+ if count == len(args.parameterlist) - 1:
post = ");"
- dtype = args['parametertypes'].get(parameter, "")
+ dtype = args.parametertypes.get(parameter, "")
if function_pointer.match(dtype):
# Pointer-to-function
self.data += f'".BI "{parenth}{function_pointer.group(1)}" " ") ({function_pointer.group(2)}){post}"' + "\n"
@@ -686,38 +657,32 @@ class ManFormat(OutputFormat):
count += 1
parenth = ""
- if parameterlist:
+ if args.parameterlist:
self.data += ".SH ARGUMENTS\n"
- for parameter in parameterlist:
+ for parameter in args.parameterlist:
parameter_name = re.sub(r'\[.*', '', parameter)
self.data += f'.IP "{parameter}" 12' + "\n"
- self.output_highlight(parameterdescs.get(parameter_name, ""))
+ self.output_highlight(args.parameterdescs.get(parameter_name, ""))
- for section in sectionlist:
+ for section, text in args.sections.items():
self.data += f'.SH "{section.upper()}"' + "\n"
- self.output_highlight(sections[section])
+ self.output_highlight(text)
def out_enum(self, fname, name, args):
-
- name = args.get('enum', '')
- parameterlist = args.get('parameterlist', [])
- sectionlist = args.get('sectionlist', [])
- sections = args.get('sections', {})
-
- self.data += f'.TH "{self.modulename}" 9 "enum {args["enum"]}" "{self.man_date}" "API Manual" LINUX' + "\n"
+ self.data += f'.TH "{self.modulename}" 9 "enum {name}" "{self.man_date}" "API Manual" LINUX' + "\n"
self.data += ".SH NAME\n"
- self.data += f"enum {args['enum']} \\- {args['purpose']}\n"
+ self.data += f"enum {name} \\- {args['purpose']}\n"
self.data += ".SH SYNOPSIS\n"
- self.data += f"enum {args['enum']}" + " {\n"
+ self.data += f"enum {name}" + " {\n"
count = 0
- for parameter in parameterlist:
+ for parameter in args.parameterlist:
self.data += f'.br\n.BI " {parameter}"' + "\n"
- if count == len(parameterlist) - 1:
+ if count == len(args.parameterlist) - 1:
self.data += "\n};\n"
else:
self.data += ", \n.br\n"
@@ -726,68 +691,59 @@ class ManFormat(OutputFormat):
self.data += ".SH Constants\n"
- for parameter in parameterlist:
+ for parameter in args.parameterlist:
parameter_name = KernRe(r'\[.*').sub('', parameter)
self.data += f'.IP "{parameter}" 12' + "\n"
- self.output_highlight(args['parameterdescs'].get(parameter_name, ""))
+ self.output_highlight(args.parameterdescs.get(parameter_name, ""))
- for section in sectionlist:
+ for section, text in args.sections.items():
self.data += f'.SH "{section}"' + "\n"
- self.output_highlight(sections[section])
+ self.output_highlight(text)
def out_typedef(self, fname, name, args):
module = self.modulename
- typedef = args.get('typedef')
purpose = args.get('purpose')
- sectionlist = args.get('sectionlist', [])
- sections = args.get('sections', {})
- self.data += f'.TH "{module}" 9 "{typedef}" "{self.man_date}" "API Manual" LINUX' + "\n"
+ self.data += f'.TH "{module}" 9 "{name}" "{self.man_date}" "API Manual" LINUX' + "\n"
self.data += ".SH NAME\n"
- self.data += f"typedef {typedef} \\- {purpose}\n"
+ self.data += f"typedef {name} \\- {purpose}\n"
- for section in sectionlist:
+ for section, text in args.sections.items():
self.data += f'.SH "{section}"' + "\n"
- self.output_highlight(sections.get(section))
+ self.output_highlight(text)
def out_struct(self, fname, name, args):
module = self.modulename
- struct_type = args.get('type')
- struct_name = args.get('struct')
purpose = args.get('purpose')
definition = args.get('definition')
- sectionlist = args.get('sectionlist', [])
- parameterlist = args.get('parameterlist', [])
- sections = args.get('sections', {})
- parameterdescs = args.get('parameterdescs', {})
- self.data += f'.TH "{module}" 9 "{struct_type} {struct_name}" "{self.man_date}" "API Manual" LINUX' + "\n"
+ self.data += f'.TH "{module}" 9 "{args.type} {name}" "{self.man_date}" "API Manual" LINUX' + "\n"
self.data += ".SH NAME\n"
- self.data += f"{struct_type} {struct_name} \\- {purpose}\n"
+ self.data += f"{args.type} {name} \\- {purpose}\n"
# Replace tabs with two spaces and handle newlines
declaration = definition.replace("\t", " ")
declaration = KernRe(r"\n").sub('"\n.br\n.BI "', declaration)
self.data += ".SH SYNOPSIS\n"
- self.data += f"{struct_type} {struct_name} " + "{" + "\n.br\n"
+ self.data += f"{args.type} {name} " + "{" + "\n.br\n"
self.data += f'.BI "{declaration}\n' + "};\n.br\n\n"
self.data += ".SH Members\n"
- for parameter in parameterlist:
+ for parameter in args.parameterlist:
if parameter.startswith("#"):
continue
parameter_name = re.sub(r"\[.*", "", parameter)
- if parameterdescs.get(parameter_name) == KernelDoc.undescribed:
+ if args.parameterdescs.get(parameter_name) == KernelDoc.undescribed:
continue
self.data += f'.IP "{parameter}" 12' + "\n"
- self.output_highlight(parameterdescs.get(parameter_name))
+ self.output_highlight(args.parameterdescs.get(parameter_name))
- for section in sectionlist:
+ for section, text in args.sections.items():
self.data += f'.SH "{section}"' + "\n"
- self.output_highlight(sections.get(section))
+ self.output_highlight(text)
diff --git a/scripts/lib/kdoc/kdoc_parser.py b/scripts/lib/kdoc/kdoc_parser.py
index 062453eefc7a..fe730099eca8 100644
--- a/scripts/lib/kdoc/kdoc_parser.py
+++ b/scripts/lib/kdoc/kdoc_parser.py
@@ -12,11 +12,12 @@ Read a C language source or header FILE and extract embedded
documentation comments
"""
+import sys
import re
from pprint import pformat
from kdoc_re import NestedMatch, KernRe
-
+from kdoc_item import KdocItem
#
# Regular expressions used to parse kernel-doc markups at KernelDoc class.
@@ -42,12 +43,13 @@ doc_decl = doc_com + KernRe(r'(\w+)', cache=False)
# @{section-name}:
# while trying to not match literal block starts like "example::"
#
+known_section_names = 'description|context|returns?|notes?|examples?'
+known_sections = KernRe(known_section_names, flags = re.I)
doc_sect = doc_com + \
- KernRe(r'\s*(\@[.\w]+|\@\.\.\.|description|context|returns?|notes?|examples?)\s*:([^:].*)?$',
- flags=re.I, cache=False)
+ KernRe(r'\s*(\@[.\w]+|\@\.\.\.|' + known_section_names + r')\s*:([^:].*)?$',
+ flags=re.I, cache=False)
doc_content = doc_com_body + KernRe(r'(.*)', cache=False)
-doc_block = doc_com + KernRe(r'DOC:\s*(.*)?', cache=False)
doc_inline_start = KernRe(r'^\s*/\*\*\s*$', cache=False)
doc_inline_sect = KernRe(r'\s*\*\s*(@\s*[\w][\w\.]*\s*):(.*)', cache=False)
doc_inline_end = KernRe(r'^\s*\*/\s*$', cache=False)
@@ -60,6 +62,25 @@ export_symbol_ns = KernRe(r'^\s*EXPORT_SYMBOL_NS(_GPL)?\s*\(\s*(\w+)\s*,\s*"\S+"
type_param = KernRe(r"\@(\w*((\.\w+)|(->\w+))*(\.\.\.)?)", cache=False)
+#
+# Tests for the beginning of a kerneldoc block in its various forms.
+#
+doc_block = doc_com + KernRe(r'DOC:\s*(.*)?', cache=False)
+doc_begin_data = KernRe(r"^\s*\*?\s*(struct|union|enum|typedef)\b\s*(\w*)", cache = False)
+doc_begin_func = KernRe(str(doc_com) + # initial " * '
+ r"(?:\w+\s*\*\s*)?" + # type (not captured)
+ r'(?:define\s+)?' + # possible "define" (not captured)
+ r'(\w+)\s*(?:\(\w*\))?\s*' + # name and optional "(...)"
+ r'(?:[-:].*)?$', # description (not captured)
+ cache = False)
+
+#
+# A little helper to get rid of excess white space
+#
+multi_space = KernRe(r'\s\s+')
+def trim_whitespace(s):
+ return multi_space.sub(' ', s.strip())
+
class state:
"""
State machine enums
@@ -68,40 +89,26 @@ class state:
# Parser states
NORMAL = 0 # normal code
NAME = 1 # looking for function name
- BODY_MAYBE = 2 # body - or maybe more description
+ DECLARATION = 2 # We have seen a declaration which might not be done
BODY = 3 # the body of the comment
- BODY_WITH_BLANK_LINE = 4 # the body which has a blank line
+ SPECIAL_SECTION = 4 # doc section ending with a blank line
PROTO = 5 # scanning prototype
DOCBLOCK = 6 # documentation block
- INLINE = 7 # gathering doc outside main block
+ INLINE_NAME = 7 # gathering doc outside main block
+ INLINE_TEXT = 8 # reading the body of inline docs
name = [
"NORMAL",
"NAME",
- "BODY_MAYBE",
+ "DECLARATION",
"BODY",
- "BODY_WITH_BLANK_LINE",
+ "SPECIAL_SECTION",
"PROTO",
"DOCBLOCK",
- "INLINE",
+ "INLINE_NAME",
+ "INLINE_TEXT",
]
- # Inline documentation state
- INLINE_NA = 0 # not applicable ($state != INLINE)
- INLINE_NAME = 1 # looking for member name (@foo:)
- INLINE_TEXT = 2 # looking for member documentation
- INLINE_END = 3 # done
- INLINE_ERROR = 4 # error - Comment without header was found.
- # Spit a warning as it's not
- # proper kernel-doc and ignore the rest.
-
- inline_name = [
- "",
- "_NAME",
- "_TEXT",
- "_END",
- "_ERROR",
- ]
SECTION_DEFAULT = "Description" # default section
@@ -110,10 +117,7 @@ class KernelEntry:
def __init__(self, config, ln):
self.config = config
- self.contents = ""
- self.function = ""
- self.sectcheck = ""
- self.struct_actual = ""
+ self._contents = []
self.prototype = ""
self.warnings = []
@@ -124,7 +128,6 @@ class KernelEntry:
self.parameterdesc_start_lines = {}
self.section_start_lines = {}
- self.sectionlist = []
self.sections = {}
self.anon_struct_union = False
@@ -133,10 +136,17 @@ class KernelEntry:
# State flags
self.brcount = 0
-
- self.in_doc_sect = False
self.declaration_start_line = ln + 1
+ #
+ # Management of section contents
+ #
+ def add_text(self, text):
+ self._contents.append(text)
+
+ def contents(self):
+ return '\n'.join(self._contents) + '\n'
+
# TODO: rename to emit_message after removal of kernel-doc.pl
def emit_msg(self, log_msg, warning=True):
"""Emit a message"""
@@ -151,13 +161,27 @@ class KernelEntry:
self.warnings.append(log_msg)
return
+ #
+ # Begin a new section.
+ #
+ def begin_section(self, line_no, title = SECTION_DEFAULT, dump = False):
+ if dump:
+ self.dump_section(start_new = True)
+ self.section = title
+ self.new_start_line = line_no
+
def dump_section(self, start_new=True):
"""
Dumps section contents to arrays/hashes intended for that purpose.
"""
-
+ #
+ # If we have accumulated no contents in the default ("description")
+ # section, don't bother.
+ #
+ if self.section == SECTION_DEFAULT and not self._contents:
+ return
name = self.section
- contents = self.contents
+ contents = self.contents()
if type_param.match(name):
name = type_param.group(1)
@@ -165,14 +189,6 @@ class KernelEntry:
self.parameterdescs[name] = contents
self.parameterdesc_start_lines[name] = self.new_start_line
- self.sectcheck += name + " "
- self.new_start_line = 0
-
- elif name == "@...":
- name = "..."
- self.parameterdescs[name] = contents
- self.sectcheck += name + " "
- self.parameterdesc_start_lines[name] = self.new_start_line
self.new_start_line = 0
else:
@@ -181,10 +197,10 @@ class KernelEntry:
if name != SECTION_DEFAULT:
self.emit_msg(self.new_start_line,
f"duplicate section name '{name}'\n")
- self.sections[name] += contents
+ # Treat as a new paragraph - add a blank line
+ self.sections[name] += '\n' + contents
else:
self.sections[name] = contents
- self.sectionlist.append(name)
self.section_start_lines[name] = self.new_start_line
self.new_start_line = 0
@@ -192,7 +208,7 @@ class KernelEntry:
if start_new:
self.section = SECTION_DEFAULT
- self.contents = ""
+ self._contents = []
class KernelDoc:
@@ -203,7 +219,6 @@ class KernelDoc:
# Section names
- section_intro = "Introduction"
section_context = "Context"
section_return = "Return"
@@ -217,7 +232,6 @@ class KernelDoc:
# Initial state for the state machines
self.state = state.NORMAL
- self.inline_doc_state = state.INLINE_NA
# Store entry currently being processed
self.entry = None
@@ -225,6 +239,14 @@ class KernelDoc:
# Place all potential outputs into an array
self.entries = []
+ #
+ # We need Python 3.7 for its "dicts remember the insertion
+ # order" guarantee
+ #
+ if sys.version_info.major == 3 and sys.version_info.minor < 7:
+ self.emit_msg(0,
+ 'Python 3.7 or later is required for correct results')
+
def emit_msg(self, ln, msg, warning=True):
"""Emit a message"""
@@ -255,32 +277,20 @@ class KernelDoc:
The actual output and output filters will be handled elsewhere
"""
- # The implementation here is different than the original kernel-doc:
- # instead of checking for output filters or actually output anything,
- # it just stores the declaration content at self.entries, as the
- # output will happen on a separate class.
- #
- # For now, we're keeping the same name of the function just to make
- # easier to compare the source code of both scripts
-
- args["declaration_start_line"] = self.entry.declaration_start_line
- args["type"] = dtype
- args["warnings"] = self.entry.warnings
-
- # TODO: use colletions.OrderedDict to remove sectionlist
-
- sections = args.get('sections', {})
- sectionlist = args.get('sectionlist', [])
+ item = KdocItem(name, dtype, self.entry.declaration_start_line, **args)
+ item.warnings = self.entry.warnings
# Drop empty sections
# TODO: improve empty sections logic to emit warnings
+ sections = self.entry.sections
for section in ["Description", "Return"]:
- if section in sectionlist:
- if not sections[section].rstrip():
- del sections[section]
- sectionlist.remove(section)
-
- self.entries.append((name, args))
+ if section in sections and not sections[section].rstrip():
+ del sections[section]
+ item.set_sections(sections, self.entry.section_start_lines)
+ item.set_params(self.entry.parameterlist, self.entry.parameterdescs,
+ self.entry.parametertypes,
+ self.entry.parameterdesc_start_lines)
+ self.entries.append(item)
self.config.log.debug("Output: %s:%s = %s", dtype, name, pformat(args))
@@ -294,7 +304,6 @@ class KernelDoc:
# State flags
self.state = state.NORMAL
- self.inline_doc_state = state.INLINE_NA
def push_parameter(self, ln, decl_type, param, dtype,
org_arg, declaration_name):
@@ -367,15 +376,6 @@ class KernelDoc:
org_arg = KernRe(r'\s\s+').sub(' ', org_arg)
self.entry.parametertypes[param] = org_arg
- def save_struct_actual(self, actual):
- """
- Strip all spaces from the actual param so that it looks like
- one string item.
- """
-
- actual = KernRe(r'\s*').sub("", actual, count=1)
-
- self.entry.struct_actual += actual + " "
def create_parameter_list(self, ln, decl_type, args,
splitter, declaration_name):
@@ -421,7 +421,6 @@ class KernelDoc:
param = arg
dtype = KernRe(r'([^\(]+\(\*?)\s*' + re.escape(param)).sub(r'\1', arg)
- self.save_struct_actual(param)
self.push_parameter(ln, decl_type, param, dtype,
arg, declaration_name)
@@ -438,7 +437,6 @@ class KernelDoc:
dtype = KernRe(r'([^\(]+\(\*?)\s*' + re.escape(param)).sub(r'\1', arg)
- self.save_struct_actual(param)
self.push_parameter(ln, decl_type, param, dtype,
arg, declaration_name)
@@ -471,7 +469,6 @@ class KernelDoc:
param = r.group(1)
- self.save_struct_actual(r.group(2))
self.push_parameter(ln, decl_type, r.group(2),
f"{dtype} {r.group(1)}",
arg, declaration_name)
@@ -483,52 +480,27 @@ class KernelDoc:
continue
if dtype != "": # Skip unnamed bit-fields
- self.save_struct_actual(r.group(1))
self.push_parameter(ln, decl_type, r.group(1),
f"{dtype}:{r.group(2)}",
arg, declaration_name)
else:
- self.save_struct_actual(param)
self.push_parameter(ln, decl_type, param, dtype,
arg, declaration_name)
- def check_sections(self, ln, decl_name, decl_type, sectcheck, prmscheck):
+ def check_sections(self, ln, decl_name, decl_type):
"""
Check for errors inside sections, emitting warnings if not found
parameters are described.
"""
-
- sects = sectcheck.split()
- prms = prmscheck.split()
- err = False
-
- for sx in range(len(sects)): # pylint: disable=C0200
- err = True
- for px in range(len(prms)): # pylint: disable=C0200
- prm_clean = prms[px]
- prm_clean = KernRe(r'\[.*\]').sub('', prm_clean)
- prm_clean = attribute.sub('', prm_clean)
-
- # ignore array size in a parameter string;
- # however, the original param string may contain
- # spaces, e.g.: addr[6 + 2]
- # and this appears in @prms as "addr[6" since the
- # parameter list is split at spaces;
- # hence just ignore "[..." for the sections check;
- prm_clean = KernRe(r'\[.*').sub('', prm_clean)
-
- if prm_clean == sects[sx]:
- err = False
- break
-
- if err:
+ for section in self.entry.sections:
+ if section not in self.entry.parameterlist and \
+ not known_sections.search(section):
if decl_type == 'function':
dname = f"{decl_type} parameter"
else:
dname = f"{decl_type} member"
-
self.emit_msg(ln,
- f"Excess {dname} '{sects[sx]}' description in '{decl_name}'")
+ f"Excess {dname} '{section}' description in '{decl_name}'")
def check_return_section(self, ln, declaration_name, return_type):
"""
@@ -666,6 +638,7 @@ class KernelDoc:
(KernRe(r'(?:__)?DECLARE_FLEX_ARRAY\s*\(' + args_pattern + r',\s*' + args_pattern + r'\)', re.S), r'\1 \2[]'),
(KernRe(r'DEFINE_DMA_UNMAP_ADDR\s*\(' + args_pattern + r'\)', re.S), r'dma_addr_t \1'),
(KernRe(r'DEFINE_DMA_UNMAP_LEN\s*\(' + args_pattern + r'\)', re.S), r'__u32 \1'),
+ (KernRe(r'VIRTIO_DECLARE_FEATURES\s*\(' + args_pattern + r'\)', re.S), r'u64 \1; u64 \1_array[VIRTIO_FEATURES_DWORDS]'),
]
# Regexes here are guaranteed to have the end limiter matching
@@ -782,8 +755,7 @@ class KernelDoc:
self.create_parameter_list(ln, decl_type, members, ';',
declaration_name)
- self.check_sections(ln, declaration_name, decl_type,
- self.entry.sectcheck, self.entry.struct_actual)
+ self.check_sections(ln, declaration_name, decl_type)
# Adjust declaration for better display
declaration = KernRe(r'([\{;])').sub(r'\1\n', declaration)
@@ -819,15 +791,7 @@ class KernelDoc:
level += 1
self.output_declaration(decl_type, declaration_name,
- struct=declaration_name,
definition=declaration,
- parameterlist=self.entry.parameterlist,
- parameterdescs=self.entry.parameterdescs,
- parametertypes=self.entry.parametertypes,
- parameterdesc_start_lines=self.entry.parameterdesc_start_lines,
- sectionlist=self.entry.sectionlist,
- sections=self.entry.sections,
- section_start_lines=self.entry.section_start_lines,
purpose=self.entry.declaration_purpose)
def dump_enum(self, ln, proto):
@@ -845,39 +809,48 @@ class KernelDoc:
# Strip #define macros inside enums
proto = KernRe(r'#\s*((define|ifdef|if)\s+|endif)[^;]*;', flags=re.S).sub('', proto)
- members = None
- declaration_name = None
-
+ #
+ # Parse out the name and members of the enum. Typedef form first.
+ #
r = KernRe(r'typedef\s+enum\s*\{(.*)\}\s*(\w*)\s*;')
if r.search(proto):
declaration_name = r.group(2)
members = r.group(1).rstrip()
+ #
+ # Failing that, look for a straight enum
+ #
else:
r = KernRe(r'enum\s+(\w*)\s*\{(.*)\}')
if r.match(proto):
declaration_name = r.group(1)
members = r.group(2).rstrip()
-
- if not members:
- self.emit_msg(ln, f"{proto}: error: Cannot parse enum!")
- return
-
+ #
+ # OK, this isn't going to work.
+ #
+ else:
+ self.emit_msg(ln, f"{proto}: error: Cannot parse enum!")
+ return
+ #
+ # Make sure we found what we were expecting.
+ #
if self.entry.identifier != declaration_name:
if self.entry.identifier == "":
self.emit_msg(ln,
f"{proto}: wrong kernel-doc identifier on prototype")
else:
self.emit_msg(ln,
- f"expecting prototype for enum {self.entry.identifier}. Prototype was for enum {declaration_name} instead")
+ f"expecting prototype for enum {self.entry.identifier}. "
+ f"Prototype was for enum {declaration_name} instead")
return
if not declaration_name:
declaration_name = "(anonymous)"
-
+ #
+ # Parse out the name of each enum member, and verify that we
+ # have a description for it.
+ #
member_set = set()
-
- members = KernRe(r'\([^;]*?[\)]').sub('', members)
-
+ members = KernRe(r'\([^;)]*\)').sub('', members)
for arg in members.split(','):
if not arg:
continue
@@ -888,20 +861,15 @@ class KernelDoc:
self.emit_msg(ln,
f"Enum value '{arg}' not described in enum '{declaration_name}'")
member_set.add(arg)
-
+ #
+ # Ensure that every described member actually exists in the enum.
+ #
for k in self.entry.parameterdescs:
if k not in member_set:
self.emit_msg(ln,
f"Excess enum value '%{k}' description in '{declaration_name}'")
self.output_declaration('enum', declaration_name,
- enum=declaration_name,
- parameterlist=self.entry.parameterlist,
- parameterdescs=self.entry.parameterdescs,
- parameterdesc_start_lines=self.entry.parameterdesc_start_lines,
- sectionlist=self.entry.sectionlist,
- sections=self.entry.sections,
- section_start_lines=self.entry.section_start_lines,
purpose=self.entry.declaration_purpose)
def dump_declaration(self, ln, prototype):
@@ -911,18 +879,13 @@ class KernelDoc:
if self.entry.decl_type == "enum":
self.dump_enum(ln, prototype)
- return
-
- if self.entry.decl_type == "typedef":
+ elif self.entry.decl_type == "typedef":
self.dump_typedef(ln, prototype)
- return
-
- if self.entry.decl_type in ["union", "struct"]:
+ elif self.entry.decl_type in ["union", "struct"]:
self.dump_struct(ln, prototype)
- return
-
- self.output_declaration(self.entry.decl_type, prototype,
- entry=self.entry)
+ else:
+ # This would be a bug
+ self.emit_message(ln, f'Unknown declaration type: {self.entry.decl_type}')
def dump_function(self, ln, prototype):
"""
@@ -1056,38 +1019,20 @@ class KernelDoc:
f"expecting prototype for {self.entry.identifier}(). Prototype was for {declaration_name}() instead")
return
- prms = " ".join(self.entry.parameterlist)
- self.check_sections(ln, declaration_name, "function",
- self.entry.sectcheck, prms)
+ self.check_sections(ln, declaration_name, "function")
self.check_return_section(ln, declaration_name, return_type)
if 'typedef' in return_type:
self.output_declaration(decl_type, declaration_name,
- function=declaration_name,
typedef=True,
functiontype=return_type,
- parameterlist=self.entry.parameterlist,
- parameterdescs=self.entry.parameterdescs,
- parametertypes=self.entry.parametertypes,
- parameterdesc_start_lines=self.entry.parameterdesc_start_lines,
- sectionlist=self.entry.sectionlist,
- sections=self.entry.sections,
- section_start_lines=self.entry.section_start_lines,
purpose=self.entry.declaration_purpose,
func_macro=func_macro)
else:
self.output_declaration(decl_type, declaration_name,
- function=declaration_name,
typedef=False,
functiontype=return_type,
- parameterlist=self.entry.parameterlist,
- parameterdescs=self.entry.parameterdescs,
- parametertypes=self.entry.parametertypes,
- parameterdesc_start_lines=self.entry.parameterdesc_start_lines,
- sectionlist=self.entry.sectionlist,
- sections=self.entry.sections,
- section_start_lines=self.entry.section_start_lines,
purpose=self.entry.declaration_purpose,
func_macro=func_macro)
@@ -1124,16 +1069,8 @@ class KernelDoc:
self.create_parameter_list(ln, decl_type, args, ',', declaration_name)
self.output_declaration(decl_type, declaration_name,
- function=declaration_name,
typedef=True,
functiontype=return_type,
- parameterlist=self.entry.parameterlist,
- parameterdescs=self.entry.parameterdescs,
- parametertypes=self.entry.parametertypes,
- parameterdesc_start_lines=self.entry.parameterdesc_start_lines,
- sectionlist=self.entry.sectionlist,
- sections=self.entry.sections,
- section_start_lines=self.entry.section_start_lines,
purpose=self.entry.declaration_purpose)
return
@@ -1153,10 +1090,6 @@ class KernelDoc:
return
self.output_declaration('typedef', declaration_name,
- typedef=declaration_name,
- sectionlist=self.entry.sectionlist,
- sections=self.entry.sections,
- section_start_lines=self.entry.section_start_lines,
purpose=self.entry.declaration_purpose)
return
@@ -1171,17 +1104,28 @@ class KernelDoc:
with a staticmethod decorator.
"""
+ # We support documenting some exported symbols with different
+ # names. A horrible hack.
+ suffixes = [ '_noprof' ]
+
# Note: it accepts only one EXPORT_SYMBOL* per line, as having
# multiple export lines would violate Kernel coding style.
if export_symbol.search(line):
symbol = export_symbol.group(2)
- function_set.add(symbol)
- return
-
- if export_symbol_ns.search(line):
+ elif export_symbol_ns.search(line):
symbol = export_symbol_ns.group(2)
- function_set.add(symbol)
+ else:
+ return False
+ #
+ # Found an export, trim out any special suffixes
+ #
+ for suffix in suffixes:
+ # Be backward compatible with Python < 3.9
+ if symbol.endswith(suffix):
+ symbol = symbol[:-len(suffix)]
+ function_set.add(symbol)
+ return True
def process_normal(self, ln, line):
"""
@@ -1193,7 +1137,6 @@ class KernelDoc:
# start a new entry
self.reset_state(ln)
- self.entry.in_doc_sect = False
# next line is always the function name
self.state = state.NAME
@@ -1202,81 +1145,61 @@ class KernelDoc:
"""
STATE_NAME: Looking for the "name - description" line
"""
-
+ #
+ # Check for a DOC: block and handle them specially.
+ #
if doc_block.search(line):
- self.entry.new_start_line = ln
if not doc_block.group(1):
- self.entry.section = self.section_intro
+ self.entry.begin_section(ln, "Introduction")
else:
- self.entry.section = doc_block.group(1)
+ self.entry.begin_section(ln, doc_block.group(1))
self.entry.identifier = self.entry.section
self.state = state.DOCBLOCK
- return
-
- if doc_decl.search(line):
+ #
+ # Otherwise we're looking for a normal kerneldoc declaration line.
+ #
+ elif doc_decl.search(line):
self.entry.identifier = doc_decl.group(1)
- self.entry.is_kernel_comment = False
-
- decl_start = str(doc_com) # comment block asterisk
- fn_type = r"(?:\w+\s*\*\s*)?" # type (for non-functions)
- parenthesis = r"(?:\(\w*\))?" # optional parenthesis on function
- decl_end = r"(?:[-:].*)" # end of the name part
-
- # test for pointer declaration type, foo * bar() - desc
- r = KernRe(fr"^{decl_start}([\w\s]+?){parenthesis}?\s*{decl_end}?$")
- if r.search(line):
- self.entry.identifier = r.group(1)
# Test for data declaration
- r = KernRe(r"^\s*\*?\s*(struct|union|enum|typedef)\b\s*(\w*)")
- if r.search(line):
- self.entry.decl_type = r.group(1)
- self.entry.identifier = r.group(2)
- self.entry.is_kernel_comment = True
+ if doc_begin_data.search(line):
+ self.entry.decl_type = doc_begin_data.group(1)
+ self.entry.identifier = doc_begin_data.group(2)
+ #
+ # Look for a function description
+ #
+ elif doc_begin_func.search(line):
+ self.entry.identifier = doc_begin_func.group(1)
+ self.entry.decl_type = "function"
+ #
+ # We struck out.
+ #
else:
- # Look for foo() or static void foo() - description;
- # or misspelt identifier
-
- r1 = KernRe(fr"^{decl_start}{fn_type}(\w+)\s*{parenthesis}\s*{decl_end}?$")
- r2 = KernRe(fr"^{decl_start}{fn_type}(\w+[^-:]*){parenthesis}\s*{decl_end}$")
-
- for r in [r1, r2]:
- if r.search(line):
- self.entry.identifier = r.group(1)
- self.entry.decl_type = "function"
-
- r = KernRe(r"define\s+")
- self.entry.identifier = r.sub("", self.entry.identifier)
- self.entry.is_kernel_comment = True
- break
-
- self.entry.identifier = self.entry.identifier.strip(" ")
-
+ self.emit_msg(ln,
+ f"This comment starts with '/**', but isn't a kernel-doc comment. Refer Documentation/doc-guide/kernel-doc.rst\n{line}")
+ self.state = state.NORMAL
+ return
+ #
+ # OK, set up for a new kerneldoc entry.
+ #
self.state = state.BODY
-
+ self.entry.identifier = self.entry.identifier.strip(" ")
# if there's no @param blocks need to set up default section here
- self.entry.section = SECTION_DEFAULT
- self.entry.new_start_line = ln + 1
-
+ self.entry.begin_section(ln + 1)
+ #
+ # Find the description portion, which *should* be there but
+ # isn't always.
+ # (We should be able to capture this from the previous parsing - someday)
+ #
r = KernRe("[-:](.*)")
if r.search(line):
- # strip leading/trailing/multiple spaces
- self.entry.descr = r.group(1).strip(" ")
-
- r = KernRe(r"\s+")
- self.entry.descr = r.sub(" ", self.entry.descr)
- self.entry.declaration_purpose = self.entry.descr
- self.state = state.BODY_MAYBE
+ self.entry.declaration_purpose = trim_whitespace(r.group(1))
+ self.state = state.DECLARATION
else:
self.entry.declaration_purpose = ""
- if not self.entry.is_kernel_comment:
- self.emit_msg(ln,
- f"This comment starts with '/**', but isn't a kernel-doc comment. Refer Documentation/doc-guide/kernel-doc.rst\n{line}")
- self.state = state.NORMAL
-
if not self.entry.declaration_purpose and self.config.wshort_desc:
self.emit_msg(ln,
f"missing initial short description on line:\n{line}")
@@ -1290,60 +1213,51 @@ class KernelDoc:
self.emit_msg(ln,
f"Scanning doc for {self.entry.decl_type} {self.entry.identifier}",
warning=False)
-
- return
-
+ #
# Failed to find an identifier. Emit a warning
- self.emit_msg(ln, f"Cannot find identifier on line:\n{line}")
-
- def process_body(self, ln, line):
- """
- STATE_BODY and STATE_BODY_MAYBE: the bulk of a kerneldoc comment.
- """
-
- if self.state == state.BODY_WITH_BLANK_LINE:
- r = KernRe(r"\s*\*\s?\S")
- if r.match(line):
- self.dump_section()
- self.entry.section = SECTION_DEFAULT
- self.entry.new_start_line = ln
- self.entry.contents = ""
+ #
+ else:
+ self.emit_msg(ln, f"Cannot find identifier on line:\n{line}")
+ #
+ # Helper function to determine if a new section is being started.
+ #
+ def is_new_section(self, ln, line):
if doc_sect.search(line):
- self.entry.in_doc_sect = True
+ self.state = state.BODY
+ #
+ # Pick out the name of our new section, tweaking it if need be.
+ #
newsection = doc_sect.group(1)
-
- if newsection.lower() in ["description", "context"]:
- newsection = newsection.title()
-
- # Special case: @return is a section, not a param description
- if newsection.lower() in ["@return", "@returns",
- "return", "returns"]:
+ if newsection.lower() == 'description':
+ newsection = 'Description'
+ elif newsection.lower() == 'context':
+ newsection = 'Context'
+ self.state = state.SPECIAL_SECTION
+ elif newsection.lower() in ["@return", "@returns",
+ "return", "returns"]:
newsection = "Return"
-
- # Perl kernel-doc has a check here for contents before sections.
- # the logic there is always false, as in_doc_sect variable is
- # always true. So, just don't implement Wcontents_before_sections
-
- # .title()
+ self.state = state.SPECIAL_SECTION
+ elif newsection[0] == '@':
+ self.state = state.SPECIAL_SECTION
+ #
+ # Initialize the contents, and get the new section going.
+ #
newcontents = doc_sect.group(2)
if not newcontents:
newcontents = ""
-
- if self.entry.contents.strip("\n"):
- self.dump_section()
-
- self.entry.new_start_line = ln
- self.entry.section = newsection
+ self.dump_section()
+ self.entry.begin_section(ln, newsection)
self.entry.leading_space = None
- self.entry.contents = newcontents.lstrip()
- if self.entry.contents:
- self.entry.contents += "\n"
-
- self.state = state.BODY
- return
+ self.entry.add_text(newcontents.lstrip())
+ return True
+ return False
+ #
+ # Helper function to detect (and effect) the end of a kerneldoc comment.
+ #
+ def is_comment_end(self, ln, line):
if doc_end.search(line):
self.dump_section()
@@ -1356,100 +1270,128 @@ class KernelDoc:
self.entry.new_start_line = ln + 1
self.state = state.PROTO
- return
+ return True
+ return False
+
+ def process_decl(self, ln, line):
+ """
+ STATE_DECLARATION: We've seen the beginning of a declaration
+ """
+ if self.is_new_section(ln, line) or self.is_comment_end(ln, line):
+ return
+ #
+ # Look for anything with the " * " line beginning.
+ #
if doc_content.search(line):
cont = doc_content.group(1)
-
+ #
+ # A blank line means that we have moved out of the declaration
+ # part of the comment (without any "special section" parameter
+ # descriptions).
+ #
if cont == "":
- if self.entry.section == self.section_context:
- self.dump_section()
-
- self.entry.new_start_line = ln
- self.state = state.BODY
- else:
- if self.entry.section != SECTION_DEFAULT:
- self.state = state.BODY_WITH_BLANK_LINE
- else:
- self.state = state.BODY
-
- self.entry.contents += "\n"
-
- elif self.state == state.BODY_MAYBE:
-
- # Continued declaration purpose
- self.entry.declaration_purpose = self.entry.declaration_purpose.rstrip()
- self.entry.declaration_purpose += " " + cont
-
- r = KernRe(r"\s+")
- self.entry.declaration_purpose = r.sub(' ',
- self.entry.declaration_purpose)
-
+ self.state = state.BODY
+ #
+ # Otherwise we have more of the declaration section to soak up.
+ #
else:
- if self.entry.section.startswith('@') or \
- self.entry.section == self.section_context:
- if self.entry.leading_space is None:
- r = KernRe(r'^(\s+)')
- if r.match(cont):
- self.entry.leading_space = len(r.group(1))
- else:
- self.entry.leading_space = 0
-
- # Double-check if leading space are realy spaces
- pos = 0
- for i in range(0, self.entry.leading_space):
- if cont[i] != " ":
- break
- pos += 1
-
- cont = cont[pos:]
+ self.entry.declaration_purpose = \
+ trim_whitespace(self.entry.declaration_purpose + ' ' + cont)
+ else:
+ # Unknown line, ignore
+ self.emit_msg(ln, f"bad line: {line}")
- # NEW LOGIC:
- # In case it is different, update it
- if self.entry.leading_space != pos:
- self.entry.leading_space = pos
- self.entry.contents += cont + "\n"
+ def process_special(self, ln, line):
+ """
+ STATE_SPECIAL_SECTION: a section ending with a blank line
+ """
+ #
+ # If we have hit a blank line (only the " * " marker), then this
+ # section is done.
+ #
+ if KernRe(r"\s*\*\s*$").match(line):
+ self.entry.begin_section(ln, dump = True)
+ self.state = state.BODY
+ return
+ #
+ # Not a blank line, look for the other ways to end the section.
+ #
+ if self.is_new_section(ln, line) or self.is_comment_end(ln, line):
return
+ #
+ # OK, we should have a continuation of the text for this section.
+ #
+ if doc_content.search(line):
+ cont = doc_content.group(1)
+ #
+ # If the lines of text after the first in a special section have
+ # leading white space, we need to trim it out or Sphinx will get
+ # confused. For the second line (the None case), see what we
+ # find there and remember it.
+ #
+ if self.entry.leading_space is None:
+ r = KernRe(r'^(\s+)')
+ if r.match(cont):
+ self.entry.leading_space = len(r.group(1))
+ else:
+ self.entry.leading_space = 0
+ #
+ # Otherwise, before trimming any leading chars, be *sure*
+ # that they are white space. We should maybe warn if this
+ # isn't the case.
+ #
+ for i in range(0, self.entry.leading_space):
+ if cont[i] != " ":
+ self.entry.leading_space = i
+ break
+ #
+ # Add the trimmed result to the section and we're done.
+ #
+ self.entry.add_text(cont[self.entry.leading_space:])
+ else:
+ # Unknown line, ignore
+ self.emit_msg(ln, f"bad line: {line}")
- # Unknown line, ignore
- self.emit_msg(ln, f"bad line: {line}")
+ def process_body(self, ln, line):
+ """
+ STATE_BODY: the bulk of a kerneldoc comment.
+ """
+ if self.is_new_section(ln, line) or self.is_comment_end(ln, line):
+ return
- def process_inline(self, ln, line):
- """STATE_INLINE: docbook comments within a prototype."""
+ if doc_content.search(line):
+ cont = doc_content.group(1)
+ self.entry.add_text(cont)
+ else:
+ # Unknown line, ignore
+ self.emit_msg(ln, f"bad line: {line}")
- if self.inline_doc_state == state.INLINE_NAME and \
- doc_inline_sect.search(line):
- self.entry.section = doc_inline_sect.group(1)
- self.entry.new_start_line = ln
+ def process_inline_name(self, ln, line):
+ """STATE_INLINE_NAME: beginning of docbook comments within a prototype."""
- self.entry.contents = doc_inline_sect.group(2).lstrip()
- if self.entry.contents != "":
- self.entry.contents += "\n"
+ if doc_inline_sect.search(line):
+ self.entry.begin_section(ln, doc_inline_sect.group(1))
+ self.entry.add_text(doc_inline_sect.group(2).lstrip())
+ self.state = state.INLINE_TEXT
+ elif doc_inline_end.search(line):
+ self.dump_section()
+ self.state = state.PROTO
+ elif doc_content.search(line):
+ self.emit_msg(ln, f"Incorrect use of kernel-doc format: {line}")
+ self.state = state.PROTO
+ # else ... ??
- self.inline_doc_state = state.INLINE_TEXT
- # Documentation block end */
- return
+ def process_inline_text(self, ln, line):
+ """STATE_INLINE_TEXT: docbook comments within a prototype."""
if doc_inline_end.search(line):
- if self.entry.contents not in ["", "\n"]:
- self.dump_section()
-
+ self.dump_section()
self.state = state.PROTO
- self.inline_doc_state = state.INLINE_NA
- return
-
- if doc_content.search(line):
- if self.inline_doc_state == state.INLINE_TEXT:
- self.entry.contents += doc_content.group(1) + "\n"
- if not self.entry.contents.strip(" ").rstrip("\n"):
- self.entry.contents = ""
-
- elif self.inline_doc_state == state.INLINE_NAME:
- self.emit_msg(ln,
- f"Incorrect use of kernel-doc format: {line}")
-
- self.inline_doc_state = state.INLINE_ERROR
+ elif doc_content.search(line):
+ self.entry.add_text(doc_content.group(1))
+ # else ... ??
def syscall_munge(self, ln, proto): # pylint: disable=W0613
"""
@@ -1531,105 +1473,94 @@ class KernelDoc:
"""Ancillary routine to process a function prototype"""
# strip C99-style comments to end of line
- r = KernRe(r"\/\/.*$", re.S)
- line = r.sub('', line)
-
+ line = KernRe(r"\/\/.*$", re.S).sub('', line)
+ #
+ # Soak up the line's worth of prototype text, stopping at { or ; if present.
+ #
if KernRe(r'\s*#\s*define').match(line):
self.entry.prototype = line
- elif line.startswith('#'):
- # Strip other macros like #ifdef/#ifndef/#endif/...
- pass
- else:
+ elif not line.startswith('#'): # skip other preprocessor stuff
r = KernRe(r'([^\{]*)')
if r.match(line):
self.entry.prototype += r.group(1) + " "
-
+ #
+ # If we now have the whole prototype, clean it up and declare victory.
+ #
if '{' in line or ';' in line or KernRe(r'\s*#\s*define').match(line):
- # strip comments
- r = KernRe(r'/\*.*?\*/')
- self.entry.prototype = r.sub('', self.entry.prototype)
-
- # strip newlines/cr's
- r = KernRe(r'[\r\n]+')
- self.entry.prototype = r.sub(' ', self.entry.prototype)
-
- # strip leading spaces
- r = KernRe(r'^\s+')
- self.entry.prototype = r.sub('', self.entry.prototype)
-
+ # strip comments and surrounding spaces
+ self.entry.prototype = KernRe(r'/\*.*\*/').sub('', self.entry.prototype).strip()
+ #
# Handle self.entry.prototypes for function pointers like:
# int (*pcs_config)(struct foo)
-
+ # by turning it into
+ # int pcs_config(struct foo)
+ #
r = KernRe(r'^(\S+\s+)\(\s*\*(\S+)\)')
self.entry.prototype = r.sub(r'\1\2', self.entry.prototype)
-
+ #
+ # Handle special declaration syntaxes
+ #
if 'SYSCALL_DEFINE' in self.entry.prototype:
self.entry.prototype = self.syscall_munge(ln,
self.entry.prototype)
-
- r = KernRe(r'TRACE_EVENT|DEFINE_EVENT|DEFINE_SINGLE_EVENT')
- if r.search(self.entry.prototype):
- self.entry.prototype = self.tracepoint_munge(ln,
- self.entry.prototype)
-
+ else:
+ r = KernRe(r'TRACE_EVENT|DEFINE_EVENT|DEFINE_SINGLE_EVENT')
+ if r.search(self.entry.prototype):
+ self.entry.prototype = self.tracepoint_munge(ln,
+ self.entry.prototype)
+ #
+ # ... and we're done
+ #
self.dump_function(ln, self.entry.prototype)
self.reset_state(ln)
def process_proto_type(self, ln, line):
"""Ancillary routine to process a type"""
- # Strip newlines/cr's.
- line = KernRe(r'[\r\n]+', re.S).sub(' ', line)
-
- # Strip leading spaces
- line = KernRe(r'^\s+', re.S).sub('', line)
-
- # Strip trailing spaces
- line = KernRe(r'\s+$', re.S).sub('', line)
-
- # Strip C99-style comments to the end of the line
- line = KernRe(r"\/\/.*$", re.S).sub('', line)
+ # Strip C99-style comments and surrounding whitespace
+ line = KernRe(r"//.*$", re.S).sub('', line).strip()
+ if not line:
+ return # nothing to see here
# To distinguish preprocessor directive from regular declaration later.
if line.startswith('#'):
line += ";"
-
- r = KernRe(r'([^\{\};]*)([\{\};])(.*)')
- while True:
- if r.search(line):
- if self.entry.prototype:
- self.entry.prototype += " "
- self.entry.prototype += r.group(1) + r.group(2)
-
- self.entry.brcount += r.group(2).count('{')
- self.entry.brcount -= r.group(2).count('}')
-
- self.entry.brcount = max(self.entry.brcount, 0)
-
- if r.group(2) == ';' and self.entry.brcount == 0:
+ #
+ # Split the declaration on any of { } or ;, and accumulate pieces
+ # until we hit a semicolon while not inside {brackets}
+ #
+ r = KernRe(r'(.*?)([{};])')
+ for chunk in r.split(line):
+ if chunk: # Ignore empty matches
+ self.entry.prototype += chunk
+ #
+ # This cries out for a match statement ... someday after we can
+ # drop Python 3.9 ...
+ #
+ if chunk == '{':
+ self.entry.brcount += 1
+ elif chunk == '}':
+ self.entry.brcount -= 1
+ elif chunk == ';' and self.entry.brcount <= 0:
self.dump_declaration(ln, self.entry.prototype)
self.reset_state(ln)
- break
-
- line = r.group(3)
- else:
- self.entry.prototype += line
- break
+ return
+ #
+ # We hit the end of the line while still in the declaration; put
+ # in a space to represent the newline.
+ #
+ self.entry.prototype += ' '
def process_proto(self, ln, line):
"""STATE_PROTO: reading a function/whatever prototype."""
if doc_inline_oneline.search(line):
- self.entry.section = doc_inline_oneline.group(1)
- self.entry.contents = doc_inline_oneline.group(2)
-
- if self.entry.contents != "":
- self.entry.contents += "\n"
- self.dump_section(start_new=False)
+ self.entry.begin_section(ln, doc_inline_oneline.group(1))
+ self.entry.add_text(doc_inline_oneline.group(2))
+ self.dump_section()
elif doc_inline_start.search(line):
- self.state = state.INLINE
- self.inline_doc_state = state.INLINE_NAME
+ self.state = state.INLINE_NAME
elif self.entry.decl_type == 'function':
self.process_proto_function(ln, line)
@@ -1642,14 +1573,11 @@ class KernelDoc:
if doc_end.search(line):
self.dump_section()
- self.output_declaration("doc", self.entry.identifier,
- sectionlist=self.entry.sectionlist,
- sections=self.entry.sections,
- section_start_lines=self.entry.section_start_lines)
+ self.output_declaration("doc", self.entry.identifier)
self.reset_state(ln)
elif doc_content.search(line):
- self.entry.contents += doc_content.group(1) + "\n"
+ self.entry.add_text(doc_content.group(1))
def parse_export(self):
"""
@@ -1670,6 +1598,22 @@ class KernelDoc:
return export_table
+ #
+ # The state/action table telling us which function to invoke in
+ # each state.
+ #
+ state_actions = {
+ state.NORMAL: process_normal,
+ state.NAME: process_name,
+ state.BODY: process_body,
+ state.DECLARATION: process_decl,
+ state.SPECIAL_SECTION: process_special,
+ state.INLINE_NAME: process_inline_name,
+ state.INLINE_TEXT: process_inline_text,
+ state.PROTO: process_proto,
+ state.DOCBLOCK: process_docblock,
+ }
+
def parse_kdoc(self):
"""
Open and process each line of a C source file.
@@ -1680,7 +1624,6 @@ class KernelDoc:
Besides parsing kernel-doc tags, it also parses export symbols.
"""
- cont = False
prev = ""
prev_ln = None
export_table = set()
@@ -1696,23 +1639,18 @@ class KernelDoc:
if self.state == state.PROTO:
if line.endswith("\\"):
prev += line.rstrip("\\")
- cont = True
-
if not prev_ln:
prev_ln = ln
-
continue
- if cont:
+ if prev:
ln = prev_ln
line = prev + line
prev = ""
- cont = False
prev_ln = None
- self.config.log.debug("%d %s%s: %s",
+ self.config.log.debug("%d %s: %s",
ln, state.name[self.state],
- state.inline_name[self.inline_doc_state],
line)
# This is an optimization over the original script.
@@ -1720,25 +1658,11 @@ class KernelDoc:
# it was read twice. Here, we use the already-existing
# loop to parse exported symbols as well.
#
- # TODO: It should be noticed that not all states are
- # needed here. On a future cleanup, process export only
- # at the states that aren't handling comment markups.
- self.process_export(export_table, line)
+ if (self.state != state.NORMAL) or \
+ not self.process_export(export_table, line):
+ # Hand this line to the appropriate state handler
+ self.state_actions[self.state](self, ln, line)
- # Hand this line to the appropriate state handler
- if self.state == state.NORMAL:
- self.process_normal(ln, line)
- elif self.state == state.NAME:
- self.process_name(ln, line)
- elif self.state in [state.BODY, state.BODY_MAYBE,
- state.BODY_WITH_BLANK_LINE]:
- self.process_body(ln, line)
- elif self.state == state.INLINE: # scanning for inline parameters
- self.process_inline(ln, line)
- elif self.state == state.PROTO:
- self.process_proto(ln, line)
- elif self.state == state.DOCBLOCK:
- self.process_docblock(ln, line)
except OSError:
self.config.log.error(f"Error: Cannot open file {self.fname}")
diff --git a/scripts/lib/kdoc/kdoc_re.py b/scripts/lib/kdoc/kdoc_re.py
index e81695b273bf..612223e1e723 100644
--- a/scripts/lib/kdoc/kdoc_re.py
+++ b/scripts/lib/kdoc/kdoc_re.py
@@ -29,12 +29,9 @@ class KernRe:
"""
Adds a new regex or re-use it from the cache.
"""
-
- if string in re_cache:
- self.regex = re_cache[string]
- else:
+ self.regex = re_cache.get(string, None)
+ if not self.regex:
self.regex = re.compile(string, flags=flags)
-
if self.cache:
re_cache[string] = self.regex
diff --git a/scripts/min-tool-version.sh b/scripts/min-tool-version.sh
index 787868183b84..0d223b4a9445 100755
--- a/scripts/min-tool-version.sh
+++ b/scripts/min-tool-version.sh
@@ -14,15 +14,13 @@ fi
case "$1" in
binutils)
- echo 2.25.0
+ echo 2.30.0
;;
gcc)
if [ "$ARCH" = parisc64 ]; then
echo 12.0.0
- elif [ "$SRCARCH" = x86 ]; then
- echo 8.1.0
else
- echo 5.1.0
+ echo 8.1.0
fi
;;
llvm)
diff --git a/scripts/misc-check b/scripts/misc-check
index d40d5484e0c5..84f08da17b2c 100755
--- a/scripts/misc-check
+++ b/scripts/misc-check
@@ -3,17 +3,74 @@
set -e
-# Detect files that are tracked but ignored by git. This is checked only when
-# ${KBUILD_EXTRA_WARN} contains 1, git is installed, and the source tree is
-# tracked by git.
+# Detect files that are tracked but ignored by git.
check_tracked_ignored_files () {
- case "${KBUILD_EXTRA_WARN}" in
- *1*) ;;
- *) return;;
- esac
-
- git -C ${srctree:-.} ls-files -i -c --exclude-per-directory=.gitignore 2>/dev/null |
+ git -C "${srctree:-.}" ls-files -i -c --exclude-per-directory=.gitignore 2>/dev/null |
sed 's/$/: warning: ignored by one of the .gitignore files/' >&2
}
-check_tracked_ignored_files
+# Check for missing #include <linux/export.h>
+#
+# The rule for including <linux/export.h> is very simple:
+# Include <linux/export.h> only when you use EXPORT_SYMBOL(). That's it.
+#
+# However, some headers include <linux/export.h> even though they are completely
+# unrelated to EXPORT_SYMBOL().
+#
+# One example is include/linux/module.h. Please note <linux/module.h> and
+# <linux/export.h> are orthogonal. <linux/module.h> should be included by files
+# that can be compiled as modules. In other words, <linux/module.h> should be
+# included by EXPORT_SYMBOL consumers. In contrast, <linux/export.h> should be
+# included from EXPORT_SYMBOL providers, which may or may not be modular.
+# Hence, include/linux/module.h should *not* include <linux/export.h>.
+#
+# Another example is include/linux/linkage.h, which is completely unrelated to
+# EXPORT_SYMBOL(). Worse, it is included by most C files, which means, most C
+# files end up including <linux/export.h>, even though only some of them
+# actually export symbols. Hence, include/linux/linkage.h should *not* include
+# <linux/export.h>.
+#
+# Before fixing such headers, we must ensure that C files using EXPORT_SYMBOL()
+# include <linux/export.h> directly, since many C files currently rely on
+# <linux/export.h> being included indirectly (likely, via <linux/linkage> etc.).
+#
+# Therefore, this check.
+#
+# The problem is simple - the warned files use EXPORT_SYMBOL(), but do not
+# include <linux/export.h>. Please add #include <linux/export.h> to them.
+#
+# If the included headers are sorted alphabetically, please insert
+# <linux/export.h> in the appropriate position to maintain the sort order.
+# For this reason, this script only checks missing <linux/export.h>, but
+# does not automatically fix it.
+check_missing_include_linux_export_h () {
+
+ git -C "${srctree:-.}" grep --files-with-matches -E 'EXPORT_SYMBOL((_NS)?(_GPL)?|_GPL_FOR_MODULES)\(.*\)' \
+ -- '*.[ch]' :^tools/ :^include/linux/export.h |
+ xargs -r git -C "${srctree:-.}" grep --files-without-match '#include[[:space:]]*<linux/export\.h>' |
+ xargs -r printf "%s: warning: EXPORT_SYMBOL() is used, but #include <linux/export.h> is missing\n" >&2
+}
+
+# If you do not use EXPORT_SYMBOL(), please do not include <linux/export.h>.
+# Currently, this is checked for *.c files, but not for *.h files, because some
+# *.c files rely on <linux/export.h> being included indirectly.
+check_unnecessary_include_linux_export_h () {
+
+ git -C "${srctree:-.}" grep --files-with-matches '#include[[:space:]]*<linux/export\.h>' \
+ -- '*.[c]' :^tools/ |
+ xargs -r git -C "${srctree:-.}" grep --files-without-match -E 'EXPORT_SYMBOL((_NS)?(_GPL)?|_GPL_FOR_MODULES)\(.*\)' |
+ xargs -r printf "%s: warning: EXPORT_SYMBOL() is not used, but #include <linux/export.h> is present\n" >&2
+}
+
+case "${KBUILD_EXTRA_WARN}" in
+*1*)
+ check_tracked_ignored_files
+ ;;
+esac
+
+case "${KBUILD_EXTRA_WARN}" in
+*2*)
+ check_missing_include_linux_export_h
+ check_unnecessary_include_linux_export_h
+ ;;
+esac
diff --git a/scripts/mod/modpost.c b/scripts/mod/modpost.c
index be89921d60b6..5ca7c268294e 100644
--- a/scripts/mod/modpost.c
+++ b/scripts/mod/modpost.c
@@ -28,6 +28,8 @@
#include "modpost.h"
#include "../../include/linux/license.h"
+#define MODULE_NS_PREFIX "module:"
+
static bool module_enabled;
/* Are we using CONFIG_MODVERSIONS? */
static bool modversions;
@@ -1595,11 +1597,14 @@ static void read_symbols(const char *modname)
license = get_next_modinfo(&info, "license", license);
}
- namespace = get_modinfo(&info, "import_ns");
- while (namespace) {
+ for (namespace = get_modinfo(&info, "import_ns");
+ namespace;
+ namespace = get_next_modinfo(&info, "import_ns", namespace)) {
+ if (strstarts(namespace, MODULE_NS_PREFIX))
+ error("%s: explicitly importing namespace \"%s\" is not allowed.\n",
+ mod->name, namespace);
+
add_namespace(&mod->imported_namespaces, namespace);
- namespace = get_next_modinfo(&info, "import_ns",
- namespace);
}
if (!get_modinfo(&info, "description"))
@@ -1684,6 +1689,46 @@ void buf_write(struct buffer *buf, const char *s, int len)
buf->pos += len;
}
+/**
+ * verify_module_namespace() - does @modname have access to this symbol's @namespace
+ * @namespace: export symbol namespace
+ * @modname: module name
+ *
+ * If @namespace is prefixed with "module:" to indicate it is a module namespace
+ * then test if @modname matches any of the comma separated patterns.
+ *
+ * The patterns only support tail-glob.
+ */
+static bool verify_module_namespace(const char *namespace, const char *modname)
+{
+ size_t len, modlen = strlen(modname);
+ const char *prefix = "module:";
+ const char *sep;
+ bool glob;
+
+ if (!strstarts(namespace, prefix))
+ return false;
+
+ for (namespace += strlen(prefix); *namespace; namespace = sep) {
+ sep = strchrnul(namespace, ',');
+ len = sep - namespace;
+
+ glob = false;
+ if (sep[-1] == '*') {
+ len--;
+ glob = true;
+ }
+
+ if (*sep)
+ sep++;
+
+ if (strncmp(namespace, modname, len) == 0 && (glob || len == modlen))
+ return true;
+ }
+
+ return false;
+}
+
static void check_exports(struct module *mod)
{
struct symbol *s, *exp;
@@ -1711,7 +1756,8 @@ static void check_exports(struct module *mod)
basename = get_basename(mod->name);
- if (!contains_namespace(&mod->imported_namespaces, exp->namespace)) {
+ if (!verify_module_namespace(exp->namespace, basename) &&
+ !contains_namespace(&mod->imported_namespaces, exp->namespace)) {
modpost_log(!allow_missing_ns_imports,
"module %s uses symbol %s from namespace %s, but does not import it.\n",
basename, exp->name, exp->namespace);
diff --git a/scripts/module.lds.S b/scripts/module.lds.S
index 450f1088d5fd..ee79c41059f3 100644
--- a/scripts/module.lds.S
+++ b/scripts/module.lds.S
@@ -52,17 +52,12 @@ SECTIONS {
.data : {
*(.data .data.[0-9a-zA-Z_]*)
*(.data..L*)
- MOD_CODETAG_SECTIONS()
}
.rodata : {
*(.rodata .rodata.[0-9a-zA-Z_]*)
*(.rodata..L*)
}
-#else
- .data : {
- MOD_CODETAG_SECTIONS()
- }
#endif
MOD_SEPARATE_CODETAG_SECTIONS()
}
diff --git a/scripts/recordmcount.pl b/scripts/recordmcount.pl
index 0871b2e92584..861b56dda64e 100755
--- a/scripts/recordmcount.pl
+++ b/scripts/recordmcount.pl
@@ -359,7 +359,7 @@ if ($arch eq "x86_64") {
$mcount_regex = "^\\s*([0-9a-fA-F]+):\\s*R_CKCORE_PCREL_JSR_IMM26BY2\\s+_mcount\$";
$alignment = 2;
} else {
- die "Arch $arch is not supported with CONFIG_FTRACE_MCOUNT_RECORD";
+ die "Arch $arch is not supported with CONFIG_DYNAMIC_FTRACE";
}
my $text_found = 0;
diff --git a/scripts/rustdoc_test_builder.rs b/scripts/rustdoc_test_builder.rs
index e5894652f12c..f7540bcf595a 100644
--- a/scripts/rustdoc_test_builder.rs
+++ b/scripts/rustdoc_test_builder.rs
@@ -28,7 +28,7 @@ fn main() {
//
// ```
// fn main() { #[allow(non_snake_case)] fn _doctest_main_rust_kernel_file_rs_28_0() {
- // fn main() { #[allow(non_snake_case)] fn _doctest_main_rust_kernel_file_rs_37_0() -> Result<(), impl core::fmt::Debug> {
+ // fn main() { #[allow(non_snake_case)] fn _doctest_main_rust_kernel_file_rs_37_0() -> Result<(), impl ::core::fmt::Debug> {
// ```
//
// It should be unlikely that doctest code matches such lines (when code is formatted properly).
@@ -49,8 +49,10 @@ fn main() {
// Qualify `Result` to avoid the collision with our own `Result` coming from the prelude.
let body = body.replace(
- &format!("{rustdoc_function_name}() -> Result<(), impl core::fmt::Debug> {{"),
- &format!("{rustdoc_function_name}() -> core::result::Result<(), impl core::fmt::Debug> {{"),
+ &format!("{rustdoc_function_name}() -> Result<(), impl ::core::fmt::Debug> {{"),
+ &format!(
+ "{rustdoc_function_name}() -> ::core::result::Result<(), impl ::core::fmt::Debug> {{"
+ ),
);
// For tests that get generated with `Result`, like above, `rustdoc` generates an `unwrap()` on
diff --git a/scripts/rustdoc_test_gen.rs b/scripts/rustdoc_test_gen.rs
index ec8d70ac888b..abb34ada2508 100644
--- a/scripts/rustdoc_test_gen.rs
+++ b/scripts/rustdoc_test_gen.rs
@@ -85,24 +85,25 @@ fn find_real_path<'a>(srctree: &Path, valid_paths: &'a mut Vec<PathBuf>, file: &
}
}
- assert!(
- valid_paths.len() > 0,
- "No path candidates found for `{file}`. This is likely a bug in the build system, or some \
- files went away while compiling."
- );
-
- if valid_paths.len() > 1 {
- eprintln!("Several path candidates found:");
- for path in valid_paths {
- eprintln!(" {path:?}");
+ match valid_paths.as_slice() {
+ [] => panic!(
+ "No path candidates found for `{file}`. This is likely a bug in the build system, or \
+ some files went away while compiling."
+ ),
+ [valid_path] => valid_path.to_str().unwrap(),
+ valid_paths => {
+ use std::fmt::Write;
+
+ let mut candidates = String::new();
+ for path in valid_paths {
+ writeln!(&mut candidates, " {path:?}").unwrap();
+ }
+ panic!(
+ "Several path candidates found for `{file}`, please resolve the ambiguity by \
+ renaming a file or folder. Candidates:\n{candidates}",
+ );
}
- panic!(
- "Several path candidates found for `{file}`, please resolve the ambiguity by renaming \
- a file or folder."
- );
}
-
- valid_paths[0].to_str().unwrap()
}
fn main() {
@@ -167,12 +168,14 @@ fn main() {
rust_tests,
r#"/// Generated `{name}` KUnit test case from a Rust documentation test.
#[no_mangle]
-pub extern "C" fn {kunit_name}(__kunit_test: *mut kernel::bindings::kunit) {{
+pub extern "C" fn {kunit_name}(__kunit_test: *mut ::kernel::bindings::kunit) {{
/// Overrides the usual [`assert!`] macro with one that calls KUnit instead.
#[allow(unused)]
macro_rules! assert {{
($cond:expr $(,)?) => {{{{
- kernel::kunit_assert!("{kunit_name}", "{real_path}", __DOCTEST_ANCHOR - {line}, $cond);
+ ::kernel::kunit_assert!(
+ "{kunit_name}", "{real_path}", __DOCTEST_ANCHOR - {line}, $cond
+ );
}}}}
}}
@@ -180,13 +183,15 @@ pub extern "C" fn {kunit_name}(__kunit_test: *mut kernel::bindings::kunit) {{
#[allow(unused)]
macro_rules! assert_eq {{
($left:expr, $right:expr $(,)?) => {{{{
- kernel::kunit_assert_eq!("{kunit_name}", "{real_path}", __DOCTEST_ANCHOR - {line}, $left, $right);
+ ::kernel::kunit_assert_eq!(
+ "{kunit_name}", "{real_path}", __DOCTEST_ANCHOR - {line}, $left, $right
+ );
}}}}
}}
// Many tests need the prelude, so provide it by default.
#[allow(unused)]
- use kernel::prelude::*;
+ use ::kernel::prelude::*;
// Unconditionally print the location of the original doctest (i.e. rather than the location in
// the generated file) so that developers can easily map the test back to the source code.
@@ -197,11 +202,11 @@ pub extern "C" fn {kunit_name}(__kunit_test: *mut kernel::bindings::kunit) {{
// This follows the syntax for declaring test metadata in the proposed KTAP v2 spec, which may
// be used for the proposed KUnit test attributes API. Thus hopefully this will make migration
// easier later on.
- kernel::kunit::info(format_args!(" # {kunit_name}.location: {real_path}:{line}\n"));
+ ::kernel::kunit::info(format_args!(" # {kunit_name}.location: {real_path}:{line}\n"));
/// The anchor where the test code body starts.
#[allow(unused)]
- static __DOCTEST_ANCHOR: i32 = core::line!() as i32 + {body_offset} + 1;
+ static __DOCTEST_ANCHOR: i32 = ::core::line!() as i32 + {body_offset} + 1;
{{
{body}
main();
diff --git a/scripts/spelling.txt b/scripts/spelling.txt
index a290db720b0f..1e89b92c2f9a 100644
--- a/scripts/spelling.txt
+++ b/scripts/spelling.txt
@@ -1099,6 +1099,7 @@ notication||notification
notications||notifications
notifcations||notifications
notifed||notified
+notifer||notifier
notity||notify
notfify||notify
nubmer||number
@@ -1240,6 +1241,8 @@ prefered||preferred
prefferably||preferably
prefitler||prefilter
preform||perform
+previleged||privileged
+previlege||privilege
premption||preemption
prepaired||prepared
prepate||prepare
diff --git a/scripts/sphinx-pre-install b/scripts/sphinx-pre-install
index ad9945ccb0cf..3f8d6925e896 100755
--- a/scripts/sphinx-pre-install
+++ b/scripts/sphinx-pre-install
@@ -245,6 +245,10 @@ sub check_missing_tex($)
sub get_sphinx_fname()
{
+ if ($ENV{'SPHINXBUILD'}) {
+ return $ENV{'SPHINXBUILD'};
+ }
+
my $fname = "sphinx-build";
return $fname if findprog($fname);
@@ -409,7 +413,7 @@ sub give_redhat_hints()
my $old = 0;
my $rel;
my $noto_sans_redhat = "google-noto-sans-cjk-ttc-fonts";
- $rel = $1 if ($system_release =~ /release\s+(\d+)/);
+ $rel = $1 if ($system_release =~ /(release|Linux)\s+(\d+)/);
if (!($system_release =~ /Fedora/)) {
$map{"virtualenv"} = "python-virtualenv";
diff --git a/scripts/syscall.tbl b/scripts/syscall.tbl
index 580b4e246aec..d1ae5e92c615 100644
--- a/scripts/syscall.tbl
+++ b/scripts/syscall.tbl
@@ -408,3 +408,5 @@
465 common listxattrat sys_listxattrat
466 common removexattrat sys_removexattrat
467 common open_tree_attr sys_open_tree_attr
+468 common file_getattr sys_file_getattr
+469 common file_setattr sys_file_setattr
diff --git a/scripts/tags.sh b/scripts/tags.sh
index 98680e9cd7be..99ce427d9a69 100755
--- a/scripts/tags.sh
+++ b/scripts/tags.sh
@@ -344,7 +344,7 @@ case "$1" in
"tags")
rm -f tags
- xtags ctags
+ xtags ${CTAGS:-ctags}
remove_structs=y
;;
diff --git a/scripts/test_doc_build.py b/scripts/test_doc_build.py
new file mode 100755
index 000000000000..47b4606569f9
--- /dev/null
+++ b/scripts/test_doc_build.py
@@ -0,0 +1,513 @@
+#!/usr/bin/env python3
+# SPDX-License-Identifier: GPL-2.0
+# Copyright(c) 2025: Mauro Carvalho Chehab <mchehab+huawei@kernel.org>
+#
+# pylint: disable=R0903,R0912,R0913,R0914,R0917,C0301
+
+"""
+Install minimal supported requirements for different Sphinx versions
+and optionally test the build.
+"""
+
+import argparse
+import asyncio
+import os.path
+import shutil
+import sys
+import time
+import subprocess
+
+# Minimal python version supported by the building system.
+
+PYTHON = os.path.basename(sys.executable)
+
+min_python_bin = None
+
+for i in range(9, 13):
+ p = f"python3.{i}"
+ if shutil.which(p):
+ min_python_bin = p
+ break
+
+if not min_python_bin:
+ min_python_bin = PYTHON
+
+# Starting from 8.0, Python 3.9 is not supported anymore.
+PYTHON_VER_CHANGES = {(8, 0, 0): PYTHON}
+
+DEFAULT_VERSIONS_TO_TEST = [
+ (3, 4, 3), # Minimal supported version
+ (5, 3, 0), # CentOS Stream 9 / AlmaLinux 9
+ (6, 1, 1), # Debian 12
+ (7, 2, 1), # openSUSE Leap 15.6
+ (7, 2, 6), # Ubuntu 24.04 LTS
+ (7, 4, 7), # Ubuntu 24.10
+ (7, 3, 0), # openSUSE Tumbleweed
+ (8, 1, 3), # Fedora 42
+ (8, 2, 3) # Latest version - covers rolling distros
+]
+
+# Sphinx versions to be installed and their incremental requirements
+SPHINX_REQUIREMENTS = {
+ # Oldest versions we support for each package required by Sphinx 3.4.3
+ (3, 4, 3): {
+ "docutils": "0.16",
+ "alabaster": "0.7.12",
+ "babel": "2.8.0",
+ "certifi": "2020.6.20",
+ "docutils": "0.16",
+ "idna": "2.10",
+ "imagesize": "1.2.0",
+ "Jinja2": "2.11.2",
+ "MarkupSafe": "1.1.1",
+ "packaging": "20.4",
+ "Pygments": "2.6.1",
+ "PyYAML": "5.1",
+ "requests": "2.24.0",
+ "snowballstemmer": "2.0.0",
+ "sphinxcontrib-applehelp": "1.0.2",
+ "sphinxcontrib-devhelp": "1.0.2",
+ "sphinxcontrib-htmlhelp": "1.0.3",
+ "sphinxcontrib-jsmath": "1.0.1",
+ "sphinxcontrib-qthelp": "1.0.3",
+ "sphinxcontrib-serializinghtml": "1.1.4",
+ "urllib3": "1.25.9",
+ },
+
+ # Update package dependencies to a more modern base. The goal here
+ # is to avoid to many incremental changes for the next entries
+ (3, 5, 0): {
+ "alabaster": "0.7.13",
+ "babel": "2.17.0",
+ "certifi": "2025.6.15",
+ "idna": "3.10",
+ "imagesize": "1.4.1",
+ "packaging": "25.0",
+ "Pygments": "2.8.1",
+ "requests": "2.32.4",
+ "snowballstemmer": "3.0.1",
+ "sphinxcontrib-applehelp": "1.0.4",
+ "sphinxcontrib-htmlhelp": "2.0.1",
+ "sphinxcontrib-serializinghtml": "1.1.5",
+ "urllib3": "2.0.0",
+ },
+
+ # Starting from here, ensure all docutils versions are covered with
+ # supported Sphinx versions. Other packages are upgraded only when
+ # required by pip
+ (4, 0, 0): {
+ "PyYAML": "5.1",
+ },
+ (4, 1, 0): {
+ "docutils": "0.17",
+ "Pygments": "2.19.1",
+ "Jinja2": "3.0.3",
+ "MarkupSafe": "2.0",
+ },
+ (4, 3, 0): {},
+ (4, 4, 0): {},
+ (4, 5, 0): {
+ "docutils": "0.17.1",
+ },
+ (5, 0, 0): {},
+ (5, 1, 0): {},
+ (5, 2, 0): {
+ "docutils": "0.18",
+ "Jinja2": "3.1.2",
+ "MarkupSafe": "2.0",
+ "PyYAML": "5.3.1",
+ },
+ (5, 3, 0): {
+ "docutils": "0.18.1",
+ },
+ (6, 0, 0): {},
+ (6, 1, 0): {},
+ (6, 2, 0): {
+ "PyYAML": "5.4.1",
+ },
+ (7, 0, 0): {},
+ (7, 1, 0): {},
+ (7, 2, 0): {
+ "docutils": "0.19",
+ "PyYAML": "6.0.1",
+ "sphinxcontrib-serializinghtml": "1.1.9",
+ },
+ (7, 2, 6): {
+ "docutils": "0.20",
+ },
+ (7, 3, 0): {
+ "alabaster": "0.7.14",
+ "PyYAML": "6.0.1",
+ "tomli": "2.0.1",
+ },
+ (7, 4, 0): {
+ "docutils": "0.20.1",
+ "PyYAML": "6.0.1",
+ },
+ (8, 0, 0): {
+ "docutils": "0.21",
+ },
+ (8, 1, 0): {
+ "docutils": "0.21.1",
+ "PyYAML": "6.0.1",
+ "sphinxcontrib-applehelp": "1.0.7",
+ "sphinxcontrib-devhelp": "1.0.6",
+ "sphinxcontrib-htmlhelp": "2.0.6",
+ "sphinxcontrib-qthelp": "1.0.6",
+ },
+ (8, 2, 0): {
+ "docutils": "0.21.2",
+ "PyYAML": "6.0.1",
+ "sphinxcontrib-serializinghtml": "1.1.9",
+ },
+}
+
+
+class AsyncCommands:
+ """Excecute command synchronously"""
+
+ def __init__(self, fp=None):
+
+ self.stdout = None
+ self.stderr = None
+ self.output = None
+ self.fp = fp
+
+ def log(self, out, verbose, is_info=True):
+ out = out.removesuffix('\n')
+
+ if verbose:
+ if is_info:
+ print(out)
+ else:
+ print(out, file=sys.stderr)
+
+ if self.fp:
+ self.fp.write(out + "\n")
+
+ async def _read(self, stream, verbose, is_info):
+ """Ancillary routine to capture while displaying"""
+
+ while stream is not None:
+ line = await stream.readline()
+ if line:
+ out = line.decode("utf-8", errors="backslashreplace")
+ self.log(out, verbose, is_info)
+ if is_info:
+ self.stdout += out
+ else:
+ self.stderr += out
+ else:
+ break
+
+ async def run(self, cmd, capture_output=False, check=False,
+ env=None, verbose=True):
+
+ """
+ Execute an arbitrary command, handling errors.
+
+ Please notice that this class is not thread safe
+ """
+
+ self.stdout = ""
+ self.stderr = ""
+
+ self.log("$ " + " ".join(cmd), verbose)
+
+ proc = await asyncio.create_subprocess_exec(cmd[0],
+ *cmd[1:],
+ env=env,
+ stdout=asyncio.subprocess.PIPE,
+ stderr=asyncio.subprocess.PIPE)
+
+ # Handle input and output in realtime
+ await asyncio.gather(
+ self._read(proc.stdout, verbose, True),
+ self._read(proc.stderr, verbose, False),
+ )
+
+ await proc.wait()
+
+ if check and proc.returncode > 0:
+ raise subprocess.CalledProcessError(returncode=proc.returncode,
+ cmd=" ".join(cmd),
+ output=self.stdout,
+ stderr=self.stderr)
+
+ if capture_output:
+ if proc.returncode > 0:
+ self.log(f"Error {proc.returncode}", verbose=True, is_info=False)
+ return ""
+
+ return self.output
+
+ ret = subprocess.CompletedProcess(args=cmd,
+ returncode=proc.returncode,
+ stdout=self.stdout,
+ stderr=self.stderr)
+
+ return ret
+
+
+class SphinxVenv:
+ """
+ Installs Sphinx on one virtual env per Sphinx version with a minimal
+ set of dependencies, adjusting them to each specific version.
+ """
+
+ def __init__(self):
+ """Initialize instance variables"""
+
+ self.built_time = {}
+ self.first_run = True
+
+ async def _handle_version(self, args, fp,
+ cur_ver, cur_requirements, python_bin):
+ """Handle a single Sphinx version"""
+
+ cmd = AsyncCommands(fp)
+
+ ver = ".".join(map(str, cur_ver))
+
+ if not self.first_run and args.wait_input and args.build:
+ ret = input("Press Enter to continue or 'a' to abort: ").strip().lower()
+ if ret == "a":
+ print("Aborted.")
+ sys.exit()
+ else:
+ self.first_run = False
+
+ venv_dir = f"Sphinx_{ver}"
+ req_file = f"requirements_{ver}.txt"
+
+ cmd.log(f"\nSphinx {ver} with {python_bin}", verbose=True)
+
+ # Create venv
+ await cmd.run([python_bin, "-m", "venv", venv_dir],
+ verbose=args.verbose, check=True)
+ pip = os.path.join(venv_dir, "bin/pip")
+
+ # Create install list
+ reqs = []
+ for pkg, verstr in cur_requirements.items():
+ reqs.append(f"{pkg}=={verstr}")
+
+ reqs.append(f"Sphinx=={ver}")
+
+ await cmd.run([pip, "install"] + reqs, check=True, verbose=args.verbose)
+
+ # Freeze environment
+ result = await cmd.run([pip, "freeze"], verbose=False, check=True)
+
+ # Pip install succeeded. Write requirements file
+ if args.req_file:
+ with open(req_file, "w", encoding="utf-8") as fp:
+ fp.write(result.stdout)
+
+ if args.build:
+ start_time = time.time()
+
+ # Prepare a venv environment
+ env = os.environ.copy()
+ bin_dir = os.path.join(venv_dir, "bin")
+ env["PATH"] = bin_dir + ":" + env["PATH"]
+ env["VIRTUAL_ENV"] = venv_dir
+ if "PYTHONHOME" in env:
+ del env["PYTHONHOME"]
+
+ # Test doc build
+ await cmd.run(["make", "cleandocs"], env=env, check=True)
+ make = ["make"]
+
+ if args.output:
+ sphinx_build = os.path.realpath(f"{bin_dir}/sphinx-build")
+ make += [f"O={args.output}", f"SPHINXBUILD={sphinx_build}"]
+
+ if args.make_args:
+ make += args.make_args
+
+ make += args.targets
+
+ if args.verbose:
+ cmd.log(f". {bin_dir}/activate", verbose=True)
+ await cmd.run(make, env=env, check=True, verbose=True)
+ if args.verbose:
+ cmd.log("deactivate", verbose=True)
+
+ end_time = time.time()
+ elapsed_time = end_time - start_time
+ hours, minutes = divmod(elapsed_time, 3600)
+ minutes, seconds = divmod(minutes, 60)
+
+ hours = int(hours)
+ minutes = int(minutes)
+ seconds = int(seconds)
+
+ self.built_time[ver] = f"{hours:02d}:{minutes:02d}:{seconds:02d}"
+
+ cmd.log(f"Finished doc build for Sphinx {ver}. Elapsed time: {self.built_time[ver]}", verbose=True)
+
+ async def run(self, args):
+ """
+ Navigate though multiple Sphinx versions, handling each of them
+ on a loop.
+ """
+
+ if args.log:
+ fp = open(args.log, "w", encoding="utf-8")
+ if not args.verbose:
+ args.verbose = False
+ else:
+ fp = None
+ if not args.verbose:
+ args.verbose = True
+
+ cur_requirements = {}
+ python_bin = min_python_bin
+
+ vers = set(SPHINX_REQUIREMENTS.keys()) | set(args.versions)
+
+ for cur_ver in sorted(vers):
+ if cur_ver in SPHINX_REQUIREMENTS:
+ new_reqs = SPHINX_REQUIREMENTS[cur_ver]
+ cur_requirements.update(new_reqs)
+
+ if cur_ver in PYTHON_VER_CHANGES: # pylint: disable=R1715
+ python_bin = PYTHON_VER_CHANGES[cur_ver]
+
+ if cur_ver not in args.versions:
+ continue
+
+ if args.min_version:
+ if cur_ver < args.min_version:
+ continue
+
+ if args.max_version:
+ if cur_ver > args.max_version:
+ break
+
+ await self._handle_version(args, fp, cur_ver, cur_requirements,
+ python_bin)
+
+ if args.build:
+ cmd = AsyncCommands(fp)
+ cmd.log("\nSummary:", verbose=True)
+ for ver, elapsed_time in sorted(self.built_time.items()):
+ cmd.log(f"\tSphinx {ver} elapsed time: {elapsed_time}",
+ verbose=True)
+
+ if fp:
+ fp.close()
+
+def parse_version(ver_str):
+ """Convert a version string into a tuple."""
+
+ return tuple(map(int, ver_str.split(".")))
+
+
+DEFAULT_VERS = " - "
+DEFAULT_VERS += "\n - ".join(map(lambda v: f"{v[0]}.{v[1]}.{v[2]}",
+ DEFAULT_VERSIONS_TO_TEST))
+
+SCRIPT = os.path.relpath(__file__)
+
+DESCRIPTION = f"""
+This tool allows creating Python virtual environments for different
+Sphinx versions that are supported by the Linux Kernel build system.
+
+Besides creating the virtual environment, it can also test building
+the documentation using "make htmldocs" (and/or other doc targets).
+
+If called without "--versions" argument, it covers the versions shipped
+on major distros, plus the lowest supported version:
+
+{DEFAULT_VERS}
+
+A typical usage is to run:
+
+ {SCRIPT} -m -l sphinx_builds.log
+
+This will create one virtual env for the default version set and run
+"make htmldocs" for each version, creating a log file with the
+excecuted commands on it.
+
+NOTE: The build time can be very long, specially on old versions. Also, there
+is a known bug with Sphinx version 6.0.x: each subprocess uses a lot of
+memory. That, together with "-jauto" may cause OOM killer to cause
+failures at the doc generation. To minimize the risk, you may use the
+"-a" command line parameter to constrain the built directories and/or
+reduce the number of threads from "-jauto" to, for instance, "-j4":
+
+ {SCRIPT} -m -V 6.0.1 -a "SPHINXDIRS=process" "SPHINXOPTS='-j4'"
+
+"""
+
+MAKE_TARGETS = [
+ "htmldocs",
+ "texinfodocs",
+ "infodocs",
+ "latexdocs",
+ "pdfdocs",
+ "epubdocs",
+ "xmldocs",
+]
+
+async def main():
+ """Main program"""
+
+ parser = argparse.ArgumentParser(description=DESCRIPTION,
+ formatter_class=argparse.RawDescriptionHelpFormatter)
+
+ ver_group = parser.add_argument_group("Version range options")
+
+ ver_group.add_argument('-V', '--versions', nargs="*",
+ default=DEFAULT_VERSIONS_TO_TEST,type=parse_version,
+ help='Sphinx versions to test')
+ ver_group.add_argument('--min-version', "--min", type=parse_version,
+ help='Sphinx minimal version')
+ ver_group.add_argument('--max-version', "--max", type=parse_version,
+ help='Sphinx maximum version')
+ ver_group.add_argument('-f', '--full', action='store_true',
+ help='Add all Sphinx (major,minor) supported versions to the version range')
+
+ build_group = parser.add_argument_group("Build options")
+
+ build_group.add_argument('-b', '--build', action='store_true',
+ help='Build documentation')
+ build_group.add_argument('-a', '--make-args', nargs="*",
+ help='extra arguments for make, like SPHINXDIRS=netlink/specs',
+ )
+ build_group.add_argument('-t', '--targets', nargs="+", choices=MAKE_TARGETS,
+ default=[MAKE_TARGETS[0]],
+ help="make build targets. Default: htmldocs.")
+ build_group.add_argument("-o", '--output',
+ help="output directory for the make O=OUTPUT")
+
+ other_group = parser.add_argument_group("Other options")
+
+ other_group.add_argument('-r', '--req-file', action='store_true',
+ help='write a requirements.txt file')
+ other_group.add_argument('-l', '--log',
+ help='Log command output on a file')
+ other_group.add_argument('-v', '--verbose', action='store_true',
+ help='Verbose all commands')
+ other_group.add_argument('-i', '--wait-input', action='store_true',
+ help='Wait for an enter before going to the next version')
+
+ args = parser.parse_args()
+
+ if not args.make_args:
+ args.make_args = []
+
+ sphinx_versions = sorted(list(SPHINX_REQUIREMENTS.keys()))
+
+ if args.full:
+ args.versions += list(SPHINX_REQUIREMENTS.keys())
+
+ venv = SphinxVenv()
+ await venv.run(args)
+
+
+# Call main method
+if __name__ == "__main__":
+ asyncio.run(main())
diff --git a/scripts/ver_linux b/scripts/ver_linux
index 1a8ee4ff0e32..d6f2362d3792 100755
--- a/scripts/ver_linux
+++ b/scripts/ver_linux
@@ -25,8 +25,6 @@ BEGIN {
printversion("Module-init-tools", version("depmod -V"))
printversion("E2fsprogs", version("tune2fs"))
printversion("Jfsutils", version("fsck.jfs -V"))
- printversion("Reiserfsprogs", version("reiserfsck -V"))
- printversion("Reiser4fsprogs", version("fsck.reiser4 -V"))
printversion("Xfsprogs", version("xfs_db -V"))
printversion("Pcmciautils", version("pccardctl -V"))
printversion("Pcmcia-cs", version("cardmgr -V"))