1#!/bin/bash
2# SPDX-License-Identifier: GPL-2.0
3# Manipulate options in a .config file from the command line
4
5myname=${0##*/}
6
7# If no prefix forced, use the default CONFIG_
8CONFIG_="${CONFIG_-CONFIG_}"
9
10usage() {
11	cat >&2 <<EOL
12Manipulate options in a .config file from the command line.
13Usage:
14$myname options command ...
15commands:
16	--enable|-e option   Enable option
17	--disable|-d option  Disable option
18	--module|-m option   Turn option into a module
19	--set-str option string
20	                     Set option to "string"
21	--set-val option value
22	                     Set option to value
23	--undefine|-u option Undefine option
24	--state|-s option    Print state of option (n,y,m,undef)
25
26	--enable-after|-E beforeopt option
27                             Enable option directly after other option
28	--disable-after|-D beforeopt option
29                             Disable option directly after other option
30	--module-after|-M beforeopt option
31                             Turn option into module directly after other option
32
33	commands can be repeated multiple times
34
35options:
36	--file config-file   .config file to change (default .config)
37	--keep-case|-k       Keep next symbols' case (dont' upper-case it)
38
39$myname doesn't check the validity of the .config file. This is done at next
40make time.
41
42By default, $myname will upper-case the given symbol. Use --keep-case to keep
43the case of all following symbols unchanged.
44
45$myname uses 'CONFIG_' as the default symbol prefix. Set the environment
46variable CONFIG_ to the prefix to use. Eg.: CONFIG_="FOO_" $myname ...
47EOL
48	exit 1
49}
50
51checkarg() {
52	ARG="$1"
53	if [ "$ARG" = "" ] ; then
54		usage
55	fi
56	case "$ARG" in
57	${CONFIG_}*)
58		ARG="${ARG/${CONFIG_}/}"
59		;;
60	esac
61	if [ "$MUNGE_CASE" = "yes" ] ; then
62		ARG="`echo $ARG | tr a-z A-Z`"
63	fi
64}
65
66txt_append() {
67	local anchor="$1"
68	local insert="$2"
69	local infile="$3"
70	local tmpfile="$infile.swp"
71
72	# sed append cmd: 'a\' + newline + text + newline
73	cmd="$(printf "a\\%b$insert" "\n")"
74
75	sed -e "/$anchor/$cmd" "$infile" >"$tmpfile"
76	# replace original file with the edited one
77	mv "$tmpfile" "$infile"
78}
79
80txt_subst() {
81	local before="$1"
82	local after="$2"
83	local infile="$3"
84	local tmpfile="$infile.swp"
85
86	sed -e "s:$before:$after:" "$infile" >"$tmpfile"
87	# replace original file with the edited one
88	mv "$tmpfile" "$infile"
89}
90
91txt_delete() {
92	local text="$1"
93	local infile="$2"
94	local tmpfile="$infile.swp"
95
96	sed -e "/$text/d" "$infile" >"$tmpfile"
97	# replace original file with the edited one
98	mv "$tmpfile" "$infile"
99}
100
101set_var() {
102	local name=$1 new=$2 before=$3
103
104	name_re="^($name=|# $name is not set)"
105	before_re="^($before=|# $before is not set)"
106	if test -n "$before" && grep -Eq "$before_re" "$FN"; then
107		txt_append "^$before=" "$new" "$FN"
108		txt_append "^# $before is not set" "$new" "$FN"
109	elif grep -Eq "$name_re" "$FN"; then
110		txt_subst "^$name=.*" "$new" "$FN"
111		txt_subst "^# $name is not set" "$new" "$FN"
112	else
113		echo "$new" >>"$FN"
114	fi
115}
116
117undef_var() {
118	local name=$1
119
120	txt_delete "^$name=" "$FN"
121	txt_delete "^# $name is not set" "$FN"
122}
123
124if [ "$1" = "--file" ]; then
125	FN="$2"
126	if [ "$FN" = "" ] ; then
127		usage
128	fi
129	shift 2
130else
131	FN=.config
132fi
133
134if [ "$1" = "" ] ; then
135	usage
136fi
137
138MUNGE_CASE=yes
139while [ "$1" != "" ] ; do
140	CMD="$1"
141	shift
142	case "$CMD" in
143	--keep-case|-k)
144		MUNGE_CASE=no
145		continue
146		;;
147	--refresh)
148		;;
149	--*-after|-E|-D|-M)
150		checkarg "$1"
151		A=$ARG
152		checkarg "$2"
153		B=$ARG
154		shift 2
155		;;
156	-*)
157		checkarg "$1"
158		shift
159		;;
160	esac
161	case "$CMD" in
162	--enable|-e)
163		set_var "${CONFIG_}$ARG" "${CONFIG_}$ARG=y"
164		;;
165
166	--disable|-d)
167		set_var "${CONFIG_}$ARG" "# ${CONFIG_}$ARG is not set"
168		;;
169
170	--module|-m)
171		set_var "${CONFIG_}$ARG" "${CONFIG_}$ARG=m"
172		;;
173
174	--set-str)
175		# sed swallows one level of escaping, so we need double-escaping
176		set_var "${CONFIG_}$ARG" "${CONFIG_}$ARG=\"${1//\"/\\\\\"}\""
177		shift
178		;;
179
180	--set-val)
181		set_var "${CONFIG_}$ARG" "${CONFIG_}$ARG=$1"
182		shift
183		;;
184	--undefine|-u)
185		undef_var "${CONFIG_}$ARG"
186		;;
187
188	--state|-s)
189		if grep -q "# ${CONFIG_}$ARG is not set" $FN ; then
190			echo n
191		else
192			V="$(grep "^${CONFIG_}$ARG=" $FN)"
193			if [ $? != 0 ] ; then
194				echo undef
195			else
196				V="${V/#${CONFIG_}$ARG=/}"
197				V="${V/#\"/}"
198				V="${V/%\"/}"
199				V="${V//\\\"/\"}"
200				echo "${V}"
201			fi
202		fi
203		;;
204
205	--enable-after|-E)
206		set_var "${CONFIG_}$B" "${CONFIG_}$B=y" "${CONFIG_}$A"
207		;;
208
209	--disable-after|-D)
210		set_var "${CONFIG_}$B" "# ${CONFIG_}$B is not set" "${CONFIG_}$A"
211		;;
212
213	--module-after|-M)
214		set_var "${CONFIG_}$B" "${CONFIG_}$B=m" "${CONFIG_}$A"
215		;;
216
217	# undocumented because it ignores --file (fixme)
218	--refresh)
219		yes "" | make oldconfig
220		;;
221
222	*)
223		usage
224		;;
225	esac
226done
227