l4re-base-25.08.0

This commit is contained in:
2025-09-12 15:55:45 +02:00
commit d959eaab98
37938 changed files with 9382688 additions and 0 deletions

View File

@@ -0,0 +1,20 @@
The Linux Kernel is provided under:
SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note
Being under the terms of the GNU General Public License version 2 only,
according with:
LICENSES/preferred/GPL-2.0
With an explicit syscall exception, as stated at:
LICENSES/exceptions/Linux-syscall-note
In addition, other licenses may also apply. Please see:
Documentation/process/license-rules.rst
for more details.
All contributions to the Linux Kernel are subject to this COPYING file.

2151
src/l4/tool/kconfig/Makefile Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,3 @@
kconfig taken from vanilla Linux 6.16, and slightly patched.

View File

@@ -0,0 +1,272 @@
# SPDX-License-Identifier: GPL-2.0
####
# kbuild: Generic definitions
# Convenient variables
comma := ,
quote := "
squote := '
empty :=
space := $(empty) $(empty)
space_escape := _-_SPACE_-_
pound := \#
define newline
endef
###
# Comparison macros.
# Usage: $(call test-lt, $(CONFIG_LLD_VERSION), 150000)
#
# Use $(intcmp ...) if supported. (Make >= 4.4)
# Otherwise, fall back to the 'test' shell command.
ifeq ($(intcmp 1,0,,,y),y)
test-ge = $(intcmp $(strip $1)0, $(strip $2)0,,y,y)
test-gt = $(intcmp $(strip $1)0, $(strip $2)0,,,y)
else
test-ge = $(shell test $(strip $1)0 -ge $(strip $2)0 && echo y)
test-gt = $(shell test $(strip $1)0 -gt $(strip $2)0 && echo y)
endif
test-le = $(call test-ge, $2, $1)
test-lt = $(call test-gt, $2, $1)
###
# Name of target with a '.' as filename prefix. foo/bar.o => foo/.bar.o
dot-target = $(dir $@).$(notdir $@)
###
# Name of target with a '.tmp_' as filename prefix. foo/bar.o => foo/.tmp_bar.o
tmp-target = $(dir $@).tmp_$(notdir $@)
###
# The temporary file to save gcc -MMD generated dependencies must not
# contain a comma
depfile = $(subst $(comma),_,$(dot-target).d)
###
# filename of target with directory and extension stripped
basetarget = $(basename $(notdir $@))
###
# real prerequisites without phony targets
real-prereqs = $(filter-out $(PHONY), $^)
###
# Escape single quote for use in echo statements
escsq = $(subst $(squote),'\$(squote)',$1)
###
# Quote a string to pass it to C files. foo => '"foo"'
stringify = $(squote)$(quote)$1$(quote)$(squote)
###
# The path to Kbuild or Makefile. Kbuild has precedence over Makefile.
kbuild-file = $(or $(wildcard $(src)/Kbuild),$(src)/Makefile)
###
# Read a file, replacing newlines with spaces
#
# Make 4.2 or later can read a file by using its builtin function.
ifneq ($(filter-out 4.0 4.1, $(MAKE_VERSION)),)
read-file = $(subst $(newline),$(space),$(file < $1))
else
read-file = $(shell cat $1 2>/dev/null)
endif
###
# Easy method for doing a status message
kecho := :
quiet_kecho := echo
silent_kecho := :
kecho := $($(quiet)kecho)
###
# filechk is used to check if the content of a generated file is updated.
# Sample usage:
#
# filechk_sample = echo $(KERNELRELEASE)
# version.h: FORCE
# $(call filechk,sample)
#
# The rule defined shall write to stdout the content of the new file.
# The existing file will be compared with the new one.
# - If no file exist it is created
# - If the content differ the new file is used
# - If they are equal no change, and no timestamp update
define filechk
$(check-FORCE)
$(Q)set -e; \
mkdir -p $(dir $@); \
trap "rm -f $(tmp-target)" EXIT; \
{ $(filechk_$(1)); } > $(tmp-target); \
if [ ! -r $@ ] || ! cmp -s $@ $(tmp-target); then \
$(kecho) ' UPD $@'; \
mv -f $(tmp-target) $@; \
fi
endef
###
# Shorthand for $(Q)$(MAKE) -f scripts/Makefile.build obj=
# Usage:
# $(Q)$(MAKE) $(build)=dir
build := -f $(srctree)/scripts/Makefile.build obj
###
# Shorthand for $(Q)$(MAKE) -f scripts/Makefile.clean obj=
# Usage:
# $(Q)$(MAKE) $(clean)=dir
clean := -f $(srctree)/scripts/Makefile.clean obj
# pring log
#
# If quiet is "silent_", print nothing and sink stdout
# If quiet is "quiet_", print short log
# If quiet is empty, print short log and whole command
silent_log_print = exec >/dev/null;
quiet_log_print = $(if $(quiet_cmd_$1), echo ' $(call escsq,$(quiet_cmd_$1)$(why))';)
log_print = echo '$(pound) $(call escsq,$(or $(quiet_cmd_$1),cmd_$1 $@)$(why))'; \
echo ' $(call escsq,$(cmd_$1))';
# Delete the target on interruption
#
# GNU Make automatically deletes the target if it has already been changed by
# the interrupted recipe. So, you can safely stop the build by Ctrl-C (Make
# will delete incomplete targets), and resume it later.
#
# However, this does not work when the stderr is piped to another program, like
# $ make >&2 | tee log
# Make dies with SIGPIPE before cleaning the targets.
#
# To address it, we clean the target in signal traps.
#
# Make deletes the target when it catches SIGHUP, SIGINT, SIGQUIT, SIGTERM.
# So, we cover them, and also SIGPIPE just in case.
#
# Of course, this is unneeded for phony targets.
delete-on-interrupt = \
$(if $(filter-out $(PHONY), $@), \
$(foreach sig, HUP INT QUIT TERM PIPE, \
trap 'rm -f $@; trap - $(sig); kill -s $(sig) $$$$' $(sig);))
# print and execute commands
cmd = @$(if $(cmd_$(1)),set -e; $($(quiet)log_print) $(delete-on-interrupt) $(cmd_$(1)),:)
###
# if_changed - execute command if any prerequisite is newer than
# target, or command line has changed
# if_changed_dep - as if_changed, but uses fixdep to reveal dependencies
# including used config symbols
# if_changed_rule - as if_changed but execute rule instead
# See Documentation/kbuild/makefiles.rst for more info
ifneq ($(KBUILD_NOCMDDEP),1)
# Check if both commands are the same including their order. Result is empty
# string if equal. User may override this check using make KBUILD_NOCMDDEP=1
# If the target does not exist, the *.cmd file should not be included so
# $(savedcmd_$@) gets empty. Then, target will be built even if $(newer-prereqs)
# happens to become empty.
cmd-check = $(filter-out $(subst $(space),$(space_escape),$(strip $(savedcmd_$@))), \
$(subst $(space),$(space_escape),$(strip $(cmd_$1))))
else
# We still need to detect missing targets.
cmd-check = $(if $(strip $(savedcmd_$@)),,1)
endif
# Replace >$< with >$$< to preserve $ when reloading the .cmd file
# (needed for make)
# Replace >#< with >$(pound)< to avoid starting a comment in the .cmd file
# (needed for make)
# Replace >'< with >'\''< to be able to enclose the whole string in '...'
# (needed for the shell)
make-cmd = $(call escsq,$(subst $(pound),$$(pound),$(subst $$,$$$$,$(cmd_$(1)))))
# Find any prerequisites that are newer than target or that do not exist.
# PHONY targets skipped in both cases.
# If there is no prerequisite other than phony targets, $(newer-prereqs) becomes
# empty even if the target does not exist. cmd-check saves this corner case.
newer-prereqs = $(filter-out $(PHONY),$?)
# It is a typical mistake to forget the FORCE prerequisite. Check it here so
# no more breakage will slip in.
check-FORCE = $(if $(filter FORCE, $^),,$(warning FORCE prerequisite is missing))
if-changed-cond = $(newer-prereqs)$(cmd-check)$(check-FORCE)
# Execute command if command has changed or prerequisite(s) are updated.
if_changed = $(if $(if-changed-cond),$(cmd_and_savecmd),@:)
cmd_and_savecmd = \
$(cmd); \
printf '%s\n' 'savedcmd_$@ := $(make-cmd)' > $(dot-target).cmd
# Execute the command and also postprocess generated .d dependencies file.
if_changed_dep = $(if $(if-changed-cond),$(cmd_and_fixdep),@:)
cmd_and_fixdep = \
$(cmd); \
$(objtree)/scripts/basic/fixdep $(depfile) $@ '$(make-cmd)' > $(dot-target).cmd;\
rm -f $(depfile)
# Usage: $(call if_changed_rule,foo)
# Will check if $(cmd_foo) or any of the prerequisites changed,
# and if so will execute $(rule_foo).
if_changed_rule = $(if $(if-changed-cond),$(rule_$(1)),@:)
###
# why - tell why a target got built
# enabled by make V=2
# Output (listed in the order they are checked):
# (1) - due to target is PHONY
# (2) - due to target missing
# (3) - due to: file1.h file2.h
# (4) - due to command line change
# (5) - due to missing .cmd file
# (6) - due to target not in $(targets)
# (1) PHONY targets are always build
# (2) No target, so we better build it
# (3) Prerequisite is newer than target
# (4) The command line stored in the file named dir/.target.cmd
# differed from actual command line. This happens when compiler
# options changes
# (5) No dir/.target.cmd file (used to store command line)
# (6) No dir/.target.cmd file and target not listed in $(targets)
# This is a good hint that there is a bug in the kbuild file
ifneq ($(findstring 2, $(KBUILD_VERBOSE)),)
_why = \
$(if $(filter $@, $(PHONY)),- due to target is PHONY, \
$(if $(wildcard $@), \
$(if $(newer-prereqs),- due to: $(newer-prereqs), \
$(if $(cmd-check), \
$(if $(savedcmd_$@),- due to command line change, \
$(if $(filter $@, $(targets)), \
- due to missing .cmd file, \
- due to $(notdir $@) not in $$(targets) \
) \
) \
) \
), \
- due to target missing \
) \
)
why = $(space)$(strip $(_why))
endif
###############################################################################
# delete partially updated (i.e. corrupted) files on error
.DELETE_ON_ERROR:
# do not delete intermediate files automatically
#
# .NOTINTERMEDIATE is more correct, but only available on newer Make versions.
# Make 4.4 introduced .NOTINTERMEDIATE, and it appears in .FEATURES, but the
# global .NOTINTERMEDIATE does not work. We can use it on Make > 4.4.
# Use .SECONDARY for older Make versions, but "newer-prereq" cannot detect
# deleted files.
ifneq ($(and $(filter notintermediate, $(.FEATURES)),$(filter-out 4.4,$(MAKE_VERSION))),)
.NOTINTERMEDIATE:
else
.SECONDARY:
endif

View File

@@ -0,0 +1,62 @@
# SPDX-License-Identifier: GPL-2.0
###
# scripts contains sources for various helper programs used throughout
# the kernel for the build process.
hostprogs-always-$(CONFIG_KALLSYMS) += kallsyms
hostprogs-always-$(BUILD_C_RECORDMCOUNT) += recordmcount
hostprogs-always-$(CONFIG_BUILDTIME_TABLE_SORT) += sorttable
hostprogs-always-$(CONFIG_ASN1) += asn1_compiler
hostprogs-always-$(CONFIG_MODULE_SIG_FORMAT) += sign-file
hostprogs-always-$(CONFIG_SYSTEM_EXTRA_CERTIFICATE) += insert-sys-cert
hostprogs-always-$(CONFIG_RUST_KERNEL_DOCTESTS) += rustdoc_test_builder
hostprogs-always-$(CONFIG_RUST_KERNEL_DOCTESTS) += rustdoc_test_gen
ifneq ($(or $(CONFIG_X86_64),$(CONFIG_X86_32)),)
always-$(CONFIG_RUST) += target.json
filechk_rust_target = $< < include/config/auto.conf
$(obj)/target.json: scripts/generate_rust_target include/config/auto.conf FORCE
$(call filechk,rust_target)
endif
hostprogs += generate_rust_target
generate_rust_target-rust := y
rustdoc_test_builder-rust := y
rustdoc_test_gen-rust := y
HOSTCFLAGS_sorttable.o = -I$(srctree)/tools/include
HOSTLDLIBS_sorttable = -lpthread
HOSTCFLAGS_asn1_compiler.o = -I$(srctree)/include
HOSTCFLAGS_sign-file.o = $(shell $(HOSTPKG_CONFIG) --cflags libcrypto 2> /dev/null)
HOSTLDLIBS_sign-file = $(shell $(HOSTPKG_CONFIG) --libs libcrypto 2> /dev/null || echo -lcrypto)
ifdef CONFIG_UNWINDER_ORC
ifeq ($(ARCH),x86_64)
SRCARCH := x86
endif
ifeq ($(ARCH),loongarch)
SRCARCH := loongarch
endif
HOSTCFLAGS_sorttable.o += -I$(srctree)/tools/arch/$(SRCARCH)/include
HOSTCFLAGS_sorttable.o += -DUNWINDER_ORC_ENABLED
endif
ifdef CONFIG_BUILDTIME_MCOUNT_SORT
HOSTCFLAGS_sorttable.o += -DMCOUNT_SORT_ENABLED
endif
# The following programs are only built on demand
hostprogs += unifdef gen_packed_field_checks
# The module linker script is preprocessed on demand
targets += module.lds
subdir-$(CONFIG_GCC_PLUGINS) += gcc-plugins
subdir-$(CONFIG_GENKSYMS) += genksyms
subdir-$(CONFIG_GENDWARFKSYMS) += gendwarfksyms
subdir-$(CONFIG_SECURITY_SELINUX) += selinux
subdir-$(CONFIG_SECURITY_IPE) += ipe
# Let clean descend into subdirs
subdir- += basic dtc gdb kconfig mod

View File

@@ -0,0 +1,588 @@
# SPDX-License-Identifier: GPL-2.0
# ==========================================================================
# Building
# ==========================================================================
src := $(srcroot)/$(obj)
PHONY := $(obj)/
$(obj)/:
# Init all relevant variables used in kbuild files so
# 1) they have correct type
# 2) they do not inherit any value from the environment
obj-y :=
obj-m :=
lib-y :=
lib-m :=
always-y :=
always-m :=
targets :=
subdir-y :=
subdir-m :=
asflags-y :=
ccflags-y :=
rustflags-y :=
cppflags-y :=
ldflags-y :=
subdir-asflags-y :=
subdir-ccflags-y :=
# Read auto.conf if it exists, otherwise ignore
-include $(objtree)/include/config/auto.conf
include $(srctree)/scripts/Kbuild.include
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
ifeq ($(need-modorder),)
ifneq ($(obj-m),)
$(warning $(patsubst %.o,'%.ko',$(obj-m)) will not be built even though obj-m is specified.)
$(warning You cannot use subdir-y/m to visit a module Makefile. Use obj-y/m instead.)
endif
endif
# ===========================================================================
# subdir-builtin and subdir-modorder may contain duplications. Use $(sort ...)
subdir-builtin := $(sort $(filter %/built-in.a, $(real-obj-y)))
subdir-modorder := $(sort $(filter %/modules.order, $(obj-m)))
targets-for-builtin := $(extra-y)
ifneq ($(strip $(lib-y) $(lib-m) $(lib-)),)
targets-for-builtin += $(obj)/lib.a
endif
ifdef need-builtin
targets-for-builtin += $(obj)/built-in.a
endif
targets-for-modules := $(foreach x, o mod, \
$(patsubst %.o, %.$x, $(filter %.o, $(obj-m))))
ifdef need-modorder
targets-for-modules += $(obj)/modules.order
endif
targets += $(targets-for-builtin) $(targets-for-modules)
# Linus' kernel sanity checking tool
ifeq ($(KBUILD_CHECKSRC),1)
quiet_cmd_checksrc = CHECK $<
cmd_checksrc = $(CHECK) $(CHECKFLAGS) $(c_flags) $<
else ifeq ($(KBUILD_CHECKSRC),2)
quiet_cmd_force_checksrc = CHECK $<
cmd_force_checksrc = $(CHECK) $(CHECKFLAGS) $(c_flags) $<
endif
ifneq ($(KBUILD_EXTRA_WARN),)
cmd_checkdoc = PYTHONDONTWRITEBYTECODE=1 $(KERNELDOC) -none $(KDOCFLAGS) \
$(if $(findstring 2, $(KBUILD_EXTRA_WARN)), -Wall) \
$<
endif
# Compile C sources (.c)
# ---------------------------------------------------------------------------
quiet_cmd_cc_s_c = CC $(quiet_modtag) $@
cmd_cc_s_c = $(CC) $(filter-out $(DEBUG_CFLAGS) $(CC_FLAGS_LTO), $(c_flags)) -fverbose-asm -S -o $@ $<
$(obj)/%.s: $(obj)/%.c FORCE
$(call if_changed_dep,cc_s_c)
quiet_cmd_cpp_i_c = CPP $(quiet_modtag) $@
cmd_cpp_i_c = $(CPP) $(c_flags) -o $@ $<
$(obj)/%.i: $(obj)/%.c FORCE
$(call if_changed_dep,cpp_i_c)
getexportsymbols = $(NM) $@ | sed -n 's/.* __export_symbol_\(.*\)/$(1)/p'
gendwarfksyms = $(objtree)/scripts/gendwarfksyms/gendwarfksyms \
$(if $(KBUILD_SYMTYPES), --symtypes $(@:.o=.symtypes)) \
$(if $(KBUILD_GENDWARFKSYMS_STABLE), --stable)
genksyms = $(objtree)/scripts/genksyms/genksyms \
$(if $(KBUILD_SYMTYPES), -T $(@:.o=.symtypes)) \
$(if $(KBUILD_PRESERVE), -p) \
$(addprefix -r , $(wildcard $(@:.o=.symref)))
# These mirror gensymtypes_S and co below, keep them in synch.
ifdef CONFIG_GENDWARFKSYMS
cmd_gensymtypes_c = $(if $(skip_gendwarfksyms),, \
$(call getexportsymbols,\1) | $(gendwarfksyms) $@)
else
cmd_gensymtypes_c = $(CPP) -D__GENKSYMS__ $(c_flags) $< | $(genksyms)
endif # CONFIG_GENDWARFKSYMS
# LLVM assembly
# Generate .ll files from .c
quiet_cmd_cc_ll_c = CC $(quiet_modtag) $@
cmd_cc_ll_c = $(CC) $(c_flags) -emit-llvm -S -fno-discard-value-names -o $@ $<
$(obj)/%.ll: $(obj)/%.c FORCE
$(call if_changed_dep,cc_ll_c)
# C (.c) files
# The C file is compiled and updated dependency information is generated.
# (See cmd_cc_o_c + relevant part of rule_cc_o_c)
is-single-obj-m = $(and $(part-of-module),$(filter $@, $(obj-m)),y)
ifdef CONFIG_MODVERSIONS
# When module versioning is enabled the following steps are executed:
# o compile a <file>.o from <file>.c
# o if <file>.o doesn't contain a __export_symbol_*, i.e. does
# not export symbols, it's done.
# o otherwise, we calculate symbol versions using the good old
# genksyms on the preprocessed source and dump them into the .cmd file.
# o modpost will extract versions from that file and create *.c files that will
# be compiled and linked to the kernel and/or modules.
gen_symversions = \
if $(NM) $@ 2>/dev/null | grep -q ' __export_symbol_'; then \
$(cmd_gensymtypes_$1) >> $(dot-target).cmd; \
fi
cmd_gen_symversions_c = $(call gen_symversions,c)
endif
ifdef CONFIG_FTRACE_MCOUNT_USE_RECORDMCOUNT
# compiler will not generate __mcount_loc use recordmcount or recordmcount.pl
ifdef BUILD_C_RECORDMCOUNT
ifeq ("$(origin RECORDMCOUNT_WARN)", "command line")
RECORDMCOUNT_FLAGS = -w
endif
# Due to recursion, we must skip empty.o.
# The empty.o file is created in the make process in order to determine
# the target endianness and word size. It is made before all other C
# files, including recordmcount.
sub_cmd_record_mcount = \
if [ $(@) != "scripts/mod/empty.o" ]; then \
$(objtree)/scripts/recordmcount $(RECORDMCOUNT_FLAGS) "$(@)"; \
fi;
recordmcount_source := $(srctree)/scripts/recordmcount.c \
$(srctree)/scripts/recordmcount.h
else
sub_cmd_record_mcount = perl $(srctree)/scripts/recordmcount.pl "$(ARCH)" \
"$(if $(CONFIG_CPU_BIG_ENDIAN),big,little)" \
"$(if $(CONFIG_64BIT),64,32)" \
"$(OBJDUMP)" "$(OBJCOPY)" "$(CC) $(KBUILD_CPPFLAGS) $(KBUILD_CFLAGS)" \
"$(LD) $(KBUILD_LDFLAGS)" "$(NM)" "$(RM)" "$(MV)" \
"$(if $(part-of-module),1,0)" "$(@)";
recordmcount_source := $(srctree)/scripts/recordmcount.pl
endif # BUILD_C_RECORDMCOUNT
cmd_record_mcount = $(if $(findstring $(strip $(CC_FLAGS_FTRACE)),$(_c_flags)), \
$(sub_cmd_record_mcount))
endif # CONFIG_FTRACE_MCOUNT_USE_RECORDMCOUNT
# 'OBJECT_FILES_NON_STANDARD := y': skip objtool checking for a directory
# 'OBJECT_FILES_NON_STANDARD_foo.o := 'y': skip objtool checking for a file
# 'OBJECT_FILES_NON_STANDARD_foo.o := 'n': override directory skip for a file
is-standard-object = $(if $(filter-out y%, $(OBJECT_FILES_NON_STANDARD_$(target-stem).o)$(OBJECT_FILES_NON_STANDARD)n),$(is-kernel-object))
ifdef CONFIG_OBJTOOL
$(obj)/%.o: private objtool-enabled = $(if $(is-standard-object),$(if $(delay-objtool),$(is-single-obj-m),y))
endif
ifneq ($(findstring 1, $(KBUILD_EXTRA_WARN)),)
cmd_warn_shared_object = $(if $(word 2, $(modname-multi)),$(warning $(kbuild-file): $*.o is added to multiple modules: $(modname-multi)))
endif
# Built-in and composite module parts
$(obj)/%.o: $(obj)/%.c $(recordmcount_source) FORCE
$(call if_changed_rule,cc_o_c)
$(call cmd,force_checksrc)
# To make this rule robust against "Argument list too long" error,
# ensure to add $(obj)/ prefix by a shell command.
cmd_mod = printf '%s\n' $(call real-search, $*.o, .o, -objs -y -m) | \
$(AWK) '!x[$$0]++ { print("$(obj)/"$$0) }' > $@
$(obj)/%.mod: FORCE
$(call if_changed,mod)
quiet_cmd_cc_lst_c = MKLST $@
cmd_cc_lst_c = $(CC) $(c_flags) -g -c -o $*.o $< && \
$(CONFIG_SHELL) $(srctree)/scripts/makelst $*.o \
System.map $(OBJDUMP) > $@
$(obj)/%.lst: $(obj)/%.c FORCE
$(call if_changed_dep,cc_lst_c)
# Compile Rust sources (.rs)
# ---------------------------------------------------------------------------
# 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(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,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
# modules case.
rust_common_cmd = \
OBJTREE=$(abspath $(objtree)) \
RUST_MODFILE=$(modfile) $(RUSTC_OR_CLIPPY) $(rust_flags) \
-Zallow-features=$(rust_allowed_features) \
-Zcrate-attr=no_std \
-Zcrate-attr='feature($(rust_allowed_features))' \
-Zunstable-options --extern pin_init --extern kernel \
--crate-type rlib -L $(objtree)/rust/ \
--crate-name $(basename $(notdir $@)) \
--sysroot=/dev/null \
--out-dir $(dir $@) --emit=dep-info=$(depfile)
# `--emit=obj`, `--emit=asm` and `--emit=llvm-ir` imply a single codegen unit
# will be used. We explicitly request `-Ccodegen-units=1` in any case, and
# the compiler shows a warning if it is not 1. However, if we ever stop
# requesting it explicitly and we start using some other `--emit` that does not
# imply it (and for which codegen is performed), then we would be out of sync,
# i.e. the outputs we would get for the different single targets (e.g. `.ll`)
# would not match each other.
quiet_cmd_rustc_o_rs = $(RUSTC_OR_CLIPPY_QUIET) $(quiet_modtag) $@
cmd_rustc_o_rs = $(rust_common_cmd) --emit=obj=$@ $< $(cmd_objtool)
define rule_rustc_o_rs
$(call cmd_and_fixdep,rustc_o_rs)
$(call cmd,gen_objtooldep)
endef
$(obj)/%.o: $(obj)/%.rs FORCE
+$(call if_changed_rule,rustc_o_rs)
quiet_cmd_rustc_rsi_rs = $(RUSTC_OR_CLIPPY_QUIET) $(quiet_modtag) $@
cmd_rustc_rsi_rs = \
$(rust_common_cmd) -Zunpretty=expanded $< >$@; \
command -v $(RUSTFMT) >/dev/null && $(RUSTFMT) $@
$(obj)/%.rsi: $(obj)/%.rs FORCE
+$(call if_changed_dep,rustc_rsi_rs)
quiet_cmd_rustc_s_rs = $(RUSTC_OR_CLIPPY_QUIET) $(quiet_modtag) $@
cmd_rustc_s_rs = $(rust_common_cmd) --emit=asm=$@ $<
$(obj)/%.s: $(obj)/%.rs FORCE
+$(call if_changed_dep,rustc_s_rs)
quiet_cmd_rustc_ll_rs = $(RUSTC_OR_CLIPPY_QUIET) $(quiet_modtag) $@
cmd_rustc_ll_rs = $(rust_common_cmd) --emit=llvm-ir=$@ $<
$(obj)/%.ll: $(obj)/%.rs FORCE
+$(call if_changed_dep,rustc_ll_rs)
quiet_cmd_rustc_rs_rs_S = RSCPP $(quiet_modtag) $@
cmd_rustc_rs_rs_S = $(CPP) $(c_flags) -xc -C -P $< | sed '1,/^\/\/ Cut here.$$/d' >$@
$(obj)/%.rs: $(obj)/%.rs.S FORCE
+$(call if_changed_dep,rustc_rs_rs_S)
# Compile assembler sources (.S)
# ---------------------------------------------------------------------------
# .S file exports must have their C prototypes defined in asm/asm-prototypes.h
# or a file that it includes, in order to get versioned symbols. We build a
# dummy C file that includes asm-prototypes and the EXPORT_SYMBOL lines from
# the .S file (with trailing ';'), and run genksyms on that, to extract vers.
#
# This is convoluted. The .S file must first be preprocessed to run guards and
# expand names, then the resulting exports must be constructed into plain
# EXPORT_SYMBOL(symbol); to build our dummy C file, and that gets preprocessed
# to make the genksyms input or compiled into an object for gendwarfksyms.
#
# These mirror gensymtypes_c and co above, keep them in synch.
getasmexports = \
{ echo "\#include <linux/kernel.h>" ; \
echo "\#include <linux/string.h>" ; \
echo "\#include <asm/asm-prototypes.h>" ; \
$(call getexportsymbols,EXPORT_SYMBOL(\1);) ; }
ifdef CONFIG_GENDWARFKSYMS
cmd_gensymtypes_S = \
$(getasmexports) | \
$(CC) $(c_flags) -c -o $(@:.o=.gendwarfksyms.o) -xc -; \
$(call getexportsymbols,\1) | \
$(gendwarfksyms) $(@:.o=.gendwarfksyms.o)
else
cmd_gensymtypes_S = \
$(getasmexports) | \
$(CPP) -D__GENKSYMS__ $(c_flags) -xc - | $(genksyms)
endif # CONFIG_GENDWARFKSYMS
quiet_cmd_cpp_s_S = CPP $(quiet_modtag) $@
cmd_cpp_s_S = $(CPP) $(a_flags) -o $@ $<
$(obj)/%.s: $(obj)/%.S FORCE
$(call if_changed_dep,cpp_s_S)
ifdef CONFIG_ASM_MODVERSIONS
# versioning matches the C process described above, with difference that
# we parse asm-prototypes.h C header to get function definitions.
cmd_gen_symversions_S = $(call gen_symversions,S)
endif
$(obj)/%.o: $(obj)/%.S FORCE
$(call if_changed_rule,as_o_S)
targets += $(filter-out $(subdir-builtin), $(real-obj-y))
targets += $(filter-out $(subdir-modorder), $(real-obj-m))
targets += $(lib-y) $(always-y)
# Linker scripts preprocessor (.lds.S -> .lds)
# ---------------------------------------------------------------------------
quiet_cmd_cpp_lds_S = LDS $@
cmd_cpp_lds_S = $(CPP) $(cpp_flags) -P -U$(ARCH) \
-D__ASSEMBLY__ -DLINKER_SCRIPT -o $@ $<
$(obj)/%.lds: $(src)/%.lds.S FORCE
$(call if_changed_dep,cpp_lds_S)
# ASN.1 grammar
# ---------------------------------------------------------------------------
quiet_cmd_asn1_compiler = ASN.1 $(basename $@).[ch]
cmd_asn1_compiler = $(objtree)/scripts/asn1_compiler $< \
$(basename $@).c $(basename $@).h
$(obj)/%.asn1.c $(obj)/%.asn1.h: $(src)/%.asn1 $(objtree)/scripts/asn1_compiler
$(call cmd,asn1_compiler)
# Build the compiled-in targets
# ---------------------------------------------------------------------------
# To build objects in subdirs, we need to descend into the directories
$(subdir-builtin): $(obj)/%/built-in.a: $(obj)/% ;
$(subdir-modorder): $(obj)/%/modules.order: $(obj)/% ;
#
# Rule to compile a set of .o files into one .a file (without symbol table)
#
# To make this rule robust against "Argument list too long" error,
# remove $(obj)/ prefix, and restore it by a shell command.
quiet_cmd_ar_builtin = AR $@
cmd_ar_builtin = rm -f $@; \
$(if $(real-prereqs), printf "$(obj)/%s " $(patsubst $(obj)/%,%,$(real-prereqs)) | xargs) \
$(AR) cDPrST $@
$(obj)/built-in.a: $(real-obj-y) FORCE
$(call if_changed,ar_builtin)
# This is a list of build artifacts from the current Makefile and its
# sub-directories. The timestamp should be updated when any of the member files.
cmd_gen_order = { $(foreach m, $(real-prereqs), \
$(if $(filter %/$(notdir $@), $m), cat $m, echo $m);) :; } \
> $@
$(obj)/modules.order: $(obj-m) FORCE
$(call if_changed,gen_order)
#
# Rule to compile a set of .o files into one .a file (with symbol table)
#
$(obj)/lib.a: $(lib-y) FORCE
$(call if_changed,ar)
quiet_cmd_ld_multi_m = LD [M] $@
cmd_ld_multi_m = $(LD) $(ld_flags) -r -o $@ @$< $(cmd_objtool)
define rule_ld_multi_m
$(call cmd_and_savecmd,ld_multi_m)
$(call cmd,gen_objtooldep)
endef
$(multi-obj-m): private objtool-enabled := $(delay-objtool)
$(multi-obj-m): private part-of-module := y
$(multi-obj-m): %.o: %.mod FORCE
$(call if_changed_rule,ld_multi_m)
$(call multi_depend, $(multi-obj-m), .o, -objs -y -m)
# Add intermediate targets:
# When building objects with specific suffix patterns, add intermediate
# targets that the final targets are derived from.
intermediate_targets = $(foreach sfx, $(2), \
$(patsubst %$(strip $(1)),%$(sfx), \
$(filter %$(strip $(1)), $(targets))))
# %.asn1.o <- %.asn1.[ch] <- %.asn1
targets += $(call intermediate_targets, .asn1.o, .asn1.c .asn1.h)
# Include additional build rules when necessary
# ---------------------------------------------------------------------------
# $(sort ...) is used here to remove duplicated words and excessive spaces.
hostprogs := $(sort $(hostprogs))
ifneq ($(hostprogs),)
include $(srctree)/scripts/Makefile.host
endif
# $(sort ...) is used here to remove duplicated words and excessive spaces.
userprogs := $(sort $(userprogs))
ifneq ($(userprogs),)
include $(srctree)/scripts/Makefile.userprogs
endif
ifneq ($(need-dtbslist)$(dtb-y)$(dtb-)$(filter %.dtb %.dtb.o %.dtbo.o,$(targets)),)
include $(srctree)/scripts/Makefile.dtbs
endif
# Build
# ---------------------------------------------------------------------------
$(obj)/: $(if $(KBUILD_BUILTIN), $(targets-for-builtin)) \
$(if $(KBUILD_MODULES), $(targets-for-modules)) \
$(subdir-ym) $(always-y)
@:
# Single targets
# ---------------------------------------------------------------------------
single-subdirs := $(foreach d, $(subdir-ym), $(if $(filter $d/%, $(MAKECMDGOALS)), $d))
single-subdir-goals := $(filter $(addsuffix /%, $(single-subdirs)), $(MAKECMDGOALS))
$(single-subdir-goals): $(single-subdirs)
@:
# Descending
# ---------------------------------------------------------------------------
PHONY += $(subdir-ym)
$(subdir-ym):
$(Q)$(MAKE) $(build)=$@ \
need-builtin=$(if $(filter $@/built-in.a, $(subdir-builtin)),1) \
need-modorder=$(if $(filter $@/modules.order, $(subdir-modorder)),1) \
$(filter $@/%, $(single-subdir-goals))
# Add FORCE to the prerequisites of a target to force it to be always rebuilt.
# ---------------------------------------------------------------------------
PHONY += FORCE
FORCE:
targets += $(filter-out $(single-subdir-goals), $(MAKECMDGOALS))
targets := $(filter-out $(PHONY), $(targets))
# Read all saved command lines and dependencies for the $(targets) we
# may be building above, using $(if_changed{,_dep}). As an
# optimization, we don't need to read them if the target does not
# exist, we will rebuild anyway in that case.
existing-targets := $(wildcard $(sort $(targets)))
-include $(foreach f,$(existing-targets),$(dir $(f)).$(notdir $(f)).cmd)
# Create directories for object files if they do not exist
obj-dirs := $(sort $(patsubst %/,%, $(dir $(targets))))
# If targets exist, their directories apparently exist. Skip mkdir.
existing-dirs := $(sort $(patsubst %/,%, $(dir $(existing-targets))))
obj-dirs := $(strip $(filter-out $(existing-dirs), $(obj-dirs)))
ifneq ($(obj-dirs),)
$(shell mkdir -p $(obj-dirs))
endif
.PHONY: $(PHONY)

View File

@@ -0,0 +1,42 @@
# Individual arch/{arch}/Makefiles should use -EL/-EB to set intended
# endianness and -m32/-m64 to set word size based on Kconfigs instead of
# relying on the target triple.
CLANG_TARGET_FLAGS_arm := arm-linux-gnueabi
CLANG_TARGET_FLAGS_arm64 := aarch64-linux-gnu
CLANG_TARGET_FLAGS_hexagon := hexagon-linux-musl
CLANG_TARGET_FLAGS_loongarch := loongarch64-linux-gnusf
CLANG_TARGET_FLAGS_m68k := m68k-linux-gnu
CLANG_TARGET_FLAGS_mips := mipsel-linux-gnu
CLANG_TARGET_FLAGS_powerpc := powerpc64le-linux-gnu
CLANG_TARGET_FLAGS_riscv := riscv64-linux-gnu
CLANG_TARGET_FLAGS_s390 := s390x-linux-gnu
CLANG_TARGET_FLAGS_sparc := sparc64-linux-gnu
CLANG_TARGET_FLAGS_x86 := x86_64-linux-gnu
# This is only for i386 UM builds, which need the 32-bit target not -m32
CLANG_TARGET_FLAGS_i386 := i386-linux-gnu
CLANG_TARGET_FLAGS_um := $(CLANG_TARGET_FLAGS_$(SUBARCH))
CLANG_TARGET_FLAGS := $(CLANG_TARGET_FLAGS_$(SRCARCH))
ifeq ($(CLANG_TARGET_FLAGS),)
$(error add '--target=' option to scripts/Makefile.clang)
else
CLANG_FLAGS += --target=$(CLANG_TARGET_FLAGS)
endif
ifeq ($(LLVM_IAS),0)
CLANG_FLAGS += -fno-integrated-as
GCC_TOOLCHAIN_DIR := $(dir $(shell which $(CROSS_COMPILE)elfedit))
CLANG_FLAGS += --prefix=$(GCC_TOOLCHAIN_DIR)$(notdir $(CROSS_COMPILE))
else
CLANG_FLAGS += -fintegrated-as
endif
# By default, clang only warns when it encounters an unknown warning flag or
# certain optimization flags it knows it has not implemented.
# Make it behave more like gcc by erroring when these flags are encountered
# so they can be implemented or wrapped in cc-option.
CLANG_FLAGS += -Werror=unknown-warning-option
CLANG_FLAGS += -Werror=ignored-optimization-argument
CLANG_FLAGS += -Werror=option-ignored
CLANG_FLAGS += -Werror=unused-command-line-argument
KBUILD_CPPFLAGS += $(CLANG_FLAGS)
export CLANG_FLAGS

View File

@@ -0,0 +1,63 @@
# SPDX-License-Identifier: GPL-2.0
# ==========================================================================
# Cleaning up
# ==========================================================================
src := $(srcroot)/$(obj)
PHONY := __clean
__clean:
include $(srctree)/scripts/Kbuild.include
include $(kbuild-file)
# Figure out what we need to build from the various variables
# ==========================================================================
subdir-ymn := $(sort $(subdir-y) $(subdir-m) $(subdir-) \
$(patsubst %/,%, $(filter %/, $(obj-y) $(obj-m) $(obj-))))
# Add subdir path
subdir-ymn := $(addprefix $(obj)/,$(subdir-ymn))
# build a list of files to remove, usually relative to the current
# directory
__clean-files := \
$(clean-files) $(targets) $(hostprogs) $(userprogs) \
$(extra-y) $(extra-m) $(extra-) \
$(always-y) $(always-m) $(always-) \
$(hostprogs-always-y) $(hostprogs-always-m) $(hostprogs-always-) \
$(userprogs-always-y) $(userprogs-always-m) $(userprogs-always-)
__clean-files := $(filter-out $(no-clean-files), $(__clean-files))
__clean-files := $(wildcard $(addprefix $(obj)/, $(__clean-files)))
# ==========================================================================
# To make this rule robust against "Argument list too long" error,
# remove $(obj)/ prefix, and restore it by a shell command.
quiet_cmd_clean = CLEAN $(obj)
cmd_clean = printf '$(obj)/%s ' $(patsubst $(obj)/%,%,$(__clean-files)) | xargs rm -rf
__clean: $(subdir-ymn)
ifneq ($(strip $(__clean-files)),)
$(call cmd,clean)
endif
@:
# ===========================================================================
# Generic stuff
# ===========================================================================
# Descending
# ---------------------------------------------------------------------------
PHONY += $(subdir-ymn)
$(subdir-ymn):
$(Q)$(MAKE) $(clean)=$@
.PHONY: $(PHONY)

View File

@@ -0,0 +1,93 @@
# SPDX-License-Identifier: GPL-2.0-only
# cc-cross-prefix
# Usage: CROSS_COMPILE := $(call cc-cross-prefix, m68k-linux-gnu- m68k-linux-)
# Return first <prefix> where a <prefix>gcc is found in PATH.
# If no gcc found in PATH with listed prefixes return nothing
#
# Note: '2>/dev/null' is here to force Make to invoke a shell. Otherwise, it
# would try to directly execute the shell builtin 'command'. This workaround
# should be kept for a long time since this issue was fixed only after the
# GNU Make 4.2.1 release.
cc-cross-prefix = $(firstword $(foreach c, $(1), \
$(if $(shell command -v -- $(c)gcc 2>/dev/null), $(c))))
# output directory for tests below
TMPOUT = .tmp_$$$$
# try-run
# Usage: option = $(call try-run, $(CC)...-o "$$TMP",option-ok,otherwise)
# Exit code chooses option. "$$TMP" serves as a temporary file and is
# automatically cleaned up.
try-run = $(shell set -e; \
TMP=$(TMPOUT)/tmp; \
trap "rm -rf $(TMPOUT)" EXIT; \
mkdir -p $(TMPOUT); \
if ($(1)) >/dev/null 2>&1; \
then echo "$(2)"; \
else echo "$(3)"; \
fi)
# as-option
# Usage: aflags-y += $(call as-option,-Wa$(comma)-isa=foo,)
as-option = $(call try-run,\
$(CC) -Werror $(KBUILD_CPPFLAGS) $(KBUILD_AFLAGS) $(1) -c -x assembler-with-cpp /dev/null -o "$$TMP",$(1),$(2))
# as-instr
# Usage: aflags-y += $(call as-instr,instr,option1,option2)
as-instr = $(call try-run,\
printf "%b\n" "$(1)" | $(CC) -Werror $(CLANG_FLAGS) $(KBUILD_AFLAGS) -Wa$(comma)--fatal-warnings -c -x assembler-with-cpp -o "$$TMP" -,$(2),$(3))
# __cc-option
# Usage: MY_CFLAGS += $(call __cc-option,$(CC),$(MY_CFLAGS),-march=winchip-c6,-march=i586)
__cc-option = $(call try-run,\
$(1) -Werror $(2) $(3:-Wno-%=-W%) -c -x c /dev/null -o "$$TMP",$(3),$(4))
# cc-option
# Usage: cflags-y += $(call cc-option,-march=winchip-c6,-march=i586)
cc-option = $(call __cc-option, $(CC),\
$(KBUILD_CPPFLAGS) $(KBUILD_CFLAGS),$(1),$(2))
# cc-option-yn
# Usage: flag := $(call cc-option-yn,-march=winchip-c6)
cc-option-yn = $(if $(call cc-option,$1),y,n)
# cc-disable-warning
# Usage: cflags-y += $(call cc-disable-warning,unused-but-set-variable)
cc-disable-warning = $(call cc-option,-Wno-$(strip $1))
# gcc-min-version
# Usage: cflags-$(call gcc-min-version, 110100) += -foo
gcc-min-version = $(call test-ge, $(CONFIG_GCC_VERSION), $1)
# clang-min-version
# Usage: cflags-$(call clang-min-version, 110000) += -foo
clang-min-version = $(call test-ge, $(CONFIG_CLANG_VERSION), $1)
# rustc-min-version
# Usage: rustc-$(call rustc-min-version, 108500) += -Cfoo
rustc-min-version = $(call test-ge, $(CONFIG_RUSTC_VERSION), $1)
# ld-option
# Usage: KBUILD_LDFLAGS += $(call ld-option, -X, -Y)
ld-option = $(call try-run, $(LD) $(KBUILD_LDFLAGS) $(1) -v,$(1),$(2),$(3))
# __rustc-option
# Usage: MY_RUSTFLAGS += $(call __rustc-option,$(RUSTC),$(MY_RUSTFLAGS),-Cinstrument-coverage,-Zinstrument-coverage)
# TODO: remove RUSTC_BOOTSTRAP=1 when we raise the minimum GNU Make version to 4.4
__rustc-option = $(call try-run,\
echo '$(pound)![allow(missing_docs)]$(pound)![feature(no_core)]$(pound)![no_core]' | RUSTC_BOOTSTRAP=1\
$(1) --sysroot=/dev/null $(filter-out --sysroot=/dev/null --target=%,$(2)) $(3)\
--crate-type=rlib --out-dir=$(TMPOUT) --emit=obj=- - >/dev/null,$(3),$(4))
# rustc-option
# Usage: rustflags-y += $(call rustc-option,-Cinstrument-coverage,-Zinstrument-coverage)
rustc-option = $(call __rustc-option, $(RUSTC),\
$(KBUILD_RUSTFLAGS),$(1),$(2))
# rustc-option-yn
# Usage: flag := $(call rustc-option-yn,-Cinstrument-coverage)
rustc-option-yn = $(if $(call rustc-option,$1),y,n)

View File

@@ -0,0 +1,84 @@
# SPDX-License-Identifier: GPL-2.0
# ==========================================================================
# Installing headers
#
# All headers under include/uapi, include/generated/uapi,
# arch/<arch>/include/uapi and arch/<arch>/include/generated/uapi are
# exported.
# They are preprocessed to remove __KERNEL__ section of the file.
#
# ==========================================================================
PHONY := __headers
__headers:
include $(srctree)/scripts/Kbuild.include
src := $(srctree)/$(obj)
gen := $(objtree)/$(subst include/,include/generated/,$(obj))
dst := usr/include
-include $(src)/Kbuild
# $(filter %/, ...) is a workaround for GNU Make <= 4.2.1, where
# $(wildcard $(src)/*/) contains not only directories but also regular files.
src-subdirs := $(patsubst $(src)/%/,%,$(filter %/, $(wildcard $(src)/*/)))
gen-subdirs := $(patsubst $(gen)/%/,%,$(filter %/, $(wildcard $(gen)/*/)))
all-subdirs := $(sort $(src-subdirs) $(gen-subdirs))
src-headers := $(if $(src-subdirs), $(shell cd $(src) && find $(src-subdirs) -name '*.h'))
src-headers := $(filter-out $(no-export-headers), $(src-headers))
gen-headers := $(if $(gen-subdirs), $(shell cd $(gen) && find $(gen-subdirs) -name '*.h'))
gen-headers := $(filter-out $(no-export-headers), $(gen-headers))
# If the same header is exported from source and generated directories,
# the former takes precedence, but this should be warned.
duplicated := $(filter $(gen-headers), $(src-headers))
$(if $(duplicated), $(warning duplicated header export: $(duplicated)))
gen-headers := $(filter-out $(duplicated), $(gen-headers))
# Add dst path prefix
all-subdirs := $(addprefix $(dst)/, $(all-subdirs))
src-headers := $(addprefix $(dst)/, $(src-headers))
gen-headers := $(addprefix $(dst)/, $(gen-headers))
all-headers := $(src-headers) $(gen-headers)
# Work out what needs to be removed
old-subdirs := $(wildcard $(all-subdirs))
old-headers := $(if $(old-subdirs),$(shell find $(old-subdirs) -name '*.h'))
unwanted := $(filter-out $(all-headers), $(old-headers))
# Create directories
existing-dirs := $(sort $(dir $(old-headers)))
wanted-dirs := $(sort $(dir $(all-headers)))
new-dirs := $(filter-out $(existing-dirs), $(wanted-dirs))
$(if $(new-dirs), $(shell mkdir -p $(new-dirs)))
# Rules
quiet_cmd_install = HDRINST $@
cmd_install = $(CONFIG_SHELL) $(srctree)/scripts/headers_install.sh $< $@
$(src-headers): $(dst)/%.h: $(src)/%.h $(srctree)/scripts/headers_install.sh FORCE
$(call if_changed,install)
$(gen-headers): $(dst)/%.h: $(gen)/%.h $(srctree)/scripts/headers_install.sh FORCE
$(call if_changed,install)
quiet_cmd_remove = REMOVE $(unwanted)
cmd_remove = rm -f $(unwanted)
__headers: $(all-headers)
ifneq ($(unwanted),)
$(call cmd,remove)
endif
@:
existing-headers := $(filter $(old-headers), $(all-headers))
-include $(foreach f,$(existing-headers),$(dir $(f)).$(notdir $(f)).cmd)
PHONY += FORCE
FORCE:
.PHONY: $(PHONY)

View File

@@ -0,0 +1,165 @@
# SPDX-License-Identifier: GPL-2.0
# LEX
# ---------------------------------------------------------------------------
quiet_cmd_flex = LEX $@
cmd_flex = $(LEX) -o$@ -L $<
$(obj)/%.lex.c: $(src)/%.l FORCE
$(call if_changed,flex)
# YACC
# ---------------------------------------------------------------------------
quiet_cmd_bison = YACC $(basename $@).[ch]
cmd_bison = $(YACC) -o $(basename $@).c --defines=$(basename $@).h -t -l $<
$(obj)/%.tab.c $(obj)/%.tab.h: $(src)/%.y FORCE
$(call if_changed,bison)
# ==========================================================================
# Building binaries on the host system
# Binaries are used during the compilation of the kernel, for example
# to preprocess a data file.
#
# Both C and C++ are supported, but preferred language is C for such utilities.
# Rust is also supported, but it may only be used in scenarios where a Rust
# toolchain is required to be available (e.g. when `CONFIG_RUST` is enabled).
#
# Sample syntax (see Documentation/kbuild/makefiles.rst for reference)
# hostprogs := bin2hex
# Will compile bin2hex.c and create an executable named bin2hex
#
# hostprogs := lxdialog
# lxdialog-objs := checklist.o lxdialog.o
# Will compile lxdialog.c and checklist.c, and then link the executable
# lxdialog, based on checklist.o and lxdialog.o
#
# hostprogs := qconf
# qconf-cxxobjs := qconf.o
# qconf-objs := menu.o
# Will compile qconf as a C++ program, and menu as a C program.
# They are linked as C++ code to the executable qconf
#
# hostprogs := target
# target-rust := y
# Will compile `target` as a Rust program, using `target.rs` as the crate root.
# The crate may consist of several source files.
# C code
# Executables compiled from a single .c file
host-csingle := $(foreach m,$(hostprogs), \
$(if $($(m)-objs)$($(m)-cxxobjs)$($(m)-rust),,$(m)))
# C executables linked based on several .o files
host-cmulti := $(foreach m,$(hostprogs),\
$(if $($(m)-cxxobjs)$($(m)-rust),,$(if $($(m)-objs),$(m))))
# Object (.o) files compiled from .c files
host-cobjs := $(sort $(foreach m,$(hostprogs),$($(m)-objs)))
# C++ code
# C++ executables compiled from at least one .cc file
# and zero or more .c files
host-cxxmulti := $(foreach m,$(hostprogs),$(if $($(m)-cxxobjs),$(m)))
# C++ Object (.o) files compiled from .cc files
host-cxxobjs := $(sort $(foreach m,$(host-cxxmulti),$($(m)-cxxobjs)))
# Rust code
# Executables compiled from a single Rust crate (which may consist of
# one or more .rs files)
host-rust := $(foreach m,$(hostprogs),$(if $($(m)-rust),$(m)))
host-csingle := $(addprefix $(obj)/,$(host-csingle))
host-cmulti := $(addprefix $(obj)/,$(host-cmulti))
host-cobjs := $(addprefix $(obj)/,$(host-cobjs))
host-cxxmulti := $(addprefix $(obj)/,$(host-cxxmulti))
host-cxxobjs := $(addprefix $(obj)/,$(host-cxxobjs))
host-rust := $(addprefix $(obj)/,$(host-rust))
#####
# Handle options to gcc. Support building with separate output directory
hostc_flags = -Wp,-MMD,$(depfile) \
$(KBUILD_HOSTCFLAGS) $(HOST_EXTRACFLAGS) \
$(HOSTCFLAGS_$(target-stem).o)
hostcxx_flags = -Wp,-MMD,$(depfile) \
$(KBUILD_HOSTCXXFLAGS) $(HOST_EXTRACXXFLAGS) \
$(HOSTCXXFLAGS_$(target-stem).o)
# `--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
# modules case.
hostrust_flags = --out-dir $(dir $@) --emit=dep-info=$(depfile) \
-Clinker-flavor=gcc -Clinker=$(HOSTCC) \
-Clink-args='$(call escsq,$(KBUILD_HOSTLDFLAGS))' \
$(KBUILD_HOSTRUSTFLAGS) $(HOST_EXTRARUSTFLAGS) \
$(HOSTRUSTFLAGS_$(target-stem))
# $(obj) for including generated headers from checkin source files
ifdef building_out_of_srctree
hostc_flags += -I $(obj)
hostcxx_flags += -I $(obj)
endif
#####
# Compile programs on the host
# Create executable from a single .c file
# host-csingle -> Executable
quiet_cmd_host-csingle = HOSTCC $@
cmd_host-csingle = $(HOSTCC) $(hostc_flags) $(KBUILD_HOSTLDFLAGS) -o $@ $< \
$(KBUILD_HOSTLDLIBS) $(HOSTLDLIBS_$(target-stem))
$(host-csingle): $(obj)/%: $(obj)/%.c FORCE
$(call if_changed_dep,host-csingle)
# Link an executable based on list of .o files, all plain c
# host-cmulti -> executable
quiet_cmd_host-cmulti = HOSTLD $@
cmd_host-cmulti = $(HOSTCC) $(KBUILD_HOSTLDFLAGS) -o $@ \
$(addprefix $(obj)/, $($(target-stem)-objs)) \
$(KBUILD_HOSTLDLIBS) $(HOSTLDLIBS_$(target-stem))
$(host-cmulti): FORCE
$(call if_changed,host-cmulti)
$(call multi_depend, $(host-cmulti), , -objs)
# Create .o file from a single .c file
# host-cobjs -> .o
quiet_cmd_host-cobjs = HOSTCC $@
cmd_host-cobjs = $(HOSTCC) $(hostc_flags) -c -o $@ $<
$(host-cobjs): $(obj)/%.o: $(obj)/%.c FORCE
$(call if_changed_dep,host-cobjs)
# Link an executable based on list of .o files, a mixture of .c and .cc
# host-cxxmulti -> executable
quiet_cmd_host-cxxmulti = HOSTLD $@
cmd_host-cxxmulti = $(HOSTCXX) $(KBUILD_HOSTLDFLAGS) -o $@ \
$(foreach o,objs cxxobjs,\
$(addprefix $(obj)/, $($(target-stem)-$(o)))) \
$(KBUILD_HOSTLDLIBS) $(HOSTLDLIBS_$(target-stem))
$(host-cxxmulti): FORCE
$(call if_changed,host-cxxmulti)
$(call multi_depend, $(host-cxxmulti), , -objs -cxxobjs)
# Create .o file from a single .cc (C++) file
quiet_cmd_host-cxxobjs = HOSTCXX $@
cmd_host-cxxobjs = $(HOSTCXX) $(hostcxx_flags) -c -o $@ $<
$(host-cxxobjs): $(obj)/%.o: $(obj)/%.cc FORCE
$(call if_changed_dep,host-cxxobjs)
# Create executable from a single Rust crate (which may consist of
# one or more `.rs` files)
# host-rust -> Executable
quiet_cmd_host-rust = HOSTRUSTC $@
cmd_host-rust = \
$(HOSTRUSTC) $(hostrust_flags) --emit=link=$@ $<
$(host-rust): $(obj)/%: $(src)/%.rs FORCE
+$(call if_changed_dep,host-rust)
targets += $(host-csingle) $(host-cmulti) $(host-cobjs) \
$(host-cxxmulti) $(host-cxxobjs) $(host-rust)
# %.lex.o <- %.lex.c <- %.l
# %.tab.o <- %.tab.[ch] <- %.y
targets += $(call intermediate_targets, .lex.o, .lex.c) \
$(call intermediate_targets, .tab.o, .tab.c .tab.h)

View File

@@ -0,0 +1,491 @@
# SPDX-License-Identifier: GPL-2.0
# 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),\
$(if $(filter $*.o, $(call suffix-search, $m, .o, -objs -y -m)),$(m:.o=))))
__modname = $(or $(modname-multi),$(basetarget))
modname = $(subst $(space),:,$(__modname))
modfile = $(addprefix $(obj)/,$(__modname))
# target with $(obj)/ and its suffix stripped
target-stem = $(basename $(patsubst $(obj)/%,%,$@))
# These flags are needed for modversions and compiling, so we define them here
# $(modname_flags) defines KBUILD_MODNAME as the name of the module it will
# end up in (or would, if it gets compiled in)
name-fix-token = $(subst $(comma),_,$(subst -,_,$1))
name-fix = $(call stringify,$(call name-fix-token,$1))
basename_flags = -DKBUILD_BASENAME=$(call name-fix,$(basetarget))
modname_flags = -DKBUILD_MODNAME=$(call name-fix,$(modname)) \
-D__KBUILD_MODNAME=kmod_$(call name-fix-token,$(modname))
modfile_flags = -DKBUILD_MODFILE=$(call stringify,$(modfile))
_c_flags = $(filter-out $(CFLAGS_REMOVE_$(target-stem).o), \
$(filter-out $(ccflags-remove-y), \
$(KBUILD_CPPFLAGS) $(KBUILD_CFLAGS) $(ccflags-y)) \
$(CFLAGS_$(target-stem).o))
_rust_flags = $(filter-out $(RUSTFLAGS_REMOVE_$(target-stem).o), \
$(filter-out $(rustflags-remove-y), \
$(KBUILD_RUSTFLAGS) $(rustflags-y)) \
$(RUSTFLAGS_$(target-stem).o))
_a_flags = $(filter-out $(AFLAGS_REMOVE_$(target-stem).o), \
$(filter-out $(asflags-remove-y), \
$(KBUILD_CPPFLAGS) $(KBUILD_AFLAGS) $(asflags-y)) \
$(AFLAGS_$(target-stem).o))
_cpp_flags = $(KBUILD_CPPFLAGS) $(cppflags-y) $(CPPFLAGS_$(target-stem).lds)
#
# Enable gcov profiling flags for a file, directory or for all files depending
# on variables GCOV_PROFILE_obj.o, GCOV_PROFILE and CONFIG_GCOV_PROFILE_ALL
# (in this order)
#
ifeq ($(CONFIG_GCOV_KERNEL),y)
_c_flags += $(if $(patsubst n%,, \
$(GCOV_PROFILE_$(target-stem).o)$(GCOV_PROFILE)$(if $(is-kernel-object),$(CONFIG_GCOV_PROFILE_ALL))), \
$(CFLAGS_GCOV))
endif
#
# Enable address sanitizer flags for kernel except some files or directories
# we don't want to check (depends on variables KASAN_SANITIZE_obj.o, KASAN_SANITIZE)
#
ifeq ($(CONFIG_KASAN),y)
ifneq ($(CONFIG_KASAN_HW_TAGS),y)
_c_flags += $(if $(patsubst n%,, \
$(KASAN_SANITIZE_$(target-stem).o)$(KASAN_SANITIZE)$(is-kernel-object)), \
$(CFLAGS_KASAN), $(CFLAGS_KASAN_NOSANITIZE))
_rust_flags += $(if $(patsubst n%,, \
$(KASAN_SANITIZE_$(target-stem).o)$(KASAN_SANITIZE)$(is-kernel-object)), \
$(RUSTFLAGS_KASAN))
endif
endif
ifeq ($(CONFIG_KMSAN),y)
_c_flags += $(if $(patsubst n%,, \
$(KMSAN_SANITIZE_$(target-stem).o)$(KMSAN_SANITIZE)$(is-kernel-object)), \
$(CFLAGS_KMSAN))
_c_flags += $(if $(patsubst n%,, \
$(KMSAN_ENABLE_CHECKS_$(target-stem).o)$(KMSAN_ENABLE_CHECKS)$(is-kernel-object)), \
, -mllvm -msan-disable-checks=1)
endif
ifeq ($(CONFIG_UBSAN),y)
_c_flags += $(if $(patsubst n%,, \
$(UBSAN_SANITIZE_$(target-stem).o)$(UBSAN_SANITIZE)$(is-kernel-object)), \
$(CFLAGS_UBSAN))
_c_flags += $(if $(patsubst n%,, \
$(UBSAN_INTEGER_WRAP_$(target-stem).o)$(UBSAN_SANITIZE_$(target-stem).o)$(UBSAN_INTEGER_WRAP)$(UBSAN_SANITIZE)$(is-kernel-object)), \
$(CFLAGS_UBSAN_INTEGER_WRAP))
endif
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
#
# Enable KCSAN flags except some files or directories we don't want to check
# (depends on variables KCSAN_SANITIZE_obj.o, KCSAN_SANITIZE)
#
ifeq ($(CONFIG_KCSAN),y)
_c_flags += $(if $(patsubst n%,, \
$(KCSAN_SANITIZE_$(target-stem).o)$(KCSAN_SANITIZE)$(is-kernel-object)), \
$(CFLAGS_KCSAN))
# Some uninstrumented files provide implied barriers required to avoid false
# positives: set KCSAN_INSTRUMENT_BARRIERS for barrier instrumentation only.
_c_flags += $(if $(patsubst n%,, \
$(KCSAN_INSTRUMENT_BARRIERS_$(target-stem).o)$(KCSAN_INSTRUMENT_BARRIERS)n), \
-D__KCSAN_INSTRUMENT_BARRIERS__)
endif
#
# Enable AutoFDO build flags except some files or directories we don't want to
# enable (depends on variables AUTOFDO_PROFILE_obj.o and AUTOFDO_PROFILE).
#
ifeq ($(CONFIG_AUTOFDO_CLANG),y)
_c_flags += $(if $(patsubst n%,, \
$(AUTOFDO_PROFILE_$(target-stem).o)$(AUTOFDO_PROFILE)$(is-kernel-object)), \
$(CFLAGS_AUTOFDO_CLANG))
endif
#
# Enable Propeller build flags except some files or directories we don't want to
# enable (depends on variables AUTOFDO_PROPELLER_obj.o and PROPELLER_PROFILE).
#
ifdef CONFIG_PROPELLER_CLANG
_c_flags += $(if $(patsubst n%,, \
$(AUTOFDO_PROFILE_$(target-stem).o)$(AUTOFDO_PROFILE)$(PROPELLER_PROFILE))$(is-kernel-object), \
$(CFLAGS_PROPELLER_CLANG))
endif
# $(src) for including checkin headers from generated source files
# $(obj) for including generated headers from checkin source files
ifdef building_out_of_srctree
_c_flags += $(addprefix -I, $(src) $(obj))
_a_flags += $(addprefix -I, $(src) $(obj))
_cpp_flags += $(addprefix -I, $(src) $(obj))
endif
# If $(is-kernel-object) is 'y', this object will be linked to vmlinux or modules
is-kernel-object = $(or $(part-of-builtin),$(part-of-module))
part-of-builtin = $(if $(filter $(basename $@).o, $(real-obj-y) $(lib-y)),y)
part-of-module = $(if $(filter $(basename $@).o, $(real-obj-m)),y)
quiet_modtag = $(if $(part-of-module),[M], )
modkern_cflags = \
$(if $(part-of-module), \
$(KBUILD_CFLAGS_MODULE) $(CFLAGS_MODULE), \
$(KBUILD_CFLAGS_KERNEL) $(CFLAGS_KERNEL) $(modfile_flags))
modkern_rustflags = \
$(if $(part-of-module), \
$(KBUILD_RUSTFLAGS_MODULE) $(RUSTFLAGS_MODULE), \
$(KBUILD_RUSTFLAGS_KERNEL) $(RUSTFLAGS_KERNEL))
modkern_aflags = $(if $(part-of-module), \
$(KBUILD_AFLAGS_MODULE) $(AFLAGS_MODULE), \
$(KBUILD_AFLAGS_KERNEL) $(AFLAGS_KERNEL) $(modfile_flags))
c_flags = -Wp,-MMD,$(depfile) $(NOSTDINC_FLAGS) $(LINUXINCLUDE) \
-include $(srctree)/include/linux/compiler_types.h \
$(_c_flags) $(modkern_cflags) \
$(basename_flags) $(modname_flags)
rust_flags = $(_rust_flags) $(modkern_rustflags) @$(objtree)/include/generated/rustc_cfg
a_flags = -Wp,-MMD,$(depfile) $(NOSTDINC_FLAGS) $(LINUXINCLUDE) \
$(_a_flags) $(modkern_aflags) $(modname_flags)
cpp_flags = -Wp,-MMD,$(depfile) $(NOSTDINC_FLAGS) $(LINUXINCLUDE) \
$(_cpp_flags)
ld_flags = $(KBUILD_LDFLAGS) $(ldflags-y) $(LDFLAGS_$(@F))
ifdef CONFIG_OBJTOOL
objtool := $(objtree)/tools/objtool/objtool
objtool-args-$(CONFIG_HAVE_JUMP_LABEL_HACK) += --hacks=jump_label
objtool-args-$(CONFIG_HAVE_NOINSTR_HACK) += --hacks=noinstr
objtool-args-$(CONFIG_MITIGATION_CALL_DEPTH_TRACKING) += --hacks=skylake
objtool-args-$(CONFIG_X86_KERNEL_IBT) += --ibt
objtool-args-$(CONFIG_FINEIBT) += --cfi
objtool-args-$(CONFIG_FTRACE_MCOUNT_USE_OBJTOOL) += --mcount
ifdef CONFIG_FTRACE_MCOUNT_USE_OBJTOOL
objtool-args-$(CONFIG_HAVE_OBJTOOL_NOP_MCOUNT) += --mnop
endif
objtool-args-$(CONFIG_UNWINDER_ORC) += --orc
objtool-args-$(CONFIG_MITIGATION_RETPOLINE) += --retpoline
objtool-args-$(CONFIG_MITIGATION_RETHUNK) += --rethunk
objtool-args-$(CONFIG_MITIGATION_SLS) += --sls
objtool-args-$(CONFIG_STACK_VALIDATION) += --stackval
objtool-args-$(CONFIG_HAVE_STATIC_CALL_INLINE) += --static-call
objtool-args-$(CONFIG_HAVE_UACCESS_VALIDATION) += --uaccess
objtool-args-$(or $(CONFIG_GCOV_KERNEL),$(CONFIG_KCOV)) += --no-unreachable
objtool-args-$(CONFIG_PREFIX_SYMBOLS) += --prefix=$(CONFIG_FUNCTION_PADDING_BYTES)
objtool-args-$(CONFIG_OBJTOOL_WERROR) += --Werror
objtool-args = $(objtool-args-y) \
$(if $(delay-objtool), --link) \
$(if $(part-of-module), --module)
delay-objtool := $(or $(CONFIG_LTO_CLANG),$(CONFIG_X86_KERNEL_IBT))
cmd_objtool = $(if $(objtool-enabled), ; $(objtool) $(objtool-args) $@)
cmd_gen_objtooldep = $(if $(objtool-enabled), { echo ; echo '$@: $$(wildcard $(objtool))' ; } >> $(dot-target).cmd)
objtool-enabled := y
endif # CONFIG_OBJTOOL
# Useful for describing the dependency of composite objects
# Usage:
# $(call multi_depend, multi_used_targets, suffix_to_remove, suffix_to_add)
define multi_depend
$(foreach m, $1, \
$(eval $m: \
$(addprefix $(obj)/, $(call suffix-search, $(patsubst $(obj)/%,%,$m), $2, $3))))
endef
# Remove ".." and "." from a path, without using "realpath"
# Usage:
# $(call normalize_path,path/to/../file)
define normalize_path
$(strip $(eval elements :=) \
$(foreach elem,$(subst /, ,$1), \
$(if $(filter-out .,$(elem)), \
$(if $(filter ..,$(elem)), \
$(eval elements := $(wordlist 2,$(words $(elements)),x $(elements))), \
$(eval elements := $(elements) $(elem))))) \
$(subst $(space),/,$(elements)))
endef
# Build commands
# ===========================================================================
# These are shared by some Makefile.* files.
ifdef CONFIG_LTO_CLANG
# Run $(LD) here to convert LLVM IR to ELF in the following cases:
# - when this object needs objtool processing, as objtool cannot process LLVM IR
# - when this is a single-object module, as modpost cannot process LLVM IR
cmd_ld_single = $(if $(objtool-enabled)$(is-single-obj-m), ; $(LD) $(ld_flags) -r -o $(tmp-target) $@; mv $(tmp-target) $@)
endif
quiet_cmd_cc_o_c = CC $(quiet_modtag) $@
cmd_cc_o_c = $(CC) $(c_flags) -c -o $@ $< \
$(cmd_ld_single) \
$(cmd_objtool)
define rule_cc_o_c
$(call cmd_and_fixdep,cc_o_c)
$(call cmd,checksrc)
$(call cmd,checkdoc)
$(call cmd,gen_objtooldep)
$(call cmd,gen_symversions_c)
$(call cmd,record_mcount)
$(call cmd,warn_shared_object)
endef
quiet_cmd_as_o_S = AS $(quiet_modtag) $@
cmd_as_o_S = $(CC) $(a_flags) -c -o $@ $< $(cmd_objtool)
define rule_as_o_S
$(call cmd_and_fixdep,as_o_S)
$(call cmd,gen_objtooldep)
$(call cmd,gen_symversions_S)
$(call cmd,warn_shared_object)
endef
# Copy a file
# ===========================================================================
# 'cp' preserves permissions. If you use it to copy a file in read-only srctree,
# the copy would be read-only as well, leading to an error when executing the
# rule next time. Use 'cat' instead in order to generate a writable file.
quiet_cmd_copy = COPY $@
cmd_copy = cat $< > $@
$(obj)/%: $(src)/%_shipped
$(call cmd,copy)
# Touch a file
# ===========================================================================
quiet_cmd_touch = TOUCH $(call normalize_path,$@)
cmd_touch = touch $@
# Commands useful for building a boot image
# ===========================================================================
#
# Use as following:
#
# target: source(s) FORCE
# $(if_changed,ld/objcopy/gzip)
#
# and add target to 'targets' so that we know we have to
# read in the saved command line
# Linking
# ---------------------------------------------------------------------------
quiet_cmd_ld = LD $@
cmd_ld = $(LD) $(ld_flags) $(real-prereqs) -o $@
# Archive
# ---------------------------------------------------------------------------
quiet_cmd_ar = AR $@
cmd_ar = rm -f $@; $(AR) cDPrsT $@ $(real-prereqs)
# Objcopy
# ---------------------------------------------------------------------------
quiet_cmd_objcopy = OBJCOPY $@
cmd_objcopy = $(OBJCOPY) $(OBJCOPYFLAGS) $(OBJCOPYFLAGS_$(@F)) $< $@
# Gzip
# ---------------------------------------------------------------------------
quiet_cmd_gzip = GZIP $@
cmd_gzip = cat $(real-prereqs) | $(KGZIP) -n -f -9 > $@
# Bzip2
# ---------------------------------------------------------------------------
# Bzip2 and LZMA do not include size in file... so we have to fake that;
# append the size as a 32-bit littleendian number as gzip does.
size_append = printf $(shell \
dec_size=0; \
for F in $(real-prereqs); do \
fsize=$$($(CONFIG_SHELL) $(srctree)/scripts/file-size.sh $$F); \
dec_size=$$(expr $$dec_size + $$fsize); \
done; \
printf "%08x\n" $$dec_size | \
sed 's/\(..\)/\1 /g' | { \
read ch0 ch1 ch2 ch3; \
for ch in $$ch3 $$ch2 $$ch1 $$ch0; do \
printf '%s%03o' '\\' $$((0x$$ch)); \
done; \
} \
)
quiet_cmd_file_size = GEN $@
cmd_file_size = $(size_append) > $@
quiet_cmd_bzip2 = BZIP2 $@
cmd_bzip2 = cat $(real-prereqs) | $(KBZIP2) -9 > $@
quiet_cmd_bzip2_with_size = BZIP2 $@
cmd_bzip2_with_size = { cat $(real-prereqs) | $(KBZIP2) -9; $(size_append); } > $@
# Lzma
# ---------------------------------------------------------------------------
quiet_cmd_lzma = LZMA $@
cmd_lzma = cat $(real-prereqs) | $(LZMA) -9 > $@
quiet_cmd_lzma_with_size = LZMA $@
cmd_lzma_with_size = { cat $(real-prereqs) | $(LZMA) -9; $(size_append); } > $@
quiet_cmd_lzo = LZO $@
cmd_lzo = cat $(real-prereqs) | $(KLZOP) -9 > $@
quiet_cmd_lzo_with_size = LZO $@
cmd_lzo_with_size = { cat $(real-prereqs) | $(KLZOP) -9; $(size_append); } > $@
quiet_cmd_lz4 = LZ4 $@
cmd_lz4 = cat $(real-prereqs) | $(LZ4) -l -9 - - > $@
quiet_cmd_lz4_with_size = LZ4 $@
cmd_lz4_with_size = { cat $(real-prereqs) | $(LZ4) -l -9 - -; \
$(size_append); } > $@
# U-Boot mkimage
# ---------------------------------------------------------------------------
MKIMAGE := $(srctree)/scripts/mkuboot.sh
# SRCARCH just happens to match slightly more than ARCH (on sparc), so reduces
# the number of overrides in arch makefiles
UIMAGE_ARCH ?= $(SRCARCH)
UIMAGE_COMPRESSION ?= $(or $(2),none)
UIMAGE_OPTS-y ?=
UIMAGE_TYPE ?= kernel
UIMAGE_LOADADDR ?= arch_must_set_this
UIMAGE_ENTRYADDR ?= $(UIMAGE_LOADADDR)
UIMAGE_NAME ?= Linux-$(KERNELRELEASE)
quiet_cmd_uimage = UIMAGE $@
cmd_uimage = $(BASH) $(MKIMAGE) -A $(UIMAGE_ARCH) -O linux \
-C $(UIMAGE_COMPRESSION) $(UIMAGE_OPTS-y) \
-T $(UIMAGE_TYPE) \
-a $(UIMAGE_LOADADDR) -e $(UIMAGE_ENTRYADDR) \
-n '$(UIMAGE_NAME)' -d $< $@
# Flat Image Tree (FIT)
# This allows for packaging of a kernel and all devicetrees files, using
# compression.
# ---------------------------------------------------------------------------
MAKE_FIT := $(srctree)/scripts/make_fit.py
# Use this to override the compression algorithm
FIT_COMPRESSION ?= gzip
quiet_cmd_fit = FIT $@
cmd_fit = $(MAKE_FIT) -o $@ --arch $(UIMAGE_ARCH) --os linux \
--name '$(UIMAGE_NAME)' \
$(if $(findstring 1,$(KBUILD_VERBOSE)),-v) \
$(if $(FIT_DECOMPOSE_DTBS),--decompose-dtbs) \
--compress $(FIT_COMPRESSION) -k $< @$(word 2,$^)
# XZ
# ---------------------------------------------------------------------------
# Use xzkern or xzkern_with_size to compress the kernel image and xzmisc to
# compress other things.
#
# xzkern uses a big LZMA2 dictionary since it doesn't increase memory usage
# of the kernel decompressor. A BCJ filter is used if it is available for
# the target architecture.
#
# xzkern_with_size also appends uncompressed size of the data using
# size_append. The .xz format has the size information available at the end
# of the file too, but it's in more complex format and it's good to avoid
# changing the part of the boot code that reads the uncompressed size.
# Note that the bytes added by size_append will make the xz tool think that
# the file is corrupt. This is expected.
#
# xzmisc doesn't use size_append, so it can be used to create normal .xz
# files. xzmisc uses smaller LZMA2 dictionary than xzkern, because a very
# big dictionary would increase the memory usage too much in the multi-call
# decompression mode. A BCJ filter isn't used either.
quiet_cmd_xzkern = XZKERN $@
cmd_xzkern = cat $(real-prereqs) | sh $(srctree)/scripts/xz_wrap.sh > $@
quiet_cmd_xzkern_with_size = XZKERN $@
cmd_xzkern_with_size = { cat $(real-prereqs) | sh $(srctree)/scripts/xz_wrap.sh; \
$(size_append); } > $@
quiet_cmd_xzmisc = XZMISC $@
cmd_xzmisc = cat $(real-prereqs) | $(XZ) --check=crc32 --lzma2=dict=1MiB > $@
# ZSTD
# ---------------------------------------------------------------------------
# Appends the uncompressed size of the data using size_append. The .zst
# format has the size information available at the beginning of the file too,
# but it's in a more complex format and it's good to avoid changing the part
# of the boot code that reads the uncompressed size.
#
# Note that the bytes added by size_append will make the zstd tool think that
# the file is corrupt. This is expected.
#
# zstd uses a maximum window size of 8 MB. zstd22 uses a maximum window size of
# 128 MB. zstd22 is used for kernel compression because it is decompressed in a
# single pass, so zstd doesn't need to allocate a window buffer. When streaming
# decompression is used, like initramfs decompression, zstd22 should likely not
# be used because it would require zstd to allocate a 128 MB buffer.
quiet_cmd_zstd = ZSTD $@
cmd_zstd = cat $(real-prereqs) | $(ZSTD) -19 > $@
quiet_cmd_zstd22 = ZSTD22 $@
cmd_zstd22 = cat $(real-prereqs) | $(ZSTD) -22 --ultra > $@
quiet_cmd_zstd22_with_size = ZSTD22 $@
cmd_zstd22_with_size = { cat $(real-prereqs) | $(ZSTD) -22 --ultra; $(size_append); } > $@
# ASM offsets
# ---------------------------------------------------------------------------
# Default sed regexp - multiline due to syntax constraints
#
# Use [:space:] because LLVM's integrated assembler inserts <tab> around
# the .ascii directive whereas GCC keeps the <space> as-is.
define sed-offsets
's:^[[:space:]]*\.ascii[[:space:]]*"\(.*\)".*:\1:; \
/^->/{s:->#\(.*\):/* \1 */:; \
s:^->\([^ ]*\) [\$$#]*\([^ ]*\) \(.*\):#define \1 \2 /* \3 */:; \
s:->::; p;}'
endef
# Use filechk to avoid rebuilds when a header changes, but the resulting file
# does not
define filechk_offsets
echo "#ifndef $2"; \
echo "#define $2"; \
echo "/*"; \
echo " * DO NOT MODIFY."; \
echo " *"; \
echo " * This file was generated by Kbuild"; \
echo " */"; \
echo ""; \
sed -ne $(sed-offsets) < $<; \
echo ""; \
echo "#endif"
endef

View File

@@ -0,0 +1,167 @@
# SPDX-License-Identifier: GPL-2.0
# ==========================================================================
# Installing modules
# ==========================================================================
PHONY := __modinst
__modinst:
include $(objtree)/include/config/auto.conf
include $(srctree)/scripts/Kbuild.include
install-y :=
ifeq ($(KBUILD_EXTMOD)$(sign-only),)
# remove the old directory and symlink
$(shell rm -fr $(MODLIB)/kernel $(MODLIB)/build)
install-$(CONFIG_MODULES) += $(addprefix $(MODLIB)/, build modules.order)
$(MODLIB)/build: FORCE
$(call cmd,symlink)
quiet_cmd_symlink = SYMLINK $@
cmd_symlink = ln -s $(CURDIR) $@
$(MODLIB)/modules.order: modules.order FORCE
$(call cmd,install_modorder)
quiet_cmd_install_modorder = INSTALL $@
cmd_install_modorder = sed 's:^\(.*\)\.o$$:kernel/\1.ko:' $< > $@
# Install modules.builtin(.modinfo,.ranges) even when CONFIG_MODULES is disabled.
install-y += $(addprefix $(MODLIB)/, modules.builtin modules.builtin.modinfo)
install-$(CONFIG_BUILTIN_MODULE_RANGES) += $(MODLIB)/modules.builtin.ranges
$(addprefix $(MODLIB)/, modules.builtin modules.builtin.modinfo modules.builtin.ranges): $(MODLIB)/%: % FORCE
$(call cmd,install)
endif
modules := $(call read-file, modules.order)
ifeq ($(KBUILD_EXTMOD),)
dst := $(MODLIB)/kernel
else
INSTALL_MOD_DIR ?= updates
dst := $(MODLIB)/$(INSTALL_MOD_DIR)
endif
$(foreach x, % :, $(if $(findstring $x, $(dst)), \
$(error module installation path cannot contain '$x')))
suffix-y :=
ifdef CONFIG_MODULE_COMPRESS_ALL
suffix-$(CONFIG_MODULE_COMPRESS_GZIP) := .gz
suffix-$(CONFIG_MODULE_COMPRESS_XZ) := .xz
suffix-$(CONFIG_MODULE_COMPRESS_ZSTD) := .zst
endif
modules := $(patsubst %.o, $(dst)/%.ko$(suffix-y), $(modules))
install-$(CONFIG_MODULES) += $(modules)
__modinst: $(install-y)
@:
#
# Installation
#
quiet_cmd_install = INSTALL $@
cmd_install = cp $< $@
# Strip
#
# INSTALL_MOD_STRIP, if defined, will cause modules to be stripped after they
# are installed. If INSTALL_MOD_STRIP is '1', then the default option
# --strip-debug will be used. Otherwise, INSTALL_MOD_STRIP value will be used
# as the options to the strip command.
ifdef INSTALL_MOD_STRIP
ifeq ($(INSTALL_MOD_STRIP),1)
strip-option := --strip-debug
else
strip-option := $(INSTALL_MOD_STRIP)
endif
quiet_cmd_strip = STRIP $@
cmd_strip = $(STRIP) $(strip-option) $@
else
quiet_cmd_strip =
cmd_strip = :
endif
#
# Signing
# Don't stop modules_install even if we can't sign external modules.
#
ifeq ($(filter pkcs11:%, $(CONFIG_MODULE_SIG_KEY)),)
sig-key := $(if $(wildcard $(CONFIG_MODULE_SIG_KEY)),,$(srctree)/)$(CONFIG_MODULE_SIG_KEY)
else
sig-key := $(CONFIG_MODULE_SIG_KEY)
endif
quiet_cmd_sign = SIGN $@
cmd_sign = $(objtree)/scripts/sign-file $(CONFIG_MODULE_SIG_HASH) "$(sig-key)" $(objtree)/certs/signing_key.x509 $@ \
$(if $(KBUILD_EXTMOD),|| true)
ifeq ($(sign-only),)
# During modules_install, modules are signed only when CONFIG_MODULE_SIG_ALL=y.
ifndef CONFIG_MODULE_SIG_ALL
quiet_cmd_sign :=
cmd_sign := :
endif
# Create necessary directories
$(foreach dir, $(sort $(dir $(install-y))), $(shell mkdir -p $(dir)))
$(dst)/%.ko: %.ko FORCE
$(call cmd,install)
$(call cmd,strip)
$(call cmd,sign)
ifdef CONFIG_MODULES
__modinst: depmod
PHONY += depmod
depmod: $(install-y)
$(call cmd,depmod)
quiet_cmd_depmod = DEPMOD $(MODLIB)
cmd_depmod = $(srctree)/scripts/depmod.sh $(KERNELRELEASE)
endif
else
$(dst)/%.ko: FORCE
$(call cmd,sign)
endif
#
# Compression
#
quiet_cmd_gzip = GZIP $@
cmd_gzip = $(KGZIP) -n -f $<
quiet_cmd_xz = XZ $@
cmd_xz = $(XZ) --check=crc32 --lzma2=dict=1MiB -f $<
quiet_cmd_zstd = ZSTD $@
cmd_zstd = $(ZSTD) --rm -f -q $<
$(dst)/%.ko.gz: $(dst)/%.ko FORCE
$(call cmd,gzip)
$(dst)/%.ko.xz: $(dst)/%.ko FORCE
$(call cmd,xz)
$(dst)/%.ko.zst: $(dst)/%.ko FORCE
$(call cmd,zstd)
PHONY += FORCE
FORCE:
.PHONY: $(PHONY)

View File

@@ -0,0 +1,157 @@
# SPDX-License-Identifier: GPL-2.0
# ===========================================================================
# Module versions
# ===========================================================================
#
# Stage one of module building created the following:
# a) The individual .o files used for the module
# b) A <module>.o file which is the .o files above linked together
# c) A <module>.mod file, listing the name of the preliminary <module>.o file,
# plus all .o files
# d) modules.order, which lists all the modules
# Stage 2 is handled by this file and does the following
# 1) Find all modules listed in modules.order
# 2) modpost is then used to
# 3) create one <module>.mod.c file per module
# 4) create one Module.symvers file with CRC for all exported symbols
# Step 3 is used to place certain information in the module's ELF
# section, including information such as:
# Version magic (see include/linux/vermagic.h for full details)
# - Kernel release
# - SMP is CONFIG_SMP
# - PREEMPT is CONFIG_PREEMPT[_RT]
# - GCC Version
# Module info
# - Module version (MODULE_VERSION)
# - Module alias'es (MODULE_ALIAS)
# - Module license (MODULE_LICENSE)
# - See include/linux/module.h for more details
# Step 4 is solely used to allow module versioning in external modules,
# where the CRC of each module is retrieved from the Module.symvers file.
PHONY := __modpost
__modpost:
include $(objtree)/include/config/auto.conf
include $(srctree)/scripts/Kbuild.include
MODPOST = $(objtree)/scripts/mod/modpost
modpost-args = \
$(if $(CONFIG_MODULES),-M) \
$(if $(CONFIG_MODVERSIONS),-m) \
$(if $(CONFIG_BASIC_MODVERSIONS),-b) \
$(if $(CONFIG_EXTENDED_MODVERSIONS),-x) \
$(if $(CONFIG_MODULE_SRCVERSION_ALL),-a) \
$(if $(CONFIG_SECTION_MISMATCH_WARN_ONLY),,-E) \
$(if $(KBUILD_MODPOST_WARN),-w) \
$(if $(KBUILD_NSDEPS),-d modules.nsdeps) \
$(if $(CONFIG_MODULE_ALLOW_MISSING_NAMESPACE_IMPORTS)$(KBUILD_NSDEPS),-N) \
$(if $(findstring 1, $(KBUILD_EXTRA_WARN)),-W) \
-o $@
modpost-deps := $(MODPOST)
# 'make -i -k' ignores compile errors, and builds as many modules as possible.
ifneq ($(findstring i,$(filter-out --%,$(MAKEFLAGS))),)
modpost-args += -n
endif
# Read out modules.order to pass in modpost.
# Otherwise, allmodconfig would fail with "Argument list too long".
ifdef KBUILD_MODULES
modpost-args += -T modules.order
modpost-deps += modules.order
endif
ifeq ($(KBUILD_EXTMOD),)
# Generate the list of in-tree objects in vmlinux
# ---------------------------------------------------------------------------
# This is used to retrieve symbol versions generated by genksyms.
ifdef CONFIG_MODVERSIONS
vmlinux.symvers Module.symvers: .vmlinux.objs
endif
# Ignore libgcc.a
# Some architectures do '$(CC) --print-libgcc-file-name' to borrow libgcc.a
# from the toolchain, but there is no EXPORT_SYMBOL in it.
quiet_cmd_vmlinux_objs = GEN $@
cmd_vmlinux_objs = \
for f in $(real-prereqs); do \
case $${f} in \
*libgcc.a) ;; \
*) $(AR) t $${f} ;; \
esac \
done > $@
targets += .vmlinux.objs
.vmlinux.objs: vmlinux.a $(KBUILD_VMLINUX_LIBS) FORCE
$(call if_changed,vmlinux_objs)
ifdef CONFIG_TRIM_UNUSED_KSYMS
ksym-wl := $(CONFIG_UNUSED_KSYMS_WHITELIST)
ksym-wl := $(if $(filter-out /%, $(ksym-wl)),$(if $(wildcard $(ksym-wl)),,$(srctree)/))$(ksym-wl)
modpost-args += -t $(addprefix -u , $(ksym-wl))
modpost-deps += $(ksym-wl)
endif
ifeq ($(wildcard vmlinux.o),)
missing-input := vmlinux.o
output-symdump := modules-only.symvers
else
modpost-args += vmlinux.o
modpost-deps += vmlinux.o
output-symdump := $(if $(KBUILD_MODULES), Module.symvers, vmlinux.symvers)
endif
else
# set src + obj - they may be used in the modules's Makefile
obj := .
src := $(srcroot)
# Include the module's Makefile to find KBUILD_EXTRA_SYMBOLS
include $(kbuild-file)
output-symdump := Module.symvers
ifeq ($(wildcard $(objtree)/Module.symvers),)
missing-input := $(objtree)/Module.symvers
else
modpost-args += -i $(objtree)/Module.symvers
modpost-deps += $(objtree)/Module.symvers
endif
modpost-args += -e $(addprefix -i , $(KBUILD_EXTRA_SYMBOLS))
endif # ($(KBUILD_EXTMOD),)
quiet_cmd_modpost = MODPOST $@
cmd_modpost = \
$(if $(missing-input), \
echo >&2 "WARNING: $(missing-input) is missing."; \
echo >&2 " Modules may not have dependencies or modversions."; \
echo >&2 " You may get many unresolved symbol errors."; \
echo >&2 " You can set KBUILD_MODPOST_WARN=1 to turn errors into warning"; \
echo >&2 " if you want to proceed at your own risk.";) \
$(MODPOST) $(modpost-args)
targets += $(output-symdump)
$(output-symdump): $(modpost-deps) FORCE
$(call if_changed,modpost)
__modpost: $(output-symdump)
PHONY += FORCE
FORCE:
existing-targets := $(wildcard $(sort $(targets)))
-include $(foreach f,$(existing-targets),$(dir $(f)).$(notdir $(f)).cmd)
.PHONY: $(PHONY)

View File

@@ -0,0 +1,3 @@
# SPDX-License-Identifier: GPL-2.0-only
/fixdep
/randstruct.seed

View File

@@ -0,0 +1,21 @@
# SPDX-License-Identifier: GPL-2.0-only
#
# fixdep: used to generate dependency information during build process
hostprogs-always-y += fixdep
# randstruct: the seed is needed before building the gcc-plugin or
# before running a Clang kernel build.
gen-randstruct-seed := $(srctree)/scripts/gen-randstruct-seed.sh
quiet_cmd_create_randstruct_seed = GENSEED $@
cmd_create_randstruct_seed = \
$(CONFIG_SHELL) $(gen-randstruct-seed) \
$@ $(objtree)/include/generated/randstruct_hash.h
$(obj)/randstruct.seed: $(gen-randstruct-seed) FORCE
$(call if_changed,create_randstruct_seed)
always-$(CONFIG_RANDSTRUCT) += randstruct.seed
# integer-wrap: if the .scl file changes, we need to do a full rebuild.
$(obj)/../../include/generated/integer-wrap.h: $(srctree)/scripts/integer-wrap-ignore.scl FORCE
$(call if_changed,touch)
always-$(CONFIG_UBSAN_INTEGER_WRAP) += ../../include/generated/integer-wrap.h

View File

@@ -0,0 +1,440 @@
/*
* "Optimize" a list of dependencies as spit out by gcc -MD
* for the kernel build
* ===========================================================================
*
* Author Kai Germaschewski
* Copyright 2002 by Kai Germaschewski <kai.germaschewski@gmx.de>
*
* This software may be used and distributed according to the terms
* of the GNU General Public License, incorporated herein by reference.
*
*
* Introduction:
*
* gcc produces a very nice and correct list of dependencies which
* tells make when to remake a file.
*
* To use this list as-is however has the drawback that virtually
* every file in the kernel includes autoconf.h.
*
* If the user re-runs make *config, autoconf.h will be
* regenerated. make notices that and will rebuild every file which
* includes autoconf.h, i.e. basically all files. This is extremely
* annoying if the user just changed CONFIG_HIS_DRIVER from n to m.
*
* So we play the same trick that "mkdep" played before. We replace
* the dependency on autoconf.h by a dependency on every config
* option which is mentioned in any of the listed prerequisites.
*
* kconfig populates a tree in include/config/ with an empty file
* for each config symbol and when the configuration is updated
* the files representing changed config options are touched
* which then let make pick up the changes and the files that use
* the config symbols are rebuilt.
*
* So if the user changes his CONFIG_HIS_DRIVER option, only the objects
* which depend on "include/config/HIS_DRIVER" will be rebuilt,
* so most likely only his driver ;-)
*
* The idea above dates, by the way, back to Michael E Chastain, AFAIK.
*
* So to get dependencies right, there are two issues:
* o if any of the files the compiler read changed, we need to rebuild
* o if the command line given to the compile the file changed, we
* better rebuild as well.
*
* The former is handled by using the -MD output, the later by saving
* the command line used to compile the old object and comparing it
* to the one we would now use.
*
* Again, also this idea is pretty old and has been discussed on
* kbuild-devel a long time ago. I don't have a sensibly working
* internet connection right now, so I rather don't mention names
* without double checking.
*
* This code here has been based partially based on mkdep.c, which
* says the following about its history:
*
* Copyright abandoned, Michael Chastain, <mailto:mec@shout.net>.
* This is a C version of syncdep.pl by Werner Almesberger.
*
*
* It is invoked as
*
* fixdep <depfile> <target> <cmdline>
*
* and will read the dependency file <depfile>
*
* The transformed dependency snipped is written to stdout.
*
* It first generates a line
*
* savedcmd_<target> = <cmdline>
*
* and then basically copies the .<target>.d file to stdout, in the
* process filtering out the dependency on autoconf.h and adding
* dependencies on include/config/MY_OPTION for every
* CONFIG_MY_OPTION encountered in any of the prerequisites.
*
* We don't even try to really parse the header files, but
* merely grep, i.e. if CONFIG_FOO is mentioned in a comment, it will
* be picked up as well. It's not a problem with respect to
* correctness, since that can only give too many dependencies, thus
* we cannot miss a rebuild. Since people tend to not mention totally
* unrelated CONFIG_ options all over the place, it's not an
* efficiency problem either.
*
* (Note: it'd be easy to port over the complete mkdep state machine,
* but I don't think the added complexity is worth it)
*/
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <stdbool.h>
#include <stdlib.h>
#include <stdio.h>
#include <ctype.h>
#include <xalloc.h>
static void usage(void)
{
fprintf(stderr, "Usage: fixdep <depfile> <target> <cmdline>\n");
exit(1);
}
struct item {
struct item *next;
unsigned int len;
unsigned int hash;
char name[];
};
#define HASHSZ 256
static struct item *config_hashtab[HASHSZ], *file_hashtab[HASHSZ];
static unsigned int strhash(const char *str, unsigned int sz)
{
/* fnv32 hash */
unsigned int i, hash = 2166136261U;
for (i = 0; i < sz; i++)
hash = (hash ^ str[i]) * 0x01000193;
return hash;
}
/*
* Add a new value to the configuration string.
*/
static void add_to_hashtable(const char *name, int len, unsigned int hash,
struct item *hashtab[])
{
struct item *aux;
aux = xmalloc(sizeof(*aux) + len);
memcpy(aux->name, name, len);
aux->len = len;
aux->hash = hash;
aux->next = hashtab[hash % HASHSZ];
hashtab[hash % HASHSZ] = aux;
}
/*
* Lookup a string in the hash table. If found, just return true.
* If not, add it to the hashtable and return false.
*/
static bool in_hashtable(const char *name, int len, struct item *hashtab[])
{
struct item *aux;
unsigned int hash = strhash(name, len);
for (aux = hashtab[hash % HASHSZ]; aux; aux = aux->next) {
if (aux->hash == hash && aux->len == len &&
memcmp(aux->name, name, len) == 0)
return true;
}
add_to_hashtable(name, len, hash, hashtab);
return false;
}
/*
* Record the use of a CONFIG_* word.
*/
static void use_config(const char *m, int slen)
{
if (in_hashtable(m, slen, config_hashtab))
return;
/* Print out a dependency path from a symbol name. */
printf(" $(wildcard $(OBJ_BASE)/include/config/%.*s) \\\n", slen, m);
}
/* test if s ends in sub */
static int str_ends_with(const char *s, int slen, const char *sub)
{
int sublen = strlen(sub);
if (sublen > slen)
return 0;
return !memcmp(s + slen - sublen, sub, sublen);
}
static void parse_config_file(const char *p)
{
const char *q, *r;
const char *start = p;
while ((p = strstr(p, "CONFIG_"))) {
if (p > start && (isalnum(p[-1]) || p[-1] == '_')) {
p += 7;
continue;
}
p += 7;
q = p;
while (isalnum(*q) || *q == '_')
q++;
if (str_ends_with(p, q - p, "_MODULE"))
r = q - 7;
else
r = q;
if (r > p)
use_config(p, r - p);
p = q;
}
}
static void *read_file(const char *filename)
{
struct stat st;
int fd;
char *buf;
fd = open(filename, O_RDONLY);
if (fd < 0) {
fprintf(stderr, "fixdep: error opening file: ");
perror(filename);
exit(2);
}
if (fstat(fd, &st) < 0) {
fprintf(stderr, "fixdep: error fstat'ing file: ");
perror(filename);
exit(2);
}
buf = xmalloc(st.st_size + 1);
if (read(fd, buf, st.st_size) != st.st_size) {
perror("fixdep: read");
exit(2);
}
buf[st.st_size] = '\0';
close(fd);
return buf;
}
/* Ignore certain dependencies */
static int is_ignored_file(const char *s, int len)
{
return str_ends_with(s, len, "include/generated/autoconf.h");
}
/* Do not parse these files */
static int is_no_parse_file(const char *s, int len)
{
/* rustc may list binary files in dep-info */
return str_ends_with(s, len, ".rlib") ||
str_ends_with(s, len, ".rmeta") ||
str_ends_with(s, len, ".so");
}
/*
* Important: The below generated source_foo.o and deps_foo.o variable
* assignments are parsed not only by make, but also by the rather simple
* parser in scripts/mod/sumversion.c.
*/
static void parse_dep_file(char *p, const char *target)
{
bool saw_any_target = false;
bool is_target = true;
bool is_source = false;
bool need_parse;
char *q, saved_c;
while (*p) {
/* handle some special characters first. */
switch (*p) {
case '#':
/*
* skip comments.
* rustc may emit comments to dep-info.
*/
p++;
while (*p != '\0' && *p != '\n') {
/*
* escaped newlines continue the comment across
* multiple lines.
*/
if (*p == '\\')
p++;
p++;
}
continue;
case ' ':
case '\t':
/* skip whitespaces */
p++;
continue;
case '\\':
/*
* backslash/newline combinations continue the
* statement. Skip it just like a whitespace.
*/
if (*(p + 1) == '\n') {
p += 2;
continue;
}
break;
case '\n':
/*
* Makefiles use a line-based syntax, where the newline
* is the end of a statement. After seeing a newline,
* we expect the next token is a target.
*/
p++;
is_target = true;
continue;
case ':':
/*
* assume the first dependency after a colon as the
* source file.
*/
p++;
is_target = false;
is_source = true;
continue;
}
/* find the end of the token */
q = p;
while (*q != ' ' && *q != '\t' && *q != '\n' && *q != '#' && *q != ':') {
if (*q == '\\') {
/*
* backslash/newline combinations work like as
* a whitespace, so this is the end of token.
*/
if (*(q + 1) == '\n')
break;
/* escaped special characters */
if (*(q + 1) == '#' || *(q + 1) == ':') {
memmove(p + 1, p, q - p);
p++;
}
q++;
}
if (*q == '\0')
break;
q++;
}
/* Just discard the target */
if (is_target) {
p = q;
continue;
}
saved_c = *q;
*q = '\0';
need_parse = false;
/*
* Do not list the source file as dependency, so that kbuild is
* not confused if a .c file is rewritten into .S or vice versa.
* Storing it in source_* is needed for modpost to compute
* srcversions.
*/
if (is_source) {
/*
* The DT build rule concatenates multiple dep files.
* When processing them, only process the first source
* name, which will be the original one, and ignore any
* other source names, which will be intermediate
* temporary files.
*
* rustc emits the same dependency list for each
* emission type. It is enough to list the source name
* just once.
*/
if (!saw_any_target) {
saw_any_target = true;
printf("source_%s := %s\n\n", target, p);
printf("deps_%s := \\\n", target);
need_parse = true;
}
} else if (!is_ignored_file(p, q - p) &&
!in_hashtable(p, q - p, file_hashtab)) {
printf(" %s \\\n", p);
need_parse = true;
}
if (need_parse && !is_no_parse_file(p, q - p)) {
void *buf;
buf = read_file(p);
parse_config_file(buf);
free(buf);
}
is_source = false;
*q = saved_c;
p = q;
}
if (!saw_any_target) {
fprintf(stderr, "fixdep: parse error; no targets found\n");
exit(1);
}
printf("\n%s: $(deps_%s)\n\n", target, target);
printf("$(deps_%s):\n", target);
}
int main(int argc, char *argv[])
{
const char *depfile, *target, *cmdline;
void *buf;
if (argc != 4)
usage();
depfile = argv[1];
target = argv[2];
cmdline = argv[3];
printf("savedcmd_%s := %s\n\n", target, cmdline);
buf = read_file(depfile);
parse_dep_file(buf, target);
free(buf);
fflush(stdout);
/*
* In the intended usage, the stdout is redirected to .*.cmd files.
* Call ferror() to catch errors such as "No space left on device".
*/
if (ferror(stdout)) {
fprintf(stderr, "fixdep: not all data was written to the output\n");
exit(1);
}
return 0;
}

View File

@@ -0,0 +1,236 @@
#!/usr/bin/env bash
# SPDX-License-Identifier: GPL-2.0
# Manipulate options in a .config file from the command line
myname=${0##*/}
# If no prefix forced, use the default CONFIG_
CONFIG_="${CONFIG_-CONFIG_}"
# We use an uncommon delimiter for sed substitutions
SED_DELIM=$(echo -en "\001")
usage() {
cat >&2 <<EOL
Manipulate options in a .config file from the command line.
Usage:
$myname options command ...
commands:
--enable|-e option Enable option
--disable|-d option Disable option
--module|-m option Turn option into a module
--set-str option string
Set option to "string"
--set-val option value
Set option to value
--undefine|-u option Undefine option
--state|-s option Print state of option (n,y,m,undef)
--enable-after|-E beforeopt option
Enable option directly after other option
--disable-after|-D beforeopt option
Disable option directly after other option
--module-after|-M beforeopt option
Turn option into module directly after other option
--refresh Refresh the config using old settings
commands can be repeated multiple times
options:
--file config-file .config file to change (default .config)
--keep-case|-k Keep next symbols' case (dont' upper-case it)
$myname doesn't check the validity of the .config file. This is done at next
make time.
By default, $myname will upper-case the given symbol. Use --keep-case to keep
the case of all following symbols unchanged.
$myname uses 'CONFIG_' as the default symbol prefix. Set the environment
variable CONFIG_ to the prefix to use. Eg.: CONFIG_="FOO_" $myname ...
EOL
exit 1
}
checkarg() {
ARG="$1"
if [ "$ARG" = "" ] ; then
usage
fi
case "$ARG" in
${CONFIG_}*)
ARG="${ARG/${CONFIG_}/}"
;;
esac
if [ "$MUNGE_CASE" = "yes" ] ; then
ARG="`echo $ARG | tr a-z A-Z`"
fi
}
txt_append() {
local anchor="$1"
local insert="$2"
local infile="$3"
local tmpfile="$infile.swp"
# sed append cmd: 'a\' + newline + text + newline
cmd="$(printf "a\\%b$insert" "\n")"
sed -e "/$anchor/$cmd" "$infile" >"$tmpfile"
# replace original file with the edited one
mv "$tmpfile" "$infile"
}
txt_subst() {
local before="$1"
local after="$2"
local infile="$3"
local tmpfile="$infile.swp"
sed -e "s$SED_DELIM$before$SED_DELIM$after$SED_DELIM" "$infile" >"$tmpfile"
# replace original file with the edited one
mv "$tmpfile" "$infile"
}
txt_delete() {
local text="$1"
local infile="$2"
local tmpfile="$infile.swp"
sed -e "/$text/d" "$infile" >"$tmpfile"
# replace original file with the edited one
mv "$tmpfile" "$infile"
}
set_var() {
local name=$1 new=$2 before=$3
name_re="^($name=|# $name is not set)"
before_re="^($before=|# $before is not set)"
if test -n "$before" && grep -Eq "$before_re" "$FN"; then
txt_append "^$before=" "$new" "$FN"
txt_append "^# $before is not set" "$new" "$FN"
elif grep -Eq "$name_re" "$FN"; then
txt_subst "^$name=.*" "$new" "$FN"
txt_subst "^# $name is not set" "$new" "$FN"
else
echo "$new" >>"$FN"
fi
}
undef_var() {
local name=$1
txt_delete "^$name=" "$FN"
txt_delete "^# $name is not set" "$FN"
}
FN=.config
CMDS=()
while [[ $# -gt 0 ]]; do
if [ "$1" = "--file" ]; then
if [ "$2" = "" ]; then
usage
fi
FN="$2"
shift 2
else
CMDS+=("$1")
shift
fi
done
set -- "${CMDS[@]}"
if [ "$1" = "" ] ; then
usage
fi
MUNGE_CASE=yes
while [ "$1" != "" ] ; do
CMD="$1"
shift
case "$CMD" in
--keep-case|-k)
MUNGE_CASE=no
continue
;;
--refresh)
;;
--*-after|-E|-D|-M)
checkarg "$1"
A=$ARG
checkarg "$2"
B=$ARG
shift 2
;;
-*)
checkarg "$1"
shift
;;
esac
case "$CMD" in
--enable|-e)
set_var "${CONFIG_}$ARG" "${CONFIG_}$ARG=y"
;;
--disable|-d)
set_var "${CONFIG_}$ARG" "# ${CONFIG_}$ARG is not set"
;;
--module|-m)
set_var "${CONFIG_}$ARG" "${CONFIG_}$ARG=m"
;;
--set-str)
# sed swallows one level of escaping, so we need double-escaping
set_var "${CONFIG_}$ARG" "${CONFIG_}$ARG=\"${1//\"/\\\\\"}\""
shift
;;
--set-val)
set_var "${CONFIG_}$ARG" "${CONFIG_}$ARG=$1"
shift
;;
--undefine|-u)
undef_var "${CONFIG_}$ARG"
;;
--state|-s)
if grep -q "# ${CONFIG_}$ARG is not set" $FN ; then
echo n
else
V="$(grep "^${CONFIG_}$ARG=" $FN)"
if [ $? != 0 ] ; then
echo undef
else
V="${V/#${CONFIG_}$ARG=/}"
V="${V/#\"/}"
V="${V/%\"/}"
V="${V//\\\"/\"}"
echo "${V}"
fi
fi
;;
--enable-after|-E)
set_var "${CONFIG_}$B" "${CONFIG_}$B=y" "${CONFIG_}$A"
;;
--disable-after|-D)
set_var "${CONFIG_}$B" "# ${CONFIG_}$B is not set" "${CONFIG_}$A"
;;
--module-after|-M)
set_var "${CONFIG_}$B" "${CONFIG_}$B=m" "${CONFIG_}$A"
;;
--refresh)
yes "" | make oldconfig KCONFIG_CONFIG=$FN
;;
*)
echo "bad command: $CMD" >&2
usage
;;
esac
done

View File

@@ -0,0 +1,11 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef ARRAY_SIZE_H
#define ARRAY_SIZE_H
/**
* ARRAY_SIZE - get the number of elements in array @arr
* @arr: array to be sized
*/
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]))
#endif /* ARRAY_SIZE_H */

View File

@@ -0,0 +1,28 @@
/* SPDX-License-Identifier: GPL-2.0-only */
#ifndef HASH_H
#define HASH_H
static inline unsigned int hash_str(const char *s)
{
/* fnv32 hash */
unsigned int hash = 2166136261U;
for (; *s; s++)
hash = (hash ^ *s) * 0x01000193;
return hash;
}
/* simplified version of functions from include/linux/hash.h */
#define GOLDEN_RATIO_32 0x61C88647
static inline unsigned int hash_32(unsigned int val)
{
return 0x61C88647 * val;
}
static inline unsigned int hash_ptr(const void *ptr)
{
return hash_32((unsigned int)(unsigned long)ptr);
}
#endif /* HASH_H */

View File

@@ -0,0 +1,98 @@
/* SPDX-License-Identifier: GPL-2.0-only */
#ifndef HASHTABLE_H
#define HASHTABLE_H
#include "array_size.h"
#include "list.h"
#define HASH_SIZE(name) (ARRAY_SIZE(name))
#define HASHTABLE_DECLARE(name, size) struct hlist_head name[size]
#define HASHTABLE_DEFINE(name, size) \
HASHTABLE_DECLARE(name, size) = \
{ [0 ... ((size) - 1)] = HLIST_HEAD_INIT }
#define hash_head(table, key) (&(table)[(key) % HASH_SIZE(table)])
static inline void __hash_init(struct hlist_head *ht, unsigned int sz)
{
unsigned int i;
for (i = 0; i < sz; i++)
INIT_HLIST_HEAD(&ht[i]);
}
/**
* hash_init - initialize a hash table
* @table: hashtable to be initialized
*
* This has to be a macro since HASH_SIZE() will not work on pointers since
* it calculates the size during preprocessing.
*/
#define hash_init(table) __hash_init(table, HASH_SIZE(table))
/**
* hash_add - add an object to a hashtable
* @table: hashtable to add to
* @node: the &struct hlist_node of the object to be added
* @key: the key of the object to be added
*/
#define hash_add(table, node, key) \
hlist_add_head(node, hash_head(table, key))
/**
* hash_del - remove an object from a hashtable
* @node: &struct hlist_node of the object to remove
*/
static inline void hash_del(struct hlist_node *node)
{
hlist_del_init(node);
}
/**
* hash_for_each - iterate over a hashtable
* @table: hashtable to iterate
* @obj: the type * to use as a loop cursor for each entry
* @member: the name of the hlist_node within the struct
*/
#define hash_for_each(table, obj, member) \
for (int _bkt = 0; _bkt < HASH_SIZE(table); _bkt++) \
hlist_for_each_entry(obj, &table[_bkt], member)
/**
* hash_for_each_safe - iterate over a hashtable safe against removal of
* hash entry
* @table: hashtable to iterate
* @obj: the type * to use as a loop cursor for each entry
* @tmp: a &struct hlist_node used for temporary storage
* @member: the name of the hlist_node within the struct
*/
#define hash_for_each_safe(table, obj, tmp, member) \
for (int _bkt = 0; _bkt < HASH_SIZE(table); _bkt++) \
hlist_for_each_entry_safe(obj, tmp, &table[_bkt], member)
/**
* hash_for_each_possible - iterate over all possible objects hashing to the
* same bucket
* @table: hashtable to iterate
* @obj: the type * to use as a loop cursor for each entry
* @member: the name of the hlist_node within the struct
* @key: the key of the objects to iterate over
*/
#define hash_for_each_possible(table, obj, member, key) \
hlist_for_each_entry(obj, hash_head(table, key), member)
/**
* hash_for_each_possible_safe - iterate over all possible objects hashing to the
* same bucket safe against removals
* @table: hashtable to iterate
* @obj: the type * to use as a loop cursor for each entry
* @tmp: a &struct hlist_node used for temporary storage
* @member: the name of the hlist_node within the struct
* @key: the key of the objects to iterate over
*/
#define hash_for_each_possible_safe(table, obj, tmp, member, key) \
hlist_for_each_entry_safe(obj, tmp, hash_head(table, key), member)
#endif /* HASHTABLE_H */

View File

@@ -0,0 +1,428 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef LIST_H
#define LIST_H
#include <stddef.h>
#include "list_types.h"
/* Are two types/vars the same type (ignoring qualifiers)? */
#define __same_type(a, b) __builtin_types_compatible_p(typeof(a), typeof(b))
/**
* container_of - cast a member of a structure out to the containing structure
* @ptr: the pointer to the member.
* @type: the type of the container struct this is embedded in.
* @member: the name of the member within the struct.
*
*/
#define container_of(ptr, type, member) ({ \
void *__mptr = (void *)(ptr); \
_Static_assert(__same_type(*(ptr), ((type *)0)->member) || \
__same_type(*(ptr), void), \
"pointer type mismatch in container_of()"); \
((type *)(__mptr - offsetof(type, member))); })
#define LIST_POISON1 ((void *) 0x100)
#define LIST_POISON2 ((void *) 0x122)
/*
* Circular doubly linked list implementation.
*
* Some of the internal functions ("__xxx") are useful when
* manipulating whole lists rather than single entries, as
* sometimes we already know the next/prev entries and we can
* generate better code by using them directly rather than
* using the generic single-entry routines.
*/
#define LIST_HEAD_INIT(name) { &(name), &(name) }
#define LIST_HEAD(name) \
struct list_head name = LIST_HEAD_INIT(name)
/**
* INIT_LIST_HEAD - Initialize a list_head structure
* @list: list_head structure to be initialized.
*
* Initializes the list_head to point to itself. If it is a list header,
* the result is an empty list.
*/
static inline void INIT_LIST_HEAD(struct list_head *list)
{
list->next = list;
list->prev = list;
}
/*
* Insert a new entry between two known consecutive entries.
*
* This is only for internal list manipulation where we know
* the prev/next entries already!
*/
static inline void __list_add(struct list_head *new,
struct list_head *prev,
struct list_head *next)
{
next->prev = new;
new->next = next;
new->prev = prev;
prev->next = new;
}
/**
* list_add - add a new entry
* @new: new entry to be added
* @head: list head to add it after
*
* Insert a new entry after the specified head.
* This is good for implementing stacks.
*/
static inline void list_add(struct list_head *new, struct list_head *head)
{
__list_add(new, head, head->next);
}
/**
* list_add_tail - add a new entry
* @new: new entry to be added
* @head: list head to add it before
*
* Insert a new entry before the specified head.
* This is useful for implementing queues.
*/
static inline void list_add_tail(struct list_head *new, struct list_head *head)
{
__list_add(new, head->prev, head);
}
/*
* Delete a list entry by making the prev/next entries
* point to each other.
*
* This is only for internal list manipulation where we know
* the prev/next entries already!
*/
static inline void __list_del(struct list_head *prev, struct list_head *next)
{
next->prev = prev;
prev->next = next;
}
static inline void __list_del_entry(struct list_head *entry)
{
__list_del(entry->prev, entry->next);
}
/**
* list_del - deletes entry from list.
* @entry: the element to delete from the list.
* Note: list_empty() on entry does not return true after this, the entry is
* in an undefined state.
*/
static inline void list_del(struct list_head *entry)
{
__list_del_entry(entry);
entry->next = LIST_POISON1;
entry->prev = LIST_POISON2;
}
/**
* list_replace - replace old entry by new one
* @old : the element to be replaced
* @new : the new element to insert
*
* If @old was empty, it will be overwritten.
*/
static inline void list_replace(struct list_head *old,
struct list_head *new)
{
new->next = old->next;
new->next->prev = new;
new->prev = old->prev;
new->prev->next = new;
}
/**
* list_replace_init - replace old entry by new one and initialize the old one
* @old : the element to be replaced
* @new : the new element to insert
*
* If @old was empty, it will be overwritten.
*/
static inline void list_replace_init(struct list_head *old,
struct list_head *new)
{
list_replace(old, new);
INIT_LIST_HEAD(old);
}
/**
* list_move - delete from one list and add as another's head
* @list: the entry to move
* @head: the head that will precede our entry
*/
static inline void list_move(struct list_head *list, struct list_head *head)
{
__list_del_entry(list);
list_add(list, head);
}
/**
* list_move_tail - delete from one list and add as another's tail
* @list: the entry to move
* @head: the head that will follow our entry
*/
static inline void list_move_tail(struct list_head *list,
struct list_head *head)
{
__list_del_entry(list);
list_add_tail(list, head);
}
/**
* list_is_first -- tests whether @list is the first entry in list @head
* @list: the entry to test
* @head: the head of the list
*/
static inline int list_is_first(const struct list_head *list, const struct list_head *head)
{
return list->prev == head;
}
/**
* list_is_last - tests whether @list is the last entry in list @head
* @list: the entry to test
* @head: the head of the list
*/
static inline int list_is_last(const struct list_head *list, const struct list_head *head)
{
return list->next == head;
}
/**
* list_is_head - tests whether @list is the list @head
* @list: the entry to test
* @head: the head of the list
*/
static inline int list_is_head(const struct list_head *list, const struct list_head *head)
{
return list == head;
}
/**
* list_empty - tests whether a list is empty
* @head: the list to test.
*/
static inline int list_empty(const struct list_head *head)
{
return head->next == head;
}
/**
* list_entry - get the struct for this entry
* @ptr: the &struct list_head pointer.
* @type: the type of the struct this is embedded in.
* @member: the name of the list_head within the struct.
*/
#define list_entry(ptr, type, member) \
container_of(ptr, type, member)
/**
* list_first_entry - get the first element from a list
* @ptr: the list head to take the element from.
* @type: the type of the struct this is embedded in.
* @member: the name of the list_head within the struct.
*
* Note, that list is expected to be not empty.
*/
#define list_first_entry(ptr, type, member) \
list_entry((ptr)->next, type, member)
/**
* list_last_entry - get the last element from a list
* @ptr: the list head to take the element from.
* @type: the type of the struct this is embedded in.
* @member: the name of the list_head within the struct.
*
* Note, that list is expected to be not empty.
*/
#define list_last_entry(ptr, type, member) \
list_entry((ptr)->prev, type, member)
/**
* list_next_entry - get the next element in list
* @pos: the type * to cursor
* @member: the name of the list_head within the struct.
*/
#define list_next_entry(pos, member) \
list_entry((pos)->member.next, typeof(*(pos)), member)
/**
* list_prev_entry - get the prev element in list
* @pos: the type * to cursor
* @member: the name of the list_head within the struct.
*/
#define list_prev_entry(pos, member) \
list_entry((pos)->member.prev, typeof(*(pos)), member)
/**
* list_entry_is_head - test if the entry points to the head of the list
* @pos: the type * to cursor
* @head: the head for your list.
* @member: the name of the list_head within the struct.
*/
#define list_entry_is_head(pos, head, member) \
(&pos->member == (head))
/**
* list_for_each_entry - iterate over list of given type
* @pos: the type * to use as a loop cursor.
* @head: the head for your list.
* @member: the name of the list_head within the struct.
*/
#define list_for_each_entry(pos, head, member) \
for (pos = list_first_entry(head, typeof(*pos), member); \
!list_entry_is_head(pos, head, member); \
pos = list_next_entry(pos, member))
/**
* list_for_each_entry_reverse - iterate backwards over list of given type.
* @pos: the type * to use as a loop cursor.
* @head: the head for your list.
* @member: the name of the list_head within the struct.
*/
#define list_for_each_entry_reverse(pos, head, member) \
for (pos = list_last_entry(head, typeof(*pos), member); \
!list_entry_is_head(pos, head, member); \
pos = list_prev_entry(pos, member))
/**
* list_for_each_entry_safe - iterate over list of given type. Safe against removal of list entry
* @pos: the type * to use as a loop cursor.
* @n: another type * to use as temporary storage
* @head: the head for your list.
* @member: the name of the list_head within the struct.
*/
#define list_for_each_entry_safe(pos, n, head, member) \
for (pos = list_first_entry(head, typeof(*pos), member), \
n = list_next_entry(pos, member); \
!list_entry_is_head(pos, head, member); \
pos = n, n = list_next_entry(n, member))
/*
* Double linked lists with a single pointer list head.
* Mostly useful for hash tables where the two pointer list head is
* too wasteful.
* You lose the ability to access the tail in O(1).
*/
#define HLIST_HEAD_INIT { .first = NULL }
#define INIT_HLIST_HEAD(ptr) ((ptr)->first = NULL)
static inline void INIT_HLIST_NODE(struct hlist_node *h)
{
h->next = NULL;
h->pprev = NULL;
}
/**
* hlist_unhashed - Has node been removed from list and reinitialized?
* @h: Node to be checked
*
* Not that not all removal functions will leave a node in unhashed
* state. For example, hlist_nulls_del_init_rcu() does leave the
* node in unhashed state, but hlist_nulls_del() does not.
*/
static inline int hlist_unhashed(const struct hlist_node *h)
{
return !h->pprev;
}
static inline void __hlist_del(struct hlist_node *n)
{
struct hlist_node *next = n->next;
struct hlist_node **pprev = n->pprev;
*pprev = next;
if (next)
next->pprev = pprev;
}
/**
* hlist_del - Delete the specified hlist_node from its list
* @n: Node to delete.
*
* Note that this function leaves the node in hashed state. Use
* hlist_del_init() or similar instead to unhash @n.
*/
static inline void hlist_del(struct hlist_node *n)
{
__hlist_del(n);
n->next = LIST_POISON1;
n->pprev = LIST_POISON2;
}
/**
* hlist_del_init - Delete the specified hlist_node from its list and initialize
* @n: Node to delete.
*
* Note that this function leaves the node in unhashed state.
*/
static inline void hlist_del_init(struct hlist_node *n)
{
if (!hlist_unhashed(n)) {
__hlist_del(n);
INIT_HLIST_NODE(n);
}
}
/**
* hlist_add_head - add a new entry at the beginning of the hlist
* @n: new entry to be added
* @h: hlist head to add it after
*
* Insert a new entry after the specified head.
* This is good for implementing stacks.
*/
static inline void hlist_add_head(struct hlist_node *n, struct hlist_head *h)
{
struct hlist_node *first = h->first;
n->next = first;
if (first)
first->pprev = &n->next;
h->first = n;
n->pprev = &h->first;
}
#define hlist_entry(ptr, type, member) container_of(ptr, type, member)
#define hlist_entry_safe(ptr, type, member) \
({ typeof(ptr) ____ptr = (ptr); \
____ptr ? hlist_entry(____ptr, type, member) : NULL; \
})
/**
* hlist_for_each_entry - iterate over list of given type
* @pos: the type * to use as a loop cursor.
* @head: the head for your list.
* @member: the name of the hlist_node within the struct.
*/
#define hlist_for_each_entry(pos, head, member) \
for (pos = hlist_entry_safe((head)->first, typeof(*(pos)), member);\
pos; \
pos = hlist_entry_safe((pos)->member.next, typeof(*(pos)), member))
/**
* hlist_for_each_entry_safe - iterate over list of given type safe against removal of list entry
* @pos: the type * to use as a loop cursor.
* @n: a &struct hlist_node to use as temporary storage
* @head: the head for your list.
* @member: the name of the hlist_node within the struct.
*/
#define hlist_for_each_entry_safe(pos, n, head, member) \
for (pos = hlist_entry_safe((head)->first, typeof(*pos), member);\
pos && ({ n = pos->member.next; 1; }); \
pos = hlist_entry_safe(n, typeof(*pos), member))
#endif /* LIST_H */

View File

@@ -0,0 +1,17 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef LIST_TYPES_H
#define LIST_TYPES_H
struct list_head {
struct list_head *next, *prev;
};
struct hlist_head {
struct hlist_node *first;
};
struct hlist_node {
struct hlist_node *next, **pprev;
};
#endif /* LIST_TYPES_H */

View File

@@ -0,0 +1,53 @@
/* SPDX-License-Identifier: GPL-2.0-only */
#ifndef XALLOC_H
#define XALLOC_H
#include <stdlib.h>
#include <string.h>
static inline void *xmalloc(size_t size)
{
void *p = malloc(size);
if (!p)
exit(1);
return p;
}
static inline void *xcalloc(size_t nmemb, size_t size)
{
void *p = calloc(nmemb, size);
if (!p)
exit(1);
return p;
}
static inline void *xrealloc(void *p, size_t size)
{
p = realloc(p, size);
if (!p)
exit(1);
return p;
}
static inline char *xstrdup(const char *s)
{
char *p = strdup(s);
if (!p)
exit(1);
return p;
}
static inline char *xstrndup(const char *s, size_t n)
{
char *p = strndup(s, n);
if (!p)
exit(1);
return p;
}
#endif /* XALLOC_H */

View File

@@ -0,0 +1,7 @@
# SPDX-License-Identifier: GPL-2.0-only
/conf
/[gmnq]conf
/[gmnq]conf-bin
/[gmnq]conf-cflags
/[gmnq]conf-libs
/qconf-moc.cc

View File

@@ -0,0 +1,236 @@
# SPDX-License-Identifier: GPL-2.0
# ===========================================================================
# Kernel configuration targets
# These targets are used from top-level makefile
ifdef KBUILD_KCONFIG
Kconfig := $(KBUILD_KCONFIG)
else
Kconfig := Kconfig
endif
ifndef KBUILD_DEFCONFIG
KBUILD_DEFCONFIG := defconfig
endif
ifeq ($(quiet),silent_)
silent := -s
endif
export KCONFIG_DEFCONFIG_LIST :=
ifndef cross_compiling
kernel-release := $(shell uname -r)
KCONFIG_DEFCONFIG_LIST += \
/lib/modules/$(kernel-release)/.config \
/etc/kernel-config \
/boot/config-$(kernel-release)
endif
KCONFIG_DEFCONFIG_LIST += arch/$(SRCARCH)/configs/$(KBUILD_DEFCONFIG)
ifneq ($(findstring c, $(KBUILD_EXTRA_WARN)),)
export KCONFIG_WARN_UNKNOWN_SYMBOLS=1
endif
ifneq ($(findstring e, $(KBUILD_EXTRA_WARN)),)
export KCONFIG_WERROR=1
endif
# We need this, in case the user has it in its environment
unexport CONFIG_
config-prog := conf
menuconfig-prog := mconf
nconfig-prog := nconf
gconfig-prog := gconf
xconfig-prog := qconf
define config_rule
PHONY += $(1)
$(1): $(obj)/$($(1)-prog)
$(Q)$$< $(silent) $(Kconfig)
PHONY += build_$(1)
build_$(1): $(obj)/$($(1)-prog)
endef
$(foreach c, config menuconfig nconfig gconfig xconfig, $(eval $(call config_rule,$(c))))
PHONY += localmodconfig localyesconfig
localyesconfig localmodconfig: $(obj)/conf
$(Q)$(PERL) $(src)/streamline_config.pl --$@ $(srctree) $(Kconfig) > .tmp.config
$(Q)if [ -f .config ]; then \
cmp -s .tmp.config .config || \
(mv -f .config .config.old.1; \
mv -f .tmp.config .config; \
$< $(silent) --oldconfig $(Kconfig); \
mv -f .config.old.1 .config.old) \
else \
mv -f .tmp.config .config; \
$< $(silent) --oldconfig $(Kconfig); \
fi
$(Q)rm -f .tmp.config
# These targets map 1:1 to the commandline options of 'conf'
#
# Note:
# syncconfig has become an internal implementation detail and is now
# deprecated for external use
simple-targets := oldconfig allnoconfig allyesconfig allmodconfig \
alldefconfig randconfig listnewconfig olddefconfig syncconfig \
helpnewconfig yes2modconfig mod2yesconfig mod2noconfig
PHONY += $(simple-targets)
$(simple-targets): $(obj)/conf
$(Q)$< $(silent) --$@ $(Kconfig)
PHONY += savedefconfig defconfig
savedefconfig: $(obj)/conf
$(Q)$< $(silent) --$@=defconfig $(Kconfig)
defconfig: $(obj)/conf
ifneq ($(wildcard $(srctree)/arch/$(SRCARCH)/configs/$(KBUILD_DEFCONFIG)),)
@$(kecho) "*** Default configuration is based on '$(KBUILD_DEFCONFIG)'"
$(Q)$< $(silent) --defconfig=arch/$(SRCARCH)/configs/$(KBUILD_DEFCONFIG) $(Kconfig)
else
@$(kecho) "*** Default configuration is based on target '$(KBUILD_DEFCONFIG)'"
$(Q)$(MAKE) -f $(srctree)/Makefile $(KBUILD_DEFCONFIG)
endif
%_defconfig: $(obj)/conf
$(Q)$< $(silent) --defconfig=arch/$(SRCARCH)/configs/$@ $(Kconfig)
configfiles = $(wildcard $(srctree)/kernel/configs/$(1) $(srctree)/arch/$(SRCARCH)/configs/$(1))
all-config-fragments = $(call configfiles,*.config)
config-fragments = $(call configfiles,$@)
cmd_merge_fragments = $(srctree)/scripts/kconfig/merge_config.sh -m $(KCONFIG_CONFIG) $(config-fragments)
%.config: $(obj)/conf
$(if $(config-fragments),, $(error $@ fragment does not exists on this architecture))
$(call cmd,merge_fragments)
$(Q)$(MAKE) -f $(srctree)/Makefile olddefconfig
PHONY += tinyconfig
tinyconfig:
$(Q)KCONFIG_ALLCONFIG=kernel/configs/tiny-base.config $(MAKE) -f $(srctree)/Makefile allnoconfig
$(Q)$(MAKE) -f $(srctree)/Makefile tiny.config
# CHECK: -o cache_dir=<path> working?
PHONY += testconfig
testconfig: $(obj)/conf
$(Q)$(PYTHON3) -B -m pytest $(src)/tests \
-o cache_dir=$(abspath $(obj)/tests/.cache) \
$(if $(findstring 1,$(KBUILD_VERBOSE)),--capture=no)
clean-files += tests/.cache
# Help text used by make help
help:
@echo 'Configuration targets:'
@echo ' config - Update current config utilising a line-oriented program'
@echo ' nconfig - Update current config utilising a ncurses menu based program'
@echo ' menuconfig - Update current config utilising a menu based program'
@echo ' xconfig - Update current config utilising a Qt based front-end'
@echo ' gconfig - Update current config utilising a GTK+ based front-end'
@echo ' oldconfig - Update current config utilising a provided .config as base'
@echo ' localmodconfig - Update current config disabling modules not loaded'
@echo ' except those preserved by LMC_KEEP environment variable'
@echo ' localyesconfig - Update current config converting local mods to core'
@echo ' except those preserved by LMC_KEEP environment variable'
@echo ' defconfig - New config with default from ARCH supplied defconfig'
@echo ' savedefconfig - Save current config as ./defconfig (minimal config)'
@echo ' allnoconfig - New config where all options are answered with no'
@echo ' allyesconfig - New config where all options are accepted with yes'
@echo ' allmodconfig - New config selecting modules when possible'
@echo ' alldefconfig - New config with all symbols set to default'
@echo ' randconfig - New config with random answer to all options'
@echo ' yes2modconfig - Change answers from yes to mod if possible'
@echo ' mod2yesconfig - Change answers from mod to yes if possible'
@echo ' mod2noconfig - Change answers from mod to no if possible'
@echo ' listnewconfig - List new options'
@echo ' helpnewconfig - List new options and help text'
@echo ' olddefconfig - Same as oldconfig but sets new symbols to their'
@echo ' default value without prompting'
@echo ' tinyconfig - Configure the tiniest possible kernel'
@echo ' testconfig - Run Kconfig unit tests (requires python3 and pytest)'
@echo ''
@echo 'Configuration topic targets:'
@$(foreach f, $(all-config-fragments), \
if help=$$(grep -m1 '^# Help: ' $(f)); then \
printf ' %-25s - %s\n' '$(notdir $(f))' "$${help#*: }"; \
fi;)
# ===========================================================================
# object files used by all kconfig flavours
common-objs := confdata.o expr.o lexer.lex.o menu.o parser.tab.o \
preprocess.o symbol.o util.o
$(obj)/lexer.lex.o: $(obj)/parser.tab.h
HOSTCFLAGS_lexer.lex.o := -I $(src)
HOSTCFLAGS_parser.tab.o := -I $(src)
# conf: Used for defconfig, oldconfig and related targets
hostprogs += conf
conf-objs := conf.o $(common-objs)
# nconf: Used for the nconfig target based on ncurses
hostprogs += nconf
nconf-objs := nconf.o nconf.gui.o mnconf-common.o $(common-objs)
HOSTLDLIBS_nconf = $(call read-file, $(obj)/nconf-libs)
HOSTCFLAGS_nconf.o = $(call read-file, $(obj)/nconf-cflags)
HOSTCFLAGS_nconf.gui.o = $(call read-file, $(obj)/nconf-cflags)
$(obj)/nconf: | $(obj)/nconf-libs
$(obj)/nconf.o $(obj)/nconf.gui.o: | $(obj)/nconf-cflags
# mconf: Used for the menuconfig target based on lxdialog
hostprogs += mconf
lxdialog := $(addprefix lxdialog/, \
checklist.o inputbox.o menubox.o textbox.o util.o yesno.o)
mconf-objs := mconf.o $(lxdialog) mnconf-common.o $(common-objs)
HOSTLDLIBS_mconf = $(call read-file, $(obj)/mconf-libs)
$(foreach f, mconf.o $(lxdialog), \
$(eval HOSTCFLAGS_$f = $$(call read-file, $(obj)/mconf-cflags)))
$(obj)/mconf: | $(obj)/mconf-libs
$(addprefix $(obj)/, mconf.o $(lxdialog)): | $(obj)/mconf-cflags
# qconf: Used for the xconfig target based on Qt
hostprogs += qconf
qconf-cxxobjs := qconf.o qconf-moc.o
qconf-objs := images.o $(common-objs)
HOSTLDLIBS_qconf = $(call read-file, $(obj)/qconf-libs)
HOSTCXXFLAGS_qconf.o = -std=c++11 -fPIC $(call read-file, $(obj)/qconf-cflags)
HOSTCXXFLAGS_qconf-moc.o = -std=c++11 -fPIC $(call read-file, $(obj)/qconf-cflags)
$(obj)/qconf: | $(obj)/qconf-libs
$(obj)/qconf.o $(obj)/qconf-moc.o: | $(obj)/qconf-cflags
quiet_cmd_moc = MOC $@
cmd_moc = $(call read-file, $(obj)/qconf-bin)/moc $< -o $@
$(obj)/qconf-moc.cc: $(src)/qconf.h FORCE | $(obj)/qconf-bin
$(call if_changed,moc)
targets += qconf-moc.cc
# gconf: Used for the gconfig target based on GTK+
hostprogs += gconf
gconf-objs := gconf.o images.o $(common-objs)
HOSTLDLIBS_gconf = $(call read-file, $(obj)/gconf-libs)
HOSTCFLAGS_gconf.o = $(call read-file, $(obj)/gconf-cflags)
$(obj)/gconf: | $(obj)/gconf-libs
$(obj)/gconf.o: | $(obj)/gconf-cflags
# check if necessary packages are available, and configure build flags
cmd_conf_cfg = $< $(addprefix $(obj)/$*conf-, cflags libs bin); touch $(obj)/$*conf-bin
$(obj)/%conf-cflags $(obj)/%conf-libs $(obj)/%conf-bin: $(src)/%conf-cfg.sh
$(call cmd,conf_cfg)
clean-files += *conf-cflags *conf-libs *conf-bin

View File

@@ -0,0 +1,869 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>
*/
#include <ctype.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <getopt.h>
#include <sys/time.h>
#include <errno.h>
#include "internal.h"
#include "lkc.h"
static void conf(struct menu *menu);
static void check_conf(struct menu *menu);
enum input_mode {
oldaskconfig,
syncconfig,
oldconfig,
allnoconfig,
allyesconfig,
allmodconfig,
alldefconfig,
randconfig,
defconfig,
savedefconfig,
listnewconfig,
helpnewconfig,
olddefconfig,
yes2modconfig,
mod2yesconfig,
mod2noconfig,
};
static enum input_mode input_mode = oldaskconfig;
static int input_mode_opt;
static int indent = 1;
static int tty_stdio;
static int sync_kconfig;
static int conf_cnt;
static char line[PATH_MAX];
static struct menu *rootEntry;
static void print_help(struct menu *menu)
{
struct gstr help = str_new();
menu_get_ext_help(menu, &help);
printf("\n%s\n", str_get(&help));
str_free(&help);
}
static void strip(char *str)
{
char *p = str;
int l;
while ((isspace(*p)))
p++;
l = strlen(p);
if (p != str)
memmove(str, p, l + 1);
if (!l)
return;
p = str + l - 1;
while ((isspace(*p)))
*p-- = 0;
}
/* Helper function to facilitate fgets() by Jean Sacren. */
static void xfgets(char *str, int size, FILE *in)
{
if (!fgets(str, size, in))
fprintf(stderr, "\nError in reading or end of file.\n");
if (!tty_stdio)
printf("%s", str);
}
static void set_randconfig_seed(void)
{
unsigned int seed;
char *env;
bool seed_set = false;
env = getenv("KCONFIG_SEED");
if (env && *env) {
char *endp;
seed = strtol(env, &endp, 0);
if (*endp == '\0')
seed_set = true;
}
if (!seed_set) {
struct timeval now;
/*
* Use microseconds derived seed, compensate for systems where it may
* be zero.
*/
gettimeofday(&now, NULL);
seed = (now.tv_sec + 1) * (now.tv_usec + 1);
}
printf("KCONFIG_SEED=0x%X\n", seed);
srand(seed);
}
/**
* randomize_choice_values - randomize choice block
*
* @choice: menu entry for the choice
*/
static void randomize_choice_values(struct menu *choice)
{
struct menu *menu;
int x;
int cnt = 0;
/*
* First, count the number of symbols to randomize. If sym_has_value()
* is true, it was specified by KCONFIG_ALLCONFIG. It needs to be
* respected.
*/
menu_for_each_sub_entry(menu, choice) {
struct symbol *sym = menu->sym;
if (sym && !sym_has_value(sym))
cnt++;
}
while (cnt > 0) {
x = rand() % cnt;
menu_for_each_sub_entry(menu, choice) {
struct symbol *sym = menu->sym;
if (sym && !sym_has_value(sym))
x--;
if (x < 0) {
sym->def[S_DEF_USER].tri = yes;
sym->flags |= SYMBOL_DEF_USER;
/*
* Move the selected item to the _tail_ because
* this needs to have a lower priority than the
* user input from KCONFIG_ALLCONFIG.
*/
list_move_tail(&sym->choice_link,
&choice->choice_members);
break;
}
}
cnt--;
}
}
enum conf_def_mode {
def_default,
def_yes,
def_mod,
def_no,
def_random
};
static void conf_set_all_new_symbols(enum conf_def_mode mode)
{
struct menu *menu;
int cnt;
/*
* can't go as the default in switch-case below, otherwise gcc whines
* about -Wmaybe-uninitialized
*/
int pby = 50; /* probability of bool = y */
int pty = 33; /* probability of tristate = y */
int ptm = 33; /* probability of tristate = m */
if (mode == def_random) {
int n, p[3];
char *env = getenv("KCONFIG_PROBABILITY");
n = 0;
while (env && *env) {
char *endp;
int tmp = strtol(env, &endp, 10);
if (tmp >= 0 && tmp <= 100) {
p[n++] = tmp;
} else {
errno = ERANGE;
perror("KCONFIG_PROBABILITY");
exit(1);
}
env = (*endp == ':') ? endp + 1 : endp;
if (n >= 3)
break;
}
switch (n) {
case 1:
pby = p[0];
ptm = pby / 2;
pty = pby - ptm;
break;
case 2:
pty = p[0];
ptm = p[1];
pby = pty + ptm;
break;
case 3:
pby = p[0];
pty = p[1];
ptm = p[2];
break;
}
if (pty + ptm > 100) {
errno = ERANGE;
perror("KCONFIG_PROBABILITY");
exit(1);
}
}
menu_for_each_entry(menu) {
struct symbol *sym = menu->sym;
tristate val;
if (!sym || !menu->prompt || sym_has_value(sym) ||
(sym->type != S_BOOLEAN && sym->type != S_TRISTATE) ||
sym_is_choice_value(sym))
continue;
if (sym_is_choice(sym)) {
if (mode == def_random)
randomize_choice_values(menu);
continue;
}
switch (mode) {
case def_yes:
val = yes;
break;
case def_mod:
val = mod;
break;
case def_no:
val = no;
break;
case def_random:
val = no;
cnt = rand() % 100;
if (sym->type == S_TRISTATE) {
if (cnt < pty)
val = yes;
else if (cnt < pty + ptm)
val = mod;
} else if (cnt < pby) {
val = yes;
}
break;
default:
continue;
}
sym->def[S_DEF_USER].tri = val;
sym->flags |= SYMBOL_DEF_USER;
}
sym_clear_all_valid();
}
static void conf_rewrite_tristates(tristate old_val, tristate new_val)
{
struct symbol *sym;
for_all_symbols(sym) {
if (sym_get_type(sym) == S_TRISTATE &&
sym->def[S_DEF_USER].tri == old_val)
sym->def[S_DEF_USER].tri = new_val;
}
sym_clear_all_valid();
}
static int conf_askvalue(struct symbol *sym, const char *def)
{
if (!sym_has_value(sym))
printf("(NEW) ");
line[0] = '\n';
line[1] = 0;
if (!sym_is_changeable(sym)) {
printf("%s\n", def);
line[0] = '\n';
line[1] = 0;
return 0;
}
switch (input_mode) {
case oldconfig:
case syncconfig:
if (sym_has_value(sym)) {
printf("%s\n", def);
return 0;
}
/* fall through */
default:
fflush(stdout);
xfgets(line, sizeof(line), stdin);
break;
}
return 1;
}
static int conf_string(struct menu *menu)
{
struct symbol *sym = menu->sym;
const char *def;
while (1) {
printf("%*s%s ", indent - 1, "", menu->prompt->text);
printf("(%s) ", sym->name);
def = sym_get_string_value(sym);
if (def)
printf("[%s] ", def);
if (!conf_askvalue(sym, def))
return 0;
switch (line[0]) {
case '\n':
break;
case '?':
/* print help */
if (line[1] == '\n') {
print_help(menu);
def = NULL;
break;
}
/* fall through */
default:
line[strlen(line)-1] = 0;
def = line;
}
if (def && sym_set_string_value(sym, def))
return 0;
}
}
static int conf_sym(struct menu *menu)
{
struct symbol *sym = menu->sym;
tristate oldval, newval;
while (1) {
printf("%*s%s ", indent - 1, "", menu->prompt->text);
if (sym->name)
printf("(%s) ", sym->name);
putchar('[');
oldval = sym_get_tristate_value(sym);
switch (oldval) {
case no:
putchar('N');
break;
case mod:
putchar('M');
break;
case yes:
putchar('Y');
break;
}
if (oldval != no && sym_tristate_within_range(sym, no))
printf("/n");
if (oldval != mod && sym_tristate_within_range(sym, mod))
printf("/m");
if (oldval != yes && sym_tristate_within_range(sym, yes))
printf("/y");
printf("/?] ");
if (!conf_askvalue(sym, sym_get_string_value(sym)))
return 0;
strip(line);
switch (line[0]) {
case 'n':
case 'N':
newval = no;
if (!line[1] || !strcmp(&line[1], "o"))
break;
continue;
case 'm':
case 'M':
newval = mod;
if (!line[1])
break;
continue;
case 'y':
case 'Y':
newval = yes;
if (!line[1] || !strcmp(&line[1], "es"))
break;
continue;
case 0:
newval = oldval;
break;
case '?':
goto help;
default:
continue;
}
if (sym_set_tristate_value(sym, newval))
return 0;
help:
print_help(menu);
}
}
static void conf_choice(struct menu *menu)
{
struct symbol *def_sym;
struct menu *child;
bool is_new = false;
while (1) {
int cnt, def;
printf("%*s%s\n", indent - 1, "", menu_get_prompt(menu));
def_sym = sym_calc_choice(menu);
cnt = def = 0;
line[0] = 0;
for (child = menu->list; child; child = child->next) {
if (!menu_is_visible(child))
continue;
if (!child->sym) {
printf("%*c %s\n", indent, '*', menu_get_prompt(child));
continue;
}
cnt++;
if (child->sym == def_sym) {
def = cnt;
printf("%*c", indent, '>');
} else
printf("%*c", indent, ' ');
printf(" %d. %s (%s)", cnt, menu_get_prompt(child),
child->sym->name);
if (!sym_has_value(child->sym)) {
is_new = true;
printf(" (NEW)");
}
printf("\n");
}
printf("%*schoice", indent - 1, "");
if (cnt == 1) {
printf("[1]: 1\n");
goto conf_childs;
}
printf("[1-%d?]: ", cnt);
switch (input_mode) {
case oldconfig:
case syncconfig:
if (!is_new) {
cnt = def;
printf("%d\n", cnt);
break;
}
/* fall through */
case oldaskconfig:
fflush(stdout);
xfgets(line, sizeof(line), stdin);
strip(line);
if (line[0] == '?') {
print_help(menu);
continue;
}
if (!line[0])
cnt = def;
else if (isdigit(line[0]))
cnt = atoi(line);
else
continue;
break;
default:
break;
}
conf_childs:
for (child = menu->list; child; child = child->next) {
if (!child->sym || !menu_is_visible(child))
continue;
if (!--cnt)
break;
}
if (!child)
continue;
if (line[0] && line[strlen(line) - 1] == '?') {
print_help(child);
continue;
}
choice_set_value(menu, child->sym);
return;
}
}
static void conf(struct menu *menu)
{
struct symbol *sym;
struct property *prop;
struct menu *child;
if (!menu_is_visible(menu))
return;
sym = menu->sym;
prop = menu->prompt;
if (prop) {
const char *prompt;
switch (prop->type) {
case P_MENU:
/*
* Except in oldaskconfig mode, we show only menus that
* contain new symbols.
*/
if (input_mode != oldaskconfig && rootEntry != menu) {
check_conf(menu);
return;
}
/* fall through */
case P_COMMENT:
prompt = menu_get_prompt(menu);
if (prompt)
printf("%*c\n%*c %s\n%*c\n",
indent, '*',
indent, '*', prompt,
indent, '*');
default:
;
}
}
if (!sym)
goto conf_childs;
if (sym_is_choice(sym)) {
conf_choice(menu);
return;
}
switch (sym->type) {
case S_INT:
case S_HEX:
case S_STRING:
conf_string(menu);
break;
default:
conf_sym(menu);
break;
}
conf_childs:
if (sym)
indent += 2;
for (child = menu->list; child; child = child->next)
conf(child);
if (sym)
indent -= 2;
}
static void check_conf(struct menu *menu)
{
struct symbol *sym;
struct menu *child;
if (!menu_is_visible(menu))
return;
sym = menu->sym;
if (sym && !sym_has_value(sym) && sym_is_changeable(sym)) {
switch (input_mode) {
case listnewconfig:
if (sym->name)
print_symbol_for_listconfig(sym);
break;
case helpnewconfig:
printf("-----\n");
print_help(menu);
printf("-----\n");
break;
default:
if (!conf_cnt++)
printf("*\n* Restart config...\n*\n");
rootEntry = menu_get_parent_menu(menu);
conf(rootEntry);
break;
}
}
for (child = menu->list; child; child = child->next)
check_conf(child);
}
static const struct option long_opts[] = {
{"help", no_argument, NULL, 'h'},
{"silent", no_argument, NULL, 's'},
{"oldaskconfig", no_argument, &input_mode_opt, oldaskconfig},
{"oldconfig", no_argument, &input_mode_opt, oldconfig},
{"syncconfig", no_argument, &input_mode_opt, syncconfig},
{"defconfig", required_argument, &input_mode_opt, defconfig},
{"savedefconfig", required_argument, &input_mode_opt, savedefconfig},
{"allnoconfig", no_argument, &input_mode_opt, allnoconfig},
{"allyesconfig", no_argument, &input_mode_opt, allyesconfig},
{"allmodconfig", no_argument, &input_mode_opt, allmodconfig},
{"alldefconfig", no_argument, &input_mode_opt, alldefconfig},
{"randconfig", no_argument, &input_mode_opt, randconfig},
{"listnewconfig", no_argument, &input_mode_opt, listnewconfig},
{"helpnewconfig", no_argument, &input_mode_opt, helpnewconfig},
{"olddefconfig", no_argument, &input_mode_opt, olddefconfig},
{"yes2modconfig", no_argument, &input_mode_opt, yes2modconfig},
{"mod2yesconfig", no_argument, &input_mode_opt, mod2yesconfig},
{"mod2noconfig", no_argument, &input_mode_opt, mod2noconfig},
{NULL, 0, NULL, 0}
};
static void conf_usage(const char *progname)
{
printf("Usage: %s [options] kconfig_file\n", progname);
printf("\n");
printf("Generic options:\n");
printf(" -h, --help Print this message and exit.\n");
printf(" -s, --silent Do not print log.\n");
printf("\n");
printf("Mode options:\n");
printf(" --listnewconfig List new options\n");
printf(" --helpnewconfig List new options and help text\n");
printf(" --oldaskconfig Start a new configuration using a line-oriented program\n");
printf(" --oldconfig Update a configuration using a provided .config as base\n");
printf(" --syncconfig Similar to oldconfig but generates configuration in\n"
" include/{generated/,config/}\n");
printf(" --olddefconfig Same as oldconfig but sets new symbols to their default value\n");
printf(" --defconfig <file> New config with default defined in <file>\n");
printf(" --savedefconfig <file> Save the minimal current configuration to <file>\n");
printf(" --allnoconfig New config where all options are answered with no\n");
printf(" --allyesconfig New config where all options are answered with yes\n");
printf(" --allmodconfig New config where all options are answered with mod\n");
printf(" --alldefconfig New config with all symbols set to default\n");
printf(" --randconfig New config with random answer to all options\n");
printf(" --yes2modconfig Change answers from yes to mod if possible\n");
printf(" --mod2yesconfig Change answers from mod to yes if possible\n");
printf(" --mod2noconfig Change answers from mod to no if possible\n");
printf(" (If none of the above is given, --oldaskconfig is the default)\n");
printf("\n");
printf("Arguments:\n");
printf(" kconfig_file Top-level Kconfig file.\n");
}
int main(int ac, char **av)
{
const char *progname = av[0];
int opt;
const char *name, *defconfig_file = NULL /* gcc uninit */;
int no_conf_write = 0;
tty_stdio = isatty(0) && isatty(1);
while ((opt = getopt_long(ac, av, "hs", long_opts, NULL)) != -1) {
switch (opt) {
case 'h':
conf_usage(progname);
exit(1);
break;
case 's':
conf_set_message_callback(NULL);
break;
case 0:
input_mode = input_mode_opt;
switch (input_mode) {
case syncconfig:
/*
* syncconfig is invoked during the build stage.
* Suppress distracting
* "configuration written to ..."
*/
conf_set_message_callback(NULL);
sync_kconfig = 1;
break;
case defconfig:
case savedefconfig:
defconfig_file = optarg;
break;
case randconfig:
set_randconfig_seed();
break;
default:
break;
}
default:
break;
}
}
if (ac == optind) {
fprintf(stderr, "%s: Kconfig file missing\n", av[0]);
conf_usage(progname);
exit(1);
}
conf_parse(av[optind]);
//zconfdump(stdout);
switch (input_mode) {
case defconfig:
if (conf_read(defconfig_file)) {
fprintf(stderr,
"***\n"
"*** Can't find default configuration \"%s\"!\n"
"***\n",
defconfig_file);
exit(1);
}
break;
case savedefconfig:
case syncconfig:
case oldaskconfig:
case oldconfig:
case listnewconfig:
case helpnewconfig:
case olddefconfig:
case yes2modconfig:
case mod2yesconfig:
case mod2noconfig:
conf_read(NULL);
break;
case allnoconfig:
case allyesconfig:
case allmodconfig:
case alldefconfig:
case randconfig:
name = getenv("KCONFIG_ALLCONFIG");
if (!name)
break;
if ((strcmp(name, "") != 0) && (strcmp(name, "1") != 0)) {
if (conf_read_simple(name, S_DEF_USER)) {
fprintf(stderr,
"*** Can't read seed configuration \"%s\"!\n",
name);
exit(1);
}
break;
}
switch (input_mode) {
case allnoconfig: name = "allno.config"; break;
case allyesconfig: name = "allyes.config"; break;
case allmodconfig: name = "allmod.config"; break;
case alldefconfig: name = "alldef.config"; break;
case randconfig: name = "allrandom.config"; break;
default: break;
}
if (conf_read_simple(name, S_DEF_USER) &&
conf_read_simple("all.config", S_DEF_USER)) {
fprintf(stderr,
"*** KCONFIG_ALLCONFIG set, but no \"%s\" or \"all.config\" file found\n",
name);
exit(1);
}
break;
default:
break;
}
if (conf_errors())
exit(1);
if (sync_kconfig) {
name = getenv("KCONFIG_NOSILENTUPDATE");
if (name && *name) {
if (conf_get_changed()) {
fprintf(stderr,
"\n*** The configuration requires explicit update.\n\n");
return 1;
}
no_conf_write = 1;
}
}
switch (input_mode) {
case allnoconfig:
conf_set_all_new_symbols(def_no);
break;
case allyesconfig:
conf_set_all_new_symbols(def_yes);
break;
case allmodconfig:
conf_set_all_new_symbols(def_mod);
break;
case alldefconfig:
conf_set_all_new_symbols(def_default);
break;
case randconfig:
conf_set_all_new_symbols(def_random);
break;
case defconfig:
conf_set_all_new_symbols(def_default);
break;
case savedefconfig:
break;
case yes2modconfig:
conf_rewrite_tristates(yes, mod);
break;
case mod2yesconfig:
conf_rewrite_tristates(mod, yes);
break;
case mod2noconfig:
conf_rewrite_tristates(mod, no);
break;
case oldaskconfig:
rootEntry = &rootmenu;
conf(&rootmenu);
input_mode = oldconfig;
/* fall through */
case oldconfig:
case listnewconfig:
case helpnewconfig:
case syncconfig:
/* Update until a loop caused no more changes */
do {
conf_cnt = 0;
check_conf(&rootmenu);
} while (conf_cnt);
break;
case olddefconfig:
default:
break;
}
if (sym_dep_errors())
exit(1);
if (input_mode == savedefconfig) {
if (conf_write_defconfig(defconfig_file)) {
fprintf(stderr, "n*** Error while saving defconfig to: %s\n\n",
defconfig_file);
return 1;
}
} else if (input_mode != listnewconfig && input_mode != helpnewconfig) {
if (!no_conf_write && conf_write(NULL)) {
fprintf(stderr, "\n*** Error during writing of the configuration.\n\n");
exit(1);
}
/*
* Create auto.conf if it does not exist.
* This prevents GNU Make 4.1 or older from emitting
* "include/config/auto.conf: No such file or directory"
* in the top-level Makefile
*
* syncconfig always creates or updates auto.conf because it is
* used during the build.
*/
if (conf_write_autoconf(sync_kconfig) && sync_kconfig) {
fprintf(stderr,
"\n*** Error during sync of the configuration.\n\n");
return 1;
}
}
return 0;
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,327 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>
*/
#ifndef EXPR_H
#define EXPR_H
#ifdef __cplusplus
extern "C" {
#endif
#include <assert.h>
#include <stdio.h>
#ifndef __cplusplus
#include <stdbool.h>
#endif
#include <list_types.h>
typedef enum tristate {
no, mod, yes
} tristate;
enum expr_type {
E_NONE, E_OR, E_AND, E_NOT,
E_EQUAL, E_UNEQUAL, E_LTH, E_LEQ, E_GTH, E_GEQ,
E_SYMBOL, E_RANGE
};
union expr_data {
struct expr * const expr;
struct symbol * const sym;
void *_initdata;
};
/**
* struct expr - expression
*
* @node: link node for the hash table
* @type: expressoin type
* @val: calculated tristate value
* @val_is_valid: indicate whether the value is valid
* @left: left node
* @right: right node
*/
struct expr {
struct hlist_node node;
enum expr_type type;
tristate val;
bool val_is_valid;
union expr_data left, right;
};
#define EXPR_OR(dep1, dep2) (((dep1)>(dep2))?(dep1):(dep2))
#define EXPR_AND(dep1, dep2) (((dep1)<(dep2))?(dep1):(dep2))
#define EXPR_NOT(dep) (2-(dep))
struct expr_value {
struct expr *expr;
tristate tri;
};
struct symbol_value {
void *val;
tristate tri;
};
enum symbol_type {
S_UNKNOWN, S_BOOLEAN, S_TRISTATE, S_INT, S_HEX, S_STRING
};
/* enum values are used as index to symbol.def[] */
enum {
S_DEF_USER, /* main user value */
S_DEF_AUTO, /* values read from auto.conf */
S_DEF_DEF3, /* Reserved for UI usage */
S_DEF_DEF4, /* Reserved for UI usage */
S_DEF_COUNT
};
/*
* Represents a configuration symbol.
*
* Choices are represented as a special kind of symbol with null name.
*
* @choice_link: linked to menu::choice_members
*/
struct symbol {
/* link node for the hash table */
struct hlist_node node;
/* The name of the symbol, e.g. "FOO" for 'config FOO' */
char *name;
/* S_BOOLEAN, S_TRISTATE, ... */
enum symbol_type type;
/*
* The calculated value of the symbol. The SYMBOL_VALID bit is set in
* 'flags' when this is up to date. Note that this value might differ
* from the user value set in e.g. a .config file, due to visibility.
*/
struct symbol_value curr;
/*
* Values for the symbol provided from outside. def[S_DEF_USER] holds
* the .config value.
*/
struct symbol_value def[S_DEF_COUNT];
/*
* An upper bound on the tristate value the user can set for the symbol
* if it is a boolean or tristate. Calculated from prompt dependencies,
* which also inherit dependencies from enclosing menus, choices, and
* ifs. If 'n', the user value will be ignored.
*
* Symbols lacking prompts always have visibility 'n'.
*/
tristate visible;
/* config entries associated with this symbol */
struct list_head menus;
struct list_head choice_link;
/* SYMBOL_* flags */
int flags;
/* List of properties. See prop_type. */
struct property *prop;
/* Dependencies from enclosing menus, choices, and ifs */
struct expr_value dir_dep;
/* Reverse dependencies through being selected by other symbols */
struct expr_value rev_dep;
/*
* "Weak" reverse dependencies through being implied by other symbols
*/
struct expr_value implied;
};
#define SYMBOL_CONST 0x0001 /* symbol is const */
#define SYMBOL_CHECK 0x0008 /* used during dependency checking */
#define SYMBOL_VALID 0x0080 /* set when symbol.curr is calculated */
#define SYMBOL_WRITE 0x0200 /* write symbol to file (KCONFIG_CONFIG) */
#define SYMBOL_WRITTEN 0x0800 /* track info to avoid double-write to .config */
#define SYMBOL_CHECKED 0x2000 /* used during dependency checking */
#define SYMBOL_WARNED 0x8000 /* warning has been issued */
/* Set when symbol.def[] is used */
#define SYMBOL_DEF 0x10000 /* First bit of SYMBOL_DEF */
#define SYMBOL_DEF_USER 0x10000 /* symbol.def[S_DEF_USER] is valid */
#define SYMBOL_DEF_AUTO 0x20000 /* symbol.def[S_DEF_AUTO] is valid */
#define SYMBOL_DEF3 0x40000 /* symbol.def[S_DEF_3] is valid */
#define SYMBOL_DEF4 0x80000 /* symbol.def[S_DEF_4] is valid */
#define SYMBOL_MAXLENGTH 256
/* A property represent the config options that can be associated
* with a config "symbol".
* Sample:
* config FOO
* default y
* prompt "foo prompt"
* select BAR
* config BAZ
* int "BAZ Value"
* range 1..255
*
* Please, also check parser.y:print_symbol() when modifying the
* list of property types!
*/
enum prop_type {
P_UNKNOWN,
P_PROMPT, /* prompt "foo prompt" or "BAZ Value" */
P_COMMENT, /* text associated with a comment */
P_MENU, /* prompt associated with a menu or menuconfig symbol */
P_DEFAULT, /* default y */
P_SELECT, /* select BAR */
P_IMPLY, /* imply BAR */
P_RANGE, /* range 7..100 (for a symbol) */
};
struct property {
struct property *next; /* next property - null if last */
enum prop_type type; /* type of property */
const char *text; /* the prompt value - P_PROMPT, P_MENU, P_COMMENT */
struct expr_value visible;
struct expr *expr; /* the optional conditional part of the property */
struct menu *menu; /* the menu the property are associated with
* valid for: P_SELECT, P_RANGE,
* P_PROMPT, P_DEFAULT, P_MENU, P_COMMENT */
const char *filename; /* what file was this property defined */
int lineno; /* what lineno was this property defined */
};
#define for_all_properties(sym, st, tok) \
for (st = sym->prop; st; st = st->next) \
if (st->type == (tok))
#define for_all_defaults(sym, st) for_all_properties(sym, st, P_DEFAULT)
#define for_all_prompts(sym, st) \
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;
/* The parent menu node, corresponding to e.g. a menu or choice */
struct menu *parent;
/* The first child menu node, for e.g. menus and choices */
struct menu *list;
/*
* The symbol associated with the menu node. Choices are implemented as
* a special kind of symbol. NULL for menus, comments, and ifs.
*/
struct symbol *sym;
struct list_head link; /* link to symbol::menus */
struct list_head choice_members;
/*
* The prompt associated with the node. This holds the prompt for a
* symbol as well as the text for a menu or comment, along with the
* type (P_PROMPT, P_MENU, etc.)
*/
struct property *prompt;
/*
* 'visible if' dependencies. If more than one is given, they will be
* ANDed together.
*/
struct expr *visibility;
/*
* Ordinary dependencies from e.g. 'depends on' and 'if', ANDed
* together
*/
struct expr *dep;
/* MENU_* flags */
unsigned int flags;
/* Any help text associated with the node */
char *help;
/* The location where the menu node appears in the Kconfig files */
const char *filename;
int lineno;
/* For use by front ends that need to store auxiliary data */
void *data;
};
/*
* Set on a menu node when the corresponding symbol changes state in some way.
* Can be checked by front ends.
*/
#define MENU_CHANGED 0x0001
#define MENU_ROOT 0x0002
struct jump_key {
struct list_head entries;
size_t offset;
struct menu *target;
};
extern struct symbol symbol_yes, symbol_no, symbol_mod;
extern struct symbol *modules_sym;
extern int cdebug;
struct expr *expr_alloc_symbol(struct symbol *sym);
struct expr *expr_alloc_one(enum expr_type type, struct expr *ce);
struct expr *expr_alloc_two(enum expr_type type, struct expr *e1, struct expr *e2);
struct expr *expr_alloc_comp(enum expr_type type, struct symbol *s1, struct symbol *s2);
struct expr *expr_alloc_and(struct expr *e1, struct expr *e2);
struct expr *expr_alloc_or(struct expr *e1, struct expr *e2);
void expr_eliminate_eq(struct expr **ep1, struct expr **ep2);
bool expr_eq(struct expr *e1, struct expr *e2);
tristate expr_calc_value(struct expr *e);
struct expr *expr_eliminate_dups(struct expr *e);
struct expr *expr_transform(struct expr *e);
bool expr_contains_symbol(struct expr *dep, struct symbol *sym);
bool expr_depends_symbol(struct expr *dep, struct symbol *sym);
struct expr *expr_trans_compare(struct expr *e, enum expr_type type, struct symbol *sym);
void expr_fprint(struct expr *e, FILE *out);
struct gstr; /* forward */
void expr_gstr_print(const struct expr *e, struct gstr *gs);
void expr_gstr_print_revdep(struct expr *e, struct gstr *gs,
tristate pr_type, const char *title);
static inline bool expr_is_yes(const struct expr *e)
{
return !e || (e->type == E_SYMBOL && e->left.sym == &symbol_yes);
}
#ifdef __cplusplus
}
#endif
#endif /* EXPR_H */

View File

@@ -0,0 +1,35 @@
#!/bin/sh
# SPDX-License-Identifier: GPL-2.0
set -eu
cflags=$1
libs=$2
PKG="gtk+-2.0 gmodule-2.0 libglade-2.0"
if [ -z "$(command -v ${HOSTPKG_CONFIG})" ]; then
echo >&2 "*"
echo >&2 "* 'make gconfig' requires '${HOSTPKG_CONFIG}'. Please install it."
echo >&2 "*"
exit 1
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 "* 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}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,661 @@
<?xml version="1.0" standalone="no"?> <!--*- mode: xml -*-->
<glade-interface>
<widget 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>
<property name="window_position">GTK_WIN_POS_NONE</property>
<property name="modal">False</property>
<property name="default_width">640</property>
<property name="default_height">480</property>
<property name="resizable">True</property>
<property name="destroy_with_parent">False</property>
<property name="decorated">True</property>
<property name="skip_taskbar_hint">False</property>
<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">
<property name="visible">True</property>
<property name="homogeneous">False</property>
<property name="spacing">0</property>
<child>
<widget class="GtkMenuBar" id="menubar1">
<property name="visible">True</property>
<child>
<widget 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>
<widget class="GtkImageMenuItem" id="load1">
<property name="visible">True</property>
<property name="tooltip" 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>
</child>
<child>
<widget class="GtkImageMenuItem" id="save1">
<property name="visible">True</property>
<property name="tooltip" 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>
</child>
<child>
<widget class="GtkImageMenuItem" id="save_as1">
<property name="visible">True</property>
<property name="tooltip" 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>
</child>
<child>
<widget class="GtkSeparatorMenuItem" id="separator1">
<property name="visible">True</property>
</widget>
</child>
<child>
<widget class="GtkImageMenuItem" 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>
</child>
</widget>
</child>
</widget>
</child>
<child>
<widget 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>
<widget class="GtkCheckMenuItem" id="show_name1">
<property name="visible">True</property>
<property name="tooltip" 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>
</child>
<child>
<widget class="GtkCheckMenuItem" id="show_range1">
<property name="visible">True</property>
<property name="tooltip" 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>
</child>
<child>
<widget class="GtkCheckMenuItem" id="show_data1">
<property name="visible">True</property>
<property name="tooltip" 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>
</child>
<child>
<widget class="GtkSeparatorMenuItem" id="separator2">
<property name="visible">True</property>
</widget>
</child>
<child>
<widget class="GtkRadioMenuItem" id="set_option_mode1">
<property name="visible">True</property>
<property name="tooltip" 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>
</child>
<child>
<widget class="GtkRadioMenuItem" id="set_option_mode2">
<property name="visible">True</property>
<property name="tooltip" 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>
</child>
<child>
<widget class="GtkRadioMenuItem" id="set_option_mode3">
<property name="visible">True</property>
<property name="tooltip" 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>
</child>
</widget>
</child>
</widget>
</child>
<child>
<widget 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>
<widget class="GtkImageMenuItem" 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>
</child>
<child>
<widget class="GtkImageMenuItem" 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>
</child>
<child>
<widget class="GtkImageMenuItem" 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>
</child>
</widget>
</child>
</widget>
</child>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">False</property>
<property name="fill">False</property>
</packing>
</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">
<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">
<property name="visible">True</property>
<property name="tooltip" translatable="yes">Goes up of 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>
<packing>
<property name="expand">False</property>
<property name="homogeneous">True</property>
</packing>
</child>
<child>
<widget 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">
<property name="visible">True</property>
</widget>
</child>
</widget>
<packing>
<property name="expand">False</property>
<property name="homogeneous">False</property>
</packing>
</child>
<child>
<widget class="GtkToolButton" id="button2">
<property name="visible">True</property>
<property name="tooltip" 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>
<packing>
<property name="expand">False</property>
<property name="homogeneous">True</property>
</packing>
</child>
<child>
<widget class="GtkToolButton" id="button3">
<property name="visible">True</property>
<property name="tooltip" 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>
<packing>
<property name="expand">False</property>
<property name="homogeneous">True</property>
</packing>
</child>
<child>
<widget 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">
<property name="visible">True</property>
</widget>
</child>
</widget>
<packing>
<property name="expand">False</property>
<property name="homogeneous">False</property>
</packing>
</child>
<child>
<widget class="GtkToolButton" id="button4">
<property name="visible">True</property>
<property name="tooltip" 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>
<packing>
<property name="expand">False</property>
<property name="homogeneous">True</property>
</packing>
</child>
<child>
<widget class="GtkToolButton" id="button5">
<property name="visible">True</property>
<property name="tooltip" 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>
<packing>
<property name="expand">False</property>
<property name="homogeneous">True</property>
</packing>
</child>
<child>
<widget class="GtkToolButton" id="button6">
<property name="visible">True</property>
<property name="tooltip" 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>
<packing>
<property name="expand">False</property>
<property name="homogeneous">True</property>
</packing>
</child>
<child>
<widget 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">
<property name="visible">True</property>
</widget>
</child>
</widget>
<packing>
<property name="expand">False</property>
<property name="homogeneous">False</property>
</packing>
</child>
<child>
<widget 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="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>
<packing>
<property name="expand">False</property>
<property name="homogeneous">True</property>
</packing>
</child>
<child>
<widget 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="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>
<packing>
<property name="expand">False</property>
<property name="homogeneous">True</property>
</packing>
</child>
</widget>
</child>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">False</property>
<property name="fill">False</property>
</packing>
</child>
<child>
<widget class="GtkHPaned" 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">
<property name="visible">True</property>
<property name="hscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
<property name="vscrollbar_policy">GTK_POLICY_AUTOMATIC</property>
<property name="shadow_type">GTK_SHADOW_IN</property>
<property name="window_placement">GTK_CORNER_TOP_LEFT</property>
<child>
<widget 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>
</child>
</widget>
<packing>
<property name="shrink">True</property>
<property name="resize">False</property>
</packing>
</child>
<child>
<widget class="GtkVPaned" id="vpaned1">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="position">0</property>
<child>
<widget 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>
<property name="shadow_type">GTK_SHADOW_IN</property>
<property name="window_placement">GTK_CORNER_TOP_LEFT</property>
<child>
<widget class="GtkTreeView" id="treeview2">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="has_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: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>
</child>
</widget>
<packing>
<property name="shrink">True</property>
<property name="resize">False</property>
</packing>
</child>
<child>
<widget 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>
<property name="shadow_type">GTK_SHADOW_IN</property>
<property name="window_placement">GTK_CORNER_TOP_LEFT</property>
<child>
<widget class="GtkTextView" id="textview3">
<property name="visible">True</property>
<property name="can_focus">True</property>
<property name="editable">False</property>
<property name="overwrite">False</property>
<property name="accepts_tab">True</property>
<property name="justification">GTK_JUSTIFY_LEFT</property>
<property name="wrap_mode">GTK_WRAP_WORD</property>
<property name="cursor_visible">True</property>
<property name="pixels_above_lines">0</property>
<property name="pixels_below_lines">0</property>
<property name="pixels_inside_wrap">0</property>
<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>
</child>
</widget>
<packing>
<property name="shrink">True</property>
<property name="resize">True</property>
</packing>
</child>
</widget>
<packing>
<property name="shrink">True</property>
<property name="resize">True</property>
</packing>
</child>
</widget>
<packing>
<property name="padding">0</property>
<property name="expand">True</property>
<property name="fill">True</property>
</packing>
</child>
</widget>
</child>
</widget>
</glade-interface>

View File

@@ -0,0 +1,328 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>
*/
#include "images.h"
const char * const xpm_load[] = {
"22 22 5 1",
". c None",
"# c #000000",
"c c #838100",
"a c #ffff00",
"b c #ffffff",
"......................",
"......................",
"......................",
"............####....#.",
"...........#....##.##.",
"..................###.",
".................####.",
".####...........#####.",
"#abab##########.......",
"#babababababab#.......",
"#ababababababa#.......",
"#babababababab#.......",
"#ababab###############",
"#babab##cccccccccccc##",
"#abab##cccccccccccc##.",
"#bab##cccccccccccc##..",
"#ab##cccccccccccc##...",
"#b##cccccccccccc##....",
"###cccccccccccc##.....",
"##cccccccccccc##......",
"###############.......",
"......................"};
const char * const xpm_save[] = {
"22 22 5 1",
". c None",
"# c #000000",
"a c #838100",
"b c #c5c2c5",
"c c #cdb6d5",
"......................",
".####################.",
".#aa#bbbbbbbbbbbb#bb#.",
".#aa#bbbbbbbbbbbb#bb#.",
".#aa#bbbbbbbbbcbb####.",
".#aa#bbbccbbbbbbb#aa#.",
".#aa#bbbccbbbbbbb#aa#.",
".#aa#bbbbbbbbbbbb#aa#.",
".#aa#bbbbbbbbbbbb#aa#.",
".#aa#bbbbbbbbbbbb#aa#.",
".#aa#bbbbbbbbbbbb#aa#.",
".#aaa############aaa#.",
".#aaaaaaaaaaaaaaaaaa#.",
".#aaaaaaaaaaaaaaaaaa#.",
".#aaa#############aa#.",
".#aaa#########bbb#aa#.",
".#aaa#########bbb#aa#.",
".#aaa#########bbb#aa#.",
".#aaa#########bbb#aa#.",
".#aaa#########bbb#aa#.",
"..##################..",
"......................"};
const char * const xpm_back[] = {
"22 22 3 1",
". c None",
"# c #000083",
"a c #838183",
"......................",
"......................",
"......................",
"......................",
"......................",
"...........######a....",
"..#......##########...",
"..##...####......##a..",
"..###.###.........##..",
"..######..........##..",
"..#####...........##..",
"..######..........##..",
"..#######.........##..",
"..########.......##a..",
"...............a###...",
"...............###....",
"......................",
"......................",
"......................",
"......................",
"......................",
"......................"};
const char * const xpm_tree_view[] = {
"22 22 2 1",
". c None",
"# c #000000",
"......................",
"......................",
"......#...............",
"......#...............",
"......#...............",
"......#...............",
"......#...............",
"......########........",
"......#...............",
"......#...............",
"......#...............",
"......#...............",
"......#...............",
"......########........",
"......#...............",
"......#...............",
"......#...............",
"......#...............",
"......#...............",
"......########........",
"......................",
"......................"};
const char * const xpm_single_view[] = {
"22 22 2 1",
". c None",
"# c #000000",
"......................",
"......................",
"..........#...........",
"..........#...........",
"..........#...........",
"..........#...........",
"..........#...........",
"..........#...........",
"..........#...........",
"..........#...........",
"..........#...........",
"..........#...........",
"..........#...........",
"..........#...........",
"..........#...........",
"..........#...........",
"..........#...........",
"..........#...........",
"..........#...........",
"..........#...........",
"......................",
"......................"};
const char * const xpm_split_view[] = {
"22 22 2 1",
". c None",
"# c #000000",
"......................",
"......................",
"......#......#........",
"......#......#........",
"......#......#........",
"......#......#........",
"......#......#........",
"......#......#........",
"......#......#........",
"......#......#........",
"......#......#........",
"......#......#........",
"......#......#........",
"......#......#........",
"......#......#........",
"......#......#........",
"......#......#........",
"......#......#........",
"......#......#........",
"......#......#........",
"......................",
"......................"};
const char * const xpm_symbol_no[] = {
"12 12 2 1",
" c white",
". c black",
" ",
" .......... ",
" . . ",
" . . ",
" . . ",
" . . ",
" . . ",
" . . ",
" . . ",
" . . ",
" .......... ",
" "};
const char * const xpm_symbol_mod[] = {
"12 12 2 1",
" c white",
". c black",
" ",
" .......... ",
" . . ",
" . . ",
" . .. . ",
" . .... . ",
" . .... . ",
" . .. . ",
" . . ",
" . . ",
" .......... ",
" "};
const char * const xpm_symbol_yes[] = {
"12 12 2 1",
" c white",
". c black",
" ",
" .......... ",
" . . ",
" . . ",
" . . . ",
" . .. . ",
" . . .. . ",
" . .... . ",
" . .. . ",
" . . ",
" .......... ",
" "};
const char * const xpm_choice_no[] = {
"12 12 2 1",
" c white",
". c black",
" ",
" .... ",
" .. .. ",
" . . ",
" . . ",
" . . ",
" . . ",
" . . ",
" . . ",
" .. .. ",
" .... ",
" "};
const char * const xpm_choice_yes[] = {
"12 12 2 1",
" c white",
". c black",
" ",
" .... ",
" .. .. ",
" . . ",
" . .. . ",
" . .... . ",
" . .... . ",
" . .. . ",
" . . ",
" .. .. ",
" .... ",
" "};
const char * const xpm_menu[] = {
"12 12 2 1",
" c white",
". c black",
" ",
" .......... ",
" . . ",
" . .. . ",
" . .... . ",
" . ...... . ",
" . ...... . ",
" . .... . ",
" . .. . ",
" . . ",
" .......... ",
" "};
const char * const xpm_menu_inv[] = {
"12 12 2 1",
" c white",
". c black",
" ",
" .......... ",
" .......... ",
" .. ...... ",
" .. .... ",
" .. .. ",
" .. .. ",
" .. .... ",
" .. ...... ",
" .......... ",
" .......... ",
" "};
const char * const xpm_menuback[] = {
"12 12 2 1",
" c white",
". c black",
" ",
" .......... ",
" . . ",
" . .. . ",
" . .... . ",
" . ...... . ",
" . ...... . ",
" . .... . ",
" . .. . ",
" . . ",
" .......... ",
" "};
const char * const xpm_void[] = {
"12 12 2 1",
" c white",
". c black",
" ",
" ",
" ",
" ",
" ",
" ",
" ",
" ",
" ",
" ",
" ",
" "};

View File

@@ -0,0 +1,33 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>
*/
#ifndef IMAGES_H
#define IMAGES_H
#ifdef __cplusplus
extern "C" {
#endif
extern const char * const xpm_load[];
extern const char * const xpm_save[];
extern const char * const xpm_back[];
extern const char * const xpm_tree_view[];
extern const char * const xpm_single_view[];
extern const char * const xpm_split_view[];
extern const char * const xpm_symbol_no[];
extern const char * const xpm_symbol_mod[];
extern const char * const xpm_symbol_yes[];
extern const char * const xpm_choice_no[];
extern const char * const xpm_choice_yes[];
extern const char * const xpm_menu[];
extern const char * const xpm_menu_inv[];
extern const char * const xpm_menuback[];
extern const char * const xpm_void[];
#ifdef __cplusplus
}
#endif
#endif /* IMAGES_H */

View File

@@ -0,0 +1,27 @@
/* SPDX-License-Identifier: GPL-2.0-only */
#ifndef INTERNAL_H
#define INTERNAL_H
#include <hashtable.h>
#define SYMBOL_HASHSIZE (1U << 14)
extern HASHTABLE_DECLARE(sym_hashtable, SYMBOL_HASHSIZE);
#define for_all_symbols(sym) \
hash_for_each(sym_hashtable, sym, node)
#define EXPR_HASHSIZE (1U << 14)
extern HASHTABLE_DECLARE(expr_hashtable, EXPR_HASHSIZE);
void expr_invalidate_all(void);
struct menu;
extern struct menu *current_menu, *current_entry;
extern const char *cur_filename;
extern int cur_lineno;
#endif /* INTERNAL_H */

View File

@@ -0,0 +1,460 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>
*/
%option nostdinit noyywrap never-interactive full ecs
%option 8bit nodefault yylineno
%x ASSIGN_VAL HELP STRING
%{
#include <assert.h>
#include <limits.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <xalloc.h>
#include "lkc.h"
#include "preprocess.h"
#include "parser.tab.h"
#define YY_DECL static int yylex1(void)
#define START_STRSIZE 16
/* The Kconfig file currently being parsed. */
const char *cur_filename;
/*
* The line number of the current statement. This does not match yylineno.
* yylineno is used by the lexer, while cur_lineno is used by the parser.
*/
int cur_lineno;
static int prev_prev_token = T_EOL;
static int prev_token = T_EOL;
static char *text;
static int text_size, text_asize;
struct buffer {
struct buffer *parent;
YY_BUFFER_STATE state;
int yylineno;
const char *filename;
int source_lineno;
};
static struct buffer *current_buf;
static int last_ts, first_ts;
static char *expand_token(const char *in, size_t n);
static void append_expanded_string(const char *in);
static void zconf_endhelp(void);
static void zconf_endfile(void);
static void new_string(void)
{
text = xmalloc(START_STRSIZE);
text_asize = START_STRSIZE;
text_size = 0;
*text = 0;
}
static void append_string(const char *str, int size)
{
int new_size = text_size + size + 1;
if (new_size > text_asize) {
new_size += START_STRSIZE - 1;
new_size &= -START_STRSIZE;
text = xrealloc(text, new_size);
text_asize = new_size;
}
memcpy(text + text_size, str, size);
text_size += size;
text[text_size] = 0;
}
static void alloc_string(const char *str, int size)
{
text = xmalloc(size + 1);
memcpy(text, str, size);
text[size] = 0;
}
static void warn_ignored_character(char chr)
{
fprintf(stderr,
"%s:%d:warning: ignoring unsupported character '%c'\n",
cur_filename, yylineno, chr);
}
%}
n [A-Za-z0-9_-]
%%
char open_quote = 0;
#.* /* ignore comment */
[ \t]* /* whitespaces */
\\\n /* escaped new line */
\n return T_EOL;
"bool" return T_BOOL;
"choice" return T_CHOICE;
"comment" return T_COMMENT;
"config" return T_CONFIG;
"def_bool" return T_DEF_BOOL;
"def_tristate" return T_DEF_TRISTATE;
"default" return T_DEFAULT;
"depends" return T_DEPENDS;
"endchoice" return T_ENDCHOICE;
"endif" return T_ENDIF;
"endmenu" return T_ENDMENU;
"help" return T_HELP;
"hex" return T_HEX;
"if" return T_IF;
"imply" return T_IMPLY;
"int" return T_INT;
"mainmenu" return T_MAINMENU;
"menu" return T_MENU;
"menuconfig" return T_MENUCONFIG;
"modules" return T_MODULES;
"on" return T_ON;
"prompt" return T_PROMPT;
"range" return T_RANGE;
"select" return T_SELECT;
"source" return T_SOURCE;
"string" return T_STRING;
"tristate" return T_TRISTATE;
"visible" return T_VISIBLE;
"||" return T_OR;
"&&" return T_AND;
"=" return T_EQUAL;
"!=" return T_UNEQUAL;
"<" return T_LESS;
"<=" return T_LESS_EQUAL;
">" return T_GREATER;
">=" return T_GREATER_EQUAL;
"!" return T_NOT;
"(" return T_OPEN_PAREN;
")" return T_CLOSE_PAREN;
":=" return T_COLON_EQUAL;
"+=" return T_PLUS_EQUAL;
\"|\' {
open_quote = yytext[0];
new_string();
BEGIN(STRING);
}
{n}+ {
alloc_string(yytext, yyleng);
yylval.string = text;
return T_WORD;
}
({n}|$)+ {
/* this token includes at least one '$' */
yylval.string = expand_token(yytext, yyleng);
if (strlen(yylval.string))
return T_WORD;
free(yylval.string);
}
. warn_ignored_character(*yytext);
<ASSIGN_VAL>{
[^[:blank:]\n]+.* {
alloc_string(yytext, yyleng);
yylval.string = text;
return T_ASSIGN_VAL;
}
\n { BEGIN(INITIAL); return T_EOL; }
.
}
<STRING>{
"$".* append_expanded_string(yytext);
[^$'"\\\n]+ {
append_string(yytext, yyleng);
}
\\.? {
append_string(yytext + 1, yyleng - 1);
}
\'|\" {
if (open_quote == yytext[0]) {
BEGIN(INITIAL);
yylval.string = text;
return T_WORD_QUOTE;
} else
append_string(yytext, 1);
}
\n {
fprintf(stderr,
"%s:%d:warning: multi-line strings not supported\n",
cur_filename, cur_lineno);
unput('\n');
BEGIN(INITIAL);
yylval.string = text;
return T_WORD_QUOTE;
}
<<EOF>> {
BEGIN(INITIAL);
yylval.string = text;
return T_WORD_QUOTE;
}
}
<HELP>{
[ \t]+ {
int ts, i;
ts = 0;
for (i = 0; i < yyleng; i++) {
if (yytext[i] == '\t')
ts = (ts & ~7) + 8;
else
ts++;
}
last_ts = ts;
if (first_ts) {
if (ts < first_ts) {
zconf_endhelp();
return T_HELPTEXT;
}
ts -= first_ts;
while (ts > 8) {
append_string(" ", 8);
ts -= 8;
}
append_string(" ", ts);
}
}
[ \t]*\n/[^ \t\n] {
zconf_endhelp();
return T_HELPTEXT;
}
[ \t]*\n {
append_string("\n", 1);
}
[^ \t\n].* {
while (yyleng) {
if ((yytext[yyleng-1] != ' ') && (yytext[yyleng-1] != '\t'))
break;
yyleng--;
}
append_string(yytext, yyleng);
if (!first_ts)
first_ts = last_ts;
}
<<EOF>> {
zconf_endhelp();
return T_HELPTEXT;
}
}
<<EOF>> {
BEGIN(INITIAL);
if (prev_token != T_EOL && prev_token != T_HELPTEXT)
fprintf(stderr, "%s:%d:warning: no new line at end of file\n",
cur_filename, yylineno);
if (current_buf) {
zconf_endfile();
return T_EOL;
}
fclose(yyin);
yyterminate();
}
%%
/* second stage lexer */
int yylex(void)
{
int token;
repeat:
token = yylex1();
if (prev_token == T_EOL || prev_token == T_HELPTEXT) {
if (token == T_EOL)
/* Do not pass unneeded T_EOL to the parser. */
goto repeat;
else
/*
* For the parser, update lineno at the first token
* of each statement. Generally, \n is a statement
* terminator in Kconfig, but it is not always true
* because \n could be escaped by a backslash.
*/
cur_lineno = yylineno;
}
if (prev_prev_token == T_EOL && prev_token == T_WORD &&
(token == T_EQUAL || token == T_COLON_EQUAL || token == T_PLUS_EQUAL))
BEGIN(ASSIGN_VAL);
prev_prev_token = prev_token;
prev_token = token;
return token;
}
static char *expand_token(const char *in, size_t n)
{
char *out;
int c;
char c2;
const char *rest, *end;
new_string();
append_string(in, n);
/*
* get the whole line because we do not know the end of token.
* input() returns 0 (not EOF!) when it reachs the end of file.
*/
while ((c = input()) != 0) {
if (c == '\n') {
unput(c);
break;
}
c2 = c;
append_string(&c2, 1);
}
rest = text;
out = expand_one_token(&rest);
/* push back unused characters to the input stream */
end = rest + strlen(rest);
while (end > rest)
unput(*--end);
free(text);
return out;
}
static void append_expanded_string(const char *str)
{
const char *end;
char *res;
str++;
res = expand_dollar(&str);
/* push back unused characters to the input stream */
end = str + strlen(str);
while (end > str)
unput(*--end);
append_string(res, strlen(res));
free(res);
}
void zconf_starthelp(void)
{
new_string();
last_ts = first_ts = 0;
BEGIN(HELP);
}
static void zconf_endhelp(void)
{
yylval.string = text;
BEGIN(INITIAL);
}
/*
* Try to open specified file with following names:
* ./name
* $(srctree)/name
* The latter is used when srctree is separate from objtree
* when compiling the kernel.
* Return NULL if file is not found.
*/
FILE *zconf_fopen(const char *name)
{
char *env, fullname[PATH_MAX+1];
FILE *f;
f = fopen(name, "r");
if (!f && name != NULL && name[0] != '/') {
env = getenv(SRCTREE);
if (env) {
snprintf(fullname, sizeof(fullname),
"%s/%s", env, name);
f = fopen(fullname, "r");
}
}
return f;
}
void zconf_initscan(const char *name)
{
yyin = zconf_fopen(name);
if (!yyin) {
fprintf(stderr, "can't find file %s\n", name);
exit(1);
}
cur_filename = file_lookup(name);
yylineno = 1;
}
void zconf_nextfile(const char *name)
{
struct buffer *buf = xmalloc(sizeof(*buf));
bool recur_include = false;
buf->state = YY_CURRENT_BUFFER;
buf->yylineno = yylineno;
buf->filename = cur_filename;
buf->source_lineno = cur_lineno;
buf->parent = current_buf;
current_buf = buf;
yyin = zconf_fopen(name);
if (!yyin) {
fprintf(stderr, "%s:%d: can't open file \"%s\"\n",
cur_filename, cur_lineno, name);
exit(1);
}
yy_switch_to_buffer(yy_create_buffer(yyin, YY_BUF_SIZE));
for (buf = current_buf; buf; buf = buf->parent) {
if (!strcmp(buf->filename, name))
recur_include = true;
}
if (recur_include) {
fprintf(stderr,
"Recursive inclusion detected.\n"
"Inclusion path:\n"
" current file : %s\n", name);
for (buf = current_buf; buf; buf = buf->parent)
fprintf(stderr, " included from: %s:%d\n",
buf->filename, buf->source_lineno);
exit(1);
}
yylineno = 1;
cur_filename = file_lookup(name);
}
static void zconf_endfile(void)
{
struct buffer *tmp;
fclose(yyin);
yy_delete_buffer(YY_CURRENT_BUFFER);
yy_switch_to_buffer(current_buf->state);
yylineno = current_buf->yylineno;
cur_filename = current_buf->filename;
tmp = current_buf;
current_buf = current_buf->parent;
free(tmp);
}

View File

@@ -0,0 +1,136 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>
*/
#ifndef LKC_H
#define LKC_H
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include "expr.h"
#ifdef __cplusplus
extern "C" {
#endif
#include "lkc_proto.h"
#define SRCTREE "l4re_srcdir"
#ifndef CONFIG_
#define CONFIG_ "CONFIG_"
#endif
static inline const char *CONFIG_prefix(void)
{
return getenv( "CONFIG_" ) ?: CONFIG_;
}
#undef CONFIG_
#define CONFIG_ CONFIG_prefix()
extern int yylineno;
void zconfdump(FILE *out);
void zconf_starthelp(void);
FILE *zconf_fopen(const char *name);
void zconf_initscan(const char *name);
void zconf_nextfile(const char *name);
/* confdata.c */
extern struct gstr autoconf_cmd;
const char *conf_get_configname(void);
/* confdata.c and expr.c */
static inline void xfwrite(const void *str, size_t len, size_t count, FILE *out)
{
assert(len != 0);
if (fwrite(str, len, count, out) != count)
fprintf(stderr, "Error in writing or end of file.\n");
}
/* util.c */
const char *file_lookup(const char *name);
/* lexer.l */
int yylex(void);
struct gstr {
size_t len;
char *s;
/*
* when max_width is not zero long lines in string s (if any) get
* wrapped not to exceed the max_width value
*/
int max_width;
};
struct gstr str_new(void);
void str_free(struct gstr *gs);
void str_append(struct gstr *gs, const char *s);
void str_printf(struct gstr *gs, const char *fmt, ...);
char *str_get(const struct gstr *gs);
/* menu.c */
struct menu *menu_next(struct menu *menu, struct menu *root);
#define menu_for_each_sub_entry(menu, root) \
for (menu = menu_next(root, root); menu; menu = menu_next(menu, root))
#define menu_for_each_entry(menu) \
menu_for_each_sub_entry(menu, &rootmenu)
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, 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,
struct expr *dep);
void menu_add_expr(enum prop_type type, struct expr *expr, struct expr *dep);
void menu_add_symbol(enum prop_type type, struct symbol *sym, struct expr *dep);
void menu_finalize(void);
void menu_set_type(int type);
extern struct menu rootmenu;
bool menu_is_empty(struct menu *menu);
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);
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);
/* symbol.c */
void sym_clear_all_valid(void);
struct symbol *sym_choice_default(struct menu *choice);
struct symbol *sym_calc_choice(struct menu *choice);
struct property *sym_get_range_prop(struct symbol *sym);
const char *sym_get_string_default(struct symbol *sym);
struct symbol *sym_check_deps(struct symbol *sym);
struct symbol *prop_get_symbol(const struct property *prop);
static inline tristate sym_get_tristate_value(const struct symbol *sym)
{
return sym->curr.tri;
}
static inline bool sym_is_choice(const struct symbol *sym)
{
/* A choice is a symbol with no name */
return sym->name == NULL;
}
bool sym_is_choice_value(const struct symbol *sym);
static inline bool sym_has_value(const struct symbol *sym)
{
return sym->flags & SYMBOL_DEF_USER ? true : false;
}
#ifdef __cplusplus
}
#endif
#endif /* LKC_H */

View File

@@ -0,0 +1,48 @@
/* SPDX-License-Identifier: GPL-2.0 */
#ifndef LKC_PROTO_H
#define LKC_PROTO_H
#include <stdarg.h>
/* confdata.c */
void conf_parse(const char *name);
int conf_read(const char *name);
int conf_read_simple(const char *name, int);
int conf_write_defconfig(const char *name);
int conf_write(const char *name);
int conf_write_autoconf(int overwrite);
void conf_set_changed(bool val);
bool conf_get_changed(void);
void conf_set_changed_callback(void (*fn)(bool));
void conf_set_message_callback(void (*fn)(const char *s));
bool conf_errors(void);
/* symbol.c */
struct symbol * sym_lookup(const char *name, int flags);
struct symbol * sym_find(const char *name);
void print_symbol_for_listconfig(struct symbol *sym);
struct symbol ** sym_re_search(const char *pattern);
const char * sym_type_name(enum symbol_type type);
void sym_calc_value(struct symbol *sym);
bool sym_dep_errors(void);
enum symbol_type sym_get_type(const struct symbol *sym);
bool sym_tristate_within_range(const struct symbol *sym, tristate tri);
bool sym_set_tristate_value(struct symbol *sym,tristate tri);
void choice_set_value(struct menu *choice, struct symbol *sym);
tristate sym_toggle_tristate_value(struct symbol *sym);
bool sym_string_valid(struct symbol *sym, const char *newval);
bool sym_string_within_range(struct symbol *sym, const char *str);
bool sym_set_string_value(struct symbol *sym, const char *newval);
bool sym_is_changeable(const struct symbol *sym);
struct menu *sym_get_prompt_menu(const struct symbol *sym);
struct menu *sym_get_choice_menu(const struct symbol *sym);
const char * sym_get_string_value(struct symbol *sym);
const char * prop_get_type_name(enum prop_type type);
/* expr.c */
void expr_print(const struct expr *e,
void (*fn)(void *, struct symbol *, const char *),
void *data, int prevtoken);
#endif /* LKC_PROTO_H */

View File

@@ -0,0 +1,4 @@
This is NOT the official version of dialog. This version has been
significantly modified from the original. It is for use by the Linux
kernel configuration script. Please do not bother Savio Lam with
questions about this program.

View File

@@ -0,0 +1,318 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* checklist.c -- implements the checklist box
*
* ORIGINAL AUTHOR: Savio Lam (lam836@cs.cuhk.hk)
* Stuart Herbert - S.Herbert@sheffield.ac.uk: radiolist extension
* Alessandro Rubini - rubini@ipvvis.unipv.it: merged the two
* MODIFIED FOR LINUX KERNEL CONFIG BY: William Roadcap (roadcap@cfw.com)
*/
#include "dialog.h"
static int list_width, check_x, item_x;
/*
* Print list item
*/
static void print_item(WINDOW * win, int choice, int selected)
{
int i;
char *list_item = malloc(list_width + 1);
strncpy(list_item, item_str(), list_width - item_x);
list_item[list_width - item_x] = '\0';
/* Clear 'residue' of last item */
wattrset(win, dlg.menubox.atr);
wmove(win, choice, 0);
for (i = 0; i < list_width; i++)
waddch(win, ' ');
wmove(win, choice, check_x);
wattrset(win, selected ? dlg.check_selected.atr
: dlg.check.atr);
if (!item_is_tag(':'))
wprintw(win, "(%c)", item_is_tag('X') ? 'X' : ' ');
wattrset(win, selected ? dlg.tag_selected.atr : dlg.tag.atr);
mvwaddch(win, choice, item_x, list_item[0]);
wattrset(win, selected ? dlg.item_selected.atr : dlg.item.atr);
waddstr(win, list_item + 1);
if (selected) {
wmove(win, choice, check_x + 1);
wrefresh(win);
}
free(list_item);
}
/*
* Print the scroll indicators.
*/
static void print_arrows(WINDOW * win, int choice, int item_no, int scroll,
int y, int x, int height)
{
wmove(win, y, x);
if (scroll > 0) {
wattrset(win, dlg.uarrow.atr);
waddch(win, ACS_UARROW);
waddstr(win, "(-)");
} else {
wattrset(win, dlg.menubox.atr);
waddch(win, ACS_HLINE);
waddch(win, ACS_HLINE);
waddch(win, ACS_HLINE);
waddch(win, ACS_HLINE);
}
y = y + height + 1;
wmove(win, y, x);
if ((height < item_no) && (scroll + choice < item_no - 1)) {
wattrset(win, dlg.darrow.atr);
waddch(win, ACS_DARROW);
waddstr(win, "(+)");
} else {
wattrset(win, dlg.menubox_border.atr);
waddch(win, ACS_HLINE);
waddch(win, ACS_HLINE);
waddch(win, ACS_HLINE);
waddch(win, ACS_HLINE);
}
}
/*
* Display the termination buttons
*/
static void print_buttons(WINDOW * dialog, int height, int width, int selected)
{
int x = width / 2 - 11;
int y = height - 2;
print_button(dialog, "Select", y, x, selected == 0);
print_button(dialog, " Help ", y, x + 14, selected == 1);
wmove(dialog, y, x + 1 + 14 * selected);
wrefresh(dialog);
}
/*
* Display a dialog box with a list of options that can be turned on or off
* in the style of radiolist (only one option turned on at a time).
*/
int dialog_checklist(const char *title, const char *prompt, int height,
int width, int list_height)
{
int i, x, y, box_x, box_y;
int key = 0, button = 0, choice = 0, scroll = 0, max_choice;
WINDOW *dialog, *list;
/* which item to highlight */
item_foreach() {
if (item_is_tag('X'))
choice = item_n();
if (item_is_selected()) {
choice = item_n();
break;
}
}
do_resize:
if (getmaxy(stdscr) < (height + CHECKLIST_HEIGHT_MIN))
return -ERRDISPLAYTOOSMALL;
if (getmaxx(stdscr) < (width + CHECKLIST_WIDTH_MIN))
return -ERRDISPLAYTOOSMALL;
max_choice = MIN(list_height, item_count());
/* center dialog box on screen */
x = (getmaxx(stdscr) - width) / 2;
y = (getmaxy(stdscr) - height) / 2;
draw_shadow(stdscr, y, x, height, width);
dialog = newwin(height, width, y, x);
keypad(dialog, TRUE);
draw_box(dialog, 0, 0, height, width,
dlg.dialog.atr, dlg.border.atr);
wattrset(dialog, dlg.border.atr);
mvwaddch(dialog, height - 3, 0, ACS_LTEE);
for (i = 0; i < width - 2; i++)
waddch(dialog, ACS_HLINE);
wattrset(dialog, dlg.dialog.atr);
waddch(dialog, ACS_RTEE);
print_title(dialog, title, width);
wattrset(dialog, dlg.dialog.atr);
print_autowrap(dialog, prompt, width - 2, 1, 3);
list_width = width - 6;
box_y = height - list_height - 5;
box_x = (width - list_width) / 2 - 1;
/* create new window for the list */
list = subwin(dialog, list_height, list_width, y + box_y + 1,
x + box_x + 1);
keypad(list, TRUE);
/* draw a box around the list items */
draw_box(dialog, box_y, box_x, list_height + 2, list_width + 2,
dlg.menubox_border.atr, dlg.menubox.atr);
/* Find length of longest item in order to center checklist */
check_x = 0;
item_foreach()
check_x = MAX(check_x, strlen(item_str()) + 4);
check_x = MIN(check_x, list_width);
check_x = (list_width - check_x) / 2;
item_x = check_x + 4;
if (choice >= list_height) {
scroll = choice - list_height + 1;
choice -= scroll;
}
/* Print the list */
for (i = 0; i < max_choice; i++) {
item_set(scroll + i);
print_item(list, i, i == choice);
}
print_arrows(dialog, choice, item_count(), scroll,
box_y, box_x + check_x + 5, list_height);
print_buttons(dialog, height, width, 0);
wmove(list, choice, check_x + 1);
wrefresh(list);
while (key != KEY_ESC) {
key = wgetch(dialog);
for (i = 0; i < max_choice; i++) {
item_set(i + scroll);
if (toupper(key) == toupper(item_str()[0]))
break;
}
if (i < max_choice || key == KEY_UP || key == KEY_DOWN ||
key == '+' || key == '-') {
if (key == KEY_UP || key == '-') {
if (!choice) {
if (!scroll)
continue;
/* Scroll list down */
if (list_height > 1) {
/* De-highlight current first item */
item_set(scroll);
print_item(list, 0, FALSE);
scrollok(list, TRUE);
wscrl(list, -1);
scrollok(list, FALSE);
}
scroll--;
item_set(scroll);
print_item(list, 0, TRUE);
print_arrows(dialog, choice, item_count(),
scroll, box_y, box_x + check_x + 5, list_height);
wnoutrefresh(dialog);
wrefresh(list);
continue; /* wait for another key press */
} else
i = choice - 1;
} else if (key == KEY_DOWN || key == '+') {
if (choice == max_choice - 1) {
if (scroll + choice >= item_count() - 1)
continue;
/* Scroll list up */
if (list_height > 1) {
/* De-highlight current last item before scrolling up */
item_set(scroll + max_choice - 1);
print_item(list,
max_choice - 1,
FALSE);
scrollok(list, TRUE);
wscrl(list, 1);
scrollok(list, FALSE);
}
scroll++;
item_set(scroll + max_choice - 1);
print_item(list, max_choice - 1, TRUE);
print_arrows(dialog, choice, item_count(),
scroll, box_y, box_x + check_x + 5, list_height);
wnoutrefresh(dialog);
wrefresh(list);
continue; /* wait for another key press */
} else
i = choice + 1;
}
if (i != choice) {
/* De-highlight current item */
item_set(scroll + choice);
print_item(list, choice, FALSE);
/* Highlight new item */
choice = i;
item_set(scroll + choice);
print_item(list, choice, TRUE);
wnoutrefresh(dialog);
wrefresh(list);
}
continue; /* wait for another key press */
}
switch (key) {
case 'H':
case 'h':
case '?':
button = 1;
/* fall-through */
case 'S':
case 's':
case ' ':
case '\n':
item_foreach()
item_set_selected(0);
item_set(scroll + choice);
item_set_selected(1);
delwin(list);
delwin(dialog);
return button;
case TAB:
case KEY_LEFT:
case KEY_RIGHT:
button = ((key == KEY_LEFT ? --button : ++button) < 0)
? 1 : (button > 1 ? 0 : button);
print_buttons(dialog, height, width, button);
wrefresh(dialog);
break;
case 'X':
case 'x':
key = KEY_ESC;
break;
case KEY_ESC:
key = on_key_esc(dialog);
break;
case KEY_RESIZE:
delwin(list);
delwin(dialog);
on_key_resize();
goto do_resize;
}
/* Now, update everything... */
doupdate();
}
delwin(list);
delwin(dialog);
return key; /* ESC pressed */
}

View File

@@ -0,0 +1,203 @@
/* SPDX-License-Identifier: GPL-2.0+ */
/*
* dialog.h -- common declarations for all dialog modules
*
* AUTHOR: Savio Lam (lam836@cs.cuhk.hk)
*/
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <ctype.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#ifdef __sun__
#define CURS_MACROS
#endif
#include <ncurses.h>
#define TR(params) _tracef params
#define KEY_ESC 27
#define TAB 9
#define MAX_LEN 2048
#define BUF_SIZE (10*1024)
#define MIN(x,y) (x < y ? x : y)
#define MAX(x,y) (x > y ? x : y)
#ifndef ACS_ULCORNER
#define ACS_ULCORNER '+'
#endif
#ifndef ACS_LLCORNER
#define ACS_LLCORNER '+'
#endif
#ifndef ACS_URCORNER
#define ACS_URCORNER '+'
#endif
#ifndef ACS_LRCORNER
#define ACS_LRCORNER '+'
#endif
#ifndef ACS_HLINE
#define ACS_HLINE '-'
#endif
#ifndef ACS_VLINE
#define ACS_VLINE '|'
#endif
#ifndef ACS_LTEE
#define ACS_LTEE '+'
#endif
#ifndef ACS_RTEE
#define ACS_RTEE '+'
#endif
#ifndef ACS_UARROW
#define ACS_UARROW '^'
#endif
#ifndef ACS_DARROW
#define ACS_DARROW 'v'
#endif
/* error return codes */
#define ERRDISPLAYTOOSMALL (KEY_MAX + 1)
/*
* Color definitions
*/
struct dialog_color {
chtype atr; /* Color attribute */
int fg; /* foreground */
int bg; /* background */
int hl; /* highlight this item */
};
struct subtitle_list {
struct subtitle_list *next;
const char *text;
};
struct dialog_info {
const char *backtitle;
struct subtitle_list *subtitles;
struct dialog_color screen;
struct dialog_color shadow;
struct dialog_color dialog;
struct dialog_color title;
struct dialog_color border;
struct dialog_color button_active;
struct dialog_color button_inactive;
struct dialog_color button_key_active;
struct dialog_color button_key_inactive;
struct dialog_color button_label_active;
struct dialog_color button_label_inactive;
struct dialog_color inputbox;
struct dialog_color position_indicator;
struct dialog_color menubox;
struct dialog_color menubox_border;
struct dialog_color item;
struct dialog_color item_selected;
struct dialog_color tag;
struct dialog_color tag_selected;
struct dialog_color tag_key;
struct dialog_color tag_key_selected;
struct dialog_color check;
struct dialog_color check_selected;
struct dialog_color uarrow;
struct dialog_color darrow;
};
/*
* Global variables
*/
extern struct dialog_info dlg;
extern char dialog_input_result[];
extern int saved_x, saved_y; /* Needed in signal handler in mconf.c */
/*
* Function prototypes
*/
/* item list as used by checklist and menubox */
void item_reset(void);
void item_make(const char *fmt, ...);
void item_add_str(const char *fmt, ...);
void item_set_tag(char tag);
void item_set_data(void *p);
void item_set_selected(int val);
int item_activate_selected(void);
void *item_data(void);
char item_tag(void);
/* item list manipulation for lxdialog use */
#define MAXITEMSTR 200
struct dialog_item {
char str[MAXITEMSTR]; /* prompt displayed */
char tag;
void *data; /* pointer to menu item - used by menubox+checklist */
int selected; /* Set to 1 by dialog_*() function if selected. */
};
/* list of lialog_items */
struct dialog_list {
struct dialog_item node;
struct dialog_list *next;
};
extern struct dialog_list *item_cur;
extern struct dialog_list item_nil;
extern struct dialog_list *item_head;
int item_count(void);
void item_set(int n);
int item_n(void);
const char *item_str(void);
int item_is_selected(void);
int item_is_tag(char tag);
#define item_foreach() \
for (item_cur = item_head ? item_head: item_cur; \
item_cur && (item_cur != &item_nil); item_cur = item_cur->next)
/* generic key handlers */
int on_key_esc(WINDOW *win);
int on_key_resize(void);
/* minimum (re)size values */
#define CHECKLIST_HEIGHT_MIN 6 /* For dialog_checklist() */
#define CHECKLIST_WIDTH_MIN 6
#define INPUTBOX_HEIGHT_MIN 2 /* For dialog_inputbox() */
#define INPUTBOX_WIDTH_MIN 2
#define MENUBOX_HEIGHT_MIN 15 /* For dialog_menu() */
#define MENUBOX_WIDTH_MIN 65
#define TEXTBOX_HEIGHT_MIN 8 /* For dialog_textbox() */
#define TEXTBOX_WIDTH_MIN 8
#define YESNO_HEIGHT_MIN 4 /* For dialog_yesno() */
#define YESNO_WIDTH_MIN 4
#define WINDOW_HEIGHT_MIN 19 /* For init_dialog() */
#define WINDOW_WIDTH_MIN 80
int init_dialog(const char *backtitle);
void set_dialog_backtitle(const char *backtitle);
void set_dialog_subtitles(struct subtitle_list *subtitles);
void end_dialog(int x, int y);
void attr_clear(WINDOW * win, int height, int width, chtype attr);
void dialog_clear(void);
void print_autowrap(WINDOW * win, const char *prompt, int width, int y, int x);
void print_button(WINDOW * win, const char *label, int y, int x, int selected);
void print_title(WINDOW *dialog, const char *title, int width);
void draw_box(WINDOW * win, int y, int x, int height, int width, chtype box,
chtype border);
void draw_shadow(WINDOW * win, int y, int x, int height, int width);
int first_alpha(const char *string, const char *exempt);
int dialog_yesno(const char *title, const char *prompt, int height, int width);
int dialog_msgbox(const char *title, const char *prompt, int height,
int width, int pause);
int dialog_textbox(const char *title, const char *tbuf, int initial_height,
int initial_width, int *_vscroll, int *_hscroll,
int (*extra_key_cb)(int, size_t, size_t, void *), void *data);
int dialog_menu(const char *title, const char *prompt,
const void *selected, int *s_scroll);
int dialog_checklist(const char *title, const char *prompt, int height,
int width, int list_height);
int dialog_inputbox(const char *title, const char *prompt, int height,
int width, const char *init);

View File

@@ -0,0 +1,289 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* inputbox.c -- implements the input box
*
* ORIGINAL AUTHOR: Savio Lam (lam836@cs.cuhk.hk)
* MODIFIED FOR LINUX KERNEL CONFIG BY: William Roadcap (roadcap@cfw.com)
*/
#include "dialog.h"
char dialog_input_result[MAX_LEN + 1];
/*
* Print the termination buttons
*/
static void print_buttons(WINDOW * dialog, int height, int width, int selected)
{
int x = width / 2 - 11;
int y = height - 2;
print_button(dialog, " Ok ", y, x, selected == 0);
print_button(dialog, " Help ", y, x + 14, selected == 1);
wmove(dialog, y, x + 1 + 14 * selected);
wrefresh(dialog);
}
/*
* Display a dialog box for inputing a string
*/
int dialog_inputbox(const char *title, const char *prompt, int height, int width,
const char *init)
{
int i, x, y, box_y, box_x, box_width;
int input_x = 0, key = 0, button = -1;
int show_x, len, pos;
char *instr = dialog_input_result;
WINDOW *dialog;
if (!init)
instr[0] = '\0';
else
strcpy(instr, init);
do_resize:
if (getmaxy(stdscr) <= (height - INPUTBOX_HEIGHT_MIN))
return -ERRDISPLAYTOOSMALL;
if (getmaxx(stdscr) <= (width - INPUTBOX_WIDTH_MIN))
return -ERRDISPLAYTOOSMALL;
/* center dialog box on screen */
x = (getmaxx(stdscr) - width) / 2;
y = (getmaxy(stdscr) - height) / 2;
draw_shadow(stdscr, y, x, height, width);
dialog = newwin(height, width, y, x);
keypad(dialog, TRUE);
draw_box(dialog, 0, 0, height, width,
dlg.dialog.atr, dlg.border.atr);
wattrset(dialog, dlg.border.atr);
mvwaddch(dialog, height - 3, 0, ACS_LTEE);
for (i = 0; i < width - 2; i++)
waddch(dialog, ACS_HLINE);
wattrset(dialog, dlg.dialog.atr);
waddch(dialog, ACS_RTEE);
print_title(dialog, title, width);
wattrset(dialog, dlg.dialog.atr);
print_autowrap(dialog, prompt, width - 2, 1, 3);
/* Draw the input field box */
box_width = width - 6;
getyx(dialog, y, x);
box_y = y + 2;
box_x = (width - box_width) / 2;
draw_box(dialog, y + 1, box_x - 1, 3, box_width + 2,
dlg.dialog.atr, dlg.border.atr);
print_buttons(dialog, height, width, 0);
/* Set up the initial value */
wmove(dialog, box_y, box_x);
wattrset(dialog, dlg.inputbox.atr);
len = strlen(instr);
pos = len;
if (len >= box_width) {
show_x = len - box_width + 1;
input_x = box_width - 1;
for (i = 0; i < box_width - 1; i++)
waddch(dialog, instr[show_x + i]);
} else {
show_x = 0;
input_x = len;
waddstr(dialog, instr);
}
wmove(dialog, box_y, box_x + input_x);
wrefresh(dialog);
while (key != KEY_ESC) {
key = wgetch(dialog);
if (button == -1) { /* Input box selected */
switch (key) {
case TAB:
case KEY_UP:
case KEY_DOWN:
break;
case KEY_BACKSPACE:
case 8: /* ^H */
case 127: /* ^? */
if (pos) {
wattrset(dialog, dlg.inputbox.atr);
if (input_x == 0) {
show_x--;
} else
input_x--;
if (pos < len) {
for (i = pos - 1; i < len; i++) {
instr[i] = instr[i+1];
}
}
pos--;
len--;
instr[len] = '\0';
wmove(dialog, box_y, box_x);
for (i = 0; i < box_width; i++) {
if (!instr[show_x + i]) {
waddch(dialog, ' ');
break;
}
waddch(dialog, instr[show_x + i]);
}
wmove(dialog, box_y, input_x + box_x);
wrefresh(dialog);
}
continue;
case KEY_LEFT:
if (pos > 0) {
if (input_x > 0) {
wmove(dialog, box_y, --input_x + box_x);
} else if (input_x == 0) {
show_x--;
wmove(dialog, box_y, box_x);
for (i = 0; i < box_width; i++) {
if (!instr[show_x + i]) {
waddch(dialog, ' ');
break;
}
waddch(dialog, instr[show_x + i]);
}
wmove(dialog, box_y, box_x);
}
pos--;
}
continue;
case KEY_RIGHT:
if (pos < len) {
if (input_x < box_width - 1) {
wmove(dialog, box_y, ++input_x + box_x);
} else if (input_x == box_width - 1) {
show_x++;
wmove(dialog, box_y, box_x);
for (i = 0; i < box_width; i++) {
if (!instr[show_x + i]) {
waddch(dialog, ' ');
break;
}
waddch(dialog, instr[show_x + i]);
}
wmove(dialog, box_y, input_x + box_x);
}
pos++;
}
continue;
default:
if (key < 0x100 && isprint(key)) {
if (len < MAX_LEN) {
wattrset(dialog, dlg.inputbox.atr);
if (pos < len) {
for (i = len; i > pos; i--)
instr[i] = instr[i-1];
instr[pos] = key;
} else {
instr[len] = key;
}
pos++;
len++;
instr[len] = '\0';
if (input_x == box_width - 1) {
show_x++;
} else {
input_x++;
}
wmove(dialog, box_y, box_x);
for (i = 0; i < box_width; i++) {
if (!instr[show_x + i]) {
waddch(dialog, ' ');
break;
}
waddch(dialog, instr[show_x + i]);
}
wmove(dialog, box_y, input_x + box_x);
wrefresh(dialog);
} else
flash(); /* Alarm user about overflow */
continue;
}
}
}
switch (key) {
case 'O':
case 'o':
delwin(dialog);
return 0;
case 'H':
case 'h':
delwin(dialog);
return 1;
case KEY_UP:
case KEY_LEFT:
switch (button) {
case -1:
button = 1; /* Indicates "Help" button is selected */
print_buttons(dialog, height, width, 1);
break;
case 0:
button = -1; /* Indicates input box is selected */
print_buttons(dialog, height, width, 0);
wmove(dialog, box_y, box_x + input_x);
wrefresh(dialog);
break;
case 1:
button = 0; /* Indicates "OK" button is selected */
print_buttons(dialog, height, width, 0);
break;
}
break;
case TAB:
case KEY_DOWN:
case KEY_RIGHT:
switch (button) {
case -1:
button = 0; /* Indicates "OK" button is selected */
print_buttons(dialog, height, width, 0);
break;
case 0:
button = 1; /* Indicates "Help" button is selected */
print_buttons(dialog, height, width, 1);
break;
case 1:
button = -1; /* Indicates input box is selected */
print_buttons(dialog, height, width, 0);
wmove(dialog, box_y, box_x + input_x);
wrefresh(dialog);
break;
}
break;
case ' ':
case '\n':
delwin(dialog);
return (button == -1 ? 0 : button);
case 'X':
case 'x':
key = KEY_ESC;
break;
case KEY_ESC:
key = on_key_esc(dialog);
break;
case KEY_RESIZE:
delwin(dialog);
on_key_resize();
goto do_resize;
}
}
delwin(dialog);
return KEY_ESC; /* ESC pressed */
}

View File

@@ -0,0 +1,416 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* menubox.c -- implements the menu box
*
* ORIGINAL AUTHOR: Savio Lam (lam836@cs.cuhk.hk)
* MODIFIED FOR LINUX KERNEL CONFIG BY: William Roadcap (roadcapw@cfw.com)
*/
/*
* Changes by Clifford Wolf (god@clifford.at)
*
* [ 1998-06-13 ]
*
* *) A bugfix for the Page-Down problem
*
* *) Formerly when I used Page Down and Page Up, the cursor would be set
* to the first position in the menu box. Now lxdialog is a bit
* smarter and works more like other menu systems (just have a look at
* it).
*
* *) Formerly if I selected something my scrolling would be broken because
* lxdialog is re-invoked by the Menuconfig shell script, can't
* remember the last scrolling position, and just sets it so that the
* cursor is at the bottom of the box. Now it writes the temporary file
* lxdialog.scrltmp which contains this information. The file is
* deleted by lxdialog if the user leaves a submenu or enters a new
* one, but it would be nice if Menuconfig could make another "rm -f"
* just to be sure. Just try it out - you will recognise a difference!
*
* [ 1998-06-14 ]
*
* *) Now lxdialog is crash-safe against broken "lxdialog.scrltmp" files
* and menus change their size on the fly.
*
* *) If for some reason the last scrolling position is not saved by
* lxdialog, it sets the scrolling so that the selected item is in the
* middle of the menu box, not at the bottom.
*
* 02 January 1999, Michael Elizabeth Chastain (mec@shout.net)
* Reset 'scroll' to 0 if the value from lxdialog.scrltmp is bogus.
* This fixes a bug in Menuconfig where using ' ' to descend into menus
* would leave mis-synchronized lxdialog.scrltmp files lying around,
* fscanf would read in 'scroll', and eventually that value would get used.
*/
#include "dialog.h"
static int menu_width, item_x;
/*
* Print menu item
*/
static void do_print_item(WINDOW * win, const char *item, int line_y,
int selected, int hotkey)
{
int j;
char *menu_item = malloc(menu_width + 1);
strncpy(menu_item, item, menu_width - item_x);
menu_item[menu_width - item_x] = '\0';
j = first_alpha(menu_item, "YyNnMmHh");
/* Clear 'residue' of last item */
wattrset(win, dlg.menubox.atr);
wmove(win, line_y, 0);
wclrtoeol(win);
wattrset(win, selected ? dlg.item_selected.atr : dlg.item.atr);
mvwaddstr(win, line_y, item_x, menu_item);
if (hotkey) {
wattrset(win, selected ? dlg.tag_key_selected.atr
: dlg.tag_key.atr);
mvwaddch(win, line_y, item_x + j, menu_item[j]);
}
if (selected) {
wmove(win, line_y, item_x + 1);
}
free(menu_item);
wrefresh(win);
}
#define print_item(index, choice, selected) \
do { \
item_set(index); \
do_print_item(menu, item_str(), choice, selected, !item_is_tag(':')); \
} while (0)
/*
* Print the scroll indicators.
*/
static void print_arrows(WINDOW * win, int item_no, int scroll, int y, int x,
int height)
{
int cur_y, cur_x;
getyx(win, cur_y, cur_x);
wmove(win, y, x);
if (scroll > 0) {
wattrset(win, dlg.uarrow.atr);
waddch(win, ACS_UARROW);
waddstr(win, "(-)");
} else {
wattrset(win, dlg.menubox.atr);
waddch(win, ACS_HLINE);
waddch(win, ACS_HLINE);
waddch(win, ACS_HLINE);
waddch(win, ACS_HLINE);
}
y = y + height + 1;
wmove(win, y, x);
wrefresh(win);
if ((height < item_no) && (scroll + height < item_no)) {
wattrset(win, dlg.darrow.atr);
waddch(win, ACS_DARROW);
waddstr(win, "(+)");
} else {
wattrset(win, dlg.menubox_border.atr);
waddch(win, ACS_HLINE);
waddch(win, ACS_HLINE);
waddch(win, ACS_HLINE);
waddch(win, ACS_HLINE);
}
wmove(win, cur_y, cur_x);
wrefresh(win);
}
/*
* Display the termination buttons.
*/
static void print_buttons(WINDOW * win, int height, int width, int selected)
{
int x = width / 2 - 28;
int y = height - 2;
print_button(win, "Select", y, x, selected == 0);
print_button(win, " Exit ", y, x + 12, selected == 1);
print_button(win, " Help ", y, x + 24, selected == 2);
print_button(win, " Save ", y, x + 36, selected == 3);
print_button(win, " Load ", y, x + 48, selected == 4);
wmove(win, y, x + 1 + 12 * selected);
wrefresh(win);
}
/* scroll up n lines (n may be negative) */
static void do_scroll(WINDOW *win, int *scroll, int n)
{
/* Scroll menu up */
scrollok(win, TRUE);
wscrl(win, n);
scrollok(win, FALSE);
*scroll = *scroll + n;
wrefresh(win);
}
/*
* Display a menu for choosing among a number of options
*/
int dialog_menu(const char *title, const char *prompt,
const void *selected, int *s_scroll)
{
int i, j, x, y, box_x, box_y;
int height, width, menu_height;
int key = 0, button = 0, scroll = 0, choice = 0;
int first_item = 0, max_choice;
WINDOW *dialog, *menu;
do_resize:
height = getmaxy(stdscr);
width = getmaxx(stdscr);
if (height < MENUBOX_HEIGHT_MIN || width < MENUBOX_WIDTH_MIN)
return -ERRDISPLAYTOOSMALL;
height -= 4;
width -= 5;
menu_height = height - 10;
max_choice = MIN(menu_height, item_count());
/* center dialog box on screen */
x = (getmaxx(stdscr) - width) / 2;
y = (getmaxy(stdscr) - height) / 2;
draw_shadow(stdscr, y, x, height, width);
dialog = newwin(height, width, y, x);
keypad(dialog, TRUE);
draw_box(dialog, 0, 0, height, width,
dlg.dialog.atr, dlg.border.atr);
wattrset(dialog, dlg.border.atr);
mvwaddch(dialog, height - 3, 0, ACS_LTEE);
for (i = 0; i < width - 2; i++)
waddch(dialog, ACS_HLINE);
wattrset(dialog, dlg.dialog.atr);
wbkgdset(dialog, dlg.dialog.atr & A_COLOR);
waddch(dialog, ACS_RTEE);
print_title(dialog, title, width);
wattrset(dialog, dlg.dialog.atr);
print_autowrap(dialog, prompt, width - 2, 1, 3);
menu_width = width - 6;
box_y = height - menu_height - 5;
box_x = (width - menu_width) / 2 - 1;
/* create new window for the menu */
menu = subwin(dialog, menu_height, menu_width,
y + box_y + 1, x + box_x + 1);
keypad(menu, TRUE);
/* draw a box around the menu items */
draw_box(dialog, box_y, box_x, menu_height + 2, menu_width + 2,
dlg.menubox_border.atr, dlg.menubox.atr);
if (menu_width >= 80)
item_x = (menu_width - 70) / 2;
else
item_x = 4;
/* Set choice to default item */
item_foreach()
if (selected && (selected == item_data()))
choice = item_n();
/* get the saved scroll info */
scroll = *s_scroll;
if ((scroll <= choice) && (scroll + max_choice > choice) &&
(scroll >= 0) && (scroll + max_choice <= item_count())) {
first_item = scroll;
choice = choice - scroll;
} else {
scroll = 0;
}
if ((choice >= max_choice)) {
if (choice >= item_count() - max_choice / 2)
scroll = first_item = item_count() - max_choice;
else
scroll = first_item = choice - max_choice / 2;
choice = choice - scroll;
}
/* Print the menu */
for (i = 0; i < max_choice; i++) {
print_item(first_item + i, i, i == choice);
}
wnoutrefresh(menu);
print_arrows(dialog, item_count(), scroll,
box_y, box_x + item_x + 1, menu_height);
print_buttons(dialog, height, width, 0);
wmove(menu, choice, item_x + 1);
wrefresh(menu);
while (key != KEY_ESC) {
key = wgetch(menu);
if (key < 256 && isalpha(key))
key = tolower(key);
if (strchr("ynmh", key))
i = max_choice;
else {
for (i = choice + 1; i < max_choice; i++) {
item_set(scroll + i);
j = first_alpha(item_str(), "YyNnMmHh");
if (key == tolower(item_str()[j]))
break;
}
if (i == max_choice)
for (i = 0; i < max_choice; i++) {
item_set(scroll + i);
j = first_alpha(item_str(), "YyNnMmHh");
if (key == tolower(item_str()[j]))
break;
}
}
if (item_count() != 0 &&
(i < max_choice ||
key == KEY_UP || key == KEY_DOWN ||
key == '-' || key == '+' ||
key == KEY_PPAGE || key == KEY_NPAGE)) {
/* Remove highligt of current item */
print_item(scroll + choice, choice, FALSE);
if (key == KEY_UP || key == '-') {
if (choice < 2 && scroll) {
/* Scroll menu down */
do_scroll(menu, &scroll, -1);
print_item(scroll, 0, FALSE);
} else
choice = MAX(choice - 1, 0);
} else if (key == KEY_DOWN || key == '+') {
print_item(scroll+choice, choice, FALSE);
if ((choice > max_choice - 3) &&
(scroll + max_choice < item_count())) {
/* Scroll menu up */
do_scroll(menu, &scroll, 1);
print_item(scroll+max_choice - 1,
max_choice - 1, FALSE);
} else
choice = MIN(choice + 1, max_choice - 1);
} else if (key == KEY_PPAGE) {
scrollok(menu, TRUE);
for (i = 0; (i < max_choice); i++) {
if (scroll > 0) {
do_scroll(menu, &scroll, -1);
print_item(scroll, 0, FALSE);
} else {
if (choice > 0)
choice--;
}
}
} else if (key == KEY_NPAGE) {
for (i = 0; (i < max_choice); i++) {
if (scroll + max_choice < item_count()) {
do_scroll(menu, &scroll, 1);
print_item(scroll+max_choice-1,
max_choice - 1, FALSE);
} else {
if (choice + 1 < max_choice)
choice++;
}
}
} else
choice = i;
print_item(scroll + choice, choice, TRUE);
print_arrows(dialog, item_count(), scroll,
box_y, box_x + item_x + 1, menu_height);
wnoutrefresh(dialog);
wrefresh(menu);
continue; /* wait for another key press */
}
switch (key) {
case KEY_LEFT:
case TAB:
case KEY_RIGHT:
button = ((key == KEY_LEFT ? --button : ++button) < 0)
? 4 : (button > 4 ? 0 : button);
print_buttons(dialog, height, width, button);
wrefresh(menu);
break;
case ' ':
case 's':
case 'y':
case 'n':
case 'm':
case '/':
case 'h':
case '?':
case 'z':
case '\n':
/* save scroll info */
*s_scroll = scroll;
delwin(menu);
delwin(dialog);
item_set(scroll + choice);
item_set_selected(1);
switch (key) {
case 'h':
case '?':
return 2;
case 's':
case 'y':
return 5;
case 'n':
return 6;
case 'm':
return 7;
case ' ':
return 8;
case '/':
return 9;
case 'z':
return 10;
case '\n':
return button;
}
return 0;
case 'e':
case 'x':
key = KEY_ESC;
break;
case KEY_ESC:
key = on_key_esc(menu);
break;
case KEY_RESIZE:
on_key_resize();
delwin(menu);
delwin(dialog);
goto do_resize;
}
}
delwin(menu);
delwin(dialog);
return key; /* ESC pressed */
}

View File

@@ -0,0 +1,358 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* textbox.c -- implements the text box
*
* ORIGINAL AUTHOR: Savio Lam (lam836@cs.cuhk.hk)
* MODIFIED FOR LINUX KERNEL CONFIG BY: William Roadcap (roadcap@cfw.com)
*/
#include "dialog.h"
static int hscroll;
static int begin_reached, end_reached, page_length;
static const char *buf, *page;
static size_t start, end;
/*
* Go back 'n' lines in text. Called by dialog_textbox().
* 'page' will be updated to point to the desired line in 'buf'.
*/
static void back_lines(int n)
{
int i;
begin_reached = 0;
/* Go back 'n' lines */
for (i = 0; i < n; i++) {
if (*page == '\0') {
if (end_reached) {
end_reached = 0;
continue;
}
}
if (page == buf) {
begin_reached = 1;
return;
}
page--;
do {
if (page == buf) {
begin_reached = 1;
return;
}
page--;
} while (*page != '\n');
page++;
}
}
/*
* Return current line of text. Called by dialog_textbox() and print_line().
* 'page' should point to start of current line before calling, and will be
* updated to point to start of next line.
*/
static char *get_line(void)
{
int i = 0;
static char line[MAX_LEN + 1];
end_reached = 0;
while (*page != '\n') {
if (*page == '\0') {
end_reached = 1;
break;
} else if (i < MAX_LEN)
line[i++] = *(page++);
else {
/* Truncate lines longer than MAX_LEN characters */
if (i == MAX_LEN)
line[i++] = '\0';
page++;
}
}
if (i <= MAX_LEN)
line[i] = '\0';
if (!end_reached)
page++; /* move past '\n' */
return line;
}
/*
* Print a new line of text.
*/
static void print_line(WINDOW *win, int row, int width)
{
char *line;
line = get_line();
line += MIN(strlen(line), hscroll); /* Scroll horizontally */
wmove(win, row, 0); /* move cursor to correct line */
waddch(win, ' ');
waddnstr(win, line, MIN(strlen(line), width - 2));
/* Clear 'residue' of previous line */
wclrtoeol(win);
}
/*
* Print a new page of text.
*/
static void print_page(WINDOW *win, int height, int width)
{
int i, passed_end = 0;
page_length = 0;
for (i = 0; i < height; i++) {
print_line(win, i, width);
if (!passed_end)
page_length++;
if (end_reached && !passed_end)
passed_end = 1;
}
wnoutrefresh(win);
}
/*
* Print current position
*/
static void print_position(WINDOW *win)
{
int percent;
wattrset(win, dlg.position_indicator.atr);
wbkgdset(win, dlg.position_indicator.atr & A_COLOR);
percent = (page - buf) * 100 / strlen(buf);
wmove(win, getmaxy(win) - 3, getmaxx(win) - 9);
wprintw(win, "(%3d%%)", percent);
}
/*
* refresh window content
*/
static void refresh_text_box(WINDOW *dialog, WINDOW *box, int boxh, int boxw,
int cur_y, int cur_x)
{
start = page - buf;
print_page(box, boxh, boxw);
print_position(dialog);
wmove(dialog, cur_y, cur_x); /* Restore cursor position */
wrefresh(dialog);
end = page - buf;
}
/*
* Display text from a file in a dialog box.
*
* keys is a null-terminated array
*/
int dialog_textbox(const char *title, const char *tbuf, int initial_height,
int initial_width, int *_vscroll, int *_hscroll,
int (*extra_key_cb)(int, size_t, size_t, void *), void *data)
{
int i, x, y, cur_x, cur_y, key = 0;
int height, width, boxh, boxw;
WINDOW *dialog, *box;
bool done = false;
begin_reached = 1;
end_reached = 0;
page_length = 0;
hscroll = 0;
buf = tbuf;
page = buf; /* page is pointer to start of page to be displayed */
if (_vscroll && *_vscroll) {
begin_reached = 0;
for (i = 0; i < *_vscroll; i++)
get_line();
}
if (_hscroll)
hscroll = *_hscroll;
do_resize:
getmaxyx(stdscr, height, width);
if (height < TEXTBOX_HEIGHT_MIN || width < TEXTBOX_WIDTH_MIN)
return -ERRDISPLAYTOOSMALL;
if (initial_height != 0)
height = initial_height;
else
if (height > 4)
height -= 4;
else
height = 0;
if (initial_width != 0)
width = initial_width;
else
if (width > 5)
width -= 5;
else
width = 0;
/* center dialog box on screen */
x = (getmaxx(stdscr) - width) / 2;
y = (getmaxy(stdscr) - height) / 2;
draw_shadow(stdscr, y, x, height, width);
dialog = newwin(height, width, y, x);
keypad(dialog, TRUE);
/* Create window for box region, used for scrolling text */
boxh = height - 4;
boxw = width - 2;
box = subwin(dialog, boxh, boxw, y + 1, x + 1);
wattrset(box, dlg.dialog.atr);
wbkgdset(box, dlg.dialog.atr & A_COLOR);
keypad(box, TRUE);
/* register the new window, along with its borders */
draw_box(dialog, 0, 0, height, width,
dlg.dialog.atr, dlg.border.atr);
wattrset(dialog, dlg.border.atr);
mvwaddch(dialog, height - 3, 0, ACS_LTEE);
for (i = 0; i < width - 2; i++)
waddch(dialog, ACS_HLINE);
wattrset(dialog, dlg.dialog.atr);
wbkgdset(dialog, dlg.dialog.atr & A_COLOR);
waddch(dialog, ACS_RTEE);
print_title(dialog, title, width);
print_button(dialog, " Exit ", height - 2, width / 2 - 4, TRUE);
wnoutrefresh(dialog);
getyx(dialog, cur_y, cur_x); /* Save cursor position */
/* Print first page of text */
attr_clear(box, boxh, boxw, dlg.dialog.atr);
refresh_text_box(dialog, box, boxh, boxw, cur_y, cur_x);
while (!done) {
key = wgetch(dialog);
switch (key) {
case 'E': /* Exit */
case 'e':
case 'X':
case 'x':
case 'q':
case '\n':
done = true;
break;
case 'g': /* First page */
case KEY_HOME:
if (!begin_reached) {
begin_reached = 1;
page = buf;
refresh_text_box(dialog, box, boxh, boxw,
cur_y, cur_x);
}
break;
case 'G': /* Last page */
case KEY_END:
end_reached = 1;
/* point to last char in buf */
page = buf + strlen(buf);
back_lines(boxh);
refresh_text_box(dialog, box, boxh, boxw, cur_y, cur_x);
break;
case 'K': /* Previous line */
case 'k':
case KEY_UP:
if (begin_reached)
break;
back_lines(page_length + 1);
refresh_text_box(dialog, box, boxh, boxw, cur_y, cur_x);
break;
case 'B': /* Previous page */
case 'b':
case 'u':
case KEY_PPAGE:
if (begin_reached)
break;
back_lines(page_length + boxh);
refresh_text_box(dialog, box, boxh, boxw, cur_y, cur_x);
break;
case 'J': /* Next line */
case 'j':
case KEY_DOWN:
if (end_reached)
break;
back_lines(page_length - 1);
refresh_text_box(dialog, box, boxh, boxw, cur_y, cur_x);
break;
case KEY_NPAGE: /* Next page */
case ' ':
case 'd':
if (end_reached)
break;
begin_reached = 0;
refresh_text_box(dialog, box, boxh, boxw, cur_y, cur_x);
break;
case '0': /* Beginning of line */
case 'H': /* Scroll left */
case 'h':
case KEY_LEFT:
if (hscroll <= 0)
break;
if (key == '0')
hscroll = 0;
else
hscroll--;
/* Reprint current page to scroll horizontally */
back_lines(page_length);
refresh_text_box(dialog, box, boxh, boxw, cur_y, cur_x);
break;
case 'L': /* Scroll right */
case 'l':
case KEY_RIGHT:
if (hscroll >= MAX_LEN)
break;
hscroll++;
/* Reprint current page to scroll horizontally */
back_lines(page_length);
refresh_text_box(dialog, box, boxh, boxw, cur_y, cur_x);
break;
case KEY_ESC:
if (on_key_esc(dialog) == KEY_ESC)
done = true;
break;
case KEY_RESIZE:
back_lines(height);
delwin(box);
delwin(dialog);
on_key_resize();
goto do_resize;
default:
if (extra_key_cb && extra_key_cb(key, start, end, data)) {
done = true;
break;
}
}
}
delwin(box);
delwin(dialog);
if (_vscroll) {
const char *s;
s = buf;
*_vscroll = 0;
back_lines(page_length);
while (s < page && (s = strchr(s, '\n'))) {
(*_vscroll)++;
s++;
}
}
if (_hscroll)
*_hscroll = hscroll;
return key;
}

View File

@@ -0,0 +1,673 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* util.c
*
* ORIGINAL AUTHOR: Savio Lam (lam836@cs.cuhk.hk)
* MODIFIED FOR LINUX KERNEL CONFIG BY: William Roadcap (roadcap@cfw.com)
*/
#include <stdarg.h>
#include "dialog.h"
/* Needed in signal handler in mconf.c */
int saved_x, saved_y;
struct dialog_info dlg;
static void set_mono_theme(void)
{
dlg.title.atr = A_BOLD;
dlg.button_active.atr = A_REVERSE;
dlg.button_inactive.atr = A_DIM;
dlg.button_key_active.atr = A_REVERSE;
dlg.button_key_inactive.atr = A_BOLD;
dlg.button_label_active.atr = A_REVERSE;
dlg.position_indicator.atr = A_BOLD;
dlg.item_selected.atr = A_REVERSE;
dlg.tag.atr = A_BOLD;
dlg.tag_selected.atr = A_REVERSE;
dlg.tag_key.atr = A_BOLD;
dlg.tag_key_selected.atr = A_REVERSE;
dlg.check.atr = A_BOLD;
dlg.check_selected.atr = A_REVERSE;
dlg.uarrow.atr = A_BOLD;
dlg.darrow.atr = A_BOLD;
}
#define DLG_COLOR(dialog, f, b, h) \
do { \
dlg.dialog.fg = (f); \
dlg.dialog.bg = (b); \
dlg.dialog.hl = (h); \
} while (0)
static void set_classic_theme(void)
{
DLG_COLOR(screen, COLOR_CYAN, COLOR_BLUE, true);
DLG_COLOR(shadow, COLOR_BLACK, COLOR_BLACK, true);
DLG_COLOR(dialog, COLOR_BLACK, COLOR_WHITE, false);
DLG_COLOR(title, COLOR_YELLOW, COLOR_WHITE, true);
DLG_COLOR(border, COLOR_WHITE, COLOR_WHITE, true);
DLG_COLOR(button_active, COLOR_WHITE, COLOR_BLUE, true);
DLG_COLOR(button_inactive, COLOR_BLACK, COLOR_WHITE, false);
DLG_COLOR(button_key_active, COLOR_WHITE, COLOR_BLUE, true);
DLG_COLOR(button_key_inactive, COLOR_RED, COLOR_WHITE, false);
DLG_COLOR(button_label_active, COLOR_YELLOW, COLOR_BLUE, true);
DLG_COLOR(button_label_inactive, COLOR_BLACK, COLOR_WHITE, true);
DLG_COLOR(inputbox, COLOR_BLACK, COLOR_WHITE, false);
DLG_COLOR(position_indicator, COLOR_YELLOW, COLOR_WHITE, true);
DLG_COLOR(menubox, COLOR_BLACK, COLOR_WHITE, false);
DLG_COLOR(menubox_border, COLOR_WHITE, COLOR_WHITE, true);
DLG_COLOR(item, COLOR_BLACK, COLOR_WHITE, false);
DLG_COLOR(item_selected, COLOR_WHITE, COLOR_BLUE, true);
DLG_COLOR(tag, COLOR_YELLOW, COLOR_WHITE, true);
DLG_COLOR(tag_selected, COLOR_YELLOW, COLOR_BLUE, true);
DLG_COLOR(tag_key, COLOR_YELLOW, COLOR_WHITE, true);
DLG_COLOR(tag_key_selected, COLOR_YELLOW, COLOR_BLUE, true);
DLG_COLOR(check, COLOR_BLACK, COLOR_WHITE, false);
DLG_COLOR(check_selected, COLOR_WHITE, COLOR_BLUE, true);
DLG_COLOR(uarrow, COLOR_GREEN, COLOR_WHITE, true);
DLG_COLOR(darrow, COLOR_GREEN, COLOR_WHITE, true);
}
static void set_blackbg_theme(void)
{
DLG_COLOR(screen, COLOR_RED, COLOR_BLACK, true);
DLG_COLOR(shadow, COLOR_BLACK, COLOR_BLACK, false);
DLG_COLOR(dialog, COLOR_WHITE, COLOR_BLACK, false);
DLG_COLOR(title, COLOR_RED, COLOR_BLACK, false);
DLG_COLOR(border, COLOR_BLACK, COLOR_BLACK, true);
DLG_COLOR(button_active, COLOR_YELLOW, COLOR_RED, false);
DLG_COLOR(button_inactive, COLOR_YELLOW, COLOR_BLACK, false);
DLG_COLOR(button_key_active, COLOR_YELLOW, COLOR_RED, true);
DLG_COLOR(button_key_inactive, COLOR_RED, COLOR_BLACK, false);
DLG_COLOR(button_label_active, COLOR_WHITE, COLOR_RED, false);
DLG_COLOR(button_label_inactive, COLOR_WHITE, COLOR_BLACK, false);
DLG_COLOR(inputbox, COLOR_YELLOW, COLOR_BLACK, false);
DLG_COLOR(position_indicator, COLOR_RED, COLOR_BLACK, false);
DLG_COLOR(menubox, COLOR_YELLOW, COLOR_BLACK, false);
DLG_COLOR(menubox_border, COLOR_BLACK, COLOR_BLACK, true);
DLG_COLOR(item, COLOR_WHITE, COLOR_BLACK, false);
DLG_COLOR(item_selected, COLOR_WHITE, COLOR_RED, false);
DLG_COLOR(tag, COLOR_RED, COLOR_BLACK, false);
DLG_COLOR(tag_selected, COLOR_YELLOW, COLOR_RED, true);
DLG_COLOR(tag_key, COLOR_RED, COLOR_BLACK, false);
DLG_COLOR(tag_key_selected, COLOR_YELLOW, COLOR_RED, true);
DLG_COLOR(check, COLOR_YELLOW, COLOR_BLACK, false);
DLG_COLOR(check_selected, COLOR_YELLOW, COLOR_RED, true);
DLG_COLOR(uarrow, COLOR_RED, COLOR_BLACK, false);
DLG_COLOR(darrow, COLOR_RED, COLOR_BLACK, false);
}
static void set_bluetitle_theme(void)
{
set_classic_theme();
DLG_COLOR(title, COLOR_BLUE, COLOR_WHITE, true);
DLG_COLOR(button_key_active, COLOR_YELLOW, COLOR_BLUE, true);
DLG_COLOR(button_label_active, COLOR_WHITE, COLOR_BLUE, true);
DLG_COLOR(position_indicator, COLOR_BLUE, COLOR_WHITE, true);
DLG_COLOR(tag, COLOR_BLUE, COLOR_WHITE, true);
DLG_COLOR(tag_key, COLOR_BLUE, COLOR_WHITE, true);
}
/*
* Select color theme
*/
static int set_theme(const char *theme)
{
int use_color = 1;
if (!theme)
set_bluetitle_theme();
else if (strcmp(theme, "classic") == 0)
set_classic_theme();
else if (strcmp(theme, "bluetitle") == 0)
set_bluetitle_theme();
else if (strcmp(theme, "blackbg") == 0)
set_blackbg_theme();
else if (strcmp(theme, "mono") == 0)
use_color = 0;
return use_color;
}
static void init_one_color(struct dialog_color *color)
{
static int pair = 0;
pair++;
init_pair(pair, color->fg, color->bg);
if (color->hl)
color->atr = A_BOLD | COLOR_PAIR(pair);
else
color->atr = COLOR_PAIR(pair);
}
static void init_dialog_colors(void)
{
init_one_color(&dlg.screen);
init_one_color(&dlg.shadow);
init_one_color(&dlg.dialog);
init_one_color(&dlg.title);
init_one_color(&dlg.border);
init_one_color(&dlg.button_active);
init_one_color(&dlg.button_inactive);
init_one_color(&dlg.button_key_active);
init_one_color(&dlg.button_key_inactive);
init_one_color(&dlg.button_label_active);
init_one_color(&dlg.button_label_inactive);
init_one_color(&dlg.inputbox);
init_one_color(&dlg.position_indicator);
init_one_color(&dlg.menubox);
init_one_color(&dlg.menubox_border);
init_one_color(&dlg.item);
init_one_color(&dlg.item_selected);
init_one_color(&dlg.tag);
init_one_color(&dlg.tag_selected);
init_one_color(&dlg.tag_key);
init_one_color(&dlg.tag_key_selected);
init_one_color(&dlg.check);
init_one_color(&dlg.check_selected);
init_one_color(&dlg.uarrow);
init_one_color(&dlg.darrow);
}
/*
* Setup for color display
*/
static void color_setup(const char *theme)
{
int use_color;
use_color = set_theme(theme);
if (use_color && has_colors()) {
start_color();
init_dialog_colors();
} else
set_mono_theme();
}
/*
* Set window to attribute 'attr'
*/
void attr_clear(WINDOW * win, int height, int width, chtype attr)
{
int i, j;
wattrset(win, attr);
for (i = 0; i < height; i++) {
wmove(win, i, 0);
for (j = 0; j < width; j++)
waddch(win, ' ');
}
touchwin(win);
}
void dialog_clear(void)
{
int lines, columns;
lines = getmaxy(stdscr);
columns = getmaxx(stdscr);
attr_clear(stdscr, lines, columns, dlg.screen.atr);
/* Display background title if it exists ... - SLH */
if (dlg.backtitle != NULL) {
int i, len = 0, skip = 0;
struct subtitle_list *pos;
wattrset(stdscr, dlg.screen.atr);
mvwaddstr(stdscr, 0, 1, (char *)dlg.backtitle);
for (pos = dlg.subtitles; pos != NULL; pos = pos->next) {
/* 3 is for the arrow and spaces */
len += strlen(pos->text) + 3;
}
wmove(stdscr, 1, 1);
if (len > columns - 2) {
const char *ellipsis = "[...] ";
waddstr(stdscr, ellipsis);
skip = len - (columns - 2 - strlen(ellipsis));
}
for (pos = dlg.subtitles; pos != NULL; pos = pos->next) {
if (skip == 0)
waddch(stdscr, ACS_RARROW);
else
skip--;
if (skip == 0)
waddch(stdscr, ' ');
else
skip--;
if (skip < strlen(pos->text)) {
waddstr(stdscr, pos->text + skip);
skip = 0;
} else
skip -= strlen(pos->text);
if (skip == 0)
waddch(stdscr, ' ');
else
skip--;
}
for (i = len + 1; i < columns - 1; i++)
waddch(stdscr, ACS_HLINE);
}
wnoutrefresh(stdscr);
}
/*
* Do some initialization for dialog
*/
int init_dialog(const char *backtitle)
{
int height, width;
initscr(); /* Init curses */
/* Get current cursor position for signal handler in mconf.c */
getyx(stdscr, saved_y, saved_x);
getmaxyx(stdscr, height, width);
if (height < WINDOW_HEIGHT_MIN || width < WINDOW_WIDTH_MIN) {
endwin();
return -ERRDISPLAYTOOSMALL;
}
dlg.backtitle = backtitle;
color_setup(getenv("MENUCONFIG_COLOR"));
keypad(stdscr, TRUE);
cbreak();
noecho();
dialog_clear();
return 0;
}
void set_dialog_backtitle(const char *backtitle)
{
dlg.backtitle = backtitle;
}
void set_dialog_subtitles(struct subtitle_list *subtitles)
{
dlg.subtitles = subtitles;
}
/*
* End using dialog functions.
*/
void end_dialog(int x, int y)
{
/* move cursor back to original position */
move(y, x);
refresh();
endwin();
}
/* Print the title of the dialog. Center the title and truncate
* tile if wider than dialog (- 2 chars).
**/
void print_title(WINDOW *dialog, const char *title, int width)
{
if (title) {
int tlen = MIN(width - 2, strlen(title));
wattrset(dialog, dlg.title.atr);
mvwaddch(dialog, 0, (width - tlen) / 2 - 1, ' ');
mvwaddnstr(dialog, 0, (width - tlen)/2, title, tlen);
waddch(dialog, ' ');
}
}
/*
* Print a string of text in a window, automatically wrap around to the
* next line if the string is too long to fit on one line. Newline
* characters '\n' are properly processed. We start on a new line
* if there is no room for at least 4 nonblanks following a double-space.
*/
void print_autowrap(WINDOW * win, const char *prompt, int width, int y, int x)
{
int newl, cur_x, cur_y;
int prompt_len, room, wlen;
char tempstr[MAX_LEN + 1], *word, *sp, *sp2, *newline_separator = 0;
strcpy(tempstr, prompt);
prompt_len = strlen(tempstr);
if (prompt_len <= width - x * 2) { /* If prompt is short */
wmove(win, y, (width - prompt_len) / 2);
waddstr(win, tempstr);
} else {
cur_x = x;
cur_y = y;
newl = 1;
word = tempstr;
while (word && *word) {
sp = strpbrk(word, "\n ");
if (sp && *sp == '\n')
newline_separator = sp;
if (sp)
*sp++ = 0;
/* Wrap to next line if either the word does not fit,
or it is the first word of a new sentence, and it is
short, and the next word does not fit. */
room = width - cur_x;
wlen = strlen(word);
if (wlen > room ||
(newl && wlen < 4 && sp
&& wlen + 1 + strlen(sp) > room
&& (!(sp2 = strpbrk(sp, "\n "))
|| wlen + 1 + (sp2 - sp) > room))) {
cur_y++;
cur_x = x;
}
wmove(win, cur_y, cur_x);
waddstr(win, word);
getyx(win, cur_y, cur_x);
/* Move to the next line if the word separator was a newline */
if (newline_separator) {
cur_y++;
cur_x = x;
newline_separator = 0;
} else
cur_x++;
if (sp && *sp == ' ') {
cur_x++; /* double space */
while (*++sp == ' ') ;
newl = 1;
} else
newl = 0;
word = sp;
}
}
}
/*
* Print a button
*/
void print_button(WINDOW * win, const char *label, int y, int x, int selected)
{
int i, temp;
wmove(win, y, x);
wattrset(win, selected ? dlg.button_active.atr
: dlg.button_inactive.atr);
waddstr(win, "<");
temp = strspn(label, " ");
label += temp;
wattrset(win, selected ? dlg.button_label_active.atr
: dlg.button_label_inactive.atr);
for (i = 0; i < temp; i++)
waddch(win, ' ');
wattrset(win, selected ? dlg.button_key_active.atr
: dlg.button_key_inactive.atr);
waddch(win, label[0]);
wattrset(win, selected ? dlg.button_label_active.atr
: dlg.button_label_inactive.atr);
waddstr(win, (char *)label + 1);
wattrset(win, selected ? dlg.button_active.atr
: dlg.button_inactive.atr);
waddstr(win, ">");
wmove(win, y, x + temp + 1);
}
/*
* Draw a rectangular box with line drawing characters
*/
void
draw_box(WINDOW * win, int y, int x, int height, int width,
chtype box, chtype border)
{
int i, j;
wattrset(win, 0);
for (i = 0; i < height; i++) {
wmove(win, y + i, x);
for (j = 0; j < width; j++)
if (!i && !j)
waddch(win, border | ACS_ULCORNER);
else if (i == height - 1 && !j)
waddch(win, border | ACS_LLCORNER);
else if (!i && j == width - 1)
waddch(win, box | ACS_URCORNER);
else if (i == height - 1 && j == width - 1)
waddch(win, box | ACS_LRCORNER);
else if (!i)
waddch(win, border | ACS_HLINE);
else if (i == height - 1)
waddch(win, box | ACS_HLINE);
else if (!j)
waddch(win, border | ACS_VLINE);
else if (j == width - 1)
waddch(win, box | ACS_VLINE);
else
waddch(win, box | ' ');
}
}
/*
* Draw shadows along the right and bottom edge to give a more 3D look
* to the boxes
*/
void draw_shadow(WINDOW * win, int y, int x, int height, int width)
{
int i;
if (has_colors()) { /* Whether terminal supports color? */
wattrset(win, dlg.shadow.atr);
wmove(win, y + height, x + 2);
for (i = 0; i < width; i++)
waddch(win, winch(win) & A_CHARTEXT);
for (i = y + 1; i < y + height + 1; i++) {
wmove(win, i, x + width);
waddch(win, winch(win) & A_CHARTEXT);
waddch(win, winch(win) & A_CHARTEXT);
}
wnoutrefresh(win);
}
}
/*
* Return the position of the first alphabetic character in a string.
*/
int first_alpha(const char *string, const char *exempt)
{
int i, in_paren = 0, c;
for (i = 0; i < strlen(string); i++) {
c = tolower(string[i]);
if (strchr("<[(", c))
++in_paren;
if (strchr(">])", c) && in_paren > 0)
--in_paren;
if ((!in_paren) && isalpha(c) && strchr(exempt, c) == 0)
return i;
}
return 0;
}
/*
* ncurses uses ESC to detect escaped char sequences. This resutl in
* a small timeout before ESC is actually delivered to the application.
* lxdialog suggest <ESC> <ESC> which is correctly translated to two
* times esc. But then we need to ignore the second esc to avoid stepping
* out one menu too much. Filter away all escaped key sequences since
* keypad(FALSE) turn off ncurses support for escape sequences - and that's
* needed to make notimeout() do as expected.
*/
int on_key_esc(WINDOW *win)
{
int key;
int key2;
int key3;
nodelay(win, TRUE);
keypad(win, FALSE);
key = wgetch(win);
key2 = wgetch(win);
do {
key3 = wgetch(win);
} while (key3 != ERR);
nodelay(win, FALSE);
keypad(win, TRUE);
if (key == KEY_ESC && key2 == ERR)
return KEY_ESC;
else if (key != ERR && key != KEY_ESC && key2 == ERR)
ungetch(key);
return -1;
}
/* redraw screen in new size */
int on_key_resize(void)
{
dialog_clear();
return KEY_RESIZE;
}
struct dialog_list *item_cur;
struct dialog_list item_nil;
struct dialog_list *item_head;
void item_reset(void)
{
struct dialog_list *p, *next;
for (p = item_head; p; p = next) {
next = p->next;
free(p);
}
item_head = NULL;
item_cur = &item_nil;
}
void item_make(const char *fmt, ...)
{
va_list ap;
struct dialog_list *p = malloc(sizeof(*p));
if (item_head)
item_cur->next = p;
else
item_head = p;
item_cur = p;
memset(p, 0, sizeof(*p));
va_start(ap, fmt);
vsnprintf(item_cur->node.str, sizeof(item_cur->node.str), fmt, ap);
va_end(ap);
}
void item_add_str(const char *fmt, ...)
{
va_list ap;
size_t avail;
avail = sizeof(item_cur->node.str) - strlen(item_cur->node.str);
va_start(ap, fmt);
vsnprintf(item_cur->node.str + strlen(item_cur->node.str),
avail, fmt, ap);
item_cur->node.str[sizeof(item_cur->node.str) - 1] = '\0';
va_end(ap);
}
void item_set_tag(char tag)
{
item_cur->node.tag = tag;
}
void item_set_data(void *ptr)
{
item_cur->node.data = ptr;
}
void item_set_selected(int val)
{
item_cur->node.selected = val;
}
int item_activate_selected(void)
{
item_foreach()
if (item_is_selected())
return 1;
return 0;
}
void *item_data(void)
{
return item_cur->node.data;
}
char item_tag(void)
{
return item_cur->node.tag;
}
int item_count(void)
{
int n = 0;
struct dialog_list *p;
for (p = item_head; p; p = p->next)
n++;
return n;
}
void item_set(int n)
{
int i = 0;
item_foreach()
if (i++ == n)
return;
}
int item_n(void)
{
int n = 0;
struct dialog_list *p;
for (p = item_head; p; p = p->next) {
if (p == item_cur)
return n;
n++;
}
return 0;
}
const char *item_str(void)
{
return item_cur->node.str;
}
int item_is_selected(void)
{
return (item_cur->node.selected != 0);
}
int item_is_tag(char tag)
{
return (item_cur->node.tag == tag);
}

View File

@@ -0,0 +1,101 @@
// SPDX-License-Identifier: GPL-2.0+
/*
* yesno.c -- implements the yes/no box
*
* ORIGINAL AUTHOR: Savio Lam (lam836@cs.cuhk.hk)
* MODIFIED FOR LINUX KERNEL CONFIG BY: William Roadcap (roadcap@cfw.com)
*/
#include "dialog.h"
/*
* Display termination buttons
*/
static void print_buttons(WINDOW * dialog, int height, int width, int selected)
{
int x = width / 2 - 10;
int y = height - 2;
print_button(dialog, " Yes ", y, x, selected == 0);
print_button(dialog, " No ", y, x + 13, selected == 1);
wmove(dialog, y, x + 1 + 13 * selected);
wrefresh(dialog);
}
/*
* Display a dialog box with two buttons - Yes and No
*/
int dialog_yesno(const char *title, const char *prompt, int height, int width)
{
int i, x, y, key = 0, button = 0;
WINDOW *dialog;
do_resize:
if (getmaxy(stdscr) < (height + YESNO_HEIGHT_MIN))
return -ERRDISPLAYTOOSMALL;
if (getmaxx(stdscr) < (width + YESNO_WIDTH_MIN))
return -ERRDISPLAYTOOSMALL;
/* center dialog box on screen */
x = (getmaxx(stdscr) - width) / 2;
y = (getmaxy(stdscr) - height) / 2;
draw_shadow(stdscr, y, x, height, width);
dialog = newwin(height, width, y, x);
keypad(dialog, TRUE);
draw_box(dialog, 0, 0, height, width,
dlg.dialog.atr, dlg.border.atr);
wattrset(dialog, dlg.border.atr);
mvwaddch(dialog, height - 3, 0, ACS_LTEE);
for (i = 0; i < width - 2; i++)
waddch(dialog, ACS_HLINE);
wattrset(dialog, dlg.dialog.atr);
waddch(dialog, ACS_RTEE);
print_title(dialog, title, width);
wattrset(dialog, dlg.dialog.atr);
print_autowrap(dialog, prompt, width - 2, 1, 3);
print_buttons(dialog, height, width, 0);
while (key != KEY_ESC) {
key = wgetch(dialog);
switch (key) {
case 'Y':
case 'y':
delwin(dialog);
return 0;
case 'N':
case 'n':
delwin(dialog);
return 1;
case TAB:
case KEY_LEFT:
case KEY_RIGHT:
button = ((key == KEY_LEFT ? --button : ++button) < 0) ? 1 : (button > 1 ? 0 : button);
print_buttons(dialog, height, width, button);
wrefresh(dialog);
break;
case ' ':
case '\n':
delwin(dialog);
return button;
case KEY_ESC:
key = on_key_esc(dialog);
break;
case KEY_RESIZE:
delwin(dialog);
on_key_resize();
goto do_resize;
}
}
delwin(dialog);
return key; /* ESC pressed */
}

View File

@@ -0,0 +1,57 @@
#!/bin/sh
# SPDX-License-Identifier: GPL-2.0
set -eu
cflags=$1
libs=$2
PKG="ncursesw"
PKG2="ncurses"
if [ -n "$(command -v ${HOSTPKG_CONFIG})" ]; then
if ${HOSTPKG_CONFIG} --exists $PKG; then
${HOSTPKG_CONFIG} --cflags ${PKG} > ${cflags}
${HOSTPKG_CONFIG} --libs ${PKG} > ${libs}
exit 0
fi
if ${HOSTPKG_CONFIG} --exists ${PKG2}; then
${HOSTPKG_CONFIG} --cflags ${PKG2} > ${cflags}
${HOSTPKG_CONFIG} --libs ${PKG2} > ${libs}
exit 0
fi
fi
# Check the default paths in case pkg-config is not installed.
# (Even if it is installed, some distributions such as openSUSE cannot
# find ncurses by pkg-config.)
if [ -f /usr/include/ncursesw/ncurses.h ]; then
echo -D_GNU_SOURCE -I/usr/include/ncursesw > ${cflags}
echo -lncursesw > ${libs}
exit 0
fi
if [ -f /usr/include/ncurses/ncurses.h ]; then
echo -D_GNU_SOURCE -I/usr/include/ncurses > ${cflags}
echo -lncurses > ${libs}
exit 0
fi
# As a final fallback before giving up, check if $HOSTCC knows of a default
# ncurses installation (e.g. from a vendor-specific sysroot).
if echo '#include <ncurses.h>' | ${HOSTCC} -E - >/dev/null 2>&1; then
echo -D_GNU_SOURCE > ${cflags}
echo -lncurses > ${libs}
exit 0
fi
echo >&2 "*"
echo >&2 "* Unable to find the ncurses package."
echo >&2 "* Install ncurses (ncurses-devel or libncurses-dev"
echo >&2 "* depending on your distribution)."
echo >&2 "*"
echo >&2 "* You may also need to install ${HOSTPKG_CONFIG} to find the"
echo >&2 "* ncurses installed in a non-default location."
echo >&2 "*"
exit 1

View File

@@ -0,0 +1,963 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>
*
* Introduced single menu mode (show all sub-menus in one large tree).
* 2002-11-06 Petr Baudis <pasky@ucw.cz>
*
* i18n, 2005, Arnaldo Carvalho de Melo <acme@conectiva.com.br>
*/
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <signal.h>
#include <unistd.h>
#include <list.h>
#include <xalloc.h>
#include "lkc.h"
#include "lxdialog/dialog.h"
#include "mnconf-common.h"
static const char mconf_readme[] =
"Overview\n"
"--------\n"
"This interface lets you select features and parameters for the build.\n"
"Features can either be built-in, modularized, or ignored. Parameters\n"
"must be entered in as decimal or hexadecimal numbers or text.\n"
"\n"
"Menu items beginning with following braces represent features that\n"
" [ ] can be built in or removed\n"
" < > can be built in, modularized or removed\n"
" { } can be built in or modularized (selected by other feature)\n"
" - - are selected by other feature,\n"
"while *, M or whitespace inside braces means to build in, build as\n"
"a module or to exclude the feature respectively.\n"
"\n"
"To change any of these features, highlight it with the cursor\n"
"keys and press <Y> to build it in, <M> to make it a module or\n"
"<N> to remove it. You may also press the <Space Bar> to cycle\n"
"through the available options (i.e. Y->N->M->Y).\n"
"\n"
"Some additional keyboard hints:\n"
"\n"
"Menus\n"
"----------\n"
"o Use the Up/Down arrow keys (cursor keys) to highlight the item you\n"
" wish to change or the submenu you wish to select and press <Enter>.\n"
" Submenus are designated by \"--->\", empty ones by \"----\".\n"
"\n"
" Shortcut: Press the option's highlighted letter (hotkey).\n"
" Pressing a hotkey more than once will sequence\n"
" through all visible items which use that hotkey.\n"
"\n"
" You may also use the <PAGE UP> and <PAGE DOWN> keys to scroll\n"
" unseen options into view.\n"
"\n"
"o To exit a menu use the cursor keys to highlight the <Exit> button\n"
" and press <ENTER>.\n"
"\n"
" Shortcut: Press <ESC><ESC> or <E> or <X> if there is no hotkey\n"
" using those letters. You may press a single <ESC>, but\n"
" there is a delayed response which you may find annoying.\n"
"\n"
" Also, the <TAB> and cursor keys will cycle between <Select>,\n"
" <Exit>, <Help>, <Save>, and <Load>.\n"
"\n"
"o To get help with an item, use the cursor keys to highlight <Help>\n"
" and press <ENTER>.\n"
"\n"
" Shortcut: Press <H> or <?>.\n"
"\n"
"o To toggle the display of hidden options, press <Z>.\n"
"\n"
"\n"
"Radiolists (Choice lists)\n"
"-----------\n"
"o Use the cursor keys to select the option you wish to set and press\n"
" <S> or the <SPACE BAR>.\n"
"\n"
" Shortcut: Press the first letter of the option you wish to set then\n"
" press <S> or <SPACE BAR>.\n"
"\n"
"o To see available help for the item, use the cursor keys to highlight\n"
" <Help> and Press <ENTER>.\n"
"\n"
" Shortcut: Press <H> or <?>.\n"
"\n"
" Also, the <TAB> and cursor keys will cycle between <Select> and\n"
" <Help>\n"
"\n"
"\n"
"Data Entry\n"
"-----------\n"
"o Enter the requested information and press <ENTER>\n"
" If you are entering hexadecimal values, it is not necessary to\n"
" add the '0x' prefix to the entry.\n"
"\n"
"o For help, use the <TAB> or cursor keys to highlight the help option\n"
" and press <ENTER>. You can try <TAB><H> as well.\n"
"\n"
"\n"
"Text Box (Help Window)\n"
"--------\n"
"o Use the cursor keys to scroll up/down/left/right. The VI editor\n"
" keys h,j,k,l function here as do <u>, <d>, <SPACE BAR> and <B> for\n"
" those who are familiar with less and lynx.\n"
"\n"
"o Press <E>, <X>, <q>, <Enter> or <Esc><Esc> to exit.\n"
"\n"
"\n"
"Alternate Configuration Files\n"
"-----------------------------\n"
"Menuconfig supports the use of alternate configuration files for\n"
"those who, for various reasons, find it necessary to switch\n"
"between different configurations.\n"
"\n"
"The <Save> button will let you save the current configuration to\n"
"a file of your choosing. Use the <Load> button to load a previously\n"
"saved alternate configuration.\n"
"\n"
"Even if you don't use alternate configuration files, but you find\n"
"during a Menuconfig session that you have completely messed up your\n"
"settings, you may use the <Load> button to restore your previously\n"
"saved settings from \".config\" without restarting Menuconfig.\n"
"\n"
"Other information\n"
"-----------------\n"
"If you use Menuconfig in an XTERM window, make sure you have your\n"
"$TERM variable set to point to an xterm definition which supports\n"
"color. Otherwise, Menuconfig will look rather bad. Menuconfig will\n"
"not display correctly in an RXVT window because rxvt displays only one\n"
"intensity of color, bright.\n"
"\n"
"Menuconfig will display larger menus on screens or xterms which are\n"
"set to display more than the standard 25 row by 80 column geometry.\n"
"In order for this to work, the \"stty size\" command must be able to\n"
"display the screen's current row and column geometry. I STRONGLY\n"
"RECOMMEND that you make sure you do NOT have the shell variables\n"
"LINES and COLUMNS exported into your environment. Some distributions\n"
"export those variables via /etc/profile. Some ncurses programs can\n"
"become confused when those variables (LINES & COLUMNS) don't reflect\n"
"the true screen size.\n"
"\n"
"Optional personality available\n"
"------------------------------\n"
"If you prefer to have all of the options listed in a single menu,\n"
"rather than the default multimenu hierarchy, run the menuconfig with\n"
"MENUCONFIG_MODE environment variable set to single_menu. Example:\n"
"\n"
"make MENUCONFIG_MODE=single_menu menuconfig\n"
"\n"
"<Enter> will then unroll the appropriate category, or enfold it if it\n"
"is already unrolled.\n"
"\n"
"Note that this mode can eventually be a little more CPU expensive\n"
"(especially with a larger number of unrolled categories) than the\n"
"default mode.\n"
"\n"
"Search\n"
"-------\n"
"Pressing the forward-slash (/) anywhere brings up a search dialog box.\n"
"\n"
"Different color themes available\n"
"--------------------------------\n"
"It is possible to select different color themes using the variable\n"
"MENUCONFIG_COLOR. To select a theme use:\n"
"\n"
"make MENUCONFIG_COLOR=<theme> menuconfig\n"
"\n"
"Available themes are\n"
" mono => selects colors suitable for monochrome displays\n"
" blackbg => selects a color scheme with black background\n"
" classic => theme with blue background. The classic look\n"
" bluetitle => an LCD friendly version of classic. (default)\n"
"\n",
menu_instructions[] =
"Arrow keys navigate the menu. "
"<Enter> selects submenus ---> (or empty submenus ----). "
"Highlighted letters are hotkeys. "
"Pressing <Y> includes, <N> excludes, <M> modularizes features. "
"Press <Esc><Esc> to exit, <?> for Help, </> for Search. "
"Legend: [*] built-in [ ] excluded <M> module < > module capable",
radiolist_instructions[] =
"Use the arrow keys to navigate this window or "
"press the hotkey of the item you wish to select "
"followed by the <SPACE BAR>. "
"Press <?> for additional information about this option.",
inputbox_instructions_int[] =
"Please enter a decimal value. "
"Fractions will not be accepted. "
"Use the <TAB> key to move from the input field to the buttons below it.",
inputbox_instructions_hex[] =
"Please enter a hexadecimal value. "
"Use the <TAB> key to move from the input field to the buttons below it.",
inputbox_instructions_string[] =
"Please enter a string value. "
"Use the <TAB> key to move from the input field to the buttons below it.",
setmod_text[] =
"This feature depends on another which has been configured as a module.\n"
"As a result, this feature will be built as a module.",
load_config_text[] =
"Enter the name of the configuration file you wish to load. "
"Accept the name shown to restore the configuration you "
"last retrieved. Leave blank to abort.",
load_config_help[] =
"\n"
"For various reasons, one may wish to keep several different\n"
"configurations available on a single machine.\n"
"\n"
"If you have saved a previous configuration in a file other than the\n"
"default one, entering its name here will allow you to modify that\n"
"configuration.\n"
"\n"
"If you are uncertain, then you have probably never used alternate\n"
"configuration files. You should therefore leave this blank to abort.\n",
save_config_text[] =
"Enter a filename to which this configuration should be saved "
"as an alternate. Leave blank to abort.",
save_config_help[] =
"\n"
"For various reasons, one may wish to keep different configurations\n"
"available on a single machine.\n"
"\n"
"Entering a file name here will allow you to later retrieve, modify\n"
"and use the current configuration as an alternate to whatever\n"
"configuration options you have selected at that time.\n"
"\n"
"If you are uncertain what all this means then you should probably\n"
"leave this blank.\n",
search_help[] =
"\n"
"Search for symbols and display their relations.\n"
"Regular expressions are allowed.\n"
"Example: search for \"^FOO\"\n"
"Result:\n"
"-----------------------------------------------------------------\n"
"Symbol: FOO [=m]\n"
"Type : tristate\n"
"Prompt: Foo bus is used to drive the bar HW\n"
" Location:\n"
" -> Bus options (PCI, PCMCIA, EISA, ISA)\n"
" -> PCI support (PCI [=y])\n"
"(1) -> PCI access mode (<choice> [=y])\n"
" Defined at drivers/pci/Kconfig:47\n"
" Depends on: X86_LOCAL_APIC && X86_IO_APIC\n"
" Selects: LIBCRC32\n"
" Selected by: BAR [=n]\n"
"-----------------------------------------------------------------\n"
"o The line 'Type:' shows the type of the configuration option for\n"
" this symbol (bool, tristate, string, ...)\n"
"o The line 'Prompt:' shows the text used in the menu structure for\n"
" this symbol\n"
"o The 'Defined at' line tells at what file / line number the symbol\n"
" is defined\n"
"o The 'Depends on:' line tells what symbols need to be defined for\n"
" this symbol to be visible in the menu (selectable)\n"
"o The 'Location:' lines tells where in the menu structure this symbol\n"
" is located\n"
" A location followed by a [=y] indicates that this is a\n"
" selectable menu item - and the current value is displayed inside\n"
" brackets.\n"
" Press the key in the (#) prefix to jump directly to that\n"
" location. You will be returned to the current search results\n"
" after exiting this new menu.\n"
"o The 'Selects:' line tells what symbols will be automatically\n"
" selected if this symbol is selected (y or m)\n"
"o The 'Selected by' line tells what symbol has selected this symbol\n"
"\n"
"Only relevant lines are shown.\n"
"\n\n"
"Search examples:\n"
"Examples: USB => find all symbols containing USB\n"
" ^USB => find all symbols starting with USB\n"
" USB$ => find all symbols ending with USB\n"
"\n";
static int indent;
static struct menu *current_menu;
static int child_count;
static int single_menu_mode;
static int show_all_options;
static int save_and_exit;
static int silent;
static void conf(struct menu *menu, struct menu *active_menu);
static char filename[PATH_MAX+1];
static void set_config_filename(const char *config_filename)
{
static char menu_backtitle[PATH_MAX+128];
snprintf(menu_backtitle, sizeof(menu_backtitle), "%s - %s",
config_filename, rootmenu.prompt->text);
set_dialog_backtitle(menu_backtitle);
snprintf(filename, sizeof(filename), "%s", config_filename);
}
struct subtitle_part {
struct list_head entries;
const char *text;
};
static LIST_HEAD(trail);
static struct subtitle_list *subtitles;
static void set_subtitle(void)
{
struct subtitle_part *sp;
struct subtitle_list *pos, *tmp;
for (pos = subtitles; pos != NULL; pos = tmp) {
tmp = pos->next;
free(pos);
}
subtitles = NULL;
list_for_each_entry(sp, &trail, entries) {
if (sp->text) {
if (pos) {
pos->next = xcalloc(1, sizeof(*pos));
pos = pos->next;
} else {
subtitles = pos = xcalloc(1, sizeof(*pos));
}
pos->text = sp->text;
}
}
set_dialog_subtitles(subtitles);
}
static void reset_subtitle(void)
{
struct subtitle_list *pos, *tmp;
for (pos = subtitles; pos != NULL; pos = tmp) {
tmp = pos->next;
free(pos);
}
subtitles = NULL;
set_dialog_subtitles(subtitles);
}
static int show_textbox_ext(const char *title, const char *text, int r, int c,
int *vscroll, int *hscroll,
int (*extra_key_cb)(int, size_t, size_t, void *),
void *data)
{
dialog_clear();
return dialog_textbox(title, text, r, c, vscroll, hscroll,
extra_key_cb, data);
}
static void show_textbox(const char *title, const char *text, int r, int c)
{
show_textbox_ext(title, text, r, c, NULL, NULL, NULL, NULL);
}
static void show_helptext(const char *title, const char *text)
{
show_textbox(title, text, 0, 0);
}
static void show_help(struct menu *menu)
{
struct gstr help = str_new();
help.max_width = getmaxx(stdscr) - 10;
menu_get_ext_help(menu, &help);
show_helptext(menu_get_prompt(menu), str_get(&help));
str_free(&help);
}
static void search_conf(void)
{
struct symbol **sym_arr;
struct gstr res;
struct gstr title;
char *dialog_input;
int dres, vscroll = 0, hscroll = 0;
bool again;
struct gstr sttext;
struct subtitle_part stpart;
title = str_new();
str_printf( &title, "Enter (sub)string or regexp to search for "
"(with or without \"%s\")", CONFIG_);
again:
dialog_clear();
dres = dialog_inputbox("Search Configuration Parameter",
str_get(&title),
10, 75, "");
switch (dres) {
case 0:
break;
case 1:
show_helptext("Search Configuration", search_help);
goto again;
default:
str_free(&title);
return;
}
/* strip the prefix if necessary */
dialog_input = dialog_input_result;
if (strncasecmp(dialog_input_result, CONFIG_, strlen(CONFIG_)) == 0)
dialog_input += strlen(CONFIG_);
sttext = str_new();
str_printf(&sttext, "Search (%s)", dialog_input_result);
stpart.text = str_get(&sttext);
list_add_tail(&stpart.entries, &trail);
sym_arr = sym_re_search(dialog_input);
do {
LIST_HEAD(head);
struct search_data data = {
.head = &head,
};
struct jump_key *pos, *tmp;
jump_key_char = 0;
res = get_relations_str(sym_arr, &head);
set_subtitle();
dres = show_textbox_ext("Search Results", str_get(&res), 0, 0,
&vscroll, &hscroll,
handle_search_keys, &data);
again = false;
if (dres >= '1' && dres <= '9') {
assert(data.target != NULL);
conf(data.target->parent, data.target);
again = true;
}
str_free(&res);
list_for_each_entry_safe(pos, tmp, &head, entries)
free(pos);
} while (again);
free(sym_arr);
str_free(&title);
list_del(trail.prev);
str_free(&sttext);
}
static void build_conf(struct menu *menu)
{
struct symbol *sym;
struct property *prop;
struct menu *child;
int type, tmp, doint = 2;
tristate val;
char ch;
bool visible;
/*
* note: menu_is_visible() has side effect that it will
* recalc the value of the symbol.
*/
visible = menu_is_visible(menu);
if (show_all_options && !menu_has_prompt(menu))
return;
else if (!show_all_options && !visible)
return;
sym = menu->sym;
prop = menu->prompt;
if (!sym) {
if (prop && menu != current_menu) {
const char *prompt = menu_get_prompt(menu);
switch (prop->type) {
case P_MENU:
child_count++;
if (single_menu_mode) {
item_make("%s%*c%s",
menu->data ? "-->" : "++>",
indent + 1, ' ', prompt);
} else
item_make(" %*c%s %s",
indent + 1, ' ', prompt,
menu_is_empty(menu) ? "----" : "--->");
item_set_tag('m');
item_set_data(menu);
if (single_menu_mode && menu->data)
goto conf_childs;
return;
case P_COMMENT:
if (prompt) {
child_count++;
item_make(" %*c*** %s ***", indent + 1, ' ', prompt);
item_set_tag(':');
item_set_data(menu);
}
break;
default:
if (prompt) {
child_count++;
item_make("---%*c%s", indent + 1, ' ', prompt);
item_set_tag(':');
item_set_data(menu);
}
}
} else
doint = 0;
goto conf_childs;
}
type = sym_get_type(sym);
if (sym_is_choice(sym)) {
struct symbol *def_sym = sym_calc_choice(menu);
struct menu *def_menu = NULL;
child_count++;
for (child = menu->list; child; child = child->next) {
if (menu_is_visible(child) && child->sym == def_sym)
def_menu = child;
}
item_make(" ");
item_set_tag(def_menu ? 't' : ':');
item_set_data(menu);
item_add_str("%*c%s", indent + 1, ' ', menu_get_prompt(menu));
if (def_menu)
item_add_str(" (%s) --->", menu_get_prompt(def_menu));
return;
} else {
if (menu == current_menu) {
item_make("---%*c%s", indent + 1, ' ', menu_get_prompt(menu));
item_set_tag(':');
item_set_data(menu);
goto conf_childs;
}
child_count++;
val = sym_get_tristate_value(sym);
switch (type) {
case S_BOOLEAN:
if (sym_is_changeable(sym))
item_make("[%c]", val == no ? ' ' : '*');
else
item_make("-%c-", val == no ? ' ' : '*');
item_set_tag('t');
item_set_data(menu);
break;
case S_TRISTATE:
switch (val) {
case yes: ch = '*'; break;
case mod: ch = 'M'; break;
default: ch = ' '; break;
}
if (sym_is_changeable(sym)) {
if (sym->rev_dep.tri == mod)
item_make("{%c}", ch);
else
item_make("<%c>", ch);
} else
item_make("-%c-", ch);
item_set_tag('t');
item_set_data(menu);
break;
default:
tmp = 2 + strlen(sym_get_string_value(sym)); /* () = 2 */
item_make("(%s)", sym_get_string_value(sym));
tmp = indent - tmp + 4;
if (tmp < 0)
tmp = 0;
item_add_str("%*c%s%s", tmp, ' ', menu_get_prompt(menu),
(sym_has_value(sym) || !sym_is_changeable(sym)) ?
"" : " (NEW)");
item_set_tag('s');
item_set_data(menu);
goto conf_childs;
}
item_add_str("%*c%s%s", indent + 1, ' ', menu_get_prompt(menu),
(sym_has_value(sym) || !sym_is_changeable(sym)) ?
"" : " (NEW)");
if (menu->prompt->type == P_MENU) {
item_add_str(" %s", menu_is_empty(menu) ? "----" : "--->");
return;
}
}
conf_childs:
indent += doint;
for (child = menu->list; child; child = child->next)
build_conf(child);
indent -= doint;
}
static void conf_choice(struct menu *menu)
{
const char *prompt = menu_get_prompt(menu);
struct menu *child;
struct symbol *active;
active = sym_calc_choice(menu);
while (1) {
int res;
int selected;
item_reset();
current_menu = menu;
for (child = menu->list; child; child = child->next) {
if (!menu_is_visible(child))
continue;
if (child->sym)
item_make("%s", menu_get_prompt(child));
else {
item_make("*** %s ***", menu_get_prompt(child));
item_set_tag(':');
}
item_set_data(child);
if (child->sym == active)
item_set_selected(1);
if (child->sym == sym_calc_choice(menu))
item_set_tag('X');
}
dialog_clear();
res = dialog_checklist(prompt ? prompt : "Main Menu",
radiolist_instructions,
MENUBOX_HEIGHT_MIN,
MENUBOX_WIDTH_MIN,
CHECKLIST_HEIGHT_MIN);
selected = item_activate_selected();
switch (res) {
case 0:
if (selected) {
child = item_data();
if (!child->sym)
break;
choice_set_value(menu, child->sym);
}
return;
case 1:
if (selected) {
child = item_data();
show_help(child);
active = child->sym;
} else
show_help(menu);
break;
case KEY_ESC:
return;
case -ERRDISPLAYTOOSMALL:
return;
}
}
}
static void conf_string(struct menu *menu)
{
const char *prompt = menu_get_prompt(menu);
while (1) {
int res;
const char *heading;
switch (sym_get_type(menu->sym)) {
case S_INT:
heading = inputbox_instructions_int;
break;
case S_HEX:
heading = inputbox_instructions_hex;
break;
case S_STRING:
heading = inputbox_instructions_string;
break;
default:
heading = "Internal mconf error!";
}
dialog_clear();
res = dialog_inputbox(prompt ? prompt : "Main Menu",
heading, 10, 75,
sym_get_string_value(menu->sym));
switch (res) {
case 0:
if (sym_set_string_value(menu->sym, dialog_input_result))
return;
show_textbox(NULL, "You have made an invalid entry.", 5, 43);
break;
case 1:
show_help(menu);
break;
case KEY_ESC:
return;
}
}
}
static void conf_load(void)
{
while (1) {
int res;
dialog_clear();
res = dialog_inputbox(NULL, load_config_text,
11, 55, filename);
switch(res) {
case 0:
if (!dialog_input_result[0])
return;
if (!conf_read(dialog_input_result)) {
set_config_filename(dialog_input_result);
conf_set_changed(true);
return;
}
show_textbox(NULL, "File does not exist!", 5, 38);
break;
case 1:
show_helptext("Load Alternate Configuration", load_config_help);
break;
case KEY_ESC:
return;
}
}
}
static void conf_save(void)
{
while (1) {
int res;
dialog_clear();
res = dialog_inputbox(NULL, save_config_text,
11, 55, filename);
switch(res) {
case 0:
if (!dialog_input_result[0])
return;
if (!conf_write(dialog_input_result)) {
set_config_filename(dialog_input_result);
return;
}
show_textbox(NULL, "Can't create file!", 5, 60);
break;
case 1:
show_helptext("Save Alternate Configuration", save_config_help);
break;
case KEY_ESC:
return;
}
}
}
static void conf(struct menu *menu, struct menu *active_menu)
{
struct menu *submenu;
const char *prompt = menu_get_prompt(menu);
struct subtitle_part stpart;
struct symbol *sym;
int res;
int s_scroll = 0;
if (menu != &rootmenu)
stpart.text = menu_get_prompt(menu);
else
stpart.text = NULL;
list_add_tail(&stpart.entries, &trail);
while (1) {
item_reset();
current_menu = menu;
build_conf(menu);
if (!child_count)
break;
set_subtitle();
dialog_clear();
res = dialog_menu(prompt ? prompt : "Main Menu",
menu_instructions,
active_menu, &s_scroll);
if (res == 1 || res == KEY_ESC || res == -ERRDISPLAYTOOSMALL)
break;
if (item_count() != 0) {
if (!item_activate_selected())
continue;
if (!item_tag())
continue;
}
submenu = item_data();
active_menu = item_data();
if (submenu)
sym = submenu->sym;
else
sym = NULL;
switch (res) {
case 0:
switch (item_tag()) {
case 'm':
if (single_menu_mode)
submenu->data = (void *) (long) !submenu->data;
else
conf(submenu, NULL);
break;
case 't':
if (sym_is_choice(sym))
conf_choice(submenu);
else if (submenu->prompt->type == P_MENU)
conf(submenu, NULL);
break;
case 's':
conf_string(submenu);
break;
}
break;
case 2:
if (sym)
show_help(submenu);
else {
reset_subtitle();
show_helptext("README", mconf_readme);
}
break;
case 3:
reset_subtitle();
conf_save();
break;
case 4:
reset_subtitle();
conf_load();
break;
case 5:
if (item_is_tag('t')) {
if (sym_set_tristate_value(sym, yes))
break;
if (sym_set_tristate_value(sym, mod))
show_textbox(NULL, setmod_text, 6, 74);
}
break;
case 6:
if (item_is_tag('t'))
sym_set_tristate_value(sym, no);
break;
case 7:
if (item_is_tag('t'))
sym_set_tristate_value(sym, mod);
break;
case 8:
if (item_is_tag('t'))
sym_toggle_tristate_value(sym);
else if (item_is_tag('m'))
conf(submenu, NULL);
break;
case 9:
search_conf();
break;
case 10:
show_all_options = !show_all_options;
break;
}
}
list_del(trail.prev);
}
static void conf_message_callback(const char *s)
{
if (save_and_exit) {
if (!silent)
printf("%s", s);
} else {
show_textbox(NULL, s, 6, 60);
}
}
static int handle_exit(void)
{
int res;
save_and_exit = 1;
reset_subtitle();
dialog_clear();
if (conf_get_changed())
res = dialog_yesno(NULL,
"Do you wish to save your new configuration?\n"
"(Press <ESC><ESC> to continue kernel configuration.)",
6, 60);
else
res = -1;
end_dialog(saved_x, saved_y);
switch (res) {
case 0:
if (conf_write(filename)) {
fprintf(stderr, "\n\n"
"Error while writing of the configuration.\n"
"Your configuration changes were NOT saved."
"\n\n");
return 1;
}
conf_write_autoconf(0);
/* fall through */
case -1:
if (!silent)
printf("\n\n"
"*** End of the configuration.\n"
"*** Execute 'make' to start the build or try 'make help'."
"\n\n");
res = 0;
break;
default:
if (!silent)
fprintf(stderr, "\n\n"
"Your configuration changes were NOT saved."
"\n\n");
if (res != KEY_ESC)
res = 0;
}
return res;
}
static void sig_handler(int signo)
{
exit(handle_exit());
}
int main(int ac, char **av)
{
char *mode;
int res;
signal(SIGINT, sig_handler);
if (ac > 1 && strcmp(av[1], "-s") == 0) {
silent = 1;
/* Silence conf_read() until the real callback is set up */
conf_set_message_callback(NULL);
av++;
}
conf_parse(av[1]);
conf_read(NULL);
mode = getenv("MENUCONFIG_MODE");
if (mode) {
if (!strcasecmp(mode, "single_menu"))
single_menu_mode = 1;
}
if (init_dialog(NULL)) {
fprintf(stderr, "Your display is too small to run Menuconfig!\n");
fprintf(stderr, "It must be at least 19 lines by 80 columns.\n");
return 1;
}
set_config_filename(conf_get_configname());
conf_set_message_callback(conf_message_callback);
do {
conf(&rootmenu, NULL);
res = handle_exit();
} while (res == KEY_ESC);
return res;
}

View File

@@ -0,0 +1,770 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>
*/
#include <ctype.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <list.h>
#include <xalloc.h>
#include "lkc.h"
#include "internal.h"
static const char nohelp_text[] = "There is no help available for this option.";
struct menu rootmenu = { .type = M_MENU };
static struct menu **last_entry_ptr;
/**
* menu_next - return the next menu entry with depth-first traversal
* @menu: pointer to the current menu
* @root: root of the sub-tree to traverse. If NULL is given, the traveral
* continues until it reaches the end of the entire menu tree.
* return: the menu to visit next, or NULL when it reaches the end.
*/
struct menu *menu_next(struct menu *menu, struct menu *root)
{
if (menu->list)
return menu->list;
while (menu != root && !menu->next)
menu = menu->parent;
if (menu == root)
return NULL;
return menu->next;
}
void menu_warn(const struct menu *menu, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
fprintf(stderr, "%s:%d:warning: ", menu->filename, menu->lineno);
vfprintf(stderr, fmt, ap);
fprintf(stderr, "\n");
va_end(ap);
}
static void prop_warn(const struct property *prop, const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
fprintf(stderr, "%s:%d:warning: ", prop->filename, prop->lineno);
vfprintf(stderr, fmt, ap);
fprintf(stderr, "\n");
va_end(ap);
}
void _menu_init(void)
{
current_entry = current_menu = &rootmenu;
last_entry_ptr = &rootmenu.list;
}
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;
menu->lineno = cur_lineno;
*last_entry_ptr = menu;
last_entry_ptr = &menu->next;
current_entry = menu;
if (sym)
list_add_tail(&menu->link, &sym->menus);
}
struct menu *menu_add_menu(void)
{
last_entry_ptr = &current_entry->list;
current_menu = current_entry;
return current_menu;
}
void menu_end_menu(void)
{
last_entry_ptr = &current_menu->next;
current_menu = current_menu->parent;
}
/*
* Rewrites 'm' to 'm' && MODULES, so that it evaluates to 'n' when running
* without modules
*/
static struct expr *rewrite_m(struct expr *e)
{
if (!e)
return e;
switch (e->type) {
case E_NOT:
e = expr_alloc_one(E_NOT, rewrite_m(e->left.expr));
break;
case E_OR:
case E_AND:
e = expr_alloc_two(e->type,
rewrite_m(e->left.expr),
rewrite_m(e->right.expr));
break;
case E_SYMBOL:
/* change 'm' into 'm' && MODULES */
if (e->left.sym == &symbol_mod)
return expr_alloc_and(e, expr_alloc_symbol(modules_sym));
break;
default:
break;
}
return e;
}
void menu_add_dep(struct expr *dep)
{
current_entry->dep = expr_alloc_and(current_entry->dep, dep);
}
void menu_set_type(int type)
{
struct symbol *sym = current_entry->sym;
if (sym->type == type)
return;
if (sym->type == S_UNKNOWN) {
sym->type = type;
return;
}
menu_warn(current_entry,
"ignoring type redefinition of '%s' from '%s' to '%s'",
sym->name ? sym->name : "<choice>",
sym_type_name(sym->type), sym_type_name(type));
}
static struct property *menu_add_prop(enum prop_type type, struct expr *expr,
struct expr *dep)
{
struct property *prop;
prop = xmalloc(sizeof(*prop));
memset(prop, 0, sizeof(*prop));
prop->type = type;
prop->filename = cur_filename;
prop->lineno = cur_lineno;
prop->menu = current_entry;
prop->expr = expr;
prop->visible.expr = dep;
/* append property to the prop list of symbol */
if (current_entry->sym) {
struct property **propp;
for (propp = &current_entry->sym->prop;
*propp;
propp = &(*propp)->next)
;
*propp = prop;
}
return prop;
}
struct property *menu_add_prompt(enum prop_type type, const char *prompt,
struct expr *dep)
{
struct property *prop = menu_add_prop(type, NULL, dep);
if (isspace(*prompt)) {
prop_warn(prop, "leading whitespace ignored");
while (isspace(*prompt))
prompt++;
}
if (current_entry->prompt)
prop_warn(prop, "prompt redefined");
/* Apply all upper menus' visibilities to actual prompts. */
if (type == P_PROMPT) {
struct menu *menu = current_entry;
while ((menu = menu->parent) != NULL) {
if (!menu->visibility)
continue;
prop->visible.expr = expr_alloc_and(prop->visible.expr,
menu->visibility);
}
}
current_entry->prompt = prop;
prop->text = prompt;
return prop;
}
void menu_add_visibility(struct expr *expr)
{
current_entry->visibility = expr_alloc_and(current_entry->visibility,
expr);
}
void menu_add_expr(enum prop_type type, struct expr *expr, struct expr *dep)
{
menu_add_prop(type, expr, dep);
}
void menu_add_symbol(enum prop_type type, struct symbol *sym, struct expr *dep)
{
menu_add_prop(type, expr_alloc_symbol(sym), dep);
}
static int menu_validate_number(struct symbol *sym, struct symbol *sym2)
{
return sym2->type == S_INT || sym2->type == S_HEX ||
(sym2->type == S_UNKNOWN && sym_string_valid(sym, sym2->name));
}
static void sym_check_prop(struct symbol *sym)
{
struct property *prop;
struct symbol *sym2;
char *use;
for (prop = sym->prop; prop; prop = prop->next) {
switch (prop->type) {
case P_DEFAULT:
if ((sym->type == S_STRING || sym->type == S_INT || sym->type == S_HEX) &&
prop->expr->type != E_SYMBOL)
prop_warn(prop,
"default for config symbol '%s'"
" must be a single symbol", sym->name);
if (prop->expr->type != E_SYMBOL)
break;
sym2 = prop_get_symbol(prop);
if (sym->type == S_HEX || sym->type == S_INT) {
if (!menu_validate_number(sym, sym2))
prop_warn(prop,
"'%s': number is invalid",
sym->name);
}
if (sym_is_choice(sym)) {
struct menu *choice = sym_get_choice_menu(sym2);
if (!choice || choice->sym != sym)
prop_warn(prop,
"choice default symbol '%s' is not contained in the choice",
sym2->name);
}
break;
case P_SELECT:
case P_IMPLY:
use = prop->type == P_SELECT ? "select" : "imply";
sym2 = prop_get_symbol(prop);
if (sym->type != S_BOOLEAN && sym->type != S_TRISTATE)
prop_warn(prop,
"config symbol '%s' uses %s, but is "
"not bool or tristate", sym->name, use);
else if (sym2->type != S_UNKNOWN &&
sym2->type != S_BOOLEAN &&
sym2->type != S_TRISTATE)
prop_warn(prop,
"'%s' has wrong type. '%s' only "
"accept arguments of bool and "
"tristate type", sym2->name, use);
break;
case P_RANGE:
if (sym->type != S_INT && sym->type != S_HEX)
prop_warn(prop, "range is only allowed "
"for int or hex symbols");
if (!menu_validate_number(sym, prop->expr->left.sym) ||
!menu_validate_number(sym, prop->expr->right.sym))
prop_warn(prop, "range is invalid");
break;
default:
;
}
}
}
static void _menu_finalize(struct menu *parent, bool inside_choice)
{
struct menu *menu, *last_menu;
struct symbol *sym;
struct property *prop;
struct expr *basedep, *dep, *dep2;
sym = parent->sym;
if (parent->list) {
/*
* This menu node has children. We (recursively) process them
* and propagate parent dependencies before moving on.
*/
/* For each child menu node... */
for (menu = parent->list; menu; menu = menu->next) {
/*
* Propagate parent dependencies to the child menu
* node, also rewriting and simplifying expressions
*/
basedep = rewrite_m(menu->dep);
basedep = expr_transform(basedep);
basedep = expr_alloc_and(parent->dep, basedep);
basedep = expr_eliminate_dups(basedep);
menu->dep = basedep;
if (menu->sym)
/*
* Note: For symbols, all prompts are included
* too in the symbol's own property list
*/
prop = menu->sym->prop;
else
/*
* For non-symbol menu nodes, we just need to
* handle the prompt
*/
prop = menu->prompt;
/* For each property... */
for (; prop; prop = prop->next) {
if (prop->menu != menu)
/*
* Two possibilities:
*
* 1. The property lacks dependencies
* and so isn't location-specific,
* e.g. an 'option'
*
* 2. The property belongs to a symbol
* defined in multiple locations and
* is from some other location. It
* will be handled there in that
* case.
*
* Skip the property.
*/
continue;
/*
* Propagate parent dependencies to the
* property's condition, rewriting and
* simplifying expressions at the same time
*/
dep = rewrite_m(prop->visible.expr);
dep = expr_transform(dep);
dep = expr_alloc_and(basedep, dep);
dep = expr_eliminate_dups(dep);
prop->visible.expr = dep;
/*
* Handle selects and implies, which modify the
* dependencies of the selected/implied symbol
*/
if (prop->type == P_SELECT) {
struct symbol *es = prop_get_symbol(prop);
es->rev_dep.expr = expr_alloc_or(es->rev_dep.expr,
expr_alloc_and(expr_alloc_symbol(menu->sym), dep));
} else if (prop->type == P_IMPLY) {
struct symbol *es = prop_get_symbol(prop);
es->implied.expr = expr_alloc_or(es->implied.expr,
expr_alloc_and(expr_alloc_symbol(menu->sym), dep));
}
}
}
/*
* Recursively process children in the same fashion before
* moving on
*/
for (menu = parent->list; menu; menu = menu->next)
_menu_finalize(menu, sym && sym_is_choice(sym));
} else if (!inside_choice && sym) {
/*
* Automatic submenu creation. If sym is a symbol and A, B, C,
* ... are consecutive items (symbols, menus, ifs, etc.) that
* all depend on sym, then the following menu structure is
* created:
*
* sym
* +-A
* +-B
* +-C
* ...
*
* This also works recursively, giving the following structure
* if A is a symbol and B depends on A:
*
* sym
* +-A
* | +-B
* +-C
* ...
*/
basedep = parent->prompt ? parent->prompt->visible.expr : NULL;
basedep = expr_trans_compare(basedep, E_UNEQUAL, &symbol_no);
basedep = expr_eliminate_dups(expr_transform(basedep));
/* Examine consecutive elements after sym */
last_menu = NULL;
for (menu = parent->next; menu; menu = menu->next) {
dep = menu->prompt ? menu->prompt->visible.expr : menu->dep;
if (!expr_contains_symbol(dep, sym))
/* No dependency, quit */
break;
if (expr_depends_symbol(dep, sym))
/* Absolute dependency, put in submenu */
goto next;
/*
* Also consider it a dependency on sym if our
* dependencies contain sym and are a "superset" of
* sym's dependencies, e.g. '(sym || Q) && R' when sym
* depends on R.
*
* Note that 'R' might be from an enclosing menu or if,
* making this a more common case than it might seem.
*/
dep = expr_trans_compare(dep, E_UNEQUAL, &symbol_no);
dep = expr_eliminate_dups(expr_transform(dep));
dep2 = basedep;
expr_eliminate_eq(&dep, &dep2);
if (!expr_is_yes(dep2)) {
/* Not superset, quit */
break;
}
/* Superset, put in submenu */
next:
_menu_finalize(menu, false);
menu->parent = parent;
last_menu = menu;
}
if (last_menu) {
parent->list = parent->next;
parent->next = last_menu->next;
last_menu->next = NULL;
}
sym->dir_dep.expr = expr_alloc_or(sym->dir_dep.expr, parent->dep);
}
for (menu = parent->list; menu; menu = menu->next) {
/*
* This code serves two purposes:
*
* (1) Flattening 'if' blocks, which do not specify a submenu
* and only add dependencies.
*
* (Automatic submenu creation might still create a submenu
* from an 'if' before this code runs.)
*
* (2) "Undoing" any automatic submenus created earlier below
* promptless symbols.
*
* Before:
*
* A
* if ... (or promptless symbol)
* +-B
* +-C
* D
*
* After:
*
* A
* if ... (or promptless symbol)
* B
* C
* D
*/
if (menu->list && (!menu->prompt || !menu->prompt->text)) {
for (last_menu = menu->list; ; last_menu = last_menu->next) {
last_menu->parent = parent;
if (!last_menu->next)
break;
}
last_menu->next = menu->next;
menu->next = menu->list;
menu->list = NULL;
}
}
if (sym && !(sym->flags & SYMBOL_WARNED)) {
if (sym->type == S_UNKNOWN)
menu_warn(parent, "config symbol defined without type");
/* Check properties connected to this symbol */
sym_check_prop(sym);
sym->flags |= SYMBOL_WARNED;
}
}
void menu_finalize(void)
{
_menu_finalize(&rootmenu, false);
}
bool menu_has_prompt(const struct menu *menu)
{
if (!menu->prompt)
return false;
return true;
}
/*
* Determine if a menu is empty.
* A menu is considered empty if it contains no or only
* invisible entries.
*/
bool menu_is_empty(struct menu *menu)
{
struct menu *child;
for (child = menu->list; child; child = child->next) {
if (menu_is_visible(child))
return(false);
}
return(true);
}
bool menu_is_visible(struct menu *menu)
{
struct menu *child;
struct symbol *sym;
tristate visible;
if (!menu->prompt)
return false;
if (menu->visibility) {
if (expr_calc_value(menu->visibility) == no)
return false;
}
sym = menu->sym;
if (sym) {
sym_calc_value(sym);
visible = menu->prompt->visible.tri;
} else
visible = menu->prompt->visible.tri = expr_calc_value(menu->prompt->visible.expr);
if (visible != no)
return true;
if (!sym || sym_get_tristate_value(menu->sym) == no)
return false;
for (child = menu->list; child; child = child->next)
if (menu_is_visible(child))
return true;
return false;
}
const char *menu_get_prompt(const struct menu *menu)
{
if (menu->prompt)
return menu->prompt->text;
else if (menu->sym)
return menu->sym->name;
return NULL;
}
struct menu *menu_get_parent_menu(struct menu *menu)
{
enum prop_type type;
for (; menu != &rootmenu; menu = menu->parent) {
type = menu->prompt ? menu->prompt->type : 0;
if (type == P_MENU)
break;
}
return menu;
}
static void get_def_str(struct gstr *r, const struct menu *menu)
{
str_printf(r, "Defined at %s:%d\n",
menu->filename, menu->lineno);
}
static void get_dep_str(struct gstr *r, const struct expr *expr,
const char *prefix)
{
if (!expr_is_yes(expr)) {
str_append(r, prefix);
expr_gstr_print(expr, r);
str_append(r, "\n");
}
}
int __attribute__((weak)) get_jump_key_char(void)
{
return -1;
}
static void get_prompt_str(struct gstr *r, struct property *prop,
struct list_head *head)
{
int i, j;
struct menu *submenu[8], *menu, *location = NULL;
struct jump_key *jump = NULL;
str_printf(r, " Prompt: %s\n", prop->text);
get_dep_str(r, prop->menu->dep, " Depends on: ");
/*
* Most prompts in Linux have visibility that exactly matches their
* dependencies. For these, we print only the dependencies to improve
* readability. However, prompts with inline "if" expressions and
* prompts with a parent that has a "visible if" expression have
* differing dependencies and visibility. In these rare cases, we
* print both.
*/
if (!expr_eq(prop->menu->dep, prop->visible.expr))
get_dep_str(r, prop->visible.expr, " Visible if: ");
menu = prop->menu;
for (i = 0; menu != &rootmenu && i < 8; menu = menu->parent) {
submenu[i++] = menu;
if (location == NULL && menu_is_visible(menu))
location = menu;
}
if (head && location) {
jump = xmalloc(sizeof(struct jump_key));
jump->target = location;
list_add_tail(&jump->entries, head);
}
str_printf(r, " Location:\n");
for (j = 0; --i >= 0; j++) {
int jk = -1;
int indent = 2 * j + 4;
menu = submenu[i];
if (jump && menu == location) {
jump->offset = strlen(r->s);
jk = get_jump_key_char();
}
if (jk >= 0) {
str_printf(r, "(%c)", jk);
indent -= 3;
}
str_printf(r, "%*c-> %s", indent, ' ', menu_get_prompt(menu));
if (menu->sym) {
str_printf(r, " (%s [=%s])", menu->sym->name ?
menu->sym->name : "<choice>",
sym_get_string_value(menu->sym));
}
str_append(r, "\n");
}
}
static void get_symbol_props_str(struct gstr *r, struct symbol *sym,
enum prop_type tok, const char *prefix)
{
bool hit = false;
struct property *prop;
for_all_properties(sym, prop, tok) {
if (!hit) {
str_append(r, prefix);
hit = true;
} else
str_printf(r, " && ");
expr_gstr_print(prop->expr, r);
}
if (hit)
str_append(r, "\n");
}
/*
* head is optional and may be NULL
*/
static void get_symbol_str(struct gstr *r, struct symbol *sym,
struct list_head *head)
{
struct property *prop;
struct menu *menu;
if (sym && sym->name) {
str_printf(r, "Symbol: %s [=%s]\n", sym->name,
sym_get_string_value(sym));
str_printf(r, "Type : %s\n", sym_type_name(sym->type));
if (sym->type == S_INT || sym->type == S_HEX) {
prop = sym_get_range_prop(sym);
if (prop) {
str_printf(r, "Range : ");
expr_gstr_print(prop->expr, r);
str_append(r, "\n");
}
}
}
/* Print the definitions with prompts before the ones without */
list_for_each_entry(menu, &sym->menus, link) {
if (menu->prompt) {
get_def_str(r, menu);
get_prompt_str(r, menu->prompt, head);
}
}
list_for_each_entry(menu, &sym->menus, link) {
if (!menu->prompt) {
get_def_str(r, menu);
get_dep_str(r, menu->dep, " Depends on: ");
}
}
get_symbol_props_str(r, sym, P_SELECT, "Selects: ");
if (sym->rev_dep.expr) {
expr_gstr_print_revdep(sym->rev_dep.expr, r, yes, "Selected by [y]:\n");
expr_gstr_print_revdep(sym->rev_dep.expr, r, mod, "Selected by [m]:\n");
expr_gstr_print_revdep(sym->rev_dep.expr, r, no, "Selected by [n]:\n");
}
get_symbol_props_str(r, sym, P_IMPLY, "Implies: ");
if (sym->implied.expr) {
expr_gstr_print_revdep(sym->implied.expr, r, yes, "Implied by [y]:\n");
expr_gstr_print_revdep(sym->implied.expr, r, mod, "Implied by [m]:\n");
expr_gstr_print_revdep(sym->implied.expr, r, no, "Implied by [n]:\n");
}
str_append(r, "\n\n");
}
struct gstr get_relations_str(struct symbol **sym_arr, struct list_head *head)
{
struct symbol *sym;
struct gstr res = str_new();
int i;
for (i = 0; sym_arr && (sym = sym_arr[i]); i++)
get_symbol_str(&res, sym, head);
if (!i)
str_append(&res, "No matches found.\n");
return res;
}
void menu_get_ext_help(struct menu *menu, struct gstr *help)
{
struct symbol *sym = menu->sym;
const char *help_text = nohelp_text;
if (menu->help) {
if (sym->name)
str_printf(help, "%s%s:\n\n", CONFIG_, sym->name);
help_text = menu->help;
}
str_printf(help, "%s\n", help_text);
if (sym)
get_symbol_str(help, sym, NULL);
}

View File

@@ -0,0 +1,53 @@
// SPDX-License-Identifier: GPL-2.0-only
#include <list.h>
#include "expr.h"
#include "mnconf-common.h"
int jump_key_char;
int next_jump_key(int key)
{
if (key < '1' || key > '9')
return '1';
key++;
if (key > '9')
key = '1';
return key;
}
int handle_search_keys(int key, size_t start, size_t end, void *_data)
{
struct search_data *data = _data;
struct jump_key *pos;
int index = 0;
if (key < '1' || key > '9')
return 0;
list_for_each_entry(pos, data->head, entries) {
index = next_jump_key(index);
if (pos->offset < start)
continue;
if (pos->offset >= end)
break;
if (key == index) {
data->target = pos->target;
return 1;
}
}
return 0;
}
int get_jump_key_char(void)
{
jump_key_char = next_jump_key(jump_key_char);
return jump_key_char;
}

View File

@@ -0,0 +1,20 @@
/* SPDX-License-Identifier: GPL-2.0-only */
#ifndef MNCONF_COMMON_H
#define MNCONF_COMMON_H
#include <stddef.h>
#include <list_types.h>
struct search_data {
struct list_head *head;
struct menu *target;
};
extern int jump_key_char;
int next_jump_key(int key);
int handle_search_keys(int key, size_t start, size_t end, void *_data);
int get_jump_key_char(void);
#endif /* MNCONF_COMMON_H */

View File

@@ -0,0 +1,55 @@
#!/bin/sh
# SPDX-License-Identifier: GPL-2.0
set -eu
cflags=$1
libs=$2
PKG="ncursesw menuw panelw"
PKG2="ncurses menu panel"
if [ -n "$(command -v ${HOSTPKG_CONFIG})" ]; then
if ${HOSTPKG_CONFIG} --exists $PKG; then
${HOSTPKG_CONFIG} --cflags ${PKG} > ${cflags}
${HOSTPKG_CONFIG} --libs ${PKG} > ${libs}
exit 0
fi
if ${HOSTPKG_CONFIG} --exists $PKG2; then
${HOSTPKG_CONFIG} --cflags ${PKG2} > ${cflags}
${HOSTPKG_CONFIG} --libs ${PKG2} > ${libs}
exit 0
fi
fi
# Check the default paths in case pkg-config is not installed.
# (Even if it is installed, some distributions such as openSUSE cannot
# find ncurses by pkg-config.)
if [ -f /usr/include/ncursesw/ncurses.h ]; then
echo -D_GNU_SOURCE -I/usr/include/ncursesw > ${cflags}
echo -lncursesw -lmenuw -lpanelw > ${libs}
exit 0
fi
if [ -f /usr/include/ncurses/ncurses.h ]; then
echo -D_GNU_SOURCE -I/usr/include/ncurses > ${cflags}
echo -lncurses -lmenu -lpanel > ${libs}
exit 0
fi
if [ -f /usr/include/ncurses.h ]; then
echo -D_GNU_SOURCE > ${cflags}
echo -lncurses -lmenu -lpanel > ${libs}
exit 0
fi
echo >&2 "*"
echo >&2 "* Unable to find the ncurses package."
echo >&2 "* Install ncurses (ncurses-devel or libncurses-dev"
echo >&2 "* depending on your distribution)."
echo >&2 "*"
echo >&2 "* You may also need to install ${HOSTPKG_CONFIG} to find the"
echo >&2 "* ncurses installed in a non-default location."
echo >&2 "*"
exit 1

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,651 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2008 Nir Tzachar <nir.tzachar@gmail.com>
*
* Derived from menuconfig.
*/
#include <xalloc.h>
#include "nconf.h"
#include "lkc.h"
int attr_normal;
int attr_main_heading;
int attr_main_menu_box;
int attr_main_menu_fore;
int attr_main_menu_back;
int attr_main_menu_grey;
int attr_main_menu_heading;
int attr_scrollwin_text;
int attr_scrollwin_heading;
int attr_scrollwin_box;
int attr_dialog_text;
int attr_dialog_menu_fore;
int attr_dialog_menu_back;
int attr_dialog_box;
int attr_input_box;
int attr_input_heading;
int attr_input_text;
int attr_input_field;
int attr_function_text;
int attr_function_highlight;
#define COLOR_ATTR(_at, _fg, _bg, _hl) \
{ .attr = &(_at), .has_color = true, .color_fg = _fg, .color_bg = _bg, .highlight = _hl }
#define NO_COLOR_ATTR(_at, _hl) \
{ .attr = &(_at), .has_color = false, .highlight = _hl }
#define COLOR_DEFAULT -1
struct nconf_attr_param {
int *attr;
bool has_color;
int color_fg;
int color_bg;
int highlight;
};
static const struct nconf_attr_param color_theme_params[] = {
COLOR_ATTR(attr_normal, COLOR_DEFAULT, COLOR_DEFAULT, A_NORMAL),
COLOR_ATTR(attr_main_heading, COLOR_MAGENTA, COLOR_DEFAULT, A_BOLD | A_UNDERLINE),
COLOR_ATTR(attr_main_menu_box, COLOR_YELLOW, COLOR_DEFAULT, A_NORMAL),
COLOR_ATTR(attr_main_menu_fore, COLOR_DEFAULT, COLOR_DEFAULT, A_REVERSE),
COLOR_ATTR(attr_main_menu_back, COLOR_DEFAULT, COLOR_DEFAULT, A_NORMAL),
COLOR_ATTR(attr_main_menu_grey, COLOR_DEFAULT, COLOR_DEFAULT, A_NORMAL),
COLOR_ATTR(attr_main_menu_heading, COLOR_GREEN, COLOR_DEFAULT, A_BOLD),
COLOR_ATTR(attr_scrollwin_text, COLOR_DEFAULT, COLOR_DEFAULT, A_NORMAL),
COLOR_ATTR(attr_scrollwin_heading, COLOR_GREEN, COLOR_DEFAULT, A_BOLD),
COLOR_ATTR(attr_scrollwin_box, COLOR_YELLOW, COLOR_DEFAULT, A_BOLD),
COLOR_ATTR(attr_dialog_text, COLOR_DEFAULT, COLOR_DEFAULT, A_BOLD),
COLOR_ATTR(attr_dialog_menu_fore, COLOR_RED, COLOR_DEFAULT, A_STANDOUT),
COLOR_ATTR(attr_dialog_menu_back, COLOR_YELLOW, COLOR_DEFAULT, A_NORMAL),
COLOR_ATTR(attr_dialog_box, COLOR_YELLOW, COLOR_DEFAULT, A_BOLD),
COLOR_ATTR(attr_input_box, COLOR_YELLOW, COLOR_DEFAULT, A_NORMAL),
COLOR_ATTR(attr_input_heading, COLOR_GREEN, COLOR_DEFAULT, A_BOLD),
COLOR_ATTR(attr_input_text, COLOR_DEFAULT, COLOR_DEFAULT, A_NORMAL),
COLOR_ATTR(attr_input_field, COLOR_DEFAULT, COLOR_DEFAULT, A_UNDERLINE),
COLOR_ATTR(attr_function_text, COLOR_YELLOW, COLOR_DEFAULT, A_REVERSE),
COLOR_ATTR(attr_function_highlight, COLOR_DEFAULT, COLOR_DEFAULT, A_BOLD),
{ /* sentinel */ }
};
static const struct nconf_attr_param no_color_theme_params[] = {
NO_COLOR_ATTR(attr_normal, A_NORMAL),
NO_COLOR_ATTR(attr_main_heading, A_BOLD | A_UNDERLINE),
NO_COLOR_ATTR(attr_main_menu_box, A_NORMAL),
NO_COLOR_ATTR(attr_main_menu_fore, A_STANDOUT),
NO_COLOR_ATTR(attr_main_menu_back, A_NORMAL),
NO_COLOR_ATTR(attr_main_menu_grey, A_NORMAL),
NO_COLOR_ATTR(attr_main_menu_heading, A_BOLD),
NO_COLOR_ATTR(attr_scrollwin_text, A_NORMAL),
NO_COLOR_ATTR(attr_scrollwin_heading, A_BOLD),
NO_COLOR_ATTR(attr_scrollwin_box, A_BOLD),
NO_COLOR_ATTR(attr_dialog_text, A_NORMAL),
NO_COLOR_ATTR(attr_dialog_menu_fore, A_STANDOUT),
NO_COLOR_ATTR(attr_dialog_menu_back, A_NORMAL),
NO_COLOR_ATTR(attr_dialog_box, A_BOLD),
NO_COLOR_ATTR(attr_input_box, A_BOLD),
NO_COLOR_ATTR(attr_input_heading, A_BOLD),
NO_COLOR_ATTR(attr_input_text, A_NORMAL),
NO_COLOR_ATTR(attr_input_field, A_UNDERLINE),
NO_COLOR_ATTR(attr_function_text, A_REVERSE),
NO_COLOR_ATTR(attr_function_highlight, A_BOLD),
{ /* sentinel */ }
};
void set_colors(void)
{
const struct nconf_attr_param *p;
int pair = 0;
if (has_colors()) {
start_color();
use_default_colors();
p = color_theme_params;
} else {
p = no_color_theme_params;
}
for (; p->attr; p++) {
int attr = p->highlight;
if (p->has_color) {
pair++;
init_pair(pair, p->color_fg, p->color_bg);
attr |= COLOR_PAIR(pair);
}
*p->attr = attr;
}
}
/* this changes the windows attributes !!! */
void print_in_middle(WINDOW *win, int y, int width, const char *str, int attrs)
{
wattrset(win, attrs);
mvwprintw(win, y, (width - strlen(str)) / 2, "%s", str);
}
int get_line_no(const char *text)
{
int i;
int total = 1;
if (!text)
return 0;
for (i = 0; text[i] != '\0'; i++)
if (text[i] == '\n')
total++;
return total;
}
const char *get_line(const char *text, int line_no)
{
int i;
int lines = 0;
if (!text)
return NULL;
for (i = 0; text[i] != '\0' && lines < line_no; i++)
if (text[i] == '\n')
lines++;
return text+i;
}
int get_line_length(const char *line)
{
int res = 0;
while (*line != '\0' && *line != '\n') {
line++;
res++;
}
return res;
}
/* print all lines to the window. */
void fill_window(WINDOW *win, const char *text)
{
int x, y;
int total_lines = get_line_no(text);
int i;
getmaxyx(win, y, x);
/* do not go over end of line */
total_lines = min(total_lines, y);
for (i = 0; i < total_lines; i++) {
char tmp[x+10];
const char *line = get_line(text, i);
int len = get_line_length(line);
strncpy(tmp, line, min(len, x));
tmp[len] = '\0';
mvwprintw(win, i, 0, "%s", tmp);
}
}
/* get the message, and buttons.
* each button must be a char*
* return the selected button
*
* this dialog is used for 2 different things:
* 1) show a text box, no buttons.
* 2) show a dialog, with horizontal buttons
*/
int btn_dialog(WINDOW *main_window, const char *msg, int btn_num, ...)
{
va_list ap;
char *btn;
int btns_width = 0;
int msg_lines = 0;
int msg_width = 0;
int total_width;
int win_rows = 0;
WINDOW *win;
WINDOW *msg_win;
WINDOW *menu_win;
MENU *menu;
ITEM *btns[btn_num+1];
int i, x, y;
int res = -1;
va_start(ap, btn_num);
for (i = 0; i < btn_num; i++) {
btn = va_arg(ap, char *);
btns[i] = new_item(btn, "");
btns_width += strlen(btn)+1;
}
va_end(ap);
btns[btn_num] = NULL;
/* find the widest line of msg: */
msg_lines = get_line_no(msg);
for (i = 0; i < msg_lines; i++) {
const char *line = get_line(msg, i);
int len = get_line_length(line);
if (msg_width < len)
msg_width = len;
}
total_width = max(msg_width, btns_width);
/* place dialog in middle of screen */
y = (getmaxy(stdscr)-(msg_lines+4))/2;
x = (getmaxx(stdscr)-(total_width+4))/2;
/* create the windows */
if (btn_num > 0)
win_rows = msg_lines+4;
else
win_rows = msg_lines+2;
win = newwin(win_rows, total_width+4, y, x);
keypad(win, TRUE);
menu_win = derwin(win, 1, btns_width, win_rows-2,
1+(total_width+2-btns_width)/2);
menu = new_menu(btns);
msg_win = derwin(win, win_rows-2, msg_width, 1,
1+(total_width+2-msg_width)/2);
set_menu_fore(menu, attr_dialog_menu_fore);
set_menu_back(menu, attr_dialog_menu_back);
wattrset(win, attr_dialog_box);
box(win, 0, 0);
/* print message */
wattrset(msg_win, attr_dialog_text);
fill_window(msg_win, msg);
set_menu_win(menu, win);
set_menu_sub(menu, menu_win);
set_menu_format(menu, 1, btn_num);
menu_opts_off(menu, O_SHOWDESC);
menu_opts_off(menu, O_SHOWMATCH);
menu_opts_on(menu, O_ONEVALUE);
menu_opts_on(menu, O_NONCYCLIC);
set_menu_mark(menu, "");
post_menu(menu);
touchwin(win);
refresh_all_windows(main_window);
while ((res = wgetch(win))) {
switch (res) {
case KEY_LEFT:
menu_driver(menu, REQ_LEFT_ITEM);
break;
case KEY_RIGHT:
menu_driver(menu, REQ_RIGHT_ITEM);
break;
case 9: /* TAB */
if (btn_num > 1) {
/* cycle through buttons */
if (item_index(current_item(menu)) == btn_num - 1)
menu_driver(menu, REQ_FIRST_ITEM);
else
menu_driver(menu, REQ_NEXT_ITEM);
}
break;
case 10: /* ENTER */
case 27: /* ESCAPE */
case ' ':
case KEY_F(F_BACK):
case KEY_F(F_EXIT):
break;
}
touchwin(win);
refresh_all_windows(main_window);
if (res == 10 || res == ' ') {
res = item_index(current_item(menu));
break;
} else if (res == 27 || res == KEY_F(F_BACK) ||
res == KEY_F(F_EXIT)) {
res = KEY_EXIT;
break;
}
}
unpost_menu(menu);
free_menu(menu);
for (i = 0; i < btn_num; i++)
free_item(btns[i]);
delwin(win);
return res;
}
int dialog_inputbox(WINDOW *main_window,
const char *title, const char *prompt,
const char *init, char **resultp, int *result_len)
{
int prompt_lines = 0;
int prompt_width = 0;
WINDOW *win;
WINDOW *prompt_win;
WINDOW *form_win;
PANEL *panel;
int i, x, y, lines, columns, win_lines, win_cols;
int res = -1;
int cursor_position = strlen(init);
int cursor_form_win;
char *result = *resultp;
getmaxyx(stdscr, lines, columns);
if (strlen(init)+1 > *result_len) {
*result_len = strlen(init)+1;
*resultp = result = xrealloc(result, *result_len);
}
/* find the widest line of msg: */
prompt_lines = get_line_no(prompt);
for (i = 0; i < prompt_lines; i++) {
const char *line = get_line(prompt, i);
int len = get_line_length(line);
prompt_width = max(prompt_width, len);
}
if (title)
prompt_width = max(prompt_width, strlen(title));
win_lines = min(prompt_lines+6, lines-2);
win_cols = min(prompt_width+7, columns-2);
prompt_lines = max(win_lines-6, 0);
prompt_width = max(win_cols-7, 0);
/* place dialog in middle of screen */
y = (lines-win_lines)/2;
x = (columns-win_cols)/2;
strncpy(result, init, *result_len);
/* create the windows */
win = newwin(win_lines, win_cols, y, x);
prompt_win = derwin(win, prompt_lines+1, prompt_width, 2, 2);
form_win = derwin(win, 1, prompt_width, prompt_lines+3, 2);
keypad(form_win, TRUE);
wattrset(form_win, attr_input_field);
wattrset(win, attr_input_box);
box(win, 0, 0);
wattrset(win, attr_input_heading);
if (title)
mvwprintw(win, 0, 3, "%s", title);
/* print message */
wattrset(prompt_win, attr_input_text);
fill_window(prompt_win, prompt);
mvwprintw(form_win, 0, 0, "%*s", prompt_width, " ");
cursor_form_win = min(cursor_position, prompt_width-1);
mvwprintw(form_win, 0, 0, "%s",
result + cursor_position-cursor_form_win);
/* create panels */
panel = new_panel(win);
/* show the cursor */
curs_set(1);
touchwin(win);
refresh_all_windows(main_window);
while ((res = wgetch(form_win))) {
int len = strlen(result);
switch (res) {
case 10: /* ENTER */
case 27: /* ESCAPE */
case KEY_F(F_HELP):
case KEY_F(F_EXIT):
case KEY_F(F_BACK):
break;
case 8: /* ^H */
case 127: /* ^? */
case KEY_BACKSPACE:
if (cursor_position > 0) {
memmove(&result[cursor_position-1],
&result[cursor_position],
len-cursor_position+1);
cursor_position--;
cursor_form_win--;
len--;
}
break;
case KEY_DC:
if (cursor_position >= 0 && cursor_position < len) {
memmove(&result[cursor_position],
&result[cursor_position+1],
len-cursor_position+1);
len--;
}
break;
case KEY_UP:
case KEY_RIGHT:
if (cursor_position < len) {
cursor_position++;
cursor_form_win++;
}
break;
case KEY_DOWN:
case KEY_LEFT:
if (cursor_position > 0) {
cursor_position--;
cursor_form_win--;
}
break;
case KEY_HOME:
cursor_position = 0;
cursor_form_win = 0;
break;
case KEY_END:
cursor_position = len;
cursor_form_win = min(cursor_position, prompt_width-1);
break;
default:
if ((isgraph(res) || isspace(res))) {
/* one for new char, one for '\0' */
if (len+2 > *result_len) {
*result_len = len+2;
*resultp = result = realloc(result,
*result_len);
}
/* insert the char at the proper position */
memmove(&result[cursor_position+1],
&result[cursor_position],
len-cursor_position+1);
result[cursor_position] = res;
cursor_position++;
cursor_form_win++;
len++;
} else {
mvprintw(0, 0, "unknown key: %d\n", res);
}
break;
}
if (cursor_form_win < 0)
cursor_form_win = 0;
else if (cursor_form_win > prompt_width-1)
cursor_form_win = prompt_width-1;
wmove(form_win, 0, 0);
wclrtoeol(form_win);
mvwprintw(form_win, 0, 0, "%*s", prompt_width, " ");
mvwprintw(form_win, 0, 0, "%s",
result + cursor_position-cursor_form_win);
wmove(form_win, 0, cursor_form_win);
touchwin(win);
refresh_all_windows(main_window);
if (res == 10) {
res = 0;
break;
} else if (res == 27 || res == KEY_F(F_BACK) ||
res == KEY_F(F_EXIT)) {
res = KEY_EXIT;
break;
} else if (res == KEY_F(F_HELP)) {
res = 1;
break;
}
}
/* hide the cursor */
curs_set(0);
del_panel(panel);
delwin(prompt_win);
delwin(form_win);
delwin(win);
return res;
}
/* refresh all windows in the correct order */
void refresh_all_windows(WINDOW *main_window)
{
update_panels();
touchwin(main_window);
refresh();
}
void show_scroll_win(WINDOW *main_window,
const char *title,
const char *text)
{
(void)show_scroll_win_ext(main_window, title, (char *)text, NULL, NULL, NULL, NULL);
}
/* layman's scrollable window... */
int show_scroll_win_ext(WINDOW *main_window, const char *title, char *text,
int *vscroll, int *hscroll,
extra_key_cb_fn extra_key_cb, void *data)
{
int res;
int total_lines = get_line_no(text);
int x, y, lines, columns;
int start_x = 0, start_y = 0;
int text_lines = 0, text_cols = 0;
int total_cols = 0;
int win_cols = 0;
int win_lines = 0;
int i = 0;
WINDOW *win;
WINDOW *pad;
PANEL *panel;
bool done = false;
if (hscroll)
start_x = *hscroll;
if (vscroll)
start_y = *vscroll;
getmaxyx(stdscr, lines, columns);
/* find the widest line of msg: */
total_lines = get_line_no(text);
for (i = 0; i < total_lines; i++) {
const char *line = get_line(text, i);
int len = get_line_length(line);
total_cols = max(total_cols, len+2);
}
/* create the pad */
pad = newpad(total_lines+10, total_cols+10);
wattrset(pad, attr_scrollwin_text);
fill_window(pad, text);
win_lines = min(total_lines+4, lines-2);
win_cols = min(total_cols+2, columns-2);
text_lines = max(win_lines-4, 0);
text_cols = max(win_cols-2, 0);
/* place window in middle of screen */
y = (lines-win_lines)/2;
x = (columns-win_cols)/2;
win = newwin(win_lines, win_cols, y, x);
keypad(win, TRUE);
/* show the help in the help window, and show the help panel */
wattrset(win, attr_scrollwin_box);
box(win, 0, 0);
wattrset(win, attr_scrollwin_heading);
mvwprintw(win, 0, 3, " %s ", title);
panel = new_panel(win);
/* handle scrolling */
while (!done) {
copywin(pad, win, start_y, start_x, 2, 2, text_lines,
text_cols, 0);
print_in_middle(win,
text_lines+2,
text_cols,
"<OK>",
attr_dialog_menu_fore);
wrefresh(win);
res = wgetch(win);
switch (res) {
case KEY_NPAGE:
case ' ':
case 'd':
start_y += text_lines-2;
break;
case KEY_PPAGE:
case 'u':
start_y -= text_lines+2;
break;
case KEY_HOME:
start_y = 0;
break;
case KEY_END:
start_y = total_lines-text_lines;
break;
case KEY_DOWN:
case 'j':
start_y++;
break;
case KEY_UP:
case 'k':
start_y--;
break;
case KEY_LEFT:
case 'h':
start_x--;
break;
case KEY_RIGHT:
case 'l':
start_x++;
break;
default:
if (extra_key_cb) {
size_t start = (get_line(text, start_y) - text);
size_t end = (get_line(text, start_y + text_lines) - text);
if (extra_key_cb(res, start, end, data)) {
done = true;
break;
}
}
}
if (res == 0 || res == 10 || res == 27 || res == 'q' ||
res == KEY_F(F_HELP) || res == KEY_F(F_BACK) ||
res == KEY_F(F_EXIT))
break;
if (start_y < 0)
start_y = 0;
if (start_y >= total_lines-text_lines)
start_y = total_lines-text_lines;
if (start_x < 0)
start_x = 0;
if (start_x >= total_cols-text_cols)
start_x = total_cols-text_cols;
}
if (hscroll)
*hscroll = start_x;
if (vscroll)
*vscroll = start_y;
del_panel(panel);
delwin(win);
refresh_all_windows(main_window);
return res;
}

View File

@@ -0,0 +1,88 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2008 Nir Tzachar <nir.tzachar@gmail.com>
*
* Derived from menuconfig.
*/
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <limits.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <ncurses.h>
#include <menu.h>
#include <panel.h>
#include <form.h>
#include <stdio.h>
#include <time.h>
#include <sys/time.h>
#define max(a, b) ({\
typeof(a) _a = a;\
typeof(b) _b = b;\
_a > _b ? _a : _b; })
#define min(a, b) ({\
typeof(a) _a = a;\
typeof(b) _b = b;\
_a < _b ? _a : _b; })
extern int attr_normal;
extern int attr_main_heading;
extern int attr_main_menu_box;
extern int attr_main_menu_fore;
extern int attr_main_menu_back;
extern int attr_main_menu_grey;
extern int attr_main_menu_heading;
extern int attr_scrollwin_text;
extern int attr_scrollwin_heading;
extern int attr_scrollwin_box;
extern int attr_dialog_text;
extern int attr_dialog_menu_fore;
extern int attr_dialog_menu_back;
extern int attr_dialog_box;
extern int attr_input_box;
extern int attr_input_heading;
extern int attr_input_text;
extern int attr_input_field;
extern int attr_function_text;
extern int attr_function_highlight;
typedef enum {
F_HELP = 1,
F_SYMBOL = 2,
F_INSTS = 3,
F_CONF = 4,
F_BACK = 5,
F_SAVE = 6,
F_LOAD = 7,
F_SEARCH = 8,
F_EXIT = 9,
} function_key;
void set_colors(void);
typedef int (*extra_key_cb_fn)(int, size_t, size_t, void *);
/* this changes the windows attributes !!! */
void print_in_middle(WINDOW *win, int y, int width, const char *str, int attrs);
int get_line_length(const char *line);
int get_line_no(const char *text);
const char *get_line(const char *text, int line_no);
void fill_window(WINDOW *win, const char *text);
int btn_dialog(WINDOW *main_window, const char *msg, int btn_num, ...);
int dialog_inputbox(WINDOW *main_window,
const char *title, const char *prompt,
const char *init, char **resultp, int *result_len);
void refresh_all_windows(WINDOW *main_window);
int show_scroll_win_ext(WINDOW *main_window, const char *title, char *text,
int *vscroll, int *hscroll,
extra_key_cb_fn extra_key_cb, void *data);
void show_scroll_win(WINDOW *main_window,
const char *title,
const char *text);

View File

@@ -0,0 +1,757 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>
*/
%{
#include <ctype.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <xalloc.h>
#include "lkc.h"
#include "internal.h"
#include "preprocess.h"
#define printd(mask, fmt...) if (cdebug & (mask)) printf(fmt)
#define PRINTD 0x0001
#define DEBUG_PARSE 0x0002
int cdebug = PRINTD;
static void yyerror(const char *err);
static void zconf_error(const char *err, ...);
static bool zconf_endtoken(const char *tokenname,
const char *expected_tokenname);
struct menu *current_menu, *current_entry, *current_choice;
%}
%union
{
char *string;
struct symbol *symbol;
struct expr *expr;
struct menu *menu;
enum symbol_type type;
enum variable_flavor flavor;
}
%token <string> T_HELPTEXT
%token <string> T_WORD
%token <string> T_WORD_QUOTE
%token T_BOOL
%token T_CHOICE
%token T_CLOSE_PAREN
%token T_COLON_EQUAL
%token T_COMMENT
%token T_CONFIG
%token T_DEFAULT
%token T_DEF_BOOL
%token T_DEF_TRISTATE
%token T_DEPENDS
%token T_ENDCHOICE
%token T_ENDIF
%token T_ENDMENU
%token T_HELP
%token T_HEX
%token T_IF
%token T_IMPLY
%token T_INT
%token T_MAINMENU
%token T_MENU
%token T_MENUCONFIG
%token T_MODULES
%token T_ON
%token T_OPEN_PAREN
%token T_PLUS_EQUAL
%token T_PROMPT
%token T_RANGE
%token T_SELECT
%token T_SOURCE
%token T_STRING
%token T_TRISTATE
%token T_VISIBLE
%token T_EOL
%token <string> T_ASSIGN_VAL
%left T_OR
%left T_AND
%left T_EQUAL T_UNEQUAL
%left T_LESS T_LESS_EQUAL T_GREATER T_GREATER_EQUAL
%nonassoc T_NOT
%type <symbol> nonconst_symbol
%type <symbol> symbol
%type <type> type default
%type <expr> expr
%type <expr> if_expr
%type <string> end
%type <menu> if_entry menu_entry choice_entry
%type <string> assign_val
%type <flavor> assign_op
%destructor {
fprintf(stderr, "%s:%d: missing end statement for this entry\n",
$$->filename, $$->lineno);
if (current_menu == $$)
menu_end_menu();
} if_entry menu_entry choice_entry
%%
input: mainmenu_stmt stmt_list | stmt_list;
/* mainmenu entry */
mainmenu_stmt: T_MAINMENU T_WORD_QUOTE T_EOL
{
menu_add_prompt(P_MENU, $2, NULL);
};
stmt_list:
/* empty */
| stmt_list assignment_stmt
| stmt_list choice_stmt
| stmt_list comment_stmt
| stmt_list config_stmt
| stmt_list if_stmt
| stmt_list menu_stmt
| stmt_list menuconfig_stmt
| stmt_list source_stmt
| stmt_list T_WORD error T_EOL { zconf_error("unknown statement \"%s\"", $2); }
| stmt_list error T_EOL { zconf_error("invalid statement"); }
;
stmt_list_in_choice:
/* empty */
| stmt_list_in_choice comment_stmt
| stmt_list_in_choice config_stmt
| stmt_list_in_choice if_stmt_in_choice
| stmt_list_in_choice error T_EOL { zconf_error("invalid statement"); }
;
/* config/menuconfig entry */
config_entry_start: T_CONFIG nonconst_symbol T_EOL
{
menu_add_entry($2, M_NORMAL);
printd(DEBUG_PARSE, "%s:%d:config %s\n", cur_filename, cur_lineno, $2->name);
};
config_stmt: config_entry_start config_option_list
{
if (current_choice) {
if (!current_entry->prompt) {
fprintf(stderr, "%s:%d: error: choice member must have a prompt\n",
current_entry->filename, current_entry->lineno);
yynerrs++;
}
if (current_entry->sym->type != S_BOOLEAN) {
fprintf(stderr, "%s:%d: error: choice member must be bool\n",
current_entry->filename, current_entry->lineno);
yynerrs++;
}
/*
* If the same symbol appears twice in a choice block, the list
* node would be added twice, leading to a broken linked list.
* list_empty() ensures that this symbol has not yet added.
*/
if (list_empty(&current_entry->sym->choice_link))
list_add_tail(&current_entry->sym->choice_link,
&current_choice->choice_members);
}
printd(DEBUG_PARSE, "%s:%d:endconfig\n", cur_filename, cur_lineno);
};
menuconfig_entry_start: T_MENUCONFIG nonconst_symbol T_EOL
{
menu_add_entry($2, M_MENU);
printd(DEBUG_PARSE, "%s:%d:menuconfig %s\n", cur_filename, cur_lineno, $2->name);
};
menuconfig_stmt: menuconfig_entry_start config_option_list
{
if (current_entry->prompt)
current_entry->prompt->type = P_MENU;
else
zconf_error("menuconfig statement without prompt");
printd(DEBUG_PARSE, "%s:%d:endconfig\n", cur_filename, cur_lineno);
};
config_option_list:
/* empty */
| config_option_list config_option
| config_option_list depends
| config_option_list help
;
config_option: type prompt_stmt_opt T_EOL
{
menu_set_type($1);
printd(DEBUG_PARSE, "%s:%d:type(%u)\n", cur_filename, cur_lineno, $1);
};
config_option: T_PROMPT T_WORD_QUOTE if_expr T_EOL
{
menu_add_prompt(P_PROMPT, $2, $3);
printd(DEBUG_PARSE, "%s:%d:prompt\n", cur_filename, cur_lineno);
};
config_option: default expr if_expr T_EOL
{
menu_add_expr(P_DEFAULT, $2, $3);
if ($1 != S_UNKNOWN)
menu_set_type($1);
printd(DEBUG_PARSE, "%s:%d:default(%u)\n", cur_filename, cur_lineno,
$1);
};
config_option: T_SELECT nonconst_symbol if_expr T_EOL
{
menu_add_symbol(P_SELECT, $2, $3);
printd(DEBUG_PARSE, "%s:%d:select\n", cur_filename, cur_lineno);
};
config_option: T_IMPLY nonconst_symbol if_expr T_EOL
{
menu_add_symbol(P_IMPLY, $2, $3);
printd(DEBUG_PARSE, "%s:%d:imply\n", cur_filename, cur_lineno);
};
config_option: T_RANGE symbol symbol if_expr T_EOL
{
menu_add_expr(P_RANGE, expr_alloc_comp(E_RANGE,$2, $3), $4);
printd(DEBUG_PARSE, "%s:%d:range\n", cur_filename, cur_lineno);
};
config_option: T_MODULES T_EOL
{
if (modules_sym)
zconf_error("symbol '%s' redefines option 'modules' already defined by symbol '%s'",
current_entry->sym->name, modules_sym->name);
modules_sym = current_entry->sym;
};
/* choice entry */
choice: T_CHOICE T_EOL
{
struct symbol *sym = sym_lookup(NULL, 0);
menu_add_entry(sym, M_CHOICE);
menu_set_type(S_BOOLEAN);
INIT_LIST_HEAD(&current_entry->choice_members);
printd(DEBUG_PARSE, "%s:%d:choice\n", cur_filename, cur_lineno);
};
choice_entry: choice choice_option_list
{
if (!current_entry->prompt) {
fprintf(stderr, "%s:%d: error: choice must have a prompt\n",
current_entry->filename, current_entry->lineno);
yynerrs++;
}
$$ = menu_add_menu();
current_choice = current_entry;
};
choice_end: end
{
current_choice = NULL;
if (zconf_endtoken($1, "choice")) {
menu_end_menu();
printd(DEBUG_PARSE, "%s:%d:endchoice\n", cur_filename, cur_lineno);
}
};
choice_stmt: choice_entry stmt_list_in_choice choice_end
;
choice_option_list:
/* empty */
| choice_option_list choice_option
| choice_option_list depends
| choice_option_list help
;
choice_option: T_PROMPT T_WORD_QUOTE if_expr T_EOL
{
menu_add_prompt(P_PROMPT, $2, $3);
printd(DEBUG_PARSE, "%s:%d:prompt\n", cur_filename, cur_lineno);
};
choice_option: T_DEFAULT nonconst_symbol if_expr T_EOL
{
menu_add_symbol(P_DEFAULT, $2, $3);
printd(DEBUG_PARSE, "%s:%d:default\n", cur_filename, cur_lineno);
};
type:
T_BOOL { $$ = S_BOOLEAN; }
| T_TRISTATE { $$ = S_TRISTATE; }
| T_INT { $$ = S_INT; }
| T_HEX { $$ = S_HEX; }
| T_STRING { $$ = S_STRING; }
default:
T_DEFAULT { $$ = S_UNKNOWN; }
| T_DEF_BOOL { $$ = S_BOOLEAN; }
| T_DEF_TRISTATE { $$ = S_TRISTATE; }
/* if entry */
if_entry: T_IF expr T_EOL
{
printd(DEBUG_PARSE, "%s:%d:if\n", cur_filename, cur_lineno);
menu_add_entry(NULL, M_IF);
menu_add_dep($2);
$$ = menu_add_menu();
};
if_end: end
{
if (zconf_endtoken($1, "if")) {
menu_end_menu();
printd(DEBUG_PARSE, "%s:%d:endif\n", cur_filename, cur_lineno);
}
};
if_stmt: if_entry stmt_list if_end
;
if_stmt_in_choice: if_entry stmt_list_in_choice if_end
;
/* menu entry */
menu: T_MENU T_WORD_QUOTE T_EOL
{
menu_add_entry(NULL, M_MENU);
menu_add_prompt(P_MENU, $2, NULL);
printd(DEBUG_PARSE, "%s:%d:menu\n", cur_filename, cur_lineno);
};
menu_entry: menu menu_option_list
{
$$ = menu_add_menu();
};
menu_end: end
{
if (zconf_endtoken($1, "menu")) {
menu_end_menu();
printd(DEBUG_PARSE, "%s:%d:endmenu\n", cur_filename, cur_lineno);
}
};
menu_stmt: menu_entry stmt_list menu_end
;
menu_option_list:
/* empty */
| menu_option_list visible
| menu_option_list depends
;
source_stmt: T_SOURCE T_WORD_QUOTE T_EOL
{
printd(DEBUG_PARSE, "%s:%d:source %s\n", cur_filename, cur_lineno, $2);
zconf_nextfile($2);
free($2);
};
/* comment entry */
comment: T_COMMENT T_WORD_QUOTE T_EOL
{
menu_add_entry(NULL, M_COMMENT);
menu_add_prompt(P_COMMENT, $2, NULL);
printd(DEBUG_PARSE, "%s:%d:comment\n", cur_filename, cur_lineno);
};
comment_stmt: comment comment_option_list
;
comment_option_list:
/* empty */
| comment_option_list depends
;
/* help option */
help_start: T_HELP T_EOL
{
printd(DEBUG_PARSE, "%s:%d:help\n", cur_filename, cur_lineno);
zconf_starthelp();
};
help: help_start T_HELPTEXT
{
if (current_entry->help) {
free(current_entry->help);
zconf_error("'%s' defined with more than one help text",
current_entry->sym->name ?: "<choice>");
}
/* Is the help text empty or all whitespace? */
if ($2[strspn($2, " \f\n\r\t\v")] == '\0')
zconf_error("'%s' defined with blank help text",
current_entry->sym->name ?: "<choice>");
current_entry->help = $2;
};
/* depends option */
depends: T_DEPENDS T_ON expr T_EOL
{
menu_add_dep($3);
printd(DEBUG_PARSE, "%s:%d:depends on\n", cur_filename, cur_lineno);
};
/* visibility option */
visible: T_VISIBLE if_expr T_EOL
{
menu_add_visibility($2);
};
/* prompt statement */
prompt_stmt_opt:
/* empty */
| T_WORD_QUOTE if_expr
{
menu_add_prompt(P_PROMPT, $1, $2);
};
end: T_ENDMENU T_EOL { $$ = "menu"; }
| T_ENDCHOICE T_EOL { $$ = "choice"; }
| T_ENDIF T_EOL { $$ = "if"; }
;
if_expr: /* empty */ { $$ = NULL; }
| T_IF expr { $$ = $2; }
;
expr: symbol { $$ = expr_alloc_symbol($1); }
| symbol T_LESS symbol { $$ = expr_alloc_comp(E_LTH, $1, $3); }
| symbol T_LESS_EQUAL symbol { $$ = expr_alloc_comp(E_LEQ, $1, $3); }
| symbol T_GREATER symbol { $$ = expr_alloc_comp(E_GTH, $1, $3); }
| symbol T_GREATER_EQUAL symbol { $$ = expr_alloc_comp(E_GEQ, $1, $3); }
| symbol T_EQUAL symbol { $$ = expr_alloc_comp(E_EQUAL, $1, $3); }
| symbol T_UNEQUAL symbol { $$ = expr_alloc_comp(E_UNEQUAL, $1, $3); }
| T_OPEN_PAREN expr T_CLOSE_PAREN { $$ = $2; }
| T_NOT expr { $$ = expr_alloc_one(E_NOT, $2); }
| expr T_OR expr { $$ = expr_alloc_two(E_OR, $1, $3); }
| expr T_AND expr { $$ = expr_alloc_two(E_AND, $1, $3); }
;
/* For symbol definitions, selects, etc., where quotes are not accepted */
nonconst_symbol: T_WORD { $$ = sym_lookup($1, 0); free($1); };
symbol: nonconst_symbol
| T_WORD_QUOTE { $$ = sym_lookup($1, SYMBOL_CONST); free($1); }
;
/* assignment statement */
assignment_stmt: T_WORD assign_op assign_val T_EOL { variable_add($1, $3, $2); free($1); free($3); }
assign_op:
T_EQUAL { $$ = VAR_RECURSIVE; }
| T_COLON_EQUAL { $$ = VAR_SIMPLE; }
| T_PLUS_EQUAL { $$ = VAR_APPEND; }
;
assign_val:
/* empty */ { $$ = xstrdup(""); };
| T_ASSIGN_VAL
;
%%
/**
* choice_check_sanity - check sanity of a choice member
*
* @menu: menu of the choice member
*
* Return: -1 if an error is found, 0 otherwise.
*/
static int choice_check_sanity(const struct menu *menu)
{
struct property *prop;
int ret = 0;
for (prop = menu->sym->prop; prop; prop = prop->next) {
if (prop->type == P_DEFAULT) {
fprintf(stderr, "%s:%d: error: %s",
prop->filename, prop->lineno,
"defaults for choice values not supported\n");
ret = -1;
}
if (prop->menu != menu && prop->type == P_PROMPT &&
prop->menu->parent != menu->parent) {
fprintf(stderr, "%s:%d: error: %s",
prop->filename, prop->lineno,
"choice value has a prompt outside its choice group\n");
ret = -1;
}
}
return ret;
}
void conf_parse(const char *name)
{
struct menu *menu;
autoconf_cmd = str_new();
str_printf(&autoconf_cmd, "\ndeps_config := \\\n");
zconf_initscan(name);
_menu_init();
if (getenv("ZCONF_DEBUG"))
yydebug = 1;
yyparse();
str_printf(&autoconf_cmd,
"\n"
"$(autoconfig): $(deps_config)\n"
"$(deps_config): ;\n");
env_write_dep(&autoconf_cmd);
/* Variables are expanded in the parse phase. We can free them here. */
variable_all_del();
if (yynerrs)
exit(1);
if (!modules_sym)
modules_sym = &symbol_no;
if (!menu_has_prompt(&rootmenu)) {
current_entry = &rootmenu;
menu_add_prompt(P_MENU, "Main menu", NULL);
}
menu_finalize();
menu_for_each_entry(menu) {
struct menu *child;
if (menu->sym && sym_check_deps(menu->sym))
yynerrs++;
if (menu->sym && sym_is_choice(menu->sym)) {
menu_for_each_sub_entry(child, menu)
if (child->sym && choice_check_sanity(child))
yynerrs++;
}
}
if (yynerrs)
exit(1);
conf_set_changed(true);
}
static bool zconf_endtoken(const char *tokenname,
const char *expected_tokenname)
{
if (strcmp(tokenname, expected_tokenname)) {
zconf_error("unexpected '%s' within %s block",
tokenname, expected_tokenname);
yynerrs++;
return false;
}
if (strcmp(current_menu->filename, cur_filename)) {
zconf_error("'%s' in different file than '%s'",
tokenname, expected_tokenname);
fprintf(stderr, "%s:%d: location of the '%s'\n",
current_menu->filename, current_menu->lineno,
expected_tokenname);
yynerrs++;
return false;
}
return true;
}
static void zconf_error(const char *err, ...)
{
va_list ap;
yynerrs++;
fprintf(stderr, "%s:%d: ", cur_filename, cur_lineno);
va_start(ap, err);
vfprintf(stderr, err, ap);
va_end(ap);
fprintf(stderr, "\n");
}
static void yyerror(const char *err)
{
fprintf(stderr, "%s:%d: %s\n", cur_filename, cur_lineno, err);
}
static void print_quoted_string(FILE *out, const char *str)
{
const char *p;
int len;
putc('"', out);
while ((p = strchr(str, '"'))) {
len = p - str;
if (len)
fprintf(out, "%.*s", len, str);
fputs("\\\"", out);
str = p + 1;
}
fputs(str, out);
putc('"', out);
}
static void print_symbol(FILE *out, const struct menu *menu)
{
struct symbol *sym = menu->sym;
struct property *prop;
if (sym_is_choice(sym))
fprintf(out, "\nchoice\n");
else
fprintf(out, "\nconfig %s\n", sym->name);
switch (sym->type) {
case S_BOOLEAN:
fputs(" bool\n", out);
break;
case S_TRISTATE:
fputs(" tristate\n", out);
break;
case S_STRING:
fputs(" string\n", out);
break;
case S_INT:
fputs(" integer\n", out);
break;
case S_HEX:
fputs(" hex\n", out);
break;
default:
fputs(" ???\n", out);
break;
}
for (prop = sym->prop; prop; prop = prop->next) {
if (prop->menu != menu)
continue;
switch (prop->type) {
case P_PROMPT:
fputs(" prompt ", out);
print_quoted_string(out, prop->text);
if (!expr_is_yes(prop->visible.expr)) {
fputs(" if ", out);
expr_fprint(prop->visible.expr, out);
}
fputc('\n', out);
break;
case P_DEFAULT:
fputs( " default ", out);
expr_fprint(prop->expr, out);
if (!expr_is_yes(prop->visible.expr)) {
fputs(" if ", out);
expr_fprint(prop->visible.expr, out);
}
fputc('\n', out);
break;
case P_SELECT:
fputs( " select ", out);
expr_fprint(prop->expr, out);
fputc('\n', out);
break;
case P_IMPLY:
fputs( " imply ", out);
expr_fprint(prop->expr, out);
fputc('\n', out);
break;
case P_RANGE:
fputs( " range ", out);
expr_fprint(prop->expr, out);
fputc('\n', out);
break;
case P_MENU:
fputs( " menu ", out);
print_quoted_string(out, prop->text);
fputc('\n', out);
break;
default:
fprintf(out, " unknown prop %d!\n", prop->type);
break;
}
}
if (menu->help) {
int len = strlen(menu->help);
while (menu->help[--len] == '\n')
menu->help[len] = 0;
fprintf(out, " help\n%s\n", menu->help);
}
}
void zconfdump(FILE *out)
{
struct property *prop;
struct symbol *sym;
struct menu *menu;
menu = rootmenu.list;
while (menu) {
if ((sym = menu->sym))
print_symbol(out, menu);
else if ((prop = menu->prompt)) {
switch (prop->type) {
case P_COMMENT:
fputs("\ncomment ", out);
print_quoted_string(out, prop->text);
fputs("\n", out);
break;
case P_MENU:
fputs("\nmenu ", out);
print_quoted_string(out, prop->text);
fputs("\n", out);
break;
default:
;
}
if (!expr_is_yes(prop->visible.expr)) {
fputs(" depends ", out);
expr_fprint(prop->visible.expr, out);
fputc('\n', out);
}
}
if (menu->list)
menu = menu->list;
else if (menu->next)
menu = menu->next;
else while ((menu = menu->parent)) {
if (menu->prompt && menu->prompt->type == P_MENU)
fputs("\nendmenu\n", out);
if (menu->next) {
menu = menu->next;
break;
}
}
}
}

View File

@@ -0,0 +1,581 @@
// SPDX-License-Identifier: GPL-2.0
//
// Copyright (C) 2018 Masahiro Yamada <yamada.masahiro@socionext.com>
#include <ctype.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <array_size.h>
#include <list.h>
#include <xalloc.h>
#include "internal.h"
#include "lkc.h"
#include "preprocess.h"
static char *expand_string_with_args(const char *in, int argc, char *argv[]);
static char *expand_string(const char *in);
static void __attribute__((noreturn)) pperror(const char *format, ...)
{
va_list ap;
fprintf(stderr, "%s:%d: ", cur_filename, yylineno);
va_start(ap, format);
vfprintf(stderr, format, ap);
va_end(ap);
fprintf(stderr, "\n");
exit(1);
}
/*
* Environment variables
*/
static LIST_HEAD(env_list);
struct env {
char *name;
char *value;
struct list_head node;
};
static void env_add(const char *name, const char *value)
{
struct env *e;
e = xmalloc(sizeof(*e));
e->name = xstrdup(name);
e->value = xstrdup(value);
list_add_tail(&e->node, &env_list);
}
static void env_del(struct env *e)
{
list_del(&e->node);
free(e->name);
free(e->value);
free(e);
}
/* The returned pointer must be freed when done */
static char *env_expand(const char *name)
{
struct env *e;
const char *value;
if (!*name)
return NULL;
list_for_each_entry(e, &env_list, node) {
if (!strcmp(name, e->name))
return xstrdup(e->value);
}
value = getenv(name);
if (!value)
return NULL;
/*
* We need to remember all referenced environment variables.
* They will be written out to include/config/auto.conf.cmd
*/
env_add(name, value);
return xstrdup(value);
}
void env_write_dep(struct gstr *s)
{
struct env *e, *tmp;
list_for_each_entry_safe(e, tmp, &env_list, node) {
str_printf(s,
"\n"
"ifneq \"$(%s)\" \"%s\"\n"
"$(autoconfig): FORCE\n"
"endif\n",
e->name, e->value);
env_del(e);
}
}
/*
* Built-in functions
*/
struct function {
const char *name;
unsigned int min_args;
unsigned int max_args;
char *(*func)(int argc, char *argv[]);
};
static char *do_error_if(int argc, char *argv[])
{
if (!strcmp(argv[0], "y"))
pperror("%s", argv[1]);
return xstrdup("");
}
static char *do_filename(int argc, char *argv[])
{
return xstrdup(cur_filename);
}
static char *do_info(int argc, char *argv[])
{
printf("%s\n", argv[0]);
return xstrdup("");
}
static char *do_lineno(int argc, char *argv[])
{
char buf[16];
sprintf(buf, "%d", yylineno);
return xstrdup(buf);
}
static char *do_shell(int argc, char *argv[])
{
FILE *p;
char buf[4096];
char *cmd;
size_t nread;
int i;
cmd = argv[0];
p = popen(cmd, "r");
if (!p) {
perror(cmd);
exit(1);
}
nread = fread(buf, 1, sizeof(buf), p);
if (nread == sizeof(buf))
nread--;
/* remove trailing new lines */
while (nread > 0 && buf[nread - 1] == '\n')
nread--;
buf[nread] = 0;
/* replace a new line with a space */
for (i = 0; i < nread; i++) {
if (buf[i] == '\n')
buf[i] = ' ';
}
if (pclose(p) == -1) {
perror(cmd);
exit(1);
}
return xstrdup(buf);
}
static char *do_warning_if(int argc, char *argv[])
{
if (!strcmp(argv[0], "y"))
fprintf(stderr, "%s:%d: %s\n", cur_filename, yylineno, argv[1]);
return xstrdup("");
}
static const struct function function_table[] = {
/* Name MIN MAX Function */
{ "error-if", 2, 2, do_error_if },
{ "filename", 0, 0, do_filename },
{ "info", 1, 1, do_info },
{ "lineno", 0, 0, do_lineno },
{ "shell", 1, 1, do_shell },
{ "warning-if", 2, 2, do_warning_if },
};
#define FUNCTION_MAX_ARGS 16
static char *function_expand(const char *name, int argc, char *argv[])
{
const struct function *f;
int i;
for (i = 0; i < ARRAY_SIZE(function_table); i++) {
f = &function_table[i];
if (strcmp(f->name, name))
continue;
if (argc < f->min_args)
pperror("too few function arguments passed to '%s'",
name);
if (argc > f->max_args)
pperror("too many function arguments passed to '%s'",
name);
return f->func(argc, argv);
}
return NULL;
}
/*
* Variables (and user-defined functions)
*/
static LIST_HEAD(variable_list);
struct variable {
char *name;
char *value;
enum variable_flavor flavor;
int exp_count;
struct list_head node;
};
static struct variable *variable_lookup(const char *name)
{
struct variable *v;
list_for_each_entry(v, &variable_list, node) {
if (!strcmp(name, v->name))
return v;
}
return NULL;
}
static char *variable_expand(const char *name, int argc, char *argv[])
{
struct variable *v;
char *res;
v = variable_lookup(name);
if (!v)
return NULL;
if (argc == 0 && v->exp_count)
pperror("Recursive variable '%s' references itself (eventually)",
name);
if (v->exp_count > 1000)
pperror("Too deep recursive expansion");
v->exp_count++;
if (v->flavor == VAR_RECURSIVE)
res = expand_string_with_args(v->value, argc, argv);
else
res = xstrdup(v->value);
v->exp_count--;
return res;
}
void variable_add(const char *name, const char *value,
enum variable_flavor flavor)
{
struct variable *v;
char *new_value;
bool append = false;
v = variable_lookup(name);
if (v) {
/* For defined variables, += inherits the existing flavor */
if (flavor == VAR_APPEND) {
flavor = v->flavor;
append = true;
} else {
free(v->value);
}
} else {
/* For undefined variables, += assumes the recursive flavor */
if (flavor == VAR_APPEND)
flavor = VAR_RECURSIVE;
v = xmalloc(sizeof(*v));
v->name = xstrdup(name);
v->exp_count = 0;
list_add_tail(&v->node, &variable_list);
}
v->flavor = flavor;
if (flavor == VAR_SIMPLE)
new_value = expand_string(value);
else
new_value = xstrdup(value);
if (append) {
v->value = xrealloc(v->value,
strlen(v->value) + strlen(new_value) + 2);
strcat(v->value, " ");
strcat(v->value, new_value);
free(new_value);
} else {
v->value = new_value;
}
}
static void variable_del(struct variable *v)
{
list_del(&v->node);
free(v->name);
free(v->value);
free(v);
}
void variable_all_del(void)
{
struct variable *v, *tmp;
list_for_each_entry_safe(v, tmp, &variable_list, node)
variable_del(v);
}
/*
* Evaluate a clause with arguments. argc/argv are arguments from the upper
* function call.
*
* Returned string must be freed when done
*/
static char *eval_clause(const char *str, size_t len, int argc, char *argv[])
{
char *tmp, *name, *res, *endptr, *prev, *p;
int new_argc = 0;
char *new_argv[FUNCTION_MAX_ARGS];
int nest = 0;
int i;
unsigned long n;
tmp = xstrndup(str, len);
/*
* If variable name is '1', '2', etc. It is generally an argument
* from a user-function call (i.e. local-scope variable). If not
* available, then look-up global-scope variables.
*/
n = strtoul(tmp, &endptr, 10);
if (!*endptr && n > 0 && n <= argc) {
res = xstrdup(argv[n - 1]);
goto free_tmp;
}
prev = p = tmp;
/*
* Split into tokens
* The function name and arguments are separated by a comma.
* For example, if the function call is like this:
* $(foo,$(x),$(y))
*
* The input string for this helper should be:
* foo,$(x),$(y)
*
* and split into:
* new_argv[0] = 'foo'
* new_argv[1] = '$(x)'
* new_argv[2] = '$(y)'
*/
while (*p) {
if (nest == 0 && *p == ',') {
*p = 0;
if (new_argc >= FUNCTION_MAX_ARGS)
pperror("too many function arguments");
new_argv[new_argc++] = prev;
prev = p + 1;
} else if (*p == '(') {
nest++;
} else if (*p == ')') {
nest--;
}
p++;
}
if (new_argc >= FUNCTION_MAX_ARGS)
pperror("too many function arguments");
new_argv[new_argc++] = prev;
/*
* Shift arguments
* new_argv[0] represents a function name or a variable name. Put it
* into 'name', then shift the rest of the arguments. This simplifies
* 'const' handling.
*/
name = expand_string_with_args(new_argv[0], argc, argv);
new_argc--;
for (i = 0; i < new_argc; i++)
new_argv[i] = expand_string_with_args(new_argv[i + 1],
argc, argv);
/* Search for variables */
res = variable_expand(name, new_argc, new_argv);
if (res)
goto free;
/* Look for built-in functions */
res = function_expand(name, new_argc, new_argv);
if (res)
goto free;
/* Last, try environment variable */
if (new_argc == 0) {
res = env_expand(name);
if (res)
goto free;
}
res = xstrdup("");
free:
for (i = 0; i < new_argc; i++)
free(new_argv[i]);
free(name);
free_tmp:
free(tmp);
return res;
}
/*
* Expand a string that follows '$'
*
* For example, if the input string is
* ($(FOO)$($(BAR)))$(BAZ)
* this helper evaluates
* $($(FOO)$($(BAR)))
* and returns a new string containing the expansion (note that the string is
* recursively expanded), also advancing 'str' to point to the next character
* after the corresponding closing parenthesis, in this case, *str will be
* $(BAR)
*/
static char *expand_dollar_with_args(const char **str, int argc, char *argv[])
{
const char *p = *str;
const char *q;
int nest = 0;
/*
* In Kconfig, variable/function references always start with "$(".
* Neither single-letter variables as in $A nor curly braces as in ${CC}
* are supported. '$' not followed by '(' loses its special meaning.
*/
if (*p != '(') {
*str = p;
return xstrdup("$");
}
p++;
q = p;
while (*q) {
if (*q == '(') {
nest++;
} else if (*q == ')') {
if (nest-- == 0)
break;
}
q++;
}
if (!*q)
pperror("unterminated reference to '%s': missing ')'", p);
/* Advance 'str' to after the expanded initial portion of the string */
*str = q + 1;
return eval_clause(p, q - p, argc, argv);
}
char *expand_dollar(const char **str)
{
return expand_dollar_with_args(str, 0, NULL);
}
static char *__expand_string(const char **str, bool (*is_end)(char c),
int argc, char *argv[])
{
const char *in, *p;
char *expansion, *out;
size_t in_len, out_len;
out = xmalloc(1);
*out = 0;
out_len = 1;
p = in = *str;
while (1) {
if (*p == '$') {
in_len = p - in;
p++;
expansion = expand_dollar_with_args(&p, argc, argv);
out_len += in_len + strlen(expansion);
out = xrealloc(out, out_len);
strncat(out, in, in_len);
strcat(out, expansion);
free(expansion);
in = p;
continue;
}
if (is_end(*p))
break;
p++;
}
in_len = p - in;
out_len += in_len;
out = xrealloc(out, out_len);
strncat(out, in, in_len);
/* Advance 'str' to the end character */
*str = p;
return out;
}
static bool is_end_of_str(char c)
{
return !c;
}
/*
* Expand variables and functions in the given string. Undefined variables
* expand to an empty string.
* The returned string must be freed when done.
*/
static char *expand_string_with_args(const char *in, int argc, char *argv[])
{
return __expand_string(&in, is_end_of_str, argc, argv);
}
static char *expand_string(const char *in)
{
return expand_string_with_args(in, 0, NULL);
}
static bool is_end_of_token(char c)
{
return !(isalnum(c) || c == '_' || c == '-');
}
/*
* Expand variables in a token. The parsing stops when a token separater
* (in most cases, it is a whitespace) is encountered. 'str' is updated to
* point to the next character.
*
* The returned string must be freed when done.
*/
char *expand_one_token(const char **str)
{
return __expand_string(str, is_end_of_token, 0, NULL);
}

View File

@@ -0,0 +1,19 @@
/* SPDX-License-Identifier: GPL-2.0-only */
#ifndef PREPROCESS_H
#define PREPROCESS_H
enum variable_flavor {
VAR_SIMPLE,
VAR_RECURSIVE,
VAR_APPEND,
};
struct gstr;
void env_write_dep(struct gstr *gs);
void variable_add(const char *name, const char *value,
enum variable_flavor flavor);
void variable_all_del(void);
char *expand_dollar(const char **str);
char *expand_one_token(const char **str);
#endif /* PREPROCESS_H */

View File

@@ -0,0 +1,42 @@
#!/bin/sh
# SPDX-License-Identifier: GPL-2.0
set -eu
cflags=$1
libs=$2
bin=$3
PKG5="Qt5Core Qt5Gui Qt5Widgets"
PKG6="Qt6Core Qt6Gui Qt6Widgets"
if [ -z "$(command -v ${HOSTPKG_CONFIG})" ]; then
echo >&2 "*"
echo >&2 "* 'make xconfig' requires '${HOSTPKG_CONFIG}'. Please install it."
echo >&2 "*"
exit 1
fi
if ${HOSTPKG_CONFIG} --exists $PKG6; then
${HOSTPKG_CONFIG} --cflags ${PKG6} > ${cflags}
# Qt6 requires C++17.
echo -std=c++17 >> ${cflags}
${HOSTPKG_CONFIG} --libs ${PKG6} > ${libs}
${HOSTPKG_CONFIG} --variable=libexecdir Qt6Core > ${bin}
exit 0
fi
if ${HOSTPKG_CONFIG} --exists $PKG5; then
${HOSTPKG_CONFIG} --cflags ${PKG5} > ${cflags}
${HOSTPKG_CONFIG} --libs ${PKG5} > ${libs}
${HOSTPKG_CONFIG} --variable=host_bins Qt5Core > ${bin}
exit 0
fi
echo >&2 "*"
echo >&2 "* Could not find Qt6 or Qt5 via ${HOSTPKG_CONFIG}."
echo >&2 "* Please install Qt6 or Qt5 and make sure it's in PKG_CONFIG_PATH"
echo >&2 "* You need $PKG6 for Qt6"
echo >&2 "* You need $PKG5 for Qt5"
echo >&2 "*"
exit 1

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,272 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>
*/
#include <QCheckBox>
#include <QDialog>
#include <QHeaderView>
#include <QLineEdit>
#include <QMainWindow>
#include <QPushButton>
#include <QSettings>
#include <QSplitter>
#include <QStyledItemDelegate>
#include <QTextBrowser>
#include <QTreeWidget>
#include "expr.h"
class ConfigList;
class ConfigItem;
class ConfigMainWindow;
class ConfigSettings : public QSettings {
public:
ConfigSettings();
QList<int> readSizes(const QString& key, bool *ok);
bool writeSizes(const QString& key, const QList<int>& value);
};
enum colIdx {
promptColIdx, nameColIdx, dataColIdx
};
enum listMode {
singleMode, menuMode, symbolMode, fullMode, listMode
};
enum optionMode {
normalOpt = 0, allOpt, promptOpt
};
class ConfigList : public QTreeWidget {
Q_OBJECT
typedef class QTreeWidget Parent;
public:
ConfigList(QWidget *parent, const char *name = 0);
~ConfigList();
void reinit(void);
ConfigItem* findConfigItem(struct menu *);
void setSelected(QTreeWidgetItem *item, bool enable) {
for (int i = 0; i < selectedItems().size(); i++)
selectedItems().at(i)->setSelected(false);
item->setSelected(enable);
}
protected:
void keyPressEvent(QKeyEvent *e);
void mouseReleaseEvent(QMouseEvent *e);
void mouseDoubleClickEvent(QMouseEvent *e);
void focusInEvent(QFocusEvent *e);
void contextMenuEvent(QContextMenuEvent *e);
public slots:
void setRootMenu(struct menu *menu);
void updateList();
void setValue(ConfigItem* item, tristate val);
void changeValue(ConfigItem* item);
void updateSelection(void);
void saveSettings(void);
void setOptionMode(QAction *action);
void setShowName(bool on);
signals:
void menuChanged(struct menu *menu);
void menuSelected(struct menu *menu);
void itemSelected(struct menu *menu);
void parentSelected(void);
void gotFocus(struct menu *);
void showNameChanged(bool on);
public:
void updateListAll(void)
{
updateAll = true;
updateList();
updateAll = false;
}
void setAllOpen(bool open);
void setParentMenu(void);
bool menuSkip(struct menu *);
void updateMenuList(ConfigItem *parent, struct menu*);
void updateMenuList(struct menu *menu);
bool updateAll;
bool showName;
enum listMode mode;
enum optionMode optMode;
struct menu *rootEntry;
QPalette disabledColorGroup;
QPalette inactivedColorGroup;
QMenu* headerPopup;
static QList<ConfigList *> allLists;
static void updateListForAll();
static void updateListAllForAll();
static QAction *showNormalAction, *showAllAction, *showPromptAction;
};
class ConfigItem : public QTreeWidgetItem {
typedef class QTreeWidgetItem Parent;
public:
ConfigItem(ConfigList *parent, ConfigItem *after, struct menu *m)
: Parent(parent, after), nextItem(0), menu(m), goParent(false)
{
init();
}
ConfigItem(ConfigItem *parent, ConfigItem *after, struct menu *m)
: Parent(parent, after), nextItem(0), menu(m), goParent(false)
{
init();
}
ConfigItem(ConfigList *parent, ConfigItem *after)
: Parent(parent, after), nextItem(0), menu(0), goParent(true)
{
init();
}
~ConfigItem(void);
void init(void);
void updateMenu(void);
void testUpdateMenu(void);
ConfigList* listView() const
{
return (ConfigList*)Parent::treeWidget();
}
ConfigItem* firstChild() const
{
return (ConfigItem *)Parent::child(0);
}
ConfigItem* nextSibling()
{
ConfigItem *ret = NULL;
ConfigItem *_parent = (ConfigItem *)parent();
if(_parent) {
ret = (ConfigItem *)_parent->child(_parent->indexOfChild(this)+1);
} else {
QTreeWidget *_treeWidget = treeWidget();
ret = (ConfigItem *)_treeWidget->topLevelItem(_treeWidget->indexOfTopLevelItem(this)+1);
}
return ret;
}
// TODO: Implement paintCell
ConfigItem* nextItem;
struct menu *menu;
bool goParent;
static QIcon symbolYesIcon, symbolModIcon, symbolNoIcon;
static QIcon choiceYesIcon, choiceNoIcon;
static QIcon menuIcon, menubackIcon;
};
class ConfigItemDelegate : public QStyledItemDelegate
{
private:
struct menu *menu;
public:
ConfigItemDelegate(QObject *parent = nullptr)
: QStyledItemDelegate(parent) {}
QWidget *createEditor(QWidget *parent,
const QStyleOptionViewItem &option,
const QModelIndex &index) const override;
void setModelData(QWidget *editor, QAbstractItemModel *model,
const QModelIndex &index) const override;
};
class ConfigInfoView : public QTextBrowser {
Q_OBJECT
typedef class QTextBrowser Parent;
QMenu *contextMenu;
public:
ConfigInfoView(QWidget* parent, const char *name = 0);
bool showDebug(void) const { return _showDebug; }
public slots:
void setInfo(struct menu *menu);
void saveSettings(void);
void setShowDebug(bool);
void clicked (const QUrl &url);
signals:
void showDebugChanged(bool);
void menuSelected(struct menu *);
protected:
void symbolInfo(void);
void menuInfo(void);
QString debug_info(struct symbol *sym);
static QString print_filter(const QString &str);
static void expr_print_help(void *data, struct symbol *sym, const char *str);
void contextMenuEvent(QContextMenuEvent *event);
struct symbol *sym;
struct menu *_menu;
bool _showDebug;
};
class ConfigSearchWindow : public QDialog {
Q_OBJECT
typedef class QDialog Parent;
public:
ConfigSearchWindow(ConfigMainWindow *parent);
public slots:
void saveSettings(void);
void search(void);
protected:
QLineEdit* editField;
QPushButton* searchButton;
QSplitter* split;
ConfigList *list;
ConfigInfoView* info;
struct symbol **result;
};
class ConfigMainWindow : public QMainWindow {
Q_OBJECT
QString configname;
static QAction *saveAction;
static void conf_changed(bool);
public:
ConfigMainWindow(void);
public slots:
void changeMenu(struct menu *);
void changeItens(struct menu *);
void setMenuLink(struct menu *);
void listFocusChanged(void);
void goBack(void);
void loadConfig(void);
bool saveConfig(void);
void saveConfigAs(void);
void searchConfig(void);
void showSingleView(void);
void showSplitView(void);
void showFullView(void);
void showIntro(void);
void showAbout(void);
void saveSettings(void);
protected:
void closeEvent(QCloseEvent *e);
ConfigSearchWindow *searchWindow;
ConfigList *menuList;
ConfigList *configList;
ConfigInfoView *helpText;
QAction *backAction;
QAction *singleViewAction;
QAction *splitViewAction;
QAction *fullViewAction;
QSplitter *split1;
QSplitter *split2;
};

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,96 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2002-2005 Roman Zippel <zippel@linux-m68k.org>
* Copyright (C) 2002-2005 Sam Ravnborg <sam@ravnborg.org>
*/
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <hash.h>
#include <hashtable.h>
#include <xalloc.h>
#include "lkc.h"
/* hash table of all parsed Kconfig files */
static HASHTABLE_DEFINE(file_hashtable, 1U << 11);
struct file {
struct hlist_node node;
char name[];
};
/* file already present in list? If not add it */
const char *file_lookup(const char *name)
{
struct file *file;
size_t len;
int hash = hash_str(name);
hash_for_each_possible(file_hashtable, file, node, hash)
if (!strcmp(name, file->name))
return file->name;
len = strlen(name);
file = xmalloc(sizeof(*file) + len + 1);
memset(file, 0, sizeof(*file));
memcpy(file->name, name, len);
file->name[len] = '\0';
hash_add(file_hashtable, &file->node, hash);
str_printf(&autoconf_cmd, "\t%s \\\n", name);
return file->name;
}
/* Allocate initial growable string */
struct gstr str_new(void)
{
struct gstr gs;
gs.s = xmalloc(sizeof(char) * 64);
gs.len = 64;
gs.max_width = 0;
strcpy(gs.s, "\0");
return gs;
}
/* Free storage for growable string */
void str_free(struct gstr *gs)
{
free(gs->s);
gs->s = NULL;
gs->len = 0;
}
/* Append to growable string */
void str_append(struct gstr *gs, const char *s)
{
size_t l;
if (s) {
l = strlen(gs->s) + strlen(s) + 1;
if (l > gs->len) {
gs->s = xrealloc(gs->s, l);
gs->len = l;
}
strcat(gs->s, s);
}
}
/* Append printf formatted string to growable string */
void str_printf(struct gstr *gs, const char *fmt, ...)
{
va_list ap;
char s[10000]; /* big enough... */
va_start(ap, fmt);
vsnprintf(s, sizeof(s), fmt, ap);
str_append(gs, s);
va_end(ap);
}
/* Retrieve value of growable string */
char *str_get(const struct gstr *gs)
{
return gs->s;
}

View File

@@ -0,0 +1,13 @@
#!/bin/sh
# SPDX-License-Identifier: GPL-2.0
#
# Usage: $ ./pahole-version.sh pahole
#
# Prints pahole's version in a 3-digit form, such as 119 for v1.19.
if [ ! -x "$(command -v "$@")" ]; then
echo 0
exit 1
fi
"$@" --version | sed -E 's/v([0-9]+)\.([0-9]+)/\1\2/'

View File

@@ -0,0 +1,13 @@
# SUBARCH tells the usermode build what the underlying arch is. That is set
# first, and if a usermode build is happening, the "ARCH=um" on the command
# line overrides the setting of ARCH below. If a native build is happening,
# then ARCH is assigned, getting whatever value it gets normally, and
# SUBARCH is subsequently ignored.
SUBARCH := $(shell uname -m | sed -e s/i.86/x86/ -e s/x86_64/x86/ \
-e s/sun4u/sparc64/ \
-e /^arm64$$/!s/arm.*/arm/ -e s/sa110/arm/ \
-e s/s390x/s390/ \
-e s/ppc.*/powerpc/ -e s/mips.*/mips/ \
-e s/sh[234].*/sh/ -e s/aarch64.*/arm64/ \
-e s/riscv.*/riscv/ -e s/loongarch.*/loongarch/)