ASR_BASE

Change-Id: Icf3719cc0afe3eeb3edc7fa80a2eb5199ca9dda1
diff --git a/scripts/autopick.sh b/scripts/autopick.sh
new file mode 100755
index 0000000..dc1cf4e
--- /dev/null
+++ b/scripts/autopick.sh
@@ -0,0 +1,107 @@
+#!/bin/bash
+set -e
+get_patch_id_commit_id_pairs() {
+	git log "$@" --pretty=tformat:'COMMITm@g1c %H %al %as %f%n%B' | awk 'function p(){if(h){print(d?d:c?c:g?g:(a":"t":"s)),h}};$1=="COMMITm@g1c"{p();h=$2;a=$3;t=$4;s=$5;c=d=g="";next};$1=="Differential"&&$2=="Revision:"{d=$3;sub("http.*/","",d);next};$1=="Change-Id:"{c=$1$2;next};$1=="git-svn-id:"{g=$2;next};END{p()}'
+}
+
+if [ -z "$1" ]; then
+	echo "Usage: `basename "$0"` branch_to_auto_pick_from"
+	exit
+fi
+FROM_BRANCH="$1"
+TO_BRANCH="$(git rev-parse --abbrev-ref HEAD)"
+if [ -z "$TO_BRANCH" ]; then
+	echo "Please git checkout to a branch first"
+	exit 1
+fi
+
+echo "Computing merge base from $FROM_BRANCH to $TO_BRANCH ..."
+MERGE_BASE="$(git merge-base --all "$FROM_BRANCH" "$TO_BRANCH")"
+if [ -z "$MERGE_BASE" ]; then
+	echo "Failed to get merge base"
+	exit 1
+elif [ $(wc -l <<< "$MERGE_BASE") -gt 1 ]; then
+	echo "Git tree with cross merge not supported"
+	exit 1
+fi
+echo "Merge base is $(get_patch_id_commit_id_pairs $MERGE_BASE^..$MERGE_BASE)"
+
+echo -n "Getting patch list on $FROM_BRANCH ... "
+declare -A MAP_FROM_PATCHES
+REV_LIST_FROM_PATCHES=
+while read c d; do
+	if [ -n "$(exec 2>/dev/null; echo ${MAP_FROM_PATCHES[$c]})" ]; then
+		echo "Duplicate patch name found for $c $d ${MAP_FROM_PATCHES[$c]}"
+		d="$d ${MAP_FROM_PATCHES[$c]}"
+	fi
+	MAP_FROM_PATCHES[$c]="$d"
+	REV_LIST_FROM_PATCHES="$c
+$REV_LIST_FROM_PATCHES"
+done < <(get_patch_id_commit_id_pairs "$MERGE_BASE..$FROM_BRANCH")
+echo "${#MAP_FROM_PATCHES[@]} patches"
+
+echo -n "Getting patch list on $TO_BRANCH ... "
+declare -A MAP_TO_PATCHES
+declare -A MAP_TO_PATCHES_DUP
+while read c d; do
+	if [ -n "$(exec 2>/dev/null; echo ${MAP_TO_PATCHES[$c]})" ]; then
+		echo "Duplicate patch name found for $c $d ${MAP_TO_PATCHES[$c]}"
+		d="$d ${MAP_TO_PATCHES[$c]}"
+		MAP_TO_PATCHES_DUP[$c]="$d"
+	fi
+	MAP_TO_PATCHES[$c]="$d"
+done < <(get_patch_id_commit_id_pairs "$MERGE_BASE..$TO_BRANCH")
+echo "${#MAP_TO_PATCHES[@]} patches"
+for d in ${MAP_TO_PATCHES_DUP[@]}; do
+	HASH="$(git diff --binary $d^..$d | sha1sum | cut -d ' ' -f 1)"
+	eval "HAS_$HASH=true"
+done
+
+echo "Computing patches to cherry-pick ..."
+PATCHES_TO_PICK=
+while read c; do
+	if [ -z "$c" ]; then
+		continue
+	fi
+	if [ -z "$(exec 2>/dev/null; echo ${MAP_TO_PATCHES[$c]})" ] || [ ${#MAP_FROM_PATCHES[$c]} -ne ${#MAP_TO_PATCHES[$c]} ]; then
+		echo -ne "$c\t"
+		if [[ "${MAP_FROM_PATCHES[$c]}" == *" "* ]]; then
+			if [ -z "$(exec 2>/dev/null; echo ${MAP_TO_PATCHES_DUP[$c]})" ]; then
+				for d in ${MAP_TO_PATCHES[$c]}; do
+					HASH="$(git diff --binary $d^..$d | sha1sum | cut -d ' ' -f 1)"
+					eval "HAS_$HASH=true"
+				done
+			fi
+			for d in ${MAP_FROM_PATCHES[$c]}; do
+				if [ -z "$(eval echo "\${VISITED_$d}")" ]; then
+					break
+				fi
+			done
+			if [ -n "$(eval echo "\${VISITED_$d}")" ]; then
+				echo "All patches visited for $c: ${MAP_FROM_PATCHES[$c]}"
+				exit 1
+			fi
+			eval "VISITED_$d=true"
+			HASH="$(git diff --binary $d^..$d | sha1sum | cut -d ' ' -f 1)"
+			if [ -n "$(eval echo "\${HAS_$HASH}")" ]; then
+				echo "$d skipped"
+				continue
+			fi
+			c="$c-$HASH"
+			MAP_FROM_PATCHES[$c]="$d"
+		fi
+		echo "${MAP_FROM_PATCHES[$c]}"
+		PATCHES_TO_PICK="$PATCHES_TO_PICK
+$c"
+	fi
+done <<< "$REV_LIST_FROM_PATCHES"
+while read c; do
+	if [ -z "$c" ]; then
+		continue
+	fi
+	d="${MAP_FROM_PATCHES[$c]}"
+	echo "Cherry-picking $d for $c ..."
+	git cherry-pick "$d"
+done <<< "$PATCHES_TO_PICK"
+
+echo "Done"
diff --git a/scripts/brcmImage.pl b/scripts/brcmImage.pl
new file mode 100755
index 0000000..60a59f2
--- /dev/null
+++ b/scripts/brcmImage.pl
@@ -0,0 +1,162 @@
+#!/usr/bin/env perl
+#
+#    Copyright (C) 2009	Henk Vergonet <Henk.Vergonet@gmail.com>
+#
+#    This program is free software; you can redistribute it and/or modify
+#    it under the terms of the GNU General Public License as published by
+#    the Free Software Foundation; either version 2 of the License, or
+#    (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU General Public License for more details.
+#
+#    You should have received a copy of the GNU General Public License
+#    along with this program; if not, write to the Free Software
+#    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+#
+
+# Description:
+#   Replacement for brcmImagebuilder
+#
+# Disclaimer:
+#   Use this software at your own risk.
+#
+# Changelog:
+#   2009-01-01	Henk.Vergonet at gmail.com
+#
+use strict;
+use Getopt::Std;
+use Compress::Zlib;
+
+my $version = "0.1";
+my %arg = (
+	o => 'bcm963xx_fs_kernel',
+	b => 'OpenWrt',
+	c => '6348',
+	s => 64,
+	f => 0xbfc00000,
+	x => 0x00010000,
+	a => 0x80010000,
+	e => 0x80010000,
+	i => 2,
+);
+my $prog = $0;
+$prog =~ s/^.*\///;
+getopts("r:k:o:lc:b:s:f:i:a:e:tpvh", \%arg);
+
+die "usage: $prog ~opts~
+
+  -r <file>	: input rootfs file
+  -k <file>	: input kernel file
+  -o <file>	: output image file, default $arg{o}
+  -l		: littleendian system, default ".($arg{l} ? 'yes' : 'no')."
+  -c <chipid>	: default $arg{c} 
+  -b <boardid>	: default $arg{b} 
+  -s <size_kb>	: erase sise flash, default $arg{s} 
+  -f <baseaddr>	: flash base, default ".sprintf('0x%x', $arg{f})."
+  -x <cfelen>	: length of cfe, default ".sprintf('0x%x', $arg{x})."
+  -i		: 2=dual image, default $arg{i}
+
+  -a <loadaddr>	: Kernel load address, default ".sprintf('0x%x', $arg{a})."
+  -e <entryaddr>: Kernel entry address, default ".sprintf('0x%x', $arg{e})."
+  -t		: Prefix kernel with load,entry,size
+
+  -p		: Add a 'gOtO' partition 
+
+  -v		: be more verbose
+  -h		: help, version $version
+
+EXAMPLES:
+    $prog -k kern -r rootfs
+" if $arg{h} || !$arg{k} || !$arg{r};
+
+sub Read_Image
+{
+	open my $fh, $_[0] or die "open $_[0]: $!";
+	local $/;	# Set input to "slurp" mode.
+	my $buf = <$fh>;
+	close $fh;
+	return $buf;
+}
+
+sub Padlen
+{
+	my $p = $_[0] % $_[1];
+	return ($p ? $_[1] - $p : 0);
+}
+
+sub Pad
+{
+	my ($buf, $off, $bs) = @_[0..2];
+	$buf .= chr(255) x Padlen(length($buf) + $off, $bs);
+	return $buf;
+}
+
+sub bcmImage
+{
+	my ($k, $f) = @_[0..1];
+	my $tmp = $arg{x} + 0x100 + $arg{f};
+	
+	# regular: rootfs+kernel
+	my ($img, $fa, $ka) = ( $f.$k, $tmp, $tmp + length($f) );
+
+	# test: kernel+rootfs
+#	my ($img, $fa, $ka) = ( $k.$f, $tmp + length($k), $tmp );
+
+	$fa = 0 unless length($f);
+
+	my $hdr = pack("a4a20a14a6a16a2a10a12a10a12a10a12a10a2a2a74Na16",
+		'6',
+		'LinuxInside', 
+		'ver. 2.0', 
+		$arg{c},
+		$arg{b},
+		($arg{l} ? '0' : '1'),
+		length($img),
+		'0',
+		'0',
+		$fa,
+		length($f),
+		$ka,
+		length($k),
+		($arg{i}==2 ? '1' : '0'),
+		'',		# if 1, the image is INACTIVE; if 0, active
+		'',
+		~crc32($k, crc32($f)),
+		'');
+	$hdr .= pack('Na16', ~crc32($hdr), '');
+
+	printf "kernel at 0x%x length 0x%x(%u)\n", $ka, length($k), length($k)
+		if $arg{v};
+	printf "rootfs at 0x%x length 0x%x(%u)\n", $fa, length($f), length($f)
+		if $arg{v};
+
+	open(FO, ">$arg{o}");
+	print FO $hdr;
+	print FO $img;
+	close FO;
+}
+
+# MAIN
+
+my $kern = Read_Image $arg{k};
+my $root = Read_Image $arg{r};
+
+$kern = pack('NNN', $arg{a}, $arg{e}, length($kern)).$kern if $arg{t};
+
+# specific fixup for the CFE that expects rootfs-kernel order
+if ($arg{p}) {
+	$kern = Pad($kern, 0x10c, $arg{s} * 1024);
+	my $dummy_root = pack('a4NN',
+			'gOtO',
+			length($kern)+12,
+			length($root)+Padlen(length($root), $arg{s} * 1024)
+	);
+	$kern .= $root;
+	$root = $dummy_root;
+}
+
+bcmImage($kern, $root);
+
diff --git a/scripts/bundle-libraries.sh b/scripts/bundle-libraries.sh
new file mode 100755
index 0000000..621bf57
--- /dev/null
+++ b/scripts/bundle-libraries.sh
@@ -0,0 +1,212 @@
+#!/usr/bin/env bash
+#
+#   Script to install host system binaries along with required libraries.
+#
+#   Copyright (C) 2012-2017 Jo-Philipp Wich <jo@mein.io>
+#
+#   This program is free software; you can redistribute it and/or modify
+#   it under the terms of the GNU General Public License as published by
+#   the Free Software Foundation; either version 2 of the License, or
+#   (at your option) any later version.
+#
+#   This program is distributed in the hope that it will be useful,
+#   but WITHOUT ANY WARRANTY; without even the implied warranty of
+#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#   GNU General Public License for more details.
+#
+#   You should have received a copy of the GNU General Public License
+#   along with this program; if not, write to the Free Software
+#   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+DIR="$1"; shift
+
+_cp() {
+	cp ${VERBOSE:+-v} -L "$1" "$2" || {
+		echo "cp($1 $2) failed" >&2
+		exit 1
+	}
+}
+
+_mv() {
+	mv ${VERBOSE:+-v} "$1" "$2" || {
+		echo "mv($1 $2) failed" >&2
+		exit 1
+	}
+}
+
+_md() {
+	mkdir ${VERBOSE:+-v} -p "$1" || {
+		echo "mkdir($1) failed" >&2
+		exit 2
+	}
+}
+
+_ln() {
+	ln ${VERBOSE:+-v} -sf "$1" "$2" || {
+		echo "ln($1 $2) failed" >&2
+		exit 3
+	}
+}
+
+_relpath() {
+	local base="$(readlink -f "$1")"
+	local dest="$(readlink -f "$2")"
+	local up
+
+	[ -d "$base" ] || base="${base%/*}"
+	[ -d "$dest" ] || dest="${dest%/*}"
+
+	while true; do
+		case "$base"
+			in "$dest"/*)
+				echo "$up/${base#$dest/}"
+				break
+			;;
+			*)
+				dest="${dest%/*}"
+				up="${up:+$up/}.."
+			;;
+		esac
+	done
+}
+
+_runas_so() {
+	cat <<-EOT | ${CC:-gcc} -x c -fPIC -shared -o "$1" -
+		#include <unistd.h>
+		#include <stdio.h>
+		#include <stdlib.h>
+
+		int mangle_arg0(int argc, char **argv, char **env) {
+			char *arg0 = getenv("RUNAS_ARG0");
+
+			if (arg0) {
+				argv[0] = arg0;
+				unsetenv("RUNAS_ARG0");
+			}
+
+			return 0;
+		}
+
+		#ifdef __APPLE__
+		__attribute__((section("__DATA,__mod_init_func")))
+		#else
+		__attribute__((section(".init_array")))
+		#endif
+		static void *mangle_arg0_constructor = &mangle_arg0;
+	EOT
+
+	[ -x "$1" ] || {
+		echo "compiling preload library failed" >&2
+		exit 5
+	}
+}
+
+_patch_ldso() {
+	_cp "$1" "$1.patched"
+	sed -i -e 's,/\(usr\|lib\|etc\)/,/###/,g' "$1.patched"
+
+	if "$1.patched" 2>&1 | grep -q -- --library-path; then
+		_mv "$1.patched" "$1"
+	else
+		echo "binary patched ${1##*/} not executable, using original" >&2
+		rm -f "$1.patched"
+	fi
+}
+
+_patch_glibc() {
+	_cp "$1" "$1.patched"
+	sed -i -e 's,/usr/\(\(lib\|share\)/locale\),/###/\1,g' "$1.patched"
+
+	if "$1.patched" 2>&1 | grep -q -- GNU; then
+		_mv "$1.patched" "$1"
+	else
+		echo "binary patched ${1##*/} not executable, using original" >&2
+		rm -f "$1.patched"
+	fi
+}
+
+should_be_patched() {
+	local bin="$1"
+
+	[ -x "$bin" ] || return 1
+
+	case "$bin" in
+		*.so|*.so.[0-9]*)
+			return 1
+		;;
+		*)
+			file "$bin" | grep -sqE "ELF.*(executable|interpreter)" && return 0
+		;;
+	esac
+
+	return 1
+}
+
+for LDD in ${PATH//://ldd }/ldd; do
+	"$LDD" --version >/dev/null 2>/dev/null && break
+	LDD=""
+done
+
+[ -n "$LDD" -a -x "$LDD" ] || LDD=
+
+for BIN in "$@"; do
+	[ -n "$BIN" -a -n "$DIR" ] || {
+		echo "Usage: $0 <destdir> <executable> ..." >&2
+		exit 1
+	}
+
+	[ ! -d "$DIR/lib" ] && {
+		_md "$DIR/lib"
+		_md "$DIR/usr"
+		_ln "../lib" "$DIR/usr/lib"
+	}
+
+	[ ! -x "$DIR/lib/runas.so" ] && {
+		_runas_so "$DIR/lib/runas.so"
+	}
+
+	LDSO=""
+
+	[ -n "$LDD" ] && should_be_patched "$BIN" && {
+		for token in $("$LDD" "$BIN" 2>/dev/null); do
+			case "$token" in */*.so*)
+				dest="$DIR/lib/${token##*/}"
+				ddir="${dest%/*}"
+
+				case "$token" in
+					*/ld-*.so*) LDSO="${token##*/}" ;;
+				esac
+
+				[ -f "$token" -a ! -f "$dest" ] && {
+					_md "$ddir"
+					_cp "$token" "$dest"
+					case "$token" in
+						*/ld-*.so*) _patch_ldso "$dest" ;;
+						*/libc.so.6) _patch_glibc "$dest" ;;
+					esac
+				}
+			;; esac
+		done
+	}
+
+	# is a dynamically linked executable
+	if [ -n "$LDSO" ]; then
+		echo "Bundling ${BIN##*/}"
+
+		RUNDIR="$(readlink -f "$BIN")"; RUNDIR="${RUNDIR%/*}"
+		RUN="${LDSO#ld-}"; RUN="run-${RUN%%.so*}.sh"
+		REL="$(_relpath "$DIR/lib" "$BIN")"
+
+		_mv "$BIN" "$RUNDIR/.${BIN##*/}.bin"
+
+		cat <<-EOF > "$BIN"
+			#!/usr/bin/env bash
+			dir="\$(dirname "\$0")"
+			export RUNAS_ARG0="\$0"
+			export LD_PRELOAD="\${LD_PRELOAD:+\$LD_PRELOAD:}\$dir/${REL:+$REL/}runas.so"
+			exec "\$dir/${REL:+$REL/}$LDSO" --library-path "\$dir/${REL:+$REL/}" "\$dir/.${BIN##*/}.bin" "\$@"
+		EOF
+
+		chmod ${VERBOSE:+-v} 0755 "$BIN"
+	fi
+done
diff --git a/scripts/cameo-imghdr.py b/scripts/cameo-imghdr.py
new file mode 100755
index 0000000..1f8292b
--- /dev/null
+++ b/scripts/cameo-imghdr.py
@@ -0,0 +1,86 @@
+#!/usr/bin/python3
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# Copyright (C) 2022 Luiz Angelo Daros de Luca <luizluca@gmail.com>
+#
+# Cameo Image header geneator, used by some D-Link DGS-1210 switches
+# and APRESIA ApresiaLightGS series
+#
+import argparse
+import pathlib
+import socket
+import struct
+
+MODEL_LEN = 20
+SIGNATURE_LEN = 16
+LINUXLOAD_LEN = 10
+BUFSIZE = 4096
+
+parser = argparse.ArgumentParser(description='Generate Cameo firmware header.')
+parser.add_argument('source_file', type=argparse.FileType('rb'))
+parser.add_argument('dest_file', type=argparse.FileType('wb'))
+parser.add_argument('model')
+parser.add_argument('signature')
+parser.add_argument('partition', type=int, choices=range(0,10),
+                    metavar="partition=[0-9]",help="partition id")
+parser.add_argument('customer_signature', type=int, choices=range(0,10),
+                    metavar="customer_signature=[0-9]",
+                    help="customer signature")
+parser.add_argument('board_version', type=int, choices=range(0,2**32),
+                    metavar="board_version=[0-4294967295]",
+                    help="board version")
+parser.add_argument('linux_loadaddr', nargs='?',
+                    help="Kernel start address in 0xFFFFFFFF format")
+args = parser.parse_args()
+
+if len(args.model) > MODEL_LEN:
+    raise ValueError(f"Model '{args.model}' is greater than {MODEL_LEN} bytes")
+
+if len(args.signature) > SIGNATURE_LEN:
+    raise ValueError(f"Signature '{args.signature}' is greater than"
+                     f"{SIGNATURE_LEN} bytes")
+
+if args.signature == "os":
+    if args.linux_loadaddr:
+        if len(args.linux_loadaddr) > LINUXLOAD_LEN:
+            raise ValueError(f"linux_loadaddr '{args.linux_loadaddr}' is greater"
+                             f"than {LINUXLOAD_LEN} bytes")
+        if (args.linux_loadaddr[0:2] != "0x"):
+            raise ValueError(f"linux_loadaddr '{args.linux_loadaddr}' must use"
+                             f"the 0x789ABCDE format")
+        int(args.linux_loadaddr[2:],16)
+    else:
+        raise ValueError(f"linux_loadaddr is required for signature 'os'")
+else:
+    args.linux_loadaddr = ""
+
+checksum = 0
+size = 0
+while True:
+    buf = args.source_file.read(BUFSIZE)
+    if not buf:
+        break
+    checksum = sum(iter(buf),checksum) % (1<<32)
+    size += len(buf)
+
+args.dest_file.write(struct.pack('!I', checksum))
+args.dest_file.write(struct.pack(f'{MODEL_LEN}s',
+                                 args.model.encode("ascii")))
+args.dest_file.write(struct.pack(f'{SIGNATURE_LEN}s',
+                                 args.signature.encode("ascii")))
+args.dest_file.write(struct.pack('!B', args.partition))
+args.dest_file.write(struct.pack('!B', 0x40)) # ??? This header size?
+args.dest_file.write(struct.pack('!B', 0x00)) # ??? Encrypted?
+args.dest_file.write(struct.pack('!B', args.customer_signature))
+args.dest_file.write(struct.pack('!I', args.board_version))
+args.dest_file.write(struct.pack('!I', size))
+args.dest_file.write(struct.pack(f'{LINUXLOAD_LEN}s',
+                                 args.linux_loadaddr.encode("ascii")))
+args.dest_file.write(struct.pack('!2x'))
+
+args.source_file.seek(0)
+while True:
+    buf = args.source_file.read(BUFSIZE)
+    if not buf:
+        break
+    args.dest_file.write(buf)
diff --git a/scripts/cameo-tag.py b/scripts/cameo-tag.py
new file mode 100755
index 0000000..becd69f
--- /dev/null
+++ b/scripts/cameo-tag.py
@@ -0,0 +1,117 @@
+#!/usr/bin/python3
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# Copyright (C) 2022 OpenWrt.org
+#
+# ./cameo-tag.py <uImageFileName> <OffsetOfRootFS>
+#
+# CAMEO tag generator used for the D-Link DGS-1210 switches. Their U-Boot
+# loader checks for the string CAMEOTAG and a checksum in the kernel and
+# rootfs partitions. If not found it complains about the boot image.
+# Nevertheless it will boot if the tags are available in the secondary
+# boot partitions. If some day we want to overwrite the original vendor
+# partition we must have the tags in place. To solve this we insert the
+# tag two times into the kernel image.
+#
+# To understand what we do here it is helpful to explain how the original
+# CAMEO tag generation/checking works. The firmware consists of two parts.
+# A kernel uImage (<1.5MB) and a rootfs image (<12MB) that are written to
+# their respective mtd partitions. The default generator simply checksums
+# both parts and appends 16 bytes [<CAMEOTAG><0001><checksum>] to each part.
+# The checksum is only an addition of all preceding bytes (b0+b1+b2+...).
+# A tag does not interfere with any data in the images itself. During boot
+# the loader will scan all primary/secondary partitions (2*kernel, 2*rootfs)
+# until it finds the CAMEO tag. If checksums match everything is fine.
+# If all 4 fail we are lost. Luckily the loader does not care about where
+# the tags are located and ignores any data beyond a tag.
+#
+# The OpenWrt image consists of a kernel (>1.5MB) and a rootfs. There is
+# no chance to add CAMEO tags at the default locations, since the kernel spans
+# both the original kernel partition and the start of the rootfs partition.
+# This would leave the kernel partition without a tag. So we must find suitable
+# space.
+#
+# Location for original kernel partition is at the end of the uImage header.
+# We will reuse the last bytes of the IH_NAME field. This is the tricky part
+# because we have the header CRC and the CAMEO checksum that must match the
+# whole header. uImage header CRC checksums all data except the CRC itself. The
+# for CAMEO checksum in turn, checksums all preceding data except itself.
+# Changing one of both results in a change of the other, but data trailing the
+# CAMEO checksum only influences the CRC.
+#
+# Location for original rootfs partition is very simple. It is behind the
+# OpenWrt compressed kernel image file that spans into the rootfs. So
+# the tag will be written somewhere to the following rootfs partition and
+# can be found by U-Boot. The CAMEO checksum calculation must start at the
+# offset of the original rootfs partition and includes the "second" half of the
+# "split" kernel uImage.
+
+import argparse
+import os
+import zlib
+
+READ_UNTIL_EOF = -1
+UIMAGE_HEADER_SIZE = 64
+UIMAGE_CRC_OFF = 4
+UIMAGE_CRC_END = 8
+UIMAGE_NAME_OFF = 32
+UIMAGE_NAME_END = 56
+UIMAGE_SUM_OFF = 56
+UIMAGE_SUM_END = 60
+UIMAGE_INV_OFF = 60
+UIMAGE_INV_END = 64
+CAMEO_TAG = bytes([0x43, 0x41, 0x4d, 0x45, 0x4f, 0x54, 0x41, 0x47, 0x00, 0x00, 0x00, 0x01])
+IMAGE_NAME = bytes([0x4f, 0x70, 0x65, 0x6e, 0x57, 0x72, 0x74, 0x00, 0x00, 0x00, 0x00, 0x00])
+CRC_00 = bytes([0x00] * 4)
+CRC_FF = bytes([0xff] * 4)
+
+def read_buffer(offset, count):
+    args.uimage_file.seek(offset)
+    return bytearray(args.uimage_file.read(count))
+
+def write_buffer(whence, buf):
+    args.uimage_file.seek(0, whence)
+    args.uimage_file.write(buf)
+
+def cameosum(buf):
+    return (sum(buf) & 0xffffffff).to_bytes(4, 'big')
+
+def invertcrc(buf):
+    return (zlib.crc32(buf) ^ 0xffffffff).to_bytes(4, 'little')
+
+def checksum_header(buf):
+    # To efficently get a combination, we will make use of the following fact:
+    #     crc32(data + littleendian(crc32(data) ^ 0xffffffff)) = 0xffffffff
+    #
+    # After manipulation the uImage header looks like this:
+    #     [...<ffffffff>...<OpenWrt><000000><CAMEOTAG><0001><checksum><InvCRC>]
+    buf[UIMAGE_NAME_OFF:UIMAGE_NAME_END] = IMAGE_NAME + CAMEO_TAG
+    buf[UIMAGE_CRC_OFF:UIMAGE_CRC_END] = CRC_FF
+    buf[UIMAGE_SUM_OFF:UIMAGE_SUM_END] = cameosum(buf[0:UIMAGE_NAME_END])
+    buf[UIMAGE_CRC_OFF:UIMAGE_CRC_END] = CRC_00
+    buf[UIMAGE_INV_OFF:UIMAGE_INV_END] = invertcrc(buf[0:UIMAGE_SUM_END])
+    buf[UIMAGE_CRC_OFF:UIMAGE_CRC_END] = CRC_FF
+    return buf
+
+parser = argparse.ArgumentParser(description='Insert CAMEO firmware tags.')
+parser.add_argument('uimage_file', type=argparse.FileType('r+b'))
+parser.add_argument('rootfs_start', type=int)
+args = parser.parse_args()
+
+args.uimage_file.seek(0, os.SEEK_END)
+if args.uimage_file.tell() <= args.rootfs_start:
+    raise ValueError(f"uImage must be larger than {args.rootfs_start} bytes")
+
+# tag for the uImage Header of 64 bytes inside the kernel
+# partition. Read and mangle it so it contains a valid CAMEO tag
+# and checksum that matches perfectly to the uImage header CRC.
+
+buf = checksum_header(read_buffer(0, UIMAGE_HEADER_SIZE))
+write_buffer(os.SEEK_SET, buf)
+
+# tag for the second part of the kernel that resides in the
+# vendor rootfs partition. For this we will add the CAMEO tag
+# and the checksum to the end of the image.
+
+buf = read_buffer(args.rootfs_start, READ_UNTIL_EOF)
+write_buffer(os.SEEK_END, CAMEO_TAG + cameosum(buf + CAMEO_TAG))
diff --git a/scripts/cfe-bin-header.py b/scripts/cfe-bin-header.py
new file mode 100755
index 0000000..3bf652b
--- /dev/null
+++ b/scripts/cfe-bin-header.py
@@ -0,0 +1,72 @@
+#!/usr/bin/env python3
+
+import argparse
+import os
+import struct
+
+def auto_int(x):
+	return int(x, 0)
+
+def create_header(args, size):
+	header = struct.pack('>III', args.entry_addr, args.load_addr, size)
+	return header
+
+def create_output(args):
+	in_st = os.stat(args.input_file)
+	in_size = in_st.st_size
+
+	header = create_header(args, in_size)
+	print(header)
+
+	in_f = open(args.input_file, 'r+b')
+	in_bytes = in_f.read(in_size)
+	in_f.close()
+
+	out_f = open(args.output_file, 'w+b')
+	out_f.write(header)
+	out_f.write(in_bytes)
+	out_f.close()
+
+def main():
+	global args
+
+	parser = argparse.ArgumentParser(description='')
+
+	parser.add_argument('--entry-addr',
+		dest='entry_addr',
+		action='store',
+		type=auto_int,
+		help='Entry Address')
+
+	parser.add_argument('--input-file',
+		dest='input_file',
+		action='store',
+		type=str,
+		help='Input file')
+
+	parser.add_argument('--load-addr',
+		dest='load_addr',
+		action='store',
+		type=auto_int,
+		help='Load Address')
+
+	parser.add_argument('--output-file',
+		dest='output_file',
+		action='store',
+		type=str,
+		help='Output file')
+
+	args = parser.parse_args()
+
+	if (not args.input_file) or (not args.output_file):
+		parser.print_help()
+
+	if not args.entry_addr:
+		args.entry_addr = 0x80010000
+
+	if not args.load_addr:
+		args.load_addr = 0x80010000
+
+	create_output(args)
+
+main()
diff --git a/scripts/cfe-partition-tag.py b/scripts/cfe-partition-tag.py
new file mode 100755
index 0000000..41495a9
--- /dev/null
+++ b/scripts/cfe-partition-tag.py
@@ -0,0 +1,139 @@
+#!/usr/bin/env python3
+
+"""
+CFE Partition Tag
+
+{
+	u32 part_id;
+	u32 part_size;
+	u16 flags;
+	char part_name[33];
+	char part_version[21];
+	u32 part_crc32;
+}
+
+"""
+
+import argparse
+import os
+import struct
+import binascii
+
+
+PART_NAME_SIZE = 33
+PART_VERSION_SIZE = 21
+
+
+def auto_int(x):
+    return int(x, 0)
+
+
+def str_to_bytes_pad(string, size):
+    str_bytes = string.encode()
+    num_bytes = len(str_bytes)
+    if num_bytes >= size:
+        str_bytes = str_bytes[: size - 1] + "\0".encode()
+    else:
+        str_bytes += "\0".encode() * (size - num_bytes)
+    return str_bytes
+
+
+def create_tag(args, in_bytes, size):
+    # JAM CRC32 is bitwise not and unsigned
+    crc = ~binascii.crc32(in_bytes) & 0xFFFFFFFF
+
+    tag = bytearray()
+    tag += struct.pack(">I", args.part_id)
+    tag += struct.pack(">I", size)
+    tag += struct.pack(">H", args.part_flags)
+    tag += str_to_bytes_pad(args.part_name, PART_NAME_SIZE)
+    tag += str_to_bytes_pad(args.part_version, PART_VERSION_SIZE)
+    tag += struct.pack(">I", crc)
+
+    return tag
+
+
+def create_output(args):
+    in_st = os.stat(args.input_file)
+    in_size = in_st.st_size
+
+    in_f = open(args.input_file, "r+b")
+    in_bytes = in_f.read(in_size)
+    in_f.close()
+
+    tag = create_tag(args, in_bytes, in_size)
+
+    out_f = open(args.output_file, "w+b")
+    out_f.write(tag)
+    out_f.close()
+
+
+def main():
+    global args
+
+    parser = argparse.ArgumentParser(description="")
+
+    parser.add_argument(
+        "--flags",
+        dest="part_flags",
+        action="store",
+        type=auto_int,
+        help="Partition Flags",
+    )
+
+    parser.add_argument(
+        "--id",
+        dest="part_id",
+        action="store",
+        type=auto_int,
+        help="Partition ID",
+    )
+
+    parser.add_argument(
+        "--input-file",
+        dest="input_file",
+        action="store",
+        type=str,
+        help="Input file",
+    )
+
+    parser.add_argument(
+        "--output-file",
+        dest="output_file",
+        action="store",
+        type=str,
+        help="Output file",
+    )
+
+    parser.add_argument(
+        "--name",
+        dest="part_name",
+        action="store",
+        type=str,
+        help="Partition Name",
+    )
+
+    parser.add_argument(
+        "--version",
+        dest="part_version",
+        action="store",
+        type=str,
+        help="Partition Version",
+    )
+
+    args = parser.parse_args()
+
+    if (
+        (not args.part_flags)
+        or (not args.part_id)
+        or (not args.input_file)
+        or (not args.output_file)
+        or (not args.part_name)
+        or (not args.part_version)
+    ):
+        parser.print_help()
+    else:
+        create_output(args)
+
+
+main()
diff --git a/scripts/cfe-wfi-tag.py b/scripts/cfe-wfi-tag.py
new file mode 100755
index 0000000..5fac8ee
--- /dev/null
+++ b/scripts/cfe-wfi-tag.py
@@ -0,0 +1,149 @@
+#!/usr/bin/env python3
+
+"""
+Whole Flash Image Tag
+
+{
+	u32 crc32;
+	u32 version;
+	u32 chipID;
+	u32 flashType;
+	u32 flags;
+}
+
+CRC32: Ethernet (Poly 0x04C11DB7)
+
+Version:
+	0x00005700: Any version
+	0x00005731: NAND 1MB data partition
+	0x00005732: Normal version
+
+Chip ID:
+	Broadcom Chip ID
+	0x00006328: BCM6328
+	0x00006362: BCM6362
+	0x00006368: BCM6368
+	0x00063268: BCM63268
+
+Flash Type:
+	1: NOR
+	2: NAND 16k blocks
+	3: NAND 128k blocks
+	4: NAND 256k blocks
+	5: NAND 512k blocks
+	6: NAND 1MB blocks
+	7: NAND 2MB blocks
+
+Flags:
+	0x00000001: PMC
+	0x00000002: Secure BootROM
+
+"""
+
+import argparse
+import os
+import struct
+import binascii
+
+
+def auto_int(x):
+    return int(x, 0)
+
+
+def create_tag(args, in_bytes):
+    # JAM CRC32 is bitwise not and unsigned
+    crc = ~binascii.crc32(in_bytes) & 0xFFFFFFFF
+    tag = struct.pack(
+        ">IIIII",
+        crc,
+        args.tag_version,
+        args.chip_id,
+        args.flash_type,
+        args.flags,
+    )
+    return tag
+
+
+def create_output(args):
+    in_st = os.stat(args.input_file)
+    in_size = in_st.st_size
+
+    in_f = open(args.input_file, "r+b")
+    in_bytes = in_f.read(in_size)
+    in_f.close()
+
+    tag = create_tag(args, in_bytes)
+
+    out_f = open(args.output_file, "w+b")
+    out_f.write(in_bytes)
+    out_f.write(tag)
+    out_f.close()
+
+
+def main():
+    global args
+
+    parser = argparse.ArgumentParser(description="")
+
+    parser.add_argument(
+        "--input-file",
+        dest="input_file",
+        action="store",
+        type=str,
+        help="Input file",
+    )
+
+    parser.add_argument(
+        "--output-file",
+        dest="output_file",
+        action="store",
+        type=str,
+        help="Output file",
+    )
+
+    parser.add_argument(
+        "--version",
+        dest="tag_version",
+        action="store",
+        type=auto_int,
+        help="WFI Tag Version",
+    )
+
+    parser.add_argument(
+        "--chip-id",
+        dest="chip_id",
+        action="store",
+        type=auto_int,
+        help="WFI Chip ID",
+    )
+
+    parser.add_argument(
+        "--flash-type",
+        dest="flash_type",
+        action="store",
+        type=auto_int,
+        help="WFI Flash Type",
+    )
+
+    parser.add_argument(
+        "--flags", dest="flags", action="store", type=auto_int, help="WFI Flags"
+    )
+
+    args = parser.parse_args()
+
+    if not args.flags:
+        args.flags = 0
+
+    if (
+        (not args.input_file)
+        or (not args.output_file)
+        or (not args.tag_version)
+        or (not args.chip_id)
+        or (not args.flash_type)
+    ):
+        parser.print_help()
+    else:
+        create_output(args)
+
+
+main()
diff --git a/scripts/check-toolchain-clean.sh b/scripts/check-toolchain-clean.sh
new file mode 100755
index 0000000..455cfb0
--- /dev/null
+++ b/scripts/check-toolchain-clean.sh
@@ -0,0 +1,14 @@
+#!/bin/sh
+eval "$(grep CONFIG_GCC_VERSION .config)"
+CONFIG_TOOLCHAIN_BUILD_VER="$CONFIG_GCC_VERSION-$(cat toolchain/build_version)"
+touch .toolchain_build_ver
+CURRENT_TOOLCHAIN_BUILD_VER="$(cat .toolchain_build_ver)"
+[ -z "$CURRENT_TOOLCHAIN_BUILD_VER" ] && {
+	echo "$CONFIG_TOOLCHAIN_BUILD_VER" > .toolchain_build_ver
+	exit 0
+}
+[ "$CONFIG_TOOLCHAIN_BUILD_VER" = "$CURRENT_TOOLCHAIN_BUILD_VER" ] && exit 0
+echo "Toolchain build version changed ($CONFIG_TOOLCHAIN_BUILD_VER != $CURRENT_TOOLCHAIN_BUILD_VER), running make targetclean"
+make targetclean
+echo "$CONFIG_TOOLCHAIN_BUILD_VER" > .toolchain_build_ver
+exit 0
diff --git a/scripts/checkpatch.pl b/scripts/checkpatch.pl
new file mode 100755
index 0000000..2adc5ef
--- /dev/null
+++ b/scripts/checkpatch.pl
@@ -0,0 +1,6872 @@
+#!/usr/bin/env perl
+# SPDX-License-Identifier: GPL-2.0
+#
+# (c) 2001, Dave Jones. (the file handling bit)
+# (c) 2005, Joel Schopp <jschopp@austin.ibm.com> (the ugly bit)
+# (c) 2007,2008, Andy Whitcroft <apw@uk.ibm.com> (new conditions, test suite)
+# (c) 2008-2010 Andy Whitcroft <apw@canonical.com>
+# (c) 2013 Vasilis Tsiligiannis <acinonyx@openwrt.gr> (adapt for OpenWrt tree)
+# (c) 2010-2018 Joe Perches <joe@perches.com>
+
+use strict;
+use warnings;
+use POSIX;
+use File::Basename;
+use Cwd 'abs_path';
+use Term::ANSIColor qw(:constants);
+use Encode qw(decode encode);
+
+my $P = $0;
+my $D = dirname(abs_path($P));
+
+my $V = '0.32-openwrt';
+
+use Getopt::Long qw(:config no_auto_abbrev);
+
+my $quiet = 0;
+my $tree = 1;
+my $chk_signoff = 1;
+my $chk_patch = 1;
+my $tst_only;
+my $emacs = 0;
+my $terse = 0;
+my $showfile = 0;
+my $file = 0;
+my $git = 0;
+my %git_commits = ();
+my $check = 0;
+my $check_orig = 0;
+my $summary = 1;
+my $mailback = 0;
+my $summary_file = 0;
+my $show_types = 0;
+my $list_types = 0;
+my $fix = 0;
+my $fix_inplace = 0;
+my $root;
+my %debug;
+my %camelcase = ();
+my %use_type = ();
+my @use = ();
+my %ignore_type = ();
+my @ignore = ();
+my $help = 0;
+my $configuration_file = ".checkpatch.conf";
+my $max_line_length = 100;
+my $ignore_perl_version = 0;
+my $minimum_perl_version = 5.10.0;
+my $min_conf_desc_length = 4;
+my $spelling_file = "$D/spelling.txt";
+my $codespell = 0;
+my $codespellfile = "/usr/share/codespell/dictionary.txt";
+my $conststructsfile = "$D/const_structs.checkpatch";
+my $typedefsfile = "";
+my $color = "auto";
+my $allow_c99_comments = 1; # Can be overridden by --ignore C99_COMMENT_TOLERANCE
+# git output parsing needs US English output, so first set backtick child process LANGUAGE
+my $git_command ='export LANGUAGE=en_US.UTF-8; git';
+my $tabsize = 8;
+
+sub help {
+	my ($exitcode) = @_;
+
+	print << "EOM";
+Usage: $P [OPTION]... [FILE]...
+Version: $V
+
+Options:
+  -q, --quiet                quiet
+  --no-tree                  run without a OpenWrt tree
+  --no-signoff               do not check for 'Signed-off-by' line
+  --patch                    treat FILE as patchfile (default)
+  --emacs                    emacs compile window format
+  --terse                    one line per report
+  --showfile                 emit diffed file position, not input file position
+  -g, --git                  treat FILE as a single commit or git revision range
+                             single git commit with:
+                               <rev>
+                               <rev>^
+                               <rev>~n
+                             multiple git commits with:
+                               <rev1>..<rev2>
+                               <rev1>...<rev2>
+                               <rev>-<count>
+                             git merges are ignored
+  -f, --file                 treat FILE as regular source file
+  --subjective, --strict     enable more subjective tests
+  --list-types               list the possible message types
+  --types TYPE(,TYPE2...)    show only these comma separated message types
+  --ignore TYPE(,TYPE2...)   ignore various comma separated message types
+  --show-types               show the specific message type in the output
+  --max-line-length=n        set the maximum line length, (default $max_line_length)
+                             if exceeded, warn on patches
+                             requires --strict for use with --file
+  --min-conf-desc-length=n   set the min description length, if shorter, warn
+  --tab-size=n               set the number of spaces for tab (default $tabsize)
+  --root=PATH                PATH to the OpenWrt tree root
+  --no-summary               suppress the per-file summary
+  --mailback                 only produce a report in case of warnings/errors
+  --summary-file             include the filename in summary
+  --debug KEY=[0|1]          turn on/off debugging of KEY, where KEY is one of
+                             'values', 'possible', 'type', and 'attr' (default
+                             is all off)
+  --test-only=WORD           report only warnings/errors containing WORD
+                             literally
+  --fix                      EXPERIMENTAL - may create horrible results
+                             If correctable single-line errors exist, create
+                             "<inputfile>.EXPERIMENTAL-checkpatch-fixes"
+                             with potential errors corrected to the preferred
+                             checkpatch style
+  --fix-inplace              EXPERIMENTAL - may create horrible results
+                             Is the same as --fix, but overwrites the input
+                             file.  It's your fault if there's no backup or git
+  --ignore-perl-version      override checking of perl version.  expect
+                             runtime errors.
+  --codespell                Use the codespell dictionary for spelling/typos
+                             (default:/usr/share/codespell/dictionary.txt)
+  --codespellfile            Use this codespell dictionary
+  --typedefsfile             Read additional types from this file
+  --color[=WHEN]             Use colors 'always', 'never', or only when output
+                             is a terminal ('auto'). Default is 'auto'.
+  -h, --help, --version      display this help and exit
+
+When FILE is - read standard input.
+EOM
+
+	exit($exitcode);
+}
+
+sub uniq {
+	my %seen;
+	return grep { !$seen{$_}++ } @_;
+}
+
+sub list_types {
+	my ($exitcode) = @_;
+
+	my $count = 0;
+
+	local $/ = undef;
+
+	open(my $script, '<', abs_path($P)) or
+	    die "$P: Can't read '$P' $!\n";
+
+	my $text = <$script>;
+	close($script);
+
+	my @types = ();
+	# Also catch when type or level is passed through a variable
+	for ($text =~ /(?:(?:\bCHK|\bWARN|\bERROR|&\{\$msg_level})\s*\(|\$msg_type\s*=)\s*"([^"]+)"/g) {
+		push (@types, $_);
+	}
+	@types = sort(uniq(@types));
+	print("#\tMessage type\n\n");
+	foreach my $type (@types) {
+		print(++$count . "\t" . $type . "\n");
+	}
+
+	exit($exitcode);
+}
+
+my $conf = which_conf($configuration_file);
+if (-f $conf) {
+	my @conf_args;
+	open(my $conffile, '<', "$conf")
+	    or warn "$P: Can't find a readable $configuration_file file $!\n";
+
+	while (<$conffile>) {
+		my $line = $_;
+
+		$line =~ s/\s*\n?$//g;
+		$line =~ s/^\s*//g;
+		$line =~ s/\s+/ /g;
+
+		next if ($line =~ m/^\s*#/);
+		next if ($line =~ m/^\s*$/);
+
+		my @words = split(" ", $line);
+		foreach my $word (@words) {
+			last if ($word =~ m/^#/);
+			push (@conf_args, $word);
+		}
+	}
+	close($conffile);
+	unshift(@ARGV, @conf_args) if @conf_args;
+}
+
+# Perl's Getopt::Long allows options to take optional arguments after a space.
+# Prevent --color by itself from consuming other arguments
+foreach (@ARGV) {
+	if ($_ eq "--color" || $_ eq "-color") {
+		$_ = "--color=$color";
+	}
+}
+
+GetOptions(
+	'q|quiet+'	=> \$quiet,
+	'tree!'		=> \$tree,
+	'signoff!'	=> \$chk_signoff,
+	'patch!'	=> \$chk_patch,
+	'emacs!'	=> \$emacs,
+	'terse!'	=> \$terse,
+	'showfile!'	=> \$showfile,
+	'f|file!'	=> \$file,
+	'g|git!'	=> \$git,
+	'subjective!'	=> \$check,
+	'strict!'	=> \$check,
+	'ignore=s'	=> \@ignore,
+	'types=s'	=> \@use,
+	'show-types!'	=> \$show_types,
+	'list-types!'	=> \$list_types,
+	'max-line-length=i' => \$max_line_length,
+	'min-conf-desc-length=i' => \$min_conf_desc_length,
+	'tab-size=i'	=> \$tabsize,
+	'root=s'	=> \$root,
+	'summary!'	=> \$summary,
+	'mailback!'	=> \$mailback,
+	'summary-file!'	=> \$summary_file,
+	'fix!'		=> \$fix,
+	'fix-inplace!'	=> \$fix_inplace,
+	'ignore-perl-version!' => \$ignore_perl_version,
+	'debug=s'	=> \%debug,
+	'test-only=s'	=> \$tst_only,
+	'codespell!'	=> \$codespell,
+	'codespellfile=s'	=> \$codespellfile,
+	'typedefsfile=s'	=> \$typedefsfile,
+	'color=s'	=> \$color,
+	'no-color'	=> \$color,	#keep old behaviors of -nocolor
+	'nocolor'	=> \$color,	#keep old behaviors of -nocolor
+	'h|help'	=> \$help,
+	'version'	=> \$help
+) or help(1);
+
+help(0) if ($help);
+
+list_types(0) if ($list_types);
+
+$fix = 1 if ($fix_inplace);
+$check_orig = $check;
+
+die "$P: --git cannot be used with --file or --fix\n" if ($git && ($file || $fix));
+
+my $exit = 0;
+
+my $perl_version_ok = 1;
+if ($^V && $^V lt $minimum_perl_version) {
+	$perl_version_ok = 0;
+	printf "$P: requires at least perl version %vd\n", $minimum_perl_version;
+	exit(1) if (!$ignore_perl_version);
+}
+
+#if no filenames are given, push '-' to read patch from stdin
+if ($#ARGV < 0) {
+	push(@ARGV, '-');
+}
+
+if ($color =~ /^[01]$/) {
+	$color = !$color;
+} elsif ($color =~ /^always$/i) {
+	$color = 1;
+} elsif ($color =~ /^never$/i) {
+	$color = 0;
+} elsif ($color =~ /^auto$/i) {
+	$color = (-t STDOUT);
+} else {
+	die "$P: Invalid color mode: $color\n";
+}
+
+# skip TAB size 1 to avoid additional checks on $tabsize - 1
+die "$P: Invalid TAB size: $tabsize\n" if ($tabsize < 2);
+
+sub hash_save_array_words {
+	my ($hashRef, $arrayRef) = @_;
+
+	my @array = split(/,/, join(',', @$arrayRef));
+	foreach my $word (@array) {
+		$word =~ s/\s*\n?$//g;
+		$word =~ s/^\s*//g;
+		$word =~ s/\s+/ /g;
+		$word =~ tr/[a-z]/[A-Z]/;
+
+		next if ($word =~ m/^\s*#/);
+		next if ($word =~ m/^\s*$/);
+
+		$hashRef->{$word}++;
+	}
+}
+
+sub hash_show_words {
+	my ($hashRef, $prefix) = @_;
+
+	if (keys %$hashRef) {
+		print "\nNOTE: $prefix message types:";
+		foreach my $word (sort keys %$hashRef) {
+			print " $word";
+		}
+		print "\n";
+	}
+}
+
+hash_save_array_words(\%ignore_type, \@ignore);
+hash_save_array_words(\%use_type, \@use);
+
+my $dbg_values = 0;
+my $dbg_possible = 0;
+my $dbg_type = 0;
+my $dbg_attr = 0;
+for my $key (keys %debug) {
+	## no critic
+	eval "\${dbg_$key} = '$debug{$key}';";
+	die "$@" if ($@);
+}
+
+my $rpt_cleaners = 0;
+
+if ($terse) {
+	$emacs = 1;
+	$quiet++;
+}
+
+if ($tree) {
+	if (defined $root) {
+		if (!top_of_openwrt_tree($root)) {
+			die "$P: $root: --root does not point at a valid tree\n";
+		}
+	} else {
+		if (top_of_openwrt_tree('.')) {
+			$root = '.';
+		} elsif ($0 =~ m@(.*)/scripts/[^/]*$@ &&
+						top_of_openwrt_tree($1)) {
+			$root = $1;
+		}
+	}
+
+	if (!defined $root) {
+		print "Must be run from the top-level dir. of a OpenWrt tree\n";
+		exit(2);
+	}
+}
+
+my $emitted_corrupt = 0;
+
+our $Ident	= qr{
+			[A-Za-z_][A-Za-z\d_]*
+			(?:\s*\#\#\s*[A-Za-z_][A-Za-z\d_]*)*
+		}x;
+our $Storage	= qr{extern|static|asmlinkage};
+our $Sparse	= qr{
+			__user|
+			__kernel|
+			__force|
+			__iomem|
+			__must_check|
+			__kprobes|
+			__ref|
+			__refconst|
+			__refdata|
+			__rcu|
+			__private
+		}x;
+our $InitAttributePrefix = qr{__(?:mem|cpu|dev|net_|)};
+our $InitAttributeData = qr{$InitAttributePrefix(?:initdata\b)};
+our $InitAttributeConst = qr{$InitAttributePrefix(?:initconst\b)};
+our $InitAttributeInit = qr{$InitAttributePrefix(?:init\b)};
+our $InitAttribute = qr{$InitAttributeData|$InitAttributeConst|$InitAttributeInit};
+
+# Notes to $Attribute:
+# We need \b after 'init' otherwise 'initconst' will cause a false positive in a check
+our $Attribute	= qr{
+			const|
+			__percpu|
+			__nocast|
+			__safe|
+			__bitwise|
+			__packed__|
+			__packed2__|
+			__naked|
+			__maybe_unused|
+			__always_unused|
+			__noreturn|
+			__used|
+			__cold|
+			__pure|
+			__noclone|
+			__deprecated|
+			__read_mostly|
+			__ro_after_init|
+			__kprobes|
+			$InitAttribute|
+			____cacheline_aligned|
+			____cacheline_aligned_in_smp|
+			____cacheline_internodealigned_in_smp|
+			__weak
+		  }x;
+our $Modifier;
+our $Inline	= qr{inline|__always_inline|noinline|__inline|__inline__};
+our $Member	= qr{->$Ident|\.$Ident|\[[^]]*\]};
+our $Lval	= qr{$Ident(?:$Member)*};
+
+our $Int_type	= qr{(?i)llu|ull|ll|lu|ul|l|u};
+our $Binary	= qr{(?i)0b[01]+$Int_type?};
+our $Hex	= qr{(?i)0x[0-9a-f]+$Int_type?};
+our $Int	= qr{[0-9]+$Int_type?};
+our $Octal	= qr{0[0-7]+$Int_type?};
+our $String	= qr{"[X\t]*"};
+our $Float_hex	= qr{(?i)0x[0-9a-f]+p-?[0-9]+[fl]?};
+our $Float_dec	= qr{(?i)(?:[0-9]+\.[0-9]*|[0-9]*\.[0-9]+)(?:e-?[0-9]+)?[fl]?};
+our $Float_int	= qr{(?i)[0-9]+e-?[0-9]+[fl]?};
+our $Float	= qr{$Float_hex|$Float_dec|$Float_int};
+our $Constant	= qr{$Float|$Binary|$Octal|$Hex|$Int};
+our $Assignment	= qr{\*\=|/=|%=|\+=|-=|<<=|>>=|&=|\^=|\|=|=};
+our $Compare    = qr{<=|>=|==|!=|<|(?<!-)>};
+our $Arithmetic = qr{\+|-|\*|\/|%};
+our $Operators	= qr{
+			<=|>=|==|!=|
+			=>|->|<<|>>|<|>|!|~|
+			&&|\|\||,|\^|\+\+|--|&|\||$Arithmetic
+		  }x;
+
+our $c90_Keywords = qr{do|for|while|if|else|return|goto|continue|switch|default|case|break}x;
+
+our $BasicType;
+our $NonptrType;
+our $NonptrTypeMisordered;
+our $NonptrTypeWithAttr;
+our $Type;
+our $TypeMisordered;
+our $Declare;
+our $DeclareMisordered;
+
+our $NON_ASCII_UTF8	= qr{
+	[\xC2-\xDF][\x80-\xBF]               # non-overlong 2-byte
+	|  \xE0[\xA0-\xBF][\x80-\xBF]        # excluding overlongs
+	| [\xE1-\xEC\xEE\xEF][\x80-\xBF]{2}  # straight 3-byte
+	|  \xED[\x80-\x9F][\x80-\xBF]        # excluding surrogates
+	|  \xF0[\x90-\xBF][\x80-\xBF]{2}     # planes 1-3
+	| [\xF1-\xF3][\x80-\xBF]{3}          # planes 4-15
+	|  \xF4[\x80-\x8F][\x80-\xBF]{2}     # plane 16
+}x;
+
+our $UTF8	= qr{
+	[\x09\x0A\x0D\x20-\x7E]              # ASCII
+	| $NON_ASCII_UTF8
+}x;
+
+our $typeC99Typedefs = qr{(?:__)?(?:[us]_?)?int_?(?:8|16|32|64)_t};
+our $typeOtherOSTypedefs = qr{(?x:
+	u_(?:char|short|int|long) |          # bsd
+	u(?:nchar|short|int|long)            # sysv
+)};
+our $typeKernelTypedefs = qr{(?x:
+	(?:__)?(?:u|s|be|le)(?:8|16|32|64)|
+	atomic_t
+)};
+our $typeTypedefs = qr{(?x:
+	$typeC99Typedefs\b|
+	$typeOtherOSTypedefs\b|
+	$typeKernelTypedefs\b
+)};
+
+our $zero_initializer = qr{(?:(?:0[xX])?0+$Int_type?|NULL|false)\b};
+
+our $logFunctions = qr{(?x:
+	printk(?:_ratelimited|_once|_deferred_once|_deferred|)|
+	(?:[a-z0-9]+_){1,2}(?:printk|emerg|alert|crit|err|warning|warn|notice|info|debug|dbg|vdbg|devel|cont|WARN)(?:_ratelimited|_once|)|
+	TP_printk|
+	WARN(?:_RATELIMIT|_ONCE|)|
+	panic|
+	MODULE_[A-Z_]+|
+	seq_vprintf|seq_printf|seq_puts
+)};
+
+our $allocFunctions = qr{(?x:
+	(?:(?:devm_)?
+		(?:kv|k|v)[czm]alloc(?:_node|_array)? |
+		kstrdup(?:_const)? |
+		kmemdup(?:_nul)?) |
+	(?:\w+)?alloc_skb(?:_ip_align)? |
+				# dev_alloc_skb/netdev_alloc_skb, et al
+	dma_alloc_coherent
+)};
+
+our $signature_tags = qr{(?xi:
+	Signed-off-by:|
+	Co-developed-by:|
+	Acked-by:|
+	Tested-by:|
+	Reviewed-by:|
+	Reported-by:|
+	Suggested-by:|
+	To:|
+	Cc:
+)};
+
+our @typeListMisordered = (
+	qr{char\s+(?:un)?signed},
+	qr{int\s+(?:(?:un)?signed\s+)?short\s},
+	qr{int\s+short(?:\s+(?:un)?signed)},
+	qr{short\s+int(?:\s+(?:un)?signed)},
+	qr{(?:un)?signed\s+int\s+short},
+	qr{short\s+(?:un)?signed},
+	qr{long\s+int\s+(?:un)?signed},
+	qr{int\s+long\s+(?:un)?signed},
+	qr{long\s+(?:un)?signed\s+int},
+	qr{int\s+(?:un)?signed\s+long},
+	qr{int\s+(?:un)?signed},
+	qr{int\s+long\s+long\s+(?:un)?signed},
+	qr{long\s+long\s+int\s+(?:un)?signed},
+	qr{long\s+long\s+(?:un)?signed\s+int},
+	qr{long\s+long\s+(?:un)?signed},
+	qr{long\s+(?:un)?signed},
+);
+
+our @typeList = (
+	qr{void},
+	qr{(?:(?:un)?signed\s+)?char},
+	qr{(?:(?:un)?signed\s+)?short\s+int},
+	qr{(?:(?:un)?signed\s+)?short},
+	qr{(?:(?:un)?signed\s+)?int},
+	qr{(?:(?:un)?signed\s+)?long\s+int},
+	qr{(?:(?:un)?signed\s+)?long\s+long\s+int},
+	qr{(?:(?:un)?signed\s+)?long\s+long},
+	qr{(?:(?:un)?signed\s+)?long},
+	qr{(?:un)?signed},
+	qr{float},
+	qr{double},
+	qr{bool},
+	qr{struct\s+$Ident},
+	qr{union\s+$Ident},
+	qr{enum\s+$Ident},
+	qr{${Ident}_t},
+	qr{${Ident}_handler},
+	qr{${Ident}_handler_fn},
+	@typeListMisordered,
+);
+
+our $C90_int_types = qr{(?x:
+	long\s+long\s+int\s+(?:un)?signed|
+	long\s+long\s+(?:un)?signed\s+int|
+	long\s+long\s+(?:un)?signed|
+	(?:(?:un)?signed\s+)?long\s+long\s+int|
+	(?:(?:un)?signed\s+)?long\s+long|
+	int\s+long\s+long\s+(?:un)?signed|
+	int\s+(?:(?:un)?signed\s+)?long\s+long|
+
+	long\s+int\s+(?:un)?signed|
+	long\s+(?:un)?signed\s+int|
+	long\s+(?:un)?signed|
+	(?:(?:un)?signed\s+)?long\s+int|
+	(?:(?:un)?signed\s+)?long|
+	int\s+long\s+(?:un)?signed|
+	int\s+(?:(?:un)?signed\s+)?long|
+
+	int\s+(?:un)?signed|
+	(?:(?:un)?signed\s+)?int
+)};
+
+our @typeListFile = ();
+our @typeListWithAttr = (
+	@typeList,
+	qr{struct\s+$InitAttribute\s+$Ident},
+	qr{union\s+$InitAttribute\s+$Ident},
+);
+
+our @modifierList = (
+	qr{fastcall},
+);
+our @modifierListFile = ();
+
+our @mode_permission_funcs = (
+	["module_param", 3],
+	["module_param_(?:array|named|string)", 4],
+	["module_param_array_named", 5],
+	["debugfs_create_(?:file|u8|u16|u32|u64|x8|x16|x32|x64|size_t|atomic_t|bool|blob|regset32|u32_array)", 2],
+	["proc_create(?:_data|)", 2],
+	["(?:CLASS|DEVICE|SENSOR|SENSOR_DEVICE|IIO_DEVICE)_ATTR", 2],
+	["IIO_DEV_ATTR_[A-Z_]+", 1],
+	["SENSOR_(?:DEVICE_|)ATTR_2", 2],
+	["SENSOR_TEMPLATE(?:_2|)", 3],
+	["__ATTR", 2],
+);
+
+#Create a search pattern for all these functions to speed up a loop below
+our $mode_perms_search = "";
+foreach my $entry (@mode_permission_funcs) {
+	$mode_perms_search .= '|' if ($mode_perms_search ne "");
+	$mode_perms_search .= $entry->[0];
+}
+$mode_perms_search = "(?:${mode_perms_search})";
+
+our %deprecated_apis = (
+	"synchronize_rcu_bh"			=> "synchronize_rcu",
+	"synchronize_rcu_bh_expedited"		=> "synchronize_rcu_expedited",
+	"call_rcu_bh"				=> "call_rcu",
+	"rcu_barrier_bh"			=> "rcu_barrier",
+	"synchronize_sched"			=> "synchronize_rcu",
+	"synchronize_sched_expedited"		=> "synchronize_rcu_expedited",
+	"call_rcu_sched"			=> "call_rcu",
+	"rcu_barrier_sched"			=> "rcu_barrier",
+	"get_state_synchronize_sched"		=> "get_state_synchronize_rcu",
+	"cond_synchronize_sched"		=> "cond_synchronize_rcu",
+);
+
+#Create a search pattern for all these strings to speed up a loop below
+our $deprecated_apis_search = "";
+foreach my $entry (keys %deprecated_apis) {
+	$deprecated_apis_search .= '|' if ($deprecated_apis_search ne "");
+	$deprecated_apis_search .= $entry;
+}
+$deprecated_apis_search = "(?:${deprecated_apis_search})";
+
+our $mode_perms_world_writable = qr{
+	S_IWUGO		|
+	S_IWOTH		|
+	S_IRWXUGO	|
+	S_IALLUGO	|
+	0[0-7][0-7][2367]
+}x;
+
+our %mode_permission_string_types = (
+	"S_IRWXU" => 0700,
+	"S_IRUSR" => 0400,
+	"S_IWUSR" => 0200,
+	"S_IXUSR" => 0100,
+	"S_IRWXG" => 0070,
+	"S_IRGRP" => 0040,
+	"S_IWGRP" => 0020,
+	"S_IXGRP" => 0010,
+	"S_IRWXO" => 0007,
+	"S_IROTH" => 0004,
+	"S_IWOTH" => 0002,
+	"S_IXOTH" => 0001,
+	"S_IRWXUGO" => 0777,
+	"S_IRUGO" => 0444,
+	"S_IWUGO" => 0222,
+	"S_IXUGO" => 0111,
+);
+
+#Create a search pattern for all these strings to speed up a loop below
+our $mode_perms_string_search = "";
+foreach my $entry (keys %mode_permission_string_types) {
+	$mode_perms_string_search .= '|' if ($mode_perms_string_search ne "");
+	$mode_perms_string_search .= $entry;
+}
+our $single_mode_perms_string_search = "(?:${mode_perms_string_search})";
+our $multi_mode_perms_string_search = qr{
+	${single_mode_perms_string_search}
+	(?:\s*\|\s*${single_mode_perms_string_search})*
+}x;
+
+sub perms_to_octal {
+	my ($string) = @_;
+
+	return trim($string) if ($string =~ /^\s*0[0-7]{3,3}\s*$/);
+
+	my $val = "";
+	my $oval = "";
+	my $to = 0;
+	my $curpos = 0;
+	my $lastpos = 0;
+	while ($string =~ /\b(($single_mode_perms_string_search)\b(?:\s*\|\s*)?\s*)/g) {
+		$curpos = pos($string);
+		my $match = $2;
+		my $omatch = $1;
+		last if ($lastpos > 0 && ($curpos - length($omatch) != $lastpos));
+		$lastpos = $curpos;
+		$to |= $mode_permission_string_types{$match};
+		$val .= '\s*\|\s*' if ($val ne "");
+		$val .= $match;
+		$oval .= $omatch;
+	}
+	$oval =~ s/^\s*\|\s*//;
+	$oval =~ s/\s*\|\s*$//;
+	return sprintf("%04o", $to);
+}
+
+our $allowed_asm_includes = qr{(?x:
+	irq|
+	memory|
+	time|
+	reboot
+)};
+# memory.h: ARM has a custom one
+
+# Load common spelling mistakes and build regular expression list.
+my $misspellings;
+my %spelling_fix;
+
+if (open(my $spelling, '<', $spelling_file)) {
+	while (<$spelling>) {
+		my $line = $_;
+
+		$line =~ s/\s*\n?$//g;
+		$line =~ s/^\s*//g;
+
+		next if ($line =~ m/^\s*#/);
+		next if ($line =~ m/^\s*$/);
+
+		my ($suspect, $fix) = split(/\|\|/, $line);
+
+		$spelling_fix{$suspect} = $fix;
+	}
+	close($spelling);
+} else {
+	warn "No typos will be found - file '$spelling_file': $!\n";
+}
+
+if ($codespell) {
+	if (open(my $spelling, '<', $codespellfile)) {
+		while (<$spelling>) {
+			my $line = $_;
+
+			$line =~ s/\s*\n?$//g;
+			$line =~ s/^\s*//g;
+
+			next if ($line =~ m/^\s*#/);
+			next if ($line =~ m/^\s*$/);
+			next if ($line =~ m/, disabled/i);
+
+			$line =~ s/,.*$//;
+
+			my ($suspect, $fix) = split(/->/, $line);
+
+			$spelling_fix{$suspect} = $fix;
+		}
+		close($spelling);
+	} else {
+		warn "No codespell typos will be found - file '$codespellfile': $!\n";
+	}
+}
+
+$misspellings = join("|", sort keys %spelling_fix) if keys %spelling_fix;
+
+sub read_words {
+	my ($wordsRef, $file) = @_;
+
+	if (open(my $words, '<', $file)) {
+		while (<$words>) {
+			my $line = $_;
+
+			$line =~ s/\s*\n?$//g;
+			$line =~ s/^\s*//g;
+
+			next if ($line =~ m/^\s*#/);
+			next if ($line =~ m/^\s*$/);
+			if ($line =~ /\s/) {
+				print("$file: '$line' invalid - ignored\n");
+				next;
+			}
+
+			$$wordsRef .= '|' if ($$wordsRef ne "");
+			$$wordsRef .= $line;
+		}
+		close($file);
+		return 1;
+	}
+
+	return 0;
+}
+
+my $const_structs = "";
+read_words(\$const_structs, $conststructsfile)
+    or warn "No structs that should be const will be found - file '$conststructsfile': $!\n";
+
+my $typeOtherTypedefs = "";
+if (length($typedefsfile)) {
+	read_words(\$typeOtherTypedefs, $typedefsfile)
+	    or warn "No additional types will be considered - file '$typedefsfile': $!\n";
+}
+$typeTypedefs .= '|' . $typeOtherTypedefs if ($typeOtherTypedefs ne "");
+
+sub build_types {
+	my $mods = "(?x:  \n" . join("|\n  ", (@modifierList, @modifierListFile)) . "\n)";
+	my $all = "(?x:  \n" . join("|\n  ", (@typeList, @typeListFile)) . "\n)";
+	my $Misordered = "(?x:  \n" . join("|\n  ", @typeListMisordered) . "\n)";
+	my $allWithAttr = "(?x:  \n" . join("|\n  ", @typeListWithAttr) . "\n)";
+	$Modifier	= qr{(?:$Attribute|$Sparse|$mods)};
+	$BasicType	= qr{
+				(?:$typeTypedefs\b)|
+				(?:${all}\b)
+		}x;
+	$NonptrType	= qr{
+			(?:$Modifier\s+|const\s+)*
+			(?:
+				(?:typeof|__typeof__)\s*\([^\)]*\)|
+				(?:$typeTypedefs\b)|
+				(?:${all}\b)
+			)
+			(?:\s+$Modifier|\s+const)*
+		  }x;
+	$NonptrTypeMisordered	= qr{
+			(?:$Modifier\s+|const\s+)*
+			(?:
+				(?:${Misordered}\b)
+			)
+			(?:\s+$Modifier|\s+const)*
+		  }x;
+	$NonptrTypeWithAttr	= qr{
+			(?:$Modifier\s+|const\s+)*
+			(?:
+				(?:typeof|__typeof__)\s*\([^\)]*\)|
+				(?:$typeTypedefs\b)|
+				(?:${allWithAttr}\b)
+			)
+			(?:\s+$Modifier|\s+const)*
+		  }x;
+	$Type	= qr{
+			$NonptrType
+			(?:(?:\s|\*|\[\])+\s*const|(?:\s|\*\s*(?:const\s*)?|\[\])+|(?:\s*\[\s*\])+){0,4}
+			(?:\s+$Inline|\s+$Modifier)*
+		  }x;
+	$TypeMisordered	= qr{
+			$NonptrTypeMisordered
+			(?:(?:\s|\*|\[\])+\s*const|(?:\s|\*\s*(?:const\s*)?|\[\])+|(?:\s*\[\s*\])+){0,4}
+			(?:\s+$Inline|\s+$Modifier)*
+		  }x;
+	$Declare	= qr{(?:$Storage\s+(?:$Inline\s+)?)?$Type};
+	$DeclareMisordered	= qr{(?:$Storage\s+(?:$Inline\s+)?)?$TypeMisordered};
+}
+build_types();
+
+our $Typecast	= qr{\s*(\(\s*$NonptrType\s*\)){0,1}\s*};
+
+# Using $balanced_parens, $LvalOrFunc, or $FuncArg
+# requires at least perl version v5.10.0
+# Any use must be runtime checked with $^V
+
+our $balanced_parens = qr/(\((?:[^\(\)]++|(?-1))*\))/;
+our $LvalOrFunc	= qr{((?:[\&\*]\s*)?$Lval)\s*($balanced_parens{0,1})\s*};
+our $FuncArg = qr{$Typecast{0,1}($LvalOrFunc|$Constant|$String)};
+
+our $declaration_macros = qr{(?x:
+	(?:$Storage\s+)?(?:[A-Z_][A-Z0-9]*_){0,2}(?:DEFINE|DECLARE)(?:_[A-Z0-9]+){1,6}\s*\(|
+	(?:$Storage\s+)?[HLP]?LIST_HEAD\s*\(|
+	(?:$Storage\s+)?${Type}\s+uninitialized_var\s*\(|
+	(?:SKCIPHER_REQUEST|SHASH_DESC|AHASH_REQUEST)_ON_STACK\s*\(
+)};
+
+sub deparenthesize {
+	my ($string) = @_;
+	return "" if (!defined($string));
+
+	while ($string =~ /^\s*\(.*\)\s*$/) {
+		$string =~ s@^\s*\(\s*@@;
+		$string =~ s@\s*\)\s*$@@;
+	}
+
+	$string =~ s@\s+@ @g;
+
+	return $string;
+}
+
+sub seed_camelcase_file {
+	my ($file) = @_;
+
+	return if (!(-f $file));
+
+	local $/;
+
+	open(my $include_file, '<', "$file")
+	    or warn "$P: Can't read '$file' $!\n";
+	my $text = <$include_file>;
+	close($include_file);
+
+	my @lines = split('\n', $text);
+
+	foreach my $line (@lines) {
+		next if ($line !~ /(?:[A-Z][a-z]|[a-z][A-Z])/);
+		if ($line =~ /^[ \t]*(?:#[ \t]*define|typedef\s+$Type)\s+(\w*(?:[A-Z][a-z]|[a-z][A-Z])\w*)/) {
+			$camelcase{$1} = 1;
+		} elsif ($line =~ /^\s*$Declare\s+(\w*(?:[A-Z][a-z]|[a-z][A-Z])\w*)\s*[\(\[,;]/) {
+			$camelcase{$1} = 1;
+		} elsif ($line =~ /^\s*(?:union|struct|enum)\s+(\w*(?:[A-Z][a-z]|[a-z][A-Z])\w*)\s*[;\{]/) {
+			$camelcase{$1} = 1;
+		}
+	}
+}
+
+our %maintained_status = ();
+
+sub is_maintained_obsolete {
+	my ($filename) = @_;
+
+	return 0 if (!$tree || !(-e "$root/scripts/get_maintainer.pl"));
+
+	if (!exists($maintained_status{$filename})) {
+		$maintained_status{$filename} = `perl $root/scripts/get_maintainer.pl --status --nom --nol --nogit --nogit-fallback -f $filename 2>&1`;
+	}
+
+	return $maintained_status{$filename} =~ /obsolete/i;
+}
+
+sub is_SPDX_License_valid {
+	my ($license) = @_;
+
+	return 1 if (!$tree || which("python") eq "" || !(-e "$root/scripts/spdxcheck.py") || !(-e "$root/.git"));
+
+	my $root_path = abs_path($root);
+	my $status = `cd "$root_path"; echo "$license" | python scripts/spdxcheck.py -`;
+	return 0 if ($status ne "");
+	return 1;
+}
+
+my $camelcase_seeded = 0;
+sub seed_camelcase_includes {
+	return if ($camelcase_seeded);
+
+	my $files;
+	my $camelcase_cache = "";
+	my @include_files = ();
+
+	$camelcase_seeded = 1;
+
+	if (-e ".git") {
+		my $git_last_include_commit = `${git_command} log --no-merges --pretty=format:"%h%n" -1 -- include`;
+		chomp $git_last_include_commit;
+		$camelcase_cache = ".checkpatch-camelcase.git.$git_last_include_commit";
+	} else {
+		my $last_mod_date = 0;
+		$files = `find $root/include -name "*.h"`;
+		@include_files = split('\n', $files);
+		foreach my $file (@include_files) {
+			my $date = POSIX::strftime("%Y%m%d%H%M",
+						   localtime((stat $file)[9]));
+			$last_mod_date = $date if ($last_mod_date < $date);
+		}
+		$camelcase_cache = ".checkpatch-camelcase.date.$last_mod_date";
+	}
+
+	if ($camelcase_cache ne "" && -f $camelcase_cache) {
+		open(my $camelcase_file, '<', "$camelcase_cache")
+		    or warn "$P: Can't read '$camelcase_cache' $!\n";
+		while (<$camelcase_file>) {
+			chomp;
+			$camelcase{$_} = 1;
+		}
+		close($camelcase_file);
+
+		return;
+	}
+
+	if (-e ".git") {
+		$files = `${git_command} ls-files "include/*.h"`;
+		@include_files = split('\n', $files);
+	}
+
+	foreach my $file (@include_files) {
+		seed_camelcase_file($file);
+	}
+
+	if ($camelcase_cache ne "") {
+		unlink glob ".checkpatch-camelcase.*";
+		open(my $camelcase_file, '>', "$camelcase_cache")
+		    or warn "$P: Can't write '$camelcase_cache' $!\n";
+		foreach (sort { lc($a) cmp lc($b) } keys(%camelcase)) {
+			print $camelcase_file ("$_\n");
+		}
+		close($camelcase_file);
+	}
+}
+
+sub git_commit_info {
+	my ($commit, $id, $desc) = @_;
+
+	return ($id, $desc) if ((which("git") eq "") || !(-e ".git"));
+
+	my $output = `${git_command} log --no-color --format='%H %s' -1 $commit 2>&1`;
+	$output =~ s/^\s*//gm;
+	my @lines = split("\n", $output);
+
+	return ($id, $desc) if ($#lines < 0);
+
+	if ($lines[0] =~ /^error: short SHA1 $commit is ambiguous/) {
+# Maybe one day convert this block of bash into something that returns
+# all matching commit ids, but it's very slow...
+#
+#		echo "checking commits $1..."
+#		git rev-list --remotes | grep -i "^$1" |
+#		while read line ; do
+#		    git log --format='%H %s' -1 $line |
+#		    echo "commit $(cut -c 1-12,41-)"
+#		done
+	} elsif ($lines[0] =~ /^fatal: ambiguous argument '$commit': unknown revision or path not in the working tree\./) {
+		$id = undef;
+	} else {
+		$id = substr($lines[0], 0, 12);
+		$desc = substr($lines[0], 41);
+	}
+
+	return ($id, $desc);
+}
+
+$chk_signoff = 0 if ($file);
+
+my @rawlines = ();
+my @lines = ();
+my @fixed = ();
+my @fixed_inserted = ();
+my @fixed_deleted = ();
+my $fixlinenr = -1;
+
+# If input is git commits, extract all commits from the commit expressions.
+# For example, HEAD-3 means we need check 'HEAD, HEAD~1, HEAD~2'.
+die "$P: No git repository found\n" if ($git && !-e ".git");
+
+if ($git) {
+	my @commits = ();
+	foreach my $commit_expr (@ARGV) {
+		my $git_range;
+		if ($commit_expr =~ m/^(.*)-(\d+)$/) {
+			$git_range = "-$2 $1";
+		} elsif ($commit_expr =~ m/\.\./) {
+			$git_range = "$commit_expr";
+		} else {
+			$git_range = "-1 $commit_expr";
+		}
+		my $lines = `${git_command} log --no-color --no-merges --pretty=format:'%H %s' $git_range`;
+		foreach my $line (split(/\n/, $lines)) {
+			$line =~ /^([0-9a-fA-F]{40,40}) (.*)$/;
+			next if (!defined($1) || !defined($2));
+			my $sha1 = $1;
+			my $subject = $2;
+			unshift(@commits, $sha1);
+			$git_commits{$sha1} = $subject;
+		}
+	}
+	die "$P: no git commits after extraction!\n" if (@commits == 0);
+	@ARGV = @commits;
+}
+
+my $vname;
+$allow_c99_comments = !defined $ignore_type{"C99_COMMENT_TOLERANCE"};
+for my $filename (@ARGV) {
+	my $FILE;
+	if ($git) {
+		open($FILE, '-|', "git format-patch -M --stdout -1 $filename") ||
+			die "$P: $filename: git format-patch failed - $!\n";
+	} elsif ($file) {
+		open($FILE, '-|', "diff -u /dev/null $filename") ||
+			die "$P: $filename: diff failed - $!\n";
+	} elsif ($filename eq '-') {
+		open($FILE, '<&STDIN');
+	} else {
+		open($FILE, '<', "$filename") ||
+			die "$P: $filename: open failed - $!\n";
+	}
+	if ($filename eq '-') {
+		$vname = 'Your patch';
+	} elsif ($git) {
+		$vname = "Commit " . substr($filename, 0, 12) . ' ("' . $git_commits{$filename} . '")';
+	} else {
+		$vname = $filename;
+	}
+	while (<$FILE>) {
+		chomp;
+		push(@rawlines, $_);
+		$vname = qq("$1") if ($filename eq '-' && $_ =~ m/^Subject:\s+(.+)/i);
+	}
+	close($FILE);
+
+	if ($#ARGV > 0 && $quiet == 0) {
+		print '-' x length($vname) . "\n";
+		print "$vname\n";
+		print '-' x length($vname) . "\n";
+	}
+
+	if (!process($filename)) {
+		$exit = 1;
+	}
+	@rawlines = ();
+	@lines = ();
+	@fixed = ();
+	@fixed_inserted = ();
+	@fixed_deleted = ();
+	$fixlinenr = -1;
+	@modifierListFile = ();
+	@typeListFile = ();
+	build_types();
+}
+
+if (!$quiet) {
+	hash_show_words(\%use_type, "Used");
+	hash_show_words(\%ignore_type, "Ignored");
+
+	if (!$perl_version_ok) {
+		print << "EOM"
+
+NOTE: perl $^V is not modern enough to detect all possible issues.
+      An upgrade to at least perl $minimum_perl_version is suggested.
+EOM
+	}
+	if ($exit) {
+		print << "EOM"
+
+NOTE: If any of the errors are false positives, please report
+      them to the maintainer, see CHECKPATCH in MAINTAINERS.
+EOM
+	}
+}
+
+exit($exit);
+
+sub top_of_openwrt_tree {
+	my ($root) = @_;
+
+	my @tree_check = (
+		"BSDmakefile", "Config.in", "LICENSES", "Makefile", "README.md",
+		"feeds.conf.default", "include", "package", "rules.mk",
+		"scripts", "target", "toolchain", "tools"
+	);
+
+	foreach my $check (@tree_check) {
+		if (! -e $root . '/' . $check) {
+			return 0;
+		}
+	}
+	return 1;
+}
+
+sub parse_email {
+	my ($formatted_email) = @_;
+
+	my $name = "";
+	my $name_comment = "";
+	my $address = "";
+	my $comment = "";
+
+	if ($formatted_email =~ /^(.*)<(\S+\@\S+)>(.*)$/) {
+		$name = $1;
+		$address = $2;
+		$comment = $3 if defined $3;
+	} elsif ($formatted_email =~ /^\s*<(\S+\@\S+)>(.*)$/) {
+		$address = $1;
+		$comment = $2 if defined $2;
+	} elsif ($formatted_email =~ /(\S+\@\S+)(.*)$/) {
+		$address = $1;
+		$comment = $2 if defined $2;
+		$formatted_email =~ s/\Q$address\E.*$//;
+		$name = $formatted_email;
+		$name = trim($name);
+		$name =~ s/^\"|\"$//g;
+		# If there's a name left after stripping spaces and
+		# leading quotes, and the address doesn't have both
+		# leading and trailing angle brackets, the address
+		# is invalid. ie:
+		#   "joe smith joe@smith.com" bad
+		#   "joe smith <joe@smith.com" bad
+		if ($name ne "" && $address !~ /^<[^>]+>$/) {
+			$name = "";
+			$address = "";
+			$comment = "";
+		}
+	}
+
+	$name = trim($name);
+	$name =~ s/^\"|\"$//g;
+	$name =~ s/(\s*\([^\)]+\))\s*//;
+	if (defined($1)) {
+		$name_comment = trim($1);
+	}
+	$address = trim($address);
+	$address =~ s/^\<|\>$//g;
+
+	if ($name =~ /[^\w \-]/i) { ##has "must quote" chars
+		$name =~ s/(?<!\\)"/\\"/g; ##escape quotes
+		$name = "\"$name\"";
+	}
+
+	return ($name, $name_comment, $address, $comment);
+}
+
+sub format_email {
+	my ($name, $address) = @_;
+
+	my $formatted_email;
+
+	$name = trim($name);
+	$name =~ s/^\"|\"$//g;
+	$address = trim($address);
+
+	if ($name =~ /[^\w \-]/i) { ##has "must quote" chars
+		$name =~ s/(?<!\\)"/\\"/g; ##escape quotes
+		$name = "\"$name\"";
+	}
+
+	if ("$name" eq "") {
+		$formatted_email = "$address";
+	} else {
+		$formatted_email = "$name <$address>";
+	}
+
+	return $formatted_email;
+}
+
+sub reformat_email {
+	my ($email) = @_;
+
+	my ($email_name, $name_comment, $email_address, $comment) = parse_email($email);
+	return format_email($email_name, $email_address);
+}
+
+sub same_email_addresses {
+	my ($email1, $email2) = @_;
+
+	my ($email1_name, $name1_comment, $email1_address, $comment1) = parse_email($email1);
+	my ($email2_name, $name2_comment, $email2_address, $comment2) = parse_email($email2);
+
+	return $email1_name eq $email2_name &&
+	       $email1_address eq $email2_address;
+}
+
+sub which {
+	my ($bin) = @_;
+
+	foreach my $path (split(/:/, $ENV{PATH})) {
+		if (-e "$path/$bin") {
+			return "$path/$bin";
+		}
+	}
+
+	return "";
+}
+
+sub which_conf {
+	my ($conf) = @_;
+
+	foreach my $path (split(/:/, ".:$ENV{HOME}:.scripts")) {
+		if (-e "$path/$conf") {
+			return "$path/$conf";
+		}
+	}
+
+	return "";
+}
+
+sub expand_tabs {
+	my ($str) = @_;
+
+	my $res = '';
+	my $n = 0;
+	for my $c (split(//, $str)) {
+		if ($c eq "\t") {
+			$res .= ' ';
+			$n++;
+			for (; ($n % $tabsize) != 0; $n++) {
+				$res .= ' ';
+			}
+			next;
+		}
+		$res .= $c;
+		$n++;
+	}
+
+	return $res;
+}
+sub copy_spacing {
+	(my $res = shift) =~ tr/\t/ /c;
+	return $res;
+}
+
+sub line_stats {
+	my ($line) = @_;
+
+	# Drop the diff line leader and expand tabs
+	$line =~ s/^.//;
+	$line = expand_tabs($line);
+
+	# Pick the indent from the front of the line.
+	my ($white) = ($line =~ /^(\s*)/);
+
+	return (length($line), length($white));
+}
+
+my $sanitise_quote = '';
+
+sub sanitise_line_reset {
+	my ($in_comment) = @_;
+
+	if ($in_comment) {
+		$sanitise_quote = '*/';
+	} else {
+		$sanitise_quote = '';
+	}
+}
+sub sanitise_line {
+	my ($line) = @_;
+
+	my $res = '';
+	my $l = '';
+
+	my $qlen = 0;
+	my $off = 0;
+	my $c;
+
+	# Always copy over the diff marker.
+	$res = substr($line, 0, 1);
+
+	for ($off = 1; $off < length($line); $off++) {
+		$c = substr($line, $off, 1);
+
+		# Comments we are whacking completely including the begin
+		# and end, all to $;.
+		if ($sanitise_quote eq '' && substr($line, $off, 2) eq '/*') {
+			$sanitise_quote = '*/';
+
+			substr($res, $off, 2, "$;$;");
+			$off++;
+			next;
+		}
+		if ($sanitise_quote eq '*/' && substr($line, $off, 2) eq '*/') {
+			$sanitise_quote = '';
+			substr($res, $off, 2, "$;$;");
+			$off++;
+			next;
+		}
+		if ($sanitise_quote eq '' && substr($line, $off, 2) eq '//') {
+			$sanitise_quote = '//';
+
+			substr($res, $off, 2, $sanitise_quote);
+			$off++;
+			next;
+		}
+
+		# A \ in a string means ignore the next character.
+		if (($sanitise_quote eq "'" || $sanitise_quote eq '"') &&
+		    $c eq "\\") {
+			substr($res, $off, 2, 'XX');
+			$off++;
+			next;
+		}
+		# Regular quotes.
+		if ($c eq "'" || $c eq '"') {
+			if ($sanitise_quote eq '') {
+				$sanitise_quote = $c;
+
+				substr($res, $off, 1, $c);
+				next;
+			} elsif ($sanitise_quote eq $c) {
+				$sanitise_quote = '';
+			}
+		}
+
+		#print "c<$c> SQ<$sanitise_quote>\n";
+		if ($off != 0 && $sanitise_quote eq '*/' && $c ne "\t") {
+			substr($res, $off, 1, $;);
+		} elsif ($off != 0 && $sanitise_quote eq '//' && $c ne "\t") {
+			substr($res, $off, 1, $;);
+		} elsif ($off != 0 && $sanitise_quote && $c ne "\t") {
+			substr($res, $off, 1, 'X');
+		} else {
+			substr($res, $off, 1, $c);
+		}
+	}
+
+	if ($sanitise_quote eq '//') {
+		$sanitise_quote = '';
+	}
+
+	# The pathname on a #include may be surrounded by '<' and '>'.
+	if ($res =~ /^.\s*\#\s*include\s+\<(.*)\>/) {
+		my $clean = 'X' x length($1);
+		$res =~ s@\<.*\>@<$clean>@;
+
+	# The whole of a #error is a string.
+	} elsif ($res =~ /^.\s*\#\s*(?:error|warning)\s+(.*)\b/) {
+		my $clean = 'X' x length($1);
+		$res =~ s@(\#\s*(?:error|warning)\s+).*@$1$clean@;
+	}
+
+	if ($allow_c99_comments && $res =~ m@(//.*$)@) {
+		my $match = $1;
+		$res =~ s/\Q$match\E/"$;" x length($match)/e;
+	}
+
+	return $res;
+}
+
+sub get_quoted_string {
+	my ($line, $rawline) = @_;
+
+	return "" if (!defined($line) || !defined($rawline));
+	return "" if ($line !~ m/($String)/g);
+	return substr($rawline, $-[0], $+[0] - $-[0]);
+}
+
+sub ctx_statement_block {
+	my ($linenr, $remain, $off) = @_;
+	my $line = $linenr - 1;
+	my $blk = '';
+	my $soff = $off;
+	my $coff = $off - 1;
+	my $coff_set = 0;
+
+	my $loff = 0;
+
+	my $type = '';
+	my $level = 0;
+	my @stack = ();
+	my $p;
+	my $c;
+	my $len = 0;
+
+	my $remainder;
+	while (1) {
+		@stack = (['', 0]) if ($#stack == -1);
+
+		#warn "CSB: blk<$blk> remain<$remain>\n";
+		# If we are about to drop off the end, pull in more
+		# context.
+		if ($off >= $len) {
+			for (; $remain > 0; $line++) {
+				last if (!defined $lines[$line]);
+				next if ($lines[$line] =~ /^-/);
+				$remain--;
+				$loff = $len;
+				$blk .= $lines[$line] . "\n";
+				$len = length($blk);
+				$line++;
+				last;
+			}
+			# Bail if there is no further context.
+			#warn "CSB: blk<$blk> off<$off> len<$len>\n";
+			if ($off >= $len) {
+				last;
+			}
+			if ($level == 0 && substr($blk, $off) =~ /^.\s*#\s*define/) {
+				$level++;
+				$type = '#';
+			}
+		}
+		$p = $c;
+		$c = substr($blk, $off, 1);
+		$remainder = substr($blk, $off);
+
+		#warn "CSB: c<$c> type<$type> level<$level> remainder<$remainder> coff_set<$coff_set>\n";
+
+		# Handle nested #if/#else.
+		if ($remainder =~ /^#\s*(?:ifndef|ifdef|if)\s/) {
+			push(@stack, [ $type, $level ]);
+		} elsif ($remainder =~ /^#\s*(?:else|elif)\b/) {
+			($type, $level) = @{$stack[$#stack - 1]};
+		} elsif ($remainder =~ /^#\s*endif\b/) {
+			($type, $level) = @{pop(@stack)};
+		}
+
+		# Statement ends at the ';' or a close '}' at the
+		# outermost level.
+		if ($level == 0 && $c eq ';') {
+			last;
+		}
+
+		# An else is really a conditional as long as its not else if
+		if ($level == 0 && $coff_set == 0 &&
+				(!defined($p) || $p =~ /(?:\s|\}|\+)/) &&
+				$remainder =~ /^(else)(?:\s|{)/ &&
+				$remainder !~ /^else\s+if\b/) {
+			$coff = $off + length($1) - 1;
+			$coff_set = 1;
+			#warn "CSB: mark coff<$coff> soff<$soff> 1<$1>\n";
+			#warn "[" . substr($blk, $soff, $coff - $soff + 1) . "]\n";
+		}
+
+		if (($type eq '' || $type eq '(') && $c eq '(') {
+			$level++;
+			$type = '(';
+		}
+		if ($type eq '(' && $c eq ')') {
+			$level--;
+			$type = ($level != 0)? '(' : '';
+
+			if ($level == 0 && $coff < $soff) {
+				$coff = $off;
+				$coff_set = 1;
+				#warn "CSB: mark coff<$coff>\n";
+			}
+		}
+		if (($type eq '' || $type eq '{') && $c eq '{') {
+			$level++;
+			$type = '{';
+		}
+		if ($type eq '{' && $c eq '}') {
+			$level--;
+			$type = ($level != 0)? '{' : '';
+
+			if ($level == 0) {
+				if (substr($blk, $off + 1, 1) eq ';') {
+					$off++;
+				}
+				last;
+			}
+		}
+		# Preprocessor commands end at the newline unless escaped.
+		if ($type eq '#' && $c eq "\n" && $p ne "\\") {
+			$level--;
+			$type = '';
+			$off++;
+			last;
+		}
+		$off++;
+	}
+	# We are truly at the end, so shuffle to the next line.
+	if ($off == $len) {
+		$loff = $len + 1;
+		$line++;
+		$remain--;
+	}
+
+	my $statement = substr($blk, $soff, $off - $soff + 1);
+	my $condition = substr($blk, $soff, $coff - $soff + 1);
+
+	#warn "STATEMENT<$statement>\n";
+	#warn "CONDITION<$condition>\n";
+
+	#print "coff<$coff> soff<$off> loff<$loff>\n";
+
+	return ($statement, $condition,
+			$line, $remain + 1, $off - $loff + 1, $level);
+}
+
+sub statement_lines {
+	my ($stmt) = @_;
+
+	# Strip the diff line prefixes and rip blank lines at start and end.
+	$stmt =~ s/(^|\n)./$1/g;
+	$stmt =~ s/^\s*//;
+	$stmt =~ s/\s*$//;
+
+	my @stmt_lines = ($stmt =~ /\n/g);
+
+	return $#stmt_lines + 2;
+}
+
+sub statement_rawlines {
+	my ($stmt) = @_;
+
+	my @stmt_lines = ($stmt =~ /\n/g);
+
+	return $#stmt_lines + 2;
+}
+
+sub statement_block_size {
+	my ($stmt) = @_;
+
+	$stmt =~ s/(^|\n)./$1/g;
+	$stmt =~ s/^\s*{//;
+	$stmt =~ s/}\s*$//;
+	$stmt =~ s/^\s*//;
+	$stmt =~ s/\s*$//;
+
+	my @stmt_lines = ($stmt =~ /\n/g);
+	my @stmt_statements = ($stmt =~ /;/g);
+
+	my $stmt_lines = $#stmt_lines + 2;
+	my $stmt_statements = $#stmt_statements + 1;
+
+	if ($stmt_lines > $stmt_statements) {
+		return $stmt_lines;
+	} else {
+		return $stmt_statements;
+	}
+}
+
+sub ctx_statement_full {
+	my ($linenr, $remain, $off) = @_;
+	my ($statement, $condition, $level);
+
+	my (@chunks);
+
+	# Grab the first conditional/block pair.
+	($statement, $condition, $linenr, $remain, $off, $level) =
+				ctx_statement_block($linenr, $remain, $off);
+	#print "F: c<$condition> s<$statement> remain<$remain>\n";
+	push(@chunks, [ $condition, $statement ]);
+	if (!($remain > 0 && $condition =~ /^\s*(?:\n[+-])?\s*(?:if|else|do)\b/s)) {
+		return ($level, $linenr, @chunks);
+	}
+
+	# Pull in the following conditional/block pairs and see if they
+	# could continue the statement.
+	for (;;) {
+		($statement, $condition, $linenr, $remain, $off, $level) =
+				ctx_statement_block($linenr, $remain, $off);
+		#print "C: c<$condition> s<$statement> remain<$remain>\n";
+		last if (!($remain > 0 && $condition =~ /^(?:\s*\n[+-])*\s*(?:else|do)\b/s));
+		#print "C: push\n";
+		push(@chunks, [ $condition, $statement ]);
+	}
+
+	return ($level, $linenr, @chunks);
+}
+
+sub ctx_block_get {
+	my ($linenr, $remain, $outer, $open, $close, $off) = @_;
+	my $line;
+	my $start = $linenr - 1;
+	my $blk = '';
+	my @o;
+	my @c;
+	my @res = ();
+
+	my $level = 0;
+	my @stack = ($level);
+	for ($line = $start; $remain > 0; $line++) {
+		next if ($rawlines[$line] =~ /^-/);
+		$remain--;
+
+		$blk .= $rawlines[$line];
+
+		# Handle nested #if/#else.
+		if ($lines[$line] =~ /^.\s*#\s*(?:ifndef|ifdef|if)\s/) {
+			push(@stack, $level);
+		} elsif ($lines[$line] =~ /^.\s*#\s*(?:else|elif)\b/) {
+			$level = $stack[$#stack - 1];
+		} elsif ($lines[$line] =~ /^.\s*#\s*endif\b/) {
+			$level = pop(@stack);
+		}
+
+		foreach my $c (split(//, $lines[$line])) {
+			##print "C<$c>L<$level><$open$close>O<$off>\n";
+			if ($off > 0) {
+				$off--;
+				next;
+			}
+
+			if ($c eq $close && $level > 0) {
+				$level--;
+				last if ($level == 0);
+			} elsif ($c eq $open) {
+				$level++;
+			}
+		}
+
+		if (!$outer || $level <= 1) {
+			push(@res, $rawlines[$line]);
+		}
+
+		last if ($level == 0);
+	}
+
+	return ($level, @res);
+}
+sub ctx_block_outer {
+	my ($linenr, $remain) = @_;
+
+	my ($level, @r) = ctx_block_get($linenr, $remain, 1, '{', '}', 0);
+	return @r;
+}
+sub ctx_block {
+	my ($linenr, $remain) = @_;
+
+	my ($level, @r) = ctx_block_get($linenr, $remain, 0, '{', '}', 0);
+	return @r;
+}
+sub ctx_statement {
+	my ($linenr, $remain, $off) = @_;
+
+	my ($level, @r) = ctx_block_get($linenr, $remain, 0, '(', ')', $off);
+	return @r;
+}
+sub ctx_block_level {
+	my ($linenr, $remain) = @_;
+
+	return ctx_block_get($linenr, $remain, 0, '{', '}', 0);
+}
+sub ctx_statement_level {
+	my ($linenr, $remain, $off) = @_;
+
+	return ctx_block_get($linenr, $remain, 0, '(', ')', $off);
+}
+
+sub ctx_locate_comment {
+	my ($first_line, $end_line) = @_;
+
+	# If c99 comment on the current line, or the line before or after
+	my ($current_comment) = ($rawlines[$end_line - 1] =~ m@^\+.*(//.*$)@);
+	return $current_comment if (defined $current_comment);
+	($current_comment) = ($rawlines[$end_line - 2] =~ m@^[\+ ].*(//.*$)@);
+	return $current_comment if (defined $current_comment);
+	($current_comment) = ($rawlines[$end_line] =~ m@^[\+ ].*(//.*$)@);
+	return $current_comment if (defined $current_comment);
+
+	# Catch a comment on the end of the line itself.
+	($current_comment) = ($rawlines[$end_line - 1] =~ m@.*(/\*.*\*/)\s*(?:\\\s*)?$@);
+	return $current_comment if (defined $current_comment);
+
+	# Look through the context and try and figure out if there is a
+	# comment.
+	my $in_comment = 0;
+	$current_comment = '';
+	for (my $linenr = $first_line; $linenr < $end_line; $linenr++) {
+		my $line = $rawlines[$linenr - 1];
+		#warn "           $line\n";
+		if ($linenr == $first_line and $line =~ m@^.\s*\*@) {
+			$in_comment = 1;
+		}
+		if ($line =~ m@/\*@) {
+			$in_comment = 1;
+		}
+		if (!$in_comment && $current_comment ne '') {
+			$current_comment = '';
+		}
+		$current_comment .= $line . "\n" if ($in_comment);
+		if ($line =~ m@\*/@) {
+			$in_comment = 0;
+		}
+	}
+
+	chomp($current_comment);
+	return($current_comment);
+}
+sub ctx_has_comment {
+	my ($first_line, $end_line) = @_;
+	my $cmt = ctx_locate_comment($first_line, $end_line);
+
+	##print "LINE: $rawlines[$end_line - 1 ]\n";
+	##print "CMMT: $cmt\n";
+
+	return ($cmt ne '');
+}
+
+sub raw_line {
+	my ($linenr, $cnt) = @_;
+
+	my $offset = $linenr - 1;
+	$cnt++;
+
+	my $line;
+	while ($cnt) {
+		$line = $rawlines[$offset++];
+		next if (defined($line) && $line =~ /^-/);
+		$cnt--;
+	}
+
+	return $line;
+}
+
+sub get_stat_real {
+	my ($linenr, $lc) = @_;
+
+	my $stat_real = raw_line($linenr, 0);
+	for (my $count = $linenr + 1; $count <= $lc; $count++) {
+		$stat_real = $stat_real . "\n" . raw_line($count, 0);
+	}
+
+	return $stat_real;
+}
+
+sub get_stat_here {
+	my ($linenr, $cnt, $here) = @_;
+
+	my $herectx = $here . "\n";
+	for (my $n = 0; $n < $cnt; $n++) {
+		$herectx .= raw_line($linenr, $n) . "\n";
+	}
+
+	return $herectx;
+}
+
+sub cat_vet {
+	my ($vet) = @_;
+	my ($res, $coded);
+
+	$res = '';
+	while ($vet =~ /([^[:cntrl:]]*)([[:cntrl:]]|$)/g) {
+		$res .= $1;
+		if ($2 ne '') {
+			$coded = sprintf("^%c", unpack('C', $2) + 64);
+			$res .= $coded;
+		}
+	}
+	$res =~ s/$/\$/;
+
+	return $res;
+}
+
+my $av_preprocessor = 0;
+my $av_pending;
+my @av_paren_type;
+my $av_pend_colon;
+
+sub annotate_reset {
+	$av_preprocessor = 0;
+	$av_pending = '_';
+	@av_paren_type = ('E');
+	$av_pend_colon = 'O';
+}
+
+sub annotate_values {
+	my ($stream, $type) = @_;
+
+	my $res;
+	my $var = '_' x length($stream);
+	my $cur = $stream;
+
+	print "$stream\n" if ($dbg_values > 1);
+
+	while (length($cur)) {
+		@av_paren_type = ('E') if ($#av_paren_type < 0);
+		print " <" . join('', @av_paren_type) .
+				"> <$type> <$av_pending>" if ($dbg_values > 1);
+		if ($cur =~ /^(\s+)/o) {
+			print "WS($1)\n" if ($dbg_values > 1);
+			if ($1 =~ /\n/ && $av_preprocessor) {
+				$type = pop(@av_paren_type);
+				$av_preprocessor = 0;
+			}
+
+		} elsif ($cur =~ /^(\(\s*$Type\s*)\)/ && $av_pending eq '_') {
+			print "CAST($1)\n" if ($dbg_values > 1);
+			push(@av_paren_type, $type);
+			$type = 'c';
+
+		} elsif ($cur =~ /^($Type)\s*(?:$Ident|,|\)|\(|\s*$)/) {
+			print "DECLARE($1)\n" if ($dbg_values > 1);
+			$type = 'T';
+
+		} elsif ($cur =~ /^($Modifier)\s*/) {
+			print "MODIFIER($1)\n" if ($dbg_values > 1);
+			$type = 'T';
+
+		} elsif ($cur =~ /^(\#\s*define\s*$Ident)(\(?)/o) {
+			print "DEFINE($1,$2)\n" if ($dbg_values > 1);
+			$av_preprocessor = 1;
+			push(@av_paren_type, $type);
+			if ($2 ne '') {
+				$av_pending = 'N';
+			}
+			$type = 'E';
+
+		} elsif ($cur =~ /^(\#\s*(?:undef\s*$Ident|include\b))/o) {
+			print "UNDEF($1)\n" if ($dbg_values > 1);
+			$av_preprocessor = 1;
+			push(@av_paren_type, $type);
+
+		} elsif ($cur =~ /^(\#\s*(?:ifdef|ifndef|if))/o) {
+			print "PRE_START($1)\n" if ($dbg_values > 1);
+			$av_preprocessor = 1;
+
+			push(@av_paren_type, $type);
+			push(@av_paren_type, $type);
+			$type = 'E';
+
+		} elsif ($cur =~ /^(\#\s*(?:else|elif))/o) {
+			print "PRE_RESTART($1)\n" if ($dbg_values > 1);
+			$av_preprocessor = 1;
+
+			push(@av_paren_type, $av_paren_type[$#av_paren_type]);
+
+			$type = 'E';
+
+		} elsif ($cur =~ /^(\#\s*(?:endif))/o) {
+			print "PRE_END($1)\n" if ($dbg_values > 1);
+
+			$av_preprocessor = 1;
+
+			# Assume all arms of the conditional end as this
+			# one does, and continue as if the #endif was not here.
+			pop(@av_paren_type);
+			push(@av_paren_type, $type);
+			$type = 'E';
+
+		} elsif ($cur =~ /^(\\\n)/o) {
+			print "PRECONT($1)\n" if ($dbg_values > 1);
+
+		} elsif ($cur =~ /^(__attribute__)\s*\(?/o) {
+			print "ATTR($1)\n" if ($dbg_values > 1);
+			$av_pending = $type;
+			$type = 'N';
+
+		} elsif ($cur =~ /^(sizeof)\s*(\()?/o) {
+			print "SIZEOF($1)\n" if ($dbg_values > 1);
+			if (defined $2) {
+				$av_pending = 'V';
+			}
+			$type = 'N';
+
+		} elsif ($cur =~ /^(if|while|for)\b/o) {
+			print "COND($1)\n" if ($dbg_values > 1);
+			$av_pending = 'E';
+			$type = 'N';
+
+		} elsif ($cur =~/^(case)/o) {
+			print "CASE($1)\n" if ($dbg_values > 1);
+			$av_pend_colon = 'C';
+			$type = 'N';
+
+		} elsif ($cur =~/^(return|else|goto|typeof|__typeof__)\b/o) {
+			print "KEYWORD($1)\n" if ($dbg_values > 1);
+			$type = 'N';
+
+		} elsif ($cur =~ /^(\()/o) {
+			print "PAREN('$1')\n" if ($dbg_values > 1);
+			push(@av_paren_type, $av_pending);
+			$av_pending = '_';
+			$type = 'N';
+
+		} elsif ($cur =~ /^(\))/o) {
+			my $new_type = pop(@av_paren_type);
+			if ($new_type ne '_') {
+				$type = $new_type;
+				print "PAREN('$1') -> $type\n"
+							if ($dbg_values > 1);
+			} else {
+				print "PAREN('$1')\n" if ($dbg_values > 1);
+			}
+
+		} elsif ($cur =~ /^($Ident)\s*\(/o) {
+			print "FUNC($1)\n" if ($dbg_values > 1);
+			$type = 'V';
+			$av_pending = 'V';
+
+		} elsif ($cur =~ /^($Ident\s*):(?:\s*\d+\s*(,|=|;))?/) {
+			if (defined $2 && $type eq 'C' || $type eq 'T') {
+				$av_pend_colon = 'B';
+			} elsif ($type eq 'E') {
+				$av_pend_colon = 'L';
+			}
+			print "IDENT_COLON($1,$type>$av_pend_colon)\n" if ($dbg_values > 1);
+			$type = 'V';
+
+		} elsif ($cur =~ /^($Ident|$Constant)/o) {
+			print "IDENT($1)\n" if ($dbg_values > 1);
+			$type = 'V';
+
+		} elsif ($cur =~ /^($Assignment)/o) {
+			print "ASSIGN($1)\n" if ($dbg_values > 1);
+			$type = 'N';
+
+		} elsif ($cur =~/^(;|{|})/) {
+			print "END($1)\n" if ($dbg_values > 1);
+			$type = 'E';
+			$av_pend_colon = 'O';
+
+		} elsif ($cur =~/^(,)/) {
+			print "COMMA($1)\n" if ($dbg_values > 1);
+			$type = 'C';
+
+		} elsif ($cur =~ /^(\?)/o) {
+			print "QUESTION($1)\n" if ($dbg_values > 1);
+			$type = 'N';
+
+		} elsif ($cur =~ /^(:)/o) {
+			print "COLON($1,$av_pend_colon)\n" if ($dbg_values > 1);
+
+			substr($var, length($res), 1, $av_pend_colon);
+			if ($av_pend_colon eq 'C' || $av_pend_colon eq 'L') {
+				$type = 'E';
+			} else {
+				$type = 'N';
+			}
+			$av_pend_colon = 'O';
+
+		} elsif ($cur =~ /^(\[)/o) {
+			print "CLOSE($1)\n" if ($dbg_values > 1);
+			$type = 'N';
+
+		} elsif ($cur =~ /^(-(?![->])|\+(?!\+)|\*|\&\&|\&)/o) {
+			my $variant;
+
+			print "OPV($1)\n" if ($dbg_values > 1);
+			if ($type eq 'V') {
+				$variant = 'B';
+			} else {
+				$variant = 'U';
+			}
+
+			substr($var, length($res), 1, $variant);
+			$type = 'N';
+
+		} elsif ($cur =~ /^($Operators)/o) {
+			print "OP($1)\n" if ($dbg_values > 1);
+			if ($1 ne '++' && $1 ne '--') {
+				$type = 'N';
+			}
+
+		} elsif ($cur =~ /(^.)/o) {
+			print "C($1)\n" if ($dbg_values > 1);
+		}
+		if (defined $1) {
+			$cur = substr($cur, length($1));
+			$res .= $type x length($1);
+		}
+	}
+
+	return ($res, $var);
+}
+
+sub possible {
+	my ($possible, $line) = @_;
+	my $notPermitted = qr{(?:
+		^(?:
+			$Modifier|
+			$Storage|
+			$Type|
+			DEFINE_\S+
+		)$|
+		^(?:
+			goto|
+			return|
+			case|
+			else|
+			asm|__asm__|
+			do|
+			\#|
+			\#\#|
+		)(?:\s|$)|
+		^(?:typedef|struct|enum)\b
+	    )}x;
+	warn "CHECK<$possible> ($line)\n" if ($dbg_possible > 2);
+	if ($possible !~ $notPermitted) {
+		# Check for modifiers.
+		$possible =~ s/\s*$Storage\s*//g;
+		$possible =~ s/\s*$Sparse\s*//g;
+		if ($possible =~ /^\s*$/) {
+
+		} elsif ($possible =~ /\s/) {
+			$possible =~ s/\s*$Type\s*//g;
+			for my $modifier (split(' ', $possible)) {
+				if ($modifier !~ $notPermitted) {
+					warn "MODIFIER: $modifier ($possible) ($line)\n" if ($dbg_possible);
+					push(@modifierListFile, $modifier);
+				}
+			}
+
+		} else {
+			warn "POSSIBLE: $possible ($line)\n" if ($dbg_possible);
+			push(@typeListFile, $possible);
+		}
+		build_types();
+	} else {
+		warn "NOTPOSS: $possible ($line)\n" if ($dbg_possible > 1);
+	}
+}
+
+my $prefix = '';
+
+sub show_type {
+	my ($type) = @_;
+
+	$type =~ tr/[a-z]/[A-Z]/;
+
+	return defined $use_type{$type} if (scalar keys %use_type > 0);
+
+	return !defined $ignore_type{$type};
+}
+
+sub report {
+	my ($level, $type, $msg) = @_;
+
+	if (!show_type($type) ||
+	    (defined $tst_only && $msg !~ /\Q$tst_only\E/)) {
+		return 0;
+	}
+	my $output = '';
+	if ($color) {
+		if ($level eq 'ERROR') {
+			$output .= RED;
+		} elsif ($level eq 'WARNING') {
+			$output .= YELLOW;
+		} else {
+			$output .= GREEN;
+		}
+	}
+	$output .= $prefix . $level . ':';
+	if ($show_types) {
+		$output .= BLUE if ($color);
+		$output .= "$type:";
+	}
+	$output .= RESET if ($color);
+	$output .= ' ' . $msg . "\n";
+
+	if ($showfile) {
+		my @lines = split("\n", $output, -1);
+		splice(@lines, 1, 1);
+		$output = join("\n", @lines);
+	}
+	$output = (split('\n', $output))[0] . "\n" if ($terse);
+
+	push(our @report, $output);
+
+	return 1;
+}
+
+sub report_dump {
+	our @report;
+}
+
+sub fixup_current_range {
+	my ($lineRef, $offset, $length) = @_;
+
+	if ($$lineRef =~ /^\@\@ -\d+,\d+ \+(\d+),(\d+) \@\@/) {
+		my $o = $1;
+		my $l = $2;
+		my $no = $o + $offset;
+		my $nl = $l + $length;
+		$$lineRef =~ s/\+$o,$l \@\@/\+$no,$nl \@\@/;
+	}
+}
+
+sub fix_inserted_deleted_lines {
+	my ($linesRef, $insertedRef, $deletedRef) = @_;
+
+	my $range_last_linenr = 0;
+	my $delta_offset = 0;
+
+	my $old_linenr = 0;
+	my $new_linenr = 0;
+
+	my $next_insert = 0;
+	my $next_delete = 0;
+
+	my @lines = ();
+
+	my $inserted = @{$insertedRef}[$next_insert++];
+	my $deleted = @{$deletedRef}[$next_delete++];
+
+	foreach my $old_line (@{$linesRef}) {
+		my $save_line = 1;
+		my $line = $old_line;	#don't modify the array
+		if ($line =~ /^(?:\+\+\+|\-\-\-)\s+\S+/) {	#new filename
+			$delta_offset = 0;
+		} elsif ($line =~ /^\@\@ -\d+,\d+ \+\d+,\d+ \@\@/) {	#new hunk
+			$range_last_linenr = $new_linenr;
+			fixup_current_range(\$line, $delta_offset, 0);
+		}
+
+		while (defined($deleted) && ${$deleted}{'LINENR'} == $old_linenr) {
+			$deleted = @{$deletedRef}[$next_delete++];
+			$save_line = 0;
+			fixup_current_range(\$lines[$range_last_linenr], $delta_offset--, -1);
+		}
+
+		while (defined($inserted) && ${$inserted}{'LINENR'} == $old_linenr) {
+			push(@lines, ${$inserted}{'LINE'});
+			$inserted = @{$insertedRef}[$next_insert++];
+			$new_linenr++;
+			fixup_current_range(\$lines[$range_last_linenr], $delta_offset++, 1);
+		}
+
+		if ($save_line) {
+			push(@lines, $line);
+			$new_linenr++;
+		}
+
+		$old_linenr++;
+	}
+
+	return @lines;
+}
+
+sub fix_insert_line {
+	my ($linenr, $line) = @_;
+
+	my $inserted = {
+		LINENR => $linenr,
+		LINE => $line,
+	};
+	push(@fixed_inserted, $inserted);
+}
+
+sub fix_delete_line {
+	my ($linenr, $line) = @_;
+
+	my $deleted = {
+		LINENR => $linenr,
+		LINE => $line,
+	};
+
+	push(@fixed_deleted, $deleted);
+}
+
+sub ERROR {
+	my ($type, $msg) = @_;
+
+	if (report("ERROR", $type, $msg)) {
+		our $clean = 0;
+		our $cnt_error++;
+		return 1;
+	}
+	return 0;
+}
+sub WARN {
+	my ($type, $msg) = @_;
+
+	if (report("WARNING", $type, $msg)) {
+		our $clean = 0;
+		our $cnt_warn++;
+		return 1;
+	}
+	return 0;
+}
+sub CHK {
+	my ($type, $msg) = @_;
+
+	if ($check && report("CHECK", $type, $msg)) {
+		our $clean = 0;
+		our $cnt_chk++;
+		return 1;
+	}
+	return 0;
+}
+
+sub check_absolute_file {
+	my ($absolute, $herecurr) = @_;
+	my $file = $absolute;
+
+	##print "absolute<$absolute>\n";
+
+	# See if any suffix of this path is a path within the tree.
+	while ($file =~ s@^[^/]*/@@) {
+		if (-f "$root/$file") {
+			##print "file<$file>\n";
+			last;
+		}
+	}
+	if (! -f _)  {
+		return 0;
+	}
+
+	# It is, so see if the prefix is acceptable.
+	my $prefix = $absolute;
+	substr($prefix, -length($file)) = '';
+
+	##print "prefix<$prefix>\n";
+	if ($prefix ne ".../") {
+		WARN("USE_RELATIVE_PATH",
+		     "use relative pathname instead of absolute in changelog text\n" . $herecurr);
+	}
+}
+
+sub trim {
+	my ($string) = @_;
+
+	$string =~ s/^\s+|\s+$//g;
+
+	return $string;
+}
+
+sub ltrim {
+	my ($string) = @_;
+
+	$string =~ s/^\s+//;
+
+	return $string;
+}
+
+sub rtrim {
+	my ($string) = @_;
+
+	$string =~ s/\s+$//;
+
+	return $string;
+}
+
+sub string_find_replace {
+	my ($string, $find, $replace) = @_;
+
+	$string =~ s/$find/$replace/g;
+
+	return $string;
+}
+
+sub tabify {
+	my ($leading) = @_;
+
+	my $source_indent = $tabsize;
+	my $max_spaces_before_tab = $source_indent - 1;
+	my $spaces_to_tab = " " x $source_indent;
+
+	#convert leading spaces to tabs
+	1 while $leading =~ s@^([\t]*)$spaces_to_tab@$1\t@g;
+	#Remove spaces before a tab
+	1 while $leading =~ s@^([\t]*)( {1,$max_spaces_before_tab})\t@$1\t@g;
+
+	return "$leading";
+}
+
+sub pos_last_openparen {
+	my ($line) = @_;
+
+	my $pos = 0;
+
+	my $opens = $line =~ tr/\(/\(/;
+	my $closes = $line =~ tr/\)/\)/;
+
+	my $last_openparen = 0;
+
+	if (($opens == 0) || ($closes >= $opens)) {
+		return -1;
+	}
+
+	my $len = length($line);
+
+	for ($pos = 0; $pos < $len; $pos++) {
+		my $string = substr($line, $pos);
+		if ($string =~ /^($FuncArg|$balanced_parens)/) {
+			$pos += length($1) - 1;
+		} elsif (substr($line, $pos, 1) eq '(') {
+			$last_openparen = $pos;
+		} elsif (index($string, '(') == -1) {
+			last;
+		}
+	}
+
+	return length(expand_tabs(substr($line, 0, $last_openparen))) + 1;
+}
+
+sub get_raw_comment {
+	my ($line, $rawline) = @_;
+	my $comment = '';
+
+	for my $i (0 .. (length($line) - 1)) {
+		if (substr($line, $i, 1) eq "$;") {
+			$comment .= substr($rawline, $i, 1);
+		}
+	}
+
+	return $comment;
+}
+
+sub process {
+	my $filename = shift;
+
+	my $linenr=0;
+	my $prevline="";
+	my $prevrawline="";
+	my $stashline="";
+	my $stashrawline="";
+
+	my $length;
+	my $indent;
+	my $previndent=0;
+	my $stashindent=0;
+
+	our $clean = 1;
+	my $signoff = 0;
+	my $author = '';
+	my $authorsignoff = 0;
+	my $is_patch = 0;
+	my $is_binding_patch = -1;
+	my $in_header_lines = $file ? 0 : 1;
+	my $in_commit_log = 0;		#Scanning lines before patch
+	my $has_patch_separator = 0;	#Found a --- line
+	my $has_commit_log = 0;		#Encountered lines before patch
+	my $commit_log_lines = 0;	#Number of commit log lines
+	my $commit_log_possible_stack_dump = 0;
+	my $commit_log_long_line = 0;
+	my $commit_log_has_diff = 0;
+	my $reported_maintainer_file = 1;
+	my $non_utf8_charset = 0;
+
+	my $last_blank_line = 0;
+	my $last_coalesced_string_linenr = -1;
+
+	our @report = ();
+	our $cnt_lines = 0;
+	our $cnt_error = 0;
+	our $cnt_warn = 0;
+	our $cnt_chk = 0;
+
+	# Trace the real file/line as we go.
+	my $realfile = '';
+	my $realline = 0;
+	my $realcnt = 0;
+	my $here = '';
+	my $context_function;		#undef'd unless there's a known function
+	my $in_comment = 0;
+	my $comment_edge = 0;
+	my $first_line = 0;
+	my $p1_prefix = '';
+
+	my $prev_values = 'E';
+
+	# suppression flags
+	my %suppress_ifbraces;
+	my %suppress_whiletrailers;
+	my %suppress_export;
+	my $suppress_statement = 0;
+
+	my %signatures = ();
+
+	my $camelcase_file_seeded = 0;
+
+	my $checklicenseline = 1;
+
+	sanitise_line_reset();
+	my $line;
+	foreach my $rawline (@rawlines) {
+		$linenr++;
+		$line = $rawline;
+
+		push(@fixed, $rawline) if ($fix);
+
+		if ($rawline =~ /^\@\@ -\d+(?:,\d+)? \+(\d+)(,(\d+))? \@\@/) {
+			$realline=$1-1;
+			if (defined $2) {
+				$realcnt=$3+1;
+			} else {
+				$realcnt=1+1;
+			}
+			$in_comment = 0;
+
+			# Guestimate if this is a continuing comment.  Run
+			# the context looking for a comment "edge".  If this
+			# edge is a close comment then we must be in a comment
+			# at context start.
+			my $edge;
+			my $cnt = $realcnt;
+			for (my $ln = $linenr + 1; $cnt > 0; $ln++) {
+				next if (defined $rawlines[$ln - 1] &&
+					 $rawlines[$ln - 1] =~ /^-/);
+				$cnt--;
+				#print "RAW<$rawlines[$ln - 1]>\n";
+				last if (!defined $rawlines[$ln - 1]);
+				if ($rawlines[$ln - 1] =~ m@(/\*|\*/)@ &&
+				    $rawlines[$ln - 1] !~ m@"[^"]*(?:/\*|\*/)[^"]*"@) {
+					($edge) = $1;
+					last;
+				}
+			}
+			if (defined $edge && $edge eq '*/') {
+				$in_comment = 1;
+			}
+
+			# Guestimate if this is a continuing comment.  If this
+			# is the start of a diff block and this line starts
+			# ' *' then it is very likely a comment.
+			if (!defined $edge &&
+			    $rawlines[$linenr] =~ m@^.\s*(?:\*\*+| \*)(?:\s|$)@)
+			{
+				$in_comment = 1;
+			}
+
+			##print "COMMENT:$in_comment edge<$edge> $rawline\n";
+			sanitise_line_reset($in_comment);
+
+		} elsif ($realcnt && $rawline =~ /^(?:\+| |$)/) {
+			# Standardise the strings and chars within the input to
+			# simplify matching -- only bother with positive lines.
+			$line = sanitise_line($rawline);
+		}
+		push(@lines, $line);
+
+		if ($realcnt > 1) {
+			$realcnt-- if ($line =~ /^(?:\+| |$)/);
+		} else {
+			$realcnt = 0;
+		}
+
+		#print "==>$rawline\n";
+		#print "-->$line\n";
+	}
+
+	$prefix = '';
+
+	$realcnt = 0;
+	$linenr = 0;
+	$fixlinenr = -1;
+	foreach my $line (@lines) {
+		$linenr++;
+		$fixlinenr++;
+		my $sline = $line;	#copy of $line
+		$sline =~ s/$;/ /g;	#with comments as spaces
+
+		my $rawline = $rawlines[$linenr - 1];
+		my $raw_comment = get_raw_comment($line, $rawline);
+
+# check if it's a mode change, rename or start of a patch
+		if (!$in_commit_log &&
+		    ($line =~ /^ mode change [0-7]+ => [0-7]+ \S+\s*$/ ||
+		    ($line =~ /^rename (?:from|to) \S+\s*$/ ||
+		     $line =~ /^diff --git a\/[\w\/\.\_\-]+ b\/\S+\s*$/))) {
+			$is_patch = 1;
+		}
+
+#extract the line range in the file after the patch is applied
+		if (!$in_commit_log &&
+		    $line =~ /^\@\@ -\d+(?:,\d+)? \+(\d+)(,(\d+))? \@\@(.*)/) {
+			my $context = $4;
+			$is_patch = 1;
+			$first_line = $linenr + 1;
+			$realline=$1-1;
+			if (defined $2) {
+				$realcnt=$3+1;
+			} else {
+				$realcnt=1+1;
+			}
+			annotate_reset();
+			$prev_values = 'E';
+
+			%suppress_ifbraces = ();
+			%suppress_whiletrailers = ();
+			%suppress_export = ();
+			$suppress_statement = 0;
+			if ($context =~ /\b(\w+)\s*\(/) {
+				$context_function = $1;
+			} else {
+				undef $context_function;
+			}
+			next;
+
+# track the line number as we move through the hunk, note that
+# new versions of GNU diff omit the leading space on completely
+# blank context lines so we need to count that too.
+		} elsif ($line =~ /^( |\+|$)/) {
+			$realline++;
+			$realcnt-- if ($realcnt != 0);
+
+			# Measure the line length and indent.
+			($length, $indent) = line_stats($rawline);
+
+			# Track the previous line.
+			($prevline, $stashline) = ($stashline, $line);
+			($previndent, $stashindent) = ($stashindent, $indent);
+			($prevrawline, $stashrawline) = ($stashrawline, $rawline);
+
+			#warn "line<$line>\n";
+
+		} elsif ($realcnt == 1) {
+			$realcnt--;
+		}
+
+		my $hunk_line = ($realcnt != 0);
+
+		$here = "#$linenr: " if (!$file);
+		$here = "#$realline: " if ($file);
+
+		my $found_file = 0;
+		# extract the filename as it passes
+		if ($line =~ /^diff --git.*?(\S+)$/) {
+			$realfile = $1;
+			$realfile =~ s@^([^/]*)/@@ if (!$file);
+			$in_commit_log = 0;
+			$found_file = 1;
+		} elsif ($line =~ /^\+\+\+\s+(\S+)/) {
+			$realfile = $1;
+			$realfile =~ s@^([^/]*)/@@ if (!$file);
+			$in_commit_log = 0;
+
+			$p1_prefix = $1;
+			if (!$file && $tree && $p1_prefix ne '' &&
+			    -e "$root/$p1_prefix") {
+				WARN("PATCH_PREFIX",
+				     "patch prefix '$p1_prefix' exists, appears to be a -p0 patch\n");
+			}
+
+			if ($realfile =~ m@^include/asm/@) {
+				ERROR("MODIFIED_INCLUDE_ASM",
+				      "do not modify files in include/asm, change architecture specific files in include/asm-<architecture>\n" . "$here$rawline\n");
+			}
+			$found_file = 1;
+		}
+
+#make up the handle for any error we report on this line
+		if ($showfile) {
+			$prefix = "$realfile:$realline: "
+		} elsif ($emacs) {
+			if ($file) {
+				$prefix = "$filename:$realline: ";
+			} else {
+				$prefix = "$filename:$linenr: ";
+			}
+		}
+
+		if ($found_file) {
+			if (is_maintained_obsolete($realfile)) {
+				WARN("OBSOLETE",
+				     "$realfile is marked as 'obsolete' in the MAINTAINERS hierarchy.  No unnecessary modifications please.\n");
+			}
+			if ($realfile =~ m@^(?:drivers/net/|net/|drivers/staging/)@) {
+				$check = 1;
+			} else {
+				$check = $check_orig;
+			}
+			$checklicenseline = 1;
+
+			if ($realfile !~ /^MAINTAINERS/) {
+				my $last_binding_patch = $is_binding_patch;
+			}
+
+			next;
+		}
+
+		$here .= "FILE: $realfile:$realline:" if ($realcnt != 0);
+
+		my $hereline = "$here\n$rawline\n";
+		my $herecurr = "$here\n$rawline\n";
+		my $hereprev = "$here\n$prevrawline\n$rawline\n";
+
+		$cnt_lines++ if ($realcnt != 0);
+
+# Verify the existence of a commit log if appropriate
+# 2 is used because a $signature is counted in $commit_log_lines
+		if ($in_commit_log) {
+			if ($line !~ /^\s*$/) {
+				$commit_log_lines++;	#could be a $signature
+			}
+		} elsif ($has_commit_log && $commit_log_lines < 2) {
+			WARN("COMMIT_MESSAGE",
+			     "Missing commit description - Add an appropriate one\n");
+			$commit_log_lines = 2;	#warn only once
+		}
+
+# Check if the commit log has what seems like a diff which can confuse patch
+		if ($in_commit_log && !$commit_log_has_diff &&
+		    (($line =~ m@^\s+diff\b.*a/[\w/]+@ &&
+		      $line =~ m@^\s+diff\b.*a/([\w/]+)\s+b/$1\b@) ||
+		     $line =~ m@^\s*(?:\-\-\-\s+a/|\+\+\+\s+b/)@ ||
+		     $line =~ m/^\s*\@\@ \-\d+,\d+ \+\d+,\d+ \@\@/)) {
+			ERROR("DIFF_IN_COMMIT_MSG",
+			      "Avoid using diff content in the commit message - patch(1) might not work\n" . $herecurr);
+			$commit_log_has_diff = 1;
+		}
+
+# Check for incorrect file permissions
+		if ($line =~ /^new (file )?mode.*[7531]\d{0,2}$/) {
+			my $permhere = $here . "FILE: $realfile\n";
+			if ($realfile !~ m@scripts/@ &&
+			    $realfile !~ /\.(py|pl|awk|sh)$/) {
+				ERROR("EXECUTE_PERMISSIONS",
+				      "do not set execute permissions for source files\n" . $permhere);
+			}
+		}
+
+# Check the patch for a From:
+		if (decode("MIME-Header", $line) =~ /^From:\s*(.*)/) {
+			$author = $1;
+			$author = encode("utf8", $author) if ($line =~ /=\?utf-8\?/i);
+			$author =~ s/"//g;
+			$author = reformat_email($author);
+		}
+
+# Check the patch for a signoff:
+		if ($line =~ /^\s*signed-off-by:\s*(.*)/i) {
+			$signoff++;
+			$in_commit_log = 0;
+			if ($author ne '') {
+				if (same_email_addresses($1, $author)) {
+					$authorsignoff = 1;
+				}
+			}
+		}
+
+# Check for patch separator
+		if ($line =~ /^---$/) {
+			$has_patch_separator = 1;
+			$in_commit_log = 0;
+		}
+
+# Check if MAINTAINERS is being updated.  If so, there's probably no need to
+# emit the "does MAINTAINERS need updating?" message on file add/move/delete
+		if ($line =~ /^\s*MAINTAINERS\s*\|/) {
+			$reported_maintainer_file = 1;
+		}
+
+# Check signature styles
+		if (!$in_header_lines &&
+		    $line =~ /^(\s*)([a-z0-9_-]+by:|$signature_tags)(\s*)(.*)/i) {
+			my $space_before = $1;
+			my $sign_off = $2;
+			my $space_after = $3;
+			my $email = $4;
+			my $ucfirst_sign_off = ucfirst(lc($sign_off));
+
+			if ($sign_off !~ /$signature_tags/) {
+				WARN("BAD_SIGN_OFF",
+				     "Non-standard signature: $sign_off\n" . $herecurr);
+			}
+			if (defined $space_before && $space_before ne "") {
+				if (WARN("BAD_SIGN_OFF",
+					 "Do not use whitespace before $ucfirst_sign_off\n" . $herecurr) &&
+				    $fix) {
+					$fixed[$fixlinenr] =
+					    "$ucfirst_sign_off $email";
+				}
+			}
+			if ($sign_off =~ /-by:$/i && $sign_off ne $ucfirst_sign_off) {
+				if (WARN("BAD_SIGN_OFF",
+					 "'$ucfirst_sign_off' is the preferred signature form\n" . $herecurr) &&
+				    $fix) {
+					$fixed[$fixlinenr] =
+					    "$ucfirst_sign_off $email";
+				}
+
+			}
+			if (!defined $space_after || $space_after ne " ") {
+				if (WARN("BAD_SIGN_OFF",
+					 "Use a single space after $ucfirst_sign_off\n" . $herecurr) &&
+				    $fix) {
+					$fixed[$fixlinenr] =
+					    "$ucfirst_sign_off $email";
+				}
+			}
+
+			my ($email_name, $name_comment, $email_address, $comment) = parse_email($email);
+			my $suggested_email = format_email(($email_name, $email_address));
+			if ($suggested_email eq "") {
+				ERROR("BAD_SIGN_OFF",
+				      "Unrecognized email address: '$email'\n" . $herecurr);
+			} else {
+				my $dequoted = $suggested_email;
+				$dequoted =~ s/^"//;
+				$dequoted =~ s/" </ </;
+				# Don't force email to have quotes
+				# Allow just an angle bracketed address
+				if (!same_email_addresses($email, $suggested_email)) {
+					WARN("BAD_SIGN_OFF",
+					     "email address '$email' might be better as '$suggested_email$comment'\n" . $herecurr);
+				}
+			}
+
+# Check for duplicate signatures
+			my $sig_nospace = $line;
+			$sig_nospace =~ s/\s//g;
+			$sig_nospace = lc($sig_nospace);
+			if (defined $signatures{$sig_nospace}) {
+				WARN("BAD_SIGN_OFF",
+				     "Duplicate signature\n" . $herecurr);
+			} else {
+				$signatures{$sig_nospace} = 1;
+			}
+
+# Check Co-developed-by: immediately followed by Signed-off-by: with same name and email
+			if ($sign_off =~ /^co-developed-by:$/i) {
+				if ($email eq $author) {
+					WARN("BAD_SIGN_OFF",
+					      "Co-developed-by: should not be used to attribute nominal patch author '$author'\n" . "$here\n" . $rawline);
+				}
+				if (!defined $lines[$linenr]) {
+					WARN("BAD_SIGN_OFF",
+                                             "Co-developed-by: must be immediately followed by Signed-off-by:\n" . "$here\n" . $rawline);
+				} elsif ($rawlines[$linenr] !~ /^\s*signed-off-by:\s*(.*)/i) {
+					WARN("BAD_SIGN_OFF",
+					     "Co-developed-by: must be immediately followed by Signed-off-by:\n" . "$here\n" . $rawline . "\n" .$rawlines[$linenr]);
+				} elsif ($1 ne $email) {
+					WARN("BAD_SIGN_OFF",
+					     "Co-developed-by and Signed-off-by: name/email do not match \n" . "$here\n" . $rawline . "\n" .$rawlines[$linenr]);
+				}
+			}
+		}
+
+# Check email subject for common tools that don't need to be mentioned
+		if ($in_header_lines &&
+		    $line =~ /^Subject:.*\b(?:checkpatch|sparse|smatch)\b[^:]/i) {
+			WARN("EMAIL_SUBJECT",
+			     "A patch subject line should describe the change not the tool that found it\n" . $herecurr);
+		}
+
+# Check for Gerrit Change-Ids not in any patch context
+		if ($realfile eq '' && !$has_patch_separator && $line =~ /^\s*change-id:/i) {
+			ERROR("GERRIT_CHANGE_ID",
+			      "Remove Gerrit Change-Id's before submitting upstream\n" . $herecurr);
+		}
+
+# Check if the commit log is in a possible stack dump
+		if ($in_commit_log && !$commit_log_possible_stack_dump &&
+		    ($line =~ /^\s*(?:WARNING:|BUG:)/ ||
+		     $line =~ /^\s*\[\s*\d+\.\d{6,6}\s*\]/ ||
+					# timestamp
+		     $line =~ /^\s*\[\<[0-9a-fA-F]{8,}\>\]/) ||
+		     $line =~ /^(?:\s+\w+:\s+[0-9a-fA-F]+){3,3}/ ||
+		     $line =~ /^\s*\#\d+\s*\[[0-9a-fA-F]+\]\s*\w+ at [0-9a-fA-F]+/) {
+					# stack dump address styles
+			$commit_log_possible_stack_dump = 1;
+		}
+
+# Check for line lengths > 75 in commit log, warn once
+		if ($in_commit_log && !$commit_log_long_line &&
+		    length($line) > 75 &&
+		    !($line =~ /^\s*[a-zA-Z0-9_\/\.]+\s+\|\s+\d+/ ||
+					# file delta changes
+		      $line =~ /^\s*(?:[\w\.\-]+\/)++[\w\.\-]+:/ ||
+					# filename then :
+		      $line =~ /^\s*(?:Fixes:|Link:)/i ||
+					# A Fixes: or Link: line
+		      $commit_log_possible_stack_dump)) {
+			WARN("COMMIT_LOG_LONG_LINE",
+			     "Possible unwrapped commit description (prefer a maximum 75 chars per line)\n" . $herecurr);
+			$commit_log_long_line = 1;
+		}
+
+# Reset possible stack dump if a blank line is found
+		if ($in_commit_log && $commit_log_possible_stack_dump &&
+		    $line =~ /^\s*$/) {
+			$commit_log_possible_stack_dump = 0;
+		}
+
+# Check for git id commit length and improperly formed commit descriptions
+		if ($in_commit_log && !$commit_log_possible_stack_dump &&
+		    $line !~ /^\s*(?:Link|Patchwork|http|https|BugLink|base-commit):/i &&
+		    $line !~ /^This reverts commit [0-9a-f]{7,40}/ &&
+		    ($line =~ /\bcommit\s+[0-9a-f]{5,}\b/i ||
+		     ($line =~ /(?:\s|^)[0-9a-f]{12,40}(?:[\s"'\(\[]|$)/i &&
+		      $line !~ /[\<\[][0-9a-f]{12,40}[\>\]]/i &&
+		      $line !~ /\bfixes:\s*[0-9a-f]{12,40}/i))) {
+			my $init_char = "c";
+			my $orig_commit = "";
+			my $short = 1;
+			my $long = 0;
+			my $case = 1;
+			my $space = 1;
+			my $hasdesc = 0;
+			my $hasparens = 0;
+			my $id = '0123456789ab';
+			my $orig_desc = "commit description";
+			my $description = "";
+
+			if ($line =~ /\b(c)ommit\s+([0-9a-f]{5,})\b/i) {
+				$init_char = $1;
+				$orig_commit = lc($2);
+			} elsif ($line =~ /\b([0-9a-f]{12,40})\b/i) {
+				$orig_commit = lc($1);
+			}
+
+			$short = 0 if ($line =~ /\bcommit\s+[0-9a-f]{12,40}/i);
+			$long = 1 if ($line =~ /\bcommit\s+[0-9a-f]{41,}/i);
+			$space = 0 if ($line =~ /\bcommit [0-9a-f]/i);
+			$case = 0 if ($line =~ /\b[Cc]ommit\s+[0-9a-f]{5,40}[^A-F]/);
+			if ($line =~ /\bcommit\s+[0-9a-f]{5,}\s+\("([^"]+)"\)/i) {
+				$orig_desc = $1;
+				$hasparens = 1;
+			} elsif ($line =~ /\bcommit\s+[0-9a-f]{5,}\s*$/i &&
+				 defined $rawlines[$linenr] &&
+				 $rawlines[$linenr] =~ /^\s*\("([^"]+)"\)/) {
+				$orig_desc = $1;
+				$hasparens = 1;
+			} elsif ($line =~ /\bcommit\s+[0-9a-f]{5,}\s+\("[^"]+$/i &&
+				 defined $rawlines[$linenr] &&
+				 $rawlines[$linenr] =~ /^\s*[^"]+"\)/) {
+				$line =~ /\bcommit\s+[0-9a-f]{5,}\s+\("([^"]+)$/i;
+				$orig_desc = $1;
+				$rawlines[$linenr] =~ /^\s*([^"]+)"\)/;
+				$orig_desc .= " " . $1;
+				$hasparens = 1;
+			}
+
+			($id, $description) = git_commit_info($orig_commit,
+							      $id, $orig_desc);
+
+			if (defined($id) &&
+			   ($short || $long || $space || $case || ($orig_desc ne $description) || !$hasparens)) {
+				ERROR("GIT_COMMIT_ID",
+				      "Please use git commit description style 'commit <12+ chars of sha1> (\"<title line>\")' - ie: '${init_char}ommit $id (\"$description\")'\n" . $herecurr);
+			}
+		}
+
+# Check for added, moved or deleted files
+		if (!$reported_maintainer_file && !$in_commit_log &&
+		    ($line =~ /^(?:new|deleted) file mode\s*\d+\s*$/ ||
+		     $line =~ /^rename (?:from|to) [\w\/\.\-]+\s*$/ ||
+		     ($line =~ /\{\s*([\w\/\.\-]*)\s*\=\>\s*([\w\/\.\-]*)\s*\}/ &&
+		      (defined($1) || defined($2))))) {
+			$is_patch = 1;
+			$reported_maintainer_file = 1;
+			WARN("FILE_PATH_CHANGES",
+			     "added, moved or deleted file(s), does MAINTAINERS need updating?\n" . $herecurr);
+		}
+
+# Check for wrappage within a valid hunk of the file
+		if ($realcnt != 0 && $line !~ m{^(?:\+|-| |\\ No newline|$)}) {
+			ERROR("CORRUPTED_PATCH",
+			      "patch seems to be corrupt (line wrapped?)\n" .
+				$herecurr) if (!$emitted_corrupt++);
+		}
+
+# UTF-8 regex found at http://www.w3.org/International/questions/qa-forms-utf-8.en.php
+		if (($realfile =~ /^$/ || $line =~ /^\+/) &&
+		    $rawline !~ m/^$UTF8*$/) {
+			my ($utf8_prefix) = ($rawline =~ /^($UTF8*)/);
+
+			my $blank = copy_spacing($rawline);
+			my $ptr = substr($blank, 0, length($utf8_prefix)) . "^";
+			my $hereptr = "$hereline$ptr\n";
+
+			CHK("INVALID_UTF8",
+			    "Invalid UTF-8, patch and commit message should be encoded in UTF-8\n" . $hereptr);
+		}
+
+# Check if it's the start of a commit log
+# (not a header line and we haven't seen the patch filename)
+		if ($in_header_lines && $realfile =~ /^$/ &&
+		    !($rawline =~ /^\s+(?:\S|$)/ ||
+		      $rawline =~ /^(?:commit\b|from\b|[\w-]+:)/i)) {
+			$in_header_lines = 0;
+			$in_commit_log = 1;
+			$has_commit_log = 1;
+		}
+
+# Check if there is UTF-8 in a commit log when a mail header has explicitly
+# declined it, i.e defined some charset where it is missing.
+		if ($in_header_lines &&
+		    $rawline =~ /^Content-Type:.+charset="(.+)".*$/ &&
+		    $1 !~ /utf-8/i) {
+			$non_utf8_charset = 1;
+		}
+
+		if ($in_commit_log && $non_utf8_charset && $realfile =~ /^$/ &&
+		    $rawline =~ /$NON_ASCII_UTF8/) {
+			WARN("UTF8_BEFORE_PATCH",
+			    "8-bit UTF-8 used in possible commit log\n" . $herecurr);
+		}
+
+# Check for absolute kernel paths in commit message
+		if ($tree && $in_commit_log) {
+			while ($line =~ m{(?:^|\s)(/\S*)}g) {
+				my $file = $1;
+
+				if ($file =~ m{^(.*?)(?::\d+)+:?$} &&
+				    check_absolute_file($1, $herecurr)) {
+					#
+				} else {
+					check_absolute_file($file, $herecurr);
+				}
+			}
+		}
+
+# Check for various typo / spelling mistakes
+		if (defined($misspellings) &&
+		    ($in_commit_log || $line =~ /^(?:\+|Subject:)/i)) {
+			while ($rawline =~ /(?:^|[^a-z@])($misspellings)(?:\b|$|[^a-z@])/gi) {
+				my $typo = $1;
+				my $typo_fix = $spelling_fix{lc($typo)};
+				$typo_fix = ucfirst($typo_fix) if ($typo =~ /^[A-Z]/);
+				$typo_fix = uc($typo_fix) if ($typo =~ /^[A-Z]+$/);
+				my $msg_level = \&WARN;
+				$msg_level = \&CHK if ($file);
+				if (&{$msg_level}("TYPO_SPELLING",
+						  "'$typo' may be misspelled - perhaps '$typo_fix'?\n" . $herecurr) &&
+				    $fix) {
+					$fixed[$fixlinenr] =~ s/(^|[^A-Za-z@])($typo)($|[^A-Za-z@])/$1$typo_fix$3/;
+				}
+			}
+		}
+
+# check for invalid commit id
+		if ($in_commit_log && $line =~ /(^fixes:|\bcommit)\s+([0-9a-f]{6,40})\b/i) {
+			my $id;
+			my $description;
+			($id, $description) = git_commit_info($2, undef, undef);
+			if (!defined($id)) {
+				WARN("UNKNOWN_COMMIT_ID",
+				     "Unknown commit id '$2', maybe rebased or not pulled?\n" . $herecurr);
+			}
+		}
+
+# ignore non-hunk lines and lines being removed
+		next if (!$hunk_line || $line =~ /^-/);
+
+#trailing whitespace
+		if ($line =~ /^\+.*\015/) {
+			my $herevet = "$here\n" . cat_vet($rawline) . "\n";
+			if (ERROR("DOS_LINE_ENDINGS",
+				  "DOS line endings\n" . $herevet) &&
+			    $fix) {
+				$fixed[$fixlinenr] =~ s/[\s\015]+$//;
+			}
+		} elsif ($rawline =~ /^\+.*\S\s+$/ || $rawline =~ /^\+\s+$/) {
+			my $herevet = "$here\n" . cat_vet($rawline) . "\n";
+			if (ERROR("TRAILING_WHITESPACE",
+				  "trailing whitespace\n" . $herevet) &&
+			    $fix) {
+				$fixed[$fixlinenr] =~ s/\s+$//;
+			}
+
+			$rpt_cleaners = 1;
+		}
+
+# Check for FSF mailing addresses.
+		if ($rawline =~ /\bwrite to the Free/i ||
+		    $rawline =~ /\b675\s+Mass\s+Ave/i ||
+		    $rawline =~ /\b59\s+Temple\s+Pl/i ||
+		    $rawline =~ /\b51\s+Franklin\s+St/i) {
+			my $herevet = "$here\n" . cat_vet($rawline) . "\n";
+			my $msg_level = \&ERROR;
+			$msg_level = \&CHK if ($file);
+			&{$msg_level}("FSF_MAILING_ADDRESS",
+				      "Do not include the paragraph about writing to the Free Software Foundation's mailing address from the sample GPL notice. The FSF has changed addresses in the past, and may do so again. Linux already includes a copy of the GPL.\n" . $herevet)
+		}
+
+# check for Kconfig help text having a real description
+# Only applies when adding the entry originally, after that we do not have
+# sufficient context to determine whether it is indeed long enough.
+		if ($realfile =~ /Kconfig/ &&
+		    # 'choice' is usually the last thing on the line (though
+		    # Kconfig supports named choices), so use a word boundary
+		    # (\b) rather than a whitespace character (\s)
+		    $line =~ /^\+\s*(?:config|menuconfig|choice)\b/) {
+			my $length = 0;
+			my $cnt = $realcnt;
+			my $ln = $linenr + 1;
+			my $f;
+			my $is_start = 0;
+			my $is_end = 0;
+			for (; $cnt > 0 && defined $lines[$ln - 1]; $ln++) {
+				$f = $lines[$ln - 1];
+				$cnt-- if ($lines[$ln - 1] !~ /^-/);
+				$is_end = $lines[$ln - 1] =~ /^\+/;
+
+				next if ($f =~ /^-/);
+				last if (!$file && $f =~ /^\@\@/);
+
+				if ($lines[$ln - 1] =~ /^\+\s*(?:bool|tristate|prompt)\s*["']/) {
+					$is_start = 1;
+				} elsif ($lines[$ln - 1] =~ /^\+\s*(?:help|---help---)\s*$/) {
+					if ($lines[$ln - 1] =~ "---help---") {
+						WARN("CONFIG_DESCRIPTION",
+						     "prefer 'help' over '---help---' for new help texts\n" . $herecurr);
+					}
+					$length = -1;
+				}
+
+				$f =~ s/^.//;
+				$f =~ s/#.*//;
+				$f =~ s/^\s+//;
+				next if ($f =~ /^$/);
+
+				# This only checks context lines in the patch
+				# and so hopefully shouldn't trigger false
+				# positives, even though some of these are
+				# common words in help texts
+				if ($f =~ /^\s*(?:config|menuconfig|choice|endchoice|
+						  if|endif|menu|endmenu|source)\b/x) {
+					$is_end = 1;
+					last;
+				}
+				$length++;
+			}
+			if ($is_start && $is_end && $length < $min_conf_desc_length) {
+				WARN("CONFIG_DESCRIPTION",
+				     "please write a paragraph that describes the config symbol fully\n" . $herecurr);
+			}
+			#print "is_start<$is_start> is_end<$is_end> length<$length>\n";
+		}
+
+# check MAINTAINERS entries
+		if ($realfile =~ /^MAINTAINERS$/) {
+# check MAINTAINERS entries for the right form
+			if ($rawline =~ /^\+[A-Z]:/ &&
+			    $rawline !~ /^\+[A-Z]:\t\S/) {
+				if (WARN("MAINTAINERS_STYLE",
+					 "MAINTAINERS entries use one tab after TYPE:\n" . $herecurr) &&
+				    $fix) {
+					$fixed[$fixlinenr] =~ s/^(\+[A-Z]):\s*/$1:\t/;
+				}
+			}
+# check MAINTAINERS entries for the right ordering too
+			my $preferred_order = 'MRLSWQBCPTFXNK';
+			if ($rawline =~ /^\+[A-Z]:/ &&
+			    $prevrawline =~ /^[\+ ][A-Z]:/) {
+				$rawline =~ /^\+([A-Z]):\s*(.*)/;
+				my $cur = $1;
+				my $curval = $2;
+				$prevrawline =~ /^[\+ ]([A-Z]):\s*(.*)/;
+				my $prev = $1;
+				my $prevval = $2;
+				my $curindex = index($preferred_order, $cur);
+				my $previndex = index($preferred_order, $prev);
+				if ($curindex < 0) {
+					WARN("MAINTAINERS_STYLE",
+					     "Unknown MAINTAINERS entry type: '$cur'\n" . $herecurr);
+				} else {
+					if ($previndex >= 0 && $curindex < $previndex) {
+						WARN("MAINTAINERS_STYLE",
+						     "Misordered MAINTAINERS entry - list '$cur:' before '$prev:'\n" . $hereprev);
+					} elsif ((($prev eq 'F' && $cur eq 'F') ||
+						  ($prev eq 'X' && $cur eq 'X')) &&
+						 ($prevval cmp $curval) > 0) {
+						WARN("MAINTAINERS_STYLE",
+						     "Misordered MAINTAINERS entry - list file patterns in alphabetic order\n" . $hereprev);
+					}
+				}
+			}
+		}
+
+# discourage the use of boolean for type definition attributes of Kconfig options
+		if ($realfile =~ /Kconfig/ &&
+		    $line =~ /^\+\s*\bboolean\b/) {
+			WARN("CONFIG_TYPE_BOOLEAN",
+			     "Use of boolean is deprecated, please use bool instead.\n" . $herecurr);
+		}
+
+		if (($realfile =~ /Makefile.*/ || $realfile =~ /Kbuild.*/) &&
+		    ($line =~ /\+(EXTRA_[A-Z]+FLAGS).*/)) {
+			my $flag = $1;
+			my $replacement = {
+				'EXTRA_AFLAGS' =>   'asflags-y',
+				'EXTRA_CFLAGS' =>   'ccflags-y',
+				'EXTRA_CPPFLAGS' => 'cppflags-y',
+				'EXTRA_LDFLAGS' =>  'ldflags-y',
+			};
+
+			WARN("DEPRECATED_VARIABLE",
+			     "Use of $flag is deprecated, please use \`$replacement->{$flag} instead.\n" . $herecurr) if ($replacement->{$flag});
+		}
+
+# check for using SPDX license tag at beginning of files
+		if ($realline == $checklicenseline) {
+			if ($rawline =~ /^[ \+]\s*\#\!\s*\//) {
+				$checklicenseline = 2;
+			} elsif ($rawline =~ /^\+/) {
+				my $comment = "";
+				if ($realfile =~ /\.(h|s|S)$/) {
+					$comment = '/*';
+				} elsif ($realfile =~ /\.(c|dts|dtsi)$/) {
+					$comment = '//';
+				} elsif (($checklicenseline == 2) || $realfile =~ /\.(sh|pl|py|awk|tc|yaml)$/) {
+					$comment = '#';
+				} elsif ($realfile =~ /\.rst$/) {
+					$comment = '..';
+				}
+
+# check SPDX comment style for .[chsS] files
+				if ($realfile =~ /\.[chsS]$/ &&
+				    $rawline =~ /SPDX-License-Identifier:/ &&
+				    $rawline !~ m@^\+\s*\Q$comment\E\s*@) {
+					WARN("SPDX_LICENSE_TAG",
+					     "Improper SPDX comment style for '$realfile', please use '$comment' instead\n" . $herecurr);
+				}
+
+				if ($comment !~ /^$/ &&
+				    $rawline !~ m@^\+\Q$comment\E SPDX-License-Identifier: @) {
+					WARN("SPDX_LICENSE_TAG",
+					     "Missing or malformed SPDX-License-Identifier tag in line $checklicenseline\n" . $herecurr);
+				} elsif ($rawline =~ /(SPDX-License-Identifier: .*)/) {
+					my $spdx_license = $1;
+					if (!is_SPDX_License_valid($spdx_license)) {
+						WARN("SPDX_LICENSE_TAG",
+						     "'$spdx_license' is not supported in LICENSES/...\n" . $herecurr);
+					}
+					if ($realfile =~ m@^Documentation/devicetree/bindings/@ &&
+					    not $spdx_license =~ /GPL-2\.0.*BSD-2-Clause/) {
+						my $msg_level = \&WARN;
+						$msg_level = \&CHK if ($file);
+						if (&{$msg_level}("SPDX_LICENSE_TAG",
+
+								  "DT binding documents should be licensed (GPL-2.0-only OR BSD-2-Clause)\n" . $herecurr) &&
+						    $fix) {
+							$fixed[$fixlinenr] =~ s/SPDX-License-Identifier: .*/SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)/;
+						}
+					}
+				}
+			}
+		}
+
+# check we are in a valid source file if not then ignore this hunk
+		next if ($realfile !~ /\.(h|c|s|S|sh|dtsi|dts)$/);
+
+# check for using SPDX-License-Identifier on the wrong line number
+		if ($realline != $checklicenseline &&
+		    $rawline =~ /\bSPDX-License-Identifier:/ &&
+		    substr($line, @-, @+ - @-) eq "$;" x (@+ - @-)) {
+			WARN("SPDX_LICENSE_TAG",
+			     "Misplaced SPDX-License-Identifier tag - use line $checklicenseline instead\n" . $herecurr);
+		}
+
+# line length limit (with some exclusions)
+#
+# There are a few types of lines that may extend beyond $max_line_length:
+#	logging functions like pr_info that end in a string
+#	lines with a single string
+#	#defines that are a single string
+#	lines with an RFC3986 like URL
+#
+# There are 3 different line length message types:
+# LONG_LINE_COMMENT	a comment starts before but extends beyond $max_line_length
+# LONG_LINE_STRING	a string starts before but extends beyond $max_line_length
+# LONG_LINE		all other lines longer than $max_line_length
+#
+# if LONG_LINE is ignored, the other 2 types are also ignored
+#
+
+		if ($line =~ /^\+/ && $length > $max_line_length) {
+			my $msg_type = "LONG_LINE";
+
+			# Check the allowed long line types first
+
+			# logging functions that end in a string that starts
+			# before $max_line_length
+			if ($line =~ /^\+\s*$logFunctions\s*\(\s*(?:(?:KERN_\S+\s*|[^"]*))?($String\s*(?:|,|\)\s*;)\s*)$/ &&
+			    length(expand_tabs(substr($line, 1, length($line) - length($1) - 1))) <= $max_line_length) {
+				$msg_type = "";
+
+			# lines with only strings (w/ possible termination)
+			# #defines with only strings
+			} elsif ($line =~ /^\+\s*$String\s*(?:\s*|,|\)\s*;)\s*$/ ||
+				 $line =~ /^\+\s*#\s*define\s+\w+\s+$String$/) {
+				$msg_type = "";
+
+			# More special cases
+			} elsif ($line =~ /^\+.*\bEFI_GUID\s*\(/ ||
+				 $line =~ /^\+\s*(?:\w+)?\s*DEFINE_PER_CPU/) {
+				$msg_type = "";
+
+			# URL ($rawline is used in case the URL is in a comment)
+			} elsif ($rawline =~ /^\+.*\b[a-z][\w\.\+\-]*:\/\/\S+/i) {
+				$msg_type = "";
+
+			# Otherwise set the alternate message types
+
+			# a comment starts before $max_line_length
+			} elsif ($line =~ /($;[\s$;]*)$/ &&
+				 length(expand_tabs(substr($line, 1, length($line) - length($1) - 1))) <= $max_line_length) {
+				$msg_type = "LONG_LINE_COMMENT"
+
+			# a quoted string starts before $max_line_length
+			} elsif ($sline =~ /\s*($String(?:\s*(?:\\|,\s*|\)\s*;\s*))?)$/ &&
+				 length(expand_tabs(substr($line, 1, length($line) - length($1) - 1))) <= $max_line_length) {
+				$msg_type = "LONG_LINE_STRING"
+			}
+
+			if ($msg_type ne "" &&
+			    (show_type("LONG_LINE") || show_type($msg_type))) {
+				my $msg_level = \&WARN;
+				$msg_level = \&CHK if ($file);
+				&{$msg_level}($msg_type,
+					      "line length of $length exceeds $max_line_length columns\n" . $herecurr);
+			}
+		}
+
+# check for adding lines without a newline.
+		if ($line =~ /^\+/ && defined $lines[$linenr] && $lines[$linenr] =~ /^\\ No newline at end of file/) {
+			WARN("MISSING_EOF_NEWLINE",
+			     "adding a line without newline at end of file\n" . $herecurr);
+		}
+
+# check we are in a valid source file C or perl if not then ignore this hunk
+		next if ($realfile !~ /\.(h|c|pl|dtsi|dts)$/);
+
+# at the beginning of a line any tabs must come first and anything
+# more than $tabsize must use tabs.
+		if ($rawline =~ /^\+\s* \t\s*\S/ ||
+		    $rawline =~ /^\+\s*        \s*/) {
+			my $herevet = "$here\n" . cat_vet($rawline) . "\n";
+			$rpt_cleaners = 1;
+			if (ERROR("CODE_INDENT",
+				  "code indent should use tabs where possible\n" . $herevet) &&
+			    $fix) {
+				$fixed[$fixlinenr] =~ s/^\+([ \t]+)/"\+" . tabify($1)/e;
+			}
+		}
+
+# check for space before tabs.
+		if ($rawline =~ /^\+/ && $rawline =~ / \t/) {
+			my $herevet = "$here\n" . cat_vet($rawline) . "\n";
+			if (WARN("SPACE_BEFORE_TAB",
+				"please, no space before tabs\n" . $herevet) &&
+			    $fix) {
+				while ($fixed[$fixlinenr] =~
+					   s/(^\+.*) {$tabsize,$tabsize}\t/$1\t\t/) {}
+				while ($fixed[$fixlinenr] =~
+					   s/(^\+.*) +\t/$1\t/) {}
+			}
+		}
+
+# check for assignments on the start of a line
+		if ($sline =~ /^\+\s+($Assignment)[^=]/) {
+			CHK("ASSIGNMENT_CONTINUATIONS",
+			    "Assignment operator '$1' should be on the previous line\n" . $hereprev);
+		}
+
+# check for && or || at the start of a line
+		if ($rawline =~ /^\+\s*(&&|\|\|)/) {
+			CHK("LOGICAL_CONTINUATIONS",
+			    "Logical continuations should be on the previous line\n" . $hereprev);
+		}
+
+# check indentation starts on a tab stop
+		if ($perl_version_ok &&
+		    $sline =~ /^\+\t+( +)(?:$c90_Keywords\b|\{\s*$|\}\s*(?:else\b|while\b|\s*$)|$Declare\s*$Ident\s*[;=])/) {
+			my $indent = length($1);
+			if ($indent % $tabsize) {
+				if (WARN("TABSTOP",
+					 "Statements should start on a tabstop\n" . $herecurr) &&
+				    $fix) {
+					$fixed[$fixlinenr] =~ s@(^\+\t+) +@$1 . "\t" x ($indent/$tabsize)@e;
+				}
+			}
+		}
+
+# check multi-line statement indentation matches previous line
+		if ($perl_version_ok &&
+		    $prevline =~ /^\+([ \t]*)((?:$c90_Keywords(?:\s+if)\s*)|(?:$Declare\s*)?(?:$Ident|\(\s*\*\s*$Ident\s*\))\s*|(?:\*\s*)*$Lval\s*=\s*$Ident\s*)\(.*(\&\&|\|\||,)\s*$/) {
+			$prevline =~ /^\+(\t*)(.*)$/;
+			my $oldindent = $1;
+			my $rest = $2;
+
+			my $pos = pos_last_openparen($rest);
+			if ($pos >= 0) {
+				$line =~ /^(\+| )([ \t]*)/;
+				my $newindent = $2;
+
+				my $goodtabindent = $oldindent .
+					"\t" x ($pos / $tabsize) .
+					" "  x ($pos % $tabsize);
+				my $goodspaceindent = $oldindent . " "  x $pos;
+
+				if ($newindent ne $goodtabindent &&
+				    $newindent ne $goodspaceindent) {
+
+					if (CHK("PARENTHESIS_ALIGNMENT",
+						"Alignment should match open parenthesis\n" . $hereprev) &&
+					    $fix && $line =~ /^\+/) {
+						$fixed[$fixlinenr] =~
+						    s/^\+[ \t]*/\+$goodtabindent/;
+					}
+				}
+			}
+		}
+
+# check for space after cast like "(int) foo" or "(struct foo) bar"
+# avoid checking a few false positives:
+#   "sizeof(<type>)" or "__alignof__(<type>)"
+#   function pointer declarations like "(*foo)(int) = bar;"
+#   structure definitions like "(struct foo) { 0 };"
+#   multiline macros that define functions
+#   known attributes or the __attribute__ keyword
+		if ($line =~ /^\+(.*)\(\s*$Type\s*\)([ \t]++)((?![={]|\\$|$Attribute|__attribute__))/ &&
+		    (!defined($1) || $1 !~ /\b(?:sizeof|__alignof__)\s*$/)) {
+			if (CHK("SPACING",
+				"No space is necessary after a cast\n" . $herecurr) &&
+			    $fix) {
+				$fixed[$fixlinenr] =~
+				    s/(\(\s*$Type\s*\))[ \t]+/$1/;
+			}
+		}
+
+# Block comment styles
+# Networking with an initial /*
+		if ($realfile =~ m@^(drivers/net/|net/)@ &&
+		    $prevrawline =~ /^\+[ \t]*\/\*[ \t]*$/ &&
+		    $rawline =~ /^\+[ \t]*\*/ &&
+		    $realline > 2) {
+			WARN("NETWORKING_BLOCK_COMMENT_STYLE",
+			     "networking block comments don't use an empty /* line, use /* Comment...\n" . $hereprev);
+		}
+
+# Block comments use * on subsequent lines
+		if ($prevline =~ /$;[ \t]*$/ &&			#ends in comment
+		    $prevrawline =~ /^\+.*?\/\*/ &&		#starting /*
+		    $prevrawline !~ /\*\/[ \t]*$/ &&		#no trailing */
+		    $rawline =~ /^\+/ &&			#line is new
+		    $rawline !~ /^\+[ \t]*\*/) {		#no leading *
+			WARN("BLOCK_COMMENT_STYLE",
+			     "Block comments use * on subsequent lines\n" . $hereprev);
+		}
+
+# Block comments use */ on trailing lines
+		if ($rawline !~ m@^\+[ \t]*\*/[ \t]*$@ &&	#trailing */
+		    $rawline !~ m@^\+.*/\*.*\*/[ \t]*$@ &&	#inline /*...*/
+		    $rawline !~ m@^\+.*\*{2,}/[ \t]*$@ &&	#trailing **/
+		    $rawline =~ m@^\+[ \t]*.+\*\/[ \t]*$@) {	#non blank */
+			WARN("BLOCK_COMMENT_STYLE",
+			     "Block comments use a trailing */ on a separate line\n" . $herecurr);
+		}
+
+# Block comment * alignment
+		if ($prevline =~ /$;[ \t]*$/ &&			#ends in comment
+		    $line =~ /^\+[ \t]*$;/ &&			#leading comment
+		    $rawline =~ /^\+[ \t]*\*/ &&		#leading *
+		    (($prevrawline =~ /^\+.*?\/\*/ &&		#leading /*
+		      $prevrawline !~ /\*\/[ \t]*$/) ||		#no trailing */
+		     $prevrawline =~ /^\+[ \t]*\*/)) {		#leading *
+			my $oldindent;
+			$prevrawline =~ m@^\+([ \t]*/?)\*@;
+			if (defined($1)) {
+				$oldindent = expand_tabs($1);
+			} else {
+				$prevrawline =~ m@^\+(.*/?)\*@;
+				$oldindent = expand_tabs($1);
+			}
+			$rawline =~ m@^\+([ \t]*)\*@;
+			my $newindent = $1;
+			$newindent = expand_tabs($newindent);
+			if (length($oldindent) ne length($newindent)) {
+				WARN("BLOCK_COMMENT_STYLE",
+				     "Block comments should align the * on each line\n" . $hereprev);
+			}
+		}
+
+# check for missing blank lines after struct/union declarations
+# with exceptions for various attributes and macros
+		if ($prevline =~ /^[\+ ]};?\s*$/ &&
+		    $line =~ /^\+/ &&
+		    !($line =~ /^\+\s*$/ ||
+		      $line =~ /^\+\s*EXPORT_SYMBOL/ ||
+		      $line =~ /^\+\s*MODULE_/i ||
+		      $line =~ /^\+\s*\#\s*(?:end|elif|else)/ ||
+		      $line =~ /^\+[a-z_]*init/ ||
+		      $line =~ /^\+\s*(?:static\s+)?[A-Z_]*ATTR/ ||
+		      $line =~ /^\+\s*DECLARE/ ||
+		      $line =~ /^\+\s*builtin_[\w_]*driver/ ||
+		      $line =~ /^\+\s*__setup/)) {
+			if (CHK("LINE_SPACING",
+				"Please use a blank line after function/struct/union/enum declarations\n" . $hereprev) &&
+			    $fix) {
+				fix_insert_line($fixlinenr, "\+");
+			}
+		}
+
+# check for multiple consecutive blank lines
+		if ($prevline =~ /^[\+ ]\s*$/ &&
+		    $line =~ /^\+\s*$/ &&
+		    $last_blank_line != ($linenr - 1)) {
+			if (CHK("LINE_SPACING",
+				"Please don't use multiple blank lines\n" . $hereprev) &&
+			    $fix) {
+				fix_delete_line($fixlinenr, $rawline);
+			}
+
+			$last_blank_line = $linenr;
+		}
+
+# check for missing blank lines after declarations
+		if ($sline =~ /^\+\s+\S/ &&			#Not at char 1
+			# actual declarations
+		    ($prevline =~ /^\+\s+$Declare\s*$Ident\s*[=,;:\[]/ ||
+			# function pointer declarations
+		     $prevline =~ /^\+\s+$Declare\s*\(\s*\*\s*$Ident\s*\)\s*[=,;:\[\(]/ ||
+			# foo bar; where foo is some local typedef or #define
+		     $prevline =~ /^\+\s+$Ident(?:\s+|\s*\*\s*)$Ident\s*[=,;\[]/ ||
+			# known declaration macros
+		     $prevline =~ /^\+\s+$declaration_macros/) &&
+			# for "else if" which can look like "$Ident $Ident"
+		    !($prevline =~ /^\+\s+$c90_Keywords\b/ ||
+			# other possible extensions of declaration lines
+		      $prevline =~ /(?:$Compare|$Assignment|$Operators)\s*$/ ||
+			# not starting a section or a macro "\" extended line
+		      $prevline =~ /(?:\{\s*|\\)$/) &&
+			# looks like a declaration
+		    !($sline =~ /^\+\s+$Declare\s*$Ident\s*[=,;:\[]/ ||
+			# function pointer declarations
+		      $sline =~ /^\+\s+$Declare\s*\(\s*\*\s*$Ident\s*\)\s*[=,;:\[\(]/ ||
+			# foo bar; where foo is some local typedef or #define
+		      $sline =~ /^\+\s+$Ident(?:\s+|\s*\*\s*)$Ident\s*[=,;\[]/ ||
+			# known declaration macros
+		      $sline =~ /^\+\s+$declaration_macros/ ||
+			# start of struct or union or enum
+		      $sline =~ /^\+\s+(?:static\s+)?(?:const\s+)?(?:union|struct|enum|typedef)\b/ ||
+			# start or end of block or continuation of declaration
+		      $sline =~ /^\+\s+(?:$|[\{\}\.\#\"\?\:\(\[])/ ||
+			# bitfield continuation
+		      $sline =~ /^\+\s+$Ident\s*:\s*\d+\s*[,;]/ ||
+			# other possible extensions of declaration lines
+		      $sline =~ /^\+\s+\(?\s*(?:$Compare|$Assignment|$Operators)/) &&
+			# indentation of previous and current line are the same
+		    (($prevline =~ /\+(\s+)\S/) && $sline =~ /^\+$1\S/)) {
+			if (WARN("LINE_SPACING",
+				 "Missing a blank line after declarations\n" . $hereprev) &&
+			    $fix) {
+				fix_insert_line($fixlinenr, "\+");
+			}
+		}
+
+# check for spaces at the beginning of a line.
+# Exceptions:
+#  1) within comments
+#  2) indented preprocessor commands
+#  3) hanging labels
+		if ($rawline =~ /^\+ / && $line !~ /^\+ *(?:$;|#|$Ident:)/)  {
+			my $herevet = "$here\n" . cat_vet($rawline) . "\n";
+			if (WARN("LEADING_SPACE",
+				 "please, no spaces at the start of a line\n" . $herevet) &&
+			    $fix) {
+				$fixed[$fixlinenr] =~ s/^\+([ \t]+)/"\+" . tabify($1)/e;
+			}
+		}
+
+# check we are in a valid C source file if not then ignore this hunk
+		next if ($realfile !~ /\.(h|c)$/);
+
+# check for unusual line ending [ or (
+		if ($line =~ /^\+.*([\[\(])\s*$/) {
+			CHK("OPEN_ENDED_LINE",
+			    "Lines should not end with a '$1'\n" . $herecurr);
+		}
+
+# check if this appears to be the start function declaration, save the name
+		if ($sline =~ /^\+\{\s*$/ &&
+		    $prevline =~ /^\+(?:(?:(?:$Storage|$Inline)\s*)*\s*$Type\s*)?($Ident)\(/) {
+			$context_function = $1;
+		}
+
+# check if this appears to be the end of function declaration
+		if ($sline =~ /^\+\}\s*$/) {
+			undef $context_function;
+		}
+
+# check indentation of any line with a bare else
+# (but not if it is a multiple line "if (foo) return bar; else return baz;")
+# if the previous line is a break or return and is indented 1 tab more...
+		if ($sline =~ /^\+([\t]+)(?:}[ \t]*)?else(?:[ \t]*{)?\s*$/) {
+			my $tabs = length($1) + 1;
+			if ($prevline =~ /^\+\t{$tabs,$tabs}break\b/ ||
+			    ($prevline =~ /^\+\t{$tabs,$tabs}return\b/ &&
+			     defined $lines[$linenr] &&
+			     $lines[$linenr] !~ /^[ \+]\t{$tabs,$tabs}return/)) {
+				WARN("UNNECESSARY_ELSE",
+				     "else is not generally useful after a break or return\n" . $hereprev);
+			}
+		}
+
+# check indentation of a line with a break;
+# if the previous line is a goto or return and is indented the same # of tabs
+		if ($sline =~ /^\+([\t]+)break\s*;\s*$/) {
+			my $tabs = $1;
+			if ($prevline =~ /^\+$tabs(?:goto|return)\b/) {
+				WARN("UNNECESSARY_BREAK",
+				     "break is not useful after a goto or return\n" . $hereprev);
+			}
+		}
+
+# check for RCS/CVS revision markers
+		if ($rawline =~ /^\+.*\$(Revision|Log|Id)(?:\$|)/) {
+			WARN("CVS_KEYWORD",
+			     "CVS style keyword markers, these will _not_ be updated\n". $herecurr);
+		}
+
+# check for old HOTPLUG __dev<foo> section markings
+		if ($line =~ /\b(__dev(init|exit)(data|const|))\b/) {
+			WARN("HOTPLUG_SECTION",
+			     "Using $1 is unnecessary\n" . $herecurr);
+		}
+
+# Check for potential 'bare' types
+		my ($stat, $cond, $line_nr_next, $remain_next, $off_next,
+		    $realline_next);
+#print "LINE<$line>\n";
+		if ($linenr > $suppress_statement &&
+		    $realcnt && $sline =~ /.\s*\S/) {
+			($stat, $cond, $line_nr_next, $remain_next, $off_next) =
+				ctx_statement_block($linenr, $realcnt, 0);
+			$stat =~ s/\n./\n /g;
+			$cond =~ s/\n./\n /g;
+
+#print "linenr<$linenr> <$stat>\n";
+			# If this statement has no statement boundaries within
+			# it there is no point in retrying a statement scan
+			# until we hit end of it.
+			my $frag = $stat; $frag =~ s/;+\s*$//;
+			if ($frag !~ /(?:{|;)/) {
+#print "skip<$line_nr_next>\n";
+				$suppress_statement = $line_nr_next;
+			}
+
+			# Find the real next line.
+			$realline_next = $line_nr_next;
+			if (defined $realline_next &&
+			    (!defined $lines[$realline_next - 1] ||
+			     substr($lines[$realline_next - 1], $off_next) =~ /^\s*$/)) {
+				$realline_next++;
+			}
+
+			my $s = $stat;
+			$s =~ s/{.*$//s;
+
+			# Ignore goto labels.
+			if ($s =~ /$Ident:\*$/s) {
+
+			# Ignore functions being called
+			} elsif ($s =~ /^.\s*$Ident\s*\(/s) {
+
+			} elsif ($s =~ /^.\s*else\b/s) {
+
+			# declarations always start with types
+			} elsif ($prev_values eq 'E' && $s =~ /^.\s*(?:$Storage\s+)?(?:$Inline\s+)?(?:const\s+)?((?:\s*$Ident)+?)\b(?:\s+$Sparse)?\s*\**\s*(?:$Ident|\(\*[^\)]*\))(?:\s*$Modifier)?\s*(?:;|=|,|\()/s) {
+				my $type = $1;
+				$type =~ s/\s+/ /g;
+				possible($type, "A:" . $s);
+
+			# definitions in global scope can only start with types
+			} elsif ($s =~ /^.(?:$Storage\s+)?(?:$Inline\s+)?(?:const\s+)?($Ident)\b\s*(?!:)/s) {
+				possible($1, "B:" . $s);
+			}
+
+			# any (foo ... *) is a pointer cast, and foo is a type
+			while ($s =~ /\(($Ident)(?:\s+$Sparse)*[\s\*]+\s*\)/sg) {
+				possible($1, "C:" . $s);
+			}
+
+			# Check for any sort of function declaration.
+			# int foo(something bar, other baz);
+			# void (*store_gdt)(x86_descr_ptr *);
+			if ($prev_values eq 'E' && $s =~ /^(.(?:typedef\s*)?(?:(?:$Storage|$Inline)\s*)*\s*$Type\s*(?:\b$Ident|\(\*\s*$Ident\))\s*)\(/s) {
+				my ($name_len) = length($1);
+
+				my $ctx = $s;
+				substr($ctx, 0, $name_len + 1, '');
+				$ctx =~ s/\)[^\)]*$//;
+
+				for my $arg (split(/\s*,\s*/, $ctx)) {
+					if ($arg =~ /^(?:const\s+)?($Ident)(?:\s+$Sparse)*\s*\**\s*(:?\b$Ident)?$/s || $arg =~ /^($Ident)$/s) {
+
+						possible($1, "D:" . $s);
+					}
+				}
+			}
+
+		}
+
+#
+# Checks which may be anchored in the context.
+#
+
+# Check for switch () and associated case and default
+# statements should be at the same indent.
+		if ($line=~/\bswitch\s*\(.*\)/) {
+			my $err = '';
+			my $sep = '';
+			my @ctx = ctx_block_outer($linenr, $realcnt);
+			shift(@ctx);
+			for my $ctx (@ctx) {
+				my ($clen, $cindent) = line_stats($ctx);
+				if ($ctx =~ /^\+\s*(case\s+|default:)/ &&
+							$indent != $cindent) {
+					$err .= "$sep$ctx\n";
+					$sep = '';
+				} else {
+					$sep = "[...]\n";
+				}
+			}
+			if ($err ne '') {
+				ERROR("SWITCH_CASE_INDENT_LEVEL",
+				      "switch and case should be at the same indent\n$hereline$err");
+			}
+		}
+
+# if/while/etc brace do not go on next line, unless defining a do while loop,
+# or if that brace on the next line is for something else
+		if ($line =~ /(.*)\b((?:if|while|for|switch|(?:[a-z_]+|)for_each[a-z_]+)\s*\(|do\b|else\b)/ && $line !~ /^.\s*\#/) {
+			my $pre_ctx = "$1$2";
+
+			my ($level, @ctx) = ctx_statement_level($linenr, $realcnt, 0);
+
+			if ($line =~ /^\+\t{6,}/) {
+				WARN("DEEP_INDENTATION",
+				     "Too many leading tabs - consider code refactoring\n" . $herecurr);
+			}
+
+			my $ctx_cnt = $realcnt - $#ctx - 1;
+			my $ctx = join("\n", @ctx);
+
+			my $ctx_ln = $linenr;
+			my $ctx_skip = $realcnt;
+
+			while ($ctx_skip > $ctx_cnt || ($ctx_skip == $ctx_cnt &&
+					defined $lines[$ctx_ln - 1] &&
+					$lines[$ctx_ln - 1] =~ /^-/)) {
+				##print "SKIP<$ctx_skip> CNT<$ctx_cnt>\n";
+				$ctx_skip-- if (!defined $lines[$ctx_ln - 1] || $lines[$ctx_ln - 1] !~ /^-/);
+				$ctx_ln++;
+			}
+
+			#print "realcnt<$realcnt> ctx_cnt<$ctx_cnt>\n";
+			#print "pre<$pre_ctx>\nline<$line>\nctx<$ctx>\nnext<$lines[$ctx_ln - 1]>\n";
+
+			if ($ctx !~ /{\s*/ && defined($lines[$ctx_ln - 1]) && $lines[$ctx_ln - 1] =~ /^\+\s*{/) {
+				ERROR("OPEN_BRACE",
+				      "that open brace { should be on the previous line\n" .
+					"$here\n$ctx\n$rawlines[$ctx_ln - 1]\n");
+			}
+			if ($level == 0 && $pre_ctx !~ /}\s*while\s*\($/ &&
+			    $ctx =~ /\)\s*\;\s*$/ &&
+			    defined $lines[$ctx_ln - 1])
+			{
+				my ($nlength, $nindent) = line_stats($lines[$ctx_ln - 1]);
+				if ($nindent > $indent) {
+					WARN("TRAILING_SEMICOLON",
+					     "trailing semicolon indicates no statements, indent implies otherwise\n" .
+						"$here\n$ctx\n$rawlines[$ctx_ln - 1]\n");
+				}
+			}
+		}
+
+# Check relative indent for conditionals and blocks.
+		if ($line =~ /\b(?:(?:if|while|for|(?:[a-z_]+|)for_each[a-z_]+)\s*\(|(?:do|else)\b)/ && $line !~ /^.\s*#/ && $line !~ /\}\s*while\s*/) {
+			($stat, $cond, $line_nr_next, $remain_next, $off_next) =
+				ctx_statement_block($linenr, $realcnt, 0)
+					if (!defined $stat);
+			my ($s, $c) = ($stat, $cond);
+
+			substr($s, 0, length($c), '');
+
+			# remove inline comments
+			$s =~ s/$;/ /g;
+			$c =~ s/$;/ /g;
+
+			# Find out how long the conditional actually is.
+			my @newlines = ($c =~ /\n/gs);
+			my $cond_lines = 1 + $#newlines;
+
+			# Make sure we remove the line prefixes as we have
+			# none on the first line, and are going to readd them
+			# where necessary.
+			$s =~ s/\n./\n/gs;
+			while ($s =~ /\n\s+\\\n/) {
+				$cond_lines += $s =~ s/\n\s+\\\n/\n/g;
+			}
+
+			# We want to check the first line inside the block
+			# starting at the end of the conditional, so remove:
+			#  1) any blank line termination
+			#  2) any opening brace { on end of the line
+			#  3) any do (...) {
+			my $continuation = 0;
+			my $check = 0;
+			$s =~ s/^.*\bdo\b//;
+			$s =~ s/^\s*{//;
+			if ($s =~ s/^\s*\\//) {
+				$continuation = 1;
+			}
+			if ($s =~ s/^\s*?\n//) {
+				$check = 1;
+				$cond_lines++;
+			}
+
+			# Also ignore a loop construct at the end of a
+			# preprocessor statement.
+			if (($prevline =~ /^.\s*#\s*define\s/ ||
+			    $prevline =~ /\\\s*$/) && $continuation == 0) {
+				$check = 0;
+			}
+
+			my $cond_ptr = -1;
+			$continuation = 0;
+			while ($cond_ptr != $cond_lines) {
+				$cond_ptr = $cond_lines;
+
+				# If we see an #else/#elif then the code
+				# is not linear.
+				if ($s =~ /^\s*\#\s*(?:else|elif)/) {
+					$check = 0;
+				}
+
+				# Ignore:
+				#  1) blank lines, they should be at 0,
+				#  2) preprocessor lines, and
+				#  3) labels.
+				if ($continuation ||
+				    $s =~ /^\s*?\n/ ||
+				    $s =~ /^\s*#\s*?/ ||
+				    $s =~ /^\s*$Ident\s*:/) {
+					$continuation = ($s =~ /^.*?\\\n/) ? 1 : 0;
+					if ($s =~ s/^.*?\n//) {
+						$cond_lines++;
+					}
+				}
+			}
+
+			my (undef, $sindent) = line_stats("+" . $s);
+			my $stat_real = raw_line($linenr, $cond_lines);
+
+			# Check if either of these lines are modified, else
+			# this is not this patch's fault.
+			if (!defined($stat_real) ||
+			    $stat !~ /^\+/ && $stat_real !~ /^\+/) {
+				$check = 0;
+			}
+			if (defined($stat_real) && $cond_lines > 1) {
+				$stat_real = "[...]\n$stat_real";
+			}
+
+			#print "line<$line> prevline<$prevline> indent<$indent> sindent<$sindent> check<$check> continuation<$continuation> s<$s> cond_lines<$cond_lines> stat_real<$stat_real> stat<$stat>\n";
+
+			if ($check && $s ne '' &&
+			    (($sindent % $tabsize) != 0 ||
+			     ($sindent < $indent) ||
+			     ($sindent == $indent &&
+			      ($s !~ /^\s*(?:\}|\{|else\b)/)) ||
+			     ($sindent > $indent + $tabsize))) {
+				WARN("SUSPECT_CODE_INDENT",
+				     "suspect code indent for conditional statements ($indent, $sindent)\n" . $herecurr . "$stat_real\n");
+			}
+		}
+
+		# Track the 'values' across context and added lines.
+		my $opline = $line; $opline =~ s/^./ /;
+		my ($curr_values, $curr_vars) =
+				annotate_values($opline . "\n", $prev_values);
+		$curr_values = $prev_values . $curr_values;
+		if ($dbg_values) {
+			my $outline = $opline; $outline =~ s/\t/ /g;
+			print "$linenr > .$outline\n";
+			print "$linenr > $curr_values\n";
+			print "$linenr >  $curr_vars\n";
+		}
+		$prev_values = substr($curr_values, -1);
+
+#ignore lines not being added
+		next if ($line =~ /^[^\+]/);
+
+# check for dereferences that span multiple lines
+		if ($prevline =~ /^\+.*$Lval\s*(?:\.|->)\s*$/ &&
+		    $line =~ /^\+\s*(?!\#\s*(?!define\s+|if))\s*$Lval/) {
+			$prevline =~ /($Lval\s*(?:\.|->))\s*$/;
+			my $ref = $1;
+			$line =~ /^.\s*($Lval)/;
+			$ref .= $1;
+			$ref =~ s/\s//g;
+			WARN("MULTILINE_DEREFERENCE",
+			     "Avoid multiple line dereference - prefer '$ref'\n" . $hereprev);
+		}
+
+# check for declarations of signed or unsigned without int
+		while ($line =~ m{\b($Declare)\s*(?!char\b|short\b|int\b|long\b)\s*($Ident)?\s*[=,;\[\)\(]}g) {
+			my $type = $1;
+			my $var = $2;
+			$var = "" if (!defined $var);
+			if ($type =~ /^(?:(?:$Storage|$Inline|$Attribute)\s+)*((?:un)?signed)((?:\s*\*)*)\s*$/) {
+				my $sign = $1;
+				my $pointer = $2;
+
+				$pointer = "" if (!defined $pointer);
+
+				if (WARN("UNSPECIFIED_INT",
+					 "Prefer '" . trim($sign) . " int" . rtrim($pointer) . "' to bare use of '$sign" . rtrim($pointer) . "'\n" . $herecurr) &&
+				    $fix) {
+					my $decl = trim($sign) . " int ";
+					my $comp_pointer = $pointer;
+					$comp_pointer =~ s/\s//g;
+					$decl .= $comp_pointer;
+					$decl = rtrim($decl) if ($var eq "");
+					$fixed[$fixlinenr] =~ s@\b$sign\s*\Q$pointer\E\s*$var\b@$decl$var@;
+				}
+			}
+		}
+
+# TEST: allow direct testing of the type matcher.
+		if ($dbg_type) {
+			if ($line =~ /^.\s*$Declare\s*$/) {
+				ERROR("TEST_TYPE",
+				      "TEST: is type\n" . $herecurr);
+			} elsif ($dbg_type > 1 && $line =~ /^.+($Declare)/) {
+				ERROR("TEST_NOT_TYPE",
+				      "TEST: is not type ($1 is)\n". $herecurr);
+			}
+			next;
+		}
+# TEST: allow direct testing of the attribute matcher.
+		if ($dbg_attr) {
+			if ($line =~ /^.\s*$Modifier\s*$/) {
+				ERROR("TEST_ATTR",
+				      "TEST: is attr\n" . $herecurr);
+			} elsif ($dbg_attr > 1 && $line =~ /^.+($Modifier)/) {
+				ERROR("TEST_NOT_ATTR",
+				      "TEST: is not attr ($1 is)\n". $herecurr);
+			}
+			next;
+		}
+
+# check for initialisation to aggregates open brace on the next line
+		if ($line =~ /^.\s*{/ &&
+		    $prevline =~ /(?:^|[^=])=\s*$/) {
+			if (ERROR("OPEN_BRACE",
+				  "that open brace { should be on the previous line\n" . $hereprev) &&
+			    $fix && $prevline =~ /^\+/ && $line =~ /^\+/) {
+				fix_delete_line($fixlinenr - 1, $prevrawline);
+				fix_delete_line($fixlinenr, $rawline);
+				my $fixedline = $prevrawline;
+				$fixedline =~ s/\s*=\s*$/ = {/;
+				fix_insert_line($fixlinenr, $fixedline);
+				$fixedline = $line;
+				$fixedline =~ s/^(.\s*)\{\s*/$1/;
+				fix_insert_line($fixlinenr, $fixedline);
+			}
+		}
+
+#
+# Checks which are anchored on the added line.
+#
+
+# check for malformed paths in #include statements (uses RAW line)
+		if ($rawline =~ m{^.\s*\#\s*include\s+[<"](.*)[">]}) {
+			my $path = $1;
+			if ($path =~ m{//}) {
+				ERROR("MALFORMED_INCLUDE",
+				      "malformed #include filename\n" . $herecurr);
+			}
+			if ($path =~ "^uapi/" && $realfile =~ m@\binclude/uapi/@) {
+				ERROR("UAPI_INCLUDE",
+				      "No #include in ...include/uapi/... should use a uapi/ path prefix\n" . $herecurr);
+			}
+		}
+
+# no C99 // comments
+		if ($line =~ m{//}) {
+			if (ERROR("C99_COMMENTS",
+				  "do not use C99 // comments\n" . $herecurr) &&
+			    $fix) {
+				my $line = $fixed[$fixlinenr];
+				if ($line =~ /\/\/(.*)$/) {
+					my $comment = trim($1);
+					$fixed[$fixlinenr] =~ s@\/\/(.*)$@/\* $comment \*/@;
+				}
+			}
+		}
+		# Remove C99 comments.
+		$line =~ s@//.*@@;
+		$opline =~ s@//.*@@;
+
+# EXPORT_SYMBOL should immediately follow the thing it is exporting, consider
+# the whole statement.
+#print "APW <$lines[$realline_next - 1]>\n";
+		if (defined $realline_next &&
+		    exists $lines[$realline_next - 1] &&
+		    !defined $suppress_export{$realline_next} &&
+		    ($lines[$realline_next - 1] =~ /EXPORT_SYMBOL.*\((.*)\)/ ||
+		     $lines[$realline_next - 1] =~ /EXPORT_UNUSED_SYMBOL.*\((.*)\)/)) {
+			# Handle definitions which produce identifiers with
+			# a prefix:
+			#   XXX(foo);
+			#   EXPORT_SYMBOL(something_foo);
+			my $name = $1;
+			if ($stat =~ /^(?:.\s*}\s*\n)?.([A-Z_]+)\s*\(\s*($Ident)/ &&
+			    $name =~ /^${Ident}_$2/) {
+#print "FOO C name<$name>\n";
+				$suppress_export{$realline_next} = 1;
+
+			} elsif ($stat !~ /(?:
+				\n.}\s*$|
+				^.DEFINE_$Ident\(\Q$name\E\)|
+				^.DECLARE_$Ident\(\Q$name\E\)|
+				^.LIST_HEAD\(\Q$name\E\)|
+				^.(?:$Storage\s+)?$Type\s*\(\s*\*\s*\Q$name\E\s*\)\s*\(|
+				\b\Q$name\E(?:\s+$Attribute)*\s*(?:;|=|\[|\()
+			    )/x) {
+#print "FOO A<$lines[$realline_next - 1]> stat<$stat> name<$name>\n";
+				$suppress_export{$realline_next} = 2;
+			} else {
+				$suppress_export{$realline_next} = 1;
+			}
+		}
+		if (!defined $suppress_export{$linenr} &&
+		    $prevline =~ /^.\s*$/ &&
+		    ($line =~ /EXPORT_SYMBOL.*\((.*)\)/ ||
+		     $line =~ /EXPORT_UNUSED_SYMBOL.*\((.*)\)/)) {
+#print "FOO B <$lines[$linenr - 1]>\n";
+			$suppress_export{$linenr} = 2;
+		}
+		if (defined $suppress_export{$linenr} &&
+		    $suppress_export{$linenr} == 2) {
+			WARN("EXPORT_SYMBOL",
+			     "EXPORT_SYMBOL(foo); should immediately follow its function/variable\n" . $herecurr);
+		}
+
+# check for global initialisers.
+		if ($line =~ /^\+$Type\s*$Ident(?:\s+$Modifier)*\s*=\s*($zero_initializer)\s*;/) {
+			if (ERROR("GLOBAL_INITIALISERS",
+				  "do not initialise globals to $1\n" . $herecurr) &&
+			    $fix) {
+				$fixed[$fixlinenr] =~ s/(^.$Type\s*$Ident(?:\s+$Modifier)*)\s*=\s*$zero_initializer\s*;/$1;/;
+			}
+		}
+# check for static initialisers.
+		if ($line =~ /^\+.*\bstatic\s.*=\s*($zero_initializer)\s*;/) {
+			if (ERROR("INITIALISED_STATIC",
+				  "do not initialise statics to $1\n" .
+				      $herecurr) &&
+			    $fix) {
+				$fixed[$fixlinenr] =~ s/(\bstatic\s.*?)\s*=\s*$zero_initializer\s*;/$1;/;
+			}
+		}
+
+# check for misordered declarations of char/short/int/long with signed/unsigned
+		while ($sline =~ m{(\b$TypeMisordered\b)}g) {
+			my $tmp = trim($1);
+			WARN("MISORDERED_TYPE",
+			     "type '$tmp' should be specified in [[un]signed] [short|int|long|long long] order\n" . $herecurr);
+		}
+
+# check for unnecessary <signed> int declarations of short/long/long long
+		while ($sline =~ m{\b($TypeMisordered(\s*\*)*|$C90_int_types)\b}g) {
+			my $type = trim($1);
+			next if ($type !~ /\bint\b/);
+			next if ($type !~ /\b(?:short|long\s+long|long)\b/);
+			my $new_type = $type;
+			$new_type =~ s/\b\s*int\s*\b/ /;
+			$new_type =~ s/\b\s*(?:un)?signed\b\s*/ /;
+			$new_type =~ s/^const\s+//;
+			$new_type = "unsigned $new_type" if ($type =~ /\bunsigned\b/);
+			$new_type = "const $new_type" if ($type =~ /^const\b/);
+			$new_type =~ s/\s+/ /g;
+			$new_type = trim($new_type);
+			if (WARN("UNNECESSARY_INT",
+				 "Prefer '$new_type' over '$type' as the int is unnecessary\n" . $herecurr) &&
+			    $fix) {
+				$fixed[$fixlinenr] =~ s/\b\Q$type\E\b/$new_type/;
+			}
+		}
+
+# check for static const char * arrays.
+		if ($line =~ /\bstatic\s+const\s+char\s*\*\s*(\w+)\s*\[\s*\]\s*=\s*/) {
+			WARN("STATIC_CONST_CHAR_ARRAY",
+			     "static const char * array should probably be static const char * const\n" .
+				$herecurr);
+		}
+
+# check for initialized const char arrays that should be static const
+		if ($line =~ /^\+\s*const\s+(char|unsigned\s+char|_*u8|(?:[us]_)?int8_t)\s+\w+\s*\[\s*(?:\w+\s*)?\]\s*=\s*"/) {
+			if (WARN("STATIC_CONST_CHAR_ARRAY",
+				 "const array should probably be static const\n" . $herecurr) &&
+			    $fix) {
+				$fixed[$fixlinenr] =~ s/(^.\s*)const\b/${1}static const/;
+			}
+		}
+
+# check for static char foo[] = "bar" declarations.
+		if ($line =~ /\bstatic\s+char\s+(\w+)\s*\[\s*\]\s*=\s*"/) {
+			WARN("STATIC_CONST_CHAR_ARRAY",
+			     "static char array declaration should probably be static const char\n" .
+				$herecurr);
+		}
+
+# check for const <foo> const where <foo> is not a pointer or array type
+		if ($sline =~ /\bconst\s+($BasicType)\s+const\b/) {
+			my $found = $1;
+			if ($sline =~ /\bconst\s+\Q$found\E\s+const\b\s*\*/) {
+				WARN("CONST_CONST",
+				     "'const $found const *' should probably be 'const $found * const'\n" . $herecurr);
+			} elsif ($sline !~ /\bconst\s+\Q$found\E\s+const\s+\w+\s*\[/) {
+				WARN("CONST_CONST",
+				     "'const $found const' should probably be 'const $found'\n" . $herecurr);
+			}
+		}
+
+# check for non-global char *foo[] = {"bar", ...} declarations.
+		if ($line =~ /^.\s+(?:static\s+|const\s+)?char\s+\*\s*\w+\s*\[\s*\]\s*=\s*\{/) {
+			WARN("STATIC_CONST_CHAR_ARRAY",
+			     "char * array declaration might be better as static const\n" .
+				$herecurr);
+               }
+
+# check for sizeof(foo)/sizeof(foo[0]) that could be ARRAY_SIZE(foo)
+		if ($line =~ m@\bsizeof\s*\(\s*($Lval)\s*\)@) {
+			my $array = $1;
+			if ($line =~ m@\b(sizeof\s*\(\s*\Q$array\E\s*\)\s*/\s*sizeof\s*\(\s*\Q$array\E\s*\[\s*0\s*\]\s*\))@) {
+				my $array_div = $1;
+				if (WARN("ARRAY_SIZE",
+					 "Prefer ARRAY_SIZE($array)\n" . $herecurr) &&
+				    $fix) {
+					$fixed[$fixlinenr] =~ s/\Q$array_div\E/ARRAY_SIZE($array)/;
+				}
+			}
+		}
+
+# check for function declarations without arguments like "int foo()"
+		if ($line =~ /(\b$Type\s*$Ident)\s*\(\s*\)/) {
+			if (ERROR("FUNCTION_WITHOUT_ARGS",
+				  "Bad function definition - $1() should probably be $1(void)\n" . $herecurr) &&
+			    $fix) {
+				$fixed[$fixlinenr] =~ s/(\b($Type)\s+($Ident))\s*\(\s*\)/$2 $3(void)/;
+			}
+		}
+
+# check for new typedefs, only function parameters and sparse annotations
+# make sense.
+		if ($line =~ /\btypedef\s/ &&
+		    $line !~ /\btypedef\s+$Type\s*\(\s*\*?$Ident\s*\)\s*\(/ &&
+		    $line !~ /\btypedef\s+$Type\s+$Ident\s*\(/ &&
+		    $line !~ /\b$typeTypedefs\b/ &&
+		    $line !~ /\b__bitwise\b/) {
+			WARN("NEW_TYPEDEFS",
+			     "do not add new typedefs\n" . $herecurr);
+		}
+
+# * goes on variable not on type
+		# (char*[ const])
+		while ($line =~ m{(\($NonptrType(\s*(?:$Modifier\b\s*|\*\s*)+)\))}g) {
+			#print "AA<$1>\n";
+			my ($ident, $from, $to) = ($1, $2, $2);
+
+			# Should start with a space.
+			$to =~ s/^(\S)/ $1/;
+			# Should not end with a space.
+			$to =~ s/\s+$//;
+			# '*'s should not have spaces between.
+			while ($to =~ s/\*\s+\*/\*\*/) {
+			}
+
+##			print "1: from<$from> to<$to> ident<$ident>\n";
+			if ($from ne $to) {
+				if (ERROR("POINTER_LOCATION",
+					  "\"(foo$from)\" should be \"(foo$to)\"\n" .  $herecurr) &&
+				    $fix) {
+					my $sub_from = $ident;
+					my $sub_to = $ident;
+					$sub_to =~ s/\Q$from\E/$to/;
+					$fixed[$fixlinenr] =~
+					    s@\Q$sub_from\E@$sub_to@;
+				}
+			}
+		}
+		while ($line =~ m{(\b$NonptrType(\s*(?:$Modifier\b\s*|\*\s*)+)($Ident))}g) {
+			#print "BB<$1>\n";
+			my ($match, $from, $to, $ident) = ($1, $2, $2, $3);
+
+			# Should start with a space.
+			$to =~ s/^(\S)/ $1/;
+			# Should not end with a space.
+			$to =~ s/\s+$//;
+			# '*'s should not have spaces between.
+			while ($to =~ s/\*\s+\*/\*\*/) {
+			}
+			# Modifiers should have spaces.
+			$to =~ s/(\b$Modifier$)/$1 /;
+
+##			print "2: from<$from> to<$to> ident<$ident>\n";
+			if ($from ne $to && $ident !~ /^$Modifier$/) {
+				if (ERROR("POINTER_LOCATION",
+					  "\"foo${from}bar\" should be \"foo${to}bar\"\n" .  $herecurr) &&
+				    $fix) {
+
+					my $sub_from = $match;
+					my $sub_to = $match;
+					$sub_to =~ s/\Q$from\E/$to/;
+					$fixed[$fixlinenr] =~
+					    s@\Q$sub_from\E@$sub_to@;
+				}
+			}
+		}
+
+# avoid BUG() or BUG_ON()
+		if ($line =~ /\b(?:BUG|BUG_ON)\b/) {
+			my $msg_level = \&WARN;
+			$msg_level = \&CHK if ($file);
+			&{$msg_level}("AVOID_BUG",
+				      "Avoid crashing the kernel - try using WARN_ON & recovery code rather than BUG() or BUG_ON()\n" . $herecurr);
+		}
+
+# avoid LINUX_VERSION_CODE
+		if ($line =~ /\bLINUX_VERSION_CODE\b/) {
+			WARN("LINUX_VERSION_CODE",
+			     "LINUX_VERSION_CODE should be avoided, code should be for the version to which it is merged\n" . $herecurr);
+		}
+
+# check for uses of printk_ratelimit
+		if ($line =~ /\bprintk_ratelimit\s*\(/) {
+			WARN("PRINTK_RATELIMITED",
+			     "Prefer printk_ratelimited or pr_<level>_ratelimited to printk_ratelimit\n" . $herecurr);
+		}
+
+# printk should use KERN_* levels
+		if ($line =~ /\bprintk\s*\(\s*(?!KERN_[A-Z]+\b)/) {
+			WARN("PRINTK_WITHOUT_KERN_LEVEL",
+			     "printk() should include KERN_<LEVEL> facility level\n" . $herecurr);
+		}
+
+		if ($line =~ /\bprintk\s*\(\s*KERN_([A-Z]+)/) {
+			my $orig = $1;
+			my $level = lc($orig);
+			$level = "warn" if ($level eq "warning");
+			my $level2 = $level;
+			$level2 = "dbg" if ($level eq "debug");
+			WARN("PREFER_PR_LEVEL",
+			     "Prefer [subsystem eg: netdev]_$level2([subsystem]dev, ... then dev_$level2(dev, ... then pr_$level(...  to printk(KERN_$orig ...\n" . $herecurr);
+		}
+
+		if ($line =~ /\bdev_printk\s*\(\s*KERN_([A-Z]+)/) {
+			my $orig = $1;
+			my $level = lc($orig);
+			$level = "warn" if ($level eq "warning");
+			$level = "dbg" if ($level eq "debug");
+			WARN("PREFER_DEV_LEVEL",
+			     "Prefer dev_$level(... to dev_printk(KERN_$orig, ...\n" . $herecurr);
+		}
+
+# ENOSYS means "bad syscall nr" and nothing else.  This will have a small
+# number of false positives, but assembly files are not checked, so at
+# least the arch entry code will not trigger this warning.
+		if ($line =~ /\bENOSYS\b/) {
+			WARN("ENOSYS",
+			     "ENOSYS means 'invalid syscall nr' and nothing else\n" . $herecurr);
+		}
+
+# ENOTSUPP is not a standard error code and should be avoided in new patches.
+# Folks usually mean EOPNOTSUPP (also called ENOTSUP), when they type ENOTSUPP.
+# Similarly to ENOSYS warning a small number of false positives is expected.
+		if (!$file && $line =~ /\bENOTSUPP\b/) {
+			if (WARN("ENOTSUPP",
+				 "ENOTSUPP is not a SUSV4 error code, prefer EOPNOTSUPP\n" . $herecurr) &&
+			    $fix) {
+				$fixed[$fixlinenr] =~ s/\bENOTSUPP\b/EOPNOTSUPP/;
+			}
+		}
+
+# function brace can't be on same line, except for #defines of do while,
+# or if closed on same line
+		if ($perl_version_ok &&
+		    $sline =~ /$Type\s*$Ident\s*$balanced_parens\s*\{/ &&
+		    $sline !~ /\#\s*define\b.*do\s*\{/ &&
+		    $sline !~ /}/) {
+			if (ERROR("OPEN_BRACE",
+				  "open brace '{' following function definitions go on the next line\n" . $herecurr) &&
+			    $fix) {
+				fix_delete_line($fixlinenr, $rawline);
+				my $fixed_line = $rawline;
+				$fixed_line =~ /(^..*$Type\s*$Ident\(.*\)\s*){(.*)$/;
+				my $line1 = $1;
+				my $line2 = $2;
+				fix_insert_line($fixlinenr, ltrim($line1));
+				fix_insert_line($fixlinenr, "\+{");
+				if ($line2 !~ /^\s*$/) {
+					fix_insert_line($fixlinenr, "\+\t" . trim($line2));
+				}
+			}
+		}
+
+# open braces for enum, union and struct go on the same line.
+		if ($line =~ /^.\s*{/ &&
+		    $prevline =~ /^.\s*(?:typedef\s+)?(enum|union|struct)(?:\s+$Ident)?\s*$/) {
+			if (ERROR("OPEN_BRACE",
+				  "open brace '{' following $1 go on the same line\n" . $hereprev) &&
+			    $fix && $prevline =~ /^\+/ && $line =~ /^\+/) {
+				fix_delete_line($fixlinenr - 1, $prevrawline);
+				fix_delete_line($fixlinenr, $rawline);
+				my $fixedline = rtrim($prevrawline) . " {";
+				fix_insert_line($fixlinenr, $fixedline);
+				$fixedline = $rawline;
+				$fixedline =~ s/^(.\s*)\{\s*/$1\t/;
+				if ($fixedline !~ /^\+\s*$/) {
+					fix_insert_line($fixlinenr, $fixedline);
+				}
+			}
+		}
+
+# missing space after union, struct or enum definition
+		if ($line =~ /^.\s*(?:typedef\s+)?(enum|union|struct)(?:\s+$Ident){1,2}[=\{]/) {
+			if (WARN("SPACING",
+				 "missing space after $1 definition\n" . $herecurr) &&
+			    $fix) {
+				$fixed[$fixlinenr] =~
+				    s/^(.\s*(?:typedef\s+)?(?:enum|union|struct)(?:\s+$Ident){1,2})([=\{])/$1 $2/;
+			}
+		}
+
+# Function pointer declarations
+# check spacing between type, funcptr, and args
+# canonical declaration is "type (*funcptr)(args...)"
+		if ($line =~ /^.\s*($Declare)\((\s*)\*(\s*)($Ident)(\s*)\)(\s*)\(/) {
+			my $declare = $1;
+			my $pre_pointer_space = $2;
+			my $post_pointer_space = $3;
+			my $funcname = $4;
+			my $post_funcname_space = $5;
+			my $pre_args_space = $6;
+
+# the $Declare variable will capture all spaces after the type
+# so check it for a missing trailing missing space but pointer return types
+# don't need a space so don't warn for those.
+			my $post_declare_space = "";
+			if ($declare =~ /(\s+)$/) {
+				$post_declare_space = $1;
+				$declare = rtrim($declare);
+			}
+			if ($declare !~ /\*$/ && $post_declare_space =~ /^$/) {
+				WARN("SPACING",
+				     "missing space after return type\n" . $herecurr);
+				$post_declare_space = " ";
+			}
+
+# unnecessary space "type  (*funcptr)(args...)"
+# This test is not currently implemented because these declarations are
+# equivalent to
+#	int  foo(int bar, ...)
+# and this is form shouldn't/doesn't generate a checkpatch warning.
+#
+#			elsif ($declare =~ /\s{2,}$/) {
+#				WARN("SPACING",
+#				     "Multiple spaces after return type\n" . $herecurr);
+#			}
+
+# unnecessary space "type ( *funcptr)(args...)"
+			if (defined $pre_pointer_space &&
+			    $pre_pointer_space =~ /^\s/) {
+				WARN("SPACING",
+				     "Unnecessary space after function pointer open parenthesis\n" . $herecurr);
+			}
+
+# unnecessary space "type (* funcptr)(args...)"
+			if (defined $post_pointer_space &&
+			    $post_pointer_space =~ /^\s/) {
+				WARN("SPACING",
+				     "Unnecessary space before function pointer name\n" . $herecurr);
+			}
+
+# unnecessary space "type (*funcptr )(args...)"
+			if (defined $post_funcname_space &&
+			    $post_funcname_space =~ /^\s/) {
+				WARN("SPACING",
+				     "Unnecessary space after function pointer name\n" . $herecurr);
+			}
+
+# unnecessary space "type (*funcptr) (args...)"
+			if (defined $pre_args_space &&
+			    $pre_args_space =~ /^\s/) {
+				WARN("SPACING",
+				     "Unnecessary space before function pointer arguments\n" . $herecurr);
+			}
+
+			if (show_type("SPACING") && $fix) {
+				$fixed[$fixlinenr] =~
+				    s/^(.\s*)$Declare\s*\(\s*\*\s*$Ident\s*\)\s*\(/$1 . $declare . $post_declare_space . '(*' . $funcname . ')('/ex;
+			}
+		}
+
+# check for spacing round square brackets; allowed:
+#  1. with a type on the left -- int [] a;
+#  2. at the beginning of a line for slice initialisers -- [0...10] = 5,
+#  3. inside a curly brace -- = { [0...10] = 5 }
+		while ($line =~ /(.*?\s)\[/g) {
+			my ($where, $prefix) = ($-[1], $1);
+			if ($prefix !~ /$Type\s+$/ &&
+			    ($where != 0 || $prefix !~ /^.\s+$/) &&
+			    $prefix !~ /[{,:]\s+$/) {
+				if (ERROR("BRACKET_SPACE",
+					  "space prohibited before open square bracket '['\n" . $herecurr) &&
+				    $fix) {
+				    $fixed[$fixlinenr] =~
+					s/^(\+.*?)\s+\[/$1\[/;
+				}
+			}
+		}
+
+# check for spaces between functions and their parentheses.
+		while ($line =~ /($Ident)\s+\(/g) {
+			my $name = $1;
+			my $ctx_before = substr($line, 0, $-[1]);
+			my $ctx = "$ctx_before$name";
+
+			# Ignore those directives where spaces _are_ permitted.
+			if ($name =~ /^(?:
+				if|for|while|switch|return|case|
+				volatile|__volatile__|
+				__attribute__|format|__extension__|
+				asm|__asm__)$/x)
+			{
+			# cpp #define statements have non-optional spaces, ie
+			# if there is a space between the name and the open
+			# parenthesis it is simply not a parameter group.
+			} elsif ($ctx_before =~ /^.\s*\#\s*define\s*$/) {
+
+			# cpp #elif statement condition may start with a (
+			} elsif ($ctx =~ /^.\s*\#\s*elif\s*$/) {
+
+			# If this whole things ends with a type its most
+			# likely a typedef for a function.
+			} elsif ($ctx =~ /$Type$/) {
+
+			} else {
+				if (WARN("SPACING",
+					 "space prohibited between function name and open parenthesis '('\n" . $herecurr) &&
+					     $fix) {
+					$fixed[$fixlinenr] =~
+					    s/\b$name\s+\(/$name\(/;
+				}
+			}
+		}
+
+# Check operator spacing.
+		if (!($line=~/\#\s*include/)) {
+			my $fixed_line = "";
+			my $line_fixed = 0;
+
+			my $ops = qr{
+				<<=|>>=|<=|>=|==|!=|
+				\+=|-=|\*=|\/=|%=|\^=|\|=|&=|
+				=>|->|<<|>>|<|>|=|!|~|
+				&&|\|\||,|\^|\+\+|--|&|\||\+|-|\*|\/|%|
+				\?:|\?|:
+			}x;
+			my @elements = split(/($ops|;)/, $opline);
+
+##			print("element count: <" . $#elements . ">\n");
+##			foreach my $el (@elements) {
+##				print("el: <$el>\n");
+##			}
+
+			my @fix_elements = ();
+			my $off = 0;
+
+			foreach my $el (@elements) {
+				push(@fix_elements, substr($rawline, $off, length($el)));
+				$off += length($el);
+			}
+
+			$off = 0;
+
+			my $blank = copy_spacing($opline);
+			my $last_after = -1;
+
+			for (my $n = 0; $n < $#elements; $n += 2) {
+
+				my $good = $fix_elements[$n] . $fix_elements[$n + 1];
+
+##				print("n: <$n> good: <$good>\n");
+
+				$off += length($elements[$n]);
+
+				# Pick up the preceding and succeeding characters.
+				my $ca = substr($opline, 0, $off);
+				my $cc = '';
+				if (length($opline) >= ($off + length($elements[$n + 1]))) {
+					$cc = substr($opline, $off + length($elements[$n + 1]));
+				}
+				my $cb = "$ca$;$cc";
+
+				my $a = '';
+				$a = 'V' if ($elements[$n] ne '');
+				$a = 'W' if ($elements[$n] =~ /\s$/);
+				$a = 'C' if ($elements[$n] =~ /$;$/);
+				$a = 'B' if ($elements[$n] =~ /(\[|\()$/);
+				$a = 'O' if ($elements[$n] eq '');
+				$a = 'E' if ($ca =~ /^\s*$/);
+
+				my $op = $elements[$n + 1];
+
+				my $c = '';
+				if (defined $elements[$n + 2]) {
+					$c = 'V' if ($elements[$n + 2] ne '');
+					$c = 'W' if ($elements[$n + 2] =~ /^\s/);
+					$c = 'C' if ($elements[$n + 2] =~ /^$;/);
+					$c = 'B' if ($elements[$n + 2] =~ /^(\)|\]|;)/);
+					$c = 'O' if ($elements[$n + 2] eq '');
+					$c = 'E' if ($elements[$n + 2] =~ /^\s*\\$/);
+				} else {
+					$c = 'E';
+				}
+
+				my $ctx = "${a}x${c}";
+
+				my $at = "(ctx:$ctx)";
+
+				my $ptr = substr($blank, 0, $off) . "^";
+				my $hereptr = "$hereline$ptr\n";
+
+				# Pull out the value of this operator.
+				my $op_type = substr($curr_values, $off + 1, 1);
+
+				# Get the full operator variant.
+				my $opv = $op . substr($curr_vars, $off, 1);
+
+				# Ignore operators passed as parameters.
+				if ($op_type ne 'V' &&
+				    $ca =~ /\s$/ && $cc =~ /^\s*[,\)]/) {
+
+#				# Ignore comments
+#				} elsif ($op =~ /^$;+$/) {
+
+				# ; should have either the end of line or a space or \ after it
+				} elsif ($op eq ';') {
+					if ($ctx !~ /.x[WEBC]/ &&
+					    $cc !~ /^\\/ && $cc !~ /^;/) {
+						if (ERROR("SPACING",
+							  "space required after that '$op' $at\n" . $hereptr)) {
+							$good = $fix_elements[$n] . trim($fix_elements[$n + 1]) . " ";
+							$line_fixed = 1;
+						}
+					}
+
+				# // is a comment
+				} elsif ($op eq '//') {
+
+				#   :   when part of a bitfield
+				} elsif ($opv eq ':B') {
+					# skip the bitfield test for now
+
+				# No spaces for:
+				#   ->
+				} elsif ($op eq '->') {
+					if ($ctx =~ /Wx.|.xW/) {
+						if (ERROR("SPACING",
+							  "spaces prohibited around that '$op' $at\n" . $hereptr)) {
+							$good = rtrim($fix_elements[$n]) . trim($fix_elements[$n + 1]);
+							if (defined $fix_elements[$n + 2]) {
+								$fix_elements[$n + 2] =~ s/^\s+//;
+							}
+							$line_fixed = 1;
+						}
+					}
+
+				# , must not have a space before and must have a space on the right.
+				} elsif ($op eq ',') {
+					my $rtrim_before = 0;
+					my $space_after = 0;
+					if ($ctx =~ /Wx./) {
+						if (ERROR("SPACING",
+							  "space prohibited before that '$op' $at\n" . $hereptr)) {
+							$line_fixed = 1;
+							$rtrim_before = 1;
+						}
+					}
+					if ($ctx !~ /.x[WEC]/ && $cc !~ /^}/) {
+						if (ERROR("SPACING",
+							  "space required after that '$op' $at\n" . $hereptr)) {
+							$line_fixed = 1;
+							$last_after = $n;
+							$space_after = 1;
+						}
+					}
+					if ($rtrim_before || $space_after) {
+						if ($rtrim_before) {
+							$good = rtrim($fix_elements[$n]) . trim($fix_elements[$n + 1]);
+						} else {
+							$good = $fix_elements[$n] . trim($fix_elements[$n + 1]);
+						}
+						if ($space_after) {
+							$good .= " ";
+						}
+					}
+
+				# '*' as part of a type definition -- reported already.
+				} elsif ($opv eq '*_') {
+					#warn "'*' is part of type\n";
+
+				# unary operators should have a space before and
+				# none after.  May be left adjacent to another
+				# unary operator, or a cast
+				} elsif ($op eq '!' || $op eq '~' ||
+					 $opv eq '*U' || $opv eq '-U' ||
+					 $opv eq '&U' || $opv eq '&&U') {
+					if ($ctx !~ /[WEBC]x./ && $ca !~ /(?:\)|!|~|\*|-|\&|\||\+\+|\-\-|\{)$/) {
+						if (ERROR("SPACING",
+							  "space required before that '$op' $at\n" . $hereptr)) {
+							if ($n != $last_after + 2) {
+								$good = $fix_elements[$n] . " " . ltrim($fix_elements[$n + 1]);
+								$line_fixed = 1;
+							}
+						}
+					}
+					if ($op eq '*' && $cc =~/\s*$Modifier\b/) {
+						# A unary '*' may be const
+
+					} elsif ($ctx =~ /.xW/) {
+						if (ERROR("SPACING",
+							  "space prohibited after that '$op' $at\n" . $hereptr)) {
+							$good = $fix_elements[$n] . rtrim($fix_elements[$n + 1]);
+							if (defined $fix_elements[$n + 2]) {
+								$fix_elements[$n + 2] =~ s/^\s+//;
+							}
+							$line_fixed = 1;
+						}
+					}
+
+				# unary ++ and unary -- are allowed no space on one side.
+				} elsif ($op eq '++' or $op eq '--') {
+					if ($ctx !~ /[WEOBC]x[^W]/ && $ctx !~ /[^W]x[WOBEC]/) {
+						if (ERROR("SPACING",
+							  "space required one side of that '$op' $at\n" . $hereptr)) {
+							$good = $fix_elements[$n] . trim($fix_elements[$n + 1]) . " ";
+							$line_fixed = 1;
+						}
+					}
+					if ($ctx =~ /Wx[BE]/ ||
+					    ($ctx =~ /Wx./ && $cc =~ /^;/)) {
+						if (ERROR("SPACING",
+							  "space prohibited before that '$op' $at\n" . $hereptr)) {
+							$good = rtrim($fix_elements[$n]) . trim($fix_elements[$n + 1]);
+							$line_fixed = 1;
+						}
+					}
+					if ($ctx =~ /ExW/) {
+						if (ERROR("SPACING",
+							  "space prohibited after that '$op' $at\n" . $hereptr)) {
+							$good = $fix_elements[$n] . trim($fix_elements[$n + 1]);
+							if (defined $fix_elements[$n + 2]) {
+								$fix_elements[$n + 2] =~ s/^\s+//;
+							}
+							$line_fixed = 1;
+						}
+					}
+
+				# << and >> may either have or not have spaces both sides
+				} elsif ($op eq '<<' or $op eq '>>' or
+					 $op eq '&' or $op eq '^' or $op eq '|' or
+					 $op eq '+' or $op eq '-' or
+					 $op eq '*' or $op eq '/' or
+					 $op eq '%')
+				{
+					if ($check) {
+						if (defined $fix_elements[$n + 2] && $ctx !~ /[EW]x[EW]/) {
+							if (CHK("SPACING",
+								"spaces preferred around that '$op' $at\n" . $hereptr)) {
+								$good = rtrim($fix_elements[$n]) . " " . trim($fix_elements[$n + 1]) . " ";
+								$fix_elements[$n + 2] =~ s/^\s+//;
+								$line_fixed = 1;
+							}
+						} elsif (!defined $fix_elements[$n + 2] && $ctx !~ /Wx[OE]/) {
+							if (CHK("SPACING",
+								"space preferred before that '$op' $at\n" . $hereptr)) {
+								$good = rtrim($fix_elements[$n]) . " " . trim($fix_elements[$n + 1]);
+								$line_fixed = 1;
+							}
+						}
+					} elsif ($ctx =~ /Wx[^WCE]|[^WCE]xW/) {
+						if (ERROR("SPACING",
+							  "need consistent spacing around '$op' $at\n" . $hereptr)) {
+							$good = rtrim($fix_elements[$n]) . " " . trim($fix_elements[$n + 1]) . " ";
+							if (defined $fix_elements[$n + 2]) {
+								$fix_elements[$n + 2] =~ s/^\s+//;
+							}
+							$line_fixed = 1;
+						}
+					}
+
+				# A colon needs no spaces before when it is
+				# terminating a case value or a label.
+				} elsif ($opv eq ':C' || $opv eq ':L') {
+					if ($ctx =~ /Wx./) {
+						if (ERROR("SPACING",
+							  "space prohibited before that '$op' $at\n" . $hereptr)) {
+							$good = rtrim($fix_elements[$n]) . trim($fix_elements[$n + 1]);
+							$line_fixed = 1;
+						}
+					}
+
+				# All the others need spaces both sides.
+				} elsif ($ctx !~ /[EWC]x[CWE]/) {
+					my $ok = 0;
+
+					# Ignore email addresses <foo@bar>
+					if (($op eq '<' &&
+					     $cc =~ /^\S+\@\S+>/) ||
+					    ($op eq '>' &&
+					     $ca =~ /<\S+\@\S+$/))
+					{
+						$ok = 1;
+					}
+
+					# for asm volatile statements
+					# ignore a colon with another
+					# colon immediately before or after
+					if (($op eq ':') &&
+					    ($ca =~ /:$/ || $cc =~ /^:/)) {
+						$ok = 1;
+					}
+
+					# messages are ERROR, but ?: are CHK
+					if ($ok == 0) {
+						my $msg_level = \&ERROR;
+						$msg_level = \&CHK if (($op eq '?:' || $op eq '?' || $op eq ':') && $ctx =~ /VxV/);
+
+						if (&{$msg_level}("SPACING",
+								  "spaces required around that '$op' $at\n" . $hereptr)) {
+							$good = rtrim($fix_elements[$n]) . " " . trim($fix_elements[$n + 1]) . " ";
+							if (defined $fix_elements[$n + 2]) {
+								$fix_elements[$n + 2] =~ s/^\s+//;
+							}
+							$line_fixed = 1;
+						}
+					}
+				}
+				$off += length($elements[$n + 1]);
+
+##				print("n: <$n> GOOD: <$good>\n");
+
+				$fixed_line = $fixed_line . $good;
+			}
+
+			if (($#elements % 2) == 0) {
+				$fixed_line = $fixed_line . $fix_elements[$#elements];
+			}
+
+			if ($fix && $line_fixed && $fixed_line ne $fixed[$fixlinenr]) {
+				$fixed[$fixlinenr] = $fixed_line;
+			}
+
+
+		}
+
+# check for whitespace before a non-naked semicolon
+		if ($line =~ /^\+.*\S\s+;\s*$/) {
+			if (WARN("SPACING",
+				 "space prohibited before semicolon\n" . $herecurr) &&
+			    $fix) {
+				1 while $fixed[$fixlinenr] =~
+				    s/^(\+.*\S)\s+;/$1;/;
+			}
+		}
+
+# check for multiple assignments
+		if ($line =~ /^.\s*$Lval\s*=\s*$Lval\s*=(?!=)/) {
+			CHK("MULTIPLE_ASSIGNMENTS",
+			    "multiple assignments should be avoided\n" . $herecurr);
+		}
+
+## # check for multiple declarations, allowing for a function declaration
+## # continuation.
+## 		if ($line =~ /^.\s*$Type\s+$Ident(?:\s*=[^,{]*)?\s*,\s*$Ident.*/ &&
+## 		    $line !~ /^.\s*$Type\s+$Ident(?:\s*=[^,{]*)?\s*,\s*$Type\s*$Ident.*/) {
+##
+## 			# Remove any bracketed sections to ensure we do not
+## 			# falsely report the parameters of functions.
+## 			my $ln = $line;
+## 			while ($ln =~ s/\([^\(\)]*\)//g) {
+## 			}
+## 			if ($ln =~ /,/) {
+## 				WARN("MULTIPLE_DECLARATION",
+##				     "declaring multiple variables together should be avoided\n" . $herecurr);
+## 			}
+## 		}
+
+#need space before brace following if, while, etc
+		if (($line =~ /\(.*\)\{/ && $line !~ /\($Type\)\{/) ||
+		    $line =~ /\b(?:else|do)\{/) {
+			if (ERROR("SPACING",
+				  "space required before the open brace '{'\n" . $herecurr) &&
+			    $fix) {
+				$fixed[$fixlinenr] =~ s/^(\+.*(?:do|else|\)))\{/$1 {/;
+			}
+		}
+
+## # check for blank lines before declarations
+##		if ($line =~ /^.\t+$Type\s+$Ident(?:\s*=.*)?;/ &&
+##		    $prevrawline =~ /^.\s*$/) {
+##			WARN("SPACING",
+##			     "No blank lines before declarations\n" . $hereprev);
+##		}
+##
+
+# closing brace should have a space following it when it has anything
+# on the line
+		if ($line =~ /}(?!(?:,|;|\)|\}))\S/) {
+			if (ERROR("SPACING",
+				  "space required after that close brace '}'\n" . $herecurr) &&
+			    $fix) {
+				$fixed[$fixlinenr] =~
+				    s/}((?!(?:,|;|\)))\S)/} $1/;
+			}
+		}
+
+# check spacing on square brackets
+		if ($line =~ /\[\s/ && $line !~ /\[\s*$/) {
+			if (ERROR("SPACING",
+				  "space prohibited after that open square bracket '['\n" . $herecurr) &&
+			    $fix) {
+				$fixed[$fixlinenr] =~
+				    s/\[\s+/\[/;
+			}
+		}
+		if ($line =~ /\s\]/) {
+			if (ERROR("SPACING",
+				  "space prohibited before that close square bracket ']'\n" . $herecurr) &&
+			    $fix) {
+				$fixed[$fixlinenr] =~
+				    s/\s+\]/\]/;
+			}
+		}
+
+# check spacing on parentheses
+		if ($line =~ /\(\s/ && $line !~ /\(\s*(?:\\)?$/ &&
+		    $line !~ /for\s*\(\s+;/) {
+			if (ERROR("SPACING",
+				  "space prohibited after that open parenthesis '('\n" . $herecurr) &&
+			    $fix) {
+				$fixed[$fixlinenr] =~
+				    s/\(\s+/\(/;
+			}
+		}
+		if ($line =~ /(\s+)\)/ && $line !~ /^.\s*\)/ &&
+		    $line !~ /for\s*\(.*;\s+\)/ &&
+		    $line !~ /:\s+\)/) {
+			if (ERROR("SPACING",
+				  "space prohibited before that close parenthesis ')'\n" . $herecurr) &&
+			    $fix) {
+				$fixed[$fixlinenr] =~
+				    s/\s+\)/\)/;
+			}
+		}
+
+# check unnecessary parentheses around addressof/dereference single $Lvals
+# ie: &(foo->bar) should be &foo->bar and *(foo->bar) should be *foo->bar
+
+		while ($line =~ /(?:[^&]&\s*|\*)\(\s*($Ident\s*(?:$Member\s*)+)\s*\)/g) {
+			my $var = $1;
+			if (CHK("UNNECESSARY_PARENTHESES",
+				"Unnecessary parentheses around $var\n" . $herecurr) &&
+			    $fix) {
+				$fixed[$fixlinenr] =~ s/\(\s*\Q$var\E\s*\)/$var/;
+			}
+		}
+
+# check for unnecessary parentheses around function pointer uses
+# ie: (foo->bar)(); should be foo->bar();
+# but not "if (foo->bar) (" to avoid some false positives
+		if ($line =~ /(\bif\s*|)(\(\s*$Ident\s*(?:$Member\s*)+\))[ \t]*\(/ && $1 !~ /^if/) {
+			my $var = $2;
+			if (CHK("UNNECESSARY_PARENTHESES",
+				"Unnecessary parentheses around function pointer $var\n" . $herecurr) &&
+			    $fix) {
+				my $var2 = deparenthesize($var);
+				$var2 =~ s/\s//g;
+				$fixed[$fixlinenr] =~ s/\Q$var\E/$var2/;
+			}
+		}
+
+# check for unnecessary parentheses around comparisons in if uses
+# when !drivers/staging or command-line uses --strict
+		if (($realfile !~ m@^(?:drivers/staging/)@ || $check_orig) &&
+		    $perl_version_ok && defined($stat) &&
+		    $stat =~ /(^.\s*if\s*($balanced_parens))/) {
+			my $if_stat = $1;
+			my $test = substr($2, 1, -1);
+			my $herectx;
+			while ($test =~ /(?:^|[^\w\&\!\~])+\s*\(\s*([\&\!\~]?\s*$Lval\s*(?:$Compare\s*$FuncArg)?)\s*\)/g) {
+				my $match = $1;
+				# avoid parentheses around potential macro args
+				next if ($match =~ /^\s*\w+\s*$/);
+				if (!defined($herectx)) {
+					$herectx = $here . "\n";
+					my $cnt = statement_rawlines($if_stat);
+					for (my $n = 0; $n < $cnt; $n++) {
+						my $rl = raw_line($linenr, $n);
+						$herectx .=  $rl . "\n";
+						last if $rl =~ /^[ \+].*\{/;
+					}
+				}
+				CHK("UNNECESSARY_PARENTHESES",
+				    "Unnecessary parentheses around '$match'\n" . $herectx);
+			}
+		}
+
+#goto labels aren't indented, allow a single space however
+		if ($line=~/^.\s+[A-Za-z\d_]+:(?![0-9]+)/ and
+		   !($line=~/^. [A-Za-z\d_]+:/) and !($line=~/^.\s+default:/)) {
+			if (WARN("INDENTED_LABEL",
+				 "labels should not be indented\n" . $herecurr) &&
+			    $fix) {
+				$fixed[$fixlinenr] =~
+				    s/^(.)\s+/$1/;
+			}
+		}
+
+# return is not a function
+		if (defined($stat) && $stat =~ /^.\s*return(\s*)\(/s) {
+			my $spacing = $1;
+			if ($perl_version_ok &&
+			    $stat =~ /^.\s*return\s*($balanced_parens)\s*;\s*$/) {
+				my $value = $1;
+				$value = deparenthesize($value);
+				if ($value =~ m/^\s*$FuncArg\s*(?:\?|$)/) {
+					ERROR("RETURN_PARENTHESES",
+					      "return is not a function, parentheses are not required\n" . $herecurr);
+				}
+			} elsif ($spacing !~ /\s+/) {
+				ERROR("SPACING",
+				      "space required before the open parenthesis '('\n" . $herecurr);
+			}
+		}
+
+# unnecessary return in a void function
+# at end-of-function, with the previous line a single leading tab, then return;
+# and the line before that not a goto label target like "out:"
+		if ($sline =~ /^[ \+]}\s*$/ &&
+		    $prevline =~ /^\+\treturn\s*;\s*$/ &&
+		    $linenr >= 3 &&
+		    $lines[$linenr - 3] =~ /^[ +]/ &&
+		    $lines[$linenr - 3] !~ /^[ +]\s*$Ident\s*:/) {
+			WARN("RETURN_VOID",
+			     "void function return statements are not generally useful\n" . $hereprev);
+               }
+
+# if statements using unnecessary parentheses - ie: if ((foo == bar))
+		if ($perl_version_ok &&
+		    $line =~ /\bif\s*((?:\(\s*){2,})/) {
+			my $openparens = $1;
+			my $count = $openparens =~ tr@\(@\(@;
+			my $msg = "";
+			if ($line =~ /\bif\s*(?:\(\s*){$count,$count}$LvalOrFunc\s*($Compare)\s*$LvalOrFunc(?:\s*\)){$count,$count}/) {
+				my $comp = $4;	#Not $1 because of $LvalOrFunc
+				$msg = " - maybe == should be = ?" if ($comp eq "==");
+				WARN("UNNECESSARY_PARENTHESES",
+				     "Unnecessary parentheses$msg\n" . $herecurr);
+			}
+		}
+
+# comparisons with a constant or upper case identifier on the left
+#	avoid cases like "foo + BAR < baz"
+#	only fix matches surrounded by parentheses to avoid incorrect
+#	conversions like "FOO < baz() + 5" being "misfixed" to "baz() > FOO + 5"
+		if ($perl_version_ok &&
+		    $line =~ /^\+(.*)\b($Constant|[A-Z_][A-Z0-9_]*)\s*($Compare)\s*($LvalOrFunc)/) {
+			my $lead = $1;
+			my $const = $2;
+			my $comp = $3;
+			my $to = $4;
+			my $newcomp = $comp;
+			if ($lead !~ /(?:$Operators|\.)\s*$/ &&
+			    $to !~ /^(?:Constant|[A-Z_][A-Z0-9_]*)$/ &&
+			    WARN("CONSTANT_COMPARISON",
+				 "Comparisons should place the constant on the right side of the test\n" . $herecurr) &&
+			    $fix) {
+				if ($comp eq "<") {
+					$newcomp = ">";
+				} elsif ($comp eq "<=") {
+					$newcomp = ">=";
+				} elsif ($comp eq ">") {
+					$newcomp = "<";
+				} elsif ($comp eq ">=") {
+					$newcomp = "<=";
+				}
+				$fixed[$fixlinenr] =~ s/\(\s*\Q$const\E\s*$Compare\s*\Q$to\E\s*\)/($to $newcomp $const)/;
+			}
+		}
+
+# Return of what appears to be an errno should normally be negative
+		if ($sline =~ /\breturn(?:\s*\(+\s*|\s+)(E[A-Z]+)(?:\s*\)+\s*|\s*)[;:,]/) {
+			my $name = $1;
+			if ($name ne 'EOF' && $name ne 'ERROR') {
+				WARN("USE_NEGATIVE_ERRNO",
+				     "return of an errno should typically be negative (ie: return -$1)\n" . $herecurr);
+			}
+		}
+
+# Need a space before open parenthesis after if, while etc
+		if ($line =~ /\b(if|while|for|switch)\(/) {
+			if (ERROR("SPACING",
+				  "space required before the open parenthesis '('\n" . $herecurr) &&
+			    $fix) {
+				$fixed[$fixlinenr] =~
+				    s/\b(if|while|for|switch)\(/$1 \(/;
+			}
+		}
+
+# Check for illegal assignment in if conditional -- and check for trailing
+# statements after the conditional.
+		if ($line =~ /do\s*(?!{)/) {
+			($stat, $cond, $line_nr_next, $remain_next, $off_next) =
+				ctx_statement_block($linenr, $realcnt, 0)
+					if (!defined $stat);
+			my ($stat_next) = ctx_statement_block($line_nr_next,
+						$remain_next, $off_next);
+			$stat_next =~ s/\n./\n /g;
+			##print "stat<$stat> stat_next<$stat_next>\n";
+
+			if ($stat_next =~ /^\s*while\b/) {
+				# If the statement carries leading newlines,
+				# then count those as offsets.
+				my ($whitespace) =
+					($stat_next =~ /^((?:\s*\n[+-])*\s*)/s);
+				my $offset =
+					statement_rawlines($whitespace) - 1;
+
+				$suppress_whiletrailers{$line_nr_next +
+								$offset} = 1;
+			}
+		}
+		if (!defined $suppress_whiletrailers{$linenr} &&
+		    defined($stat) && defined($cond) &&
+		    $line =~ /\b(?:if|while|for)\s*\(/ && $line !~ /^.\s*#/) {
+			my ($s, $c) = ($stat, $cond);
+
+			if ($c =~ /\bif\s*\(.*[^<>!=]=[^=].*/s) {
+				ERROR("ASSIGN_IN_IF",
+				      "do not use assignment in if condition\n" . $herecurr);
+			}
+
+			# Find out what is on the end of the line after the
+			# conditional.
+			substr($s, 0, length($c), '');
+			$s =~ s/\n.*//g;
+			$s =~ s/$;//g;	# Remove any comments
+			if (length($c) && $s !~ /^\s*{?\s*\\*\s*$/ &&
+			    $c !~ /}\s*while\s*/)
+			{
+				# Find out how long the conditional actually is.
+				my @newlines = ($c =~ /\n/gs);
+				my $cond_lines = 1 + $#newlines;
+				my $stat_real = '';
+
+				$stat_real = raw_line($linenr, $cond_lines)
+							. "\n" if ($cond_lines);
+				if (defined($stat_real) && $cond_lines > 1) {
+					$stat_real = "[...]\n$stat_real";
+				}
+
+				ERROR("TRAILING_STATEMENTS",
+				      "trailing statements should be on next line\n" . $herecurr . $stat_real);
+			}
+		}
+
+# Check for bitwise tests written as boolean
+		if ($line =~ /
+			(?:
+				(?:\[|\(|\&\&|\|\|)
+				\s*0[xX][0-9]+\s*
+				(?:\&\&|\|\|)
+			|
+				(?:\&\&|\|\|)
+				\s*0[xX][0-9]+\s*
+				(?:\&\&|\|\||\)|\])
+			)/x)
+		{
+			WARN("HEXADECIMAL_BOOLEAN_TEST",
+			     "boolean test with hexadecimal, perhaps just 1 \& or \|?\n" . $herecurr);
+		}
+
+# if and else should not have general statements after it
+		if ($line =~ /^.\s*(?:}\s*)?else\b(.*)/) {
+			my $s = $1;
+			$s =~ s/$;//g;	# Remove any comments
+			if ($s !~ /^\s*(?:\sif|(?:{|)\s*\\?\s*$)/) {
+				ERROR("TRAILING_STATEMENTS",
+				      "trailing statements should be on next line\n" . $herecurr);
+			}
+		}
+# if should not continue a brace
+		if ($line =~ /}\s*if\b/) {
+			ERROR("TRAILING_STATEMENTS",
+			      "trailing statements should be on next line (or did you mean 'else if'?)\n" .
+				$herecurr);
+		}
+# case and default should not have general statements after them
+		if ($line =~ /^.\s*(?:case\s*.*|default\s*):/g &&
+		    $line !~ /\G(?:
+			(?:\s*$;*)(?:\s*{)?(?:\s*$;*)(?:\s*\\)?\s*$|
+			\s*return\s+
+		    )/xg)
+		{
+			ERROR("TRAILING_STATEMENTS",
+			      "trailing statements should be on next line\n" . $herecurr);
+		}
+
+		# Check for }<nl>else {, these must be at the same
+		# indent level to be relevant to each other.
+		if ($prevline=~/}\s*$/ and $line=~/^.\s*else\s*/ &&
+		    $previndent == $indent) {
+			if (ERROR("ELSE_AFTER_BRACE",
+				  "else should follow close brace '}'\n" . $hereprev) &&
+			    $fix && $prevline =~ /^\+/ && $line =~ /^\+/) {
+				fix_delete_line($fixlinenr - 1, $prevrawline);
+				fix_delete_line($fixlinenr, $rawline);
+				my $fixedline = $prevrawline;
+				$fixedline =~ s/}\s*$//;
+				if ($fixedline !~ /^\+\s*$/) {
+					fix_insert_line($fixlinenr, $fixedline);
+				}
+				$fixedline = $rawline;
+				$fixedline =~ s/^(.\s*)else/$1} else/;
+				fix_insert_line($fixlinenr, $fixedline);
+			}
+		}
+
+		if ($prevline=~/}\s*$/ and $line=~/^.\s*while\s*/ &&
+		    $previndent == $indent) {
+			my ($s, $c) = ctx_statement_block($linenr, $realcnt, 0);
+
+			# Find out what is on the end of the line after the
+			# conditional.
+			substr($s, 0, length($c), '');
+			$s =~ s/\n.*//g;
+
+			if ($s =~ /^\s*;/) {
+				if (ERROR("WHILE_AFTER_BRACE",
+					  "while should follow close brace '}'\n" . $hereprev) &&
+				    $fix && $prevline =~ /^\+/ && $line =~ /^\+/) {
+					fix_delete_line($fixlinenr - 1, $prevrawline);
+					fix_delete_line($fixlinenr, $rawline);
+					my $fixedline = $prevrawline;
+					my $trailing = $rawline;
+					$trailing =~ s/^\+//;
+					$trailing = trim($trailing);
+					$fixedline =~ s/}\s*$/} $trailing/;
+					fix_insert_line($fixlinenr, $fixedline);
+				}
+			}
+		}
+
+#Specific variable tests
+		while ($line =~ m{($Constant|$Lval)}g) {
+			my $var = $1;
+
+#CamelCase
+			if ($var !~ /^$Constant$/ &&
+			    $var =~ /[A-Z][a-z]|[a-z][A-Z]/ &&
+#Ignore Page<foo> variants
+			    $var !~ /^(?:Clear|Set|TestClear|TestSet|)Page[A-Z]/ &&
+#Ignore SI style variants like nS, mV and dB
+#(ie: max_uV, regulator_min_uA_show, RANGE_mA_VALUE)
+			    $var !~ /^(?:[a-z0-9_]*|[A-Z0-9_]*)?_?[a-z][A-Z](?:_[a-z0-9_]+|_[A-Z0-9_]+)?$/ &&
+#Ignore some three character SI units explicitly, like MiB and KHz
+			    $var !~ /^(?:[a-z_]*?)_?(?:[KMGT]iB|[KMGT]?Hz)(?:_[a-z_]+)?$/) {
+				while ($var =~ m{($Ident)}g) {
+					my $word = $1;
+					next if ($word !~ /[A-Z][a-z]|[a-z][A-Z]/);
+					if ($check) {
+						seed_camelcase_includes();
+						if (!$file && !$camelcase_file_seeded) {
+							seed_camelcase_file($realfile);
+							$camelcase_file_seeded = 1;
+						}
+					}
+					if (!defined $camelcase{$word}) {
+						$camelcase{$word} = 1;
+						CHK("CAMELCASE",
+						    "Avoid CamelCase: <$word>\n" . $herecurr);
+					}
+				}
+			}
+		}
+
+#no spaces allowed after \ in define
+		if ($line =~ /\#\s*define.*\\\s+$/) {
+			if (WARN("WHITESPACE_AFTER_LINE_CONTINUATION",
+				 "Whitespace after \\ makes next lines useless\n" . $herecurr) &&
+			    $fix) {
+				$fixed[$fixlinenr] =~ s/\s+$//;
+			}
+		}
+
+# warn if <asm/foo.h> is #included and <linux/foo.h> is available and includes
+# itself <asm/foo.h> (uses RAW line)
+		if ($tree && $rawline =~ m{^.\s*\#\s*include\s*\<asm\/(.*)\.h\>}) {
+			my $file = "$1.h";
+			my $checkfile = "include/linux/$file";
+			if (-f "$root/$checkfile" &&
+			    $realfile ne $checkfile &&
+			    $1 !~ /$allowed_asm_includes/)
+			{
+				my $asminclude = `grep -Ec "#include\\s+<asm/$file>" $root/$checkfile`;
+				if ($asminclude > 0) {
+					if ($realfile =~ m{^arch/}) {
+						CHK("ARCH_INCLUDE_LINUX",
+						    "Consider using #include <linux/$file> instead of <asm/$file>\n" . $herecurr);
+					} else {
+						WARN("INCLUDE_LINUX",
+						     "Use #include <linux/$file> instead of <asm/$file>\n" . $herecurr);
+					}
+				}
+			}
+		}
+
+# multi-statement macros should be enclosed in a do while loop, grab the
+# first statement and ensure its the whole macro if its not enclosed
+# in a known good container
+		if ($realfile !~ m@/vmlinux.lds.h$@ &&
+		    $line =~ /^.\s*\#\s*define\s*$Ident(\()?/) {
+			my $ln = $linenr;
+			my $cnt = $realcnt;
+			my ($off, $dstat, $dcond, $rest);
+			my $ctx = '';
+			my $has_flow_statement = 0;
+			my $has_arg_concat = 0;
+			($dstat, $dcond, $ln, $cnt, $off) =
+				ctx_statement_block($linenr, $realcnt, 0);
+			$ctx = $dstat;
+			#print "dstat<$dstat> dcond<$dcond> cnt<$cnt> off<$off>\n";
+			#print "LINE<$lines[$ln-1]> len<" . length($lines[$ln-1]) . "\n";
+
+			$has_flow_statement = 1 if ($ctx =~ /\b(goto|return)\b/);
+			$has_arg_concat = 1 if ($ctx =~ /\#\#/ && $ctx !~ /\#\#\s*(?:__VA_ARGS__|args)\b/);
+
+			$dstat =~ s/^.\s*\#\s*define\s+$Ident(\([^\)]*\))?\s*//;
+			my $define_args = $1;
+			my $define_stmt = $dstat;
+			my @def_args = ();
+
+			if (defined $define_args && $define_args ne "") {
+				$define_args = substr($define_args, 1, length($define_args) - 2);
+				$define_args =~ s/\s*//g;
+				$define_args =~ s/\\\+?//g;
+				@def_args = split(",", $define_args);
+			}
+
+			$dstat =~ s/$;//g;
+			$dstat =~ s/\\\n.//g;
+			$dstat =~ s/^\s*//s;
+			$dstat =~ s/\s*$//s;
+
+			# Flatten any parentheses and braces
+			while ($dstat =~ s/\([^\(\)]*\)/1/ ||
+			       $dstat =~ s/\{[^\{\}]*\}/1/ ||
+			       $dstat =~ s/.\[[^\[\]]*\]/1/)
+			{
+			}
+
+			# Flatten any obvious string concatenation.
+			while ($dstat =~ s/($String)\s*$Ident/$1/ ||
+			       $dstat =~ s/$Ident\s*($String)/$1/)
+			{
+			}
+
+			# Make asm volatile uses seem like a generic function
+			$dstat =~ s/\b_*asm_*\s+_*volatile_*\b/asm_volatile/g;
+
+			my $exceptions = qr{
+				$Declare|
+				module_param_named|
+				MODULE_PARM_DESC|
+				DECLARE_PER_CPU|
+				DEFINE_PER_CPU|
+				__typeof__\(|
+				union|
+				struct|
+				\.$Ident\s*=\s*|
+				^\"|\"$|
+				^\[
+			}x;
+			#print "REST<$rest> dstat<$dstat> ctx<$ctx>\n";
+
+			$ctx =~ s/\n*$//;
+			my $stmt_cnt = statement_rawlines($ctx);
+			my $herectx = get_stat_here($linenr, $stmt_cnt, $here);
+
+			if ($dstat ne '' &&
+			    $dstat !~ /^(?:$Ident|-?$Constant),$/ &&			# 10, // foo(),
+			    $dstat !~ /^(?:$Ident|-?$Constant);$/ &&			# foo();
+			    $dstat !~ /^[!~-]?(?:$Lval|$Constant)$/ &&		# 10 // foo() // !foo // ~foo // -foo // foo->bar // foo.bar->baz
+			    $dstat !~ /^'X'$/ && $dstat !~ /^'XX'$/ &&			# character constants
+			    $dstat !~ /$exceptions/ &&
+			    $dstat !~ /^\.$Ident\s*=/ &&				# .foo =
+			    $dstat !~ /^(?:\#\s*$Ident|\#\s*$Constant)\s*$/ &&		# stringification #foo
+			    $dstat !~ /^do\s*$Constant\s*while\s*$Constant;?$/ &&	# do {...} while (...); // do {...} while (...)
+			    $dstat !~ /^for\s*$Constant$/ &&				# for (...)
+			    $dstat !~ /^for\s*$Constant\s+(?:$Ident|-?$Constant)$/ &&	# for (...) bar()
+			    $dstat !~ /^do\s*{/ &&					# do {...
+			    $dstat !~ /^\(\{/ &&						# ({...
+			    $ctx !~ /^.\s*#\s*define\s+TRACE_(?:SYSTEM|INCLUDE_FILE|INCLUDE_PATH)\b/)
+			{
+				if ($dstat =~ /^\s*if\b/) {
+					ERROR("MULTISTATEMENT_MACRO_USE_DO_WHILE",
+					      "Macros starting with if should be enclosed by a do - while loop to avoid possible if/else logic defects\n" . "$herectx");
+				} elsif ($dstat =~ /;/) {
+					ERROR("MULTISTATEMENT_MACRO_USE_DO_WHILE",
+					      "Macros with multiple statements should be enclosed in a do - while loop\n" . "$herectx");
+				} else {
+					ERROR("COMPLEX_MACRO",
+					      "Macros with complex values should be enclosed in parentheses\n" . "$herectx");
+				}
+
+			}
+
+			# Make $define_stmt single line, comment-free, etc
+			my @stmt_array = split('\n', $define_stmt);
+			my $first = 1;
+			$define_stmt = "";
+			foreach my $l (@stmt_array) {
+				$l =~ s/\\$//;
+				if ($first) {
+					$define_stmt = $l;
+					$first = 0;
+				} elsif ($l =~ /^[\+ ]/) {
+					$define_stmt .= substr($l, 1);
+				}
+			}
+			$define_stmt =~ s/$;//g;
+			$define_stmt =~ s/\s+/ /g;
+			$define_stmt = trim($define_stmt);
+
+# check if any macro arguments are reused (ignore '...' and 'type')
+			foreach my $arg (@def_args) {
+			        next if ($arg =~ /\.\.\./);
+			        next if ($arg =~ /^type$/i);
+				my $tmp_stmt = $define_stmt;
+				$tmp_stmt =~ s/\b(sizeof|typeof|__typeof__|__builtin\w+|typecheck\s*\(\s*$Type\s*,|\#+)\s*\(*\s*$arg\s*\)*\b//g;
+				$tmp_stmt =~ s/\#+\s*$arg\b//g;
+				$tmp_stmt =~ s/\b$arg\s*\#\#//g;
+				my $use_cnt = () = $tmp_stmt =~ /\b$arg\b/g;
+				if ($use_cnt > 1) {
+					CHK("MACRO_ARG_REUSE",
+					    "Macro argument reuse '$arg' - possible side-effects?\n" . "$herectx");
+				    }
+# check if any macro arguments may have other precedence issues
+				if ($tmp_stmt =~ m/($Operators)?\s*\b$arg\b\s*($Operators)?/m &&
+				    ((defined($1) && $1 ne ',') ||
+				     (defined($2) && $2 ne ','))) {
+					CHK("MACRO_ARG_PRECEDENCE",
+					    "Macro argument '$arg' may be better as '($arg)' to avoid precedence issues\n" . "$herectx");
+				}
+			}
+
+# check for macros with flow control, but without ## concatenation
+# ## concatenation is commonly a macro that defines a function so ignore those
+			if ($has_flow_statement && !$has_arg_concat) {
+				my $cnt = statement_rawlines($ctx);
+				my $herectx = get_stat_here($linenr, $cnt, $here);
+
+				WARN("MACRO_WITH_FLOW_CONTROL",
+				     "Macros with flow control statements should be avoided\n" . "$herectx");
+			}
+
+# check for line continuations outside of #defines, preprocessor #, and asm
+
+		} else {
+			if ($prevline !~ /^..*\\$/ &&
+			    $line !~ /^\+\s*\#.*\\$/ &&		# preprocessor
+			    $line !~ /^\+.*\b(__asm__|asm)\b.*\\$/ &&	# asm
+			    $line =~ /^\+.*\\$/) {
+				WARN("LINE_CONTINUATIONS",
+				     "Avoid unnecessary line continuations\n" . $herecurr);
+			}
+		}
+
+# do {} while (0) macro tests:
+# single-statement macros do not need to be enclosed in do while (0) loop,
+# macro should not end with a semicolon
+		if ($perl_version_ok &&
+		    $realfile !~ m@/vmlinux.lds.h$@ &&
+		    $line =~ /^.\s*\#\s*define\s+$Ident(\()?/) {
+			my $ln = $linenr;
+			my $cnt = $realcnt;
+			my ($off, $dstat, $dcond, $rest);
+			my $ctx = '';
+			($dstat, $dcond, $ln, $cnt, $off) =
+				ctx_statement_block($linenr, $realcnt, 0);
+			$ctx = $dstat;
+
+			$dstat =~ s/\\\n.//g;
+			$dstat =~ s/$;/ /g;
+
+			if ($dstat =~ /^\+\s*#\s*define\s+$Ident\s*${balanced_parens}\s*do\s*{(.*)\s*}\s*while\s*\(\s*0\s*\)\s*([;\s]*)\s*$/) {
+				my $stmts = $2;
+				my $semis = $3;
+
+				$ctx =~ s/\n*$//;
+				my $cnt = statement_rawlines($ctx);
+				my $herectx = get_stat_here($linenr, $cnt, $here);
+
+				if (($stmts =~ tr/;/;/) == 1 &&
+				    $stmts !~ /^\s*(if|while|for|switch)\b/) {
+					WARN("SINGLE_STATEMENT_DO_WHILE_MACRO",
+					     "Single statement macros should not use a do {} while (0) loop\n" . "$herectx");
+				}
+				if (defined $semis && $semis ne "") {
+					WARN("DO_WHILE_MACRO_WITH_TRAILING_SEMICOLON",
+					     "do {} while (0) macros should not be semicolon terminated\n" . "$herectx");
+				}
+			} elsif ($dstat =~ /^\+\s*#\s*define\s+$Ident.*;\s*$/) {
+				$ctx =~ s/\n*$//;
+				my $cnt = statement_rawlines($ctx);
+				my $herectx = get_stat_here($linenr, $cnt, $here);
+
+				WARN("TRAILING_SEMICOLON",
+				     "macros should not use a trailing semicolon\n" . "$herectx");
+			}
+		}
+
+# check for redundant bracing round if etc
+		if ($line =~ /(^.*)\bif\b/ && $1 !~ /else\s*$/) {
+			my ($level, $endln, @chunks) =
+				ctx_statement_full($linenr, $realcnt, 1);
+			#print "chunks<$#chunks> linenr<$linenr> endln<$endln> level<$level>\n";
+			#print "APW: <<$chunks[1][0]>><<$chunks[1][1]>>\n";
+			if ($#chunks > 0 && $level == 0) {
+				my @allowed = ();
+				my $allow = 0;
+				my $seen = 0;
+				my $herectx = $here . "\n";
+				my $ln = $linenr - 1;
+				for my $chunk (@chunks) {
+					my ($cond, $block) = @{$chunk};
+
+					# If the condition carries leading newlines, then count those as offsets.
+					my ($whitespace) = ($cond =~ /^((?:\s*\n[+-])*\s*)/s);
+					my $offset = statement_rawlines($whitespace) - 1;
+
+					$allowed[$allow] = 0;
+					#print "COND<$cond> whitespace<$whitespace> offset<$offset>\n";
+
+					# We have looked at and allowed this specific line.
+					$suppress_ifbraces{$ln + $offset} = 1;
+
+					$herectx .= "$rawlines[$ln + $offset]\n[...]\n";
+					$ln += statement_rawlines($block) - 1;
+
+					substr($block, 0, length($cond), '');
+
+					$seen++ if ($block =~ /^\s*{/);
+
+					#print "cond<$cond> block<$block> allowed<$allowed[$allow]>\n";
+					if (statement_lines($cond) > 1) {
+						#print "APW: ALLOWED: cond<$cond>\n";
+						$allowed[$allow] = 1;
+					}
+					if ($block =~/\b(?:if|for|while)\b/) {
+						#print "APW: ALLOWED: block<$block>\n";
+						$allowed[$allow] = 1;
+					}
+					if (statement_block_size($block) > 1) {
+						#print "APW: ALLOWED: lines block<$block>\n";
+						$allowed[$allow] = 1;
+					}
+					$allow++;
+				}
+				if ($seen) {
+					my $sum_allowed = 0;
+					foreach (@allowed) {
+						$sum_allowed += $_;
+					}
+					if ($sum_allowed == 0) {
+						WARN("BRACES",
+						     "braces {} are not necessary for any arm of this statement\n" . $herectx);
+					} elsif ($sum_allowed != $allow &&
+						 $seen != $allow) {
+						CHK("BRACES",
+						    "braces {} should be used on all arms of this statement\n" . $herectx);
+					}
+				}
+			}
+		}
+		if (!defined $suppress_ifbraces{$linenr - 1} &&
+					$line =~ /\b(if|while|for|else)\b/) {
+			my $allowed = 0;
+
+			# Check the pre-context.
+			if (substr($line, 0, $-[0]) =~ /(\}\s*)$/) {
+				#print "APW: ALLOWED: pre<$1>\n";
+				$allowed = 1;
+			}
+
+			my ($level, $endln, @chunks) =
+				ctx_statement_full($linenr, $realcnt, $-[0]);
+
+			# Check the condition.
+			my ($cond, $block) = @{$chunks[0]};
+			#print "CHECKING<$linenr> cond<$cond> block<$block>\n";
+			if (defined $cond) {
+				substr($block, 0, length($cond), '');
+			}
+			if (statement_lines($cond) > 1) {
+				#print "APW: ALLOWED: cond<$cond>\n";
+				$allowed = 1;
+			}
+			if ($block =~/\b(?:if|for|while)\b/) {
+				#print "APW: ALLOWED: block<$block>\n";
+				$allowed = 1;
+			}
+			if (statement_block_size($block) > 1) {
+				#print "APW: ALLOWED: lines block<$block>\n";
+				$allowed = 1;
+			}
+			# Check the post-context.
+			if (defined $chunks[1]) {
+				my ($cond, $block) = @{$chunks[1]};
+				if (defined $cond) {
+					substr($block, 0, length($cond), '');
+				}
+				if ($block =~ /^\s*\{/) {
+					#print "APW: ALLOWED: chunk-1 block<$block>\n";
+					$allowed = 1;
+				}
+			}
+			if ($level == 0 && $block =~ /^\s*\{/ && !$allowed) {
+				my $cnt = statement_rawlines($block);
+				my $herectx = get_stat_here($linenr, $cnt, $here);
+
+				WARN("BRACES",
+				     "braces {} are not necessary for single statement blocks\n" . $herectx);
+			}
+		}
+
+# check for single line unbalanced braces
+		if ($sline =~ /^.\s*\}\s*else\s*$/ ||
+		    $sline =~ /^.\s*else\s*\{\s*$/) {
+			CHK("BRACES", "Unbalanced braces around else statement\n" . $herecurr);
+		}
+
+# check for unnecessary blank lines around braces
+		if (($line =~ /^.\s*}\s*$/ && $prevrawline =~ /^.\s*$/)) {
+			if (CHK("BRACES",
+				"Blank lines aren't necessary before a close brace '}'\n" . $hereprev) &&
+			    $fix && $prevrawline =~ /^\+/) {
+				fix_delete_line($fixlinenr - 1, $prevrawline);
+			}
+		}
+		if (($rawline =~ /^.\s*$/ && $prevline =~ /^..*{\s*$/)) {
+			if (CHK("BRACES",
+				"Blank lines aren't necessary after an open brace '{'\n" . $hereprev) &&
+			    $fix) {
+				fix_delete_line($fixlinenr, $rawline);
+			}
+		}
+
+# no volatiles please
+		my $asm_volatile = qr{\b(__asm__|asm)\s+(__volatile__|volatile)\b};
+		if ($line =~ /\bvolatile\b/ && $line !~ /$asm_volatile/) {
+			WARN("VOLATILE",
+			     "Use of volatile is usually wrong: see Documentation/process/volatile-considered-harmful.rst\n" . $herecurr);
+		}
+
+# Check for user-visible strings broken across lines, which breaks the ability
+# to grep for the string.  Make exceptions when the previous string ends in a
+# newline (multiple lines in one string constant) or '\t', '\r', ';', or '{'
+# (common in inline assembly) or is a octal \123 or hexadecimal \xaf value
+		if ($line =~ /^\+\s*$String/ &&
+		    $prevline =~ /"\s*$/ &&
+		    $prevrawline !~ /(?:\\(?:[ntr]|[0-7]{1,3}|x[0-9a-fA-F]{1,2})|;\s*|\{\s*)"\s*$/) {
+			if (WARN("SPLIT_STRING",
+				 "quoted string split across lines\n" . $hereprev) &&
+				     $fix &&
+				     $prevrawline =~ /^\+.*"\s*$/ &&
+				     $last_coalesced_string_linenr != $linenr - 1) {
+				my $extracted_string = get_quoted_string($line, $rawline);
+				my $comma_close = "";
+				if ($rawline =~ /\Q$extracted_string\E(\s*\)\s*;\s*$|\s*,\s*)/) {
+					$comma_close = $1;
+				}
+
+				fix_delete_line($fixlinenr - 1, $prevrawline);
+				fix_delete_line($fixlinenr, $rawline);
+				my $fixedline = $prevrawline;
+				$fixedline =~ s/"\s*$//;
+				$fixedline .= substr($extracted_string, 1) . trim($comma_close);
+				fix_insert_line($fixlinenr - 1, $fixedline);
+				$fixedline = $rawline;
+				$fixedline =~ s/\Q$extracted_string\E\Q$comma_close\E//;
+				if ($fixedline !~ /\+\s*$/) {
+					fix_insert_line($fixlinenr, $fixedline);
+				}
+				$last_coalesced_string_linenr = $linenr;
+			}
+		}
+
+# check for missing a space in a string concatenation
+		if ($prevrawline =~ /[^\\]\w"$/ && $rawline =~ /^\+[\t ]+"\w/) {
+			WARN('MISSING_SPACE',
+			     "break quoted strings at a space character\n" . $hereprev);
+		}
+
+# check for an embedded function name in a string when the function is known
+# This does not work very well for -f --file checking as it depends on patch
+# context providing the function name or a single line form for in-file
+# function declarations
+		if ($line =~ /^\+.*$String/ &&
+		    defined($context_function) &&
+		    get_quoted_string($line, $rawline) =~ /\b$context_function\b/ &&
+		    length(get_quoted_string($line, $rawline)) != (length($context_function) + 2)) {
+			WARN("EMBEDDED_FUNCTION_NAME",
+			     "Prefer using '\"%s...\", __func__' to using '$context_function', this function's name, in a string\n" . $herecurr);
+		}
+
+# check for spaces before a quoted newline
+		if ($rawline =~ /^.*\".*\s\\n/) {
+			if (WARN("QUOTED_WHITESPACE_BEFORE_NEWLINE",
+				 "unnecessary whitespace before a quoted newline\n" . $herecurr) &&
+			    $fix) {
+				$fixed[$fixlinenr] =~ s/^(\+.*\".*)\s+\\n/$1\\n/;
+			}
+
+		}
+
+# concatenated string without spaces between elements
+		if ($line =~ /$String[A-Za-z0-9_]/ || $line =~ /[A-Za-z0-9_]$String/) {
+			if (CHK("CONCATENATED_STRING",
+				"Concatenated strings should use spaces between elements\n" . $herecurr) &&
+			    $fix) {
+				while ($line =~ /($String)/g) {
+					my $extracted_string = substr($rawline, $-[0], $+[0] - $-[0]);
+					$fixed[$fixlinenr] =~ s/\Q$extracted_string\E([A-Za-z0-9_])/$extracted_string $1/;
+					$fixed[$fixlinenr] =~ s/([A-Za-z0-9_])\Q$extracted_string\E/$1 $extracted_string/;
+				}
+			}
+		}
+
+# uncoalesced string fragments
+		if ($line =~ /$String\s*"/) {
+			if (WARN("STRING_FRAGMENTS",
+				 "Consecutive strings are generally better as a single string\n" . $herecurr) &&
+			    $fix) {
+				while ($line =~ /($String)(?=\s*")/g) {
+					my $extracted_string = substr($rawline, $-[0], $+[0] - $-[0]);
+					$fixed[$fixlinenr] =~ s/\Q$extracted_string\E\s*"/substr($extracted_string, 0, -1)/e;
+				}
+			}
+		}
+
+# check for non-standard and hex prefixed decimal printf formats
+		my $show_L = 1;	#don't show the same defect twice
+		my $show_Z = 1;
+		while ($line =~ /(?:^|")([X\t]*)(?:"|$)/g) {
+			my $string = substr($rawline, $-[1], $+[1] - $-[1]);
+			$string =~ s/%%/__/g;
+			# check for %L
+			if ($show_L && $string =~ /%[\*\d\.\$]*L([diouxX])/) {
+				WARN("PRINTF_L",
+				     "\%L$1 is non-standard C, use %ll$1\n" . $herecurr);
+				$show_L = 0;
+			}
+			# check for %Z
+			if ($show_Z && $string =~ /%[\*\d\.\$]*Z([diouxX])/) {
+				WARN("PRINTF_Z",
+				     "%Z$1 is non-standard C, use %z$1\n" . $herecurr);
+				$show_Z = 0;
+			}
+			# check for 0x<decimal>
+			if ($string =~ /0x%[\*\d\.\$\Llzth]*[diou]/) {
+				ERROR("PRINTF_0XDECIMAL",
+				      "Prefixing 0x with decimal output is defective\n" . $herecurr);
+			}
+		}
+
+# check for line continuations in quoted strings with odd counts of "
+		if ($rawline =~ /\\$/ && $sline =~ tr/"/"/ % 2) {
+			WARN("LINE_CONTINUATIONS",
+			     "Avoid line continuations in quoted strings\n" . $herecurr);
+		}
+
+# warn about #if 0
+		if ($line =~ /^.\s*\#\s*if\s+0\b/) {
+			WARN("IF_0",
+			     "Consider removing the code enclosed by this #if 0 and its #endif\n" . $herecurr);
+		}
+
+# warn about #if 1
+		if ($line =~ /^.\s*\#\s*if\s+1\b/) {
+			WARN("IF_1",
+			     "Consider removing the #if 1 and its #endif\n" . $herecurr);
+		}
+
+# check for needless "if (<foo>) fn(<foo>)" uses
+		if ($prevline =~ /\bif\s*\(\s*($Lval)\s*\)/) {
+			my $tested = quotemeta($1);
+			my $expr = '\s*\(\s*' . $tested . '\s*\)\s*;';
+			if ($line =~ /\b(kfree|usb_free_urb|debugfs_remove(?:_recursive)?|(?:kmem_cache|mempool|dma_pool)_destroy)$expr/) {
+				my $func = $1;
+				if (WARN('NEEDLESS_IF',
+					 "$func(NULL) is safe and this check is probably not required\n" . $hereprev) &&
+				    $fix) {
+					my $do_fix = 1;
+					my $leading_tabs = "";
+					my $new_leading_tabs = "";
+					if ($lines[$linenr - 2] =~ /^\+(\t*)if\s*\(\s*$tested\s*\)\s*$/) {
+						$leading_tabs = $1;
+					} else {
+						$do_fix = 0;
+					}
+					if ($lines[$linenr - 1] =~ /^\+(\t+)$func\s*\(\s*$tested\s*\)\s*;\s*$/) {
+						$new_leading_tabs = $1;
+						if (length($leading_tabs) + 1 ne length($new_leading_tabs)) {
+							$do_fix = 0;
+						}
+					} else {
+						$do_fix = 0;
+					}
+					if ($do_fix) {
+						fix_delete_line($fixlinenr - 1, $prevrawline);
+						$fixed[$fixlinenr] =~ s/^\+$new_leading_tabs/\+$leading_tabs/;
+					}
+				}
+			}
+		}
+
+# check for unnecessary "Out of Memory" messages
+		if ($line =~ /^\+.*\b$logFunctions\s*\(/ &&
+		    $prevline =~ /^[ \+]\s*if\s*\(\s*(\!\s*|NULL\s*==\s*)?($Lval)(\s*==\s*NULL\s*)?\s*\)/ &&
+		    (defined $1 || defined $3) &&
+		    $linenr > 3) {
+			my $testval = $2;
+			my $testline = $lines[$linenr - 3];
+
+			my ($s, $c) = ctx_statement_block($linenr - 3, $realcnt, 0);
+#			print("line: <$line>\nprevline: <$prevline>\ns: <$s>\nc: <$c>\n\n\n");
+
+			if ($s =~ /(?:^|\n)[ \+]\s*(?:$Type\s*)?\Q$testval\E\s*=\s*(?:\([^\)]*\)\s*)?\s*$allocFunctions\s*\(/ &&
+			    $s !~ /\b__GFP_NOWARN\b/ ) {
+				WARN("OOM_MESSAGE",
+				     "Possible unnecessary 'out of memory' message\n" . $hereprev);
+			}
+		}
+
+# check for logging functions with KERN_<LEVEL>
+		if ($line !~ /printk(?:_ratelimited|_once)?\s*\(/ &&
+		    $line =~ /\b$logFunctions\s*\(.*\b(KERN_[A-Z]+)\b/) {
+			my $level = $1;
+			if (WARN("UNNECESSARY_KERN_LEVEL",
+				 "Possible unnecessary $level\n" . $herecurr) &&
+			    $fix) {
+				$fixed[$fixlinenr] =~ s/\s*$level\s*//;
+			}
+		}
+
+# check for logging continuations
+		if ($line =~ /\bprintk\s*\(\s*KERN_CONT\b|\bpr_cont\s*\(/) {
+			WARN("LOGGING_CONTINUATION",
+			     "Avoid logging continuation uses where feasible\n" . $herecurr);
+		}
+
+# check for mask then right shift without a parentheses
+		if ($perl_version_ok &&
+		    $line =~ /$LvalOrFunc\s*\&\s*($LvalOrFunc)\s*>>/ &&
+		    $4 !~ /^\&/) { # $LvalOrFunc may be &foo, ignore if so
+			WARN("MASK_THEN_SHIFT",
+			     "Possible precedence defect with mask then right shift - may need parentheses\n" . $herecurr);
+		}
+
+# check for pointer comparisons to NULL
+		if ($perl_version_ok) {
+			while ($line =~ /\b$LvalOrFunc\s*(==|\!=)\s*NULL\b/g) {
+				my $val = $1;
+				my $equal = "!";
+				$equal = "" if ($4 eq "!=");
+				if (CHK("COMPARISON_TO_NULL",
+					"Comparison to NULL could be written \"${equal}${val}\"\n" . $herecurr) &&
+					    $fix) {
+					$fixed[$fixlinenr] =~ s/\b\Q$val\E\s*(?:==|\!=)\s*NULL\b/$equal$val/;
+				}
+			}
+		}
+
+# check for bad placement of section $InitAttribute (e.g.: __initdata)
+		if ($line =~ /(\b$InitAttribute\b)/) {
+			my $attr = $1;
+			if ($line =~ /^\+\s*static\s+(?:const\s+)?(?:$attr\s+)?($NonptrTypeWithAttr)\s+(?:$attr\s+)?($Ident(?:\[[^]]*\])?)\s*[=;]/) {
+				my $ptr = $1;
+				my $var = $2;
+				if ((($ptr =~ /\b(union|struct)\s+$attr\b/ &&
+				      ERROR("MISPLACED_INIT",
+					    "$attr should be placed after $var\n" . $herecurr)) ||
+				     ($ptr !~ /\b(union|struct)\s+$attr\b/ &&
+				      WARN("MISPLACED_INIT",
+					   "$attr should be placed after $var\n" . $herecurr))) &&
+				    $fix) {
+					$fixed[$fixlinenr] =~ s/(\bstatic\s+(?:const\s+)?)(?:$attr\s+)?($NonptrTypeWithAttr)\s+(?:$attr\s+)?($Ident(?:\[[^]]*\])?)\s*([=;])\s*/"$1" . trim(string_find_replace($2, "\\s*$attr\\s*", " ")) . " " . trim(string_find_replace($3, "\\s*$attr\\s*", "")) . " $attr" . ("$4" eq ";" ? ";" : " = ")/e;
+				}
+			}
+		}
+
+# check for $InitAttributeData (ie: __initdata) with const
+		if ($line =~ /\bconst\b/ && $line =~ /($InitAttributeData)/) {
+			my $attr = $1;
+			$attr =~ /($InitAttributePrefix)(.*)/;
+			my $attr_prefix = $1;
+			my $attr_type = $2;
+			if (ERROR("INIT_ATTRIBUTE",
+				  "Use of const init definition must use ${attr_prefix}initconst\n" . $herecurr) &&
+			    $fix) {
+				$fixed[$fixlinenr] =~
+				    s/$InitAttributeData/${attr_prefix}initconst/;
+			}
+		}
+
+# check for $InitAttributeConst (ie: __initconst) without const
+		if ($line !~ /\bconst\b/ && $line =~ /($InitAttributeConst)/) {
+			my $attr = $1;
+			if (ERROR("INIT_ATTRIBUTE",
+				  "Use of $attr requires a separate use of const\n" . $herecurr) &&
+			    $fix) {
+				my $lead = $fixed[$fixlinenr] =~
+				    /(^\+\s*(?:static\s+))/;
+				$lead = rtrim($1);
+				$lead = "$lead " if ($lead !~ /^\+$/);
+				$lead = "${lead}const ";
+				$fixed[$fixlinenr] =~ s/(^\+\s*(?:static\s+))/$lead/;
+			}
+		}
+
+# check for __read_mostly with const non-pointer (should just be const)
+		if ($line =~ /\b__read_mostly\b/ &&
+		    $line =~ /($Type)\s*$Ident/ && $1 !~ /\*\s*$/ && $1 =~ /\bconst\b/) {
+			if (ERROR("CONST_READ_MOSTLY",
+				  "Invalid use of __read_mostly with const type\n" . $herecurr) &&
+			    $fix) {
+				$fixed[$fixlinenr] =~ s/\s+__read_mostly\b//;
+			}
+		}
+
+# don't use __constant_<foo> functions outside of include/uapi/
+		if ($realfile !~ m@^include/uapi/@ &&
+		    $line =~ /(__constant_(?:htons|ntohs|[bl]e(?:16|32|64)_to_cpu|cpu_to_[bl]e(?:16|32|64)))\s*\(/) {
+			my $constant_func = $1;
+			my $func = $constant_func;
+			$func =~ s/^__constant_//;
+			if (WARN("CONSTANT_CONVERSION",
+				 "$constant_func should be $func\n" . $herecurr) &&
+			    $fix) {
+				$fixed[$fixlinenr] =~ s/\b$constant_func\b/$func/g;
+			}
+		}
+
+# prefer usleep_range over udelay
+		if ($line =~ /\budelay\s*\(\s*(\d+)\s*\)/) {
+			my $delay = $1;
+			# ignore udelay's < 10, however
+			if (! ($delay < 10) ) {
+				CHK("USLEEP_RANGE",
+				    "usleep_range is preferred over udelay; see Documentation/timers/timers-howto.rst\n" . $herecurr);
+			}
+			if ($delay > 2000) {
+				WARN("LONG_UDELAY",
+				     "long udelay - prefer mdelay; see arch/arm/include/asm/delay.h\n" . $herecurr);
+			}
+		}
+
+# warn about unexpectedly long msleep's
+		if ($line =~ /\bmsleep\s*\((\d+)\);/) {
+			if ($1 < 20) {
+				WARN("MSLEEP",
+				     "msleep < 20ms can sleep for up to 20ms; see Documentation/timers/timers-howto.rst\n" . $herecurr);
+			}
+		}
+
+# check for comparisons of jiffies
+		if ($line =~ /\bjiffies\s*$Compare|$Compare\s*jiffies\b/) {
+			WARN("JIFFIES_COMPARISON",
+			     "Comparing jiffies is almost always wrong; prefer time_after, time_before and friends\n" . $herecurr);
+		}
+
+# check for comparisons of get_jiffies_64()
+		if ($line =~ /\bget_jiffies_64\s*\(\s*\)\s*$Compare|$Compare\s*get_jiffies_64\s*\(\s*\)/) {
+			WARN("JIFFIES_COMPARISON",
+			     "Comparing get_jiffies_64() is almost always wrong; prefer time_after64, time_before64 and friends\n" . $herecurr);
+		}
+
+# warn about #ifdefs in C files
+#		if ($line =~ /^.\s*\#\s*if(|n)def/ && ($realfile =~ /\.c$/)) {
+#			print "#ifdef in C files should be avoided\n";
+#			print "$herecurr";
+#			$clean = 0;
+#		}
+
+# warn about spacing in #ifdefs
+		if ($line =~ /^.\s*\#\s*(ifdef|ifndef|elif)\s\s+/) {
+			if (ERROR("SPACING",
+				  "exactly one space required after that #$1\n" . $herecurr) &&
+			    $fix) {
+				$fixed[$fixlinenr] =~
+				    s/^(.\s*\#\s*(ifdef|ifndef|elif))\s{2,}/$1 /;
+			}
+
+		}
+
+# check for spinlock_t definitions without a comment.
+		if ($line =~ /^.\s*(struct\s+mutex|spinlock_t)\s+\S+;/ ||
+		    $line =~ /^.\s*(DEFINE_MUTEX)\s*\(/) {
+			my $which = $1;
+			if (!ctx_has_comment($first_line, $linenr)) {
+				CHK("UNCOMMENTED_DEFINITION",
+				    "$1 definition without comment\n" . $herecurr);
+			}
+		}
+# check for memory barriers without a comment.
+
+		my $barriers = qr{
+			mb|
+			rmb|
+			wmb|
+			read_barrier_depends
+		}x;
+		my $barrier_stems = qr{
+			mb__before_atomic|
+			mb__after_atomic|
+			store_release|
+			load_acquire|
+			store_mb|
+			(?:$barriers)
+		}x;
+		my $all_barriers = qr{
+			(?:$barriers)|
+			smp_(?:$barrier_stems)|
+			virt_(?:$barrier_stems)
+		}x;
+
+		if ($line =~ /\b(?:$all_barriers)\s*\(/) {
+			if (!ctx_has_comment($first_line, $linenr)) {
+				WARN("MEMORY_BARRIER",
+				     "memory barrier without comment\n" . $herecurr);
+			}
+		}
+
+		my $underscore_smp_barriers = qr{__smp_(?:$barrier_stems)}x;
+
+		if ($realfile !~ m@^include/asm-generic/@ &&
+		    $realfile !~ m@/barrier\.h$@ &&
+		    $line =~ m/\b(?:$underscore_smp_barriers)\s*\(/ &&
+		    $line !~ m/^.\s*\#\s*define\s+(?:$underscore_smp_barriers)\s*\(/) {
+			WARN("MEMORY_BARRIER",
+			     "__smp memory barriers shouldn't be used outside barrier.h and asm-generic\n" . $herecurr);
+		}
+
+# check for waitqueue_active without a comment.
+		if ($line =~ /\bwaitqueue_active\s*\(/) {
+			if (!ctx_has_comment($first_line, $linenr)) {
+				WARN("WAITQUEUE_ACTIVE",
+				     "waitqueue_active without comment\n" . $herecurr);
+			}
+		}
+
+# check for data_race without a comment.
+		if ($line =~ /\bdata_race\s*\(/) {
+			if (!ctx_has_comment($first_line, $linenr)) {
+				WARN("DATA_RACE",
+				     "data_race without comment\n" . $herecurr);
+			}
+		}
+
+# check for smp_read_barrier_depends and read_barrier_depends
+		if (!$file && $line =~ /\b(smp_|)read_barrier_depends\s*\(/) {
+			WARN("READ_BARRIER_DEPENDS",
+			     "$1read_barrier_depends should only be used in READ_ONCE or DEC Alpha code\n" . $herecurr);
+		}
+
+# check of hardware specific defines
+		if ($line =~ m@^.\s*\#\s*if.*\b(__i386__|__powerpc64__|__sun__|__s390x__)\b@ && $realfile !~ m@include/asm-@) {
+			CHK("ARCH_DEFINES",
+			    "architecture specific defines should be avoided\n" .  $herecurr);
+		}
+
+# check that the storage class is not after a type
+		if ($line =~ /\b($Type)\s+($Storage)\b/) {
+			WARN("STORAGE_CLASS",
+			     "storage class '$2' should be located before type '$1'\n" . $herecurr);
+		}
+# Check that the storage class is at the beginning of a declaration
+		if ($line =~ /\b$Storage\b/ &&
+		    $line !~ /^.\s*$Storage/ &&
+		    $line =~ /^.\s*(.+?)\$Storage\s/ &&
+		    $1 !~ /[\,\)]\s*$/) {
+			WARN("STORAGE_CLASS",
+			     "storage class should be at the beginning of the declaration\n" . $herecurr);
+		}
+
+# check the location of the inline attribute, that it is between
+# storage class and type.
+		if ($line =~ /\b$Type\s+$Inline\b/ ||
+		    $line =~ /\b$Inline\s+$Storage\b/) {
+			ERROR("INLINE_LOCATION",
+			      "inline keyword should sit between storage class and type\n" . $herecurr);
+		}
+
+# Check for __inline__ and __inline, prefer inline
+		if ($realfile !~ m@\binclude/uapi/@ &&
+		    $line =~ /\b(__inline__|__inline)\b/) {
+			if (WARN("INLINE",
+				 "plain inline is preferred over $1\n" . $herecurr) &&
+			    $fix) {
+				$fixed[$fixlinenr] =~ s/\b(__inline__|__inline)\b/inline/;
+
+			}
+		}
+
+# Check for __attribute__ packed, prefer __packed
+		if ($realfile !~ m@\binclude/uapi/@ &&
+		    $line =~ /\b__attribute__\s*\(\s*\(.*\bpacked\b/) {
+			WARN("PREFER_PACKED",
+			     "__packed is preferred over __attribute__((packed))\n" . $herecurr);
+		}
+
+# Check for __attribute__ aligned, prefer __aligned
+		if ($realfile !~ m@\binclude/uapi/@ &&
+		    $line =~ /\b__attribute__\s*\(\s*\(.*aligned/) {
+			WARN("PREFER_ALIGNED",
+			     "__aligned(size) is preferred over __attribute__((aligned(size)))\n" . $herecurr);
+		}
+
+# Check for __attribute__ section, prefer __section
+		if ($realfile !~ m@\binclude/uapi/@ &&
+		    $line =~ /\b__attribute__\s*\(\s*\(.*_*section_*\s*\(\s*("[^"]*")/) {
+			my $old = substr($rawline, $-[1], $+[1] - $-[1]);
+			my $new = substr($old, 1, -1);
+			if (WARN("PREFER_SECTION",
+				 "__section($new) is preferred over __attribute__((section($old)))\n" . $herecurr) &&
+			    $fix) {
+				$fixed[$fixlinenr] =~ s/\b__attribute__\s*\(\s*\(\s*_*section_*\s*\(\s*\Q$old\E\s*\)\s*\)\s*\)/__section($new)/;
+			}
+		}
+
+# Check for __attribute__ format(printf, prefer __printf
+		if ($realfile !~ m@\binclude/uapi/@ &&
+		    $line =~ /\b__attribute__\s*\(\s*\(\s*format\s*\(\s*printf/) {
+			if (WARN("PREFER_PRINTF",
+				 "__printf(string-index, first-to-check) is preferred over __attribute__((format(printf, string-index, first-to-check)))\n" . $herecurr) &&
+			    $fix) {
+				$fixed[$fixlinenr] =~ s/\b__attribute__\s*\(\s*\(\s*format\s*\(\s*printf\s*,\s*(.*)\)\s*\)\s*\)/"__printf(" . trim($1) . ")"/ex;
+
+			}
+		}
+
+# Check for __attribute__ format(scanf, prefer __scanf
+		if ($realfile !~ m@\binclude/uapi/@ &&
+		    $line =~ /\b__attribute__\s*\(\s*\(\s*format\s*\(\s*scanf\b/) {
+			if (WARN("PREFER_SCANF",
+				 "__scanf(string-index, first-to-check) is preferred over __attribute__((format(scanf, string-index, first-to-check)))\n" . $herecurr) &&
+			    $fix) {
+				$fixed[$fixlinenr] =~ s/\b__attribute__\s*\(\s*\(\s*format\s*\(\s*scanf\s*,\s*(.*)\)\s*\)\s*\)/"__scanf(" . trim($1) . ")"/ex;
+			}
+		}
+
+# Check for __attribute__ weak, or __weak declarations (may have link issues)
+		if ($perl_version_ok &&
+		    $line =~ /(?:$Declare|$DeclareMisordered)\s*$Ident\s*$balanced_parens\s*(?:$Attribute)?\s*;/ &&
+		    ($line =~ /\b__attribute__\s*\(\s*\(.*\bweak\b/ ||
+		     $line =~ /\b__weak\b/)) {
+			ERROR("WEAK_DECLARATION",
+			      "Using weak declarations can have unintended link defects\n" . $herecurr);
+		}
+
+# check for c99 types like uint8_t used outside of uapi/ and tools/
+		if ($realfile !~ m@\binclude/uapi/@ &&
+		    $realfile !~ m@\btools/@ &&
+		    $line =~ /\b($Declare)\s*$Ident\s*[=;,\[]/) {
+			my $type = $1;
+			if ($type =~ /\b($typeC99Typedefs)\b/) {
+				$type = $1;
+				my $kernel_type = 'u';
+				$kernel_type = 's' if ($type =~ /^_*[si]/);
+				$type =~ /(\d+)/;
+				$kernel_type .= $1;
+				if (CHK("PREFER_KERNEL_TYPES",
+					"Prefer kernel type '$kernel_type' over '$type'\n" . $herecurr) &&
+				    $fix) {
+					$fixed[$fixlinenr] =~ s/\b$type\b/$kernel_type/;
+				}
+			}
+		}
+
+# check for cast of C90 native int or longer types constants
+		if ($line =~ /(\(\s*$C90_int_types\s*\)\s*)($Constant)\b/) {
+			my $cast = $1;
+			my $const = $2;
+			if (WARN("TYPECAST_INT_CONSTANT",
+				 "Unnecessary typecast of c90 int constant\n" . $herecurr) &&
+			    $fix) {
+				my $suffix = "";
+				my $newconst = $const;
+				$newconst =~ s/${Int_type}$//;
+				$suffix .= 'U' if ($cast =~ /\bunsigned\b/);
+				if ($cast =~ /\blong\s+long\b/) {
+					$suffix .= 'LL';
+				} elsif ($cast =~ /\blong\b/) {
+					$suffix .= 'L';
+				}
+				$fixed[$fixlinenr] =~ s/\Q$cast\E$const\b/$newconst$suffix/;
+			}
+		}
+
+# check for sizeof(&)
+		if ($line =~ /\bsizeof\s*\(\s*\&/) {
+			WARN("SIZEOF_ADDRESS",
+			     "sizeof(& should be avoided\n" . $herecurr);
+		}
+
+# check for sizeof without parenthesis
+		if ($line =~ /\bsizeof\s+((?:\*\s*|)$Lval|$Type(?:\s+$Lval|))/) {
+			if (WARN("SIZEOF_PARENTHESIS",
+				 "sizeof $1 should be sizeof($1)\n" . $herecurr) &&
+			    $fix) {
+				$fixed[$fixlinenr] =~ s/\bsizeof\s+((?:\*\s*|)$Lval|$Type(?:\s+$Lval|))/"sizeof(" . trim($1) . ")"/ex;
+			}
+		}
+
+# check for struct spinlock declarations
+		if ($line =~ /^.\s*\bstruct\s+spinlock\s+\w+\s*;/) {
+			WARN("USE_SPINLOCK_T",
+			     "struct spinlock should be spinlock_t\n" . $herecurr);
+		}
+
+# check for seq_printf uses that could be seq_puts
+		if ($sline =~ /\bseq_printf\s*\(.*"\s*\)\s*;\s*$/) {
+			my $fmt = get_quoted_string($line, $rawline);
+			$fmt =~ s/%%//g;
+			if ($fmt !~ /%/) {
+				if (WARN("PREFER_SEQ_PUTS",
+					 "Prefer seq_puts to seq_printf\n" . $herecurr) &&
+				    $fix) {
+					$fixed[$fixlinenr] =~ s/\bseq_printf\b/seq_puts/;
+				}
+			}
+		}
+
+# check for vsprintf extension %p<foo> misuses
+		if ($perl_version_ok &&
+		    defined $stat &&
+		    $stat =~ /^\+(?![^\{]*\{\s*).*\b(\w+)\s*\(.*$String\s*,/s &&
+		    $1 !~ /^_*volatile_*$/) {
+			my $stat_real;
+
+			my $lc = $stat =~ tr@\n@@;
+			$lc = $lc + $linenr;
+		        for (my $count = $linenr; $count <= $lc; $count++) {
+				my $specifier;
+				my $extension;
+				my $qualifier;
+				my $bad_specifier = "";
+				my $fmt = get_quoted_string($lines[$count - 1], raw_line($count, 0));
+				$fmt =~ s/%%//g;
+
+				while ($fmt =~ /(\%[\*\d\.]*p(\w)(\w*))/g) {
+					$specifier = $1;
+					$extension = $2;
+					$qualifier = $3;
+					if ($extension !~ /[SsBKRraEehMmIiUDdgVCbGNOxtf]/ ||
+					    ($extension eq "f" &&
+					     defined $qualifier && $qualifier !~ /^w/)) {
+						$bad_specifier = $specifier;
+						last;
+					}
+					if ($extension eq "x" && !defined($stat_real)) {
+						if (!defined($stat_real)) {
+							$stat_real = get_stat_real($linenr, $lc);
+						}
+						WARN("VSPRINTF_SPECIFIER_PX",
+						     "Using vsprintf specifier '\%px' potentially exposes the kernel memory layout, if you don't really need the address please consider using '\%p'.\n" . "$here\n$stat_real\n");
+					}
+				}
+				if ($bad_specifier ne "") {
+					my $stat_real = get_stat_real($linenr, $lc);
+					my $ext_type = "Invalid";
+					my $use = "";
+					if ($bad_specifier =~ /p[Ff]/) {
+						$use = " - use %pS instead";
+						$use =~ s/pS/ps/ if ($bad_specifier =~ /pf/);
+					}
+
+					WARN("VSPRINTF_POINTER_EXTENSION",
+					     "$ext_type vsprintf pointer extension '$bad_specifier'$use\n" . "$here\n$stat_real\n");
+				}
+			}
+		}
+
+# Check for misused memsets
+		if ($perl_version_ok &&
+		    defined $stat &&
+		    $stat =~ /^\+(?:.*?)\bmemset\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*\,\s*$FuncArg\s*\)/) {
+
+			my $ms_addr = $2;
+			my $ms_val = $7;
+			my $ms_size = $12;
+
+			if ($ms_size =~ /^(0x|)0$/i) {
+				ERROR("MEMSET",
+				      "memset to 0's uses 0 as the 2nd argument, not the 3rd\n" . "$here\n$stat\n");
+			} elsif ($ms_size =~ /^(0x|)1$/i) {
+				WARN("MEMSET",
+				     "single byte memset is suspicious. Swapped 2nd/3rd argument?\n" . "$here\n$stat\n");
+			}
+		}
+
+# Check for memcpy(foo, bar, ETH_ALEN) that could be ether_addr_copy(foo, bar)
+#		if ($perl_version_ok &&
+#		    defined $stat &&
+#		    $stat =~ /^\+(?:.*?)\bmemcpy\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*\,\s*ETH_ALEN\s*\)/) {
+#			if (WARN("PREFER_ETHER_ADDR_COPY",
+#				 "Prefer ether_addr_copy() over memcpy() if the Ethernet addresses are __aligned(2)\n" . "$here\n$stat\n") &&
+#			    $fix) {
+#				$fixed[$fixlinenr] =~ s/\bmemcpy\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*\,\s*ETH_ALEN\s*\)/ether_addr_copy($2, $7)/;
+#			}
+#		}
+
+# Check for memcmp(foo, bar, ETH_ALEN) that could be ether_addr_equal*(foo, bar)
+#		if ($perl_version_ok &&
+#		    defined $stat &&
+#		    $stat =~ /^\+(?:.*?)\bmemcmp\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*\,\s*ETH_ALEN\s*\)/) {
+#			WARN("PREFER_ETHER_ADDR_EQUAL",
+#			     "Prefer ether_addr_equal() or ether_addr_equal_unaligned() over memcmp()\n" . "$here\n$stat\n")
+#		}
+
+# check for memset(foo, 0x0, ETH_ALEN) that could be eth_zero_addr
+# check for memset(foo, 0xFF, ETH_ALEN) that could be eth_broadcast_addr
+#		if ($perl_version_ok &&
+#		    defined $stat &&
+#		    $stat =~ /^\+(?:.*?)\bmemset\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*\,\s*ETH_ALEN\s*\)/) {
+#
+#			my $ms_val = $7;
+#
+#			if ($ms_val =~ /^(?:0x|)0+$/i) {
+#				if (WARN("PREFER_ETH_ZERO_ADDR",
+#					 "Prefer eth_zero_addr over memset()\n" . "$here\n$stat\n") &&
+#				    $fix) {
+#					$fixed[$fixlinenr] =~ s/\bmemset\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*,\s*ETH_ALEN\s*\)/eth_zero_addr($2)/;
+#				}
+#			} elsif ($ms_val =~ /^(?:0xff|255)$/i) {
+#				if (WARN("PREFER_ETH_BROADCAST_ADDR",
+#					 "Prefer eth_broadcast_addr() over memset()\n" . "$here\n$stat\n") &&
+#				    $fix) {
+#					$fixed[$fixlinenr] =~ s/\bmemset\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*,\s*ETH_ALEN\s*\)/eth_broadcast_addr($2)/;
+#				}
+#			}
+#		}
+
+# typecasts on min/max could be min_t/max_t
+		if ($perl_version_ok &&
+		    defined $stat &&
+		    $stat =~ /^\+(?:.*?)\b(min|max)\s*\(\s*$FuncArg\s*,\s*$FuncArg\s*\)/) {
+			if (defined $2 || defined $7) {
+				my $call = $1;
+				my $cast1 = deparenthesize($2);
+				my $arg1 = $3;
+				my $cast2 = deparenthesize($7);
+				my $arg2 = $8;
+				my $cast;
+
+				if ($cast1 ne "" && $cast2 ne "" && $cast1 ne $cast2) {
+					$cast = "$cast1 or $cast2";
+				} elsif ($cast1 ne "") {
+					$cast = $cast1;
+				} else {
+					$cast = $cast2;
+				}
+				WARN("MINMAX",
+				     "$call() should probably be ${call}_t($cast, $arg1, $arg2)\n" . "$here\n$stat\n");
+			}
+		}
+
+# check usleep_range arguments
+		if ($perl_version_ok &&
+		    defined $stat &&
+		    $stat =~ /^\+(?:.*?)\busleep_range\s*\(\s*($FuncArg)\s*,\s*($FuncArg)\s*\)/) {
+			my $min = $1;
+			my $max = $7;
+			if ($min eq $max) {
+				WARN("USLEEP_RANGE",
+				     "usleep_range should not use min == max args; see Documentation/timers/timers-howto.rst\n" . "$here\n$stat\n");
+			} elsif ($min =~ /^\d+$/ && $max =~ /^\d+$/ &&
+				 $min > $max) {
+				WARN("USLEEP_RANGE",
+				     "usleep_range args reversed, use min then max; see Documentation/timers/timers-howto.rst\n" . "$here\n$stat\n");
+			}
+		}
+
+# check for naked sscanf
+		if ($perl_version_ok &&
+		    defined $stat &&
+		    $line =~ /\bsscanf\b/ &&
+		    ($stat !~ /$Ident\s*=\s*sscanf\s*$balanced_parens/ &&
+		     $stat !~ /\bsscanf\s*$balanced_parens\s*(?:$Compare)/ &&
+		     $stat !~ /(?:$Compare)\s*\bsscanf\s*$balanced_parens/)) {
+			my $lc = $stat =~ tr@\n@@;
+			$lc = $lc + $linenr;
+			my $stat_real = get_stat_real($linenr, $lc);
+			WARN("NAKED_SSCANF",
+			     "unchecked sscanf return value\n" . "$here\n$stat_real\n");
+		}
+
+# check for simple sscanf that should be kstrto<foo>
+		if ($perl_version_ok &&
+		    defined $stat &&
+		    $line =~ /\bsscanf\b/) {
+			my $lc = $stat =~ tr@\n@@;
+			$lc = $lc + $linenr;
+			my $stat_real = get_stat_real($linenr, $lc);
+			if ($stat_real =~ /\bsscanf\b\s*\(\s*$FuncArg\s*,\s*("[^"]+")/) {
+				my $format = $6;
+				my $count = $format =~ tr@%@%@;
+				if ($count == 1 &&
+				    $format =~ /^"\%(?i:ll[udxi]|[udxi]ll|ll|[hl]h?[udxi]|[udxi][hl]h?|[hl]h?|[udxi])"$/) {
+					WARN("SSCANF_TO_KSTRTO",
+					     "Prefer kstrto<type> to single variable sscanf\n" . "$here\n$stat_real\n");
+				}
+			}
+		}
+
+# check for new externs in .h files.
+		if ($realfile =~ /\.h$/ &&
+		    $line =~ /^\+\s*(extern\s+)$Type\s*$Ident\s*\(/s) {
+			if (CHK("AVOID_EXTERNS",
+				"extern prototypes should be avoided in .h files\n" . $herecurr) &&
+			    $fix) {
+				$fixed[$fixlinenr] =~ s/(.*)\bextern\b\s*(.*)/$1$2/;
+			}
+		}
+
+# check for new externs in .c files.
+		if ($realfile =~ /\.c$/ && defined $stat &&
+		    $stat =~ /^.\s*(?:extern\s+)?$Type\s+($Ident)(\s*)\(/s)
+		{
+			my $function_name = $1;
+			my $paren_space = $2;
+
+			my $s = $stat;
+			if (defined $cond) {
+				substr($s, 0, length($cond), '');
+			}
+			if ($s =~ /^\s*;/ &&
+			    $function_name ne 'uninitialized_var')
+			{
+				WARN("AVOID_EXTERNS",
+				     "externs should be avoided in .c files\n" .  $herecurr);
+			}
+
+			if ($paren_space =~ /\n/) {
+				WARN("FUNCTION_ARGUMENTS",
+				     "arguments for function declarations should follow identifier\n" . $herecurr);
+			}
+
+		} elsif ($realfile =~ /\.c$/ && defined $stat &&
+		    $stat =~ /^.\s*extern\s+/)
+		{
+			WARN("AVOID_EXTERNS",
+			     "externs should be avoided in .c files\n" .  $herecurr);
+		}
+
+# check for function declarations that have arguments without identifier names
+# while avoiding uninitialized_var(x)
+		if (defined $stat &&
+		    $stat =~ /^.\s*(?:extern\s+)?$Type\s*(?:($Ident)|\(\s*\*\s*$Ident\s*\))\s*\(\s*([^{]+)\s*\)\s*;/s &&
+		    (!defined($1) ||
+		     (defined($1) && $1 ne "uninitialized_var")) &&
+		     $2 ne "void") {
+			my $args = trim($2);
+			while ($args =~ m/\s*($Type\s*(?:$Ident|\(\s*\*\s*$Ident?\s*\)\s*$balanced_parens)?)/g) {
+				my $arg = trim($1);
+				if ($arg =~ /^$Type$/ &&
+					$arg !~ /enum\s+$Ident$/) {
+					WARN("FUNCTION_ARGUMENTS",
+					     "function definition argument '$arg' should also have an identifier name\n" . $herecurr);
+				}
+			}
+		}
+
+# check for function definitions
+		if ($perl_version_ok &&
+		    defined $stat &&
+		    $stat =~ /^.\s*(?:$Storage\s+)?$Type\s*($Ident)\s*$balanced_parens\s*{/s) {
+			$context_function = $1;
+
+# check for multiline function definition with misplaced open brace
+			my $ok = 0;
+			my $cnt = statement_rawlines($stat);
+			my $herectx = $here . "\n";
+			for (my $n = 0; $n < $cnt; $n++) {
+				my $rl = raw_line($linenr, $n);
+				$herectx .=  $rl . "\n";
+				$ok = 1 if ($rl =~ /^[ \+]\{/);
+				$ok = 1 if ($rl =~ /\{/ && $n == 0);
+				last if $rl =~ /^[ \+].*\{/;
+			}
+			if (!$ok) {
+				ERROR("OPEN_BRACE",
+				      "open brace '{' following function definitions go on the next line\n" . $herectx);
+			}
+		}
+
+# check for pointless casting of alloc functions
+		if ($line =~ /\*\s*\)\s*$allocFunctions\b/) {
+			WARN("UNNECESSARY_CASTS",
+			     "unnecessary cast may hide bugs, see http://c-faq.com/malloc/mallocnocast.html\n" . $herecurr);
+		}
+
+# alloc style
+# p = alloc(sizeof(struct foo), ...) should be p = alloc(sizeof(*p), ...)
+		if ($perl_version_ok &&
+		    $line =~ /\b($Lval)\s*\=\s*(?:$balanced_parens)?\s*((?:kv|k|v)[mz]alloc(?:_node)?)\s*\(\s*(sizeof\s*\(\s*struct\s+$Lval\s*\))/) {
+			CHK("ALLOC_SIZEOF_STRUCT",
+			    "Prefer $3(sizeof(*$1)...) over $3($4...)\n" . $herecurr);
+		}
+
+# check for k[mz]alloc with multiplies that could be kmalloc_array/kcalloc
+		if ($perl_version_ok &&
+		    defined $stat &&
+		    $stat =~ /^\+\s*($Lval)\s*\=\s*(?:$balanced_parens)?\s*(k[mz]alloc)\s*\(\s*($FuncArg)\s*\*\s*($FuncArg)\s*,/) {
+			my $oldfunc = $3;
+			my $a1 = $4;
+			my $a2 = $10;
+			my $newfunc = "kmalloc_array";
+			$newfunc = "kcalloc" if ($oldfunc eq "kzalloc");
+			my $r1 = $a1;
+			my $r2 = $a2;
+			if ($a1 =~ /^sizeof\s*\S/) {
+				$r1 = $a2;
+				$r2 = $a1;
+			}
+			if ($r1 !~ /^sizeof\b/ && $r2 =~ /^sizeof\s*\S/ &&
+			    !($r1 =~ /^$Constant$/ || $r1 =~ /^[A-Z_][A-Z0-9_]*$/)) {
+				my $cnt = statement_rawlines($stat);
+				my $herectx = get_stat_here($linenr, $cnt, $here);
+
+				if (WARN("ALLOC_WITH_MULTIPLY",
+					 "Prefer $newfunc over $oldfunc with multiply\n" . $herectx) &&
+				    $cnt == 1 &&
+				    $fix) {
+					$fixed[$fixlinenr] =~ s/\b($Lval)\s*\=\s*(?:$balanced_parens)?\s*(k[mz]alloc)\s*\(\s*($FuncArg)\s*\*\s*($FuncArg)/$1 . ' = ' . "$newfunc(" . trim($r1) . ', ' . trim($r2)/e;
+				}
+			}
+		}
+
+# check for krealloc arg reuse
+		if ($perl_version_ok &&
+		    $line =~ /\b($Lval)\s*\=\s*(?:$balanced_parens)?\s*krealloc\s*\(\s*($Lval)\s*,/ &&
+		    $1 eq $3) {
+			WARN("KREALLOC_ARG_REUSE",
+			     "Reusing the krealloc arg is almost always a bug\n" . $herecurr);
+		}
+
+# check for alloc argument mismatch
+		if ($line =~ /\b(kcalloc|kmalloc_array)\s*\(\s*sizeof\b/) {
+			WARN("ALLOC_ARRAY_ARGS",
+			     "$1 uses number as first arg, sizeof is generally wrong\n" . $herecurr);
+		}
+
+# check for multiple semicolons
+		if ($line =~ /;\s*;\s*$/) {
+			if (WARN("ONE_SEMICOLON",
+				 "Statements terminations use 1 semicolon\n" . $herecurr) &&
+			    $fix) {
+				$fixed[$fixlinenr] =~ s/(\s*;\s*){2,}$/;/g;
+			}
+		}
+
+# check for #defines like: 1 << <digit> that could be BIT(digit), it is not exported to uapi
+		if ($realfile !~ m@^include/uapi/@ &&
+		    $line =~ /#\s*define\s+\w+\s+\(?\s*1\s*([ulUL]*)\s*\<\<\s*(?:\d+|$Ident)\s*\)?/) {
+			my $ull = "";
+			$ull = "_ULL" if (defined($1) && $1 =~ /ll/i);
+			if (CHK("BIT_MACRO",
+				"Prefer using the BIT$ull macro\n" . $herecurr) &&
+			    $fix) {
+				$fixed[$fixlinenr] =~ s/\(?\s*1\s*[ulUL]*\s*<<\s*(\d+|$Ident)\s*\)?/BIT${ull}($1)/;
+			}
+		}
+
+# check for #if defined CONFIG_<FOO> || defined CONFIG_<FOO>_MODULE
+		if ($line =~ /^\+\s*#\s*if\s+defined(?:\s*\(?\s*|\s+)(CONFIG_[A-Z_]+)\s*\)?\s*\|\|\s*defined(?:\s*\(?\s*|\s+)\1_MODULE\s*\)?\s*$/) {
+			my $config = $1;
+			if (WARN("PREFER_IS_ENABLED",
+				 "Prefer IS_ENABLED(<FOO>) to CONFIG_<FOO> || CONFIG_<FOO>_MODULE\n" . $herecurr) &&
+			    $fix) {
+				$fixed[$fixlinenr] = "\+#if IS_ENABLED($config)";
+			}
+		}
+
+# check for case / default statements not preceded by break/fallthrough/switch
+		if ($line =~ /^.\s*(?:case\s+(?:$Ident|$Constant)\s*|default):/) {
+			my $has_break = 0;
+			my $has_statement = 0;
+			my $count = 0;
+			my $prevline = $linenr;
+			while ($prevline > 1 && ($file || $count < 3) && !$has_break) {
+				$prevline--;
+				my $rline = $rawlines[$prevline - 1];
+				my $fline = $lines[$prevline - 1];
+				last if ($fline =~ /^\@\@/);
+				next if ($fline =~ /^\-/);
+				next if ($fline =~ /^.(?:\s*(?:case\s+(?:$Ident|$Constant)[\s$;]*|default):[\s$;]*)*$/);
+				$has_break = 1 if ($rline =~ /fall[\s_-]*(through|thru)/i);
+				next if ($fline =~ /^.[\s$;]*$/);
+				$has_statement = 1;
+				$count++;
+				$has_break = 1 if ($fline =~ /\bswitch\b|\b(?:break\s*;[\s$;]*$|exit\s*\(\b|return\b|goto\b|continue\b)/);
+			}
+			if (!$has_break && $has_statement) {
+				WARN("MISSING_BREAK",
+				     "Possible switch case/default not preceded by break or fallthrough comment\n" . $herecurr);
+			}
+		}
+
+# check for /* fallthrough */ like comment, prefer fallthrough;
+		my @fallthroughs = (
+			'fallthrough',
+			'@fallthrough@',
+			'lint -fallthrough[ \t]*',
+			'intentional(?:ly)?[ \t]*fall(?:(?:s | |-)[Tt]|t)hr(?:ough|u|ew)',
+			'(?:else,?\s*)?FALL(?:S | |-)?THR(?:OUGH|U|EW)[ \t.!]*(?:-[^\n\r]*)?',
+			'Fall(?:(?:s | |-)[Tt]|t)hr(?:ough|u|ew)[ \t.!]*(?:-[^\n\r]*)?',
+			'fall(?:s | |-)?thr(?:ough|u|ew)[ \t.!]*(?:-[^\n\r]*)?',
+		    );
+		if ($raw_comment ne '') {
+			foreach my $ft (@fallthroughs) {
+				if ($raw_comment =~ /$ft/) {
+					my $msg_level = \&WARN;
+					$msg_level = \&CHK if ($file);
+					&{$msg_level}("PREFER_FALLTHROUGH",
+						      "Prefer 'fallthrough;' over fallthrough comment\n" . $herecurr);
+					last;
+				}
+			}
+		}
+
+# check for switch/default statements without a break;
+		if ($perl_version_ok &&
+		    defined $stat &&
+		    $stat =~ /^\+[$;\s]*(?:case[$;\s]+\w+[$;\s]*:[$;\s]*|)*[$;\s]*\bdefault[$;\s]*:[$;\s]*;/g) {
+			my $cnt = statement_rawlines($stat);
+			my $herectx = get_stat_here($linenr, $cnt, $here);
+
+			WARN("DEFAULT_NO_BREAK",
+			     "switch default: should use break\n" . $herectx);
+		}
+
+# check for gcc specific __FUNCTION__
+		if ($line =~ /\b__FUNCTION__\b/) {
+			if (WARN("USE_FUNC",
+				 "__func__ should be used instead of gcc specific __FUNCTION__\n"  . $herecurr) &&
+			    $fix) {
+				$fixed[$fixlinenr] =~ s/\b__FUNCTION__\b/__func__/g;
+			}
+		}
+
+# check for uses of __DATE__, __TIME__, __TIMESTAMP__
+		while ($line =~ /\b(__(?:DATE|TIME|TIMESTAMP)__)\b/g) {
+			ERROR("DATE_TIME",
+			      "Use of the '$1' macro makes the build non-deterministic\n" . $herecurr);
+		}
+
+# check for use of yield()
+		if ($line =~ /\byield\s*\(\s*\)/) {
+			WARN("YIELD",
+			     "Using yield() is generally wrong. See yield() kernel-doc (sched/core.c)\n"  . $herecurr);
+		}
+
+# check for comparisons against true and false
+		if ($line =~ /\+\s*(.*?)\b(true|false|$Lval)\s*(==|\!=)\s*(true|false|$Lval)\b(.*)$/i) {
+			my $lead = $1;
+			my $arg = $2;
+			my $test = $3;
+			my $otype = $4;
+			my $trail = $5;
+			my $op = "!";
+
+			($arg, $otype) = ($otype, $arg) if ($arg =~ /^(?:true|false)$/i);
+
+			my $type = lc($otype);
+			if ($type =~ /^(?:true|false)$/) {
+				if (("$test" eq "==" && "$type" eq "true") ||
+				    ("$test" eq "!=" && "$type" eq "false")) {
+					$op = "";
+				}
+
+				CHK("BOOL_COMPARISON",
+				    "Using comparison to $otype is error prone\n" . $herecurr);
+
+## maybe suggesting a correct construct would better
+##				    "Using comparison to $otype is error prone.  Perhaps use '${lead}${op}${arg}${trail}'\n" . $herecurr);
+
+			}
+		}
+
+# check for semaphores initialized locked
+		if ($line =~ /^.\s*sema_init.+,\W?0\W?\)/) {
+			WARN("CONSIDER_COMPLETION",
+			     "consider using a completion\n" . $herecurr);
+		}
+
+# recommend kstrto* over simple_strto* and strict_strto*
+		if ($line =~ /\b((simple|strict)_(strto(l|ll|ul|ull)))\s*\(/) {
+			WARN("CONSIDER_KSTRTO",
+			     "$1 is obsolete, use k$3 instead\n" . $herecurr);
+		}
+
+# check for __initcall(), use device_initcall() explicitly or more appropriate function please
+		if ($line =~ /^.\s*__initcall\s*\(/) {
+			WARN("USE_DEVICE_INITCALL",
+			     "please use device_initcall() or more appropriate function instead of __initcall() (see include/linux/init.h)\n" . $herecurr);
+		}
+
+# check for spin_is_locked(), suggest lockdep instead
+		if ($line =~ /\bspin_is_locked\(/) {
+			WARN("USE_LOCKDEP",
+			     "Where possible, use lockdep_assert_held instead of assertions based on spin_is_locked\n" . $herecurr);
+		}
+
+# check for deprecated apis
+		if ($line =~ /\b($deprecated_apis_search)\b\s*\(/) {
+			my $deprecated_api = $1;
+			my $new_api = $deprecated_apis{$deprecated_api};
+			WARN("DEPRECATED_API",
+			     "Deprecated use of '$deprecated_api', prefer '$new_api' instead\n" . $herecurr);
+		}
+
+# check for various structs that are normally const (ops, kgdb, device_tree)
+# and avoid what seem like struct definitions 'struct foo {'
+		if ($line !~ /\bconst\b/ &&
+		    $line =~ /\bstruct\s+($const_structs)\b(?!\s*\{)/) {
+			WARN("CONST_STRUCT",
+			     "struct $1 should normally be const\n" . $herecurr);
+		}
+
+# use of NR_CPUS is usually wrong
+# ignore definitions of NR_CPUS and usage to define arrays as likely right
+		if ($line =~ /\bNR_CPUS\b/ &&
+		    $line !~ /^.\s*\s*#\s*if\b.*\bNR_CPUS\b/ &&
+		    $line !~ /^.\s*\s*#\s*define\b.*\bNR_CPUS\b/ &&
+		    $line !~ /^.\s*$Declare\s.*\[[^\]]*NR_CPUS[^\]]*\]/ &&
+		    $line !~ /\[[^\]]*\.\.\.[^\]]*NR_CPUS[^\]]*\]/ &&
+		    $line !~ /\[[^\]]*NR_CPUS[^\]]*\.\.\.[^\]]*\]/)
+		{
+			WARN("NR_CPUS",
+			     "usage of NR_CPUS is often wrong - consider using cpu_possible(), num_possible_cpus(), for_each_possible_cpu(), etc\n" . $herecurr);
+		}
+
+# Use of __ARCH_HAS_<FOO> or ARCH_HAVE_<BAR> is wrong.
+		if ($line =~ /\+\s*#\s*define\s+((?:__)?ARCH_(?:HAS|HAVE)\w*)\b/) {
+			ERROR("DEFINE_ARCH_HAS",
+			      "#define of '$1' is wrong - use Kconfig variables or standard guards instead\n" . $herecurr);
+		}
+
+# likely/unlikely comparisons similar to "(likely(foo) > 0)"
+		if ($perl_version_ok &&
+		    $line =~ /\b((?:un)?likely)\s*\(\s*$FuncArg\s*\)\s*$Compare/) {
+			WARN("LIKELY_MISUSE",
+			     "Using $1 should generally have parentheses around the comparison\n" . $herecurr);
+		}
+
+# nested likely/unlikely calls
+		if ($line =~ /\b(?:(?:un)?likely)\s*\(\s*!?\s*(IS_ERR(?:_OR_NULL|_VALUE)?|WARN)/) {
+			WARN("LIKELY_MISUSE",
+			     "nested (un)?likely() calls, $1 already uses unlikely() internally\n" . $herecurr);
+		}
+
+# whine mightly about in_atomic
+		if ($line =~ /\bin_atomic\s*\(/) {
+			if ($realfile =~ m@^drivers/@) {
+				ERROR("IN_ATOMIC",
+				      "do not use in_atomic in drivers\n" . $herecurr);
+			} elsif ($realfile !~ m@^kernel/@) {
+				WARN("IN_ATOMIC",
+				     "use of in_atomic() is incorrect outside core kernel code\n" . $herecurr);
+			}
+		}
+
+# check for mutex_trylock_recursive usage
+		if ($line =~ /mutex_trylock_recursive/) {
+			ERROR("LOCKING",
+			      "recursive locking is bad, do not use this ever.\n" . $herecurr);
+		}
+
+# check for lockdep_set_novalidate_class
+		if ($line =~ /^.\s*lockdep_set_novalidate_class\s*\(/ ||
+		    $line =~ /__lockdep_no_validate__\s*\)/ ) {
+			if ($realfile !~ m@^kernel/lockdep@ &&
+			    $realfile !~ m@^include/linux/lockdep@ &&
+			    $realfile !~ m@^drivers/base/core@) {
+				ERROR("LOCKDEP",
+				      "lockdep_no_validate class is reserved for device->mutex.\n" . $herecurr);
+			}
+		}
+
+		if ($line =~ /debugfs_create_\w+.*\b$mode_perms_world_writable\b/ ||
+		    $line =~ /DEVICE_ATTR.*\b$mode_perms_world_writable\b/) {
+			WARN("EXPORTED_WORLD_WRITABLE",
+			     "Exporting world writable files is usually an error. Consider more restrictive permissions.\n" . $herecurr);
+		}
+
+# check for DEVICE_ATTR uses that could be DEVICE_ATTR_<FOO>
+# and whether or not function naming is typical and if
+# DEVICE_ATTR permissions uses are unusual too
+		if ($perl_version_ok &&
+		    defined $stat &&
+		    $stat =~ /\bDEVICE_ATTR\s*\(\s*(\w+)\s*,\s*\(?\s*(\s*(?:${multi_mode_perms_string_search}|0[0-7]{3,3})\s*)\s*\)?\s*,\s*(\w+)\s*,\s*(\w+)\s*\)/) {
+			my $var = $1;
+			my $perms = $2;
+			my $show = $3;
+			my $store = $4;
+			my $octal_perms = perms_to_octal($perms);
+			if ($show =~ /^${var}_show$/ &&
+			    $store =~ /^${var}_store$/ &&
+			    $octal_perms eq "0644") {
+				if (WARN("DEVICE_ATTR_RW",
+					 "Use DEVICE_ATTR_RW\n" . $herecurr) &&
+				    $fix) {
+					$fixed[$fixlinenr] =~ s/\bDEVICE_ATTR\s*\(\s*$var\s*,\s*\Q$perms\E\s*,\s*$show\s*,\s*$store\s*\)/DEVICE_ATTR_RW(${var})/;
+				}
+			} elsif ($show =~ /^${var}_show$/ &&
+				 $store =~ /^NULL$/ &&
+				 $octal_perms eq "0444") {
+				if (WARN("DEVICE_ATTR_RO",
+					 "Use DEVICE_ATTR_RO\n" . $herecurr) &&
+				    $fix) {
+					$fixed[$fixlinenr] =~ s/\bDEVICE_ATTR\s*\(\s*$var\s*,\s*\Q$perms\E\s*,\s*$show\s*,\s*NULL\s*\)/DEVICE_ATTR_RO(${var})/;
+				}
+			} elsif ($show =~ /^NULL$/ &&
+				 $store =~ /^${var}_store$/ &&
+				 $octal_perms eq "0200") {
+				if (WARN("DEVICE_ATTR_WO",
+					 "Use DEVICE_ATTR_WO\n" . $herecurr) &&
+				    $fix) {
+					$fixed[$fixlinenr] =~ s/\bDEVICE_ATTR\s*\(\s*$var\s*,\s*\Q$perms\E\s*,\s*NULL\s*,\s*$store\s*\)/DEVICE_ATTR_WO(${var})/;
+				}
+			} elsif ($octal_perms eq "0644" ||
+				 $octal_perms eq "0444" ||
+				 $octal_perms eq "0200") {
+				my $newshow = "$show";
+				$newshow = "${var}_show" if ($show ne "NULL" && $show ne "${var}_show");
+				my $newstore = $store;
+				$newstore = "${var}_store" if ($store ne "NULL" && $store ne "${var}_store");
+				my $rename = "";
+				if ($show ne $newshow) {
+					$rename .= " '$show' to '$newshow'";
+				}
+				if ($store ne $newstore) {
+					$rename .= " '$store' to '$newstore'";
+				}
+				WARN("DEVICE_ATTR_FUNCTIONS",
+				     "Consider renaming function(s)$rename\n" . $herecurr);
+			} else {
+				WARN("DEVICE_ATTR_PERMS",
+				     "DEVICE_ATTR unusual permissions '$perms' used\n" . $herecurr);
+			}
+		}
+
+# Mode permission misuses where it seems decimal should be octal
+# This uses a shortcut match to avoid unnecessary uses of a slow foreach loop
+# o Ignore module_param*(...) uses with a decimal 0 permission as that has a
+#   specific definition of not visible in sysfs.
+# o Ignore proc_create*(...) uses with a decimal 0 permission as that means
+#   use the default permissions
+		if ($perl_version_ok &&
+		    defined $stat &&
+		    $line =~ /$mode_perms_search/) {
+			foreach my $entry (@mode_permission_funcs) {
+				my $func = $entry->[0];
+				my $arg_pos = $entry->[1];
+
+				my $lc = $stat =~ tr@\n@@;
+				$lc = $lc + $linenr;
+				my $stat_real = get_stat_real($linenr, $lc);
+
+				my $skip_args = "";
+				if ($arg_pos > 1) {
+					$arg_pos--;
+					$skip_args = "(?:\\s*$FuncArg\\s*,\\s*){$arg_pos,$arg_pos}";
+				}
+				my $test = "\\b$func\\s*\\(${skip_args}($FuncArg(?:\\|\\s*$FuncArg)*)\\s*[,\\)]";
+				if ($stat =~ /$test/) {
+					my $val = $1;
+					$val = $6 if ($skip_args ne "");
+					if (!($func =~ /^(?:module_param|proc_create)/ && $val eq "0") &&
+					    (($val =~ /^$Int$/ && $val !~ /^$Octal$/) ||
+					     ($val =~ /^$Octal$/ && length($val) ne 4))) {
+						ERROR("NON_OCTAL_PERMISSIONS",
+						      "Use 4 digit octal (0777) not decimal permissions\n" . "$here\n" . $stat_real);
+					}
+					if ($val =~ /^$Octal$/ && (oct($val) & 02)) {
+						ERROR("EXPORTED_WORLD_WRITABLE",
+						      "Exporting writable files is usually an error. Consider more restrictive permissions.\n" . "$here\n" . $stat_real);
+					}
+				}
+			}
+		}
+
+# check for uses of S_<PERMS> that could be octal for readability
+		while ($line =~ m{\b($multi_mode_perms_string_search)\b}g) {
+			my $oval = $1;
+			my $octal = perms_to_octal($oval);
+			if (WARN("SYMBOLIC_PERMS",
+				 "Symbolic permissions '$oval' are not preferred. Consider using octal permissions '$octal'.\n" . $herecurr) &&
+			    $fix) {
+				$fixed[$fixlinenr] =~ s/\Q$oval\E/$octal/;
+			}
+		}
+
+# validate content of MODULE_LICENSE against list from include/linux/module.h
+		if ($line =~ /\bMODULE_LICENSE\s*\(\s*($String)\s*\)/) {
+			my $extracted_string = get_quoted_string($line, $rawline);
+			my $valid_licenses = qr{
+						GPL|
+						GPL\ v2|
+						GPL\ and\ additional\ rights|
+						Dual\ BSD/GPL|
+						Dual\ MIT/GPL|
+						Dual\ MPL/GPL|
+						Proprietary
+					}x;
+			if ($extracted_string !~ /^"(?:$valid_licenses)"$/x) {
+				WARN("MODULE_LICENSE",
+				     "unknown module license " . $extracted_string . "\n" . $herecurr);
+			}
+		}
+
+# check for sysctl duplicate constants
+		if ($line =~ /\.extra[12]\s*=\s*&(zero|one|int_max)\b/) {
+			WARN("DUPLICATED_SYSCTL_CONST",
+				"duplicated sysctl range checking value '$1', consider using the shared one in include/linux/sysctl.h\n" . $herecurr);
+		}
+	}
+
+	# If we have no input at all, then there is nothing to report on
+	# so just keep quiet.
+	if ($#rawlines == -1) {
+		exit(0);
+	}
+
+	# In mailback mode only produce a report in the negative, for
+	# things that appear to be patches.
+	if ($mailback && ($clean == 1 || !$is_patch)) {
+		exit(0);
+	}
+
+	# This is not a patch, and we are are in 'no-patch' mode so
+	# just keep quiet.
+	if (!$chk_patch && !$is_patch) {
+		exit(0);
+	}
+
+	if (!$is_patch && $filename !~ /cover-letter\.patch$/) {
+		ERROR("NOT_UNIFIED_DIFF",
+		      "Does not appear to be a unified-diff format patch\n");
+	}
+	if ($is_patch && $has_commit_log && $chk_signoff) {
+		if ($signoff == 0) {
+			ERROR("MISSING_SIGN_OFF",
+			      "Missing Signed-off-by: line(s)\n");
+		} elsif (!$authorsignoff) {
+			WARN("NO_AUTHOR_SIGN_OFF",
+			     "Missing Signed-off-by: line by nominal patch author '$author'\n");
+		}
+	}
+
+	print report_dump();
+	if ($summary && !($clean == 1 && $quiet == 1)) {
+		print "$filename " if ($summary_file);
+		print "total: $cnt_error errors, $cnt_warn warnings, " .
+			(($check)? "$cnt_chk checks, " : "") .
+			"$cnt_lines lines checked\n";
+	}
+
+	if ($quiet == 0) {
+		# If there were any defects found and not already fixing them
+		if (!$clean and !$fix) {
+			print << "EOM"
+
+NOTE: For some of the reported defects, checkpatch may be able to
+      mechanically convert to the typical style using --fix or --fix-inplace.
+EOM
+		}
+		# If there were whitespace errors which cleanpatch can fix
+		# then suggest that.
+		if ($rpt_cleaners) {
+			$rpt_cleaners = 0;
+			print << "EOM"
+
+NOTE: Whitespace errors detected.
+      You may wish to use scripts/cleanpatch or scripts/cleanfile
+EOM
+		}
+	}
+
+	if ($clean == 0 && $fix &&
+	    ("@rawlines" ne "@fixed" ||
+	     $#fixed_inserted >= 0 || $#fixed_deleted >= 0)) {
+		my $newfile = $filename;
+		$newfile .= ".EXPERIMENTAL-checkpatch-fixes" if (!$fix_inplace);
+		my $linecount = 0;
+		my $f;
+
+		@fixed = fix_inserted_deleted_lines(\@fixed, \@fixed_inserted, \@fixed_deleted);
+
+		open($f, '>', $newfile)
+		    or die "$P: Can't open $newfile for write\n";
+		foreach my $fixed_line (@fixed) {
+			$linecount++;
+			if ($file) {
+				if ($linecount > 3) {
+					$fixed_line =~ s/^\+//;
+					print $f $fixed_line . "\n";
+				}
+			} else {
+				print $f $fixed_line . "\n";
+			}
+		}
+		close($f);
+
+		if (!$quiet) {
+			print << "EOM";
+
+Wrote EXPERIMENTAL --fix correction(s) to '$newfile'
+
+Do _NOT_ trust the results written to this file.
+Do _NOT_ submit these changes without inspecting them for correctness.
+
+This EXPERIMENTAL file is simply a convenience to help rewrite patches.
+No warranties, expressed or implied...
+EOM
+		}
+	}
+
+	if ($quiet == 0) {
+		print "\n";
+		if ($clean == 1) {
+			print "$vname has no obvious style problems and is ready for submission.\n";
+		} else {
+			print "$vname has style problems, please review.\n";
+		}
+	}
+	return $clean;
+}
diff --git a/scripts/clean-package.sh b/scripts/clean-package.sh
new file mode 100755
index 0000000..0357256
--- /dev/null
+++ b/scripts/clean-package.sh
@@ -0,0 +1,25 @@
+#!/usr/bin/env bash
+IFS=$'\n'
+[ -n "$1" -a -n "$2" ] || {
+	echo "Usage: $0 <file> <directory>"
+	exit 1
+}
+[ -f "$1" -a -d "$2" ] || {
+	echo "File/directory not found"
+	exit 1
+}
+cat "$1" | (
+	cd "$2"
+	while read entry; do
+		[ -n "$entry" ] || break
+		[ ! -d "$entry" ] || [ -L "$entry" ] && rm -f "$entry"
+	done
+)
+sort -r "$1" | (
+	cd "$2"
+	while read entry; do
+		[ -n "$entry" ] || break
+		[ -d "$entry" ] && rmdir "$entry" > /dev/null 2>&1
+	done
+)
+true
diff --git a/scripts/cleanfile b/scripts/cleanfile
new file mode 100755
index 0000000..64507c4
--- /dev/null
+++ b/scripts/cleanfile
@@ -0,0 +1,177 @@
+#!/usr/bin/env perl
+#
+# Clean a text file -- or directory of text files -- of stealth whitespace.
+# WARNING: this can be a highly destructive operation.  Use with caution.
+#
+
+use bytes;
+use File::Basename;
+use warnings;
+
+# Default options
+$max_width = 79;
+
+# Clean up space-tab sequences, either by removing spaces or
+# replacing them with tabs.
+sub clean_space_tabs($)
+{
+    no bytes;			# Tab alignment depends on characters
+
+    my($li) = @_;
+    my($lo) = '';
+    my $pos = 0;
+    my $nsp = 0;
+    my($i, $c);
+
+    for ($i = 0; $i < length($li); $i++) {
+	$c = substr($li, $i, 1);
+	if ($c eq "\t") {
+	    my $npos = ($pos+$nsp+8) & ~7;
+	    my $ntab = ($npos >> 3) - ($pos >> 3);
+	    $lo .= "\t" x $ntab;
+	    $pos = $npos;
+	    $nsp = 0;
+	} elsif ($c eq "\n" || $c eq "\r") {
+	    $lo .= " " x $nsp;
+	    $pos += $nsp;
+	    $nsp = 0;
+	    $lo .= $c;
+	    $pos = 0;
+	} elsif ($c eq " ") {
+	    $nsp++;
+	} else {
+	    $lo .= " " x $nsp;
+	    $pos += $nsp;
+	    $nsp = 0;
+	    $lo .= $c;
+	    $pos++;
+	}
+    }
+    $lo .= " " x $nsp;
+    return $lo;
+}
+
+# Compute the visual width of a string
+sub strwidth($) {
+    no bytes;			# Tab alignment depends on characters
+
+    my($li) = @_;
+    my($c, $i);
+    my $pos = 0;
+    my $mlen = 0;
+
+    for ($i = 0; $i < length($li); $i++) {
+	$c = substr($li,$i,1);
+	if ($c eq "\t") {
+	    $pos = ($pos+8) & ~7;
+	} elsif ($c eq "\n") {
+	    $mlen = $pos if ($pos > $mlen);
+	    $pos = 0;
+	} else {
+	    $pos++;
+	}
+    }
+
+    $mlen = $pos if ($pos > $mlen);
+    return $mlen;
+}
+
+$name = basename($0);
+
+@files = ();
+
+while (defined($a = shift(@ARGV))) {
+    if ($a =~ /^-/) {
+	if ($a eq '-width' || $a eq '-w') {
+	    $max_width = shift(@ARGV)+0;
+	} else {
+	    print STDERR "Usage: $name [-width #] files...\n";
+	    exit 1;
+	}
+    } else {
+	push(@files, $a);
+    }
+}
+
+foreach $f ( @files ) {
+    print STDERR "$name: $f\n";
+
+    if (! -f $f) {
+	print STDERR "$f: not a file\n";
+	next;
+    }
+
+    if (!open(FILE, '+<', $f)) {
+	print STDERR "$name: Cannot open file: $f: $!\n";
+	next;
+    }
+
+    binmode FILE;
+
+    # First, verify that it is not a binary file; consider any file
+    # with a zero byte to be a binary file.  Is there any better, or
+    # additional, heuristic that should be applied?
+    $is_binary = 0;
+
+    while (read(FILE, $data, 65536) > 0) {
+	if ($data =~ /\0/) {
+	    $is_binary = 1;
+	    last;
+	}
+    }
+
+    if ($is_binary) {
+	print STDERR "$name: $f: binary file\n";
+	next;
+    }
+
+    seek(FILE, 0, 0);
+
+    $in_bytes = 0;
+    $out_bytes = 0;
+    $blank_bytes = 0;
+
+    @blanks = ();
+    @lines  = ();
+    $lineno = 0;
+
+    while ( defined($line = <FILE>) ) {
+	$lineno++;
+	$in_bytes += length($line);
+	$line =~ s/[ \t\r]*$//;		# Remove trailing spaces
+	$line = clean_space_tabs($line);
+
+	if ( $line eq "\n" ) {
+	    push(@blanks, $line);
+	    $blank_bytes += length($line);
+	} else {
+	    push(@lines, @blanks);
+	    $out_bytes += $blank_bytes;
+	    push(@lines, $line);
+	    $out_bytes += length($line);
+	    @blanks = ();
+	    $blank_bytes = 0;
+	}
+
+	$l_width = strwidth($line);
+	if ($max_width && $l_width > $max_width) {
+	    print STDERR
+		"$f:$lineno: line exceeds $max_width characters ($l_width)\n";
+	}
+    }
+
+    # Any blanks at the end of the file are discarded
+
+    if ($in_bytes != $out_bytes) {
+	# Only write to the file if changed
+	seek(FILE, 0, 0);
+	print FILE @lines;
+
+	if ( !defined($where = tell(FILE)) ||
+	     !truncate(FILE, $where) ) {
+	    die "$name: Failed to truncate modified file: $f: $!\n";
+	}
+    }
+
+    close(FILE);
+}
diff --git a/scripts/cleanpatch b/scripts/cleanpatch
new file mode 100755
index 0000000..ab5ebdd
--- /dev/null
+++ b/scripts/cleanpatch
@@ -0,0 +1,259 @@
+#!/usr/bin/env perl 
+#
+# Clean a patch file -- or directory of patch files -- of stealth whitespace.
+# WARNING: this can be a highly destructive operation.  Use with caution.
+#
+
+use bytes;
+use File::Basename;
+use warnings;
+
+# Default options
+$max_width = 79;
+
+# Clean up space-tab sequences, either by removing spaces or
+# replacing them with tabs.
+sub clean_space_tabs($)
+{
+    no bytes;			# Tab alignment depends on characters
+
+    my($li) = @_;
+    my($lo) = '';
+    my $pos = 0;
+    my $nsp = 0;
+    my($i, $c);
+
+    for ($i = 0; $i < length($li); $i++) {
+	$c = substr($li, $i, 1);
+	if ($c eq "\t") {
+	    my $npos = ($pos+$nsp+8) & ~7;
+	    my $ntab = ($npos >> 3) - ($pos >> 3);
+	    $lo .= "\t" x $ntab;
+	    $pos = $npos;
+	    $nsp = 0;
+	} elsif ($c eq "\n" || $c eq "\r") {
+	    $lo .= " " x $nsp;
+	    $pos += $nsp;
+	    $nsp = 0;
+	    $lo .= $c;
+	    $pos = 0;
+	} elsif ($c eq " ") {
+	    $nsp++;
+	} else {
+	    $lo .= " " x $nsp;
+	    $pos += $nsp;
+	    $nsp = 0;
+	    $lo .= $c;
+	    $pos++;
+	}
+    }
+    $lo .= " " x $nsp;
+    return $lo;
+}
+
+# Compute the visual width of a string
+sub strwidth($) {
+    no bytes;			# Tab alignment depends on characters
+
+    my($li) = @_;
+    my($c, $i);
+    my $pos = 0;
+    my $mlen = 0;
+
+    for ($i = 0; $i < length($li); $i++) {
+	$c = substr($li,$i,1);
+	if ($c eq "\t") {
+	    $pos = ($pos+8) & ~7;
+	} elsif ($c eq "\n") {
+	    $mlen = $pos if ($pos > $mlen);
+	    $pos = 0;
+	} else {
+	    $pos++;
+	}
+    }
+
+    $mlen = $pos if ($pos > $mlen);
+    return $mlen;
+}
+
+$name = basename($0);
+
+@files = ();
+
+while (defined($a = shift(@ARGV))) {
+    if ($a =~ /^-/) {
+	if ($a eq '-width' || $a eq '-w') {
+	    $max_width = shift(@ARGV)+0;
+	} else {
+	    print STDERR "Usage: $name [-width #] files...\n";
+	    exit 1;
+	}
+    } else {
+	push(@files, $a);
+    }
+}
+
+foreach $f ( @files ) {
+    print STDERR "$name: $f\n";
+
+    if (! -f $f) {
+	print STDERR "$f: not a file\n";
+	next;
+    }
+
+    if (!open(FILE, '+<', $f)) {
+	print STDERR "$name: Cannot open file: $f: $!\n";
+	next;
+    }
+
+    binmode FILE;
+
+    # First, verify that it is not a binary file; consider any file
+    # with a zero byte to be a binary file.  Is there any better, or
+    # additional, heuristic that should be applied?
+    $is_binary = 0;
+
+    while (read(FILE, $data, 65536) > 0) {
+	if ($data =~ /\0/) {
+	    $is_binary = 1;
+	    last;
+	}
+    }
+
+    if ($is_binary) {
+	print STDERR "$name: $f: binary file\n";
+	next;
+    }
+
+    seek(FILE, 0, 0);
+
+    $in_bytes = 0;
+    $out_bytes = 0;
+    $lineno = 0;
+
+    @lines  = ();
+
+    $in_hunk = 0;
+    $err = 0;
+
+    while ( defined($line = <FILE>) ) {
+	$lineno++;
+	$in_bytes += length($line);
+
+	if (!$in_hunk) {
+	    if ($line =~
+		/^\@\@\s+\-([0-9]+),([0-9]+)\s+\+([0-9]+),([0-9]+)\s\@\@/) {
+		$minus_lines = $2;
+		$plus_lines = $4;
+		if ($minus_lines || $plus_lines) {
+		    $in_hunk = 1;
+		    @hunk_lines = ($line);
+		}
+	    } else {
+		push(@lines, $line);
+		$out_bytes += length($line);
+	    }
+	} else {
+	    # We're in a hunk
+
+	    if ($line =~ /^\+/) {
+		$plus_lines--;
+
+		$text = substr($line, 1);
+		$text =~ s/[ \t\r]*$//;		# Remove trailing spaces
+		$text = clean_space_tabs($text);
+
+		$l_width = strwidth($text);
+		if ($max_width && $l_width > $max_width) {
+		    print STDERR
+			"$f:$lineno: adds line exceeds $max_width ",
+			"characters ($l_width)\n";
+		}
+
+		push(@hunk_lines, '+'.$text);
+	    } elsif ($line =~ /^\-/) {
+		$minus_lines--;
+		push(@hunk_lines, $line);
+	    } elsif ($line =~ /^ /) {
+		$plus_lines--;
+		$minus_lines--;
+		push(@hunk_lines, $line);
+	    } else {
+		print STDERR "$name: $f: malformed patch\n";
+		$err = 1;
+		last;
+	    }
+
+	    if ($plus_lines < 0 || $minus_lines < 0) {
+		print STDERR "$name: $f: malformed patch\n";
+		$err = 1;
+		last;
+	    } elsif ($plus_lines == 0 && $minus_lines == 0) {
+		# End of a hunk.  Process this hunk.
+		my $i;
+		my $l;
+		my @h = ();
+		my $adj = 0;
+		my $done = 0;
+
+		for ($i = scalar(@hunk_lines)-1; $i > 0; $i--) {
+		    $l = $hunk_lines[$i];
+		    if (!$done && $l eq "+\n") {
+			$adj++; # Skip this line
+		    } elsif ($l =~ /^[ +]/) {
+			$done = 1;
+			unshift(@h, $l);
+		    } else {
+			unshift(@h, $l);
+		    }
+		}
+
+		$l = $hunk_lines[0];  # Hunk header
+		undef @hunk_lines;    # Free memory
+
+		if ($adj) {
+		    die unless
+			($l =~ /^\@\@\s+\-([0-9]+),([0-9]+)\s+\+([0-9]+),([0-9]+)\s\@\@(.*)$/);
+		    my $mstart = $1;
+		    my $mlin = $2;
+		    my $pstart = $3;
+		    my $plin = $4;
+		    my $tail = $5; # doesn't include the final newline
+
+		    $l = sprintf("@@ -%d,%d +%d,%d @@%s\n",
+				 $mstart, $mlin, $pstart, $plin-$adj,
+				 $tail);
+		}
+		unshift(@h, $l);
+
+		# Transfer to the output array
+		foreach $l (@h) {
+		    $out_bytes += length($l);
+		    push(@lines, $l);
+		}
+
+		$in_hunk = 0;
+	    }
+	}
+    }
+
+    if ($in_hunk) {
+	print STDERR "$name: $f: malformed patch\n";
+	$err = 1;
+    }
+
+    if (!$err) {
+	if ($in_bytes != $out_bytes) {
+	    # Only write to the file if changed
+	    seek(FILE, 0, 0);
+	    print FILE @lines;
+
+	    if ( !defined($where = tell(FILE)) ||
+		 !truncate(FILE, $where) ) {
+		die "$name: Failed to truncate modified file: $f: $!\n";
+	    }
+	}
+    }
+
+    close(FILE);
+}
diff --git a/scripts/combined-ext-image.sh b/scripts/combined-ext-image.sh
new file mode 100755
index 0000000..c20203f
--- /dev/null
+++ b/scripts/combined-ext-image.sh
@@ -0,0 +1,78 @@
+#!/bin/sh
+#
+# Copyright (C) 2011 OpenWrt.org
+#
+# This is free software, licensed under the GNU General Public License v2.
+# See /LICENSE for more information.
+#
+
+# Write image header followed by all specified files
+# The header is padded to 64k, format is:
+#  CE               magic word ("Combined Extended Image") (2 bytes)
+#  <CE_VERSION>     file format version field (2 bytes) 
+#  <TYPE>           short description of the target device (32 bytes)
+#  <NUM FILES>      number of files following the header (2 byte)
+#  <file1_name>     name of the first file (32 bytes)
+#  <file1_length>   length of the first file encoded as zero padded 8 digit hex (8 bytes)
+#  <file1_md5>      md5 checksum of the first file (32 bytes)
+#  <fileN_name>     name of the Nth file (32 bytes)
+#  <fileN_length>   length of the Nth file encoded as zero padded 8 digit hex (8 bytes)
+#  <fileN_md5>      md5 checksum of the Nth file (32 bytes)
+
+## version history
+# * version 1: initial file format with num files / name / length / md5 checksum 
+
+set -e
+
+ME="${0##*/}"
+
+usage() {
+	echo "Usage: $ME <type> <ext filename> <file1> <filename1> [<file2> <filename2> <fileN> <filenameN>]"
+	[ "$IMG_OUT" ] && rm -f "$IMG_OUT"
+	exit 1
+}
+
+[ "$#" -lt 4 ] && usage
+
+CE_VERSION=1
+IMG_TYPE=$1; shift
+IMG_OUT=$1; shift
+FILE_NUM=$(($# / 2))
+FILES=""
+
+tmpdir="$( mktemp -d 2> /dev/null )"
+if [ -z "$tmpdir" ]; then
+	# try OSX signature
+	tmpdir="$( mktemp -t 'ubitmp' -d )"
+fi
+
+if [ -z "$tmpdir" ]; then
+	exit 1
+fi
+
+trap "rm -rf $tmpdir" EXIT
+
+IMG_TMP_OUT="${tmpdir}/out"
+
+printf "CE%02x%-32s%02x" $CE_VERSION "$IMG_TYPE" $FILE_NUM > "${IMG_TMP_OUT}"
+
+while [ "$#" -gt 1 ]
+   do
+      file=$1
+      filename=$2
+
+      [ ! -f "$file" ] && echo "$ME: Not a valid file: $file" && usage
+      FILES="$FILES $file"
+      md5=$($MKHASH md5 "$file")
+      printf "%-32s%08x%32s" "$filename" $(stat -c "%s" "$file") "${md5%% *}" >> "${IMG_TMP_OUT}"
+      shift 2
+   done
+
+[ "$#" -eq 1 ] && echo "$ME: Filename not specified: $1" && usage
+
+mv "${IMG_TMP_OUT}" "${IMG_TMP_OUT}".tmp
+dd if="${IMG_TMP_OUT}.tmp" of="${IMG_TMP_OUT}" bs=65536 conv=sync 2>/dev/null
+rm "${IMG_TMP_OUT}".tmp
+
+cat $FILES >> "${IMG_TMP_OUT}"
+cp "${IMG_TMP_OUT}" "${IMG_OUT}"
diff --git a/scripts/combined-image.sh b/scripts/combined-image.sh
new file mode 100755
index 0000000..a365fbc
--- /dev/null
+++ b/scripts/combined-image.sh
@@ -0,0 +1,34 @@
+#!/bin/sh
+
+BLKSZ=65536
+
+[ -f "$1" -a -f "$2" ] || {
+	echo "Usage: $0 <kernel image> <rootfs image> [output file]"
+	exit 1
+}
+
+IMAGE=${3:-openwrt-combined.img}
+
+# Make sure provided images are 64k aligned.
+kern="${IMAGE}.kernel"
+root="${IMAGE}.rootfs"
+dd if="$1" of="$kern" bs=$BLKSZ conv=sync 2>/dev/null
+dd if="$2" of="$root" bs=$BLKSZ conv=sync 2>/dev/null
+
+# Calculate md5sum over combined kernel and rootfs image.
+md5=$(cat "$kern" "$root" | $MKHASH md5)
+
+# Write image header followed by kernel and rootfs image.
+# The header is padded to 64k, format is:
+#  CI               magic word ("Combined Image")
+#  <kernel length>  length of kernel encoded as zero padded 8 digit hex
+#  <rootfs length>  length of rootfs encoded as zero padded 8 digit hex
+#  <md5sum>         checksum of the combined kernel and rootfs image
+( printf "CI%08x%08x%32s" \
+	$(stat -c "%s" "$kern") $(stat -c "%s" "$root") "${md5%% *}" | \
+	dd bs=$BLKSZ conv=sync;
+  cat "$kern" "$root"
+) > ${IMAGE} 2>/dev/null
+
+# Clean up.
+rm -f "$kern" "$root"
diff --git a/scripts/command_all.sh b/scripts/command_all.sh
new file mode 100755
index 0000000..452b66f
--- /dev/null
+++ b/scripts/command_all.sh
@@ -0,0 +1,11 @@
+#! /bin/sh
+# SPDX-License-Identifier: GPL-2.0-or-later
+# Reduced version of which -a using command utility
+
+case $PATH in
+	(*[!:]:) PATH="$PATH:" ;;
+esac
+
+for ELEMENT in $(echo $PATH | tr ":" "\n"); do
+        PATH=$ELEMENT command -v "$@"
+done
diff --git a/scripts/config.guess b/scripts/config.guess
new file mode 100755
index 0000000..48a6846
--- /dev/null
+++ b/scripts/config.guess
@@ -0,0 +1,1815 @@
+#! /bin/sh
+# Attempt to guess a canonical system name.
+#   Copyright 1992-2024 Free Software Foundation, Inc.
+
+# shellcheck disable=SC2006,SC2268 # see below for rationale
+
+timestamp='2024-07-27'
+
+# This file is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses/>.
+#
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that
+# program.  This Exception is an additional permission under section 7
+# of the GNU General Public License, version 3 ("GPLv3").
+#
+# Originally written by Per Bothner; maintained since 2000 by Ben Elliston.
+#
+# You can get the latest version of this script from:
+# https://git.savannah.gnu.org/cgit/config.git/plain/config.guess
+#
+# Please send patches to <config-patches@gnu.org>.
+
+
+# The "shellcheck disable" line above the timestamp inhibits complaints
+# about features and limitations of the classic Bourne shell that were
+# superseded or lifted in POSIX.  However, this script identifies a wide
+# variety of pre-POSIX systems that do not have POSIX shells at all, and
+# even some reasonably current systems (Solaris 10 as case-in-point) still
+# have a pre-POSIX /bin/sh.
+
+
+me=`echo "$0" | sed -e 's,.*/,,'`
+
+usage="\
+Usage: $0 [OPTION]
+
+Output the configuration name of the system '$me' is run on.
+
+Options:
+  -h, --help         print this help, then exit
+  -t, --time-stamp   print date of last modification, then exit
+  -v, --version      print version number, then exit
+
+Report bugs and patches to <config-patches@gnu.org>."
+
+version="\
+GNU config.guess ($timestamp)
+
+Originally written by Per Bothner.
+Copyright 1992-2024 Free Software Foundation, Inc.
+
+This is free software; see the source for copying conditions.  There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
+
+help="
+Try '$me --help' for more information."
+
+# Parse command line
+while test $# -gt 0 ; do
+  case $1 in
+    --time-stamp | --time* | -t )
+       echo "$timestamp" ; exit ;;
+    --version | -v )
+       echo "$version" ; exit ;;
+    --help | --h* | -h )
+       echo "$usage"; exit ;;
+    -- )     # Stop option processing
+       shift; break ;;
+    - )	# Use stdin as input.
+       break ;;
+    -* )
+       echo "$me: invalid option $1$help" >&2
+       exit 1 ;;
+    * )
+       break ;;
+  esac
+done
+
+if test $# != 0; then
+  echo "$me: too many arguments$help" >&2
+  exit 1
+fi
+
+# Just in case it came from the environment.
+GUESS=
+
+# CC_FOR_BUILD -- compiler used by this script. Note that the use of a
+# compiler to aid in system detection is discouraged as it requires
+# temporary files to be created and, as you can see below, it is a
+# headache to deal with in a portable fashion.
+
+# Historically, 'CC_FOR_BUILD' used to be named 'HOST_CC'. We still
+# use 'HOST_CC' if defined, but it is deprecated.
+
+# Portable tmp directory creation inspired by the Autoconf team.
+
+tmp=
+# shellcheck disable=SC2172
+trap 'test -z "$tmp" || rm -fr "$tmp"' 0 1 2 13 15
+
+set_cc_for_build() {
+    # prevent multiple calls if $tmp is already set
+    test "$tmp" && return 0
+    : "${TMPDIR=/tmp}"
+    # shellcheck disable=SC2039,SC3028
+    { tmp=`(umask 077 && mktemp -d "$TMPDIR/cgXXXXXX") 2>/dev/null` && test -n "$tmp" && test -d "$tmp" ; } ||
+	{ test -n "$RANDOM" && tmp=$TMPDIR/cg$$-$RANDOM && (umask 077 && mkdir "$tmp" 2>/dev/null) ; } ||
+	{ tmp=$TMPDIR/cg-$$ && (umask 077 && mkdir "$tmp" 2>/dev/null) && echo "Warning: creating insecure temp directory" >&2 ; } ||
+	{ echo "$me: cannot create a temporary directory in $TMPDIR" >&2 ; exit 1 ; }
+    dummy=$tmp/dummy
+    case ${CC_FOR_BUILD-},${HOST_CC-},${CC-} in
+	,,)    echo "int x;" > "$dummy.c"
+	       for driver in cc gcc c17 c99 c89 ; do
+		   if ($driver -c -o "$dummy.o" "$dummy.c") >/dev/null 2>&1 ; then
+		       CC_FOR_BUILD=$driver
+		       break
+		   fi
+	       done
+	       if test x"$CC_FOR_BUILD" = x ; then
+		   CC_FOR_BUILD=no_compiler_found
+	       fi
+	       ;;
+	,,*)   CC_FOR_BUILD=$CC ;;
+	,*,*)  CC_FOR_BUILD=$HOST_CC ;;
+    esac
+}
+
+# This is needed to find uname on a Pyramid OSx when run in the BSD universe.
+# (ghazi@noc.rutgers.edu 1994-08-24)
+if test -f /.attbin/uname ; then
+	PATH=$PATH:/.attbin ; export PATH
+fi
+
+UNAME_MACHINE=`(uname -m) 2>/dev/null` || UNAME_MACHINE=unknown
+UNAME_RELEASE=`(uname -r) 2>/dev/null` || UNAME_RELEASE=unknown
+UNAME_SYSTEM=`(uname -s) 2>/dev/null` || UNAME_SYSTEM=unknown
+UNAME_VERSION=`(uname -v) 2>/dev/null` || UNAME_VERSION=unknown
+
+case $UNAME_SYSTEM in
+Linux|GNU|GNU/*)
+	LIBC=unknown
+
+	set_cc_for_build
+	cat <<-EOF > "$dummy.c"
+	#if defined(__ANDROID__)
+	LIBC=android
+	#else
+	#include <features.h>
+	#if defined(__UCLIBC__)
+	LIBC=uclibc
+	#elif defined(__dietlibc__)
+	LIBC=dietlibc
+	#elif defined(__GLIBC__)
+	LIBC=gnu
+	#elif defined(__LLVM_LIBC__)
+	LIBC=llvm
+	#else
+	#include <stdarg.h>
+	/* First heuristic to detect musl libc.  */
+	#ifdef __DEFINED_va_list
+	LIBC=musl
+	#endif
+	#endif
+	#endif
+	EOF
+	cc_set_libc=`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^LIBC' | sed 's, ,,g'`
+	eval "$cc_set_libc"
+
+	# Second heuristic to detect musl libc.
+	if [ "$LIBC" = unknown ] &&
+	   command -v ldd >/dev/null &&
+	   ldd --version 2>&1 | grep -q ^musl; then
+		LIBC=musl
+	fi
+
+	# If the system lacks a compiler, then just pick glibc.
+	# We could probably try harder.
+	if [ "$LIBC" = unknown ]; then
+		LIBC=gnu
+	fi
+	;;
+esac
+
+# Note: order is significant - the case branches are not exclusive.
+
+case $UNAME_MACHINE:$UNAME_SYSTEM:$UNAME_RELEASE:$UNAME_VERSION in
+    *:NetBSD:*:*)
+	# NetBSD (nbsd) targets should (where applicable) match one or
+	# more of the tuples: *-*-netbsdelf*, *-*-netbsdaout*,
+	# *-*-netbsdecoff* and *-*-netbsd*.  For targets that recently
+	# switched to ELF, *-*-netbsd* would select the old
+	# object file format.  This provides both forward
+	# compatibility and a consistent mechanism for selecting the
+	# object file format.
+	#
+	# Note: NetBSD doesn't particularly care about the vendor
+	# portion of the name.  We always set it to "unknown".
+	UNAME_MACHINE_ARCH=`(uname -p 2>/dev/null || \
+	    /sbin/sysctl -n hw.machine_arch 2>/dev/null || \
+	    /usr/sbin/sysctl -n hw.machine_arch 2>/dev/null || \
+	    echo unknown)`
+	case $UNAME_MACHINE_ARCH in
+	    aarch64eb) machine=aarch64_be-unknown ;;
+	    armeb) machine=armeb-unknown ;;
+	    arm*) machine=arm-unknown ;;
+	    sh3el) machine=shl-unknown ;;
+	    sh3eb) machine=sh-unknown ;;
+	    sh5el) machine=sh5le-unknown ;;
+	    earmv*)
+		arch=`echo "$UNAME_MACHINE_ARCH" | sed -e 's,^e\(armv[0-9]\).*$,\1,'`
+		endian=`echo "$UNAME_MACHINE_ARCH" | sed -ne 's,^.*\(eb\)$,\1,p'`
+		machine=${arch}${endian}-unknown
+		;;
+	    *) machine=$UNAME_MACHINE_ARCH-unknown ;;
+	esac
+	# The Operating System including object format, if it has switched
+	# to ELF recently (or will in the future) and ABI.
+	case $UNAME_MACHINE_ARCH in
+	    earm*)
+		os=netbsdelf
+		;;
+	    arm*|i386|m68k|ns32k|sh3*|sparc|vax)
+		set_cc_for_build
+		if echo __ELF__ | $CC_FOR_BUILD -E - 2>/dev/null \
+			| grep -q __ELF__
+		then
+		    # Once all utilities can be ECOFF (netbsdecoff) or a.out (netbsdaout).
+		    # Return netbsd for either.  FIX?
+		    os=netbsd
+		else
+		    os=netbsdelf
+		fi
+		;;
+	    *)
+		os=netbsd
+		;;
+	esac
+	# Determine ABI tags.
+	case $UNAME_MACHINE_ARCH in
+	    earm*)
+		expr='s/^earmv[0-9]/-eabi/;s/eb$//'
+		abi=`echo "$UNAME_MACHINE_ARCH" | sed -e "$expr"`
+		;;
+	esac
+	# The OS release
+	# Debian GNU/NetBSD machines have a different userland, and
+	# thus, need a distinct triplet. However, they do not need
+	# kernel version information, so it can be replaced with a
+	# suitable tag, in the style of linux-gnu.
+	case $UNAME_VERSION in
+	    Debian*)
+		release='-gnu'
+		;;
+	    *)
+		release=`echo "$UNAME_RELEASE" | sed -e 's/[-_].*//' | cut -d. -f1,2`
+		;;
+	esac
+	# Since CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM:
+	# contains redundant information, the shorter form:
+	# CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM is used.
+	GUESS=$machine-${os}${release}${abi-}
+	;;
+    *:Bitrig:*:*)
+	UNAME_MACHINE_ARCH=`arch | sed 's/Bitrig.//'`
+	GUESS=$UNAME_MACHINE_ARCH-unknown-bitrig$UNAME_RELEASE
+	;;
+    *:OpenBSD:*:*)
+	UNAME_MACHINE_ARCH=`arch | sed 's/OpenBSD.//'`
+	GUESS=$UNAME_MACHINE_ARCH-unknown-openbsd$UNAME_RELEASE
+	;;
+    *:SecBSD:*:*)
+	UNAME_MACHINE_ARCH=`arch | sed 's/SecBSD.//'`
+	GUESS=$UNAME_MACHINE_ARCH-unknown-secbsd$UNAME_RELEASE
+	;;
+    *:LibertyBSD:*:*)
+	UNAME_MACHINE_ARCH=`arch | sed 's/^.*BSD\.//'`
+	GUESS=$UNAME_MACHINE_ARCH-unknown-libertybsd$UNAME_RELEASE
+	;;
+    *:MidnightBSD:*:*)
+	GUESS=$UNAME_MACHINE-unknown-midnightbsd$UNAME_RELEASE
+	;;
+    *:ekkoBSD:*:*)
+	GUESS=$UNAME_MACHINE-unknown-ekkobsd$UNAME_RELEASE
+	;;
+    *:SolidBSD:*:*)
+	GUESS=$UNAME_MACHINE-unknown-solidbsd$UNAME_RELEASE
+	;;
+    *:OS108:*:*)
+	GUESS=$UNAME_MACHINE-unknown-os108_$UNAME_RELEASE
+	;;
+    macppc:MirBSD:*:*)
+	GUESS=powerpc-unknown-mirbsd$UNAME_RELEASE
+	;;
+    *:MirBSD:*:*)
+	GUESS=$UNAME_MACHINE-unknown-mirbsd$UNAME_RELEASE
+	;;
+    *:Sortix:*:*)
+	GUESS=$UNAME_MACHINE-unknown-sortix
+	;;
+    *:Twizzler:*:*)
+	GUESS=$UNAME_MACHINE-unknown-twizzler
+	;;
+    *:Redox:*:*)
+	GUESS=$UNAME_MACHINE-unknown-redox
+	;;
+    mips:OSF1:*.*)
+	GUESS=mips-dec-osf1
+	;;
+    alpha:OSF1:*:*)
+	# Reset EXIT trap before exiting to avoid spurious non-zero exit code.
+	trap '' 0
+	case $UNAME_RELEASE in
+	*4.0)
+		UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $3}'`
+		;;
+	*5.*)
+		UNAME_RELEASE=`/usr/sbin/sizer -v | awk '{print $4}'`
+		;;
+	esac
+	# According to Compaq, /usr/sbin/psrinfo has been available on
+	# OSF/1 and Tru64 systems produced since 1995.  I hope that
+	# covers most systems running today.  This code pipes the CPU
+	# types through head -n 1, so we only detect the type of CPU 0.
+	ALPHA_CPU_TYPE=`/usr/sbin/psrinfo -v | sed -n -e 's/^  The alpha \(.*\) processor.*$/\1/p' | head -n 1`
+	case $ALPHA_CPU_TYPE in
+	    "EV4 (21064)")
+		UNAME_MACHINE=alpha ;;
+	    "EV4.5 (21064)")
+		UNAME_MACHINE=alpha ;;
+	    "LCA4 (21066/21068)")
+		UNAME_MACHINE=alpha ;;
+	    "EV5 (21164)")
+		UNAME_MACHINE=alphaev5 ;;
+	    "EV5.6 (21164A)")
+		UNAME_MACHINE=alphaev56 ;;
+	    "EV5.6 (21164PC)")
+		UNAME_MACHINE=alphapca56 ;;
+	    "EV5.7 (21164PC)")
+		UNAME_MACHINE=alphapca57 ;;
+	    "EV6 (21264)")
+		UNAME_MACHINE=alphaev6 ;;
+	    "EV6.7 (21264A)")
+		UNAME_MACHINE=alphaev67 ;;
+	    "EV6.8CB (21264C)")
+		UNAME_MACHINE=alphaev68 ;;
+	    "EV6.8AL (21264B)")
+		UNAME_MACHINE=alphaev68 ;;
+	    "EV6.8CX (21264D)")
+		UNAME_MACHINE=alphaev68 ;;
+	    "EV6.9A (21264/EV69A)")
+		UNAME_MACHINE=alphaev69 ;;
+	    "EV7 (21364)")
+		UNAME_MACHINE=alphaev7 ;;
+	    "EV7.9 (21364A)")
+		UNAME_MACHINE=alphaev79 ;;
+	esac
+	# A Pn.n version is a patched version.
+	# A Vn.n version is a released version.
+	# A Tn.n version is a released field test version.
+	# A Xn.n version is an unreleased experimental baselevel.
+	# 1.2 uses "1.2" for uname -r.
+	OSF_REL=`echo "$UNAME_RELEASE" | sed -e 's/^[PVTX]//' | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz`
+	GUESS=$UNAME_MACHINE-dec-osf$OSF_REL
+	;;
+    Amiga*:UNIX_System_V:4.0:*)
+	GUESS=m68k-unknown-sysv4
+	;;
+    *:[Aa]miga[Oo][Ss]:*:*)
+	GUESS=$UNAME_MACHINE-unknown-amigaos
+	;;
+    *:[Mm]orph[Oo][Ss]:*:*)
+	GUESS=$UNAME_MACHINE-unknown-morphos
+	;;
+    *:OS/390:*:*)
+	GUESS=i370-ibm-openedition
+	;;
+    *:z/VM:*:*)
+	GUESS=s390-ibm-zvmoe
+	;;
+    *:OS400:*:*)
+	GUESS=powerpc-ibm-os400
+	;;
+    arm:RISC*:1.[012]*:*|arm:riscix:1.[012]*:*)
+	GUESS=arm-acorn-riscix$UNAME_RELEASE
+	;;
+    arm*:riscos:*:*|arm*:RISCOS:*:*)
+	GUESS=arm-unknown-riscos
+	;;
+    SR2?01:HI-UX/MPP:*:* | SR8000:HI-UX/MPP:*:*)
+	GUESS=hppa1.1-hitachi-hiuxmpp
+	;;
+    Pyramid*:OSx*:*:* | MIS*:OSx*:*:* | MIS*:SMP_DC-OSx*:*:*)
+	# akee@wpdis03.wpafb.af.mil (Earle F. Ake) contributed MIS and NILE.
+	case `(/bin/universe) 2>/dev/null` in
+	    att) GUESS=pyramid-pyramid-sysv3 ;;
+	    *)   GUESS=pyramid-pyramid-bsd   ;;
+	esac
+	;;
+    NILE*:*:*:dcosx)
+	GUESS=pyramid-pyramid-svr4
+	;;
+    DRS?6000:unix:4.0:6*)
+	GUESS=sparc-icl-nx6
+	;;
+    DRS?6000:UNIX_SV:4.2*:7* | DRS?6000:isis:4.2*:7*)
+	case `/usr/bin/uname -p` in
+	    sparc) GUESS=sparc-icl-nx7 ;;
+	esac
+	;;
+    s390x:SunOS:*:*)
+	SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'`
+	GUESS=$UNAME_MACHINE-ibm-solaris2$SUN_REL
+	;;
+    sun4H:SunOS:5.*:*)
+	SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'`
+	GUESS=sparc-hal-solaris2$SUN_REL
+	;;
+    sun4*:SunOS:5.*:* | tadpole*:SunOS:5.*:*)
+	SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'`
+	GUESS=sparc-sun-solaris2$SUN_REL
+	;;
+    i86pc:AuroraUX:5.*:* | i86xen:AuroraUX:5.*:*)
+	GUESS=i386-pc-auroraux$UNAME_RELEASE
+	;;
+    i86pc:SunOS:5.*:* | i86xen:SunOS:5.*:*)
+	set_cc_for_build
+	SUN_ARCH=i386
+	# If there is a compiler, see if it is configured for 64-bit objects.
+	# Note that the Sun cc does not turn __LP64__ into 1 like gcc does.
+	# This test works for both compilers.
+	if test "$CC_FOR_BUILD" != no_compiler_found; then
+	    if (echo '#ifdef __amd64'; echo IS_64BIT_ARCH; echo '#endif') | \
+		(CCOPTS="" $CC_FOR_BUILD -m64 -E - 2>/dev/null) | \
+		grep IS_64BIT_ARCH >/dev/null
+	    then
+		SUN_ARCH=x86_64
+	    fi
+	fi
+	SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'`
+	GUESS=$SUN_ARCH-pc-solaris2$SUN_REL
+	;;
+    sun4*:SunOS:6*:*)
+	# According to config.sub, this is the proper way to canonicalize
+	# SunOS6.  Hard to guess exactly what SunOS6 will be like, but
+	# it's likely to be more like Solaris than SunOS4.
+	SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'`
+	GUESS=sparc-sun-solaris3$SUN_REL
+	;;
+    sun4*:SunOS:*:*)
+	case `/usr/bin/arch -k` in
+	    Series*|S4*)
+		UNAME_RELEASE=`uname -v`
+		;;
+	esac
+	# Japanese Language versions have a version number like '4.1.3-JL'.
+	SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/-/_/'`
+	GUESS=sparc-sun-sunos$SUN_REL
+	;;
+    sun3*:SunOS:*:*)
+	GUESS=m68k-sun-sunos$UNAME_RELEASE
+	;;
+    sun*:*:4.2BSD:*)
+	UNAME_RELEASE=`(sed 1q /etc/motd | awk '{print substr($5,1,3)}') 2>/dev/null`
+	test "x$UNAME_RELEASE" = x && UNAME_RELEASE=3
+	case `/bin/arch` in
+	    sun3)
+		GUESS=m68k-sun-sunos$UNAME_RELEASE
+		;;
+	    sun4)
+		GUESS=sparc-sun-sunos$UNAME_RELEASE
+		;;
+	esac
+	;;
+    aushp:SunOS:*:*)
+	GUESS=sparc-auspex-sunos$UNAME_RELEASE
+	;;
+    # The situation for MiNT is a little confusing.  The machine name
+    # can be virtually everything (everything which is not
+    # "atarist" or "atariste" at least should have a processor
+    # > m68000).  The system name ranges from "MiNT" over "FreeMiNT"
+    # to the lowercase version "mint" (or "freemint").  Finally
+    # the system name "TOS" denotes a system which is actually not
+    # MiNT.  But MiNT is downward compatible to TOS, so this should
+    # be no problem.
+    atarist[e]:*MiNT:*:* | atarist[e]:*mint:*:* | atarist[e]:*TOS:*:*)
+	GUESS=m68k-atari-mint$UNAME_RELEASE
+	;;
+    atari*:*MiNT:*:* | atari*:*mint:*:* | atarist[e]:*TOS:*:*)
+	GUESS=m68k-atari-mint$UNAME_RELEASE
+	;;
+    *falcon*:*MiNT:*:* | *falcon*:*mint:*:* | *falcon*:*TOS:*:*)
+	GUESS=m68k-atari-mint$UNAME_RELEASE
+	;;
+    milan*:*MiNT:*:* | milan*:*mint:*:* | *milan*:*TOS:*:*)
+	GUESS=m68k-milan-mint$UNAME_RELEASE
+	;;
+    hades*:*MiNT:*:* | hades*:*mint:*:* | *hades*:*TOS:*:*)
+	GUESS=m68k-hades-mint$UNAME_RELEASE
+	;;
+    *:*MiNT:*:* | *:*mint:*:* | *:*TOS:*:*)
+	GUESS=m68k-unknown-mint$UNAME_RELEASE
+	;;
+    m68k:machten:*:*)
+	GUESS=m68k-apple-machten$UNAME_RELEASE
+	;;
+    powerpc:machten:*:*)
+	GUESS=powerpc-apple-machten$UNAME_RELEASE
+	;;
+    RISC*:Mach:*:*)
+	GUESS=mips-dec-mach_bsd4.3
+	;;
+    RISC*:ULTRIX:*:*)
+	GUESS=mips-dec-ultrix$UNAME_RELEASE
+	;;
+    VAX*:ULTRIX*:*:*)
+	GUESS=vax-dec-ultrix$UNAME_RELEASE
+	;;
+    2020:CLIX:*:* | 2430:CLIX:*:*)
+	GUESS=clipper-intergraph-clix$UNAME_RELEASE
+	;;
+    mips:*:*:UMIPS | mips:*:*:RISCos)
+	set_cc_for_build
+	sed 's/^	//' << EOF > "$dummy.c"
+#ifdef __cplusplus
+#include <stdio.h>  /* for printf() prototype */
+	int main (int argc, char *argv[]) {
+#else
+	int main (argc, argv) int argc; char *argv[]; {
+#endif
+	#if defined (host_mips) && defined (MIPSEB)
+	#if defined (SYSTYPE_SYSV)
+	  printf ("mips-mips-riscos%ssysv\\n", argv[1]); exit (0);
+	#endif
+	#if defined (SYSTYPE_SVR4)
+	  printf ("mips-mips-riscos%ssvr4\\n", argv[1]); exit (0);
+	#endif
+	#if defined (SYSTYPE_BSD43) || defined(SYSTYPE_BSD)
+	  printf ("mips-mips-riscos%sbsd\\n", argv[1]); exit (0);
+	#endif
+	#endif
+	  exit (-1);
+	}
+EOF
+	$CC_FOR_BUILD -o "$dummy" "$dummy.c" &&
+	  dummyarg=`echo "$UNAME_RELEASE" | sed -n 's/\([0-9]*\).*/\1/p'` &&
+	  SYSTEM_NAME=`"$dummy" "$dummyarg"` &&
+	    { echo "$SYSTEM_NAME"; exit; }
+	GUESS=mips-mips-riscos$UNAME_RELEASE
+	;;
+    Motorola:PowerMAX_OS:*:*)
+	GUESS=powerpc-motorola-powermax
+	;;
+    Motorola:*:4.3:PL8-*)
+	GUESS=powerpc-harris-powermax
+	;;
+    Night_Hawk:*:*:PowerMAX_OS | Synergy:PowerMAX_OS:*:*)
+	GUESS=powerpc-harris-powermax
+	;;
+    Night_Hawk:Power_UNIX:*:*)
+	GUESS=powerpc-harris-powerunix
+	;;
+    m88k:CX/UX:7*:*)
+	GUESS=m88k-harris-cxux7
+	;;
+    m88k:*:4*:R4*)
+	GUESS=m88k-motorola-sysv4
+	;;
+    m88k:*:3*:R3*)
+	GUESS=m88k-motorola-sysv3
+	;;
+    AViiON:dgux:*:*)
+	# DG/UX returns AViiON for all architectures
+	UNAME_PROCESSOR=`/usr/bin/uname -p`
+	if test "$UNAME_PROCESSOR" = mc88100 || test "$UNAME_PROCESSOR" = mc88110
+	then
+	    if test "$TARGET_BINARY_INTERFACE"x = m88kdguxelfx || \
+	       test "$TARGET_BINARY_INTERFACE"x = x
+	    then
+		GUESS=m88k-dg-dgux$UNAME_RELEASE
+	    else
+		GUESS=m88k-dg-dguxbcs$UNAME_RELEASE
+	    fi
+	else
+	    GUESS=i586-dg-dgux$UNAME_RELEASE
+	fi
+	;;
+    M88*:DolphinOS:*:*)	# DolphinOS (SVR3)
+	GUESS=m88k-dolphin-sysv3
+	;;
+    M88*:*:R3*:*)
+	# Delta 88k system running SVR3
+	GUESS=m88k-motorola-sysv3
+	;;
+    XD88*:*:*:*) # Tektronix XD88 system running UTekV (SVR3)
+	GUESS=m88k-tektronix-sysv3
+	;;
+    Tek43[0-9][0-9]:UTek:*:*) # Tektronix 4300 system running UTek (BSD)
+	GUESS=m68k-tektronix-bsd
+	;;
+    *:IRIX*:*:*)
+	IRIX_REL=`echo "$UNAME_RELEASE" | sed -e 's/-/_/g'`
+	GUESS=mips-sgi-irix$IRIX_REL
+	;;
+    ????????:AIX?:[12].1:2)   # AIX 2.2.1 or AIX 2.1.1 is RT/PC AIX.
+	GUESS=romp-ibm-aix    # uname -m gives an 8 hex-code CPU id
+	;;                    # Note that: echo "'`uname -s`'" gives 'AIX '
+    i*86:AIX:*:*)
+	GUESS=i386-ibm-aix
+	;;
+    ia64:AIX:*:*)
+	if test -x /usr/bin/oslevel ; then
+		IBM_REV=`/usr/bin/oslevel`
+	else
+		IBM_REV=$UNAME_VERSION.$UNAME_RELEASE
+	fi
+	GUESS=$UNAME_MACHINE-ibm-aix$IBM_REV
+	;;
+    *:AIX:2:3)
+	if grep bos325 /usr/include/stdio.h >/dev/null 2>&1; then
+		set_cc_for_build
+		sed 's/^		//' << EOF > "$dummy.c"
+		#include <sys/systemcfg.h>
+
+		int
+		main ()
+			{
+			if (!__power_pc())
+				exit(1);
+			puts("powerpc-ibm-aix3.2.5");
+			exit(0);
+			}
+EOF
+		if $CC_FOR_BUILD -o "$dummy" "$dummy.c" && SYSTEM_NAME=`"$dummy"`
+		then
+			GUESS=$SYSTEM_NAME
+		else
+			GUESS=rs6000-ibm-aix3.2.5
+		fi
+	elif grep bos324 /usr/include/stdio.h >/dev/null 2>&1; then
+		GUESS=rs6000-ibm-aix3.2.4
+	else
+		GUESS=rs6000-ibm-aix3.2
+	fi
+	;;
+    *:AIX:*:[4567])
+	IBM_CPU_ID=`/usr/sbin/lsdev -C -c processor -S available | sed 1q | awk '{ print $1 }'`
+	if /usr/sbin/lsattr -El "$IBM_CPU_ID" | grep ' POWER' >/dev/null 2>&1; then
+		IBM_ARCH=rs6000
+	else
+		IBM_ARCH=powerpc
+	fi
+	if test -x /usr/bin/lslpp ; then
+		IBM_REV=`/usr/bin/lslpp -Lqc bos.rte.libc | \
+			   awk -F: '{ print $3 }' | sed s/[0-9]*$/0/`
+	else
+		IBM_REV=$UNAME_VERSION.$UNAME_RELEASE
+	fi
+	GUESS=$IBM_ARCH-ibm-aix$IBM_REV
+	;;
+    *:AIX:*:*)
+	GUESS=rs6000-ibm-aix
+	;;
+    ibmrt:4.4BSD:*|romp-ibm:4.4BSD:*)
+	GUESS=romp-ibm-bsd4.4
+	;;
+    ibmrt:*BSD:*|romp-ibm:BSD:*)            # covers RT/PC BSD and
+	GUESS=romp-ibm-bsd$UNAME_RELEASE    # 4.3 with uname added to
+	;;                                  # report: romp-ibm BSD 4.3
+    *:BOSX:*:*)
+	GUESS=rs6000-bull-bosx
+	;;
+    DPX/2?00:B.O.S.:*:*)
+	GUESS=m68k-bull-sysv3
+	;;
+    9000/[34]??:4.3bsd:1.*:*)
+	GUESS=m68k-hp-bsd
+	;;
+    hp300:4.4BSD:*:* | 9000/[34]??:4.3bsd:2.*:*)
+	GUESS=m68k-hp-bsd4.4
+	;;
+    9000/[34678]??:HP-UX:*:*)
+	HPUX_REV=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*.[0B]*//'`
+	case $UNAME_MACHINE in
+	    9000/31?)            HP_ARCH=m68000 ;;
+	    9000/[34]??)         HP_ARCH=m68k ;;
+	    9000/[678][0-9][0-9])
+		if test -x /usr/bin/getconf; then
+		    sc_cpu_version=`/usr/bin/getconf SC_CPU_VERSION 2>/dev/null`
+		    sc_kernel_bits=`/usr/bin/getconf SC_KERNEL_BITS 2>/dev/null`
+		    case $sc_cpu_version in
+		      523) HP_ARCH=hppa1.0 ;; # CPU_PA_RISC1_0
+		      528) HP_ARCH=hppa1.1 ;; # CPU_PA_RISC1_1
+		      532)                      # CPU_PA_RISC2_0
+			case $sc_kernel_bits in
+			  32) HP_ARCH=hppa2.0n ;;
+			  64) HP_ARCH=hppa2.0w ;;
+			  '') HP_ARCH=hppa2.0 ;;   # HP-UX 10.20
+			esac ;;
+		    esac
+		fi
+		if test "$HP_ARCH" = ""; then
+		    set_cc_for_build
+		    sed 's/^		//' << EOF > "$dummy.c"
+
+		#define _HPUX_SOURCE
+		#include <stdlib.h>
+		#include <unistd.h>
+
+		int
+		main ()
+		{
+		#if defined(_SC_KERNEL_BITS)
+		    long bits = sysconf(_SC_KERNEL_BITS);
+		#endif
+		    long cpu  = sysconf (_SC_CPU_VERSION);
+
+		    switch (cpu)
+			{
+			case CPU_PA_RISC1_0: puts ("hppa1.0"); break;
+			case CPU_PA_RISC1_1: puts ("hppa1.1"); break;
+			case CPU_PA_RISC2_0:
+		#if defined(_SC_KERNEL_BITS)
+			    switch (bits)
+				{
+				case 64: puts ("hppa2.0w"); break;
+				case 32: puts ("hppa2.0n"); break;
+				default: puts ("hppa2.0"); break;
+				} break;
+		#else  /* !defined(_SC_KERNEL_BITS) */
+			    puts ("hppa2.0"); break;
+		#endif
+			default: puts ("hppa1.0"); break;
+			}
+		    exit (0);
+		}
+EOF
+		    (CCOPTS="" $CC_FOR_BUILD -o "$dummy" "$dummy.c" 2>/dev/null) && HP_ARCH=`"$dummy"`
+		    test -z "$HP_ARCH" && HP_ARCH=hppa
+		fi ;;
+	esac
+	if test "$HP_ARCH" = hppa2.0w
+	then
+	    set_cc_for_build
+
+	    # hppa2.0w-hp-hpux* has a 64-bit kernel and a compiler generating
+	    # 32-bit code.  hppa64-hp-hpux* has the same kernel and a compiler
+	    # generating 64-bit code.  GNU and HP use different nomenclature:
+	    #
+	    # $ CC_FOR_BUILD=cc ./config.guess
+	    # => hppa2.0w-hp-hpux11.23
+	    # $ CC_FOR_BUILD="cc +DA2.0w" ./config.guess
+	    # => hppa64-hp-hpux11.23
+
+	    if echo __LP64__ | (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) |
+		grep -q __LP64__
+	    then
+		HP_ARCH=hppa2.0w
+	    else
+		HP_ARCH=hppa64
+	    fi
+	fi
+	GUESS=$HP_ARCH-hp-hpux$HPUX_REV
+	;;
+    ia64:HP-UX:*:*)
+	HPUX_REV=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*.[0B]*//'`
+	GUESS=ia64-hp-hpux$HPUX_REV
+	;;
+    3050*:HI-UX:*:*)
+	set_cc_for_build
+	sed 's/^	//' << EOF > "$dummy.c"
+	#include <unistd.h>
+	int
+	main ()
+	{
+	  long cpu = sysconf (_SC_CPU_VERSION);
+	  /* The order matters, because CPU_IS_HP_MC68K erroneously returns
+	     true for CPU_PA_RISC1_0.  CPU_IS_PA_RISC returns correct
+	     results, however.  */
+	  if (CPU_IS_PA_RISC (cpu))
+	    {
+	      switch (cpu)
+		{
+		  case CPU_PA_RISC1_0: puts ("hppa1.0-hitachi-hiuxwe2"); break;
+		  case CPU_PA_RISC1_1: puts ("hppa1.1-hitachi-hiuxwe2"); break;
+		  case CPU_PA_RISC2_0: puts ("hppa2.0-hitachi-hiuxwe2"); break;
+		  default: puts ("hppa-hitachi-hiuxwe2"); break;
+		}
+	    }
+	  else if (CPU_IS_HP_MC68K (cpu))
+	    puts ("m68k-hitachi-hiuxwe2");
+	  else puts ("unknown-hitachi-hiuxwe2");
+	  exit (0);
+	}
+EOF
+	$CC_FOR_BUILD -o "$dummy" "$dummy.c" && SYSTEM_NAME=`"$dummy"` &&
+		{ echo "$SYSTEM_NAME"; exit; }
+	GUESS=unknown-hitachi-hiuxwe2
+	;;
+    9000/7??:4.3bsd:*:* | 9000/8?[79]:4.3bsd:*:*)
+	GUESS=hppa1.1-hp-bsd
+	;;
+    9000/8??:4.3bsd:*:*)
+	GUESS=hppa1.0-hp-bsd
+	;;
+    *9??*:MPE/iX:*:* | *3000*:MPE/iX:*:*)
+	GUESS=hppa1.0-hp-mpeix
+	;;
+    hp7??:OSF1:*:* | hp8?[79]:OSF1:*:*)
+	GUESS=hppa1.1-hp-osf
+	;;
+    hp8??:OSF1:*:*)
+	GUESS=hppa1.0-hp-osf
+	;;
+    i*86:OSF1:*:*)
+	if test -x /usr/sbin/sysversion ; then
+	    GUESS=$UNAME_MACHINE-unknown-osf1mk
+	else
+	    GUESS=$UNAME_MACHINE-unknown-osf1
+	fi
+	;;
+    parisc*:Lites*:*:*)
+	GUESS=hppa1.1-hp-lites
+	;;
+    C1*:ConvexOS:*:* | convex:ConvexOS:C1*:*)
+	GUESS=c1-convex-bsd
+	;;
+    C2*:ConvexOS:*:* | convex:ConvexOS:C2*:*)
+	if getsysinfo -f scalar_acc
+	then echo c32-convex-bsd
+	else echo c2-convex-bsd
+	fi
+	exit ;;
+    C34*:ConvexOS:*:* | convex:ConvexOS:C34*:*)
+	GUESS=c34-convex-bsd
+	;;
+    C38*:ConvexOS:*:* | convex:ConvexOS:C38*:*)
+	GUESS=c38-convex-bsd
+	;;
+    C4*:ConvexOS:*:* | convex:ConvexOS:C4*:*)
+	GUESS=c4-convex-bsd
+	;;
+    CRAY*Y-MP:*:*:*)
+	CRAY_REL=`echo "$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'`
+	GUESS=ymp-cray-unicos$CRAY_REL
+	;;
+    CRAY*[A-Z]90:*:*:*)
+	echo "$UNAME_MACHINE"-cray-unicos"$UNAME_RELEASE" \
+	| sed -e 's/CRAY.*\([A-Z]90\)/\1/' \
+	      -e y/ABCDEFGHIJKLMNOPQRSTUVWXYZ/abcdefghijklmnopqrstuvwxyz/ \
+	      -e 's/\.[^.]*$/.X/'
+	exit ;;
+    CRAY*TS:*:*:*)
+	CRAY_REL=`echo "$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'`
+	GUESS=t90-cray-unicos$CRAY_REL
+	;;
+    CRAY*T3E:*:*:*)
+	CRAY_REL=`echo "$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'`
+	GUESS=alphaev5-cray-unicosmk$CRAY_REL
+	;;
+    CRAY*SV1:*:*:*)
+	CRAY_REL=`echo "$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'`
+	GUESS=sv1-cray-unicos$CRAY_REL
+	;;
+    *:UNICOS/mp:*:*)
+	CRAY_REL=`echo "$UNAME_RELEASE" | sed -e 's/\.[^.]*$/.X/'`
+	GUESS=craynv-cray-unicosmp$CRAY_REL
+	;;
+    F30[01]:UNIX_System_V:*:* | F700:UNIX_System_V:*:*)
+	FUJITSU_PROC=`uname -m | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz`
+	FUJITSU_SYS=`uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///'`
+	FUJITSU_REL=`echo "$UNAME_RELEASE" | sed -e 's/ /_/'`
+	GUESS=${FUJITSU_PROC}-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}
+	;;
+    5000:UNIX_System_V:4.*:*)
+	FUJITSU_SYS=`uname -p | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/\///'`
+	FUJITSU_REL=`echo "$UNAME_RELEASE" | tr ABCDEFGHIJKLMNOPQRSTUVWXYZ abcdefghijklmnopqrstuvwxyz | sed -e 's/ /_/'`
+	GUESS=sparc-fujitsu-${FUJITSU_SYS}${FUJITSU_REL}
+	;;
+    i*86:BSD/386:*:* | i*86:BSD/OS:*:* | *:Ascend\ Embedded/OS:*:*)
+	GUESS=$UNAME_MACHINE-pc-bsdi$UNAME_RELEASE
+	;;
+    sparc*:BSD/OS:*:*)
+	GUESS=sparc-unknown-bsdi$UNAME_RELEASE
+	;;
+    *:BSD/OS:*:*)
+	GUESS=$UNAME_MACHINE-unknown-bsdi$UNAME_RELEASE
+	;;
+    arm:FreeBSD:*:*)
+	UNAME_PROCESSOR=`uname -p`
+	set_cc_for_build
+	if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \
+	    | grep -q __ARM_PCS_VFP
+	then
+	    FREEBSD_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'`
+	    GUESS=$UNAME_PROCESSOR-unknown-freebsd$FREEBSD_REL-gnueabi
+	else
+	    FREEBSD_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'`
+	    GUESS=$UNAME_PROCESSOR-unknown-freebsd$FREEBSD_REL-gnueabihf
+	fi
+	;;
+    *:FreeBSD:*:*)
+	UNAME_PROCESSOR=`uname -p`
+	case $UNAME_PROCESSOR in
+	    amd64)
+		UNAME_PROCESSOR=x86_64 ;;
+	    i386)
+		UNAME_PROCESSOR=i586 ;;
+	esac
+	FREEBSD_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'`
+	GUESS=$UNAME_PROCESSOR-unknown-freebsd$FREEBSD_REL
+	;;
+    i*:CYGWIN*:*)
+	GUESS=$UNAME_MACHINE-pc-cygwin
+	;;
+    *:MINGW64*:*)
+	GUESS=$UNAME_MACHINE-pc-mingw64
+	;;
+    *:MINGW*:*)
+	GUESS=$UNAME_MACHINE-pc-mingw32
+	;;
+    *:MSYS*:*)
+	GUESS=$UNAME_MACHINE-pc-msys
+	;;
+    i*:PW*:*)
+	GUESS=$UNAME_MACHINE-pc-pw32
+	;;
+    *:SerenityOS:*:*)
+        GUESS=$UNAME_MACHINE-pc-serenity
+        ;;
+    *:Interix*:*)
+	case $UNAME_MACHINE in
+	    x86)
+		GUESS=i586-pc-interix$UNAME_RELEASE
+		;;
+	    authenticamd | genuineintel | EM64T)
+		GUESS=x86_64-unknown-interix$UNAME_RELEASE
+		;;
+	    IA64)
+		GUESS=ia64-unknown-interix$UNAME_RELEASE
+		;;
+	esac ;;
+    i*:UWIN*:*)
+	GUESS=$UNAME_MACHINE-pc-uwin
+	;;
+    amd64:CYGWIN*:*:* | x86_64:CYGWIN*:*:*)
+	GUESS=x86_64-pc-cygwin
+	;;
+    prep*:SunOS:5.*:*)
+	SUN_REL=`echo "$UNAME_RELEASE" | sed -e 's/[^.]*//'`
+	GUESS=powerpcle-unknown-solaris2$SUN_REL
+	;;
+    *:GNU:*:*)
+	# the GNU system
+	GNU_ARCH=`echo "$UNAME_MACHINE" | sed -e 's,[-/].*$,,'`
+	GNU_REL=`echo "$UNAME_RELEASE" | sed -e 's,/.*$,,'`
+	GUESS=$GNU_ARCH-unknown-$LIBC$GNU_REL
+	;;
+    *:GNU/*:*:*)
+	# other systems with GNU libc and userland
+	GNU_SYS=`echo "$UNAME_SYSTEM" | sed 's,^[^/]*/,,' | tr "[:upper:]" "[:lower:]"`
+	GNU_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'`
+	GUESS=$UNAME_MACHINE-unknown-$GNU_SYS$GNU_REL-$LIBC
+	;;
+    x86_64:[Mm]anagarm:*:*|i?86:[Mm]anagarm:*:*)
+	GUESS="$UNAME_MACHINE-pc-managarm-mlibc"
+	;;
+    *:[Mm]anagarm:*:*)
+	GUESS="$UNAME_MACHINE-unknown-managarm-mlibc"
+	;;
+    *:Minix:*:*)
+	GUESS=$UNAME_MACHINE-unknown-minix
+	;;
+    aarch64:Linux:*:*)
+	set_cc_for_build
+	CPU=$UNAME_MACHINE
+	LIBCABI=$LIBC
+	if test "$CC_FOR_BUILD" != no_compiler_found; then
+	    ABI=64
+	    sed 's/^	    //' << EOF > "$dummy.c"
+	    #ifdef __ARM_EABI__
+	    #ifdef __ARM_PCS_VFP
+	    ABI=eabihf
+	    #else
+	    ABI=eabi
+	    #endif
+	    #endif
+EOF
+	    cc_set_abi=`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^ABI' | sed 's, ,,g'`
+	    eval "$cc_set_abi"
+	    case $ABI in
+		eabi | eabihf) CPU=armv8l; LIBCABI=$LIBC$ABI ;;
+	    esac
+	fi
+	GUESS=$CPU-unknown-linux-$LIBCABI
+	;;
+    aarch64_be:Linux:*:*)
+	UNAME_MACHINE=aarch64_be
+	GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+	;;
+    alpha:Linux:*:*)
+	case `sed -n '/^cpu model/s/^.*: \(.*\)/\1/p' /proc/cpuinfo 2>/dev/null` in
+	  EV5)   UNAME_MACHINE=alphaev5 ;;
+	  EV56)  UNAME_MACHINE=alphaev56 ;;
+	  PCA56) UNAME_MACHINE=alphapca56 ;;
+	  PCA57) UNAME_MACHINE=alphapca56 ;;
+	  EV6)   UNAME_MACHINE=alphaev6 ;;
+	  EV67)  UNAME_MACHINE=alphaev67 ;;
+	  EV68*) UNAME_MACHINE=alphaev68 ;;
+	esac
+	objdump --private-headers /bin/sh | grep -q ld.so.1
+	if test "$?" = 0 ; then LIBC=gnulibc1 ; fi
+	GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+	;;
+    arc:Linux:*:* | arceb:Linux:*:* | arc32:Linux:*:* | arc64:Linux:*:*)
+	GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+	;;
+    arm*:Linux:*:*)
+	set_cc_for_build
+	if echo __ARM_EABI__ | $CC_FOR_BUILD -E - 2>/dev/null \
+	    | grep -q __ARM_EABI__
+	then
+	    GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+	else
+	    if echo __ARM_PCS_VFP | $CC_FOR_BUILD -E - 2>/dev/null \
+		| grep -q __ARM_PCS_VFP
+	    then
+		GUESS=$UNAME_MACHINE-unknown-linux-${LIBC}eabi
+	    else
+		GUESS=$UNAME_MACHINE-unknown-linux-${LIBC}eabihf
+	    fi
+	fi
+	;;
+    avr32*:Linux:*:*)
+	GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+	;;
+    cris:Linux:*:*)
+	GUESS=$UNAME_MACHINE-axis-linux-$LIBC
+	;;
+    crisv32:Linux:*:*)
+	GUESS=$UNAME_MACHINE-axis-linux-$LIBC
+	;;
+    e2k:Linux:*:*)
+	GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+	;;
+    frv:Linux:*:*)
+	GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+	;;
+    hexagon:Linux:*:*)
+	GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+	;;
+    i*86:Linux:*:*)
+	GUESS=$UNAME_MACHINE-pc-linux-$LIBC
+	;;
+    ia64:Linux:*:*)
+	GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+	;;
+    k1om:Linux:*:*)
+	GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+	;;
+    kvx:Linux:*:*)
+	GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+	;;
+    kvx:cos:*:*)
+	GUESS=$UNAME_MACHINE-unknown-cos
+	;;
+    kvx:mbr:*:*)
+	GUESS=$UNAME_MACHINE-unknown-mbr
+	;;
+    loongarch32:Linux:*:* | loongarch64:Linux:*:*)
+	GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+	;;
+    m32r*:Linux:*:*)
+	GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+	;;
+    m68*:Linux:*:*)
+	GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+	;;
+    mips:Linux:*:* | mips64:Linux:*:*)
+	set_cc_for_build
+	IS_GLIBC=0
+	test x"${LIBC}" = xgnu && IS_GLIBC=1
+	sed 's/^	//' << EOF > "$dummy.c"
+	#undef CPU
+	#undef mips
+	#undef mipsel
+	#undef mips64
+	#undef mips64el
+	#if ${IS_GLIBC} && defined(_ABI64)
+	LIBCABI=gnuabi64
+	#else
+	#if ${IS_GLIBC} && defined(_ABIN32)
+	LIBCABI=gnuabin32
+	#else
+	LIBCABI=${LIBC}
+	#endif
+	#endif
+
+	#if ${IS_GLIBC} && defined(__mips64) && defined(__mips_isa_rev) && __mips_isa_rev>=6
+	CPU=mipsisa64r6
+	#else
+	#if ${IS_GLIBC} && !defined(__mips64) && defined(__mips_isa_rev) && __mips_isa_rev>=6
+	CPU=mipsisa32r6
+	#else
+	#if defined(__mips64)
+	CPU=mips64
+	#else
+	CPU=mips
+	#endif
+	#endif
+	#endif
+
+	#if defined(__MIPSEL__) || defined(__MIPSEL) || defined(_MIPSEL) || defined(MIPSEL)
+	MIPS_ENDIAN=el
+	#else
+	#if defined(__MIPSEB__) || defined(__MIPSEB) || defined(_MIPSEB) || defined(MIPSEB)
+	MIPS_ENDIAN=
+	#else
+	MIPS_ENDIAN=
+	#endif
+	#endif
+EOF
+	cc_set_vars=`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^CPU\|^MIPS_ENDIAN\|^LIBCABI'`
+	eval "$cc_set_vars"
+	test "x$CPU" != x && { echo "$CPU${MIPS_ENDIAN}-unknown-linux-$LIBCABI"; exit; }
+	;;
+    mips64el:Linux:*:*)
+	GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+	;;
+    openrisc*:Linux:*:*)
+	GUESS=or1k-unknown-linux-$LIBC
+	;;
+    or32:Linux:*:* | or1k*:Linux:*:*)
+	GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+	;;
+    padre:Linux:*:*)
+	GUESS=sparc-unknown-linux-$LIBC
+	;;
+    parisc64:Linux:*:* | hppa64:Linux:*:*)
+	GUESS=hppa64-unknown-linux-$LIBC
+	;;
+    parisc:Linux:*:* | hppa:Linux:*:*)
+	# Look for CPU level
+	case `grep '^cpu[^a-z]*:' /proc/cpuinfo 2>/dev/null | cut -d' ' -f2` in
+	  PA7*) GUESS=hppa1.1-unknown-linux-$LIBC ;;
+	  PA8*) GUESS=hppa2.0-unknown-linux-$LIBC ;;
+	  *)    GUESS=hppa-unknown-linux-$LIBC ;;
+	esac
+	;;
+    ppc64:Linux:*:*)
+	GUESS=powerpc64-unknown-linux-$LIBC
+	;;
+    ppc:Linux:*:*)
+	GUESS=powerpc-unknown-linux-$LIBC
+	;;
+    ppc64le:Linux:*:*)
+	GUESS=powerpc64le-unknown-linux-$LIBC
+	;;
+    ppcle:Linux:*:*)
+	GUESS=powerpcle-unknown-linux-$LIBC
+	;;
+    riscv32:Linux:*:* | riscv32be:Linux:*:* | riscv64:Linux:*:* | riscv64be:Linux:*:*)
+	GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+	;;
+    s390:Linux:*:* | s390x:Linux:*:*)
+	GUESS=$UNAME_MACHINE-ibm-linux-$LIBC
+	;;
+    sh64*:Linux:*:*)
+	GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+	;;
+    sh*:Linux:*:*)
+	GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+	;;
+    sparc:Linux:*:* | sparc64:Linux:*:*)
+	GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+	;;
+    tile*:Linux:*:*)
+	GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+	;;
+    vax:Linux:*:*)
+	GUESS=$UNAME_MACHINE-dec-linux-$LIBC
+	;;
+    x86_64:Linux:*:*)
+	set_cc_for_build
+	CPU=$UNAME_MACHINE
+	LIBCABI=$LIBC
+	if test "$CC_FOR_BUILD" != no_compiler_found; then
+	    ABI=64
+	    sed 's/^	    //' << EOF > "$dummy.c"
+	    #ifdef __i386__
+	    ABI=x86
+	    #else
+	    #ifdef __ILP32__
+	    ABI=x32
+	    #endif
+	    #endif
+EOF
+	    cc_set_abi=`$CC_FOR_BUILD -E "$dummy.c" 2>/dev/null | grep '^ABI' | sed 's, ,,g'`
+	    eval "$cc_set_abi"
+	    case $ABI in
+		x86) CPU=i686 ;;
+		x32) LIBCABI=${LIBC}x32 ;;
+	    esac
+	fi
+	GUESS=$CPU-pc-linux-$LIBCABI
+	;;
+    xtensa*:Linux:*:*)
+	GUESS=$UNAME_MACHINE-unknown-linux-$LIBC
+	;;
+    i*86:DYNIX/ptx:4*:*)
+	# ptx 4.0 does uname -s correctly, with DYNIX/ptx in there.
+	# earlier versions are messed up and put the nodename in both
+	# sysname and nodename.
+	GUESS=i386-sequent-sysv4
+	;;
+    i*86:UNIX_SV:4.2MP:2.*)
+	# Unixware is an offshoot of SVR4, but it has its own version
+	# number series starting with 2...
+	# I am not positive that other SVR4 systems won't match this,
+	# I just have to hope.  -- rms.
+	# Use sysv4.2uw... so that sysv4* matches it.
+	GUESS=$UNAME_MACHINE-pc-sysv4.2uw$UNAME_VERSION
+	;;
+    i*86:OS/2:*:*)
+	# If we were able to find 'uname', then EMX Unix compatibility
+	# is probably installed.
+	GUESS=$UNAME_MACHINE-pc-os2-emx
+	;;
+    i*86:XTS-300:*:STOP)
+	GUESS=$UNAME_MACHINE-unknown-stop
+	;;
+    i*86:atheos:*:*)
+	GUESS=$UNAME_MACHINE-unknown-atheos
+	;;
+    i*86:syllable:*:*)
+	GUESS=$UNAME_MACHINE-pc-syllable
+	;;
+    i*86:LynxOS:2.*:* | i*86:LynxOS:3.[01]*:* | i*86:LynxOS:4.[02]*:*)
+	GUESS=i386-unknown-lynxos$UNAME_RELEASE
+	;;
+    i*86:*DOS:*:*)
+	GUESS=$UNAME_MACHINE-pc-msdosdjgpp
+	;;
+    i*86:*:4.*:*)
+	UNAME_REL=`echo "$UNAME_RELEASE" | sed 's/\/MP$//'`
+	if grep Novell /usr/include/link.h >/dev/null 2>/dev/null; then
+		GUESS=$UNAME_MACHINE-univel-sysv$UNAME_REL
+	else
+		GUESS=$UNAME_MACHINE-pc-sysv$UNAME_REL
+	fi
+	;;
+    i*86:*:5:[678]*)
+	# UnixWare 7.x, OpenUNIX and OpenServer 6.
+	case `/bin/uname -X | grep "^Machine"` in
+	    *486*)	     UNAME_MACHINE=i486 ;;
+	    *Pentium)	     UNAME_MACHINE=i586 ;;
+	    *Pent*|*Celeron) UNAME_MACHINE=i686 ;;
+	esac
+	GUESS=$UNAME_MACHINE-unknown-sysv${UNAME_RELEASE}${UNAME_SYSTEM}${UNAME_VERSION}
+	;;
+    i*86:*:3.2:*)
+	if test -f /usr/options/cb.name; then
+		UNAME_REL=`sed -n 's/.*Version //p' </usr/options/cb.name`
+		GUESS=$UNAME_MACHINE-pc-isc$UNAME_REL
+	elif /bin/uname -X 2>/dev/null >/dev/null ; then
+		UNAME_REL=`(/bin/uname -X|grep Release|sed -e 's/.*= //')`
+		(/bin/uname -X|grep i80486 >/dev/null) && UNAME_MACHINE=i486
+		(/bin/uname -X|grep '^Machine.*Pentium' >/dev/null) \
+			&& UNAME_MACHINE=i586
+		(/bin/uname -X|grep '^Machine.*Pent *II' >/dev/null) \
+			&& UNAME_MACHINE=i686
+		(/bin/uname -X|grep '^Machine.*Pentium Pro' >/dev/null) \
+			&& UNAME_MACHINE=i686
+		GUESS=$UNAME_MACHINE-pc-sco$UNAME_REL
+	else
+		GUESS=$UNAME_MACHINE-pc-sysv32
+	fi
+	;;
+    pc:*:*:*)
+	# Left here for compatibility:
+	# uname -m prints for DJGPP always 'pc', but it prints nothing about
+	# the processor, so we play safe by assuming i586.
+	# Note: whatever this is, it MUST be the same as what config.sub
+	# prints for the "djgpp" host, or else GDB configure will decide that
+	# this is a cross-build.
+	GUESS=i586-pc-msdosdjgpp
+	;;
+    Intel:Mach:3*:*)
+	GUESS=i386-pc-mach3
+	;;
+    paragon:*:*:*)
+	GUESS=i860-intel-osf1
+	;;
+    i860:*:4.*:*) # i860-SVR4
+	if grep Stardent /usr/include/sys/uadmin.h >/dev/null 2>&1 ; then
+	  GUESS=i860-stardent-sysv$UNAME_RELEASE    # Stardent Vistra i860-SVR4
+	else # Add other i860-SVR4 vendors below as they are discovered.
+	  GUESS=i860-unknown-sysv$UNAME_RELEASE     # Unknown i860-SVR4
+	fi
+	;;
+    mini*:CTIX:SYS*5:*)
+	# "miniframe"
+	GUESS=m68010-convergent-sysv
+	;;
+    mc68k:UNIX:SYSTEM5:3.51m)
+	GUESS=m68k-convergent-sysv
+	;;
+    M680?0:D-NIX:5.3:*)
+	GUESS=m68k-diab-dnix
+	;;
+    M68*:*:R3V[5678]*:*)
+	test -r /sysV68 && { echo 'm68k-motorola-sysv'; exit; } ;;
+    3[345]??:*:4.0:3.0 | 3[34]??A:*:4.0:3.0 | 3[34]??,*:*:4.0:3.0 | 3[34]??/*:*:4.0:3.0 | 4400:*:4.0:3.0 | 4850:*:4.0:3.0 | SKA40:*:4.0:3.0 | SDS2:*:4.0:3.0 | SHG2:*:4.0:3.0 | S7501*:*:4.0:3.0)
+	OS_REL=''
+	test -r /etc/.relid \
+	&& OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid`
+	/bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+	  && { echo i486-ncr-sysv4.3"$OS_REL"; exit; }
+	/bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \
+	  && { echo i586-ncr-sysv4.3"$OS_REL"; exit; } ;;
+    3[34]??:*:4.0:* | 3[34]??,*:*:4.0:*)
+	/bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+	  && { echo i486-ncr-sysv4; exit; } ;;
+    NCR*:*:4.2:* | MPRAS*:*:4.2:*)
+	OS_REL='.3'
+	test -r /etc/.relid \
+	    && OS_REL=.`sed -n 's/[^ ]* [^ ]* \([0-9][0-9]\).*/\1/p' < /etc/.relid`
+	/bin/uname -p 2>/dev/null | grep 86 >/dev/null \
+	    && { echo i486-ncr-sysv4.3"$OS_REL"; exit; }
+	/bin/uname -p 2>/dev/null | /bin/grep entium >/dev/null \
+	    && { echo i586-ncr-sysv4.3"$OS_REL"; exit; }
+	/bin/uname -p 2>/dev/null | /bin/grep pteron >/dev/null \
+	    && { echo i586-ncr-sysv4.3"$OS_REL"; exit; } ;;
+    m68*:LynxOS:2.*:* | m68*:LynxOS:3.0*:*)
+	GUESS=m68k-unknown-lynxos$UNAME_RELEASE
+	;;
+    mc68030:UNIX_System_V:4.*:*)
+	GUESS=m68k-atari-sysv4
+	;;
+    TSUNAMI:LynxOS:2.*:*)
+	GUESS=sparc-unknown-lynxos$UNAME_RELEASE
+	;;
+    rs6000:LynxOS:2.*:*)
+	GUESS=rs6000-unknown-lynxos$UNAME_RELEASE
+	;;
+    PowerPC:LynxOS:2.*:* | PowerPC:LynxOS:3.[01]*:* | PowerPC:LynxOS:4.[02]*:*)
+	GUESS=powerpc-unknown-lynxos$UNAME_RELEASE
+	;;
+    SM[BE]S:UNIX_SV:*:*)
+	GUESS=mips-dde-sysv$UNAME_RELEASE
+	;;
+    RM*:ReliantUNIX-*:*:*)
+	GUESS=mips-sni-sysv4
+	;;
+    RM*:SINIX-*:*:*)
+	GUESS=mips-sni-sysv4
+	;;
+    *:SINIX-*:*:*)
+	if uname -p 2>/dev/null >/dev/null ; then
+		UNAME_MACHINE=`(uname -p) 2>/dev/null`
+		GUESS=$UNAME_MACHINE-sni-sysv4
+	else
+		GUESS=ns32k-sni-sysv
+	fi
+	;;
+    PENTIUM:*:4.0*:*)	# Unisys 'ClearPath HMP IX 4000' SVR4/MP effort
+			# says <Richard.M.Bartel@ccMail.Census.GOV>
+	GUESS=i586-unisys-sysv4
+	;;
+    *:UNIX_System_V:4*:FTX*)
+	# From Gerald Hewes <hewes@openmarket.com>.
+	# How about differentiating between stratus architectures? -djm
+	GUESS=hppa1.1-stratus-sysv4
+	;;
+    *:*:*:FTX*)
+	# From seanf@swdc.stratus.com.
+	GUESS=i860-stratus-sysv4
+	;;
+    i*86:VOS:*:*)
+	# From Paul.Green@stratus.com.
+	GUESS=$UNAME_MACHINE-stratus-vos
+	;;
+    *:VOS:*:*)
+	# From Paul.Green@stratus.com.
+	GUESS=hppa1.1-stratus-vos
+	;;
+    mc68*:A/UX:*:*)
+	GUESS=m68k-apple-aux$UNAME_RELEASE
+	;;
+    news*:NEWS-OS:6*:*)
+	GUESS=mips-sony-newsos6
+	;;
+    R[34]000:*System_V*:*:* | R4000:UNIX_SYSV:*:* | R*000:UNIX_SV:*:*)
+	if test -d /usr/nec; then
+		GUESS=mips-nec-sysv$UNAME_RELEASE
+	else
+		GUESS=mips-unknown-sysv$UNAME_RELEASE
+	fi
+	;;
+    BeBox:BeOS:*:*)	# BeOS running on hardware made by Be, PPC only.
+	GUESS=powerpc-be-beos
+	;;
+    BeMac:BeOS:*:*)	# BeOS running on Mac or Mac clone, PPC only.
+	GUESS=powerpc-apple-beos
+	;;
+    BePC:BeOS:*:*)	# BeOS running on Intel PC compatible.
+	GUESS=i586-pc-beos
+	;;
+    BePC:Haiku:*:*)	# Haiku running on Intel PC compatible.
+	GUESS=i586-pc-haiku
+	;;
+    ppc:Haiku:*:*)	# Haiku running on Apple PowerPC
+	GUESS=powerpc-apple-haiku
+	;;
+    *:Haiku:*:*)	# Haiku modern gcc (not bound by BeOS compat)
+	GUESS=$UNAME_MACHINE-unknown-haiku
+	;;
+    SX-4:SUPER-UX:*:*)
+	GUESS=sx4-nec-superux$UNAME_RELEASE
+	;;
+    SX-5:SUPER-UX:*:*)
+	GUESS=sx5-nec-superux$UNAME_RELEASE
+	;;
+    SX-6:SUPER-UX:*:*)
+	GUESS=sx6-nec-superux$UNAME_RELEASE
+	;;
+    SX-7:SUPER-UX:*:*)
+	GUESS=sx7-nec-superux$UNAME_RELEASE
+	;;
+    SX-8:SUPER-UX:*:*)
+	GUESS=sx8-nec-superux$UNAME_RELEASE
+	;;
+    SX-8R:SUPER-UX:*:*)
+	GUESS=sx8r-nec-superux$UNAME_RELEASE
+	;;
+    SX-ACE:SUPER-UX:*:*)
+	GUESS=sxace-nec-superux$UNAME_RELEASE
+	;;
+    Power*:Rhapsody:*:*)
+	GUESS=powerpc-apple-rhapsody$UNAME_RELEASE
+	;;
+    *:Rhapsody:*:*)
+	GUESS=$UNAME_MACHINE-apple-rhapsody$UNAME_RELEASE
+	;;
+    arm64:Darwin:*:*)
+	GUESS=aarch64-apple-darwin$UNAME_RELEASE
+	;;
+    *:Darwin:*:*)
+	UNAME_PROCESSOR=`uname -p`
+	case $UNAME_PROCESSOR in
+	    unknown) UNAME_PROCESSOR=powerpc ;;
+	esac
+	if command -v xcode-select > /dev/null 2> /dev/null && \
+		! xcode-select --print-path > /dev/null 2> /dev/null ; then
+	    # Avoid executing cc if there is no toolchain installed as
+	    # cc will be a stub that puts up a graphical alert
+	    # prompting the user to install developer tools.
+	    CC_FOR_BUILD=no_compiler_found
+	else
+	    set_cc_for_build
+	fi
+	if test "$CC_FOR_BUILD" != no_compiler_found; then
+	    if (echo '#ifdef __LP64__'; echo IS_64BIT_ARCH; echo '#endif') | \
+		   (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \
+		   grep IS_64BIT_ARCH >/dev/null
+	    then
+		case $UNAME_PROCESSOR in
+		    i386) UNAME_PROCESSOR=x86_64 ;;
+		    powerpc) UNAME_PROCESSOR=powerpc64 ;;
+		esac
+	    fi
+	    # On 10.4-10.6 one might compile for PowerPC via gcc -arch ppc
+	    if (echo '#ifdef __POWERPC__'; echo IS_PPC; echo '#endif') | \
+		   (CCOPTS="" $CC_FOR_BUILD -E - 2>/dev/null) | \
+		   grep IS_PPC >/dev/null
+	    then
+		UNAME_PROCESSOR=powerpc
+	    fi
+	elif test "$UNAME_PROCESSOR" = i386 ; then
+	    # uname -m returns i386 or x86_64
+	    UNAME_PROCESSOR=$UNAME_MACHINE
+	fi
+	GUESS=$UNAME_PROCESSOR-apple-darwin$UNAME_RELEASE
+	;;
+    *:procnto*:*:* | *:QNX:[0123456789]*:*)
+	UNAME_PROCESSOR=`uname -p`
+	if test "$UNAME_PROCESSOR" = x86; then
+		UNAME_PROCESSOR=i386
+		UNAME_MACHINE=pc
+	fi
+	GUESS=$UNAME_PROCESSOR-$UNAME_MACHINE-nto-qnx$UNAME_RELEASE
+	;;
+    *:QNX:*:4*)
+	GUESS=i386-pc-qnx
+	;;
+    NEO-*:NONSTOP_KERNEL:*:*)
+	GUESS=neo-tandem-nsk$UNAME_RELEASE
+	;;
+    NSE-*:NONSTOP_KERNEL:*:*)
+	GUESS=nse-tandem-nsk$UNAME_RELEASE
+	;;
+    NSR-*:NONSTOP_KERNEL:*:*)
+	GUESS=nsr-tandem-nsk$UNAME_RELEASE
+	;;
+    NSV-*:NONSTOP_KERNEL:*:*)
+	GUESS=nsv-tandem-nsk$UNAME_RELEASE
+	;;
+    NSX-*:NONSTOP_KERNEL:*:*)
+	GUESS=nsx-tandem-nsk$UNAME_RELEASE
+	;;
+    *:NonStop-UX:*:*)
+	GUESS=mips-compaq-nonstopux
+	;;
+    BS2000:POSIX*:*:*)
+	GUESS=bs2000-siemens-sysv
+	;;
+    DS/*:UNIX_System_V:*:*)
+	GUESS=$UNAME_MACHINE-$UNAME_SYSTEM-$UNAME_RELEASE
+	;;
+    *:Plan9:*:*)
+	# "uname -m" is not consistent, so use $cputype instead. 386
+	# is converted to i386 for consistency with other x86
+	# operating systems.
+	if test "${cputype-}" = 386; then
+	    UNAME_MACHINE=i386
+	elif test "x${cputype-}" != x; then
+	    UNAME_MACHINE=$cputype
+	fi
+	GUESS=$UNAME_MACHINE-unknown-plan9
+	;;
+    *:TOPS-10:*:*)
+	GUESS=pdp10-unknown-tops10
+	;;
+    *:TENEX:*:*)
+	GUESS=pdp10-unknown-tenex
+	;;
+    KS10:TOPS-20:*:* | KL10:TOPS-20:*:* | TYPE4:TOPS-20:*:*)
+	GUESS=pdp10-dec-tops20
+	;;
+    XKL-1:TOPS-20:*:* | TYPE5:TOPS-20:*:*)
+	GUESS=pdp10-xkl-tops20
+	;;
+    *:TOPS-20:*:*)
+	GUESS=pdp10-unknown-tops20
+	;;
+    *:ITS:*:*)
+	GUESS=pdp10-unknown-its
+	;;
+    SEI:*:*:SEIUX)
+	GUESS=mips-sei-seiux$UNAME_RELEASE
+	;;
+    *:DragonFly:*:*)
+	DRAGONFLY_REL=`echo "$UNAME_RELEASE" | sed -e 's/[-(].*//'`
+	GUESS=$UNAME_MACHINE-unknown-dragonfly$DRAGONFLY_REL
+	;;
+    *:*VMS:*:*)
+	UNAME_MACHINE=`(uname -p) 2>/dev/null`
+	case $UNAME_MACHINE in
+	    A*) GUESS=alpha-dec-vms ;;
+	    I*) GUESS=ia64-dec-vms ;;
+	    V*) GUESS=vax-dec-vms ;;
+	esac ;;
+    *:XENIX:*:SysV)
+	GUESS=i386-pc-xenix
+	;;
+    i*86:skyos:*:*)
+	SKYOS_REL=`echo "$UNAME_RELEASE" | sed -e 's/ .*$//'`
+	GUESS=$UNAME_MACHINE-pc-skyos$SKYOS_REL
+	;;
+    i*86:rdos:*:*)
+	GUESS=$UNAME_MACHINE-pc-rdos
+	;;
+    i*86:Fiwix:*:*)
+	GUESS=$UNAME_MACHINE-pc-fiwix
+	;;
+    *:AROS:*:*)
+	GUESS=$UNAME_MACHINE-unknown-aros
+	;;
+    x86_64:VMkernel:*:*)
+	GUESS=$UNAME_MACHINE-unknown-esx
+	;;
+    amd64:Isilon\ OneFS:*:*)
+	GUESS=x86_64-unknown-onefs
+	;;
+    *:Unleashed:*:*)
+	GUESS=$UNAME_MACHINE-unknown-unleashed$UNAME_RELEASE
+	;;
+    *:Ironclad:*:*)
+	GUESS=$UNAME_MACHINE-unknown-ironclad
+	;;
+esac
+
+# Do we have a guess based on uname results?
+if test "x$GUESS" != x; then
+    echo "$GUESS"
+    exit
+fi
+
+# No uname command or uname output not recognized.
+set_cc_for_build
+cat > "$dummy.c" <<EOF
+#ifdef _SEQUENT_
+#include <sys/types.h>
+#include <sys/utsname.h>
+#endif
+#if defined(ultrix) || defined(_ultrix) || defined(__ultrix) || defined(__ultrix__)
+#if defined (vax) || defined (__vax) || defined (__vax__) || defined(mips) || defined(__mips) || defined(__mips__) || defined(MIPS) || defined(__MIPS__)
+#include <signal.h>
+#if defined(_SIZE_T_) || defined(SIGLOST)
+#include <sys/utsname.h>
+#endif
+#endif
+#endif
+int
+main ()
+{
+#if defined (sony)
+#if defined (MIPSEB)
+  /* BFD wants "bsd" instead of "newsos".  Perhaps BFD should be changed,
+     I don't know....  */
+  printf ("mips-sony-bsd\n"); exit (0);
+#else
+#include <sys/param.h>
+  printf ("m68k-sony-newsos%s\n",
+#ifdef NEWSOS4
+  "4"
+#else
+  ""
+#endif
+  ); exit (0);
+#endif
+#endif
+
+#if defined (NeXT)
+#if !defined (__ARCHITECTURE__)
+#define __ARCHITECTURE__ "m68k"
+#endif
+  int version;
+  version=`(hostinfo | sed -n 's/.*NeXT Mach \([0-9]*\).*/\1/p') 2>/dev/null`;
+  if (version < 4)
+    printf ("%s-next-nextstep%d\n", __ARCHITECTURE__, version);
+  else
+    printf ("%s-next-openstep%d\n", __ARCHITECTURE__, version);
+  exit (0);
+#endif
+
+#if defined (MULTIMAX) || defined (n16)
+#if defined (UMAXV)
+  printf ("ns32k-encore-sysv\n"); exit (0);
+#else
+#if defined (CMU)
+  printf ("ns32k-encore-mach\n"); exit (0);
+#else
+  printf ("ns32k-encore-bsd\n"); exit (0);
+#endif
+#endif
+#endif
+
+#if defined (__386BSD__)
+  printf ("i386-pc-bsd\n"); exit (0);
+#endif
+
+#if defined (sequent)
+#if defined (i386)
+  printf ("i386-sequent-dynix\n"); exit (0);
+#endif
+#if defined (ns32000)
+  printf ("ns32k-sequent-dynix\n"); exit (0);
+#endif
+#endif
+
+#if defined (_SEQUENT_)
+  struct utsname un;
+
+  uname(&un);
+  if (strncmp(un.version, "V2", 2) == 0) {
+    printf ("i386-sequent-ptx2\n"); exit (0);
+  }
+  if (strncmp(un.version, "V1", 2) == 0) { /* XXX is V1 correct? */
+    printf ("i386-sequent-ptx1\n"); exit (0);
+  }
+  printf ("i386-sequent-ptx\n"); exit (0);
+#endif
+
+#if defined (vax)
+#if !defined (ultrix)
+#include <sys/param.h>
+#if defined (BSD)
+#if BSD == 43
+  printf ("vax-dec-bsd4.3\n"); exit (0);
+#else
+#if BSD == 199006
+  printf ("vax-dec-bsd4.3reno\n"); exit (0);
+#else
+  printf ("vax-dec-bsd\n"); exit (0);
+#endif
+#endif
+#else
+  printf ("vax-dec-bsd\n"); exit (0);
+#endif
+#else
+#if defined(_SIZE_T_) || defined(SIGLOST)
+  struct utsname un;
+  uname (&un);
+  printf ("vax-dec-ultrix%s\n", un.release); exit (0);
+#else
+  printf ("vax-dec-ultrix\n"); exit (0);
+#endif
+#endif
+#endif
+#if defined(ultrix) || defined(_ultrix) || defined(__ultrix) || defined(__ultrix__)
+#if defined(mips) || defined(__mips) || defined(__mips__) || defined(MIPS) || defined(__MIPS__)
+#if defined(_SIZE_T_) || defined(SIGLOST)
+  struct utsname *un;
+  uname (&un);
+  printf ("mips-dec-ultrix%s\n", un.release); exit (0);
+#else
+  printf ("mips-dec-ultrix\n"); exit (0);
+#endif
+#endif
+#endif
+
+#if defined (alliant) && defined (i860)
+  printf ("i860-alliant-bsd\n"); exit (0);
+#endif
+
+  exit (1);
+}
+EOF
+
+$CC_FOR_BUILD -o "$dummy" "$dummy.c" 2>/dev/null && SYSTEM_NAME=`"$dummy"` &&
+	{ echo "$SYSTEM_NAME"; exit; }
+
+# Apollos put the system type in the environment.
+test -d /usr/apollo && { echo "$ISP-apollo-$SYSTYPE"; exit; }
+
+echo "$0: unable to guess system type" >&2
+
+case $UNAME_MACHINE:$UNAME_SYSTEM in
+    mips:Linux | mips64:Linux)
+	# If we got here on MIPS GNU/Linux, output extra information.
+	cat >&2 <<EOF
+
+NOTE: MIPS GNU/Linux systems require a C compiler to fully recognize
+the system type. Please install a C compiler and try again.
+EOF
+	;;
+esac
+
+cat >&2 <<EOF
+
+This script (version $timestamp), has failed to recognize the
+operating system you are using. If your script is old, overwrite *all*
+copies of config.guess and config.sub with the latest versions from:
+
+  https://git.savannah.gnu.org/cgit/config.git/plain/config.guess
+and
+  https://git.savannah.gnu.org/cgit/config.git/plain/config.sub
+EOF
+
+our_year=`echo $timestamp | sed 's,-.*,,'`
+thisyear=`date +%Y`
+# shellcheck disable=SC2003
+script_age=`expr "$thisyear" - "$our_year"`
+if test "$script_age" -lt 3 ; then
+   cat >&2 <<EOF
+
+If $0 has already been updated, send the following data and any
+information you think might be pertinent to config-patches@gnu.org to
+provide the necessary information to handle your system.
+
+config.guess timestamp = $timestamp
+
+uname -m = `(uname -m) 2>/dev/null || echo unknown`
+uname -r = `(uname -r) 2>/dev/null || echo unknown`
+uname -s = `(uname -s) 2>/dev/null || echo unknown`
+uname -v = `(uname -v) 2>/dev/null || echo unknown`
+
+/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null`
+/bin/uname -X     = `(/bin/uname -X) 2>/dev/null`
+
+hostinfo               = `(hostinfo) 2>/dev/null`
+/bin/universe          = `(/bin/universe) 2>/dev/null`
+/usr/bin/arch -k       = `(/usr/bin/arch -k) 2>/dev/null`
+/bin/arch              = `(/bin/arch) 2>/dev/null`
+/usr/bin/oslevel       = `(/usr/bin/oslevel) 2>/dev/null`
+/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null`
+
+UNAME_MACHINE = "$UNAME_MACHINE"
+UNAME_RELEASE = "$UNAME_RELEASE"
+UNAME_SYSTEM  = "$UNAME_SYSTEM"
+UNAME_VERSION = "$UNAME_VERSION"
+EOF
+fi
+
+exit 1
+
+# Local variables:
+# eval: (add-hook 'before-save-hook 'time-stamp)
+# time-stamp-start: "timestamp='"
+# time-stamp-format: "%:y-%02m-%02d"
+# time-stamp-end: "'"
+# End:
diff --git a/scripts/config.rpath b/scripts/config.rpath
new file mode 100755
index 0000000..c547c68
--- /dev/null
+++ b/scripts/config.rpath
@@ -0,0 +1,666 @@
+#! /bin/sh
+# Output a system dependent set of variables, describing how to set the
+# run time search path of shared libraries in an executable.
+#
+#   Copyright 1996-2007 Free Software Foundation, Inc.
+#   Taken from GNU libtool, 2001
+#   Originally by Gordon Matzigkeit <gord@gnu.ai.mit.edu>, 1996
+#
+#   This file is free software; the Free Software Foundation gives
+#   unlimited permission to copy and/or distribute it, with or without
+#   modifications, as long as this notice is preserved.
+#
+# The first argument passed to this file is the canonical host specification,
+#    CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM
+# or
+#    CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM
+# The environment variables CC, GCC, LDFLAGS, LD, with_gnu_ld
+# should be set by the caller.
+#
+# The set of defined variables is at the end of this script.
+
+# Known limitations:
+# - On IRIX 6.5 with CC="cc", the run time search patch must not be longer
+#   than 256 bytes, otherwise the compiler driver will dump core. The only
+#   known workaround is to choose shorter directory names for the build
+#   directory and/or the installation directory.
+
+# All known linkers require a `.a' archive for static linking (except MSVC,
+# which needs '.lib').
+libext=a
+shrext=.so
+
+host="$1"
+host_cpu=`echo "$host" | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\1/'`
+host_vendor=`echo "$host" | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\2/'`
+host_os=`echo "$host" | sed 's/^\([^-]*\)-\([^-]*\)-\(.*\)$/\3/'`
+
+# Code taken from libtool.m4's _LT_CC_BASENAME.
+
+for cc_temp in $CC""; do
+  case $cc_temp in
+    compile | *[\\/]compile | ccache | *[\\/]ccache ) ;;
+    distcc | *[\\/]distcc | purify | *[\\/]purify ) ;;
+    \-*) ;;
+    *) break;;
+  esac
+done
+cc_basename=`echo "$cc_temp" | sed -e 's%^.*/%%'`
+
+# Code taken from libtool.m4's AC_LIBTOOL_PROG_COMPILER_PIC.
+
+wl=
+if test "$GCC" = yes; then
+  wl='-Wl,'
+else
+  case "$host_os" in
+    aix*)
+      wl='-Wl,'
+      ;;
+    darwin*)
+      case $cc_basename in
+        xlc*)
+          wl='-Wl,'
+          ;;
+      esac
+      ;;
+    mingw* | cygwin* | pw32* | os2*)
+      ;;
+    hpux9* | hpux10* | hpux11*)
+      wl='-Wl,'
+      ;;
+    irix5* | irix6* | nonstopux*)
+      wl='-Wl,'
+      ;;
+    newsos6)
+      ;;
+    linux* | k*bsd*-gnu)
+      case $cc_basename in
+        icc* | ecc*)
+          wl='-Wl,'
+          ;;
+        pgcc | pgf77 | pgf90)
+          wl='-Wl,'
+          ;;
+        ccc*)
+          wl='-Wl,'
+          ;;
+        como)
+          wl='-lopt='
+          ;;
+        *)
+          case `$CC -V 2>&1 | sed 5q` in
+            *Sun\ C*)
+              wl='-Wl,'
+              ;;
+          esac
+          ;;
+      esac
+      ;;
+    osf3* | osf4* | osf5*)
+      wl='-Wl,'
+      ;;
+    rdos*)
+      ;;
+    solaris*)
+      wl='-Wl,'
+      ;;
+    sunos4*)
+      wl='-Qoption ld '
+      ;;
+    sysv4 | sysv4.2uw2* | sysv4.3*)
+      wl='-Wl,'
+      ;;
+    sysv4*MP*)
+      ;;
+    sysv5* | unixware* | sco3.2v5* | sco5v6* | OpenUNIX*)
+      wl='-Wl,'
+      ;;
+    unicos*)
+      wl='-Wl,'
+      ;;
+    uts4*)
+      ;;
+  esac
+fi
+
+# Code taken from libtool.m4's AC_LIBTOOL_PROG_LD_SHLIBS.
+
+hardcode_libdir_flag_spec=
+hardcode_libdir_separator=
+hardcode_direct=no
+hardcode_minus_L=no
+
+case "$host_os" in
+  cygwin* | mingw* | pw32*)
+    # FIXME: the MSVC++ port hasn't been tested in a loooong time
+    # When not using gcc, we currently assume that we are using
+    # Microsoft Visual C++.
+    if test "$GCC" != yes; then
+      with_gnu_ld=no
+    fi
+    ;;
+  interix*)
+    # we just hope/assume this is gcc and not c89 (= MSVC++)
+    with_gnu_ld=yes
+    ;;
+  openbsd*)
+    with_gnu_ld=no
+    ;;
+esac
+
+ld_shlibs=yes
+if test "$with_gnu_ld" = yes; then
+  # Set some defaults for GNU ld with shared library support. These
+  # are reset later if shared libraries are not supported. Putting them
+  # here allows them to be overridden if necessary.
+  # Unlike libtool, we use -rpath here, not --rpath, since the documented
+  # option of GNU ld is called -rpath, not --rpath.
+  hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir'
+  case "$host_os" in
+    aix3* | aix4* | aix5*)
+      # On AIX/PPC, the GNU linker is very broken
+      if test "$host_cpu" != ia64; then
+        ld_shlibs=no
+      fi
+      ;;
+    amigaos*)
+      hardcode_libdir_flag_spec='-L$libdir'
+      hardcode_minus_L=yes
+      # Samuel A. Falvo II <kc5tja@dolphin.openprojects.net> reports
+      # that the semantics of dynamic libraries on AmigaOS, at least up
+      # to version 4, is to share data among multiple programs linked
+      # with the same dynamic library.  Since this doesn't match the
+      # behavior of shared libraries on other platforms, we cannot use
+      # them.
+      ld_shlibs=no
+      ;;
+    beos*)
+      if $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then
+        :
+      else
+        ld_shlibs=no
+      fi
+      ;;
+    cygwin* | mingw* | pw32*)
+      # hardcode_libdir_flag_spec is actually meaningless, as there is
+      # no search path for DLLs.
+      hardcode_libdir_flag_spec='-L$libdir'
+      if $LD --help 2>&1 | grep 'auto-import' > /dev/null; then
+        :
+      else
+        ld_shlibs=no
+      fi
+      ;;
+    interix[3-9]*)
+      hardcode_direct=no
+      hardcode_libdir_flag_spec='${wl}-rpath,$libdir'
+      ;;
+    gnu* | linux* | k*bsd*-gnu)
+      if $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then
+        :
+      else
+        ld_shlibs=no
+      fi
+      ;;
+    netbsd*)
+      ;;
+    solaris*)
+      if $LD -v 2>&1 | grep 'BFD 2\.8' > /dev/null; then
+        ld_shlibs=no
+      elif $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then
+        :
+      else
+        ld_shlibs=no
+      fi
+      ;;
+    sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX*)
+      case `$LD -v 2>&1` in
+        *\ [01].* | *\ 2.[0-9].* | *\ 2.1[0-5].*)
+          ld_shlibs=no
+          ;;
+        *)
+          if $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then
+            hardcode_libdir_flag_spec='`test -z "$SCOABSPATH" && echo ${wl}-rpath,$libdir`'
+          else
+            ld_shlibs=no
+          fi
+          ;;
+      esac
+      ;;
+    sunos4*)
+      hardcode_direct=yes
+      ;;
+    *)
+      if $LD --help 2>&1 | grep ': supported targets:.* elf' > /dev/null; then
+        :
+      else
+        ld_shlibs=no
+      fi
+      ;;
+  esac
+  if test "$ld_shlibs" = no; then
+    hardcode_libdir_flag_spec=
+  fi
+else
+  case "$host_os" in
+    aix3*)
+      # Note: this linker hardcodes the directories in LIBPATH if there
+      # are no directories specified by -L.
+      hardcode_minus_L=yes
+      if test "$GCC" = yes; then
+        # Neither direct hardcoding nor static linking is supported with a
+        # broken collect2.
+        hardcode_direct=unsupported
+      fi
+      ;;
+    aix4* | aix5*)
+      if test "$host_cpu" = ia64; then
+        # On IA64, the linker does run time linking by default, so we don't
+        # have to do anything special.
+        aix_use_runtimelinking=no
+      else
+        aix_use_runtimelinking=no
+        # Test if we are trying to use run time linking or normal
+        # AIX style linking. If -brtl is somewhere in LDFLAGS, we
+        # need to do runtime linking.
+        case $host_os in aix4.[23]|aix4.[23].*|aix5*)
+          for ld_flag in $LDFLAGS; do
+            if (test $ld_flag = "-brtl" || test $ld_flag = "-Wl,-brtl"); then
+              aix_use_runtimelinking=yes
+              break
+            fi
+          done
+          ;;
+        esac
+      fi
+      hardcode_direct=yes
+      hardcode_libdir_separator=':'
+      if test "$GCC" = yes; then
+        case $host_os in aix4.[012]|aix4.[012].*)
+          collect2name=`${CC} -print-prog-name=collect2`
+          if test -f "$collect2name" && \
+            strings "$collect2name" | grep resolve_lib_name >/dev/null
+          then
+            # We have reworked collect2
+            :
+          else
+            # We have old collect2
+            hardcode_direct=unsupported
+            hardcode_minus_L=yes
+            hardcode_libdir_flag_spec='-L$libdir'
+            hardcode_libdir_separator=
+          fi
+          ;;
+        esac
+      fi
+      # Begin _LT_AC_SYS_LIBPATH_AIX.
+      echo 'int main () { return 0; }' > conftest.c
+      ${CC} ${LDFLAGS} conftest.c -o conftest
+      aix_libpath=`dump -H conftest 2>/dev/null | sed -n -e '/Import File Strings/,/^$/ { /^0/ { s/^0  *\(.*\)$/\1/; p; }
+}'`
+      if test -z "$aix_libpath"; then
+        aix_libpath=`dump -HX64 conftest 2>/dev/null | sed -n -e '/Import File Strings/,/^$/ { /^0/ { s/^0  *\(.*\)$/\1/; p; }
+}'`
+      fi
+      if test -z "$aix_libpath"; then
+        aix_libpath="/usr/lib:/lib"
+      fi
+      rm -f conftest.c conftest
+      # End _LT_AC_SYS_LIBPATH_AIX.
+      if test "$aix_use_runtimelinking" = yes; then
+        hardcode_libdir_flag_spec='${wl}-blibpath:$libdir:'"$aix_libpath"
+      else
+        if test "$host_cpu" = ia64; then
+          hardcode_libdir_flag_spec='${wl}-R $libdir:/usr/lib:/lib'
+        else
+          hardcode_libdir_flag_spec='${wl}-blibpath:$libdir:'"$aix_libpath"
+        fi
+      fi
+      ;;
+    amigaos*)
+      hardcode_libdir_flag_spec='-L$libdir'
+      hardcode_minus_L=yes
+      # see comment about different semantics on the GNU ld section
+      ld_shlibs=no
+      ;;
+    bsdi[45]*)
+      ;;
+    cygwin* | mingw* | pw32*)
+      # When not using gcc, we currently assume that we are using
+      # Microsoft Visual C++.
+      # hardcode_libdir_flag_spec is actually meaningless, as there is
+      # no search path for DLLs.
+      hardcode_libdir_flag_spec=' '
+      libext=lib
+      ;;
+    darwin* | rhapsody*)
+      hardcode_direct=no
+      if test "$GCC" = yes ; then
+        :
+      else
+        case $cc_basename in
+          xlc*)
+            ;;
+          *)
+            ld_shlibs=no
+            ;;
+        esac
+      fi
+      ;;
+    dgux*)
+      hardcode_libdir_flag_spec='-L$libdir'
+      ;;
+    freebsd1*)
+      ld_shlibs=no
+      ;;
+    freebsd2.2*)
+      hardcode_libdir_flag_spec='-R$libdir'
+      hardcode_direct=yes
+      ;;
+    freebsd2*)
+      hardcode_direct=yes
+      hardcode_minus_L=yes
+      ;;
+    freebsd* | dragonfly*)
+      hardcode_libdir_flag_spec='-R$libdir'
+      hardcode_direct=yes
+      ;;
+    hpux9*)
+      hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir'
+      hardcode_libdir_separator=:
+      hardcode_direct=yes
+      # hardcode_minus_L: Not really in the search PATH,
+      # but as the default location of the library.
+      hardcode_minus_L=yes
+      ;;
+    hpux10*)
+      if test "$with_gnu_ld" = no; then
+        hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir'
+        hardcode_libdir_separator=:
+        hardcode_direct=yes
+        # hardcode_minus_L: Not really in the search PATH,
+        # but as the default location of the library.
+        hardcode_minus_L=yes
+      fi
+      ;;
+    hpux11*)
+      if test "$with_gnu_ld" = no; then
+        hardcode_libdir_flag_spec='${wl}+b ${wl}$libdir'
+        hardcode_libdir_separator=:
+        case $host_cpu in
+          hppa*64*|ia64*)
+            hardcode_direct=no
+            ;;
+          *)
+            hardcode_direct=yes
+            # hardcode_minus_L: Not really in the search PATH,
+            # but as the default location of the library.
+            hardcode_minus_L=yes
+            ;;
+        esac
+      fi
+      ;;
+    irix5* | irix6* | nonstopux*)
+      hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir'
+      hardcode_libdir_separator=:
+      ;;
+    netbsd*)
+      hardcode_libdir_flag_spec='-R$libdir'
+      hardcode_direct=yes
+      ;;
+    newsos6)
+      hardcode_direct=yes
+      hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir'
+      hardcode_libdir_separator=:
+      ;;
+    openbsd*)
+      if test -f /usr/libexec/ld.so; then
+        hardcode_direct=yes
+        if test -z "`echo __ELF__ | $CC -E - | grep __ELF__`" || test "$host_os-$host_cpu" = "openbsd2.8-powerpc"; then
+          hardcode_libdir_flag_spec='${wl}-rpath,$libdir'
+        else
+          case "$host_os" in
+            openbsd[01].* | openbsd2.[0-7] | openbsd2.[0-7].*)
+              hardcode_libdir_flag_spec='-R$libdir'
+              ;;
+            *)
+              hardcode_libdir_flag_spec='${wl}-rpath,$libdir'
+              ;;
+          esac
+        fi
+      else
+        ld_shlibs=no
+      fi
+      ;;
+    os2*)
+      hardcode_libdir_flag_spec='-L$libdir'
+      hardcode_minus_L=yes
+      ;;
+    osf3*)
+      hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir'
+      hardcode_libdir_separator=:
+      ;;
+    osf4* | osf5*)
+      if test "$GCC" = yes; then
+        hardcode_libdir_flag_spec='${wl}-rpath ${wl}$libdir'
+      else
+        # Both cc and cxx compiler support -rpath directly
+        hardcode_libdir_flag_spec='-rpath $libdir'
+      fi
+      hardcode_libdir_separator=:
+      ;;
+    solaris*)
+      hardcode_libdir_flag_spec='-R$libdir'
+      ;;
+    sunos4*)
+      hardcode_libdir_flag_spec='-L$libdir'
+      hardcode_direct=yes
+      hardcode_minus_L=yes
+      ;;
+    sysv4)
+      case $host_vendor in
+        sni)
+          hardcode_direct=yes # is this really true???
+          ;;
+        siemens)
+          hardcode_direct=no
+          ;;
+        motorola)
+          hardcode_direct=no #Motorola manual says yes, but my tests say they lie
+          ;;
+      esac
+      ;;
+    sysv4.3*)
+      ;;
+    sysv4*MP*)
+      if test -d /usr/nec; then
+        ld_shlibs=yes
+      fi
+      ;;
+    sysv4*uw2* | sysv5OpenUNIX* | sysv5UnixWare7.[01].[10]* | unixware7* | sco3.2v5.0.[024]*)
+      ;;
+    sysv5* | sco3.2v5* | sco5v6*)
+      hardcode_libdir_flag_spec='`test -z "$SCOABSPATH" && echo ${wl}-R,$libdir`'
+      hardcode_libdir_separator=':'
+      ;;
+    uts4*)
+      hardcode_libdir_flag_spec='-L$libdir'
+      ;;
+    *)
+      ld_shlibs=no
+      ;;
+  esac
+fi
+
+# Check dynamic linker characteristics
+# Code taken from libtool.m4's AC_LIBTOOL_SYS_DYNAMIC_LINKER.
+# Unlike libtool.m4, here we don't care about _all_ names of the library, but
+# only about the one the linker finds when passed -lNAME. This is the last
+# element of library_names_spec in libtool.m4, or possibly two of them if the
+# linker has special search rules.
+library_names_spec=      # the last element of library_names_spec in libtool.m4
+libname_spec='lib$name'
+case "$host_os" in
+  aix3*)
+    library_names_spec='$libname.a'
+    ;;
+  aix4* | aix5*)
+    library_names_spec='$libname$shrext'
+    ;;
+  amigaos*)
+    library_names_spec='$libname.a'
+    ;;
+  beos*)
+    library_names_spec='$libname$shrext'
+    ;;
+  bsdi[45]*)
+    library_names_spec='$libname$shrext'
+    ;;
+  cygwin* | mingw* | pw32*)
+    shrext=.dll
+    library_names_spec='$libname.dll.a $libname.lib'
+    ;;
+  darwin* | rhapsody*)
+    shrext=.dylib
+    library_names_spec='$libname$shrext'
+    ;;
+  dgux*)
+    library_names_spec='$libname$shrext'
+    ;;
+  freebsd1*)
+    ;;
+  freebsd* | dragonfly*)
+    case "$host_os" in
+      freebsd[123]*)
+        library_names_spec='$libname$shrext$versuffix' ;;
+      *)
+        library_names_spec='$libname$shrext' ;;
+    esac
+    ;;
+  gnu*)
+    library_names_spec='$libname$shrext'
+    ;;
+  hpux9* | hpux10* | hpux11*)
+    case $host_cpu in
+      ia64*)
+        shrext=.so
+        ;;
+      hppa*64*)
+        shrext=.sl
+        ;;
+      *)
+        shrext=.sl
+        ;;
+    esac
+    library_names_spec='$libname$shrext'
+    ;;
+  interix[3-9]*)
+    library_names_spec='$libname$shrext'
+    ;;
+  irix5* | irix6* | nonstopux*)
+    library_names_spec='$libname$shrext'
+    case "$host_os" in
+      irix5* | nonstopux*)
+        libsuff= shlibsuff=
+        ;;
+      *)
+        case $LD in
+          *-32|*"-32 "|*-melf32bsmip|*"-melf32bsmip ") libsuff= shlibsuff= ;;
+          *-n32|*"-n32 "|*-melf32bmipn32|*"-melf32bmipn32 ") libsuff=32 shlibsuff=N32 ;;
+          *-64|*"-64 "|*-melf64bmip|*"-melf64bmip ") libsuff=64 shlibsuff=64 ;;
+          *) libsuff= shlibsuff= ;;
+        esac
+        ;;
+    esac
+    ;;
+  linux*oldld* | linux*aout* | linux*coff*)
+    ;;
+  linux* | k*bsd*-gnu)
+    library_names_spec='$libname$shrext'
+    ;;
+  knetbsd*-gnu)
+    library_names_spec='$libname$shrext'
+    ;;
+  netbsd*)
+    library_names_spec='$libname$shrext'
+    ;;
+  newsos6)
+    library_names_spec='$libname$shrext'
+    ;;
+  nto-qnx*)
+    library_names_spec='$libname$shrext'
+    ;;
+  openbsd*)
+    library_names_spec='$libname$shrext$versuffix'
+    ;;
+  os2*)
+    libname_spec='$name'
+    shrext=.dll
+    library_names_spec='$libname.a'
+    ;;
+  osf3* | osf4* | osf5*)
+    library_names_spec='$libname$shrext'
+    ;;
+  rdos*)
+    ;;
+  solaris*)
+    library_names_spec='$libname$shrext'
+    ;;
+  sunos4*)
+    library_names_spec='$libname$shrext$versuffix'
+    ;;
+  sysv4 | sysv4.3*)
+    library_names_spec='$libname$shrext'
+    ;;
+  sysv4*MP*)
+    library_names_spec='$libname$shrext'
+    ;;
+  sysv5* | sco3.2v5* | sco5v6* | unixware* | OpenUNIX* | sysv4*uw2*)
+    library_names_spec='$libname$shrext'
+    ;;
+  uts4*)
+    library_names_spec='$libname$shrext'
+    ;;
+esac
+
+sed_quote_subst='s/\(["`$\\]\)/\\\1/g'
+escaped_wl=`echo "X$wl" | sed -e 's/^X//' -e "$sed_quote_subst"`
+shlibext=`echo "$shrext" | sed -e 's,^\.,,'`
+escaped_libname_spec=`echo "X$libname_spec" | sed -e 's/^X//' -e "$sed_quote_subst"`
+escaped_library_names_spec=`echo "X$library_names_spec" | sed -e 's/^X//' -e "$sed_quote_subst"`
+escaped_hardcode_libdir_flag_spec=`echo "X$hardcode_libdir_flag_spec" | sed -e 's/^X//' -e "$sed_quote_subst"`
+
+LC_ALL=C sed -e 's/^\([a-zA-Z0-9_]*\)=/acl_cv_\1=/' <<EOF
+
+# How to pass a linker flag through the compiler.
+wl="$escaped_wl"
+
+# Static library suffix (normally "a").
+libext="$libext"
+
+# Shared library suffix (normally "so").
+shlibext="$shlibext"
+
+# Format of library name prefix.
+libname_spec="$escaped_libname_spec"
+
+# Library names that the linker finds when passed -lNAME.
+library_names_spec="$escaped_library_names_spec"
+
+# Flag to hardcode \$libdir into a binary during linking.
+# This must work even if \$libdir does not exist.
+hardcode_libdir_flag_spec="$escaped_hardcode_libdir_flag_spec"
+
+# Whether we need a single -rpath flag with a separated argument.
+hardcode_libdir_separator="$hardcode_libdir_separator"
+
+# Set to yes if using DIR/libNAME.so during linking hardcodes DIR into the
+# resulting binary.
+hardcode_direct="$hardcode_direct"
+
+# Set to yes if using the -LDIR flag during linking hardcodes DIR into the
+# resulting binary.
+hardcode_minus_L="$hardcode_minus_L"
+
+EOF
diff --git a/scripts/config.sub b/scripts/config.sub
new file mode 100755
index 0000000..4aaae46
--- /dev/null
+++ b/scripts/config.sub
@@ -0,0 +1,2354 @@
+#! /bin/sh
+# Configuration validation subroutine script.
+#   Copyright 1992-2024 Free Software Foundation, Inc.
+
+# shellcheck disable=SC2006,SC2268,SC2162 # see below for rationale
+
+timestamp='2024-05-27'
+
+# This file is free software; you can redistribute it and/or modify it
+# under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses/>.
+#
+# As a special exception to the GNU General Public License, if you
+# distribute this file as part of a program that contains a
+# configuration script generated by Autoconf, you may include it under
+# the same distribution terms that you use for the rest of that
+# program.  This Exception is an additional permission under section 7
+# of the GNU General Public License, version 3 ("GPLv3").
+
+
+# Please send patches to <config-patches@gnu.org>.
+#
+# Configuration subroutine to validate and canonicalize a configuration type.
+# Supply the specified configuration type as an argument.
+# If it is invalid, we print an error message on stderr and exit with code 1.
+# Otherwise, we print the canonical config type on stdout and succeed.
+
+# You can get the latest version of this script from:
+# https://git.savannah.gnu.org/cgit/config.git/plain/config.sub
+
+# This file is supposed to be the same for all GNU packages
+# and recognize all the CPU types, system types and aliases
+# that are meaningful with *any* GNU software.
+# Each package is responsible for reporting which valid configurations
+# it does not support.  The user should be able to distinguish
+# a failure to support a valid configuration from a meaningless
+# configuration.
+
+# The goal of this file is to map all the various variations of a given
+# machine specification into a single specification in the form:
+#	CPU_TYPE-MANUFACTURER-OPERATING_SYSTEM
+# or in some cases, the newer four-part form:
+#	CPU_TYPE-MANUFACTURER-KERNEL-OPERATING_SYSTEM
+# It is wrong to echo any other type of specification.
+
+# The "shellcheck disable" line above the timestamp inhibits complaints
+# about features and limitations of the classic Bourne shell that were
+# superseded or lifted in POSIX.  However, this script identifies a wide
+# variety of pre-POSIX systems that do not have POSIX shells at all, and
+# even some reasonably current systems (Solaris 10 as case-in-point) still
+# have a pre-POSIX /bin/sh.
+
+me=`echo "$0" | sed -e 's,.*/,,'`
+
+usage="\
+Usage: $0 [OPTION] CPU-MFR-OPSYS or ALIAS
+
+Canonicalize a configuration name.
+
+Options:
+  -h, --help         print this help, then exit
+  -t, --time-stamp   print date of last modification, then exit
+  -v, --version      print version number, then exit
+
+Report bugs and patches to <config-patches@gnu.org>."
+
+version="\
+GNU config.sub ($timestamp)
+
+Copyright 1992-2024 Free Software Foundation, Inc.
+
+This is free software; see the source for copying conditions.  There is NO
+warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE."
+
+help="
+Try '$me --help' for more information."
+
+# Parse command line
+while test $# -gt 0 ; do
+  case $1 in
+    --time-stamp | --time* | -t )
+       echo "$timestamp" ; exit ;;
+    --version | -v )
+       echo "$version" ; exit ;;
+    --help | --h* | -h )
+       echo "$usage"; exit ;;
+    -- )     # Stop option processing
+       shift; break ;;
+    - )	# Use stdin as input.
+       break ;;
+    -* )
+       echo "$me: invalid option $1$help" >&2
+       exit 1 ;;
+
+    *local*)
+       # First pass through any local machine types.
+       echo "$1"
+       exit ;;
+
+    * )
+       break ;;
+  esac
+done
+
+case $# in
+ 0) echo "$me: missing argument$help" >&2
+    exit 1;;
+ 1) ;;
+ *) echo "$me: too many arguments$help" >&2
+    exit 1;;
+esac
+
+# Split fields of configuration type
+saved_IFS=$IFS
+IFS="-" read field1 field2 field3 field4 <<EOF
+$1
+EOF
+IFS=$saved_IFS
+
+# Separate into logical components for further validation
+case $1 in
+	*-*-*-*-*)
+		echo "Invalid configuration '$1': more than four components" >&2
+		exit 1
+		;;
+	*-*-*-*)
+		basic_machine=$field1-$field2
+		basic_os=$field3-$field4
+		;;
+	*-*-*)
+		# Ambiguous whether COMPANY is present, or skipped and KERNEL-OS is two
+		# parts
+		maybe_os=$field2-$field3
+		case $maybe_os in
+			  cloudabi*-eabi* \
+			| kfreebsd*-gnu* \
+			| knetbsd*-gnu* \
+			| kopensolaris*-gnu* \
+			| linux-* \
+			| managarm-* \
+			| netbsd*-eabi* \
+			| netbsd*-gnu* \
+			| nto-qnx* \
+			| os2-emx* \
+			| rtmk-nova* \
+			| storm-chaos* \
+			| uclinux-gnu* \
+			| uclinux-uclibc* \
+			| windows-* )
+				basic_machine=$field1
+				basic_os=$maybe_os
+				;;
+			android-linux)
+				basic_machine=$field1-unknown
+				basic_os=linux-android
+				;;
+			*)
+				basic_machine=$field1-$field2
+				basic_os=$field3
+				;;
+		esac
+		;;
+	*-*)
+		case $field1-$field2 in
+			# Shorthands that happen to contain a single dash
+			convex-c[12] | convex-c3[248])
+				basic_machine=$field2-convex
+				basic_os=
+				;;
+			decstation-3100)
+				basic_machine=mips-dec
+				basic_os=
+				;;
+			*-*)
+				# Second component is usually, but not always the OS
+				case $field2 in
+					# Do not treat sunos as a manufacturer
+					sun*os*)
+						basic_machine=$field1
+						basic_os=$field2
+						;;
+					# Manufacturers
+					  3100* \
+					| 32* \
+					| 3300* \
+					| 3600* \
+					| 7300* \
+					| acorn \
+					| altos* \
+					| apollo \
+					| apple \
+					| atari \
+					| att* \
+					| axis \
+					| be \
+					| bull \
+					| cbm \
+					| ccur \
+					| cisco \
+					| commodore \
+					| convergent* \
+					| convex* \
+					| cray \
+					| crds \
+					| dec* \
+					| delta* \
+					| dg \
+					| digital \
+					| dolphin \
+					| encore* \
+					| gould \
+					| harris \
+					| highlevel \
+					| hitachi* \
+					| hp \
+					| ibm* \
+					| intergraph \
+					| isi* \
+					| knuth \
+					| masscomp \
+					| microblaze* \
+					| mips* \
+					| motorola* \
+					| ncr* \
+					| news \
+					| next \
+					| ns \
+					| oki \
+					| omron* \
+					| pc533* \
+					| rebel \
+					| rom68k \
+					| rombug \
+					| semi \
+					| sequent* \
+					| siemens \
+					| sgi* \
+					| siemens \
+					| sim \
+					| sni \
+					| sony* \
+					| stratus \
+					| sun \
+					| sun[234]* \
+					| tektronix \
+					| tti* \
+					| ultra \
+					| unicom* \
+					| wec \
+					| winbond \
+					| wrs)
+						basic_machine=$field1-$field2
+						basic_os=
+						;;
+					zephyr*)
+						basic_machine=$field1-unknown
+						basic_os=$field2
+						;;
+					*)
+						basic_machine=$field1
+						basic_os=$field2
+						;;
+				esac
+			;;
+		esac
+		;;
+	*)
+		# Convert single-component short-hands not valid as part of
+		# multi-component configurations.
+		case $field1 in
+			386bsd)
+				basic_machine=i386-pc
+				basic_os=bsd
+				;;
+			a29khif)
+				basic_machine=a29k-amd
+				basic_os=udi
+				;;
+			adobe68k)
+				basic_machine=m68010-adobe
+				basic_os=scout
+				;;
+			alliant)
+				basic_machine=fx80-alliant
+				basic_os=
+				;;
+			altos | altos3068)
+				basic_machine=m68k-altos
+				basic_os=
+				;;
+			am29k)
+				basic_machine=a29k-none
+				basic_os=bsd
+				;;
+			amdahl)
+				basic_machine=580-amdahl
+				basic_os=sysv
+				;;
+			amiga)
+				basic_machine=m68k-unknown
+				basic_os=
+				;;
+			amigaos | amigados)
+				basic_machine=m68k-unknown
+				basic_os=amigaos
+				;;
+			amigaunix | amix)
+				basic_machine=m68k-unknown
+				basic_os=sysv4
+				;;
+			apollo68)
+				basic_machine=m68k-apollo
+				basic_os=sysv
+				;;
+			apollo68bsd)
+				basic_machine=m68k-apollo
+				basic_os=bsd
+				;;
+			aros)
+				basic_machine=i386-pc
+				basic_os=aros
+				;;
+			aux)
+				basic_machine=m68k-apple
+				basic_os=aux
+				;;
+			balance)
+				basic_machine=ns32k-sequent
+				basic_os=dynix
+				;;
+			blackfin)
+				basic_machine=bfin-unknown
+				basic_os=linux
+				;;
+			cegcc)
+				basic_machine=arm-unknown
+				basic_os=cegcc
+				;;
+			cray)
+				basic_machine=j90-cray
+				basic_os=unicos
+				;;
+			crds | unos)
+				basic_machine=m68k-crds
+				basic_os=
+				;;
+			da30)
+				basic_machine=m68k-da30
+				basic_os=
+				;;
+			decstation | pmax | pmin | dec3100 | decstatn)
+				basic_machine=mips-dec
+				basic_os=
+				;;
+			delta88)
+				basic_machine=m88k-motorola
+				basic_os=sysv3
+				;;
+			dicos)
+				basic_machine=i686-pc
+				basic_os=dicos
+				;;
+			djgpp)
+				basic_machine=i586-pc
+				basic_os=msdosdjgpp
+				;;
+			ebmon29k)
+				basic_machine=a29k-amd
+				basic_os=ebmon
+				;;
+			es1800 | OSE68k | ose68k | ose | OSE)
+				basic_machine=m68k-ericsson
+				basic_os=ose
+				;;
+			gmicro)
+				basic_machine=tron-gmicro
+				basic_os=sysv
+				;;
+			go32)
+				basic_machine=i386-pc
+				basic_os=go32
+				;;
+			h8300hms)
+				basic_machine=h8300-hitachi
+				basic_os=hms
+				;;
+			h8300xray)
+				basic_machine=h8300-hitachi
+				basic_os=xray
+				;;
+			h8500hms)
+				basic_machine=h8500-hitachi
+				basic_os=hms
+				;;
+			harris)
+				basic_machine=m88k-harris
+				basic_os=sysv3
+				;;
+			hp300 | hp300hpux)
+				basic_machine=m68k-hp
+				basic_os=hpux
+				;;
+			hp300bsd)
+				basic_machine=m68k-hp
+				basic_os=bsd
+				;;
+			hppaosf)
+				basic_machine=hppa1.1-hp
+				basic_os=osf
+				;;
+			hppro)
+				basic_machine=hppa1.1-hp
+				basic_os=proelf
+				;;
+			i386mach)
+				basic_machine=i386-mach
+				basic_os=mach
+				;;
+			isi68 | isi)
+				basic_machine=m68k-isi
+				basic_os=sysv
+				;;
+			m68knommu)
+				basic_machine=m68k-unknown
+				basic_os=linux
+				;;
+			magnum | m3230)
+				basic_machine=mips-mips
+				basic_os=sysv
+				;;
+			merlin)
+				basic_machine=ns32k-utek
+				basic_os=sysv
+				;;
+			mingw64)
+				basic_machine=x86_64-pc
+				basic_os=mingw64
+				;;
+			mingw32)
+				basic_machine=i686-pc
+				basic_os=mingw32
+				;;
+			mingw32ce)
+				basic_machine=arm-unknown
+				basic_os=mingw32ce
+				;;
+			monitor)
+				basic_machine=m68k-rom68k
+				basic_os=coff
+				;;
+			morphos)
+				basic_machine=powerpc-unknown
+				basic_os=morphos
+				;;
+			moxiebox)
+				basic_machine=moxie-unknown
+				basic_os=moxiebox
+				;;
+			msdos)
+				basic_machine=i386-pc
+				basic_os=msdos
+				;;
+			msys)
+				basic_machine=i686-pc
+				basic_os=msys
+				;;
+			mvs)
+				basic_machine=i370-ibm
+				basic_os=mvs
+				;;
+			nacl)
+				basic_machine=le32-unknown
+				basic_os=nacl
+				;;
+			ncr3000)
+				basic_machine=i486-ncr
+				basic_os=sysv4
+				;;
+			netbsd386)
+				basic_machine=i386-pc
+				basic_os=netbsd
+				;;
+			netwinder)
+				basic_machine=armv4l-rebel
+				basic_os=linux
+				;;
+			news | news700 | news800 | news900)
+				basic_machine=m68k-sony
+				basic_os=newsos
+				;;
+			news1000)
+				basic_machine=m68030-sony
+				basic_os=newsos
+				;;
+			necv70)
+				basic_machine=v70-nec
+				basic_os=sysv
+				;;
+			nh3000)
+				basic_machine=m68k-harris
+				basic_os=cxux
+				;;
+			nh[45]000)
+				basic_machine=m88k-harris
+				basic_os=cxux
+				;;
+			nindy960)
+				basic_machine=i960-intel
+				basic_os=nindy
+				;;
+			mon960)
+				basic_machine=i960-intel
+				basic_os=mon960
+				;;
+			nonstopux)
+				basic_machine=mips-compaq
+				basic_os=nonstopux
+				;;
+			os400)
+				basic_machine=powerpc-ibm
+				basic_os=os400
+				;;
+			OSE68000 | ose68000)
+				basic_machine=m68000-ericsson
+				basic_os=ose
+				;;
+			os68k)
+				basic_machine=m68k-none
+				basic_os=os68k
+				;;
+			paragon)
+				basic_machine=i860-intel
+				basic_os=osf
+				;;
+			parisc)
+				basic_machine=hppa-unknown
+				basic_os=linux
+				;;
+			psp)
+				basic_machine=mipsallegrexel-sony
+				basic_os=psp
+				;;
+			pw32)
+				basic_machine=i586-unknown
+				basic_os=pw32
+				;;
+			rdos | rdos64)
+				basic_machine=x86_64-pc
+				basic_os=rdos
+				;;
+			rdos32)
+				basic_machine=i386-pc
+				basic_os=rdos
+				;;
+			rom68k)
+				basic_machine=m68k-rom68k
+				basic_os=coff
+				;;
+			sa29200)
+				basic_machine=a29k-amd
+				basic_os=udi
+				;;
+			sei)
+				basic_machine=mips-sei
+				basic_os=seiux
+				;;
+			sequent)
+				basic_machine=i386-sequent
+				basic_os=
+				;;
+			sps7)
+				basic_machine=m68k-bull
+				basic_os=sysv2
+				;;
+			st2000)
+				basic_machine=m68k-tandem
+				basic_os=
+				;;
+			stratus)
+				basic_machine=i860-stratus
+				basic_os=sysv4
+				;;
+			sun2)
+				basic_machine=m68000-sun
+				basic_os=
+				;;
+			sun2os3)
+				basic_machine=m68000-sun
+				basic_os=sunos3
+				;;
+			sun2os4)
+				basic_machine=m68000-sun
+				basic_os=sunos4
+				;;
+			sun3)
+				basic_machine=m68k-sun
+				basic_os=
+				;;
+			sun3os3)
+				basic_machine=m68k-sun
+				basic_os=sunos3
+				;;
+			sun3os4)
+				basic_machine=m68k-sun
+				basic_os=sunos4
+				;;
+			sun4)
+				basic_machine=sparc-sun
+				basic_os=
+				;;
+			sun4os3)
+				basic_machine=sparc-sun
+				basic_os=sunos3
+				;;
+			sun4os4)
+				basic_machine=sparc-sun
+				basic_os=sunos4
+				;;
+			sun4sol2)
+				basic_machine=sparc-sun
+				basic_os=solaris2
+				;;
+			sun386 | sun386i | roadrunner)
+				basic_machine=i386-sun
+				basic_os=
+				;;
+			sv1)
+				basic_machine=sv1-cray
+				basic_os=unicos
+				;;
+			symmetry)
+				basic_machine=i386-sequent
+				basic_os=dynix
+				;;
+			t3e)
+				basic_machine=alphaev5-cray
+				basic_os=unicos
+				;;
+			t90)
+				basic_machine=t90-cray
+				basic_os=unicos
+				;;
+			toad1)
+				basic_machine=pdp10-xkl
+				basic_os=tops20
+				;;
+			tpf)
+				basic_machine=s390x-ibm
+				basic_os=tpf
+				;;
+			udi29k)
+				basic_machine=a29k-amd
+				basic_os=udi
+				;;
+			ultra3)
+				basic_machine=a29k-nyu
+				basic_os=sym1
+				;;
+			v810 | necv810)
+				basic_machine=v810-nec
+				basic_os=none
+				;;
+			vaxv)
+				basic_machine=vax-dec
+				basic_os=sysv
+				;;
+			vms)
+				basic_machine=vax-dec
+				basic_os=vms
+				;;
+			vsta)
+				basic_machine=i386-pc
+				basic_os=vsta
+				;;
+			vxworks960)
+				basic_machine=i960-wrs
+				basic_os=vxworks
+				;;
+			vxworks68)
+				basic_machine=m68k-wrs
+				basic_os=vxworks
+				;;
+			vxworks29k)
+				basic_machine=a29k-wrs
+				basic_os=vxworks
+				;;
+			xbox)
+				basic_machine=i686-pc
+				basic_os=mingw32
+				;;
+			ymp)
+				basic_machine=ymp-cray
+				basic_os=unicos
+				;;
+			*)
+				basic_machine=$1
+				basic_os=
+				;;
+		esac
+		;;
+esac
+
+# Decode 1-component or ad-hoc basic machines
+case $basic_machine in
+	# Here we handle the default manufacturer of certain CPU types.  It is in
+	# some cases the only manufacturer, in others, it is the most popular.
+	w89k)
+		cpu=hppa1.1
+		vendor=winbond
+		;;
+	op50n)
+		cpu=hppa1.1
+		vendor=oki
+		;;
+	op60c)
+		cpu=hppa1.1
+		vendor=oki
+		;;
+	ibm*)
+		cpu=i370
+		vendor=ibm
+		;;
+	orion105)
+		cpu=clipper
+		vendor=highlevel
+		;;
+	mac | mpw | mac-mpw)
+		cpu=m68k
+		vendor=apple
+		;;
+	pmac | pmac-mpw)
+		cpu=powerpc
+		vendor=apple
+		;;
+
+	# Recognize the various machine names and aliases which stand
+	# for a CPU type and a company and sometimes even an OS.
+	3b1 | 7300 | 7300-att | att-7300 | pc7300 | safari | unixpc)
+		cpu=m68000
+		vendor=att
+		;;
+	3b*)
+		cpu=we32k
+		vendor=att
+		;;
+	bluegene*)
+		cpu=powerpc
+		vendor=ibm
+		basic_os=cnk
+		;;
+	decsystem10* | dec10*)
+		cpu=pdp10
+		vendor=dec
+		basic_os=tops10
+		;;
+	decsystem20* | dec20*)
+		cpu=pdp10
+		vendor=dec
+		basic_os=tops20
+		;;
+	delta | 3300 | delta-motorola | 3300-motorola | motorola-delta | motorola-3300)
+		cpu=m68k
+		vendor=motorola
+		;;
+	# This used to be dpx2*, but that gets the RS6000-based
+	# DPX/20 and the x86-based DPX/2-100 wrong.  See
+	# https://oldskool.silicium.org/stations/bull_dpx20.htm
+	# https://www.feb-patrimoine.com/english/bull_dpx2.htm
+	# https://www.feb-patrimoine.com/english/unix_and_bull.htm
+	dpx2 | dpx2[23]00 | dpx2[23]xx)
+		cpu=m68k
+		vendor=bull
+		;;
+	dpx2100 | dpx21xx)
+		cpu=i386
+		vendor=bull
+		;;
+	dpx20)
+		cpu=rs6000
+		vendor=bull
+		;;
+	encore | umax | mmax)
+		cpu=ns32k
+		vendor=encore
+		;;
+	elxsi)
+		cpu=elxsi
+		vendor=elxsi
+		basic_os=${basic_os:-bsd}
+		;;
+	fx2800)
+		cpu=i860
+		vendor=alliant
+		;;
+	genix)
+		cpu=ns32k
+		vendor=ns
+		;;
+	h3050r* | hiux*)
+		cpu=hppa1.1
+		vendor=hitachi
+		basic_os=hiuxwe2
+		;;
+	hp3k9[0-9][0-9] | hp9[0-9][0-9])
+		cpu=hppa1.0
+		vendor=hp
+		;;
+	hp9k2[0-9][0-9] | hp9k31[0-9])
+		cpu=m68000
+		vendor=hp
+		;;
+	hp9k3[2-9][0-9])
+		cpu=m68k
+		vendor=hp
+		;;
+	hp9k6[0-9][0-9] | hp6[0-9][0-9])
+		cpu=hppa1.0
+		vendor=hp
+		;;
+	hp9k7[0-79][0-9] | hp7[0-79][0-9])
+		cpu=hppa1.1
+		vendor=hp
+		;;
+	hp9k78[0-9] | hp78[0-9])
+		# FIXME: really hppa2.0-hp
+		cpu=hppa1.1
+		vendor=hp
+		;;
+	hp9k8[67]1 | hp8[67]1 | hp9k80[24] | hp80[24] | hp9k8[78]9 | hp8[78]9 | hp9k893 | hp893)
+		# FIXME: really hppa2.0-hp
+		cpu=hppa1.1
+		vendor=hp
+		;;
+	hp9k8[0-9][13679] | hp8[0-9][13679])
+		cpu=hppa1.1
+		vendor=hp
+		;;
+	hp9k8[0-9][0-9] | hp8[0-9][0-9])
+		cpu=hppa1.0
+		vendor=hp
+		;;
+	i*86v32)
+		cpu=`echo "$1" | sed -e 's/86.*/86/'`
+		vendor=pc
+		basic_os=sysv32
+		;;
+	i*86v4*)
+		cpu=`echo "$1" | sed -e 's/86.*/86/'`
+		vendor=pc
+		basic_os=sysv4
+		;;
+	i*86v)
+		cpu=`echo "$1" | sed -e 's/86.*/86/'`
+		vendor=pc
+		basic_os=sysv
+		;;
+	i*86sol2)
+		cpu=`echo "$1" | sed -e 's/86.*/86/'`
+		vendor=pc
+		basic_os=solaris2
+		;;
+	j90 | j90-cray)
+		cpu=j90
+		vendor=cray
+		basic_os=${basic_os:-unicos}
+		;;
+	iris | iris4d)
+		cpu=mips
+		vendor=sgi
+		case $basic_os in
+		    irix*)
+			;;
+		    *)
+			basic_os=irix4
+			;;
+		esac
+		;;
+	miniframe)
+		cpu=m68000
+		vendor=convergent
+		;;
+	*mint | mint[0-9]* | *MiNT | *MiNT[0-9]*)
+		cpu=m68k
+		vendor=atari
+		basic_os=mint
+		;;
+	news-3600 | risc-news)
+		cpu=mips
+		vendor=sony
+		basic_os=newsos
+		;;
+	next | m*-next)
+		cpu=m68k
+		vendor=next
+		;;
+	np1)
+		cpu=np1
+		vendor=gould
+		;;
+	op50n-* | op60c-*)
+		cpu=hppa1.1
+		vendor=oki
+		basic_os=proelf
+		;;
+	pa-hitachi)
+		cpu=hppa1.1
+		vendor=hitachi
+		basic_os=hiuxwe2
+		;;
+	pbd)
+		cpu=sparc
+		vendor=tti
+		;;
+	pbb)
+		cpu=m68k
+		vendor=tti
+		;;
+	pc532)
+		cpu=ns32k
+		vendor=pc532
+		;;
+	pn)
+		cpu=pn
+		vendor=gould
+		;;
+	power)
+		cpu=power
+		vendor=ibm
+		;;
+	ps2)
+		cpu=i386
+		vendor=ibm
+		;;
+	rm[46]00)
+		cpu=mips
+		vendor=siemens
+		;;
+	rtpc | rtpc-*)
+		cpu=romp
+		vendor=ibm
+		;;
+	sde)
+		cpu=mipsisa32
+		vendor=sde
+		basic_os=${basic_os:-elf}
+		;;
+	simso-wrs)
+		cpu=sparclite
+		vendor=wrs
+		basic_os=vxworks
+		;;
+	tower | tower-32)
+		cpu=m68k
+		vendor=ncr
+		;;
+	vpp*|vx|vx-*)
+		cpu=f301
+		vendor=fujitsu
+		;;
+	w65)
+		cpu=w65
+		vendor=wdc
+		;;
+	w89k-*)
+		cpu=hppa1.1
+		vendor=winbond
+		basic_os=proelf
+		;;
+	none)
+		cpu=none
+		vendor=none
+		;;
+	leon|leon[3-9])
+		cpu=sparc
+		vendor=$basic_machine
+		;;
+	leon-*|leon[3-9]-*)
+		cpu=sparc
+		vendor=`echo "$basic_machine" | sed 's/-.*//'`
+		;;
+
+	*-*)
+		saved_IFS=$IFS
+		IFS="-" read cpu vendor <<EOF
+$basic_machine
+EOF
+		IFS=$saved_IFS
+		;;
+	# We use 'pc' rather than 'unknown'
+	# because (1) that's what they normally are, and
+	# (2) the word "unknown" tends to confuse beginning users.
+	i*86 | x86_64)
+		cpu=$basic_machine
+		vendor=pc
+		;;
+	# These rules are duplicated from below for sake of the special case above;
+	# i.e. things that normalized to x86 arches should also default to "pc"
+	pc98)
+		cpu=i386
+		vendor=pc
+		;;
+	x64 | amd64)
+		cpu=x86_64
+		vendor=pc
+		;;
+	# Recognize the basic CPU types without company name.
+	*)
+		cpu=$basic_machine
+		vendor=unknown
+		;;
+esac
+
+unset -v basic_machine
+
+# Decode basic machines in the full and proper CPU-Company form.
+case $cpu-$vendor in
+	# Here we handle the default manufacturer of certain CPU types in canonical form.
+	# It is in some cases the only manufacturer, in others, it is the most popular.
+	c[12]-convex | c[12]-unknown | c3[248]-convex | c3[248]-unknown)
+		vendor=convex
+		basic_os=${basic_os:-bsd}
+		;;
+	craynv-unknown)
+		vendor=cray
+		basic_os=${basic_os:-unicosmp}
+		;;
+	c90-unknown | c90-cray)
+		vendor=cray
+		basic_os=${basic_os:-unicos}
+		;;
+	fx80-unknown)
+		vendor=alliant
+		;;
+	romp-unknown)
+		vendor=ibm
+		;;
+	mmix-unknown)
+		vendor=knuth
+		;;
+	microblaze-unknown | microblazeel-unknown)
+		vendor=xilinx
+		;;
+	rs6000-unknown)
+		vendor=ibm
+		;;
+	vax-unknown)
+		vendor=dec
+		;;
+	pdp11-unknown)
+		vendor=dec
+		;;
+	we32k-unknown)
+		vendor=att
+		;;
+	cydra-unknown)
+		vendor=cydrome
+		;;
+	i370-ibm*)
+		vendor=ibm
+		;;
+	orion-unknown)
+		vendor=highlevel
+		;;
+	xps-unknown | xps100-unknown)
+		cpu=xps100
+		vendor=honeywell
+		;;
+
+	# Here we normalize CPU types with a missing or matching vendor
+	armh-unknown | armh-alt)
+		cpu=armv7l
+		vendor=alt
+		basic_os=${basic_os:-linux-gnueabihf}
+		;;
+
+	# Normalized CPU+vendor pairs that imply an OS, if not otherwise specified
+	m68k-isi)
+		basic_os=${basic_os:-sysv}
+		;;
+	m68k-sony)
+		basic_os=${basic_os:-newsos}
+		;;
+	m68k-tektronix)
+		basic_os=${basic_os:-bsd}
+		;;
+	m88k-harris)
+		basic_os=${basic_os:-sysv3}
+		;;
+	i386-bull | m68k-bull)
+		basic_os=${basic_os:-sysv3}
+		;;
+	rs6000-bull)
+		basic_os=${basic_os:-bosx}
+		;;
+	mips-sni)
+		basic_os=${basic_os:-sysv4}
+		;;
+
+	# Here we normalize CPU types irrespective of the vendor
+	amd64-*)
+		cpu=x86_64
+		;;
+	blackfin-*)
+		cpu=bfin
+		basic_os=${basic_os:-linux}
+		;;
+	c54x-*)
+		cpu=tic54x
+		;;
+	c55x-*)
+		cpu=tic55x
+		;;
+	c6x-*)
+		cpu=tic6x
+		;;
+	e500v[12]-*)
+		cpu=powerpc
+		basic_os=${basic_os}"spe"
+		;;
+	mips3*-*)
+		cpu=mips64
+		;;
+	ms1-*)
+		cpu=mt
+		;;
+	m68knommu-*)
+		cpu=m68k
+		basic_os=${basic_os:-linux}
+		;;
+	m9s12z-* | m68hcs12z-* | hcs12z-* | s12z-*)
+		cpu=s12z
+		;;
+	openrisc-*)
+		cpu=or32
+		;;
+	parisc-*)
+		cpu=hppa
+		basic_os=${basic_os:-linux}
+		;;
+	pentium-* | p5-* | k5-* | k6-* | nexgen-* | viac3-*)
+		cpu=i586
+		;;
+	pentiumpro-* | p6-* | 6x86-* | athlon-* | athlon_*-*)
+		cpu=i686
+		;;
+	pentiumii-* | pentium2-* | pentiumiii-* | pentium3-*)
+		cpu=i686
+		;;
+	pentium4-*)
+		cpu=i786
+		;;
+	ppc-* | ppcbe-*)
+		cpu=powerpc
+		;;
+	ppcle-* | powerpclittle-*)
+		cpu=powerpcle
+		;;
+	ppc64-*)
+		cpu=powerpc64
+		;;
+	ppc64le-* | powerpc64little-*)
+		cpu=powerpc64le
+		;;
+	sb1-*)
+		cpu=mipsisa64sb1
+		;;
+	sb1el-*)
+		cpu=mipsisa64sb1el
+		;;
+	sh5e[lb]-*)
+		cpu=`echo "$cpu" | sed 's/^\(sh.\)e\(.\)$/\1\2e/'`
+		;;
+	spur-*)
+		cpu=spur
+		;;
+	strongarm-* | thumb-*)
+		cpu=arm
+		;;
+	tx39-*)
+		cpu=mipstx39
+		;;
+	tx39el-*)
+		cpu=mipstx39el
+		;;
+	xscale-* | xscalee[bl]-*)
+		cpu=`echo "$cpu" | sed 's/^xscale/arm/'`
+		;;
+	arm64-* | aarch64le-*)
+		cpu=aarch64
+		;;
+
+	# Recognize the canonical CPU Types that limit and/or modify the
+	# company names they are paired with.
+	cr16-*)
+		basic_os=${basic_os:-elf}
+		;;
+	crisv32-* | etraxfs*-*)
+		cpu=crisv32
+		vendor=axis
+		;;
+	cris-* | etrax*-*)
+		cpu=cris
+		vendor=axis
+		;;
+	crx-*)
+		basic_os=${basic_os:-elf}
+		;;
+	neo-tandem)
+		cpu=neo
+		vendor=tandem
+		;;
+	nse-tandem)
+		cpu=nse
+		vendor=tandem
+		;;
+	nsr-tandem)
+		cpu=nsr
+		vendor=tandem
+		;;
+	nsv-tandem)
+		cpu=nsv
+		vendor=tandem
+		;;
+	nsx-tandem)
+		cpu=nsx
+		vendor=tandem
+		;;
+	mipsallegrexel-sony)
+		cpu=mipsallegrexel
+		vendor=sony
+		;;
+	tile*-*)
+		basic_os=${basic_os:-linux-gnu}
+		;;
+
+	*)
+		# Recognize the canonical CPU types that are allowed with any
+		# company name.
+		case $cpu in
+			  1750a \
+			| 580 \
+			| [cjt]90 \
+			| a29k \
+			| aarch64 \
+			| aarch64_be \
+			| aarch64c \
+			| abacus \
+			| alpha \
+			| alpha64 \
+			| alpha64ev56 \
+			| alpha64ev6[78] \
+			| alpha64ev[4-8] \
+			| alpha64pca5[67] \
+			| alphaev56 \
+			| alphaev6[78] \
+			| alphaev[4-8] \
+			| alphapca5[67] \
+			| am33_2.0 \
+			| amdgcn \
+			| arc \
+			| arc32 \
+			| arc64 \
+			| arceb \
+			| arm \
+			| arm64e \
+			| arm64ec \
+			| arm[lb]e \
+			| arme[lb] \
+			| armv* \
+			| asmjs \
+			| avr \
+			| avr32 \
+			| ba \
+			| be32 \
+			| be64 \
+			| bfin \
+			| bpf \
+			| bs2000 \
+			| c30 \
+			| c4x \
+			| c8051 \
+			| c[123]* \
+			| clipper \
+			| craynv \
+			| csky \
+			| cydra \
+			| d10v \
+			| d30v \
+			| dlx \
+			| dsp16xx \
+			| e2k \
+			| elxsi \
+			| epiphany \
+			| f30[01] \
+			| f700 \
+			| fido \
+			| fr30 \
+			| frv \
+			| ft32 \
+			| fx80 \
+			| h8300 \
+			| h8500 \
+			| hexagon \
+			| hppa \
+			| hppa1.[01] \
+			| hppa2.0 \
+			| hppa2.0[nw] \
+			| hppa64 \
+			| i*86 \
+			| i370 \
+			| i860 \
+			| i960 \
+			| ia16 \
+			| ia64 \
+			| ip2k \
+			| iq2000 \
+			| javascript \
+			| k1om \
+			| kvx \
+			| le32 \
+			| le64 \
+			| lm32 \
+			| loongarch32 \
+			| loongarch64 \
+			| m32c \
+			| m32r \
+			| m32rle \
+			| m5200 \
+			| m68000 \
+			| m680[012346]0 \
+			| m6811 \
+			| m6812 \
+			| m68360 \
+			| m683?2 \
+			| m68hc11 \
+			| m68hc12 \
+			| m68hcs12x \
+			| m68k \
+			| m88110 \
+			| m88k \
+			| maxq \
+			| mb \
+			| mcore \
+			| mep \
+			| metag \
+			| microblaze \
+			| microblazeel \
+			| mips* \
+			| mmix \
+			| mn10200 \
+			| mn10300 \
+			| moxie \
+			| msp430 \
+			| mt \
+			| nanomips* \
+			| nds32 \
+			| nds32be \
+			| nds32le \
+			| nfp \
+			| nios \
+			| nios2 \
+			| nios2eb \
+			| nios2el \
+			| none \
+			| np1 \
+			| ns16k \
+			| ns32k \
+			| nvptx \
+			| open8 \
+			| or1k* \
+			| or32 \
+			| orion \
+			| pdp10 \
+			| pdp11 \
+			| picochip \
+			| pj \
+			| pjl \
+			| pn \
+			| power \
+			| powerpc \
+			| powerpc64 \
+			| powerpc64le \
+			| powerpcle \
+			| powerpcspe \
+			| pru \
+			| pyramid \
+			| riscv \
+			| riscv32 \
+			| riscv32be \
+			| riscv64 \
+			| riscv64be \
+			| rl78 \
+			| romp \
+			| rs6000 \
+			| rx \
+			| s390 \
+			| s390x \
+			| score \
+			| sh \
+			| sh64 \
+			| sh64le \
+			| sh[12345][lb]e \
+			| sh[1234] \
+			| sh[1234]e[lb] \
+			| sh[23]e \
+			| sh[23]ele \
+			| sh[24]a \
+			| sh[24]ae[lb] \
+			| sh[lb]e \
+			| she[lb] \
+			| shl \
+			| sparc \
+			| sparc64 \
+			| sparc64b \
+			| sparc64v \
+			| sparc86x \
+			| sparclet \
+			| sparclite \
+			| sparcv8 \
+			| sparcv9 \
+			| sparcv9b \
+			| sparcv9v \
+			| spu \
+			| sv1 \
+			| sx* \
+			| tahoe \
+			| thumbv7* \
+			| tic30 \
+			| tic4x \
+			| tic54x \
+			| tic55x \
+			| tic6x \
+			| tic80 \
+			| tron \
+			| ubicom32 \
+			| v70 \
+			| v810 \
+			| v850 \
+			| v850e \
+			| v850e1 \
+			| v850e2 \
+			| v850e2v3 \
+			| v850es \
+			| vax \
+			| vc4 \
+			| visium \
+			| w65 \
+			| wasm32 \
+			| wasm64 \
+			| we32k \
+			| x86 \
+			| x86_64 \
+			| xc16x \
+			| xgate \
+			| xps100 \
+			| xstormy16 \
+			| xtensa* \
+			| ymp \
+			| z80 \
+			| z8k)
+				;;
+
+			*)
+				echo "Invalid configuration '$1': machine '$cpu-$vendor' not recognized" 1>&2
+				exit 1
+				;;
+		esac
+		;;
+esac
+
+# Here we canonicalize certain aliases for manufacturers.
+case $vendor in
+	digital*)
+		vendor=dec
+		;;
+	commodore*)
+		vendor=cbm
+		;;
+	*)
+		;;
+esac
+
+# Decode manufacturer-specific aliases for certain operating systems.
+
+if test x"$basic_os" != x
+then
+
+# First recognize some ad-hoc cases, or perhaps split kernel-os, or else just
+# set os.
+obj=
+case $basic_os in
+	gnu/linux*)
+		kernel=linux
+		os=`echo "$basic_os" | sed -e 's|gnu/linux|gnu|'`
+		;;
+	os2-emx)
+		kernel=os2
+		os=`echo "$basic_os" | sed -e 's|os2-emx|emx|'`
+		;;
+	nto-qnx*)
+		kernel=nto
+		os=`echo "$basic_os" | sed -e 's|nto-qnx|qnx|'`
+		;;
+	*-*)
+		saved_IFS=$IFS
+		IFS="-" read kernel os <<EOF
+$basic_os
+EOF
+		IFS=$saved_IFS
+		;;
+	# Default OS when just kernel was specified
+	nto*)
+		kernel=nto
+		os=`echo "$basic_os" | sed -e 's|nto|qnx|'`
+		;;
+	linux*)
+		kernel=linux
+		os=`echo "$basic_os" | sed -e 's|linux|gnu|'`
+		;;
+	managarm*)
+		kernel=managarm
+		os=`echo "$basic_os" | sed -e 's|managarm|mlibc|'`
+		;;
+	*)
+		kernel=
+		os=$basic_os
+		;;
+esac
+
+# Now, normalize the OS (knowing we just have one component, it's not a kernel,
+# etc.)
+case $os in
+	# First match some system type aliases that might get confused
+	# with valid system types.
+	# solaris* is a basic system type, with this one exception.
+	auroraux)
+		os=auroraux
+		;;
+	bluegene*)
+		os=cnk
+		;;
+	solaris1 | solaris1.*)
+		os=`echo "$os" | sed -e 's|solaris1|sunos4|'`
+		;;
+	solaris)
+		os=solaris2
+		;;
+	unixware*)
+		os=sysv4.2uw
+		;;
+	# The marketing names for NeXT's operating systems were
+	# NeXTSTEP, NeXTSTEP 2, OpenSTEP 3, OpenSTEP 4.  'openstep' is
+	# mapped to 'openstep3', but 'openstep1' and 'openstep2' are
+	# mapped to 'nextstep' and 'nextstep2', consistent with the
+	# treatment of SunOS/Solaris.
+	ns | ns1 | nextstep | nextstep1 | openstep1)
+		os=nextstep
+		;;
+	ns2 | nextstep2 | openstep2)
+		os=nextstep2
+		;;
+	ns3 | nextstep3 | openstep | openstep3)
+		os=openstep3
+		;;
+	ns4 | nextstep4 | openstep4)
+		os=openstep4
+		;;
+	# es1800 is here to avoid being matched by es* (a different OS)
+	es1800*)
+		os=ose
+		;;
+	# Some version numbers need modification
+	chorusos*)
+		os=chorusos
+		;;
+	isc)
+		os=isc2.2
+		;;
+	sco6)
+		os=sco5v6
+		;;
+	sco5)
+		os=sco3.2v5
+		;;
+	sco4)
+		os=sco3.2v4
+		;;
+	sco3.2.[4-9]*)
+		os=`echo "$os" | sed -e 's/sco3.2./sco3.2v/'`
+		;;
+	sco*v* | scout)
+		# Don't match below
+		;;
+	sco*)
+		os=sco3.2v2
+		;;
+	psos*)
+		os=psos
+		;;
+	qnx*)
+		os=qnx
+		;;
+	hiux*)
+		os=hiuxwe2
+		;;
+	lynx*178)
+		os=lynxos178
+		;;
+	lynx*5)
+		os=lynxos5
+		;;
+	lynxos*)
+		# don't get caught up in next wildcard
+		;;
+	lynx*)
+		os=lynxos
+		;;
+	mac[0-9]*)
+		os=`echo "$os" | sed -e 's|mac|macos|'`
+		;;
+	opened*)
+		os=openedition
+		;;
+	os400*)
+		os=os400
+		;;
+	sunos5*)
+		os=`echo "$os" | sed -e 's|sunos5|solaris2|'`
+		;;
+	sunos6*)
+		os=`echo "$os" | sed -e 's|sunos6|solaris3|'`
+		;;
+	wince*)
+		os=wince
+		;;
+	utek*)
+		os=bsd
+		vendor=`echo "$vendor" | sed -e 's|^unknown$|tektronix|'`
+		;;
+	dynix*)
+		os=bsd
+		;;
+	acis*)
+		os=aos
+		;;
+	atheos*)
+		os=atheos
+		;;
+	syllable*)
+		os=syllable
+		;;
+	386bsd)
+		os=bsd
+		;;
+	ctix*)
+		os=sysv
+		vendor=`echo "$vendor" | sed -e 's|^unknown$|convergent|'`
+		;;
+	uts*)
+		os=sysv
+		;;
+	nova*)
+		kernel=rtmk
+		os=nova
+		;;
+	# Preserve the version number of sinix5.
+	sinix5.*)
+		os=`echo "$os" | sed -e 's|sinix|sysv|'`
+		vendor=`echo "$vendor" | sed -e 's|^unknown$|sni|'`
+		;;
+	sinix*)
+		os=sysv4
+		vendor=`echo "$vendor" | sed -e 's|^unknown$|sni|'`
+		;;
+	tpf*)
+		os=tpf
+		;;
+	triton*)
+		os=sysv3
+		;;
+	oss*)
+		os=sysv3
+		;;
+	svr4*)
+		os=sysv4
+		;;
+	svr3)
+		os=sysv3
+		;;
+	sysvr4)
+		os=sysv4
+		;;
+	ose*)
+		os=ose
+		;;
+	*mint | mint[0-9]* | *MiNT | MiNT[0-9]*)
+		os=mint
+		;;
+	dicos*)
+		os=dicos
+		;;
+	pikeos*)
+		# Until real need of OS specific support for
+		# particular features comes up, bare metal
+		# configurations are quite functional.
+		case $cpu in
+		    arm*)
+			os=eabi
+			;;
+		    *)
+			os=
+			obj=elf
+			;;
+		esac
+		;;
+	aout* | coff* | elf* | pe*)
+		# These are machine code file formats, not OSes
+		obj=$os
+		os=
+		;;
+	*)
+		# No normalization, but not necessarily accepted, that comes below.
+		;;
+esac
+
+else
+
+# Here we handle the default operating systems that come with various machines.
+# The value should be what the vendor currently ships out the door with their
+# machine or put another way, the most popular os provided with the machine.
+
+# Note that if you're going to try to match "-MANUFACTURER" here (say,
+# "-sun"), then you have to tell the case statement up towards the top
+# that MANUFACTURER isn't an operating system.  Otherwise, code above
+# will signal an error saying that MANUFACTURER isn't an operating
+# system, and we'll never get to this point.
+
+kernel=
+obj=
+case $cpu-$vendor in
+	score-*)
+		os=
+		obj=elf
+		;;
+	spu-*)
+		os=
+		obj=elf
+		;;
+	*-acorn)
+		os=riscix1.2
+		;;
+	arm*-rebel)
+		kernel=linux
+		os=gnu
+		;;
+	arm*-semi)
+		os=
+		obj=aout
+		;;
+	c4x-* | tic4x-*)
+		os=
+		obj=coff
+		;;
+	c8051-*)
+		os=
+		obj=elf
+		;;
+	clipper-intergraph)
+		os=clix
+		;;
+	hexagon-*)
+		os=
+		obj=elf
+		;;
+	tic54x-*)
+		os=
+		obj=coff
+		;;
+	tic55x-*)
+		os=
+		obj=coff
+		;;
+	tic6x-*)
+		os=
+		obj=coff
+		;;
+	# This must come before the *-dec entry.
+	pdp10-*)
+		os=tops20
+		;;
+	pdp11-*)
+		os=none
+		;;
+	*-dec | vax-*)
+		os=ultrix4.2
+		;;
+	m68*-apollo)
+		os=domain
+		;;
+	i386-sun)
+		os=sunos4.0.2
+		;;
+	m68000-sun)
+		os=sunos3
+		;;
+	m68*-cisco)
+		os=
+		obj=aout
+		;;
+	mep-*)
+		os=
+		obj=elf
+		;;
+	# The -sgi and -siemens entries must be before the mips- entry
+	# or we get the wrong os.
+	*-sgi)
+		os=irix
+		;;
+	*-siemens)
+		os=sysv4
+		;;
+	mips*-cisco)
+		os=
+		obj=elf
+		;;
+	mips*-*|nanomips*-*)
+		os=
+		obj=elf
+		;;
+	or32-*)
+		os=
+		obj=coff
+		;;
+	# This must be before the sparc-* entry or we get the wrong os.
+	*-tti)
+		os=sysv3
+		;;
+	sparc-* | *-sun)
+		os=sunos4.1.1
+		;;
+	pru-*)
+		os=
+		obj=elf
+		;;
+	*-be)
+		os=beos
+		;;
+	*-ibm)
+		os=aix
+		;;
+	*-knuth)
+		os=mmixware
+		;;
+	*-wec)
+		os=proelf
+		;;
+	*-winbond)
+		os=proelf
+		;;
+	*-oki)
+		os=proelf
+		;;
+	*-hp)
+		os=hpux
+		;;
+	*-hitachi)
+		os=hiuxwe2
+		;;
+	i860-* | *-att | *-ncr | *-altos | *-motorola | *-convergent)
+		os=sysv
+		;;
+	*-cbm)
+		os=amigaos
+		;;
+	*-dg)
+		os=dgux
+		;;
+	*-dolphin)
+		os=sysv3
+		;;
+	m68k-ccur)
+		os=rtu
+		;;
+	m88k-omron*)
+		os=luna
+		;;
+	*-next)
+		os=nextstep
+		;;
+	*-sequent)
+		os=ptx
+		;;
+	*-crds)
+		os=unos
+		;;
+	*-ns)
+		os=genix
+		;;
+	i370-*)
+		os=mvs
+		;;
+	*-gould)
+		os=sysv
+		;;
+	*-highlevel)
+		os=bsd
+		;;
+	*-encore)
+		os=bsd
+		;;
+	*-masscomp)
+		os=rtu
+		;;
+	f30[01]-fujitsu | f700-fujitsu)
+		os=uxpv
+		;;
+	*-rom68k)
+		os=
+		obj=coff
+		;;
+	*-*bug)
+		os=
+		obj=coff
+		;;
+	*-apple)
+		os=macos
+		;;
+	*-atari*)
+		os=mint
+		;;
+	*-wrs)
+		os=vxworks
+		;;
+	*)
+		os=none
+		;;
+esac
+
+fi
+
+# Now, validate our (potentially fixed-up) individual pieces (OS, OBJ).
+
+case $os in
+	# Sometimes we do "kernel-libc", so those need to count as OSes.
+	llvm* | musl* | newlib* | relibc* | uclibc*)
+		;;
+	# Likewise for "kernel-abi"
+	eabi* | gnueabi*)
+		;;
+	# VxWorks passes extra cpu info in the 4th filed.
+	simlinux | simwindows | spe)
+		;;
+	# See `case $cpu-$os` validation below
+	ghcjs)
+		;;
+	# Now accept the basic system types.
+	# Each alternative MUST end in a * to match a version number.
+	  abug \
+	| aix* \
+	| amdhsa* \
+	| amigados* \
+	| amigaos* \
+	| android* \
+	| aof* \
+	| aos* \
+	| aros* \
+	| atheos* \
+	| auroraux* \
+	| aux* \
+	| beos* \
+	| bitrig* \
+	| bme* \
+	| bosx* \
+	| bsd* \
+	| cegcc* \
+	| chorusos* \
+	| chorusrdb* \
+	| clix* \
+	| cloudabi* \
+	| cnk* \
+	| conix* \
+	| cos* \
+	| cxux* \
+	| cygwin* \
+	| darwin* \
+	| dgux* \
+	| dicos* \
+	| dnix* \
+	| domain* \
+	| dragonfly* \
+	| drops* \
+	| ebmon* \
+	| ecoff* \
+	| ekkobsd* \
+	| emscripten* \
+	| emx* \
+	| es* \
+	| fiwix* \
+	| freebsd* \
+	| fuchsia* \
+	| genix* \
+	| genode* \
+	| glidix* \
+	| gnu* \
+	| go32* \
+	| haiku* \
+	| hcos* \
+	| hiux* \
+	| hms* \
+	| hpux* \
+	| ieee* \
+	| interix* \
+	| ios* \
+	| iris* \
+	| irix* \
+	| ironclad* \
+	| isc* \
+	| its* \
+	| l4re* \
+	| libertybsd* \
+	| lites* \
+	| lnews* \
+	| luna* \
+	| lynxos* \
+	| mach* \
+	| macos* \
+	| magic* \
+	| mbr* \
+	| midipix* \
+	| midnightbsd* \
+	| mingw32* \
+	| mingw64* \
+	| minix* \
+	| mint* \
+	| mirbsd* \
+	| mks* \
+	| mlibc* \
+	| mmixware* \
+	| mon960* \
+	| morphos* \
+	| moss* \
+	| moxiebox* \
+	| mpeix* \
+	| mpw* \
+	| msdos* \
+	| msys* \
+	| mvs* \
+	| nacl* \
+	| netbsd* \
+	| netware* \
+	| newsos* \
+	| nextstep* \
+	| nindy* \
+	| nonstopux* \
+	| nova* \
+	| nsk* \
+	| nucleus* \
+	| nx6 \
+	| nx7 \
+	| oabi* \
+	| ohos* \
+	| onefs* \
+	| openbsd* \
+	| openedition* \
+	| openstep* \
+	| os108* \
+	| os2* \
+	| os400* \
+	| os68k* \
+	| os9* \
+	| ose* \
+	| osf* \
+	| oskit* \
+	| osx* \
+	| palmos* \
+	| phoenix* \
+	| plan9* \
+	| powermax* \
+	| powerunix* \
+	| proelf* \
+	| psos* \
+	| psp* \
+	| ptx* \
+	| pw32* \
+	| qnx* \
+	| rdos* \
+	| redox* \
+	| rhapsody* \
+	| riscix* \
+	| riscos* \
+	| rtems* \
+	| rtmk* \
+	| rtu* \
+	| scout* \
+	| secbsd* \
+	| sei* \
+	| serenity* \
+	| sim* \
+	| skyos* \
+	| solaris* \
+	| solidbsd* \
+	| sortix* \
+	| storm-chaos* \
+	| sunos \
+	| sunos[34]* \
+	| superux* \
+	| syllable* \
+	| sym* \
+	| sysv* \
+	| tenex* \
+	| tirtos* \
+	| toppers* \
+	| tops10* \
+	| tops20* \
+	| tpf* \
+	| tvos* \
+	| twizzler* \
+	| uclinux* \
+	| udi* \
+	| udk* \
+	| ultrix* \
+	| unicos* \
+	| uniplus* \
+	| unleashed* \
+	| unos* \
+	| uwin* \
+	| uxpv* \
+	| v88r* \
+	|*vms* \
+	| vos* \
+	| vsta* \
+	| vxsim* \
+	| vxworks* \
+	| wasi* \
+	| watchos* \
+	| wince* \
+	| windiss* \
+	| windows* \
+	| winnt* \
+	| xenix* \
+	| xray* \
+	| zephyr* \
+	| zvmoe* )
+		;;
+	# This one is extra strict with allowed versions
+	sco3.2v2 | sco3.2v[4-9]* | sco5v6*)
+		# Don't forget version if it is 3.2v4 or newer.
+		;;
+	# This refers to builds using the UEFI calling convention
+	# (which depends on the architecture) and PE file format.
+	# Note that this is both a different calling convention and
+	# different file format than that of GNU-EFI
+	# (x86_64-w64-mingw32).
+	uefi)
+		;;
+	none)
+		;;
+	kernel* | msvc* )
+		# Restricted further below
+		;;
+	'')
+		if test x"$obj" = x
+		then
+			echo "Invalid configuration '$1': Blank OS only allowed with explicit machine code file format" 1>&2
+		fi
+		;;
+	*)
+		echo "Invalid configuration '$1': OS '$os' not recognized" 1>&2
+		exit 1
+		;;
+esac
+
+case $obj in
+	aout* | coff* | elf* | pe*)
+		;;
+	'')
+		# empty is fine
+		;;
+	*)
+		echo "Invalid configuration '$1': Machine code format '$obj' not recognized" 1>&2
+		exit 1
+		;;
+esac
+
+# Here we handle the constraint that a (synthetic) cpu and os are
+# valid only in combination with each other and nowhere else.
+case $cpu-$os in
+	# The "javascript-unknown-ghcjs" triple is used by GHC; we
+	# accept it here in order to tolerate that, but reject any
+	# variations.
+	javascript-ghcjs)
+		;;
+	javascript-* | *-ghcjs)
+		echo "Invalid configuration '$1': cpu '$cpu' is not valid with os '$os$obj'" 1>&2
+		exit 1
+		;;
+esac
+
+# As a final step for OS-related things, validate the OS-kernel combination
+# (given a valid OS), if there is a kernel.
+case $kernel-$os-$obj in
+	linux-gnu*- | linux-android*- | linux-dietlibc*- | linux-llvm*- \
+		    | linux-mlibc*- | linux-musl*- | linux-newlib*- \
+		    | linux-relibc*- | linux-uclibc*- | linux-ohos*- )
+		;;
+	uclinux-uclibc*- | uclinux-gnu*- )
+		;;
+	managarm-mlibc*- | managarm-kernel*- )
+		;;
+	windows*-msvc*-)
+		;;
+	-dietlibc*- | -llvm*- | -mlibc*- | -musl*- | -newlib*- | -relibc*- \
+		    | -uclibc*- )
+		# These are just libc implementations, not actual OSes, and thus
+		# require a kernel.
+		echo "Invalid configuration '$1': libc '$os' needs explicit kernel." 1>&2
+		exit 1
+		;;
+	-kernel*- )
+		echo "Invalid configuration '$1': '$os' needs explicit kernel." 1>&2
+		exit 1
+		;;
+	*-kernel*- )
+		echo "Invalid configuration '$1': '$kernel' does not support '$os'." 1>&2
+		exit 1
+		;;
+	*-msvc*- )
+		echo "Invalid configuration '$1': '$os' needs 'windows'." 1>&2
+		exit 1
+		;;
+	kfreebsd*-gnu*- | knetbsd*-gnu*- | netbsd*-gnu*- | kopensolaris*-gnu*-)
+		;;
+	vxworks-simlinux- | vxworks-simwindows- | vxworks-spe-)
+		;;
+	nto-qnx*-)
+		;;
+	os2-emx-)
+		;;
+	rtmk-nova-)
+		;;
+	*-eabi*- | *-gnueabi*-)
+		;;
+	none--*)
+		# None (no kernel, i.e. freestanding / bare metal),
+		# can be paired with an machine code file format
+		;;
+	-*-)
+		# Blank kernel with real OS is always fine.
+		;;
+	--*)
+		# Blank kernel and OS with real machine code file format is always fine.
+		;;
+	*-*-*)
+		echo "Invalid configuration '$1': Kernel '$kernel' not known to work with OS '$os'." 1>&2
+		exit 1
+		;;
+esac
+
+# Here we handle the case where we know the os, and the CPU type, but not the
+# manufacturer.  We pick the logical manufacturer.
+case $vendor in
+	unknown)
+		case $cpu-$os in
+			*-riscix*)
+				vendor=acorn
+				;;
+			*-sunos* | *-solaris*)
+				vendor=sun
+				;;
+			*-cnk* | *-aix*)
+				vendor=ibm
+				;;
+			*-beos*)
+				vendor=be
+				;;
+			*-hpux*)
+				vendor=hp
+				;;
+			*-mpeix*)
+				vendor=hp
+				;;
+			*-hiux*)
+				vendor=hitachi
+				;;
+			*-unos*)
+				vendor=crds
+				;;
+			*-dgux*)
+				vendor=dg
+				;;
+			*-luna*)
+				vendor=omron
+				;;
+			*-genix*)
+				vendor=ns
+				;;
+			*-clix*)
+				vendor=intergraph
+				;;
+			*-mvs* | *-opened*)
+				vendor=ibm
+				;;
+			*-os400*)
+				vendor=ibm
+				;;
+			s390-* | s390x-*)
+				vendor=ibm
+				;;
+			*-ptx*)
+				vendor=sequent
+				;;
+			*-tpf*)
+				vendor=ibm
+				;;
+			*-vxsim* | *-vxworks* | *-windiss*)
+				vendor=wrs
+				;;
+			*-aux*)
+				vendor=apple
+				;;
+			*-hms*)
+				vendor=hitachi
+				;;
+			*-mpw* | *-macos*)
+				vendor=apple
+				;;
+			*-*mint | *-mint[0-9]* | *-*MiNT | *-MiNT[0-9]*)
+				vendor=atari
+				;;
+			*-vos*)
+				vendor=stratus
+				;;
+		esac
+		;;
+esac
+
+echo "$cpu-$vendor${kernel:+-$kernel}${os:+-$os}${obj:+-$obj}"
+exit
+
+# Local variables:
+# eval: (add-hook 'before-save-hook 'time-stamp)
+# time-stamp-start: "timestamp='"
+# time-stamp-format: "%:y-%02m-%02d"
+# time-stamp-end: "'"
+# End:
diff --git a/scripts/config/.gitignore b/scripts/config/.gitignore
new file mode 100644
index 0000000..1cef9de
--- /dev/null
+++ b/scripts/config/.gitignore
@@ -0,0 +1,14 @@
+# SPDX-License-Identifier: GPL-2.0-only
+/conf
+/[gmnq]conf
+/[gmnq]conf-bin
+/[gmnq]conf-cflags
+/[gmnq]conf-libs
+/qconf-moc.cc
+
+#
+# Added by openwrt
+#
+mconf_check
+# The next line should be removed after 23.05 is EOL
+*conf-cfg
diff --git a/scripts/config/Kbuild.include b/scripts/config/Kbuild.include
new file mode 100644
index 0000000..7778cc9
--- /dev/null
+++ b/scripts/config/Kbuild.include
@@ -0,0 +1,279 @@
+# 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-dir = $(if $(filter /%,$(src)),$(src),$(srctree)/$(src))
+kbuild-file = $(or $(wildcard $(kbuild-dir)/Kbuild),$(kbuild-dir)/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 3.% 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.dtbinst obj=
+# Usage:
+# $(Q)$(MAKE) $(dtbinst)=dir
+dtbinst := -f $(srctree)/scripts/Makefile.dtbinst 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);                                                              \
+	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
diff --git a/scripts/config/Makefile b/scripts/config/Makefile
new file mode 100644
index 0000000..5e7dd95
--- /dev/null
+++ b/scripts/config/Makefile
@@ -0,0 +1,125 @@
+# SPDX-License-Identifier: GPL-2.0-only
+# ===========================================================================
+# OpenWrt configuration targets
+
+.PHONY: clean all
+all: conf mconf
+clean:
+	rm -f $(clean-files) $(hostprogs)
+
+clean-files	:= *.o lxdialog/*.o *.moc qconf-moc.cc \
+		   *conf-cfg # <- This should be removed after 23.05 is EOL
+
+# ===========================================================================
+# Variables needed by the upstream Makefile
+
+export HOSTPKG_CONFIG=pkg-config
+CONFIG_SHELL:=$(SHELL)
+src:=$(CURDIR)
+obj:=.
+Q:=$(if $V,,@)
+quiet:=$(if $V,,_silent)
+include Kbuild.include
+
+### Stripped down upstream Makefile follows:
+# ===========================================================================
+# 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 $(srctree)/$(src)
+HOSTCFLAGS_parser.tab.o	:= -I $(srctree)/$(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 $(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) $(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
+
+# 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
+
+# ===========================================================================
+# OpenWrt rules and final adjustments that need to be made after reading the
+# full upstream Makefile
+
+FORCE:
+
+ifdef BUILD_SHIPPED_FILES
+shipped-files := lexer.lex.c parser.tab.c parser.tab.h
+clean-files += $(shipped-files)
+
+.SECONDARY: $(shipped-files)
+
+%.tab.c %.tab.h: %.y
+	bison -l -d -b $* $<
+
+%.lex.c: %.l
+	flex -L -o$@ $<
+endif
+
+define link_rule
+$(1): LDLIBS+=$$(HOSTLDLIBS_$(1))
+$(1): $($(1)-objs) $$($(1)-cxxobjs)
+$(if $($(1)-cxxobjs),	$(CXX) $$(LDFLAGS) -o $$@ $$^ $$(LDLIBS))
+all-objs += $($(1)-objs)
+all-cxxobjs += $($(1)-cxxobjs)
+endef
+
+all-objs:=
+all-cxxobjs:=
+$(foreach f,$(hostprogs),$(eval $(call link_rule,$f)))
+
+
+$(foreach f,$(sort $(all-objs)), \
+  $(eval $f: CFLAGS+=$$(HOSTCFLAGS_$f)))
+
+$(foreach f,$(sort $(all-cxxobjs)), \
+  $(eval $f: CXXFLAGS+=$$(HOSTCXXFLAGS_$f)))
diff --git a/scripts/config/README b/scripts/config/README
new file mode 100644
index 0000000..e1ebf89
--- /dev/null
+++ b/scripts/config/README
@@ -0,0 +1,27 @@
+These files were taken from the Linux Kernel Configuration System v6.6.16, 
+at commit eb3e299184cc4f40d4bd84fda269b3a20ddcff80 (Feb 5, 2024),  and modified
+for the OpenWrt Buildroot:
+ - Removed gconf, tests and kernel configuration targets.
+ - Adjusted the Makefile to compile outside the kernel.
+ - Always use default file when running make all{no,mod,yes}config.
+ - Added a 'reset' command to reset config when the target changes.
+ - Allow config reading from & writing to a different file.
+ - Allow 'source' command to use globs to include multiple files.
+ - Don't warn when selecting a symbol with unmet direct dependencies.
+ - Don't write auto.conf and other files under include/ directory.
+ - Reverted a commit to allow use of '/' & '.' in unquoted config symbols.
+   There are too many of those in OpenWrt right now.
+ - Reverted a commit that was issuing a warning when there were more than
+   one help text.  This is used in a few packages to use different texts
+   for the menuconfig help, and the ipkg package description.
+ - Reverted an upstream change that avoids writing symbols that are not
+   visible to .config, which breaks OpenWrt busybox's '.config' generation
+   logic.
+ - Treat recursive dependency as a warning only; add a --fatalrecursive
+   option to conf to treat recursive deps as a fatal error.
+ - Use pre-built *.lex.c *.tab.[ch] files by default, to avoid depending on
+   flex & bison.  Rebuild/remove these files only if running make with
+   BUILD_SHIPPED_FILES defined
+
+For a full list of changes, see the repository at:
+https://github.com/cotequeiroz/linux/commits/openwrt-v6.6.16/scripts/kconfig
diff --git a/scripts/config/conf.c b/scripts/config/conf.c
new file mode 100644
index 0000000..1bd6f4f
--- /dev/null
+++ b/scripts/config/conf.c
@@ -0,0 +1,909 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * 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 "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,
+	fatalrecursive,
+};
+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);
+}
+
+static bool randomize_choice_values(struct symbol *csym)
+{
+	struct property *prop;
+	struct symbol *sym;
+	struct expr *e;
+	int cnt, def;
+
+	/*
+	 * If choice is mod then we may have more items selected
+	 * and if no then no-one.
+	 * In both cases stop.
+	 */
+	if (csym->curr.tri != yes)
+		return false;
+
+	prop = sym_get_choice_prop(csym);
+
+	/* count entries in choice block */
+	cnt = 0;
+	expr_list_for_each_sym(prop->expr, e, sym)
+		cnt++;
+
+	/*
+	 * find a random value and set it to yes,
+	 * set the rest to no so we have only one set
+	 */
+	def = rand() % cnt;
+
+	cnt = 0;
+	expr_list_for_each_sym(prop->expr, e, sym) {
+		if (def == cnt++) {
+			sym->def[S_DEF_USER].tri = yes;
+			csym->def[S_DEF_USER].val = sym;
+		} else {
+			sym->def[S_DEF_USER].tri = no;
+		}
+		sym->flags |= SYMBOL_DEF_USER;
+		/* clear VALID to get value calculated */
+		sym->flags &= ~SYMBOL_VALID;
+	}
+	csym->flags |= SYMBOL_DEF_USER;
+	/* clear VALID to get value calculated */
+	csym->flags &= ~SYMBOL_VALID;
+
+	return true;
+}
+
+enum conf_def_mode {
+	def_default,
+	def_yes,
+	def_mod,
+	def_no,
+	def_random
+};
+
+static bool conf_set_all_new_symbols(enum conf_def_mode mode)
+{
+	struct symbol *sym, *csym;
+	int i, 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 */
+	bool has_changed = false;
+
+	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);
+		}
+	}
+
+	sym_clear_all_valid();
+
+	for_all_symbols(i, sym) {
+		if (sym_has_value(sym) || sym->flags & SYMBOL_VALID)
+			continue;
+		switch (sym_get_type(sym)) {
+		case S_BOOLEAN:
+		case S_TRISTATE:
+			has_changed = true;
+			switch (mode) {
+			case def_yes:
+				sym->def[S_DEF_USER].tri = yes;
+				break;
+			case def_mod:
+				sym->def[S_DEF_USER].tri = mod;
+				break;
+			case def_no:
+				sym->def[S_DEF_USER].tri = no;
+				break;
+			case def_random:
+				sym->def[S_DEF_USER].tri = no;
+				cnt = rand() % 100;
+				if (sym->type == S_TRISTATE) {
+					if (cnt < pty)
+						sym->def[S_DEF_USER].tri = yes;
+					else if (cnt < pty + ptm)
+						sym->def[S_DEF_USER].tri = mod;
+				} else if (cnt < pby)
+					sym->def[S_DEF_USER].tri = yes;
+				break;
+			default:
+				continue;
+			}
+			if (!(sym_is_choice(sym) && mode == def_random))
+				sym->flags |= SYMBOL_DEF_USER;
+			break;
+		default:
+			break;
+		}
+
+	}
+
+	/*
+	 * We have different type of choice blocks.
+	 * If curr.tri equals to mod then we can select several
+	 * choice symbols in one block.
+	 * In this case we do nothing.
+	 * If curr.tri equals yes then only one symbol can be
+	 * selected in a choice block and we set it to yes,
+	 * and the rest to no.
+	 */
+	if (mode != def_random) {
+		for_all_symbols(i, csym) {
+			if ((sym_is_choice(csym) && !sym_has_value(csym)) ||
+			    sym_is_choice_value(csym))
+				csym->flags |= SYMBOL_NEED_SET_CHOICE_VALUES;
+		}
+	}
+
+	for_all_symbols(i, csym) {
+		if (sym_has_value(csym) || !sym_is_choice(csym))
+			continue;
+
+		sym_calc_value(csym);
+		if (mode == def_random)
+			has_changed |= randomize_choice_values(csym);
+		else {
+			set_all_choice_values(csym);
+			has_changed = true;
+		}
+	}
+
+	return has_changed;
+}
+
+static void conf_rewrite_tristates(tristate old_val, tristate new_val)
+{
+	struct symbol *sym;
+	int i;
+
+	for_all_symbols(i, 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 int conf_choice(struct menu *menu)
+{
+	struct symbol *sym, *def_sym;
+	struct menu *child;
+	bool is_new;
+
+	sym = menu->sym;
+	is_new = !sym_has_value(sym);
+	if (sym_is_changeable(sym)) {
+		conf_sym(menu);
+		sym_calc_value(sym);
+		switch (sym_get_tristate_value(sym)) {
+		case no:
+			return 1;
+		case mod:
+			return 0;
+		case yes:
+			break;
+		}
+	} else {
+		switch (sym_get_tristate_value(sym)) {
+		case no:
+			return 1;
+		case mod:
+			printf("%*s%s\n", indent - 1, "", menu_get_prompt(menu));
+			return 0;
+		case yes:
+			break;
+		}
+	}
+
+	while (1) {
+		int cnt, def;
+
+		printf("%*s%s\n", indent - 1, "", menu_get_prompt(menu));
+		def_sym = sym_get_choice_value(sym);
+		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", cnt, menu_get_prompt(child));
+			if (child->sym->name)
+				printf(" (%s)", child->sym->name);
+			if (!sym_has_value(child->sym))
+				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;
+		}
+		sym_set_tristate_value(child->sym, yes);
+		for (child = child->list; child; child = child->next) {
+			indent += 2;
+			conf(child);
+			indent -= 2;
+		}
+		return 1;
+	}
+}
+
+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);
+		if (sym->curr.tri != mod)
+			return;
+		goto conf_childs;
+	}
+
+	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) ||
+	     (sym_is_choice(sym) && sym_get_tristate_value(sym) == yes))) {
+
+		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},
+	{"fatalrecursive",no_argument,       &input_mode_opt, fatalrecursive},
+	{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("  -r <file>               Read <file> as input.\n");
+	printf("  -s, --silent            Do not print log.\n");
+	printf("  -w <file>               Write config to <file>.\n");
+	printf("  --fatalrecursive        Treat recursive dependency as error.\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");
+}
+
+int main(int ac, char **av)
+{
+	const char *progname = av[0];
+	int opt;
+	const char *name, *defconfig_file = NULL /* gcc uninit */;
+	const char *input_file = NULL, *output_file = NULL;
+	int no_conf_write = 0;
+
+	tty_stdio = isatty(0) && isatty(1);
+
+	while ((opt = getopt_long(ac, av, "hr:w:s", long_opts, NULL)) != -1) {
+		switch (opt) {
+		case 'h':
+			conf_usage(progname);
+			exit(1);
+			break;
+		case 'r':
+			input_file = optarg;
+			break;
+		case 's':
+			conf_set_message_callback(NULL);
+			break;
+		case 'w':
+			output_file = optarg;
+			break;
+		case 0:
+			switch (input_mode_opt) {
+			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;
+			case fatalrecursive:
+				recursive_is_error = 1;
+				continue;
+			default:
+				break;
+			}
+			input_mode = input_mode_opt;
+		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:
+	case allnoconfig:
+	case allyesconfig:
+	case allmodconfig:
+	case alldefconfig:
+	case randconfig:
+		conf_read(input_file);
+		break;
+	default:
+		break;
+	}
+
+	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:
+		/* Really nothing to do in this loop */
+		while (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 (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 ((output_file || !no_conf_write) &&
+		    conf_write(output_file)) {
+			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;
+}
diff --git a/scripts/config/confdata.c b/scripts/config/confdata.c
new file mode 100644
index 0000000..e3f6fdf
--- /dev/null
+++ b/scripts/config/confdata.c
@@ -0,0 +1,1288 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>
+ */
+
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <ctype.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+
+#include "lkc.h"
+
+/* return true if 'path' exists, false otherwise */
+static bool is_present(const char *path)
+{
+	struct stat st;
+
+	return !stat(path, &st);
+}
+
+/* return true if 'path' exists and it is a directory, false otherwise */
+static bool is_dir(const char *path)
+{
+	struct stat st;
+
+	if (stat(path, &st))
+		return false;
+
+	return S_ISDIR(st.st_mode);
+}
+
+/* return true if the given two files are the same, false otherwise */
+static bool is_same(const char *file1, const char *file2)
+{
+	int fd1, fd2;
+	struct stat st1, st2;
+	void *map1, *map2;
+	bool ret = false;
+
+	fd1 = open(file1, O_RDONLY);
+	if (fd1 < 0)
+		return ret;
+
+	fd2 = open(file2, O_RDONLY);
+	if (fd2 < 0)
+		goto close1;
+
+	ret = fstat(fd1, &st1);
+	if (ret)
+		goto close2;
+	ret = fstat(fd2, &st2);
+	if (ret)
+		goto close2;
+
+	if (st1.st_size != st2.st_size)
+		goto close2;
+
+	map1 = mmap(NULL, st1.st_size, PROT_READ, MAP_PRIVATE, fd1, 0);
+	if (map1 == MAP_FAILED)
+		goto close2;
+
+	map2 = mmap(NULL, st2.st_size, PROT_READ, MAP_PRIVATE, fd2, 0);
+	if (map2 == MAP_FAILED)
+		goto close2;
+
+	if (bcmp(map1, map2, st1.st_size))
+		goto close2;
+
+	ret = true;
+close2:
+	close(fd2);
+close1:
+	close(fd1);
+
+	return ret;
+}
+
+/*
+ * Create the parent directory of the given path.
+ *
+ * For example, if 'include/config/auto.conf' is given, create 'include/config'.
+ */
+static int make_parent_dir(const char *path)
+{
+	char tmp[PATH_MAX + 1];
+	char *p;
+
+	strncpy(tmp, path, sizeof(tmp));
+	tmp[sizeof(tmp) - 1] = 0;
+
+	/* Remove the base name. Just return if nothing is left */
+	p = strrchr(tmp, '/');
+	if (!p)
+		return 0;
+	*(p + 1) = 0;
+
+	/* Just in case it is an absolute path */
+	p = tmp;
+	while (*p == '/')
+		p++;
+
+	while ((p = strchr(p, '/'))) {
+		*p = 0;
+
+		/* skip if the directory exists */
+		if (!is_dir(tmp) && mkdir(tmp, 0755))
+			return -1;
+
+		*p = '/';
+		while (*p == '/')
+			p++;
+	}
+
+	return 0;
+}
+
+static char depfile_path[PATH_MAX];
+static size_t depfile_prefix_len;
+
+/* touch depfile for symbol 'name' */
+static int conf_touch_dep(const char *name)
+{
+	int fd;
+
+	/* check overflow: prefix + name + '\0' must fit in buffer. */
+	if (depfile_prefix_len + strlen(name) + 1 > sizeof(depfile_path))
+		return -1;
+
+	strcpy(depfile_path + depfile_prefix_len, name);
+
+	fd = open(depfile_path, O_WRONLY | O_CREAT | O_TRUNC, 0644);
+	if (fd == -1)
+		return -1;
+	close(fd);
+
+	return 0;
+}
+
+static void conf_warning(const char *fmt, ...)
+	__attribute__ ((format (printf, 1, 2)));
+
+static void conf_message(const char *fmt, ...)
+	__attribute__ ((format (printf, 1, 2)));
+
+static const char *conf_filename;
+static int conf_lineno, conf_warnings;
+
+static void conf_warning(const char *fmt, ...)
+{
+	va_list ap;
+	va_start(ap, fmt);
+	fprintf(stderr, "%s:%d:warning: ", conf_filename, conf_lineno);
+	vfprintf(stderr, fmt, ap);
+	fprintf(stderr, "\n");
+	va_end(ap);
+	conf_warnings++;
+}
+
+static void conf_default_message_callback(const char *s)
+{
+	printf("#\n# ");
+	printf("%s", s);
+	printf("\n#\n");
+}
+
+static void (*conf_message_callback)(const char *s) =
+	conf_default_message_callback;
+void conf_set_message_callback(void (*fn)(const char *s))
+{
+	conf_message_callback = fn;
+}
+
+static void conf_message(const char *fmt, ...)
+{
+	va_list ap;
+	char buf[4096];
+
+	if (!conf_message_callback)
+		return;
+
+	va_start(ap, fmt);
+
+	vsnprintf(buf, sizeof(buf), fmt, ap);
+	conf_message_callback(buf);
+	va_end(ap);
+}
+
+const char *conf_get_configname(void)
+{
+	char *name = getenv("KCONFIG_CONFIG");
+
+	return name ? name : ".config";
+}
+
+static const char *conf_get_autoconfig_name(void)
+{
+	char *name = getenv("KCONFIG_AUTOCONFIG");
+
+	return name ? name : "include/config/auto.conf";
+}
+
+static const char *conf_get_autoheader_name(void)
+{
+	char *name = getenv("KCONFIG_AUTOHEADER");
+
+	return name ? name : "include/generated/autoconf.h";
+}
+
+static const char *conf_get_rustccfg_name(void)
+{
+	char *name = getenv("KCONFIG_RUSTCCFG");
+
+	return name ? name : "include/generated/rustc_cfg";
+}
+
+static int conf_set_sym_val(struct symbol *sym, int def, int def_flags, char *p)
+{
+	char *p2;
+
+	switch (sym->type) {
+	case S_TRISTATE:
+		if (p[0] == 'm') {
+			sym->def[def].tri = mod;
+			sym->flags |= def_flags;
+			break;
+		}
+		/* fall through */
+	case S_BOOLEAN:
+		if (p[0] == 'y') {
+			sym->def[def].tri = yes;
+			sym->flags |= def_flags;
+			break;
+		}
+		if (p[0] == 'n') {
+			sym->def[def].tri = no;
+			sym->flags |= def_flags;
+			break;
+		}
+		if (def != S_DEF_AUTO)
+			conf_warning("symbol value '%s' invalid for %s",
+				     p, sym->name);
+		return 1;
+	case S_STRING:
+		/* No escaping for S_DEF_AUTO (include/config/auto.conf) */
+		if (def != S_DEF_AUTO) {
+			if (*p++ != '"')
+				break;
+			for (p2 = p; (p2 = strpbrk(p2, "\"\\")); p2++) {
+				if (*p2 == '"') {
+					*p2 = 0;
+					break;
+				}
+				memmove(p2, p2 + 1, strlen(p2));
+			}
+			if (!p2) {
+				conf_warning("invalid string found");
+				return 1;
+			}
+		}
+		/* fall through */
+	case S_INT:
+	case S_HEX:
+		if (sym_string_valid(sym, p)) {
+			sym->def[def].val = xstrdup(p);
+			sym->flags |= def_flags;
+		} else {
+			if (def != S_DEF_AUTO)
+				conf_warning("symbol value '%s' invalid for %s",
+					     p, sym->name);
+			return 1;
+		}
+		break;
+	default:
+		;
+	}
+	return 0;
+}
+
+#define LINE_GROWTH 16
+static int add_byte(int c, char **lineptr, size_t slen, size_t *n)
+{
+	char *nline;
+	size_t new_size = slen + 1;
+	if (new_size > *n) {
+		new_size += LINE_GROWTH - 1;
+		new_size *= 2;
+		nline = xrealloc(*lineptr, new_size);
+		if (!nline)
+			return -1;
+
+		*lineptr = nline;
+		*n = new_size;
+	}
+
+	(*lineptr)[slen] = c;
+
+	return 0;
+}
+
+static ssize_t compat_getline(char **lineptr, size_t *n, FILE *stream)
+{
+	char *line = *lineptr;
+	size_t slen = 0;
+
+	for (;;) {
+		int c = getc(stream);
+
+		switch (c) {
+		case '\n':
+			if (add_byte(c, &line, slen, n) < 0)
+				goto e_out;
+			slen++;
+			/* fall through */
+		case EOF:
+			if (add_byte('\0', &line, slen, n) < 0)
+				goto e_out;
+			*lineptr = line;
+			if (slen == 0)
+				return -1;
+			return slen;
+		default:
+			if (add_byte(c, &line, slen, n) < 0)
+				goto e_out;
+			slen++;
+		}
+	}
+
+e_out:
+	line[slen-1] = '\0';
+	*lineptr = line;
+	return -1;
+}
+
+void conf_reset(int def)
+{
+	struct symbol *sym;
+	int i, def_flags;
+
+	def_flags = SYMBOL_DEF << def;
+	for_all_symbols(i, sym) {
+		sym->flags |= SYMBOL_CHANGED;
+		sym->flags &= ~(def_flags|SYMBOL_VALID);
+		if (sym_is_choice(sym))
+			sym->flags |= def_flags;
+		switch (sym->type) {
+		case S_INT:
+		case S_HEX:
+		case S_STRING:
+			if (sym->def[def].val)
+				free(sym->def[def].val);
+			/* fall through */
+		default:
+			sym->def[def].val = NULL;
+			sym->def[def].tri = no;
+		}
+	}
+}
+
+int conf_read_simple(const char *name, int def)
+{
+	FILE *in = NULL;
+	char   *line = NULL;
+	size_t  line_asize = 0;
+	char *p, *p2;
+	struct symbol *sym;
+	int def_flags;
+	const char *warn_unknown;
+	const char *werror;
+
+	warn_unknown = getenv("KCONFIG_WARN_UNKNOWN_SYMBOLS");
+	werror = getenv("KCONFIG_WERROR");
+	if (name) {
+		in = zconf_fopen(name);
+	} else {
+		char *env;
+
+		name = conf_get_configname();
+		in = zconf_fopen(name);
+		if (in)
+			goto load;
+		conf_set_changed(true);
+
+		env = getenv("KCONFIG_DEFCONFIG_LIST");
+		if (!env)
+			return 1;
+
+		while (1) {
+			bool is_last;
+
+			while (isspace(*env))
+				env++;
+
+			if (!*env)
+				break;
+
+			p = env;
+			while (*p && !isspace(*p))
+				p++;
+
+			is_last = (*p == '\0');
+
+			*p = '\0';
+
+			in = zconf_fopen(env);
+			if (in) {
+				conf_message("using defaults found in %s",
+					     env);
+				goto load;
+			}
+
+			if (is_last)
+				break;
+
+			env = p + 1;
+		}
+	}
+	if (!in)
+		return 1;
+
+load:
+	conf_filename = name;
+	conf_lineno = 0;
+	conf_warnings = 0;
+
+	def_flags = SYMBOL_DEF << def;
+	conf_reset(def);
+
+	while (compat_getline(&line, &line_asize, in) != -1) {
+		conf_lineno++;
+		sym = NULL;
+		if (line[0] == '#') {
+			if (memcmp(line + 2, CONFIG_, strlen(CONFIG_)))
+				continue;
+			p = strchr(line + 2 + strlen(CONFIG_), ' ');
+			if (!p)
+				continue;
+			*p++ = 0;
+			if (strncmp(p, "is not set", 10))
+				continue;
+			if (def == S_DEF_USER) {
+				sym = sym_find(line + 2 + strlen(CONFIG_));
+				if (!sym) {
+					if (warn_unknown)
+						conf_warning("unknown symbol: %s",
+							     line + 2 + strlen(CONFIG_));
+
+					conf_set_changed(true);
+					continue;
+				}
+			} else {
+				sym = sym_lookup(line + 2 + strlen(CONFIG_), 0);
+				if (sym->type == S_UNKNOWN)
+					sym->type = S_BOOLEAN;
+			}
+			switch (sym->type) {
+			case S_BOOLEAN:
+			case S_TRISTATE:
+				sym->def[def].tri = no;
+				sym->flags |= def_flags;
+				break;
+			default:
+				;
+			}
+		} else if (memcmp(line, CONFIG_, strlen(CONFIG_)) == 0) {
+			p = strchr(line + strlen(CONFIG_), '=');
+			if (!p)
+				continue;
+			*p++ = 0;
+			p2 = strchr(p, '\n');
+			if (p2) {
+				*p2-- = 0;
+				if (*p2 == '\r')
+					*p2 = 0;
+			}
+
+			sym = sym_find(line + strlen(CONFIG_));
+			if (!sym) {
+				if (def == S_DEF_AUTO) {
+					/*
+					 * Reading from include/config/auto.conf
+					 * If CONFIG_FOO previously existed in
+					 * auto.conf but it is missing now,
+					 * include/config/FOO must be touched.
+					 */
+					conf_touch_dep(line + strlen(CONFIG_));
+				} else {
+					if (warn_unknown)
+						conf_warning("unknown symbol: %s",
+							     line + strlen(CONFIG_));
+
+					conf_set_changed(true);
+				}
+				continue;
+			}
+
+			if (conf_set_sym_val(sym, def, def_flags, p))
+				continue;
+		} else {
+			if (line[0] != '\r' && line[0] != '\n')
+				conf_warning("unexpected data: %.*s",
+					     (int)strcspn(line, "\r\n"), line);
+
+			continue;
+		}
+
+		if (sym && sym_is_choice_value(sym)) {
+			struct symbol *cs = prop_get_symbol(sym_get_choice_prop(sym));
+			switch (sym->def[def].tri) {
+			case no:
+				break;
+			case mod:
+				if (cs->def[def].tri == yes) {
+					conf_warning("%s creates inconsistent choice state", sym->name);
+					cs->flags &= ~def_flags;
+				}
+				break;
+			case yes:
+				if (cs->def[def].tri != no)
+					conf_warning("override: %s changes choice state", sym->name);
+				cs->def[def].val = sym;
+				break;
+			}
+			cs->def[def].tri = EXPR_OR(cs->def[def].tri, sym->def[def].tri);
+		}
+	}
+	free(line);
+	fclose(in);
+
+	if (conf_warnings && werror)
+		exit(1);
+
+	return 0;
+}
+
+int conf_read(const char *name)
+{
+	struct symbol *sym;
+	int conf_unsaved = 0;
+	int i;
+
+	conf_set_changed(false);
+
+	if (conf_read_simple(name, S_DEF_USER)) {
+		sym_calc_value(modules_sym);
+		return 1;
+	}
+
+	sym_calc_value(modules_sym);
+
+	for_all_symbols(i, sym) {
+		sym_calc_value(sym);
+		if (sym_is_choice(sym) || (sym->flags & SYMBOL_NO_WRITE))
+			continue;
+		if (sym_has_value(sym) && (sym->flags & SYMBOL_WRITE)) {
+			/* check that calculated value agrees with saved value */
+			switch (sym->type) {
+			case S_BOOLEAN:
+			case S_TRISTATE:
+				if (sym->def[S_DEF_USER].tri == sym_get_tristate_value(sym))
+					continue;
+				break;
+			default:
+				if (!strcmp(sym->curr.val, sym->def[S_DEF_USER].val))
+					continue;
+				break;
+			}
+		} else if (!sym_has_value(sym) && !(sym->flags & SYMBOL_WRITE))
+			/* no previous value and not saved */
+			continue;
+		conf_unsaved++;
+		/* maybe print value in verbose mode... */
+	}
+
+	for_all_symbols(i, sym) {
+		if (sym_has_value(sym) && !sym_is_choice_value(sym)) {
+			/* Reset values of generates values, so they'll appear
+			 * as new, if they should become visible, but that
+			 * doesn't quite work if the Kconfig and the saved
+			 * configuration disagree.
+			 */
+			if (sym->visible == no && !conf_unsaved)
+				sym->flags &= ~SYMBOL_DEF_USER;
+			switch (sym->type) {
+			case S_STRING:
+			case S_INT:
+			case S_HEX:
+				/* Reset a string value if it's out of range */
+				if (sym_string_within_range(sym, sym->def[S_DEF_USER].val))
+					break;
+				sym->flags &= ~(SYMBOL_VALID|SYMBOL_DEF_USER);
+				conf_unsaved++;
+				break;
+			default:
+				break;
+			}
+		}
+	}
+
+	if (conf_warnings || conf_unsaved)
+		conf_set_changed(true);
+
+	return 0;
+}
+
+struct comment_style {
+	const char *decoration;
+	const char *prefix;
+	const char *postfix;
+};
+
+static const struct comment_style comment_style_pound = {
+	.decoration = "#",
+	.prefix = "#",
+	.postfix = "#",
+};
+
+static const struct comment_style comment_style_c = {
+	.decoration = " *",
+	.prefix = "/*",
+	.postfix = " */",
+};
+
+static void conf_write_heading(FILE *fp, const struct comment_style *cs)
+{
+	if (!cs)
+		return;
+
+	fprintf(fp, "%s\n", cs->prefix);
+
+	fprintf(fp, "%s Automatically generated file; DO NOT EDIT.\n",
+		cs->decoration);
+
+	fprintf(fp, "%s %s\n", cs->decoration, rootmenu.prompt->text);
+
+	fprintf(fp, "%s\n", cs->postfix);
+}
+
+/* The returned pointer must be freed on the caller side */
+static char *escape_string_value(const char *in)
+{
+	const char *p;
+	char *out;
+	size_t len;
+
+	len = strlen(in) + strlen("\"\"") + 1;
+
+	p = in;
+	while (1) {
+		p += strcspn(p, "\"\\");
+
+		if (p[0] == '\0')
+			break;
+
+		len++;
+		p++;
+	}
+
+	out = xmalloc(len);
+	out[0] = '\0';
+
+	strcat(out, "\"");
+
+	p = in;
+	while (1) {
+		len = strcspn(p, "\"\\");
+		strncat(out, p, len);
+		p += len;
+
+		if (p[0] == '\0')
+			break;
+
+		strcat(out, "\\");
+		strncat(out, p++, 1);
+	}
+
+	strcat(out, "\"");
+
+	return out;
+}
+
+enum output_n { OUTPUT_N, OUTPUT_N_AS_UNSET, OUTPUT_N_NONE };
+
+static void __print_symbol(FILE *fp, struct symbol *sym, enum output_n output_n,
+			   bool escape_string)
+{
+	const char *val;
+	char *escaped = NULL;
+
+	if (sym->type == S_UNKNOWN)
+		return;
+
+	val = sym_get_string_value(sym);
+
+	if ((sym->type == S_BOOLEAN || sym->type == S_TRISTATE) &&
+	    output_n != OUTPUT_N && *val == 'n') {
+		if (output_n == OUTPUT_N_AS_UNSET)
+			fprintf(fp, "# %s%s is not set\n", CONFIG_, sym->name);
+		return;
+	}
+
+	if (sym->type == S_STRING && escape_string) {
+		escaped = escape_string_value(val);
+		val = escaped;
+	}
+
+	fprintf(fp, "%s%s=%s\n", CONFIG_, sym->name, val);
+
+	free(escaped);
+}
+
+static void print_symbol_for_dotconfig(FILE *fp, struct symbol *sym)
+{
+	__print_symbol(fp, sym, OUTPUT_N_AS_UNSET, true);
+}
+
+static void print_symbol_for_autoconf(FILE *fp, struct symbol *sym)
+{
+	__print_symbol(fp, sym, OUTPUT_N_NONE, false);
+}
+
+void print_symbol_for_listconfig(struct symbol *sym)
+{
+	__print_symbol(stdout, sym, OUTPUT_N, true);
+}
+
+static void print_symbol_for_c(FILE *fp, struct symbol *sym)
+{
+	const char *val;
+	const char *sym_suffix = "";
+	const char *val_prefix = "";
+	char *escaped = NULL;
+
+	if (sym->type == S_UNKNOWN)
+		return;
+
+	val = sym_get_string_value(sym);
+
+	switch (sym->type) {
+	case S_BOOLEAN:
+	case S_TRISTATE:
+		switch (*val) {
+		case 'n':
+			return;
+		case 'm':
+			sym_suffix = "_MODULE";
+			/* fall through */
+		default:
+			val = "1";
+		}
+		break;
+	case S_HEX:
+		if (val[0] != '0' || (val[1] != 'x' && val[1] != 'X'))
+			val_prefix = "0x";
+		break;
+	case S_STRING:
+		escaped = escape_string_value(val);
+		val = escaped;
+	default:
+		break;
+	}
+
+	fprintf(fp, "#define %s%s%s %s%s\n", CONFIG_, sym->name, sym_suffix,
+		val_prefix, val);
+
+	free(escaped);
+}
+
+static void print_symbol_for_rustccfg(FILE *fp, struct symbol *sym)
+{
+	const char *val;
+	const char *val_prefix = "";
+	char *val_prefixed = NULL;
+	size_t val_prefixed_len;
+	char *escaped = NULL;
+
+	if (sym->type == S_UNKNOWN)
+		return;
+
+	val = sym_get_string_value(sym);
+
+	switch (sym->type) {
+	case S_BOOLEAN:
+	case S_TRISTATE:
+		/*
+		 * We do not care about disabled ones, i.e. no need for
+		 * what otherwise are "comments" in other printers.
+		 */
+		if (*val == 'n')
+			return;
+
+		/*
+		 * To have similar functionality to the C macro `IS_ENABLED()`
+		 * we provide an empty `--cfg CONFIG_X` here in both `y`
+		 * and `m` cases.
+		 *
+		 * Then, the common `fprintf()` below will also give us
+		 * a `--cfg CONFIG_X="y"` or `--cfg CONFIG_X="m"`, which can
+		 * be used as the equivalent of `IS_BUILTIN()`/`IS_MODULE()`.
+		 */
+		fprintf(fp, "--cfg=%s%s\n", CONFIG_, sym->name);
+		break;
+	case S_HEX:
+		if (val[0] != '0' || (val[1] != 'x' && val[1] != 'X'))
+			val_prefix = "0x";
+		break;
+	default:
+		break;
+	}
+
+	if (strlen(val_prefix) > 0) {
+		val_prefixed_len = strlen(val) + strlen(val_prefix) + 1;
+		val_prefixed = xmalloc(val_prefixed_len);
+		snprintf(val_prefixed, val_prefixed_len, "%s%s", val_prefix, val);
+		val = val_prefixed;
+	}
+
+	/* All values get escaped: the `--cfg` option only takes strings */
+	escaped = escape_string_value(val);
+	val = escaped;
+
+	fprintf(fp, "--cfg=%s%s=%s\n", CONFIG_, sym->name, val);
+
+	free(escaped);
+	free(val_prefixed);
+}
+
+/*
+ * Write out a minimal config.
+ * All values that has default values are skipped as this is redundant.
+ */
+int conf_write_defconfig(const char *filename)
+{
+	struct symbol *sym;
+	struct menu *menu;
+	FILE *out;
+
+	out = fopen(filename, "w");
+	if (!out)
+		return 1;
+
+	sym_clear_all_valid();
+
+	/* Traverse all menus to find all relevant symbols */
+	menu = rootmenu.list;
+
+	while (menu != NULL)
+	{
+		sym = menu->sym;
+		if (sym == NULL) {
+			if (!menu_is_visible(menu))
+				goto next_menu;
+		} else if (!sym_is_choice(sym)) {
+			sym_calc_value(sym);
+			if (!(sym->flags & SYMBOL_WRITE))
+				goto next_menu;
+			sym->flags &= ~SYMBOL_WRITE;
+			/* If we cannot change the symbol - skip */
+			if (!sym_is_changeable(sym))
+				goto next_menu;
+			/* If symbol equals to default value - skip */
+			if (strcmp(sym_get_string_value(sym), sym_get_string_default(sym)) == 0)
+				goto next_menu;
+
+			/*
+			 * If symbol is a choice value and equals to the
+			 * default for a choice - skip.
+			 * But only if value is bool and equal to "y" and
+			 * choice is not "optional".
+			 * (If choice is "optional" then all values can be "n")
+			 */
+			if (sym_is_choice_value(sym)) {
+				struct symbol *cs;
+				struct symbol *ds;
+
+				cs = prop_get_symbol(sym_get_choice_prop(sym));
+				ds = sym_choice_default(cs);
+				if (!sym_is_optional(cs) && sym == ds) {
+					if ((sym->type == S_BOOLEAN) &&
+					    sym_get_tristate_value(sym) == yes)
+						goto next_menu;
+				}
+			}
+			print_symbol_for_dotconfig(out, sym);
+		}
+next_menu:
+		if (menu->list != NULL) {
+			menu = menu->list;
+		}
+		else if (menu->next != NULL) {
+			menu = menu->next;
+		} else {
+			while ((menu = menu->parent)) {
+				if (menu->next != NULL) {
+					menu = menu->next;
+					break;
+				}
+			}
+		}
+	}
+	fclose(out);
+	return 0;
+}
+
+int conf_write(const char *name)
+{
+	FILE *out;
+	struct symbol *sym;
+	struct menu *menu;
+	const char *str;
+	char tmpname[PATH_MAX + 1], oldname[PATH_MAX + 1];
+	char *env;
+	int i;
+	bool need_newline = false;
+
+	if (!name)
+		name = conf_get_configname();
+
+	if (!*name) {
+		fprintf(stderr, "config name is empty\n");
+		return -1;
+	}
+
+	if (is_dir(name)) {
+		fprintf(stderr, "%s: Is a directory\n", name);
+		return -1;
+	}
+
+	if (make_parent_dir(name))
+		return -1;
+
+	env = getenv("KCONFIG_OVERWRITECONFIG");
+	if (env && *env) {
+		*tmpname = 0;
+		out = fopen(name, "w");
+	} else {
+		snprintf(tmpname, sizeof(tmpname), "%s.%d.tmp",
+			 name, (int)getpid());
+		out = fopen(tmpname, "w");
+	}
+	if (!out)
+		return 1;
+
+	conf_write_heading(out, &comment_style_pound);
+
+	if (!conf_get_changed())
+		sym_clear_all_valid();
+
+	menu = rootmenu.list;
+	while (menu) {
+		sym = menu->sym;
+		if (!sym) {
+			if (!menu_is_visible(menu))
+				goto next;
+			str = menu_get_prompt(menu);
+			fprintf(out, "\n"
+				     "#\n"
+				     "# %s\n"
+				     "#\n", str);
+			need_newline = false;
+		} else if (!(sym->flags & SYMBOL_CHOICE) &&
+			   !(sym->flags & SYMBOL_WRITTEN)) {
+			sym_calc_value(sym);
+			if (!(sym->flags & SYMBOL_WRITE))
+				goto next;
+			if (need_newline) {
+				fprintf(out, "\n");
+				need_newline = false;
+			}
+			sym->flags |= SYMBOL_WRITTEN;
+			print_symbol_for_dotconfig(out, sym);
+		}
+
+next:
+		if (menu->list) {
+			menu = menu->list;
+			continue;
+		}
+
+end_check:
+		if (!menu->sym && menu_is_visible(menu) && menu != &rootmenu &&
+		    menu->prompt->type == P_MENU) {
+			fprintf(out, "# end of %s\n", menu_get_prompt(menu));
+			need_newline = true;
+		}
+
+		if (menu->next) {
+			menu = menu->next;
+		} else {
+			menu = menu->parent;
+			if (menu)
+				goto end_check;
+		}
+	}
+	fclose(out);
+
+	for_all_symbols(i, sym)
+		sym->flags &= ~SYMBOL_WRITTEN;
+
+	if (*tmpname) {
+		if (is_same(name, tmpname)) {
+			conf_message("No change to %s", name);
+			unlink(tmpname);
+			conf_set_changed(false);
+			return 0;
+		}
+
+		snprintf(oldname, sizeof(oldname), "%s.old", name);
+		rename(name, oldname);
+		if (rename(tmpname, name))
+			return 1;
+	}
+
+	conf_message("configuration written to %s", name);
+
+	conf_set_changed(false);
+
+	return 0;
+}
+
+/* write a dependency file as used by kbuild to track dependencies */
+static int conf_write_autoconf_cmd(const char *autoconf_name)
+{
+	char name[PATH_MAX], tmp[PATH_MAX];
+	struct file *file;
+	FILE *out;
+	int ret;
+
+	ret = snprintf(name, sizeof(name), "%s.cmd", autoconf_name);
+	if (ret >= sizeof(name)) /* check truncation */
+		return -1;
+
+	if (make_parent_dir(name))
+		return -1;
+
+	ret = snprintf(tmp, sizeof(tmp), "%s.cmd.tmp", autoconf_name);
+	if (ret >= sizeof(tmp)) /* check truncation */
+		return -1;
+
+	out = fopen(tmp, "w");
+	if (!out) {
+		perror("fopen");
+		return -1;
+	}
+
+	fprintf(out, "deps_config := \\\n");
+	for (file = file_list; file; file = file->next)
+		fprintf(out, "\t%s \\\n", file->name);
+
+	fprintf(out, "\n%s: $(deps_config)\n\n", autoconf_name);
+
+	env_write_dep(out, autoconf_name);
+
+	fprintf(out, "\n$(deps_config): ;\n");
+
+	fflush(out);
+	ret = ferror(out); /* error check for all fprintf() calls */
+	fclose(out);
+	if (ret)
+		return -1;
+
+	if (rename(tmp, name)) {
+		perror("rename");
+		return -1;
+	}
+
+	return 0;
+}
+
+static int conf_touch_deps(void)
+{
+	const char *name, *tmp;
+	struct symbol *sym;
+	int res, i;
+
+	name = conf_get_autoconfig_name();
+	tmp = strrchr(name, '/');
+	depfile_prefix_len = tmp ? tmp - name + 1 : 0;
+	if (depfile_prefix_len + 1 > sizeof(depfile_path))
+		return -1;
+
+	strncpy(depfile_path, name, depfile_prefix_len);
+	depfile_path[depfile_prefix_len] = 0;
+
+	conf_read_simple(name, S_DEF_AUTO);
+	sym_calc_value(modules_sym);
+
+	for_all_symbols(i, sym) {
+		sym_calc_value(sym);
+		if ((sym->flags & SYMBOL_NO_WRITE) || !sym->name)
+			continue;
+		if (sym->flags & SYMBOL_WRITE) {
+			if (sym->flags & SYMBOL_DEF_AUTO) {
+				/*
+				 * symbol has old and new value,
+				 * so compare them...
+				 */
+				switch (sym->type) {
+				case S_BOOLEAN:
+				case S_TRISTATE:
+					if (sym_get_tristate_value(sym) ==
+					    sym->def[S_DEF_AUTO].tri)
+						continue;
+					break;
+				case S_STRING:
+				case S_HEX:
+				case S_INT:
+					if (!strcmp(sym_get_string_value(sym),
+						    sym->def[S_DEF_AUTO].val))
+						continue;
+					break;
+				default:
+					break;
+				}
+			} else {
+				/*
+				 * If there is no old value, only 'no' (unset)
+				 * is allowed as new value.
+				 */
+				switch (sym->type) {
+				case S_BOOLEAN:
+				case S_TRISTATE:
+					if (sym_get_tristate_value(sym) == no)
+						continue;
+					break;
+				default:
+					break;
+				}
+			}
+		} else if (!(sym->flags & SYMBOL_DEF_AUTO))
+			/* There is neither an old nor a new value. */
+			continue;
+		/* else
+		 *	There is an old value, but no new value ('no' (unset)
+		 *	isn't saved in auto.conf, so the old value is always
+		 *	different from 'no').
+		 */
+
+		res = conf_touch_dep(sym->name);
+		if (res)
+			return res;
+	}
+
+	return 0;
+}
+
+static int __conf_write_autoconf(const char *filename,
+				 void (*print_symbol)(FILE *, struct symbol *),
+				 const struct comment_style *comment_style)
+{
+	char tmp[PATH_MAX];
+	FILE *file;
+	struct symbol *sym;
+	int ret, i;
+
+	if (make_parent_dir(filename))
+		return -1;
+
+	ret = snprintf(tmp, sizeof(tmp), "%s.tmp", filename);
+	if (ret >= sizeof(tmp)) /* check truncation */
+		return -1;
+
+	file = fopen(tmp, "w");
+	if (!file) {
+		perror("fopen");
+		return -1;
+	}
+
+	conf_write_heading(file, comment_style);
+
+	for_all_symbols(i, sym)
+		if ((sym->flags & SYMBOL_WRITE) && sym->name)
+			print_symbol(file, sym);
+
+	fflush(file);
+	/* check possible errors in conf_write_heading() and print_symbol() */
+	ret = ferror(file);
+	fclose(file);
+	if (ret)
+		return -1;
+
+	if (rename(tmp, filename)) {
+		perror("rename");
+		return -1;
+	}
+
+	return 0;
+}
+
+int conf_write_autoconf(int overwrite)
+{
+	struct symbol *sym;
+	const char *autoconf_name = conf_get_autoconfig_name();
+	int ret, i;
+
+#ifndef OPENWRT_DOES_NOT_WANT_THIS
+	return 0;
+#endif
+	if (!overwrite && is_present(autoconf_name))
+		return 0;
+
+	ret = conf_write_autoconf_cmd(autoconf_name);
+	if (ret)
+		return -1;
+
+	if (conf_touch_deps())
+		return 1;
+
+	for_all_symbols(i, sym)
+		sym_calc_value(sym);
+
+	ret = __conf_write_autoconf(conf_get_autoheader_name(),
+				    print_symbol_for_c,
+				    &comment_style_c);
+	if (ret)
+		return ret;
+
+	ret = __conf_write_autoconf(conf_get_rustccfg_name(),
+				    print_symbol_for_rustccfg,
+				    NULL);
+	if (ret)
+		return ret;
+
+	/*
+	 * Create include/config/auto.conf. This must be the last step because
+	 * Kbuild has a dependency on auto.conf and this marks the successful
+	 * completion of the previous steps.
+	 */
+	ret = __conf_write_autoconf(conf_get_autoconfig_name(),
+				    print_symbol_for_autoconf,
+				    &comment_style_pound);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static bool conf_changed;
+static void (*conf_changed_callback)(void);
+
+void conf_set_changed(bool val)
+{
+	bool changed = conf_changed != val;
+
+	conf_changed = val;
+
+	if (conf_changed_callback && changed)
+		conf_changed_callback();
+}
+
+bool conf_get_changed(void)
+{
+	return conf_changed;
+}
+
+void conf_set_changed_callback(void (*fn)(void))
+{
+	conf_changed_callback = fn;
+}
+
+void set_all_choice_values(struct symbol *csym)
+{
+	struct property *prop;
+	struct symbol *sym;
+	struct expr *e;
+
+	prop = sym_get_choice_prop(csym);
+
+	/*
+	 * Set all non-assinged choice values to no
+	 */
+	expr_list_for_each_sym(prop->expr, e, sym) {
+		if (!sym_has_value(sym))
+			sym->def[S_DEF_USER].tri = no;
+	}
+	csym->flags |= SYMBOL_DEF_USER;
+	/* clear VALID to get value calculated */
+	csym->flags &= ~(SYMBOL_VALID | SYMBOL_NEED_SET_CHOICE_VALUES);
+}
diff --git a/scripts/config/expr.c b/scripts/config/expr.c
new file mode 100644
index 0000000..552c214
--- /dev/null
+++ b/scripts/config/expr.c
@@ -0,0 +1,1303 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>
+ */
+
+#include <ctype.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "lkc.h"
+
+#define DEBUG_EXPR	0
+
+static struct expr *expr_eliminate_yn(struct expr *e);
+
+struct expr *expr_alloc_symbol(struct symbol *sym)
+{
+	struct expr *e = xcalloc(1, sizeof(*e));
+	e->type = E_SYMBOL;
+	e->left.sym = sym;
+	return e;
+}
+
+struct expr *expr_alloc_one(enum expr_type type, struct expr *ce)
+{
+	struct expr *e = xcalloc(1, sizeof(*e));
+	e->type = type;
+	e->left.expr = ce;
+	return e;
+}
+
+struct expr *expr_alloc_two(enum expr_type type, struct expr *e1, struct expr *e2)
+{
+	struct expr *e = xcalloc(1, sizeof(*e));
+	e->type = type;
+	e->left.expr = e1;
+	e->right.expr = e2;
+	return e;
+}
+
+struct expr *expr_alloc_comp(enum expr_type type, struct symbol *s1, struct symbol *s2)
+{
+	struct expr *e = xcalloc(1, sizeof(*e));
+	e->type = type;
+	e->left.sym = s1;
+	e->right.sym = s2;
+	return e;
+}
+
+struct expr *expr_alloc_and(struct expr *e1, struct expr *e2)
+{
+	if (!e1)
+		return e2;
+	return e2 ? expr_alloc_two(E_AND, e1, e2) : e1;
+}
+
+struct expr *expr_alloc_or(struct expr *e1, struct expr *e2)
+{
+	if (!e1)
+		return e2;
+	return e2 ? expr_alloc_two(E_OR, e1, e2) : e1;
+}
+
+struct expr *expr_copy(const struct expr *org)
+{
+	struct expr *e;
+
+	if (!org)
+		return NULL;
+
+	e = xmalloc(sizeof(*org));
+	memcpy(e, org, sizeof(*org));
+	switch (org->type) {
+	case E_SYMBOL:
+		e->left = org->left;
+		break;
+	case E_NOT:
+		e->left.expr = expr_copy(org->left.expr);
+		break;
+	case E_EQUAL:
+	case E_GEQ:
+	case E_GTH:
+	case E_LEQ:
+	case E_LTH:
+	case E_UNEQUAL:
+		e->left.sym = org->left.sym;
+		e->right.sym = org->right.sym;
+		break;
+	case E_AND:
+	case E_OR:
+	case E_LIST:
+		e->left.expr = expr_copy(org->left.expr);
+		e->right.expr = expr_copy(org->right.expr);
+		break;
+	default:
+		fprintf(stderr, "can't copy type %d\n", e->type);
+		free(e);
+		e = NULL;
+		break;
+	}
+
+	return e;
+}
+
+void expr_free(struct expr *e)
+{
+	if (!e)
+		return;
+
+	switch (e->type) {
+	case E_SYMBOL:
+		break;
+	case E_NOT:
+		expr_free(e->left.expr);
+		break;
+	case E_EQUAL:
+	case E_GEQ:
+	case E_GTH:
+	case E_LEQ:
+	case E_LTH:
+	case E_UNEQUAL:
+		break;
+	case E_OR:
+	case E_AND:
+		expr_free(e->left.expr);
+		expr_free(e->right.expr);
+		break;
+	default:
+		fprintf(stderr, "how to free type %d?\n", e->type);
+		break;
+	}
+	free(e);
+}
+
+static int trans_count;
+
+#define e1 (*ep1)
+#define e2 (*ep2)
+
+/*
+ * expr_eliminate_eq() helper.
+ *
+ * Walks the two expression trees given in 'ep1' and 'ep2'. Any node that does
+ * not have type 'type' (E_OR/E_AND) is considered a leaf, and is compared
+ * against all other leaves. Two equal leaves are both replaced with either 'y'
+ * or 'n' as appropriate for 'type', to be eliminated later.
+ */
+static void __expr_eliminate_eq(enum expr_type type, struct expr **ep1, struct expr **ep2)
+{
+	/* Recurse down to leaves */
+
+	if (e1->type == type) {
+		__expr_eliminate_eq(type, &e1->left.expr, &e2);
+		__expr_eliminate_eq(type, &e1->right.expr, &e2);
+		return;
+	}
+	if (e2->type == type) {
+		__expr_eliminate_eq(type, &e1, &e2->left.expr);
+		__expr_eliminate_eq(type, &e1, &e2->right.expr);
+		return;
+	}
+
+	/* e1 and e2 are leaves. Compare them. */
+
+	if (e1->type == E_SYMBOL && e2->type == E_SYMBOL &&
+	    e1->left.sym == e2->left.sym &&
+	    (e1->left.sym == &symbol_yes || e1->left.sym == &symbol_no))
+		return;
+	if (!expr_eq(e1, e2))
+		return;
+
+	/* e1 and e2 are equal leaves. Prepare them for elimination. */
+
+	trans_count++;
+	expr_free(e1); expr_free(e2);
+	switch (type) {
+	case E_OR:
+		e1 = expr_alloc_symbol(&symbol_no);
+		e2 = expr_alloc_symbol(&symbol_no);
+		break;
+	case E_AND:
+		e1 = expr_alloc_symbol(&symbol_yes);
+		e2 = expr_alloc_symbol(&symbol_yes);
+		break;
+	default:
+		;
+	}
+}
+
+/*
+ * Rewrites the expressions 'ep1' and 'ep2' to remove operands common to both.
+ * Example reductions:
+ *
+ *	ep1: A && B           ->  ep1: y
+ *	ep2: A && B && C      ->  ep2: C
+ *
+ *	ep1: A || B           ->  ep1: n
+ *	ep2: A || B || C      ->  ep2: C
+ *
+ *	ep1: A && (B && FOO)  ->  ep1: FOO
+ *	ep2: (BAR && B) && A  ->  ep2: BAR
+ *
+ *	ep1: A && (B || C)    ->  ep1: y
+ *	ep2: (C || B) && A    ->  ep2: y
+ *
+ * Comparisons are done between all operands at the same "level" of && or ||.
+ * For example, in the expression 'e1 && (e2 || e3) && (e4 || e5)', the
+ * following operands will be compared:
+ *
+ *	- 'e1', 'e2 || e3', and 'e4 || e5', against each other
+ *	- e2 against e3
+ *	- e4 against e5
+ *
+ * Parentheses are irrelevant within a single level. 'e1 && (e2 && e3)' and
+ * '(e1 && e2) && e3' are both a single level.
+ *
+ * See __expr_eliminate_eq() as well.
+ */
+void expr_eliminate_eq(struct expr **ep1, struct expr **ep2)
+{
+	if (!e1 || !e2)
+		return;
+	switch (e1->type) {
+	case E_OR:
+	case E_AND:
+		__expr_eliminate_eq(e1->type, ep1, ep2);
+	default:
+		;
+	}
+	if (e1->type != e2->type) switch (e2->type) {
+	case E_OR:
+	case E_AND:
+		__expr_eliminate_eq(e2->type, ep1, ep2);
+	default:
+		;
+	}
+	e1 = expr_eliminate_yn(e1);
+	e2 = expr_eliminate_yn(e2);
+}
+
+#undef e1
+#undef e2
+
+/*
+ * Returns true if 'e1' and 'e2' are equal, after minor simplification. Two
+ * &&/|| expressions are considered equal if every operand in one expression
+ * equals some operand in the other (operands do not need to appear in the same
+ * order), recursively.
+ */
+int expr_eq(struct expr *e1, struct expr *e2)
+{
+	int res, old_count;
+
+	/*
+	 * A NULL expr is taken to be yes, but there's also a different way to
+	 * represent yes. expr_is_yes() checks for either representation.
+	 */
+	if (!e1 || !e2)
+		return expr_is_yes(e1) && expr_is_yes(e2);
+
+	if (e1->type != e2->type)
+		return 0;
+	switch (e1->type) {
+	case E_EQUAL:
+	case E_GEQ:
+	case E_GTH:
+	case E_LEQ:
+	case E_LTH:
+	case E_UNEQUAL:
+		return e1->left.sym == e2->left.sym && e1->right.sym == e2->right.sym;
+	case E_SYMBOL:
+		return e1->left.sym == e2->left.sym;
+	case E_NOT:
+		return expr_eq(e1->left.expr, e2->left.expr);
+	case E_AND:
+	case E_OR:
+		e1 = expr_copy(e1);
+		e2 = expr_copy(e2);
+		old_count = trans_count;
+		expr_eliminate_eq(&e1, &e2);
+		res = (e1->type == E_SYMBOL && e2->type == E_SYMBOL &&
+		       e1->left.sym == e2->left.sym);
+		expr_free(e1);
+		expr_free(e2);
+		trans_count = old_count;
+		return res;
+	case E_LIST:
+	case E_RANGE:
+	case E_NONE:
+		/* panic */;
+	}
+
+	if (DEBUG_EXPR) {
+		expr_fprint(e1, stdout);
+		printf(" = ");
+		expr_fprint(e2, stdout);
+		printf(" ?\n");
+	}
+
+	return 0;
+}
+
+/*
+ * Recursively performs the following simplifications in-place (as well as the
+ * corresponding simplifications with swapped operands):
+ *
+ *	expr && n  ->  n
+ *	expr && y  ->  expr
+ *	expr || n  ->  expr
+ *	expr || y  ->  y
+ *
+ * Returns the optimized expression.
+ */
+static struct expr *expr_eliminate_yn(struct expr *e)
+{
+	struct expr *tmp;
+
+	if (e) switch (e->type) {
+	case E_AND:
+		e->left.expr = expr_eliminate_yn(e->left.expr);
+		e->right.expr = expr_eliminate_yn(e->right.expr);
+		if (e->left.expr->type == E_SYMBOL) {
+			if (e->left.expr->left.sym == &symbol_no) {
+				expr_free(e->left.expr);
+				expr_free(e->right.expr);
+				e->type = E_SYMBOL;
+				e->left.sym = &symbol_no;
+				e->right.expr = NULL;
+				return e;
+			} else if (e->left.expr->left.sym == &symbol_yes) {
+				free(e->left.expr);
+				tmp = e->right.expr;
+				*e = *(e->right.expr);
+				free(tmp);
+				return e;
+			}
+		}
+		if (e->right.expr->type == E_SYMBOL) {
+			if (e->right.expr->left.sym == &symbol_no) {
+				expr_free(e->left.expr);
+				expr_free(e->right.expr);
+				e->type = E_SYMBOL;
+				e->left.sym = &symbol_no;
+				e->right.expr = NULL;
+				return e;
+			} else if (e->right.expr->left.sym == &symbol_yes) {
+				free(e->right.expr);
+				tmp = e->left.expr;
+				*e = *(e->left.expr);
+				free(tmp);
+				return e;
+			}
+		}
+		break;
+	case E_OR:
+		e->left.expr = expr_eliminate_yn(e->left.expr);
+		e->right.expr = expr_eliminate_yn(e->right.expr);
+		if (e->left.expr->type == E_SYMBOL) {
+			if (e->left.expr->left.sym == &symbol_no) {
+				free(e->left.expr);
+				tmp = e->right.expr;
+				*e = *(e->right.expr);
+				free(tmp);
+				return e;
+			} else if (e->left.expr->left.sym == &symbol_yes) {
+				expr_free(e->left.expr);
+				expr_free(e->right.expr);
+				e->type = E_SYMBOL;
+				e->left.sym = &symbol_yes;
+				e->right.expr = NULL;
+				return e;
+			}
+		}
+		if (e->right.expr->type == E_SYMBOL) {
+			if (e->right.expr->left.sym == &symbol_no) {
+				free(e->right.expr);
+				tmp = e->left.expr;
+				*e = *(e->left.expr);
+				free(tmp);
+				return e;
+			} else if (e->right.expr->left.sym == &symbol_yes) {
+				expr_free(e->left.expr);
+				expr_free(e->right.expr);
+				e->type = E_SYMBOL;
+				e->left.sym = &symbol_yes;
+				e->right.expr = NULL;
+				return e;
+			}
+		}
+		break;
+	default:
+		;
+	}
+	return e;
+}
+
+/*
+ * bool FOO!=n => FOO
+ */
+struct expr *expr_trans_bool(struct expr *e)
+{
+	if (!e)
+		return NULL;
+	switch (e->type) {
+	case E_AND:
+	case E_OR:
+	case E_NOT:
+		e->left.expr = expr_trans_bool(e->left.expr);
+		e->right.expr = expr_trans_bool(e->right.expr);
+		break;
+	case E_UNEQUAL:
+		// FOO!=n -> FOO
+		if (e->left.sym->type == S_TRISTATE) {
+			if (e->right.sym == &symbol_no) {
+				e->type = E_SYMBOL;
+				e->right.sym = NULL;
+			}
+		}
+		break;
+	default:
+		;
+	}
+	return e;
+}
+
+/*
+ * e1 || e2 -> ?
+ */
+static struct expr *expr_join_or(struct expr *e1, struct expr *e2)
+{
+	struct expr *tmp;
+	struct symbol *sym1, *sym2;
+
+	if (expr_eq(e1, e2))
+		return expr_copy(e1);
+	if (e1->type != E_EQUAL && e1->type != E_UNEQUAL && e1->type != E_SYMBOL && e1->type != E_NOT)
+		return NULL;
+	if (e2->type != E_EQUAL && e2->type != E_UNEQUAL && e2->type != E_SYMBOL && e2->type != E_NOT)
+		return NULL;
+	if (e1->type == E_NOT) {
+		tmp = e1->left.expr;
+		if (tmp->type != E_EQUAL && tmp->type != E_UNEQUAL && tmp->type != E_SYMBOL)
+			return NULL;
+		sym1 = tmp->left.sym;
+	} else
+		sym1 = e1->left.sym;
+	if (e2->type == E_NOT) {
+		if (e2->left.expr->type != E_SYMBOL)
+			return NULL;
+		sym2 = e2->left.expr->left.sym;
+	} else
+		sym2 = e2->left.sym;
+	if (sym1 != sym2)
+		return NULL;
+	if (sym1->type != S_BOOLEAN && sym1->type != S_TRISTATE)
+		return NULL;
+	if (sym1->type == S_TRISTATE) {
+		if (e1->type == E_EQUAL && e2->type == E_EQUAL &&
+		    ((e1->right.sym == &symbol_yes && e2->right.sym == &symbol_mod) ||
+		     (e1->right.sym == &symbol_mod && e2->right.sym == &symbol_yes))) {
+			// (a='y') || (a='m') -> (a!='n')
+			return expr_alloc_comp(E_UNEQUAL, sym1, &symbol_no);
+		}
+		if (e1->type == E_EQUAL && e2->type == E_EQUAL &&
+		    ((e1->right.sym == &symbol_yes && e2->right.sym == &symbol_no) ||
+		     (e1->right.sym == &symbol_no && e2->right.sym == &symbol_yes))) {
+			// (a='y') || (a='n') -> (a!='m')
+			return expr_alloc_comp(E_UNEQUAL, sym1, &symbol_mod);
+		}
+		if (e1->type == E_EQUAL && e2->type == E_EQUAL &&
+		    ((e1->right.sym == &symbol_mod && e2->right.sym == &symbol_no) ||
+		     (e1->right.sym == &symbol_no && e2->right.sym == &symbol_mod))) {
+			// (a='m') || (a='n') -> (a!='y')
+			return expr_alloc_comp(E_UNEQUAL, sym1, &symbol_yes);
+		}
+	}
+	if (sym1->type == S_BOOLEAN && sym1 == sym2) {
+		if ((e1->type == E_NOT && e1->left.expr->type == E_SYMBOL && e2->type == E_SYMBOL) ||
+		    (e2->type == E_NOT && e2->left.expr->type == E_SYMBOL && e1->type == E_SYMBOL))
+			return expr_alloc_symbol(&symbol_yes);
+	}
+
+	if (DEBUG_EXPR) {
+		printf("optimize (");
+		expr_fprint(e1, stdout);
+		printf(") || (");
+		expr_fprint(e2, stdout);
+		printf(")?\n");
+	}
+	return NULL;
+}
+
+static struct expr *expr_join_and(struct expr *e1, struct expr *e2)
+{
+	struct expr *tmp;
+	struct symbol *sym1, *sym2;
+
+	if (expr_eq(e1, e2))
+		return expr_copy(e1);
+	if (e1->type != E_EQUAL && e1->type != E_UNEQUAL && e1->type != E_SYMBOL && e1->type != E_NOT)
+		return NULL;
+	if (e2->type != E_EQUAL && e2->type != E_UNEQUAL && e2->type != E_SYMBOL && e2->type != E_NOT)
+		return NULL;
+	if (e1->type == E_NOT) {
+		tmp = e1->left.expr;
+		if (tmp->type != E_EQUAL && tmp->type != E_UNEQUAL && tmp->type != E_SYMBOL)
+			return NULL;
+		sym1 = tmp->left.sym;
+	} else
+		sym1 = e1->left.sym;
+	if (e2->type == E_NOT) {
+		if (e2->left.expr->type != E_SYMBOL)
+			return NULL;
+		sym2 = e2->left.expr->left.sym;
+	} else
+		sym2 = e2->left.sym;
+	if (sym1 != sym2)
+		return NULL;
+	if (sym1->type != S_BOOLEAN && sym1->type != S_TRISTATE)
+		return NULL;
+
+	if ((e1->type == E_SYMBOL && e2->type == E_EQUAL && e2->right.sym == &symbol_yes) ||
+	    (e2->type == E_SYMBOL && e1->type == E_EQUAL && e1->right.sym == &symbol_yes))
+		// (a) && (a='y') -> (a='y')
+		return expr_alloc_comp(E_EQUAL, sym1, &symbol_yes);
+
+	if ((e1->type == E_SYMBOL && e2->type == E_UNEQUAL && e2->right.sym == &symbol_no) ||
+	    (e2->type == E_SYMBOL && e1->type == E_UNEQUAL && e1->right.sym == &symbol_no))
+		// (a) && (a!='n') -> (a)
+		return expr_alloc_symbol(sym1);
+
+	if ((e1->type == E_SYMBOL && e2->type == E_UNEQUAL && e2->right.sym == &symbol_mod) ||
+	    (e2->type == E_SYMBOL && e1->type == E_UNEQUAL && e1->right.sym == &symbol_mod))
+		// (a) && (a!='m') -> (a='y')
+		return expr_alloc_comp(E_EQUAL, sym1, &symbol_yes);
+
+	if (sym1->type == S_TRISTATE) {
+		if (e1->type == E_EQUAL && e2->type == E_UNEQUAL) {
+			// (a='b') && (a!='c') -> 'b'='c' ? 'n' : a='b'
+			sym2 = e1->right.sym;
+			if ((e2->right.sym->flags & SYMBOL_CONST) && (sym2->flags & SYMBOL_CONST))
+				return sym2 != e2->right.sym ? expr_alloc_comp(E_EQUAL, sym1, sym2)
+							     : expr_alloc_symbol(&symbol_no);
+		}
+		if (e1->type == E_UNEQUAL && e2->type == E_EQUAL) {
+			// (a='b') && (a!='c') -> 'b'='c' ? 'n' : a='b'
+			sym2 = e2->right.sym;
+			if ((e1->right.sym->flags & SYMBOL_CONST) && (sym2->flags & SYMBOL_CONST))
+				return sym2 != e1->right.sym ? expr_alloc_comp(E_EQUAL, sym1, sym2)
+							     : expr_alloc_symbol(&symbol_no);
+		}
+		if (e1->type == E_UNEQUAL && e2->type == E_UNEQUAL &&
+			   ((e1->right.sym == &symbol_yes && e2->right.sym == &symbol_no) ||
+			    (e1->right.sym == &symbol_no && e2->right.sym == &symbol_yes)))
+			// (a!='y') && (a!='n') -> (a='m')
+			return expr_alloc_comp(E_EQUAL, sym1, &symbol_mod);
+
+		if (e1->type == E_UNEQUAL && e2->type == E_UNEQUAL &&
+			   ((e1->right.sym == &symbol_yes && e2->right.sym == &symbol_mod) ||
+			    (e1->right.sym == &symbol_mod && e2->right.sym == &symbol_yes)))
+			// (a!='y') && (a!='m') -> (a='n')
+			return expr_alloc_comp(E_EQUAL, sym1, &symbol_no);
+
+		if (e1->type == E_UNEQUAL && e2->type == E_UNEQUAL &&
+			   ((e1->right.sym == &symbol_mod && e2->right.sym == &symbol_no) ||
+			    (e1->right.sym == &symbol_no && e2->right.sym == &symbol_mod)))
+			// (a!='m') && (a!='n') -> (a='m')
+			return expr_alloc_comp(E_EQUAL, sym1, &symbol_yes);
+
+		if ((e1->type == E_SYMBOL && e2->type == E_EQUAL && e2->right.sym == &symbol_mod) ||
+		    (e2->type == E_SYMBOL && e1->type == E_EQUAL && e1->right.sym == &symbol_mod) ||
+		    (e1->type == E_SYMBOL && e2->type == E_UNEQUAL && e2->right.sym == &symbol_yes) ||
+		    (e2->type == E_SYMBOL && e1->type == E_UNEQUAL && e1->right.sym == &symbol_yes))
+			return NULL;
+	}
+
+	if (DEBUG_EXPR) {
+		printf("optimize (");
+		expr_fprint(e1, stdout);
+		printf(") && (");
+		expr_fprint(e2, stdout);
+		printf(")?\n");
+	}
+	return NULL;
+}
+
+/*
+ * expr_eliminate_dups() helper.
+ *
+ * Walks the two expression trees given in 'ep1' and 'ep2'. Any node that does
+ * not have type 'type' (E_OR/E_AND) is considered a leaf, and is compared
+ * against all other leaves to look for simplifications.
+ */
+static void expr_eliminate_dups1(enum expr_type type, struct expr **ep1, struct expr **ep2)
+{
+#define e1 (*ep1)
+#define e2 (*ep2)
+	struct expr *tmp;
+
+	/* Recurse down to leaves */
+
+	if (e1->type == type) {
+		expr_eliminate_dups1(type, &e1->left.expr, &e2);
+		expr_eliminate_dups1(type, &e1->right.expr, &e2);
+		return;
+	}
+	if (e2->type == type) {
+		expr_eliminate_dups1(type, &e1, &e2->left.expr);
+		expr_eliminate_dups1(type, &e1, &e2->right.expr);
+		return;
+	}
+
+	/* e1 and e2 are leaves. Compare and process them. */
+
+	if (e1 == e2)
+		return;
+
+	switch (e1->type) {
+	case E_OR: case E_AND:
+		expr_eliminate_dups1(e1->type, &e1, &e1);
+	default:
+		;
+	}
+
+	switch (type) {
+	case E_OR:
+		tmp = expr_join_or(e1, e2);
+		if (tmp) {
+			expr_free(e1); expr_free(e2);
+			e1 = expr_alloc_symbol(&symbol_no);
+			e2 = tmp;
+			trans_count++;
+		}
+		break;
+	case E_AND:
+		tmp = expr_join_and(e1, e2);
+		if (tmp) {
+			expr_free(e1); expr_free(e2);
+			e1 = expr_alloc_symbol(&symbol_yes);
+			e2 = tmp;
+			trans_count++;
+		}
+		break;
+	default:
+		;
+	}
+#undef e1
+#undef e2
+}
+
+/*
+ * Rewrites 'e' in-place to remove ("join") duplicate and other redundant
+ * operands.
+ *
+ * Example simplifications:
+ *
+ *	A || B || A    ->  A || B
+ *	A && B && A=y  ->  A=y && B
+ *
+ * Returns the deduplicated expression.
+ */
+struct expr *expr_eliminate_dups(struct expr *e)
+{
+	int oldcount;
+	if (!e)
+		return e;
+
+	oldcount = trans_count;
+	while (1) {
+		trans_count = 0;
+		switch (e->type) {
+		case E_OR: case E_AND:
+			expr_eliminate_dups1(e->type, &e, &e);
+		default:
+			;
+		}
+		if (!trans_count)
+			/* No simplifications done in this pass. We're done */
+			break;
+		e = expr_eliminate_yn(e);
+	}
+	trans_count = oldcount;
+	return e;
+}
+
+/*
+ * Performs various simplifications involving logical operators and
+ * comparisons.
+ *
+ * Allocates and returns a new expression.
+ */
+struct expr *expr_transform(struct expr *e)
+{
+	struct expr *tmp;
+
+	if (!e)
+		return NULL;
+	switch (e->type) {
+	case E_EQUAL:
+	case E_GEQ:
+	case E_GTH:
+	case E_LEQ:
+	case E_LTH:
+	case E_UNEQUAL:
+	case E_SYMBOL:
+	case E_LIST:
+		break;
+	default:
+		e->left.expr = expr_transform(e->left.expr);
+		e->right.expr = expr_transform(e->right.expr);
+	}
+
+	switch (e->type) {
+	case E_EQUAL:
+		if (e->left.sym->type != S_BOOLEAN)
+			break;
+		if (e->right.sym == &symbol_no) {
+			e->type = E_NOT;
+			e->left.expr = expr_alloc_symbol(e->left.sym);
+			e->right.sym = NULL;
+			break;
+		}
+		if (e->right.sym == &symbol_mod) {
+			printf("boolean symbol %s tested for 'm'? test forced to 'n'\n", e->left.sym->name);
+			e->type = E_SYMBOL;
+			e->left.sym = &symbol_no;
+			e->right.sym = NULL;
+			break;
+		}
+		if (e->right.sym == &symbol_yes) {
+			e->type = E_SYMBOL;
+			e->right.sym = NULL;
+			break;
+		}
+		break;
+	case E_UNEQUAL:
+		if (e->left.sym->type != S_BOOLEAN)
+			break;
+		if (e->right.sym == &symbol_no) {
+			e->type = E_SYMBOL;
+			e->right.sym = NULL;
+			break;
+		}
+		if (e->right.sym == &symbol_mod) {
+			printf("boolean symbol %s tested for 'm'? test forced to 'y'\n", e->left.sym->name);
+			e->type = E_SYMBOL;
+			e->left.sym = &symbol_yes;
+			e->right.sym = NULL;
+			break;
+		}
+		if (e->right.sym == &symbol_yes) {
+			e->type = E_NOT;
+			e->left.expr = expr_alloc_symbol(e->left.sym);
+			e->right.sym = NULL;
+			break;
+		}
+		break;
+	case E_NOT:
+		switch (e->left.expr->type) {
+		case E_NOT:
+			// !!a -> a
+			tmp = e->left.expr->left.expr;
+			free(e->left.expr);
+			free(e);
+			e = tmp;
+			e = expr_transform(e);
+			break;
+		case E_EQUAL:
+		case E_UNEQUAL:
+			// !a='x' -> a!='x'
+			tmp = e->left.expr;
+			free(e);
+			e = tmp;
+			e->type = e->type == E_EQUAL ? E_UNEQUAL : E_EQUAL;
+			break;
+		case E_LEQ:
+		case E_GEQ:
+			// !a<='x' -> a>'x'
+			tmp = e->left.expr;
+			free(e);
+			e = tmp;
+			e->type = e->type == E_LEQ ? E_GTH : E_LTH;
+			break;
+		case E_LTH:
+		case E_GTH:
+			// !a<'x' -> a>='x'
+			tmp = e->left.expr;
+			free(e);
+			e = tmp;
+			e->type = e->type == E_LTH ? E_GEQ : E_LEQ;
+			break;
+		case E_OR:
+			// !(a || b) -> !a && !b
+			tmp = e->left.expr;
+			e->type = E_AND;
+			e->right.expr = expr_alloc_one(E_NOT, tmp->right.expr);
+			tmp->type = E_NOT;
+			tmp->right.expr = NULL;
+			e = expr_transform(e);
+			break;
+		case E_AND:
+			// !(a && b) -> !a || !b
+			tmp = e->left.expr;
+			e->type = E_OR;
+			e->right.expr = expr_alloc_one(E_NOT, tmp->right.expr);
+			tmp->type = E_NOT;
+			tmp->right.expr = NULL;
+			e = expr_transform(e);
+			break;
+		case E_SYMBOL:
+			if (e->left.expr->left.sym == &symbol_yes) {
+				// !'y' -> 'n'
+				tmp = e->left.expr;
+				free(e);
+				e = tmp;
+				e->type = E_SYMBOL;
+				e->left.sym = &symbol_no;
+				break;
+			}
+			if (e->left.expr->left.sym == &symbol_mod) {
+				// !'m' -> 'm'
+				tmp = e->left.expr;
+				free(e);
+				e = tmp;
+				e->type = E_SYMBOL;
+				e->left.sym = &symbol_mod;
+				break;
+			}
+			if (e->left.expr->left.sym == &symbol_no) {
+				// !'n' -> 'y'
+				tmp = e->left.expr;
+				free(e);
+				e = tmp;
+				e->type = E_SYMBOL;
+				e->left.sym = &symbol_yes;
+				break;
+			}
+			break;
+		default:
+			;
+		}
+		break;
+	default:
+		;
+	}
+	return e;
+}
+
+int expr_contains_symbol(struct expr *dep, struct symbol *sym)
+{
+	if (!dep)
+		return 0;
+
+	switch (dep->type) {
+	case E_AND:
+	case E_OR:
+		return expr_contains_symbol(dep->left.expr, sym) ||
+		       expr_contains_symbol(dep->right.expr, sym);
+	case E_SYMBOL:
+		return dep->left.sym == sym;
+	case E_EQUAL:
+	case E_GEQ:
+	case E_GTH:
+	case E_LEQ:
+	case E_LTH:
+	case E_UNEQUAL:
+		return dep->left.sym == sym ||
+		       dep->right.sym == sym;
+	case E_NOT:
+		return expr_contains_symbol(dep->left.expr, sym);
+	default:
+		;
+	}
+	return 0;
+}
+
+bool expr_depends_symbol(struct expr *dep, struct symbol *sym)
+{
+	if (!dep)
+		return false;
+
+	switch (dep->type) {
+	case E_AND:
+		return expr_depends_symbol(dep->left.expr, sym) ||
+		       expr_depends_symbol(dep->right.expr, sym);
+	case E_SYMBOL:
+		return dep->left.sym == sym;
+	case E_EQUAL:
+		if (dep->left.sym == sym) {
+			if (dep->right.sym == &symbol_yes || dep->right.sym == &symbol_mod)
+				return true;
+		}
+		break;
+	case E_UNEQUAL:
+		if (dep->left.sym == sym) {
+			if (dep->right.sym == &symbol_no)
+				return true;
+		}
+		break;
+	default:
+		;
+	}
+ 	return false;
+}
+
+/*
+ * Inserts explicit comparisons of type 'type' to symbol 'sym' into the
+ * expression 'e'.
+ *
+ * Examples transformations for type == E_UNEQUAL, sym == &symbol_no:
+ *
+ *	A              ->  A!=n
+ *	!A             ->  A=n
+ *	A && B         ->  !(A=n || B=n)
+ *	A || B         ->  !(A=n && B=n)
+ *	A && (B || C)  ->  !(A=n || (B=n && C=n))
+ *
+ * Allocates and returns a new expression.
+ */
+struct expr *expr_trans_compare(struct expr *e, enum expr_type type, struct symbol *sym)
+{
+	struct expr *e1, *e2;
+
+	if (!e) {
+		e = expr_alloc_symbol(sym);
+		if (type == E_UNEQUAL)
+			e = expr_alloc_one(E_NOT, e);
+		return e;
+	}
+	switch (e->type) {
+	case E_AND:
+		e1 = expr_trans_compare(e->left.expr, E_EQUAL, sym);
+		e2 = expr_trans_compare(e->right.expr, E_EQUAL, sym);
+		if (sym == &symbol_yes)
+			e = expr_alloc_two(E_AND, e1, e2);
+		if (sym == &symbol_no)
+			e = expr_alloc_two(E_OR, e1, e2);
+		if (type == E_UNEQUAL)
+			e = expr_alloc_one(E_NOT, e);
+		return e;
+	case E_OR:
+		e1 = expr_trans_compare(e->left.expr, E_EQUAL, sym);
+		e2 = expr_trans_compare(e->right.expr, E_EQUAL, sym);
+		if (sym == &symbol_yes)
+			e = expr_alloc_two(E_OR, e1, e2);
+		if (sym == &symbol_no)
+			e = expr_alloc_two(E_AND, e1, e2);
+		if (type == E_UNEQUAL)
+			e = expr_alloc_one(E_NOT, e);
+		return e;
+	case E_NOT:
+		return expr_trans_compare(e->left.expr, type == E_EQUAL ? E_UNEQUAL : E_EQUAL, sym);
+	case E_UNEQUAL:
+	case E_LTH:
+	case E_LEQ:
+	case E_GTH:
+	case E_GEQ:
+	case E_EQUAL:
+		if (type == E_EQUAL) {
+			if (sym == &symbol_yes)
+				return expr_copy(e);
+			if (sym == &symbol_mod)
+				return expr_alloc_symbol(&symbol_no);
+			if (sym == &symbol_no)
+				return expr_alloc_one(E_NOT, expr_copy(e));
+		} else {
+			if (sym == &symbol_yes)
+				return expr_alloc_one(E_NOT, expr_copy(e));
+			if (sym == &symbol_mod)
+				return expr_alloc_symbol(&symbol_yes);
+			if (sym == &symbol_no)
+				return expr_copy(e);
+		}
+		break;
+	case E_SYMBOL:
+		return expr_alloc_comp(type, e->left.sym, sym);
+	case E_LIST:
+	case E_RANGE:
+	case E_NONE:
+		/* panic */;
+	}
+	return NULL;
+}
+
+enum string_value_kind {
+	k_string,
+	k_signed,
+	k_unsigned,
+};
+
+union string_value {
+	unsigned long long u;
+	signed long long s;
+};
+
+static enum string_value_kind expr_parse_string(const char *str,
+						enum symbol_type type,
+						union string_value *val)
+{
+	char *tail;
+	enum string_value_kind kind;
+
+	errno = 0;
+	switch (type) {
+	case S_BOOLEAN:
+	case S_TRISTATE:
+		val->s = !strcmp(str, "n") ? 0 :
+			 !strcmp(str, "m") ? 1 :
+			 !strcmp(str, "y") ? 2 : -1;
+		return k_signed;
+	case S_INT:
+		val->s = strtoll(str, &tail, 10);
+		kind = k_signed;
+		break;
+	case S_HEX:
+		val->u = strtoull(str, &tail, 16);
+		kind = k_unsigned;
+		break;
+	default:
+		val->s = strtoll(str, &tail, 0);
+		kind = k_signed;
+		break;
+	}
+	return !errno && !*tail && tail > str && isxdigit(tail[-1])
+	       ? kind : k_string;
+}
+
+tristate expr_calc_value(struct expr *e)
+{
+	tristate val1, val2;
+	const char *str1, *str2;
+	enum string_value_kind k1 = k_string, k2 = k_string;
+	union string_value lval = {}, rval = {};
+	int res;
+
+	if (!e)
+		return yes;
+
+	switch (e->type) {
+	case E_SYMBOL:
+		sym_calc_value(e->left.sym);
+		return e->left.sym->curr.tri;
+	case E_AND:
+		val1 = expr_calc_value(e->left.expr);
+		val2 = expr_calc_value(e->right.expr);
+		return EXPR_AND(val1, val2);
+	case E_OR:
+		val1 = expr_calc_value(e->left.expr);
+		val2 = expr_calc_value(e->right.expr);
+		return EXPR_OR(val1, val2);
+	case E_NOT:
+		val1 = expr_calc_value(e->left.expr);
+		return EXPR_NOT(val1);
+	case E_EQUAL:
+	case E_GEQ:
+	case E_GTH:
+	case E_LEQ:
+	case E_LTH:
+	case E_UNEQUAL:
+		break;
+	default:
+		printf("expr_calc_value: %d?\n", e->type);
+		return no;
+	}
+
+	sym_calc_value(e->left.sym);
+	sym_calc_value(e->right.sym);
+	str1 = sym_get_string_value(e->left.sym);
+	str2 = sym_get_string_value(e->right.sym);
+
+	if (e->left.sym->type != S_STRING || e->right.sym->type != S_STRING) {
+		k1 = expr_parse_string(str1, e->left.sym->type, &lval);
+		k2 = expr_parse_string(str2, e->right.sym->type, &rval);
+	}
+
+	if (k1 == k_string || k2 == k_string)
+		res = strcmp(str1, str2);
+	else if (k1 == k_unsigned || k2 == k_unsigned)
+		res = (lval.u > rval.u) - (lval.u < rval.u);
+	else /* if (k1 == k_signed && k2 == k_signed) */
+		res = (lval.s > rval.s) - (lval.s < rval.s);
+
+	switch(e->type) {
+	case E_EQUAL:
+		return res ? no : yes;
+	case E_GEQ:
+		return res >= 0 ? yes : no;
+	case E_GTH:
+		return res > 0 ? yes : no;
+	case E_LEQ:
+		return res <= 0 ? yes : no;
+	case E_LTH:
+		return res < 0 ? yes : no;
+	case E_UNEQUAL:
+		return res ? yes : no;
+	default:
+		printf("expr_calc_value: relation %d?\n", e->type);
+		return no;
+	}
+}
+
+static int expr_compare_type(enum expr_type t1, enum expr_type t2)
+{
+	if (t1 == t2)
+		return 0;
+	switch (t1) {
+	case E_LEQ:
+	case E_LTH:
+	case E_GEQ:
+	case E_GTH:
+		if (t2 == E_EQUAL || t2 == E_UNEQUAL)
+			return 1;
+	case E_EQUAL:
+	case E_UNEQUAL:
+		if (t2 == E_NOT)
+			return 1;
+	case E_NOT:
+		if (t2 == E_AND)
+			return 1;
+	case E_AND:
+		if (t2 == E_OR)
+			return 1;
+	case E_OR:
+		if (t2 == E_LIST)
+			return 1;
+	case E_LIST:
+		if (t2 == 0)
+			return 1;
+	default:
+		return -1;
+	}
+	printf("[%dgt%d?]", t1, t2);
+	return 0;
+}
+
+void expr_print(struct expr *e,
+		void (*fn)(void *, struct symbol *, const char *),
+		void *data, int prevtoken)
+{
+	if (!e) {
+		fn(data, NULL, "y");
+		return;
+	}
+
+	if (expr_compare_type(prevtoken, e->type) > 0)
+		fn(data, NULL, "(");
+	switch (e->type) {
+	case E_SYMBOL:
+		if (e->left.sym->name)
+			fn(data, e->left.sym, e->left.sym->name);
+		else
+			fn(data, NULL, "<choice>");
+		break;
+	case E_NOT:
+		fn(data, NULL, "!");
+		expr_print(e->left.expr, fn, data, E_NOT);
+		break;
+	case E_EQUAL:
+		if (e->left.sym->name)
+			fn(data, e->left.sym, e->left.sym->name);
+		else
+			fn(data, NULL, "<choice>");
+		fn(data, NULL, "=");
+		fn(data, e->right.sym, e->right.sym->name);
+		break;
+	case E_LEQ:
+	case E_LTH:
+		if (e->left.sym->name)
+			fn(data, e->left.sym, e->left.sym->name);
+		else
+			fn(data, NULL, "<choice>");
+		fn(data, NULL, e->type == E_LEQ ? "<=" : "<");
+		fn(data, e->right.sym, e->right.sym->name);
+		break;
+	case E_GEQ:
+	case E_GTH:
+		if (e->left.sym->name)
+			fn(data, e->left.sym, e->left.sym->name);
+		else
+			fn(data, NULL, "<choice>");
+		fn(data, NULL, e->type == E_GEQ ? ">=" : ">");
+		fn(data, e->right.sym, e->right.sym->name);
+		break;
+	case E_UNEQUAL:
+		if (e->left.sym->name)
+			fn(data, e->left.sym, e->left.sym->name);
+		else
+			fn(data, NULL, "<choice>");
+		fn(data, NULL, "!=");
+		fn(data, e->right.sym, e->right.sym->name);
+		break;
+	case E_OR:
+		expr_print(e->left.expr, fn, data, E_OR);
+		fn(data, NULL, " || ");
+		expr_print(e->right.expr, fn, data, E_OR);
+		break;
+	case E_AND:
+		expr_print(e->left.expr, fn, data, E_AND);
+		fn(data, NULL, " && ");
+		expr_print(e->right.expr, fn, data, E_AND);
+		break;
+	case E_LIST:
+		fn(data, e->right.sym, e->right.sym->name);
+		if (e->left.expr) {
+			fn(data, NULL, " ^ ");
+			expr_print(e->left.expr, fn, data, E_LIST);
+		}
+		break;
+	case E_RANGE:
+		fn(data, NULL, "[");
+		fn(data, e->left.sym, e->left.sym->name);
+		fn(data, NULL, " ");
+		fn(data, e->right.sym, e->right.sym->name);
+		fn(data, NULL, "]");
+		break;
+	default:
+	  {
+		char buf[32];
+		sprintf(buf, "<unknown type %d>", e->type);
+		fn(data, NULL, buf);
+		break;
+	  }
+	}
+	if (expr_compare_type(prevtoken, e->type) > 0)
+		fn(data, NULL, ")");
+}
+
+static void expr_print_file_helper(void *data, struct symbol *sym, const char *str)
+{
+	xfwrite(str, strlen(str), 1, data);
+}
+
+void expr_fprint(struct expr *e, FILE *out)
+{
+	expr_print(e, expr_print_file_helper, out, E_NONE);
+}
+
+static void expr_print_gstr_helper(void *data, struct symbol *sym, const char *str)
+{
+	struct gstr *gs = (struct gstr*)data;
+	const char *sym_str = NULL;
+
+	if (sym)
+		sym_str = sym_get_string_value(sym);
+
+	if (gs->max_width) {
+		unsigned extra_length = strlen(str);
+		const char *last_cr = strrchr(gs->s, '\n');
+		unsigned last_line_length;
+
+		if (sym_str)
+			extra_length += 4 + strlen(sym_str);
+
+		if (!last_cr)
+			last_cr = gs->s;
+
+		last_line_length = strlen(gs->s) - (last_cr - gs->s);
+
+		if ((last_line_length + extra_length) > gs->max_width)
+			str_append(gs, "\\\n");
+	}
+
+	str_append(gs, str);
+	if (sym && sym->type != S_UNKNOWN)
+		str_printf(gs, " [=%s]", sym_str);
+}
+
+void expr_gstr_print(struct expr *e, struct gstr *gs)
+{
+	expr_print(e, expr_print_gstr_helper, gs, E_NONE);
+}
+
+/*
+ * Transform the top level "||" tokens into newlines and prepend each
+ * line with a minus. This makes expressions much easier to read.
+ * Suitable for reverse dependency expressions.
+ */
+static void expr_print_revdep(struct expr *e,
+			      void (*fn)(void *, struct symbol *, const char *),
+			      void *data, tristate pr_type, const char **title)
+{
+	if (e->type == E_OR) {
+		expr_print_revdep(e->left.expr, fn, data, pr_type, title);
+		expr_print_revdep(e->right.expr, fn, data, pr_type, title);
+	} else if (expr_calc_value(e) == pr_type) {
+		if (*title) {
+			fn(data, NULL, *title);
+			*title = NULL;
+		}
+
+		fn(data, NULL, "  - ");
+		expr_print(e, fn, data, E_NONE);
+		fn(data, NULL, "\n");
+	}
+}
+
+void expr_gstr_print_revdep(struct expr *e, struct gstr *gs,
+			    tristate pr_type, const char *title)
+{
+	expr_print_revdep(e, expr_print_gstr_helper, gs, pr_type, &title);
+}
diff --git a/scripts/config/expr.h b/scripts/config/expr.h
new file mode 100644
index 0000000..005e27f
--- /dev/null
+++ b/scripts/config/expr.h
@@ -0,0 +1,326 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * 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>
+#include "list.h"
+#ifndef __cplusplus
+#include <stdbool.h>
+#endif
+
+struct file {
+	struct file *next;
+	struct file *parent;
+	const char *name;
+	int lineno;
+};
+
+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_LIST, E_SYMBOL, E_RANGE
+};
+
+union expr_data {
+	struct expr *expr;
+	struct symbol *sym;
+};
+
+struct expr {
+	enum expr_type type;
+	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))
+
+#define expr_list_for_each_sym(l, e, s) \
+	for (e = (l); e && (s = e->right.sym); e = e->left.expr)
+
+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 and have the
+ * SYMBOL_CHOICE bit set in 'flags'.
+ */
+struct symbol {
+	/* The next symbol in the same bucket in the symbol hash table */
+	struct symbol *next;
+
+	/* 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;
+
+	/* 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 for_all_symbols(i, sym) for (i = 0; i < SYMBOL_HASHSIZE; i++) for (sym = symbol_hash[i]; sym; sym = sym->next)
+
+#define SYMBOL_CONST      0x0001  /* symbol is const */
+#define SYMBOL_CHECK      0x0008  /* used during dependency checking */
+#define SYMBOL_CHOICE     0x0010  /* start of a choice block (null name) */
+#define SYMBOL_CHOICEVAL  0x0020  /* used as a value in a choice block */
+#define SYMBOL_VALID      0x0080  /* set when symbol.curr is calculated */
+#define SYMBOL_OPTIONAL   0x0100  /* choice is optional - values can be 'n' */
+#define SYMBOL_WRITE      0x0200  /* write symbol to file (KCONFIG_CONFIG) */
+#define SYMBOL_CHANGED    0x0400  /* ? */
+#define SYMBOL_WRITTEN    0x0800  /* track info to avoid double-write to .config */
+#define SYMBOL_NO_WRITE   0x1000  /* Symbol for internal use only; it will not be written */
+#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 */
+
+/* choice values need to be set before calculating this symbol value */
+#define SYMBOL_NEED_SET_CHOICE_VALUES  0x100000
+
+#define SYMBOL_MAXLENGTH	256
+#define SYMBOL_HASHSIZE		9973
+
+/* 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_CHOICE,   /* choice value */
+	P_SELECT,   /* select BAR */
+	P_IMPLY,    /* imply BAR */
+	P_RANGE,    /* range 7..100 (for a symbol) */
+	P_SYMBOL,   /* where a symbol is defined */
+	P_RESET,	/* reset to defaults condition */
+};
+
+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_CHOICE,
+	                            * P_PROMPT, P_DEFAULT, P_MENU, P_COMMENT */
+	struct file *file;         /* 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_choices(sym, st) for_all_properties(sym, st, P_CHOICE)
+#define for_all_prompts(sym, st) \
+	for (st = sym->prop; st; st = st->next) \
+		if (st->text)
+
+/*
+ * 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.
+ */
+struct menu {
+	/* 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;
+
+	/*
+	 * 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 */
+	struct file *file;
+	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 file *file_list;
+extern struct file *current_file;
+struct file *lookup_file(const char *name);
+
+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);
+struct expr *expr_copy(const struct expr *org);
+void expr_free(struct expr *e);
+void expr_eliminate_eq(struct expr **ep1, struct expr **ep2);
+int expr_eq(struct expr *e1, struct expr *e2);
+tristate expr_calc_value(struct expr *e);
+struct expr *expr_trans_bool(struct expr *e);
+struct expr *expr_eliminate_dups(struct expr *e);
+struct expr *expr_transform(struct expr *e);
+int 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(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 int expr_is_yes(struct expr *e)
+{
+	return !e || (e->type == E_SYMBOL && e->left.sym == &symbol_yes);
+}
+
+static inline int expr_is_no(struct expr *e)
+{
+	return e && (e->type == E_SYMBOL && e->left.sym == &symbol_no);
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* EXPR_H */
diff --git a/scripts/config/images.c b/scripts/config/images.c
new file mode 100644
index 0000000..ea3bfab
--- /dev/null
+++ b/scripts/config/images.c
@@ -0,0 +1,328 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * 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",
+"            ",
+"            ",
+"            ",
+"            ",
+"            ",
+"            ",
+"            ",
+"            ",
+"            ",
+"            ",
+"            ",
+"            "};
diff --git a/scripts/config/images.h b/scripts/config/images.h
new file mode 100644
index 0000000..1e3c736
--- /dev/null
+++ b/scripts/config/images.h
@@ -0,0 +1,33 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * 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 */
diff --git a/scripts/config/internal.h b/scripts/config/internal.h
new file mode 100644
index 0000000..2f7298c
--- /dev/null
+++ b/scripts/config/internal.h
@@ -0,0 +1,9 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+#ifndef INTERNAL_H
+#define INTERNAL_H
+
+struct menu;
+
+extern struct menu *current_menu, *current_entry;
+
+#endif /* INTERNAL_H */
diff --git a/scripts/config/lexer.l b/scripts/config/lexer.l
new file mode 100644
index 0000000..e73b262
--- /dev/null
+++ b/scripts/config/lexer.l
@@ -0,0 +1,522 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * 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 <glob.h>
+#include <libgen.h>
+
+#include "lkc.h"
+#include "parser.tab.h"
+
+#define YY_DECL		static int yylex1(void)
+
+#define START_STRSIZE	16
+
+static struct {
+	struct file *file;
+	int lineno;
+} current_pos;
+
+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;
+};
+
+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",
+	        current_file->name, 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;
+"optional"		return T_OPTIONAL;
+"prompt"		return T_PROMPT;
+"range"			return T_RANGE;
+"reset"			return T_RESET;
+"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",
+			zconf_curname(), zconf_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",
+			current_file->name, yylineno);
+
+	if (current_file) {
+		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 file/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.
+			 */
+			current_pos.file = current_file;
+			current_pos.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. */
+	while ((c = input()) != EOF) {
+		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);
+	}
+
+	current_buf = xmalloc(sizeof(*current_buf));
+	memset(current_buf, 0, sizeof(*current_buf));
+
+	current_file = file_lookup(name);
+	yylineno = 1;
+}
+
+static void __zconf_nextfile(const char *name)
+{
+	struct file *iter;
+	struct file *file = file_lookup(name);
+	struct buffer *buf = xmalloc(sizeof(*buf));
+	memset(buf, 0, sizeof(*buf));
+
+	current_buf->state = YY_CURRENT_BUFFER;
+	yyin = zconf_fopen(file->name);
+	if (!yyin) {
+		fprintf(stderr, "%s:%d: can't open file \"%s\"\n",
+			zconf_curname(), zconf_lineno(), file->name);
+		exit(1);
+	}
+	yy_switch_to_buffer(yy_create_buffer(yyin, YY_BUF_SIZE));
+	buf->parent = current_buf;
+	current_buf = buf;
+
+	current_file->lineno = yylineno;
+	file->parent = current_file;
+
+	for (iter = current_file; iter; iter = iter->parent) {
+		if (!strcmp(iter->name, file->name)) {
+			fprintf(stderr,
+				"Recursive inclusion detected.\n"
+				"Inclusion path:\n"
+				"  current file : %s\n", file->name);
+			iter = file;
+			do {
+				iter = iter->parent;
+				fprintf(stderr, "  included from: %s:%d\n",
+					iter->name, iter->lineno - 1);
+			} while (strcmp(iter->name, file->name));
+			exit(1);
+		}
+	}
+
+	yylineno = 1;
+	current_file = file;
+}
+
+void zconf_nextfile(const char *name)
+{
+	glob_t gl;
+	int err;
+	int i;
+	char path[PATH_MAX], *p;
+
+	err = glob(name, GLOB_ERR | GLOB_MARK, NULL, &gl);
+
+	/* ignore wildcard patterns that return no result */
+	if (err == GLOB_NOMATCH && strchr(name, '*')) {
+		err = 0;
+		gl.gl_pathc = 0;
+	}
+
+	if (err == GLOB_NOMATCH) {
+		p = strdup(current_file->name);
+		if (p) {
+			snprintf(path, sizeof(path), "%s/%s", dirname(p), name);
+			err = glob(path, GLOB_ERR | GLOB_MARK, NULL, &gl);
+			free(p);
+		}
+	}
+
+	if (err) {
+		const char *reason = "unknown error";
+
+		switch (err) {
+		case GLOB_NOSPACE:
+			reason = "out of memory";
+			break;
+		case GLOB_ABORTED:
+			reason = "read error";
+			break;
+		case GLOB_NOMATCH:
+			reason = "No files found";
+			break;
+		default:
+			break;
+		}
+
+		printf("%s:%d: glob failed: %s \"%s\"\n", zconf_curname(), zconf_lineno(),
+			reason, name);
+
+		exit(1);
+	}
+
+	for (i = 0; i < gl.gl_pathc; i++)
+		__zconf_nextfile(gl.gl_pathv[i]);
+}
+
+static void zconf_endfile(void)
+{
+	struct buffer *parent;
+
+	current_file = current_file->parent;
+	if (current_file)
+		yylineno = current_file->lineno;
+
+	parent = current_buf->parent;
+	if (parent) {
+		fclose(yyin);
+		yy_delete_buffer(YY_CURRENT_BUFFER);
+		yy_switch_to_buffer(parent->state);
+	}
+	free(current_buf);
+	current_buf = parent;
+}
+
+int zconf_lineno(void)
+{
+	return current_pos.lineno;
+}
+
+const char *zconf_curname(void)
+{
+	return current_pos.file ? current_pos.file->name : "<none>";
+}
diff --git a/scripts/config/lexer.lex.c b/scripts/config/lexer.lex.c
new file mode 100644
index 0000000..c57119f
--- /dev/null
+++ b/scripts/config/lexer.lex.c
@@ -0,0 +1,4203 @@
+
+#define  YY_INT_ALIGNED short int
+
+/* A lexical scanner generated by flex */
+
+#define FLEX_SCANNER
+#define YY_FLEX_MAJOR_VERSION 2
+#define YY_FLEX_MINOR_VERSION 6
+#define YY_FLEX_SUBMINOR_VERSION 4
+#if YY_FLEX_SUBMINOR_VERSION > 0
+#define FLEX_BETA
+#endif
+
+/* First, we deal with  platform-specific or compiler-specific issues. */
+
+/* begin standard C headers. */
+
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <stdlib.h>
+
+/* end standard C headers. */
+
+/* flex integer type definitions */
+
+#ifndef FLEXINT_H
+#define FLEXINT_H
+
+/* C99 systems have <inttypes.h>. Non-C99 systems may or may not. */
+
+#if defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L
+
+/* C++ systems might need __STDC_LIMIT_MACROS defined before including
+ * <stdint.h>, if you want the limit (max/min) macros for int types.
+ */
+#ifndef __STDC_LIMIT_MACROS
+#define __STDC_LIMIT_MACROS 1
+#endif
+
+#include <inttypes.h>
+typedef int8_t flex_int8_t;
+typedef uint8_t flex_uint8_t;
+typedef int16_t flex_int16_t;
+typedef uint16_t flex_uint16_t;
+typedef int32_t flex_int32_t;
+typedef uint32_t flex_uint32_t;
+#else
+typedef signed char flex_int8_t;
+typedef short int flex_int16_t;
+typedef int flex_int32_t;
+typedef unsigned char flex_uint8_t; 
+typedef unsigned short int flex_uint16_t;
+typedef unsigned int flex_uint32_t;
+
+/* Limits of integral types. */
+#ifndef INT8_MIN
+#define INT8_MIN               (-128)
+#endif
+#ifndef INT16_MIN
+#define INT16_MIN              (-32767-1)
+#endif
+#ifndef INT32_MIN
+#define INT32_MIN              (-2147483647-1)
+#endif
+#ifndef INT8_MAX
+#define INT8_MAX               (127)
+#endif
+#ifndef INT16_MAX
+#define INT16_MAX              (32767)
+#endif
+#ifndef INT32_MAX
+#define INT32_MAX              (2147483647)
+#endif
+#ifndef UINT8_MAX
+#define UINT8_MAX              (255U)
+#endif
+#ifndef UINT16_MAX
+#define UINT16_MAX             (65535U)
+#endif
+#ifndef UINT32_MAX
+#define UINT32_MAX             (4294967295U)
+#endif
+
+#ifndef SIZE_MAX
+#define SIZE_MAX               (~(size_t)0)
+#endif
+
+#endif /* ! C99 */
+
+#endif /* ! FLEXINT_H */
+
+/* begin standard C++ headers. */
+
+/* TODO: this is always defined, so inline it */
+#define yyconst const
+
+#if defined(__GNUC__) && __GNUC__ >= 3
+#define yynoreturn __attribute__((__noreturn__))
+#else
+#define yynoreturn
+#endif
+
+/* Returned upon end-of-file. */
+#define YY_NULL 0
+
+/* Promotes a possibly negative, possibly signed char to an
+ *   integer in range [0..255] for use as an array index.
+ */
+#define YY_SC_TO_UI(c) ((YY_CHAR) (c))
+
+/* Enter a start condition.  This macro really ought to take a parameter,
+ * but we do it the disgusting crufty way forced on us by the ()-less
+ * definition of BEGIN.
+ */
+#define BEGIN (yy_start) = 1 + 2 *
+/* Translate the current start state into a value that can be later handed
+ * to BEGIN to return to the state.  The YYSTATE alias is for lex
+ * compatibility.
+ */
+#define YY_START (((yy_start) - 1) / 2)
+#define YYSTATE YY_START
+/* Action number for EOF rule of a given start state. */
+#define YY_STATE_EOF(state) (YY_END_OF_BUFFER + state + 1)
+/* Special action meaning "start processing a new file". */
+#define YY_NEW_FILE yyrestart( yyin  )
+#define YY_END_OF_BUFFER_CHAR 0
+
+/* Size of default input buffer. */
+#ifndef YY_BUF_SIZE
+#ifdef __ia64__
+/* On IA-64, the buffer size is 16k, not 8k.
+ * Moreover, YY_BUF_SIZE is 2*YY_READ_BUF_SIZE in the general case.
+ * Ditto for the __ia64__ case accordingly.
+ */
+#define YY_BUF_SIZE 32768
+#else
+#define YY_BUF_SIZE 16384
+#endif /* __ia64__ */
+#endif
+
+/* The state buf must be large enough to hold one state per character in the main buffer.
+ */
+#define YY_STATE_BUF_SIZE   ((YY_BUF_SIZE + 2) * sizeof(yy_state_type))
+
+#ifndef YY_TYPEDEF_YY_BUFFER_STATE
+#define YY_TYPEDEF_YY_BUFFER_STATE
+typedef struct yy_buffer_state *YY_BUFFER_STATE;
+#endif
+
+#ifndef YY_TYPEDEF_YY_SIZE_T
+#define YY_TYPEDEF_YY_SIZE_T
+typedef size_t yy_size_t;
+#endif
+
+extern int yyleng;
+
+extern FILE *yyin, *yyout;
+
+#define EOB_ACT_CONTINUE_SCAN 0
+#define EOB_ACT_END_OF_FILE 1
+#define EOB_ACT_LAST_MATCH 2
+    
+    /* Note: We specifically omit the test for yy_rule_can_match_eol because it requires
+     *       access to the local variable yy_act. Since yyless() is a macro, it would break
+     *       existing scanners that call yyless() from OUTSIDE yylex.
+     *       One obvious solution it to make yy_act a global. I tried that, and saw
+     *       a 5% performance hit in a non-yylineno scanner, because yy_act is
+     *       normally declared as a register variable-- so it is not worth it.
+     */
+    #define  YY_LESS_LINENO(n) \
+            do { \
+                int yyl;\
+                for ( yyl = n; yyl < yyleng; ++yyl )\
+                    if ( yytext[yyl] == '\n' )\
+                        --yylineno;\
+            }while(0)
+    #define YY_LINENO_REWIND_TO(dst) \
+            do {\
+                const char *p;\
+                for ( p = yy_cp-1; p >= (dst); --p)\
+                    if ( *p == '\n' )\
+                        --yylineno;\
+            }while(0)
+    
+/* Return all but the first "n" matched characters back to the input stream. */
+#define yyless(n) \
+	do \
+		{ \
+		/* Undo effects of setting up yytext. */ \
+        int yyless_macro_arg = (n); \
+        YY_LESS_LINENO(yyless_macro_arg);\
+		*yy_cp = (yy_hold_char); \
+		YY_RESTORE_YY_MORE_OFFSET \
+		(yy_c_buf_p) = yy_cp = yy_bp + yyless_macro_arg - YY_MORE_ADJ; \
+		YY_DO_BEFORE_ACTION; /* set up yytext again */ \
+		} \
+	while ( 0 )
+#define unput(c) yyunput( c, (yytext_ptr)  )
+
+#ifndef YY_STRUCT_YY_BUFFER_STATE
+#define YY_STRUCT_YY_BUFFER_STATE
+struct yy_buffer_state
+	{
+	FILE *yy_input_file;
+
+	char *yy_ch_buf;		/* input buffer */
+	char *yy_buf_pos;		/* current position in input buffer */
+
+	/* Size of input buffer in bytes, not including room for EOB
+	 * characters.
+	 */
+	int yy_buf_size;
+
+	/* Number of characters read into yy_ch_buf, not including EOB
+	 * characters.
+	 */
+	int yy_n_chars;
+
+	/* Whether we "own" the buffer - i.e., we know we created it,
+	 * and can realloc() it to grow it, and should free() it to
+	 * delete it.
+	 */
+	int yy_is_our_buffer;
+
+	/* Whether this is an "interactive" input source; if so, and
+	 * if we're using stdio for input, then we want to use getc()
+	 * instead of fread(), to make sure we stop fetching input after
+	 * each newline.
+	 */
+	int yy_is_interactive;
+
+	/* Whether we're considered to be at the beginning of a line.
+	 * If so, '^' rules will be active on the next match, otherwise
+	 * not.
+	 */
+	int yy_at_bol;
+
+    int yy_bs_lineno; /**< The line count. */
+    int yy_bs_column; /**< The column count. */
+
+	/* Whether to try to fill the input buffer when we reach the
+	 * end of it.
+	 */
+	int yy_fill_buffer;
+
+	int yy_buffer_status;
+
+#define YY_BUFFER_NEW 0
+#define YY_BUFFER_NORMAL 1
+	/* When an EOF's been seen but there's still some text to process
+	 * then we mark the buffer as YY_EOF_PENDING, to indicate that we
+	 * shouldn't try reading from the input source any more.  We might
+	 * still have a bunch of tokens to match, though, because of
+	 * possible backing-up.
+	 *
+	 * When we actually see the EOF, we change the status to "new"
+	 * (via yyrestart()), so that the user can continue scanning by
+	 * just pointing yyin at a new input file.
+	 */
+#define YY_BUFFER_EOF_PENDING 2
+
+	};
+#endif /* !YY_STRUCT_YY_BUFFER_STATE */
+
+/* Stack of input buffers. */
+static size_t yy_buffer_stack_top = 0; /**< index of top of stack. */
+static size_t yy_buffer_stack_max = 0; /**< capacity of stack. */
+static YY_BUFFER_STATE * yy_buffer_stack = NULL; /**< Stack as an array. */
+
+/* We provide macros for accessing buffer states in case in the
+ * future we want to put the buffer states in a more general
+ * "scanner state".
+ *
+ * Returns the top of the stack, or NULL.
+ */
+#define YY_CURRENT_BUFFER ( (yy_buffer_stack) \
+                          ? (yy_buffer_stack)[(yy_buffer_stack_top)] \
+                          : NULL)
+/* Same as previous macro, but useful when we know that the buffer stack is not
+ * NULL or when we need an lvalue. For internal use only.
+ */
+#define YY_CURRENT_BUFFER_LVALUE (yy_buffer_stack)[(yy_buffer_stack_top)]
+
+/* yy_hold_char holds the character lost when yytext is formed. */
+static char yy_hold_char;
+static int yy_n_chars;		/* number of characters read into yy_ch_buf */
+int yyleng;
+
+/* Points to current character in buffer. */
+static char *yy_c_buf_p = NULL;
+static int yy_init = 0;		/* whether we need to initialize */
+static int yy_start = 0;	/* start state number */
+
+/* Flag which is used to allow yywrap()'s to do buffer switches
+ * instead of setting up a fresh yyin.  A bit of a hack ...
+ */
+static int yy_did_buffer_switch_on_eof;
+
+void yyrestart ( FILE *input_file  );
+void yy_switch_to_buffer ( YY_BUFFER_STATE new_buffer  );
+YY_BUFFER_STATE yy_create_buffer ( FILE *file, int size  );
+void yy_delete_buffer ( YY_BUFFER_STATE b  );
+void yy_flush_buffer ( YY_BUFFER_STATE b  );
+void yypush_buffer_state ( YY_BUFFER_STATE new_buffer  );
+void yypop_buffer_state ( void );
+
+static void yyensure_buffer_stack ( void );
+static void yy_load_buffer_state ( void );
+static void yy_init_buffer ( YY_BUFFER_STATE b, FILE *file  );
+#define YY_FLUSH_BUFFER yy_flush_buffer( YY_CURRENT_BUFFER )
+
+YY_BUFFER_STATE yy_scan_buffer ( char *base, yy_size_t size  );
+YY_BUFFER_STATE yy_scan_string ( const char *yy_str  );
+YY_BUFFER_STATE yy_scan_bytes ( const char *bytes, int len  );
+
+void *yyalloc ( yy_size_t  );
+void *yyrealloc ( void *, yy_size_t  );
+void yyfree ( void *  );
+
+#define yy_new_buffer yy_create_buffer
+#define yy_set_interactive(is_interactive) \
+	{ \
+	if ( ! YY_CURRENT_BUFFER ){ \
+        yyensure_buffer_stack (); \
+		YY_CURRENT_BUFFER_LVALUE =    \
+            yy_create_buffer( yyin, YY_BUF_SIZE ); \
+	} \
+	YY_CURRENT_BUFFER_LVALUE->yy_is_interactive = is_interactive; \
+	}
+#define yy_set_bol(at_bol) \
+	{ \
+	if ( ! YY_CURRENT_BUFFER ){\
+        yyensure_buffer_stack (); \
+		YY_CURRENT_BUFFER_LVALUE =    \
+            yy_create_buffer( yyin, YY_BUF_SIZE ); \
+	} \
+	YY_CURRENT_BUFFER_LVALUE->yy_at_bol = at_bol; \
+	}
+#define YY_AT_BOL() (YY_CURRENT_BUFFER_LVALUE->yy_at_bol)
+
+/* Begin user sect3 */
+
+#define yywrap() (/*CONSTCOND*/1)
+#define YY_SKIP_YYWRAP
+typedef flex_uint8_t YY_CHAR;
+
+FILE *yyin = NULL, *yyout = NULL;
+
+typedef int yy_state_type;
+
+extern int yylineno;
+int yylineno = 1;
+
+extern char *yytext;
+#ifdef yytext_ptr
+#undef yytext_ptr
+#endif
+#define yytext_ptr yytext
+
+static const flex_int16_t yy_nxt[][43] =
+    {
+    {
+        0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+        0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+        0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+        0,    0,    0,    0,    0,    0,    0,    0,    0,    0,
+        0,    0,    0
+    },
+
+    {
+        9,   10,   11,   12,   13,   14,   15,   16,   17,   14,
+       18,   19,   20,   21,   21,   22,   23,   24,   25,   26,
+       21,   21,   27,   28,   29,   30,   21,   21,   31,   32,
+       21,   33,   21,   34,   35,   36,   37,   38,   21,   39,
+       21,   21,   40
+
+    },
+
+    {
+        9,   10,   11,   12,   13,   14,   15,   16,   17,   14,
+       18,   19,   20,   21,   21,   22,   23,   24,   25,   26,
+       21,   21,   27,   28,   29,   30,   21,   21,   31,   32,
+       21,   33,   21,   34,   35,   36,   37,   38,   21,   39,
+       21,   21,   40
+    },
+
+    {
+        9,   41,   42,   43,   41,   41,   41,   41,   41,   41,
+       41,   41,   41,   41,   41,   41,   41,   41,   41,   41,
+       41,   41,   41,   41,   41,   41,   41,   41,   41,   41,
+       41,   41,   41,   41,   41,   41,   41,   41,   41,   41,
+       41,   41,   41
+
+    },
+
+    {
+        9,   41,   42,   43,   41,   41,   41,   41,   41,   41,
+       41,   41,   41,   41,   41,   41,   41,   41,   41,   41,
+       41,   41,   41,   41,   41,   41,   41,   41,   41,   41,
+       41,   41,   41,   41,   41,   41,   41,   41,   41,   41,
+       41,   41,   41
+    },
+
+    {
+        9,   44,   45,   46,   44,   44,   44,   44,   44,   44,
+       44,   44,   44,   44,   44,   44,   44,   44,   44,   44,
+       44,   44,   44,   44,   44,   44,   44,   44,   44,   44,
+       44,   44,   44,   44,   44,   44,   44,   44,   44,   44,
+       44,   44,   44
+
+    },
+
+    {
+        9,   44,   45,   46,   44,   44,   44,   44,   44,   44,
+       44,   44,   44,   44,   44,   44,   44,   44,   44,   44,
+       44,   44,   44,   44,   44,   44,   44,   44,   44,   44,
+       44,   44,   44,   44,   44,   44,   44,   44,   44,   44,
+       44,   44,   44
+    },
+
+    {
+        9,   47,   47,   48,   47,   49,   47,   50,   47,   49,
+       47,   47,   47,   47,   47,   47,   47,   47,   47,   51,
+       47,   47,   47,   47,   47,   47,   47,   47,   47,   47,
+       47,   47,   47,   47,   47,   47,   47,   47,   47,   47,
+       47,   47,   47
+
+    },
+
+    {
+        9,   47,   47,   48,   47,   49,   47,   50,   47,   49,
+       47,   47,   47,   47,   47,   47,   47,   47,   47,   51,
+       47,   47,   47,   47,   47,   47,   47,   47,   47,   47,
+       47,   47,   47,   47,   47,   47,   47,   47,   47,   47,
+       47,   47,   47
+    },
+
+    {
+       -9,   -9,   -9,   -9,   -9,   -9,   -9,   -9,   -9,   -9,
+       -9,   -9,   -9,   -9,   -9,   -9,   -9,   -9,   -9,   -9,
+       -9,   -9,   -9,   -9,   -9,   -9,   -9,   -9,   -9,   -9,
+       -9,   -9,   -9,   -9,   -9,   -9,   -9,   -9,   -9,   -9,
+       -9,   -9,   -9
+
+    },
+
+    {
+        9,  -10,  -10,  -10,  -10,  -10,  -10,  -10,  -10,  -10,
+      -10,  -10,  -10,  -10,  -10,  -10,  -10,  -10,  -10,  -10,
+      -10,  -10,  -10,  -10,  -10,  -10,  -10,  -10,  -10,  -10,
+      -10,  -10,  -10,  -10,  -10,  -10,  -10,  -10,  -10,  -10,
+      -10,  -10,  -10
+    },
+
+    {
+        9,  -11,   52,  -11,  -11,  -11,  -11,  -11,  -11,  -11,
+      -11,  -11,  -11,  -11,  -11,  -11,  -11,  -11,  -11,  -11,
+      -11,  -11,  -11,  -11,  -11,  -11,  -11,  -11,  -11,  -11,
+      -11,  -11,  -11,  -11,  -11,  -11,  -11,  -11,  -11,  -11,
+      -11,  -11,  -11
+
+    },
+
+    {
+        9,  -12,  -12,  -12,  -12,  -12,  -12,  -12,  -12,  -12,
+      -12,  -12,  -12,  -12,  -12,  -12,  -12,  -12,  -12,  -12,
+      -12,  -12,  -12,  -12,  -12,  -12,  -12,  -12,  -12,  -12,
+      -12,  -12,  -12,  -12,  -12,  -12,  -12,  -12,  -12,  -12,
+      -12,  -12,  -12
+    },
+
+    {
+        9,  -13,  -13,  -13,  -13,  -13,  -13,  -13,  -13,  -13,
+      -13,  -13,  -13,  -13,  -13,  -13,  -13,   53,  -13,  -13,
+      -13,  -13,  -13,  -13,  -13,  -13,  -13,  -13,  -13,  -13,
+      -13,  -13,  -13,  -13,  -13,  -13,  -13,  -13,  -13,  -13,
+      -13,  -13,  -13
+
+    },
+
+    {
+        9,  -14,  -14,  -14,  -14,  -14,  -14,  -14,  -14,  -14,
+      -14,  -14,  -14,  -14,  -14,  -14,  -14,  -14,  -14,  -14,
+      -14,  -14,  -14,  -14,  -14,  -14,  -14,  -14,  -14,  -14,
+      -14,  -14,  -14,  -14,  -14,  -14,  -14,  -14,  -14,  -14,
+      -14,  -14,  -14
+    },
+
+    {
+        9,   54,   54,  -15,   54,   54,   54,   54,   54,   54,
+       54,   54,   54,   54,   54,   54,   54,   54,   54,   54,
+       54,   54,   54,   54,   54,   54,   54,   54,   54,   54,
+       54,   54,   54,   54,   54,   54,   54,   54,   54,   54,
+       54,   54,   54
+
+    },
+
+    {
+        9,  -16,  -16,  -16,  -16,  -16,  -16,   55,  -16,  -16,
+      -16,  -16,  -16,   55,   55,  -16,  -16,  -16,  -16,  -16,
+       55,   55,   55,   55,   55,   55,   55,   55,   55,   55,
+       55,   55,   55,   55,   55,   55,   55,   55,   55,   55,
+       55,   55,  -16
+    },
+
+    {
+        9,  -17,  -17,  -17,  -17,  -17,  -17,  -17,   56,  -17,
+      -17,  -17,  -17,  -17,  -17,  -17,  -17,  -17,  -17,  -17,
+      -17,  -17,  -17,  -17,  -17,  -17,  -17,  -17,  -17,  -17,
+      -17,  -17,  -17,  -17,  -17,  -17,  -17,  -17,  -17,  -17,
+      -17,  -17,  -17
+
+    },
+
+    {
+        9,  -18,  -18,  -18,  -18,  -18,  -18,  -18,  -18,  -18,
+      -18,  -18,  -18,  -18,  -18,  -18,  -18,  -18,  -18,  -18,
+      -18,  -18,  -18,  -18,  -18,  -18,  -18,  -18,  -18,  -18,
+      -18,  -18,  -18,  -18,  -18,  -18,  -18,  -18,  -18,  -18,
+      -18,  -18,  -18
+    },
+
+    {
+        9,  -19,  -19,  -19,  -19,  -19,  -19,  -19,  -19,  -19,
+      -19,  -19,  -19,  -19,  -19,  -19,  -19,  -19,  -19,  -19,
+      -19,  -19,  -19,  -19,  -19,  -19,  -19,  -19,  -19,  -19,
+      -19,  -19,  -19,  -19,  -19,  -19,  -19,  -19,  -19,  -19,
+      -19,  -19,  -19
+
+    },
+
+    {
+        9,  -20,  -20,  -20,  -20,  -20,  -20,  -20,  -20,  -20,
+      -20,  -20,  -20,  -20,  -20,  -20,  -20,   57,  -20,  -20,
+      -20,  -20,  -20,  -20,  -20,  -20,  -20,  -20,  -20,  -20,
+      -20,  -20,  -20,  -20,  -20,  -20,  -20,  -20,  -20,  -20,
+      -20,  -20,  -20
+    },
+
+    {
+        9,  -21,  -21,  -21,  -21,  -21,  -21,   55,  -21,  -21,
+      -21,  -21,  -21,   58,   58,  -21,  -21,  -21,  -21,  -21,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,  -21
+
+    },
+
+    {
+        9,  -22,  -22,  -22,  -22,  -22,  -22,  -22,  -22,  -22,
+      -22,  -22,  -22,  -22,  -22,  -22,  -22,   59,  -22,  -22,
+      -22,  -22,  -22,  -22,  -22,  -22,  -22,  -22,  -22,  -22,
+      -22,  -22,  -22,  -22,  -22,  -22,  -22,  -22,  -22,  -22,
+      -22,  -22,  -22
+    },
+
+    {
+        9,  -23,  -23,  -23,  -23,  -23,  -23,  -23,  -23,  -23,
+      -23,  -23,  -23,  -23,  -23,  -23,  -23,   60,  -23,  -23,
+      -23,  -23,  -23,  -23,  -23,  -23,  -23,  -23,  -23,  -23,
+      -23,  -23,  -23,  -23,  -23,  -23,  -23,  -23,  -23,  -23,
+      -23,  -23,  -23
+
+    },
+
+    {
+        9,  -24,  -24,  -24,  -24,  -24,  -24,  -24,  -24,  -24,
+      -24,  -24,  -24,  -24,  -24,  -24,  -24,  -24,  -24,  -24,
+      -24,  -24,  -24,  -24,  -24,  -24,  -24,  -24,  -24,  -24,
+      -24,  -24,  -24,  -24,  -24,  -24,  -24,  -24,  -24,  -24,
+      -24,  -24,  -24
+    },
+
+    {
+        9,  -25,  -25,  -25,  -25,  -25,  -25,  -25,  -25,  -25,
+      -25,  -25,  -25,  -25,  -25,  -25,  -25,   61,  -25,  -25,
+      -25,  -25,  -25,  -25,  -25,  -25,  -25,  -25,  -25,  -25,
+      -25,  -25,  -25,  -25,  -25,  -25,  -25,  -25,  -25,  -25,
+      -25,  -25,  -25
+
+    },
+
+    {
+        9,  -26,  -26,   62,  -26,  -26,  -26,  -26,  -26,  -26,
+      -26,  -26,  -26,  -26,  -26,  -26,  -26,  -26,  -26,  -26,
+      -26,  -26,  -26,  -26,  -26,  -26,  -26,  -26,  -26,  -26,
+      -26,  -26,  -26,  -26,  -26,  -26,  -26,  -26,  -26,  -26,
+      -26,  -26,  -26
+    },
+
+    {
+        9,  -27,  -27,  -27,  -27,  -27,  -27,   55,  -27,  -27,
+      -27,  -27,  -27,   58,   58,  -27,  -27,  -27,  -27,  -27,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   63,   58,   58,   58,   58,   58,   58,
+       58,   58,  -27
+
+    },
+
+    {
+        9,  -28,  -28,  -28,  -28,  -28,  -28,   55,  -28,  -28,
+      -28,  -28,  -28,   58,   58,  -28,  -28,  -28,  -28,  -28,
+       58,   58,   58,   58,   58,   58,   58,   58,   64,   58,
+       58,   58,   58,   65,   58,   58,   58,   58,   58,   58,
+       58,   58,  -28
+    },
+
+    {
+        9,  -29,  -29,  -29,  -29,  -29,  -29,   55,  -29,  -29,
+      -29,  -29,  -29,   58,   58,  -29,  -29,  -29,  -29,  -29,
+       58,   58,   58,   58,   58,   66,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,  -29
+
+    },
+
+    {
+        9,  -30,  -30,  -30,  -30,  -30,  -30,   55,  -30,  -30,
+      -30,  -30,  -30,   58,   58,  -30,  -30,  -30,  -30,  -30,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   67,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,  -30
+    },
+
+    {
+        9,  -31,  -31,  -31,  -31,  -31,  -31,   55,  -31,  -31,
+      -31,  -31,  -31,   58,   58,  -31,  -31,  -31,  -31,  -31,
+       58,   58,   58,   58,   58,   68,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,  -31
+
+    },
+
+    {
+        9,  -32,  -32,  -32,  -32,  -32,  -32,   55,  -32,  -32,
+      -32,  -32,  -32,   58,   58,  -32,  -32,  -32,  -32,  -32,
+       58,   58,   58,   58,   58,   58,   69,   58,   58,   58,
+       58,   70,   71,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,  -32
+    },
+
+    {
+        9,  -33,  -33,  -33,  -33,  -33,  -33,   55,  -33,  -33,
+      -33,  -33,  -33,   58,   58,  -33,  -33,  -33,  -33,  -33,
+       58,   72,   58,   58,   58,   73,   58,   58,   58,   58,
+       58,   58,   58,   74,   58,   58,   58,   58,   58,   58,
+       58,   58,  -33
+
+    },
+
+    {
+        9,  -34,  -34,  -34,  -34,  -34,  -34,   55,  -34,  -34,
+      -34,  -34,  -34,   58,   58,  -34,  -34,  -34,  -34,  -34,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   75,   58,   76,   58,   58,   58,   58,   58,
+       58,   58,  -34
+    },
+
+    {
+        9,  -35,  -35,  -35,  -35,  -35,  -35,   55,  -35,  -35,
+      -35,  -35,  -35,   58,   58,  -35,  -35,  -35,  -35,  -35,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   77,   58,   58,   58,   58,
+       58,   58,  -35
+
+    },
+
+    {
+        9,  -36,  -36,  -36,  -36,  -36,  -36,   55,  -36,  -36,
+      -36,  -36,  -36,   58,   58,  -36,  -36,  -36,  -36,  -36,
+       58,   78,   58,   58,   58,   79,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,  -36
+    },
+
+    {
+        9,  -37,  -37,  -37,  -37,  -37,  -37,   55,  -37,  -37,
+      -37,  -37,  -37,   58,   58,  -37,  -37,  -37,  -37,  -37,
+       58,   58,   58,   58,   58,   80,   58,   58,   58,   58,
+       58,   58,   58,   81,   58,   58,   58,   82,   58,   58,
+       58,   58,  -37
+
+    },
+
+    {
+        9,  -38,  -38,  -38,  -38,  -38,  -38,   55,  -38,  -38,
+      -38,  -38,  -38,   58,   58,  -38,  -38,  -38,  -38,  -38,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   83,   58,   58,   58,   58,
+       58,   58,  -38
+    },
+
+    {
+        9,  -39,  -39,  -39,  -39,  -39,  -39,   55,  -39,  -39,
+      -39,  -39,  -39,   58,   58,  -39,  -39,  -39,  -39,  -39,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   84,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,  -39
+
+    },
+
+    {
+        9,  -40,  -40,  -40,  -40,  -40,  -40,  -40,  -40,  -40,
+      -40,  -40,  -40,  -40,  -40,  -40,  -40,  -40,  -40,  -40,
+      -40,  -40,  -40,  -40,  -40,  -40,  -40,  -40,  -40,  -40,
+      -40,  -40,  -40,  -40,  -40,  -40,  -40,  -40,  -40,  -40,
+      -40,  -40,   85
+    },
+
+    {
+        9,   86,   87,  -41,   86,   86,   86,   86,   86,   86,
+       86,   86,   86,   86,   86,   86,   86,   86,   86,   86,
+       86,   86,   86,   86,   86,   86,   86,   86,   86,   86,
+       86,   86,   86,   86,   86,   86,   86,   86,   86,   86,
+       86,   86,   86
+
+    },
+
+    {
+        9,  -42,  -42,  -42,  -42,  -42,  -42,  -42,  -42,  -42,
+      -42,  -42,  -42,  -42,  -42,  -42,  -42,  -42,  -42,  -42,
+      -42,  -42,  -42,  -42,  -42,  -42,  -42,  -42,  -42,  -42,
+      -42,  -42,  -42,  -42,  -42,  -42,  -42,  -42,  -42,  -42,
+      -42,  -42,  -42
+    },
+
+    {
+        9,  -43,  -43,  -43,  -43,  -43,  -43,  -43,  -43,  -43,
+      -43,  -43,  -43,  -43,  -43,  -43,  -43,  -43,  -43,  -43,
+      -43,  -43,  -43,  -43,  -43,  -43,  -43,  -43,  -43,  -43,
+      -43,  -43,  -43,  -43,  -43,  -43,  -43,  -43,  -43,  -43,
+      -43,  -43,  -43
+
+    },
+
+    {
+        9,   88,   88,  -44,   88,   88,   88,   88,   88,   88,
+       88,   88,   88,   88,   88,   88,   88,   88,   88,   88,
+       88,   88,   88,   88,   88,   88,   88,   88,   88,   88,
+       88,   88,   88,   88,   88,   88,   88,   88,   88,   88,
+       88,   88,   88
+    },
+
+    {
+        9,  -45,   89,   90,  -45,  -45,  -45,  -45,  -45,  -45,
+      -45,  -45,  -45,  -45,  -45,  -45,  -45,  -45,  -45,  -45,
+      -45,  -45,  -45,  -45,  -45,  -45,  -45,  -45,  -45,  -45,
+      -45,  -45,  -45,  -45,  -45,  -45,  -45,  -45,  -45,  -45,
+      -45,  -45,  -45
+
+    },
+
+    {
+        9,   91,  -46,  -46,   91,   91,   91,   91,   91,   91,
+       91,   91,   91,   91,   91,   91,   91,   91,   91,   91,
+       91,   91,   91,   91,   91,   91,   91,   91,   91,   91,
+       91,   91,   91,   91,   91,   91,   91,   91,   91,   91,
+       91,   91,   91
+    },
+
+    {
+        9,   92,   92,  -47,   92,  -47,   92,  -47,   92,  -47,
+       92,   92,   92,   92,   92,   92,   92,   92,   92,  -47,
+       92,   92,   92,   92,   92,   92,   92,   92,   92,   92,
+       92,   92,   92,   92,   92,   92,   92,   92,   92,   92,
+       92,   92,   92
+
+    },
+
+    {
+        9,  -48,  -48,  -48,  -48,  -48,  -48,  -48,  -48,  -48,
+      -48,  -48,  -48,  -48,  -48,  -48,  -48,  -48,  -48,  -48,
+      -48,  -48,  -48,  -48,  -48,  -48,  -48,  -48,  -48,  -48,
+      -48,  -48,  -48,  -48,  -48,  -48,  -48,  -48,  -48,  -48,
+      -48,  -48,  -48
+    },
+
+    {
+        9,  -49,  -49,  -49,  -49,  -49,  -49,  -49,  -49,  -49,
+      -49,  -49,  -49,  -49,  -49,  -49,  -49,  -49,  -49,  -49,
+      -49,  -49,  -49,  -49,  -49,  -49,  -49,  -49,  -49,  -49,
+      -49,  -49,  -49,  -49,  -49,  -49,  -49,  -49,  -49,  -49,
+      -49,  -49,  -49
+
+    },
+
+    {
+        9,   93,   93,  -50,   93,   93,   93,   93,   93,   93,
+       93,   93,   93,   93,   93,   93,   93,   93,   93,   93,
+       93,   93,   93,   93,   93,   93,   93,   93,   93,   93,
+       93,   93,   93,   93,   93,   93,   93,   93,   93,   93,
+       93,   93,   93
+    },
+
+    {
+        9,   94,   94,  -51,   94,   94,   94,   94,   94,   94,
+       94,   94,   94,   94,   94,   94,   94,   94,   94,   94,
+       94,   94,   94,   94,   94,   94,   94,   94,   94,   94,
+       94,   94,   94,   94,   94,   94,   94,   94,   94,   94,
+       94,   94,   94
+
+    },
+
+    {
+        9,  -52,   52,  -52,  -52,  -52,  -52,  -52,  -52,  -52,
+      -52,  -52,  -52,  -52,  -52,  -52,  -52,  -52,  -52,  -52,
+      -52,  -52,  -52,  -52,  -52,  -52,  -52,  -52,  -52,  -52,
+      -52,  -52,  -52,  -52,  -52,  -52,  -52,  -52,  -52,  -52,
+      -52,  -52,  -52
+    },
+
+    {
+        9,  -53,  -53,  -53,  -53,  -53,  -53,  -53,  -53,  -53,
+      -53,  -53,  -53,  -53,  -53,  -53,  -53,  -53,  -53,  -53,
+      -53,  -53,  -53,  -53,  -53,  -53,  -53,  -53,  -53,  -53,
+      -53,  -53,  -53,  -53,  -53,  -53,  -53,  -53,  -53,  -53,
+      -53,  -53,  -53
+
+    },
+
+    {
+        9,   54,   54,  -54,   54,   54,   54,   54,   54,   54,
+       54,   54,   54,   54,   54,   54,   54,   54,   54,   54,
+       54,   54,   54,   54,   54,   54,   54,   54,   54,   54,
+       54,   54,   54,   54,   54,   54,   54,   54,   54,   54,
+       54,   54,   54
+    },
+
+    {
+        9,  -55,  -55,  -55,  -55,  -55,  -55,   55,  -55,  -55,
+      -55,  -55,  -55,   55,   55,  -55,  -55,  -55,  -55,  -55,
+       55,   55,   55,   55,   55,   55,   55,   55,   55,   55,
+       55,   55,   55,   55,   55,   55,   55,   55,   55,   55,
+       55,   55,  -55
+
+    },
+
+    {
+        9,  -56,  -56,  -56,  -56,  -56,  -56,  -56,  -56,  -56,
+      -56,  -56,  -56,  -56,  -56,  -56,  -56,  -56,  -56,  -56,
+      -56,  -56,  -56,  -56,  -56,  -56,  -56,  -56,  -56,  -56,
+      -56,  -56,  -56,  -56,  -56,  -56,  -56,  -56,  -56,  -56,
+      -56,  -56,  -56
+    },
+
+    {
+        9,  -57,  -57,  -57,  -57,  -57,  -57,  -57,  -57,  -57,
+      -57,  -57,  -57,  -57,  -57,  -57,  -57,  -57,  -57,  -57,
+      -57,  -57,  -57,  -57,  -57,  -57,  -57,  -57,  -57,  -57,
+      -57,  -57,  -57,  -57,  -57,  -57,  -57,  -57,  -57,  -57,
+      -57,  -57,  -57
+
+    },
+
+    {
+        9,  -58,  -58,  -58,  -58,  -58,  -58,   55,  -58,  -58,
+      -58,  -58,  -58,   58,   58,  -58,  -58,  -58,  -58,  -58,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,  -58
+    },
+
+    {
+        9,  -59,  -59,  -59,  -59,  -59,  -59,  -59,  -59,  -59,
+      -59,  -59,  -59,  -59,  -59,  -59,  -59,  -59,  -59,  -59,
+      -59,  -59,  -59,  -59,  -59,  -59,  -59,  -59,  -59,  -59,
+      -59,  -59,  -59,  -59,  -59,  -59,  -59,  -59,  -59,  -59,
+      -59,  -59,  -59
+
+    },
+
+    {
+        9,  -60,  -60,  -60,  -60,  -60,  -60,  -60,  -60,  -60,
+      -60,  -60,  -60,  -60,  -60,  -60,  -60,  -60,  -60,  -60,
+      -60,  -60,  -60,  -60,  -60,  -60,  -60,  -60,  -60,  -60,
+      -60,  -60,  -60,  -60,  -60,  -60,  -60,  -60,  -60,  -60,
+      -60,  -60,  -60
+    },
+
+    {
+        9,  -61,  -61,  -61,  -61,  -61,  -61,  -61,  -61,  -61,
+      -61,  -61,  -61,  -61,  -61,  -61,  -61,  -61,  -61,  -61,
+      -61,  -61,  -61,  -61,  -61,  -61,  -61,  -61,  -61,  -61,
+      -61,  -61,  -61,  -61,  -61,  -61,  -61,  -61,  -61,  -61,
+      -61,  -61,  -61
+
+    },
+
+    {
+        9,  -62,  -62,  -62,  -62,  -62,  -62,  -62,  -62,  -62,
+      -62,  -62,  -62,  -62,  -62,  -62,  -62,  -62,  -62,  -62,
+      -62,  -62,  -62,  -62,  -62,  -62,  -62,  -62,  -62,  -62,
+      -62,  -62,  -62,  -62,  -62,  -62,  -62,  -62,  -62,  -62,
+      -62,  -62,  -62
+    },
+
+    {
+        9,  -63,  -63,  -63,  -63,  -63,  -63,   55,  -63,  -63,
+      -63,  -63,  -63,   58,   58,  -63,  -63,  -63,  -63,  -63,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   95,   58,   58,   58,   58,   58,   58,
+       58,   58,  -63
+
+    },
+
+    {
+        9,  -64,  -64,  -64,  -64,  -64,  -64,   55,  -64,  -64,
+      -64,  -64,  -64,   58,   58,  -64,  -64,  -64,  -64,  -64,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   96,   58,   58,   58,   58,   58,   58,
+       58,   58,  -64
+    },
+
+    {
+        9,  -65,  -65,  -65,  -65,  -65,  -65,   55,  -65,  -65,
+      -65,  -65,  -65,   58,   58,  -65,  -65,  -65,  -65,  -65,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   97,   98,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,  -65
+
+    },
+
+    {
+        9,  -66,  -66,  -66,  -66,  -66,  -66,   55,  -66,  -66,
+      -66,  -66,  -66,   58,   58,  -66,  -66,  -66,  -66,  -66,
+       58,   58,   58,   58,   58,   58,   99,   58,   58,   58,
+       58,   58,   58,   58,  100,   58,   58,   58,   58,   58,
+       58,   58,  -66
+    },
+
+    {
+        9,  -67,  -67,  -67,  -67,  -67,  -67,   55,  -67,  -67,
+      -67,  -67,  -67,   58,   58,  -67,  -67,  -67,  -67,  -67,
+       58,   58,   58,   58,  101,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,  -67
+
+    },
+
+    {
+        9,  -68,  -68,  -68,  -68,  -68,  -68,   55,  -68,  -68,
+      -68,  -68,  -68,   58,   58,  -68,  -68,  -68,  -68,  -68,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+      102,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+      103,   58,  -68
+    },
+
+    {
+        9,  -69,  -69,  -69,  -69,  -69,  -69,   55,  -69,  -69,
+      -69,  -69,  -69,   58,   58,  -69,  -69,  -69,  -69,  -69,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,  -69
+
+    },
+
+    {
+        9,  -70,  -70,  -70,  -70,  -70,  -70,   55,  -70,  -70,
+      -70,  -70,  -70,   58,   58,  -70,  -70,  -70,  -70,  -70,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,  104,   58,   58,   58,   58,   58,
+       58,   58,  -70
+    },
+
+    {
+        9,  -71,  -71,  -71,  -71,  -71,  -71,   55,  -71,  -71,
+      -71,  -71,  -71,   58,   58,  -71,  -71,  -71,  -71,  -71,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,  105,   58,   58,
+       58,   58,  -71
+
+    },
+
+    {
+        9,  -72,  -72,  -72,  -72,  -72,  -72,   55,  -72,  -72,
+      -72,  -72,  -72,   58,   58,  -72,  -72,  -72,  -72,  -72,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,  106,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,  -72
+    },
+
+    {
+        9,  -73,  -73,  -73,  -73,  -73,  -73,   55,  -73,  -73,
+      -73,  -73,  -73,   58,   58,  -73,  -73,  -73,  -73,  -73,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,  107,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,  -73
+
+    },
+
+    {
+        9,  -74,  -74,  -74,  -74,  -74,  -74,   55,  -74,  -74,
+      -74,  -74,  -74,   58,   58,  -74,  -74,  -74,  -74,  -74,
+       58,   58,   58,   58,  108,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,  -74
+    },
+
+    {
+        9,  -75,  -75,  -75,  -75,  -75,  -75,   55,  -75,  -75,
+      -75,  -75,  -75,   58,   58,  -75,  -75,  -75,  -75,  -75,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,  -75
+
+    },
+
+    {
+        9,  -76,  -76,  -76,  -76,  -76,  -76,   55,  -76,  -76,
+      -76,  -76,  -76,   58,   58,  -76,  -76,  -76,  -76,  -76,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,  109,   58,   58,
+       58,   58,  -76
+    },
+
+    {
+        9,  -77,  -77,  -77,  -77,  -77,  -77,   55,  -77,  -77,
+      -77,  -77,  -77,   58,   58,  -77,  -77,  -77,  -77,  -77,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,  110,   58,   58,   58,   58,   58,   58,
+       58,   58,  -77
+
+    },
+
+    {
+        9,  -78,  -78,  -78,  -78,  -78,  -78,   55,  -78,  -78,
+      -78,  -78,  -78,   58,   58,  -78,  -78,  -78,  -78,  -78,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,  111,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,  -78
+    },
+
+    {
+        9,  -79,  -79,  -79,  -79,  -79,  -79,   55,  -79,  -79,
+      -79,  -79,  -79,   58,   58,  -79,  -79,  -79,  -79,  -79,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,  112,   58,   58,   58,
+       58,   58,  -79
+
+    },
+
+    {
+        9,  -80,  -80,  -80,  -80,  -80,  -80,   55,  -80,  -80,
+      -80,  -80,  -80,   58,   58,  -80,  -80,  -80,  -80,  -80,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+      113,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,  -80
+    },
+
+    {
+        9,  -81,  -81,  -81,  -81,  -81,  -81,   55,  -81,  -81,
+      -81,  -81,  -81,   58,   58,  -81,  -81,  -81,  -81,  -81,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,  114,   58,
+       58,   58,  -81
+
+    },
+
+    {
+        9,  -82,  -82,  -82,  -82,  -82,  -82,   55,  -82,  -82,
+      -82,  -82,  -82,   58,   58,  -82,  -82,  -82,  -82,  -82,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,  115,   58,   58,   58,   58,
+       58,   58,  -82
+    },
+
+    {
+        9,  -83,  -83,  -83,  -83,  -83,  -83,   55,  -83,  -83,
+      -83,  -83,  -83,   58,   58,  -83,  -83,  -83,  -83,  -83,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,  116,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,  -83
+
+    },
+
+    {
+        9,  -84,  -84,  -84,  -84,  -84,  -84,   55,  -84,  -84,
+      -84,  -84,  -84,   58,   58,  -84,  -84,  -84,  -84,  -84,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,  117,   58,   58,   58,
+       58,   58,  -84
+    },
+
+    {
+        9,  -85,  -85,  -85,  -85,  -85,  -85,  -85,  -85,  -85,
+      -85,  -85,  -85,  -85,  -85,  -85,  -85,  -85,  -85,  -85,
+      -85,  -85,  -85,  -85,  -85,  -85,  -85,  -85,  -85,  -85,
+      -85,  -85,  -85,  -85,  -85,  -85,  -85,  -85,  -85,  -85,
+      -85,  -85,  -85
+
+    },
+
+    {
+        9,   86,   87,  -86,   86,   86,   86,   86,   86,   86,
+       86,   86,   86,   86,   86,   86,   86,   86,   86,   86,
+       86,   86,   86,   86,   86,   86,   86,   86,   86,   86,
+       86,   86,   86,   86,   86,   86,   86,   86,   86,   86,
+       86,   86,   86
+    },
+
+    {
+        9,   87,   87,  -87,   87,   87,   87,   87,   87,   87,
+       87,   87,   87,   87,   87,   87,   87,   87,   87,   87,
+       87,   87,   87,   87,   87,   87,   87,   87,   87,   87,
+       87,   87,   87,   87,   87,   87,   87,   87,   87,   87,
+       87,   87,   87
+
+    },
+
+    {
+        9,   88,   88,  -88,   88,   88,   88,   88,   88,   88,
+       88,   88,   88,   88,   88,   88,   88,   88,   88,   88,
+       88,   88,   88,   88,   88,   88,   88,   88,   88,   88,
+       88,   88,   88,   88,   88,   88,   88,   88,   88,   88,
+       88,   88,   88
+    },
+
+    {
+        9,  -89,   89,   90,  -89,  -89,  -89,  -89,  -89,  -89,
+      -89,  -89,  -89,  -89,  -89,  -89,  -89,  -89,  -89,  -89,
+      -89,  -89,  -89,  -89,  -89,  -89,  -89,  -89,  -89,  -89,
+      -89,  -89,  -89,  -89,  -89,  -89,  -89,  -89,  -89,  -89,
+      -89,  -89,  -89
+
+    },
+
+    {
+        9,   91,  -90,  -90,   91,   91,   91,   91,   91,   91,
+       91,   91,   91,   91,   91,   91,   91,   91,   91,   91,
+       91,   91,   91,   91,   91,   91,   91,   91,   91,   91,
+       91,   91,   91,   91,   91,   91,   91,   91,   91,   91,
+       91,   91,   91
+    },
+
+    {
+        9,  -91,  -91,  -91,  -91,  -91,  -91,  -91,  -91,  -91,
+      -91,  -91,  -91,  -91,  -91,  -91,  -91,  -91,  -91,  -91,
+      -91,  -91,  -91,  -91,  -91,  -91,  -91,  -91,  -91,  -91,
+      -91,  -91,  -91,  -91,  -91,  -91,  -91,  -91,  -91,  -91,
+      -91,  -91,  -91
+
+    },
+
+    {
+        9,   92,   92,  -92,   92,  -92,   92,  -92,   92,  -92,
+       92,   92,   92,   92,   92,   92,   92,   92,   92,  -92,
+       92,   92,   92,   92,   92,   92,   92,   92,   92,   92,
+       92,   92,   92,   92,   92,   92,   92,   92,   92,   92,
+       92,   92,   92
+    },
+
+    {
+        9,   93,   93,  -93,   93,   93,   93,   93,   93,   93,
+       93,   93,   93,   93,   93,   93,   93,   93,   93,   93,
+       93,   93,   93,   93,   93,   93,   93,   93,   93,   93,
+       93,   93,   93,   93,   93,   93,   93,   93,   93,   93,
+       93,   93,   93
+
+    },
+
+    {
+        9,  -94,  -94,  -94,  -94,  -94,  -94,  -94,  -94,  -94,
+      -94,  -94,  -94,  -94,  -94,  -94,  -94,  -94,  -94,  -94,
+      -94,  -94,  -94,  -94,  -94,  -94,  -94,  -94,  -94,  -94,
+      -94,  -94,  -94,  -94,  -94,  -94,  -94,  -94,  -94,  -94,
+      -94,  -94,  -94
+    },
+
+    {
+        9,  -95,  -95,  -95,  -95,  -95,  -95,   55,  -95,  -95,
+      -95,  -95,  -95,   58,   58,  -95,  -95,  -95,  -95,  -95,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+      118,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,  -95
+
+    },
+
+    {
+        9,  -96,  -96,  -96,  -96,  -96,  -96,   55,  -96,  -96,
+      -96,  -96,  -96,   58,   58,  -96,  -96,  -96,  -96,  -96,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,  119,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,  -96
+    },
+
+    {
+        9,  -97,  -97,  -97,  -97,  -97,  -97,   55,  -97,  -97,
+      -97,  -97,  -97,   58,   58,  -97,  -97,  -97,  -97,  -97,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,  120,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,  -97
+
+    },
+
+    {
+        9,  -98,  -98,  -98,  -98,  -98,  -98,   55,  -98,  -98,
+      -98,  -98,  -98,   58,   58,  -98,  -98,  -98,  -98,  -98,
+       58,   58,   58,   58,   58,   58,  121,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,  -98
+    },
+
+    {
+        9,  -99,  -99,  -99,  -99,  -99,  -99,   55,  -99,  -99,
+      -99,  -99,  -99,   58,   58,  -99,  -99,  -99,  -99,  -99,
+      122,  123,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,  -99
+
+    },
+
+    {
+        9, -100, -100, -100, -100, -100, -100,   55, -100, -100,
+     -100, -100, -100,   58,   58, -100, -100, -100, -100, -100,
+       58,   58,   58,   58,   58,  124,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58, -100
+    },
+
+    {
+        9, -101, -101, -101, -101, -101, -101,   55, -101, -101,
+     -101, -101, -101,   58,   58, -101, -101, -101, -101, -101,
+       58,   58,   58,  125,   58,   58,   58,   58,   58,  126,
+       58,  127,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58, -101
+
+    },
+
+    {
+        9, -102, -102, -102, -102, -102, -102,   55, -102, -102,
+     -102, -102, -102,   58,   58, -102, -102, -102, -102, -102,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,  128,   58,   58,   58,   58,   58,
+       58,   58, -102
+    },
+
+    {
+        9, -103, -103, -103, -103, -103, -103,   55, -103, -103,
+     -103, -103, -103,   58,   58, -103, -103, -103, -103, -103,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58, -103
+
+    },
+
+    {
+        9, -104, -104, -104, -104, -104, -104,   55, -104, -104,
+     -104, -104, -104,   58,   58, -104, -104, -104, -104, -104,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+      129,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58, -104
+    },
+
+    {
+        9, -105, -105, -105, -105, -105, -105,   55, -105, -105,
+     -105, -105, -105,   58,   58, -105, -105, -105, -105, -105,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58, -105
+
+    },
+
+    {
+        9, -106, -106, -106, -106, -106, -106,   55, -106, -106,
+     -106, -106, -106,   58,   58, -106, -106, -106, -106, -106,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,  130,   58,   58,   58,   58,   58,   58,   58,
+       58,   58, -106
+    },
+
+    {
+        9, -107, -107, -107, -107, -107, -107,   55, -107, -107,
+     -107, -107, -107,   58,   58, -107, -107, -107, -107, -107,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,  131,   58,
+       58,   58, -107
+
+    },
+
+    {
+        9, -108, -108, -108, -108, -108, -108,   55, -108, -108,
+     -108, -108, -108,   58,   58, -108, -108, -108, -108, -108,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,  132,   58,
+       58,   58, -108
+    },
+
+    {
+        9, -109, -109, -109, -109, -109, -109,   55, -109, -109,
+     -109, -109, -109,   58,   58, -109, -109, -109, -109, -109,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,  133,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58, -109
+
+    },
+
+    {
+        9, -110, -110, -110, -110, -110, -110,   55, -110, -110,
+     -110, -110, -110,   58,   58, -110, -110, -110, -110, -110,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,  134,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58, -110
+    },
+
+    {
+        9, -111, -111, -111, -111, -111, -111,   55, -111, -111,
+     -111, -111, -111,   58,   58, -111, -111, -111, -111, -111,
+       58,   58,   58,   58,   58,   58,   58,  135,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58, -111
+
+    },
+
+    {
+        9, -112, -112, -112, -112, -112, -112,   55, -112, -112,
+     -112, -112, -112,   58,   58, -112, -112, -112, -112, -112,
+       58,   58,   58,   58,   58,  136,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58, -112
+    },
+
+    {
+        9, -113, -113, -113, -113, -113, -113,   55, -113, -113,
+     -113, -113, -113,   58,   58, -113, -113, -113, -113, -113,
+       58,   58,   58,   58,   58,  137,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58, -113
+
+    },
+
+    {
+        9, -114, -114, -114, -114, -114, -114,   55, -114, -114,
+     -114, -114, -114,   58,   58, -114, -114, -114, -114, -114,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,  138,   58,   58,   58,   58,
+       58,   58, -114
+    },
+
+    {
+        9, -115, -115, -115, -115, -115, -115,   55, -115, -115,
+     -115, -115, -115,   58,   58, -115, -115, -115, -115, -115,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,  139,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58, -115
+
+    },
+
+    {
+        9, -116, -116, -116, -116, -116, -116,   55, -116, -116,
+     -116, -116, -116,   58,   58, -116, -116, -116, -116, -116,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,  140,   58,   58,   58,
+       58,   58, -116
+    },
+
+    {
+        9, -117, -117, -117, -117, -117, -117,   55, -117, -117,
+     -117, -117, -117,   58,   58, -117, -117, -117, -117, -117,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,  141,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58, -117
+
+    },
+
+    {
+        9, -118, -118, -118, -118, -118, -118,   55, -118, -118,
+     -118, -118, -118,   58,   58, -118, -118, -118, -118, -118,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58, -118
+    },
+
+    {
+        9, -119, -119, -119, -119, -119, -119,   55, -119, -119,
+     -119, -119, -119,   58,   58, -119, -119, -119, -119, -119,
+       58,   58,   58,  142,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58, -119
+
+    },
+
+    {
+        9, -120, -120, -120, -120, -120, -120,   55, -120, -120,
+     -120, -120, -120,   58,   58, -120, -120, -120, -120, -120,
+       58,   58,   58,   58,   58,  143,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58, -120
+    },
+
+    {
+        9, -121, -121, -121, -121, -121, -121,   55, -121, -121,
+     -121, -121, -121,   58,   58, -121, -121, -121, -121, -121,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,  144,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58, -121
+
+    },
+
+    {
+        9, -122, -122, -122, -122, -122, -122,   55, -122, -122,
+     -122, -122, -122,   58,   58, -122, -122, -122, -122, -122,
+       58,   58,  145,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,  146,   58,   58,
+       58,   58, -122
+    },
+
+    {
+        9, -123, -123, -123, -123, -123, -123,   55, -123, -123,
+     -123, -123, -123,   58,   58, -123, -123, -123, -123, -123,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,  147,   58,
+       58,   58, -123
+
+    },
+
+    {
+        9, -124, -124, -124, -124, -124, -124,   55, -124, -124,
+     -124, -124, -124,   58,   58, -124, -124, -124, -124, -124,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,  148,   58,   58,   58,   58,   58,   58,   58,
+       58,   58, -124
+    },
+
+    {
+        9, -125, -125, -125, -125, -125, -125,   55, -125, -125,
+     -125, -125, -125,   58,   58, -125, -125, -125, -125, -125,
+       58,   58,   58,   58,   58,   58,   58,   58,  149,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58, -125
+
+    },
+
+    {
+        9, -126, -126, -126, -126, -126, -126,   55, -126, -126,
+     -126, -126, -126,   58,   58, -126, -126, -126, -126, -126,
+       58,   58,   58,   58,   58,   58,  150,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58, -126
+    },
+
+    {
+        9, -127, -127, -127, -127, -127, -127,   55, -127, -127,
+     -127, -127, -127,   58,   58, -127, -127, -127, -127, -127,
+       58,   58,   58,   58,   58,  151,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58, -127
+
+    },
+
+    {
+        9, -128, -128, -128, -128, -128, -128,   55, -128, -128,
+     -128, -128, -128,   58,   58, -128, -128, -128, -128, -128,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58, -128
+    },
+
+    {
+        9, -129, -129, -129, -129, -129, -129,   55, -129, -129,
+     -129, -129, -129,   58,   58, -129, -129, -129, -129, -129,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,  152, -129
+
+    },
+
+    {
+        9, -130, -130, -130, -130, -130, -130,   55, -130, -130,
+     -130, -130, -130,   58,   58, -130, -130, -130, -130, -130,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,  153,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58, -130
+    },
+
+    {
+        9, -131, -131, -131, -131, -131, -131,   55, -131, -131,
+     -131, -131, -131,   58,   58, -131, -131, -131, -131, -131,
+       58,   58,   58,  154,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58, -131
+
+    },
+
+    {
+        9, -132, -132, -132, -132, -132, -132,   55, -132, -132,
+     -132, -132, -132,   58,   58, -132, -132, -132, -132, -132,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+      155,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58, -132
+    },
+
+    {
+        9, -133, -133, -133, -133, -133, -133,   55, -133, -133,
+     -133, -133, -133,   58,   58, -133, -133, -133, -133, -133,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,  156,   58,   58,   58,   58,   58,   58,
+       58,   58, -133
+
+    },
+
+    {
+        9, -134, -134, -134, -134, -134, -134,   55, -134, -134,
+     -134, -134, -134,   58,   58, -134, -134, -134, -134, -134,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,  157,   58,   58,   58,   58,   58,
+       58,   58, -134
+    },
+
+    {
+        9, -135, -135, -135, -135, -135, -135,   55, -135, -135,
+     -135, -135, -135,   58,   58, -135, -135, -135, -135, -135,
+       58,   58,   58,   58,   58,  158,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58, -135
+
+    },
+
+    {
+        9, -136, -136, -136, -136, -136, -136,   55, -136, -136,
+     -136, -136, -136,   58,   58, -136, -136, -136, -136, -136,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,  159,   58,   58,
+       58,   58, -136
+    },
+
+    {
+        9, -137, -137, -137, -137, -137, -137,   55, -137, -137,
+     -137, -137, -137,   58,   58, -137, -137, -137, -137, -137,
+       58,   58,   58,  160,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58, -137
+
+    },
+
+    {
+        9, -138, -138, -138, -138, -138, -138,   55, -138, -138,
+     -138, -138, -138,   58,   58, -138, -138, -138, -138, -138,
+       58,   58,   58,  161,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58, -138
+    },
+
+    {
+        9, -139, -139, -139, -139, -139, -139,   55, -139, -139,
+     -139, -139, -139,   58,   58, -139, -139, -139, -139, -139,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,  162,   58,   58,   58,   58,   58,   58,   58,
+       58,   58, -139
+
+    },
+
+    {
+        9, -140, -140, -140, -140, -140, -140,   55, -140, -140,
+     -140, -140, -140,   58,   58, -140, -140, -140, -140, -140,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,  163,   58,   58,
+       58,   58, -140
+    },
+
+    {
+        9, -141, -141, -141, -141, -141, -141,   55, -141, -141,
+     -141, -141, -141,   58,   58, -141, -141, -141, -141, -141,
+       58,   58,  164,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58, -141
+
+    },
+
+    {
+        9, -142, -142, -142, -142, -142, -142,   55, -142, -142,
+     -142, -142, -142,   58,   58, -142, -142, -142, -142, -142,
+       58,   58,   58,   58,   58,  165,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58, -142
+    },
+
+    {
+        9, -143, -143, -143, -143, -143, -143,   55, -143, -143,
+     -143, -143, -143,   58,   58, -143, -143, -143, -143, -143,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,  166,   58,   58,   58,   58,   58,   58,   58,
+       58,   58, -143
+
+    },
+
+    {
+        9, -144, -144, -144, -144, -144, -144,   55, -144, -144,
+     -144, -144, -144,   58,   58, -144, -144, -144, -144, -144,
+       58,   58,   58,   58,   58,   58,   58,  167,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58, -144
+    },
+
+    {
+        9, -145, -145, -145, -145, -145, -145,   55, -145, -145,
+     -145, -145, -145,   58,   58, -145, -145, -145, -145, -145,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,  168,   58,   58,   58,   58,   58,   58,
+       58,   58, -145
+
+    },
+
+    {
+        9, -146, -146, -146, -146, -146, -146,   55, -146, -146,
+     -146, -146, -146,   58,   58, -146, -146, -146, -146, -146,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,  169,   58,   58,   58,   58,
+       58,   58, -146
+    },
+
+    {
+        9, -147, -147, -147, -147, -147, -147,   55, -147, -147,
+     -147, -147, -147,   58,   58, -147, -147, -147, -147, -147,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+      170,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58, -147
+
+    },
+
+    {
+        9, -148, -148, -148, -148, -148, -148,   55, -148, -148,
+     -148, -148, -148,   58,   58, -148, -148, -148, -148, -148,
+       58,   58,   58,   58,  171,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58, -148
+    },
+
+    {
+        9, -149, -149, -149, -149, -149, -149,   55, -149, -149,
+     -149, -149, -149,   58,   58, -149, -149, -149, -149, -149,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,  172,   58,   58,   58,   58,   58,   58,
+       58,   58, -149
+
+    },
+
+    {
+        9, -150, -150, -150, -150, -150, -150,   55, -150, -150,
+     -150, -150, -150,   58,   58, -150, -150, -150, -150, -150,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58, -150
+    },
+
+    {
+        9, -151, -151, -151, -151, -151, -151,   55, -151, -151,
+     -151, -151, -151,   58,   58, -151, -151, -151, -151, -151,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,  173,   58,   58,   58,   58,   58,   58,   58,
+       58,   58, -151
+
+    },
+
+    {
+        9, -152, -152, -152, -152, -152, -152,   55, -152, -152,
+     -152, -152, -152,   58,   58, -152, -152, -152, -152, -152,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58, -152
+    },
+
+    {
+        9, -153, -153, -153, -153, -153, -153,   55, -153, -153,
+     -153, -153, -153,   58,   58, -153, -153, -153, -153, -153,
+       58,   58,   58,   58,   58,  174,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58, -153
+
+    },
+
+    {
+        9, -154, -154, -154, -154, -154, -154,   55, -154, -154,
+     -154, -154, -154,   58,   58, -154, -154, -154, -154, -154,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,  175,   58,   58,   58,   58,   58,   58,
+       58,   58, -154
+    },
+
+    {
+        9, -155, -155, -155, -155, -155, -155,   55, -155, -155,
+     -155, -155, -155,   58,   58, -155, -155, -155, -155, -155,
+       58,   58,   58,   58,   58,  176,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58, -155
+
+    },
+
+    {
+        9, -156, -156, -156, -156, -156, -156,   55, -156, -156,
+     -156, -156, -156,   58,   58, -156, -156, -156, -156, -156,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,  177,   58,   58,   58,   58,   58,   58,   58,
+       58,   58, -156
+    },
+
+    {
+        9, -157, -157, -157, -157, -157, -157,   55, -157, -157,
+     -157, -157, -157,   58,   58, -157, -157, -157, -157, -157,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,  178,   58,   58,
+       58,   58, -157
+
+    },
+
+    {
+        9, -158, -158, -158, -158, -158, -158,   55, -158, -158,
+     -158, -158, -158,   58,   58, -158, -158, -158, -158, -158,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58, -158
+    },
+
+    {
+        9, -159, -159, -159, -159, -159, -159,   55, -159, -159,
+     -159, -159, -159,   58,   58, -159, -159, -159, -159, -159,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58, -159
+
+    },
+
+    {
+        9, -160, -160, -160, -160, -160, -160,   55, -160, -160,
+     -160, -160, -160,   58,   58, -160, -160, -160, -160, -160,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,  179,   58,   58,
+       58,   58, -160
+    },
+
+    {
+        9, -161, -161, -161, -161, -161, -161,   55, -161, -161,
+     -161, -161, -161,   58,   58, -161, -161, -161, -161, -161,
+       58,   58,   58,   58,   58,  180,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58, -161
+
+    },
+
+    {
+        9, -162, -162, -162, -162, -162, -162,   55, -162, -162,
+     -162, -162, -162,   58,   58, -162, -162, -162, -162, -162,
+       58,   58,   58,   58,   58,   58,   58,  181,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58, -162
+    },
+
+    {
+        9, -163, -163, -163, -163, -163, -163,   55, -163, -163,
+     -163, -163, -163,   58,   58, -163, -163, -163, -163, -163,
+       58,  182,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58, -163
+
+    },
+
+    {
+        9, -164, -164, -164, -164, -164, -164,   55, -164, -164,
+     -164, -164, -164,   58,   58, -164, -164, -164, -164, -164,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+      183,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58, -164
+    },
+
+    {
+        9, -165, -165, -165, -165, -165, -165,   55, -165, -165,
+     -165, -165, -165,   58,   58, -165, -165, -165, -165, -165,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58, -165
+
+    },
+
+    {
+        9, -166, -166, -166, -166, -166, -166,   55, -166, -166,
+     -166, -166, -166,   58,   58, -166, -166, -166, -166, -166,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,  184,   58,   58,
+       58,   58, -166
+    },
+
+    {
+        9, -167, -167, -167, -167, -167, -167,   55, -167, -167,
+     -167, -167, -167,   58,   58, -167, -167, -167, -167, -167,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58, -167
+
+    },
+
+    {
+        9, -168, -168, -168, -168, -168, -168,   55, -168, -168,
+     -168, -168, -168,   58,   58, -168, -168, -168, -168, -168,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,  185,   58,   58,   58,   58,   58,   58,
+       58,   58, -168
+    },
+
+    {
+        9, -169, -169, -169, -169, -169, -169,   55, -169, -169,
+     -169, -169, -169,   58,   58, -169, -169, -169, -169, -169,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,  186,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58, -169
+
+    },
+
+    {
+        9, -170, -170, -170, -170, -170, -170,   55, -170, -170,
+     -170, -170, -170,   58,   58, -170, -170, -170, -170, -170,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,  187,   58,   58,
+       58,   58, -170
+    },
+
+    {
+        9, -171, -171, -171, -171, -171, -171,   55, -171, -171,
+     -171, -171, -171,   58,   58, -171, -171, -171, -171, -171,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,  188,   58,   58,   58,
+       58,   58, -171
+
+    },
+
+    {
+        9, -172, -172, -172, -172, -172, -172,   55, -172, -172,
+     -172, -172, -172,   58,   58, -172, -172, -172, -172, -172,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,  189,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58, -172
+    },
+
+    {
+        9, -173, -173, -173, -173, -173, -173,   55, -173, -173,
+     -173, -173, -173,   58,   58, -173, -173, -173, -173, -173,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,  190,   58,
+       58,   58, -173
+
+    },
+
+    {
+        9, -174, -174, -174, -174, -174, -174,   55, -174, -174,
+     -174, -174, -174,   58,   58, -174, -174, -174, -174, -174,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,  191,   58,   58,   58,   58,   58,   58,   58,
+       58,   58, -174
+    },
+
+    {
+        9, -175, -175, -175, -175, -175, -175,   55, -175, -175,
+     -175, -175, -175,   58,   58, -175, -175, -175, -175, -175,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,  192,   58,   58,   58,   58,   58,   58,   58,
+       58,   58, -175
+
+    },
+
+    {
+        9, -176, -176, -176, -176, -176, -176,   55, -176, -176,
+     -176, -176, -176,   58,   58, -176, -176, -176, -176, -176,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,  193,   58,   58,   58,
+       58,   58, -176
+    },
+
+    {
+        9, -177, -177, -177, -177, -177, -177,   55, -177, -177,
+     -177, -177, -177,   58,   58, -177, -177, -177, -177, -177,
+       58,  194,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58, -177
+
+    },
+
+    {
+        9, -178, -178, -178, -178, -178, -178,   55, -178, -178,
+     -178, -178, -178,   58,   58, -178, -178, -178, -178, -178,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58, -178
+    },
+
+    {
+        9, -179, -179, -179, -179, -179, -179,   55, -179, -179,
+     -179, -179, -179,   58,   58, -179, -179, -179, -179, -179,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58, -179
+
+    },
+
+    {
+        9, -180, -180, -180, -180, -180, -180,   55, -180, -180,
+     -180, -180, -180,   58,   58, -180, -180, -180, -180, -180,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58, -180
+    },
+
+    {
+        9, -181, -181, -181, -181, -181, -181,   55, -181, -181,
+     -181, -181, -181,   58,   58, -181, -181, -181, -181, -181,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58, -181
+
+    },
+
+    {
+        9, -182, -182, -182, -182, -182, -182,   55, -182, -182,
+     -182, -182, -182,   58,   58, -182, -182, -182, -182, -182,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,  195,   58,   58,
+       58,   58, -182
+    },
+
+    {
+        9, -183, -183, -183, -183, -183, -183,   55, -183, -183,
+     -183, -183, -183,   58,   58, -183, -183, -183, -183, -183,
+       58,   58,   58,   58,   58,  196,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58, -183
+
+    },
+
+    {
+        9, -184, -184, -184, -184, -184, -184,   55, -184, -184,
+     -184, -184, -184,   58,   58, -184, -184, -184, -184, -184,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58, -184
+    },
+
+    {
+        9, -185, -185, -185, -185, -185, -185,   55, -185, -185,
+     -185, -185, -185,   58,   58, -185, -185, -185, -185, -185,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+      197,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58, -185
+
+    },
+
+    {
+        9, -186, -186, -186, -186, -186, -186,   55, -186, -186,
+     -186, -186, -186,   58,   58, -186, -186, -186, -186, -186,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,  198,   58,   58,   58,
+       58,   58, -186
+    },
+
+    {
+        9, -187, -187, -187, -187, -187, -187,   55, -187, -187,
+     -187, -187, -187,   58,   58, -187, -187, -187, -187, -187,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58, -187
+
+    },
+
+    {
+        9, -188, -188, -188, -188, -188, -188,   55, -188, -188,
+     -188, -188, -188,   58,   58, -188, -188, -188, -188, -188,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58, -188
+    },
+
+    {
+        9, -189, -189, -189, -189, -189, -189,   55, -189, -189,
+     -189, -189, -189,   58,   58, -189, -189, -189, -189, -189,
+       58,   58,   58,  199,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58, -189
+
+    },
+
+    {
+        9, -190, -190, -190, -190, -190, -190,   55, -190, -190,
+     -190, -190, -190,   58,   58, -190, -190, -190, -190, -190,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58, -190
+    },
+
+    {
+        9, -191, -191, -191, -191, -191, -191,   55, -191, -191,
+     -191, -191, -191,   58,   58, -191, -191, -191, -191, -191,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,  200,   58,
+       58,   58, -191
+
+    },
+
+    {
+        9, -192, -192, -192, -192, -192, -192,   55, -192, -192,
+     -192, -192, -192,   58,   58, -192, -192, -192, -192, -192,
+       58,   58,   58,   58,   58,   58,  201,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58, -192
+    },
+
+    {
+        9, -193, -193, -193, -193, -193, -193,   55, -193, -193,
+     -193, -193, -193,   58,   58, -193, -193, -193, -193, -193,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58, -193
+
+    },
+
+    {
+        9, -194, -194, -194, -194, -194, -194,   55, -194, -194,
+     -194, -194, -194,   58,   58, -194, -194, -194, -194, -194,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+      202,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58, -194
+    },
+
+    {
+        9, -195, -195, -195, -195, -195, -195,   55, -195, -195,
+     -195, -195, -195,   58,   58, -195, -195, -195, -195, -195,
+       58,   58,   58,   58,   58,  203,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58, -195
+
+    },
+
+    {
+        9, -196, -196, -196, -196, -196, -196,   55, -196, -196,
+     -196, -196, -196,   58,   58, -196, -196, -196, -196, -196,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58, -196
+    },
+
+    {
+        9, -197, -197, -197, -197, -197, -197,   55, -197, -197,
+     -197, -197, -197,   58,   58, -197, -197, -197, -197, -197,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58, -197
+
+    },
+
+    {
+        9, -198, -198, -198, -198, -198, -198,   55, -198, -198,
+     -198, -198, -198,   58,   58, -198, -198, -198, -198, -198,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,  204,   58,   58,
+       58,   58, -198
+    },
+
+    {
+        9, -199, -199, -199, -199, -199, -199,   55, -199, -199,
+     -199, -199, -199,   58,   58, -199, -199, -199, -199, -199,
+       58,   58,   58,   58,   58,  205,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58, -199
+
+    },
+
+    {
+        9, -200, -200, -200, -200, -200, -200,   55, -200, -200,
+     -200, -200, -200,   58,   58, -200, -200, -200, -200, -200,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58, -200
+    },
+
+    {
+        9, -201, -201, -201, -201, -201, -201,   55, -201, -201,
+     -201, -201, -201,   58,   58, -201, -201, -201, -201, -201,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,  206,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58, -201
+
+    },
+
+    {
+        9, -202, -202, -202, -202, -202, -202,   55, -202, -202,
+     -202, -202, -202,   58,   58, -202, -202, -202, -202, -202,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58, -202
+    },
+
+    {
+        9, -203, -203, -203, -203, -203, -203,   55, -203, -203,
+     -203, -203, -203,   58,   58, -203, -203, -203, -203, -203,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58, -203
+
+    },
+
+    {
+        9, -204, -204, -204, -204, -204, -204,   55, -204, -204,
+     -204, -204, -204,   58,   58, -204, -204, -204, -204, -204,
+       58,  207,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58, -204
+    },
+
+    {
+        9, -205, -205, -205, -205, -205, -205,   55, -205, -205,
+     -205, -205, -205,   58,   58, -205, -205, -205, -205, -205,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58, -205
+
+    },
+
+    {
+        9, -206, -206, -206, -206, -206, -206,   55, -206, -206,
+     -206, -206, -206,   58,   58, -206, -206, -206, -206, -206,
+       58,   58,   58,   58,   58,   58,   58,  208,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58, -206
+    },
+
+    {
+        9, -207, -207, -207, -207, -207, -207,   55, -207, -207,
+     -207, -207, -207,   58,   58, -207, -207, -207, -207, -207,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,  209,   58,   58,
+       58,   58, -207
+
+    },
+
+    {
+        9, -208, -208, -208, -208, -208, -208,   55, -208, -208,
+     -208, -208, -208,   58,   58, -208, -208, -208, -208, -208,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58, -208
+    },
+
+    {
+        9, -209, -209, -209, -209, -209, -209,   55, -209, -209,
+     -209, -209, -209,   58,   58, -209, -209, -209, -209, -209,
+       58,   58,   58,   58,   58,  210,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58, -209
+
+    },
+
+    {
+        9, -210, -210, -210, -210, -210, -210,   55, -210, -210,
+     -210, -210, -210,   58,   58, -210, -210, -210, -210, -210,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58,   58,   58,   58,   58,   58,   58,   58,   58,
+       58,   58, -210
+    },
+
+    } ;
+
+static yy_state_type yy_get_previous_state ( void );
+static yy_state_type yy_try_NUL_trans ( yy_state_type current_state  );
+static int yy_get_next_buffer ( void );
+static void yynoreturn yy_fatal_error ( const char* msg  );
+
+/* Done after the current pattern has been matched and before the
+ * corresponding action - sets up yytext.
+ */
+#define YY_DO_BEFORE_ACTION \
+	(yytext_ptr) = yy_bp; \
+	yyleng = (int) (yy_cp - yy_bp); \
+	(yy_hold_char) = *yy_cp; \
+	*yy_cp = '\0'; \
+	(yy_c_buf_p) = yy_cp;
+#define YY_NUM_RULES 64
+#define YY_END_OF_BUFFER 65
+/* This struct is not used in this scanner,
+   but its presence is necessary. */
+struct yy_trans_info
+	{
+	flex_int32_t yy_verify;
+	flex_int32_t yy_nxt;
+	};
+static const flex_int16_t yy_accept[211] =
+    {   0,
+        2,    2,    0,    0,    0,    0,    0,    0,   65,   51,
+        2,    4,   43,   48,    1,   50,   51,   44,   45,   51,
+       49,   51,   39,   37,   41,   51,   49,   49,   49,   49,
+       49,   49,   49,   49,   49,   49,   49,   49,   49,   51,
+       52,   54,   53,   63,   60,   62,   56,   59,   58,   55,
+       57,    2,   38,    1,   50,   36,   47,   49,   46,   40,
+       42,    3,   49,   49,   49,   49,   49,   49,   18,   49,
+       49,   49,   49,   49,   25,   49,   49,   49,   49,   49,
+       49,   49,   49,   49,   35,   52,   52,   63,   60,   62,
+       61,   56,   55,   57,   49,   49,   49,   49,   49,   49,
+
+       49,   49,   17,   49,   20,   49,   49,   49,   49,   49,
+       49,   49,   49,   49,   49,   49,   49,    5,   49,   49,
+       49,   49,   49,   49,   49,   49,   49,   16,   49,   49,
+       22,   49,   49,   49,   49,   49,   49,   49,   49,   49,
+       49,   49,   49,   49,   49,   49,   49,   49,   49,   14,
+       49,   19,   49,   49,   49,   49,   49,   28,   29,   49,
+       49,   49,   49,   49,    6,   49,    8,   49,   49,   49,
+       49,   49,   49,   49,   49,   49,   49,   27,   30,   31,
+       32,   49,   49,    7,   49,   49,   11,   12,   49,   15,
+       49,   49,   24,   49,   49,   34,    9,   49,   49,   21,
+
+       49,   26,   33,   49,   13,   49,   49,   23,   49,   10
+    } ;
+
+static const YY_CHAR yy_ec[256] =
+    {   0,
+        1,    1,    1,    1,    1,    1,    1,    1,    2,    3,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    2,    4,    5,    6,    7,    1,    8,    9,   10,
+       11,    1,   12,    1,   13,   14,   14,   13,   13,   13,
+       13,   13,   13,   13,   13,   13,   13,   15,    1,   16,
+       17,   18,    1,    1,   13,   13,   13,   13,   13,   13,
+       13,   13,   13,   13,   13,   13,   13,   13,   13,   13,
+       13,   13,   13,   13,   13,   13,   13,   13,   13,   13,
+        1,   19,    1,    1,   20,    1,   21,   22,   23,   24,
+
+       25,   26,   27,   28,   29,   13,   13,   30,   31,   32,
+       33,   34,   13,   35,   36,   37,   38,   39,   13,   40,
+       41,   13,    1,   42,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1,    1,    1,    1,    1,    1,
+        1,    1,    1,    1,    1
+    } ;
+
+/* Table of booleans, true if rule could match eol. */
+static const flex_int32_t yy_rule_can_match_eol[65] =
+    {   0,
+0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 
+    0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 
+    0, 1, 1, 0, 0,     };
+
+extern int yy_flex_debug;
+int yy_flex_debug = 0;
+
+/* The intent behind this definition is that it'll catch
+ * any uses of REJECT which flex missed.
+ */
+#define REJECT reject_used_but_not_detected
+#define yymore() yymore_used_but_not_detected
+#define YY_MORE_ADJ 0
+#define YY_RESTORE_YY_MORE_OFFSET
+char *yytext;
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>
+ */
+
+#include <assert.h>
+#include <limits.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <glob.h>
+#include <libgen.h>
+
+#include "lkc.h"
+#include "parser.tab.h"
+
+#define YY_DECL		static int yylex1(void)
+
+#define START_STRSIZE	16
+
+static struct {
+	struct file *file;
+	int lineno;
+} current_pos;
+
+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;
+};
+
+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",
+	        current_file->name, yylineno, chr);
+}
+
+#define INITIAL 0
+#define ASSIGN_VAL 1
+#define HELP 2
+#define STRING 3
+
+#ifndef YY_NO_UNISTD_H
+/* Special case for "unistd.h", since it is non-ANSI. We include it way
+ * down here because we want the user's section 1 to have been scanned first.
+ * The user has a chance to override it with an option.
+ */
+#include <unistd.h>
+#endif
+
+#ifndef YY_EXTRA_TYPE
+#define YY_EXTRA_TYPE void *
+#endif
+
+static int yy_init_globals ( void );
+
+/* Accessor methods to globals.
+   These are made visible to non-reentrant scanners for convenience. */
+
+int yylex_destroy ( void );
+
+int yyget_debug ( void );
+
+void yyset_debug ( int debug_flag  );
+
+YY_EXTRA_TYPE yyget_extra ( void );
+
+void yyset_extra ( YY_EXTRA_TYPE user_defined  );
+
+FILE *yyget_in ( void );
+
+void yyset_in  ( FILE * _in_str  );
+
+FILE *yyget_out ( void );
+
+void yyset_out  ( FILE * _out_str  );
+
+			int yyget_leng ( void );
+
+char *yyget_text ( void );
+
+int yyget_lineno ( void );
+
+void yyset_lineno ( int _line_number  );
+
+/* Macros after this point can all be overridden by user definitions in
+ * section 1.
+ */
+
+#ifndef YY_SKIP_YYWRAP
+#ifdef __cplusplus
+extern "C" int yywrap ( void );
+#else
+extern int yywrap ( void );
+#endif
+#endif
+
+#ifndef YY_NO_UNPUT
+    
+    static void yyunput ( int c, char *buf_ptr  );
+    
+#endif
+
+#ifndef yytext_ptr
+static void yy_flex_strncpy ( char *, const char *, int );
+#endif
+
+#ifdef YY_NEED_STRLEN
+static int yy_flex_strlen ( const char * );
+#endif
+
+#ifndef YY_NO_INPUT
+#ifdef __cplusplus
+static int yyinput ( void );
+#else
+static int input ( void );
+#endif
+
+#endif
+
+/* Amount of stuff to slurp up with each read. */
+#ifndef YY_READ_BUF_SIZE
+#ifdef __ia64__
+/* On IA-64, the buffer size is 16k, not 8k */
+#define YY_READ_BUF_SIZE 16384
+#else
+#define YY_READ_BUF_SIZE 8192
+#endif /* __ia64__ */
+#endif
+
+/* Copy whatever the last rule matched to the standard output. */
+#ifndef ECHO
+/* This used to be an fputs(), but since the string might contain NUL's,
+ * we now use fwrite().
+ */
+#define ECHO do { if (fwrite( yytext, (size_t) yyleng, 1, yyout )) {} } while (0)
+#endif
+
+/* Gets input and stuffs it into "buf".  number of characters read, or YY_NULL,
+ * is returned in "result".
+ */
+#ifndef YY_INPUT
+#define YY_INPUT(buf,result,max_size) \
+	errno=0; \
+	while ( (result = (int) read( fileno(yyin), buf, (yy_size_t) max_size )) < 0 ) \
+	{ \
+		if( errno != EINTR) \
+		{ \
+			YY_FATAL_ERROR( "input in flex scanner failed" ); \
+			break; \
+		} \
+		errno=0; \
+		clearerr(yyin); \
+	}\
+\
+
+#endif
+
+/* No semi-colon after return; correct usage is to write "yyterminate();" -
+ * we don't want an extra ';' after the "return" because that will cause
+ * some compilers to complain about unreachable statements.
+ */
+#ifndef yyterminate
+#define yyterminate() return YY_NULL
+#endif
+
+/* Number of entries by which start-condition stack grows. */
+#ifndef YY_START_STACK_INCR
+#define YY_START_STACK_INCR 25
+#endif
+
+/* Report a fatal error. */
+#ifndef YY_FATAL_ERROR
+#define YY_FATAL_ERROR(msg) yy_fatal_error( msg )
+#endif
+
+/* end tables serialization structures and prototypes */
+
+/* Default declaration of generated scanner - a define so the user can
+ * easily add parameters.
+ */
+#ifndef YY_DECL
+#define YY_DECL_IS_OURS 1
+
+extern int yylex (void);
+
+#define YY_DECL int yylex (void)
+#endif /* !YY_DECL */
+
+/* Code executed at the beginning of each rule, after yytext and yyleng
+ * have been set up.
+ */
+#ifndef YY_USER_ACTION
+#define YY_USER_ACTION
+#endif
+
+/* Code executed at the end of each rule. */
+#ifndef YY_BREAK
+#define YY_BREAK /*LINTED*/break;
+#endif
+
+#define YY_RULE_SETUP \
+	YY_USER_ACTION
+
+/** The main scanner function which does all the work.
+ */
+YY_DECL
+{
+	yy_state_type yy_current_state;
+	char *yy_cp, *yy_bp;
+	int yy_act;
+    
+	if ( !(yy_init) )
+		{
+		(yy_init) = 1;
+
+#ifdef YY_USER_INIT
+		YY_USER_INIT;
+#endif
+
+		if ( ! (yy_start) )
+			(yy_start) = 1;	/* first start state */
+
+		if ( ! yyin )
+			yyin = stdin;
+
+		if ( ! yyout )
+			yyout = stdout;
+
+		if ( ! YY_CURRENT_BUFFER ) {
+			yyensure_buffer_stack ();
+			YY_CURRENT_BUFFER_LVALUE =
+				yy_create_buffer( yyin, YY_BUF_SIZE );
+		}
+
+		yy_load_buffer_state(  );
+		}
+
+	{
+
+	char open_quote = 0;
+
+	while ( /*CONSTCOND*/1 )		/* loops until end-of-file is reached */
+		{
+		yy_cp = (yy_c_buf_p);
+
+		/* Support of yytext. */
+		*yy_cp = (yy_hold_char);
+
+		/* yy_bp points to the position in yy_ch_buf of the start of
+		 * the current run.
+		 */
+		yy_bp = yy_cp;
+
+		yy_current_state = (yy_start);
+yy_match:
+		while ( (yy_current_state = yy_nxt[yy_current_state][ yy_ec[YY_SC_TO_UI(*yy_cp)]  ]) > 0 )
+			++yy_cp;
+
+		yy_current_state = -yy_current_state;
+
+yy_find_action:
+		yy_act = yy_accept[yy_current_state];
+
+		YY_DO_BEFORE_ACTION;
+
+		if ( yy_act != YY_END_OF_BUFFER && yy_rule_can_match_eol[yy_act] )
+			{
+			int yyl;
+			for ( yyl = 0; yyl < yyleng; ++yyl )
+				if ( yytext[yyl] == '\n' )
+					
+    yylineno++;
+;
+			}
+
+do_action:	/* This label is used only to access EOF actions. */
+
+		switch ( yy_act )
+	{ /* beginning of action switch */
+case 1:
+YY_RULE_SETUP
+/* ignore comment */
+	YY_BREAK
+case 2:
+YY_RULE_SETUP
+/* whitespaces */
+	YY_BREAK
+case 3:
+/* rule 3 can match eol */
+YY_RULE_SETUP
+/* escaped new line */
+	YY_BREAK
+case 4:
+/* rule 4 can match eol */
+YY_RULE_SETUP
+return T_EOL;
+	YY_BREAK
+case 5:
+YY_RULE_SETUP
+return T_BOOL;
+	YY_BREAK
+case 6:
+YY_RULE_SETUP
+return T_CHOICE;
+	YY_BREAK
+case 7:
+YY_RULE_SETUP
+return T_COMMENT;
+	YY_BREAK
+case 8:
+YY_RULE_SETUP
+return T_CONFIG;
+	YY_BREAK
+case 9:
+YY_RULE_SETUP
+return T_DEF_BOOL;
+	YY_BREAK
+case 10:
+YY_RULE_SETUP
+return T_DEF_TRISTATE;
+	YY_BREAK
+case 11:
+YY_RULE_SETUP
+return T_DEFAULT;
+	YY_BREAK
+case 12:
+YY_RULE_SETUP
+return T_DEPENDS;
+	YY_BREAK
+case 13:
+YY_RULE_SETUP
+return T_ENDCHOICE;
+	YY_BREAK
+case 14:
+YY_RULE_SETUP
+return T_ENDIF;
+	YY_BREAK
+case 15:
+YY_RULE_SETUP
+return T_ENDMENU;
+	YY_BREAK
+case 16:
+YY_RULE_SETUP
+return T_HELP;
+	YY_BREAK
+case 17:
+YY_RULE_SETUP
+return T_HEX;
+	YY_BREAK
+case 18:
+YY_RULE_SETUP
+return T_IF;
+	YY_BREAK
+case 19:
+YY_RULE_SETUP
+return T_IMPLY;
+	YY_BREAK
+case 20:
+YY_RULE_SETUP
+return T_INT;
+	YY_BREAK
+case 21:
+YY_RULE_SETUP
+return T_MAINMENU;
+	YY_BREAK
+case 22:
+YY_RULE_SETUP
+return T_MENU;
+	YY_BREAK
+case 23:
+YY_RULE_SETUP
+return T_MENUCONFIG;
+	YY_BREAK
+case 24:
+YY_RULE_SETUP
+return T_MODULES;
+	YY_BREAK
+case 25:
+YY_RULE_SETUP
+return T_ON;
+	YY_BREAK
+case 26:
+YY_RULE_SETUP
+return T_OPTIONAL;
+	YY_BREAK
+case 27:
+YY_RULE_SETUP
+return T_PROMPT;
+	YY_BREAK
+case 28:
+YY_RULE_SETUP
+return T_RANGE;
+	YY_BREAK
+case 29:
+YY_RULE_SETUP
+return T_RESET;
+	YY_BREAK
+case 30:
+YY_RULE_SETUP
+return T_SELECT;
+	YY_BREAK
+case 31:
+YY_RULE_SETUP
+return T_SOURCE;
+	YY_BREAK
+case 32:
+YY_RULE_SETUP
+return T_STRING;
+	YY_BREAK
+case 33:
+YY_RULE_SETUP
+return T_TRISTATE;
+	YY_BREAK
+case 34:
+YY_RULE_SETUP
+return T_VISIBLE;
+	YY_BREAK
+case 35:
+YY_RULE_SETUP
+return T_OR;
+	YY_BREAK
+case 36:
+YY_RULE_SETUP
+return T_AND;
+	YY_BREAK
+case 37:
+YY_RULE_SETUP
+return T_EQUAL;
+	YY_BREAK
+case 38:
+YY_RULE_SETUP
+return T_UNEQUAL;
+	YY_BREAK
+case 39:
+YY_RULE_SETUP
+return T_LESS;
+	YY_BREAK
+case 40:
+YY_RULE_SETUP
+return T_LESS_EQUAL;
+	YY_BREAK
+case 41:
+YY_RULE_SETUP
+return T_GREATER;
+	YY_BREAK
+case 42:
+YY_RULE_SETUP
+return T_GREATER_EQUAL;
+	YY_BREAK
+case 43:
+YY_RULE_SETUP
+return T_NOT;
+	YY_BREAK
+case 44:
+YY_RULE_SETUP
+return T_OPEN_PAREN;
+	YY_BREAK
+case 45:
+YY_RULE_SETUP
+return T_CLOSE_PAREN;
+	YY_BREAK
+case 46:
+YY_RULE_SETUP
+return T_COLON_EQUAL;
+	YY_BREAK
+case 47:
+YY_RULE_SETUP
+return T_PLUS_EQUAL;
+	YY_BREAK
+case 48:
+YY_RULE_SETUP
+{
+				open_quote = yytext[0];
+				new_string();
+				BEGIN(STRING);
+			}
+	YY_BREAK
+case 49:
+YY_RULE_SETUP
+{
+				alloc_string(yytext, yyleng);
+				yylval.string = text;
+				return T_WORD;
+			}
+	YY_BREAK
+case 50:
+YY_RULE_SETUP
+{
+				/* this token includes at least one '$' */
+				yylval.string = expand_token(yytext, yyleng);
+				if (strlen(yylval.string))
+					return T_WORD;
+				free(yylval.string);
+			}
+	YY_BREAK
+case 51:
+YY_RULE_SETUP
+warn_ignored_character(*yytext);
+	YY_BREAK
+
+case 52:
+YY_RULE_SETUP
+{
+		alloc_string(yytext, yyleng);
+		yylval.string = text;
+		return T_ASSIGN_VAL;
+	}
+	YY_BREAK
+case 53:
+/* rule 53 can match eol */
+YY_RULE_SETUP
+{ BEGIN(INITIAL); return T_EOL; }
+	YY_BREAK
+case 54:
+YY_RULE_SETUP
+
+	YY_BREAK
+
+case 55:
+YY_RULE_SETUP
+append_expanded_string(yytext);
+	YY_BREAK
+case 56:
+YY_RULE_SETUP
+{
+		append_string(yytext, yyleng);
+	}
+	YY_BREAK
+case 57:
+YY_RULE_SETUP
+{
+		append_string(yytext + 1, yyleng - 1);
+	}
+	YY_BREAK
+case 58:
+YY_RULE_SETUP
+{
+		if (open_quote == yytext[0]) {
+			BEGIN(INITIAL);
+			yylval.string = text;
+			return T_WORD_QUOTE;
+		} else
+			append_string(yytext, 1);
+	}
+	YY_BREAK
+case 59:
+/* rule 59 can match eol */
+YY_RULE_SETUP
+{
+		fprintf(stderr,
+			"%s:%d:warning: multi-line strings not supported\n",
+			zconf_curname(), zconf_lineno());
+		unput('\n');
+		BEGIN(INITIAL);
+		yylval.string = text;
+		return T_WORD_QUOTE;
+	}
+	YY_BREAK
+case YY_STATE_EOF(STRING):
+{
+		BEGIN(INITIAL);
+		yylval.string = text;
+		return T_WORD_QUOTE;
+	}
+	YY_BREAK
+
+case 60:
+YY_RULE_SETUP
+{
+		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);
+		}
+	}
+	YY_BREAK
+case 61:
+/* rule 61 can match eol */
+*yy_cp = (yy_hold_char); /* undo effects of setting up yytext */
+YY_LINENO_REWIND_TO(yy_cp - 1);
+(yy_c_buf_p) = yy_cp -= 1;
+YY_DO_BEFORE_ACTION; /* set up yytext again */
+YY_RULE_SETUP
+{
+		zconf_endhelp();
+		return T_HELPTEXT;
+	}
+	YY_BREAK
+case 62:
+/* rule 62 can match eol */
+YY_RULE_SETUP
+{
+		append_string("\n", 1);
+	}
+	YY_BREAK
+case 63:
+YY_RULE_SETUP
+{
+		while (yyleng) {
+			if ((yytext[yyleng-1] != ' ') && (yytext[yyleng-1] != '\t'))
+				break;
+			yyleng--;
+		}
+		append_string(yytext, yyleng);
+		if (!first_ts)
+			first_ts = last_ts;
+	}
+	YY_BREAK
+case YY_STATE_EOF(HELP):
+{
+		zconf_endhelp();
+		return T_HELPTEXT;
+	}
+	YY_BREAK
+
+case YY_STATE_EOF(INITIAL):
+case YY_STATE_EOF(ASSIGN_VAL):
+{
+	BEGIN(INITIAL);
+
+	if (prev_token != T_EOL && prev_token != T_HELPTEXT)
+		fprintf(stderr, "%s:%d:warning: no new line at end of file\n",
+			current_file->name, yylineno);
+
+	if (current_file) {
+		zconf_endfile();
+		return T_EOL;
+	}
+	fclose(yyin);
+	yyterminate();
+}
+	YY_BREAK
+case 64:
+YY_RULE_SETUP
+YY_FATAL_ERROR( "flex scanner jammed" );
+	YY_BREAK
+
+	case YY_END_OF_BUFFER:
+		{
+		/* Amount of text matched not including the EOB char. */
+		int yy_amount_of_matched_text = (int) (yy_cp - (yytext_ptr)) - 1;
+
+		/* Undo the effects of YY_DO_BEFORE_ACTION. */
+		*yy_cp = (yy_hold_char);
+		YY_RESTORE_YY_MORE_OFFSET
+
+		if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_NEW )
+			{
+			/* We're scanning a new file or input source.  It's
+			 * possible that this happened because the user
+			 * just pointed yyin at a new source and called
+			 * yylex().  If so, then we have to assure
+			 * consistency between YY_CURRENT_BUFFER and our
+			 * globals.  Here is the right place to do so, because
+			 * this is the first action (other than possibly a
+			 * back-up) that will match for the new input source.
+			 */
+			(yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars;
+			YY_CURRENT_BUFFER_LVALUE->yy_input_file = yyin;
+			YY_CURRENT_BUFFER_LVALUE->yy_buffer_status = YY_BUFFER_NORMAL;
+			}
+
+		/* Note that here we test for yy_c_buf_p "<=" to the position
+		 * of the first EOB in the buffer, since yy_c_buf_p will
+		 * already have been incremented past the NUL character
+		 * (since all states make transitions on EOB to the
+		 * end-of-buffer state).  Contrast this with the test
+		 * in input().
+		 */
+		if ( (yy_c_buf_p) <= &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] )
+			{ /* This was really a NUL. */
+			yy_state_type yy_next_state;
+
+			(yy_c_buf_p) = (yytext_ptr) + yy_amount_of_matched_text;
+
+			yy_current_state = yy_get_previous_state(  );
+
+			/* Okay, we're now positioned to make the NUL
+			 * transition.  We couldn't have
+			 * yy_get_previous_state() go ahead and do it
+			 * for us because it doesn't know how to deal
+			 * with the possibility of jamming (and we don't
+			 * want to build jamming into it because then it
+			 * will run more slowly).
+			 */
+
+			yy_next_state = yy_try_NUL_trans( yy_current_state );
+
+			yy_bp = (yytext_ptr) + YY_MORE_ADJ;
+
+			if ( yy_next_state )
+				{
+				/* Consume the NUL. */
+				yy_cp = ++(yy_c_buf_p);
+				yy_current_state = yy_next_state;
+				goto yy_match;
+				}
+
+			else
+				{
+				yy_cp = (yy_c_buf_p);
+				goto yy_find_action;
+				}
+			}
+
+		else switch ( yy_get_next_buffer(  ) )
+			{
+			case EOB_ACT_END_OF_FILE:
+				{
+				(yy_did_buffer_switch_on_eof) = 0;
+
+				if ( yywrap(  ) )
+					{
+					/* Note: because we've taken care in
+					 * yy_get_next_buffer() to have set up
+					 * yytext, we can now set up
+					 * yy_c_buf_p so that if some total
+					 * hoser (like flex itself) wants to
+					 * call the scanner after we return the
+					 * YY_NULL, it'll still work - another
+					 * YY_NULL will get returned.
+					 */
+					(yy_c_buf_p) = (yytext_ptr) + YY_MORE_ADJ;
+
+					yy_act = YY_STATE_EOF(YY_START);
+					goto do_action;
+					}
+
+				else
+					{
+					if ( ! (yy_did_buffer_switch_on_eof) )
+						YY_NEW_FILE;
+					}
+				break;
+				}
+
+			case EOB_ACT_CONTINUE_SCAN:
+				(yy_c_buf_p) =
+					(yytext_ptr) + yy_amount_of_matched_text;
+
+				yy_current_state = yy_get_previous_state(  );
+
+				yy_cp = (yy_c_buf_p);
+				yy_bp = (yytext_ptr) + YY_MORE_ADJ;
+				goto yy_match;
+
+			case EOB_ACT_LAST_MATCH:
+				(yy_c_buf_p) =
+				&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)];
+
+				yy_current_state = yy_get_previous_state(  );
+
+				yy_cp = (yy_c_buf_p);
+				yy_bp = (yytext_ptr) + YY_MORE_ADJ;
+				goto yy_find_action;
+			}
+		break;
+		}
+
+	default:
+		YY_FATAL_ERROR(
+			"fatal flex scanner internal error--no action found" );
+	} /* end of action switch */
+		} /* end of scanning one token */
+	} /* end of user's declarations */
+} /* end of yylex */
+
+/* yy_get_next_buffer - try to read in a new buffer
+ *
+ * Returns a code representing an action:
+ *	EOB_ACT_LAST_MATCH -
+ *	EOB_ACT_CONTINUE_SCAN - continue scanning from current position
+ *	EOB_ACT_END_OF_FILE - end of file
+ */
+static int yy_get_next_buffer (void)
+{
+    	char *dest = YY_CURRENT_BUFFER_LVALUE->yy_ch_buf;
+	char *source = (yytext_ptr);
+	int number_to_move, i;
+	int ret_val;
+
+	if ( (yy_c_buf_p) > &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] )
+		YY_FATAL_ERROR(
+		"fatal flex scanner internal error--end of buffer missed" );
+
+	if ( YY_CURRENT_BUFFER_LVALUE->yy_fill_buffer == 0 )
+		{ /* Don't try to fill the buffer, so this is an EOF. */
+		if ( (yy_c_buf_p) - (yytext_ptr) - YY_MORE_ADJ == 1 )
+			{
+			/* We matched a single character, the EOB, so
+			 * treat this as a final EOF.
+			 */
+			return EOB_ACT_END_OF_FILE;
+			}
+
+		else
+			{
+			/* We matched some text prior to the EOB, first
+			 * process it.
+			 */
+			return EOB_ACT_LAST_MATCH;
+			}
+		}
+
+	/* Try to read more data. */
+
+	/* First move last chars to start of buffer. */
+	number_to_move = (int) ((yy_c_buf_p) - (yytext_ptr) - 1);
+
+	for ( i = 0; i < number_to_move; ++i )
+		*(dest++) = *(source++);
+
+	if ( YY_CURRENT_BUFFER_LVALUE->yy_buffer_status == YY_BUFFER_EOF_PENDING )
+		/* don't do the read, it's not guaranteed to return an EOF,
+		 * just force an EOF
+		 */
+		YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars) = 0;
+
+	else
+		{
+			int num_to_read =
+			YY_CURRENT_BUFFER_LVALUE->yy_buf_size - number_to_move - 1;
+
+		while ( num_to_read <= 0 )
+			{ /* Not enough room in the buffer - grow it. */
+
+			/* just a shorter name for the current buffer */
+			YY_BUFFER_STATE b = YY_CURRENT_BUFFER_LVALUE;
+
+			int yy_c_buf_p_offset =
+				(int) ((yy_c_buf_p) - b->yy_ch_buf);
+
+			if ( b->yy_is_our_buffer )
+				{
+				int new_size = b->yy_buf_size * 2;
+
+				if ( new_size <= 0 )
+					b->yy_buf_size += b->yy_buf_size / 8;
+				else
+					b->yy_buf_size *= 2;
+
+				b->yy_ch_buf = (char *)
+					/* Include room in for 2 EOB chars. */
+					yyrealloc( (void *) b->yy_ch_buf,
+							 (yy_size_t) (b->yy_buf_size + 2)  );
+				}
+			else
+				/* Can't grow it, we don't own it. */
+				b->yy_ch_buf = NULL;
+
+			if ( ! b->yy_ch_buf )
+				YY_FATAL_ERROR(
+				"fatal error - scanner input buffer overflow" );
+
+			(yy_c_buf_p) = &b->yy_ch_buf[yy_c_buf_p_offset];
+
+			num_to_read = YY_CURRENT_BUFFER_LVALUE->yy_buf_size -
+						number_to_move - 1;
+
+			}
+
+		if ( num_to_read > YY_READ_BUF_SIZE )
+			num_to_read = YY_READ_BUF_SIZE;
+
+		/* Read in more data. */
+		YY_INPUT( (&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move]),
+			(yy_n_chars), num_to_read );
+
+		YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars);
+		}
+
+	if ( (yy_n_chars) == 0 )
+		{
+		if ( number_to_move == YY_MORE_ADJ )
+			{
+			ret_val = EOB_ACT_END_OF_FILE;
+			yyrestart( yyin  );
+			}
+
+		else
+			{
+			ret_val = EOB_ACT_LAST_MATCH;
+			YY_CURRENT_BUFFER_LVALUE->yy_buffer_status =
+				YY_BUFFER_EOF_PENDING;
+			}
+		}
+
+	else
+		ret_val = EOB_ACT_CONTINUE_SCAN;
+
+	if (((yy_n_chars) + number_to_move) > YY_CURRENT_BUFFER_LVALUE->yy_buf_size) {
+		/* Extend the array by 50%, plus the number we really need. */
+		int new_size = (yy_n_chars) + number_to_move + ((yy_n_chars) >> 1);
+		YY_CURRENT_BUFFER_LVALUE->yy_ch_buf = (char *) yyrealloc(
+			(void *) YY_CURRENT_BUFFER_LVALUE->yy_ch_buf, (yy_size_t) new_size  );
+		if ( ! YY_CURRENT_BUFFER_LVALUE->yy_ch_buf )
+			YY_FATAL_ERROR( "out of dynamic memory in yy_get_next_buffer()" );
+		/* "- 2" to take care of EOB's */
+		YY_CURRENT_BUFFER_LVALUE->yy_buf_size = (int) (new_size - 2);
+	}
+
+	(yy_n_chars) += number_to_move;
+	YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] = YY_END_OF_BUFFER_CHAR;
+	YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars) + 1] = YY_END_OF_BUFFER_CHAR;
+
+	(yytext_ptr) = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[0];
+
+	return ret_val;
+}
+
+/* yy_get_previous_state - get the state just before the EOB char was reached */
+
+    static yy_state_type yy_get_previous_state (void)
+{
+	yy_state_type yy_current_state;
+	char *yy_cp;
+    
+	yy_current_state = (yy_start);
+
+	for ( yy_cp = (yytext_ptr) + YY_MORE_ADJ; yy_cp < (yy_c_buf_p); ++yy_cp )
+		{
+		yy_current_state = yy_nxt[yy_current_state][(*yy_cp ? yy_ec[YY_SC_TO_UI(*yy_cp)] : 1)];
+		}
+
+	return yy_current_state;
+}
+
+/* yy_try_NUL_trans - try to make a transition on the NUL character
+ *
+ * synopsis
+ *	next_state = yy_try_NUL_trans( current_state );
+ */
+    static yy_state_type yy_try_NUL_trans  (yy_state_type yy_current_state )
+{
+	int yy_is_jam;
+    
+	yy_current_state = yy_nxt[yy_current_state][1];
+	yy_is_jam = (yy_current_state <= 0);
+
+		return yy_is_jam ? 0 : yy_current_state;
+}
+
+#ifndef YY_NO_UNPUT
+
+    static void yyunput (int c, char * yy_bp )
+{
+	char *yy_cp;
+    
+    yy_cp = (yy_c_buf_p);
+
+	/* undo effects of setting up yytext */
+	*yy_cp = (yy_hold_char);
+
+	if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 )
+		{ /* need to shift things up to make room */
+		/* +2 for EOB chars. */
+		int number_to_move = (yy_n_chars) + 2;
+		char *dest = &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[
+					YY_CURRENT_BUFFER_LVALUE->yy_buf_size + 2];
+		char *source =
+				&YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[number_to_move];
+
+		while ( source > YY_CURRENT_BUFFER_LVALUE->yy_ch_buf )
+			*--dest = *--source;
+
+		yy_cp += (int) (dest - source);
+		yy_bp += (int) (dest - source);
+		YY_CURRENT_BUFFER_LVALUE->yy_n_chars =
+			(yy_n_chars) = (int) YY_CURRENT_BUFFER_LVALUE->yy_buf_size;
+
+		if ( yy_cp < YY_CURRENT_BUFFER_LVALUE->yy_ch_buf + 2 )
+			YY_FATAL_ERROR( "flex scanner push-back overflow" );
+		}
+
+	*--yy_cp = (char) c;
+
+    if ( c == '\n' ){
+        --yylineno;
+    }
+
+	(yytext_ptr) = yy_bp;
+	(yy_hold_char) = *yy_cp;
+	(yy_c_buf_p) = yy_cp;
+}
+
+#endif
+
+#ifndef YY_NO_INPUT
+#ifdef __cplusplus
+    static int yyinput (void)
+#else
+    static int input  (void)
+#endif
+
+{
+	int c;
+    
+	*(yy_c_buf_p) = (yy_hold_char);
+
+	if ( *(yy_c_buf_p) == YY_END_OF_BUFFER_CHAR )
+		{
+		/* yy_c_buf_p now points to the character we want to return.
+		 * If this occurs *before* the EOB characters, then it's a
+		 * valid NUL; if not, then we've hit the end of the buffer.
+		 */
+		if ( (yy_c_buf_p) < &YY_CURRENT_BUFFER_LVALUE->yy_ch_buf[(yy_n_chars)] )
+			/* This was really a NUL. */
+			*(yy_c_buf_p) = '\0';
+
+		else
+			{ /* need more input */
+			int offset = (int) ((yy_c_buf_p) - (yytext_ptr));
+			++(yy_c_buf_p);
+
+			switch ( yy_get_next_buffer(  ) )
+				{
+				case EOB_ACT_LAST_MATCH:
+					/* This happens because yy_g_n_b()
+					 * sees that we've accumulated a
+					 * token and flags that we need to
+					 * try matching the token before
+					 * proceeding.  But for input(),
+					 * there's no matching to consider.
+					 * So convert the EOB_ACT_LAST_MATCH
+					 * to EOB_ACT_END_OF_FILE.
+					 */
+
+					/* Reset buffer status. */
+					yyrestart( yyin );
+
+					/*FALLTHROUGH*/
+
+				case EOB_ACT_END_OF_FILE:
+					{
+					if ( yywrap(  ) )
+						return 0;
+
+					if ( ! (yy_did_buffer_switch_on_eof) )
+						YY_NEW_FILE;
+#ifdef __cplusplus
+					return yyinput();
+#else
+					return input();
+#endif
+					}
+
+				case EOB_ACT_CONTINUE_SCAN:
+					(yy_c_buf_p) = (yytext_ptr) + offset;
+					break;
+				}
+			}
+		}
+
+	c = *(unsigned char *) (yy_c_buf_p);	/* cast for 8-bit char's */
+	*(yy_c_buf_p) = '\0';	/* preserve yytext */
+	(yy_hold_char) = *++(yy_c_buf_p);
+
+	if ( c == '\n' )
+		
+    yylineno++;
+;
+
+	return c;
+}
+#endif	/* ifndef YY_NO_INPUT */
+
+/** Immediately switch to a different input stream.
+ * @param input_file A readable stream.
+ * 
+ * @note This function does not reset the start condition to @c INITIAL .
+ */
+    void yyrestart  (FILE * input_file )
+{
+    
+	if ( ! YY_CURRENT_BUFFER ){
+        yyensure_buffer_stack ();
+		YY_CURRENT_BUFFER_LVALUE =
+            yy_create_buffer( yyin, YY_BUF_SIZE );
+	}
+
+	yy_init_buffer( YY_CURRENT_BUFFER, input_file );
+	yy_load_buffer_state(  );
+}
+
+/** Switch to a different input buffer.
+ * @param new_buffer The new input buffer.
+ * 
+ */
+    void yy_switch_to_buffer  (YY_BUFFER_STATE  new_buffer )
+{
+    
+	/* TODO. We should be able to replace this entire function body
+	 * with
+	 *		yypop_buffer_state();
+	 *		yypush_buffer_state(new_buffer);
+     */
+	yyensure_buffer_stack ();
+	if ( YY_CURRENT_BUFFER == new_buffer )
+		return;
+
+	if ( YY_CURRENT_BUFFER )
+		{
+		/* Flush out information for old buffer. */
+		*(yy_c_buf_p) = (yy_hold_char);
+		YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p);
+		YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars);
+		}
+
+	YY_CURRENT_BUFFER_LVALUE = new_buffer;
+	yy_load_buffer_state(  );
+
+	/* We don't actually know whether we did this switch during
+	 * EOF (yywrap()) processing, but the only time this flag
+	 * is looked at is after yywrap() is called, so it's safe
+	 * to go ahead and always set it.
+	 */
+	(yy_did_buffer_switch_on_eof) = 1;
+}
+
+static void yy_load_buffer_state  (void)
+{
+    	(yy_n_chars) = YY_CURRENT_BUFFER_LVALUE->yy_n_chars;
+	(yytext_ptr) = (yy_c_buf_p) = YY_CURRENT_BUFFER_LVALUE->yy_buf_pos;
+	yyin = YY_CURRENT_BUFFER_LVALUE->yy_input_file;
+	(yy_hold_char) = *(yy_c_buf_p);
+}
+
+/** Allocate and initialize an input buffer state.
+ * @param file A readable stream.
+ * @param size The character buffer size in bytes. When in doubt, use @c YY_BUF_SIZE.
+ * 
+ * @return the allocated buffer state.
+ */
+    YY_BUFFER_STATE yy_create_buffer  (FILE * file, int  size )
+{
+	YY_BUFFER_STATE b;
+    
+	b = (YY_BUFFER_STATE) yyalloc( sizeof( struct yy_buffer_state )  );
+	if ( ! b )
+		YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" );
+
+	b->yy_buf_size = size;
+
+	/* yy_ch_buf has to be 2 characters longer than the size given because
+	 * we need to put in 2 end-of-buffer characters.
+	 */
+	b->yy_ch_buf = (char *) yyalloc( (yy_size_t) (b->yy_buf_size + 2)  );
+	if ( ! b->yy_ch_buf )
+		YY_FATAL_ERROR( "out of dynamic memory in yy_create_buffer()" );
+
+	b->yy_is_our_buffer = 1;
+
+	yy_init_buffer( b, file );
+
+	return b;
+}
+
+/** Destroy the buffer.
+ * @param b a buffer created with yy_create_buffer()
+ * 
+ */
+    void yy_delete_buffer (YY_BUFFER_STATE  b )
+{
+    
+	if ( ! b )
+		return;
+
+	if ( b == YY_CURRENT_BUFFER ) /* Not sure if we should pop here. */
+		YY_CURRENT_BUFFER_LVALUE = (YY_BUFFER_STATE) 0;
+
+	if ( b->yy_is_our_buffer )
+		yyfree( (void *) b->yy_ch_buf  );
+
+	yyfree( (void *) b  );
+}
+
+/* Initializes or reinitializes a buffer.
+ * This function is sometimes called more than once on the same buffer,
+ * such as during a yyrestart() or at EOF.
+ */
+    static void yy_init_buffer  (YY_BUFFER_STATE  b, FILE * file )
+
+{
+	int oerrno = errno;
+    
+	yy_flush_buffer( b );
+
+	b->yy_input_file = file;
+	b->yy_fill_buffer = 1;
+
+    /* If b is the current buffer, then yy_init_buffer was _probably_
+     * called from yyrestart() or through yy_get_next_buffer.
+     * In that case, we don't want to reset the lineno or column.
+     */
+    if (b != YY_CURRENT_BUFFER){
+        b->yy_bs_lineno = 1;
+        b->yy_bs_column = 0;
+    }
+
+        b->yy_is_interactive = 0;
+    
+	errno = oerrno;
+}
+
+/** Discard all buffered characters. On the next scan, YY_INPUT will be called.
+ * @param b the buffer state to be flushed, usually @c YY_CURRENT_BUFFER.
+ * 
+ */
+    void yy_flush_buffer (YY_BUFFER_STATE  b )
+{
+    	if ( ! b )
+		return;
+
+	b->yy_n_chars = 0;
+
+	/* We always need two end-of-buffer characters.  The first causes
+	 * a transition to the end-of-buffer state.  The second causes
+	 * a jam in that state.
+	 */
+	b->yy_ch_buf[0] = YY_END_OF_BUFFER_CHAR;
+	b->yy_ch_buf[1] = YY_END_OF_BUFFER_CHAR;
+
+	b->yy_buf_pos = &b->yy_ch_buf[0];
+
+	b->yy_at_bol = 1;
+	b->yy_buffer_status = YY_BUFFER_NEW;
+
+	if ( b == YY_CURRENT_BUFFER )
+		yy_load_buffer_state(  );
+}
+
+/** Pushes the new state onto the stack. The new state becomes
+ *  the current state. This function will allocate the stack
+ *  if necessary.
+ *  @param new_buffer The new state.
+ *  
+ */
+void yypush_buffer_state (YY_BUFFER_STATE new_buffer )
+{
+    	if (new_buffer == NULL)
+		return;
+
+	yyensure_buffer_stack();
+
+	/* This block is copied from yy_switch_to_buffer. */
+	if ( YY_CURRENT_BUFFER )
+		{
+		/* Flush out information for old buffer. */
+		*(yy_c_buf_p) = (yy_hold_char);
+		YY_CURRENT_BUFFER_LVALUE->yy_buf_pos = (yy_c_buf_p);
+		YY_CURRENT_BUFFER_LVALUE->yy_n_chars = (yy_n_chars);
+		}
+
+	/* Only push if top exists. Otherwise, replace top. */
+	if (YY_CURRENT_BUFFER)
+		(yy_buffer_stack_top)++;
+	YY_CURRENT_BUFFER_LVALUE = new_buffer;
+
+	/* copied from yy_switch_to_buffer. */
+	yy_load_buffer_state(  );
+	(yy_did_buffer_switch_on_eof) = 1;
+}
+
+/** Removes and deletes the top of the stack, if present.
+ *  The next element becomes the new top.
+ *  
+ */
+void yypop_buffer_state (void)
+{
+    	if (!YY_CURRENT_BUFFER)
+		return;
+
+	yy_delete_buffer(YY_CURRENT_BUFFER );
+	YY_CURRENT_BUFFER_LVALUE = NULL;
+	if ((yy_buffer_stack_top) > 0)
+		--(yy_buffer_stack_top);
+
+	if (YY_CURRENT_BUFFER) {
+		yy_load_buffer_state(  );
+		(yy_did_buffer_switch_on_eof) = 1;
+	}
+}
+
+/* Allocates the stack if it does not exist.
+ *  Guarantees space for at least one push.
+ */
+static void yyensure_buffer_stack (void)
+{
+	yy_size_t num_to_alloc;
+    
+	if (!(yy_buffer_stack)) {
+
+		/* First allocation is just for 2 elements, since we don't know if this
+		 * scanner will even need a stack. We use 2 instead of 1 to avoid an
+		 * immediate realloc on the next call.
+         */
+      num_to_alloc = 1; /* After all that talk, this was set to 1 anyways... */
+		(yy_buffer_stack) = (struct yy_buffer_state**)yyalloc
+								(num_to_alloc * sizeof(struct yy_buffer_state*)
+								);
+		if ( ! (yy_buffer_stack) )
+			YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" );
+
+		memset((yy_buffer_stack), 0, num_to_alloc * sizeof(struct yy_buffer_state*));
+
+		(yy_buffer_stack_max) = num_to_alloc;
+		(yy_buffer_stack_top) = 0;
+		return;
+	}
+
+	if ((yy_buffer_stack_top) >= ((yy_buffer_stack_max)) - 1){
+
+		/* Increase the buffer to prepare for a possible push. */
+		yy_size_t grow_size = 8 /* arbitrary grow size */;
+
+		num_to_alloc = (yy_buffer_stack_max) + grow_size;
+		(yy_buffer_stack) = (struct yy_buffer_state**)yyrealloc
+								((yy_buffer_stack),
+								num_to_alloc * sizeof(struct yy_buffer_state*)
+								);
+		if ( ! (yy_buffer_stack) )
+			YY_FATAL_ERROR( "out of dynamic memory in yyensure_buffer_stack()" );
+
+		/* zero only the new slots.*/
+		memset((yy_buffer_stack) + (yy_buffer_stack_max), 0, grow_size * sizeof(struct yy_buffer_state*));
+		(yy_buffer_stack_max) = num_to_alloc;
+	}
+}
+
+/** Setup the input buffer state to scan directly from a user-specified character buffer.
+ * @param base the character buffer
+ * @param size the size in bytes of the character buffer
+ * 
+ * @return the newly allocated buffer state object.
+ */
+YY_BUFFER_STATE yy_scan_buffer  (char * base, yy_size_t  size )
+{
+	YY_BUFFER_STATE b;
+    
+	if ( size < 2 ||
+	     base[size-2] != YY_END_OF_BUFFER_CHAR ||
+	     base[size-1] != YY_END_OF_BUFFER_CHAR )
+		/* They forgot to leave room for the EOB's. */
+		return NULL;
+
+	b = (YY_BUFFER_STATE) yyalloc( sizeof( struct yy_buffer_state )  );
+	if ( ! b )
+		YY_FATAL_ERROR( "out of dynamic memory in yy_scan_buffer()" );
+
+	b->yy_buf_size = (int) (size - 2);	/* "- 2" to take care of EOB's */
+	b->yy_buf_pos = b->yy_ch_buf = base;
+	b->yy_is_our_buffer = 0;
+	b->yy_input_file = NULL;
+	b->yy_n_chars = b->yy_buf_size;
+	b->yy_is_interactive = 0;
+	b->yy_at_bol = 1;
+	b->yy_fill_buffer = 0;
+	b->yy_buffer_status = YY_BUFFER_NEW;
+
+	yy_switch_to_buffer( b  );
+
+	return b;
+}
+
+/** Setup the input buffer state to scan a string. The next call to yylex() will
+ * scan from a @e copy of @a str.
+ * @param yystr a NUL-terminated string to scan
+ * 
+ * @return the newly allocated buffer state object.
+ * @note If you want to scan bytes that may contain NUL values, then use
+ *       yy_scan_bytes() instead.
+ */
+YY_BUFFER_STATE yy_scan_string (const char * yystr )
+{
+    
+	return yy_scan_bytes( yystr, (int) strlen(yystr) );
+}
+
+/** Setup the input buffer state to scan the given bytes. The next call to yylex() will
+ * scan from a @e copy of @a bytes.
+ * @param yybytes the byte buffer to scan
+ * @param _yybytes_len the number of bytes in the buffer pointed to by @a bytes.
+ * 
+ * @return the newly allocated buffer state object.
+ */
+YY_BUFFER_STATE yy_scan_bytes  (const char * yybytes, int  _yybytes_len )
+{
+	YY_BUFFER_STATE b;
+	char *buf;
+	yy_size_t n;
+	int i;
+    
+	/* Get memory for full buffer, including space for trailing EOB's. */
+	n = (yy_size_t) (_yybytes_len + 2);
+	buf = (char *) yyalloc( n  );
+	if ( ! buf )
+		YY_FATAL_ERROR( "out of dynamic memory in yy_scan_bytes()" );
+
+	for ( i = 0; i < _yybytes_len; ++i )
+		buf[i] = yybytes[i];
+
+	buf[_yybytes_len] = buf[_yybytes_len+1] = YY_END_OF_BUFFER_CHAR;
+
+	b = yy_scan_buffer( buf, n );
+	if ( ! b )
+		YY_FATAL_ERROR( "bad buffer in yy_scan_bytes()" );
+
+	/* It's okay to grow etc. this buffer, and we should throw it
+	 * away when we're done.
+	 */
+	b->yy_is_our_buffer = 1;
+
+	return b;
+}
+
+#ifndef YY_EXIT_FAILURE
+#define YY_EXIT_FAILURE 2
+#endif
+
+static void yynoreturn yy_fatal_error (const char* msg )
+{
+			fprintf( stderr, "%s\n", msg );
+	exit( YY_EXIT_FAILURE );
+}
+
+/* Redefine yyless() so it works in section 3 code. */
+
+#undef yyless
+#define yyless(n) \
+	do \
+		{ \
+		/* Undo effects of setting up yytext. */ \
+        int yyless_macro_arg = (n); \
+        YY_LESS_LINENO(yyless_macro_arg);\
+		yytext[yyleng] = (yy_hold_char); \
+		(yy_c_buf_p) = yytext + yyless_macro_arg; \
+		(yy_hold_char) = *(yy_c_buf_p); \
+		*(yy_c_buf_p) = '\0'; \
+		yyleng = yyless_macro_arg; \
+		} \
+	while ( 0 )
+
+/* Accessor  methods (get/set functions) to struct members. */
+
+/** Get the current line number.
+ * 
+ */
+int yyget_lineno  (void)
+{
+    
+    return yylineno;
+}
+
+/** Get the input stream.
+ * 
+ */
+FILE *yyget_in  (void)
+{
+        return yyin;
+}
+
+/** Get the output stream.
+ * 
+ */
+FILE *yyget_out  (void)
+{
+        return yyout;
+}
+
+/** Get the length of the current token.
+ * 
+ */
+int yyget_leng  (void)
+{
+        return yyleng;
+}
+
+/** Get the current token.
+ * 
+ */
+
+char *yyget_text  (void)
+{
+        return yytext;
+}
+
+/** Set the current line number.
+ * @param _line_number line number
+ * 
+ */
+void yyset_lineno (int  _line_number )
+{
+    
+    yylineno = _line_number;
+}
+
+/** Set the input stream. This does not discard the current
+ * input buffer.
+ * @param _in_str A readable stream.
+ * 
+ * @see yy_switch_to_buffer
+ */
+void yyset_in (FILE *  _in_str )
+{
+        yyin = _in_str ;
+}
+
+void yyset_out (FILE *  _out_str )
+{
+        yyout = _out_str ;
+}
+
+int yyget_debug  (void)
+{
+        return yy_flex_debug;
+}
+
+void yyset_debug (int  _bdebug )
+{
+        yy_flex_debug = _bdebug ;
+}
+
+static int yy_init_globals (void)
+{
+        /* Initialization is the same as for the non-reentrant scanner.
+     * This function is called from yylex_destroy(), so don't allocate here.
+     */
+
+    /* We do not touch yylineno unless the option is enabled. */
+    yylineno =  1;
+    
+    (yy_buffer_stack) = NULL;
+    (yy_buffer_stack_top) = 0;
+    (yy_buffer_stack_max) = 0;
+    (yy_c_buf_p) = NULL;
+    (yy_init) = 0;
+    (yy_start) = 0;
+
+/* Defined in main.c */
+#ifdef YY_STDINIT
+    yyin = stdin;
+    yyout = stdout;
+#else
+    yyin = NULL;
+    yyout = NULL;
+#endif
+
+    /* For future reference: Set errno on error, since we are called by
+     * yylex_init()
+     */
+    return 0;
+}
+
+/* yylex_destroy is for both reentrant and non-reentrant scanners. */
+int yylex_destroy  (void)
+{
+    
+    /* Pop the buffer stack, destroying each element. */
+	while(YY_CURRENT_BUFFER){
+		yy_delete_buffer( YY_CURRENT_BUFFER  );
+		YY_CURRENT_BUFFER_LVALUE = NULL;
+		yypop_buffer_state();
+	}
+
+	/* Destroy the stack itself. */
+	yyfree((yy_buffer_stack) );
+	(yy_buffer_stack) = NULL;
+
+    /* Reset the globals. This is important in a non-reentrant scanner so the next time
+     * yylex() is called, initialization will occur. */
+    yy_init_globals( );
+
+    return 0;
+}
+
+/*
+ * Internal utility routines.
+ */
+
+#ifndef yytext_ptr
+static void yy_flex_strncpy (char* s1, const char * s2, int n )
+{
+		
+	int i;
+	for ( i = 0; i < n; ++i )
+		s1[i] = s2[i];
+}
+#endif
+
+#ifdef YY_NEED_STRLEN
+static int yy_flex_strlen (const char * s )
+{
+	int n;
+	for ( n = 0; s[n]; ++n )
+		;
+
+	return n;
+}
+#endif
+
+void *yyalloc (yy_size_t  size )
+{
+			return malloc(size);
+}
+
+void *yyrealloc  (void * ptr, yy_size_t  size )
+{
+		
+	/* The cast to (char *) in the following accommodates both
+	 * implementations that use char* generic pointers, and those
+	 * that use void* generic pointers.  It works with the latter
+	 * because both ANSI C and C++ allow castless assignment from
+	 * any pointer type to void*, and deal with argument conversions
+	 * as though doing an assignment.
+	 */
+	return realloc(ptr, size);
+}
+
+void yyfree (void * ptr )
+{
+			free( (char *) ptr );	/* see yyrealloc() for (char *) cast */
+}
+
+#define YYTABLES_NAME "yytables"
+
+/* 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 file/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.
+			 */
+			current_pos.file = current_file;
+			current_pos.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. */
+	while ((c = input()) != EOF) {
+		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);
+	}
+
+	current_buf = xmalloc(sizeof(*current_buf));
+	memset(current_buf, 0, sizeof(*current_buf));
+
+	current_file = file_lookup(name);
+	yylineno = 1;
+}
+
+static void __zconf_nextfile(const char *name)
+{
+	struct file *iter;
+	struct file *file = file_lookup(name);
+	struct buffer *buf = xmalloc(sizeof(*buf));
+	memset(buf, 0, sizeof(*buf));
+
+	current_buf->state = YY_CURRENT_BUFFER;
+	yyin = zconf_fopen(file->name);
+	if (!yyin) {
+		fprintf(stderr, "%s:%d: can't open file \"%s\"\n",
+			zconf_curname(), zconf_lineno(), file->name);
+		exit(1);
+	}
+	yy_switch_to_buffer(yy_create_buffer(yyin, YY_BUF_SIZE));
+	buf->parent = current_buf;
+	current_buf = buf;
+
+	current_file->lineno = yylineno;
+	file->parent = current_file;
+
+	for (iter = current_file; iter; iter = iter->parent) {
+		if (!strcmp(iter->name, file->name)) {
+			fprintf(stderr,
+				"Recursive inclusion detected.\n"
+				"Inclusion path:\n"
+				"  current file : %s\n", file->name);
+			iter = file;
+			do {
+				iter = iter->parent;
+				fprintf(stderr, "  included from: %s:%d\n",
+					iter->name, iter->lineno - 1);
+			} while (strcmp(iter->name, file->name));
+			exit(1);
+		}
+	}
+
+	yylineno = 1;
+	current_file = file;
+}
+
+void zconf_nextfile(const char *name)
+{
+	glob_t gl;
+	int err;
+	int i;
+	char path[PATH_MAX], *p;
+
+	err = glob(name, GLOB_ERR | GLOB_MARK, NULL, &gl);
+
+	/* ignore wildcard patterns that return no result */
+	if (err == GLOB_NOMATCH && strchr(name, '*')) {
+		err = 0;
+		gl.gl_pathc = 0;
+	}
+
+	if (err == GLOB_NOMATCH) {
+		p = strdup(current_file->name);
+		if (p) {
+			snprintf(path, sizeof(path), "%s/%s", dirname(p), name);
+			err = glob(path, GLOB_ERR | GLOB_MARK, NULL, &gl);
+			free(p);
+		}
+	}
+
+	if (err) {
+		const char *reason = "unknown error";
+
+		switch (err) {
+		case GLOB_NOSPACE:
+			reason = "out of memory";
+			break;
+		case GLOB_ABORTED:
+			reason = "read error";
+			break;
+		case GLOB_NOMATCH:
+			reason = "No files found";
+			break;
+		default:
+			break;
+		}
+
+		printf("%s:%d: glob failed: %s \"%s\"\n", zconf_curname(), zconf_lineno(),
+			reason, name);
+
+		exit(1);
+	}
+
+	for (i = 0; i < gl.gl_pathc; i++)
+		__zconf_nextfile(gl.gl_pathv[i]);
+}
+
+static void zconf_endfile(void)
+{
+	struct buffer *parent;
+
+	current_file = current_file->parent;
+	if (current_file)
+		yylineno = current_file->lineno;
+
+	parent = current_buf->parent;
+	if (parent) {
+		fclose(yyin);
+		yy_delete_buffer(YY_CURRENT_BUFFER);
+		yy_switch_to_buffer(parent->state);
+	}
+	free(current_buf);
+	current_buf = parent;
+}
+
+int zconf_lineno(void)
+{
+	return current_pos.lineno;
+}
+
+const char *zconf_curname(void)
+{
+	return current_pos.file ? current_pos.file->name : "<none>";
+}
+
diff --git a/scripts/config/list.h b/scripts/config/list.h
new file mode 100644
index 0000000..979cd51
--- /dev/null
+++ b/scripts/config/list.h
@@ -0,0 +1,132 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+#ifndef LIST_H
+#define LIST_H
+
+/*
+ * Copied from include/linux/...
+ */
+
+#undef offsetof
+#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
+
+/**
+ * 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) ({                      \
+	const typeof( ((type *)0)->member ) *__mptr = (ptr);    \
+	(type *)( (char *)__mptr - offsetof(type,member) );})
+
+
+struct list_head {
+	struct list_head *next, *prev;
+};
+
+
+#define LIST_HEAD_INIT(name) { &(name), &(name) }
+
+#define LIST_HEAD(name) \
+	struct list_head name = LIST_HEAD_INIT(name)
+
+/**
+ * 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_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_entry((head)->next, typeof(*pos), member);	\
+	     &pos->member != (head); 	\
+	     pos = list_entry(pos->member.next, typeof(*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_entry((head)->next, typeof(*pos), member),	\
+		n = list_entry(pos->member.next, typeof(*pos), member);	\
+	     &pos->member != (head);					\
+	     pos = n, n = list_entry(n->member.next, typeof(*n), member))
+
+/**
+ * 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;
+}
+
+/*
+ * 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_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;
+}
+
+#define LIST_POISON1  ((void *) 0x00100100)
+#define LIST_POISON2  ((void *) 0x00200200)
+/**
+ * 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->prev, entry->next);
+	entry->next = (struct list_head*)LIST_POISON1;
+	entry->prev = (struct list_head*)LIST_POISON2;
+}
+#endif
diff --git a/scripts/config/lkc.h b/scripts/config/lkc.h
new file mode 100644
index 0000000..1c8717d
--- /dev/null
+++ b/scripts/config/lkc.h
@@ -0,0 +1,153 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * 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 "srctree"
+
+#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);
+int zconf_lineno(void);
+const char *zconf_curname(void);
+extern int recursive_is_error;
+
+/* confdata.c */
+const char *conf_get_configname(void);
+void set_all_choice_values(struct symbol *csym);
+
+/* 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 */
+struct file *file_lookup(const char *name);
+void *xmalloc(size_t size);
+void *xcalloc(size_t nmemb, size_t size);
+void *xrealloc(void *p, size_t size);
+char *xstrdup(const char *s);
+char *xstrndup(const char *s, size_t n);
+
+/* 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(struct gstr *gs);
+
+/* menu.c */
+void _menu_init(void);
+void menu_warn(struct menu *menu, const char *fmt, ...);
+struct menu *menu_add_menu(void);
+void menu_end_menu(void);
+void menu_add_entry(struct symbol *sym);
+void menu_add_dep(struct expr *dep);
+void menu_add_visibility(struct expr *dep);
+struct property *menu_add_prop(enum prop_type type, struct expr *expr, struct expr *dep);
+struct property *menu_add_prompt(enum prop_type type, 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(struct menu *parent);
+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(struct menu *menu);
+const char *menu_get_prompt(struct menu *menu);
+struct menu *menu_get_parent_menu(struct menu *menu);
+bool menu_has_help(struct menu *menu);
+const char *menu_get_help(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 symbol *sym);
+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(struct property *prop);
+
+static inline tristate sym_get_tristate_value(struct symbol *sym)
+{
+	return sym->curr.tri;
+}
+
+
+static inline struct symbol *sym_get_choice_value(struct symbol *sym)
+{
+	return (struct symbol *)sym->curr.val;
+}
+
+static inline bool sym_is_choice(struct symbol *sym)
+{
+	return sym->flags & SYMBOL_CHOICE ? true : false;
+}
+
+static inline bool sym_is_choice_value(struct symbol *sym)
+{
+	return sym->flags & SYMBOL_CHOICEVAL ? true : false;
+}
+
+static inline bool sym_is_optional(struct symbol *sym)
+{
+	return sym->flags & SYMBOL_OPTIONAL ? true : false;
+}
+
+static inline bool sym_has_value(struct symbol *sym)
+{
+	return sym->flags & SYMBOL_DEF_USER ? true : false;
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LKC_H */
diff --git a/scripts/config/lkc_proto.h b/scripts/config/lkc_proto.h
new file mode 100644
index 0000000..4babf64
--- /dev/null
+++ b/scripts/config/lkc_proto.h
@@ -0,0 +1,53 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+#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);
+void conf_reset(int def);
+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)(void));
+void conf_set_message_callback(void (*fn)(const char *s));
+
+/* symbol.c */
+extern struct symbol * symbol_hash[SYMBOL_HASHSIZE];
+
+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);
+enum symbol_type sym_get_type(struct symbol *sym);
+bool sym_tristate_within_range(struct symbol *sym,tristate tri);
+bool sym_set_tristate_value(struct symbol *sym,tristate tri);
+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(struct symbol *sym);
+struct property * sym_get_choice_prop(struct symbol *sym);
+const char * sym_get_string_value(struct symbol *sym);
+
+const char * prop_get_type_name(enum prop_type type);
+
+/* preprocess.c */
+enum variable_flavor {
+	VAR_SIMPLE,
+	VAR_RECURSIVE,
+	VAR_APPEND,
+};
+void env_write_dep(FILE *f, const char *auto_conf_name);
+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);
+
+/* expr.c */
+void expr_print(struct expr *e, void (*fn)(void *, struct symbol *, const char *), void *data, int prevtoken);
diff --git a/scripts/config/lxdialog/checklist.c b/scripts/config/lxdialog/checklist.c
new file mode 100644
index 0000000..29960ed
--- /dev/null
+++ b/scripts/config/lxdialog/checklist.c
@@ -0,0 +1,319 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ *  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_HEIGTH_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);
+
+	wnoutrefresh(dialog);
+	wnoutrefresh(list);
+	doupdate();
+
+	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 */
+}
diff --git a/scripts/config/lxdialog/dialog.h b/scripts/config/lxdialog/dialog.h
new file mode 100644
index 0000000..3128bd6
--- /dev/null
+++ b/scripts/config/lxdialog/dialog.h
@@ -0,0 +1,207 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ *  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 inputbox_border;
+	struct dialog_color searchbox;
+	struct dialog_color searchbox_title;
+	struct dialog_color searchbox_border;
+	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_HEIGTH_MIN 6	/* For dialog_checklist() */
+#define CHECKLIST_WIDTH_MIN 6
+#define INPUTBOX_HEIGTH_MIN 2	/* For dialog_inputbox() */
+#define INPUTBOX_WIDTH_MIN 2
+#define MENUBOX_HEIGTH_MIN 15	/* For dialog_menu() */
+#define MENUBOX_WIDTH_MIN 65
+#define TEXTBOX_HEIGTH_MIN 8	/* For dialog_textbox() */
+#define TEXTBOX_WIDTH_MIN 8
+#define YESNO_HEIGTH_MIN 4	/* For dialog_yesno() */
+#define YESNO_WIDTH_MIN 4
+#define WINDOW_HEIGTH_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);
diff --git a/scripts/config/lxdialog/inputbox.c b/scripts/config/lxdialog/inputbox.c
new file mode 100644
index 0000000..b8b6f15
--- /dev/null
+++ b/scripts/config/lxdialog/inputbox.c
@@ -0,0 +1,289 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ *  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_HEIGTH_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 */
+}
diff --git a/scripts/config/lxdialog/menubox.c b/scripts/config/lxdialog/menubox.c
new file mode 100644
index 0000000..bcdf3bb
--- /dev/null
+++ b/scripts/config/lxdialog/menubox.c
@@ -0,0 +1,416 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ *  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_HEIGTH_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 */
+}
diff --git a/scripts/config/lxdialog/textbox.c b/scripts/config/lxdialog/textbox.c
new file mode 100644
index 0000000..e02acb7
--- /dev/null
+++ b/scripts/config/lxdialog/textbox.c
@@ -0,0 +1,358 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ *  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_HEIGTH_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;
+}
diff --git a/scripts/config/lxdialog/util.c b/scripts/config/lxdialog/util.c
new file mode 100644
index 0000000..f2bfc5c
--- /dev/null
+++ b/scripts/config/lxdialog/util.c
@@ -0,0 +1,700 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ *  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.screen.atr = A_NORMAL;
+	dlg.shadow.atr = A_NORMAL;
+	dlg.dialog.atr = A_NORMAL;
+	dlg.title.atr = A_BOLD;
+	dlg.border.atr = A_NORMAL;
+	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.button_label_inactive.atr = A_NORMAL;
+	dlg.inputbox.atr = A_NORMAL;
+	dlg.inputbox_border.atr = A_NORMAL;
+	dlg.searchbox.atr = A_NORMAL;
+	dlg.searchbox_title.atr = A_BOLD;
+	dlg.searchbox_border.atr = A_NORMAL;
+	dlg.position_indicator.atr = A_BOLD;
+	dlg.menubox.atr = A_NORMAL;
+	dlg.menubox_border.atr = A_NORMAL;
+	dlg.item.atr = A_NORMAL;
+	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(inputbox_border,       COLOR_BLACK,  COLOR_WHITE,  false);
+	DLG_COLOR(searchbox,             COLOR_BLACK,  COLOR_WHITE,  false);
+	DLG_COLOR(searchbox_title,       COLOR_YELLOW, COLOR_WHITE,  true);
+	DLG_COLOR(searchbox_border,      COLOR_WHITE,  COLOR_WHITE,  true);
+	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_BLACK,  COLOR_BLACK, true);
+
+	DLG_COLOR(inputbox,         COLOR_YELLOW, COLOR_BLACK, false);
+	DLG_COLOR(inputbox_border,  COLOR_YELLOW, COLOR_BLACK, false);
+
+	DLG_COLOR(searchbox,        COLOR_YELLOW, COLOR_BLACK, false);
+	DLG_COLOR(searchbox_title,  COLOR_YELLOW, COLOR_BLACK, true);
+	DLG_COLOR(searchbox_border, COLOR_BLACK,  COLOR_BLACK, true);
+
+	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(searchbox_title,     COLOR_BLUE,   COLOR_WHITE, 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.inputbox_border);
+	init_one_color(&dlg.searchbox);
+	init_one_color(&dlg.searchbox_title);
+	init_one_color(&dlg.searchbox_border);
+	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_HEIGTH_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);
+}
diff --git a/scripts/config/lxdialog/yesno.c b/scripts/config/lxdialog/yesno.c
new file mode 100644
index 0000000..ff1db42
--- /dev/null
+++ b/scripts/config/lxdialog/yesno.c
@@ -0,0 +1,101 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ *  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_HEIGTH_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 */
+}
diff --git a/scripts/config/mconf-cfg.sh b/scripts/config/mconf-cfg.sh
new file mode 100755
index 0000000..4e48cc1
--- /dev/null
+++ b/scripts/config/mconf-cfg.sh
@@ -0,0 +1,55 @@
+#!/bin/sh
+# SPDX-License-Identifier: GPL-2.0-only
+
+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
diff --git a/scripts/config/mconf.c b/scripts/config/mconf.c
new file mode 100644
index 0000000..d357cf1
--- /dev/null
+++ b/scripts/config/mconf.c
@@ -0,0 +1,1060 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * 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 "lkc.h"
+#include "lxdialog/dialog.h"
+
+static const char mconf_readme[] =
+"OpenWrt config is based on Kernel kconfig\n"
+"so ipkg packages are referred here as modules.\n"
+"\n"
+"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 || IA64\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 int jump_key_char;
+
+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);
+}
+
+struct search_data {
+	struct list_head *head;
+	struct menu *target;
+};
+
+static int next_jump_key(int key)
+{
+	if (key < '1' || key > '9')
+		return '1';
+
+	key++;
+
+	if (key > '9')
+		key = '1';
+
+	return key;
+}
+
+static 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;
+}
+
+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_get_choice_value(sym);
+		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;
+		}
+
+		val = sym_get_tristate_value(sym);
+		if (sym_is_changeable(sym)) {
+			switch (type) {
+			case S_BOOLEAN:
+				item_make("[%c]", val == no ? ' ' : '*');
+				break;
+			case S_TRISTATE:
+				switch (val) {
+				case yes: ch = '*'; break;
+				case mod: ch = 'M'; break;
+				default:  ch = ' '; break;
+				}
+				item_make("<%c>", ch);
+				break;
+			}
+			item_set_tag('t');
+			item_set_data(menu);
+		} else {
+			item_make("   ");
+			item_set_tag(def_menu ? 't' : ':');
+			item_set_data(menu);
+		}
+
+		item_add_str("%*c%s", indent + 1, ' ', menu_get_prompt(menu));
+		if (val == yes) {
+			if (def_menu) {
+				item_add_str(" (%s)", menu_get_prompt(def_menu));
+				item_add_str("  --->");
+				if (def_menu->list) {
+					indent += 2;
+					build_conf(def_menu);
+					indent -= 2;
+				}
+			}
+			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);
+		if (sym_is_choice_value(sym) && val == yes) {
+			item_make("   ");
+			item_set_tag(':');
+			item_set_data(menu);
+		} else {
+			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;
+	struct property *prop;
+
+	active = sym_get_choice_value(menu->sym);
+	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_get_choice_value(menu->sym))
+				item_set_tag('X');
+		}
+		dialog_clear();
+		res = dialog_checklist(prompt ? prompt : "Main Menu",
+					radiolist_instructions,
+					MENUBOX_HEIGTH_MIN,
+					MENUBOX_WIDTH_MIN,
+					CHECKLIST_HEIGTH_MIN);
+		selected = item_activate_selected();
+		switch (res) {
+		case 0:
+			if (selected) {
+				child = item_data();
+				if (!child->sym)
+					break;
+
+				if (sym_get_tristate_value(child->sym) != yes) {
+					for_all_properties(menu->sym, prop, P_RESET) {
+						if (expr_calc_value(prop->visible.expr) == no)
+							continue;
+
+						conf_reset(S_DEF_USER);
+						break;
+					}
+				}
+				sym_set_tristate_value(child->sym, yes);
+			}
+			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) && sym_get_tristate_value(sym) == yes)
+					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;
+}
diff --git a/scripts/config/menu.c b/scripts/config/menu.c
new file mode 100644
index 0000000..d41a61a
--- /dev/null
+++ b/scripts/config/menu.c
@@ -0,0 +1,867 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>
+ */
+
+#include <ctype.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "lkc.h"
+#include "internal.h"
+
+static const char nohelp_text[] = "There is no help available for this option.";
+
+struct menu rootmenu;
+static struct menu **last_entry_ptr;
+
+struct file *file_list;
+struct file *current_file;
+
+void menu_warn(struct menu *menu, const char *fmt, ...)
+{
+	va_list ap;
+	va_start(ap, fmt);
+	fprintf(stderr, "%s:%d:warning: ", menu->file->name, menu->lineno);
+	vfprintf(stderr, fmt, ap);
+	fprintf(stderr, "\n");
+	va_end(ap);
+}
+
+static void prop_warn(struct property *prop, const char *fmt, ...)
+{
+	va_list ap;
+	va_start(ap, fmt);
+	fprintf(stderr, "%s:%d:warning: ", prop->file->name, 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)
+{
+	struct menu *menu;
+
+	menu = xmalloc(sizeof(*menu));
+	memset(menu, 0, sizeof(*menu));
+	menu->sym = sym;
+	menu->parent = current_menu;
+	menu->file = current_file;
+	menu->lineno = zconf_lineno();
+
+	*last_entry_ptr = menu;
+	last_entry_ptr = &menu->next;
+	current_entry = menu;
+	if (sym)
+		menu_add_symbol(P_SYMBOL, sym, NULL);
+}
+
+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->left.expr = rewrite_m(e->left.expr);
+		break;
+	case E_OR:
+	case E_AND:
+		e->left.expr = rewrite_m(e->left.expr);
+		e->right.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));
+}
+
+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->file = current_file;
+	prop->lineno = zconf_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, 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) {
+			struct expr *dup_expr;
+
+			if (!menu->visibility)
+				continue;
+			/*
+			 * Do not add a reference to the menu's visibility
+			 * expression but use a copy of it. Otherwise the
+			 * expression reduction functions will modify
+			 * expressions that have multiple references which
+			 * can cause unwanted side effects.
+			 */
+			dup_expr = expr_copy(menu->visibility);
+
+			prop->visible.expr = expr_alloc_and(prop->visible.expr,
+							    dup_expr);
+		}
+	}
+
+	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 property *choice_prop =
+					sym_get_choice_prop(sym2);
+
+				if (!choice_prop ||
+				    prop_get_symbol(choice_prop) != 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:
+			;
+		}
+	}
+}
+
+void menu_finalize(struct menu *parent)
+{
+	struct menu *menu, *last_menu;
+	struct symbol *sym;
+	struct property *prop;
+	struct expr *parentdep, *basedep, *dep, *dep2, **ep;
+
+	sym = parent->sym;
+	if (parent->list) {
+		/*
+		 * This menu node has children. We (recursively) process them
+		 * and propagate parent dependencies before moving on.
+		 */
+
+		if (sym && sym_is_choice(sym)) {
+			if (sym->type == S_UNKNOWN) {
+				/* find the first choice value to find out choice type */
+				current_entry = parent;
+				for (menu = parent->list; menu; menu = menu->next) {
+					if (menu->sym && menu->sym->type != S_UNKNOWN) {
+						menu_set_type(menu->sym->type);
+						break;
+					}
+				}
+			}
+			/* set the type of the remaining choice values */
+			for (menu = parent->list; menu; menu = menu->next) {
+				current_entry = menu;
+				if (menu->sym && menu->sym->type == S_UNKNOWN)
+					menu_set_type(sym->type);
+			}
+
+			/*
+			 * Use the choice itself as the parent dependency of
+			 * the contained items. This turns the mode of the
+			 * choice into an upper bound on the visibility of the
+			 * choice value symbols.
+			 */
+			parentdep = expr_alloc_symbol(sym);
+		} else {
+			/* Menu node for 'menu', 'if' */
+			parentdep = parent->dep;
+		}
+
+		/* 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(expr_copy(parentdep), 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(expr_copy(basedep), dep);
+				dep = expr_eliminate_dups(dep);
+				if (menu->sym && menu->sym->type != S_TRISTATE)
+					dep = expr_trans_bool(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), expr_copy(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), expr_copy(dep)));
+				}
+			}
+		}
+
+		if (sym && sym_is_choice(sym))
+			expr_free(parentdep);
+
+		/*
+		 * Recursively process children in the same fashion before
+		 * moving on
+		 */
+		for (menu = parent->list; menu; menu = menu->next)
+			menu_finalize(menu);
+	} else if (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 = expr_copy(basedep);
+			expr_eliminate_eq(&dep, &dep2);
+			expr_free(dep);
+			if (!expr_is_yes(dep2)) {
+				/* Not superset, quit */
+				expr_free(dep2);
+				break;
+			}
+			/* Superset, put in submenu */
+			expr_free(dep2);
+		next:
+			menu_finalize(menu);
+			menu->parent = parent;
+			last_menu = menu;
+		}
+		expr_free(basedep);
+		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) {
+		if (sym && sym_is_choice(sym) &&
+		    menu->sym && !sym_is_choice_value(menu->sym)) {
+			current_entry = menu;
+			menu->sym->flags |= SYMBOL_CHOICEVAL;
+			if (!menu->prompt)
+				menu_warn(menu, "choice value must have a prompt");
+			for (prop = menu->sym->prop; prop; prop = prop->next) {
+				if (prop->type == P_DEFAULT)
+					prop_warn(prop, "defaults for choice "
+						  "values not supported");
+				if (prop->menu == menu)
+					continue;
+				if (prop->type == P_PROMPT &&
+				    prop->menu->parent->sym != sym)
+					prop_warn(prop, "choice value used outside its choice group");
+			}
+			/* Non-tristate choice values of tristate choices must
+			 * depend on the choice being set to Y. The choice
+			 * values' dependencies were propagated to their
+			 * properties above, so the change here must be re-
+			 * propagated.
+			 */
+			if (sym->type == S_TRISTATE && menu->sym->type != S_TRISTATE) {
+				basedep = expr_alloc_comp(E_EQUAL, sym, &symbol_yes);
+				menu->dep = expr_alloc_and(basedep, menu->dep);
+				for (prop = menu->sym->prop; prop; prop = prop->next) {
+					if (prop->menu != menu)
+						continue;
+					prop->visible.expr = expr_alloc_and(expr_copy(basedep),
+									    prop->visible.expr);
+				}
+			}
+			menu_add_symbol(P_CHOICE, sym, NULL);
+			prop = sym_get_choice_prop(sym);
+			for (ep = &prop->expr; *ep; ep = &(*ep)->left.expr)
+				;
+			*ep = expr_alloc_one(E_LIST, NULL);
+			(*ep)->right.sym = menu->sym;
+		}
+
+		/*
+		 * 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");
+
+		if (sym_is_choice(sym) && !parent->prompt)
+			menu_warn(parent, "choice must have a prompt");
+
+		/* Check properties connected to this symbol */
+		sym_check_prop(sym);
+		sym->flags |= SYMBOL_WARNED;
+	}
+
+	/*
+	 * For non-optional choices, add a reverse dependency (corresponding to
+	 * a select) of '<visibility> && m'. This prevents the user from
+	 * setting the choice mode to 'n' when the choice is visible.
+	 *
+	 * This would also work for non-choice symbols, but only non-optional
+	 * choices clear SYMBOL_OPTIONAL as of writing. Choices are implemented
+	 * as a type of symbol.
+	 */
+	if (sym && !sym_is_optional(sym) && parent->prompt) {
+		sym->rev_dep.expr = expr_alloc_or(sym->rev_dep.expr,
+				expr_alloc_and(parent->prompt->visible.expr,
+					expr_alloc_symbol(&symbol_mod)));
+	}
+}
+
+bool menu_has_prompt(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)) {
+			if (sym)
+				sym->flags |= SYMBOL_DEF_USER;
+			return true;
+		}
+	}
+
+	return false;
+}
+
+const char *menu_get_prompt(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;
+}
+
+bool menu_has_help(struct menu *menu)
+{
+	return menu->help != NULL;
+}
+
+const char *menu_get_help(struct menu *menu)
+{
+	if (menu->help)
+		return menu->help;
+	else
+		return "";
+}
+
+static void get_def_str(struct gstr *r, struct menu *menu)
+{
+	str_printf(r, "Defined at %s:%d\n",
+		   menu->file->name, menu->lineno);
+}
+
+static void get_dep_str(struct gstr *r, 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;
+
+	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 */
+	for_all_properties(sym, prop, P_SYMBOL) {
+		if (prop->menu->prompt) {
+			get_def_str(r, prop->menu);
+			get_prompt_str(r, prop->menu->prompt, head);
+		}
+	}
+
+	for_all_properties(sym, prop, P_SYMBOL) {
+		if (!prop->menu->prompt) {
+			get_def_str(r, prop->menu);
+			get_dep_str(r, prop->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_has_help(menu)) {
+		if (sym->name)
+			str_printf(help, "%s%s:\n\n", CONFIG_, sym->name);
+		help_text = menu_get_help(menu);
+	}
+	str_printf(help, "%s\n", help_text);
+	if (sym)
+		get_symbol_str(help, sym, NULL);
+}
diff --git a/scripts/config/nconf-cfg.sh b/scripts/config/nconf-cfg.sh
new file mode 100755
index 0000000..9d40960
--- /dev/null
+++ b/scripts/config/nconf-cfg.sh
@@ -0,0 +1,53 @@
+#!/bin/sh
+# SPDX-License-Identifier: GPL-2.0-only
+
+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
diff --git a/scripts/config/nconf.c b/scripts/config/nconf.c
new file mode 100644
index 0000000..ece8672
--- /dev/null
+++ b/scripts/config/nconf.c
@@ -0,0 +1,1659 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2008 Nir Tzachar <nir.tzachar@gmail.com>
+ *
+ * Derived from menuconfig.
+ */
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+#include <string.h>
+#include <strings.h>
+#include <stdlib.h>
+
+#include "lkc.h"
+#include "nconf.h"
+#include <ctype.h>
+
+static const char nconf_global_help[] =
+"Help windows\n"
+"------------\n"
+"o  Global help:  Unless in a data entry window, pressing <F1> will give \n"
+"   you the global help window, which you are just reading.\n"
+"\n"
+"o  A short version of the global help is available by pressing <F3>.\n"
+"\n"
+"o  Local help:  To get help related to the current menu entry, use any\n"
+"   of <?> <h>, or if in a data entry window then press <F1>.\n"
+"\n"
+"\n"
+"OpenWrt config is based on Kernel kconfig\n"
+"so ipkg packages are referred here as modules.\n"
+"\n"
+"Menu entries\n"
+"------------\n"
+"This interface lets you select features and parameters for the build.\n"
+"Features can either be built-in, modularized, or removed.\n"
+"Parameters must be entered as text or decimal or hexadecimal numbers.\n"
+"\n"
+"Menu entries 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, are selected by another feature\n"
+"  - -  are selected by another feature\n"
+"  XXX  cannot be selected.  Symbol Info <F2> tells you why.\n"
+"*, M or whitespace inside braces means to build in, build as a module\n"
+"or to exclude the feature respectively.\n"
+"\n"
+"To change any of these features, highlight it with the movement keys\n"
+"listed below and press <y> to build it in, <m> to make it a module or\n"
+"<n> to remove it.  You may press the <Space> key to cycle through the\n"
+"available options.\n"
+"\n"
+"A trailing \"--->\" designates a submenu, a trailing \"----\" an\n"
+"empty submenu.\n"
+"\n"
+"Menu navigation keys\n"
+"----------------------------------------------------------------------\n"
+"Linewise up                 <Up>    <k>\n"
+"Linewise down               <Down>  <j>\n"
+"Pagewise up                 <Page Up>\n"
+"Pagewise down               <Page Down>\n"
+"First entry                 <Home>\n"
+"Last entry                  <End>\n"
+"Enter a submenu             <Right>  <Enter>\n"
+"Go back to parent menu      <Left>   <Esc>  <F5>\n"
+"Close a help window         <Enter>  <Esc>  <F5>\n"
+"Close entry window, apply   <Enter>\n"
+"Close entry window, forget  <Esc>  <F5>\n"
+"Start incremental, case-insensitive search for STRING in menu entries,\n"
+"    no regex support, STRING is displayed in upper left corner\n"
+"                            </>STRING\n"
+"    Remove last character   <Backspace>\n"
+"    Jump to next hit        <Down>\n"
+"    Jump to previous hit    <Up>\n"
+"Exit menu search mode       </>  <Esc>\n"
+"Search for configuration variables with or without leading CONFIG_\n"
+"                            <F8>RegExpr<Enter>\n"
+"Verbose search help         <F8><F1>\n"
+"----------------------------------------------------------------------\n"
+"\n"
+"Unless in a data entry window, key <1> may be used instead of <F1>,\n"
+"<2> instead of <F2>, etc.\n"
+"\n"
+"\n"
+"Radiolist (Choice list)\n"
+"-----------------------\n"
+"Use the movement keys listed above to select the option you wish to set\n"
+"and press <Space>.\n"
+"\n"
+"\n"
+"Data entry\n"
+"----------\n"
+"Enter the requested information and press <Enter>.  Hexadecimal values\n"
+"may be entered without the \"0x\" prefix.\n"
+"\n"
+"\n"
+"Text Box (Help Window)\n"
+"----------------------\n"
+"Use movement keys as listed in table above.\n"
+"\n"
+"Press any of <Enter> <Esc> <q> <F5> <F9> to exit.\n"
+"\n"
+"\n"
+"Alternate configuration files\n"
+"-----------------------------\n"
+"nconfig supports switching between different configurations.\n"
+"Press <F6> to save your current configuration.  Press <F7> and enter\n"
+"a file name to load a previously saved configuration.\n"
+"\n"
+"\n"
+"Terminal configuration\n"
+"----------------------\n"
+"If you use nconfig in a xterm window, make sure your TERM environment\n"
+"variable specifies a terminal configuration which supports at least\n"
+"16 colors.  Otherwise nconfig will look rather bad.\n"
+"\n"
+"If the \"stty size\" command reports the current terminalsize correctly,\n"
+"nconfig will adapt to sizes larger than the traditional 80x25 \"standard\"\n"
+"and display longer menus properly.\n"
+"\n"
+"\n"
+"Single menu mode\n"
+"----------------\n"
+"If you prefer to have all of the menu entries listed in a single menu,\n"
+"rather than the default multimenu hierarchy, run nconfig with\n"
+"NCONFIG_MODE environment variable set to single_menu.  Example:\n"
+"\n"
+"make NCONFIG_MODE=single_menu nconfig\n"
+"\n"
+"<Enter> will then unfold the appropriate category, or fold it if it\n"
+"is already unfolded.  Folded menu entries will be designated by a\n"
+"leading \"++>\" and unfolded entries by a leading \"-->\".\n"
+"\n"
+"Note that this mode can eventually be a little more CPU expensive than\n"
+"the default mode, especially with a larger number of unfolded submenus.\n"
+"\n",
+menu_no_f_instructions[] =
+"Legend:  [*] built-in  [ ] excluded  <M> module  < > module capable.\n"
+"Submenus are designated by a trailing \"--->\", empty ones by \"----\".\n"
+"\n"
+"Use the following keys to navigate the menus:\n"
+"Move up or down with <Up> and <Down>.\n"
+"Enter a submenu with <Enter> or <Right>.\n"
+"Exit a submenu to its parent menu with <Esc> or <Left>.\n"
+"Pressing <y> includes, <n> excludes, <m> modularizes features.\n"
+"Pressing <Space> cycles through the available options.\n"
+"To search for menu entries press </>.\n"
+"<Esc> always leaves the current window.\n"
+"\n"
+"You do not have function keys support.\n"
+"Press <1> instead of <F1>, <2> instead of <F2>, etc.\n"
+"For verbose global help use key <1>.\n"
+"For help related to the current menu entry press <?> or <h>.\n",
+menu_instructions[] =
+"Legend:  [*] built-in  [ ] excluded  <M> module  < > module capable.\n"
+"Submenus are designated by a trailing \"--->\", empty ones by \"----\".\n"
+"\n"
+"Use the following keys to navigate the menus:\n"
+"Move up or down with <Up> or <Down>.\n"
+"Enter a submenu with <Enter> or <Right>.\n"
+"Exit a submenu to its parent menu with <Esc> or <Left>.\n"
+"Pressing <y> includes, <n> excludes, <m> modularizes features.\n"
+"Pressing <Space> cycles through the available options.\n"
+"To search for menu entries press </>.\n"
+"<Esc> always leaves the current window.\n"
+"\n"
+"Pressing <1> may be used instead of <F1>, <2> instead of <F2>, etc.\n"
+"For verbose global help press <F1>.\n"
+"For help related to the current menu entry press <?> or <h>.\n",
+radiolist_instructions[] =
+"Press <Up>, <Down>, <Home> or <End> to navigate a radiolist, select\n"
+"with <Space>.\n"
+"For help related to the current entry press <?> or <h>.\n"
+"For global help press <F1>.\n",
+inputbox_instructions_int[] =
+"Please enter a decimal value.\n"
+"Fractions will not be accepted.\n"
+"Press <Enter> to apply, <Esc> to cancel.",
+inputbox_instructions_hex[] =
+"Please enter a hexadecimal value.\n"
+"Press <Enter> to apply, <Esc> to cancel.",
+inputbox_instructions_string[] =
+"Please enter a string value.\n"
+"Press <Enter> to apply, <Esc> to cancel.",
+setmod_text[] =
+"This feature depends on another feature which has been configured as a\n"
+"module.  As a result, the current feature will be built as a module too.",
+load_config_text[] =
+"Enter the name of the configuration file you wish to load.\n"
+"Accept the name shown to restore the configuration you last\n"
+"retrieved.  Leave empty to abort.",
+load_config_help[] =
+"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 load and modify\n"
+"that configuration.\n"
+"\n"
+"Leave empty to abort.\n",
+save_config_text[] =
+"Enter a filename to which this configuration should be saved\n"
+"as an alternate.  Leave empty to abort.",
+save_config_help[] =
+"For various reasons, one may wish to keep several different\n"
+"configurations 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"
+"Leave empty to abort.\n",
+search_help[] =
+"Search for symbols (configuration variable names CONFIG_*) and display\n"
+"their relations.  Regular expressions are supported.\n"
+"Example:  Search for \"^FOO\".\n"
+"Result:\n"
+"-----------------------------------------------------------------\n"
+"Symbol: FOO [ = m]\n"
+"Prompt: Foo bus is used to drive the bar HW\n"
+"Defined at drivers/pci/Kconfig:47\n"
+"Depends on: X86_LOCAL_APIC && X86_IO_APIC || IA64\n"
+"Location:\n"
+"  -> Bus options (PCI, PCMCIA, EISA, ISA)\n"
+"    -> PCI support (PCI [ = y])\n"
+"(1)   -> PCI access mode (<choice> [ = y])\n"
+"Selects: LIBCRC32\n"
+"Selected by: BAR\n"
+"-----------------------------------------------------------------\n"
+"o  The line 'Prompt:' shows the text displayed for this symbol in\n"
+"   the menu hierarchy.\n"
+"o  The 'Defined at' line tells at what file / line number the symbol is\n"
+"   defined.\n"
+"o  The 'Depends on:' line lists symbols that need to be defined for\n"
+"   this symbol to be visible and selectable in the menu.\n"
+"o  The 'Location:' lines tell, where in the menu structure this symbol\n"
+"   is located.\n"
+"     A location followed by a [ = y] indicates that this is\n"
+"     a 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 symbol will be automatically selected\n"
+"   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"
+"USB  => find all symbols containing USB\n"
+"^USB => find all symbols starting with USB\n"
+"USB$ => find all symbols ending with USB\n"
+"\n";
+
+struct mitem {
+	char str[256];
+	char tag;
+	void *usrptr;
+	int is_visible;
+};
+
+#define MAX_MENU_ITEMS 4096
+static int show_all_items;
+static int indent;
+static struct menu *current_menu;
+static int child_count;
+static int single_menu_mode;
+/* the window in which all information appears */
+static WINDOW *main_window;
+/* the largest size of the menu window */
+static int mwin_max_lines;
+static int mwin_max_cols;
+/* the window in which we show option buttons */
+static MENU *curses_menu;
+static ITEM *curses_menu_items[MAX_MENU_ITEMS];
+static struct mitem k_menu_items[MAX_MENU_ITEMS];
+static unsigned int items_num;
+static int global_exit;
+/* the currently selected button */
+static const char *current_instructions = menu_instructions;
+
+static char *dialog_input_result;
+static int dialog_input_result_len;
+static int jump_key_char;
+
+static void selected_conf(struct menu *menu, struct menu *active_menu);
+static void conf(struct menu *menu);
+static void conf_choice(struct menu *menu);
+static void conf_string(struct menu *menu);
+static void conf_load(void);
+static void conf_save(void);
+static void show_help(struct menu *menu);
+static int do_exit(void);
+static void setup_windows(void);
+static void search_conf(void);
+
+typedef void (*function_key_handler_t)(int *key, struct menu *menu);
+static void handle_f1(int *key, struct menu *current_item);
+static void handle_f2(int *key, struct menu *current_item);
+static void handle_f3(int *key, struct menu *current_item);
+static void handle_f4(int *key, struct menu *current_item);
+static void handle_f5(int *key, struct menu *current_item);
+static void handle_f6(int *key, struct menu *current_item);
+static void handle_f7(int *key, struct menu *current_item);
+static void handle_f8(int *key, struct menu *current_item);
+static void handle_f9(int *key, struct menu *current_item);
+
+struct function_keys {
+	const char *key_str;
+	const char *func;
+	function_key key;
+	function_key_handler_t handler;
+};
+
+static const int function_keys_num = 9;
+static struct function_keys function_keys[] = {
+	{
+		.key_str = "F1",
+		.func = "Help",
+		.key = F_HELP,
+		.handler = handle_f1,
+	},
+	{
+		.key_str = "F2",
+		.func = "SymInfo",
+		.key = F_SYMBOL,
+		.handler = handle_f2,
+	},
+	{
+		.key_str = "F3",
+		.func = "Help 2",
+		.key = F_INSTS,
+		.handler = handle_f3,
+	},
+	{
+		.key_str = "F4",
+		.func = "ShowAll",
+		.key = F_CONF,
+		.handler = handle_f4,
+	},
+	{
+		.key_str = "F5",
+		.func = "Back",
+		.key = F_BACK,
+		.handler = handle_f5,
+	},
+	{
+		.key_str = "F6",
+		.func = "Save",
+		.key = F_SAVE,
+		.handler = handle_f6,
+	},
+	{
+		.key_str = "F7",
+		.func = "Load",
+		.key = F_LOAD,
+		.handler = handle_f7,
+	},
+	{
+		.key_str = "F8",
+		.func = "SymSearch",
+		.key = F_SEARCH,
+		.handler = handle_f8,
+	},
+	{
+		.key_str = "F9",
+		.func = "Exit",
+		.key = F_EXIT,
+		.handler = handle_f9,
+	},
+};
+
+static void print_function_line(void)
+{
+	int i;
+	int offset = 1;
+	const int skip = 1;
+	int lines = getmaxy(stdscr);
+
+	for (i = 0; i < function_keys_num; i++) {
+		wattrset(main_window, attr_function_highlight);
+		mvwprintw(main_window, lines-3, offset,
+				"%s",
+				function_keys[i].key_str);
+		wattrset(main_window, attr_function_text);
+		offset += strlen(function_keys[i].key_str);
+		mvwprintw(main_window, lines-3,
+				offset, "%s",
+				function_keys[i].func);
+		offset += strlen(function_keys[i].func) + skip;
+	}
+	wattrset(main_window, attr_normal);
+}
+
+/* help */
+static void handle_f1(int *key, struct menu *current_item)
+{
+	show_scroll_win(main_window,
+			"Global help", nconf_global_help);
+	return;
+}
+
+/* symbole help */
+static void handle_f2(int *key, struct menu *current_item)
+{
+	show_help(current_item);
+	return;
+}
+
+/* instructions */
+static void handle_f3(int *key, struct menu *current_item)
+{
+	show_scroll_win(main_window,
+			"Short help",
+			current_instructions);
+	return;
+}
+
+/* config */
+static void handle_f4(int *key, struct menu *current_item)
+{
+	int res = btn_dialog(main_window,
+			"Show all symbols?",
+			2,
+			"   <Show All>   ",
+			"<Don't show all>");
+	if (res == 0)
+		show_all_items = 1;
+	else if (res == 1)
+		show_all_items = 0;
+
+	return;
+}
+
+/* back */
+static void handle_f5(int *key, struct menu *current_item)
+{
+	*key = KEY_LEFT;
+	return;
+}
+
+/* save */
+static void handle_f6(int *key, struct menu *current_item)
+{
+	conf_save();
+	return;
+}
+
+/* load */
+static void handle_f7(int *key, struct menu *current_item)
+{
+	conf_load();
+	return;
+}
+
+/* search */
+static void handle_f8(int *key, struct menu *current_item)
+{
+	search_conf();
+	return;
+}
+
+/* exit */
+static void handle_f9(int *key, struct menu *current_item)
+{
+	do_exit();
+	return;
+}
+
+/* return != 0 to indicate the key was handles */
+static int process_special_keys(int *key, struct menu *menu)
+{
+	int i;
+
+	if (*key == KEY_RESIZE) {
+		setup_windows();
+		return 1;
+	}
+
+	for (i = 0; i < function_keys_num; i++) {
+		if (*key == KEY_F(function_keys[i].key) ||
+		    *key == '0' + function_keys[i].key){
+			function_keys[i].handler(key, menu);
+			return 1;
+		}
+	}
+
+	return 0;
+}
+
+static void clean_items(void)
+{
+	int i;
+	for (i = 0; curses_menu_items[i]; i++)
+		free_item(curses_menu_items[i]);
+	bzero(curses_menu_items, sizeof(curses_menu_items));
+	bzero(k_menu_items, sizeof(k_menu_items));
+	items_num = 0;
+}
+
+typedef enum {MATCH_TINKER_PATTERN_UP, MATCH_TINKER_PATTERN_DOWN,
+	FIND_NEXT_MATCH_DOWN, FIND_NEXT_MATCH_UP} match_f;
+
+/* return the index of the matched item, or -1 if no such item exists */
+static int get_mext_match(const char *match_str, match_f flag)
+{
+	int match_start, index;
+
+	/* Do not search if the menu is empty (i.e. items_num == 0) */
+	match_start = item_index(current_item(curses_menu));
+	if (match_start == ERR)
+		return -1;
+
+	if (flag == FIND_NEXT_MATCH_DOWN)
+		++match_start;
+	else if (flag == FIND_NEXT_MATCH_UP)
+		--match_start;
+
+	match_start = (match_start + items_num) % items_num;
+	index = match_start;
+	while (true) {
+		char *str = k_menu_items[index].str;
+		if (strcasestr(str, match_str) != NULL)
+			return index;
+		if (flag == FIND_NEXT_MATCH_UP ||
+		    flag == MATCH_TINKER_PATTERN_UP)
+			--index;
+		else
+			++index;
+		index = (index + items_num) % items_num;
+		if (index == match_start)
+			return -1;
+	}
+}
+
+/* Make a new item. */
+static void item_make(struct menu *menu, char tag, const char *fmt, ...)
+{
+	va_list ap;
+
+	if (items_num > MAX_MENU_ITEMS-1)
+		return;
+
+	bzero(&k_menu_items[items_num], sizeof(k_menu_items[0]));
+	k_menu_items[items_num].tag = tag;
+	k_menu_items[items_num].usrptr = menu;
+	if (menu != NULL)
+		k_menu_items[items_num].is_visible =
+			menu_is_visible(menu);
+	else
+		k_menu_items[items_num].is_visible = 1;
+
+	va_start(ap, fmt);
+	vsnprintf(k_menu_items[items_num].str,
+		  sizeof(k_menu_items[items_num].str),
+		  fmt, ap);
+	va_end(ap);
+
+	if (!k_menu_items[items_num].is_visible)
+		memcpy(k_menu_items[items_num].str, "XXX", 3);
+
+	curses_menu_items[items_num] = new_item(
+			k_menu_items[items_num].str,
+			k_menu_items[items_num].str);
+	set_item_userptr(curses_menu_items[items_num],
+			&k_menu_items[items_num]);
+	/*
+	if (!k_menu_items[items_num].is_visible)
+		item_opts_off(curses_menu_items[items_num], O_SELECTABLE);
+	*/
+
+	items_num++;
+	curses_menu_items[items_num] = NULL;
+}
+
+/* very hackish. adds a string to the last item added */
+static void item_add_str(const char *fmt, ...)
+{
+	va_list ap;
+	int index = items_num-1;
+	char new_str[256];
+	char tmp_str[256];
+
+	if (index < 0)
+		return;
+
+	va_start(ap, fmt);
+	vsnprintf(new_str, sizeof(new_str), fmt, ap);
+	va_end(ap);
+	snprintf(tmp_str, sizeof(tmp_str), "%s%s",
+			k_menu_items[index].str, new_str);
+	strncpy(k_menu_items[index].str,
+		tmp_str,
+		sizeof(k_menu_items[index].str));
+
+	free_item(curses_menu_items[index]);
+	curses_menu_items[index] = new_item(
+			k_menu_items[index].str,
+			k_menu_items[index].str);
+	set_item_userptr(curses_menu_items[index],
+			&k_menu_items[index]);
+}
+
+/* get the tag of the currently selected item */
+static char item_tag(void)
+{
+	ITEM *cur;
+	struct mitem *mcur;
+
+	cur = current_item(curses_menu);
+	if (cur == NULL)
+		return 0;
+	mcur = (struct mitem *) item_userptr(cur);
+	return mcur->tag;
+}
+
+static int curses_item_index(void)
+{
+	return  item_index(current_item(curses_menu));
+}
+
+static void *item_data(void)
+{
+	ITEM *cur;
+	struct mitem *mcur;
+
+	cur = current_item(curses_menu);
+	if (!cur)
+		return NULL;
+	mcur = (struct mitem *) item_userptr(cur);
+	return mcur->usrptr;
+
+}
+
+static int item_is_tag(char tag)
+{
+	return item_tag() == tag;
+}
+
+static char filename[PATH_MAX+1];
+static char menu_backtitle[PATH_MAX+128];
+static void set_config_filename(const char *config_filename)
+{
+	snprintf(menu_backtitle, sizeof(menu_backtitle), "%s - %s",
+		 config_filename, rootmenu.prompt->text);
+
+	snprintf(filename, sizeof(filename), "%s", config_filename);
+}
+
+/* return = 0 means we are successful.
+ * -1 means go on doing what you were doing
+ */
+static int do_exit(void)
+{
+	int res;
+	if (!conf_get_changed()) {
+		global_exit = 1;
+		return 0;
+	}
+	res = btn_dialog(main_window,
+			"Do you wish to save your new configuration?\n"
+				"<ESC> to cancel and resume nconfig.",
+			2,
+			"   <save>   ",
+			"<don't save>");
+	if (res == KEY_EXIT) {
+		global_exit = 0;
+		return -1;
+	}
+
+	/* if we got here, the user really wants to exit */
+	switch (res) {
+	case 0:
+		res = conf_write(filename);
+		if (res)
+			btn_dialog(
+				main_window,
+				"Error during writing of configuration.\n"
+				  "Your configuration changes were NOT saved.",
+				  1,
+				  "<OK>");
+		conf_write_autoconf(0);
+		break;
+	default:
+		btn_dialog(
+			main_window,
+			"Your configuration changes were NOT saved.",
+			1,
+			"<OK>");
+		break;
+	}
+	global_exit = 1;
+	return 0;
+}
+
+struct search_data {
+	struct list_head *head;
+	struct menu *target;
+};
+
+static int next_jump_key(int key)
+{
+	if (key < '1' || key > '9')
+		return '1';
+
+	key++;
+
+	if (key > '9')
+		key = '1';
+
+	return key;
+}
+
+static 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;
+}
+
+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;
+
+	title = str_new();
+	str_printf( &title, "Enter (sub)string or regexp to search for "
+			      "(with or without \"%s\")", CONFIG_);
+
+again:
+	dres = dialog_inputbox(main_window,
+			"Search Configuration Parameter",
+			str_get(&title),
+			"", &dialog_input_result, &dialog_input_result_len);
+	switch (dres) {
+	case 0:
+		break;
+	case 1:
+		show_scroll_win(main_window,
+				"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_);
+
+	sym_arr = sym_re_search(dialog_input);
+
+	do {
+		LIST_HEAD(head);
+		struct search_data data = {
+			.head = &head,
+			.target = NULL,
+		};
+		jump_key_char = 0;
+		res = get_relations_str(sym_arr, &head);
+		dres = show_scroll_win_ext(main_window,
+				"Search Results", str_get(&res),
+				&vscroll, &hscroll,
+				handle_search_keys, &data);
+		again = false;
+		if (dres >= '1' && dres <= '9') {
+			assert(data.target != NULL);
+			selected_conf(data.target->parent, data.target);
+			again = true;
+		}
+		str_free(&res);
+	} while (again);
+	free(sym_arr);
+	str_free(&title);
+}
+
+
+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;
+
+	if (!menu || (!show_all_items && !menu_is_visible(menu)))
+		return;
+
+	sym = menu->sym;
+	prop = menu->prompt;
+	if (!sym) {
+		if (prop && menu != current_menu) {
+			const char *prompt = menu_get_prompt(menu);
+			enum prop_type ptype;
+			ptype = menu->prompt ? menu->prompt->type : P_UNKNOWN;
+			switch (ptype) {
+			case P_MENU:
+				child_count++;
+				if (single_menu_mode) {
+					item_make(menu, 'm',
+						"%s%*c%s",
+						menu->data ? "-->" : "++>",
+						indent + 1, ' ', prompt);
+				} else
+					item_make(menu, 'm',
+						  "   %*c%s  %s",
+						  indent + 1, ' ', prompt,
+						  menu_is_empty(menu) ? "----" : "--->");
+
+				if (single_menu_mode && menu->data)
+					goto conf_childs;
+				return;
+			case P_COMMENT:
+				if (prompt) {
+					child_count++;
+					item_make(menu, ':',
+						"   %*c*** %s ***",
+						indent + 1, ' ',
+						prompt);
+				}
+				break;
+			default:
+				if (prompt) {
+					child_count++;
+					item_make(menu, ':', "---%*c%s",
+						indent + 1, ' ',
+						prompt);
+				}
+			}
+		} else
+			doint = 0;
+		goto conf_childs;
+	}
+
+	type = sym_get_type(sym);
+	if (sym_is_choice(sym)) {
+		struct symbol *def_sym = sym_get_choice_value(sym);
+		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;
+		}
+
+		val = sym_get_tristate_value(sym);
+		if (sym_is_changeable(sym)) {
+			switch (type) {
+			case S_BOOLEAN:
+				item_make(menu, 't', "[%c]",
+						val == no ? ' ' : '*');
+				break;
+			case S_TRISTATE:
+				switch (val) {
+				case yes:
+					ch = '*';
+					break;
+				case mod:
+					ch = 'M';
+					break;
+				default:
+					ch = ' ';
+					break;
+				}
+				item_make(menu, 't', "<%c>", ch);
+				break;
+			}
+		} else {
+			item_make(menu, def_menu ? 't' : ':', "   ");
+		}
+
+		item_add_str("%*c%s", indent + 1,
+				' ', menu_get_prompt(menu));
+		if (val == yes) {
+			if (def_menu) {
+				item_add_str(" (%s)",
+					menu_get_prompt(def_menu));
+				item_add_str("  --->");
+				if (def_menu->list) {
+					indent += 2;
+					build_conf(def_menu);
+					indent -= 2;
+				}
+			}
+			return;
+		}
+	} else {
+		if (menu == current_menu) {
+			item_make(menu, ':',
+				"---%*c%s", indent + 1,
+				' ', menu_get_prompt(menu));
+			goto conf_childs;
+		}
+		child_count++;
+		val = sym_get_tristate_value(sym);
+		if (sym_is_choice_value(sym) && val == yes) {
+			item_make(menu, ':', "   ");
+		} else {
+			switch (type) {
+			case S_BOOLEAN:
+				if (sym_is_changeable(sym))
+					item_make(menu, 't', "[%c]",
+						val == no ? ' ' : '*');
+				else
+					item_make(menu, 't', "-%c-",
+						val == no ? ' ' : '*');
+				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(menu,
+							't', "{%c}", ch);
+					else
+						item_make(menu,
+							't', "<%c>", ch);
+				} else
+					item_make(menu, 't', "-%c-", ch);
+				break;
+			default:
+				tmp = 2 + strlen(sym_get_string_value(sym));
+				item_make(menu, 's', "    (%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)");
+				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 && 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 reset_menu(void)
+{
+	unpost_menu(curses_menu);
+	clean_items();
+}
+
+/* adjust the menu to show this item.
+ * prefer not to scroll the menu if possible*/
+static void center_item(int selected_index, int *last_top_row)
+{
+	int toprow;
+
+	set_top_row(curses_menu, *last_top_row);
+	toprow = top_row(curses_menu);
+	if (selected_index < toprow ||
+	    selected_index >= toprow+mwin_max_lines) {
+		toprow = max(selected_index-mwin_max_lines/2, 0);
+		if (toprow >= item_count(curses_menu)-mwin_max_lines)
+			toprow = item_count(curses_menu)-mwin_max_lines;
+		set_top_row(curses_menu, toprow);
+	}
+	set_current_item(curses_menu,
+			curses_menu_items[selected_index]);
+	*last_top_row = toprow;
+	post_menu(curses_menu);
+	refresh_all_windows(main_window);
+}
+
+/* this function assumes reset_menu has been called before */
+static void show_menu(const char *prompt, const char *instructions,
+		int selected_index, int *last_top_row)
+{
+	int maxx, maxy;
+	WINDOW *menu_window;
+
+	current_instructions = instructions;
+
+	clear();
+	print_in_middle(stdscr, 1, getmaxx(stdscr),
+			menu_backtitle,
+			attr_main_heading);
+
+	wattrset(main_window, attr_main_menu_box);
+	box(main_window, 0, 0);
+	wattrset(main_window, attr_main_menu_heading);
+	mvwprintw(main_window, 0, 3, " %s ", prompt);
+	wattrset(main_window, attr_normal);
+
+	set_menu_items(curses_menu, curses_menu_items);
+
+	/* position the menu at the middle of the screen */
+	scale_menu(curses_menu, &maxy, &maxx);
+	maxx = min(maxx, mwin_max_cols-2);
+	maxy = mwin_max_lines;
+	menu_window = derwin(main_window,
+			maxy,
+			maxx,
+			2,
+			(mwin_max_cols-maxx)/2);
+	keypad(menu_window, TRUE);
+	set_menu_win(curses_menu, menu_window);
+	set_menu_sub(curses_menu, menu_window);
+
+	/* must reassert this after changing items, otherwise returns to a
+	 * default of 16
+	 */
+	set_menu_format(curses_menu, maxy, 1);
+	center_item(selected_index, last_top_row);
+	set_menu_format(curses_menu, maxy, 1);
+
+	print_function_line();
+
+	/* Post the menu */
+	post_menu(curses_menu);
+	refresh_all_windows(main_window);
+}
+
+static void adj_match_dir(match_f *match_direction)
+{
+	if (*match_direction == FIND_NEXT_MATCH_DOWN)
+		*match_direction =
+			MATCH_TINKER_PATTERN_DOWN;
+	else if (*match_direction == FIND_NEXT_MATCH_UP)
+		*match_direction =
+			MATCH_TINKER_PATTERN_UP;
+	/* else, do no change.. */
+}
+
+struct match_state
+{
+	int in_search;
+	match_f match_direction;
+	char pattern[256];
+};
+
+/* Return 0 means I have handled the key. In such a case, ans should hold the
+ * item to center, or -1 otherwise.
+ * Else return -1 .
+ */
+static int do_match(int key, struct match_state *state, int *ans)
+{
+	char c = (char) key;
+	int terminate_search = 0;
+	*ans = -1;
+	if (key == '/' || (state->in_search && key == 27)) {
+		move(0, 0);
+		refresh();
+		clrtoeol();
+		state->in_search = 1-state->in_search;
+		bzero(state->pattern, sizeof(state->pattern));
+		state->match_direction = MATCH_TINKER_PATTERN_DOWN;
+		return 0;
+	} else if (!state->in_search)
+		return 1;
+
+	if (isalnum(c) || isgraph(c) || c == ' ') {
+		state->pattern[strlen(state->pattern)] = c;
+		state->pattern[strlen(state->pattern)] = '\0';
+		adj_match_dir(&state->match_direction);
+		*ans = get_mext_match(state->pattern,
+				state->match_direction);
+	} else if (key == KEY_DOWN) {
+		state->match_direction = FIND_NEXT_MATCH_DOWN;
+		*ans = get_mext_match(state->pattern,
+				state->match_direction);
+	} else if (key == KEY_UP) {
+		state->match_direction = FIND_NEXT_MATCH_UP;
+		*ans = get_mext_match(state->pattern,
+				state->match_direction);
+	} else if (key == KEY_BACKSPACE || key == 8 || key == 127) {
+		state->pattern[strlen(state->pattern)-1] = '\0';
+		adj_match_dir(&state->match_direction);
+	} else
+		terminate_search = 1;
+
+	if (terminate_search) {
+		state->in_search = 0;
+		bzero(state->pattern, sizeof(state->pattern));
+		move(0, 0);
+		refresh();
+		clrtoeol();
+		return -1;
+	}
+	return 0;
+}
+
+static void conf(struct menu *menu)
+{
+	selected_conf(menu, NULL);
+}
+
+static void selected_conf(struct menu *menu, struct menu *active_menu)
+{
+	struct menu *submenu = NULL;
+	struct symbol *sym;
+	int i, res;
+	int current_index = 0;
+	int last_top_row = 0;
+	struct match_state match_state = {
+		.in_search = 0,
+		.match_direction = MATCH_TINKER_PATTERN_DOWN,
+		.pattern = "",
+	};
+
+	while (!global_exit) {
+		reset_menu();
+		current_menu = menu;
+		build_conf(menu);
+		if (!child_count)
+			break;
+
+		if (active_menu != NULL) {
+			for (i = 0; i < items_num; i++) {
+				struct mitem *mcur;
+
+				mcur = (struct mitem *) item_userptr(curses_menu_items[i]);
+				if ((struct menu *) mcur->usrptr == active_menu) {
+					current_index = i;
+					break;
+				}
+			}
+			active_menu = NULL;
+		}
+
+		show_menu(menu_get_prompt(menu), menu_instructions,
+			  current_index, &last_top_row);
+		keypad((menu_win(curses_menu)), TRUE);
+		while (!global_exit) {
+			if (match_state.in_search) {
+				mvprintw(0, 0,
+					"searching: %s", match_state.pattern);
+				clrtoeol();
+			}
+			refresh_all_windows(main_window);
+			res = wgetch(menu_win(curses_menu));
+			if (!res)
+				break;
+			if (do_match(res, &match_state, &current_index) == 0) {
+				if (current_index != -1)
+					center_item(current_index,
+						    &last_top_row);
+				continue;
+			}
+			if (process_special_keys(&res,
+						(struct menu *) item_data()))
+				break;
+			switch (res) {
+			case KEY_DOWN:
+			case 'j':
+				menu_driver(curses_menu, REQ_DOWN_ITEM);
+				break;
+			case KEY_UP:
+			case 'k':
+				menu_driver(curses_menu, REQ_UP_ITEM);
+				break;
+			case KEY_NPAGE:
+				menu_driver(curses_menu, REQ_SCR_DPAGE);
+				break;
+			case KEY_PPAGE:
+				menu_driver(curses_menu, REQ_SCR_UPAGE);
+				break;
+			case KEY_HOME:
+				menu_driver(curses_menu, REQ_FIRST_ITEM);
+				break;
+			case KEY_END:
+				menu_driver(curses_menu, REQ_LAST_ITEM);
+				break;
+			case 'h':
+			case '?':
+				show_help((struct menu *) item_data());
+				break;
+			}
+			if (res == 10 || res == 27 ||
+				res == 32 || res == 'n' || res == 'y' ||
+				res == KEY_LEFT || res == KEY_RIGHT ||
+				res == 'm')
+				break;
+			refresh_all_windows(main_window);
+		}
+
+		refresh_all_windows(main_window);
+		/* if ESC or left*/
+		if (res == 27 || (menu != &rootmenu && res == KEY_LEFT))
+			break;
+
+		/* remember location in the menu */
+		last_top_row = top_row(curses_menu);
+		current_index = curses_item_index();
+
+		if (!item_tag())
+			continue;
+
+		submenu = (struct menu *) item_data();
+		if (!submenu || !menu_is_visible(submenu))
+			continue;
+		sym = submenu->sym;
+
+		switch (res) {
+		case ' ':
+			if (item_is_tag('t'))
+				sym_toggle_tristate_value(sym);
+			else if (item_is_tag('m'))
+				conf(submenu);
+			break;
+		case KEY_RIGHT:
+		case 10: /* ENTER WAS PRESSED */
+			switch (item_tag()) {
+			case 'm':
+				if (single_menu_mode)
+					submenu->data =
+						(void *) (long) !submenu->data;
+				else
+					conf(submenu);
+				break;
+			case 't':
+				if (sym_is_choice(sym) &&
+				    sym_get_tristate_value(sym) == yes)
+					conf_choice(submenu);
+				else if (submenu->prompt &&
+					 submenu->prompt->type == P_MENU)
+					conf(submenu);
+				else if (res == 10)
+					sym_toggle_tristate_value(sym);
+				break;
+			case 's':
+				conf_string(submenu);
+				break;
+			}
+			break;
+		case 'y':
+			if (item_is_tag('t')) {
+				if (sym_set_tristate_value(sym, yes))
+					break;
+				if (sym_set_tristate_value(sym, mod))
+					btn_dialog(main_window, setmod_text, 0);
+			}
+			break;
+		case 'n':
+			if (item_is_tag('t'))
+				sym_set_tristate_value(sym, no);
+			break;
+		case 'm':
+			if (item_is_tag('t'))
+				sym_set_tristate_value(sym, mod);
+			break;
+		}
+	}
+}
+
+static void conf_message_callback(const char *s)
+{
+	btn_dialog(main_window, s, 1, "<OK>");
+}
+
+static void show_help(struct menu *menu)
+{
+	struct gstr help;
+
+	if (!menu)
+		return;
+
+	help = str_new();
+	menu_get_ext_help(menu, &help);
+	show_scroll_win(main_window, menu_get_prompt(menu), str_get(&help));
+	str_free(&help);
+}
+
+static void conf_choice(struct menu *menu)
+{
+	const char *prompt = menu_get_prompt(menu);
+	struct menu *child = NULL;
+	struct symbol *active;
+	struct property *prop;
+	int selected_index = 0;
+	int last_top_row = 0;
+	int res, i = 0;
+	struct match_state match_state = {
+		.in_search = 0,
+		.match_direction = MATCH_TINKER_PATTERN_DOWN,
+		.pattern = "",
+	};
+
+	active = sym_get_choice_value(menu->sym);
+	/* this is mostly duplicated from the conf() function. */
+	while (!global_exit) {
+		reset_menu();
+
+		for (i = 0, child = menu->list; child; child = child->next) {
+			if (!show_all_items && !menu_is_visible(child))
+				continue;
+
+			if (child->sym == sym_get_choice_value(menu->sym))
+				item_make(child, ':', "<X> %s",
+						menu_get_prompt(child));
+			else if (child->sym)
+				item_make(child, ':', "    %s",
+						menu_get_prompt(child));
+			else
+				item_make(child, ':', "*** %s ***",
+						menu_get_prompt(child));
+
+			if (child->sym == active){
+				last_top_row = top_row(curses_menu);
+				selected_index = i;
+			}
+			i++;
+		}
+		show_menu(prompt ? prompt : "Choice Menu",
+				radiolist_instructions,
+				selected_index,
+				&last_top_row);
+		while (!global_exit) {
+			if (match_state.in_search) {
+				mvprintw(0, 0, "searching: %s",
+					 match_state.pattern);
+				clrtoeol();
+			}
+			refresh_all_windows(main_window);
+			res = wgetch(menu_win(curses_menu));
+			if (!res)
+				break;
+			if (do_match(res, &match_state, &selected_index) == 0) {
+				if (selected_index != -1)
+					center_item(selected_index,
+						    &last_top_row);
+				continue;
+			}
+			if (process_special_keys(
+						&res,
+						(struct menu *) item_data()))
+				break;
+			switch (res) {
+			case KEY_DOWN:
+			case 'j':
+				menu_driver(curses_menu, REQ_DOWN_ITEM);
+				break;
+			case KEY_UP:
+			case 'k':
+				menu_driver(curses_menu, REQ_UP_ITEM);
+				break;
+			case KEY_NPAGE:
+				menu_driver(curses_menu, REQ_SCR_DPAGE);
+				break;
+			case KEY_PPAGE:
+				menu_driver(curses_menu, REQ_SCR_UPAGE);
+				break;
+			case KEY_HOME:
+				menu_driver(curses_menu, REQ_FIRST_ITEM);
+				break;
+			case KEY_END:
+				menu_driver(curses_menu, REQ_LAST_ITEM);
+				break;
+			case 'h':
+			case '?':
+				show_help((struct menu *) item_data());
+				break;
+			}
+			if (res == 10 || res == 27 || res == ' ' ||
+					res == KEY_LEFT){
+				break;
+			}
+			refresh_all_windows(main_window);
+		}
+		/* if ESC or left */
+		if (res == 27 || res == KEY_LEFT)
+			break;
+
+		child = item_data();
+		if (!child || !menu_is_visible(child) || !child->sym)
+			continue;
+		switch (res) {
+		case ' ':
+		case  10:
+		case KEY_RIGHT:
+			if (sym_get_tristate_value(child->sym) != yes) {
+				for_all_properties(menu->sym, prop, P_RESET) {
+					if (expr_calc_value(prop->visible.expr) == no)
+						continue;
+
+					conf_reset(S_DEF_USER);
+					break;
+				}
+			}
+			sym_set_tristate_value(child->sym, yes);
+			return;
+		case 'h':
+		case '?':
+			show_help(child);
+			active = child->sym;
+			break;
+		case KEY_EXIT:
+			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 nconf error!";
+		}
+		res = dialog_inputbox(main_window,
+				prompt ? prompt : "Main Menu",
+				heading,
+				sym_get_string_value(menu->sym),
+				&dialog_input_result,
+				&dialog_input_result_len);
+		switch (res) {
+		case 0:
+			if (sym_set_string_value(menu->sym,
+						dialog_input_result))
+				return;
+			btn_dialog(main_window,
+				"You have made an invalid entry.", 0);
+			break;
+		case 1:
+			show_help(menu);
+			break;
+		case KEY_EXIT:
+			return;
+		}
+	}
+}
+
+static void conf_load(void)
+{
+	while (1) {
+		int res;
+		res = dialog_inputbox(main_window,
+				NULL, load_config_text,
+				filename,
+				&dialog_input_result,
+				&dialog_input_result_len);
+		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;
+			}
+			btn_dialog(main_window, "File does not exist!", 0);
+			break;
+		case 1:
+			show_scroll_win(main_window,
+					"Load Alternate Configuration",
+					load_config_help);
+			break;
+		case KEY_EXIT:
+			return;
+		}
+	}
+}
+
+static void conf_save(void)
+{
+	while (1) {
+		int res;
+		res = dialog_inputbox(main_window,
+				NULL, save_config_text,
+				filename,
+				&dialog_input_result,
+				&dialog_input_result_len);
+		switch (res) {
+		case 0:
+			if (!dialog_input_result[0])
+				return;
+			res = conf_write(dialog_input_result);
+			if (!res) {
+				set_config_filename(dialog_input_result);
+				return;
+			}
+			btn_dialog(main_window, "Can't create file!",
+				1, "<OK>");
+			break;
+		case 1:
+			show_scroll_win(main_window,
+				"Save Alternate Configuration",
+				save_config_help);
+			break;
+		case KEY_EXIT:
+			return;
+		}
+	}
+}
+
+static void setup_windows(void)
+{
+	int lines, columns;
+
+	getmaxyx(stdscr, lines, columns);
+
+	if (main_window != NULL)
+		delwin(main_window);
+
+	/* set up the menu and menu window */
+	main_window = newwin(lines-2, columns-2, 2, 1);
+	keypad(main_window, TRUE);
+	mwin_max_lines = lines-7;
+	mwin_max_cols = columns-6;
+
+	/* panels order is from bottom to top */
+	new_panel(main_window);
+}
+
+int main(int ac, char **av)
+{
+	int lines, columns;
+	char *mode;
+
+	if (ac > 1 && strcmp(av[1], "-s") == 0) {
+		/* 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("NCONFIG_MODE");
+	if (mode) {
+		if (!strcasecmp(mode, "single_menu"))
+			single_menu_mode = 1;
+	}
+
+	/* Initialize curses */
+	initscr();
+	/* set color theme */
+	set_colors();
+
+	cbreak();
+	noecho();
+	keypad(stdscr, TRUE);
+	curs_set(0);
+
+	getmaxyx(stdscr, lines, columns);
+	if (columns < 75 || lines < 20) {
+		endwin();
+		printf("Your terminal should have at "
+			"least 20 lines and 75 columns\n");
+		return 1;
+	}
+
+	notimeout(stdscr, FALSE);
+#if NCURSES_REENTRANT
+	set_escdelay(1);
+#else
+	ESCDELAY = 1;
+#endif
+
+	/* set btns menu */
+	curses_menu = new_menu(curses_menu_items);
+	menu_opts_off(curses_menu, O_SHOWDESC);
+	menu_opts_on(curses_menu, O_SHOWMATCH);
+	menu_opts_on(curses_menu, O_ONEVALUE);
+	menu_opts_on(curses_menu, O_NONCYCLIC);
+	menu_opts_on(curses_menu, O_IGNORECASE);
+	set_menu_mark(curses_menu, " ");
+	set_menu_fore(curses_menu, attr_main_menu_fore);
+	set_menu_back(curses_menu, attr_main_menu_back);
+	set_menu_grey(curses_menu, attr_main_menu_grey);
+
+	set_config_filename(conf_get_configname());
+	setup_windows();
+
+	/* check for KEY_FUNC(1) */
+	if (has_key(KEY_F(1)) == FALSE) {
+		show_scroll_win(main_window,
+				"Instructions",
+				menu_no_f_instructions);
+	}
+
+	conf_set_message_callback(conf_message_callback);
+	/* do the work */
+	while (!global_exit) {
+		conf(&rootmenu);
+		if (!global_exit && do_exit() == 0)
+			break;
+	}
+	/* ok, we are done */
+	unpost_menu(curses_menu);
+	free_menu(curses_menu);
+	delwin(main_window);
+	clear();
+	refresh();
+	endwin();
+	return 0;
+}
diff --git a/scripts/config/nconf.gui.c b/scripts/config/nconf.gui.c
new file mode 100644
index 0000000..48ba1c1
--- /dev/null
+++ b/scripts/config/nconf.gui.c
@@ -0,0 +1,641 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2008 Nir Tzachar <nir.tzachar@gmail.com>
+ *
+ * Derived from menuconfig.
+ */
+#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 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;
+}
diff --git a/scripts/config/nconf.h b/scripts/config/nconf.h
new file mode 100644
index 0000000..174b035
--- /dev/null
+++ b/scripts/config/nconf.h
@@ -0,0 +1,88 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * 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);
diff --git a/scripts/config/parser.tab.c b/scripts/config/parser.tab.c
new file mode 100644
index 0000000..93fdc25
--- /dev/null
+++ b/scripts/config/parser.tab.c
@@ -0,0 +1,2176 @@
+/* A Bison parser, made by GNU Bison 3.8.2.  */
+
+/* Bison implementation for Yacc-like parsers in C
+
+   Copyright (C) 1984, 1989-1990, 2000-2015, 2018-2021 Free Software Foundation,
+   Inc.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation, either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
+
+/* As a special exception, you may create a larger work that contains
+   part or all of the Bison parser skeleton and distribute that work
+   under terms of your choice, so long as that work isn't itself a
+   parser generator using the skeleton or a modified version thereof
+   as a parser skeleton.  Alternatively, if you modify or redistribute
+   the parser skeleton itself, you may (at your option) remove this
+   special exception, which will cause the skeleton and the resulting
+   Bison output files to be licensed under the GNU General Public
+   License without this special exception.
+
+   This special exception was added by the Free Software Foundation in
+   version 2.2 of Bison.  */
+
+/* C LALR(1) parser skeleton written by Richard Stallman, by
+   simplifying the original so-called "semantic" parser.  */
+
+/* DO NOT RELY ON FEATURES THAT ARE NOT DOCUMENTED in the manual,
+   especially those whose name start with YY_ or yy_.  They are
+   private implementation details that can be changed or removed.  */
+
+/* All symbols defined below should begin with yy or YY, to avoid
+   infringing on user name space.  This should be done even for local
+   variables, as they might otherwise be expanded by user macros.
+   There are some unavoidable exceptions within include files to
+   define necessary library symbols; they are noted "INFRINGES ON
+   USER NAME SPACE" below.  */
+
+/* Identify Bison output, and Bison version.  */
+#define YYBISON 30802
+
+/* Bison version string.  */
+#define YYBISON_VERSION "3.8.2"
+
+/* Skeleton name.  */
+#define YYSKELETON_NAME "yacc.c"
+
+/* Pure parsers.  */
+#define YYPURE 0
+
+/* Push parsers.  */
+#define YYPUSH 0
+
+/* Pull parsers.  */
+#define YYPULL 1
+
+
+
+
+/* First part of user prologue.  */
+
+
+#include <ctype.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdbool.h>
+
+#include "lkc.h"
+#include "internal.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 zconfprint(const char *err, ...);
+static void zconf_error(const char *err, ...);
+static bool zconf_endtoken(const char *tokenname,
+			   const char *expected_tokenname);
+
+struct symbol *symbol_hash[SYMBOL_HASHSIZE];
+
+struct menu *current_menu, *current_entry;
+
+
+
+# ifndef YY_CAST
+#  ifdef __cplusplus
+#   define YY_CAST(Type, Val) static_cast<Type> (Val)
+#   define YY_REINTERPRET_CAST(Type, Val) reinterpret_cast<Type> (Val)
+#  else
+#   define YY_CAST(Type, Val) ((Type) (Val))
+#   define YY_REINTERPRET_CAST(Type, Val) ((Type) (Val))
+#  endif
+# endif
+# ifndef YY_NULLPTR
+#  if defined __cplusplus
+#   if 201103L <= __cplusplus
+#    define YY_NULLPTR nullptr
+#   else
+#    define YY_NULLPTR 0
+#   endif
+#  else
+#   define YY_NULLPTR ((void*)0)
+#  endif
+# endif
+
+#include "parser.tab.h"
+/* Symbol kind.  */
+enum yysymbol_kind_t
+{
+  YYSYMBOL_YYEMPTY = -2,
+  YYSYMBOL_YYEOF = 0,                      /* "end of file"  */
+  YYSYMBOL_YYerror = 1,                    /* error  */
+  YYSYMBOL_YYUNDEF = 2,                    /* "invalid token"  */
+  YYSYMBOL_T_HELPTEXT = 3,                 /* T_HELPTEXT  */
+  YYSYMBOL_T_WORD = 4,                     /* T_WORD  */
+  YYSYMBOL_T_WORD_QUOTE = 5,               /* T_WORD_QUOTE  */
+  YYSYMBOL_T_BOOL = 6,                     /* T_BOOL  */
+  YYSYMBOL_T_CHOICE = 7,                   /* T_CHOICE  */
+  YYSYMBOL_T_CLOSE_PAREN = 8,              /* T_CLOSE_PAREN  */
+  YYSYMBOL_T_COLON_EQUAL = 9,              /* T_COLON_EQUAL  */
+  YYSYMBOL_T_COMMENT = 10,                 /* T_COMMENT  */
+  YYSYMBOL_T_CONFIG = 11,                  /* T_CONFIG  */
+  YYSYMBOL_T_DEFAULT = 12,                 /* T_DEFAULT  */
+  YYSYMBOL_T_DEF_BOOL = 13,                /* T_DEF_BOOL  */
+  YYSYMBOL_T_DEF_TRISTATE = 14,            /* T_DEF_TRISTATE  */
+  YYSYMBOL_T_DEPENDS = 15,                 /* T_DEPENDS  */
+  YYSYMBOL_T_ENDCHOICE = 16,               /* T_ENDCHOICE  */
+  YYSYMBOL_T_ENDIF = 17,                   /* T_ENDIF  */
+  YYSYMBOL_T_ENDMENU = 18,                 /* T_ENDMENU  */
+  YYSYMBOL_T_HELP = 19,                    /* T_HELP  */
+  YYSYMBOL_T_HEX = 20,                     /* T_HEX  */
+  YYSYMBOL_T_IF = 21,                      /* T_IF  */
+  YYSYMBOL_T_IMPLY = 22,                   /* T_IMPLY  */
+  YYSYMBOL_T_INT = 23,                     /* T_INT  */
+  YYSYMBOL_T_MAINMENU = 24,                /* T_MAINMENU  */
+  YYSYMBOL_T_MENU = 25,                    /* T_MENU  */
+  YYSYMBOL_T_MENUCONFIG = 26,              /* T_MENUCONFIG  */
+  YYSYMBOL_T_MODULES = 27,                 /* T_MODULES  */
+  YYSYMBOL_T_ON = 28,                      /* T_ON  */
+  YYSYMBOL_T_OPEN_PAREN = 29,              /* T_OPEN_PAREN  */
+  YYSYMBOL_T_OPTIONAL = 30,                /* T_OPTIONAL  */
+  YYSYMBOL_T_PLUS_EQUAL = 31,              /* T_PLUS_EQUAL  */
+  YYSYMBOL_T_PROMPT = 32,                  /* T_PROMPT  */
+  YYSYMBOL_T_RANGE = 33,                   /* T_RANGE  */
+  YYSYMBOL_T_RESET = 34,                   /* T_RESET  */
+  YYSYMBOL_T_SELECT = 35,                  /* T_SELECT  */
+  YYSYMBOL_T_SOURCE = 36,                  /* T_SOURCE  */
+  YYSYMBOL_T_STRING = 37,                  /* T_STRING  */
+  YYSYMBOL_T_TRISTATE = 38,                /* T_TRISTATE  */
+  YYSYMBOL_T_VISIBLE = 39,                 /* T_VISIBLE  */
+  YYSYMBOL_T_EOL = 40,                     /* T_EOL  */
+  YYSYMBOL_T_ASSIGN_VAL = 41,              /* T_ASSIGN_VAL  */
+  YYSYMBOL_T_OR = 42,                      /* T_OR  */
+  YYSYMBOL_T_AND = 43,                     /* T_AND  */
+  YYSYMBOL_T_EQUAL = 44,                   /* T_EQUAL  */
+  YYSYMBOL_T_UNEQUAL = 45,                 /* T_UNEQUAL  */
+  YYSYMBOL_T_LESS = 46,                    /* T_LESS  */
+  YYSYMBOL_T_LESS_EQUAL = 47,              /* T_LESS_EQUAL  */
+  YYSYMBOL_T_GREATER = 48,                 /* T_GREATER  */
+  YYSYMBOL_T_GREATER_EQUAL = 49,           /* T_GREATER_EQUAL  */
+  YYSYMBOL_T_NOT = 50,                     /* T_NOT  */
+  YYSYMBOL_YYACCEPT = 51,                  /* $accept  */
+  YYSYMBOL_input = 52,                     /* input  */
+  YYSYMBOL_mainmenu_stmt = 53,             /* mainmenu_stmt  */
+  YYSYMBOL_stmt_list = 54,                 /* stmt_list  */
+  YYSYMBOL_stmt_list_in_choice = 55,       /* stmt_list_in_choice  */
+  YYSYMBOL_config_entry_start = 56,        /* config_entry_start  */
+  YYSYMBOL_config_stmt = 57,               /* config_stmt  */
+  YYSYMBOL_menuconfig_entry_start = 58,    /* menuconfig_entry_start  */
+  YYSYMBOL_menuconfig_stmt = 59,           /* menuconfig_stmt  */
+  YYSYMBOL_config_option_list = 60,        /* config_option_list  */
+  YYSYMBOL_config_option = 61,             /* config_option  */
+  YYSYMBOL_choice = 62,                    /* choice  */
+  YYSYMBOL_choice_entry = 63,              /* choice_entry  */
+  YYSYMBOL_choice_end = 64,                /* choice_end  */
+  YYSYMBOL_choice_stmt = 65,               /* choice_stmt  */
+  YYSYMBOL_choice_option_list = 66,        /* choice_option_list  */
+  YYSYMBOL_choice_option = 67,             /* choice_option  */
+  YYSYMBOL_type = 68,                      /* type  */
+  YYSYMBOL_logic_type = 69,                /* logic_type  */
+  YYSYMBOL_default = 70,                   /* default  */
+  YYSYMBOL_if_entry = 71,                  /* if_entry  */
+  YYSYMBOL_if_end = 72,                    /* if_end  */
+  YYSYMBOL_if_stmt = 73,                   /* if_stmt  */
+  YYSYMBOL_if_stmt_in_choice = 74,         /* if_stmt_in_choice  */
+  YYSYMBOL_menu = 75,                      /* menu  */
+  YYSYMBOL_menu_entry = 76,                /* menu_entry  */
+  YYSYMBOL_menu_end = 77,                  /* menu_end  */
+  YYSYMBOL_menu_stmt = 78,                 /* menu_stmt  */
+  YYSYMBOL_menu_option_list = 79,          /* menu_option_list  */
+  YYSYMBOL_source_stmt = 80,               /* source_stmt  */
+  YYSYMBOL_comment = 81,                   /* comment  */
+  YYSYMBOL_comment_stmt = 82,              /* comment_stmt  */
+  YYSYMBOL_comment_option_list = 83,       /* comment_option_list  */
+  YYSYMBOL_help_start = 84,                /* help_start  */
+  YYSYMBOL_help = 85,                      /* help  */
+  YYSYMBOL_depends = 86,                   /* depends  */
+  YYSYMBOL_visible = 87,                   /* visible  */
+  YYSYMBOL_prompt_stmt_opt = 88,           /* prompt_stmt_opt  */
+  YYSYMBOL_end = 89,                       /* end  */
+  YYSYMBOL_if_expr = 90,                   /* if_expr  */
+  YYSYMBOL_expr = 91,                      /* expr  */
+  YYSYMBOL_nonconst_symbol = 92,           /* nonconst_symbol  */
+  YYSYMBOL_symbol = 93,                    /* symbol  */
+  YYSYMBOL_word_opt = 94,                  /* word_opt  */
+  YYSYMBOL_assignment_stmt = 95,           /* assignment_stmt  */
+  YYSYMBOL_assign_op = 96,                 /* assign_op  */
+  YYSYMBOL_assign_val = 97                 /* assign_val  */
+};
+typedef enum yysymbol_kind_t yysymbol_kind_t;
+
+
+
+
+#ifdef short
+# undef short
+#endif
+
+/* On compilers that do not define __PTRDIFF_MAX__ etc., make sure
+   <limits.h> and (if available) <stdint.h> are included
+   so that the code can choose integer types of a good width.  */
+
+#ifndef __PTRDIFF_MAX__
+# include <limits.h> /* INFRINGES ON USER NAME SPACE */
+# if defined __STDC_VERSION__ && 199901 <= __STDC_VERSION__
+#  include <stdint.h> /* INFRINGES ON USER NAME SPACE */
+#  define YY_STDINT_H
+# endif
+#endif
+
+/* Narrow types that promote to a signed type and that can represent a
+   signed or unsigned integer of at least N bits.  In tables they can
+   save space and decrease cache pressure.  Promoting to a signed type
+   helps avoid bugs in integer arithmetic.  */
+
+#ifdef __INT_LEAST8_MAX__
+typedef __INT_LEAST8_TYPE__ yytype_int8;
+#elif defined YY_STDINT_H
+typedef int_least8_t yytype_int8;
+#else
+typedef signed char yytype_int8;
+#endif
+
+#ifdef __INT_LEAST16_MAX__
+typedef __INT_LEAST16_TYPE__ yytype_int16;
+#elif defined YY_STDINT_H
+typedef int_least16_t yytype_int16;
+#else
+typedef short yytype_int16;
+#endif
+
+/* Work around bug in HP-UX 11.23, which defines these macros
+   incorrectly for preprocessor constants.  This workaround can likely
+   be removed in 2023, as HPE has promised support for HP-UX 11.23
+   (aka HP-UX 11i v2) only through the end of 2022; see Table 2 of
+   <https://h20195.www2.hpe.com/V2/getpdf.aspx/4AA4-7673ENW.pdf>.  */
+#ifdef __hpux
+# undef UINT_LEAST8_MAX
+# undef UINT_LEAST16_MAX
+# define UINT_LEAST8_MAX 255
+# define UINT_LEAST16_MAX 65535
+#endif
+
+#if defined __UINT_LEAST8_MAX__ && __UINT_LEAST8_MAX__ <= __INT_MAX__
+typedef __UINT_LEAST8_TYPE__ yytype_uint8;
+#elif (!defined __UINT_LEAST8_MAX__ && defined YY_STDINT_H \
+       && UINT_LEAST8_MAX <= INT_MAX)
+typedef uint_least8_t yytype_uint8;
+#elif !defined __UINT_LEAST8_MAX__ && UCHAR_MAX <= INT_MAX
+typedef unsigned char yytype_uint8;
+#else
+typedef short yytype_uint8;
+#endif
+
+#if defined __UINT_LEAST16_MAX__ && __UINT_LEAST16_MAX__ <= __INT_MAX__
+typedef __UINT_LEAST16_TYPE__ yytype_uint16;
+#elif (!defined __UINT_LEAST16_MAX__ && defined YY_STDINT_H \
+       && UINT_LEAST16_MAX <= INT_MAX)
+typedef uint_least16_t yytype_uint16;
+#elif !defined __UINT_LEAST16_MAX__ && USHRT_MAX <= INT_MAX
+typedef unsigned short yytype_uint16;
+#else
+typedef int yytype_uint16;
+#endif
+
+#ifndef YYPTRDIFF_T
+# if defined __PTRDIFF_TYPE__ && defined __PTRDIFF_MAX__
+#  define YYPTRDIFF_T __PTRDIFF_TYPE__
+#  define YYPTRDIFF_MAXIMUM __PTRDIFF_MAX__
+# elif defined PTRDIFF_MAX
+#  ifndef ptrdiff_t
+#   include <stddef.h> /* INFRINGES ON USER NAME SPACE */
+#  endif
+#  define YYPTRDIFF_T ptrdiff_t
+#  define YYPTRDIFF_MAXIMUM PTRDIFF_MAX
+# else
+#  define YYPTRDIFF_T long
+#  define YYPTRDIFF_MAXIMUM LONG_MAX
+# endif
+#endif
+
+#ifndef YYSIZE_T
+# ifdef __SIZE_TYPE__
+#  define YYSIZE_T __SIZE_TYPE__
+# elif defined size_t
+#  define YYSIZE_T size_t
+# elif defined __STDC_VERSION__ && 199901 <= __STDC_VERSION__
+#  include <stddef.h> /* INFRINGES ON USER NAME SPACE */
+#  define YYSIZE_T size_t
+# else
+#  define YYSIZE_T unsigned
+# endif
+#endif
+
+#define YYSIZE_MAXIMUM                                  \
+  YY_CAST (YYPTRDIFF_T,                                 \
+           (YYPTRDIFF_MAXIMUM < YY_CAST (YYSIZE_T, -1)  \
+            ? YYPTRDIFF_MAXIMUM                         \
+            : YY_CAST (YYSIZE_T, -1)))
+
+#define YYSIZEOF(X) YY_CAST (YYPTRDIFF_T, sizeof (X))
+
+
+/* Stored state numbers (used for stacks). */
+typedef yytype_uint8 yy_state_t;
+
+/* State numbers in computations.  */
+typedef int yy_state_fast_t;
+
+#ifndef YY_
+# if defined YYENABLE_NLS && YYENABLE_NLS
+#  if ENABLE_NLS
+#   include <libintl.h> /* INFRINGES ON USER NAME SPACE */
+#   define YY_(Msgid) dgettext ("bison-runtime", Msgid)
+#  endif
+# endif
+# ifndef YY_
+#  define YY_(Msgid) Msgid
+# endif
+#endif
+
+
+#ifndef YY_ATTRIBUTE_PURE
+# if defined __GNUC__ && 2 < __GNUC__ + (96 <= __GNUC_MINOR__)
+#  define YY_ATTRIBUTE_PURE __attribute__ ((__pure__))
+# else
+#  define YY_ATTRIBUTE_PURE
+# endif
+#endif
+
+#ifndef YY_ATTRIBUTE_UNUSED
+# if defined __GNUC__ && 2 < __GNUC__ + (7 <= __GNUC_MINOR__)
+#  define YY_ATTRIBUTE_UNUSED __attribute__ ((__unused__))
+# else
+#  define YY_ATTRIBUTE_UNUSED
+# endif
+#endif
+
+/* Suppress unused-variable warnings by "using" E.  */
+#if ! defined lint || defined __GNUC__
+# define YY_USE(E) ((void) (E))
+#else
+# define YY_USE(E) /* empty */
+#endif
+
+/* Suppress an incorrect diagnostic about yylval being uninitialized.  */
+#if defined __GNUC__ && ! defined __ICC && 406 <= __GNUC__ * 100 + __GNUC_MINOR__
+# if __GNUC__ * 100 + __GNUC_MINOR__ < 407
+#  define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN                           \
+    _Pragma ("GCC diagnostic push")                                     \
+    _Pragma ("GCC diagnostic ignored \"-Wuninitialized\"")
+# else
+#  define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN                           \
+    _Pragma ("GCC diagnostic push")                                     \
+    _Pragma ("GCC diagnostic ignored \"-Wuninitialized\"")              \
+    _Pragma ("GCC diagnostic ignored \"-Wmaybe-uninitialized\"")
+# endif
+# define YY_IGNORE_MAYBE_UNINITIALIZED_END      \
+    _Pragma ("GCC diagnostic pop")
+#else
+# define YY_INITIAL_VALUE(Value) Value
+#endif
+#ifndef YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN
+# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN
+# define YY_IGNORE_MAYBE_UNINITIALIZED_END
+#endif
+#ifndef YY_INITIAL_VALUE
+# define YY_INITIAL_VALUE(Value) /* Nothing. */
+#endif
+
+#if defined __cplusplus && defined __GNUC__ && ! defined __ICC && 6 <= __GNUC__
+# define YY_IGNORE_USELESS_CAST_BEGIN                          \
+    _Pragma ("GCC diagnostic push")                            \
+    _Pragma ("GCC diagnostic ignored \"-Wuseless-cast\"")
+# define YY_IGNORE_USELESS_CAST_END            \
+    _Pragma ("GCC diagnostic pop")
+#endif
+#ifndef YY_IGNORE_USELESS_CAST_BEGIN
+# define YY_IGNORE_USELESS_CAST_BEGIN
+# define YY_IGNORE_USELESS_CAST_END
+#endif
+
+
+#define YY_ASSERT(E) ((void) (0 && (E)))
+
+#if !defined yyoverflow
+
+/* The parser invokes alloca or malloc; define the necessary symbols.  */
+
+# ifdef YYSTACK_USE_ALLOCA
+#  if YYSTACK_USE_ALLOCA
+#   ifdef __GNUC__
+#    define YYSTACK_ALLOC __builtin_alloca
+#   elif defined __BUILTIN_VA_ARG_INCR
+#    include <alloca.h> /* INFRINGES ON USER NAME SPACE */
+#   elif defined _AIX
+#    define YYSTACK_ALLOC __alloca
+#   elif defined _MSC_VER
+#    include <malloc.h> /* INFRINGES ON USER NAME SPACE */
+#    define alloca _alloca
+#   else
+#    define YYSTACK_ALLOC alloca
+#    if ! defined _ALLOCA_H && ! defined EXIT_SUCCESS
+#     include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
+      /* Use EXIT_SUCCESS as a witness for stdlib.h.  */
+#     ifndef EXIT_SUCCESS
+#      define EXIT_SUCCESS 0
+#     endif
+#    endif
+#   endif
+#  endif
+# endif
+
+# ifdef YYSTACK_ALLOC
+   /* Pacify GCC's 'empty if-body' warning.  */
+#  define YYSTACK_FREE(Ptr) do { /* empty */; } while (0)
+#  ifndef YYSTACK_ALLOC_MAXIMUM
+    /* The OS might guarantee only one guard page at the bottom of the stack,
+       and a page size can be as small as 4096 bytes.  So we cannot safely
+       invoke alloca (N) if N exceeds 4096.  Use a slightly smaller number
+       to allow for a few compiler-allocated temporary stack slots.  */
+#   define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */
+#  endif
+# else
+#  define YYSTACK_ALLOC YYMALLOC
+#  define YYSTACK_FREE YYFREE
+#  ifndef YYSTACK_ALLOC_MAXIMUM
+#   define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM
+#  endif
+#  if (defined __cplusplus && ! defined EXIT_SUCCESS \
+       && ! ((defined YYMALLOC || defined malloc) \
+             && (defined YYFREE || defined free)))
+#   include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
+#   ifndef EXIT_SUCCESS
+#    define EXIT_SUCCESS 0
+#   endif
+#  endif
+#  ifndef YYMALLOC
+#   define YYMALLOC malloc
+#   if ! defined malloc && ! defined EXIT_SUCCESS
+void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */
+#   endif
+#  endif
+#  ifndef YYFREE
+#   define YYFREE free
+#   if ! defined free && ! defined EXIT_SUCCESS
+void free (void *); /* INFRINGES ON USER NAME SPACE */
+#   endif
+#  endif
+# endif
+#endif /* !defined yyoverflow */
+
+#if (! defined yyoverflow \
+     && (! defined __cplusplus \
+         || (defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL)))
+
+/* A type that is properly aligned for any stack member.  */
+union yyalloc
+{
+  yy_state_t yyss_alloc;
+  YYSTYPE yyvs_alloc;
+};
+
+/* The size of the maximum gap between one aligned stack and the next.  */
+# define YYSTACK_GAP_MAXIMUM (YYSIZEOF (union yyalloc) - 1)
+
+/* The size of an array large to enough to hold all stacks, each with
+   N elements.  */
+# define YYSTACK_BYTES(N) \
+     ((N) * (YYSIZEOF (yy_state_t) + YYSIZEOF (YYSTYPE)) \
+      + YYSTACK_GAP_MAXIMUM)
+
+# define YYCOPY_NEEDED 1
+
+/* Relocate STACK from its old location to the new one.  The
+   local variables YYSIZE and YYSTACKSIZE give the old and new number of
+   elements in the stack, and YYPTR gives the new location of the
+   stack.  Advance YYPTR to a properly aligned location for the next
+   stack.  */
+# define YYSTACK_RELOCATE(Stack_alloc, Stack)                           \
+    do                                                                  \
+      {                                                                 \
+        YYPTRDIFF_T yynewbytes;                                         \
+        YYCOPY (&yyptr->Stack_alloc, Stack, yysize);                    \
+        Stack = &yyptr->Stack_alloc;                                    \
+        yynewbytes = yystacksize * YYSIZEOF (*Stack) + YYSTACK_GAP_MAXIMUM; \
+        yyptr += yynewbytes / YYSIZEOF (*yyptr);                        \
+      }                                                                 \
+    while (0)
+
+#endif
+
+#if defined YYCOPY_NEEDED && YYCOPY_NEEDED
+/* Copy COUNT objects from SRC to DST.  The source and destination do
+   not overlap.  */
+# ifndef YYCOPY
+#  if defined __GNUC__ && 1 < __GNUC__
+#   define YYCOPY(Dst, Src, Count) \
+      __builtin_memcpy (Dst, Src, YY_CAST (YYSIZE_T, (Count)) * sizeof (*(Src)))
+#  else
+#   define YYCOPY(Dst, Src, Count)              \
+      do                                        \
+        {                                       \
+          YYPTRDIFF_T yyi;                      \
+          for (yyi = 0; yyi < (Count); yyi++)   \
+            (Dst)[yyi] = (Src)[yyi];            \
+        }                                       \
+      while (0)
+#  endif
+# endif
+#endif /* !YYCOPY_NEEDED */
+
+/* YYFINAL -- State number of the termination state.  */
+#define YYFINAL  6
+/* YYLAST -- Last index in YYTABLE.  */
+#define YYLAST   194
+
+/* YYNTOKENS -- Number of terminals.  */
+#define YYNTOKENS  51
+/* YYNNTS -- Number of nonterminals.  */
+#define YYNNTS  47
+/* YYNRULES -- Number of rules.  */
+#define YYNRULES  106
+/* YYNSTATES -- Number of states.  */
+#define YYNSTATES  187
+
+/* YYMAXUTOK -- Last valid token kind.  */
+#define YYMAXUTOK   305
+
+
+/* YYTRANSLATE(TOKEN-NUM) -- Symbol number corresponding to TOKEN-NUM
+   as returned by yylex, with out-of-bounds checking.  */
+#define YYTRANSLATE(YYX)                                \
+  (0 <= (YYX) && (YYX) <= YYMAXUTOK                     \
+   ? YY_CAST (yysymbol_kind_t, yytranslate[YYX])        \
+   : YYSYMBOL_YYUNDEF)
+
+/* YYTRANSLATE[TOKEN-NUM] -- Symbol number corresponding to TOKEN-NUM
+   as returned by yylex.  */
+static const yytype_int8 yytranslate[] =
+{
+       0,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     1,     2,     3,     4,
+       5,     6,     7,     8,     9,    10,    11,    12,    13,    14,
+      15,    16,    17,    18,    19,    20,    21,    22,    23,    24,
+      25,    26,    27,    28,    29,    30,    31,    32,    33,    34,
+      35,    36,    37,    38,    39,    40,    41,    42,    43,    44,
+      45,    46,    47,    48,    49,    50
+};
+
+#if YYDEBUG
+/* YYRLINE[YYN] -- Source line where rule number YYN was defined.  */
+static const yytype_int16 yyrline[] =
+{
+       0,   110,   110,   110,   114,   119,   121,   122,   123,   124,
+     125,   126,   127,   128,   129,   130,   133,   135,   136,   137,
+     138,   143,   150,   155,   162,   171,   173,   174,   175,   178,
+     186,   192,   202,   208,   214,   220,   230,   240,   245,   253,
+     256,   258,   259,   260,   263,   269,   276,   282,   287,   295,
+     296,   297,   298,   301,   302,   305,   306,   307,   311,   319,
+     327,   330,   335,   342,   347,   355,   358,   360,   361,   364,
+     373,   380,   383,   385,   390,   396,   408,   415,   422,   424,
+     429,   430,   431,   434,   435,   438,   439,   440,   441,   442,
+     443,   444,   445,   446,   447,   448,   452,   454,   455,   458,
+     459,   463,   466,   467,   468,   472,   473
+};
+#endif
+
+/** Accessing symbol of state STATE.  */
+#define YY_ACCESSING_SYMBOL(State) YY_CAST (yysymbol_kind_t, yystos[State])
+
+#if YYDEBUG || 0
+/* The user-facing name of the symbol whose (internal) number is
+   YYSYMBOL.  No bounds checking.  */
+static const char *yysymbol_name (yysymbol_kind_t yysymbol) YY_ATTRIBUTE_UNUSED;
+
+/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM.
+   First, the terminals, then, starting at YYNTOKENS, nonterminals.  */
+static const char *const yytname[] =
+{
+  "\"end of file\"", "error", "\"invalid token\"", "T_HELPTEXT", "T_WORD",
+  "T_WORD_QUOTE", "T_BOOL", "T_CHOICE", "T_CLOSE_PAREN", "T_COLON_EQUAL",
+  "T_COMMENT", "T_CONFIG", "T_DEFAULT", "T_DEF_BOOL", "T_DEF_TRISTATE",
+  "T_DEPENDS", "T_ENDCHOICE", "T_ENDIF", "T_ENDMENU", "T_HELP", "T_HEX",
+  "T_IF", "T_IMPLY", "T_INT", "T_MAINMENU", "T_MENU", "T_MENUCONFIG",
+  "T_MODULES", "T_ON", "T_OPEN_PAREN", "T_OPTIONAL", "T_PLUS_EQUAL",
+  "T_PROMPT", "T_RANGE", "T_RESET", "T_SELECT", "T_SOURCE", "T_STRING",
+  "T_TRISTATE", "T_VISIBLE", "T_EOL", "T_ASSIGN_VAL", "T_OR", "T_AND",
+  "T_EQUAL", "T_UNEQUAL", "T_LESS", "T_LESS_EQUAL", "T_GREATER",
+  "T_GREATER_EQUAL", "T_NOT", "$accept", "input", "mainmenu_stmt",
+  "stmt_list", "stmt_list_in_choice", "config_entry_start", "config_stmt",
+  "menuconfig_entry_start", "menuconfig_stmt", "config_option_list",
+  "config_option", "choice", "choice_entry", "choice_end", "choice_stmt",
+  "choice_option_list", "choice_option", "type", "logic_type", "default",
+  "if_entry", "if_end", "if_stmt", "if_stmt_in_choice", "menu",
+  "menu_entry", "menu_end", "menu_stmt", "menu_option_list", "source_stmt",
+  "comment", "comment_stmt", "comment_option_list", "help_start", "help",
+  "depends", "visible", "prompt_stmt_opt", "end", "if_expr", "expr",
+  "nonconst_symbol", "symbol", "word_opt", "assignment_stmt", "assign_op",
+  "assign_val", YY_NULLPTR
+};
+
+static const char *
+yysymbol_name (yysymbol_kind_t yysymbol)
+{
+  return yytname[yysymbol];
+}
+#endif
+
+#define YYPACT_NINF (-105)
+
+#define yypact_value_is_default(Yyn) \
+  ((Yyn) == YYPACT_NINF)
+
+#define YYTABLE_NINF (-4)
+
+#define yytable_value_is_error(Yyn) \
+  0
+
+/* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing
+   STATE-NUM.  */
+static const yytype_int16 yypact[] =
+{
+      -5,    17,    37,  -105,    57,     8,  -105,    91,    39,    15,
+      46,    72,    80,    10,    82,    80,    83,  -105,  -105,  -105,
+    -105,  -105,  -105,  -105,  -105,  -105,  -105,  -105,  -105,  -105,
+    -105,  -105,  -105,  -105,  -105,    45,  -105,  -105,  -105,    48,
+    -105,    50,    59,  -105,    60,  -105,    10,    10,    -7,  -105,
+      25,    63,    64,    65,   132,   132,   156,   162,   114,    14,
+     114,    95,  -105,  -105,    71,  -105,  -105,  -105,     9,  -105,
+    -105,    10,    10,    27,    27,    27,    27,    27,    27,  -105,
+    -105,  -105,  -105,  -105,  -105,  -105,    69,    73,  -105,    80,
+    -105,    74,   115,    27,    80,  -105,  -105,  -105,   117,  -105,
+      10,   116,  -105,  -105,    80,    86,   118,   107,  -105,   117,
+    -105,  -105,    89,    93,    94,    96,  -105,  -105,  -105,  -105,
+    -105,  -105,  -105,  -105,   107,  -105,  -105,  -105,  -105,  -105,
+    -105,  -105,    98,  -105,  -105,  -105,  -105,  -105,  -105,  -105,
+      10,  -105,   107,  -105,   107,    27,   107,   107,    97,    13,
+    -105,   107,  -105,   107,    10,   102,   103,  -105,  -105,  -105,
+    -105,   162,   108,    23,   109,   113,   107,   120,  -105,  -105,
+     121,   126,   134,    33,  -105,  -105,  -105,  -105,  -105,  -105,
+    -105,   136,  -105,  -105,  -105,  -105,  -105
+};
+
+/* YYDEFACT[STATE-NUM] -- Default reduction number in state STATE-NUM.
+   Performed when YYTABLE does not specify something else to do.  Zero
+   means the default is an error.  */
+static const yytype_int8 yydefact[] =
+{
+       5,     0,     0,     5,     0,     0,     1,     0,     0,     0,
+      99,     0,     0,     0,     0,     0,     0,    25,     9,    25,
+      12,    40,    16,     7,     5,    10,    66,     5,    11,    13,
+      72,     8,     6,     4,    15,     0,   103,   104,   102,   105,
+     100,     0,     0,    96,     0,    98,     0,     0,     0,    97,
+      85,     0,     0,     0,    22,    24,    37,     0,     0,    63,
+       0,    71,    14,   106,     0,    36,    70,    21,     0,    93,
+      58,     0,     0,     0,     0,     0,     0,     0,     0,    62,
+      23,    69,    53,    55,    56,    57,     0,     0,    51,     0,
+      50,     0,     0,     0,     0,    52,    54,    26,    78,    49,
+       0,     0,    28,    27,     0,     0,     0,    83,    41,    78,
+      43,    42,     0,     0,     0,     0,    18,    39,    16,    19,
+      17,    38,    60,    59,    83,    68,    67,    65,    64,    73,
+     101,    92,    94,    95,    90,    91,    86,    87,    88,    89,
+       0,    74,    83,    35,    83,     0,    83,    83,     0,    83,
+      75,    83,    46,    83,     0,     0,     0,    20,    81,    82,
+      80,     0,     0,     0,     0,     0,    83,     0,    79,    29,
+       0,     0,     0,    84,    47,    45,    61,    77,    76,    33,
+      30,     0,    32,    31,    48,    44,    34
+};
+
+/* YYPGOTO[NTERM-NUM].  */
+static const yytype_int16 yypgoto[] =
+{
+    -105,  -105,  -105,     3,    38,  -105,   -55,  -105,  -105,   138,
+    -105,  -105,  -105,  -105,  -105,  -105,  -105,  -105,   125,  -105,
+     -54,    -3,  -105,  -105,  -105,  -105,  -105,  -105,  -105,  -105,
+    -105,   -52,  -105,  -105,   128,   -38,  -105,    68,   -16,  -104,
+     -46,    -8,   -65,  -105,  -105,  -105,  -105
+};
+
+/* YYDEFGOTO[NTERM-NUM].  */
+static const yytype_uint8 yydefgoto[] =
+{
+       0,     2,     3,     4,    57,    17,    18,    19,    20,    54,
+      97,    21,    22,   117,    23,    56,   108,    98,    99,   100,
+      24,   122,    25,   119,    26,    27,   127,    28,    59,    29,
+      30,    31,    61,   101,   102,   103,   126,   148,   123,   155,
+      48,    49,    50,    41,    32,    39,    64
+};
+
+/* YYTABLE[YYPACT[STATE-NUM]] -- What to do in state STATE-NUM.  If
+   positive, shift that token.  If negative, reduce the rule whose
+   number is the opposite.  If YYTABLE_NINF, syntax error.  */
+static const yytype_int16 yytable[] =
+{
+      68,    69,   116,   118,    44,   120,     7,    52,   134,   135,
+     136,   137,   138,   139,    43,    45,    35,   131,   111,     1,
+     162,   125,     5,   129,    36,   132,   133,    58,   145,    86,
+      60,    43,    45,    70,   154,    71,    72,     6,   164,    46,
+     165,   121,   167,   168,   128,   170,    37,   171,    33,   172,
+      40,    71,    72,   124,   149,    71,    72,    -3,     8,    38,
+      47,     9,   181,   178,    10,    71,    72,    11,    12,    73,
+      74,    75,    76,    77,    78,    71,    72,    42,    13,    34,
+     166,   142,    14,    15,    43,    62,   146,    51,    53,    63,
+      65,    -2,     8,    16,   163,     9,   151,   140,    10,    66,
+      67,    11,    12,    79,    80,    81,   116,   118,   173,   120,
+      86,   130,    13,   141,   143,     8,    14,    15,     9,   150,
+     144,    10,   147,   153,    11,    12,   152,    16,   154,   157,
+     113,   114,   115,   158,   159,    13,   160,   169,    82,    14,
+      15,    72,   174,   175,    83,    84,    85,    86,   177,   179,
+      16,    87,    88,   180,    89,    90,   161,    55,   176,    91,
+     182,   183,    82,   112,    92,    93,   184,    94,   104,    95,
+      96,    86,    11,    12,   185,    87,   186,   156,   113,   114,
+     115,   109,     0,    13,   110,     0,   105,     0,   106,     0,
+     107,     0,     0,     0,    96
+};
+
+static const yytype_int16 yycheck[] =
+{
+      46,    47,    57,    57,    12,    57,     3,    15,    73,    74,
+      75,    76,    77,    78,     4,     5,     1,     8,    56,    24,
+     124,    59,     5,    61,     9,    71,    72,    24,    93,    15,
+      27,     4,     5,    40,    21,    42,    43,     0,   142,    29,
+     144,    57,   146,   147,    60,   149,    31,   151,    40,   153,
+       4,    42,    43,    39,   100,    42,    43,     0,     1,    44,
+      50,     4,   166,    40,     7,    42,    43,    10,    11,    44,
+      45,    46,    47,    48,    49,    42,    43,     5,    21,    40,
+     145,    89,    25,    26,     4,    40,    94,     5,     5,    41,
+      40,     0,     1,    36,   140,     4,   104,    28,     7,    40,
+      40,    10,    11,    40,    40,    40,   161,   161,   154,   161,
+      15,    40,    21,    40,    40,     1,    25,    26,     4,     3,
+       5,     7,     5,     5,    10,    11,    40,    36,    21,    40,
+      16,    17,    18,    40,    40,    21,    40,    40,     6,    25,
+      26,    43,    40,    40,    12,    13,    14,    15,    40,    40,
+      36,    19,    20,    40,    22,    23,   118,    19,   161,    27,
+      40,    40,     6,     1,    32,    33,    40,    35,    12,    37,
+      38,    15,    10,    11,    40,    19,    40,   109,    16,    17,
+      18,    56,    -1,    21,    56,    -1,    30,    -1,    32,    -1,
+      34,    -1,    -1,    -1,    38
+};
+
+/* YYSTOS[STATE-NUM] -- The symbol kind of the accessing symbol of
+   state STATE-NUM.  */
+static const yytype_int8 yystos[] =
+{
+       0,    24,    52,    53,    54,     5,     0,    54,     1,     4,
+       7,    10,    11,    21,    25,    26,    36,    56,    57,    58,
+      59,    62,    63,    65,    71,    73,    75,    76,    78,    80,
+      81,    82,    95,    40,    40,     1,     9,    31,    44,    96,
+       4,    94,     5,     4,    92,     5,    29,    50,    91,    92,
+      93,     5,    92,     5,    60,    60,    66,    55,    54,    79,
+      54,    83,    40,    41,    97,    40,    40,    40,    91,    91,
+      40,    42,    43,    44,    45,    46,    47,    48,    49,    40,
+      40,    40,     6,    12,    13,    14,    15,    19,    20,    22,
+      23,    27,    32,    33,    35,    37,    38,    61,    68,    69,
+      70,    84,    85,    86,    12,    30,    32,    34,    67,    69,
+      85,    86,     1,    16,    17,    18,    57,    64,    71,    74,
+      82,    89,    72,    89,    39,    86,    87,    77,    89,    86,
+      40,     8,    91,    91,    93,    93,    93,    93,    93,    93,
+      28,    40,    92,    40,     5,    93,    92,     5,    88,    91,
+       3,    92,    40,     5,    21,    90,    88,    40,    40,    40,
+      40,    55,    90,    91,    90,    90,    93,    90,    90,    40,
+      90,    90,    90,    91,    40,    40,    72,    40,    40,    40,
+      40,    90,    40,    40,    40,    40,    40
+};
+
+/* YYR1[RULE-NUM] -- Symbol kind of the left-hand side of rule RULE-NUM.  */
+static const yytype_int8 yyr1[] =
+{
+       0,    51,    52,    52,    53,    54,    54,    54,    54,    54,
+      54,    54,    54,    54,    54,    54,    55,    55,    55,    55,
+      55,    56,    57,    58,    59,    60,    60,    60,    60,    61,
+      61,    61,    61,    61,    61,    61,    62,    63,    64,    65,
+      66,    66,    66,    66,    67,    67,    67,    67,    67,    68,
+      68,    68,    68,    69,    69,    70,    70,    70,    71,    72,
+      73,    74,    75,    76,    77,    78,    79,    79,    79,    80,
+      81,    82,    83,    83,    84,    85,    86,    87,    88,    88,
+      89,    89,    89,    90,    90,    91,    91,    91,    91,    91,
+      91,    91,    91,    91,    91,    91,    92,    93,    93,    94,
+      94,    95,    96,    96,    96,    97,    97
+};
+
+/* YYR2[RULE-NUM] -- Number of symbols on the right-hand side of rule RULE-NUM.  */
+static const yytype_int8 yyr2[] =
+{
+       0,     2,     2,     1,     3,     0,     2,     2,     2,     2,
+       2,     2,     2,     2,     4,     3,     0,     2,     2,     2,
+       3,     3,     2,     3,     2,     0,     2,     2,     2,     3,
+       4,     4,     4,     4,     5,     2,     3,     2,     1,     3,
+       0,     2,     2,     2,     4,     3,     2,     3,     4,     1,
+       1,     1,     1,     1,     1,     1,     1,     1,     3,     1,
+       3,     3,     3,     2,     1,     3,     0,     2,     2,     3,
+       3,     2,     0,     2,     2,     2,     4,     3,     0,     2,
+       2,     2,     2,     0,     2,     1,     3,     3,     3,     3,
+       3,     3,     3,     2,     3,     3,     1,     1,     1,     0,
+       1,     4,     1,     1,     1,     0,     1
+};
+
+
+enum { YYENOMEM = -2 };
+
+#define yyerrok         (yyerrstatus = 0)
+#define yyclearin       (yychar = YYEMPTY)
+
+#define YYACCEPT        goto yyacceptlab
+#define YYABORT         goto yyabortlab
+#define YYERROR         goto yyerrorlab
+#define YYNOMEM         goto yyexhaustedlab
+
+
+#define YYRECOVERING()  (!!yyerrstatus)
+
+#define YYBACKUP(Token, Value)                                    \
+  do                                                              \
+    if (yychar == YYEMPTY)                                        \
+      {                                                           \
+        yychar = (Token);                                         \
+        yylval = (Value);                                         \
+        YYPOPSTACK (yylen);                                       \
+        yystate = *yyssp;                                         \
+        goto yybackup;                                            \
+      }                                                           \
+    else                                                          \
+      {                                                           \
+        yyerror (YY_("syntax error: cannot back up")); \
+        YYERROR;                                                  \
+      }                                                           \
+  while (0)
+
+/* Backward compatibility with an undocumented macro.
+   Use YYerror or YYUNDEF. */
+#define YYERRCODE YYUNDEF
+
+
+/* Enable debugging if requested.  */
+#if YYDEBUG
+
+# ifndef YYFPRINTF
+#  include <stdio.h> /* INFRINGES ON USER NAME SPACE */
+#  define YYFPRINTF fprintf
+# endif
+
+# define YYDPRINTF(Args)                        \
+do {                                            \
+  if (yydebug)                                  \
+    YYFPRINTF Args;                             \
+} while (0)
+
+
+
+
+# define YY_SYMBOL_PRINT(Title, Kind, Value, Location)                    \
+do {                                                                      \
+  if (yydebug)                                                            \
+    {                                                                     \
+      YYFPRINTF (stderr, "%s ", Title);                                   \
+      yy_symbol_print (stderr,                                            \
+                  Kind, Value); \
+      YYFPRINTF (stderr, "\n");                                           \
+    }                                                                     \
+} while (0)
+
+
+/*-----------------------------------.
+| Print this symbol's value on YYO.  |
+`-----------------------------------*/
+
+static void
+yy_symbol_value_print (FILE *yyo,
+                       yysymbol_kind_t yykind, YYSTYPE const * const yyvaluep)
+{
+  FILE *yyoutput = yyo;
+  YY_USE (yyoutput);
+  if (!yyvaluep)
+    return;
+  YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN
+  YY_USE (yykind);
+  YY_IGNORE_MAYBE_UNINITIALIZED_END
+}
+
+
+/*---------------------------.
+| Print this symbol on YYO.  |
+`---------------------------*/
+
+static void
+yy_symbol_print (FILE *yyo,
+                 yysymbol_kind_t yykind, YYSTYPE const * const yyvaluep)
+{
+  YYFPRINTF (yyo, "%s %s (",
+             yykind < YYNTOKENS ? "token" : "nterm", yysymbol_name (yykind));
+
+  yy_symbol_value_print (yyo, yykind, yyvaluep);
+  YYFPRINTF (yyo, ")");
+}
+
+/*------------------------------------------------------------------.
+| yy_stack_print -- Print the state stack from its BOTTOM up to its |
+| TOP (included).                                                   |
+`------------------------------------------------------------------*/
+
+static void
+yy_stack_print (yy_state_t *yybottom, yy_state_t *yytop)
+{
+  YYFPRINTF (stderr, "Stack now");
+  for (; yybottom <= yytop; yybottom++)
+    {
+      int yybot = *yybottom;
+      YYFPRINTF (stderr, " %d", yybot);
+    }
+  YYFPRINTF (stderr, "\n");
+}
+
+# define YY_STACK_PRINT(Bottom, Top)                            \
+do {                                                            \
+  if (yydebug)                                                  \
+    yy_stack_print ((Bottom), (Top));                           \
+} while (0)
+
+
+/*------------------------------------------------.
+| Report that the YYRULE is going to be reduced.  |
+`------------------------------------------------*/
+
+static void
+yy_reduce_print (yy_state_t *yyssp, YYSTYPE *yyvsp,
+                 int yyrule)
+{
+  int yylno = yyrline[yyrule];
+  int yynrhs = yyr2[yyrule];
+  int yyi;
+  YYFPRINTF (stderr, "Reducing stack by rule %d (line %d):\n",
+             yyrule - 1, yylno);
+  /* The symbols being reduced.  */
+  for (yyi = 0; yyi < yynrhs; yyi++)
+    {
+      YYFPRINTF (stderr, "   $%d = ", yyi + 1);
+      yy_symbol_print (stderr,
+                       YY_ACCESSING_SYMBOL (+yyssp[yyi + 1 - yynrhs]),
+                       &yyvsp[(yyi + 1) - (yynrhs)]);
+      YYFPRINTF (stderr, "\n");
+    }
+}
+
+# define YY_REDUCE_PRINT(Rule)          \
+do {                                    \
+  if (yydebug)                          \
+    yy_reduce_print (yyssp, yyvsp, Rule); \
+} while (0)
+
+/* Nonzero means print parse trace.  It is left uninitialized so that
+   multiple parsers can coexist.  */
+int yydebug;
+#else /* !YYDEBUG */
+# define YYDPRINTF(Args) ((void) 0)
+# define YY_SYMBOL_PRINT(Title, Kind, Value, Location)
+# define YY_STACK_PRINT(Bottom, Top)
+# define YY_REDUCE_PRINT(Rule)
+#endif /* !YYDEBUG */
+
+
+/* YYINITDEPTH -- initial size of the parser's stacks.  */
+#ifndef YYINITDEPTH
+# define YYINITDEPTH 200
+#endif
+
+/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only
+   if the built-in stack extension method is used).
+
+   Do not make this value too large; the results are undefined if
+   YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH)
+   evaluated with infinite-precision integer arithmetic.  */
+
+#ifndef YYMAXDEPTH
+# define YYMAXDEPTH 10000
+#endif
+
+
+
+
+
+
+/*-----------------------------------------------.
+| Release the memory associated to this symbol.  |
+`-----------------------------------------------*/
+
+static void
+yydestruct (const char *yymsg,
+            yysymbol_kind_t yykind, YYSTYPE *yyvaluep)
+{
+  YY_USE (yyvaluep);
+  if (!yymsg)
+    yymsg = "Deleting";
+  YY_SYMBOL_PRINT (yymsg, yykind, yyvaluep, yylocationp);
+
+  YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN
+  switch (yykind)
+    {
+    case YYSYMBOL_choice_entry: /* choice_entry  */
+            {
+	fprintf(stderr, "%s:%d: missing end statement for this entry\n",
+		((*yyvaluep).menu)->file->name, ((*yyvaluep).menu)->lineno);
+	if (current_menu == ((*yyvaluep).menu))
+		menu_end_menu();
+}
+        break;
+
+    case YYSYMBOL_if_entry: /* if_entry  */
+            {
+	fprintf(stderr, "%s:%d: missing end statement for this entry\n",
+		((*yyvaluep).menu)->file->name, ((*yyvaluep).menu)->lineno);
+	if (current_menu == ((*yyvaluep).menu))
+		menu_end_menu();
+}
+        break;
+
+    case YYSYMBOL_menu_entry: /* menu_entry  */
+            {
+	fprintf(stderr, "%s:%d: missing end statement for this entry\n",
+		((*yyvaluep).menu)->file->name, ((*yyvaluep).menu)->lineno);
+	if (current_menu == ((*yyvaluep).menu))
+		menu_end_menu();
+}
+        break;
+
+      default:
+        break;
+    }
+  YY_IGNORE_MAYBE_UNINITIALIZED_END
+}
+
+
+/* Lookahead token kind.  */
+int yychar;
+
+/* The semantic value of the lookahead symbol.  */
+YYSTYPE yylval;
+/* Number of syntax errors so far.  */
+int yynerrs;
+
+
+
+
+/*----------.
+| yyparse.  |
+`----------*/
+
+int
+yyparse (void)
+{
+    yy_state_fast_t yystate = 0;
+    /* Number of tokens to shift before error messages enabled.  */
+    int yyerrstatus = 0;
+
+    /* Refer to the stacks through separate pointers, to allow yyoverflow
+       to reallocate them elsewhere.  */
+
+    /* Their size.  */
+    YYPTRDIFF_T yystacksize = YYINITDEPTH;
+
+    /* The state stack: array, bottom, top.  */
+    yy_state_t yyssa[YYINITDEPTH];
+    yy_state_t *yyss = yyssa;
+    yy_state_t *yyssp = yyss;
+
+    /* The semantic value stack: array, bottom, top.  */
+    YYSTYPE yyvsa[YYINITDEPTH];
+    YYSTYPE *yyvs = yyvsa;
+    YYSTYPE *yyvsp = yyvs;
+
+  int yyn;
+  /* The return value of yyparse.  */
+  int yyresult;
+  /* Lookahead symbol kind.  */
+  yysymbol_kind_t yytoken = YYSYMBOL_YYEMPTY;
+  /* The variables used to return semantic value and location from the
+     action routines.  */
+  YYSTYPE yyval;
+
+
+
+#define YYPOPSTACK(N)   (yyvsp -= (N), yyssp -= (N))
+
+  /* The number of symbols on the RHS of the reduced rule.
+     Keep to zero when no symbol should be popped.  */
+  int yylen = 0;
+
+  YYDPRINTF ((stderr, "Starting parse\n"));
+
+  yychar = YYEMPTY; /* Cause a token to be read.  */
+
+  goto yysetstate;
+
+
+/*------------------------------------------------------------.
+| yynewstate -- push a new state, which is found in yystate.  |
+`------------------------------------------------------------*/
+yynewstate:
+  /* In all cases, when you get here, the value and location stacks
+     have just been pushed.  So pushing a state here evens the stacks.  */
+  yyssp++;
+
+
+/*--------------------------------------------------------------------.
+| yysetstate -- set current state (the top of the stack) to yystate.  |
+`--------------------------------------------------------------------*/
+yysetstate:
+  YYDPRINTF ((stderr, "Entering state %d\n", yystate));
+  YY_ASSERT (0 <= yystate && yystate < YYNSTATES);
+  YY_IGNORE_USELESS_CAST_BEGIN
+  *yyssp = YY_CAST (yy_state_t, yystate);
+  YY_IGNORE_USELESS_CAST_END
+  YY_STACK_PRINT (yyss, yyssp);
+
+  if (yyss + yystacksize - 1 <= yyssp)
+#if !defined yyoverflow && !defined YYSTACK_RELOCATE
+    YYNOMEM;
+#else
+    {
+      /* Get the current used size of the three stacks, in elements.  */
+      YYPTRDIFF_T yysize = yyssp - yyss + 1;
+
+# if defined yyoverflow
+      {
+        /* Give user a chance to reallocate the stack.  Use copies of
+           these so that the &'s don't force the real ones into
+           memory.  */
+        yy_state_t *yyss1 = yyss;
+        YYSTYPE *yyvs1 = yyvs;
+
+        /* Each stack pointer address is followed by the size of the
+           data in use in that stack, in bytes.  This used to be a
+           conditional around just the two extra args, but that might
+           be undefined if yyoverflow is a macro.  */
+        yyoverflow (YY_("memory exhausted"),
+                    &yyss1, yysize * YYSIZEOF (*yyssp),
+                    &yyvs1, yysize * YYSIZEOF (*yyvsp),
+                    &yystacksize);
+        yyss = yyss1;
+        yyvs = yyvs1;
+      }
+# else /* defined YYSTACK_RELOCATE */
+      /* Extend the stack our own way.  */
+      if (YYMAXDEPTH <= yystacksize)
+        YYNOMEM;
+      yystacksize *= 2;
+      if (YYMAXDEPTH < yystacksize)
+        yystacksize = YYMAXDEPTH;
+
+      {
+        yy_state_t *yyss1 = yyss;
+        union yyalloc *yyptr =
+          YY_CAST (union yyalloc *,
+                   YYSTACK_ALLOC (YY_CAST (YYSIZE_T, YYSTACK_BYTES (yystacksize))));
+        if (! yyptr)
+          YYNOMEM;
+        YYSTACK_RELOCATE (yyss_alloc, yyss);
+        YYSTACK_RELOCATE (yyvs_alloc, yyvs);
+#  undef YYSTACK_RELOCATE
+        if (yyss1 != yyssa)
+          YYSTACK_FREE (yyss1);
+      }
+# endif
+
+      yyssp = yyss + yysize - 1;
+      yyvsp = yyvs + yysize - 1;
+
+      YY_IGNORE_USELESS_CAST_BEGIN
+      YYDPRINTF ((stderr, "Stack size increased to %ld\n",
+                  YY_CAST (long, yystacksize)));
+      YY_IGNORE_USELESS_CAST_END
+
+      if (yyss + yystacksize - 1 <= yyssp)
+        YYABORT;
+    }
+#endif /* !defined yyoverflow && !defined YYSTACK_RELOCATE */
+
+
+  if (yystate == YYFINAL)
+    YYACCEPT;
+
+  goto yybackup;
+
+
+/*-----------.
+| yybackup.  |
+`-----------*/
+yybackup:
+  /* Do appropriate processing given the current state.  Read a
+     lookahead token if we need one and don't already have one.  */
+
+  /* First try to decide what to do without reference to lookahead token.  */
+  yyn = yypact[yystate];
+  if (yypact_value_is_default (yyn))
+    goto yydefault;
+
+  /* Not known => get a lookahead token if don't already have one.  */
+
+  /* YYCHAR is either empty, or end-of-input, or a valid lookahead.  */
+  if (yychar == YYEMPTY)
+    {
+      YYDPRINTF ((stderr, "Reading a token\n"));
+      yychar = yylex ();
+    }
+
+  if (yychar <= YYEOF)
+    {
+      yychar = YYEOF;
+      yytoken = YYSYMBOL_YYEOF;
+      YYDPRINTF ((stderr, "Now at end of input.\n"));
+    }
+  else if (yychar == YYerror)
+    {
+      /* The scanner already issued an error message, process directly
+         to error recovery.  But do not keep the error token as
+         lookahead, it is too special and may lead us to an endless
+         loop in error recovery. */
+      yychar = YYUNDEF;
+      yytoken = YYSYMBOL_YYerror;
+      goto yyerrlab1;
+    }
+  else
+    {
+      yytoken = YYTRANSLATE (yychar);
+      YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc);
+    }
+
+  /* If the proper action on seeing token YYTOKEN is to reduce or to
+     detect an error, take that action.  */
+  yyn += yytoken;
+  if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken)
+    goto yydefault;
+  yyn = yytable[yyn];
+  if (yyn <= 0)
+    {
+      if (yytable_value_is_error (yyn))
+        goto yyerrlab;
+      yyn = -yyn;
+      goto yyreduce;
+    }
+
+  /* Count tokens shifted since error; after three, turn off error
+     status.  */
+  if (yyerrstatus)
+    yyerrstatus--;
+
+  /* Shift the lookahead token.  */
+  YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc);
+  yystate = yyn;
+  YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN
+  *++yyvsp = yylval;
+  YY_IGNORE_MAYBE_UNINITIALIZED_END
+
+  /* Discard the shifted token.  */
+  yychar = YYEMPTY;
+  goto yynewstate;
+
+
+/*-----------------------------------------------------------.
+| yydefault -- do the default action for the current state.  |
+`-----------------------------------------------------------*/
+yydefault:
+  yyn = yydefact[yystate];
+  if (yyn == 0)
+    goto yyerrlab;
+  goto yyreduce;
+
+
+/*-----------------------------.
+| yyreduce -- do a reduction.  |
+`-----------------------------*/
+yyreduce:
+  /* yyn is the number of a rule to reduce with.  */
+  yylen = yyr2[yyn];
+
+  /* If YYLEN is nonzero, implement the default value of the action:
+     '$$ = $1'.
+
+     Otherwise, the following line sets YYVAL to garbage.
+     This behavior is undocumented and Bison
+     users should not rely upon it.  Assigning to YYVAL
+     unconditionally makes the parser a bit smaller, and it avoids a
+     GCC warning that YYVAL may be used uninitialized.  */
+  yyval = yyvsp[1-yylen];
+
+
+  YY_REDUCE_PRINT (yyn);
+  switch (yyn)
+    {
+  case 4: /* mainmenu_stmt: T_MAINMENU T_WORD_QUOTE T_EOL  */
+{
+	menu_add_prompt(P_MENU, (yyvsp[-1].string), NULL);
+}
+    break;
+
+  case 14: /* stmt_list: stmt_list T_WORD error T_EOL  */
+                                        { zconf_error("unknown statement \"%s\"", (yyvsp[-2].string)); }
+    break;
+
+  case 15: /* stmt_list: stmt_list error T_EOL  */
+                                        { zconf_error("invalid statement"); }
+    break;
+
+  case 20: /* stmt_list_in_choice: stmt_list_in_choice error T_EOL  */
+                                                { zconf_error("invalid statement"); }
+    break;
+
+  case 21: /* config_entry_start: T_CONFIG nonconst_symbol T_EOL  */
+{
+	(yyvsp[-1].symbol)->flags |= SYMBOL_OPTIONAL;
+	menu_add_entry((yyvsp[-1].symbol));
+	printd(DEBUG_PARSE, "%s:%d:config %s\n", zconf_curname(), zconf_lineno(), (yyvsp[-1].symbol)->name);
+}
+    break;
+
+  case 22: /* config_stmt: config_entry_start config_option_list  */
+{
+	printd(DEBUG_PARSE, "%s:%d:endconfig\n", zconf_curname(), zconf_lineno());
+}
+    break;
+
+  case 23: /* menuconfig_entry_start: T_MENUCONFIG nonconst_symbol T_EOL  */
+{
+	(yyvsp[-1].symbol)->flags |= SYMBOL_OPTIONAL;
+	menu_add_entry((yyvsp[-1].symbol));
+	printd(DEBUG_PARSE, "%s:%d:menuconfig %s\n", zconf_curname(), zconf_lineno(), (yyvsp[-1].symbol)->name);
+}
+    break;
+
+  case 24: /* menuconfig_stmt: menuconfig_entry_start config_option_list  */
+{
+	if (current_entry->prompt)
+		current_entry->prompt->type = P_MENU;
+	else
+		zconfprint("warning: menuconfig statement without prompt");
+	printd(DEBUG_PARSE, "%s:%d:endconfig\n", zconf_curname(), zconf_lineno());
+}
+    break;
+
+  case 29: /* config_option: type prompt_stmt_opt T_EOL  */
+{
+	menu_set_type((yyvsp[-2].type));
+	printd(DEBUG_PARSE, "%s:%d:type(%u)\n",
+		zconf_curname(), zconf_lineno(),
+		(yyvsp[-2].type));
+}
+    break;
+
+  case 30: /* config_option: T_PROMPT T_WORD_QUOTE if_expr T_EOL  */
+{
+	menu_add_prompt(P_PROMPT, (yyvsp[-2].string), (yyvsp[-1].expr));
+	printd(DEBUG_PARSE, "%s:%d:prompt\n", zconf_curname(), zconf_lineno());
+}
+    break;
+
+  case 31: /* config_option: default expr if_expr T_EOL  */
+{
+	menu_add_expr(P_DEFAULT, (yyvsp[-2].expr), (yyvsp[-1].expr));
+	if ((yyvsp[-3].type) != S_UNKNOWN)
+		menu_set_type((yyvsp[-3].type));
+	printd(DEBUG_PARSE, "%s:%d:default(%u)\n",
+		zconf_curname(), zconf_lineno(),
+		(yyvsp[-3].type));
+}
+    break;
+
+  case 32: /* config_option: T_SELECT nonconst_symbol if_expr T_EOL  */
+{
+	menu_add_symbol(P_SELECT, (yyvsp[-2].symbol), (yyvsp[-1].expr));
+	printd(DEBUG_PARSE, "%s:%d:select\n", zconf_curname(), zconf_lineno());
+}
+    break;
+
+  case 33: /* config_option: T_IMPLY nonconst_symbol if_expr T_EOL  */
+{
+	menu_add_symbol(P_IMPLY, (yyvsp[-2].symbol), (yyvsp[-1].expr));
+	printd(DEBUG_PARSE, "%s:%d:imply\n", zconf_curname(), zconf_lineno());
+}
+    break;
+
+  case 34: /* config_option: T_RANGE symbol symbol if_expr T_EOL  */
+{
+	menu_add_expr(P_RANGE, expr_alloc_comp(E_RANGE,(yyvsp[-3].symbol), (yyvsp[-2].symbol)), (yyvsp[-1].expr));
+	printd(DEBUG_PARSE, "%s:%d:range\n", zconf_curname(), zconf_lineno());
+}
+    break;
+
+  case 35: /* 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;
+}
+    break;
+
+  case 36: /* choice: T_CHOICE word_opt T_EOL  */
+{
+	struct symbol *sym = sym_lookup((yyvsp[-1].string), SYMBOL_CHOICE);
+	sym->flags |= SYMBOL_NO_WRITE;
+	menu_add_entry(sym);
+	menu_add_expr(P_CHOICE, NULL, NULL);
+	free((yyvsp[-1].string));
+	printd(DEBUG_PARSE, "%s:%d:choice\n", zconf_curname(), zconf_lineno());
+}
+    break;
+
+  case 37: /* choice_entry: choice choice_option_list  */
+{
+	(yyval.menu) = menu_add_menu();
+}
+    break;
+
+  case 38: /* choice_end: end  */
+{
+	if (zconf_endtoken((yyvsp[0].string), "choice")) {
+		menu_end_menu();
+		printd(DEBUG_PARSE, "%s:%d:endchoice\n", zconf_curname(), zconf_lineno());
+	}
+}
+    break;
+
+  case 44: /* choice_option: T_PROMPT T_WORD_QUOTE if_expr T_EOL  */
+{
+	menu_add_prompt(P_PROMPT, (yyvsp[-2].string), (yyvsp[-1].expr));
+	printd(DEBUG_PARSE, "%s:%d:prompt\n", zconf_curname(), zconf_lineno());
+}
+    break;
+
+  case 45: /* choice_option: logic_type prompt_stmt_opt T_EOL  */
+{
+	menu_set_type((yyvsp[-2].type));
+	printd(DEBUG_PARSE, "%s:%d:type(%u)\n",
+	       zconf_curname(), zconf_lineno(), (yyvsp[-2].type));
+}
+    break;
+
+  case 46: /* choice_option: T_OPTIONAL T_EOL  */
+{
+	current_entry->sym->flags |= SYMBOL_OPTIONAL;
+	printd(DEBUG_PARSE, "%s:%d:optional\n", zconf_curname(), zconf_lineno());
+}
+    break;
+
+  case 47: /* choice_option: T_RESET if_expr T_EOL  */
+{
+	menu_add_prop(P_RESET, NULL, (yyvsp[-1].expr));
+}
+    break;
+
+  case 48: /* choice_option: T_DEFAULT nonconst_symbol if_expr T_EOL  */
+{
+	menu_add_symbol(P_DEFAULT, (yyvsp[-2].symbol), (yyvsp[-1].expr));
+	printd(DEBUG_PARSE, "%s:%d:default\n",
+	       zconf_curname(), zconf_lineno());
+}
+    break;
+
+  case 50: /* type: T_INT  */
+                                { (yyval.type) = S_INT; }
+    break;
+
+  case 51: /* type: T_HEX  */
+                                { (yyval.type) = S_HEX; }
+    break;
+
+  case 52: /* type: T_STRING  */
+                                { (yyval.type) = S_STRING; }
+    break;
+
+  case 53: /* logic_type: T_BOOL  */
+                                { (yyval.type) = S_BOOLEAN; }
+    break;
+
+  case 54: /* logic_type: T_TRISTATE  */
+                                { (yyval.type) = S_TRISTATE; }
+    break;
+
+  case 55: /* default: T_DEFAULT  */
+                                { (yyval.type) = S_UNKNOWN; }
+    break;
+
+  case 56: /* default: T_DEF_BOOL  */
+                                { (yyval.type) = S_BOOLEAN; }
+    break;
+
+  case 57: /* default: T_DEF_TRISTATE  */
+                                { (yyval.type) = S_TRISTATE; }
+    break;
+
+  case 58: /* if_entry: T_IF expr T_EOL  */
+{
+	printd(DEBUG_PARSE, "%s:%d:if\n", zconf_curname(), zconf_lineno());
+	menu_add_entry(NULL);
+	menu_add_dep((yyvsp[-1].expr));
+	(yyval.menu) = menu_add_menu();
+}
+    break;
+
+  case 59: /* if_end: end  */
+{
+	if (zconf_endtoken((yyvsp[0].string), "if")) {
+		menu_end_menu();
+		printd(DEBUG_PARSE, "%s:%d:endif\n", zconf_curname(), zconf_lineno());
+	}
+}
+    break;
+
+  case 62: /* menu: T_MENU T_WORD_QUOTE T_EOL  */
+{
+	menu_add_entry(NULL);
+	menu_add_prompt(P_MENU, (yyvsp[-1].string), NULL);
+	printd(DEBUG_PARSE, "%s:%d:menu\n", zconf_curname(), zconf_lineno());
+}
+    break;
+
+  case 63: /* menu_entry: menu menu_option_list  */
+{
+	(yyval.menu) = menu_add_menu();
+}
+    break;
+
+  case 64: /* menu_end: end  */
+{
+	if (zconf_endtoken((yyvsp[0].string), "menu")) {
+		menu_end_menu();
+		printd(DEBUG_PARSE, "%s:%d:endmenu\n", zconf_curname(), zconf_lineno());
+	}
+}
+    break;
+
+  case 69: /* source_stmt: T_SOURCE T_WORD_QUOTE T_EOL  */
+{
+	printd(DEBUG_PARSE, "%s:%d:source %s\n", zconf_curname(), zconf_lineno(), (yyvsp[-1].string));
+	zconf_nextfile((yyvsp[-1].string));
+	free((yyvsp[-1].string));
+}
+    break;
+
+  case 70: /* comment: T_COMMENT T_WORD_QUOTE T_EOL  */
+{
+	menu_add_entry(NULL);
+	menu_add_prompt(P_COMMENT, (yyvsp[-1].string), NULL);
+	printd(DEBUG_PARSE, "%s:%d:comment\n", zconf_curname(), zconf_lineno());
+}
+    break;
+
+  case 74: /* help_start: T_HELP T_EOL  */
+{
+	printd(DEBUG_PARSE, "%s:%d:help\n", zconf_curname(), zconf_lineno());
+	zconf_starthelp();
+}
+    break;
+
+  case 75: /* help: help_start T_HELPTEXT  */
+{
+	/* Is the help text empty or all whitespace? */
+	if ((yyvsp[0].string)[strspn((yyvsp[0].string), " \f\n\r\t\v")] == '\0')
+		zconfprint("warning: '%s' defined with blank help text",
+			   current_entry->sym->name ?: "<choice>");
+
+	current_entry->help = (yyvsp[0].string);
+}
+    break;
+
+  case 76: /* depends: T_DEPENDS T_ON expr T_EOL  */
+{
+	menu_add_dep((yyvsp[-1].expr));
+	printd(DEBUG_PARSE, "%s:%d:depends on\n", zconf_curname(), zconf_lineno());
+}
+    break;
+
+  case 77: /* visible: T_VISIBLE if_expr T_EOL  */
+{
+	menu_add_visibility((yyvsp[-1].expr));
+}
+    break;
+
+  case 79: /* prompt_stmt_opt: T_WORD_QUOTE if_expr  */
+{
+	menu_add_prompt(P_PROMPT, (yyvsp[-1].string), (yyvsp[0].expr));
+}
+    break;
+
+  case 80: /* end: T_ENDMENU T_EOL  */
+                                { (yyval.string) = "menu"; }
+    break;
+
+  case 81: /* end: T_ENDCHOICE T_EOL  */
+                                { (yyval.string) = "choice"; }
+    break;
+
+  case 82: /* end: T_ENDIF T_EOL  */
+                                { (yyval.string) = "if"; }
+    break;
+
+  case 83: /* if_expr: %empty  */
+                                        { (yyval.expr) = NULL; }
+    break;
+
+  case 84: /* if_expr: T_IF expr  */
+                                        { (yyval.expr) = (yyvsp[0].expr); }
+    break;
+
+  case 85: /* expr: symbol  */
+                                                { (yyval.expr) = expr_alloc_symbol((yyvsp[0].symbol)); }
+    break;
+
+  case 86: /* expr: symbol T_LESS symbol  */
+                                                { (yyval.expr) = expr_alloc_comp(E_LTH, (yyvsp[-2].symbol), (yyvsp[0].symbol)); }
+    break;
+
+  case 87: /* expr: symbol T_LESS_EQUAL symbol  */
+                                                { (yyval.expr) = expr_alloc_comp(E_LEQ, (yyvsp[-2].symbol), (yyvsp[0].symbol)); }
+    break;
+
+  case 88: /* expr: symbol T_GREATER symbol  */
+                                                { (yyval.expr) = expr_alloc_comp(E_GTH, (yyvsp[-2].symbol), (yyvsp[0].symbol)); }
+    break;
+
+  case 89: /* expr: symbol T_GREATER_EQUAL symbol  */
+                                                { (yyval.expr) = expr_alloc_comp(E_GEQ, (yyvsp[-2].symbol), (yyvsp[0].symbol)); }
+    break;
+
+  case 90: /* expr: symbol T_EQUAL symbol  */
+                                                { (yyval.expr) = expr_alloc_comp(E_EQUAL, (yyvsp[-2].symbol), (yyvsp[0].symbol)); }
+    break;
+
+  case 91: /* expr: symbol T_UNEQUAL symbol  */
+                                                { (yyval.expr) = expr_alloc_comp(E_UNEQUAL, (yyvsp[-2].symbol), (yyvsp[0].symbol)); }
+    break;
+
+  case 92: /* expr: T_OPEN_PAREN expr T_CLOSE_PAREN  */
+                                                { (yyval.expr) = (yyvsp[-1].expr); }
+    break;
+
+  case 93: /* expr: T_NOT expr  */
+                                                { (yyval.expr) = expr_alloc_one(E_NOT, (yyvsp[0].expr)); }
+    break;
+
+  case 94: /* expr: expr T_OR expr  */
+                                                { (yyval.expr) = expr_alloc_two(E_OR, (yyvsp[-2].expr), (yyvsp[0].expr)); }
+    break;
+
+  case 95: /* expr: expr T_AND expr  */
+                                                { (yyval.expr) = expr_alloc_two(E_AND, (yyvsp[-2].expr), (yyvsp[0].expr)); }
+    break;
+
+  case 96: /* nonconst_symbol: T_WORD  */
+                        { (yyval.symbol) = sym_lookup((yyvsp[0].string), 0); free((yyvsp[0].string)); }
+    break;
+
+  case 98: /* symbol: T_WORD_QUOTE  */
+                        { (yyval.symbol) = sym_lookup((yyvsp[0].string), SYMBOL_CONST); free((yyvsp[0].string)); }
+    break;
+
+  case 99: /* word_opt: %empty  */
+                                        { (yyval.string) = NULL; }
+    break;
+
+  case 101: /* assignment_stmt: T_WORD assign_op assign_val T_EOL  */
+                                                        { variable_add((yyvsp[-3].string), (yyvsp[-1].string), (yyvsp[-2].flavor)); free((yyvsp[-3].string)); free((yyvsp[-1].string)); }
+    break;
+
+  case 102: /* assign_op: T_EQUAL  */
+                        { (yyval.flavor) = VAR_RECURSIVE; }
+    break;
+
+  case 103: /* assign_op: T_COLON_EQUAL  */
+                        { (yyval.flavor) = VAR_SIMPLE; }
+    break;
+
+  case 104: /* assign_op: T_PLUS_EQUAL  */
+                        { (yyval.flavor) = VAR_APPEND; }
+    break;
+
+  case 105: /* assign_val: %empty  */
+                                { (yyval.string) = xstrdup(""); }
+    break;
+
+
+
+      default: break;
+    }
+  /* User semantic actions sometimes alter yychar, and that requires
+     that yytoken be updated with the new translation.  We take the
+     approach of translating immediately before every use of yytoken.
+     One alternative is translating here after every semantic action,
+     but that translation would be missed if the semantic action invokes
+     YYABORT, YYACCEPT, or YYERROR immediately after altering yychar or
+     if it invokes YYBACKUP.  In the case of YYABORT or YYACCEPT, an
+     incorrect destructor might then be invoked immediately.  In the
+     case of YYERROR or YYBACKUP, subsequent parser actions might lead
+     to an incorrect destructor call or verbose syntax error message
+     before the lookahead is translated.  */
+  YY_SYMBOL_PRINT ("-> $$ =", YY_CAST (yysymbol_kind_t, yyr1[yyn]), &yyval, &yyloc);
+
+  YYPOPSTACK (yylen);
+  yylen = 0;
+
+  *++yyvsp = yyval;
+
+  /* Now 'shift' the result of the reduction.  Determine what state
+     that goes to, based on the state we popped back to and the rule
+     number reduced by.  */
+  {
+    const int yylhs = yyr1[yyn] - YYNTOKENS;
+    const int yyi = yypgoto[yylhs] + *yyssp;
+    yystate = (0 <= yyi && yyi <= YYLAST && yycheck[yyi] == *yyssp
+               ? yytable[yyi]
+               : yydefgoto[yylhs]);
+  }
+
+  goto yynewstate;
+
+
+/*--------------------------------------.
+| yyerrlab -- here on detecting error.  |
+`--------------------------------------*/
+yyerrlab:
+  /* Make sure we have latest lookahead translation.  See comments at
+     user semantic actions for why this is necessary.  */
+  yytoken = yychar == YYEMPTY ? YYSYMBOL_YYEMPTY : YYTRANSLATE (yychar);
+  /* If not already recovering from an error, report this error.  */
+  if (!yyerrstatus)
+    {
+      ++yynerrs;
+      yyerror (YY_("syntax error"));
+    }
+
+  if (yyerrstatus == 3)
+    {
+      /* If just tried and failed to reuse lookahead token after an
+         error, discard it.  */
+
+      if (yychar <= YYEOF)
+        {
+          /* Return failure if at end of input.  */
+          if (yychar == YYEOF)
+            YYABORT;
+        }
+      else
+        {
+          yydestruct ("Error: discarding",
+                      yytoken, &yylval);
+          yychar = YYEMPTY;
+        }
+    }
+
+  /* Else will try to reuse lookahead token after shifting the error
+     token.  */
+  goto yyerrlab1;
+
+
+/*---------------------------------------------------.
+| yyerrorlab -- error raised explicitly by YYERROR.  |
+`---------------------------------------------------*/
+yyerrorlab:
+  /* Pacify compilers when the user code never invokes YYERROR and the
+     label yyerrorlab therefore never appears in user code.  */
+  if (0)
+    YYERROR;
+  ++yynerrs;
+
+  /* Do not reclaim the symbols of the rule whose action triggered
+     this YYERROR.  */
+  YYPOPSTACK (yylen);
+  yylen = 0;
+  YY_STACK_PRINT (yyss, yyssp);
+  yystate = *yyssp;
+  goto yyerrlab1;
+
+
+/*-------------------------------------------------------------.
+| yyerrlab1 -- common code for both syntax error and YYERROR.  |
+`-------------------------------------------------------------*/
+yyerrlab1:
+  yyerrstatus = 3;      /* Each real token shifted decrements this.  */
+
+  /* Pop stack until we find a state that shifts the error token.  */
+  for (;;)
+    {
+      yyn = yypact[yystate];
+      if (!yypact_value_is_default (yyn))
+        {
+          yyn += YYSYMBOL_YYerror;
+          if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYSYMBOL_YYerror)
+            {
+              yyn = yytable[yyn];
+              if (0 < yyn)
+                break;
+            }
+        }
+
+      /* Pop the current state because it cannot handle the error token.  */
+      if (yyssp == yyss)
+        YYABORT;
+
+
+      yydestruct ("Error: popping",
+                  YY_ACCESSING_SYMBOL (yystate), yyvsp);
+      YYPOPSTACK (1);
+      yystate = *yyssp;
+      YY_STACK_PRINT (yyss, yyssp);
+    }
+
+  YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN
+  *++yyvsp = yylval;
+  YY_IGNORE_MAYBE_UNINITIALIZED_END
+
+
+  /* Shift the error token.  */
+  YY_SYMBOL_PRINT ("Shifting", YY_ACCESSING_SYMBOL (yyn), yyvsp, yylsp);
+
+  yystate = yyn;
+  goto yynewstate;
+
+
+/*-------------------------------------.
+| yyacceptlab -- YYACCEPT comes here.  |
+`-------------------------------------*/
+yyacceptlab:
+  yyresult = 0;
+  goto yyreturnlab;
+
+
+/*-----------------------------------.
+| yyabortlab -- YYABORT comes here.  |
+`-----------------------------------*/
+yyabortlab:
+  yyresult = 1;
+  goto yyreturnlab;
+
+
+/*-----------------------------------------------------------.
+| yyexhaustedlab -- YYNOMEM (memory exhaustion) comes here.  |
+`-----------------------------------------------------------*/
+yyexhaustedlab:
+  yyerror (YY_("memory exhausted"));
+  yyresult = 2;
+  goto yyreturnlab;
+
+
+/*----------------------------------------------------------.
+| yyreturnlab -- parsing is finished, clean up and return.  |
+`----------------------------------------------------------*/
+yyreturnlab:
+  if (yychar != YYEMPTY)
+    {
+      /* Make sure we have latest lookahead translation.  See comments at
+         user semantic actions for why this is necessary.  */
+      yytoken = YYTRANSLATE (yychar);
+      yydestruct ("Cleanup: discarding lookahead",
+                  yytoken, &yylval);
+    }
+  /* Do not reclaim the symbols of the rule whose action triggered
+     this YYABORT or YYACCEPT.  */
+  YYPOPSTACK (yylen);
+  YY_STACK_PRINT (yyss, yyssp);
+  while (yyssp != yyss)
+    {
+      yydestruct ("Cleanup: popping",
+                  YY_ACCESSING_SYMBOL (+*yyssp), yyvsp);
+      YYPOPSTACK (1);
+    }
+#ifndef yyoverflow
+  if (yyss != yyssa)
+    YYSTACK_FREE (yyss);
+#endif
+
+  return yyresult;
+}
+
+
+
+void conf_parse(const char *name)
+{
+	struct symbol *sym;
+	int i;
+
+	zconf_initscan(name);
+
+	_menu_init();
+
+#if YYDEBUG
+	if (getenv("ZCONF_DEBUG"))
+		yydebug = 1;
+#endif
+	yyparse();
+
+	/* Variables are expanded in the parse phase. We can free them here. */
+	variable_all_del();
+
+	if (yynerrs)
+		exit(1);
+	if (!modules_sym)
+		modules_sym = sym_find( "n" );
+
+	if (!menu_has_prompt(&rootmenu)) {
+		current_entry = &rootmenu;
+		menu_add_prompt(P_MENU, "Main menu", NULL);
+	}
+
+	menu_finalize(&rootmenu);
+	for_all_symbols(i, sym) {
+		if (sym_check_deps(sym))
+			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 (current_menu->file != current_file) {
+		zconf_error("'%s' in different file than '%s'",
+			    tokenname, expected_tokenname);
+		fprintf(stderr, "%s:%d: location of the '%s'\n",
+			current_menu->file->name, current_menu->lineno,
+			expected_tokenname);
+		yynerrs++;
+		return false;
+	}
+	return true;
+}
+
+static void zconfprint(const char *err, ...)
+{
+	va_list ap;
+
+	fprintf(stderr, "%s:%d: ", zconf_curname(), zconf_lineno());
+	va_start(ap, err);
+	vfprintf(stderr, err, ap);
+	va_end(ap);
+	fprintf(stderr, "\n");
+}
+
+static void zconf_error(const char *err, ...)
+{
+	va_list ap;
+
+	yynerrs++;
+	fprintf(stderr, "%s:%d: ", zconf_curname(), zconf_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", zconf_curname(), zconf_lineno() + 1, 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, 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_CHOICE:
+			fputs("  #choice value\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;
+		case P_SYMBOL:
+			fputs( "  symbol ", out);
+			fprintf(out, "%s\n", prop->menu->sym->name);
+			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;
+			}
+		}
+	}
+}
diff --git a/scripts/config/parser.tab.h b/scripts/config/parser.tab.h
new file mode 100644
index 0000000..fa3eb34
--- /dev/null
+++ b/scripts/config/parser.tab.h
@@ -0,0 +1,135 @@
+/* A Bison parser, made by GNU Bison 3.8.2.  */
+
+/* Bison interface for Yacc-like parsers in C
+
+   Copyright (C) 1984, 1989-1990, 2000-2015, 2018-2021 Free Software Foundation,
+   Inc.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation, either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
+
+/* As a special exception, you may create a larger work that contains
+   part or all of the Bison parser skeleton and distribute that work
+   under terms of your choice, so long as that work isn't itself a
+   parser generator using the skeleton or a modified version thereof
+   as a parser skeleton.  Alternatively, if you modify or redistribute
+   the parser skeleton itself, you may (at your option) remove this
+   special exception, which will cause the skeleton and the resulting
+   Bison output files to be licensed under the GNU General Public
+   License without this special exception.
+
+   This special exception was added by the Free Software Foundation in
+   version 2.2 of Bison.  */
+
+/* DO NOT RELY ON FEATURES THAT ARE NOT DOCUMENTED in the manual,
+   especially those whose name start with YY_ or yy_.  They are
+   private implementation details that can be changed or removed.  */
+
+#ifndef YY_YY_PARSER_TAB_H_INCLUDED
+# define YY_YY_PARSER_TAB_H_INCLUDED
+/* Debug traces.  */
+#ifndef YYDEBUG
+# define YYDEBUG 0
+#endif
+#if YYDEBUG
+extern int yydebug;
+#endif
+
+/* Token kinds.  */
+#ifndef YYTOKENTYPE
+# define YYTOKENTYPE
+  enum yytokentype
+  {
+    YYEMPTY = -2,
+    YYEOF = 0,                     /* "end of file"  */
+    YYerror = 256,                 /* error  */
+    YYUNDEF = 257,                 /* "invalid token"  */
+    T_HELPTEXT = 258,              /* T_HELPTEXT  */
+    T_WORD = 259,                  /* T_WORD  */
+    T_WORD_QUOTE = 260,            /* T_WORD_QUOTE  */
+    T_BOOL = 261,                  /* T_BOOL  */
+    T_CHOICE = 262,                /* T_CHOICE  */
+    T_CLOSE_PAREN = 263,           /* T_CLOSE_PAREN  */
+    T_COLON_EQUAL = 264,           /* T_COLON_EQUAL  */
+    T_COMMENT = 265,               /* T_COMMENT  */
+    T_CONFIG = 266,                /* T_CONFIG  */
+    T_DEFAULT = 267,               /* T_DEFAULT  */
+    T_DEF_BOOL = 268,              /* T_DEF_BOOL  */
+    T_DEF_TRISTATE = 269,          /* T_DEF_TRISTATE  */
+    T_DEPENDS = 270,               /* T_DEPENDS  */
+    T_ENDCHOICE = 271,             /* T_ENDCHOICE  */
+    T_ENDIF = 272,                 /* T_ENDIF  */
+    T_ENDMENU = 273,               /* T_ENDMENU  */
+    T_HELP = 274,                  /* T_HELP  */
+    T_HEX = 275,                   /* T_HEX  */
+    T_IF = 276,                    /* T_IF  */
+    T_IMPLY = 277,                 /* T_IMPLY  */
+    T_INT = 278,                   /* T_INT  */
+    T_MAINMENU = 279,              /* T_MAINMENU  */
+    T_MENU = 280,                  /* T_MENU  */
+    T_MENUCONFIG = 281,            /* T_MENUCONFIG  */
+    T_MODULES = 282,               /* T_MODULES  */
+    T_ON = 283,                    /* T_ON  */
+    T_OPEN_PAREN = 284,            /* T_OPEN_PAREN  */
+    T_OPTIONAL = 285,              /* T_OPTIONAL  */
+    T_PLUS_EQUAL = 286,            /* T_PLUS_EQUAL  */
+    T_PROMPT = 287,                /* T_PROMPT  */
+    T_RANGE = 288,                 /* T_RANGE  */
+    T_RESET = 289,                 /* T_RESET  */
+    T_SELECT = 290,                /* T_SELECT  */
+    T_SOURCE = 291,                /* T_SOURCE  */
+    T_STRING = 292,                /* T_STRING  */
+    T_TRISTATE = 293,              /* T_TRISTATE  */
+    T_VISIBLE = 294,               /* T_VISIBLE  */
+    T_EOL = 295,                   /* T_EOL  */
+    T_ASSIGN_VAL = 296,            /* T_ASSIGN_VAL  */
+    T_OR = 297,                    /* T_OR  */
+    T_AND = 298,                   /* T_AND  */
+    T_EQUAL = 299,                 /* T_EQUAL  */
+    T_UNEQUAL = 300,               /* T_UNEQUAL  */
+    T_LESS = 301,                  /* T_LESS  */
+    T_LESS_EQUAL = 302,            /* T_LESS_EQUAL  */
+    T_GREATER = 303,               /* T_GREATER  */
+    T_GREATER_EQUAL = 304,         /* T_GREATER_EQUAL  */
+    T_NOT = 305                    /* T_NOT  */
+  };
+  typedef enum yytokentype yytoken_kind_t;
+#endif
+
+/* Value type.  */
+#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
+union YYSTYPE
+{
+
+	char *string;
+	struct symbol *symbol;
+	struct expr *expr;
+	struct menu *menu;
+	enum symbol_type type;
+	enum variable_flavor flavor;
+
+
+};
+typedef union YYSTYPE YYSTYPE;
+# define YYSTYPE_IS_TRIVIAL 1
+# define YYSTYPE_IS_DECLARED 1
+#endif
+
+
+extern YYSTYPE yylval;
+
+
+int yyparse (void);
+
+
+#endif /* !YY_YY_PARSER_TAB_H_INCLUDED  */
diff --git a/scripts/config/parser.y b/scripts/config/parser.y
new file mode 100644
index 0000000..523c9a9
--- /dev/null
+++ b/scripts/config/parser.y
@@ -0,0 +1,718 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * 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 "lkc.h"
+#include "internal.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 zconfprint(const char *err, ...);
+static void zconf_error(const char *err, ...);
+static bool zconf_endtoken(const char *tokenname,
+			   const char *expected_tokenname);
+
+struct symbol *symbol_hash[SYMBOL_HASHSIZE];
+
+struct menu *current_menu, *current_entry;
+
+%}
+
+%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_OPTIONAL
+%token T_PLUS_EQUAL
+%token T_PROMPT
+%token T_RANGE
+%token T_RESET
+%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 logic_type default
+%type <expr> expr
+%type <expr> if_expr
+%type <string> end
+%type <menu> if_entry menu_entry choice_entry
+%type <string> word_opt assign_val
+%type <flavor> assign_op
+
+%destructor {
+	fprintf(stderr, "%s:%d: missing end statement for this entry\n",
+		$$->file->name, $$->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
+{
+	$2->flags |= SYMBOL_OPTIONAL;
+	menu_add_entry($2);
+	printd(DEBUG_PARSE, "%s:%d:config %s\n", zconf_curname(), zconf_lineno(), $2->name);
+};
+
+config_stmt: config_entry_start config_option_list
+{
+	printd(DEBUG_PARSE, "%s:%d:endconfig\n", zconf_curname(), zconf_lineno());
+};
+
+menuconfig_entry_start: T_MENUCONFIG nonconst_symbol T_EOL
+{
+	$2->flags |= SYMBOL_OPTIONAL;
+	menu_add_entry($2);
+	printd(DEBUG_PARSE, "%s:%d:menuconfig %s\n", zconf_curname(), zconf_lineno(), $2->name);
+};
+
+menuconfig_stmt: menuconfig_entry_start config_option_list
+{
+	if (current_entry->prompt)
+		current_entry->prompt->type = P_MENU;
+	else
+		zconfprint("warning: menuconfig statement without prompt");
+	printd(DEBUG_PARSE, "%s:%d:endconfig\n", zconf_curname(), zconf_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",
+		zconf_curname(), zconf_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", zconf_curname(), zconf_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",
+		zconf_curname(), zconf_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", zconf_curname(), zconf_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", zconf_curname(), zconf_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", zconf_curname(), zconf_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 word_opt T_EOL
+{
+	struct symbol *sym = sym_lookup($2, SYMBOL_CHOICE);
+	sym->flags |= SYMBOL_NO_WRITE;
+	menu_add_entry(sym);
+	menu_add_expr(P_CHOICE, NULL, NULL);
+	free($2);
+	printd(DEBUG_PARSE, "%s:%d:choice\n", zconf_curname(), zconf_lineno());
+};
+
+choice_entry: choice choice_option_list
+{
+	$$ = menu_add_menu();
+};
+
+choice_end: end
+{
+	if (zconf_endtoken($1, "choice")) {
+		menu_end_menu();
+		printd(DEBUG_PARSE, "%s:%d:endchoice\n", zconf_curname(), zconf_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", zconf_curname(), zconf_lineno());
+};
+
+choice_option: logic_type prompt_stmt_opt T_EOL
+{
+	menu_set_type($1);
+	printd(DEBUG_PARSE, "%s:%d:type(%u)\n",
+	       zconf_curname(), zconf_lineno(), $1);
+};
+
+choice_option: T_OPTIONAL T_EOL
+{
+	current_entry->sym->flags |= SYMBOL_OPTIONAL;
+	printd(DEBUG_PARSE, "%s:%d:optional\n", zconf_curname(), zconf_lineno());
+};
+
+choice_option: T_RESET if_expr T_EOL
+{
+	menu_add_prop(P_RESET, NULL, $2);
+};
+
+choice_option: T_DEFAULT nonconst_symbol if_expr T_EOL
+{
+	menu_add_symbol(P_DEFAULT, $2, $3);
+	printd(DEBUG_PARSE, "%s:%d:default\n",
+	       zconf_curname(), zconf_lineno());
+};
+
+type:
+	  logic_type
+	| T_INT			{ $$ = S_INT; }
+	| T_HEX			{ $$ = S_HEX; }
+	| T_STRING		{ $$ = S_STRING; }
+
+logic_type:
+	  T_BOOL		{ $$ = S_BOOLEAN; }
+	| T_TRISTATE		{ $$ = S_TRISTATE; }
+
+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", zconf_curname(), zconf_lineno());
+	menu_add_entry(NULL);
+	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", zconf_curname(), zconf_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);
+	menu_add_prompt(P_MENU, $2, NULL);
+	printd(DEBUG_PARSE, "%s:%d:menu\n", zconf_curname(), zconf_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", zconf_curname(), zconf_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", zconf_curname(), zconf_lineno(), $2);
+	zconf_nextfile($2);
+	free($2);
+};
+
+/* comment entry */
+
+comment: T_COMMENT T_WORD_QUOTE T_EOL
+{
+	menu_add_entry(NULL);
+	menu_add_prompt(P_COMMENT, $2, NULL);
+	printd(DEBUG_PARSE, "%s:%d:comment\n", zconf_curname(), zconf_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", zconf_curname(), zconf_lineno());
+	zconf_starthelp();
+};
+
+help: help_start T_HELPTEXT
+{
+	/* Is the help text empty or all whitespace? */
+	if ($2[strspn($2, " \f\n\r\t\v")] == '\0')
+		zconfprint("warning: '%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", zconf_curname(), zconf_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); }
+;
+
+word_opt: /* empty */			{ $$ = NULL; }
+	| T_WORD
+
+/* 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
+;
+
+%%
+
+void conf_parse(const char *name)
+{
+	struct symbol *sym;
+	int i;
+
+	zconf_initscan(name);
+
+	_menu_init();
+
+#if YYDEBUG
+	if (getenv("ZCONF_DEBUG"))
+		yydebug = 1;
+#endif
+	yyparse();
+
+	/* Variables are expanded in the parse phase. We can free them here. */
+	variable_all_del();
+
+	if (yynerrs)
+		exit(1);
+	if (!modules_sym)
+		modules_sym = sym_find( "n" );
+
+	if (!menu_has_prompt(&rootmenu)) {
+		current_entry = &rootmenu;
+		menu_add_prompt(P_MENU, "Main menu", NULL);
+	}
+
+	menu_finalize(&rootmenu);
+	for_all_symbols(i, sym) {
+		if (sym_check_deps(sym))
+			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 (current_menu->file != current_file) {
+		zconf_error("'%s' in different file than '%s'",
+			    tokenname, expected_tokenname);
+		fprintf(stderr, "%s:%d: location of the '%s'\n",
+			current_menu->file->name, current_menu->lineno,
+			expected_tokenname);
+		yynerrs++;
+		return false;
+	}
+	return true;
+}
+
+static void zconfprint(const char *err, ...)
+{
+	va_list ap;
+
+	fprintf(stderr, "%s:%d: ", zconf_curname(), zconf_lineno());
+	va_start(ap, err);
+	vfprintf(stderr, err, ap);
+	va_end(ap);
+	fprintf(stderr, "\n");
+}
+
+static void zconf_error(const char *err, ...)
+{
+	va_list ap;
+
+	yynerrs++;
+	fprintf(stderr, "%s:%d: ", zconf_curname(), zconf_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", zconf_curname(), zconf_lineno() + 1, 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, 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_CHOICE:
+			fputs("  #choice value\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;
+		case P_SYMBOL:
+			fputs( "  symbol ", out);
+			fprintf(out, "%s\n", prop->menu->sym->name);
+			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;
+			}
+		}
+	}
+}
diff --git a/scripts/config/preprocess.c b/scripts/config/preprocess.c
new file mode 100644
index 0000000..3c544b7
--- /dev/null
+++ b/scripts/config/preprocess.c
@@ -0,0 +1,578 @@
+// SPDX-License-Identifier: GPL-2.0-only
+//
+// 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 "list.h"
+#include "lkc.h"
+
+#define ARRAY_SIZE(arr)		(sizeof(arr) / sizeof((arr)[0]))
+
+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: ", current_file->name, 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(FILE *f, const char *autoconfig_name)
+{
+	struct env *e, *tmp;
+
+	list_for_each_entry_safe(e, tmp, &env_list, node) {
+		fprintf(f, "ifneq \"$(%s)\" \"%s\"\n", e->name, e->value);
+		fprintf(f, "%s: FORCE\n", autoconfig_name);
+		fprintf(f, "endif\n");
+		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(current_file->name);
+}
+
+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",
+			current_file->name, 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)
+{
+	/* Why are '.' and '/' valid characters for symbols? */
+	return !(isalnum(c) || c == '_' || 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);
+}
diff --git a/scripts/config/qconf-cfg.sh b/scripts/config/qconf-cfg.sh
new file mode 100755
index 0000000..203ddf4
--- /dev/null
+++ b/scripts/config/qconf-cfg.sh
@@ -0,0 +1,40 @@
+#!/bin/sh
+# SPDX-License-Identifier: GPL-2.0-only
+
+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
diff --git a/scripts/config/qconf.cc b/scripts/config/qconf.cc
new file mode 100644
index 0000000..abf0d0b
--- /dev/null
+++ b/scripts/config/qconf.cc
@@ -0,0 +1,1928 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>
+ * Copyright (C) 2015 Boris Barbulovski <bbarbulovski@gmail.com>
+ */
+
+#include <QAction>
+#include <QActionGroup>
+#include <QApplication>
+#include <QCloseEvent>
+#include <QDebug>
+#include <QFileDialog>
+#include <QLabel>
+#include <QLayout>
+#include <QList>
+#include <QMenu>
+#include <QMenuBar>
+#include <QMessageBox>
+#include <QRegularExpression>
+#include <QScreen>
+#include <QToolBar>
+
+#include <stdlib.h>
+
+#include "lkc.h"
+#include "qconf.h"
+
+#include "images.h"
+
+
+static QApplication *configApp;
+static ConfigSettings *configSettings;
+
+QAction *ConfigMainWindow::saveAction;
+
+ConfigSettings::ConfigSettings()
+	: QSettings("kernel.org", "qconf")
+{
+}
+
+/**
+ * Reads a list of integer values from the application settings.
+ */
+QList<int> ConfigSettings::readSizes(const QString& key, bool *ok)
+{
+	QList<int> result;
+
+	if (contains(key))
+	{
+		QStringList entryList = value(key).toStringList();
+		QStringList::Iterator it;
+
+		for (it = entryList.begin(); it != entryList.end(); ++it)
+			result.push_back((*it).toInt());
+
+		*ok = true;
+	}
+	else
+		*ok = false;
+
+	return result;
+}
+
+/**
+ * Writes a list of integer values to the application settings.
+ */
+bool ConfigSettings::writeSizes(const QString& key, const QList<int>& value)
+{
+	QStringList stringList;
+	QList<int>::ConstIterator it;
+
+	for (it = value.begin(); it != value.end(); ++it)
+		stringList.push_back(QString::number(*it));
+	setValue(key, stringList);
+
+	return true;
+}
+
+QIcon ConfigItem::symbolYesIcon;
+QIcon ConfigItem::symbolModIcon;
+QIcon ConfigItem::symbolNoIcon;
+QIcon ConfigItem::choiceYesIcon;
+QIcon ConfigItem::choiceNoIcon;
+QIcon ConfigItem::menuIcon;
+QIcon ConfigItem::menubackIcon;
+
+/*
+ * update the displayed of a menu entry
+ */
+void ConfigItem::updateMenu(void)
+{
+	ConfigList* list;
+	struct symbol* sym;
+	struct property *prop;
+	QString prompt;
+	int type;
+	tristate expr;
+
+	list = listView();
+	if (goParent) {
+		setIcon(promptColIdx, menubackIcon);
+		prompt = "..";
+		goto set_prompt;
+	}
+
+	sym = menu->sym;
+	prop = menu->prompt;
+	prompt = menu_get_prompt(menu);
+
+	if (prop) switch (prop->type) {
+	case P_MENU:
+		if (list->mode == singleMode || list->mode == symbolMode) {
+			/* a menuconfig entry is displayed differently
+			 * depending whether it's at the view root or a child.
+			 */
+			if (sym && list->rootEntry == menu)
+				break;
+			setIcon(promptColIdx, menuIcon);
+		} else {
+			if (sym)
+				break;
+			setIcon(promptColIdx, QIcon());
+		}
+		goto set_prompt;
+	case P_COMMENT:
+		setIcon(promptColIdx, QIcon());
+		prompt = "*** " + prompt + " ***";
+		goto set_prompt;
+	default:
+		;
+	}
+	if (!sym)
+		goto set_prompt;
+
+	setText(nameColIdx, sym->name);
+
+	type = sym_get_type(sym);
+	switch (type) {
+	case S_BOOLEAN:
+	case S_TRISTATE:
+		char ch;
+
+		if (!sym_is_changeable(sym) && list->optMode == normalOpt) {
+			setIcon(promptColIdx, QIcon());
+			break;
+		}
+		expr = sym_get_tristate_value(sym);
+		switch (expr) {
+		case yes:
+			if (sym_is_choice_value(sym) && type == S_BOOLEAN)
+				setIcon(promptColIdx, choiceYesIcon);
+			else
+				setIcon(promptColIdx, symbolYesIcon);
+			ch = 'Y';
+			break;
+		case mod:
+			setIcon(promptColIdx, symbolModIcon);
+			ch = 'M';
+			break;
+		default:
+			if (sym_is_choice_value(sym) && type == S_BOOLEAN)
+				setIcon(promptColIdx, choiceNoIcon);
+			else
+				setIcon(promptColIdx, symbolNoIcon);
+			ch = 'N';
+			break;
+		}
+
+		setText(dataColIdx, QChar(ch));
+		break;
+	case S_INT:
+	case S_HEX:
+	case S_STRING:
+		setText(dataColIdx, sym_get_string_value(sym));
+		break;
+	}
+	if (!sym_has_value(sym) && visible)
+		prompt += " (NEW)";
+set_prompt:
+	setText(promptColIdx, prompt);
+}
+
+void ConfigItem::testUpdateMenu(bool v)
+{
+	ConfigItem* i;
+
+	visible = v;
+	if (!menu)
+		return;
+
+	sym_calc_value(menu->sym);
+	if (menu->flags & MENU_CHANGED) {
+		/* the menu entry changed, so update all list items */
+		menu->flags &= ~MENU_CHANGED;
+		for (i = (ConfigItem*)menu->data; i; i = i->nextItem)
+			i->updateMenu();
+	} else if (listView()->updateAll)
+		updateMenu();
+}
+
+
+/*
+ * construct a menu entry
+ */
+void ConfigItem::init(void)
+{
+	if (menu) {
+		ConfigList* list = listView();
+		nextItem = (ConfigItem*)menu->data;
+		menu->data = this;
+
+		if (list->mode != fullMode)
+			setExpanded(true);
+		sym_calc_value(menu->sym);
+
+		if (menu->sym) {
+			enum symbol_type type = menu->sym->type;
+
+			// Allow to edit "int", "hex", and "string" in-place in
+			// the data column. Unfortunately, you cannot specify
+			// the flags per column. Set ItemIsEditable for all
+			// columns here, and check the column in createEditor().
+			if (type == S_INT || type == S_HEX || type == S_STRING)
+				setFlags(flags() | Qt::ItemIsEditable);
+		}
+	}
+	updateMenu();
+}
+
+/*
+ * destruct a menu entry
+ */
+ConfigItem::~ConfigItem(void)
+{
+	if (menu) {
+		ConfigItem** ip = (ConfigItem**)&menu->data;
+		for (; *ip; ip = &(*ip)->nextItem) {
+			if (*ip == this) {
+				*ip = nextItem;
+				break;
+			}
+		}
+	}
+}
+
+QWidget *ConfigItemDelegate::createEditor(QWidget *parent,
+					  const QStyleOptionViewItem &option,
+					  const QModelIndex &index) const
+{
+	ConfigItem *item;
+
+	// Only the data column is editable
+	if (index.column() != dataColIdx)
+		return nullptr;
+
+	// You cannot edit invisible menus
+	item = static_cast<ConfigItem *>(index.internalPointer());
+	if (!item || !item->menu || !menu_is_visible(item->menu))
+		return nullptr;
+
+	return QStyledItemDelegate::createEditor(parent, option, index);
+}
+
+void ConfigItemDelegate::setModelData(QWidget *editor,
+				      QAbstractItemModel *model,
+				      const QModelIndex &index) const
+{
+	QLineEdit *lineEdit;
+	ConfigItem *item;
+	struct symbol *sym;
+	bool success;
+
+	lineEdit = qobject_cast<QLineEdit *>(editor);
+	// If this is not a QLineEdit, use the parent's default.
+	// (does this happen?)
+	if (!lineEdit)
+		goto parent;
+
+	item = static_cast<ConfigItem *>(index.internalPointer());
+	if (!item || !item->menu)
+		goto parent;
+
+	sym = item->menu->sym;
+	if (!sym)
+		goto parent;
+
+	success = sym_set_string_value(sym, lineEdit->text().toUtf8().data());
+	if (success) {
+		ConfigList::updateListForAll();
+	} else {
+		QMessageBox::information(editor, "qconf",
+			"Cannot set the data (maybe due to out of range).\n"
+			"Setting the old value.");
+		lineEdit->setText(sym_get_string_value(sym));
+	}
+
+parent:
+	QStyledItemDelegate::setModelData(editor, model, index);
+}
+
+ConfigList::ConfigList(QWidget *parent, const char *name)
+	: QTreeWidget(parent),
+	  updateAll(false),
+	  showName(false), mode(singleMode), optMode(normalOpt),
+	  rootEntry(0), headerPopup(0)
+{
+	setObjectName(name);
+	setSortingEnabled(false);
+	setRootIsDecorated(true);
+
+	setVerticalScrollMode(ScrollPerPixel);
+	setHorizontalScrollMode(ScrollPerPixel);
+
+	setHeaderLabels(QStringList() << "Option" << "Name" << "Value");
+
+	connect(this, &ConfigList::itemSelectionChanged,
+		this, &ConfigList::updateSelection);
+
+	if (name) {
+		configSettings->beginGroup(name);
+		showName = configSettings->value("/showName", false).toBool();
+		optMode = (enum optionMode)configSettings->value("/optionMode", 0).toInt();
+		configSettings->endGroup();
+		connect(configApp, &QApplication::aboutToQuit,
+			this, &ConfigList::saveSettings);
+	}
+
+	showColumn(promptColIdx);
+
+	setItemDelegate(new ConfigItemDelegate(this));
+
+	allLists.append(this);
+
+	reinit();
+}
+
+ConfigList::~ConfigList()
+{
+	allLists.removeOne(this);
+}
+
+bool ConfigList::menuSkip(struct menu *menu)
+{
+	if (optMode == normalOpt && menu_is_visible(menu))
+		return false;
+	if (optMode == promptOpt && menu_has_prompt(menu))
+		return false;
+	if (optMode == allOpt)
+		return false;
+	return true;
+}
+
+void ConfigList::reinit(void)
+{
+	hideColumn(nameColIdx);
+
+	if (showName)
+		showColumn(nameColIdx);
+
+	updateListAll();
+}
+
+void ConfigList::setOptionMode(QAction *action)
+{
+	if (action == showNormalAction)
+		optMode = normalOpt;
+	else if (action == showAllAction)
+		optMode = allOpt;
+	else
+		optMode = promptOpt;
+
+	updateListAll();
+}
+
+void ConfigList::saveSettings(void)
+{
+	if (!objectName().isEmpty()) {
+		configSettings->beginGroup(objectName());
+		configSettings->setValue("/showName", showName);
+		configSettings->setValue("/optionMode", (int)optMode);
+		configSettings->endGroup();
+	}
+}
+
+ConfigItem* ConfigList::findConfigItem(struct menu *menu)
+{
+	ConfigItem* item = (ConfigItem*)menu->data;
+
+	for (; item; item = item->nextItem) {
+		if (this == item->listView())
+			break;
+	}
+
+	return item;
+}
+
+void ConfigList::updateSelection(void)
+{
+	struct menu *menu;
+	enum prop_type type;
+
+	if (selectedItems().count() == 0)
+		return;
+
+	ConfigItem* item = (ConfigItem*)selectedItems().first();
+	if (!item)
+		return;
+
+	menu = item->menu;
+	emit menuChanged(menu);
+	if (!menu)
+		return;
+	type = menu->prompt ? menu->prompt->type : P_UNKNOWN;
+	if (mode == menuMode && type == P_MENU)
+		emit menuSelected(menu);
+}
+
+void ConfigList::updateList()
+{
+	ConfigItem* last = 0;
+	ConfigItem *item;
+
+	if (!rootEntry) {
+		if (mode != listMode)
+			goto update;
+		QTreeWidgetItemIterator it(this);
+
+		while (*it) {
+			item = (ConfigItem*)(*it);
+			if (!item->menu)
+				continue;
+			item->testUpdateMenu(menu_is_visible(item->menu));
+
+			++it;
+		}
+		return;
+	}
+
+	if (rootEntry != &rootmenu && (mode == singleMode ||
+	    (mode == symbolMode && rootEntry->parent != &rootmenu))) {
+		item = (ConfigItem *)topLevelItem(0);
+		if (!item)
+			item = new ConfigItem(this, 0, true);
+		last = item;
+	}
+	if ((mode == singleMode || (mode == symbolMode && !(rootEntry->flags & MENU_ROOT))) &&
+	    rootEntry->sym && rootEntry->prompt) {
+		item = last ? last->nextSibling() : nullptr;
+		if (!item)
+			item = new ConfigItem(this, last, rootEntry, true);
+		else
+			item->testUpdateMenu(true);
+
+		updateMenuList(item, rootEntry);
+		update();
+		resizeColumnToContents(0);
+		return;
+	}
+update:
+	updateMenuList(rootEntry);
+	update();
+	resizeColumnToContents(0);
+}
+
+void ConfigList::updateListForAll()
+{
+	QListIterator<ConfigList *> it(allLists);
+
+	while (it.hasNext()) {
+		ConfigList *list = it.next();
+
+		list->updateList();
+	}
+}
+
+void ConfigList::updateListAllForAll()
+{
+	QListIterator<ConfigList *> it(allLists);
+
+	while (it.hasNext()) {
+		ConfigList *list = it.next();
+
+		list->updateList();
+	}
+}
+
+void ConfigList::setValue(ConfigItem* item, tristate val)
+{
+	struct symbol* sym;
+	int type;
+	tristate oldval;
+
+	sym = item->menu ? item->menu->sym : 0;
+	if (!sym)
+		return;
+
+	type = sym_get_type(sym);
+	switch (type) {
+	case S_BOOLEAN:
+	case S_TRISTATE:
+		oldval = sym_get_tristate_value(sym);
+
+		if (!sym_set_tristate_value(sym, val))
+			return;
+		if (oldval == no && item->menu->list)
+			item->setExpanded(true);
+		ConfigList::updateListForAll();
+		break;
+	}
+}
+
+void ConfigList::changeValue(ConfigItem* item)
+{
+	struct symbol* sym;
+	struct menu* menu;
+	int type, oldexpr, newexpr;
+
+	menu = item->menu;
+	if (!menu)
+		return;
+	sym = menu->sym;
+	if (!sym) {
+		if (item->menu->list)
+			item->setExpanded(!item->isExpanded());
+		return;
+	}
+
+	type = sym_get_type(sym);
+	switch (type) {
+	case S_BOOLEAN:
+	case S_TRISTATE:
+		oldexpr = sym_get_tristate_value(sym);
+		newexpr = sym_toggle_tristate_value(sym);
+		if (item->menu->list) {
+			if (oldexpr == newexpr)
+				item->setExpanded(!item->isExpanded());
+			else if (oldexpr == no)
+				item->setExpanded(true);
+		}
+		if (oldexpr != newexpr)
+			ConfigList::updateListForAll();
+		break;
+	default:
+		break;
+	}
+}
+
+void ConfigList::setRootMenu(struct menu *menu)
+{
+	enum prop_type type;
+
+	if (rootEntry == menu)
+		return;
+	type = menu && menu->prompt ? menu->prompt->type : P_UNKNOWN;
+	if (type != P_MENU)
+		return;
+	updateMenuList(0);
+	rootEntry = menu;
+	updateListAll();
+	if (currentItem()) {
+		setSelected(currentItem(), hasFocus());
+		scrollToItem(currentItem());
+	}
+}
+
+void ConfigList::setParentMenu(void)
+{
+	ConfigItem* item;
+	struct menu *oldroot;
+
+	oldroot = rootEntry;
+	if (rootEntry == &rootmenu)
+		return;
+	setRootMenu(menu_get_parent_menu(rootEntry->parent));
+
+	QTreeWidgetItemIterator it(this);
+	while (*it) {
+		item = (ConfigItem *)(*it);
+		if (item->menu == oldroot) {
+			setCurrentItem(item);
+			scrollToItem(item);
+			break;
+		}
+
+		++it;
+	}
+}
+
+/*
+ * update all the children of a menu entry
+ *   removes/adds the entries from the parent widget as necessary
+ *
+ * parent: either the menu list widget or a menu entry widget
+ * menu: entry to be updated
+ */
+void ConfigList::updateMenuList(ConfigItem *parent, struct menu* menu)
+{
+	struct menu* child;
+	ConfigItem* item;
+	ConfigItem* last;
+	bool visible;
+	enum prop_type type;
+
+	if (!menu) {
+		while (parent->childCount() > 0)
+		{
+			delete parent->takeChild(0);
+		}
+
+		return;
+	}
+
+	last = parent->firstChild();
+	if (last && !last->goParent)
+		last = 0;
+	for (child = menu->list; child; child = child->next) {
+		item = last ? last->nextSibling() : parent->firstChild();
+		type = child->prompt ? child->prompt->type : P_UNKNOWN;
+
+		switch (mode) {
+		case menuMode:
+			if (!(child->flags & MENU_ROOT))
+				goto hide;
+			break;
+		case symbolMode:
+			if (child->flags & MENU_ROOT)
+				goto hide;
+			break;
+		default:
+			break;
+		}
+
+		visible = menu_is_visible(child);
+		if (!menuSkip(child)) {
+			if (!child->sym && !child->list && !child->prompt)
+				continue;
+			if (!item || item->menu != child)
+				item = new ConfigItem(parent, last, child, visible);
+			else
+				item->testUpdateMenu(visible);
+
+			if (mode == fullMode || mode == menuMode || type != P_MENU)
+				updateMenuList(item, child);
+			else
+				updateMenuList(item, 0);
+			last = item;
+			continue;
+		}
+hide:
+		if (item && item->menu == child) {
+			last = parent->firstChild();
+			if (last == item)
+				last = 0;
+			else while (last->nextSibling() != item)
+				last = last->nextSibling();
+			delete item;
+		}
+	}
+}
+
+void ConfigList::updateMenuList(struct menu *menu)
+{
+	struct menu* child;
+	ConfigItem* item;
+	ConfigItem* last;
+	bool visible;
+	enum prop_type type;
+
+	if (!menu) {
+		while (topLevelItemCount() > 0)
+		{
+			delete takeTopLevelItem(0);
+		}
+
+		return;
+	}
+
+	last = (ConfigItem *)topLevelItem(0);
+	if (last && !last->goParent)
+		last = 0;
+	for (child = menu->list; child; child = child->next) {
+		item = last ? last->nextSibling() : (ConfigItem *)topLevelItem(0);
+		type = child->prompt ? child->prompt->type : P_UNKNOWN;
+
+		switch (mode) {
+		case menuMode:
+			if (!(child->flags & MENU_ROOT))
+				goto hide;
+			break;
+		case symbolMode:
+			if (child->flags & MENU_ROOT)
+				goto hide;
+			break;
+		default:
+			break;
+		}
+
+		visible = menu_is_visible(child);
+		if (!menuSkip(child)) {
+			if (!child->sym && !child->list && !child->prompt)
+				continue;
+			if (!item || item->menu != child)
+				item = new ConfigItem(this, last, child, visible);
+			else
+				item->testUpdateMenu(visible);
+
+			if (mode == fullMode || mode == menuMode || type != P_MENU)
+				updateMenuList(item, child);
+			else
+				updateMenuList(item, 0);
+			last = item;
+			continue;
+		}
+hide:
+		if (item && item->menu == child) {
+			last = (ConfigItem *)topLevelItem(0);
+			if (last == item)
+				last = 0;
+			else while (last->nextSibling() != item)
+				last = last->nextSibling();
+			delete item;
+		}
+	}
+}
+
+void ConfigList::keyPressEvent(QKeyEvent* ev)
+{
+	QTreeWidgetItem* i = currentItem();
+	ConfigItem* item;
+	struct menu *menu;
+	enum prop_type type;
+
+	if (ev->key() == Qt::Key_Escape && mode != fullMode && mode != listMode) {
+		emit parentSelected();
+		ev->accept();
+		return;
+	}
+
+	if (!i) {
+		Parent::keyPressEvent(ev);
+		return;
+	}
+	item = (ConfigItem*)i;
+
+	switch (ev->key()) {
+	case Qt::Key_Return:
+	case Qt::Key_Enter:
+		if (item->goParent) {
+			emit parentSelected();
+			break;
+		}
+		menu = item->menu;
+		if (!menu)
+			break;
+		type = menu->prompt ? menu->prompt->type : P_UNKNOWN;
+		if (type == P_MENU && rootEntry != menu &&
+		    mode != fullMode && mode != menuMode) {
+			if (mode == menuMode)
+				emit menuSelected(menu);
+			else
+				emit itemSelected(menu);
+			break;
+		}
+	case Qt::Key_Space:
+		changeValue(item);
+		break;
+	case Qt::Key_N:
+		setValue(item, no);
+		break;
+	case Qt::Key_M:
+		setValue(item, mod);
+		break;
+	case Qt::Key_Y:
+		setValue(item, yes);
+		break;
+	default:
+		Parent::keyPressEvent(ev);
+		return;
+	}
+	ev->accept();
+}
+
+void ConfigList::mousePressEvent(QMouseEvent* e)
+{
+	//QPoint p(contentsToViewport(e->pos()));
+	//printf("contentsMousePressEvent: %d,%d\n", p.x(), p.y());
+	Parent::mousePressEvent(e);
+}
+
+void ConfigList::mouseReleaseEvent(QMouseEvent* e)
+{
+	QPoint p = e->pos();
+	ConfigItem* item = (ConfigItem*)itemAt(p);
+	struct menu *menu;
+	enum prop_type ptype;
+	QIcon icon;
+	int idx, x;
+
+	if (!item)
+		goto skip;
+
+	menu = item->menu;
+	x = header()->offset() + p.x();
+	idx = header()->logicalIndexAt(x);
+	switch (idx) {
+	case promptColIdx:
+		icon = item->icon(promptColIdx);
+		if (!icon.isNull()) {
+			int off = header()->sectionPosition(0) + visualRect(indexAt(p)).x() + 4; // 4 is Hardcoded image offset. There might be a way to do it properly.
+			if (x >= off && x < off + icon.availableSizes().first().width()) {
+				if (item->goParent) {
+					emit parentSelected();
+					break;
+				} else if (!menu)
+					break;
+				ptype = menu->prompt ? menu->prompt->type : P_UNKNOWN;
+				if (ptype == P_MENU && rootEntry != menu &&
+				    mode != fullMode && mode != menuMode &&
+                                    mode != listMode)
+					emit menuSelected(menu);
+				else
+					changeValue(item);
+			}
+		}
+		break;
+	case dataColIdx:
+		changeValue(item);
+		break;
+	}
+
+skip:
+	//printf("contentsMouseReleaseEvent: %d,%d\n", p.x(), p.y());
+	Parent::mouseReleaseEvent(e);
+}
+
+void ConfigList::mouseMoveEvent(QMouseEvent* e)
+{
+	//QPoint p(contentsToViewport(e->pos()));
+	//printf("contentsMouseMoveEvent: %d,%d\n", p.x(), p.y());
+	Parent::mouseMoveEvent(e);
+}
+
+void ConfigList::mouseDoubleClickEvent(QMouseEvent* e)
+{
+	QPoint p = e->pos();
+	ConfigItem* item = (ConfigItem*)itemAt(p);
+	struct menu *menu;
+	enum prop_type ptype;
+
+	if (!item)
+		goto skip;
+	if (item->goParent) {
+		emit parentSelected();
+		goto skip;
+	}
+	menu = item->menu;
+	if (!menu)
+		goto skip;
+	ptype = menu->prompt ? menu->prompt->type : P_UNKNOWN;
+	if (ptype == P_MENU && mode != listMode) {
+		if (mode == singleMode)
+			emit itemSelected(menu);
+		else if (mode == symbolMode)
+			emit menuSelected(menu);
+	} else if (menu->sym)
+		changeValue(item);
+
+skip:
+	//printf("contentsMouseDoubleClickEvent: %d,%d\n", p.x(), p.y());
+	Parent::mouseDoubleClickEvent(e);
+}
+
+void ConfigList::focusInEvent(QFocusEvent *e)
+{
+	struct menu *menu = NULL;
+
+	Parent::focusInEvent(e);
+
+	ConfigItem* item = (ConfigItem *)currentItem();
+	if (item) {
+		setSelected(item, true);
+		menu = item->menu;
+	}
+	emit gotFocus(menu);
+}
+
+void ConfigList::contextMenuEvent(QContextMenuEvent *e)
+{
+	if (!headerPopup) {
+		QAction *action;
+
+		headerPopup = new QMenu(this);
+		action = new QAction("Show Name", this);
+		action->setCheckable(true);
+		connect(action, &QAction::toggled,
+			this, &ConfigList::setShowName);
+		connect(this, &ConfigList::showNameChanged,
+			action, &QAction::setChecked);
+		action->setChecked(showName);
+		headerPopup->addAction(action);
+	}
+
+	headerPopup->exec(e->globalPos());
+	e->accept();
+}
+
+void ConfigList::setShowName(bool on)
+{
+	if (showName == on)
+		return;
+
+	showName = on;
+	reinit();
+	emit showNameChanged(on);
+}
+
+QList<ConfigList *> ConfigList::allLists;
+QAction *ConfigList::showNormalAction;
+QAction *ConfigList::showAllAction;
+QAction *ConfigList::showPromptAction;
+
+void ConfigList::setAllOpen(bool open)
+{
+	QTreeWidgetItemIterator it(this);
+
+	while (*it) {
+		(*it)->setExpanded(open);
+
+		++it;
+	}
+}
+
+ConfigInfoView::ConfigInfoView(QWidget* parent, const char *name)
+	: Parent(parent), sym(0), _menu(0)
+{
+	setObjectName(name);
+	setOpenLinks(false);
+
+	if (!objectName().isEmpty()) {
+		configSettings->beginGroup(objectName());
+		setShowDebug(configSettings->value("/showDebug", false).toBool());
+		configSettings->endGroup();
+		connect(configApp, &QApplication::aboutToQuit,
+			this, &ConfigInfoView::saveSettings);
+	}
+
+	contextMenu = createStandardContextMenu();
+	QAction *action = new QAction("Show Debug Info", contextMenu);
+
+	action->setCheckable(true);
+	connect(action, &QAction::toggled,
+		this, &ConfigInfoView::setShowDebug);
+	connect(this, &ConfigInfoView::showDebugChanged,
+		action, &QAction::setChecked);
+	action->setChecked(showDebug());
+	contextMenu->addSeparator();
+	contextMenu->addAction(action);
+}
+
+void ConfigInfoView::saveSettings(void)
+{
+	if (!objectName().isEmpty()) {
+		configSettings->beginGroup(objectName());
+		configSettings->setValue("/showDebug", showDebug());
+		configSettings->endGroup();
+	}
+}
+
+void ConfigInfoView::setShowDebug(bool b)
+{
+	if (_showDebug != b) {
+		_showDebug = b;
+		if (_menu)
+			menuInfo();
+		else if (sym)
+			symbolInfo();
+		emit showDebugChanged(b);
+	}
+}
+
+void ConfigInfoView::setInfo(struct menu *m)
+{
+	if (_menu == m)
+		return;
+	_menu = m;
+	sym = NULL;
+	if (!_menu)
+		clear();
+	else
+		menuInfo();
+}
+
+void ConfigInfoView::symbolInfo(void)
+{
+	QString str;
+
+	str += "<big>Symbol: <b>";
+	str += print_filter(sym->name);
+	str += "</b></big><br><br>value: ";
+	str += print_filter(sym_get_string_value(sym));
+	str += "<br>visibility: ";
+	str += sym->visible == yes ? "y" : sym->visible == mod ? "m" : "n";
+	str += "<br>";
+	str += debug_info(sym);
+
+	setText(str);
+}
+
+void ConfigInfoView::menuInfo(void)
+{
+	struct symbol* sym;
+	QString info;
+	QTextStream stream(&info);
+
+	sym = _menu->sym;
+	if (sym) {
+		if (_menu->prompt) {
+			stream << "<big><b>";
+			stream << print_filter(_menu->prompt->text);
+			stream << "</b></big>";
+			if (sym->name) {
+				stream << " (";
+				if (showDebug())
+					stream << "<a href=\"s" << sym->name << "\">";
+				stream << print_filter(sym->name);
+				if (showDebug())
+					stream << "</a>";
+				stream << ")";
+			}
+		} else if (sym->name) {
+			stream << "<big><b>";
+			if (showDebug())
+				stream << "<a href=\"s" << sym->name << "\">";
+			stream << print_filter(sym->name);
+			if (showDebug())
+				stream << "</a>";
+			stream << "</b></big>";
+		}
+		stream << "<br><br>";
+
+		if (showDebug())
+			stream << debug_info(sym);
+
+		struct gstr help_gstr = str_new();
+
+		menu_get_ext_help(_menu, &help_gstr);
+		stream << print_filter(str_get(&help_gstr));
+		str_free(&help_gstr);
+	} else if (_menu->prompt) {
+		stream << "<big><b>";
+		stream << print_filter(_menu->prompt->text);
+		stream << "</b></big><br><br>";
+		if (showDebug()) {
+			if (_menu->prompt->visible.expr) {
+				stream << "&nbsp;&nbsp;dep: ";
+				expr_print(_menu->prompt->visible.expr,
+					   expr_print_help, &stream, E_NONE);
+				stream << "<br><br>";
+			}
+
+			stream << "defined at " << _menu->file->name << ":"
+			       << _menu->lineno << "<br><br>";
+		}
+	}
+
+	setText(info);
+}
+
+QString ConfigInfoView::debug_info(struct symbol *sym)
+{
+	QString debug;
+	QTextStream stream(&debug);
+
+	stream << "type: ";
+	stream << print_filter(sym_type_name(sym->type));
+	if (sym_is_choice(sym))
+		stream << " (choice)";
+	debug += "<br>";
+	if (sym->rev_dep.expr) {
+		stream << "reverse dep: ";
+		expr_print(sym->rev_dep.expr, expr_print_help, &stream, E_NONE);
+		stream << "<br>";
+	}
+	for (struct property *prop = sym->prop; prop; prop = prop->next) {
+		switch (prop->type) {
+		case P_PROMPT:
+		case P_MENU:
+			stream << "prompt: <a href=\"m" << sym->name << "\">";
+			stream << print_filter(prop->text);
+			stream << "</a><br>";
+			break;
+		case P_DEFAULT:
+		case P_SELECT:
+		case P_RANGE:
+		case P_COMMENT:
+		case P_IMPLY:
+		case P_SYMBOL:
+			stream << prop_get_type_name(prop->type);
+			stream << ": ";
+			expr_print(prop->expr, expr_print_help,
+				   &stream, E_NONE);
+			stream << "<br>";
+			break;
+		case P_CHOICE:
+			if (sym_is_choice(sym)) {
+				stream << "choice: ";
+				expr_print(prop->expr, expr_print_help,
+					   &stream, E_NONE);
+				stream << "<br>";
+			}
+			break;
+		default:
+			stream << "unknown property: ";
+			stream << prop_get_type_name(prop->type);
+			stream << "<br>";
+		}
+		if (prop->visible.expr) {
+			stream << "&nbsp;&nbsp;&nbsp;&nbsp;dep: ";
+			expr_print(prop->visible.expr, expr_print_help,
+				   &stream, E_NONE);
+			stream << "<br>";
+		}
+	}
+	stream << "<br>";
+
+	return debug;
+}
+
+QString ConfigInfoView::print_filter(const QString &str)
+{
+	QRegularExpression re("[<>&\"\\n]");
+	QString res = str;
+	for (int i = 0; (i = res.indexOf(re, i)) >= 0;) {
+		switch (res[i].toLatin1()) {
+		case '<':
+			res.replace(i, 1, "&lt;");
+			i += 4;
+			break;
+		case '>':
+			res.replace(i, 1, "&gt;");
+			i += 4;
+			break;
+		case '&':
+			res.replace(i, 1, "&amp;");
+			i += 5;
+			break;
+		case '"':
+			res.replace(i, 1, "&quot;");
+			i += 6;
+			break;
+		case '\n':
+			res.replace(i, 1, "<br>");
+			i += 4;
+			break;
+		}
+	}
+	return res;
+}
+
+void ConfigInfoView::expr_print_help(void *data, struct symbol *sym, const char *str)
+{
+	QTextStream *stream = reinterpret_cast<QTextStream *>(data);
+
+	if (sym && sym->name && !(sym->flags & SYMBOL_CONST)) {
+		*stream << "<a href=\"s" << sym->name << "\">";
+		*stream << print_filter(str);
+		*stream << "</a>";
+	} else {
+		*stream << print_filter(str);
+	}
+}
+
+void ConfigInfoView::clicked(const QUrl &url)
+{
+	QByteArray str = url.toEncoded();
+	const std::size_t count = str.size();
+	char *data = new char[count + 1];
+	struct symbol **result;
+	struct menu *m = NULL;
+
+	if (count < 1) {
+		delete[] data;
+		return;
+	}
+
+	memcpy(data, str.constData(), count);
+	data[count] = '\0';
+
+	/* Seek for exact match */
+	data[0] = '^';
+	strcat(data, "$");
+	result = sym_re_search(data);
+	if (!result) {
+		delete[] data;
+		return;
+	}
+
+	sym = *result;
+
+	/* Seek for the menu which holds the symbol */
+	for (struct property *prop = sym->prop; prop; prop = prop->next) {
+		    if (prop->type != P_PROMPT && prop->type != P_MENU)
+			    continue;
+		    m = prop->menu;
+		    break;
+	}
+
+	if (!m) {
+		/* Symbol is not visible as a menu */
+		symbolInfo();
+		emit showDebugChanged(true);
+	} else {
+		emit menuSelected(m);
+	}
+
+	free(result);
+	delete[] data;
+}
+
+void ConfigInfoView::contextMenuEvent(QContextMenuEvent *event)
+{
+	contextMenu->popup(event->globalPos());
+	event->accept();
+}
+
+ConfigSearchWindow::ConfigSearchWindow(ConfigMainWindow *parent)
+	: Parent(parent), result(NULL)
+{
+	setObjectName("search");
+	setWindowTitle("Search Config");
+
+	QVBoxLayout* layout1 = new QVBoxLayout(this);
+	layout1->setContentsMargins(11, 11, 11, 11);
+	layout1->setSpacing(6);
+
+	QHBoxLayout* layout2 = new QHBoxLayout();
+	layout2->setContentsMargins(0, 0, 0, 0);
+	layout2->setSpacing(6);
+	layout2->addWidget(new QLabel("Find:", this));
+	editField = new QLineEdit(this);
+	connect(editField, &QLineEdit::returnPressed,
+		this, &ConfigSearchWindow::search);
+	layout2->addWidget(editField);
+	searchButton = new QPushButton("Search", this);
+	searchButton->setAutoDefault(false);
+	connect(searchButton, &QPushButton::clicked,
+		this, &ConfigSearchWindow::search);
+	layout2->addWidget(searchButton);
+	layout1->addLayout(layout2);
+
+	split = new QSplitter(this);
+	split->setOrientation(Qt::Vertical);
+	list = new ConfigList(split, "search");
+	list->mode = listMode;
+	info = new ConfigInfoView(split, "search");
+	connect(list, &ConfigList::menuChanged,
+		info, &ConfigInfoView::setInfo);
+	connect(list, &ConfigList::menuChanged,
+		parent, &ConfigMainWindow::setMenuLink);
+
+	layout1->addWidget(split);
+
+	QVariant x, y;
+	int width, height;
+	bool ok;
+
+	configSettings->beginGroup("search");
+	width = configSettings->value("/window width", parent->width() / 2).toInt();
+	height = configSettings->value("/window height", parent->height() / 2).toInt();
+	resize(width, height);
+	x = configSettings->value("/window x");
+	y = configSettings->value("/window y");
+	if (x.isValid() && y.isValid())
+		move(x.toInt(), y.toInt());
+	QList<int> sizes = configSettings->readSizes("/split", &ok);
+	if (ok)
+		split->setSizes(sizes);
+	configSettings->endGroup();
+	connect(configApp, &QApplication::aboutToQuit,
+		this, &ConfigSearchWindow::saveSettings);
+}
+
+void ConfigSearchWindow::saveSettings(void)
+{
+	if (!objectName().isEmpty()) {
+		configSettings->beginGroup(objectName());
+		configSettings->setValue("/window x", pos().x());
+		configSettings->setValue("/window y", pos().y());
+		configSettings->setValue("/window width", size().width());
+		configSettings->setValue("/window height", size().height());
+		configSettings->writeSizes("/split", split->sizes());
+		configSettings->endGroup();
+	}
+}
+
+void ConfigSearchWindow::search(void)
+{
+	struct symbol **p;
+	struct property *prop;
+	ConfigItem *lastItem = NULL;
+
+	free(result);
+	list->clear();
+	info->clear();
+
+	result = sym_re_search(editField->text().toLatin1());
+	if (!result)
+		return;
+	for (p = result; *p; p++) {
+		for_all_prompts((*p), prop)
+			lastItem = new ConfigItem(list, lastItem, prop->menu,
+						  menu_is_visible(prop->menu));
+	}
+}
+
+/*
+ * Construct the complete config widget
+ */
+ConfigMainWindow::ConfigMainWindow(void)
+	: searchWindow(0)
+{
+	bool ok = true;
+	QVariant x, y;
+	int width, height;
+	char title[256];
+
+	snprintf(title, sizeof(title), "%s%s",
+		rootmenu.prompt->text,
+		""
+		);
+	setWindowTitle(title);
+
+	QRect g = configApp->primaryScreen()->geometry();
+	width = configSettings->value("/window width", g.width() - 64).toInt();
+	height = configSettings->value("/window height", g.height() - 64).toInt();
+	resize(width, height);
+	x = configSettings->value("/window x");
+	y = configSettings->value("/window y");
+	if ((x.isValid())&&(y.isValid()))
+		move(x.toInt(), y.toInt());
+
+	// set up icons
+	ConfigItem::symbolYesIcon = QIcon(QPixmap(xpm_symbol_yes));
+	ConfigItem::symbolModIcon = QIcon(QPixmap(xpm_symbol_mod));
+	ConfigItem::symbolNoIcon = QIcon(QPixmap(xpm_symbol_no));
+	ConfigItem::choiceYesIcon = QIcon(QPixmap(xpm_choice_yes));
+	ConfigItem::choiceNoIcon = QIcon(QPixmap(xpm_choice_no));
+	ConfigItem::menuIcon = QIcon(QPixmap(xpm_menu));
+	ConfigItem::menubackIcon = QIcon(QPixmap(xpm_menuback));
+
+	QWidget *widget = new QWidget(this);
+	QVBoxLayout *layout = new QVBoxLayout(widget);
+	setCentralWidget(widget);
+
+	split1 = new QSplitter(widget);
+	split1->setOrientation(Qt::Horizontal);
+	split1->setChildrenCollapsible(false);
+
+	menuList = new ConfigList(widget, "menu");
+
+	split2 = new QSplitter(widget);
+	split2->setChildrenCollapsible(false);
+	split2->setOrientation(Qt::Vertical);
+
+	// create config tree
+	configList = new ConfigList(widget, "config");
+
+	helpText = new ConfigInfoView(widget, "help");
+
+	layout->addWidget(split2);
+	split2->addWidget(split1);
+	split1->addWidget(configList);
+	split1->addWidget(menuList);
+	split2->addWidget(helpText);
+
+	setTabOrder(configList, helpText);
+	configList->setFocus();
+
+	backAction = new QAction(QPixmap(xpm_back), "Back", this);
+	connect(backAction, &QAction::triggered,
+		this, &ConfigMainWindow::goBack);
+
+	QAction *quitAction = new QAction("&Quit", this);
+	quitAction->setShortcut(Qt::CTRL | Qt::Key_Q);
+	connect(quitAction, &QAction::triggered,
+		this, &ConfigMainWindow::close);
+
+	QAction *loadAction = new QAction(QPixmap(xpm_load), "&Load", this);
+	loadAction->setShortcut(Qt::CTRL | Qt::Key_L);
+	connect(loadAction, &QAction::triggered,
+		this, &ConfigMainWindow::loadConfig);
+
+	saveAction = new QAction(QPixmap(xpm_save), "&Save", this);
+	saveAction->setShortcut(Qt::CTRL | Qt::Key_S);
+	connect(saveAction, &QAction::triggered,
+		this, &ConfigMainWindow::saveConfig);
+
+	conf_set_changed_callback(conf_changed);
+
+	// Set saveAction's initial state
+	conf_changed();
+	configname = xstrdup(conf_get_configname());
+
+	QAction *saveAsAction = new QAction("Save &As...", this);
+	connect(saveAsAction, &QAction::triggered,
+		this, &ConfigMainWindow::saveConfigAs);
+	QAction *searchAction = new QAction("&Find", this);
+	searchAction->setShortcut(Qt::CTRL | Qt::Key_F);
+	connect(searchAction, &QAction::triggered,
+		this, &ConfigMainWindow::searchConfig);
+	singleViewAction = new QAction(QPixmap(xpm_single_view), "Single View", this);
+	singleViewAction->setCheckable(true);
+	connect(singleViewAction, &QAction::triggered,
+		this, &ConfigMainWindow::showSingleView);
+	splitViewAction = new QAction(QPixmap(xpm_split_view), "Split View", this);
+	splitViewAction->setCheckable(true);
+	connect(splitViewAction, &QAction::triggered,
+		this, &ConfigMainWindow::showSplitView);
+	fullViewAction = new QAction(QPixmap(xpm_tree_view), "Full View", this);
+	fullViewAction->setCheckable(true);
+	connect(fullViewAction, &QAction::triggered,
+		this, &ConfigMainWindow::showFullView);
+
+	QAction *showNameAction = new QAction("Show Name", this);
+	  showNameAction->setCheckable(true);
+	connect(showNameAction, &QAction::toggled,
+		configList, &ConfigList::setShowName);
+	showNameAction->setChecked(configList->showName);
+
+	QActionGroup *optGroup = new QActionGroup(this);
+	optGroup->setExclusive(true);
+	connect(optGroup, &QActionGroup::triggered,
+		configList, &ConfigList::setOptionMode);
+	connect(optGroup, &QActionGroup::triggered,
+		menuList, &ConfigList::setOptionMode);
+
+	ConfigList::showNormalAction = new QAction("Show Normal Options", optGroup);
+	ConfigList::showNormalAction->setCheckable(true);
+	ConfigList::showAllAction = new QAction("Show All Options", optGroup);
+	ConfigList::showAllAction->setCheckable(true);
+	ConfigList::showPromptAction = new QAction("Show Prompt Options", optGroup);
+	ConfigList::showPromptAction->setCheckable(true);
+
+	QAction *showDebugAction = new QAction("Show Debug Info", this);
+	  showDebugAction->setCheckable(true);
+	connect(showDebugAction, &QAction::toggled,
+		helpText, &ConfigInfoView::setShowDebug);
+	  showDebugAction->setChecked(helpText->showDebug());
+
+	QAction *showIntroAction = new QAction("Introduction", this);
+	connect(showIntroAction, &QAction::triggered,
+		this, &ConfigMainWindow::showIntro);
+	QAction *showAboutAction = new QAction("About", this);
+	connect(showAboutAction, &QAction::triggered,
+		this, &ConfigMainWindow::showAbout);
+
+	// init tool bar
+	QToolBar *toolBar = addToolBar("Tools");
+	toolBar->addAction(backAction);
+	toolBar->addSeparator();
+	toolBar->addAction(loadAction);
+	toolBar->addAction(saveAction);
+	toolBar->addSeparator();
+	toolBar->addAction(singleViewAction);
+	toolBar->addAction(splitViewAction);
+	toolBar->addAction(fullViewAction);
+
+	// create file menu
+	QMenu *menu = menuBar()->addMenu("&File");
+	menu->addAction(loadAction);
+	menu->addAction(saveAction);
+	menu->addAction(saveAsAction);
+	menu->addSeparator();
+	menu->addAction(quitAction);
+
+	// create edit menu
+	menu = menuBar()->addMenu("&Edit");
+	menu->addAction(searchAction);
+
+	// create options menu
+	menu = menuBar()->addMenu("&Option");
+	menu->addAction(showNameAction);
+	menu->addSeparator();
+	menu->addActions(optGroup->actions());
+	menu->addSeparator();
+	menu->addAction(showDebugAction);
+
+	// create help menu
+	menu = menuBar()->addMenu("&Help");
+	menu->addAction(showIntroAction);
+	menu->addAction(showAboutAction);
+
+	connect(helpText, &ConfigInfoView::anchorClicked,
+		helpText, &ConfigInfoView::clicked);
+
+	connect(configList, &ConfigList::menuChanged,
+		helpText, &ConfigInfoView::setInfo);
+	connect(configList, &ConfigList::menuSelected,
+		this, &ConfigMainWindow::changeMenu);
+	connect(configList, &ConfigList::itemSelected,
+		this, &ConfigMainWindow::changeItens);
+	connect(configList, &ConfigList::parentSelected,
+		this, &ConfigMainWindow::goBack);
+	connect(menuList, &ConfigList::menuChanged,
+		helpText, &ConfigInfoView::setInfo);
+	connect(menuList, &ConfigList::menuSelected,
+		this, &ConfigMainWindow::changeMenu);
+
+	connect(configList, &ConfigList::gotFocus,
+		helpText, &ConfigInfoView::setInfo);
+	connect(menuList, &ConfigList::gotFocus,
+		helpText, &ConfigInfoView::setInfo);
+	connect(menuList, &ConfigList::gotFocus,
+		this, &ConfigMainWindow::listFocusChanged);
+	connect(helpText, &ConfigInfoView::menuSelected,
+		this, &ConfigMainWindow::setMenuLink);
+
+	QString listMode = configSettings->value("/listMode", "symbol").toString();
+	if (listMode == "single")
+		showSingleView();
+	else if (listMode == "full")
+		showFullView();
+	else /*if (listMode == "split")*/
+		showSplitView();
+
+	// UI setup done, restore splitter positions
+	QList<int> sizes = configSettings->readSizes("/split1", &ok);
+	if (ok)
+		split1->setSizes(sizes);
+
+	sizes = configSettings->readSizes("/split2", &ok);
+	if (ok)
+		split2->setSizes(sizes);
+}
+
+void ConfigMainWindow::loadConfig(void)
+{
+	QString str;
+	QByteArray ba;
+	const char *name;
+
+	str = QFileDialog::getOpenFileName(this, "", configname);
+	if (str.isNull())
+		return;
+
+	ba = str.toLocal8Bit();
+	name = ba.data();
+
+	if (conf_read(name))
+		QMessageBox::information(this, "qconf", "Unable to load configuration!");
+
+	free(configname);
+	configname = xstrdup(name);
+
+	ConfigList::updateListAllForAll();
+}
+
+bool ConfigMainWindow::saveConfig(void)
+{
+	if (conf_write(configname)) {
+		QMessageBox::information(this, "qconf", "Unable to save configuration!");
+		return false;
+	}
+	conf_write_autoconf(0);
+
+	return true;
+}
+
+void ConfigMainWindow::saveConfigAs(void)
+{
+	QString str;
+	QByteArray ba;
+	const char *name;
+
+	str = QFileDialog::getSaveFileName(this, "", configname);
+	if (str.isNull())
+		return;
+
+	ba = str.toLocal8Bit();
+	name = ba.data();
+
+	if (conf_write(name)) {
+		QMessageBox::information(this, "qconf", "Unable to save configuration!");
+	}
+	conf_write_autoconf(0);
+
+	free(configname);
+	configname = xstrdup(name);
+}
+
+void ConfigMainWindow::searchConfig(void)
+{
+	if (!searchWindow)
+		searchWindow = new ConfigSearchWindow(this);
+	searchWindow->show();
+}
+
+void ConfigMainWindow::changeItens(struct menu *menu)
+{
+	configList->setRootMenu(menu);
+}
+
+void ConfigMainWindow::changeMenu(struct menu *menu)
+{
+	menuList->setRootMenu(menu);
+}
+
+void ConfigMainWindow::setMenuLink(struct menu *menu)
+{
+	struct menu *parent;
+	ConfigList* list = NULL;
+	ConfigItem* item;
+
+	if (configList->menuSkip(menu))
+		return;
+
+	switch (configList->mode) {
+	case singleMode:
+		list = configList;
+		parent = menu_get_parent_menu(menu);
+		if (!parent)
+			return;
+		list->setRootMenu(parent);
+		break;
+	case menuMode:
+		if (menu->flags & MENU_ROOT) {
+			menuList->setRootMenu(menu);
+			configList->clearSelection();
+			list = configList;
+		} else {
+			parent = menu_get_parent_menu(menu->parent);
+			if (!parent)
+				return;
+
+			/* Select the config view */
+			item = configList->findConfigItem(parent);
+			if (item) {
+				configList->setSelected(item, true);
+				configList->scrollToItem(item);
+			}
+
+			menuList->setRootMenu(parent);
+			menuList->clearSelection();
+			list = menuList;
+		}
+		break;
+	case fullMode:
+		list = configList;
+		break;
+	default:
+		break;
+	}
+
+	if (list) {
+		item = list->findConfigItem(menu);
+		if (item) {
+			list->setSelected(item, true);
+			list->scrollToItem(item);
+			list->setFocus();
+			helpText->setInfo(menu);
+		}
+	}
+}
+
+void ConfigMainWindow::listFocusChanged(void)
+{
+	if (menuList->mode == menuMode)
+		configList->clearSelection();
+}
+
+void ConfigMainWindow::goBack(void)
+{
+	if (configList->rootEntry == &rootmenu)
+		return;
+
+	configList->setParentMenu();
+}
+
+void ConfigMainWindow::showSingleView(void)
+{
+	singleViewAction->setEnabled(false);
+	singleViewAction->setChecked(true);
+	splitViewAction->setEnabled(true);
+	splitViewAction->setChecked(false);
+	fullViewAction->setEnabled(true);
+	fullViewAction->setChecked(false);
+
+	backAction->setEnabled(true);
+
+	menuList->hide();
+	menuList->setRootMenu(0);
+	configList->mode = singleMode;
+	if (configList->rootEntry == &rootmenu)
+		configList->updateListAll();
+	else
+		configList->setRootMenu(&rootmenu);
+	configList->setFocus();
+}
+
+void ConfigMainWindow::showSplitView(void)
+{
+	singleViewAction->setEnabled(true);
+	singleViewAction->setChecked(false);
+	splitViewAction->setEnabled(false);
+	splitViewAction->setChecked(true);
+	fullViewAction->setEnabled(true);
+	fullViewAction->setChecked(false);
+
+	backAction->setEnabled(false);
+
+	configList->mode = menuMode;
+	if (configList->rootEntry == &rootmenu)
+		configList->updateListAll();
+	else
+		configList->setRootMenu(&rootmenu);
+	configList->setAllOpen(true);
+	configApp->processEvents();
+	menuList->mode = symbolMode;
+	menuList->setRootMenu(&rootmenu);
+	menuList->setAllOpen(true);
+	menuList->show();
+	menuList->setFocus();
+}
+
+void ConfigMainWindow::showFullView(void)
+{
+	singleViewAction->setEnabled(true);
+	singleViewAction->setChecked(false);
+	splitViewAction->setEnabled(true);
+	splitViewAction->setChecked(false);
+	fullViewAction->setEnabled(false);
+	fullViewAction->setChecked(true);
+
+	backAction->setEnabled(false);
+
+	menuList->hide();
+	menuList->setRootMenu(0);
+	configList->mode = fullMode;
+	if (configList->rootEntry == &rootmenu)
+		configList->updateListAll();
+	else
+		configList->setRootMenu(&rootmenu);
+	configList->setFocus();
+}
+
+/*
+ * ask for saving configuration before quitting
+ */
+void ConfigMainWindow::closeEvent(QCloseEvent* e)
+{
+	if (!conf_get_changed()) {
+		e->accept();
+		return;
+	}
+
+	QMessageBox mb(QMessageBox::Icon::Warning, "qconf",
+		       "Save configuration?");
+
+	QPushButton *yb = mb.addButton(QMessageBox::Yes);
+	QPushButton *db = mb.addButton(QMessageBox::No);
+	QPushButton *cb = mb.addButton(QMessageBox::Cancel);
+
+	yb->setText("&Save Changes");
+	db->setText("&Discard Changes");
+	cb->setText("Cancel Exit");
+
+	mb.setDefaultButton(yb);
+	mb.setEscapeButton(cb);
+
+	switch (mb.exec()) {
+	case QMessageBox::Yes:
+		if (saveConfig())
+			e->accept();
+		else
+			e->ignore();
+		break;
+	case QMessageBox::No:
+		e->accept();
+		break;
+	case QMessageBox::Cancel:
+		e->ignore();
+		break;
+	}
+}
+
+void ConfigMainWindow::showIntro(void)
+{
+	static const QString str =
+		"Welcome to the qconf graphical configuration tool.\n"
+		"\n"
+		"For bool and tristate options, a blank box indicates the "
+		"feature is disabled, a check indicates it is enabled, and a "
+		"dot indicates that it is to be compiled as a module. Clicking "
+		"on the box will cycle through the three states. For int, hex, "
+		"and string options, double-clicking or pressing F2 on the "
+		"Value cell will allow you to edit the value.\n"
+		"\n"
+		"If you do not see an option (e.g., a device driver) that you "
+		"believe should be present, try turning on Show All Options "
+		"under the Options menu. Enabling Show Debug Info will help you"
+		"figure out what other options must be enabled to support the "
+		"option you are interested in, and hyperlinks will navigate to "
+		"them.\n"
+		"\n"
+		"Toggling Show Debug Info under the Options menu will show the "
+		"dependencies, which you can then match by examining other "
+		"options.\n";
+
+	QMessageBox::information(this, "qconf", str);
+}
+
+void ConfigMainWindow::showAbout(void)
+{
+	static const QString str = "qconf is Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>.\n"
+		"Copyright (C) 2015 Boris Barbulovski <bbarbulovski@gmail.com>.\n"
+		"\n"
+		"Bug reports and feature request can also be entered at http://bugzilla.kernel.org/\n"
+		"\n"
+		"Qt Version: ";
+
+	QMessageBox::information(this, "qconf", str + qVersion());
+}
+
+void ConfigMainWindow::saveSettings(void)
+{
+	configSettings->setValue("/window x", pos().x());
+	configSettings->setValue("/window y", pos().y());
+	configSettings->setValue("/window width", size().width());
+	configSettings->setValue("/window height", size().height());
+
+	QString entry;
+	switch(configList->mode) {
+	case singleMode :
+		entry = "single";
+		break;
+
+	case symbolMode :
+		entry = "split";
+		break;
+
+	case fullMode :
+		entry = "full";
+		break;
+
+	default:
+		break;
+	}
+	configSettings->setValue("/listMode", entry);
+
+	configSettings->writeSizes("/split1", split1->sizes());
+	configSettings->writeSizes("/split2", split2->sizes());
+}
+
+void ConfigMainWindow::conf_changed(void)
+{
+	if (saveAction)
+		saveAction->setEnabled(conf_get_changed());
+}
+
+void fixup_rootmenu(struct menu *menu)
+{
+	struct menu *child;
+	static int menu_cnt = 0;
+
+	menu->flags |= MENU_ROOT;
+	for (child = menu->list; child; child = child->next) {
+		if (child->prompt && child->prompt->type == P_MENU) {
+			menu_cnt++;
+			fixup_rootmenu(child);
+			menu_cnt--;
+		} else if (!menu_cnt)
+			fixup_rootmenu(child);
+	}
+}
+
+static const char *progname;
+
+static void usage(void)
+{
+	printf("%s [-s] <config>\n", progname);
+	exit(0);
+}
+
+int main(int ac, char** av)
+{
+	ConfigMainWindow* v;
+	const char *name;
+
+	progname = av[0];
+	if (ac > 1 && av[1][0] == '-') {
+		switch (av[1][1]) {
+		case 's':
+			conf_set_message_callback(NULL);
+			break;
+		case 'h':
+		case '?':
+			usage();
+		}
+		name = av[2];
+	} else
+		name = av[1];
+	if (!name)
+		usage();
+
+	conf_parse(name);
+	fixup_rootmenu(&rootmenu);
+	conf_read(NULL);
+	//zconfdump(stdout);
+
+	configApp = new QApplication(ac, av);
+
+	configSettings = new ConfigSettings();
+	configSettings->beginGroup("/kconfig/qconf");
+	v = new ConfigMainWindow();
+
+	//zconfdump(stdout);
+	configApp->connect(configApp, SIGNAL(lastWindowClosed()), SLOT(quit()));
+	configApp->connect(configApp, SIGNAL(aboutToQuit()), v, SLOT(saveSettings()));
+	v->show();
+	configApp->exec();
+
+	configSettings->endGroup();
+	delete configSettings;
+	delete v;
+	delete configApp;
+
+	return 0;
+}
diff --git a/scripts/config/qconf.h b/scripts/config/qconf.h
new file mode 100644
index 0000000..c50d192
--- /dev/null
+++ b/scripts/config/qconf.h
@@ -0,0 +1,275 @@
+/* SPDX-License-Identifier: GPL-2.0-only */
+/*
+ * 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 mousePressEvent(QMouseEvent *e);
+	void mouseReleaseEvent(QMouseEvent *e);
+	void mouseMoveEvent(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, bool v)
+	: Parent(parent, after), nextItem(0), menu(m), visible(v), goParent(false)
+	{
+		init();
+	}
+	ConfigItem(ConfigItem *parent, ConfigItem *after, struct menu *m, bool v)
+	: Parent(parent, after), nextItem(0), menu(m), visible(v), goParent(false)
+	{
+		init();
+	}
+	ConfigItem(ConfigList *parent, ConfigItem *after, bool v)
+	: Parent(parent, after), nextItem(0), menu(0), visible(v), goParent(true)
+	{
+		init();
+	}
+	~ConfigItem(void);
+	void init(void);
+	void updateMenu(void);
+	void testUpdateMenu(bool v);
+	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 visible;
+	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
+
+	char *configname;
+	static QAction *saveAction;
+	static void conf_changed(void);
+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;
+};
diff --git a/scripts/config/symbol.c b/scripts/config/symbol.c
new file mode 100644
index 0000000..6ef44ad
--- /dev/null
+++ b/scripts/config/symbol.c
@@ -0,0 +1,1253 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2002 Roman Zippel <zippel@linux-m68k.org>
+ */
+
+#include <sys/types.h>
+#include <ctype.h>
+#include <stdlib.h>
+#include <string.h>
+#include <regex.h>
+
+#include "lkc.h"
+
+struct symbol symbol_yes = {
+	.name = "y",
+	.curr = { "y", yes },
+	.flags = SYMBOL_CONST|SYMBOL_VALID,
+};
+
+struct symbol symbol_mod = {
+	.name = "m",
+	.curr = { "m", mod },
+	.flags = SYMBOL_CONST|SYMBOL_VALID,
+};
+
+struct symbol symbol_no = {
+	.name = "n",
+	.curr = { "n", no },
+	.flags = SYMBOL_CONST|SYMBOL_VALID,
+};
+
+static struct symbol symbol_empty = {
+	.name = "",
+	.curr = { "", no },
+	.flags = SYMBOL_VALID,
+};
+
+struct symbol *modules_sym;
+static tristate modules_val;
+int recursive_is_error;
+
+enum symbol_type sym_get_type(struct symbol *sym)
+{
+	enum symbol_type type = sym->type;
+
+	if (type == S_TRISTATE) {
+		if (sym_is_choice_value(sym) && sym->visible == yes)
+			type = S_BOOLEAN;
+		else if (modules_val == no)
+			type = S_BOOLEAN;
+	}
+	return type;
+}
+
+const char *sym_type_name(enum symbol_type type)
+{
+	switch (type) {
+	case S_BOOLEAN:
+		return "bool";
+	case S_TRISTATE:
+		return "tristate";
+	case S_INT:
+		return "integer";
+	case S_HEX:
+		return "hex";
+	case S_STRING:
+		return "string";
+	case S_UNKNOWN:
+		return "unknown";
+	}
+	return "???";
+}
+
+struct property *sym_get_choice_prop(struct symbol *sym)
+{
+	struct property *prop;
+
+	for_all_choices(sym, prop)
+		return prop;
+	return NULL;
+}
+
+static struct property *sym_get_default_prop(struct symbol *sym)
+{
+	struct property *prop;
+
+	for_all_defaults(sym, prop) {
+		prop->visible.tri = expr_calc_value(prop->visible.expr);
+		if (prop->visible.tri != no)
+			return prop;
+	}
+	return NULL;
+}
+
+struct property *sym_get_range_prop(struct symbol *sym)
+{
+	struct property *prop;
+
+	for_all_properties(sym, prop, P_RANGE) {
+		prop->visible.tri = expr_calc_value(prop->visible.expr);
+		if (prop->visible.tri != no)
+			return prop;
+	}
+	return NULL;
+}
+
+static long long sym_get_range_val(struct symbol *sym, int base)
+{
+	sym_calc_value(sym);
+	switch (sym->type) {
+	case S_INT:
+		base = 10;
+		break;
+	case S_HEX:
+		base = 16;
+		break;
+	default:
+		break;
+	}
+	return strtoll(sym->curr.val, NULL, base);
+}
+
+static void sym_validate_range(struct symbol *sym)
+{
+	struct property *prop;
+	struct symbol *range_sym;
+	int base;
+	long long val, val2;
+
+	switch (sym->type) {
+	case S_INT:
+		base = 10;
+		break;
+	case S_HEX:
+		base = 16;
+		break;
+	default:
+		return;
+	}
+	prop = sym_get_range_prop(sym);
+	if (!prop)
+		return;
+	val = strtoll(sym->curr.val, NULL, base);
+	range_sym = prop->expr->left.sym;
+	val2 = sym_get_range_val(range_sym, base);
+	if (val >= val2) {
+		range_sym = prop->expr->right.sym;
+		val2 = sym_get_range_val(range_sym, base);
+		if (val <= val2)
+			return;
+	}
+	sym->curr.val = range_sym->curr.val;
+}
+
+static void sym_set_changed(struct symbol *sym)
+{
+	struct property *prop;
+
+	sym->flags |= SYMBOL_CHANGED;
+	for (prop = sym->prop; prop; prop = prop->next) {
+		if (prop->menu)
+			prop->menu->flags |= MENU_CHANGED;
+	}
+}
+
+static void sym_set_all_changed(void)
+{
+	struct symbol *sym;
+	int i;
+
+	for_all_symbols(i, sym)
+		sym_set_changed(sym);
+}
+
+static void sym_calc_visibility(struct symbol *sym)
+{
+	struct property *prop;
+	struct symbol *choice_sym = NULL;
+	tristate tri;
+
+	/* any prompt visible? */
+	tri = no;
+
+	if (sym_is_choice_value(sym))
+		choice_sym = prop_get_symbol(sym_get_choice_prop(sym));
+
+	for_all_prompts(sym, prop) {
+		prop->visible.tri = expr_calc_value(prop->visible.expr);
+		/*
+		 * Tristate choice_values with visibility 'mod' are
+		 * not visible if the corresponding choice's value is
+		 * 'yes'.
+		 */
+		if (choice_sym && sym->type == S_TRISTATE &&
+		    prop->visible.tri == mod && choice_sym->curr.tri == yes)
+			prop->visible.tri = no;
+
+		tri = EXPR_OR(tri, prop->visible.tri);
+	}
+	if (tri == mod && (sym->type != S_TRISTATE || modules_val == no))
+		tri = yes;
+	if (sym->visible != tri) {
+		sym->visible = tri;
+		sym_set_changed(sym);
+	}
+	if (sym_is_choice_value(sym))
+		return;
+	/* defaulting to "yes" if no explicit "depends on" are given */
+	tri = yes;
+	if (sym->dir_dep.expr)
+		tri = expr_calc_value(sym->dir_dep.expr);
+	if (tri == mod && sym_get_type(sym) == S_BOOLEAN)
+		tri = yes;
+	if (sym->dir_dep.tri != tri) {
+		sym->dir_dep.tri = tri;
+		sym_set_changed(sym);
+	}
+	tri = no;
+	if (sym->rev_dep.expr)
+		tri = expr_calc_value(sym->rev_dep.expr);
+	if (tri == mod && sym_get_type(sym) == S_BOOLEAN)
+		tri = yes;
+	if (sym->rev_dep.tri != tri) {
+		sym->rev_dep.tri = tri;
+		sym_set_changed(sym);
+	}
+	tri = no;
+	if (sym->implied.expr)
+		tri = expr_calc_value(sym->implied.expr);
+	if (tri == mod && sym_get_type(sym) == S_BOOLEAN)
+		tri = yes;
+	if (sym->implied.tri != tri) {
+		sym->implied.tri = tri;
+		sym_set_changed(sym);
+	}
+}
+
+/*
+ * Find the default symbol for a choice.
+ * First try the default values for the choice symbol
+ * Next locate the first visible choice value
+ * Return NULL if none was found
+ */
+struct symbol *sym_choice_default(struct symbol *sym)
+{
+	struct symbol *def_sym;
+	struct property *prop;
+	struct expr *e;
+
+	/* any of the defaults visible? */
+	for_all_defaults(sym, prop) {
+		prop->visible.tri = expr_calc_value(prop->visible.expr);
+		if (prop->visible.tri == no)
+			continue;
+		def_sym = prop_get_symbol(prop);
+		if (def_sym->visible != no)
+			return def_sym;
+	}
+
+	/* just get the first visible value */
+	prop = sym_get_choice_prop(sym);
+	expr_list_for_each_sym(prop->expr, e, def_sym)
+		if (def_sym->visible != no)
+			return def_sym;
+
+	/* failed to locate any defaults */
+	return NULL;
+}
+
+static struct symbol *sym_calc_choice(struct symbol *sym)
+{
+	struct symbol *def_sym;
+	struct property *prop;
+	struct expr *e;
+	int flags;
+
+	/* first calculate all choice values' visibilities */
+	flags = sym->flags;
+	prop = sym_get_choice_prop(sym);
+	expr_list_for_each_sym(prop->expr, e, def_sym) {
+		sym_calc_visibility(def_sym);
+		if (def_sym->visible != no)
+			flags &= def_sym->flags;
+	}
+
+	sym->flags &= flags | ~SYMBOL_DEF_USER;
+
+	/* is the user choice visible? */
+	def_sym = sym->def[S_DEF_USER].val;
+	if (def_sym && def_sym->visible != no)
+		return def_sym;
+
+	def_sym = sym_choice_default(sym);
+
+	if (def_sym == NULL)
+		/* no choice? reset tristate value */
+		sym->curr.tri = no;
+
+	return def_sym;
+}
+
+void sym_calc_value(struct symbol *sym)
+{
+	struct symbol_value newval, oldval;
+	struct property *prop;
+	struct expr *e;
+
+	if (!sym)
+		return;
+
+	if (sym->flags & SYMBOL_VALID)
+		return;
+
+	if (sym_is_choice_value(sym) &&
+	    sym->flags & SYMBOL_NEED_SET_CHOICE_VALUES) {
+		sym->flags &= ~SYMBOL_NEED_SET_CHOICE_VALUES;
+		prop = sym_get_choice_prop(sym);
+		sym_calc_value(prop_get_symbol(prop));
+	}
+
+	sym->flags |= SYMBOL_VALID;
+
+	oldval = sym->curr;
+
+	switch (sym->type) {
+	case S_INT:
+	case S_HEX:
+	case S_STRING:
+		newval = symbol_empty.curr;
+		break;
+	case S_BOOLEAN:
+	case S_TRISTATE:
+		newval = symbol_no.curr;
+		break;
+	default:
+		sym->curr.val = sym->name;
+		sym->curr.tri = no;
+		return;
+	}
+	sym->flags &= ~SYMBOL_WRITE;
+
+	sym_calc_visibility(sym);
+
+	if (sym->visible != no)
+		sym->flags |= SYMBOL_WRITE;
+
+	/* set default if recursively called */
+	sym->curr = newval;
+
+	switch (sym_get_type(sym)) {
+	case S_BOOLEAN:
+	case S_TRISTATE:
+		if (sym_is_choice_value(sym) && sym->visible == yes) {
+			prop = sym_get_choice_prop(sym);
+			newval.tri = (prop_get_symbol(prop)->curr.val == sym) ? yes : no;
+		} else {
+			if (sym->visible != no) {
+				/* if the symbol is visible use the user value
+				 * if available, otherwise try the default value
+				 */
+				if (sym_has_value(sym)) {
+					newval.tri = EXPR_AND(sym->def[S_DEF_USER].tri,
+							      sym->visible);
+					goto calc_newval;
+				}
+			}
+			if (sym->rev_dep.tri != no)
+				sym->flags |= SYMBOL_WRITE;
+			if (!sym_is_choice(sym)) {
+				prop = sym_get_default_prop(sym);
+				if (prop) {
+					sym->flags |= SYMBOL_WRITE;
+					newval.tri = EXPR_AND(expr_calc_value(prop->expr),
+							      prop->visible.tri);
+				}
+				if (sym->implied.tri != no) {
+					sym->flags |= SYMBOL_WRITE;
+					newval.tri = EXPR_OR(newval.tri, sym->implied.tri);
+					newval.tri = EXPR_AND(newval.tri,
+							      sym->dir_dep.tri);
+				}
+			}
+		calc_newval:
+			if (sym->dir_dep.tri == no && sym->rev_dep.tri != no)
+				newval.tri = no;
+			else
+				newval.tri = EXPR_OR(newval.tri, sym->rev_dep.tri);
+		}
+		if (newval.tri == mod && sym_get_type(sym) == S_BOOLEAN)
+			newval.tri = yes;
+		break;
+	case S_STRING:
+	case S_HEX:
+	case S_INT:
+		if (sym->visible != no && sym_has_value(sym)) {
+			newval.val = sym->def[S_DEF_USER].val;
+			break;
+		}
+		prop = sym_get_default_prop(sym);
+		if (prop) {
+			struct symbol *ds = prop_get_symbol(prop);
+			if (ds) {
+				sym->flags |= SYMBOL_WRITE;
+				sym_calc_value(ds);
+				newval.val = ds->curr.val;
+			}
+		}
+		break;
+	default:
+		;
+	}
+
+	sym->curr = newval;
+	if (sym_is_choice(sym) && newval.tri == yes)
+		sym->curr.val = sym_calc_choice(sym);
+	sym_validate_range(sym);
+
+	if (memcmp(&oldval, &sym->curr, sizeof(oldval))) {
+		sym_set_changed(sym);
+		if (modules_sym == sym) {
+			sym_set_all_changed();
+			modules_val = modules_sym->curr.tri;
+		}
+	}
+
+	if (sym_is_choice(sym)) {
+		struct symbol *choice_sym;
+
+		prop = sym_get_choice_prop(sym);
+		expr_list_for_each_sym(prop->expr, e, choice_sym) {
+			if ((sym->flags & SYMBOL_WRITE) &&
+			    choice_sym->visible != no)
+				choice_sym->flags |= SYMBOL_WRITE;
+			if (sym->flags & SYMBOL_CHANGED)
+				sym_set_changed(choice_sym);
+		}
+	}
+
+	if (sym->flags & SYMBOL_NO_WRITE)
+		sym->flags &= ~SYMBOL_WRITE;
+
+	if (sym->flags & SYMBOL_NEED_SET_CHOICE_VALUES)
+		set_all_choice_values(sym);
+}
+
+void sym_clear_all_valid(void)
+{
+	struct symbol *sym;
+	int i;
+
+	for_all_symbols(i, sym)
+		sym->flags &= ~SYMBOL_VALID;
+	conf_set_changed(true);
+	sym_calc_value(modules_sym);
+}
+
+bool sym_tristate_within_range(struct symbol *sym, tristate val)
+{
+	int type = sym_get_type(sym);
+
+	if (sym->visible == no)
+		return false;
+
+	if (type != S_BOOLEAN && type != S_TRISTATE)
+		return false;
+
+	if (type == S_BOOLEAN && val == mod)
+		return false;
+	if (sym->visible <= sym->rev_dep.tri)
+		return false;
+	if (sym_is_choice_value(sym) && sym->visible == yes)
+		return val == yes;
+	return val >= sym->rev_dep.tri && val <= sym->visible;
+}
+
+bool sym_set_tristate_value(struct symbol *sym, tristate val)
+{
+	tristate oldval = sym_get_tristate_value(sym);
+
+	if (oldval != val && !sym_tristate_within_range(sym, val))
+		return false;
+
+	if (!(sym->flags & SYMBOL_DEF_USER)) {
+		sym->flags |= SYMBOL_DEF_USER;
+		sym_set_changed(sym);
+	}
+	/*
+	 * setting a choice value also resets the new flag of the choice
+	 * symbol and all other choice values.
+	 */
+	if (sym_is_choice_value(sym) && val == yes) {
+		struct symbol *cs = prop_get_symbol(sym_get_choice_prop(sym));
+		struct property *prop;
+		struct expr *e;
+
+		cs->def[S_DEF_USER].val = sym;
+		cs->flags |= SYMBOL_DEF_USER;
+		prop = sym_get_choice_prop(cs);
+		for (e = prop->expr; e; e = e->left.expr) {
+			if (e->right.sym->visible != no)
+				e->right.sym->flags |= SYMBOL_DEF_USER;
+		}
+	}
+
+	sym->def[S_DEF_USER].tri = val;
+	if (oldval != val)
+		sym_clear_all_valid();
+
+	return true;
+}
+
+tristate sym_toggle_tristate_value(struct symbol *sym)
+{
+	tristate oldval, newval;
+
+	oldval = newval = sym_get_tristate_value(sym);
+	do {
+		switch (newval) {
+		case no:
+			newval = mod;
+			break;
+		case mod:
+			newval = yes;
+			break;
+		case yes:
+			newval = no;
+			break;
+		}
+		if (sym_set_tristate_value(sym, newval))
+			break;
+	} while (oldval != newval);
+	return newval;
+}
+
+bool sym_string_valid(struct symbol *sym, const char *str)
+{
+	signed char ch;
+
+	switch (sym->type) {
+	case S_STRING:
+		return true;
+	case S_INT:
+		ch = *str++;
+		if (ch == '-')
+			ch = *str++;
+		if (!isdigit(ch))
+			return false;
+		if (ch == '0' && *str != 0)
+			return false;
+		while ((ch = *str++)) {
+			if (!isdigit(ch))
+				return false;
+		}
+		return true;
+	case S_HEX:
+		if (str[0] == '0' && (str[1] == 'x' || str[1] == 'X'))
+			str += 2;
+		ch = *str++;
+		do {
+			if (!isxdigit(ch))
+				return false;
+		} while ((ch = *str++));
+		return true;
+	case S_BOOLEAN:
+	case S_TRISTATE:
+		switch (str[0]) {
+		case 'y': case 'Y':
+		case 'm': case 'M':
+		case 'n': case 'N':
+			return true;
+		}
+		return false;
+	default:
+		return false;
+	}
+}
+
+bool sym_string_within_range(struct symbol *sym, const char *str)
+{
+	struct property *prop;
+	long long val;
+
+	switch (sym->type) {
+	case S_STRING:
+		return sym_string_valid(sym, str);
+	case S_INT:
+		if (!sym_string_valid(sym, str))
+			return false;
+		prop = sym_get_range_prop(sym);
+		if (!prop)
+			return true;
+		val = strtoll(str, NULL, 10);
+		return val >= sym_get_range_val(prop->expr->left.sym, 10) &&
+		       val <= sym_get_range_val(prop->expr->right.sym, 10);
+	case S_HEX:
+		if (!sym_string_valid(sym, str))
+			return false;
+		prop = sym_get_range_prop(sym);
+		if (!prop)
+			return true;
+		val = strtoll(str, NULL, 16);
+		return val >= sym_get_range_val(prop->expr->left.sym, 16) &&
+		       val <= sym_get_range_val(prop->expr->right.sym, 16);
+	case S_BOOLEAN:
+	case S_TRISTATE:
+		switch (str[0]) {
+		case 'y': case 'Y':
+			return sym_tristate_within_range(sym, yes);
+		case 'm': case 'M':
+			return sym_tristate_within_range(sym, mod);
+		case 'n': case 'N':
+			return sym_tristate_within_range(sym, no);
+		}
+		return false;
+	default:
+		return false;
+	}
+}
+
+bool sym_set_string_value(struct symbol *sym, const char *newval)
+{
+	const char *oldval;
+	char *val;
+	int size;
+
+	switch (sym->type) {
+	case S_BOOLEAN:
+	case S_TRISTATE:
+		switch (newval[0]) {
+		case 'y': case 'Y':
+			return sym_set_tristate_value(sym, yes);
+		case 'm': case 'M':
+			return sym_set_tristate_value(sym, mod);
+		case 'n': case 'N':
+			return sym_set_tristate_value(sym, no);
+		}
+		return false;
+	default:
+		;
+	}
+
+	if (!sym_string_within_range(sym, newval))
+		return false;
+
+	if (!(sym->flags & SYMBOL_DEF_USER)) {
+		sym->flags |= SYMBOL_DEF_USER;
+		sym_set_changed(sym);
+	}
+
+	oldval = sym->def[S_DEF_USER].val;
+	size = strlen(newval) + 1;
+	if (sym->type == S_HEX && (newval[0] != '0' || (newval[1] != 'x' && newval[1] != 'X'))) {
+		size += 2;
+		sym->def[S_DEF_USER].val = val = xmalloc(size);
+		*val++ = '0';
+		*val++ = 'x';
+	} else if (!oldval || strcmp(oldval, newval))
+		sym->def[S_DEF_USER].val = val = xmalloc(size);
+	else
+		return true;
+
+	strcpy(val, newval);
+	free((void *)oldval);
+	sym_clear_all_valid();
+
+	return true;
+}
+
+/*
+ * Find the default value associated to a symbol.
+ * For tristate symbol handle the modules=n case
+ * in which case "m" becomes "y".
+ * If the symbol does not have any default then fallback
+ * to the fixed default values.
+ */
+const char *sym_get_string_default(struct symbol *sym)
+{
+	struct property *prop;
+	struct symbol *ds;
+	const char *str;
+	tristate val;
+
+	sym_calc_visibility(sym);
+	sym_calc_value(modules_sym);
+	val = symbol_no.curr.tri;
+	str = symbol_empty.curr.val;
+
+	/* If symbol has a default value look it up */
+	prop = sym_get_default_prop(sym);
+	if (prop != NULL) {
+		switch (sym->type) {
+		case S_BOOLEAN:
+		case S_TRISTATE:
+			/* The visibility may limit the value from yes => mod */
+			val = EXPR_AND(expr_calc_value(prop->expr), prop->visible.tri);
+			break;
+		default:
+			/*
+			 * The following fails to handle the situation
+			 * where a default value is further limited by
+			 * the valid range.
+			 */
+			ds = prop_get_symbol(prop);
+			if (ds != NULL) {
+				sym_calc_value(ds);
+				str = (const char *)ds->curr.val;
+			}
+		}
+	}
+
+	/* Handle select statements */
+	val = EXPR_OR(val, sym->rev_dep.tri);
+
+	/* transpose mod to yes if modules are not enabled */
+	if (val == mod)
+		if (!sym_is_choice_value(sym) && modules_sym->curr.tri == no)
+			val = yes;
+
+	/* transpose mod to yes if type is bool */
+	if (sym->type == S_BOOLEAN && val == mod)
+		val = yes;
+
+	/* adjust the default value if this symbol is implied by another */
+	if (val < sym->implied.tri)
+		val = sym->implied.tri;
+
+	switch (sym->type) {
+	case S_BOOLEAN:
+	case S_TRISTATE:
+		switch (val) {
+		case no: return "n";
+		case mod: return "m";
+		case yes: return "y";
+		}
+	case S_INT:
+	case S_HEX:
+		return str;
+	case S_STRING:
+		return str;
+	case S_UNKNOWN:
+		break;
+	}
+	return "";
+}
+
+const char *sym_get_string_value(struct symbol *sym)
+{
+	tristate val;
+
+	switch (sym->type) {
+	case S_BOOLEAN:
+	case S_TRISTATE:
+		val = sym_get_tristate_value(sym);
+		switch (val) {
+		case no:
+			return "n";
+		case mod:
+			sym_calc_value(modules_sym);
+			return (modules_sym->curr.tri == no) ? "n" : "m";
+		case yes:
+			return "y";
+		}
+		break;
+	default:
+		;
+	}
+	return (const char *)sym->curr.val;
+}
+
+bool sym_is_changeable(struct symbol *sym)
+{
+	return sym->visible > sym->rev_dep.tri;
+}
+
+static unsigned strhash(const char *s)
+{
+	/* fnv32 hash */
+	unsigned hash = 2166136261U;
+	for (; *s; s++)
+		hash = (hash ^ *s) * 0x01000193;
+	return hash;
+}
+
+struct symbol *sym_lookup(const char *name, int flags)
+{
+	struct symbol *symbol;
+	char *new_name;
+	int hash;
+
+	if (name) {
+		if (name[0] && !name[1]) {
+			switch (name[0]) {
+			case 'y': return &symbol_yes;
+			case 'm': return &symbol_mod;
+			case 'n': return &symbol_no;
+			}
+		}
+		hash = strhash(name) % SYMBOL_HASHSIZE;
+
+		for (symbol = symbol_hash[hash]; symbol; symbol = symbol->next) {
+			if (symbol->name &&
+			    !strcmp(symbol->name, name) &&
+			    (flags ? symbol->flags & flags
+				   : !(symbol->flags & (SYMBOL_CONST|SYMBOL_CHOICE))))
+				return symbol;
+		}
+		new_name = xstrdup(name);
+	} else {
+		new_name = NULL;
+		hash = 0;
+	}
+
+	symbol = xmalloc(sizeof(*symbol));
+	memset(symbol, 0, sizeof(*symbol));
+	symbol->name = new_name;
+	symbol->type = S_UNKNOWN;
+	symbol->flags = flags;
+
+	symbol->next = symbol_hash[hash];
+	symbol_hash[hash] = symbol;
+
+	return symbol;
+}
+
+struct symbol *sym_find(const char *name)
+{
+	struct symbol *symbol = NULL;
+	int hash = 0;
+
+	if (!name)
+		return NULL;
+
+	if (name[0] && !name[1]) {
+		switch (name[0]) {
+		case 'y': return &symbol_yes;
+		case 'm': return &symbol_mod;
+		case 'n': return &symbol_no;
+		}
+	}
+	hash = strhash(name) % SYMBOL_HASHSIZE;
+
+	for (symbol = symbol_hash[hash]; symbol; symbol = symbol->next) {
+		if (symbol->name &&
+		    !strcmp(symbol->name, name) &&
+		    !(symbol->flags & SYMBOL_CONST))
+				break;
+	}
+
+	return symbol;
+}
+
+struct sym_match {
+	struct symbol	*sym;
+	off_t		so, eo;
+};
+
+/* Compare matched symbols as thus:
+ * - first, symbols that match exactly
+ * - then, alphabetical sort
+ */
+static int sym_rel_comp(const void *sym1, const void *sym2)
+{
+	const struct sym_match *s1 = sym1;
+	const struct sym_match *s2 = sym2;
+	int exact1, exact2;
+
+	/* Exact match:
+	 * - if matched length on symbol s1 is the length of that symbol,
+	 *   then this symbol should come first;
+	 * - if matched length on symbol s2 is the length of that symbol,
+	 *   then this symbol should come first.
+	 * Note: since the search can be a regexp, both symbols may match
+	 * exactly; if this is the case, we can't decide which comes first,
+	 * and we fallback to sorting alphabetically.
+	 */
+	exact1 = (s1->eo - s1->so) == strlen(s1->sym->name);
+	exact2 = (s2->eo - s2->so) == strlen(s2->sym->name);
+	if (exact1 && !exact2)
+		return -1;
+	if (!exact1 && exact2)
+		return 1;
+
+	/* As a fallback, sort symbols alphabetically */
+	return strcmp(s1->sym->name, s2->sym->name);
+}
+
+struct symbol **sym_re_search(const char *pattern)
+{
+	struct symbol *sym, **sym_arr = NULL;
+	struct sym_match *sym_match_arr = NULL;
+	int i, cnt, size;
+	regex_t re;
+	regmatch_t match[1];
+
+	cnt = size = 0;
+	/* Skip if empty */
+	if (strlen(pattern) == 0)
+		return NULL;
+	if (regcomp(&re, pattern, REG_EXTENDED|REG_ICASE))
+		return NULL;
+
+	for_all_symbols(i, sym) {
+		if (sym->flags & SYMBOL_CONST || !sym->name)
+			continue;
+		if (regexec(&re, sym->name, 1, match, 0))
+			continue;
+		if (cnt >= size) {
+			void *tmp;
+			size += 16;
+			tmp = realloc(sym_match_arr, size * sizeof(struct sym_match));
+			if (!tmp)
+				goto sym_re_search_free;
+			sym_match_arr = tmp;
+		}
+		sym_calc_value(sym);
+		/* As regexec returned 0, we know we have a match, so
+		 * we can use match[0].rm_[se]o without further checks
+		 */
+		sym_match_arr[cnt].so = match[0].rm_so;
+		sym_match_arr[cnt].eo = match[0].rm_eo;
+		sym_match_arr[cnt++].sym = sym;
+	}
+	if (sym_match_arr) {
+		qsort(sym_match_arr, cnt, sizeof(struct sym_match), sym_rel_comp);
+		sym_arr = malloc((cnt+1) * sizeof(struct symbol *));
+		if (!sym_arr)
+			goto sym_re_search_free;
+		for (i = 0; i < cnt; i++)
+			sym_arr[i] = sym_match_arr[i].sym;
+		sym_arr[cnt] = NULL;
+	}
+sym_re_search_free:
+	/* sym_match_arr can be NULL if no match, but free(NULL) is OK */
+	free(sym_match_arr);
+	regfree(&re);
+
+	return sym_arr;
+}
+
+/*
+ * When we check for recursive dependencies we use a stack to save
+ * current state so we can print out relevant info to user.
+ * The entries are located on the call stack so no need to free memory.
+ * Note insert() remove() must always match to properly clear the stack.
+ */
+static struct dep_stack {
+	struct dep_stack *prev, *next;
+	struct symbol *sym;
+	struct property *prop;
+	struct expr **expr;
+} *check_top;
+
+static void dep_stack_insert(struct dep_stack *stack, struct symbol *sym)
+{
+	memset(stack, 0, sizeof(*stack));
+	if (check_top)
+		check_top->next = stack;
+	stack->prev = check_top;
+	stack->sym = sym;
+	check_top = stack;
+}
+
+static void dep_stack_remove(void)
+{
+	check_top = check_top->prev;
+	if (check_top)
+		check_top->next = NULL;
+}
+
+/*
+ * Called when we have detected a recursive dependency.
+ * check_top point to the top of the stact so we use
+ * the ->prev pointer to locate the bottom of the stack.
+ */
+static void sym_check_print_recursive(struct symbol *last_sym)
+{
+	struct dep_stack *stack;
+	struct symbol *sym, *next_sym;
+	struct menu *menu = NULL;
+	struct property *prop;
+	struct dep_stack cv_stack;
+
+	if (sym_is_choice_value(last_sym)) {
+		dep_stack_insert(&cv_stack, last_sym);
+		last_sym = prop_get_symbol(sym_get_choice_prop(last_sym));
+	}
+
+	for (stack = check_top; stack != NULL; stack = stack->prev)
+		if (stack->sym == last_sym)
+			break;
+	if (!stack) {
+		fprintf(stderr, "unexpected recursive dependency error\n");
+		return;
+	}
+
+	for (; stack; stack = stack->next) {
+		sym = stack->sym;
+		next_sym = stack->next ? stack->next->sym : last_sym;
+		prop = stack->prop;
+		if (prop == NULL)
+			prop = stack->sym->prop;
+
+		/* for choice values find the menu entry (used below) */
+		if (sym_is_choice(sym) || sym_is_choice_value(sym)) {
+			for (prop = sym->prop; prop; prop = prop->next) {
+				menu = prop->menu;
+				if (prop->menu)
+					break;
+			}
+		}
+		if (stack->sym == last_sym)
+			fprintf(stderr, "%s:%d:error: recursive dependency detected!\n",
+				prop->file->name, prop->lineno);
+
+		if (sym_is_choice(sym)) {
+			fprintf(stderr, "%s:%d:\tchoice %s contains symbol %s\n",
+				menu->file->name, menu->lineno,
+				sym->name ? sym->name : "<choice>",
+				next_sym->name ? next_sym->name : "<choice>");
+		} else if (sym_is_choice_value(sym)) {
+			fprintf(stderr, "%s:%d:\tsymbol %s is part of choice %s\n",
+				menu->file->name, menu->lineno,
+				sym->name ? sym->name : "<choice>",
+				next_sym->name ? next_sym->name : "<choice>");
+		} else if (stack->expr == &sym->dir_dep.expr) {
+			fprintf(stderr, "%s:%d:\tsymbol %s depends on %s\n",
+				prop->file->name, prop->lineno,
+				sym->name ? sym->name : "<choice>",
+				next_sym->name ? next_sym->name : "<choice>");
+		} else if (stack->expr == &sym->rev_dep.expr) {
+			fprintf(stderr, "%s:%d:\tsymbol %s is selected by %s\n",
+				prop->file->name, prop->lineno,
+				sym->name ? sym->name : "<choice>",
+				next_sym->name ? next_sym->name : "<choice>");
+		} else if (stack->expr == &sym->implied.expr) {
+			fprintf(stderr, "%s:%d:\tsymbol %s is implied by %s\n",
+				prop->file->name, prop->lineno,
+				sym->name ? sym->name : "<choice>",
+				next_sym->name ? next_sym->name : "<choice>");
+		} else if (stack->expr) {
+			fprintf(stderr, "%s:%d:\tsymbol %s %s value contains %s\n",
+				prop->file->name, prop->lineno,
+				sym->name ? sym->name : "<choice>",
+				prop_get_type_name(prop->type),
+				next_sym->name ? next_sym->name : "<choice>");
+		} else {
+			fprintf(stderr, "%s:%d:\tsymbol %s %s is visible depending on %s\n",
+				prop->file->name, prop->lineno,
+				sym->name ? sym->name : "<choice>",
+				prop_get_type_name(prop->type),
+				next_sym->name ? next_sym->name : "<choice>");
+		}
+	}
+
+	fprintf(stderr,
+		"For a resolution refer to Documentation/kbuild/kconfig-language.rst\n"
+		"subsection \"Kconfig recursive dependency limitations\"\n"
+		"\n");
+
+	if (check_top == &cv_stack)
+		dep_stack_remove();
+}
+
+static struct symbol *sym_check_expr_deps(struct expr *e)
+{
+	struct symbol *sym;
+
+	if (!e)
+		return NULL;
+	switch (e->type) {
+	case E_OR:
+	case E_AND:
+		sym = sym_check_expr_deps(e->left.expr);
+		if (sym)
+			return sym;
+		return sym_check_expr_deps(e->right.expr);
+	case E_NOT:
+		return sym_check_expr_deps(e->left.expr);
+	case E_EQUAL:
+	case E_GEQ:
+	case E_GTH:
+	case E_LEQ:
+	case E_LTH:
+	case E_UNEQUAL:
+		sym = sym_check_deps(e->left.sym);
+		if (sym)
+			return sym;
+		return sym_check_deps(e->right.sym);
+	case E_SYMBOL:
+		return sym_check_deps(e->left.sym);
+	default:
+		break;
+	}
+	fprintf(stderr, "Oops! How to check %d?\n", e->type);
+	return NULL;
+}
+
+/* return NULL when dependencies are OK */
+static struct symbol *sym_check_sym_deps(struct symbol *sym)
+{
+	struct symbol *sym2;
+	struct property *prop;
+	struct dep_stack stack;
+
+	dep_stack_insert(&stack, sym);
+
+	stack.expr = &sym->dir_dep.expr;
+	sym2 = sym_check_expr_deps(sym->dir_dep.expr);
+	if (sym2)
+		goto out;
+
+	stack.expr = &sym->rev_dep.expr;
+	sym2 = sym_check_expr_deps(sym->rev_dep.expr);
+	if (sym2)
+		goto out;
+
+	stack.expr = &sym->implied.expr;
+	sym2 = sym_check_expr_deps(sym->implied.expr);
+	if (sym2)
+		goto out;
+
+	stack.expr = NULL;
+
+	for (prop = sym->prop; prop; prop = prop->next) {
+		if (prop->type == P_CHOICE || prop->type == P_SELECT ||
+		    prop->type == P_IMPLY)
+			continue;
+		stack.prop = prop;
+		sym2 = sym_check_expr_deps(prop->visible.expr);
+		if (sym2)
+			break;
+		if (prop->type != P_DEFAULT || sym_is_choice(sym))
+			continue;
+		stack.expr = &prop->expr;
+		sym2 = sym_check_expr_deps(prop->expr);
+		if (sym2)
+			break;
+		stack.expr = NULL;
+	}
+
+out:
+	dep_stack_remove();
+
+	return sym2;
+}
+
+static struct symbol *sym_check_choice_deps(struct symbol *choice)
+{
+	struct symbol *sym, *sym2;
+	struct property *prop;
+	struct expr *e;
+	struct dep_stack stack;
+
+	dep_stack_insert(&stack, choice);
+
+	prop = sym_get_choice_prop(choice);
+	expr_list_for_each_sym(prop->expr, e, sym)
+		sym->flags |= (SYMBOL_CHECK | SYMBOL_CHECKED);
+
+	choice->flags |= (SYMBOL_CHECK | SYMBOL_CHECKED);
+	sym2 = sym_check_sym_deps(choice);
+	choice->flags &= ~SYMBOL_CHECK;
+	if (sym2)
+		goto out;
+
+	expr_list_for_each_sym(prop->expr, e, sym) {
+		sym2 = sym_check_sym_deps(sym);
+		if (sym2)
+			break;
+	}
+out:
+	expr_list_for_each_sym(prop->expr, e, sym)
+		sym->flags &= ~SYMBOL_CHECK;
+
+	if (sym2 && sym_is_choice_value(sym2) &&
+	    prop_get_symbol(sym_get_choice_prop(sym2)) == choice)
+		sym2 = choice;
+
+	dep_stack_remove();
+
+	return sym2;
+}
+
+struct symbol *sym_check_deps(struct symbol *sym)
+{
+	struct symbol *sym2;
+	struct property *prop;
+
+	if (sym->flags & SYMBOL_CHECK) {
+		sym_check_print_recursive(sym);
+		return sym;
+	}
+	if (sym->flags & SYMBOL_CHECKED)
+		return NULL;
+
+	if (sym_is_choice_value(sym)) {
+		struct dep_stack stack;
+
+		/* for choice groups start the check with main choice symbol */
+		dep_stack_insert(&stack, sym);
+		prop = sym_get_choice_prop(sym);
+		sym2 = sym_check_deps(prop_get_symbol(prop));
+		dep_stack_remove();
+	} else if (sym_is_choice(sym)) {
+		sym2 = sym_check_choice_deps(sym);
+	} else {
+		sym->flags |= (SYMBOL_CHECK | SYMBOL_CHECKED);
+		sym2 = sym_check_sym_deps(sym);
+		sym->flags &= ~SYMBOL_CHECK;
+	}
+
+	if (!recursive_is_error && sym2 && sym2 == sym)
+		sym2 = NULL;
+
+	return sym2;
+}
+
+struct symbol *prop_get_symbol(struct property *prop)
+{
+	if (prop->expr && (prop->expr->type == E_SYMBOL ||
+			   prop->expr->type == E_LIST))
+		return prop->expr->left.sym;
+	return NULL;
+}
+
+const char *prop_get_type_name(enum prop_type type)
+{
+	switch (type) {
+	case P_PROMPT:
+		return "prompt";
+	case P_COMMENT:
+		return "comment";
+	case P_MENU:
+		return "menu";
+	case P_DEFAULT:
+		return "default";
+	case P_CHOICE:
+		return "choice";
+	case P_SELECT:
+		return "select";
+	case P_IMPLY:
+		return "imply";
+	case P_RANGE:
+		return "range";
+	case P_SYMBOL:
+		return "symbol";
+	case P_RESET:
+		return "reset";
+	case P_UNKNOWN:
+		break;
+	}
+	return "unknown";
+}
diff --git a/scripts/config/util.c b/scripts/config/util.c
new file mode 100644
index 0000000..53e079e
--- /dev/null
+++ b/scripts/config/util.c
@@ -0,0 +1,129 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * 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 "lkc.h"
+
+/* file already present in list? If not add it */
+struct file *file_lookup(const char *name)
+{
+	struct file *file;
+
+	for (file = file_list; file; file = file->next) {
+		if (!strcmp(name, file->name)) {
+			return file;
+		}
+	}
+
+	file = xmalloc(sizeof(*file));
+	memset(file, 0, sizeof(*file));
+	file->name = xstrdup(name);
+	file->next = file_list;
+	file_list = file;
+	return file;
+}
+
+/* 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)
+{
+	if (gs->s)
+		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(struct gstr *gs)
+{
+	return gs->s;
+}
+
+void *xmalloc(size_t size)
+{
+	void *p = malloc(size);
+	if (p)
+		return p;
+	fprintf(stderr, "Out of memory.\n");
+	exit(1);
+}
+
+void *xcalloc(size_t nmemb, size_t size)
+{
+	void *p = calloc(nmemb, size);
+	if (p)
+		return p;
+	fprintf(stderr, "Out of memory.\n");
+	exit(1);
+}
+
+void *xrealloc(void *p, size_t size)
+{
+	p = realloc(p, size);
+	if (p)
+		return p;
+	fprintf(stderr, "Out of memory.\n");
+	exit(1);
+}
+
+char *xstrdup(const char *s)
+{
+	char *p;
+
+	p = strdup(s);
+	if (p)
+		return p;
+	fprintf(stderr, "Out of memory.\n");
+	exit(1);
+}
+
+char *xstrndup(const char *s, size_t n)
+{
+	char *p;
+
+	p = strndup(s, n);
+	if (p)
+		return p;
+	fprintf(stderr, "Out of memory.\n");
+	exit(1);
+}
diff --git a/scripts/const_structs.checkpatch b/scripts/const_structs.checkpatch
new file mode 100644
index 0000000..1aae4f4
--- /dev/null
+++ b/scripts/const_structs.checkpatch
@@ -0,0 +1,68 @@
+acpi_dock_ops
+address_space_operations
+backlight_ops
+block_device_operations
+clk_ops
+comedi_lrange
+component_ops
+dentry_operations
+dev_pm_ops
+dma_map_ops
+driver_info
+drm_connector_funcs
+drm_encoder_funcs
+drm_encoder_helper_funcs
+ethtool_ops
+extent_io_ops
+file_lock_operations
+file_operations
+hv_ops
+ide_dma_ops
+ide_port_ops
+inode_operations
+intel_dvo_dev_ops
+irq_domain_ops
+item_operations
+iwl_cfg
+iwl_ops
+kgdb_arch
+kgdb_io
+kset_uevent_ops
+lock_manager_operations
+machine_desc
+microcode_ops
+mlxsw_reg_info
+mtrr_ops
+neigh_ops
+net_device_ops
+nlmsvc_binding
+nvkm_device_chip
+of_device_id
+pci_raw_ops
+phy_ops
+pinctrl_ops
+pinmux_ops
+pipe_buf_operations
+platform_hibernation_ops
+platform_suspend_ops
+proto_ops
+regmap_access_table
+regulator_ops
+rpc_pipe_ops
+rtc_class_ops
+sd_desc
+seq_operations
+sirfsoc_padmux
+snd_ac97_build_ops
+snd_soc_component_driver
+soc_pcmcia_socket_ops
+stacktrace_ops
+sysfs_ops
+tty_operations
+uart_ops
+usb_mon_operations
+v4l2_ctrl_ops
+v4l2_ioctl_ops
+vm_operations_struct
+wacom_features
+wd_ops
diff --git a/scripts/deptest.sh b/scripts/deptest.sh
new file mode 100755
index 0000000..03da9f5
--- /dev/null
+++ b/scripts/deptest.sh
@@ -0,0 +1,211 @@
+#!/usr/bin/env bash
+#
+# Automated OpenWrt package dependency checker
+#
+# Copyright (C) 2009-2010 OpenWrt.org
+#
+# This is free software, licensed under the GNU General Public License v2.
+# See /LICENSE for more information.
+#
+
+SCRIPTDIR="$(dirname "$0")"
+[ "${SCRIPTDIR:0:1}" = "/" ] || SCRIPTDIR="$PWD/$SCRIPTDIR"
+BASEDIR="$SCRIPTDIR/.."
+
+DIR="$BASEDIR/tmp/deptest"
+STAMP_DIR_SUCCESS="$DIR/stamp-success"
+STAMP_DIR_FAILED="$DIR/stamp-failed"
+STAMP_DIR_BLACKLIST="$DIR/stamp-blacklist"
+BUILD_DIR="$DIR/build_dir/target"
+BUILD_DIR_HOST="$DIR/build_dir/host"
+KERNEL_BUILD_DIR="$DIR/build_dir/linux"
+STAGING_DIR="$DIR/staging_dir/target"
+STAGING_DIR_HOST="$DIR/staging_dir/host"
+STAGING_DIR_HOST_TMPL="$DIR/staging_dir_host_tmpl"
+BIN_DIR="$DIR/staging_dir/bin_dir"
+LOG_DIR_NAME="logs"
+LOG_DIR="$DIR/$LOG_DIR_NAME"
+
+die()
+{
+	echo "$@"
+	exit 1
+}
+
+usage()
+{
+	echo "deptest.sh [OPTIONS] [PACKAGES]"
+	echo
+	echo "OPTIONS:"
+	echo "  --lean       Run a lean test. Do not clean the build directory for each"
+	echo "               package test."
+	echo "  --force      Force a test, even if a success/blacklist stamp is available"
+	echo "  -j X         Number of make jobs"
+	echo
+	echo "PACKAGES are packages to test. If not specified, all installed packages"
+	echo "will be tested."
+}
+
+deptest_make()
+{
+	local target="$1"
+	shift
+	local logfile="$1"
+	shift
+	make -j$nrjobs "$target" \
+		BUILD_DIR="$BUILD_DIR" \
+		BUILD_DIR_HOST="$BUILD_DIR_HOST" \
+		KERNEL_BUILD_DIR="$KERNEL_BUILD_DIR" \
+		BIN_DIR="$BIN_DIR" \
+		STAGING_DIR="$STAGING_DIR" \
+		STAGING_DIR_HOST="$STAGING_DIR_HOST" \
+		FORCE_HOST_INSTALL=1 \
+		V=99 "$@" >"$LOG_DIR/$logfile" 2>&1
+}
+
+clean_kernel_build_dir()
+{
+	# delete everything, except the kernel build dir "linux-X.X.X"
+	(
+		cd "$KERNEL_BUILD_DIR" || die "Failed to enter kernel build dir"
+		for entry in *; do
+			[ -z "$(echo "$entry" | egrep -e '^linux-*.*.*$')" ] || continue
+			rm -rf "$entry" || die "Failed to clean kernel build dir"
+		done
+	)
+}
+
+stamp_exists() # $1=stamp
+{
+	[ -e "$1" -o -L "$1" ]
+}
+
+test_package() # $1=pkgname
+{
+	local pkg="$1"
+	[ -n "$pkg" -a -z "$(echo "$pkg" | grep -e '/')" -a "$pkg" != "." -a "$pkg" != ".." ] || \
+		die "Package name \"$pkg\" contains illegal characters"
+	local SELECTED=
+	for conf in `grep CONFIG_PACKAGE tmp/.packagedeps | grep -E "[ /]$pkg\$" | sed -e 's,package-$(\(CONFIG_PACKAGE_.*\)).*,\1,'`; do
+		grep "$conf=" .config > /dev/null && SELECTED=1 && break
+	done
+	local STAMP_SUCCESS="$STAMP_DIR_SUCCESS/$pkg"
+	local STAMP_FAILED="$STAMP_DIR_FAILED/$pkg"
+	local STAMP_BLACKLIST="$STAMP_DIR_BLACKLIST/$pkg"
+	rm -f "$STAMP_FAILED"
+	stamp_exists "$STAMP_SUCCESS" && [ $force -eq 0 ] && return
+	rm -f "$STAMP_SUCCESS"
+	[ -n "$SELECTED" ] || {
+		echo "Package $pkg is not selected"
+		return
+	}
+	stamp_exists "$STAMP_BLACKLIST" && [ $force -eq 0 ] && {
+		echo "Package $pkg is blacklisted"
+		return
+	}
+	echo "Testing package $pkg..."
+	rm -rf "$STAGING_DIR" "$STAGING_DIR_HOST"
+	mkdir -p "$STAGING_DIR"
+	cp -al "$STAGING_DIR_HOST_TMPL" "$STAGING_DIR_HOST"
+	[ $lean_test -eq 0 ] && {
+		rm -rf "$BUILD_DIR" "$BUILD_DIR_HOST"
+		clean_kernel_build_dir
+	}
+	mkdir -p "$BUILD_DIR" "$BUILD_DIR_HOST"
+	local logfile="$(basename $pkg).log"
+	deptest_make "package/$pkg/compile" "$logfile"
+	if [ $? -eq 0 ]; then
+		( cd "$STAMP_DIR_SUCCESS"; ln -s "../$LOG_DIR_NAME/$logfile" "./$pkg" )
+	else
+		( cd "$STAMP_DIR_FAILED"; ln -s "../$LOG_DIR_NAME/$logfile" "./$pkg" )
+		echo "Building package $pkg FAILED"
+	fi
+}
+
+# parse commandline options
+packages=
+lean_test=0
+force=0
+nrjobs=1
+while [ $# -ne 0 ]; do
+	case "$1" in
+	--help|-h)
+		usage
+		exit 0
+		;;
+	--lean)
+		lean_test=1
+		;;
+	--force)
+		force=1
+		;;
+	-j*)
+		if [ -n "${1:2}" ]; then
+			nrjobs="${1:2}"
+		else
+			shift
+			nrjobs="$1"
+		fi
+		;;
+	*)
+		packages="$packages $1"
+		;;
+	esac
+	shift
+done
+
+[ -f "$BASEDIR/include/toplevel.mk" ] || \
+	die "Error: Could not find buildsystem base directory"
+[ -f "$BASEDIR/.config" ] || \
+	die "The buildsystem is not configured. Please run make menuconfig."
+cd "$BASEDIR" || die "Failed to enter base directory"
+
+mkdir -p "$STAMP_DIR_SUCCESS" "$STAMP_DIR_FAILED" "$STAMP_DIR_BLACKLIST" \
+	"$BIN_DIR" "$LOG_DIR"
+
+bootstrap_deptest_make()
+{
+	local target="$1"
+	shift
+	local logfile="bootstrap-deptest-$(echo "$target" | tr / -).log"
+	echo "deptest-make $target"
+	deptest_make "$target" "$logfile" "$@" || \
+		die "make $target failed, please check $logfile"
+}
+
+bootstrap_native_make()
+{
+	local target="$1"
+	shift
+	local logfile="bootstrap-native-$(echo "$target" | tr / -).log"
+	echo "make $target"
+	make -j$nrjobs "$target" \
+		V=99 "$@" >"$LOG_DIR/$logfile" 2>&1 || \
+		die "make $target failed, please check $logfile"
+}
+
+[ -d "$STAGING_DIR_HOST_TMPL" ] || {
+	echo "Bootstrapping build environment..."
+	rm -rf "$STAGING_DIR" "$STAGING_DIR_HOST" "$BUILD_DIR" "$BUILD_DIR_HOST" "$KERNEL_BUILD_DIR"
+	mkdir -p "$STAGING_DIR" "$STAGING_DIR_HOST" \
+		"$BUILD_DIR" "$BUILD_DIR_HOST" "$KERNEL_BUILD_DIR"
+	bootstrap_native_make tools/install
+	bootstrap_native_make toolchain/install
+	bootstrap_deptest_make tools/install
+	bootstrap_deptest_make target/linux/install
+	cp -al "$STAGING_DIR_HOST" "$STAGING_DIR_HOST_TMPL"
+	rm -rf "$STAGING_DIR" "$STAGING_DIR_HOST" "$BUILD_DIR" "$BUILD_DIR_HOST"
+	echo "Build environment OK."
+}
+
+if [ -z "$packages" ]; then
+	# iterate over all packages
+	for pkg in `cat tmp/.packagedeps  | grep CONFIG_PACKAGE | grep -v curdir | sed -e 's,.*[/=]\s*,,' | sort -u`; do
+		test_package "$pkg"
+	done
+else
+	# only check the specified packages
+	for pkg in $packages; do
+		test_package "$pkg"
+	done
+fi
diff --git a/scripts/diffconfig.sh b/scripts/diffconfig.sh
new file mode 100755
index 0000000..7ffe9dd
--- /dev/null
+++ b/scripts/diffconfig.sh
@@ -0,0 +1,17 @@
+#!/bin/sh
+make ./scripts/config/conf >/dev/null || { make ./scripts/config/conf; exit 1; }
+grep \^CONFIG_TARGET_ .config | head -n3 > tmp/.diffconfig.head
+grep \^CONFIG_TARGET_DEVICE_ .config >> tmp/.diffconfig.head
+grep '^CONFIG_ALL=y' .config >> tmp/.diffconfig.head
+grep '^CONFIG_ALL_KMODS=y' .config >> tmp/.diffconfig.head
+grep '^CONFIG_ALL_NONSHARED=y' .config >> tmp/.diffconfig.head
+grep '^CONFIG_DEVEL=y' .config >> tmp/.diffconfig.head
+grep '^CONFIG_TOOLCHAINOPTS=y' .config >> tmp/.diffconfig.head
+grep '^CONFIG_BUSYBOX_CUSTOM=y' .config >> tmp/.diffconfig.head
+grep '^CONFIG_TARGET_PER_DEVICE_ROOTFS=y' .config >> tmp/.diffconfig.head
+./scripts/config/conf --defconfig=tmp/.diffconfig.head -w tmp/.diffconfig.stage1 Config.in >/dev/null
+./scripts/kconfig.pl '>+' tmp/.diffconfig.stage1 .config >> tmp/.diffconfig.head
+./scripts/config/conf --defconfig=tmp/.diffconfig.head -w tmp/.diffconfig.stage2 Config.in >/dev/null
+./scripts/kconfig.pl '>' tmp/.diffconfig.stage2 .config >> tmp/.diffconfig.head
+cat tmp/.diffconfig.head
+rm -f tmp/.diffconfig tmp/.diffconfig.head
diff --git a/scripts/dl_cleanup.py b/scripts/dl_cleanup.py
new file mode 100755
index 0000000..6d4f96c
--- /dev/null
+++ b/scripts/dl_cleanup.py
@@ -0,0 +1,332 @@
+#!/usr/bin/env python3
+"""
+# OpenWrt download directory cleanup utility.
+# Delete all but the very last version of the program tarballs.
+#
+# Copyright (C) 2010-2015 Michael Buesch <m@bues.ch>
+# Copyright (C) 2013-2015 OpenWrt.org
+"""
+
+from __future__ import print_function
+
+import sys
+import os
+import re
+import getopt
+import shutil
+
+# Commandline options
+opt_dryrun = False
+
+
+def parseVer_1234(match, filepath):
+    progname = match.group(1)
+    progversion = (
+        (int(match.group(2)) << 64)
+        | (int(match.group(3)) << 48)
+        | (int(match.group(4)) << 32)
+        | (int(match.group(5)) << 16)
+    )
+    return (progname, progversion)
+
+
+def parseVer_123(match, filepath):
+    progname = match.group(1)
+    try:
+        patchlevel = match.group(5)
+    except IndexError as e:
+        patchlevel = None
+    if patchlevel:
+        patchlevel = ord(patchlevel[0])
+    else:
+        patchlevel = 0
+    progversion = (
+        (int(match.group(2)) << 64)
+        | (int(match.group(3)) << 48)
+        | (int(match.group(4)) << 32)
+        | patchlevel
+    )
+    return (progname, progversion)
+
+
+def parseVer_12(match, filepath):
+    progname = match.group(1)
+    try:
+        patchlevel = match.group(4)
+    except IndexError as e:
+        patchlevel = None
+    if patchlevel:
+        patchlevel = ord(patchlevel[0])
+    else:
+        patchlevel = 0
+    progversion = (int(match.group(2)) << 64) | (int(match.group(3)) << 48) | patchlevel
+    return (progname, progversion)
+
+
+def parseVer_r(match, filepath):
+    progname = match.group(1)
+    progversion = int(match.group(2)) << 64
+    return (progname, progversion)
+
+
+def parseVer_ymd_GIT_SHASUM(match, filepath):
+    progname = match.group(1)
+    progversion = (
+        (int(match.group(2)) << 64)
+        | (int(match.group(3)) << 48)
+        | (int(match.group(4)) << 32)
+    )
+    return (progname, progversion)
+
+
+def parseVer_ymd(match, filepath):
+    progname = match.group(1)
+    progversion = (
+        (int(match.group(2)) << 64)
+        | (int(match.group(3)) << 48)
+        | (int(match.group(4)) << 32)
+    )
+    return (progname, progversion)
+
+
+def parseVer_GIT(match, filepath):
+    progname = match.group(1)
+    st = os.stat(filepath)
+    progversion = int(st.st_mtime) << 64
+    return (progname, progversion)
+
+
+extensions = (
+    ".tar.gz",
+    ".tar.bz2",
+    ".tar.xz",
+    ".tar.zst",
+    ".orig.tar.gz",
+    ".orig.tar.bz2",
+    ".orig.tar.xz",
+    ".zip",
+    ".tgz",
+    ".tbz",
+    ".txz",
+)
+
+versionRegex = (
+    (re.compile(r"(gcc[-_]\d+)\.(\d+)\.(\d+)"), parseVer_12),  # gcc.1.2
+    (re.compile(r"(linux[-_]\d+\.\d+)\.(\d+)"), parseVer_r),  # linux.1
+    (re.compile(r"(.+)[-_](\d+)\.(\d+)\.(\d+)\.(\d+)"), parseVer_1234),  # xxx-1.2.3.4
+    (
+        re.compile(r"(.+)[-_](\d\d\d\d)-?(\d\d)-?(\d\d)-"),
+        parseVer_ymd_GIT_SHASUM,
+    ),  # xxx-YYYY-MM-DD-GIT_SHASUM
+    (re.compile(r"(.+)[-_](\d\d\d\d)-?(\d\d)-?(\d\d)"), parseVer_ymd),  # xxx-YYYY-MM-DD
+    (re.compile(r"(.+)[-_]([0-9a-fA-F]{40,40})"), parseVer_GIT),  # xxx-GIT_SHASUM
+    (re.compile(r"(.+)[-_](\d+)\.(\d+)\.(\d+)(\w?)"), parseVer_123),  # xxx-1.2.3a
+    (re.compile(r"(.+)[-_]v(\d+)\.(\d+)\.(\d+)(\w?)"), parseVer_123),  # xxx-v1.2.3a
+    (re.compile(r"(.+)[-_](\d+)_(\d+)_(\d+)"), parseVer_123),  # xxx-1_2_3
+    (re.compile(r"(.+)[-_](\d+)\.(\d+)(\w?)"), parseVer_12),  # xxx-1.2a
+    (re.compile(r"(.+)[-_]v(\d+)\.(\d+)(\w?)"), parseVer_12),  # xxx-v1.2a
+    (re.compile(r"(.+)[-_]r?(\d+)"), parseVer_r),  # xxx-r1111
+)
+
+blacklist = [
+    ("wl_apsta", re.compile(r"wl_apsta.*")),
+    (".fw", re.compile(r".*\.fw")),
+    (".arm", re.compile(r".*\.arm")),
+    (".bin", re.compile(r".*\.bin")),
+    ("rt-firmware", re.compile(r"RT[\d\w]+_Firmware.*")),
+]
+
+
+class EntryParseError(Exception):
+    pass
+
+
+class Entry:
+    def __init__(self, directory, builddir, filename):
+        self.directory = directory
+        self.filename = filename
+        self.builddir = builddir
+        self.progname = ""
+        self.fileext = ""
+        self.filenoext = ""
+
+        if os.path.isdir(self.getPath()):
+            self.filenoext = filename
+        else:
+            for ext in extensions:
+                if filename.endswith(ext):
+                    filename = filename[0 : 0 - len(ext)]
+                    self.filenoext = filename
+                    self.fileext = ext
+                    break
+            else:
+                print(self.filename, "has an unknown file-extension")
+                raise EntryParseError("ext")
+        for (regex, parseVersion) in versionRegex:
+            match = regex.match(filename)
+            if match:
+                (self.progname, self.version) = parseVersion(
+                    match, directory + "/" + filename + self.fileext
+                )
+                break
+        else:
+            print(self.filename, "has an unknown version pattern")
+            raise EntryParseError("ver")
+
+    def getPath(self):
+        return (self.directory + "/" + self.filename).replace("//", "/")
+
+    def getBuildPaths(self):
+        paths = []
+        for subdir in os.scandir(self.builddir):
+            package_build_dir = os.path.join(subdir.path, self.filenoext)
+            if os.path.exists(package_build_dir):
+                paths.append(package_build_dir)
+        return paths
+
+    def deleteFile(self):
+        path = self.getPath()
+        print("Deleting", path)
+        if not opt_dryrun:
+            if os.path.isdir(path):
+                shutil.rmtree(path)
+            else:
+                os.unlink(path)
+
+    def deleteBuildDir(self):
+        paths = self.getBuildPaths()
+        for path in paths:
+            print("Deleting BuildDir", path)
+            if not opt_dryrun:
+                    shutil.rmtree(path)
+
+    def __ge__(self, y):
+        return self.version >= y.version
+
+
+def usage():
+    print("OpenWrt download directory cleanup utility")
+    print("Usage: " + sys.argv[0] + " [OPTIONS] <path/to/dl>")
+    print("")
+    print(" -d|--dry-run            Do a dry-run. Don't delete any files")
+    print(" -B|--show-blacklist     Show the blacklist and exit")
+    print(" -w|--whitelist ITEM     Remove ITEM from blacklist")
+    print(
+        " -D|--download-dir       Provide path to dl dir to clean also the build directory"
+    )
+    print(
+        " -b|--build-dir          Provide path to build dir to clean also the build directory"
+    )
+
+
+def main(argv):
+    global opt_dryrun
+
+    try:
+        (opts, args) = getopt.getopt(
+            argv[1:],
+            "hdBw:D:b:",
+            [
+                "help",
+                "dry-run",
+                "show-blacklist",
+                "whitelist=",
+                "download-dir=",
+                "build-dir=",
+            ],
+        )
+    except getopt.GetoptError as e:
+        usage()
+        return 1
+
+    directory = "dl/"
+    builddir = "build_dir/"
+
+    for (o, v) in opts:
+        if o in ("-h", "--help"):
+            usage()
+            return 0
+        if o in ("-d", "--dry-run"):
+            opt_dryrun = True
+        if o in ("-w", "--whitelist"):
+            for i in range(0, len(blacklist)):
+                (name, regex) = blacklist[i]
+                if name == v:
+                    del blacklist[i]
+                    break
+            else:
+                print("Whitelist error: Item", v, "is not in blacklist")
+                return 1
+        if o in ("-B", "--show-blacklist"):
+            for (name, regex) in blacklist:
+                sep = "\t\t"
+                if len(name) >= 8:
+                    sep = "\t"
+                print("%s%s(%s)" % (name, sep, regex.pattern))
+            return 0
+        if o in ("-D", "--download-dir"):
+            directory = v
+        if o in ("-b", "--build-dir"):
+            builddir = v
+
+    if args:
+        directory = args[0]
+
+    if not os.path.exists(directory):
+        print("Can't find download directory", directory)
+        return 1
+
+    if not os.path.exists(builddir):
+        print("Can't find build directory", builddir)
+        return 1
+
+    # Create a directory listing and parse the file names.
+    entries = []
+    for filename in os.listdir(directory):
+        if filename == "." or filename == "..":
+            continue
+        for (name, regex) in blacklist:
+            if regex.match(filename):
+                if opt_dryrun:
+                    print(filename, "is blacklisted")
+                break
+        else:
+            try:
+                entries.append(Entry(directory, builddir, filename))
+            except EntryParseError as e:
+                pass
+
+    # Create a map of programs
+    progmap = {}
+    for entry in entries:
+        if entry.progname in progmap.keys():
+            progmap[entry.progname].append(entry)
+        else:
+            progmap[entry.progname] = [
+                entry,
+            ]
+
+    # Traverse the program map and delete everything but the last version
+    for prog in progmap:
+        lastVersion = None
+        versions = progmap[prog]
+        for version in versions:
+            if lastVersion:
+                if os.path.isdir(lastVersion.getPath()) and not os.path.isdir(version.getPath()):
+                    continue
+            if lastVersion is None or version >= lastVersion:
+                lastVersion = version
+        if lastVersion:
+            for version in versions:
+                if version is not lastVersion:
+                    version.deleteFile()
+                    if builddir:
+                        version.deleteBuildDir()
+            if opt_dryrun:
+                print("Keeping", lastVersion.getPath())
+
+    return 0
+
+
+if __name__ == "__main__":
+    sys.exit(main(sys.argv))
diff --git a/scripts/dl_github_archive.py b/scripts/dl_github_archive.py
new file mode 100755
index 0000000..570ed3c
--- /dev/null
+++ b/scripts/dl_github_archive.py
@@ -0,0 +1,434 @@
+#!/usr/bin/env python3
+#
+# Copyright (c) 2018 Yousong Zhou <yszhou4tech@gmail.com>
+#
+# This is free software, licensed under the GNU General Public License v2.
+# See /LICENSE for more information.
+
+import argparse
+import calendar
+import datetime
+import errno
+import fcntl
+import hashlib
+import json
+import os
+import os.path
+import re
+import shutil
+import ssl
+import subprocess
+import sys
+import time
+import urllib.request
+
+TMPDIR = os.environ.get('TMP_DIR') or '/tmp'
+TMPDIR_DL = os.path.join(TMPDIR, 'dl')
+
+
+class PathException(Exception): pass
+class DownloadGitHubError(Exception): pass
+
+
+class Path(object):
+    """Context class for preparing and cleaning up directories.
+
+    If ```preclean` is ``False``, ``path`` will NOT be removed on context enter
+
+    If ``path`` ``isdir``, then it will be created on context enter.
+
+    If ``keep`` is True, then ``path`` will NOT be removed on context exit
+    """
+
+    def __init__(self, path, isdir=True, preclean=False, keep=False):
+        self.path = path
+        self.isdir = isdir
+        self.preclean = preclean
+        self.keep = keep
+
+    def __enter__(self):
+        if self.preclean:
+            self.rm_all(self.path)
+        if self.isdir:
+            self.mkdir_all(self.path)
+        return self
+
+    def __exit__(self, exc_type, exc_value, traceback):
+        if not self.keep:
+            self.rm_all(self.path)
+
+    @staticmethod
+    def mkdir_all(path):
+        """Same as mkdir -p."""
+        names = os.path.split(path)
+        p = ''
+        for name in names:
+            p = os.path.join(p, name)
+            Path._mkdir(p)
+
+    @staticmethod
+    def _rmdir_dir(dir_):
+        names = Path._listdir(dir_)
+        for name in names:
+            p = os.path.join(dir_, name)
+            Path.rm_all(p)
+        Path._rmdir(dir_)
+
+    @staticmethod
+    def _mkdir(path):
+        Path._os_func(os.mkdir, path, errno.EEXIST)
+
+    @staticmethod
+    def _rmdir(path):
+        Path._os_func(os.rmdir, path, errno.ENOENT)
+
+    @staticmethod
+    def _remove(path):
+        Path._os_func(os.remove, path, errno.ENOENT)
+
+    @staticmethod
+    def _listdir(path):
+        return Path._os_func(os.listdir, path, errno.ENOENT, default=[])
+
+    @staticmethod
+    def _os_func(func, path, errno, default=None):
+        """Call func(path) in an idempotent way.
+
+        On exception ``ex``, if the type is OSError and ``ex.errno == errno``,
+        return ``default``, otherwise, re-raise
+        """
+        try:
+            return func(path)
+        except OSError as e:
+            if e.errno == errno:
+                return default
+            else:
+                raise
+
+    @staticmethod
+    def rm_all(path):
+        """Same as rm -r."""
+        if os.path.islink(path):
+            Path._remove(path)
+        elif os.path.isdir(path):
+            Path._rmdir_dir(path)
+        else:
+            Path._remove(path)
+
+    @staticmethod
+    def untar(path, into=None):
+        """Extract tarball at ``path`` into subdir ``into``.
+
+        return subdir name if and only if there exists one, otherwise raise PathException
+        """
+        args = ('tar', '-C', into, '-xzf', path, '--no-same-permissions')
+        subprocess.check_call(args, preexec_fn=lambda: os.umask(0o22))
+        dirs = os.listdir(into)
+        if len(dirs) == 1:
+            return dirs[0]
+        else:
+            raise PathException('untar %s: expecting a single subdir, got %s' % (path, dirs))
+
+    @staticmethod
+    def tar(path, subdir, into=None, ts=None):
+        """Pack ``path`` into tarball ``into``."""
+        # --sort=name requires a recent build of GNU tar
+        args = ['tar', '--numeric-owner', '--owner=0', '--group=0', '--sort=name', '--mode=a-s']
+        args += ['-C', path, '-cf', into, subdir]
+        envs = os.environ.copy()
+        if ts is not None:
+            args.append('--mtime=@%d' % ts)
+        if into.endswith('.zst'):
+            args.append('-I zstd -T0 --ultra -20')
+        elif into.endswith('.xz'):
+            envs['XZ_OPT'] = '-7e'
+            args.append('-J')
+        elif into.endswith('.bz2'):
+            args.append('-j')
+        elif into.endswith('.gz'):
+            args.append('-z')
+            envs['GZIP'] = '-n'
+        else:
+            raise PathException('unknown compression type %s' % into)
+        subprocess.check_call(args, env=envs)
+
+
+class GitHubCommitTsCache(object):
+    __cachef = 'github.commit.ts.cache'
+    __cachen = 2048
+
+    def __init__(self):
+        Path.mkdir_all(TMPDIR_DL)
+        self.cachef = os.path.join(TMPDIR_DL, self.__cachef)
+        self.cache = {}
+
+    def get(self, k):
+        """Get timestamp with key ``k``."""
+        fileno = os.open(self.cachef, os.O_RDONLY | os.O_CREAT)
+        with os.fdopen(fileno) as fin:
+            try:
+                fcntl.lockf(fileno, fcntl.LOCK_SH)
+                self._cache_init(fin)
+                if k in self.cache:
+                    ts = self.cache[k][0]
+                    return ts
+            finally:
+                fcntl.lockf(fileno, fcntl.LOCK_UN)
+        return None
+
+    def set(self, k, v):
+        """Update timestamp with ``k``."""
+        fileno = os.open(self.cachef, os.O_RDWR | os.O_CREAT)
+        with os.fdopen(fileno, 'w+') as f:
+            try:
+                fcntl.lockf(fileno, fcntl.LOCK_EX)
+                self._cache_init(f)
+                self.cache[k] = (v, int(time.time()))
+                self._cache_flush(f)
+            finally:
+                fcntl.lockf(fileno, fcntl.LOCK_UN)
+
+    def _cache_init(self, fin):
+        for line in fin:
+            k, ts, updated = line.split()
+            ts = int(ts)
+            updated = int(updated)
+            self.cache[k] = (ts, updated)
+
+    def _cache_flush(self, fout):
+        cache = sorted(self.cache.items(), key=lambda a: a[1][1])
+        cache = cache[:self.__cachen]
+        self.cache = {}
+        os.ftruncate(fout.fileno(), 0)
+        fout.seek(0, os.SEEK_SET)
+        for k, ent in cache:
+            ts = ent[0]
+            updated = ent[1]
+            line = '{0} {1} {2}\n'.format(k, ts, updated)
+            fout.write(line)
+
+
+class DownloadGitHubTarball(object):
+    """Download and repack archive tarball from GitHub.
+
+    Compared with the method of packing after cloning the whole repo, this
+    method is more friendly to users with fragile internet connection.
+
+    However, there are limitations with this method
+
+     - GitHub imposes a 60 reqs/hour limit for unauthenticated API access.
+       This affects fetching commit date for reproducible tarballs.  Download
+       through the archive link is not affected.
+
+     - GitHub archives do not contain source codes for submodules.
+
+     - GitHub archives seem to respect .gitattributes and ignore paths with
+       export-ignore attributes.
+
+    For the first two issues, the method will fail loudly to allow fallback to
+    clone-then-pack method.
+
+    As for the 3rd issue, to make sure that this method only produces identical
+    tarballs as the fallback method, we require the expected hash value to be
+    supplied.  That means the first tarball will need to be prepared by the
+    clone-then-pack method
+    """
+
+    __repo_url_regex = re.compile(r'^(?:https|git)://github.com/(?P<owner>[^/]+)/(?P<repo>[^/]+)')
+
+    def __init__(self, args):
+        self.dl_dir = args.dl_dir
+        self.version = args.version
+        self.subdir = args.subdir
+        self.source = args.source
+        self.submodules = args.submodules
+        self.url = args.url
+        self._init_owner_repo()
+        self.xhash = args.hash
+        self._init_hasher()
+        self.commit_ts = None           # lazy load commit timestamp
+        self.commit_ts_cache = GitHubCommitTsCache()
+        self.name = 'github-tarball'
+
+    def download(self):
+        """Download and repack GitHub archive tarball."""
+        if self.submodules and self.submodules != ['skip']:
+            raise self._error('Fetching submodules is not yet supported')
+        self._init_commit_ts()
+        with Path(TMPDIR_DL, keep=True) as dir_dl:
+            # fetch tarball from GitHub
+            tarball_path = os.path.join(dir_dl.path, self.subdir + '.tar.gz.dl')
+            with Path(tarball_path, isdir=False):
+                self._fetch(tarball_path)
+                # unpack
+                d = os.path.join(dir_dl.path, self.subdir + '.untar')
+                with Path(d, preclean=True) as dir_untar:
+                    tarball_prefix = Path.untar(tarball_path, into=dir_untar.path)
+                    dir0 = os.path.join(dir_untar.path, tarball_prefix)
+                    dir1 = os.path.join(dir_untar.path, self.subdir)
+                    # submodules check
+                    if self.submodules != ['skip'] and self._has_submodule(dir0):
+                        raise self._error('Fetching submodules is not yet supported')
+                    # rename subdir
+                    os.rename(dir0, dir1)
+                    # repack
+                    into=os.path.join(TMPDIR_DL, self.source)
+                    Path.tar(dir_untar.path, self.subdir, into=into, ts=self.commit_ts)
+                    try:
+                        self._hash_check(into)
+                    except Exception:
+                        Path.rm_all(into)
+                        raise
+                    # move to target location
+                    file1 = os.path.join(self.dl_dir, self.source)
+                    if into != file1:
+                        shutil.move(into, file1)
+
+    def _has_submodule(self, dir_):
+        m = os.path.join(dir_, '.gitmodules')
+        try:
+            st = os.stat(m)
+            return st.st_size > 0
+        except OSError as e:
+            return e.errno != errno.ENOENT
+
+    def _init_owner_repo(self):
+        m = self.__repo_url_regex.search(self.url)
+        if m is None:
+            raise self._error('Invalid github url: {}'.format(self.url))
+        owner = m.group('owner')
+        repo = m.group('repo')
+        if repo.endswith('.git'):
+            repo = repo[:-4]
+        self.owner = owner
+        self.repo = repo
+
+    def _init_hasher(self):
+        xhash = self.xhash
+        if len(xhash) == 64:
+            self.hasher = hashlib.sha256()
+        elif len(xhash) == 32:
+            self.hasher = hashlib.md5()
+        else:
+            raise self._error('Requires sha256sum for verification')
+        self.xhash = xhash
+
+    def _hash_check(self, f):
+        with open(f, 'rb') as fin:
+            while True:
+                d = fin.read(4096)
+                if not d:
+                    break
+                self.hasher.update(d)
+        xhash = self.hasher.hexdigest()
+        if xhash != self.xhash:
+            raise self._error('Wrong hash (probably caused by .gitattributes), expecting {}, got {}'.format(self.xhash, xhash))
+
+    def _init_commit_ts(self):
+        if self.commit_ts is not None:
+            return
+        # GitHub provides 2 APIs[1,2] for fetching commit data.  API[1] is more
+        # terse while API[2] provides more verbose info such as commit diff
+        # etc.  That's the main reason why API[1] is preferred: the response
+        # size is predictable.
+        #
+        # However, API[1] only accepts complete commit sha1sum as the parameter
+        # while API[2] is more liberal accepting also partial commit id and
+        # tags, etc.
+        #
+        # [1] Get a single commit, Repositories, https://developer.github.com/v3/repos/commits/#get-a-single-commit
+        # [2] Git Commits, Git Data, https://developer.github.com/v3/git/commits/#get-a-commit
+        apis = [
+            {
+                'url': self._make_repo_url_path('git', 'commits', self.version),
+                'attr_path': ('committer', 'date'),
+            }, {
+                'url': self._make_repo_url_path('commits', self.version),
+                'attr_path': ('commit', 'committer', 'date'),
+            },
+        ]
+        version_is_sha1sum = len(self.version) == 40
+        if not version_is_sha1sum:
+            apis.insert(0, apis.pop())
+        reasons = ''
+        for api in apis:
+            url = api['url']
+            attr_path = api['attr_path']
+            try:
+                ct = self.commit_ts_cache.get(url)
+                if ct is not None:
+                    self.commit_ts = ct
+                    return
+                ct = self._init_commit_ts_remote_get(url, attr_path)
+                self.commit_ts = ct
+                self.commit_ts_cache.set(url, ct)
+                return
+            except Exception as e:
+                reasons += '\n' + ("  {}: {}".format(url, e))
+        raise self._error('Cannot fetch commit ts:{}'.format(reasons))
+
+    def _init_commit_ts_remote_get(self, url, attrpath):
+        resp = self._make_request(url)
+        data = resp.read()
+        date = json.loads(data)
+        for attr in attrpath:
+            date = date[attr]
+        date = datetime.datetime.strptime(date, '%Y-%m-%dT%H:%M:%SZ')
+        date = date.timetuple()
+        ct = calendar.timegm(date)
+        return ct
+
+    def _fetch(self, path):
+        """Fetch tarball of the specified version ref."""
+        ref = self.version
+        url = self._make_repo_url_path('tarball', ref)
+        resp = self._make_request(url)
+        with open(path, 'wb') as fout:
+            while True:
+                d = resp.read(4096)
+                if not d:
+                    break
+                fout.write(d)
+
+    def _make_repo_url_path(self, *args):
+        url = '/repos/{0}/{1}'.format(self.owner, self.repo)
+        if args:
+            url += '/' + '/'.join(args)
+        return url
+
+    def _make_request(self, path):
+        """Request GitHub API endpoint on ``path``."""
+        url = 'https://api.github.com' + path
+        headers = {
+            'Accept': 'application/vnd.github.v3+json',
+            'User-Agent': 'OpenWrt',
+        }
+        req = urllib.request.Request(url, headers=headers)
+        sslcontext = ssl._create_unverified_context()
+        fileobj = urllib.request.urlopen(req, context=sslcontext)
+        return fileobj
+
+    def _error(self, msg):
+        return DownloadGitHubError('{}: {}'.format(self.source, msg))
+
+
+def main():
+    parser = argparse.ArgumentParser()
+    parser.add_argument('--dl-dir', default=os.getcwd(), help='Download dir')
+    parser.add_argument('--url', help='Download URL')
+    parser.add_argument('--subdir', help='Source code subdir name')
+    parser.add_argument('--version', help='Source code version')
+    parser.add_argument('--source', help='Source tarball filename')
+    parser.add_argument('--hash', help='Source tarball\'s expected sha256sum')
+    parser.add_argument('--submodules', nargs='*', help='List of submodules, or "skip"')
+    args = parser.parse_args()
+    try:
+        method = DownloadGitHubTarball(args)
+        method.download()
+    except Exception as ex:
+        sys.stderr.write('{}: Download from {} failed\n'.format(args.source, args.url))
+        sys.stderr.write('{}\n'.format(ex))
+        sys.exit(1)
+
+if __name__ == '__main__':
+    main()
diff --git a/scripts/download-check-artifact.sh b/scripts/download-check-artifact.sh
new file mode 100755
index 0000000..bc0918b
--- /dev/null
+++ b/scripts/download-check-artifact.sh
@@ -0,0 +1,159 @@
+#!/usr/bin/env bash
+# Script to perform verified file downloads.
+# Exit codes:
+#  0 - File downloaded successfully and verified
+#  1 - Failed to download requested file
+#  2 - Failed to download sha256sums file
+#  3 - Failed to download sha256sums.gpg file
+#  4 - GnuPG is available but fails to verify the signature (missing pubkey, file integrity error, ...)
+#  5 - The checksums do not match
+#  6 - Unable to copy the requested file to its final destination
+#  254 - The script got interrupted by a signal
+#  255 - A suitable download or checksum utility is missing
+
+[ -n "$1" ] || {
+	echo "$0 - Download and verify build artifacts"
+	echo "Usage: $0 <url>" >&2
+	exit 1
+}
+
+finish() {
+	[ -e "/tmp/verify.$$" ] && {
+		echo "Cleaning up."
+		rm -r "/tmp/verify.$$"
+	}
+	exit "$1"
+}
+
+trap "finish 254" INT TERM
+
+destdir="$(pwd)"
+image_url="$1"
+image_file="${image_url##*/}"
+sha256_url="${image_url%/*}/sha256sums"
+gpgsig_url="${image_url%/*}/sha256sums.asc"
+keyserver_url="hkp://keyserver.ubuntu.com"
+
+# Find a suitable download utility
+if which curl >/dev/null; then
+	download() { curl --progress-bar -o "$1" "$2"; }
+elif which wget >/dev/null; then
+	download() { wget -O "$1" "$2"; }
+elif which fetch >/dev/null; then
+	download() { fetch -o "$1" "$2"; }
+else
+	echo "No suitable download utility found, cannot download files!" >&2
+	finish 255
+fi
+
+# Find a suitable checksum utility
+if which sha256sum >/dev/null; then
+	checksum() { sha256sum -c --ignore-missing "sha256sums"; }
+elif which shasum >/dev/null; then
+	checksum() {
+		local sum
+		sum="$(shasum -a 256 "$image_file")";
+		grep -xF "${sum%% *} *$image_file" "sha256sums";
+	}
+else
+	echo "No SHA256 checksum executable installed, cannot verify checksums!" >&2
+	finish 255
+fi
+
+# Check for gpg availability
+if which gpg >/dev/null; then
+	runpgp() { gpg "$@"; }
+else
+	runpgp() {
+		echo "WARNING: No GnuPG installed, cannot verify digital signature!" >&2
+		return 0
+	}
+fi
+
+tmpdir="$(mktemp -d)"
+cd "$tmpdir" || {
+	echo "Failed to create temporary directory!" >&2
+	finish 255
+}
+
+echo ""
+echo "1) Downloading artifact file"
+echo "========================="
+download "$image_file" "$image_url" || {
+	echo "Failed to download image file!" >&2
+	finish 1
+}
+
+echo ""
+echo "2) Downloading checksum file"
+echo "============================"
+download "sha256sums" "$sha256_url" || {
+	echo "Failed to download checksum file!" >&2
+	finish 2
+}
+
+echo ""
+echo "3) Downloading the GPG signature"
+echo "================================"
+download "sha256sums.gpg" "$gpgsig_url" || {
+	echo "Failed to download GPG signature!" >&2
+	finish 3
+}
+
+echo ""
+echo "4) Verifying GPG signature"
+echo "=========================="
+missing_key=$(runpgp --status-fd 1 --with-fingerprint --verify \
+	"sha256sums.gpg" "sha256sums" 2>/dev/null | sed -ne 's!^.* NO_PUBKEY !!p')
+
+if [ -n "$missing_key" ]; then
+	echo "The signature was signed by a public key with the id $missing_key" >&2
+	echo "which is not present on this system."                              >&2
+	echo ""                                                                  >&2
+
+	echo "Provide a public keyserver url below or press enter to accept the" >&2
+	echo "default suggestion. Hit Ctrl-C to abort the operation."            >&2
+	echo ""                                                                  >&2
+
+	while true; do
+		printf 'Keyserver to use? [%s] > ' "$keyserver_url"
+		read -r url; case "${url:-$keyserver_url}" in
+			hkp://*)
+				gpg --keyserver "${url:-$keyserver_url}" --recv-keys "$missing_key" || {
+					echo "Failed to download public key." >&2
+					finish 7
+				}
+				break
+			;;
+			*)
+				echo "Expecting a key server url in the form 'hkp://hostname'." >&2
+			;;
+		esac
+	done
+fi
+
+runpgp --with-fingerprint --verify "sha256sums.gpg" "sha256sums" || {
+	echo "Failed to verify checksum file with GPG signature!" >&2
+	finish 4
+}
+
+echo ""
+echo "5) Verifying SHA256 checksum"
+echo "============================"
+checksum || {
+	echo "Checksums do not match!" >&2
+	finish 5
+}
+
+cp "$image_file" "$destdir/$image_file" || {
+	echo "Failed to write '$destdir/$image_file'" >&2
+	finish 6
+}
+
+echo ""
+echo "Verification done!"
+echo "=================="
+echo "Downloaded artifact placed in '$destdir/$image_file'."
+echo ""
+
+finish 0
diff --git a/scripts/download.pl b/scripts/download.pl
new file mode 100755
index 0000000..c6c9b8e
--- /dev/null
+++ b/scripts/download.pl
@@ -0,0 +1,330 @@
+#!/usr/bin/env perl
+# 
+# Copyright (C) 2006 OpenWrt.org
+# Copyright (C) 2016 LEDE project
+#
+# This is free software, licensed under the GNU General Public License v2.
+# See /LICENSE for more information.
+#
+
+use strict;
+use warnings;
+use File::Basename;
+use File::Copy;
+use File::Path;
+use Text::ParseWords;
+use JSON::PP;
+
+@ARGV > 2 or die "Syntax: $0 <target dir> <filename> <hash> <url filename> [<mirror> ...]\n";
+
+my $url_filename;
+my $target = glob(shift @ARGV);
+my $filename = shift @ARGV;
+my $file_hash = shift @ARGV;
+$url_filename = shift @ARGV unless $ARGV[0] =~ /:\/\//;
+my $scriptdir = dirname($0);
+my @mirrors;
+my $ok;
+
+my $check_certificate = $ENV{DOWNLOAD_CHECK_CERTIFICATE} eq "y";
+my $custom_tool = $ENV{DOWNLOAD_TOOL_CUSTOM};
+my $download_tool;
+
+$url_filename or $url_filename = $filename;
+
+sub localmirrors {
+	my @mlist;
+	open LM, "$scriptdir/localmirrors" and do {
+	    while (<LM>) {
+			chomp $_;
+			push @mlist, $_ if $_;
+		}
+		close LM;
+	};
+	open CONFIG, "<".$ENV{'TOPDIR'}."/.config" and do {
+		while (<CONFIG>) {
+			/^CONFIG_LOCALMIRROR="(.+)"/ and do {
+				chomp;
+				my @local_mirrors = split(/;/, $1);
+				push @mlist, @local_mirrors;
+			};
+		}
+		close CONFIG;
+	};
+
+	my $mirror = $ENV{'DOWNLOAD_MIRROR'};
+	$mirror and push @mlist, split(/;/, $mirror);
+
+	return @mlist;
+}
+
+sub projectsmirrors {
+	my $project = shift;
+	my $append = shift;
+
+	open (PM, "$scriptdir/projectsmirrors.json") ||
+		die "Can´t open $scriptdir/projectsmirrors.json: $!\n";
+	local $/;
+	my $mirror_json = <PM>;
+	my $mirror = decode_json $mirror_json;
+
+	foreach (@{$mirror->{$project}}) {
+		push @mirrors, $_ . "/" . ($append or "");
+	}
+}
+
+sub which($) {
+	my $prog = shift;
+	my $res = `command -v $prog`;
+	$res or return undef;
+	return $res;
+}
+
+sub hash_cmd() {
+	my $len = length($file_hash);
+	my $cmd;
+
+	$len == 64 and return "$ENV{'MKHASH'} sha256";
+	$len == 32 and return "$ENV{'MKHASH'} md5";
+	return undef;
+}
+
+sub tool_present {
+	my $tool_name = shift;
+	my $compare_line = shift;
+	my $present = 0;
+
+	if (open TOOL, "$tool_name --version 2>/dev/null |") {
+		if (defined(my $line = readline TOOL)) {
+			$present = 1 if $line =~ /^$compare_line /;
+		}
+		close TOOL;
+	}
+
+	return $present
+}
+
+sub select_tool {
+	$custom_tool =~ tr/"//d;
+	if ($custom_tool) {
+		return $custom_tool;
+	}
+
+	# Try to use curl if available
+	if (tool_present("curl", "curl")) {
+		return "curl";
+	}
+
+	# No tool found, fallback to wget
+	return "wget";
+}
+
+sub download_cmd {
+	my $url = shift;
+	my $filename = shift;
+
+	if ($download_tool eq "curl") {
+		return (qw(curl -f --connect-timeout 20 --retry 5 --location),
+			$check_certificate ? () : '--insecure',
+			shellwords($ENV{CURL_OPTIONS} || ''),
+			$url);
+	} elsif ($download_tool eq "wget") {
+		return (qw(wget --tries=5 --timeout=20 --output-document=-),
+			$check_certificate ? () : '--no-check-certificate',
+			shellwords($ENV{WGET_OPTIONS} || ''),
+			$url);
+	} elsif ($download_tool eq "aria2c") {
+		my $additional_mirrors = join(" ", map "$_/$filename", @_);
+		my @chArray = ('a'..'z', 'A'..'Z', 0..9);
+		my $rfn = join '', "${filename}_", map{ $chArray[int rand @chArray] } 0..9;
+
+		@mirrors=();
+
+		return join(" ", "[ -d $ENV{'TMPDIR'}/aria2c ] || mkdir $ENV{'TMPDIR'}/aria2c;",
+			"touch $ENV{'TMPDIR'}/aria2c/${rfn}_spp;",
+			qw(aria2c --stderr -c -x2 -s10 -j10 -k1M), $url, $additional_mirrors,
+			$check_certificate ? () : '--check-certificate=false',
+			"--server-stat-of=$ENV{'TMPDIR'}/aria2c/${rfn}_spp",
+			"--server-stat-if=$ENV{'TMPDIR'}/aria2c/${rfn}_spp",
+			"--daemon=false --no-conf", shellwords($ENV{ARIA2C_OPTIONS} || ''),
+			"-d $ENV{'TMPDIR'}/aria2c -o $rfn;",
+			"cat $ENV{'TMPDIR'}/aria2c/$rfn;",
+			"rm $ENV{'TMPDIR'}/aria2c/$rfn $ENV{'TMPDIR'}/aria2c/${rfn}_spp");
+	} else {
+		return join(" ", $download_tool, $url);
+	}
+}
+
+my $hash_cmd = hash_cmd();
+$hash_cmd or ($file_hash eq "skip") or die "Cannot find appropriate hash command, ensure the provided hash is either a MD5 or SHA256 checksum.\n";
+
+sub download
+{
+	my $mirror = shift;
+	my $download_filename = shift;
+	my @additional_mirrors = @_;
+
+	$mirror =~ s!/$!!;
+
+	if ($mirror =~ s!^file://!!) {
+		if (! -d "$mirror") {
+			print STDERR "Wrong local cache directory -$mirror-.\n";
+			cleanup();
+			return;
+		}
+
+		if (! -d "$target") {
+			make_path($target);
+		}
+
+		if (! open TMPDLS, "find $mirror -follow -name $filename 2>/dev/null |") {
+			print("Failed to search for $filename in $mirror\n");
+			return;
+		}
+
+		my $link;
+
+		while (defined(my $line = readline TMPDLS)) {
+			chomp ($link = $line);
+			if ($. > 1) {
+				print("$. or more instances of $filename in $mirror found . Only one instance allowed.\n");
+				return;
+			}
+		}
+
+		close TMPDLS;
+
+		if (! $link) {
+			print("No instances of $filename found in $mirror.\n");
+			return;
+		}
+
+		print("Copying $filename from $link\n");
+		copy($link, "$target/$filename.dl");
+
+		$hash_cmd and do {
+			if (system("cat '$target/$filename.dl' | $hash_cmd > '$target/$filename.hash'")) {
+				print("Failed to generate hash for $filename\n");
+				return;
+			}
+		};
+	} else {
+		my @cmd = download_cmd("$mirror/$download_filename", $download_filename, @additional_mirrors);
+		print STDERR "+ ".join(" ",@cmd)."\n";
+		open(FETCH_FD, '-|', @cmd) or die "Cannot launch aria2c, curl or wget.\n";
+		$hash_cmd and do {
+			open MD5SUM, "| $hash_cmd > '$target/$filename.hash'" or die "Cannot launch $hash_cmd.\n";
+		};
+		open OUTPUT, "> $target/$filename.dl" or die "Cannot create file $target/$filename.dl: $!\n";
+		my $buffer;
+		while (read FETCH_FD, $buffer, 1048576) {
+			$hash_cmd and print MD5SUM $buffer;
+			print OUTPUT $buffer;
+		}
+		$hash_cmd and close MD5SUM;
+		close FETCH_FD;
+		close OUTPUT;
+
+		if ($? >> 8) {
+			print STDERR "Download failed.\n";
+			cleanup();
+			return;
+		}
+	}
+
+	$hash_cmd and do {
+		my $sum = `cat "$target/$filename.hash"`;
+		$sum =~ /^(\w+)\s*/ or die "Could not generate file hash\n";
+		$sum = $1;
+
+		if ($sum ne $file_hash) {
+			print STDERR "Hash of the downloaded file does not match (file: $sum, requested: $file_hash) - deleting download.\n";
+			cleanup();
+			return;
+		}
+	};
+
+	unlink "$target/$filename";
+	move("$target/$filename.dl", "$target/$filename");
+	cleanup();
+}
+
+sub cleanup
+{
+	unlink "$target/$filename.dl";
+	unlink "$target/$filename.hash";
+}
+
+@mirrors = localmirrors();
+
+foreach my $mirror (@ARGV) {
+	if ($mirror =~ /^\@SF\/(.+)$/) {
+		# give sourceforge a few more tries, because it redirects to different mirrors
+		for (1 .. 5) {
+			projectsmirrors '@SF', $1;
+		}
+	} elsif ($mirror =~ /^\@OPENWRT$/) {
+		# use OpenWrt source server directly
+	} elsif ($mirror =~ /^\@DEBIAN\/(.+)$/) {
+		projectsmirrors '@DEBIAN', $1;
+	} elsif ($mirror =~ /^\@APACHE\/(.+)$/) {
+		projectsmirrors '@APACHE', $1;
+	} elsif ($mirror =~ /^\@GITHUB\/(.+)$/) {
+		# give github a few more tries (different mirrors)
+		for (1 .. 5) {
+			projectsmirrors '@GITHUB', $1;
+		}
+	} elsif ($mirror =~ /^\@GNU\/(.+)$/) {
+		projectsmirrors '@GNU', $1;
+	} elsif ($mirror =~ /^\@SAVANNAH\/(.+)$/) {
+		projectsmirrors '@SAVANNAH', $1;
+	} elsif ($mirror =~ /^\@KERNEL\/(.+)$/) {
+		my @extra = ( $1 );
+		if ($filename =~ /linux-\d+\.\d+(?:\.\d+)?-rc/) {
+			push @extra, "$extra[0]/testing";
+		} elsif ($filename =~ /linux-(\d+\.\d+(?:\.\d+)?)/) {
+			push @extra, "$extra[0]/longterm/v$1";
+		}
+		foreach my $dir (@extra) {
+			projectsmirrors '@KERNEL', $dir;
+		}
+	} elsif ($mirror =~ /^\@GNOME\/(.+)$/) {
+		projectsmirrors '@GNOME', $1;
+	} else {
+		push @mirrors, $mirror;
+	}
+}
+
+projectsmirrors '@OPENWRT';
+
+if (-f "$target/$filename") {
+	$hash_cmd and do {
+		if (system("cat '$target/$filename' | $hash_cmd > '$target/$filename.hash'")) {
+			die "Failed to generate hash for $filename\n";
+		}
+
+		my $sum = `cat "$target/$filename.hash"`;
+		$sum =~ /^(\w+)\s*/ or die "Could not generate file hash\n";
+		$sum = $1;
+
+		cleanup();
+		exit 0 if $sum eq $file_hash;
+
+		die "Hash of the local file $filename does not match (file: $sum, requested: $file_hash) - deleting download.\n";
+		unlink "$target/$filename";
+	};
+}
+
+$download_tool = select_tool();
+
+while (!-f "$target/$filename") {
+	my $mirror = shift @mirrors;
+	$mirror or die "No more mirrors to try - giving up.\n";
+
+	download($mirror, $url_filename, @mirrors);
+	if (!-f "$target/$filename" && $url_filename ne $filename) {
+		download($mirror, $filename, @mirrors);
+	}
+}
+
+$SIG{INT} = \&cleanup;
diff --git a/scripts/dump-target-info.pl b/scripts/dump-target-info.pl
new file mode 100755
index 0000000..eec06ed
--- /dev/null
+++ b/scripts/dump-target-info.pl
@@ -0,0 +1,189 @@
+#!/usr/bin/env perl
+
+use strict;
+use warnings;
+use Cwd;
+
+my (%targets, %architectures, %kernels, %devices);
+
+$ENV{'TOPDIR'} = Cwd::getcwd();
+
+
+sub parse_targetinfo {
+	my ($target_dir, $subtarget) = @_;
+
+	if (open M, "make -C '$target_dir' --no-print-directory DUMP=1 TARGET_BUILD=1 SUBTARGET='$subtarget' |") {
+		my ($target_name, $target_arch, $target_kernel, $target_testing_kernel, @target_features);
+		while (defined(my $line = readline M)) {
+			chomp $line;
+
+			if ($line =~ /^Target: (.+)$/) {
+				$target_name = $1;
+			}
+			elsif ($line =~ /^Target-Arch-Packages: (.+)$/) {
+				$target_arch = $1;
+			}
+			elsif ($line =~ /^Linux-Version: (\d\.\d+)\.\d+$/) {
+				$target_kernel = $1;
+			}
+			elsif ($line =~ /^Linux-Testing-Version: (\d\.\d+)\.\d+$/) {
+				$target_testing_kernel = $1;
+			}
+			elsif ($line =~ /^Target-Features: (.+)$/) {
+				@target_features = split /\s+/, $1;
+			}
+			elsif ($line =~ /^@\@$/) {
+				if ($target_name && $target_arch && $target_kernel &&
+				    !grep { $_ eq 'broken' or $_ eq 'source-only' } @target_features) {
+					$targets{$target_name} = $target_arch;
+					$architectures{$target_arch} ||= [];
+					push @{$architectures{$target_arch}}, $target_name;
+					$kernels{$target_name} ||= [];
+					push @{$kernels{$target_name}}, $target_kernel;
+					if ($target_testing_kernel) {
+						push @{$kernels{$target_name}}, $target_testing_kernel;
+					}
+				}
+
+				undef $target_name;
+				undef $target_arch;
+				undef $target_kernel;
+				undef $target_testing_kernel;
+				@target_features = ();
+			}
+		}
+		close M;
+	}
+}
+
+sub parse_devices {
+	my ($target_dir, $subtarget) = @_;
+
+	if (open M, "make -C '$target_dir' --no-print-directory DUMP=1 TARGET_BUILD=1 SUBTARGET='$subtarget' V=s |") {
+		my ($device_profile, $device_name, @device_alt_names, $device_is_alt);
+		while (defined(my $line = readline M)) {
+			chomp $line;
+
+			if ($line =~ /^Target-Profile-Name: (.+)$/) {
+				$device_name = $1;
+			}
+			elsif ($line =~ /^Target-Profile: DEVICE_(.+)$/) {
+				$device_profile = $1;
+			}
+			# Logic behind this.
+			# DUMP duplicate info for each alternative device name and
+			# the alternative device name are printed first before the
+			# primary device name
+			# Alternative device titles always have the full list of
+			# all the alternative device name.
+			# The device name pattern for an alternative device name is
+			# Target-Profile-Name: ALT_NAME (PRIMARY_NAME)
+			# We compare the detected device name and check if it does
+			# match the alternative device name pattern with one of
+			# the alternative device name in Alternative device titles:
+			# If an alternative device name is detected,
+			# alternative device is skipped.
+			elsif ($line =~ /^Alternative device titles:$/) {
+				while (defined($line = readline M)) {
+					if ($line =~ /^- (.+)$/) {
+						if ($device_name =~ /^\Q$1\E \((.+)\)$/) {
+							$device_is_alt = 1;
+							last;
+						}
+						push @device_alt_names, $1;
+					}
+					else {
+						last;
+					}
+				}
+			}
+			if ($line =~ /^@\@$/) {
+				if ($device_name && $device_profile && ! $device_is_alt) {
+					push @{$devices{$device_profile}}, $device_name;
+
+					if (scalar @device_alt_names) {
+						foreach my $device_alt_name (sort values @device_alt_names) {
+							push @{$devices{$device_profile}}, $device_alt_name;
+						}
+					}
+				}
+
+				undef $device_name;
+				undef $device_profile;
+				undef $device_is_alt;
+				@device_alt_names = ();
+			}
+		}
+		close M;
+	}
+}
+
+sub get_targetinfo {
+	foreach my $target_makefile (glob "target/linux/*/Makefile") {
+		my ($target_dir) = $target_makefile =~ m!^(.+)/Makefile$!;
+		my @subtargets;
+
+		if (open M, "make -C '$target_dir' --no-print-directory DUMP=1 TARGET_BUILD=1 val.FEATURES V=s 2>/dev/null |") {
+			if (defined(my $line = readline M)) {
+				chomp $line;
+				if (grep { $_ eq 'broken' or $_ eq 'source-only' } split /\s+/, $line) {
+					next;
+				}
+			}
+		}
+
+		if (open M, "make -C '$target_dir' --no-print-directory DUMP=1 TARGET_BUILD=1 val.SUBTARGETS V=s 2>/dev/null |") {
+			if (defined(my $line = readline M)) {
+				chomp $line;
+				@subtargets = split /\s+/, $line;
+			}
+			close M;
+		}
+
+		push @subtargets, 'generic' if @subtargets == 0;
+
+		foreach my $subtarget (@subtargets) {
+			parse_targetinfo($target_dir, $subtarget);
+		}
+	}
+}
+
+sub get_devices {
+	my ($target_subtarget) = @_;
+	my ($target, $subtarget) = split /\//, $target_subtarget;
+
+	my ($target_dir) = "target/linux/" . $target;
+
+	parse_devices($target_dir, $subtarget)
+}
+
+if (@ARGV == 1 && $ARGV[0] eq 'targets') {
+	get_targetinfo();
+	foreach my $target_name (sort keys %targets) {
+		printf "%s %s\n", $target_name, $targets{$target_name};
+	}
+}
+elsif (@ARGV == 1 && $ARGV[0] eq 'architectures') {
+	get_targetinfo();
+	foreach my $target_arch (sort keys %architectures) {
+		printf "%s %s\n", $target_arch, join ' ', @{$architectures{$target_arch}};
+	}
+}
+elsif (@ARGV == 1 && $ARGV[0] eq 'kernels') {
+	get_targetinfo();
+	foreach my $target_name (sort keys %targets) {
+		printf "%s %s\n", $target_name, join ' ', @{$kernels{$target_name}};
+	}
+}
+elsif (@ARGV == 2 && $ARGV[0] eq 'devices') {
+	get_devices($ARGV[1]);
+	foreach my $device (sort keys %devices) {
+		printf "%s \"%s\"\n", $device, join '" "', @{$devices{$device}};
+	}
+}
+else {
+	print "Usage: $0 targets\n";
+	print "Usage: $0 architectures\n";
+	print "Usage: $0 kernels\n";
+	print "Usage: $0 devices <target/subtarget>\n";
+}
diff --git a/scripts/env b/scripts/env
new file mode 100755
index 0000000..5987b41
--- /dev/null
+++ b/scripts/env
@@ -0,0 +1,226 @@
+#!/usr/bin/env bash
+BASEDIR="$PWD"
+ENVDIR="$PWD/env"
+export GREP_OPTIONS=
+
+usage() {
+	cat <<EOF
+Usage: $0 [options] <command> [arguments]
+Commands:
+	help              This help text
+	list              List environments
+	clear             Delete all environment and revert to flat config/files
+	new <name>        Create a new environment
+	switch <name>     Switch to a different environment
+	delete <name>     Delete an environment
+	rename <newname>  Rename the current environment
+	diff              Show differences between current state and environment
+	save [message]    Save your changes to the environment, optionally using
+	                  the given commit message
+	revert            Revert your changes since last save
+
+Options:
+
+EOF
+	exit "${1:-1}"
+}
+
+error() {
+	echo "$0: $*"
+	exit 1
+}
+
+ask_bool() {
+	local DEFAULT="$1"; shift
+	local def defstr val
+	case "$DEFAULT" in
+		1) def=0; defstr="Y/n";;
+		0) def=1; defstr="y/N";;
+		*) def=;  defstr="y/n";;
+	esac
+	while [ -z "$val" ]; do
+		local VAL
+
+		echo -n "$* ($defstr): "
+		read -r VAL
+		case "$VAL" in
+			y*|Y*) val=0;;
+			n*|N*) val=1;;
+			*) val="$def";;
+		esac
+	done
+	return "$val"
+}
+
+env_init() {
+	local CREATE="$1"
+	if [ -z "$CREATE" ]; then
+		[ -d "$ENVDIR" ] || exit 0
+	fi
+	command -v git >/dev/null || error "Git is not installed"
+	mkdir -p "$ENVDIR" || error "Failed to create the environment directory"
+	cd "$ENVDIR" || error "Failed to switch to the environment directory"
+	[ -d .git ] || { 
+		git init -b master &&
+		touch .config &&
+		mkdir files &&
+		git add . && 
+		git commit -q -m "Initial import"
+	} || {
+		rm -rf .git
+		error "Failed to initialize the environment directory"
+	}
+}
+
+env_sync_data() {
+	[ ! -L "$BASEDIR/.config" ] && [ -f "$BASEDIR/.config" ] && mv "$BASEDIR/.config" "$ENVDIR"
+	git add .
+	git add -u
+}
+
+env_sync() {
+	local STR="$1"
+	env_sync_data
+	git commit -m "${STR:-Update} at $(date)"
+}
+
+env_link_config() {
+	rm -f "$BASEDIR/.config"
+	ln -s env/.config "$BASEDIR/.config"
+	mkdir -p "$ENVDIR/files"
+	[ -L "$BASEDIR/files" ] || ln -s env/files "$BASEDIR/files"
+}
+
+env_do_reset() {
+	git reset --hard HEAD
+	git clean -d -f
+}
+
+env_list() {
+	env_init
+	git branch --color | grep -vE '^. master$'
+}
+
+env_diff() {
+	env_init
+	env_sync_data
+	git diff --cached --color=auto
+	env_link_config
+}
+
+env_save() {
+	env_init
+	env_sync "$@"
+	env_link_config
+}
+
+env_revert() {
+	env_init
+	env_do_reset
+	env_link_config
+}
+
+env_ask_sync() {
+	env_sync_data
+	LINES="$(env_diff | wc -l)" # implies env_init
+	[ "$LINES" -gt 0 ] && {
+		if ask_bool 1 "Do you want to save your changes"; then
+			env_sync
+		else
+			env_do_reset
+		fi
+	}
+}
+
+env_clear() {
+	env_init
+	[ -L "$BASEDIR/.config" ] && rm -f "$BASEDIR/.config"
+	[ -L "$BASEDIR/files" ] && rm -f "$BASEDIR/files"
+	[ -f "$ENVDIR/.config" ] || ( cd "$ENVDIR/files" && find . | grep -vE '^\.$' > /dev/null )
+	env_sync_data
+	if ask_bool 1 "Do you want to keep your current config and files"; then
+		mkdir -p "$BASEDIR/files"
+		shopt -s dotglob
+		cp -a "$ENVDIR/files/"* "$BASEDIR/files" 2>/dev/null >/dev/null
+		shopt -u dotglob
+		cp "$ENVDIR/.config" "$BASEDIR/"
+	else
+		rm -rf "$BASEDIR/files" "$BASEDIR/.config"
+	fi
+	cd "$BASEDIR" || exit 1
+	rm -rf "$ENVDIR"
+}
+
+env_delete() {
+	local name="${1##*/}"
+	env_init
+	[ -z "$name" ] && usage
+	branch="$(git branch | grep '^\* ' | awk '{print $2}')"
+	[ "$name" = "$branch" ] && error "cannot delete the currently selected environment"
+	git branch -D "$name"
+}
+
+env_switch() {
+	local name="${1##*/}"
+	[ -z "$name" ] && usage
+
+	env_init
+	env_ask_sync
+	git checkout "$name" || error "environment '$name' not found"
+	env_link_config
+}
+
+env_rename() {
+	local NAME="${1##*/}"
+	env_init
+	git branch -m "$NAME"
+}
+
+env_new() {
+	local NAME="$1"
+	local branch
+	local from="master"
+
+	[ -z "$NAME" ] && usage
+	env_init 1
+	
+	branch="$(git branch | grep '^\* ' | awk '{print $2}')"
+	if [ -n "$branch" ] && [ "$branch" != "master" ]; then
+		env_ask_sync
+		if ask_bool 0 "Do you want to clone the current environment?"; then
+			from="$branch"
+		fi
+		rm -f "$BASEDIR/.config" "$BASEDIR/files"
+	fi
+	git checkout -b "$1" "$from"
+	if [ -f "$BASEDIR/.config" ] || [ -d "$BASEDIR/files" ]; then
+		if ask_bool 1 "Do you want to start your configuration repository with the current configuration?"; then
+			if [ -d "$BASEDIR/files" ] && [ ! -L "$BASEDIR/files" ]; then
+				mkdir -p "$ENVDIR/files"
+				shopt -s dotglob
+				mv "$BASEDIR/files/"* "$ENVDIR/files/" 2>/dev/null
+				shopt -u dotglob
+				rmdir "$BASEDIR/files"
+			fi
+			env_sync
+		else
+			rm -rf "$BASEDIR/.config" "$BASEDIR/files"
+		fi
+	fi
+	env_link_config
+}
+
+COMMAND="$1"; shift
+case "$COMMAND" in
+	help) usage 0;;
+	new) env_new "$@";;
+	list) env_list "$@";;
+	clear) env_clear "$@";;
+	switch) env_switch "$@";;
+	delete) env_delete "$@";;
+	rename) env_rename "$@";;
+	diff) env_diff "$@";;
+	save) env_save "$@";;
+	revert) env_revert "$@";;
+	*) usage;;
+esac
diff --git a/scripts/env_tools/env_setup.bash b/scripts/env_tools/env_setup.bash
new file mode 100755
index 0000000..357b0a6
--- /dev/null
+++ b/scripts/env_tools/env_setup.bash
@@ -0,0 +1,346 @@
+#!/bin/bash
+
+# script to setup work enviroment for linux developers on open WRT
+#Usage:
+#  cd to <open wrt root>
+#  source ./scripts/env_tools/env_setup.bash
+#
+# this script will setup git autocomplete. make sure phabricator client is in the PATH
+# and setup PS1 to show GIT status.
+
+function echo_blue()
+{
+	echo -e '\033[1;34m'"$@"'\033[0m'
+}
+
+function echo_red()
+{
+	echo -e '\033[1;31m'"$@"'\033[0m'
+}
+
+function echo_purple()
+{
+	echo -e '\033[1;35m'"$@"'\033[0m'
+}
+
+export OWRT_ROOT=`pwd -P`
+
+function git_set_openwrt_alias()
+{
+	alias r='cd ${OWRT_ROOT}'
+	alias l='cd ${OWRT_ROOT}/marvell/linux'
+	alias t='cd ${OWRT_ROOT}/marvell/lte-telephony'
+	alias w='cd ${OWRT_ROOT}/marvell/linux/drivers/marvell/sd8897'
+	alias f='cd ${OWRT_ROOT}/marvell/fastpath'
+	alias i='cd ${OWRT_ROOT}/marvell/ims'
+	alias s='cd ${OWRT_ROOT}/marvell/services'
+	alias u='cd ${OWRT_ROOT}/marvell/uboot'
+	alias obm='cd ${OWRT_ROOT}/marvell/obm'
+	alias out='cd ${OWRT_ROOT}/bin/'
+
+	# scripts aliases
+	alias mgit='${OWRT_ROOT}/mgit.sh'
+	alias sa_lindent='${OWRT_ROOT}/scripts/env_tools/sa_lindent.sh'
+
+}
+
+function set_openwrt_git()
+{
+	ENV_TOOLS_DIR=$OWRT_ROOT/scripts/env_tools
+	
+	echo_blue "Environment setup script for NZ2/3 open wrt project"
+	
+	echo "setting PS1 with Git info"
+	source ${ENV_TOOLS_DIR}/git_prompt.bash
+	setps1
+	echo "adding git auto compleate to shell"
+	source ${ENV_TOOLS_DIR}/git_completion.bash
+	
+	# make sure /usr/local/bin/ to your PATH environment variable
+	if ( ! (echo $PATH | grep -c -q ":/usr/local/bin") ); then
+		export PATH=$PATH:/usr/local/bin
+	fi
+	
+	echo_blue "Done !"
+}
+
+function set_p305_git()
+{
+	#clean previous device setup
+	if [ -n "${DEVICE+set}" ]; then
+		unset DEVICE
+	fi
+
+	echo_blue "Device setup for p305"
+	# insert saar specific configuration here
+	export DEVICE=pxa1826p305
+	export BOARD=pxa1826p305
+	cd ${OWRT_ROOT} && make defconfig_${DEVICE} && cd -
+	echo_blue "Done !"
+}
+
+function set_spinor_git()
+{
+	#clean previous device setup
+	if [ -n "${DEVICE+set}" ]; then
+		unset DEVICE
+	fi
+
+	echo_blue "Device setup for spinor"
+	# insert spinor specific configuration here
+	export DEVICE=pxa1826spinor
+	export BOARD=pxa1826
+	cd ${OWRT_ROOT} && make defconfig_${DEVICE} && cd -
+	echo_blue "Done !"
+}
+
+function set_pcie_git()
+{
+	#clean previous device setup
+	if [ -n "${DEVICE+set}" ]; then
+		unset DEVICE
+	fi
+
+	echo_blue "Device setup for pxa1826pcie"
+	# insert pcie specific configuration here
+	export DEVICE=pxa1826pcie
+	export BOARD=pxa1826pcie
+	cd ${OWRT_ROOT} && make defconfig_${DEVICE} && cd -
+	echo_blue "Done !"
+}
+
+function set_dkb_git()
+{
+	#clean previous device setup
+	if [ -n "${DEVICE+set}" ]; then
+		unset DEVICE
+	fi
+
+	echo_blue "Device setup for pxa1826DKB"
+	# insert DKB specific configuration here
+	export DEVICE=pxa1826
+	export BOARD=pxa1826
+	cd ${OWRT_ROOT} && make defconfig_${DEVICE} && cd -
+	echo_blue "Done !"
+}
+
+function mk()
+{
+	BIN_DIR=$OWRT_ROOT/bin
+	DEFAULT='-j4 V=s'
+
+	if [ -z "${DEVICE}" ]; then
+		export DEVICE=pxa1826
+	fi
+
+	if [ -z "${BOARD}" ]; then
+		export BOARD=pxa1826
+	fi
+	
+	local rc
+	if [ "$1" != "I_am_respawned" ];then
+		# respawn myself with output redirection
+		mk I_am_respawned $* 2>&1 |  mrvlfilter ${OWRT_ROOT}/buildall.log
+		return
+	fi
+	shift 	# removing "I_am_respawned" parameter
+
+	if [ -z "$1" ] ; then	# no input parameter
+		echo_blue "what would you like to build?"
+		echo " 1.  build all"
+		echo " 2.  build Kernel"
+		echo " 3.  build Telephony"
+		echo " 4.  build uboot"
+		echo " 5.  build Obm"
+		echo " 6.  Clean previous builds"
+		echo " 7.  build clean - Clean previous builds and build all"
+		echo "=========================="
+		echo "for debug mode add the flag: -j1"
+		read x_ANSWER_ALL; ANSWER_ALL=( $x_ANSWER_ALL );
+		ANSWER=${ANSWER_ALL[0]}
+		for ((i=1; i<${#ANSWER_ALL[@]}; i++));do
+			VAROPTS+="${ANSWER_ALL[$i]} "
+		done
+	else
+		echo $1
+		ANSWER=$1
+		shift
+		VAROPTS="$@"
+	fi
+	
+	date
+	BUILD_START_TIME=$(date)
+	BUILD_START_TIME_S=$(date +%s)
+	
+	case $ANSWER in
+	1)
+		echo_blue "build NZ open WRT $OWRT_ROOT"
+		rm -rf $BIN_DIR/*
+		echo_blue "build $DEVICE device"
+		echo MRVL_START_FILTER_ACTION_START
+		cd ${OWRT_ROOT} && make package/network/services/lte-telephony/clean && make $DEFAULT $VAROPTS
+		check_build_result $?
+		echo MRVL_START_FILTER_ACTION_END
+		;;
+	2)
+		echo MRVL_START_FILTER_ACTION_START
+		cd ${OWRT_ROOT} && make target/linux/{compile,install} IB=y $DEFAULT $VAROPTS
+		check_build_result $?
+		echo MRVL_START_FILTER_ACTION_END
+		;;
+	3)
+		echo MRVL_START_FILTER_ACTION_START
+		cd ${OWRT_ROOT} && make package/network/services/lte-telephony/{clean,configure,compile} $DEFAULT $VAROPTS
+		[ $? -eq 0 ] && make package/install $DEFAULT $VAROPTS
+		[ $? -eq 0 ] && make target/linux/install $DEFAULT $VAROPTS
+		check_build_result $?
+		echo MRVL_START_FILTER_ACTION_END
+		;;
+	4)
+		echo MRVL_START_FILTER_ACTION_START
+		cd ${OWRT_ROOT} && make package/boot/uboot-mmp/{configure,compile} $DEFAULT $VAROPTS
+		mv -f ${BIN_DIR}/${BOARD}/*u-boot.bin ${BIN_DIR}/${BOARD}/swd/
+		check_build_result $?
+		echo MRVL_START_FILTER_ACTION_END
+		;;
+	5)
+		echo MRVL_START_FILTER_ACTION_START
+		cd ${OWRT_ROOT} && make package/obm-mmp/compile $DEFAULT $VAROPTS
+		mv -f ${BIN_DIR}/${BOARD}/*NonTLoader_NAND_DDR.bin ${BIN_DIR}/${BOARD}/swd/
+		check_build_result $?
+		echo MRVL_START_FILTER_ACTION_END
+		;;
+	6)
+		echo MRVL_START_FILTER_ACTION_START
+		echo_blue "Clean previous builds"
+		cd ${OWRT_ROOT} && make distclean V=s
+		./scripts/feeds update -a
+		./scripts/feeds install -a 
+		make defconfig_${DEVICE}
+		echo MRVL_START_FILTER_ACTION_END
+		;;
+	7)
+		echo MRVL_START_FILTER_ACTION_START
+		echo_blue "Clean previous builds"
+		cd ${OWRT_ROOT} && make distclean V=s
+		./scripts/feeds update -a
+		./scripts/feeds install -a 
+		make defconfig_${DEVICE}
+		echo_blue "build NZ open WRT $OWRT_ROOT"
+		make $DEFAULT $VAROPTS
+		check_build_result $?
+		echo MRVL_START_FILTER_ACTION_END
+		;;
+	*)
+		echo_red "This option isn't defined"
+		;;
+	esac		
+}
+
+function mrvlfilter()
+{
+	rm $1 2> /dev/null || true
+	awk -v logfile=$1 '
+		BEGIN{
+			c_pur = "\033[1;35m"; c_red = "\033[1;31m";c_end = "\033[0m";start=0;
+		}
+		/MRVL_START_FILTER_ACTION_START/{wrns=0;start=1;next;};
+		/MRVL_START_FILTER_ACTION_END/{start=0;
+		printf("%s@ ==================== @ %s\n",c_red, c_end);
+		printf("%s@ WARNINGS_COUNT %d %s\n",c_red, wrns, c_end);
+		printf("%s@ ==================== @ %s\n",c_red, c_end);
+		next;};
+
+		{print $0 >> logfile;};
+		start == 0 {print $0; next;};
+		/[Ww]arning[ \t]*:/||/[Ee]rror[ \t]*:/||/ERROR[ \t]*:/||/WARNING[ \t]*:/{
+			wrns += 1;
+			printf("%s%s%s\n",c_red, $0, c_end); next;};
+		/Removing unused section/{gsub(/([^[:space:]])*(linux-x86\/toolchain\/arm-eabi)([^[:space:]])*/,"");
+			printf("%s%s%s\n",c_pur, $0, c_end); next;}
+		{print $0;};
+	'
+}
+
+function copy_to_windows()
+{
+	if [ "${SYNC_PATH}" == "" ]; then
+		export SYNC_PATH=/nfs/pt/swdev/areas/projects/gr_platdev/${USER}/owrt_out
+	fi
+	echo_blue "copy open WRT build output to ${SYNC_PATH}"
+	mkdir -p ${SYNC_PATH}
+	rsync -L --chmod=Dgo+w,Fgo+w -avq ${OWRT_ROOT}/bin/ ${SYNC_PATH}/
+}
+
+function check_build_result() {
+	local result=${1:-$?}
+	if [ $result -ne 0 ] ; then
+		echo_red "build $DEVICE device"
+		echo_red "BUILD FAILS.\n"
+	else
+		echo_purple "build $DEVICE device"
+		echo_purple "BUILD SUCCESSFUL"
+		copy_to_windows
+	fi
+	BUILD_END_TIME=$(date)
+	BUILD_END_TIME_S=$(date +%s)
+
+	DIFF=$(($BUILD_END_TIME_S - $BUILD_START_TIME_S))
+	H1=$((DIFF / 3600))
+	M1=$((DIFF / 60 % 60))
+	S1=$((DIFF % 60))
+
+	echo ""
+	echo_purple "Build Start Time: $BUILD_START_TIME"
+	echo_purple "Build End Time:   $BUILD_END_TIME"
+
+	if (($H1 > 0)); then
+		printf "Build Duration:   %d seconds = %02dh%02dm%02ds\n" $DIFF $H1 $M1 $S1
+	else
+		printf "Build Duration:   %d seconds = %02dm%02ds\n" $DIFF $M1 $S1
+	fi
+	echo_purple "log found in ${OWRT_ROOT}/buildall.log"
+}
+
+function arc_land()
+{
+	echo_blue "arc_land"
+	branch="$(git branch | sed -n -e 's/^\* \(.*\)/\1/p')"
+	[ "$branch" == "master" ] && echo_red on branch master, abort. && return 1
+
+	# check if master branch exists
+	git show-ref --verify --quiet refs/heads/master
+	if [ $? -eq 0 ]; then
+		echo_red In order to land, need to create master branch. Delete current master branch?
+		select yn in "Yes" "No"; do
+			case $yn in
+				Yes ) git branch -D master; break;;
+				No ) echo_red abort.; return 1;;
+			esac
+		done
+	fi
+	
+	# Create a temporary master branch for arc land command
+	git branch --track master origin/master
+	[ ! $? -eq 0 ] && echo_red failed to create master branch, abort. && return 1
+
+	if [ "$branch" == "(no branch)" ]; then
+		tmp_branch=$(date +tmp_%N)
+		git checkout -b $tmp_branch
+	fi
+
+	arc land
+	result=$?
+
+	repo sync -d .
+	git branch -D master
+	[ ! -z "$tmp_branch" ] && git branch -D $tmp_branch
+
+	if [ $result -eq 0 ]; then
+		echo_blue Done.
+	else
+		echo_red Failed.
+	fi
+
+	return $result
+}
diff --git a/scripts/env_tools/git_completion.bash b/scripts/env_tools/git_completion.bash
new file mode 100755
index 0000000..545bd4b
--- /dev/null
+++ b/scripts/env_tools/git_completion.bash
@@ -0,0 +1,2275 @@
+#!bash
+#
+# bash completion support for core Git.
+#
+# Copyright (C) 2006,2007 Shawn O. Pearce <spearce@spearce.org>
+# Conceptually based on gitcompletion (http://gitweb.hawaga.org.uk/).
+# Distributed under the GNU General Public License, version 2.0.
+#
+# The contained completion routines provide support for completing:
+#
+#    *) local and remote branch names
+#    *) local and remote tag names
+#    *) .git/remotes file names
+#    *) git 'subcommands'
+#    *) tree paths within 'ref:path/to/file' expressions
+#    *) common --long-options
+#
+# To use these routines:
+#
+#    1) Copy this file to somewhere (e.g. ~/.git-completion.sh).
+#    2) Added the following line to your .bashrc:
+#        source ~/.git-completion.sh
+#
+#    3) Consider changing your PS1 to also show the current branch:
+#        PS1='[\u@\h \W$(__git_ps1 " (%s)")]\$ '
+#
+#       The argument to __git_ps1 will be displayed only if you
+#       are currently in a git repository.  The %s token will be
+#       the name of the current branch.
+#
+#       In addition, if you set GIT_PS1_SHOWDIRTYSTATE to a nonempty
+#       value, unstaged (*) and staged (+) changes will be shown next
+#       to the branch name.  You can configure this per-repository
+#       with the bash.showDirtyState variable, which defaults to true
+#       once GIT_PS1_SHOWDIRTYSTATE is enabled.
+#
+#       You can also see if currently something is stashed, by setting
+#       GIT_PS1_SHOWSTASHSTATE to a nonempty value. If something is stashed,
+#       then a '$' will be shown next to the branch name.
+#
+#       If you would like to see if there're untracked files, then you can
+#       set GIT_PS1_SHOWUNTRACKEDFILES to a nonempty value. If there're
+#       untracked files, then a '%' will be shown next to the branch name.
+#
+# To submit patches:
+#
+#    *) Read Documentation/SubmittingPatches
+#    *) Send all patches to the current maintainer:
+#
+#       "Shawn O. Pearce" <spearce@spearce.org>
+#
+#    *) Always CC the Git mailing list:
+#
+#       git@vger.kernel.org
+#
+
+case "$COMP_WORDBREAKS" in
+*:*) : great ;;
+*)   COMP_WORDBREAKS="$COMP_WORDBREAKS:"
+esac
+
+# __gitdir accepts 0 or 1 arguments (i.e., location)
+# returns location of .git repo
+__gitdir ()
+{
+	if [ -z "${1-}" ]; then
+		if [ -n "${__git_dir-}" ]; then
+			echo "$__git_dir"
+		elif [ -d .git ]; then
+			echo .git
+		else
+			git rev-parse --git-dir 2>/dev/null
+		fi
+	elif [ -d "$1/.git" ]; then
+		echo "$1/.git"
+	else
+		echo "$1"
+	fi
+}
+
+# __git_ps1 accepts 0 or 1 arguments (i.e., format string)
+# returns text to add to bash PS1 prompt (includes branch name)
+__git_ps1 ()
+{
+	local g="$(__gitdir)"
+	if [ -n "$g" ]; then
+		local r
+		local b
+		if [ -f "$g/rebase-merge/interactive" ]; then
+			r="|REBASE-i"
+			b="$(cat "$g/rebase-merge/head-name")"
+		elif [ -d "$g/rebase-merge" ]; then
+			r="|REBASE-m"
+			b="$(cat "$g/rebase-merge/head-name")"
+		else
+			if [ -d "$g/rebase-apply" ]; then
+				if [ -f "$g/rebase-apply/rebasing" ]; then
+					r="|REBASE"
+				elif [ -f "$g/rebase-apply/applying" ]; then
+					r="|AM"
+				else
+					r="|AM/REBASE"
+				fi
+			elif [ -f "$g/MERGE_HEAD" ]; then
+				r="|MERGING"
+			elif [ -f "$g/BISECT_LOG" ]; then
+				r="|BISECTING"
+			fi
+
+			b="$(git symbolic-ref HEAD 2>/dev/null)" || {
+
+				b="$(
+				case "${GIT_PS1_DESCRIBE_STYLE-}" in
+				(contains)
+					git describe --contains HEAD ;;
+				(branch)
+					git describe --contains --all HEAD ;;
+				(describe)
+					git describe HEAD ;;
+				(* | default)
+					git describe --exact-match HEAD ;;
+				esac 2>/dev/null)" ||
+
+				b="$(cut -c1-7 "$g/HEAD" 2>/dev/null)..." ||
+				b="unknown"
+				b="($b)"
+			}
+		fi
+
+		local w
+		local i
+		local s
+		local u
+		local c
+
+		if [ "true" = "$(git rev-parse --is-inside-git-dir 2>/dev/null)" ]; then
+			if [ "true" = "$(git rev-parse --is-bare-repository 2>/dev/null)" ]; then
+				c="BARE:"
+			else
+				b="GIT_DIR!"
+			fi
+		elif [ "true" = "$(git rev-parse --is-inside-work-tree 2>/dev/null)" ]; then
+			if [ -n "${GIT_PS1_SHOWDIRTYSTATE-}" ]; then
+				if [ "$(git config --bool bash.showDirtyState)" != "false" ]; then
+					git diff --no-ext-diff --quiet --exit-code || w="*"
+					if git rev-parse --quiet --verify HEAD >/dev/null; then
+						git diff-index --cached --quiet HEAD -- || i="+"
+					else
+						i="#"
+					fi
+				fi
+			fi
+			if [ -n "${GIT_PS1_SHOWSTASHSTATE-}" ]; then
+			        git rev-parse --verify refs/stash >/dev/null 2>&1 && s="$"
+			fi
+
+			if [ -n "${GIT_PS1_SHOWUNTRACKEDFILES-}" ]; then
+			   if [ -n "$(git ls-files --others --exclude-standard)" ]; then
+			      u="%"
+			   fi
+			fi
+		fi
+
+		local f="$w$i$s$u"
+		printf "${1:- (%s)}" "$c${b##refs/heads/}${f:+ $f}$r"
+	fi
+}
+
+# __gitcomp_1 requires 2 arguments
+__gitcomp_1 ()
+{
+	local c IFS=' '$'\t'$'\n'
+	for c in $1; do
+		case "$c$2" in
+		--*=*) printf %s$'\n' "$c$2" ;;
+		*.)    printf %s$'\n' "$c$2" ;;
+		*)     printf %s$'\n' "$c$2 " ;;
+		esac
+	done
+}
+
+# __gitcomp accepts 1, 2, 3, or 4 arguments
+# generates completion reply with compgen
+__gitcomp ()
+{
+	local cur="${COMP_WORDS[COMP_CWORD]}"
+	if [ $# -gt 2 ]; then
+		cur="$3"
+	fi
+	case "$cur" in
+	--*=)
+		COMPREPLY=()
+		;;
+	*)
+		local IFS=$'\n'
+		COMPREPLY=($(compgen -P "${2-}" \
+			-W "$(__gitcomp_1 "${1-}" "${4-}")" \
+			-- "$cur"))
+		;;
+	esac
+}
+
+# __git_heads accepts 0 or 1 arguments (to pass to __gitdir)
+__git_heads ()
+{
+	local cmd i is_hash=y dir="$(__gitdir "${1-}")"
+	if [ -d "$dir" ]; then
+		git --git-dir="$dir" for-each-ref --format='%(refname:short)' \
+			refs/heads
+		return
+	fi
+	for i in $(git ls-remote "${1-}" 2>/dev/null); do
+		case "$is_hash,$i" in
+		y,*) is_hash=n ;;
+		n,*^{}) is_hash=y ;;
+		n,refs/heads/*) is_hash=y; echo "${i#refs/heads/}" ;;
+		n,*) is_hash=y; echo "$i" ;;
+		esac
+	done
+}
+
+# __git_tags accepts 0 or 1 arguments (to pass to __gitdir)
+__git_tags ()
+{
+	local cmd i is_hash=y dir="$(__gitdir "${1-}")"
+	if [ -d "$dir" ]; then
+		git --git-dir="$dir" for-each-ref --format='%(refname:short)' \
+			refs/tags
+		return
+	fi
+	for i in $(git ls-remote "${1-}" 2>/dev/null); do
+		case "$is_hash,$i" in
+		y,*) is_hash=n ;;
+		n,*^{}) is_hash=y ;;
+		n,refs/tags/*) is_hash=y; echo "${i#refs/tags/}" ;;
+		n,*) is_hash=y; echo "$i" ;;
+		esac
+	done
+}
+
+# __git_refs accepts 0 or 1 arguments (to pass to __gitdir)
+__git_refs ()
+{
+	local i is_hash=y dir="$(__gitdir "${1-}")"
+	local cur="${COMP_WORDS[COMP_CWORD]}" format refs
+	if [ -d "$dir" ]; then
+		case "$cur" in
+		refs|refs/*)
+			format="refname"
+			refs="${cur%/*}"
+			;;
+		*)
+			for i in HEAD FETCH_HEAD ORIG_HEAD MERGE_HEAD; do
+				if [ -e "$dir/$i" ]; then echo $i; fi
+			done
+			format="refname:short"
+			refs="refs/tags refs/heads refs/remotes"
+			;;
+		esac
+		git --git-dir="$dir" for-each-ref --format="%($format)" \
+			$refs
+		return
+	fi
+	for i in $(git ls-remote "$dir" 2>/dev/null); do
+		case "$is_hash,$i" in
+		y,*) is_hash=n ;;
+		n,*^{}) is_hash=y ;;
+		n,refs/tags/*) is_hash=y; echo "${i#refs/tags/}" ;;
+		n,refs/heads/*) is_hash=y; echo "${i#refs/heads/}" ;;
+		n,refs/remotes/*) is_hash=y; echo "${i#refs/remotes/}" ;;
+		n,*) is_hash=y; echo "$i" ;;
+		esac
+	done
+}
+
+# __git_refs2 requires 1 argument (to pass to __git_refs)
+__git_refs2 ()
+{
+	local i
+	for i in $(__git_refs "$1"); do
+		echo "$i:$i"
+	done
+}
+
+# __git_refs_remotes requires 1 argument (to pass to ls-remote)
+__git_refs_remotes ()
+{
+	local cmd i is_hash=y
+	for i in $(git ls-remote "$1" 2>/dev/null); do
+		case "$is_hash,$i" in
+		n,refs/heads/*)
+			is_hash=y
+			echo "$i:refs/remotes/$1/${i#refs/heads/}"
+			;;
+		y,*) is_hash=n ;;
+		n,*^{}) is_hash=y ;;
+		n,refs/tags/*) is_hash=y;;
+		n,*) is_hash=y; ;;
+		esac
+	done
+}
+
+__git_remotes ()
+{
+	local i ngoff IFS=$'\n' d="$(__gitdir)"
+	shopt -q nullglob || ngoff=1
+	shopt -s nullglob
+	for i in "$d/remotes"/*; do
+		echo ${i#$d/remotes/}
+	done
+	[ "$ngoff" ] && shopt -u nullglob
+	for i in $(git --git-dir="$d" config --get-regexp 'remote\..*\.url' 2>/dev/null); do
+		i="${i#remote.}"
+		echo "${i/.url*/}"
+	done
+}
+
+__git_list_merge_strategies ()
+{
+	git merge -s help 2>&1 |
+	sed -n -e '/[Aa]vailable strategies are: /,/^$/{
+		s/\.$//
+		s/.*://
+		s/^[ 	]*//
+		s/[ 	]*$//
+		p
+	}'
+}
+
+__git_merge_strategies=
+# 'git merge -s help' (and thus detection of the merge strategy
+# list) fails, unfortunately, if run outside of any git working
+# tree.  __git_merge_strategies is set to the empty string in
+# that case, and the detection will be repeated the next time it
+# is needed.
+__git_compute_merge_strategies ()
+{
+	: ${__git_merge_strategies:=$(__git_list_merge_strategies)}
+}
+
+__git_complete_file ()
+{
+	local pfx ls ref cur="${COMP_WORDS[COMP_CWORD]}"
+	case "$cur" in
+	?*:*)
+		ref="${cur%%:*}"
+		cur="${cur#*:}"
+		case "$cur" in
+		?*/*)
+			pfx="${cur%/*}"
+			cur="${cur##*/}"
+			ls="$ref:$pfx"
+			pfx="$pfx/"
+			;;
+		*)
+			ls="$ref"
+			;;
+	    esac
+
+		case "$COMP_WORDBREAKS" in
+		*:*) : great ;;
+		*)   pfx="$ref:$pfx" ;;
+		esac
+
+		local IFS=$'\n'
+		COMPREPLY=($(compgen -P "$pfx" \
+			-W "$(git --git-dir="$(__gitdir)" ls-tree "$ls" \
+				| sed '/^100... blob /{
+				           s,^.*	,,
+				           s,$, ,
+				       }
+				       /^120000 blob /{
+				           s,^.*	,,
+				           s,$, ,
+				       }
+				       /^040000 tree /{
+				           s,^.*	,,
+				           s,$,/,
+				       }
+				       s/^.*	//')" \
+			-- "$cur"))
+		;;
+	*)
+		__gitcomp "$(__git_refs)"
+		;;
+	esac
+}
+
+__git_complete_revlist ()
+{
+	local pfx cur="${COMP_WORDS[COMP_CWORD]}"
+	case "$cur" in
+	*...*)
+		pfx="${cur%...*}..."
+		cur="${cur#*...}"
+		__gitcomp "$(__git_refs)" "$pfx" "$cur"
+		;;
+	*..*)
+		pfx="${cur%..*}.."
+		cur="${cur#*..}"
+		__gitcomp "$(__git_refs)" "$pfx" "$cur"
+		;;
+	*)
+		__gitcomp "$(__git_refs)"
+		;;
+	esac
+}
+
+__git_complete_remote_or_refspec ()
+{
+	local cmd="${COMP_WORDS[1]}"
+	local cur="${COMP_WORDS[COMP_CWORD]}"
+	local i c=2 remote="" pfx="" lhs=1 no_complete_refspec=0
+	while [ $c -lt $COMP_CWORD ]; do
+		i="${COMP_WORDS[c]}"
+		case "$i" in
+		--mirror) [ "$cmd" = "push" ] && no_complete_refspec=1 ;;
+		--all)
+			case "$cmd" in
+			push) no_complete_refspec=1 ;;
+			fetch)
+				COMPREPLY=()
+				return
+				;;
+			*) ;;
+			esac
+			;;
+		-*) ;;
+		*) remote="$i"; break ;;
+		esac
+		c=$((++c))
+	done
+	if [ -z "$remote" ]; then
+		__gitcomp "$(__git_remotes)"
+		return
+	fi
+	if [ $no_complete_refspec = 1 ]; then
+		COMPREPLY=()
+		return
+	fi
+	[ "$remote" = "." ] && remote=
+	case "$cur" in
+	*:*)
+		case "$COMP_WORDBREAKS" in
+		*:*) : great ;;
+		*)   pfx="${cur%%:*}:" ;;
+		esac
+		cur="${cur#*:}"
+		lhs=0
+		;;
+	+*)
+		pfx="+"
+		cur="${cur#+}"
+		;;
+	esac
+	case "$cmd" in
+	fetch)
+		if [ $lhs = 1 ]; then
+			__gitcomp "$(__git_refs2 "$remote")" "$pfx" "$cur"
+		else
+			__gitcomp "$(__git_refs)" "$pfx" "$cur"
+		fi
+		;;
+	pull)
+		if [ $lhs = 1 ]; then
+			__gitcomp "$(__git_refs "$remote")" "$pfx" "$cur"
+		else
+			__gitcomp "$(__git_refs)" "$pfx" "$cur"
+		fi
+		;;
+	push)
+		if [ $lhs = 1 ]; then
+			__gitcomp "$(__git_refs)" "$pfx" "$cur"
+		else
+			__gitcomp "$(__git_refs "$remote")" "$pfx" "$cur"
+		fi
+		;;
+	esac
+}
+
+__git_complete_strategy ()
+{
+	__git_compute_merge_strategies
+	case "${COMP_WORDS[COMP_CWORD-1]}" in
+	-s|--strategy)
+		__gitcomp "$__git_merge_strategies"
+		return 0
+	esac
+	local cur="${COMP_WORDS[COMP_CWORD]}"
+	case "$cur" in
+	--strategy=*)
+		__gitcomp "$__git_merge_strategies" "" "${cur##--strategy=}"
+		return 0
+		;;
+	esac
+	return 1
+}
+
+__git_list_all_commands ()
+{
+	local i IFS=" "$'\n'
+	for i in $(git help -a|egrep '^  [a-zA-Z0-9]')
+	do
+		case $i in
+		*--*)             : helper pattern;;
+		*) echo $i;;
+		esac
+	done
+}
+
+__git_all_commands=
+__git_compute_all_commands ()
+{
+	: ${__git_all_commands:=$(__git_list_all_commands)}
+}
+
+__git_list_porcelain_commands ()
+{
+	local i IFS=" "$'\n'
+	__git_compute_all_commands
+	for i in "help" $__git_all_commands
+	do
+		case $i in
+		*--*)             : helper pattern;;
+		applymbox)        : ask gittus;;
+		applypatch)       : ask gittus;;
+		archimport)       : import;;
+		cat-file)         : plumbing;;
+		check-attr)       : plumbing;;
+		check-ref-format) : plumbing;;
+		checkout-index)   : plumbing;;
+		commit-tree)      : plumbing;;
+		count-objects)    : infrequent;;
+		cvsexportcommit)  : export;;
+		cvsimport)        : import;;
+		cvsserver)        : daemon;;
+		daemon)           : daemon;;
+		diff-files)       : plumbing;;
+		diff-index)       : plumbing;;
+		diff-tree)        : plumbing;;
+		fast-import)      : import;;
+		fast-export)      : export;;
+		fsck-objects)     : plumbing;;
+		fetch-pack)       : plumbing;;
+		fmt-merge-msg)    : plumbing;;
+		for-each-ref)     : plumbing;;
+		hash-object)      : plumbing;;
+		http-*)           : transport;;
+		index-pack)       : plumbing;;
+		init-db)          : deprecated;;
+		local-fetch)      : plumbing;;
+		lost-found)       : infrequent;;
+		ls-files)         : plumbing;;
+		ls-remote)        : plumbing;;
+		ls-tree)          : plumbing;;
+		mailinfo)         : plumbing;;
+		mailsplit)        : plumbing;;
+		merge-*)          : plumbing;;
+		mktree)           : plumbing;;
+		mktag)            : plumbing;;
+		pack-objects)     : plumbing;;
+		pack-redundant)   : plumbing;;
+		pack-refs)        : plumbing;;
+		parse-remote)     : plumbing;;
+		patch-id)         : plumbing;;
+		peek-remote)      : plumbing;;
+		prune)            : plumbing;;
+		prune-packed)     : plumbing;;
+		quiltimport)      : import;;
+		read-tree)        : plumbing;;
+		receive-pack)     : plumbing;;
+		reflog)           : plumbing;;
+		remote-*)         : transport;;
+		repo-config)      : deprecated;;
+		rerere)           : plumbing;;
+		rev-list)         : plumbing;;
+		rev-parse)        : plumbing;;
+		runstatus)        : plumbing;;
+		sh-setup)         : internal;;
+		shell)            : daemon;;
+		show-ref)         : plumbing;;
+		send-pack)        : plumbing;;
+		show-index)       : plumbing;;
+		ssh-*)            : transport;;
+		stripspace)       : plumbing;;
+		symbolic-ref)     : plumbing;;
+		tar-tree)         : deprecated;;
+		unpack-file)      : plumbing;;
+		unpack-objects)   : plumbing;;
+		update-index)     : plumbing;;
+		update-ref)       : plumbing;;
+		update-server-info) : daemon;;
+		upload-archive)   : plumbing;;
+		upload-pack)      : plumbing;;
+		write-tree)       : plumbing;;
+		var)              : infrequent;;
+		verify-pack)      : infrequent;;
+		verify-tag)       : plumbing;;
+		*) echo $i;;
+		esac
+	done
+}
+
+__git_porcelain_commands=
+__git_compute_porcelain_commands ()
+{
+	__git_compute_all_commands
+	: ${__git_porcelain_commands:=$(__git_list_porcelain_commands)}
+}
+
+__git_aliases ()
+{
+	local i IFS=$'\n'
+	for i in $(git --git-dir="$(__gitdir)" config --get-regexp "alias\..*" 2>/dev/null); do
+		case "$i" in
+		alias.*)
+			i="${i#alias.}"
+			echo "${i/ */}"
+			;;
+		esac
+	done
+}
+
+# __git_aliased_command requires 1 argument
+__git_aliased_command ()
+{
+	local word cmdline=$(git --git-dir="$(__gitdir)" \
+		config --get "alias.$1")
+	for word in $cmdline; do
+		case "$word" in
+		\!gitk|gitk)
+			echo "gitk"
+			return
+			;;
+		\!*)	: shell command alias ;;
+		-*)	: option ;;
+		*=*)	: setting env ;;
+		git)	: git itself ;;
+		*)
+			echo "$word"
+			return
+		esac
+	done
+}
+
+# __git_find_on_cmdline requires 1 argument
+__git_find_on_cmdline ()
+{
+	local word subcommand c=1
+
+	while [ $c -lt $COMP_CWORD ]; do
+		word="${COMP_WORDS[c]}"
+		for subcommand in $1; do
+			if [ "$subcommand" = "$word" ]; then
+				echo "$subcommand"
+				return
+			fi
+		done
+		c=$((++c))
+	done
+}
+
+__git_has_doubledash ()
+{
+	local c=1
+	while [ $c -lt $COMP_CWORD ]; do
+		if [ "--" = "${COMP_WORDS[c]}" ]; then
+			return 0
+		fi
+		c=$((++c))
+	done
+	return 1
+}
+
+__git_whitespacelist="nowarn warn error error-all fix"
+
+_git_am ()
+{
+	local cur="${COMP_WORDS[COMP_CWORD]}" dir="$(__gitdir)"
+	if [ -d "$dir"/rebase-apply ]; then
+		__gitcomp "--skip --continue --resolved --abort"
+		return
+	fi
+	case "$cur" in
+	--whitespace=*)
+		__gitcomp "$__git_whitespacelist" "" "${cur##--whitespace=}"
+		return
+		;;
+	--*)
+		__gitcomp "
+			--3way --committer-date-is-author-date --ignore-date
+			--ignore-whitespace --ignore-space-change
+			--interactive --keep --no-utf8 --signoff --utf8
+			--whitespace= --scissors
+			"
+		return
+	esac
+	COMPREPLY=()
+}
+
+_git_apply ()
+{
+	local cur="${COMP_WORDS[COMP_CWORD]}"
+	case "$cur" in
+	--whitespace=*)
+		__gitcomp "$__git_whitespacelist" "" "${cur##--whitespace=}"
+		return
+		;;
+	--*)
+		__gitcomp "
+			--stat --numstat --summary --check --index
+			--cached --index-info --reverse --reject --unidiff-zero
+			--apply --no-add --exclude=
+			--ignore-whitespace --ignore-space-change
+			--whitespace= --inaccurate-eof --verbose
+			"
+		return
+	esac
+	COMPREPLY=()
+}
+
+_git_add ()
+{
+	__git_has_doubledash && return
+
+	local cur="${COMP_WORDS[COMP_CWORD]}"
+	case "$cur" in
+	--*)
+		__gitcomp "
+			--interactive --refresh --patch --update --dry-run
+			--ignore-errors --intent-to-add
+			"
+		return
+	esac
+	COMPREPLY=()
+}
+
+_git_archive ()
+{
+	local cur="${COMP_WORDS[COMP_CWORD]}"
+	case "$cur" in
+	--format=*)
+		__gitcomp "$(git archive --list)" "" "${cur##--format=}"
+		return
+		;;
+	--remote=*)
+		__gitcomp "$(__git_remotes)" "" "${cur##--remote=}"
+		return
+		;;
+	--*)
+		__gitcomp "
+			--format= --list --verbose
+			--prefix= --remote= --exec=
+			"
+		return
+		;;
+	esac
+	__git_complete_file
+}
+
+_git_bisect ()
+{
+	__git_has_doubledash && return
+
+	local subcommands="start bad good skip reset visualize replay log run"
+	local subcommand="$(__git_find_on_cmdline "$subcommands")"
+	if [ -z "$subcommand" ]; then
+		__gitcomp "$subcommands"
+		return
+	fi
+
+	case "$subcommand" in
+	bad|good|reset|skip)
+		__gitcomp "$(__git_refs)"
+		;;
+	*)
+		COMPREPLY=()
+		;;
+	esac
+}
+
+_git_branch ()
+{
+	local i c=1 only_local_ref="n" has_r="n"
+
+	while [ $c -lt $COMP_CWORD ]; do
+		i="${COMP_WORDS[c]}"
+		case "$i" in
+		-d|-m)	only_local_ref="y" ;;
+		-r)	has_r="y" ;;
+		esac
+		c=$((++c))
+	done
+
+	case "${COMP_WORDS[COMP_CWORD]}" in
+	--*)
+		__gitcomp "
+			--color --no-color --verbose --abbrev= --no-abbrev
+			--track --no-track --contains --merged --no-merged
+			"
+		;;
+	*)
+		if [ $only_local_ref = "y" -a $has_r = "n" ]; then
+			__gitcomp "$(__git_heads)"
+		else
+			__gitcomp "$(__git_refs)"
+		fi
+		;;
+	esac
+}
+
+_git_bundle ()
+{
+	local cmd="${COMP_WORDS[2]}"
+	case "$COMP_CWORD" in
+	2)
+		__gitcomp "create list-heads verify unbundle"
+		;;
+	3)
+		# looking for a file
+		;;
+	*)
+		case "$cmd" in
+			create)
+				__git_complete_revlist
+			;;
+		esac
+		;;
+	esac
+}
+
+_git_checkout ()
+{
+	__git_has_doubledash && return
+
+	local cur="${COMP_WORDS[COMP_CWORD]}"
+	case "$cur" in
+	--conflict=*)
+		__gitcomp "diff3 merge" "" "${cur##--conflict=}"
+		;;
+	--*)
+		__gitcomp "
+			--quiet --ours --theirs --track --no-track --merge
+			--conflict= --patch
+			"
+		;;
+	*)
+		__gitcomp "$(__git_refs)"
+		;;
+	esac
+}
+
+_git_cherry ()
+{
+	__gitcomp "$(__git_refs)"
+}
+
+_git_cherry_pick ()
+{
+	local cur="${COMP_WORDS[COMP_CWORD]}"
+	case "$cur" in
+	--*)
+		__gitcomp "--edit --no-commit"
+		;;
+	*)
+		__gitcomp "$(__git_refs)"
+		;;
+	esac
+}
+
+_git_clean ()
+{
+	__git_has_doubledash && return
+
+	local cur="${COMP_WORDS[COMP_CWORD]}"
+	case "$cur" in
+	--*)
+		__gitcomp "--dry-run --quiet"
+		return
+		;;
+	esac
+	COMPREPLY=()
+}
+
+_git_clone ()
+{
+	local cur="${COMP_WORDS[COMP_CWORD]}"
+	case "$cur" in
+	--*)
+		__gitcomp "
+			--local
+			--no-hardlinks
+			--shared
+			--reference
+			--quiet
+			--no-checkout
+			--bare
+			--mirror
+			--origin
+			--upload-pack
+			--template=
+			--depth
+			"
+		return
+		;;
+	esac
+	COMPREPLY=()
+}
+
+_git_commit ()
+{
+	__git_has_doubledash && return
+
+	local cur="${COMP_WORDS[COMP_CWORD]}"
+	case "$cur" in
+	--cleanup=*)
+		__gitcomp "default strip verbatim whitespace
+			" "" "${cur##--cleanup=}"
+		return
+		;;
+	--reuse-message=*)
+		__gitcomp "$(__git_refs)" "" "${cur##--reuse-message=}"
+		return
+		;;
+	--reedit-message=*)
+		__gitcomp "$(__git_refs)" "" "${cur##--reedit-message=}"
+		return
+		;;
+	--untracked-files=*)
+		__gitcomp "all no normal" "" "${cur##--untracked-files=}"
+		return
+		;;
+	--*)
+		__gitcomp "
+			--all --author= --signoff --verify --no-verify
+			--edit --amend --include --only --interactive
+			--dry-run --reuse-message= --reedit-message=
+			--reset-author --file= --message= --template=
+			--cleanup= --untracked-files --untracked-files=
+			--verbose --quiet
+			"
+		return
+	esac
+	COMPREPLY=()
+}
+
+_git_describe ()
+{
+	local cur="${COMP_WORDS[COMP_CWORD]}"
+	case "$cur" in
+	--*)
+		__gitcomp "
+			--all --tags --contains --abbrev= --candidates=
+			--exact-match --debug --long --match --always
+			"
+		return
+	esac
+	__gitcomp "$(__git_refs)"
+}
+
+__git_diff_common_options="--stat --numstat --shortstat --summary
+			--patch-with-stat --name-only --name-status --color
+			--no-color --color-words --no-renames --check
+			--full-index --binary --abbrev --diff-filter=
+			--find-copies-harder
+			--text --ignore-space-at-eol --ignore-space-change
+			--ignore-all-space --exit-code --quiet --ext-diff
+			--no-ext-diff
+			--no-prefix --src-prefix= --dst-prefix=
+			--inter-hunk-context=
+			--patience
+			--raw
+			--dirstat --dirstat= --dirstat-by-file
+			--dirstat-by-file= --cumulative
+"
+
+_git_diff ()
+{
+	__git_has_doubledash && return
+
+	local cur="${COMP_WORDS[COMP_CWORD]}"
+	case "$cur" in
+	--*)
+		__gitcomp "--cached --staged --pickaxe-all --pickaxe-regex
+			--base --ours --theirs
+			$__git_diff_common_options
+			"
+		return
+		;;
+	esac
+	__git_complete_file
+}
+
+__git_mergetools_common="diffuse ecmerge emerge kdiff3 meld opendiff
+			tkdiff vimdiff gvimdiff xxdiff araxis p4merge
+"
+
+_git_difftool ()
+{
+	__git_has_doubledash && return
+
+	local cur="${COMP_WORDS[COMP_CWORD]}"
+	case "$cur" in
+	--tool=*)
+		__gitcomp "$__git_mergetools_common kompare" "" "${cur##--tool=}"
+		return
+		;;
+	--*)
+		__gitcomp "--cached --staged --pickaxe-all --pickaxe-regex
+			--base --ours --theirs
+			--no-renames --diff-filter= --find-copies-harder
+			--relative --ignore-submodules
+			--tool="
+		return
+		;;
+	esac
+	__git_complete_file
+}
+
+__git_fetch_options="
+	--quiet --verbose --append --upload-pack --force --keep --depth=
+	--tags --no-tags --all --prune --dry-run
+"
+
+_git_fetch ()
+{
+	local cur="${COMP_WORDS[COMP_CWORD]}"
+	case "$cur" in
+	--*)
+		__gitcomp "$__git_fetch_options"
+		return
+		;;
+	esac
+	__git_complete_remote_or_refspec
+}
+
+_git_format_patch ()
+{
+	local cur="${COMP_WORDS[COMP_CWORD]}"
+	case "$cur" in
+	--thread=*)
+		__gitcomp "
+			deep shallow
+			" "" "${cur##--thread=}"
+		return
+		;;
+	--*)
+		__gitcomp "
+			--stdout --attach --no-attach --thread --thread=
+			--output-directory
+			--numbered --start-number
+			--numbered-files
+			--keep-subject
+			--signoff
+			--in-reply-to= --cc=
+			--full-index --binary
+			--not --all
+			--cover-letter
+			--no-prefix --src-prefix= --dst-prefix=
+			--inline --suffix= --ignore-if-in-upstream
+			--subject-prefix=
+			"
+		return
+		;;
+	esac
+	__git_complete_revlist
+}
+
+_git_fsck ()
+{
+	local cur="${COMP_WORDS[COMP_CWORD]}"
+	case "$cur" in
+	--*)
+		__gitcomp "
+			--tags --root --unreachable --cache --no-reflogs --full
+			--strict --verbose --lost-found
+			"
+		return
+		;;
+	esac
+	COMPREPLY=()
+}
+
+_git_gc ()
+{
+	local cur="${COMP_WORDS[COMP_CWORD]}"
+	case "$cur" in
+	--*)
+		__gitcomp "--prune --aggressive"
+		return
+		;;
+	esac
+	COMPREPLY=()
+}
+
+_git_gitk ()
+{
+	_gitk
+}
+
+_git_grep ()
+{
+	__git_has_doubledash && return
+
+	local cur="${COMP_WORDS[COMP_CWORD]}"
+	case "$cur" in
+	--*)
+		__gitcomp "
+			--cached
+			--text --ignore-case --word-regexp --invert-match
+			--full-name
+			--extended-regexp --basic-regexp --fixed-strings
+			--files-with-matches --name-only
+			--files-without-match
+			--max-depth
+			--count
+			--and --or --not --all-match
+			"
+		return
+		;;
+	esac
+
+	__gitcomp "$(__git_refs)"
+}
+
+_git_help ()
+{
+	local cur="${COMP_WORDS[COMP_CWORD]}"
+	case "$cur" in
+	--*)
+		__gitcomp "--all --info --man --web"
+		return
+		;;
+	esac
+	__git_compute_all_commands
+	__gitcomp "$__git_all_commands
+		attributes cli core-tutorial cvs-migration
+		diffcore gitk glossary hooks ignore modules
+		repository-layout tutorial tutorial-2
+		workflows
+		"
+}
+
+_git_init ()
+{
+	local cur="${COMP_WORDS[COMP_CWORD]}"
+	case "$cur" in
+	--shared=*)
+		__gitcomp "
+			false true umask group all world everybody
+			" "" "${cur##--shared=}"
+		return
+		;;
+	--*)
+		__gitcomp "--quiet --bare --template= --shared --shared="
+		return
+		;;
+	esac
+	COMPREPLY=()
+}
+
+_git_ls_files ()
+{
+	__git_has_doubledash && return
+
+	local cur="${COMP_WORDS[COMP_CWORD]}"
+	case "$cur" in
+	--*)
+		__gitcomp "--cached --deleted --modified --others --ignored
+			--stage --directory --no-empty-directory --unmerged
+			--killed --exclude= --exclude-from=
+			--exclude-per-directory= --exclude-standard
+			--error-unmatch --with-tree= --full-name
+			--abbrev --ignored --exclude-per-directory
+			"
+		return
+		;;
+	esac
+	COMPREPLY=()
+}
+
+_git_ls_remote ()
+{
+	__gitcomp "$(__git_remotes)"
+}
+
+_git_ls_tree ()
+{
+	__git_complete_file
+}
+
+# Options that go well for log, shortlog and gitk
+__git_log_common_options="
+	--not --all
+	--branches --tags --remotes
+	--first-parent --merges --no-merges
+	--max-count=
+	--max-age= --since= --after=
+	--min-age= --until= --before=
+"
+# Options that go well for log and gitk (not shortlog)
+__git_log_gitk_options="
+	--dense --sparse --full-history
+	--simplify-merges --simplify-by-decoration
+	--left-right
+"
+# Options that go well for log and shortlog (not gitk)
+__git_log_shortlog_options="
+	--author= --committer= --grep=
+	--all-match
+"
+
+__git_log_pretty_formats="oneline short medium full fuller email raw format:"
+__git_log_date_formats="relative iso8601 rfc2822 short local default raw"
+
+_git_log ()
+{
+	__git_has_doubledash && return
+
+	local cur="${COMP_WORDS[COMP_CWORD]}"
+	local g="$(git rev-parse --git-dir 2>/dev/null)"
+	local merge=""
+	if [ -f "$g/MERGE_HEAD" ]; then
+		merge="--merge"
+	fi
+	case "$cur" in
+	--pretty=*)
+		__gitcomp "$__git_log_pretty_formats
+			" "" "${cur##--pretty=}"
+		return
+		;;
+	--format=*)
+		__gitcomp "$__git_log_pretty_formats
+			" "" "${cur##--format=}"
+		return
+		;;
+	--date=*)
+		__gitcomp "$__git_log_date_formats" "" "${cur##--date=}"
+		return
+		;;
+	--decorate=*)
+		__gitcomp "long short" "" "${cur##--decorate=}"
+		return
+		;;
+	--*)
+		__gitcomp "
+			$__git_log_common_options
+			$__git_log_shortlog_options
+			$__git_log_gitk_options
+			--root --topo-order --date-order --reverse
+			--follow --full-diff
+			--abbrev-commit --abbrev=
+			--relative-date --date=
+			--pretty= --format= --oneline
+			--cherry-pick
+			--graph
+			--decorate --decorate=
+			--walk-reflogs
+			--parents --children
+			$merge
+			$__git_diff_common_options
+			--pickaxe-all --pickaxe-regex
+			"
+		return
+		;;
+	esac
+	__git_complete_revlist
+}
+
+__git_merge_options="
+	--no-commit --no-stat --log --no-log --squash --strategy
+	--commit --stat --no-squash --ff --no-ff --ff-only
+"
+
+_git_merge ()
+{
+	__git_complete_strategy && return
+
+	local cur="${COMP_WORDS[COMP_CWORD]}"
+	case "$cur" in
+	--*)
+		__gitcomp "$__git_merge_options"
+		return
+	esac
+	__gitcomp "$(__git_refs)"
+}
+
+_git_mergetool ()
+{
+	local cur="${COMP_WORDS[COMP_CWORD]}"
+	case "$cur" in
+	--tool=*)
+		__gitcomp "$__git_mergetools_common tortoisemerge" "" "${cur##--tool=}"
+		return
+		;;
+	--*)
+		__gitcomp "--tool="
+		return
+		;;
+	esac
+	COMPREPLY=()
+}
+
+_git_merge_base ()
+{
+	__gitcomp "$(__git_refs)"
+}
+
+_git_mv ()
+{
+	local cur="${COMP_WORDS[COMP_CWORD]}"
+	case "$cur" in
+	--*)
+		__gitcomp "--dry-run"
+		return
+		;;
+	esac
+	COMPREPLY=()
+}
+
+_git_name_rev ()
+{
+	__gitcomp "--tags --all --stdin"
+}
+
+_git_notes ()
+{
+	local subcommands="edit show"
+	if [ -z "$(__git_find_on_cmdline "$subcommands")" ]; then
+		__gitcomp "$subcommands"
+		return
+	fi
+
+	case "${COMP_WORDS[COMP_CWORD-1]}" in
+	-m|-F)
+		COMPREPLY=()
+		;;
+	*)
+		__gitcomp "$(__git_refs)"
+		;;
+	esac
+}
+
+_git_pull ()
+{
+	__git_complete_strategy && return
+
+	local cur="${COMP_WORDS[COMP_CWORD]}"
+	case "$cur" in
+	--*)
+		__gitcomp "
+			--rebase --no-rebase
+			$__git_merge_options
+			$__git_fetch_options
+		"
+		return
+		;;
+	esac
+	__git_complete_remote_or_refspec
+}
+
+_git_push ()
+{
+	local cur="${COMP_WORDS[COMP_CWORD]}"
+	case "${COMP_WORDS[COMP_CWORD-1]}" in
+	--repo)
+		__gitcomp "$(__git_remotes)"
+		return
+	esac
+	case "$cur" in
+	--repo=*)
+		__gitcomp "$(__git_remotes)" "" "${cur##--repo=}"
+		return
+		;;
+	--*)
+		__gitcomp "
+			--all --mirror --tags --dry-run --force --verbose
+			--receive-pack= --repo=
+		"
+		return
+		;;
+	esac
+	__git_complete_remote_or_refspec
+}
+
+_git_rebase ()
+{
+	local cur="${COMP_WORDS[COMP_CWORD]}" dir="$(__gitdir)"
+	if [ -d "$dir"/rebase-apply ] || [ -d "$dir"/rebase-merge ]; then
+		__gitcomp "--continue --skip --abort"
+		return
+	fi
+	__git_complete_strategy && return
+	case "$cur" in
+	--whitespace=*)
+		__gitcomp "$__git_whitespacelist" "" "${cur##--whitespace=}"
+		return
+		;;
+	--*)
+		__gitcomp "
+			--onto --merge --strategy --interactive
+			--preserve-merges --stat --no-stat
+			--committer-date-is-author-date --ignore-date
+			--ignore-whitespace --whitespace=
+			--autosquash
+			"
+
+		return
+	esac
+	__gitcomp "$(__git_refs)"
+}
+
+__git_send_email_confirm_options="always never auto cc compose"
+__git_send_email_suppresscc_options="author self cc bodycc sob cccmd body all"
+
+_git_send_email ()
+{
+	local cur="${COMP_WORDS[COMP_CWORD]}"
+	case "$cur" in
+	--confirm=*)
+		__gitcomp "
+			$__git_send_email_confirm_options
+			" "" "${cur##--confirm=}"
+		return
+		;;
+	--suppress-cc=*)
+		__gitcomp "
+			$__git_send_email_suppresscc_options
+			" "" "${cur##--suppress-cc=}"
+
+		return
+		;;
+	--smtp-encryption=*)
+		__gitcomp "ssl tls" "" "${cur##--smtp-encryption=}"
+		return
+		;;
+	--*)
+		__gitcomp "--annotate --bcc --cc --cc-cmd --chain-reply-to
+			--compose --confirm= --dry-run --envelope-sender
+			--from --identity
+			--in-reply-to --no-chain-reply-to --no-signed-off-by-cc
+			--no-suppress-from --no-thread --quiet
+			--signed-off-by-cc --smtp-pass --smtp-server
+			--smtp-server-port --smtp-encryption= --smtp-user
+			--subject --suppress-cc= --suppress-from --thread --to
+			--validate --no-validate"
+		return
+		;;
+	esac
+	COMPREPLY=()
+}
+
+_git_stage ()
+{
+	_git_add
+}
+
+__git_config_get_set_variables ()
+{
+	local prevword word config_file= c=$COMP_CWORD
+	while [ $c -gt 1 ]; do
+		word="${COMP_WORDS[c]}"
+		case "$word" in
+		--global|--system|--file=*)
+			config_file="$word"
+			break
+			;;
+		-f|--file)
+			config_file="$word $prevword"
+			break
+			;;
+		esac
+		prevword=$word
+		c=$((--c))
+	done
+
+	git --git-dir="$(__gitdir)" config $config_file --list 2>/dev/null |
+	while read line
+	do
+		case "$line" in
+		*.*=*)
+			echo "${line/=*/}"
+			;;
+		esac
+	done
+}
+
+_git_config ()
+{
+	local cur="${COMP_WORDS[COMP_CWORD]}"
+	local prv="${COMP_WORDS[COMP_CWORD-1]}"
+	case "$prv" in
+	branch.*.remote)
+		__gitcomp "$(__git_remotes)"
+		return
+		;;
+	branch.*.merge)
+		__gitcomp "$(__git_refs)"
+		return
+		;;
+	remote.*.fetch)
+		local remote="${prv#remote.}"
+		remote="${remote%.fetch}"
+		__gitcomp "$(__git_refs_remotes "$remote")"
+		return
+		;;
+	remote.*.push)
+		local remote="${prv#remote.}"
+		remote="${remote%.push}"
+		__gitcomp "$(git --git-dir="$(__gitdir)" \
+			for-each-ref --format='%(refname):%(refname)' \
+			refs/heads)"
+		return
+		;;
+	pull.twohead|pull.octopus)
+		__git_compute_merge_strategies
+		__gitcomp "$__git_merge_strategies"
+		return
+		;;
+	color.branch|color.diff|color.interactive|\
+	color.showbranch|color.status|color.ui)
+		__gitcomp "always never auto"
+		return
+		;;
+	color.pager)
+		__gitcomp "false true"
+		return
+		;;
+	color.*.*)
+		__gitcomp "
+			normal black red green yellow blue magenta cyan white
+			bold dim ul blink reverse
+			"
+		return
+		;;
+	help.format)
+		__gitcomp "man info web html"
+		return
+		;;
+	log.date)
+		__gitcomp "$__git_log_date_formats"
+		return
+		;;
+	sendemail.aliasesfiletype)
+		__gitcomp "mutt mailrc pine elm gnus"
+		return
+		;;
+	sendemail.confirm)
+		__gitcomp "$__git_send_email_confirm_options"
+		return
+		;;
+	sendemail.suppresscc)
+		__gitcomp "$__git_send_email_suppresscc_options"
+		return
+		;;
+	--get|--get-all|--unset|--unset-all)
+		__gitcomp "$(__git_config_get_set_variables)"
+		return
+		;;
+	*.*)
+		COMPREPLY=()
+		return
+		;;
+	esac
+	case "$cur" in
+	--*)
+		__gitcomp "
+			--global --system --file=
+			--list --replace-all
+			--get --get-all --get-regexp
+			--add --unset --unset-all
+			--remove-section --rename-section
+			"
+		return
+		;;
+	branch.*.*)
+		local pfx="${cur%.*}."
+		cur="${cur##*.}"
+		__gitcomp "remote merge mergeoptions rebase" "$pfx" "$cur"
+		return
+		;;
+	branch.*)
+		local pfx="${cur%.*}."
+		cur="${cur#*.}"
+		__gitcomp "$(__git_heads)" "$pfx" "$cur" "."
+		return
+		;;
+	guitool.*.*)
+		local pfx="${cur%.*}."
+		cur="${cur##*.}"
+		__gitcomp "
+			argprompt cmd confirm needsfile noconsole norescan
+			prompt revprompt revunmerged title
+			" "$pfx" "$cur"
+		return
+		;;
+	difftool.*.*)
+		local pfx="${cur%.*}."
+		cur="${cur##*.}"
+		__gitcomp "cmd path" "$pfx" "$cur"
+		return
+		;;
+	man.*.*)
+		local pfx="${cur%.*}."
+		cur="${cur##*.}"
+		__gitcomp "cmd path" "$pfx" "$cur"
+		return
+		;;
+	mergetool.*.*)
+		local pfx="${cur%.*}."
+		cur="${cur##*.}"
+		__gitcomp "cmd path trustExitCode" "$pfx" "$cur"
+		return
+		;;
+	pager.*)
+		local pfx="${cur%.*}."
+		cur="${cur#*.}"
+		__git_compute_all_commands
+		__gitcomp "$__git_all_commands" "$pfx" "$cur"
+		return
+		;;
+	remote.*.*)
+		local pfx="${cur%.*}."
+		cur="${cur##*.}"
+		__gitcomp "
+			url proxy fetch push mirror skipDefaultUpdate
+			receivepack uploadpack tagopt pushurl
+			" "$pfx" "$cur"
+		return
+		;;
+	remote.*)
+		local pfx="${cur%.*}."
+		cur="${cur#*.}"
+		__gitcomp "$(__git_remotes)" "$pfx" "$cur" "."
+		return
+		;;
+	url.*.*)
+		local pfx="${cur%.*}."
+		cur="${cur##*.}"
+		__gitcomp "insteadOf pushInsteadOf" "$pfx" "$cur"
+		return
+		;;
+	esac
+	__gitcomp "
+		add.ignore-errors
+		alias.
+		apply.ignorewhitespace
+		apply.whitespace
+		branch.autosetupmerge
+		branch.autosetuprebase
+		clean.requireForce
+		color.branch
+		color.branch.current
+		color.branch.local
+		color.branch.plain
+		color.branch.remote
+		color.diff
+		color.diff.commit
+		color.diff.frag
+		color.diff.meta
+		color.diff.new
+		color.diff.old
+		color.diff.plain
+		color.diff.whitespace
+		color.grep
+		color.grep.external
+		color.grep.match
+		color.interactive
+		color.interactive.header
+		color.interactive.help
+		color.interactive.prompt
+		color.pager
+		color.showbranch
+		color.status
+		color.status.added
+		color.status.changed
+		color.status.header
+		color.status.nobranch
+		color.status.untracked
+		color.status.updated
+		color.ui
+		commit.template
+		core.autocrlf
+		core.bare
+		core.compression
+		core.createObject
+		core.deltaBaseCacheLimit
+		core.editor
+		core.excludesfile
+		core.fileMode
+		core.fsyncobjectfiles
+		core.gitProxy
+		core.ignoreCygwinFSTricks
+		core.ignoreStat
+		core.logAllRefUpdates
+		core.loosecompression
+		core.packedGitLimit
+		core.packedGitWindowSize
+		core.pager
+		core.preferSymlinkRefs
+		core.preloadindex
+		core.quotepath
+		core.repositoryFormatVersion
+		core.safecrlf
+		core.sharedRepository
+		core.symlinks
+		core.trustctime
+		core.warnAmbiguousRefs
+		core.whitespace
+		core.worktree
+		diff.autorefreshindex
+		diff.external
+		diff.mnemonicprefix
+		diff.renameLimit
+		diff.renameLimit.
+		diff.renames
+		diff.suppressBlankEmpty
+		diff.tool
+		diff.wordRegex
+		difftool.
+		difftool.prompt
+		fetch.unpackLimit
+		format.attach
+		format.cc
+		format.headers
+		format.numbered
+		format.pretty
+		format.signoff
+		format.subjectprefix
+		format.suffix
+		format.thread
+		gc.aggressiveWindow
+		gc.auto
+		gc.autopacklimit
+		gc.packrefs
+		gc.pruneexpire
+		gc.reflogexpire
+		gc.reflogexpireunreachable
+		gc.rerereresolved
+		gc.rerereunresolved
+		gitcvs.allbinary
+		gitcvs.commitmsgannotation
+		gitcvs.dbTableNamePrefix
+		gitcvs.dbdriver
+		gitcvs.dbname
+		gitcvs.dbpass
+		gitcvs.dbuser
+		gitcvs.enabled
+		gitcvs.logfile
+		gitcvs.usecrlfattr
+		guitool.
+		gui.blamehistoryctx
+		gui.commitmsgwidth
+		gui.copyblamethreshold
+		gui.diffcontext
+		gui.encoding
+		gui.fastcopyblame
+		gui.matchtrackingbranch
+		gui.newbranchtemplate
+		gui.pruneduringfetch
+		gui.spellingdictionary
+		gui.trustmtime
+		help.autocorrect
+		help.browser
+		help.format
+		http.lowSpeedLimit
+		http.lowSpeedTime
+		http.maxRequests
+		http.noEPSV
+		http.proxy
+		http.sslCAInfo
+		http.sslCAPath
+		http.sslCert
+		http.sslKey
+		http.sslVerify
+		i18n.commitEncoding
+		i18n.logOutputEncoding
+		imap.folder
+		imap.host
+		imap.pass
+		imap.port
+		imap.preformattedHTML
+		imap.sslverify
+		imap.tunnel
+		imap.user
+		instaweb.browser
+		instaweb.httpd
+		instaweb.local
+		instaweb.modulepath
+		instaweb.port
+		interactive.singlekey
+		log.date
+		log.showroot
+		mailmap.file
+		man.
+		man.viewer
+		merge.conflictstyle
+		merge.log
+		merge.renameLimit
+		merge.stat
+		merge.tool
+		merge.verbosity
+		mergetool.
+		mergetool.keepBackup
+		mergetool.prompt
+		pack.compression
+		pack.deltaCacheLimit
+		pack.deltaCacheSize
+		pack.depth
+		pack.indexVersion
+		pack.packSizeLimit
+		pack.threads
+		pack.window
+		pack.windowMemory
+		pager.
+		pull.octopus
+		pull.twohead
+		push.default
+		rebase.stat
+		receive.denyCurrentBranch
+		receive.denyDeletes
+		receive.denyNonFastForwards
+		receive.fsckObjects
+		receive.unpackLimit
+		repack.usedeltabaseoffset
+		rerere.autoupdate
+		rerere.enabled
+		sendemail.aliasesfile
+		sendemail.aliasesfiletype
+		sendemail.bcc
+		sendemail.cc
+		sendemail.cccmd
+		sendemail.chainreplyto
+		sendemail.confirm
+		sendemail.envelopesender
+		sendemail.multiedit
+		sendemail.signedoffbycc
+		sendemail.smtpencryption
+		sendemail.smtppass
+		sendemail.smtpserver
+		sendemail.smtpserverport
+		sendemail.smtpuser
+		sendemail.suppresscc
+		sendemail.suppressfrom
+		sendemail.thread
+		sendemail.to
+		sendemail.validate
+		showbranch.default
+		status.relativePaths
+		status.showUntrackedFiles
+		tar.umask
+		transfer.unpackLimit
+		url.
+		user.email
+		user.name
+		user.signingkey
+		web.browser
+		branch. remote.
+	"
+}
+
+_git_remote ()
+{
+	local subcommands="add rename rm show prune update set-head"
+	local subcommand="$(__git_find_on_cmdline "$subcommands")"
+	if [ -z "$subcommand" ]; then
+		__gitcomp "$subcommands"
+		return
+	fi
+
+	case "$subcommand" in
+	rename|rm|show|prune)
+		__gitcomp "$(__git_remotes)"
+		;;
+	update)
+		local i c='' IFS=$'\n'
+		for i in $(git --git-dir="$(__gitdir)" config --get-regexp "remotes\..*" 2>/dev/null); do
+			i="${i#remotes.}"
+			c="$c ${i/ */}"
+		done
+		__gitcomp "$c"
+		;;
+	*)
+		COMPREPLY=()
+		;;
+	esac
+}
+
+_git_replace ()
+{
+	__gitcomp "$(__git_refs)"
+}
+
+_git_reset ()
+{
+	__git_has_doubledash && return
+
+	local cur="${COMP_WORDS[COMP_CWORD]}"
+	case "$cur" in
+	--*)
+		__gitcomp "--merge --mixed --hard --soft --patch"
+		return
+		;;
+	esac
+	__gitcomp "$(__git_refs)"
+}
+
+_git_revert ()
+{
+	local cur="${COMP_WORDS[COMP_CWORD]}"
+	case "$cur" in
+	--*)
+		__gitcomp "--edit --mainline --no-edit --no-commit --signoff"
+		return
+		;;
+	esac
+	__gitcomp "$(__git_refs)"
+}
+
+_git_rm ()
+{
+	__git_has_doubledash && return
+
+	local cur="${COMP_WORDS[COMP_CWORD]}"
+	case "$cur" in
+	--*)
+		__gitcomp "--cached --dry-run --ignore-unmatch --quiet"
+		return
+		;;
+	esac
+	COMPREPLY=()
+}
+
+_git_shortlog ()
+{
+	__git_has_doubledash && return
+
+	local cur="${COMP_WORDS[COMP_CWORD]}"
+	case "$cur" in
+	--*)
+		__gitcomp "
+			$__git_log_common_options
+			$__git_log_shortlog_options
+			--numbered --summary
+			"
+		return
+		;;
+	esac
+	__git_complete_revlist
+}
+
+_git_show ()
+{
+	__git_has_doubledash && return
+
+	local cur="${COMP_WORDS[COMP_CWORD]}"
+	case "$cur" in
+	--pretty=*)
+		__gitcomp "$__git_log_pretty_formats
+			" "" "${cur##--pretty=}"
+		return
+		;;
+	--format=*)
+		__gitcomp "$__git_log_pretty_formats
+			" "" "${cur##--format=}"
+		return
+		;;
+	--*)
+		__gitcomp "--pretty= --format= --abbrev-commit --oneline
+			$__git_diff_common_options
+			"
+		return
+		;;
+	esac
+	__git_complete_file
+}
+
+_git_show_branch ()
+{
+	local cur="${COMP_WORDS[COMP_CWORD]}"
+	case "$cur" in
+	--*)
+		__gitcomp "
+			--all --remotes --topo-order --current --more=
+			--list --independent --merge-base --no-name
+			--color --no-color
+			--sha1-name --sparse --topics --reflog
+			"
+		return
+		;;
+	esac
+	__git_complete_revlist
+}
+
+_git_stash ()
+{
+	local cur="${COMP_WORDS[COMP_CWORD]}"
+	local save_opts='--keep-index --no-keep-index --quiet --patch'
+	local subcommands='save list show apply clear drop pop create branch'
+	local subcommand="$(__git_find_on_cmdline "$subcommands")"
+	if [ -z "$subcommand" ]; then
+		case "$cur" in
+		--*)
+			__gitcomp "$save_opts"
+			;;
+		*)
+			if [ -z "$(__git_find_on_cmdline "$save_opts")" ]; then
+				__gitcomp "$subcommands"
+			else
+				COMPREPLY=()
+			fi
+			;;
+		esac
+	else
+		case "$subcommand,$cur" in
+		save,--*)
+			__gitcomp "$save_opts"
+			;;
+		apply,--*|pop,--*)
+			__gitcomp "--index --quiet"
+			;;
+		show,--*|drop,--*|branch,--*)
+			COMPREPLY=()
+			;;
+		show,*|apply,*|drop,*|pop,*|branch,*)
+			__gitcomp "$(git --git-dir="$(__gitdir)" stash list \
+					| sed -n -e 's/:.*//p')"
+			;;
+		*)
+			COMPREPLY=()
+			;;
+		esac
+	fi
+}
+
+_git_submodule ()
+{
+	__git_has_doubledash && return
+
+	local subcommands="add status init update summary foreach sync"
+	if [ -z "$(__git_find_on_cmdline "$subcommands")" ]; then
+		local cur="${COMP_WORDS[COMP_CWORD]}"
+		case "$cur" in
+		--*)
+			__gitcomp "--quiet --cached"
+			;;
+		*)
+			__gitcomp "$subcommands"
+			;;
+		esac
+		return
+	fi
+}
+
+_git_svn ()
+{
+	local subcommands="
+		init fetch clone rebase dcommit log find-rev
+		set-tree commit-diff info create-ignore propget
+		proplist show-ignore show-externals branch tag blame
+		migrate mkdirs reset gc
+		"
+	local subcommand="$(__git_find_on_cmdline "$subcommands")"
+	if [ -z "$subcommand" ]; then
+		__gitcomp "$subcommands"
+	else
+		local remote_opts="--username= --config-dir= --no-auth-cache"
+		local fc_opts="
+			--follow-parent --authors-file= --repack=
+			--no-metadata --use-svm-props --use-svnsync-props
+			--log-window-size= --no-checkout --quiet
+			--repack-flags --use-log-author --localtime
+			--ignore-paths= $remote_opts
+			"
+		local init_opts="
+			--template= --shared= --trunk= --tags=
+			--branches= --stdlayout --minimize-url
+			--no-metadata --use-svm-props --use-svnsync-props
+			--rewrite-root= --prefix= --use-log-author
+			--add-author-from $remote_opts
+			"
+		local cmt_opts="
+			--edit --rmdir --find-copies-harder --copy-similarity=
+			"
+
+		local cur="${COMP_WORDS[COMP_CWORD]}"
+		case "$subcommand,$cur" in
+		fetch,--*)
+			__gitcomp "--revision= --fetch-all $fc_opts"
+			;;
+		clone,--*)
+			__gitcomp "--revision= $fc_opts $init_opts"
+			;;
+		init,--*)
+			__gitcomp "$init_opts"
+			;;
+		dcommit,--*)
+			__gitcomp "
+				--merge --strategy= --verbose --dry-run
+				--fetch-all --no-rebase --commit-url
+				--revision $cmt_opts $fc_opts
+				"
+			;;
+		set-tree,--*)
+			__gitcomp "--stdin $cmt_opts $fc_opts"
+			;;
+		create-ignore,--*|propget,--*|proplist,--*|show-ignore,--*|\
+		show-externals,--*|mkdirs,--*)
+			__gitcomp "--revision="
+			;;
+		log,--*)
+			__gitcomp "
+				--limit= --revision= --verbose --incremental
+				--oneline --show-commit --non-recursive
+				--authors-file= --color
+				"
+			;;
+		rebase,--*)
+			__gitcomp "
+				--merge --verbose --strategy= --local
+				--fetch-all --dry-run $fc_opts
+				"
+			;;
+		commit-diff,--*)
+			__gitcomp "--message= --file= --revision= $cmt_opts"
+			;;
+		info,--*)
+			__gitcomp "--url"
+			;;
+		branch,--*)
+			__gitcomp "--dry-run --message --tag"
+			;;
+		tag,--*)
+			__gitcomp "--dry-run --message"
+			;;
+		blame,--*)
+			__gitcomp "--git-format"
+			;;
+		migrate,--*)
+			__gitcomp "
+				--config-dir= --ignore-paths= --minimize
+				--no-auth-cache --username=
+				"
+			;;
+		reset,--*)
+			__gitcomp "--revision= --parent"
+			;;
+		*)
+			COMPREPLY=()
+			;;
+		esac
+	fi
+}
+
+_git_tag ()
+{
+	local i c=1 f=0
+	while [ $c -lt $COMP_CWORD ]; do
+		i="${COMP_WORDS[c]}"
+		case "$i" in
+		-d|-v)
+			__gitcomp "$(__git_tags)"
+			return
+			;;
+		-f)
+			f=1
+			;;
+		esac
+		c=$((++c))
+	done
+
+	case "${COMP_WORDS[COMP_CWORD-1]}" in
+	-m|-F)
+		COMPREPLY=()
+		;;
+	-*|tag)
+		if [ $f = 1 ]; then
+			__gitcomp "$(__git_tags)"
+		else
+			COMPREPLY=()
+		fi
+		;;
+	*)
+		__gitcomp "$(__git_refs)"
+		;;
+	esac
+}
+
+_git_whatchanged ()
+{
+	_git_log
+}
+
+_git ()
+{
+	local i c=1 command __git_dir
+
+	while [ $c -lt $COMP_CWORD ]; do
+		i="${COMP_WORDS[c]}"
+		case "$i" in
+		--git-dir=*) __git_dir="${i#--git-dir=}" ;;
+		--bare)      __git_dir="." ;;
+		--version|-p|--paginate) ;;
+		--help) command="help"; break ;;
+		*) command="$i"; break ;;
+		esac
+		c=$((++c))
+	done
+
+	if [ -z "$command" ]; then
+		case "${COMP_WORDS[COMP_CWORD]}" in
+		--*)   __gitcomp "
+			--paginate
+			--no-pager
+			--git-dir=
+			--bare
+			--version
+			--exec-path
+			--html-path
+			--work-tree=
+			--help
+			"
+			;;
+		*)     __git_compute_porcelain_commands
+		       __gitcomp "$__git_porcelain_commands $(__git_aliases)" ;;
+		esac
+		return
+	fi
+
+	local completion_func="_git_${command//-/_}"
+	declare -F $completion_func >/dev/null && $completion_func && return
+
+	local expansion=$(__git_aliased_command "$command")
+	if [ -n "$expansion" ]; then
+		completion_func="_git_${expansion//-/_}"
+		declare -F $completion_func >/dev/null && $completion_func
+	fi
+}
+
+_gitk ()
+{
+	__git_has_doubledash && return
+
+	local cur="${COMP_WORDS[COMP_CWORD]}"
+	local g="$(__gitdir)"
+	local merge=""
+	if [ -f "$g/MERGE_HEAD" ]; then
+		merge="--merge"
+	fi
+	case "$cur" in
+	--*)
+		__gitcomp "
+			$__git_log_common_options
+			$__git_log_gitk_options
+			$merge
+			"
+		return
+		;;
+	esac
+	__git_complete_revlist
+}
+
+complete -o bashdefault -o default -o nospace -F _git git 2>/dev/null \
+	|| complete -o default -o nospace -F _git git
+complete -o bashdefault -o default -o nospace -F _gitk gitk 2>/dev/null \
+	|| complete -o default -o nospace -F _gitk gitk
+
+# The following are necessary only for Cygwin, and only are needed
+# when the user has tab-completed the executable name and consequently
+# included the '.exe' suffix.
+#
+if [ Cygwin = "$(uname -o 2>/dev/null)" ]; then
+complete -o bashdefault -o default -o nospace -F _git git.exe 2>/dev/null \
+	|| complete -o default -o nospace -F _git git.exe
+fi
diff --git a/scripts/env_tools/git_prompt.bash b/scripts/env_tools/git_prompt.bash
new file mode 100755
index 0000000..bef8cb9
--- /dev/null
+++ b/scripts/env_tools/git_prompt.bash
@@ -0,0 +1,251 @@
+#!/bin/bash
+###############################################################################
+# IDENTIFICATION OF LOCAL HOST: CHANGE TO YOUR COMPUTER NAME
+###############################################################################
+
+
+PRIMARYHOST="localhost"
+
+###############################################################################
+# PROMPT
+###############################################################################
+
+###############################################################################
+# Terminal Title
+
+set_terminal_title() {
+    if [[ -z $@ ]]
+    then
+        TERMINAL_TITLE=$(pwd -P)
+    else
+        TERMINAL_TITLE=$@
+    fi
+}
+alias stt='set_terminal_title'
+alarm()  { perl -e 'alarm shift; exec @ARGV' "$@"; }
+
+STANDARD_PROMPT_COMMAND='history -a ; echo -ne "\033]0;${TERMINAL_TITLE}\007"'
+PROMPT_COMMAND=$STANDARD_PROMPT_COMMAND
+
+###############################################################################
+# Parses Git info for prompt
+
+function _set_git_envar_info {
+    GIT_BRANCH=""
+    GIT_HEAD=""
+    GIT_STATE=""
+    GIT_LEADER=""
+    GIT_ROOT=""
+    GIT_REPO=""
+    GIT_ACTION=""
+
+    if [[ $(which git 2>/dev/null) ]]
+    then
+
+        local IS_GIT
+	IS_GIT=$(\git rev-parse --show-toplevel 2>/dev/null)
+        if [[ -z $IS_GIT ]]
+        then
+            return
+        fi
+
+        GIT_ROOT=./$(\git rev-parse --show-cdup 2>/dev/null)
+	GIT_GIT=$(\git rev-parse --git-dir 2>/dev/null)
+
+        local STATUS
+        # STATUS=$(\git status 2>/dev/null)
+	STATUS=$(alarm 3 \git status 2>/dev/null)
+        if [[ -z $STATUS ]]
+        then
+            STATUS="alarmed"
+        fi
+
+        GIT_LEADER=":"
+        GIT_BRANCH="$(\git branch --no-color 2>/dev/null | sed -e '/^[^*]/d' -e 's/* \(.*\)/\1/')"
+        GIT_HEAD=":$(\git log -n1 --pretty=format:%h 2>/dev/null)"
+        GIT_REPO="$(\git rev-parse --show-toplevel 2>/dev/null | awk -F/ '{print $NF}')"
+
+        if [[ "$STATUS" == *'working directory clean'* ]]
+        then
+            GIT_STATE=""
+        else
+            GIT_HEAD=$GIT_HEAD":"
+            GIT_STATE=""
+            if [[ "$STATUS" == *'Changes to be committed:'* ]]
+            then
+                GIT_STATE=$GIT_STATE'+I' # Index has files staged for commit
+            fi
+            if [[ "$STATUS" == *'Changed but not updated:'* || "$STATUS" == *'Changes not staged for commit'* ]]
+            then
+                GIT_STATE=$GIT_STATE"+M" # Working tree has files modified but unstaged
+            fi
+            if [[ "$STATUS" == *'Untracked files:'* ]]
+            then
+                GIT_STATE=$GIT_STATE'+U' # Working tree has untracked files
+            fi
+            if [[ "$STATUS" == *'alarmed'* ]]
+            then
+                GIT_STATE=$GIT_STATE'*' # too much time to proceed status
+            fi
+            GIT_STATE=$GIT_STATE''
+        fi
+
+        if [ -f "$GIT_GIT/rebase-merge/interactive" ]; then
+            GIT_ACTION="REBASE-i"
+        elif [ -d "$GIT_GIT/rebase-merge" ]; then
+            GIT_ACTION="REBASE-m"
+        else
+            if [ -d "$GIT_GIT/rebase-apply" ]; then
+                if [ -f "$GIT_GIT/rebase-apply/rebasing" ]; then
+                    GIT_ACTION="REBASE"
+                elif [ -f "$GIT_GIT/rebase-apply/applying" ]; then
+                    GIT_ACTION="AM"
+                else
+                    GIT_ACTION="AM/REBASE"
+                fi
+            elif [ -f "$GIT_GIT/MERGE_HEAD" ]; then
+                GIT_ACTION="MERGING"
+            elif [ -f "$GIT_GIT/BISECT_LOG" ]; then
+                GIT_ACTION="BISECTING"
+            fi
+	fi
+
+	if [ ! -z "$GIT_ACTION" ]; then
+	    GIT_ACTION="|"$GIT_ACTION
+	fi
+
+    fi
+
+}
+
+###############################################################################
+# Composes prompt.
+function setps1 {
+
+    # Help message.
+#    local USAGE="Usage: setps1 [none] [screen=<0|1>] [user=<0|1>] [dir=<0|1|2>] [git=<0|1>] [wrap=<0|1>] [which-python=<0|1>]"
+
+    if [[ (-z $@) || ($@ == "*-h*") || ($@ == "*--h*") ]]
+    then
+        echo $USAGE
+        return
+    fi
+
+    # Prompt colors.
+    local CLEAR="\[\033[0m\]"
+    local STY_COLOR='\[\033[1;37;41m\]'
+    local PROMPT_COLOR='\[\033[1;94m\]'
+    local USER_HOST_COLOR='\[\033[1;30m\]'
+    local PROMPT_DIR_COLOR='\[\033[1;94m\]'
+    local GIT_LEADER_COLOR='\[\033[1;30m\]'
+    local GIT_BRANCH_COLOR=$CLEAR'\[\033[1;90m\]\[\033[4;90m\]'
+    local GIT_HEAD_COLOR=$CLEAR'\[\033[1;32m\]'
+    local GIT_STATE_COLOR=$CLEAR'\[\033[1;31m\]'
+
+    # Hostname-based colors in prompt.
+    if [[ $HOSTNAME != $PRIMARYHOST ]]
+    then
+        USER_HOST_COLOR=$REMOTE_USER_HOST_COLOR
+    fi
+
+    # Start with empty prompt.
+    local PROMPTSTR=""
+
+    # Set screen session id.
+    if [[ $@ == *screen=1* ]]
+    then
+        ## Decorate prompt with indication of screen session ##
+        if [[ -z "$STY" ]] # if screen session variable is not defined
+        then
+            local SCRTAG=""
+        else
+            local SCRTAG="$STY_COLOR(STY ${STY%%.*})$CLEAR" # get screen session number
+        fi
+    fi
+
+    # Set user@host.
+    # if [[ $@ == *user=1* ]]
+    # then
+    #      PROMPTSTR=$PROMPTSTR"$USER_HOST_COLOR\\u@\\h$CLEAR"
+    # fi
+
+    # Set directory.
+    if [[ -n $PROMPTSTR && ($@ == *dir=1* || $@ == *dir=2*) ]]
+    then
+            PROMPTSTR=$PROMPTSTR"$PROMPT_COLOR:"
+    fi
+
+    if [[ $@ == *dir=1* ]]
+    then
+        #PROMPTSTR=$PROMPTSTR"$PROMPT_DIR_COLOR\W$CLEAR"
+        PROMPTSTR=$PROMPTSTR"$PROMPT_DIR_COLOR\$GIT_REPO$CLEAR"
+    elif [[ $@ == *dir=2* ]]
+    then
+        PROMPTSTR=$PROMPTSTR"$PROMPT_DIR_COLOR\$(pwd -P)$CLEAR"
+    fi
+
+#     if [[ $@ == *dir=1* ]]
+#     then
+#         PROMPTSTR=$PROMPTSTR"$PROMPT_DIR_COLOR\W$CLEAR"
+#     elif [[ $@ == *dir=2* ]]
+#     then
+#         PROMPTSTR=$PROMPTSTR"$PROMPT_DIR_COLOR\w$CLEAR"
+#     fi
+#
+    # Set git.
+    if [[ $@ == *git=1* ]]
+    then
+        PROMPT_COMMAND="$STANDARD_PROMPT_COMMAND && _set_git_envar_info"
+        PROMPTSTR=$PROMPTSTR"$BG_COLOR$GIT_LEADER_COLOR\$GIT_LEADER$GIT_BRANCH_COLOR"
+        PROMPTSTR=$PROMPTSTR"\$GIT_BRANCH$GIT_HEAD_COLOR\$GIT_HEAD$GIT_STATE_COLOR\$GIT_STATE\$GIT_ACTION$CLEAR"
+    else
+        PROMPT_COMMAND=$STANDARD_PROMPT_COMMAND
+    fi
+
+    # Set wrap.
+    if [[ $@ == *wrap=1* ]]
+    then
+        local WRAP="$CLEAR\n"
+    else
+        local WRAP=""
+    fi
+
+    # Set wrap.
+    if [[ $@ == *which-python=1* ]]
+    then
+        local WHICHPYTHON="$CLEAR\n(python is '\$(which python)')$CLEAR\n"
+    else
+        local WHICHPYTHON=""
+    fi
+
+    # Finalize.
+    if [[ -z $PROMPTSTR || $@ == none ]]
+    then
+        PROMPTSTR="\$ "
+    else
+        PROMPTSTR="$TITLEBAR\n$SCRTAG${PROMPT_COLOR}[$CLEAR$PROMPTSTR$PROMPT_COLOR]$WRAP$WHICHPYTHON$PROMPT_COLOR\$$CLEAR "
+    fi
+
+    # Set.
+    PS1="\[\e]0;\w\a\]\n\[\e[32m\]\u@\h \[\e[33m\]\w\[\e[0m\]$PROMPTSTR"
+    PS2='> '
+    PS4='+ '
+}
+
+alias setps1-long='setps1 screen=1 user=1 dir=2 git=1 wrap=1'
+alias setps1-short='setps1 screen=1 user=1 dir=1 git=1 wrap=0'
+alias setps1-default='setps1-short'
+alias setps1-plain='setps1 screen=0 user=0 dir=0 git=0 wrap=0'
+alias setps1-nogit='setps1 screen=0 user=1 dir=1 git=0 wrap=0'
+alias setps1-local-long='setps1 screen=1 user=0 dir=2 git=1 wrap=1'
+alias setps1-local-short='setps1 screen=0 user=0 dir=1 git=1 wrap=0'
+alias setps1-local='setps1-local-short'
+alias setps1-dev-short='setps1 screen=0 user=0 dir=1 git=1 wrap=0 which-python=1'
+alias setps1-dev-long='setps1 screen=0 user=1 dir=2 git=1 wrap=0 which-python=1'
+alias setps1-dev-remote='setps1 screen=0 user=1 dir=1 git=1 wrap=0 which-python=1'
+if [[ "$HOSTNAME" = "$PRIMARYHOST" ]]
+then
+    setps1 screen=0 user=0 dir=1 git=1 wrap=0 which-python=0
+else
+    setps1 screen=1 user=1 dir=1 git=1 wrap=0 which-python=0
+fi
diff --git a/scripts/env_tools/manifest.xml b/scripts/env_tools/manifest.xml
new file mode 100644
index 0000000..24e1a15
--- /dev/null
+++ b/scripts/env_tools/manifest.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<manifest>
+  <remote fetch="ssh://git@10.26.130.134/" name="origin" review="http://10.26.130.134/"/>
+  <default remote="origin" revision="master" sync-j="2"/>
+  <project name="openwrt" path="openwrt" remote="origin" revision="master" />
+  <!--downloaded open source packages -->
+  <project name="dl_ow" path="openwrt/dl" remote="origin" revision="master" />
+  <!-- 3rd party projects -->
+  <project name="management_ow" path="openwrt/external/management" remote="origin" revision="master" />
+  <project name="routing_ow" path="openwrt/external/routing" remote="origin" revision="master" />
+  <project name="subpack_ow" path="openwrt/external/subpack" remote="origin" revision="master" />
+  <!--Marvell projects start -->
+  <project name="linux_ow" path="openwrt/marvell/linux" remote="origin" revision="master" />
+  <project name="lte-telephony_ow" path="openwrt/marvell/lte-telephony" remote="origin" revision="master" />
+  <project name="obm_ow" path="openwrt/marvell/obm" remote="origin" revision="master" />
+  <project name="services_ow" path="openwrt/marvell/services" remote="origin" revision="master" />
+  <project name="swd_ow" path="openwrt/marvell/swd" remote="origin" revision="master" />
+  <project name="uboot_ow" path="openwrt/marvell/uboot" remote="origin" revision="master" />
+  <project name="fota_ow" path="openwrt/marvell/fota" remote="origin" revision="master" />
+  <project name="webui_ow" path="openwrt/marvell/webui" remote="origin" revision="master" />
+  <!--Marvell projects end -->
+</manifest>
diff --git a/scripts/env_tools/manifest_gen.bash b/scripts/env_tools/manifest_gen.bash
new file mode 100755
index 0000000..241907a
--- /dev/null
+++ b/scripts/env_tools/manifest_gen.bash
@@ -0,0 +1,3 @@
+#!/bin/bash
+awk 'NR==FNR{e[i++]=$0;next} /master/{gsub("master",e[j++])}1' $1 $2
+
diff --git a/scripts/env_tools/sa_lindent.sh b/scripts/env_tools/sa_lindent.sh
new file mode 100755
index 0000000..84fce6a
--- /dev/null
+++ b/scripts/env_tools/sa_lindent.sh
@@ -0,0 +1,80 @@
+#!/bin/bash
+
+function sa_set_param()
+{
+  if [ "$1" = "tel_ptk" ];then
+    PARAM="-npro -kr -i8 -ts8 -sob -l120 -ss -ncs -cp1"
+  else
+    PARAM="-npro -kr -i8 -ts8 -sob -l120 -ss -ncs -cp1 -bl -bli0 -bls -nce -cli8"
+  fi
+  local RES=`indent --version`
+  local V1=`echo $RES | cut -d' ' -f3 | cut -d'.' -f1`
+  local V2=`echo $RES | cut -d' ' -f3 | cut -d'.' -f2`
+  local V3=`echo $RES | cut -d' ' -f3 | cut -d'.' -f3`
+  if [ $V1 -gt 2 ]; then
+    PARAM="$PARAM -il0"
+  elif [ $V1 -eq 2 ]; then
+    if [ $V2 -gt 2 ]; then
+      PARAM="$PARAM -il0";
+    elif [ $V2 -eq 2 ]; then
+      if [ $V3 -ge 10 ]; then
+        PARAM="$PARAM -il0"
+      fi
+    fi
+  fi
+}
+
+function sa_usage()
+{
+  echo "Simple code format utility inspired by \${android_root}/kernel/kernel/scripts/Lindent"
+  echo "Usage  : `basename $0` [Options] <file1> <file2> ..."
+  echo "Options:"
+  echo "-h       - help"
+  echo "-a       - apply format recursively on all *.[ch] files"
+  echo "-o       - use old (non-default) format style"
+  echo "Examples:"
+  echo "         `basename $0` -a"
+  echo "         `basename $0` code.c newcode.c newcode.h"
+  echo "         `basename $0` -o code.c newcode.c newcode.h"
+  exit 1
+}
+
+function sa_make_indent()
+{
+  export SIMPLE_BACKUP_SUFFIX=.INDENT_BAK
+  echo files ${#@}
+  if [ ${#@} -eq 0 ]; then
+    echo "ABORT: no files supplied."
+    sa_usage
+  fi
+  #echo indent $PARAM "$@"
+  echo "################################"
+  for i in "$@"; do
+    fromdos "$i"
+    indent $PARAM "$i"
+    diff $i ${i}${SIMPLE_BACKUP_SUFFIX} 1>/dev/null 2>&1
+    if [ $? -eq 1 ]; then
+      echo Changed: ${i}
+    fi
+    rm ${i}${SIMPLE_BACKUP_SUFFIX} #2>&- || true
+  done
+}
+
+sa_doall=0
+sa_doold=0
+sa_set_param tel_ptk
+while getopts ":aoh" opt; do
+  case $opt in
+  a)sa_doall=1;;
+  o)sa_set_param tel_ptk_old_style;;
+  h)sa_usage;;
+  \?)sa_usage;;
+  esac
+done
+
+if [ $sa_doall -eq 1 ]; then
+  sa_make_indent `find -type f -a -name \*.[ch]`
+else
+  sa_make_indent $@
+fi
+
diff --git a/scripts/env_tools/sa_mylist_apps.sh b/scripts/env_tools/sa_mylist_apps.sh
new file mode 100755
index 0000000..d5ca38f
--- /dev/null
+++ b/scripts/env_tools/sa_mylist_apps.sh
@@ -0,0 +1,153 @@
+#!/bin/bash
+function echo_red()
+{
+	echo -e '\033[1;31m'"$@"'\033[0m'
+}
+
+function echo_purple()
+{
+	echo -e '\033[1;35m'"$@"'\033[0m'
+}
+
+function echo_blue()
+{
+	echo -e '\033[1;34m'"$@"'\033[0m'
+}
+
+function doset()
+{
+	local pkg
+	if [ "$1" = "ALL" ];then
+		shift
+		echo_blue "Testing install of $@"
+		yes | sudo apt-get --fix-missing install $@
+		return 0
+	fi
+	for pkg in $@; do
+		dpkg -l $pkg | grep "ii  ";
+		[ $? -eq 0 ] && continue
+		echo_blue "Testing install of $pkg"
+		yes | sudo apt-get --fix-missing install $pkg
+		echo_blue "==============="
+	done
+	return 0
+}
+
+function update_sources_list()
+{
+	local ptadd="$1"
+	if sudo grep -q "$ptadd" /etc/apt/sources.list ; then
+		echo_purple "==== \"$ptadd\" already updated ===="
+	else
+		echo_blue "Adding \"$ptadd\""
+		sudo add-apt-repository "$ptadd"
+	fi
+}
+
+function update_src_repositories()
+{
+	sudo apt-get clean
+	doset python-software-properties
+	if [ "$UBUNTU" = "10.04" ]; then
+		for ptadd in "deb http://archive.canonical.com/ lucid partner" \
+			     "deb http://security.ubuntu.com/ubuntu lucid-security multiverse" \
+			     "deb http://us.archive.ubuntu.com/ubuntu/ lucid multiverse" \
+			     "deb-src http://us.archive.ubuntu.com/ubuntu/ lucid multiverse" \
+			     "deb http://us.archive.ubuntu.com/ubuntu/ lucid-updates multiverse" \
+			     "deb-src http://us.archive.ubuntu.com/ubuntu/ lucid-updates multiverse" ;
+		do
+			update_sources_list "$ptadd"
+		done
+		if [ ! -f /etc/apt/sources.list.d/git-core-ppa-lucid.list ];then
+			# get advanced GIT
+			sudo add-apt-repository ppa:git-core/ppa
+		else
+			echo_purple "==== GIT repository already set to PPA ===="
+		fi
+	else # ubuntu 12.04
+		for ptadd in "deb http://archive.canonical.com/ precise partner" \
+			     "deb http://security.ubuntu.com/ubuntu precise-security multiverse" \
+			     "deb http://us.archive.ubuntu.com/ubuntu/ precise multiverse" \
+			     "deb-src http://us.archive.ubuntu.com/ubuntu/ precise multiverse" \
+			     "deb http://us.archive.ubuntu.com/ubuntu/ precise-updates multiverse" \
+			     "deb-src http://us.archive.ubuntu.com/ubuntu/ precise-updates multiverse" ;
+		do
+			update_sources_list "$ptadd"
+		done
+	fi
+	sudo apt-get update
+	yes | sudo apt-get upgrade
+}
+
+function update_openwrt_dev_utils()
+{
+	# open WRT requiered packages (based on http://wiki.openwrt.org/doc/howto/buildroot.exigence)
+	doset build-essential subversion git-core
+	doset libncurses5-dev zlib1g-dev gawk
+	doset gcc-multilib flex gettext g++
+	if [ "$(uname -m)" = "x86_64" ];then
+		update_openwrt_dev_utils_64bit
+	else # 32 bit environment
+		update_openwrt_dev_utils_32bit
+	fi
+	#     GIT basics
+	doset git-gui git-doc gitk qgit
+	# perl help utilities for GIT activities
+	echo_red "Update packages for gerrit wrappers"
+	doset libconfig-inifiles-perl liblist-moreutils-perl
+
+	# packages for kernel build
+	doset bc make exuberant-ctags
+
+	#packages for uboot build
+	doset uboot-mkimage uuid-dev
+
+	# Diff tools & Editors
+	doset geany hexer vim xxdiff xxdiff-scripts meld
+	# Emacs + indentation tool + directory viewer
+	doset indent emacs ispell wamerican-large tree
+
+	# Install Phabricator client packages
+	doset php5-cli php5-curl
+}
+
+function update_openwrt_dev_utils_32bit()
+{
+	#open WRT 32 bit spesific packages
+	# (based on http://wiki.openwrt.org/doc/howto/buildroot.exigence)
+	doset patch bzip2 bison
+	doset autoconf unzip
+	doset ncurses-term
+	doset libz-dev libssl-dev
+	doset quilt libssl-dev xsltproc
+	doset libxml-parser-perl mercurial bzr ecj cvs
+}
+function update_openwrt_dev_utils_64bit()
+{
+	#open WRT 64 bit spesific libraries.
+	doset geany
+}
+
+#main code starts here:
+
+if [ "$(uname -m)" = "x86_64" ];then
+	echo_purple "You are working on 64bit Linux !"
+else
+	echo_purple "You are working on 32bit Linux !"
+fi
+#check ubuntu version, supported 10.04 and 12.04 only
+grep DISTRIB_DESCRIPTION /etc/lsb-release | grep -q 12.04;
+if [ $? -eq 0 ];then
+	UBUNTU=12.04
+else
+	UBUNTU=10.04
+fi
+echo_purple "    UBUNTU version $UBUNTU"
+update_src_repositories
+update_openwrt_dev_utils
+echo_purple "    UBUNTU version $UBUNTU RUN Phase 2"
+update_openwrt_dev_utils
+
+yes | sudo apt-get autoremove
+
+echo_blue "  Done"
diff --git a/scripts/ext-toolchain.sh b/scripts/ext-toolchain.sh
new file mode 100755
index 0000000..e49c011
--- /dev/null
+++ b/scripts/ext-toolchain.sh
@@ -0,0 +1,646 @@
+#!/usr/bin/env bash
+#
+#   Script for various external toolchain tasks, refer to
+#   the --help output for more information.
+#
+#   Copyright (C) 2012 Jo-Philipp Wich <jo@mein.io>
+#
+#   This program is free software; you can redistribute it and/or modify
+#   it under the terms of the GNU General Public License as published by
+#   the Free Software Foundation; either version 2 of the License, or
+#   (at your option) any later version.
+#
+#   This program is distributed in the hope that it will be useful,
+#   but WITHOUT ANY WARRANTY; without even the implied warranty of
+#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#   GNU General Public License for more details.
+#
+#   You should have received a copy of the GNU General Public License
+#   along with this program; if not, write to the Free Software
+#   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+CC=""
+CXX=""
+CPP=""
+
+CFLAGS=""
+TOOLCHAIN="."
+
+LIBC_TYPE=""
+GCC_VERSION=""
+
+
+# Library specs
+LIB_SPECS="
+	c:        ld-* lib{anl,c,cidn,crypt,dl,m,nsl,nss_dns,nss_files,resolv,util}
+	rt:       librt-* librt
+	pthread:  libpthread-* libpthread
+	stdcpp:   libstdc++
+	thread_db: libthread-db
+	gcc:      libgcc_s
+	ssp:      libssp
+	gfortran: libgfortran
+	gomp:	  libgomp
+	atomic:	  libatomic
+	quadmath: libquadmath
+	asan:	  libasan
+	tasan:	  libtsan
+	lasan:	  liblsan
+	ubasan:	  libubsan
+"
+
+# Binary specs
+BIN_SPECS="
+	ldd:       ldd
+	ldconfig:  ldconfig
+	gdb:       gdb
+	gdbserver: gdbserver
+"
+
+OVERWRITE_CONFIG=""
+
+test_c() {
+	cat <<-EOT | "${CC:-false}" $CFLAGS -o /dev/null -x c - 2>/dev/null
+		#include <stdio.h>
+
+		int main(int argc, char **argv)
+		{
+			printf("Hello, world!\n");
+			return 0;
+		}
+	EOT
+}
+
+test_cxx() {
+	cat <<-EOT | "${CXX:-false}" $CFLAGS -o /dev/null -x c++ - 2>/dev/null
+		#include <iostream>
+
+		using namespace std;
+
+		int main()
+		{
+			cout << "Hello, world!" << endl;
+			return 0;
+		}
+	EOT
+}
+
+test_softfloat() {
+	cat <<-EOT | "$CC" $CFLAGS -msoft-float -o /dev/null -x c - 2>/dev/null
+		int main(int argc, char **argv)
+		{
+			double a = 0.1;
+			double b = 0.2;
+			double c = (a + b) / (a * b);
+			return 1;
+		}
+	EOT
+}
+
+test_uclibc() {
+	local sysroot="$("$CC" $CFLAGS -print-sysroot 2>/dev/null)"
+	if [ -d "${sysroot:-$TOOLCHAIN}" ]; then
+		local lib
+		for lib in "${sysroot:-$TOOLCHAIN}"/{lib,usr/lib,usr/local/lib}/ld*-uClibc*.so*; do
+			if [ -f "$lib" ] && [ ! -h "$lib" ]; then
+				return 0
+			fi
+		done
+	fi
+	return 1
+}
+
+test_feature() {
+	local feature="$1"; shift
+
+	# find compilers, libc type
+	probe_cc
+	probe_cxx
+	probe_libc
+
+	# common toolchain feature tests
+	case "$feature" in
+		c)     test_c;         return $? ;;
+		c++)   test_cxx;       return $? ;;
+		soft*) test_softfloat; return $? ;;
+	esac
+
+	# assume eglibc/glibc supports all libc features
+	if [ "$LIBC_TYPE" != "uclibc" ]; then
+		return 0
+	fi
+
+	# uclibc feature tests
+	local inc
+	local sysroot="$("$CC" "$@" -muclibc -print-sysroot 2>/dev/null)"
+	for inc in "include" "usr/include" "usr/local/include"; do
+		local conf="${sysroot:-$TOOLCHAIN}/$inc/bits/uClibc_config.h"
+		if [ -f "$conf" ]; then
+			case "$feature" in
+				lfs)     grep -q '__UCLIBC_HAS_LFS__ 1'     "$conf"; return $?;;
+				ipv6)    grep -q '__UCLIBC_HAS_IPV6__ 1'    "$conf"; return $?;;
+				rpc)     grep -q '__UCLIBC_HAS_RPC__ 1'     "$conf"; return $?;;
+				locale)  grep -q '__UCLIBC_HAS_LOCALE__ 1'  "$conf"; return $?;;
+				wchar)   grep -q '__UCLIBC_HAS_WCHAR__ 1'   "$conf"; return $?;;
+				threads) grep -q '__UCLIBC_HAS_THREADS__ 1' "$conf"; return $?;;
+			esac
+		fi
+	done
+
+	return 1
+}
+
+
+find_libs() {
+	local spec="$(echo "$LIB_SPECS" | sed -ne "s#^[[:space:]]*$1:##ip")"
+
+	if [ -n "$spec" ] && probe_cpp; then
+		local libdir libdirs
+		for libdir in $(
+			"$CPP" $CFLAGS -v -x c /dev/null 2>&1 | \
+				sed -ne 's#:# #g; s#^LIBRARY_PATH=##p'
+		); do
+			if [ -d "$libdir" ]; then
+				libdirs="$libdirs $(cd "$libdir"; pwd)/"
+			fi
+		done
+
+		local pattern
+		for pattern in $(eval echo $spec); do
+			find $libdirs -name "$pattern.so*" | sort -u
+		done
+
+		return 0
+	fi
+
+	return 1
+}
+
+find_bins() {
+	local spec="$(echo "$BIN_SPECS" | sed -ne "s#^[[:space:]]*$1:##ip")"
+
+	if [ -n "$spec" ] && probe_cpp; then
+		local sysroot="$("$CPP" -print-sysroot)"
+
+		local bindir bindirs
+		for bindir in $(
+			echo "${sysroot:-$TOOLCHAIN}/bin";
+			echo "${sysroot:-$TOOLCHAIN}/usr/bin";
+			echo "${sysroot:-$TOOLCHAIN}/usr/local/bin";
+ 			"$CPP" $CFLAGS -v -x c /dev/null 2>&1 | \
+				sed -ne 's#:# #g; s#^COMPILER_PATH=##p'
+		); do
+			if [ -d "$bindir" ]; then
+				bindirs="$bindirs $(cd "$bindir"; pwd)/"
+			fi
+		done
+
+		local pattern
+		for pattern in $(eval echo $spec); do
+			find $bindirs -name "$pattern" | sort -u
+		done
+
+		return 0
+	fi
+
+	return 1
+}
+
+find_gcc_version() {
+	if [ -f $TOOLCHAIN/info.mk ]; then
+		GCC_VERSION=$(grep GCC_VERSION $TOOLCHAIN/info.mk | sed 's/GCC_VERSION=//')
+		return 0
+	fi
+
+	echo "Warning! Can't find info.mk, trying to detect with alternative way."
+
+	# Very fragile detection
+	GCC_VERSION=$(find $TOOLCHAIN/bin | grep -oE "gcc-[0-9]+\.[0-9]+\.[0-9]+$" | \
+		head -1 | sed 's/gcc-//')
+}
+
+
+wrap_bin_cc() {
+	local out="$1"
+	local bin="$2"
+
+	echo    '#!/bin/sh'                                                > "$out"
+	echo    'for arg in "$@"; do'                                     >> "$out"
+	echo    ' case "$arg" in -l*|-L*|-shared|-static)'                >> "$out"
+	echo -n '  exec "'"$bin"'" '"$CFLAGS"' ${STAGING_DIR:+'           >> "$out"
+	echo -n '-idirafter "$STAGING_DIR/usr/include" '                  >> "$out"
+	echo -n '-L "$STAGING_DIR/usr/lib" '                              >> "$out"
+	echo    '-Wl,-rpath-link,"$STAGING_DIR/usr/lib"} "$@" ;;'         >> "$out"
+	echo    ' esac'                                                   >> "$out"
+	echo    'done'                                                    >> "$out"
+	echo -n 'exec "'"$bin"'" '"$CFLAGS"' ${STAGING_DIR:+'             >> "$out"
+	echo    '-idirafter "$STAGING_DIR/usr/include"} "$@"'             >> "$out"
+
+	chmod +x "$out"
+}
+
+wrap_bin_ld() {
+	local out="$1"
+	local bin="$2"
+
+	echo    '#!/bin/sh'                                                > "$out"
+	echo -n 'exec "'"$bin"'" ${STAGING_DIR:+'                         >> "$out"
+	echo -n '-L "$STAGING_DIR/usr/lib" '                              >> "$out"
+	echo    '-rpath-link "$STAGING_DIR/usr/lib"} "$@"'                >> "$out"
+
+	chmod +x "$out"
+}
+
+wrap_bin_other() {
+	local out="$1"
+	local bin="$2"
+
+	echo    '#!/bin/sh'                                                > "$out"
+	echo    'exec "'"$bin"'" "$@"'                                    >> "$out"
+
+	chmod +x "$out"
+}
+
+wrap_bins() {
+	if probe_cc; then
+		mkdir -p "$1" || return 1
+
+		local cmd
+		for cmd in "${CC%-*}-"*; do
+			if [ -x "$cmd" ]; then
+				local out="$1/${cmd##*/}"
+				local bin="$cmd"
+
+				if [ -x "$out" ] && ! grep -q STAGING_DIR "$out"; then
+					mv "$out" "$out.bin"
+					bin='$(dirname "$0")/'"${out##*/}"'.bin'
+				fi
+
+				case "${cmd##*/}" in
+					*-*cc|*-*cc-*|*-*++|*-*++-*|*-cpp)
+						wrap_bin_cc "$out" "$bin"
+					;;
+					*-ld)
+						wrap_bin_ld "$out" "$bin"
+					;;
+					*)
+						wrap_bin_other "$out" "$bin"
+					;;
+				esac
+			fi
+		done
+
+		return 0
+	fi
+
+	return 1
+}
+
+
+print_config() {
+	local mktarget="$1"
+	local mksubtarget
+
+	local target="$("$CC" $CFLAGS -dumpmachine)"
+	local version="$("$CC" $CFLAGS -dumpversion)"
+	local cpuarch="${target%%-*}"
+
+	# get CC; strip version; strip gcc and add - suffix
+	local prefix="${CC##*/}"; prefix="${prefix%-$version}"; prefix="${prefix%-*}-"
+	local config="${0%/scripts/*}/.config"
+
+	# if no target specified, print choice list and exit
+	if [ -z "$mktarget" ]; then
+		# prepare metadata
+		if [ ! -f "${0%/scripts/*}/tmp/.targetinfo" ]; then
+			"${0%/*}/scripts/config/mconf" prepare-tmpinfo
+		fi
+
+		local mktargets=$(
+			sed -ne "
+				/^Target: / { h };
+				/^Target-Arch: $cpuarch\$/ { x; s#^Target: ##p }
+			" "${0%/scripts/*}/tmp/.targetinfo" | sort -u
+		)
+
+		for mktarget in $mktargets; do
+			case "$mktarget" in */*)
+				mktargets=$(echo "$mktargets" | sed -e "/^${mktarget%/*}\$/d")
+			esac
+		done
+
+		if [ -n "$mktargets" ]; then
+			echo "Available targets:"                               >&2
+			echo $mktargets                                         >&2
+		else
+			echo -e "Could not find a suitable OpenWrt target for " >&2
+			echo -e "CPU architecture '$cpuarch' - you need to "    >&2
+			echo -e "define one first!"                             >&2
+		fi
+		return 1
+	fi
+
+	# bail out if there is a .config already
+	if [ -f "$config" ]; then
+		if [ "$OVERWRITE_CONFIG" == "" ]; then
+			echo "There already is a .config file, refusing to overwrite!" >&2
+			return 1
+		else
+			echo "There already is a .config file, trying to overwrite!"
+		fi
+	fi
+
+	case "$mktarget" in */*)
+		mksubtarget="${mktarget#*/}"
+		mktarget="${mktarget%/*}"
+	;; esac
+
+	if [ ! -f "$config" ]; then
+		touch "$config"
+	fi
+
+	echo "CONFIG_TARGET_${mktarget}=y" >> "$config"
+
+	if [ -n "$mksubtarget" ]; then
+		echo "CONFIG_TARGET_${mktarget}_${mksubtarget}=y" >> "$config"
+	fi
+
+	if test_feature "softfloat"; then
+		echo "CONFIG_SOFT_FLOAT=y" >> "$config"
+	else
+		echo "# CONFIG_SOFT_FLOAT is not set" >> "$config"
+	fi
+
+	if test_feature "ipv6"; then
+		echo "CONFIG_IPV6=y" >> "$config"
+	else
+		echo "# CONFIG_IPV6 is not set" >> "$config"
+	fi
+
+	if test_feature "locale"; then
+		echo "CONFIG_BUILD_NLS=y" >> "$config"
+	else
+		echo "# CONFIG_BUILD_NLS is not set" >> "$config"
+	fi
+
+	echo "CONFIG_DEVEL=y" >> "$config"
+	echo "CONFIG_EXTERNAL_TOOLCHAIN=y" >> "$config"
+	echo "CONFIG_TOOLCHAIN_ROOT=\"$TOOLCHAIN\"" >> "$config"
+	echo "CONFIG_TOOLCHAIN_PREFIX=\"$prefix\"" >> "$config"
+	echo "CONFIG_TARGET_NAME=\"$target\"" >> "$config"
+
+	if [ -f "$config" ]; then
+		sed -i '/CONFIG_EXTERNAL_TOOLCHAIN_LIBC_USE_MUSL/d' "$config"
+		sed -i '/CONFIG_EXTERNAL_TOOLCHAIN_LIBC_USE_GLIBC/d' "$config"
+	fi
+
+	if [ "$LIBC_TYPE" == glibc ]; then
+		echo "CONFIG_EXTERNAL_TOOLCHAIN_LIBC_USE_GLIBC=y" >> "$config"
+	elif [ "$LIBC_TYPE" == musl ]; then
+		echo "CONFIG_EXTERNAL_TOOLCHAIN_LIBC_USE_MUSL=y" >> "$config"
+	else
+		echo "Can't detect LIBC type. Aborting!" >&2
+		return 1
+	fi
+
+	if [ -n "$GCC_VERSION" ]; then
+		echo "CONFIG_EXTERNAL_GCC_VERSION=\"$GCC_VERSION\"" >> "$config"
+	else
+		echo "Can't detect GCC version. Aborting!" >&2
+		return 1
+	fi
+
+	local lib
+	for lib in C RT PTHREAD GCC STDCPP SSP GFORTRAN GOMP ATOMIC QUADMATH ASAN TSAN LSAN UBSAN; do
+		local file
+		local spec=""
+		local llib="$(echo "$lib" | sed -e 's#.*#\L&#')"
+		for file in $(find_libs "$lib"); do
+			spec="${spec:+$spec }$(echo "$file" | sed -e "s#^$TOOLCHAIN#.#")"
+		done
+		if [ -n "$spec" ]; then
+			echo "CONFIG_PACKAGE_lib${llib}=y" >> "$config"
+			echo "CONFIG_LIB${lib}_FILE_SPEC=\"$spec\"" >> "$config"
+		else
+			echo "# CONFIG_PACKAGE_lib${llib} is not set" >> "$config"
+		fi
+	done
+
+	local bin
+	for bin in LDD LDCONFIG; do
+		local file
+		local spec=""
+		local lbin="$(echo "$bin" | sed -e 's#.*#\L&#')"
+		for file in $(find_bins "$bin"); do
+			spec="${spec:+$spec }$(echo "$file" | sed -e "s#^$TOOLCHAIN#.#")"
+		done
+		if [ -n "$spec" ]; then
+			echo "CONFIG_PACKAGE_${lbin}=y" >> "$config"
+			echo "CONFIG_${bin}_FILE_SPEC=\"$spec\"" >> "$config"
+		else
+			echo "# CONFIG_PACKAGE_${lbin} is not set" >> "$config"
+		fi
+	done
+
+	# inflate
+	make -C "${0%/scripts/*}" defconfig
+	return 0
+}
+
+
+probe_cc() {
+	if [ -z "$CC" ]; then
+		local bin
+		for bin in "bin" "usr/bin" "usr/local/bin"; do
+			local cmd
+			for cmd in "$TOOLCHAIN/$bin/"*-*cc*; do
+				if [ -x "$cmd" ] && [ ! -h "$cmd" ]; then
+					CC="$(cd "${cmd%/*}"; pwd)/${cmd##*/}"
+					return 0
+				fi
+			done
+		done
+		return 1
+	fi
+	return 0
+}
+
+probe_cxx() {
+	if [ -z "$CXX" ]; then
+		local bin
+		for bin in "bin" "usr/bin" "usr/local/bin"; do
+			local cmd
+			for cmd in "$TOOLCHAIN/$bin/"*-*++*; do
+				if [ -x "$cmd" ] && [ ! -h "$cmd" ]; then
+					CXX="$(cd "${cmd%/*}"; pwd)/${cmd##*/}"
+					return 0
+				fi
+			done
+		done
+		return 1
+	fi
+	return 0
+}
+
+probe_cpp() {
+	if [ -z "$CPP" ]; then
+		local bin
+		for bin in "bin" "usr/bin" "usr/local/bin"; do
+			local cmd
+			for cmd in "$TOOLCHAIN/$bin/"*-cpp*; do
+				if [ -x "$cmd" ] && [ ! -h "$cmd" ]; then
+					CPP="$(cd "${cmd%/*}"; pwd)/${cmd##*/}"
+					return 0
+				fi
+			done
+		done
+		return 1
+	fi
+	return 0
+}
+
+probe_libc() {
+	if [ -f $TOOLCHAIN/info.mk ]; then
+		LIBC_TYPE=$(grep LIBC_TYPE $TOOLCHAIN/info.mk | sed 's/LIBC_TYPE=//')
+		return 0
+	fi
+
+	echo "Warning! Can't find info.mk, trying to detect with alternative way."
+
+	if [ -z "$LIBC_TYPE" ]; then
+		if test_uclibc; then
+			LIBC_TYPE="uclibc"
+		else
+			LIBC_TYPE="glibc"
+		fi
+	fi
+	return 0
+}
+
+
+while [ -n "$1" ]; do
+	arg="$1"; shift
+	case "$arg" in
+		--toolchain)
+			[ -d "$1" ] || {
+				echo "Toolchain directory '$1' does not exist." >&2
+				exit 1
+			}
+			TOOLCHAIN="$(cd "$1"; pwd)"; shift
+		;;
+
+		--cflags)
+			CFLAGS="${CFLAGS:+$CFLAGS }$1"; shift
+		;;
+
+		--print-libc)
+			if probe_cc; then
+				probe_libc
+				echo "$LIBC_TYPE"
+				exit 0
+			fi
+			echo "No C compiler found in '$TOOLCHAIN'." >&2
+			exit 1
+		;;
+
+		--print-target)
+			if probe_cc; then
+				exec "$CC" $CFLAGS -dumpmachine
+			fi
+			echo "No C compiler found in '$TOOLCHAIN'." >&2
+			exit 1
+		;;
+
+		--print-bin)
+			if [ -z "$1" ]; then
+				echo "Available programs:"                      >&2
+				echo $(echo "$BIN_SPECS" | sed -ne 's#:.*$##p') >&2
+				exit 1
+			fi
+
+			find_bins "$1" || exec "$0" --toolchain "$TOOLCHAIN" --print-bin
+			exit 0
+		;;
+
+		--print-libs)
+			if [ -z "$1" ]; then
+				echo "Available libraries:"                     >&2
+				echo $(echo "$LIB_SPECS" | sed -ne 's#:.*$##p') >&2
+				exit 1
+			fi
+
+			find_libs "$1" || exec "$0" --toolchain "$TOOLCHAIN" --print-libs
+			exit 0
+		;;
+
+		--test)
+			test_feature "$1"
+			exit $?
+		;;
+
+		--wrap)
+			[ -n "$1" ] || exec "$0" --help
+			wrap_bins "$1"
+			exit $?
+		;;
+
+		--overwrite-config)
+			OVERWRITE_CONFIG=y
+		;;
+
+		--config)
+			if probe_cc; then
+				probe_libc
+				find_gcc_version
+				print_config "$1"
+				exit $?
+			fi
+			echo "No C compiler found in '$TOOLCHAIN'." >&2
+			exit 1
+		;;
+
+		-h|--help)
+			me="$(basename "$0")"
+			echo -e "\nUsage:\n"                                            >&2
+			echo -e "  $me --toolchain {directory} --print-libc"            >&2
+			echo -e "    Print the libc implementation and exit.\n"         >&2
+			echo -e "  $me --toolchain {directory} --print-target"          >&2
+			echo -e "    Print the GNU target name and exit.\n"             >&2
+			echo -e "  $me --toolchain {directory} --print-bin {program}"   >&2
+			echo -e "    Print executables belonging to given program,"     >&2
+			echo -e "    omit program argument to get a list of names.\n"   >&2
+			echo -e "  $me --toolchain {directory} --print-libs {library}"  >&2
+			echo -e "    Print shared objects belonging to given library,"  >&2
+			echo -e "    omit library argument to get a list of names.\n"   >&2
+			echo -e "  $me --toolchain {directory} --test {feature}"        >&2
+			echo -e "    Test given feature, exit code indicates success."  >&2
+			echo -e "    Possible features are 'c', 'c++', 'softfloat',"    >&2
+			echo -e "    'lfs', 'rpc', 'ipv6', 'wchar', 'locale' and "      >&2
+			echo -e "    'threads'.\n"                                      >&2
+			echo -e "  $me --toolchain {directory} --wrap {directory}"      >&2
+			echo -e "    Create wrapper scripts for C and C++ compiler, "   >&2
+			echo -e "    linker, assembler and other key executables in "   >&2
+			echo -e "    the directory given with --wrap.\n"                >&2
+			echo -e "  $me --toolchain {directory} --config {target}"       >&2
+			echo -e "    Analyze the given toolchain and print a suitable"  >&2
+			echo -e "    .config for the given target. Omit target "        >&2
+			echo -e "    argument to get a list of names.\n"                >&2
+			echo -e "  $me --help"                                          >&2
+			echo -e "    Display this help text and exit.\n\n"              >&2
+			echo -e "  Most commands also take a --cflags parameter which " >&2
+			echo -e "  is used to specify C flags to be passed to the "     >&2
+			echo -e "  cross compiler when performing tests."               >&2
+			echo -e "  This parameter may be repeated multiple times."      >&2
+			echo -e "  Use --overwrite-config before --config to overwrite" >&2
+			echo -e "  an already present config with the required changes.">&2
+			exit 1
+		;;
+
+		*)
+			echo "Unknown argument '$arg'" >&2
+			exec $0 --help
+		;;
+	esac
+done
+
+exec $0 --help
diff --git a/scripts/ext-tools.sh b/scripts/ext-tools.sh
new file mode 100755
index 0000000..b58296b
--- /dev/null
+++ b/scripts/ext-tools.sh
@@ -0,0 +1,114 @@
+#!/usr/bin/env bash
+
+TOOLS_TAR=""
+HOST_BUILD_DIR=$(pwd)/"build_dir/host"
+HOST_STAGING_DIR_STAMP=$(pwd)/"staging_dir/host/stamp"
+
+refresh_timestamps() {
+	find -H "$1" -not -type l -print0 | xargs -0 touch
+}
+
+extract_prebuilt_tar() {
+	tar -xf "$1"
+}
+
+refresh_prebuilt_tools() {
+	if [ ! -d "$HOST_BUILD_DIR" ]; then
+		echo "Can't find Host Build Dir "$HOST_BUILD_DIR"" >&2
+		exit 1
+	fi
+
+	refresh_timestamps "$HOST_BUILD_DIR"
+	sleep 1
+
+	if [ ! -d "$HOST_STAGING_DIR_STAMP" ]; then
+		echo "Can't find Host Staging Dir Stamp "$HOST_STAGING_DIR_STAMP"" >&2
+		exit 1
+	fi
+
+	refresh_timestamps "$HOST_STAGING_DIR_STAMP"
+
+	return 0
+}
+
+install_prebuilt_tools() {
+	extract_prebuilt_tar "$TOOLS_TAR"
+
+	refresh_prebuilt_tools
+
+	return 0
+}
+
+while [ -n "$1" ]; do
+	arg="$1"; shift
+	case "$arg" in
+		--host-build-dir)
+			[ -d "$1" ] || {
+				echo "Directory '$1' does not exist." >&2
+				exit 1
+			}
+			HOST_BUILD_DIR="$(cd "$1"; pwd)"; shift
+		;;
+
+		--host-staging-dir-stamp)
+			[ -d "$1" ] || {
+				echo "Directory '$1' does not exist." >&2
+				exit 1
+			}
+			HOST_STAGING_DIR_STAMP="$(cd "$1"; pwd)"; shift
+		;;
+
+		--tools)
+			[ -f "$1" ] || {
+				echo "Tools tar file '$1' does not exist." >&2
+				exit 1
+			}
+			TOOLS_TAR="$1"; shift
+			install_prebuilt_tools
+
+			exit $?
+		;;
+
+		--refresh)
+			refresh_prebuilt_tools
+
+			exit $?
+		;;
+
+		-h|--help)
+			me="$(basename "$0")"
+			echo -e "\nUsage:\n"                                            >&2
+			echo -e "  $me --host-build-dir {directory}"                    >&2
+			echo -e "    Set to refresh timestamp of this build directory"  >&2
+			echo -e "    with --tools."                                     >&2
+			echo -e "    THIS OPTION MUST BE SET BEFORE --tools."           >&2
+			echo -e "    If not provided the default directory is:"         >&2
+			echo -e "    $(pwd)/build_dir/host\n"                           >&2
+			echo -e "  $me --host-staging-dir-stamp {directory}"            >&2
+			echo -e "    Set to refresh staging timestamp present in this"  >&2
+			echo -e "    directory with --tools."                           >&2
+			echo -e "    THIS OPTION MUST BE SET BEFORE --tools."           >&2
+			echo -e "    If not provided the default directory is:"         >&2
+			echo -e "    $(pwd)/staging_dir/host/stamp\n"                   >&2
+			echo -e "  $me --tools {tar}"                                   >&2
+			echo -e "    Install the prebuilt tools present in the passed"  >&2
+			echo -e "    tar and prepare them."                             >&2
+			echo -e "    To correctly use them it's needed to update the"   >&2
+			echo -e "    timestamp of each tools to skip recompilation.\n"  >&2
+			echo -e "  $me --refresh"                                       >&2
+			echo -e "    Refresh timestamps of already extracted prebuilt"  >&2
+			echo -e "    tools to correctly use them and skip"              >&2
+			echo -e "    recompilation.\n"                                  >&2
+			echo -e "  $me --help"                                          >&2
+			echo -e "    Display this help text and exit.\n\n"              >&2
+			exit 1
+		;;
+
+		*)
+			echo "Unknown argument '$arg'" >&2
+			exec $0 --help
+		;;
+	esac
+done
+
+exec $0 --help
diff --git a/scripts/feeds b/scripts/feeds
new file mode 100755
index 0000000..959995c
--- /dev/null
+++ b/scripts/feeds
@@ -0,0 +1,962 @@
+#!/usr/bin/env perl
+use Getopt::Std;
+use FindBin;
+use Cwd;
+use lib "$FindBin::Bin";
+use metadata;
+use warnings;
+use strict;
+use Cwd 'abs_path';
+
+chdir "$FindBin::Bin/..";
+$ENV{TOPDIR} //= getcwd();
+chdir $ENV{TOPDIR};
+$ENV{GIT_CONFIG_PARAMETERS}="'core.autocrlf=false'";
+$ENV{GREP_OPTIONS}="";
+
+my $mk=`command -v gmake 2>/dev/null`;	# select the right 'make' program
+chomp($mk);		# trim trailing newline
+$mk or $mk = "make";	# default to 'make'
+
+# check version of make
+my @mkver = split /\s+/, `$mk -v`, 4;
+my $valid_mk = 1;
+$mkver[0] =~ /^GNU/ or $valid_mk = 0;
+$mkver[1] =~ /^Make/ or $valid_mk = 0;
+
+my ($mkv1, $mkv2) = split /\./, $mkver[2];
+($mkv1 >= 4 || ($mkv1 == 3 && $mkv2 >= 81)) or $valid_mk = 0;
+
+$valid_mk or die "Unsupported version of make found: $mk\n";
+
+my @feeds;
+my %build_packages;
+my %installed;
+my %installed_pkg;
+my %installed_targets;
+my %feed_cache;
+
+my $feed_package = {};
+my $feed_src = {};
+my $feed_target = {};
+my $feed_vpackage = {};
+
+sub parse_file($$);
+
+sub parse_file($$) {
+	my ($fname, $existing) = @_;
+	my $line = 0;
+	my $fh;
+
+	open $fh, $fname or return undef;
+	while (<$fh>) {
+		chomp;
+		s/#.+$//;
+		$line++;
+		next unless /\S/;
+
+		my ($type, $flags, $name, $urls) = m!^src-([\w\-]+)((?:\s+--\w+(?:=\S+)?)*)\s+(\w+)(?:\s+(\S.*))?$!;
+		unless ($type && $name) {
+			die "Syntax error in $fname, line $line\n";
+		}
+
+		if ($existing->{$name}++) {
+			die "Duplicate feed name '$name' in '$fname' line: $line\n";
+		}
+
+		my @src = defined($urls) ? split /\s+/, $urls : ();
+		push @src, '' if @src == 0;
+
+		my %flags;
+		if (defined $flags) {
+			while ($flags =~ m!\s+--(\w+)(?:=(\S+))?!g) {
+				$flags{$1} = defined($2) ? $2 : 1;
+			}
+		}
+
+		if ($type eq "include") {
+			parse_file($urls, $existing) or
+			    die "Unable to open included file '$urls'";
+			next;
+		}
+
+		push @feeds, ["src-$type", $name, \@src, \%flags];
+	}
+	close $fh;
+	return 1;
+}
+
+sub parse_config() {
+	my %name;
+	parse_file("feeds.conf", \%name) or
+	    parse_file("feeds.conf.default", \%name)  or
+	    die "Unable to open feeds configuration";
+}
+
+sub update_location($$)
+{
+	my $name = shift;
+	my $url  = shift;
+	my $old_url;
+
+	-d "./feeds/$name.tmp" or mkdir "./feeds/$name.tmp" or return 1;
+
+	if( open LOC, "< ./feeds/$name.tmp/location" )
+	{
+		chomp($old_url = readline LOC);
+		close LOC;
+	}
+
+	if( !$old_url || $old_url ne $url )
+	{
+		if( open LOC, "> ./feeds/$name.tmp/location" )
+		{
+			print LOC $url, "\n";
+			close LOC;
+		}
+		return $old_url ? 1 : 0;
+	}
+
+	return 0;
+}
+
+sub update_index($)
+{
+	my $name = shift;
+
+	-d "./feeds/$name.tmp" or mkdir "./feeds/$name.tmp" or return 1;
+	-d "./feeds/$name.tmp/info" or mkdir "./feeds/$name.tmp/info" or return 1;
+
+	system("$mk -s prepare-mk OPENWRT_BUILD= TMP_DIR=\"$ENV{TOPDIR}/feeds/$name.tmp\"");
+	system("$mk -s -f include/scan.mk IS_TTY=1 SCAN_TARGET=\"packageinfo\" SCAN_DIR=\"feeds/$name\" SCAN_NAME=\"package\" SCAN_DEPTH=5 SCAN_EXTRA=\"\" TMP_DIR=\"$ENV{TOPDIR}/feeds/$name.tmp\"");
+	system("$mk -s -f include/scan.mk IS_TTY=1 SCAN_TARGET=\"targetinfo\" SCAN_DIR=\"feeds/$name\" SCAN_NAME=\"target\" SCAN_DEPTH=5 SCAN_EXTRA=\"\" SCAN_MAKEOPTS=\"TARGET_BUILD=1\" TMP_DIR=\"$ENV{TOPDIR}/feeds/$name.tmp\"");
+	system("ln -sf $name.tmp/.packageinfo ./feeds/$name.index");
+	system("ln -sf $name.tmp/.targetinfo ./feeds/$name.targetindex");
+
+	return 0;
+}
+
+my %update_method = (
+	'src-svn' => {
+		'init'		=> "svn checkout '%s' '%s'",
+		'update'	=> "svn update",
+		'controldir'	=> ".svn",
+		'revision'	=> "svn info | grep 'Revision' | cut -d ' ' -f 2 | tr -d '\n'"},
+	'src-cpy' => {
+		'init'		=> "cp -Rf '%s' '%s'",
+		'update'	=> "",
+		'revision'	=> "echo -n 'local'"},
+	'src-link' => {
+		'init'		=> "ln -s '%s' '%s'",
+		'update'	=> "",
+		'revision'	=> "echo -n 'local'"},
+	'src-dummy' => {
+		'init'		=> "true '%s' && mkdir '%s'",
+		'update'	=> "",
+		'revision'	=> "echo -n 'dummy'"},
+	'src-git' => {
+		'init'          => "git clone --depth 1 '%s' '%s'",
+		'init_branch'   => "git clone --depth 1 --branch '%s' '%s' '%s'",
+		'init_commit'   => "git clone --depth 1 '%s' '%s' && cd '%s' && git fetch --depth=1 origin '%s' && git -c advice.detachedHead=false checkout '%s' && cd -",
+		'update'	=> "git pull --ff-only",
+		'update_rebase'	=> "git pull --rebase=merges",
+		'update_stash'	=> "git pull --rebase=merges --autostash",
+		'update_force'	=> "git pull --ff-only || (git reset --hard HEAD; git pull --ff-only; exit 1)",
+		'post_update'	=> "git submodule update --init --recursive --depth 1",
+		'controldir'	=> ".git",
+		'revision'	=> "git rev-parse HEAD | tr -d '\n'"},
+	'src-git-full' => {
+		'init'          => "git clone '%s' '%s'",
+		'init_branch'   => "git clone --branch '%s' '%s' '%s'",
+		'init_commit'   => "git clone '%s' '%s' && cd '%s' && git checkout -b '%s' '%s' && cd -",
+		'update'	=> "git pull --ff-only",
+		'update_rebase'	=> "git pull --rebase=merges",
+		'update_stash'	=> "git pull --rebase=merges --autostash",
+		'update_force'	=> "git pull --ff-only || (git reset --hard HEAD; git pull --ff-only; exit 1)",
+		'post_update'	=> "git submodule update --init --recursive",
+		'controldir'	=> ".git",
+		'revision'	=> "git rev-parse HEAD | tr -d '\n'"},
+	'src-gitsvn' => {
+		'init'	=> "git svn clone -r HEAD '%s' '%s'",
+		'update'	=> "git svn rebase",
+		'controldir'	=> ".git",
+		'revision'	=> "git rev-parse HEAD | tr -d '\n'"},
+	'src-bzr' => {
+		'init'		=> "bzr checkout --lightweight '%s' '%s'",
+		'update'	=> "bzr update",
+		'controldir'	=> ".bzr"},
+	'src-hg' => {
+		'init'		=> "hg clone '%s' '%s'",
+		'update'	=> "hg pull --update",
+		'controldir'	=> ".hg"},
+	'src-darcs' => {
+		'init'    => "darcs get '%s' '%s'",
+		'update'  => "darcs pull -a",
+		'controldir' => "_darcs"},
+);
+
+# src-git: pull broken
+# src-cpy: broken if `basename $src` != $name
+
+sub update_feed_via($$$$$$$) {
+	my $type = shift;
+	my $name = shift;
+	my $src = shift;
+	my $relocate = shift;
+	my $force = shift;
+	my $rebase = shift;
+	my $stash = shift;
+
+	my $m = $update_method{$type};
+	my $localpath = "./feeds/$name";
+	my $safepath = $localpath;
+	$safepath =~ s/'/'\\''/;
+	my ($base_branch, $branch) = split(/;/, $src, 2);
+	my ($base_commit, $commit) = split(/\^/, $src, 2);
+
+	if( $relocate || !$m->{'update'} || !-d "$localpath/$m->{'controldir'}" ) {
+		system("rm -rf '$safepath'");
+		if ($m->{'init_branch'} and $branch) {
+			system(sprintf($m->{'init_branch'}, $branch, $base_branch, $safepath)) == 0 or return 1;
+		} elsif ($m->{'init_commit'} and $commit) {
+			system(sprintf($m->{'init_commit'}, $base_commit, $safepath, $safepath, $commit, $commit)) == 0 or return 1;
+		} else {
+			system(sprintf($m->{'init'}, $src, $safepath)) == 0 or return 1;
+		}
+	} elsif ($m->{'init_commit'} and $commit) {
+		# in case git hash has been provided don't update the feed
+	} else {
+		my $update_cmd = $m->{'update'};
+		if ($force && exists $m->{'update_force'}) {
+			$update_cmd = $m->{'update_force'};
+		}
+		if ($rebase && exists $m->{'update_rebase'}) {
+			$update_cmd = $m->{'update_rebase'};
+		}
+		if ($stash && exists $m->{'update_stash'}) {
+			$update_cmd = $m->{'update_stash'};
+		}
+		system("cd '$safepath'; $update_cmd") == 0 or return 1;
+	}
+	if ($m->{'post_update'}) {
+		my $cmd = $m->{'post_update'};
+		system("cd '$safepath'; $cmd") == 0 or return 1;
+	}
+
+	return 0;
+}
+
+sub get_targets($) {
+	my $file = shift;
+	my @target = parse_target_metadata($file);
+	my %target;
+	foreach my $target (@target) {
+		$target{$target->{id}} = $target;
+	}
+	return %target
+}
+
+sub get_feed($) {
+	my $feed = shift;
+
+	if (!defined($feed_cache{$feed})) {
+		my $file = "./feeds/$feed.index";
+
+		clear_packages();
+		-f $file or do {
+			print "Ignoring feed '$feed' - index missing\n";
+			return;
+		};
+		parse_package_metadata($file) or return;
+		my %target = get_targets("./feeds/$feed.targetindex");
+
+		$feed_cache{$feed} = [ { %package }, { %srcpackage }, { %target }, { %vpackage } ];
+	}
+
+	$feed_package = $feed_cache{$feed}->[0];
+	$feed_src = $feed_cache{$feed}->[1];
+	$feed_target = $feed_cache{$feed}->[2];
+	$feed_vpackage = $feed_cache{$feed}->[3];
+}
+
+sub get_installed() {
+	system("$mk -s prepare-tmpinfo OPENWRT_BUILD=");
+	clear_packages();
+	parse_package_metadata("./tmp/.packageinfo");
+	%installed_pkg = %vpackage;
+	%installed = %srcpackage;
+	%installed_targets = get_targets("./tmp/.targetinfo");
+}
+
+sub search_feed {
+	my $feed = shift;
+	my @substr = @_;
+	my $display;
+
+	return unless @substr > 0;
+	get_feed($feed);
+	foreach my $name (sort { lc($a) cmp lc($b) } keys %$feed_package) {
+		my $pkg = $feed_package->{$name};
+		my $substr;
+		my $pkgmatch = 1;
+
+		foreach my $substr (@substr) {
+			my $match;
+			foreach my $key (qw(name title description src)) {
+				$pkg->{$key} and $substr and $pkg->{$key} =~ m/$substr/i and $match = 1;
+			}
+			$match or undef $pkgmatch;
+		};
+		$pkgmatch and do {
+			$display or do {
+				print "Search results in feed '$feed':\n";
+				$display = 1;
+			};
+			printf "\%-25s\t\%s\n", $pkg->{name}, $pkg->{title};
+		};
+	}
+
+	foreach my $name (sort { lc($a) cmp lc($b) } keys %$feed_target) {
+		my $target = $feed_target->{$name};
+		my $targetmatch = 1;
+
+		foreach my $substr (@substr) {
+			my $match;
+			foreach my $key (qw(id name description)) {
+				$target->{$key} and $substr and $target->{$key} =~ m/$substr/i and $match = 1;
+			}
+			$match or undef $targetmatch;
+		};
+		$targetmatch and do {
+			$display or do {
+				print "Search results in feed '$feed':\n";
+				$display = 1;
+			};
+			printf "TARGET: \%-17s\t\%s\n", $target->{id}, $target->{name};
+		};
+	}
+	return 0;
+}
+
+sub search {
+	my %opts;
+
+	getopt('r:', \%opts);
+	foreach my $feed (@feeds) {
+		search_feed($feed->[1], @ARGV) if (!defined($opts{r}) or $opts{r} eq $feed->[1]);
+	}
+}
+
+sub list_feed {
+	my $feed = shift;
+
+	get_feed($feed);
+	foreach my $name (sort { lc($a) cmp lc($b) } keys %$feed_package) {
+		my $pkg = $feed_package->{$name};
+		if($pkg->{name}) {
+			printf "\%-32s\t\%s\n", $pkg->{name}, $pkg->{title};
+		}
+	}
+
+	foreach my $name (sort { lc($a) cmp lc($b) } keys %$feed_target) {
+		my $target = $feed_target->{$name};
+		if($target->{name}) {
+			printf "TARGET: \%-24s\t\%s\n", $target->{id}, $target->{name};
+		}
+	}
+
+	return 0;
+}
+
+sub list {
+	my %opts;
+
+	getopts('r:d:nshf', \%opts);
+	if ($opts{h}) {
+		usage();
+		return 0;
+	}
+	if ($opts{n}) {
+		foreach my $feed (@feeds) {
+			printf "%s\n", $feed->[1];
+		}
+		return 0;
+	}
+	if ($opts{s}) {
+		foreach my $feed (@feeds) {
+			my $localpath = "./feeds/$feed->[1]";
+			my $m = $update_method{$feed->[0]};
+			my $revision;
+			if (!-d "$localpath" || !$m->{'revision'}) {
+				$revision = "X";
+			}
+			elsif( $m->{'controldir'} && -d "$localpath/$m->{'controldir'}" ) {
+				$revision = `cd '$localpath'; $m->{'revision'}`;
+			}
+			else {
+				$revision = "local";
+			}
+			if ($opts{d}) {
+				printf "%s%s%s%s%s%s%s\n", $feed->[1], $opts{d}, $feed->[0], $opts{d}, $revision, $opts{d}, join(", ", @{$feed->[2]});
+			}
+			elsif ($opts{f}) {
+				my $uri = join(", ", @{$feed->[2]});
+				if ($revision ne "local" && $revision ne "X") {
+					$uri =~ s/[;^].*//;
+					$uri .= "^" . $revision;
+				}
+				printf "%s %s %s\n", $feed->[0], $feed->[1], $uri;
+			}
+			else {
+				printf "\%-10s \%-8s \%-8s \%s\n", $feed->[1], $feed->[0], $revision, join(", ", @{$feed->[2]});
+			}
+		}
+		return 0;
+	}
+	foreach my $feed (@feeds) {
+		list_feed($feed->[1], @ARGV) if (!defined($opts{r}) or $opts{r} eq $feed->[1]);
+	}
+	return 0;
+}
+
+sub do_install_src($$) {
+	my $feed = shift;
+	my $src = shift;
+
+	my $path = $src->{makefile};
+	if ($path) {
+		$path =~ s/\/Makefile$//;
+
+		-d "./package/feeds" or mkdir "./package/feeds";
+		-d "./package/feeds/$feed->[1]" or mkdir "./package/feeds/$feed->[1]";
+		system("ln -sf ../../../$path ./package/feeds/$feed->[1]/");
+	} else {
+		warn "Package is not valid\n";
+		return 1;
+	}
+
+	return 0;
+}
+
+sub do_install_target($) {
+	my $target = shift;
+	my $path = $target->{makefile};
+
+	if ($path) {
+		$path =~ s/\/Makefile$//;
+		my $name = $path;
+		$name =~ s/.*\///;
+		my $dest = "./target/linux/feeds/$name";
+
+		-d "./target/linux/feeds" or mkdir "./target/linux/feeds";
+
+		-e $dest and do {
+			warn "Path $dest already exists";
+			return 1;
+		};
+
+		system("ln -sf ../../../$path ./target/linux/feeds/");
+	} else {
+		warn "Target is not valid\n";
+		return 1;
+	}
+
+	# Clean packageinfo of linux kernel to force the scan.
+	# Otherwise kernel modules defined at target level are not scanned, as the
+	# linux kernel package was scanned before the installation of the target.
+	unlink "tmp/info/.packageinfo-kernel_linux";
+
+	return 0;
+}
+
+sub lookup_src($$) {
+	my $feed = shift;
+	my $src = shift;
+
+	foreach my $feed ($feed, @feeds) {
+		next unless $feed->[1];
+		next unless $feed_cache{$feed->[1]};
+		$feed_cache{$feed->[1]}->[1]->{$src} and return $feed;
+	}
+	return;
+}
+
+sub lookup_package($$) {
+	my $feed = shift;
+	my $package = shift;
+
+	foreach my $feed ($feed, @feeds) {
+		next unless $feed->[1];
+		next unless $feed_cache{$feed->[1]};
+		$feed_cache{$feed->[1]}->[3]->{$package} and return $feed;
+	}
+	return;
+}
+
+sub lookup_target($$) {
+	my $feed = shift;
+	my $target = shift;
+
+	foreach my $feed ($feed, @feeds) {
+		next unless $feed->[1];
+		next unless $feed_cache{$feed->[1]};
+		$feed_cache{$feed->[1]}->[2]->{$target} and return $feed;
+	}
+	return;
+}
+
+sub is_core_src($) {
+	my $src = shift;
+	foreach my $file ("tmp/info/.packageinfo-$src", glob("tmp/info/.packageinfo-*_$src")) {
+		next unless index($file, "tmp/info/.packageinfo-feeds_");
+		return 1 if -s $file;
+	}
+	return 0;
+}
+
+sub install_target {
+	my $feed = shift;
+	my $name = shift;
+	my $force = shift;
+
+	$feed = lookup_target($feed, $name);
+	my $feed_name = $feed->[1];
+
+	-e "target/linux/feeds/$name" and return 0;
+
+	# enable force flag if feed src line was declared with --force
+	if (exists($feed->[3]{force})) {
+		$force = 1;
+	}
+
+	$feed = $feed_cache{$feed_name}->[2];
+	$feed or return 0;
+
+	my $target = $feed->{$name};
+	$target or return 0;
+
+	if (-e "target/linux/$name") {
+		if ($force) {
+			warn "Overriding target '$name' with version from '$feed_name'\n";
+		} else {
+			warn "WARNING: Not overriding core target '$name'; use -f to force\n";
+			return 0;
+		}
+	} else {
+		warn "Installing target '$name'\n";
+	}
+	return do_install_target($target);
+}
+
+sub install_src {
+	my $feed = shift;
+	my $name = shift;
+	my $force = shift;
+	my $ret = 0;
+
+	my $select_feed = lookup_src($feed, $name);
+	unless ($select_feed) {
+		$installed{$name} and return 0;
+		$feed_src->{$name} or warn "WARNING: No feed for source package '$name' found\n";
+		return 0;
+	}
+
+	# switch to the metadata for the selected feed
+	get_feed($select_feed->[1]);
+	my $src = $feed_src->{$name} or return 1;
+
+	# enable force flag if feed src line was declared with --force
+	if (exists($select_feed->[3]{force})) {
+		$force = 1;
+	}
+
+	# If it's a core package and we don't want to override, just return
+	my $override = 0;
+	if (is_core_src($name)) {
+		if (!$force) {
+			if ($name ne "toolchain" && $name ne "linux") {
+				warn "WARNING: Not overriding core package '$name'; use -f to force\n";
+			}
+			return 0;
+		}
+		$override = 1;
+	}
+
+	if ($installed{$name}) {
+		# newly installed packages set the source package to 1
+		return 0 if ($installed{$name} == 1);
+		return 0 unless ($override);
+	}
+
+	$installed{$name} = 1;
+	foreach my $pkg (@{$src->{packages}}) {
+		foreach my $vpkg (@{$pkg->{provides}}) {
+			$installed_pkg{$vpkg} = 1;
+		}
+	}
+
+	if ($override) {
+		warn "Overriding core package '$name' with version from $select_feed->[1]\n";
+	} else {
+		warn "Installing package '$name' from $select_feed->[1]\n";
+	}
+
+	do_install_src($select_feed, $src) == 0 or do {
+		warn "failed.\n";
+		return 1;
+	};
+
+	# install all dependencies referenced from the source package
+	foreach my $dep (
+		@{$src->{builddepends}},
+		@{$src->{'builddepends/host'}},
+	) {
+		next if $dep =~ /@/;
+		$dep =~ s/^.+://;
+		$dep =~ s/\/.+$//;
+		next unless $dep;
+		install_src($feed, $dep, 0) == 0 or $ret = 1;
+	}
+
+	foreach my $pkg (@{$src->{packages}}) {
+		foreach my $dep (@{$pkg->{depends}}) {
+			next if $dep =~ /@/;
+			$dep =~ s/^\+//;
+			$dep =~ s/^.+://;
+			next unless $dep;
+			install_package($feed, $dep, 0) == 0 or $ret = 1;
+		}
+	}
+
+	return $ret;
+}
+
+sub install_package {
+	my $feed = shift;
+	my $name = shift;
+	my $force = shift;
+
+	my $select_feed = lookup_package($feed, $name);
+	unless ($select_feed) {
+		$installed_pkg{$name} and return 0;
+		$feed_vpackage->{$name} or warn "WARNING: No feed for package '$name' found\n";
+		return 0;
+	}
+
+	# switch to the metadata for the selected feed
+	get_feed($select_feed->[1]);
+	my $pkg = $feed_vpackage->{$name} or return 1;
+	return install_src($feed, $pkg->[0]{src}{name}, $force);
+}
+
+sub install_target_or_package {
+	my $feed = shift;
+	my $name = shift;
+	my $force = shift;
+
+	lookup_target($feed, $name) and do {
+		return install_target($feed, $name, $force);
+	};
+
+	lookup_src($feed, $name) and do {
+		return install_src($feed, $name, $force);
+	};
+
+	return install_package($feed, $name, $force);
+}
+
+sub refresh_config {
+	my $default = shift;
+
+	# Don't create .config if it doesn't already exist so that making a
+	# config only occurs when the user intends it do (however we do
+	# want to refresh an existing config).
+	return if not (-e '.config');
+
+	# workaround for timestamp check
+	system("rm -f tmp/.packageinfo");
+
+	# refresh the config
+	if ($default) {
+		system("$mk oldconfig CONFDEFAULT=\"$default\" Config.in >/dev/null 2>/dev/null");
+	} else {
+		system("$mk defconfig Config.in >/dev/null 2>/dev/null");
+	}
+}
+
+sub install {
+	my $name;
+	my %opts;
+	my $feed;
+	my $ret = 0;
+
+	getopts('ap:d:fh', \%opts);
+
+	if ($opts{h}) {
+		usage();
+		return 0;
+	}
+
+	get_installed();
+
+	foreach my $f (@feeds) {
+		# fetch all feeds
+		get_feed($f->[1]);
+
+		# look up the preferred feed
+		$opts{p} and $f->[1] eq $opts{p} and $feed = $f;
+	}
+
+	if($opts{a}) {
+		foreach my $f (@feeds) {
+			if (!defined($opts{p}) or $opts{p} eq $f->[1]) {
+				printf "Installing all packages from feed %s.\n", $f->[1];
+				get_feed($f->[1]);
+				foreach my $name (sort { lc($a) cmp lc($b) } keys %$feed_src) {
+					install_src($feed, $name, exists($opts{f})) == 0 or $ret = 1;
+					get_feed($f->[1]);
+				}
+			}
+		}
+	} else {
+		while ($name = shift @ARGV) {
+			install_target_or_package($feed, $name, exists($opts{f})) == 0 or $ret = 1;
+		}
+	}
+
+	# workaround for timestamp check
+
+	# set the defaults
+	if ($opts{d} and $opts{d} =~ /^[ymn]$/) {
+		refresh_config($opts{d});
+	}
+
+	return $ret;
+}
+
+sub uninstall_target($) {
+	my $dir = shift;
+	my $name = $dir;
+	$name =~ s/.*\///g;
+
+	my $dest = readlink $dir;
+	return unless $dest =~ /..\/..\/feeds/;
+	warn "Uninstalling target '$name'\n";
+	unlink "$dir";
+}
+
+sub uninstall {
+	my %opts;
+	my $name;
+	my $uninstall;
+
+	getopts('ah', \%opts);
+
+	if ($opts{h}) {
+		usage();
+		return 0;
+	}
+
+	if ($opts{a}) {
+		system("rm -rvf ./package/feeds");
+		foreach my $dir (glob "target/linux/*") {
+			next unless -l $dir;
+			uninstall_target($dir);
+		}
+		$uninstall = 1;
+	} else {
+		if($#ARGV == -1) {
+			warn "WARNING: no package to uninstall\n";
+			return 0;
+		}
+		get_installed();
+		while ($name = shift @ARGV) {
+			my $target = "target/linux/feeds/$name";
+			-l "$target" and do {
+				uninstall_target($target);
+				$uninstall = 1;
+				next;
+			};
+
+			my $pkg = $installed{$name};
+			$pkg or do {
+				warn "WARNING: $name not installed\n";
+				next;
+			};
+			$pkg->{src} and $name = $pkg->{src}{name};
+			warn "Uninstalling package '$name'\n";
+			system("rm -f ./package/feeds/*/$name");
+			$uninstall = 1;
+		}
+	}
+	$uninstall and refresh_config();
+	return 0;
+}
+
+sub update_feed($$$$$$)
+{
+	my $type=shift;
+	my $name=shift;
+	my $src=shift;
+	my $force_update=shift;
+	my $rebase_update=shift;
+	my $stash_update=shift;
+	my $force_relocate=update_location( $name, "@$src" );
+	my $rv=0;
+
+	if( $force_relocate ) {
+		warn "Source of feed $name has changed, replacing copy\n";
+	}
+	$update_method{$type} or do {
+		warn "Unknown type '$type' in feed $name\n";
+		return 1;
+	};
+
+	my $failed = 1;
+	foreach my $feedsrc (@$src) {
+		warn "Updating feed '$name' from '$feedsrc' ...\n";
+		if (update_feed_via($type, $name, $feedsrc, $force_relocate, $force_update, $rebase_update, $stash_update) != 0) {
+			if ($force_update) {
+				$rv=1;
+				$failed=0;
+				warn "failed, ignore.\n";
+				next;
+			}
+			last;
+		}
+		$failed = 0;
+	}
+	$failed and do {
+		warn "failed.\n";
+		return 1;
+	};
+	return $rv;
+}
+
+sub update {
+	my %opts;
+	my %argv_feeds;
+	my $failed=0;
+
+	$ENV{SCAN_COOKIE} = $$;
+	$ENV{OPENWRT_VERBOSE} = 's';
+
+	getopts('ahifrs', \%opts);
+	%argv_feeds = map { $_ => 1 } @ARGV;
+
+	if ($opts{h}) {
+		usage();
+		return 0;
+	}
+
+	if ($opts{f} && ($opts{r} || $opts{s})) {
+		warn "Force and rebase/stash are incompatible update options.\n";;
+		return 1;
+	}
+
+	-d "feeds" or do {
+			mkdir "feeds" or die "Unable to create the feeds directory";
+		};
+
+	my @index_feeds;
+	foreach my $feed (@feeds) {
+		my ($type, $name, $src) = @$feed;
+		next unless $#ARGV == -1 or $opts{a} or $argv_feeds{$name};
+		if (not $opts{i}) {
+			update_feed($type, $name, $src, $opts{f}, $opts{r}, $opts{s}) == 0 or $failed=1;
+		}
+		push @index_feeds, $name;
+	}
+	foreach my $name (@index_feeds) {
+		warn "Create index file './feeds/$name.index' \n";
+		update_index($name) == 0 or do {
+			warn "failed.\n";
+			$failed=1;
+		};
+	}
+
+	refresh_config();
+
+	return $failed;
+}
+
+sub feed_config() {
+	foreach my $feed (@feeds) {
+		my $installed = (-f "feeds/$feed->[1].index");
+
+		printf "\tconfig FEED_%s\n", $feed->[1];
+		printf "\t\ttristate \"Enable feed %s\"\n", $feed->[1];
+		printf "\t\tdepends on PER_FEED_REPO\n";
+		printf "\t\tdefault y\n" if $installed;
+		printf "\t\thelp\n";
+		printf "\t\t Enable the \\\"%s\\\" feed in opkg distfeeds.conf and apk repositories.\n", $feed->[1];
+		printf "\t\t Say M to add the feed commented out.\n";
+		printf "\n";
+	}
+
+	return 0;
+}
+
+sub usage() {
+	print <<EOF;
+Usage: $0 <command> [options]
+
+Commands:
+	list [options]: List feeds, their content and revisions (if installed)
+	Options:
+	    -n :            List of feed names.
+	    -s :            List of feed names and their URL.
+	    -r <feedname>:  List packages of specified feed.
+	    -d <delimiter>: Use specified delimiter to distinguish rows (default: spaces)
+	    -f :            List feeds in opkg feeds.conf compatible format (when using -s).
+
+	install [options] <package>: Install a package
+	Options:
+	    -a :           Install all packages from all feeds or from the specified feed using the -p option.
+	    -p <feedname>: Prefer this feed when installing packages.
+	    -d <y|m|n>:    Set default for newly installed packages.
+	    -f :           Install will be forced even if the package exists in core OpenWrt (override)
+
+	search [options] <substring>: Search for a package
+	Options:
+	    -r <feedname>: Only search in this feed
+
+	uninstall -a|<package>: Uninstall a package
+	Options:
+	    -a :           Uninstalls all packages.
+
+	update -a|<feedname(s)>: Update packages and lists of feeds in feeds.conf .
+	Options:
+	    -a :           Update all feeds listed within feeds.conf. Otherwise the specified feeds will be updated.
+	    -r :           Update by rebase. (git only. Useful if local commits exist)
+	    -s :           Update by rebase and autostash. (git only. Useful if local commits and uncommited changes exist)
+	    -i :           Recreate the index only. No feed update from repository is performed.
+	    -f :           Force updating feeds even if there are changed, uncommitted files.
+
+	clean:             Remove downloaded/generated files.
+
+EOF
+	exit(1);
+}
+
+my %commands = (
+	'list' => \&list,
+	'update' => \&update,
+	'install' => \&install,
+	'search' => \&search,
+	'uninstall' => \&uninstall,
+	'feed_config' => \&feed_config,
+	'clean' => sub {
+		system("rm -rf ./feeds ./package/feeds ./target/linux/feeds");
+	}
+);
+
+my $arg = shift @ARGV;
+$arg or usage();
+parse_config;
+foreach my $cmd (keys %commands) {
+	$arg eq $cmd and do {
+		exit(&{$commands{$cmd}}());
+	};
+}
+usage();
diff --git a/scripts/fixup-makefile.pl b/scripts/fixup-makefile.pl
new file mode 100755
index 0000000..b6f1c74
--- /dev/null
+++ b/scripts/fixup-makefile.pl
@@ -0,0 +1,135 @@
+#!/usr/bin/env perl
+use strict;
+
+my $error;
+my %state;
+
+sub usage() {
+die <<EOF;
+Usage: $0 <file> <command> [<arguments>]
+
+Commands:
+add-hash <variable> <value>
+fix-hash <variable> <value>
+rename-var <variable> <name>
+
+EOF
+}
+
+sub set_var($) {
+	my $var = shift;
+
+	$state{var} = $var;
+	if ($var =~ /(.*):(.*)/) {
+		$state{template} = $1;
+		$state{var} = $2;
+		$state{related_var} = "URL";
+	} else {
+		$state{context} = 1;
+		$state{related_var} = "PKG_SOURCE_URL";
+	}
+}
+
+my %check_command = (
+	"add-hash" => sub {
+		set_var($ARGV[0]);
+
+		$state{value} = $ARGV[1];
+		length($ARGV[1]) == 64 or die "Invalid hash value\n";
+	},
+	"fix-hash" => sub {
+		set_var($ARGV[0]);
+
+		$state{value} = $ARGV[1];
+		$state{prev_value} = $ARGV[2];
+
+		length($ARGV[1]) == 64 or die "Invalid hash value\n";
+	},
+	"rename-var" => sub {
+		set_var($ARGV[0]);
+		$state{new_var} = $ARGV[1];
+		$state{new_var} =~ s/.*://g;
+	},
+);
+
+sub check_context($) {
+	my $line = shift;
+	return unless $state{template};
+
+	$state{next} and do {
+		$state{context} = 1;
+		undef $state{next};
+		return;
+	};
+
+	if (not $state{context}) {
+		$line =~ /^\s*define\s+$state{template}/ and $state{next} = 1;
+	} else {
+		$line =~ /^\s*endef/ and do {
+			$state{done} = 1;
+			undef $state{context};
+		}
+	}
+}
+
+my %commands = (
+	"add-hash" => sub {
+		my $line = shift;
+		check_context($line);
+		return $line unless $state{context};
+
+		# skip existing hash variable
+		return "" if $line =~ /^(\s*)$state{var}(\s*):?=(\s*)(.*)\n/;
+
+		# insert md5sum after related variable
+		return $line unless $line =~ /^(\s*)$state{related_var}(\s*):?=(\s*)(.*)\n/;
+		return "$line$1$state{var}$2:=$3$state{value}\n";
+	},
+	"fix-hash" => sub {
+		my $line = shift;
+		check_context($line);
+		return $line unless $state{context};
+		return $line unless $line =~ /^(\s*)$state{var}(\s*):?=(\s*)(.*)$state{prev_value}(.*)\n/;
+		$state{done} = 1;
+		$4 =~ /\$/ and do {
+			warn "$state{var} contains a reference to another variable, can't fix automatically\n";
+			return $line;
+		};
+		return "$1$state{var}$2:=$3$state{value}\n";
+	},
+	"rename-var" => sub {
+		my $line = shift;
+		check_context($line);
+		return $line unless $state{context};
+		return $line unless $line =~ /^(\s*)$state{var}(\s*:?=.*)\n/;
+		return "$1$state{new_var}$2\n";
+	},
+);
+
+my $file = shift @ARGV;
+my $command = shift @ARGV;
+
+($file and $command and $check_command{$command}) or usage;
+&{$check_command{$command}}();
+
+-f $file or die "File $file not found\n";
+
+open IN, "<${file}" or die "Cannot open input file\n";
+open OUT, ">${file}.new" or die "Cannot open output file\n";
+
+my $cmd = $commands{$command};
+while (my $line = <IN>) {
+	$line = &$cmd($line) unless $state{done};
+	print OUT $line;
+	last if $error;
+}
+
+close OUT;
+close IN;
+
+$error and do {
+	unlink "${file}.new";
+	exit 1;
+};
+
+rename "${file}.new", "$file";
diff --git a/scripts/flashing/adam2flash-502T.pl b/scripts/flashing/adam2flash-502T.pl
new file mode 100755
index 0000000..0c4c9d7
--- /dev/null
+++ b/scripts/flashing/adam2flash-502T.pl
@@ -0,0 +1,342 @@
+#!/usr/bin/env perl
+#
+#   D-Link DSL-502T flash utility
+#
+#   Copyright (c) 2007 Oliver Jowett <oliver@opencloud.com>
+#
+#   Based on adam2flash.pl for the D-Link DSL-G6x4T, which is:
+#   Copyright (C) 2005 Felix Fietkau <mailto@nbd.name>
+#   based on fbox recovery util by Enrik Berkhan
+#
+#   This program is free software; you can redistribute it and/or modify
+#   it under the terms of the GNU General Public License as published by
+#   the Free Software Foundation; either version 2 of the License, or
+#   (at your option) any later version.
+#
+#   This program is distributed in the hope that it will be useful,
+#   but WITHOUT ANY WARRANTY; without even the implied warranty of
+#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#   GNU General Public License for more details.
+#
+#   You should have received a copy of the GNU General Public License
+#   along with this program; if not, write to the Free Software
+#   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+#
+
+# The default DSL-502T mtd map looks like this:
+#
+# mtd0 0x90091000,0x903f0000    # filesystem
+# mtd1 0x90010090,0x90091000    # kernel
+# mtd2 0x90000000,0x90010000    # bootloader - DO NOT MODIFY
+# mtd3 0x903f0000,0x90400000    # config space - DO NOT MODIFY
+# mtd4 0x90010000,0x903f0000    # firmware signature + kernel + filesystem, used to flash new firmware
+#
+# i.e. the flash layout is:
+#
+# 90000000-9000FFFF mtd2 bootloader
+# 90010000-9001008F ---- firmware signature )
+# 90010090-90090FFF mtd1 kernel             ) mtd4 spans these three regions
+# 90091000-903EFFFF mtd0 filesystem         )
+# 903F0000-903FFFFF mtd3 config space
+#
+# The ADAM2 bootloader uses the mtd1 settings to find the start of the image to boot.
+# The image to load contains information about the loadable size of the image. If ADAM2 sees
+# that the image appears to extend beyond the end of mtd1, it will refuse to load it. On
+# the DSL-502T, this manifests as the USB light blinking rapidly on boot.
+#
+# The OpenWRT kernel does not follow quite the same layout:
+#  (a) it does not have a 0x90-byte firmware signature prefix
+#  (b) it is larger than the default mtd1 size
+#
+# (a) would be avoidable (build a custom image with a 0x90-byte prefix) but (b) is unavoidable.
+# So we *have* to change mtd1. The simplest thing to do seems to make it span all of
+# the flashable area, producing this layout:
+#
+# mtd0 0x90091000,0x903f0000    # filesystem
+# mtd1 0x90010000,0x903f0000    # kernel (CHANGED)
+# mtd2 0x90000000,0x90010000    # bootloader - DO NOT MODIFY
+# mtd3 0x903f0000,0x90400000    # config space - DO NOT MODIFY
+# mtd4 0x90010000,0x903f0000    # kernel + filesystem, used to flash new firmware
+#
+# *** NOTE NOTE NOTE NOTE ***
+#
+# /dev/mtd0 .. /dev/mtd4 when using OpenWRT do **NOT** correspond to the ADAM2 mtd0-4 settings!
+# Instead, OpenWRT scans the MTD itself and determines its own boundaries which are arranged
+# quite differently to ADAM2. It will look something like this, see dmsg on boot:
+#
+# (/dev/mtd0) 0x00000000-0x00010000 : "loader"        # Bootloader, read-only
+# (/dev/mtd1) 0x003f0000-0x00400000 : "config"        # Config space
+# (/dev/mtd2) 0x00010000-0x003f0000 : "linux"         # Firmware area (kernel + root fs + JFFS area)
+# (/dev/mtd3) 0x000d0d58-0x003f0000 : "rootfs"        # Root FS, starts immediately after kernel
+# (/dev/mtd4) 0x00280000-0x003f0000 : "rootfs_data"   # If rootfs is squashfs, start of JFFS area.
+#
+# All of those boundaries are autodetected by examining the data in flash.
+#
+# *** NOTE NOTE NOTE NOTE ***
+
+use IO::Socket::INET;
+use Socket;
+use strict;
+use warnings;
+
+sub usage() {
+	print STDERR "Usage: $0 <ip> [-setmtd1] [-noflash] [firmware.bin]\n\n";
+	print STDERR "Acquires the ADAM2 bootloader of a D-Link DSL-504T at <ip>\n";
+	print STDERR "Power off the device, start this script, then power it on.\n";
+	print STDERR "<ip> may be any spare address on the local subnet.\n\n";
+	print STDERR "If a firmware file is specified, MTD settings are verified and\n";
+	print STDERR "then the firmware is written to the router's flash.\n";
+	print STDERR "The firmware type (D-Link or OpenWRT) is automatically detected.\n\n";
+	print STDERR "  -setmtd1  update mtd1 if it is not the appropriate value for this firmware\n";
+	print STDERR "  -noflash  does normal checks, updates mtd1 if requested, but does not actually write firmware\n\n";
+	exit 0;
+}
+
+my $ip = shift @ARGV;
+$ip and $ip =~ /\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/ or usage();
+
+my $probe = IO::Socket::INET->new(Proto => 'udp',
+                                  Broadcast => 1,
+                                  LocalPort => 5035) or die "socket: $!";
+my $setip = unpack("N", inet_aton($ip));
+$setip > 0 or usage();
+
+my @packets;
+foreach my $ver ([18, 1], [22, 2]) {
+	push @packets, pack("vCCVNV", 0, @$ver, 1, $setip, 0);
+}
+print STDERR "Looking for device: ";
+my $broadcast = sockaddr_in(5035, INADDR_BROADCAST);
+my $scanning;
+my $box;
+
+$SIG{"ALRM"} = sub {
+	return if --$scanning <= 0;
+	foreach my $packet (@packets) {
+		$probe->send($packet, 0, $broadcast);
+	}
+	print STDERR ".";
+};
+
+$scanning = 15;
+foreach my $packet (@packets) {
+	$probe->send($packet, 0, $broadcast);
+}
+print STDERR ".";
+
+while($scanning) {
+	my $reply;
+
+	alarm(1);
+	if (my $peer = $probe->recv($reply, 16)) {
+		next if (length($reply) < 16);
+		my ($port, $addr) = sockaddr_in($peer);
+		my ($major, $minor1, $minor2, $code, $addr2) = unpack("vCCVV", $reply);
+		$addr2 = pack("N", $addr2);
+		if ($code == 2) {
+			$scanning = 0;
+			printf STDERR " found!\nADAM2 version $major.$minor1.$minor2 at %s (%s)\n", inet_ntoa($addr), inet_ntoa($addr2);
+			$box = inet_ntoa($addr);
+		}
+	}
+}
+
+$box or die " not found!\n";
+
+alarm(0);
+
+{
+	package ADAM2FTP;
+	use base qw(Net::FTP);
+	
+	# ADAM2 requires upper case commands, some brain dead firewall doesn't ;-)
+	sub _USER {
+		shift->command("USER",@_)->response()
+	}
+	
+	sub _GETENV {
+		my $ftp = shift;
+		my ($ok, $name, $value);
+		
+		$ftp->command("GETENV",@_);
+			while(length($ok = $ftp->response()) < 1) {
+			my $line = $ftp->getline();
+			unless (defined($value)) {
+				chomp($line);
+				($name, $value) = split(/\s+/, $line, 2);
+			}
+		}
+		$ftp->debug_print(0, "getenv: $value\n")
+		if $ftp->debug();
+		return $value;
+	}
+	
+	sub getenv {
+		my $ftp = shift;
+		my $name = shift;
+		return $ftp->_GETENV($name);
+	}
+	
+	sub _REBOOT {
+		shift->command("REBOOT")->response() == Net::FTP::CMD_OK
+	}
+	
+	sub reboot {
+		my $ftp = shift;
+		$ftp->_REBOOT;
+		$ftp->close;
+	}
+}
+
+my $file;
+my $arg;
+my $noflash = 0;
+my $setmtd1 = 0;
+while ($arg = shift @ARGV) {
+  if ($arg eq "-noflash") { $noflash = 1; }
+  elsif ($arg eq "-setmtd1") { $setmtd1 = 1; }
+  else { $file = $arg; }
+}
+
+if (!$file) {
+  print STDERR "No firmware file specified, exiting.\n";
+  exit 0;
+}
+
+#
+# Firmware checks
+#
+
+open FILE, "<$file" or die "can't open firmware file\n";
+
+# D-Link firmware starts with "MTD4" little-endian, then has an image header at 0x90
+# OpenWRT firmware just starts with an image header at 0x00
+
+my $signature;
+my $sbytes = read FILE, $signature, 4;
+($sbytes == 4) or die "can't read firmware signature: $!";
+
+my $expectedmtd4 = "0x90010000,0x903f0000";
+my $fwtype;
+my $expectedmtd1;
+
+if ($signature eq "4DTM") {
+  seek FILE, 0x90, 0 or die "can't read firmware signature: $!";
+  $sbytes = read FILE, $signature, 4;
+  ($sbytes == 4) or die "can't read firmware signature: $!";
+  if ($signature eq "\x42\xfa\xed\xfe") {
+    $fwtype = "D-Link (little-endian)";
+    $expectedmtd1 = "0x90010090,0x90091000";
+  } elsif ($signature eq "\xde\xad\xbe\x42") {
+    $fwtype = "D-Link (big-endian)";
+    $expectedmtd1 = "0x90010090,0x90091000";
+  }
+} elsif ($signature eq "\x42\xfa\xed\xfe") {
+  $fwtype = "OpenWRT (little-endian)";
+  $expectedmtd1 = "0x90010000,0x903f0000";
+} elsif ($signature eq "\xde\xad\xbe\x42") {
+  $fwtype = "OpenWRT (big-endian)";
+  $expectedmtd1 = "0x90010000,0x903f0000";
+}
+
+$fwtype or die "Unknown firmware signature (are you sure that's the right firmware?)";
+print STDERR "Firmware type: $fwtype\n";
+
+#
+# Bootloader login
+#
+
+print STDERR "logging into ADAM2 bootloader.. ";
+my $ftp = ADAM2FTP->new($box, Debug => 0, Timeout => 600) or die "can't open control connection\n";
+$ftp->login("adam2", "adam2") or die "can't login\n";
+print STDERR "ok.\n";
+
+#
+# Hardware checks
+#
+
+print STDERR "checking hardware.. ";
+my $prd = $ftp->getenv("ProductID");
+my $usb = $ftp->getenv("usb_prod");
+print STDERR "$prd / $usb.\n";
+($prd eq "AR7RD" || $prd eq "AR7DB") or die "doesn't look like a DSL-502T?";
+($usb eq "DSL-502T") or die "doesn't look like a DSL-502T?";
+
+#
+# MTD checks and update
+#
+
+print STDERR "checking MTD settings.. ";
+
+my $mtd4 = $ftp->getenv("mtd4");
+($mtd4 eq $expectedmtd4) or die "MTD4 was not as expected (should be '$expectedmtd4', was '$mtd4'). Cowardly refusing to do anything about it!";
+
+# check MTD1 setting and update if needed
+my $mtd1 = $ftp->getenv("mtd1");
+if ($mtd1 ne $expectedmtd1) {
+  die "MTD1 was not as expected (should be '$expectedmtd1', was '$mtd1'). Run with -setmtd1 to reset mtd1" unless ($setmtd1);
+  print STDERR "Setting mtd1.. ";
+  ($ftp->command("SETENV","mtd1,$expectedmtd1")->response() == Net::FTP::CMD_OK) or die "can't set mtd1";
+  $file = shift @ARGV;
+}
+
+print STDERR "ok.\n";
+
+#
+# Firmware size check
+#
+
+my $fwsize = (stat(FILE))[7];
+printf STDERR "Firmware size: 0x%08x\n", $fwsize;
+my $flashsize;
+$mtd4 =~ /^(0x\w+),(0x\w+)$/ and $flashsize = hex($2) - hex($1);
+printf STDERR "Available flash space: 0x%08x\n", $flashsize;
+die "firmware is too large" if ($flashsize < $fwsize);
+
+#
+# Flash it!
+#
+
+if ($noflash) {
+  print STDERR "Not flashing firmware as -noflash was specified.\n";
+  exit 0;
+}
+
+seek FILE, 0, 0, or die "can't seek in firmware: $!";
+
+print STDERR "Preparing to flash.. ";
+($ftp->command("MEDIA FLSH")->response() == Net::FTP::CMD_OK) or die "can't set MEDIA FLSH";
+$ftp->binary() or die "can't set binary mode";
+print STDERR "ok.\n";
+print STDERR "Erasing flash and establishing data connection (this may take a while): ";
+
+my $dc = $ftp->stor("fs mtd4");
+$dc or die "can't open data connection: $!\n";
+print STDERR "ok.\n";
+
+print STDERR "Writing firmware: ";
+while ($fwsize > 0) {
+	my $buffer;
+	my $len = ($fwsize > 1024 ? 1024 : $fwsize);
+
+	my $rbytes = read FILE, $buffer, $len;
+	($rbytes < 0) and die "read error on firmware file: $!";
+	($rbytes == $len) or die "short read on firmware file ($rbytes < $len)";
+
+	my $wbytes = $dc->write($buffer, $len, 600);
+	($wbytes < 0) and die "write error on FTP data connection: $!";
+	($rbytes == $wbytes) or die "short write on FTP data connection ($wbytes < $rbytes)";
+
+	$fwsize -= $len;
+	print STDERR ".";
+}
+
+$dc->close();
+print STDERR " done.\n";
+
+#
+# Reboot
+#
+
+print STDERR "Rebooting device.\n";
+$ftp->reboot();
diff --git a/scripts/flashing/adam2flash-fritzbox.pl b/scripts/flashing/adam2flash-fritzbox.pl
new file mode 100755
index 0000000..f8d745f
--- /dev/null
+++ b/scripts/flashing/adam2flash-fritzbox.pl
@@ -0,0 +1,209 @@
+#!/usr/bin/env perl
+#
+#   D-Link DSL-G6x4T flash utility
+#
+#   Copyright (C) 2005 Felix Fietkau <mailto@nbd.name>
+#   based on fbox recovery util by Enrik Berkhan
+#
+#   This program is free software; you can redistribute it and/or modify
+#   it under the terms of the GNU General Public License as published by
+#   the Free Software Foundation; either version 2 of the License, or
+#   (at your option) any later version.
+#
+#   This program is distributed in the hope that it will be useful,
+#   but WITHOUT ANY WARRANTY; without even the implied warranty of
+#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#   GNU General Public License for more details.
+#
+#   You should have received a copy of the GNU General Public License
+#   along with this program; if not, write to the Free Software
+#   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+#
+
+use IO::Socket::INET;
+use IO::Select;
+use Socket;
+use strict;
+use warnings;
+
+sub usage() {
+	print STDERR "Usage: $0 <ip> [firmware.bin]\n\n";
+	exit 0;
+}
+
+my $ip = shift @ARGV;
+$ip and $ip =~ /\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/ or usage();
+
+my $setip = unpack("N", inet_aton($ip));
+$setip > 0 or usage();
+
+my @packets;
+foreach my $ver ([18, 1], [22, 2]) {
+	push @packets, pack("vCCVNV", 0, @$ver, 1, $setip, 0);
+}
+print STDERR "Looking for device: ";
+my $scanning;
+my $box;
+
+my $probe = IO::Socket::INET->new(Proto => 'udp',
+                                  Broadcast => 1,
+                                  LocalAddr => $ip,
+                                  LocalPort => 5035) or die "socket: $!";
+my $sel = IO::Select->new($probe);
+my $packet = pack("vCCVNV", 0, 18, 1, 1, 0, 0);
+my $broadcast = sockaddr_in(5035, INADDR_BROADCAST);
+
+$probe->send($packet, 0, $broadcast);
+
+
+scan_again:
+print "Looking for Fritz!Box ";
+my @boxes = ();
+my $peer;
+$scanning = 100;
+print "o";
+while($scanning) {
+  my $reply;
+  my @ready;
+
+  if (@ready = $sel->can_read(0.2)) {
+    $peer = $probe->recv($reply, 16);
+    next if (length($reply) < 16);
+    my ($port, $addr) = sockaddr_in($peer);
+    my ($major, $minor1, $minor2, $code, $addr2) = unpack("vCCVV", $reply);
+    $addr2 = pack("N", $addr2);
+    if ($code == 2) {
+      print "O";
+      push @boxes, [$major, $minor1, $minor2, $addr, $addr2];
+      $scanning = 2 if ($scanning > 2);
+    }
+  } else {
+    $scanning--;
+    if (scalar @boxes == 0) {
+      $probe->send($packet, 0, $broadcast);
+      print "o";
+    } else {
+      print ".";
+    }
+  }
+}
+
+if (scalar @boxes == 0) {
+  print " none found, giving up.\n";
+  exit 1;
+} else {
+  print " found!\n";
+}
+
+{
+  package ADAM2FTP;
+  use base qw(Net::FTP);
+  # ADAM2 requires upper case commands, some brain dead firewall doesn't ;-)
+  sub _USER { shift->command("USER",@_)->response() }
+  sub _PASV { shift->command("P\@SW")->response() == Net::FTP::CMD_OK }
+  sub _GETENV {
+    my $ftp = shift;
+    my ($ok, $name, $value);
+
+    $ftp->command("GETENV",@_);
+    while(length($ok = $ftp->response()) < 1) {
+      my $line = $ftp->getline();
+      unless (defined($value)) {
+        chomp($line);
+        ($name, $value) = split(/\s+/, $line, 2);
+      }
+    }
+    $ftp->debug_print(0, "getenv: $value\n")
+      if $ftp->debug();
+    return $value;
+  }
+  sub getenv {
+    my $ftp = shift;
+    my $name = shift;
+    return $ftp->_GETENV($name);
+  }
+  sub _REBOOT { shift->command("REBOOT")->response() == Net::FTP::CMD_OK }
+  sub reboot {
+    my $ftp = shift;
+    $ftp->_REBOOT;
+    $ftp->close;
+  }
+  sub check {
+    my $ftp = shift;
+    
+    delete ${*$ftp}{'net_ftp_port'};
+    delete ${*$ftp}{'net_ftp_pasv'};
+
+    my $data = $ftp->_data_cmd('CHECK' ,@_) or return undef;
+    my $sum;
+    if (${${*$ftp}{'net_cmd_resp'}}[0] =~ /^Flash check 0x([0-9A-F]{8})/) {
+      $sum = hex($1);
+    }
+    $data->_close();
+    return $sum;
+  }
+}
+
+# passive mode geht mit Net::FTP nicht, connected zu spaet fuer ADAM2!
+my $ftp = ADAM2FTP->new($ip, Passive => 0, Debug => 0, Timeout => 600)
+  or die "can't FTP ADAM2";
+$ftp->login("adam2", "adam2") or die "can't login adam2";
+$ftp->binary();
+my $pid   = $ftp->getenv('ProductID');
+my $hwrev = $ftp->getenv('HWRevision');
+my $fwrev = $ftp->getenv('firmware_info');
+my $ulrev = $ftp->getenv('urlader-version');
+
+print "Product ID: $pid\n";
+print "Hardware Revision: $hwrev\n";
+print "Urlader  Revision: $ulrev\n";
+print "Firmware Revision: $fwrev\n";
+
+$ftp->hash(\*STDOUT, 64 * 1024);
+
+my $file = shift @ARGV;
+$file || exit 0;
+
+open FILE, "<$file" or die "can't open firmware file\n";
+
+my $mtd0 = $ftp->getenv("mtd0");
+my $mtd1 = $ftp->getenv("mtd1");
+my ($ksize, $fssize);
+
+$mtd1 =~ /^(0x\w+),(0x\w+)$/ and $ksize = hex($2) - hex($1);
+$mtd0 =~ /^(0x\w+),(0x\w+)$/ and $fssize = hex($2) - hex($1);
+$ksize and $fssize or die 'cannot read partition offsets';
+printf STDERR "Available flash space: 0x%08x (0x%08x + 0x%08x)\n", $ksize + $fssize, $ksize, $fssize;
+
+$ftp->command("MEDIA FLSH")->response();
+$ftp->binary();
+print STDERR "Writing to mtd1...\n";
+
+my $dc = $ftp->stor("fs mtd1");
+$dc or die "can't open data connection\n";
+my $rbytes = 1;
+
+while (($ksize > 0) and ($rbytes > 0)) {
+	my $buffer;
+	my $len = ($ksize > 1024 ? 1024 : $ksize);
+	$rbytes = read FILE, $buffer, $len;
+	$rbytes and $ksize -= $dc->write($buffer, $rbytes, 600);
+}
+
+$dc->close();
+$rbytes or die "no more data left to write\n";
+
+print STDERR "Writing to mtd0...\n";
+
+$dc = $ftp->stor("fs mtd0");
+$dc or die "can't open data connection\n";
+
+while (($fssize > 0) and ($rbytes > 0)) {
+	my $buffer;
+	my $len = ($fssize > 1024 ? 1024 : $fssize);
+	$rbytes = read FILE, $buffer, $len;
+	$rbytes and $fssize -= $dc->write($buffer, $rbytes, 600);
+}
+
+$dc->close();
+$ftp->reboot();
diff --git a/scripts/flashing/adam2flash.pl b/scripts/flashing/adam2flash.pl
new file mode 100755
index 0000000..8550f80
--- /dev/null
+++ b/scripts/flashing/adam2flash.pl
@@ -0,0 +1,174 @@
+#!/usr/bin/env perl
+#
+#   D-Link DSL-G6x4T flash utility
+#
+#   Copyright (C) 2005 Felix Fietkau <mailto@nbd.name>
+#   based on fbox recovery util by Enrik Berkhan
+#
+#   This program is free software; you can redistribute it and/or modify
+#   it under the terms of the GNU General Public License as published by
+#   the Free Software Foundation; either version 2 of the License, or
+#   (at your option) any later version.
+#
+#   This program is distributed in the hope that it will be useful,
+#   but WITHOUT ANY WARRANTY; without even the implied warranty of
+#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#   GNU General Public License for more details.
+#
+#   You should have received a copy of the GNU General Public License
+#   along with this program; if not, write to the Free Software
+#   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+#
+
+use IO::Socket::INET;
+use Socket;
+use strict;
+use warnings;
+
+sub usage() {
+	print STDERR "Usage: $0 <ip> [firmware.bin]\n\n";
+	exit 0;
+}
+
+my $ip = shift @ARGV;
+$ip and $ip =~ /\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/ or usage();
+
+my $probe = IO::Socket::INET->new(Proto => 'udp',
+                                  Broadcast => 1,
+                                  LocalPort => 5035) or die "socket: $!";
+my $setip = unpack("N", inet_aton($ip));
+$setip > 0 or usage();
+
+my @packets;
+foreach my $ver ([18, 1], [22, 2]) {
+	push @packets, pack("vCCVNV", 0, @$ver, 1, $setip, 0);
+}
+print STDERR "Looking for device: ";
+my $broadcast = sockaddr_in(5035, INADDR_BROADCAST);
+my $scanning;
+my $box;
+
+$SIG{"ALRM"} = sub {
+	return if --$scanning <= 0;
+	foreach my $packet (@packets) {
+		$probe->send($packet, 0, $broadcast);
+	}
+	print STDERR ".";
+};
+
+$scanning = 10;
+foreach my $packet (@packets) {
+	$probe->send($packet, 0, $broadcast);
+}
+print STDERR ".";
+
+while($scanning) {
+	my $reply;
+
+	alarm(1);
+	if (my $peer = $probe->recv($reply, 16)) {
+		next if (length($reply) < 16);
+		my ($port, $addr) = sockaddr_in($peer);
+		my ($major, $minor1, $minor2, $code, $addr2) = unpack("vCCVV", $reply);
+		$addr2 = pack("N", $addr2);
+		if ($code == 2) {
+			$scanning = 0;
+			printf STDERR " found!\nADAM2 version $major.$minor1.$minor2 at %s (%s)\n", inet_ntoa($addr), inet_ntoa($addr2);
+			$box = inet_ntoa($addr);
+		}
+	}
+}
+
+$box or die " not found!\n";
+
+{
+	package ADAM2FTP;
+	use base qw(Net::FTP);
+	
+	# ADAM2 requires upper case commands, some brain dead firewall doesn't ;-)
+	sub _USER {
+		shift->command("USER",@_)->response()
+	}
+	
+	sub _GETENV {
+		my $ftp = shift;
+		my ($ok, $name, $value);
+		
+		$ftp->command("GETENV",@_);
+			while(length($ok = $ftp->response()) < 1) {
+			my $line = $ftp->getline();
+			unless (defined($value)) {
+				chomp($line);
+				($name, $value) = split(/\s+/, $line, 2);
+			}
+		}
+		$ftp->debug_print(0, "getenv: $value\n")
+		if $ftp->debug();
+		return $value;
+	}
+	
+	sub getenv {
+		my $ftp = shift;
+		my $name = shift;
+		return $ftp->_GETENV($name);
+	}
+	
+	sub _REBOOT {
+		shift->command("REBOOT")->response() == Net::FTP::CMD_OK
+	}
+	
+	sub reboot {
+		my $ftp = shift;
+		$ftp->_REBOOT;
+		$ftp->close;
+	}
+}
+
+my $file = shift @ARGV;
+$file || exit 0;
+
+open FILE, "<$file" or die "can't open firmware file\n";
+my $ftp = ADAM2FTP->new($box, Debug => 0, Timeout => 600) or die "can't open control connection\n";
+$ftp->login("adam2", "adam2") or die "can't login\n";
+
+my $mtd0 = $ftp->getenv("mtd0");
+my $mtd1 = $ftp->getenv("mtd1");
+my ($ksize, $fssize);
+
+$mtd1 =~ /^(0x\w+),(0x\w+)$/ and $ksize = hex($2) - hex($1);
+$mtd0 =~ /^(0x\w+),(0x\w+)$/ and $fssize = hex($2) - hex($1);
+$ksize and $fssize or die 'cannot read partition offsets';
+printf STDERR "Available flash space: 0x%08x (0x%08x + 0x%08x)\n", $ksize + $fssize, $ksize, $fssize;
+
+$ftp->command("MEDIA FLSH")->response();
+$ftp->binary();
+print STDERR "Writing to mtd1...\n";
+
+my $dc = $ftp->stor("fs mtd1");
+$dc or die "can't open data connection\n";
+my $rbytes = 1;
+
+while (($ksize > 0) and ($rbytes > 0)) {
+	my $buffer;
+	my $len = ($ksize > 1024 ? 1024 : $ksize);
+	$rbytes = read FILE, $buffer, $len;
+	$rbytes and $ksize -= $dc->write($buffer, $rbytes, 600);
+}
+
+$dc->close();
+$rbytes or die "no more data left to write\n";
+
+print STDERR "Writing to mtd0...\n";
+
+$dc = $ftp->stor("fs mtd0");
+$dc or die "can't open data connection\n";
+
+while (($fssize > 0) and ($rbytes > 0)) {
+	my $buffer;
+	my $len = ($fssize > 1024 ? 1024 : $fssize);
+	$rbytes = read FILE, $buffer, $len;
+	$rbytes and $fssize -= $dc->write($buffer, $rbytes, 600);
+}
+
+$dc->close();
+$ftp->reboot();
diff --git a/scripts/flashing/adsl2mue_flash.pl b/scripts/flashing/adsl2mue_flash.pl
new file mode 100755
index 0000000..0162eaf
--- /dev/null
+++ b/scripts/flashing/adsl2mue_flash.pl
@@ -0,0 +1,170 @@
+#!/usr/bin/env perl
+#
+#   Linksys ADSL2MUE Flash utility.
+#
+#   Copyright (C) 2008 Alexandre Lissy <alexandrelissy@free.fr>
+#   based on D-Link DSL-G6x4T flash utility by Felix Fietkau <mailto@nbd.name>
+#   based on fbox recovery util by Enrik Berkhan
+#
+#   This program is free software; you can redistribute it and/or modify
+#   it under the terms of the GNU General Public License as published by
+#   the Free Software Foundation; either version 2 of the License, or
+#   (at your option) any later version.
+#
+#   This program is distributed in the hope that it will be useful,
+#   but WITHOUT ANY WARRANTY; without even the implied warranty of
+#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#   GNU General Public License for more details.
+#
+#   You should have received a copy of the GNU General Public License
+#   along with this program; if not, write to the Free Software
+#   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+#
+
+use IO::Socket::INET;
+use Socket;
+use strict;
+use warnings;
+
+sub usage() {
+	print STDERR "Usage: $0 <ip> [firmware.bin] [partition]\n\n";
+	exit 0;
+}
+
+my $ip = shift @ARGV;
+$ip and $ip =~ /\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/ or usage();
+
+my $probe = IO::Socket::INET->new(Proto => 'udp',
+                                  Broadcast => 1,
+                                  LocalPort => 5035) or die "socket: $!";
+my $setip = unpack("N", inet_aton($ip));
+$setip > 0 or usage();
+
+my @packets;
+foreach my $ver ([18, 1], [22, 2]) {
+	push @packets, pack("vCCVNV", 0, @$ver, 1, $setip, 0);
+}
+print STDERR "Looking for device: ";
+my $broadcast = sockaddr_in(5035, INADDR_BROADCAST);
+my $scanning;
+my $box;
+
+$SIG{"ALRM"} = sub {
+	return if --$scanning <= 0;
+	foreach my $packet (@packets) {
+		$probe->send($packet, 0, $broadcast);
+	}
+	print STDERR ".";
+};
+
+$scanning = 10;
+foreach my $packet (@packets) {
+	$probe->send($packet, 0, $broadcast);
+}
+print STDERR ".";
+
+while($scanning) {
+	my $reply;
+
+	alarm(1);
+	if (my $peer = $probe->recv($reply, 16)) {
+		next if (length($reply) < 16);
+		my ($port, $addr) = sockaddr_in($peer);
+		my ($major, $minor1, $minor2, $code, $addr2) = unpack("vCCVN", $reply);
+		$addr2 = pack("N", $addr2);
+		if ($code == 1) {
+			$scanning = 0;
+			printf STDERR " found!\nADAM2 version $major.$minor1.$minor2 at %s (%s)\n", inet_ntoa($addr2), inet_ntoa($addr);
+			$box = inet_ntoa($addr2);
+		}
+	}
+}
+
+$box or die " not found!\n";
+
+{
+	package ADAM2FTP;
+	use base qw(Net::FTP);
+	
+	# ADAM2 requires upper case commands, some brain dead firewall doesn't ;-)
+	sub _USER {
+		shift->command("USER",@_)->response()
+	}
+	
+	sub _GETENV {
+		my $ftp = shift;
+		my ($ok, $name, $value);
+		
+		$ftp->command("GETENV",@_);
+			while(length($ok = $ftp->response()) < 1) {
+			my $line = $ftp->getline();
+			unless (defined($value)) {
+				chomp($line);
+				($name, $value) = split(/\s+/, $line, 2);
+			}
+		}
+		$ftp->debug_print(0, "getenv: $value\n")
+		if $ftp->debug();
+		return $value;
+	}
+	
+	sub getenv {
+		my $ftp = shift;
+		my $name = shift;
+		return $ftp->_GETENV($name);
+	}
+	
+	sub _REBOOT {
+		shift->command("REBOOT")->response() == Net::FTP::CMD_OK
+	}
+	
+	sub reboot {
+		my $ftp = shift;
+		$ftp->_REBOOT;
+		$ftp->close;
+	}
+}
+
+my $file = shift @ARGV;
+my $part = shift @ARGV;
+$file || exit 0;
+$part || exit 0;
+
+open FILE, "<$file" or die "can't open firmware file\n";
+my $ftp = ADAM2FTP->new($box, Debug => 0, Timeout => 600) or die "can't open control connection\n";
+$ftp->login("adam2", "adam2") or die "can't login\n";
+
+# my $mtd0 = $ftp->getenv("mtd0");
+# my $mtd1 = $ftp->getenv("mtd1");
+my $mtd4 = $ftp->getenv($part);
+# my ($ksize, $fssize);
+my ($ossize, $mtd_start, $mtd_end);
+
+# $mtd1 =~ /^(0x\w+),(0x\w+)$/ and $ksize = hex($2) - hex($1);
+# $mtd0 =~ /^(0x\w+),(0x\w+)$/ and $fssize = hex($2) - hex($1);
+$mtd4 =~ /^(0x\w+),(0x\w+)$/;
+$ossize = hex($2) - hex($1);
+$mtd_start = hex($1);
+$mtd_end = hex($2);
+$ossize and $mtd_start and $mtd_end or die 'cannot read partition offsets';
+printf STDERR "Available flash space: 0x%08x ($part: 0x%08x to 0x%08x)\n", $ossize, $mtd_start, $mtd_end;
+
+$ftp->command("MEDIA FLSH")->response();
+$ftp->binary();
+
+print STDERR "Writing to $part ...\n";
+my $dc = $ftp->stor("data $part");
+$dc or die "can't open data connection\n";
+my $rbytes = 1;
+
+while (($ossize > 0) and ($rbytes > 0)) {
+	my $buffer;
+	my $len = ($ossize > 1024 ? 1024 : $ossize);
+	$rbytes = read FILE, $buffer, $len;
+	printf STDERR ".";
+	$rbytes and $ossize -= $dc->write($buffer, $rbytes, 600);
+}
+
+printf STDERR "\nDone.\n";
+
+$dc->close();
diff --git a/scripts/flashing/eva_ramboot.py b/scripts/flashing/eva_ramboot.py
new file mode 100755
index 0000000..365fcee
--- /dev/null
+++ b/scripts/flashing/eva_ramboot.py
@@ -0,0 +1,44 @@
+#!/usr/bin/env python3
+
+import argparse
+
+from ftplib import FTP
+from os import stat
+
+parser = argparse.ArgumentParser(description='Tool to boot AVM EVA ramdisk images.')
+parser.add_argument('ip', type=str, help='IP-address to transfer the image to')
+parser.add_argument('image', type=str, help='Location of the ramdisk image')
+parser.add_argument('--offset', type=lambda x: int(x,0), help='Offset to load the image to in hex format with leading 0x. Only needed for non-lantiq devices.')
+args = parser.parse_args()
+
+size = stat(args.image).st_size
+# arbitrary size limit, to prevent the address calculations from overflows etc.
+assert size < 0x2000000
+
+if args.offset:
+	addr = size
+	haddr = args.offset
+else:
+	# We need to align the address.
+	# A page boundary seems to be sufficient on 7362sl and 7412
+	addr = ((0x8000000 - size) & ~0xfff)
+	haddr = 0x80000000 + addr
+
+img = open(args.image, "rb")
+ftp = FTP(args.ip, 'adam2', 'adam2')
+
+def adam(cmd):
+	print("> %s"%(cmd))
+	resp = ftp.sendcmd(cmd)
+	print("< %s"%(resp))
+	assert resp[0:3] == "200"
+
+ftp.set_pasv(True)
+# The following parameters allow booting the avm recovery system with this
+# script.
+adam('SETENV memsize 0x%08x'%(addr))
+adam('SETENV kernel_args_tmp mtdram1=0x%08x,0x88000000'%(haddr))
+adam('MEDIA SDRAM')
+ftp.storbinary('STOR 0x%08x 0x88000000'%(haddr), img)
+img.close()
+ftp.close()
diff --git a/scripts/flashing/flash.sh b/scripts/flashing/flash.sh
new file mode 100755
index 0000000..d568ed5
--- /dev/null
+++ b/scripts/flashing/flash.sh
@@ -0,0 +1,66 @@
+#!/bin/bash
+#
+# tftp flash script for wireless routers
+#
+# Copyright (C) 2004 by Oleg I. Vdovikin <oleg@cs.msu.su>
+# Copyright (C) 2005 by Waldemar Brodkorb <wbx@openwrt.org>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+#
+
+if [ -z "$1" ] || [ ! -f "$1" ] || [ -z "$2" ]; then
+    echo Usage: "$0" firmware vendor
+cat << EOF
+IMPORTANT:
+Notes for Linksys / Asus WL500gx router:
+   be sure you have set boot_wait to yes. Power on your router
+   after executing this script.
+
+Notes for Asus WL500g router:
+   be sure POWER led is flashing (If this is not the case
+   poweroff the device, push the reset button & power on
+   it again, then release button)
+
+1) connect your pc to the LAN port
+2) be sure your link is up and has an address in the
+   192.168.1.0/24 address range (and not the 192.168.1.1)
+
+Notes for Toshiba router:
+   boot_wait is enabled by default on these units.
+
+1) connect your pc to any of the four LAN ports
+2) be sure your link is up and has an address in the
+   192.168.10.1/24 address range (and not the 192.168.10.1)
+3) run this script (unit will only accept .trx images)
+4) Turn unit power on.
+
+EOF
+    exit 0
+fi
+if [ "$2" = "asus" ]; then
+echo Confirming IP address setting...
+echo -en "get ASUSSPACELINK\x01\x01\xa8\xc0 /dev/null\nquit\n" | tftp 192.168.1.1
+echo Flashing 192.168.1.1 using "$1"...
+echo -en "binary\nput $1 ASUSSPACELINK\nquit\n" | tftp 192.168.1.1
+echo Please wait until leds stops flashing.
+elif [ "$2" = "linksys" ]; then
+echo Flashing 192.168.1.1 using "$1"...
+echo -en "rexmt 1\ntrace\nbinary\nput $1\nquit\n" | tftp 192.168.1.1
+echo Please wait until power led stops flashing. Do not poweroff! Then you can login via telnet 192.168.1.1.
+elif [ "$2" = "toshiba" ]; then
+echo Flashing 192.168.10.1 using "$1"...
+echo -en "rexmt 1\ntrace\nbinary\nput $1\nquit\n" | tftp 192.168.10.1
+echo Unit will automatically reboot within 5 minutes.  Do not power off.  Then you can login via telnet 192.168.10.1.
+fi
diff --git a/scripts/flashing/jungo-image.py b/scripts/flashing/jungo-image.py
new file mode 100755
index 0000000..fdd4562
--- /dev/null
+++ b/scripts/flashing/jungo-image.py
@@ -0,0 +1,283 @@
+#!/usr/bin/env python3
+#
+# Copyright 2008, 2009 (C) Jose Vasconcellos <jvasco@verizon.net>
+#
+# A script that can communicate with jungo-based routers
+# (such as MI424-WR, USR8200 and WRV54G) to backup the installed
+# firmware and replace the boot loader.
+#
+# Tested with Python 2.5 on Linux and Windows
+#
+"""Usage: %s [options] <IP_address> [image.bin | url]
+Valid options:
+\t-h | --help: usage statement
+\t-d | --dump: create a flash dump
+\t-f | --file: use <filename> to store dump contents
+\t-u | --user: provide username (default admin)
+\t-p | --pass: provide password (default password1)
+\t     --port: set port for http (default 8080)
+\t-q | --quiet: don't display unnecessary information
+\t-r | --reboot: reboot target on successful transfer
+\t-V | --version: display version information
+
+If no image (or url) is given, a flash dump is created.
+A built-in http server is used when an image file is provided.
+"""
+
+import os
+import sys
+import getopt
+import getpass
+import telnetlib
+import string
+import binascii
+import socket
+import _thread
+import socketserver
+import http.server
+
+reboot = 0
+HOST = "192.168.1.1"
+PORT = 8080
+user = "admin"
+#password = getpass.getpass()
+password = "password1"
+proto = "http"
+url = ""
+imagefile = ""
+dumpfile = ""
+verbose = 1
+do_dump = 0
+dumplen = 0x10000
+flashsize=4*1024*1024
+#device="br0"
+device="ixp0"
+
+####################
+
+def start_server(server):
+    httpd = socketserver.TCPServer((server,PORT),http.server.SimpleHTTPRequestHandler)
+    _thread.start_new_thread(httpd.serve_forever,())
+
+####################
+
+def get_flash_size():
+    # make sure we don't have an A0 stepping
+    tn.write("cat /proc/cpuinfo\n")
+    buf = tn.read_until("Returned 0", 3)
+    if not buf:
+        print("Unable to obtain CPU information; make sure to not use A0 stepping!")
+    elif buf.find('rev 0') > 0:
+        print("Warning: IXP42x stepping A0 detected!")
+        if imagefile or url:
+            print("Error: No linux support for A0 stepping!")
+            sys.exit(2)
+
+    # now get flash size
+    tn.write("cat /proc/mtd\n")
+    buf = tn.read_until("Returned 0", 3)
+    if buf:
+        i = buf.find('mtd0:')
+        if i > 0:
+            return int(buf[i+6:].split()[0],16)
+        # use different command
+        tn.write("flash_layout\n")
+        buf = tn.read_until("Returned 0", 3)
+        i = buf.rfind('Range ')
+        if i > 0:
+            return int(buf[i+17:].split()[0],16)
+        print("Can't determine flash size!")
+    else:
+        print("Unable to obtain flash size!")
+    sys.exit(2)
+
+def image_dump(tn, dumpfile):
+    if not dumpfile:
+        tn.write("ver\n");
+        buf = tn.read_until("Returned 0",2)
+        i = buf.find("Platform:")
+        if i < 0:
+	    platform="jungo"
+	else:
+	    line=buf[i+9:]
+	    i=line.find('\n')
+	    platform=line[:i].split()[-1]
+
+        tn.write("rg_conf_print /dev/%s/mac\n" % device);
+        buf = tn.read_until("Returned 0",3)
+
+	i = buf.find("mac(")
+	if i > 0:
+	    i += 4
+	else:
+	    print("No MAC address found! (use -f option)")
+	    sys.exit(1)
+        dumpfile = "%s-%s.bin" % (platform, buf[i:i+17].replace(':',''))
+    else:
+        tn.write("\n")
+
+    print("Dumping flash contents (%dMB) to %s" % (flashsize/1048576, dumpfile))
+    f = open(dumpfile, "wb")
+
+    t=flashsize/dumplen
+    for addr in range(t):
+	if verbose:
+	    sys.stdout.write('\r%d%%'%(100*addr/t))
+	    sys.stdout.flush()
+
+        tn.write("flash_dump -r 0x%x -l %d -4\n" % (addr*dumplen, dumplen))
+	tn.read_until("\n")
+
+	count = addr*dumplen
+        while 1:
+            buf = tn.read_until("\n")
+            if buf.strip() == "Returned 0":
+                break
+            s = buf.split()
+            if s and s[0][-1] == ':':
+		a=int(s[0][:-1],16)
+		if a != count:
+		    print("Format error: %x != %x"%(a,count))
+		    sys.exit(2)
+	    	count += 16
+		f.write(binascii.a2b_hex(string.join(s[1:],'')))
+	tn.read_until(">",1)
+
+    f.close()
+    if verbose:
+	print("")
+
+def telnet_option(sock,cmd,option):
+    #print "Option: %d %d" % (ord(cmd), ord(option))
+    if cmd == telnetlib.DO:
+        c=telnetlib.WILL
+    elif cmd == telnetlib.WILL:
+        c=telnetlib.DO
+    sock.sendall(telnetlib.IAC + c + option)
+
+def telnet_timeout():
+    print("Fatal error: telnet timeout!")
+    sys.exit(1)
+
+def usage():
+    print(__doc__ % os.path.basename(sys.argv[0]))
+
+####################
+
+try:
+    opts, args = getopt.getopt(sys.argv[1:], "hdf:qp:P:rvV", \
+	["help", "dump", "file=", "user=", "pass=", "port=",
+	 "quiet=", "reboot", "verbose", "version"])
+except getopt.GetoptError:
+    # print help information and exit:
+    usage()
+    sys.exit(1)
+
+for o, a in opts:
+    if o in ("-h", "--help"):
+	usage()
+	sys.exit(1)
+    elif o in ("-V", "--version"):
+	print("%s: 0.11" % sys.argv[0])
+	sys.exit(1)
+    elif o in ("-d", "--no-dump"):
+	do_dump = 1
+    elif o in ("-f", "--file"):
+	dumpfile = a
+    elif o in ("-u", "--user"):
+	user = a
+    elif o in ("-p", "--pass"):
+	password = a
+    elif o == "--port":
+	PORT = int(a)
+    elif o in ("-q", "--quiet"):
+	verbose = 0
+    elif o in ("-r", "--reboot"):
+	reboot = 1
+    elif o in ("-v", "--verbose"):
+	verbose = 1
+
+# make sure we have enough arguments
+if len(args) > 0:
+    HOST = args[0]
+
+if len(args) == 2:
+    if args[1].split(':')[0] in ("tftp", "http", "ftp"):
+        url = args[1]
+    else:
+        imagefile = args[1]
+else:
+    do_dump = 1;
+
+####################
+# create a telnet session to the router
+try:
+    tn = telnetlib.Telnet(HOST)
+except socket.error as msg:
+    print("Unable to establish telnet session to %s: %s" % (HOST, msg))
+    sys.exit(1)
+
+tn.set_option_negotiation_callback(telnet_option)
+
+buf = tn.read_until("Username: ", 3)
+if not buf:
+    telnet_timeout()
+tn.write(user+"\n")
+if password:
+    buf = tn.read_until("Password: ", 3)
+    if not buf:
+        telnet_timeout()
+    tn.write(password+"\n")
+
+# wait for prompt
+buf = tn.read_until("> ", 3)
+if not buf:
+    telnet_timeout()
+
+flashsize = get_flash_size()
+
+if do_dump:
+    image_dump(tn, dumpfile)
+
+if imagefile or url:
+    splitpath = os.path.split(imagefile)
+
+    # create load command
+    if url:
+        cmd = "load -u %s -r 0\n" % (url)
+    else:
+        server = tn.get_socket().getsockname()[0]
+        cmd = "load -u http://%s:%d/%s -r 0\n" % (server, PORT, splitpath[1])
+
+        if not os.access(imagefile, os.R_OK):
+            print("File access error: %s" % (imagefile))
+            sys.exit(3)
+
+        # make sure we're in the directory where the image is located
+        if splitpath[0]:
+            os.chdir(splitpath[0])
+
+        start_server(server)
+
+    if verbose:
+	print("Unlocking flash...")
+    tn.write("unlock 0 0x%x\n" % flashsize)
+    buf = tn.read_until("Returned 0",5)
+
+    if verbose:
+	print("Writing new image...")
+    print(cmd, end=' ')
+    tn.write(cmd)
+    buf = tn.read_until("Returned 0",10)
+
+    # wait till the transfer completed
+    buf = tn.read_until("Download completed successfully",20)
+    if buf:
+	print("Flash update complete!")
+        if reboot:
+            tn.write("reboot\n")
+            print("Rebooting...")
+
+tn.write("exit\n")
+tn.close()
+
diff --git a/scripts/functions.sh b/scripts/functions.sh
new file mode 100644
index 0000000..ec0618c
--- /dev/null
+++ b/scripts/functions.sh
@@ -0,0 +1,48 @@
+#!/bin/sh
+
+
+get_magic_word() {
+	dd if=$1 bs=4 count=1 2>/dev/null | od -A n -N 4 -t x1 | tr -d ' '
+}
+
+get_post_padding_word() {
+	local rootfs_length="$(stat -c%s "$1")"
+	[ "$rootfs_length" -ge 4 ] || return
+	rootfs_length=$((rootfs_length-4))
+
+	# the JFFS2 end marker must be on a 4K boundary (often 64K or 256K)
+	local unaligned_bytes=$((rootfs_length%4096))
+	[ "$unaligned_bytes" = 0 ] || return
+
+	# skip rootfs data except the potential EOF marker
+	dd if="$1" bs=1 skip="$rootfs_length" 2>/dev/null | od -A n -N 4 -t x1 | tr -d ' '
+}
+
+get_fs_type() {
+	local magic_word="$(get_magic_word "$1")"
+
+	case "$magic_word" in
+	"3118"*)
+		echo "ubifs"
+		;;
+	"68737173")
+		local post_padding_word="$(get_post_padding_word "$1")"
+
+		case "$post_padding_word" in
+		"deadc0de")
+			echo "squashfs-jffs2"
+			;;
+		*)
+			echo "squashfs"
+			;;
+		esac
+		;;
+	*)
+		echo "unknown"
+		;;
+	esac
+}
+
+round_up() {
+	echo "$(((($1 + ($2 - 1))/ $2) * $2))"
+}
diff --git a/scripts/gen-dependencies.sh b/scripts/gen-dependencies.sh
new file mode 100755
index 0000000..4beff74
--- /dev/null
+++ b/scripts/gen-dependencies.sh
@@ -0,0 +1,33 @@
+#!/bin/sh
+#
+# Copyright (C) 2012 OpenWrt.org
+#
+# This is free software, licensed under the GNU General Public License v2.
+# See /LICENSE for more information.
+#
+SELF=${0##*/}
+
+READELF="${READELF:-readelf}"
+OBJCOPY="${OBJCOPY:-objcopy}"
+TARGETS=$*
+XARGS="${XARGS:-xargs -r}"
+
+[ -z "$TARGETS" ] && {
+  echo "$SELF: no directories / files specified"
+  echo "usage: $SELF [PATH...]"
+  exit 1
+}
+
+find $TARGETS -type f -a -exec file {} \; | \
+  sed -n -e 's/^\(.*\):.*ELF.*\(executable\|shared object\).*,.*/\1/p' | \
+  $XARGS -n1 $READELF -d | \
+  awk '$2 ~ /NEEDED/ && $NF !~ /interpreter/ && $NF ~ /^\[?lib.*\.so/ { gsub(/[\[\]]/, "", $NF); print $NF }' | \
+  sort -u
+
+tmp=$(mktemp $TMP_DIR/dep.XXXXXXXX)
+for kmod in $(find $TARGETS -type f -name \*.ko); do
+	$OBJCOPY -O binary -j .modinfo $kmod $tmp
+	sed -e 's,\x00,\n,g' $tmp | \
+		sed -ne '/^depends=.\+/ { s/^depends=//; s/,/.ko\n/g; s/$/.ko/p; q }'
+done | sort -u
+rm -f $tmp
diff --git a/scripts/gen_image_generic.sh b/scripts/gen_image_generic.sh
new file mode 100755
index 0000000..11e40f3
--- /dev/null
+++ b/scripts/gen_image_generic.sh
@@ -0,0 +1,60 @@
+#!/bin/sh
+# Copyright (C) 2006-2012 OpenWrt.org
+set -e -x
+if [ $# -ne 5 ] && [ $# -ne 6 ]; then
+    echo "SYNTAX: $0 <file> <kernel size> <kernel directory> <rootfs size> <rootfs image> [<align>]"
+    exit 1
+fi
+
+OUTPUT="$1"
+KERNELSIZE="$2"
+KERNELDIR="$3"
+KERNELPARTTYPE=${KERNELPARTTYPE:-83}
+ROOTFSSIZE="$4"
+ROOTFSIMAGE="$5"
+ROOTFSPARTTYPE=${ROOTFSPARTTYPE:-83}
+ALIGN="$6"
+
+rm -f "$OUTPUT"
+
+head=16
+sect=63
+
+# create partition table
+set $(ptgen -o "$OUTPUT" -h $head -s $sect ${GUID:+-g} -t "${KERNELPARTTYPE}" -p "${KERNELSIZE}m${PARTOFFSET:+@$PARTOFFSET}" -t "${ROOTFSPARTTYPE}" -p "${ROOTFSSIZE}m" ${ALIGN:+-l $ALIGN} ${SIGNATURE:+-S 0x$SIGNATURE} ${GUID:+-G $GUID})
+
+KERNELOFFSET="$(($1 / 512))"
+KERNELSIZE="$2"
+ROOTFSOFFSET="$(($3 / 512))"
+ROOTFSSIZE="$(($4 / 512))"
+
+# Using mcopy -s ... is using READDIR(3) to iterate through the directory
+# entries, hence they end up in the FAT filesystem in traversal order which
+# breaks reproducibility.
+# Implement recursive copy with reproducible order.
+dos_dircopy() {
+  local entry
+  local baseentry
+  for entry in "$1"/* ; do
+    if [ -f "$entry" ]; then
+      mcopy -i "$OUTPUT.kernel" "$entry" ::"$2"
+    elif [ -d "$entry" ]; then
+      baseentry="$(basename "$entry")"
+      mmd -i "$OUTPUT.kernel" ::"$2""$baseentry"
+      dos_dircopy "$entry" "$2""$baseentry"/
+    fi
+  done
+}
+
+[ -n "$PADDING" ] && dd if=/dev/zero of="$OUTPUT" bs=512 seek="$ROOTFSOFFSET" conv=notrunc count="$ROOTFSSIZE"
+dd if="$ROOTFSIMAGE" of="$OUTPUT" bs=512 seek="$ROOTFSOFFSET" conv=notrunc
+
+if [ -n "$GUID" ]; then
+    [ -n "$PADDING" ] && dd if=/dev/zero of="$OUTPUT" bs=512 seek="$((ROOTFSOFFSET + ROOTFSSIZE))" conv=notrunc count="$sect"
+    mkfs.fat --invariant -n kernel -C "$OUTPUT.kernel" -S 512 "$((KERNELSIZE / 1024))"
+    LC_ALL=C dos_dircopy "$KERNELDIR" /
+else
+    make_ext4fs -J -L kernel -l "$KERNELSIZE" ${SOURCE_DATE_EPOCH:+-T ${SOURCE_DATE_EPOCH}} "$OUTPUT.kernel" "$KERNELDIR"
+fi
+dd if="$OUTPUT.kernel" of="$OUTPUT" bs=512 seek="$KERNELOFFSET" conv=notrunc
+rm -f "$OUTPUT.kernel"
diff --git a/scripts/get_source_date_epoch.sh b/scripts/get_source_date_epoch.sh
new file mode 100755
index 0000000..727cb03
--- /dev/null
+++ b/scripts/get_source_date_epoch.sh
@@ -0,0 +1,35 @@
+#!/usr/bin/env bash
+export LANG=C
+export LC_ALL=C
+
+if [ -n "$TOPDIR" ]; then
+	cd "$TOPDIR" || exit 1
+fi
+
+SOURCE="${1:-.}"
+
+try_version() {
+	[ -f "$SOURCE/version.date" ] || return 1
+	SOURCE_DATE_EPOCH=$(cat "$SOURCE/version.date")
+	[ -n "$SOURCE_DATE_EPOCH" ]
+}
+
+try_git() {
+	SOURCE_DATE_EPOCH=$(git -C "$SOURCE" log -1 --no-show-signature \
+		--format=format:%ct "$SOURCE" 2>/dev/null)
+	[ -n "$SOURCE_DATE_EPOCH" ]
+}
+
+try_hg() {
+	SOURCE_DATE_EPOCH=$(hg --cwd "$SOURCE" log --template '{date}' -l 1 \
+		"$SOURCE" 2>/dev/null | cut -d. -f1)
+	[ -n "$SOURCE_DATE_EPOCH" ]
+}
+
+try_mtime() {
+	SOURCE_DATE_EPOCH=$(perl -e 'print((stat $ARGV[0])[9])' "$0")
+	[ -n "$SOURCE_DATE_EPOCH" ]
+}
+
+try_version || try_git || try_hg || try_mtime || SOURCE_DATE_EPOCH=""
+echo "$SOURCE_DATE_EPOCH"
diff --git a/scripts/gettools.sh b/scripts/gettools.sh
new file mode 100755
index 0000000..211b4eb
--- /dev/null
+++ b/scripts/gettools.sh
@@ -0,0 +1,17 @@
+#!/usr/bin/env bash
+export LANG=C
+export LC_ALL=C
+BITNUM=$(getconf LONG_BIT)
+#TOPDIR=/home/lianghu/openwrt
+#TOOLDIR="$TOPDIR/owtoolchain/linux$BITNUM"
+#Note: the TOPDIR variable can't be transferred into the sript by test!
+#      However, this script will be run only in the TOPDIR in toplevel.mk
+#      So we get some lazy method here like below
+TOOLDIR="owtoolchain/linux$BITNUM"
+HOSTDIR="host/linux$BITNUM"
+if [ -d $TOOLDIR ] && [ -d $HOSTDIR ]; then
+	NONEXIST=0
+else
+	NONEXIST=1
+fi
+echo "$NONEXIST"
diff --git a/scripts/getver.sh b/scripts/getver.sh
new file mode 100755
index 0000000..0659d80
--- /dev/null
+++ b/scripts/getver.sh
@@ -0,0 +1,59 @@
+#!/usr/bin/env bash
+export LANG=C
+export LC_ALL=C
+[ -n "$TOPDIR" ] && cd $TOPDIR
+
+GET_REV=$1
+
+try_version() {
+	[ -f version ] || return 1
+	REV="$(cat version)"
+	[ -n "$REV" ]
+}
+
+try_git() {
+	REBOOT=ee53a240ac902dc83209008a2671e7fdcf55957a
+	git rev-parse --git-dir >/dev/null 2>&1 || return 1
+
+	[ -n "$GET_REV" ] || GET_REV="HEAD"
+
+	case "$GET_REV" in
+	r*)
+		GET_REV="$(echo $GET_REV | tr -d 'r')"
+		BASE_REV="$(git rev-list ${REBOOT}..HEAD 2>/dev/null | wc -l | awk '{print $1}')"
+		[ $((BASE_REV - GET_REV)) -ge 0 ] && REV="$(git rev-parse HEAD~$((BASE_REV - GET_REV)))"
+		;;
+	*)
+		BRANCH="$(git rev-parse --abbrev-ref HEAD)"
+		ORIGIN="$(git rev-parse --verify --symbolic-full-name ${BRANCH}@{u} 2>/dev/null)"
+		[ -n "$ORIGIN" ] || ORIGIN="$(git rev-parse --verify --symbolic-full-name main@{u} 2>/dev/null)"
+		REV="$(git rev-list ${REBOOT}..$GET_REV 2>/dev/null | wc -l | awk '{print $1}')"
+
+		if [ -n "$ORIGIN" ]; then
+			UPSTREAM_BASE="$(git merge-base $GET_REV $ORIGIN)"
+			UPSTREAM_REV="$(git rev-list ${REBOOT}..$UPSTREAM_BASE 2>/dev/null | wc -l | awk '{print $1}')"
+		else
+			UPSTREAM_REV=0
+		fi
+
+		if [ "$REV" -gt "$UPSTREAM_REV" ]; then
+			REV="${UPSTREAM_REV}+$((REV - UPSTREAM_REV))"
+		fi
+
+		REV="${REV:+r$REV-$(git log -n 1 --format="%h" $UPSTREAM_BASE)}"
+
+		;;
+	esac
+
+	[ -n "$REV" ]
+}
+
+try_hg() {
+	[ -d .hg ] || return 1
+	REV="$(hg log -r-1 --template '{desc}' | awk '{print $2}' | sed 's/\].*//')"
+	REV="${REV:+r$REV}"
+	[ -n "$REV" ]
+}
+
+try_version || try_git || try_hg || REV="unknown"
+echo "$REV"
diff --git a/scripts/ipkg-build b/scripts/ipkg-build
new file mode 100755
index 0000000..6abcc58
--- /dev/null
+++ b/scripts/ipkg-build
@@ -0,0 +1,200 @@
+#!/bin/sh
+
+# ipkg-build -- construct a .ipk from a directory
+# Carl Worth <cworth@east.isi.edu>
+# based on a script by Steve Redler IV, steve@sr-tech.com 5-21-2001
+# 2003-04-25 rea@sr.unh.edu
+#   Updated to work on Familiar Pre0.7rc1, with busybox tar.
+#   Note it Requires: binutils-ar (since the busybox ar can't create)
+#   For UID debugging it needs a better "find".
+set -e
+
+version=1.0
+FIND="$(command -v find)"
+FIND="${FIND:-$(command -v gfind)}"
+TAR="${TAR:-$(command -v tar)}"
+
+# try to use fixed source epoch
+if [ -n "$PKG_SOURCE_DATE_EPOCH" ]; then
+	TIMESTAMP=$(date --date="@$PKG_SOURCE_DATE_EPOCH")
+elif [ -n "$SOURCE_DATE_EPOCH" ]; then
+	TIMESTAMP=$(date --date="@$SOURCE_DATE_EPOCH")
+else
+	TIMESTAMP=$(date)
+fi
+
+ipkg_extract_value() {
+	sed -e "s/^[^:]*:[[:space:]]*//"
+}
+
+required_field() {
+	field=$1
+
+	grep "^$field:" < "$CONTROL/control" | ipkg_extract_value
+}
+
+pkg_appears_sane() {
+	local pkg_dir="$1"
+
+	local owd="$PWD"
+	cd "$pkg_dir"
+
+	PKG_ERROR=0
+	pkg="$(required_field Package)"
+	version="$(required_field Version | sed 's/Version://; s/^.://g;')"
+	arch="$(required_field Architecture)"
+
+	if echo "$pkg" | grep '[^a-zA-Z0-9_.+-]'; then
+		echo "*** Error: Package name $name contains illegal characters, (other than [a-z0-9.+-])" >&2
+		PKG_ERROR=1;
+	fi
+
+	if [ -f "$CONTROL/conffiles" ]; then
+		rm -f "$CONTROL/conffiles.resolved"
+
+		for cf in $($FIND $(sed -e "s!^/!$pkg_dir/!" "$CONTROL/conffiles") -type f); do
+			echo "${cf#$pkg_dir}" >> "$CONTROL/conffiles.resolved"
+		done
+
+		rm "$CONTROL"/conffiles
+		if [ -f "$CONTROL"/conffiles.resolved ]; then
+			LC_ALL=C sort -o "$CONTROL"/conffiles "$CONTROL"/conffiles.resolved
+			rm "$CONTROL"/conffiles.resolved
+			chmod 0644 "$CONTROL"/conffiles
+		fi
+	fi
+
+	cd "$owd"
+	return $PKG_ERROR
+}
+
+resolve_file_mode_id() {
+	local var=$1 type=$2 name=$3 id
+
+	case "$name" in
+		root)
+			id=0
+		;;
+		*[!0-9]*)
+			id=$(sed -ne "s#^$type $name \\([0-9]\\+\\)\\b.*\$#\\1#p" "$TOPDIR/tmp/.packageusergroup" 2>/dev/null)
+		;;
+		*)
+			id=$name
+		;;
+	esac
+
+	export "$var=$id"
+
+	[ -n "$id" ]
+}
+
+###
+# ipkg-build "main"
+###
+file_modes=""
+usage="Usage: $0 [-v] [-h] [-m] <pkg_directory> [<destination_directory>]"
+while getopts "hvm:" opt; do
+    case $opt in
+	v ) echo "$version"
+	    exit 0
+	    ;;
+	h ) 	echo "$usage"  >&2 ;;
+	m )	file_modes=$OPTARG ;;
+	\? ) 	echo "$usage"  >&2
+	esac
+done
+
+
+shift $((OPTIND - 1))
+
+# continue on to process additional arguments
+
+case $# in
+1)
+	dest_dir=$PWD
+	;;
+2)
+	dest_dir=$2
+	if [ "$dest_dir" = "." ] || [ "$dest_dir" = "./" ] ; then
+	    dest_dir=$PWD
+	fi
+	;;
+*)
+	echo "$usage" >&2
+	exit 1
+	;;
+esac
+
+pkg_dir="$(realpath "$1")"
+
+if [ ! -d "$pkg_dir" ]; then
+	echo "*** Error: Directory $pkg_dir does not exist" >&2
+	exit 1
+fi
+
+# CONTROL is second so that it takes precedence
+CONTROL=
+[ -d "$pkg_dir"/CONTROL ] && CONTROL=CONTROL
+if [ -z "$CONTROL" ]; then
+	echo "*** Error: Directory $pkg_dir has no CONTROL subdirectory." >&2
+	exit 1
+fi
+
+if ! pkg_appears_sane "$pkg_dir"; then
+	echo >&2
+	echo "ipkg-build: Please fix the above errors and try again." >&2
+	exit 1
+fi
+
+tmp_dir=$dest_dir/IPKG_BUILD.$$
+mkdir "$tmp_dir"
+
+echo $CONTROL > "$tmp_dir"/tarX
+cd "$pkg_dir"
+for file_mode in $file_modes; do
+	case $file_mode in
+	/*:*:*:*)
+	    ;;
+	*)
+	    echo "ERROR: file modes must use absolute path and contain user:group:mode"
+	    echo "$file_mode"
+	    exit 1
+	    ;;
+	esac
+
+	mode=${file_mode##*:}; path=${file_mode%:*}
+	group=${path##*:};     path=${path%:*}
+	user=${path##*:};      path=${path%:*}
+
+	if ! resolve_file_mode_id uid user "$user"; then
+		echo "ERROR: unable to resolve uid of $user" >&2
+		exit 1
+	fi
+
+	if ! resolve_file_mode_id gid group "$group"; then
+		echo "ERROR: unable to resolve gid of $group" >&2
+		exit 1
+	fi
+
+	chown "$uid:$gid" "$pkg_dir/$path"
+	chmod  "$mode" "$pkg_dir/$path"
+done
+$TAR -X "$tmp_dir"/tarX --format=gnu --numeric-owner --sort=name -cpf - --mtime="$TIMESTAMP" . | gzip -n - > "$tmp_dir"/data.tar.gz
+
+installed_size=$(zcat < "$tmp_dir"/data.tar.gz | wc -c)
+sed -i -e "s/^Installed-Size: .*/Installed-Size: $installed_size/" \
+	"$pkg_dir"/$CONTROL/control
+
+( cd "$pkg_dir"/$CONTROL && $TAR --format=gnu --numeric-owner --sort=name -cf -  --mtime="$TIMESTAMP" . | gzip -n - > "$tmp_dir"/control.tar.gz )
+rm "$tmp_dir"/tarX
+
+echo "2.0" > "$tmp_dir"/debian-binary
+
+pkg_file=$dest_dir/${pkg}_${version}_${arch}.ipk
+rm -f "$pkg_file"
+( cd "$tmp_dir" && $TAR --format=gnu --numeric-owner --sort=name -cf -  --mtime="$TIMESTAMP" ./debian-binary ./data.tar.gz ./control.tar.gz | gzip -n - > "$pkg_file" )
+
+rm "$tmp_dir"/debian-binary "$tmp_dir"/data.tar.gz "$tmp_dir"/control.tar.gz
+rmdir "$tmp_dir"
+
+echo "Packaged contents of $pkg_dir into $pkg_file"
diff --git a/scripts/ipkg-make-index.sh b/scripts/ipkg-make-index.sh
new file mode 100755
index 0000000..8965d0a
--- /dev/null
+++ b/scripts/ipkg-make-index.sh
@@ -0,0 +1,31 @@
+#!/usr/bin/env bash
+set -e
+
+pkg_dir=$1
+
+if [ -z $pkg_dir ] || [ ! -d $pkg_dir ]; then
+	echo "Usage: ipkg-make-index <package_directory>" >&2
+	exit 1
+fi
+
+empty=1
+
+for pkg in `find $pkg_dir -name '*.ipk' | sort`; do
+	empty=
+	name="${pkg##*/}"
+	name="${name%%_*}"
+	[[ "$name" = "kernel" ]] && continue
+	[[ "$name" = "libc" ]] && continue
+	echo "Generating index for package $pkg" >&2
+	file_size=$(stat -L -c%s $pkg)
+	sha256sum=$($MKHASH sha256 $pkg)
+	# Take pains to make variable value sed-safe
+	sed_safe_pkg=`echo $pkg | sed -e 's/^\.\///g' -e 's/\\//\\\\\\//g'`
+	tar -xzOf $pkg ./control.tar.gz | tar xzOf - ./control | sed -e "s/^Description:/Filename: $sed_safe_pkg\\
+Size: $file_size\\
+SHA256sum: $sha256sum\\
+Description:/"
+	echo ""
+done
+[ -n "$empty" ] && echo
+exit 0
diff --git a/scripts/ipkg-remove b/scripts/ipkg-remove
new file mode 100755
index 0000000..f495700
--- /dev/null
+++ b/scripts/ipkg-remove
@@ -0,0 +1,20 @@
+#!/usr/bin/env bash
+
+sourcename="$1"; shift
+
+for pkg in "$@"; do
+	tar -Ozxf "$pkg" ./control.tar.gz 2>/dev/null | tar -Ozxf - ./control 2>/dev/null | \
+	while read field value; do
+		if [ "$field" = "SourceName:" ] && [ "$value" = "$sourcename" ]; then
+			rm -vf "$pkg"
+			break
+		fi
+	done
+	case "$pkg" in
+		*/"${sourcename}_"*.ipk)
+			rm -vf "$pkg"
+		;;
+	esac
+done
+
+exit 0
diff --git a/scripts/json_add_image_info.py b/scripts/json_add_image_info.py
new file mode 100755
index 0000000..915e5f6
--- /dev/null
+++ b/scripts/json_add_image_info.py
@@ -0,0 +1,84 @@
+#!/usr/bin/env python3
+
+from os import getenv
+from pathlib import Path
+from sys import argv
+import hashlib
+import json
+
+if len(argv) != 2:
+    print("ERROR: JSON info script requires output arg")
+    exit(1)
+
+json_path = Path(argv[1])
+file_path = Path(getenv("FILE_DIR")) / getenv("FILE_NAME")
+
+if not file_path.is_file():
+    print("Skip JSON creation for non existing file", file_path)
+    exit(0)
+
+
+def get_titles():
+    titles = []
+    for prefix in ["", "ALT0_", "ALT1_", "ALT2_", "ALT3_", "ALT4_", "ALT5_"]:
+        title = {}
+        for var in ["vendor", "model", "variant"]:
+            if getenv("DEVICE_{}{}".format(prefix, var.upper())):
+                title[var] = getenv("DEVICE_{}{}".format(prefix, var.upper()))
+
+        if title:
+            titles.append(title)
+
+    if not titles:
+        titles.append({"title": getenv("DEVICE_TITLE")})
+
+    return titles
+
+
+device_id = getenv("DEVICE_ID")
+
+sha256_hash = hashlib.sha256()
+with open(str(file_path),"rb") as f:
+    # Read and update hash string value in blocks of 4K
+    for byte_block in iter(lambda: f.read(4096),b""):
+        sha256_hash.update(byte_block)
+
+hash_file = sha256_hash.hexdigest()
+
+if file_path.with_suffix(file_path.suffix + ".sha256sum").exists():
+    hash_unsigned = (
+        file_path.with_suffix(file_path.suffix + ".sha256sum").read_text().strip()
+    )
+else:
+    hash_unsigned = hash_file
+
+file_info = {
+    "metadata_version": 1,
+    "target": "{}/{}".format(getenv("TARGET"), getenv("SUBTARGET")),
+    "version_code": getenv("VERSION_CODE"),
+    "version_number": getenv("VERSION_NUMBER"),
+    "source_date_epoch": int(getenv("SOURCE_DATE_EPOCH")),
+    "profiles": {
+        device_id: {
+            "image_prefix": getenv("DEVICE_IMG_PREFIX"),
+            "images": [
+                {
+                    "type": getenv("FILE_TYPE"),
+                    "name": getenv("FILE_NAME"),
+                    "sha256": hash_file,
+                    "sha256_unsigned": hash_unsigned,
+                }
+            ],
+            "device_packages": getenv("DEVICE_PACKAGES").split(),
+            "supported_devices": getenv("SUPPORTED_DEVICES").split(),
+            "titles": get_titles(),
+        }
+    },
+}
+
+if getenv("FILE_FILESYSTEM"):
+    file_info["profiles"][device_id]["images"][0]["filesystem"] = getenv(
+        "FILE_FILESYSTEM"
+    )
+
+json_path.write_text(json.dumps(file_info, separators=(",", ":")))
diff --git a/scripts/json_overview_image_info.py b/scripts/json_overview_image_info.py
new file mode 100755
index 0000000..96921c2
--- /dev/null
+++ b/scripts/json_overview_image_info.py
@@ -0,0 +1,83 @@
+#!/usr/bin/env python3
+
+from os import getenv, environ
+from pathlib import Path
+from subprocess import run, PIPE
+from sys import argv
+import json
+
+if len(argv) != 2:
+    print("JSON info files script requires output file as argument")
+    exit(1)
+
+output_path = Path(argv[1])
+
+assert getenv("WORK_DIR"), "$WORK_DIR required"
+
+work_dir = Path(getenv("WORK_DIR"))
+
+output = {}
+
+
+def get_initial_output(image_info):
+    # preserve existing profiles.json
+    if output_path.is_file():
+        profiles = json.loads(output_path.read_text())
+        if profiles["version_code"] == image_info["version_code"]:
+            return profiles
+    return image_info
+
+
+for json_file in work_dir.glob("*.json"):
+    image_info = json.loads(json_file.read_text())
+
+    if not output:
+        output = get_initial_output(image_info)
+
+    # get first and only profile in json file
+    device_id, profile = next(iter(image_info["profiles"].items()))
+    if device_id not in output["profiles"]:
+        output["profiles"][device_id] = profile
+    else:
+        output["profiles"][device_id]["images"].extend(profile["images"])
+
+# make image lists unique by name, keep last/latest
+for device_id, profile in output.get("profiles", {}).items():
+    profile["images"] = list({e["name"]: e for e in profile["images"]}.values())
+
+
+if output:
+    (
+        default_packages,
+        output["arch_packages"],
+        linux_version,
+        linux_release,
+        linux_vermagic,
+    ) = run(
+        [
+            "make",
+            "--no-print-directory",
+            "-C",
+            "target/linux/",
+            "val.DEFAULT_PACKAGES",
+            "val.ARCH_PACKAGES",
+            "val.LINUX_VERSION",
+            "val.LINUX_RELEASE",
+            "val.LINUX_VERMAGIC",
+            "V=s",
+        ],
+        stdout=PIPE,
+        check=True,
+        env=environ.copy().update({"TOPDIR": Path().cwd()}),
+        universal_newlines=True,
+    ).stdout.splitlines()
+
+    output["default_packages"] = sorted(default_packages.split())
+    output["linux_kernel"] = {
+        "version": linux_version,
+        "release": linux_release,
+        "vermagic": linux_vermagic,
+    }
+    output_path.write_text(json.dumps(output, sort_keys=True, separators=(",", ":")))
+else:
+    print("JSON info file script could not find any JSON files for target")
diff --git a/scripts/kconfig-reorder.sh b/scripts/kconfig-reorder.sh
new file mode 100755
index 0000000..9ab7858
--- /dev/null
+++ b/scripts/kconfig-reorder.sh
@@ -0,0 +1,16 @@
+#!/bin/sh
+
+# This script reorders all config-* files in the target directory.
+
+find_files=$(find target -type f -name 'config-*' -print)
+
+if [ -n "$find_files" ]; then
+  for file in $find_files; do
+    echo "Reordering options in $file"
+    LANG=C ./scripts/kconfig.pl '+' "$file" /dev/null > "$file"-new
+    mv "$file"-new "$file"
+  done
+else
+  echo "No files named config-* found."
+fi
+
diff --git a/scripts/kconfig.pl b/scripts/kconfig.pl
new file mode 100755
index 0000000..392f1d5
--- /dev/null
+++ b/scripts/kconfig.pl
@@ -0,0 +1,189 @@
+#!/usr/bin/env perl
+# 
+# Copyright (C) 2006 Felix Fietkau <nbd@nbd.name>
+#
+# This is free software, licensed under the GNU General Public License v2.
+# See /LICENSE for more information.
+#
+
+use warnings;
+use strict;
+
+my @arg;
+my $PREFIX = "CONFIG_";
+
+sub set_config($$$$) {
+	my $config = shift;
+	my $idx = shift;
+	my $newval = shift;
+	my $mod_plus = shift;
+
+	if (!defined($config->{$idx}) or !$mod_plus or
+	    $config->{$idx} eq '#undef' or $newval eq 'y') {
+		$config->{$idx} = $newval;
+	}
+}
+
+sub load_config($$) {
+	my $file = shift;
+	my $mod_plus = shift;
+	my %config;
+
+	open FILE, "$file" or die "can't open file '$file'";
+	while (<FILE>) {
+		chomp;
+		/^$PREFIX(.+?)=(.+)/ and do {
+			set_config(\%config, $1, $2, $mod_plus);
+			next;
+		};
+		/^# $PREFIX(.+?) is not set/ and do {
+			set_config(\%config, $1, "#undef", $mod_plus);
+			next;
+		};
+		/^#/ and next;
+		/^(.+)$/ and warn "WARNING: can't parse line: $1\n";
+	}
+	return \%config;
+}
+
+
+sub config_and($$) {
+	my $cfg1 = shift;
+	my $cfg2 = shift;
+	my %config;
+
+	foreach my $config (keys %$cfg1) {
+		my $val1 = $cfg1->{$config};
+		my $val2 = $cfg2->{$config};
+		$val2 and ($val1 eq $val2) and do {
+			$config{$config} = $val1;
+		};
+	}
+	return \%config;
+}
+
+
+sub config_add($$$) {
+	my $cfg1 = shift;
+	my $cfg2 = shift;
+	my $mod_plus = shift;
+	my %config;
+	
+	for ($cfg1, $cfg2) {
+		my %cfg = %$_;
+		
+		foreach my $config (keys %cfg) {
+			if ($mod_plus and $config{$config}) {
+				next if $config{$config} eq "y";
+				next if $cfg{$config} eq '#undef';
+			}
+			$config{$config} = $cfg{$config};
+		}
+	}
+	return \%config;
+}
+
+sub config_diff($$$) {
+	my $cfg1 = shift;
+	my $cfg2 = shift;
+	my $new_only = shift;
+	my %config;
+	
+	foreach my $config (keys %$cfg2) {
+		if (!defined($cfg1->{$config}) or $cfg1->{$config} ne $cfg2->{$config}) {
+			next if $new_only and !defined($cfg1->{$config}) and $cfg2->{$config} eq '#undef';
+			$config{$config} = $cfg2->{$config};
+		}
+	}
+	return \%config
+}
+
+sub config_sub($$) {
+	my $cfg1 = shift;
+	my $cfg2 = shift;
+	my %config = %{$cfg1};
+	my @keys = map {
+		my $expr = $_;
+		$expr =~ /[?.*]/ ?
+			map {
+				/^$expr$/ ? $_ : ()
+			} keys %config : $expr;
+	} keys %$cfg2;
+
+	foreach my $config (@keys) {
+		delete $config{$config};
+	}
+	return \%config;
+}
+
+sub print_cfgline($$) {
+	my $name = shift;
+	my $val = shift;
+	if ($val eq '#undef' or $val eq 'n') {
+		print "# $PREFIX$name is not set\n";
+	} else {
+		print "$PREFIX$name=$val\n";
+	}
+}
+
+
+sub dump_config($) {
+	my $cfg = shift;
+	die "argument error in dump_config" unless ($cfg);
+	my %config = %$cfg;
+	foreach my $config (sort keys %config) {
+		print_cfgline($config, $config{$config});
+	}
+}
+
+sub parse_expr {
+	my $pos = shift;
+	my $mod_plus = shift;
+	my $arg = $arg[$$pos++];
+
+	die "Parse error" if (!$arg);
+
+	if ($arg eq '&') {
+		my $arg1 = parse_expr($pos);
+		my $arg2 = parse_expr($pos);
+		return config_and($arg1, $arg2);
+	} elsif ($arg =~ /^\+/) {
+		my $arg1 = parse_expr($pos);
+		my $arg2 = parse_expr($pos);
+		return config_add($arg1, $arg2, 0);
+	} elsif ($arg =~ /^m\+/) {
+		my $arg1 = parse_expr($pos);
+		my $arg2 = parse_expr($pos, 1);
+		return config_add($arg1, $arg2, 1);
+	} elsif ($arg eq '>') {
+		my $arg1 = parse_expr($pos);
+		my $arg2 = parse_expr($pos);
+		return config_diff($arg1, $arg2, 0);
+	} elsif ($arg eq '>+') {
+		my $arg1 = parse_expr($pos);
+		my $arg2 = parse_expr($pos);
+		return config_diff($arg1, $arg2, 1);
+	} elsif ($arg eq '-') {
+		my $arg1 = parse_expr($pos);
+		my $arg2 = parse_expr($pos);
+		return config_sub($arg1, $arg2);
+	} else {
+		return load_config($arg, $mod_plus);
+	}
+}
+
+while (@ARGV > 0 and $ARGV[0] =~ /^-\w+$/) {
+	my $cmd = shift @ARGV;
+	if ($cmd =~ /^-n$/) {
+		$PREFIX = "";
+	} elsif ($cmd =~ /^-p$/) {
+		$PREFIX = shift @ARGV;
+	} else {
+		die "Invalid option: $cmd\n";
+	}
+}
+@arg = @ARGV;
+
+my $pos = 0;
+dump_config(parse_expr(\$pos));
+die "Parse error" if ($arg[$pos]);
diff --git a/scripts/kernel_bump.sh b/scripts/kernel_bump.sh
new file mode 100755
index 0000000..b1c17d6
--- /dev/null
+++ b/scripts/kernel_bump.sh
@@ -0,0 +1,263 @@
+#!/bin/sh
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# Copyright (C) 2024 Olliver Schinagl <oliver@schinagl.nl>
+
+set -eu
+if [ -n "${DEBUG_TRACE_SH:-}" ] && \
+   [ "${DEBUG_TRACE_SH:-}" != "${DEBUG_TRACE_SH#*"$(basename "${0}")"*}" ] || \
+   [ "${DEBUG_TRACE_SH:-}" = 'all' ]; then
+	set -x
+fi
+
+REQUIRED_COMMANDS='
+	[
+	basename
+	command
+	echo
+	exit
+	git
+	printf
+	sed
+	set
+	shift
+	sort
+'
+
+_msg()
+{
+	_level="${1:?Missing argument to function}"
+	shift
+
+	if [ "${#}" -le 0 ]; then
+		echo "${_level}: No content for this message ..."
+		return
+	fi
+
+	echo "${_level}: ${*}"
+}
+
+e_err()
+{
+	_msg 'err' "${*}" >&2
+}
+
+e_warn()
+{
+	_msg 'warning' "${*}"
+}
+
+e_notice()
+{
+	_msg 'notice' "${*}"
+}
+
+usage()
+{
+	echo "Usage: ${0}"
+	echo 'Helper script to bump the target kernel version, whilst keeping history.'
+	echo '    -c  Migrate config files (e.g. subtargets) only.'
+	echo "    -p  Optional Platform name (e.g. 'ath79' [PLATFORM_NAME]"
+	echo "    -r  Optional comma separated list of sub-targets (e.g. 'rtl930x' [SUBTARGET_NAMES]"
+	echo "    -s  Source version of kernel (e.g. 'v6.1' [SOURCE_VERSION])"
+	echo "    -t  Target version of kernel (e.g. 'v6.6' [TARGET_VERSION]')"
+	echo
+	echo 'All options can also be passed in environment variables (listed between [BRACKETS]).'
+	echo 'Note that this script must be run from within the OpenWrt git repository.'
+	echo 'Example: scripts/kernel_bump.sh -p realtek -s v6.1 -t v6.6'
+}
+
+cleanup()
+{
+	trap - EXIT HUP INT QUIT ABRT ALRM TERM
+
+	if [ -n "${initial_branch:-}" ] && \
+	   [ "$(git rev-parse --abbrev-ref HEAD)" != "${initial_branch:-}" ]; then
+		git switch "${initial_branch}"
+	fi
+}
+
+init()
+{
+	src_file="$(readlink -f "${0}")"
+	src_dir="${src_file%%"${src_file##*'/'}"}"
+	initial_branch="$(git rev-parse --abbrev-ref HEAD)"
+	initial_commitish="$(git rev-parse HEAD)"
+
+	if [ -n "$(git status --porcelain | grep -v '^?? .*')" ]; then
+		echo 'Git respository not in a clean state, will not continue.'
+		exit 1
+	fi
+
+	if [ -n "${src_dir##*'/scripts/'}" ]; then
+		echo "This script '${src_file}' is not in the scripts subdirectory, this is unexpected, cannot continue."
+		exit 1
+	fi
+
+	source_version="${source_version#v}"
+	target_version="${target_version#v}"
+
+	trap cleanup EXIT HUP INT QUIT ABRT ALRM TERM
+}
+
+bump_kernel()
+{
+	if [ -z "${platform_name}" ] || \
+	   [ -d "${PWD}/image" ]; then
+		platform_name="${PWD}"
+	fi
+	platform_name="${platform_name##*'/'}"
+
+	_target_dir="${src_dir}/../target/linux/${platform_name}"
+
+	if [ ! -d "${_target_dir}/image" ]; then
+		e_err "Cannot find target linux directory '${_target_dir:-not defined}'. Not in a platform directory, or -p not set."
+		exit 1
+	fi
+
+	git switch --force-create '__openwrt_kernel_files_mover'
+
+	if [ "${config_only:-false}" != 'true' ]; then
+		for _path in $(git ls-tree -d -r --name-only '__openwrt_kernel_files_mover' "${_target_dir}" |
+			       sed -n "s|^\(.*-${source_version}\).*|\1|p" |
+			       sort -u); do
+			if [ ! -e "${_path}" ] || \
+			   [ "${_path}" = "${_path%%"-${source_version}"}" ]; then
+				continue
+			fi
+
+			_target_path="${_path%%"-${source_version}"}-${target_version}"
+			if [ -e "${_target_path}" ]; then
+				e_err "Target '${_target_path}' already exists!"
+				exit 1
+			fi
+
+			git mv \
+				"${_path}" \
+				"${_target_path}"
+		done
+	fi
+
+	for _config in $(git ls-files "${_target_dir}" |
+	                 sed -n "s|^\(.*config-${source_version}\).*|\1|p" |
+	                 sort -u); do
+		if [ ! -e "${_config}" ]; then
+			continue
+		fi
+
+		_subtarget="${_config%%"/config-${source_version}"}"
+		if [ -n "${subtarget_names:-}" ]; then
+			echo "${subtarget_names:-}" | while IFS=',' read -r _subtarget_name; do
+				if [ "${_subtarget_name}" = "${_subtarget##*'/'}" ]; then
+					git mv "${_config}" "${_subtarget}/config-${target_version}"
+				fi
+			done
+		else
+			git mv "${_config}" "${_subtarget}/config-${target_version}"
+		fi
+	done
+
+	git commit \
+		--signoff \
+		--message "kernel/${platform_name}: Create kernel files for v${target_version} (from v${source_version})" \
+		--message 'This is an automatically generated commit.' \
+		--message 'When doing `git bisect`, consider `git bisect --skip`.'
+
+	git checkout 'HEAD~' "${_target_dir}"
+	git commit \
+		--signoff \
+		--message "kernel/${platform_name}: Restore kernel files for v${source_version}" \
+		--message "$(printf "This is an automatically generated commit which aids following Kernel patch\nhistory, as git will see the move and copy as a rename thus defeating the\npurpose.\n\nFor the original discussion see:\nhttps://lists.openwrt.org/pipermail/openwrt-devel/2023-October/041673.html")"
+	git switch "${initial_branch:?Unable to switch back to original branch. Quitting.}"
+	GIT_EDITOR=true git merge --no-ff '__openwrt_kernel_files_mover'
+	git branch --delete '__openwrt_kernel_files_mover'
+	echo "Deleting merge commit ($(git rev-parse HEAD))."
+	git rebase HEAD~1
+
+	echo "Original commitish was '${initial_commitish}'."
+	echo 'Kernel bump complete. Remember to use `git log --follow`.'
+}
+
+check_requirements()
+{
+	for _cmd in ${REQUIRED_COMMANDS}; do
+		if ! _test_result="$(command -V "${_cmd}")"; then
+			_test_result_fail="${_test_result_fail:-}${_test_result}\n"
+		else
+			_test_result_pass="${_test_result_pass:-}${_test_result}\n"
+		fi
+	done
+
+	echo 'Available commands:'
+	# As the results contain \n, we expect these to be interpreted.
+	# shellcheck disable=SC2059
+	printf "${_test_result_pass:-none\n}"
+	echo
+	echo 'Missing commands:'
+	# shellcheck disable=SC2059
+	printf "${_test_result_fail:-none\n}"
+	echo
+
+	if [ -n "${_test_result_fail:-}" ]; then
+		echo 'Command test failed, missing programs.'
+		test_failed=1
+	fi
+}
+
+main()
+{
+	while getopts 'chp:r:s:t:' _options; do
+		case "${_options}" in
+		'c')
+			config_only='true'
+			;;
+		'h')
+			usage
+			exit 0
+			;;
+		'p')
+			platform_name="${OPTARG}"
+			;;
+		'r')
+			subtarget_names="${OPTARG}"
+			;;
+		's')
+			source_version="${OPTARG}"
+			;;
+		't')
+			target_version="${OPTARG}"
+			;;
+		':')
+			e_err "Option -${OPTARG} requires an argument."
+			exit 1
+			;;
+		*)
+			e_err "Invalid option: -${OPTARG}"
+			exit 1
+			;;
+		esac
+	done
+	shift "$((OPTIND - 1))"
+
+	platform_name="${platform_name:-${PLATFORM_NAME:-}}"
+	subtarget_names="${subtarget_names:-${SUBTARGET_NAMES:-}}"
+	source_version="${source_version:-${SOURCE_VERSION:-}}"
+	target_version="${target_version:-${TARGET_VERSION:-}}"
+
+	if [ -z "${source_version:-}" ] || [ -z "${target_version:-}" ]; then
+		e_err "Source (${source_version:-missing source version}) and target (${target_version:-missing target version}) versions need to be defined."
+		echo
+		usage
+		exit 1
+	fi
+
+	check_requirements
+
+	init
+	bump_kernel
+	cleanup
+}
+
+main "${@}"
+
+exit 0
diff --git a/scripts/make-ipkg-dir.sh b/scripts/make-ipkg-dir.sh
new file mode 100755
index 0000000..529e430
--- /dev/null
+++ b/scripts/make-ipkg-dir.sh
@@ -0,0 +1,21 @@
+#!/bin/sh
+BASE=http://svn.openwrt.org/openwrt/trunk/openwrt
+TARGET=$1
+CONTROL=$2
+VERSION=$3
+ARCH=$4
+
+WD=$(pwd)
+
+mkdir -p "$TARGET/CONTROL"
+grep '^[^(Version|Architecture)]' "$CONTROL" > "$TARGET/CONTROL/control"
+grep '^Maintainer' "$CONTROL" 2>&1 >/dev/null || \
+        echo "Maintainer: LEDE Community <lede-dev@lists.infradead.org>" >> "$TARGET/CONTROL/control"
+grep '^Source' "$CONTROL" 2>&1 >/dev/null || {
+        pkgbase=$(echo "$WD" | sed -e "s|^$TOPDIR/||g")
+        [ "$pkgbase" = "$WD" ] && src="N/A" || src="$BASE/$pkgbase"
+        echo "Source: $src" >> "$TARGET/CONTROL/control"
+}
+echo "Version: $VERSION" >> "$TARGET/CONTROL/control"
+echo "Architecture: $ARCH" >> "$TARGET/CONTROL/control"
+chmod 644 "$TARGET/CONTROL/control"
diff --git a/scripts/md5sum b/scripts/md5sum
new file mode 100755
index 0000000..a5779c4
--- /dev/null
+++ b/scripts/md5sum
@@ -0,0 +1,2 @@
+#!/bin/sh
+cat "$@" | md5
diff --git a/scripts/metadata.pm b/scripts/metadata.pm
new file mode 100644
index 0000000..ecfe42c
--- /dev/null
+++ b/scripts/metadata.pm
@@ -0,0 +1,360 @@
+package metadata;
+use base 'Exporter';
+use strict;
+use warnings;
+our @EXPORT = qw(%package %vpackage %srcpackage %category %overrides clear_packages parse_package_metadata parse_package_manifest_metadata parse_target_metadata get_multiline @ignore %usernames %groupnames);
+
+our %package;
+our %vpackage;
+our %srcpackage;
+our %category;
+our %overrides;
+our @ignore;
+
+our %usernames;
+our %groupnames;
+our %userids;
+our %groupids;
+
+sub get_multiline {
+	my $fh = shift;
+	my $prefix = shift;
+	my $str;
+	while (<$fh>) {
+		last if /^@@/;
+		$str .= (($_ and $prefix) ? $prefix . $_ : $_);
+	}
+
+	return $str ? $str : "";
+}
+
+sub confstr($) {
+	my $conf = shift;
+	$conf =~ tr#/\.\-/#___#;
+	return $conf;
+}
+
+sub parse_package_metadata_usergroup($$$$$) {
+	my $makefile = shift;
+	my $typename = shift;
+	my $names = shift;
+	my $ids = shift;
+	my $spec = shift;
+	my $name;
+	my $id;
+
+	# the regex for name is taken from is_valid_name() of package shadow
+	if ($spec =~ /^([a-z_][a-z0-9_-]*\$?)$/) {
+		$name = $spec;
+		$id = -1;
+	} elsif ($spec =~ /^([a-z_][a-z0-9_-]*\$?)=(\d+)$/) {
+		$name = $1;
+		$id = $2;
+	} else {
+		warn "$makefile: invalid $typename spec $spec\n";
+		return 0;
+	}
+
+	if ($id =~ /^[1-9]\d*$/) {
+		if ($id >= 65536) {
+			warn "$makefile: $typename $name id $id >= 65536";
+			return 0;
+		}
+		if (not exists $ids->{$id}) {
+			$ids->{$id} = {
+				name => $name,
+				makefile => $makefile,
+			};
+		} elsif ($ids->{$id}{name} ne $name) {
+			warn "$makefile: $typename $name id $id is already taken by $ids->{$id}{makefile}\n";
+			return 0;
+		}
+	} elsif ($id != -1) {
+		warn "$makefile: $typename $name has invalid id $id\n";
+		return 0;
+	}
+
+	if (not exists $names->{$name}) {
+		$names->{$name} = {
+			id => $id,
+			makefile => $makefile,
+		};
+	} elsif ($names->{$name}{id} != $id) {
+		warn "$makefile: id of $typename $name collides with that defined defined in $names->{$name}{makefile}\n";
+		return 0;
+	}
+	return 1;
+}
+
+sub parse_target_metadata($) {
+	my $file = shift;
+	my ($target, @target, $profile);
+	my %target;
+	my $makefile;
+
+	open FILE, "<$file" or do {
+		warn "Can't open file '$file': $!\n";
+		return;
+	};
+	while (<FILE>) {
+		chomp;
+		/^Source-Makefile: \s*((.+\/)([^\/]+)\/Makefile)\s*$/ and $makefile = $1;
+		/^Target:\s*(.+)\s*$/ and do {
+			my $name = $1;
+			$target = {
+				id => $name,
+				board => $name,
+				makefile => $makefile,
+				boardconf => confstr($name),
+				conf => confstr($name),
+				profiles => [],
+				features => [],
+				depends => [],
+				subtargets => []
+			};
+			push @target, $target;
+			$target{$name} = $target;
+			if ($name =~ /([^\/]+)\/([^\/]+)/) {
+				push @{$target{$1}->{subtargets}}, $2;
+				$target->{board} = $1;
+				$target->{boardconf} = confstr($1);
+				$target->{subtarget} = 1;
+				$target->{parent} = $target{$1};
+			}
+		};
+		/^Target-Name:\s*(.+)\s*$/ and $target->{name} = $1;
+		/^Target-Arch:\s*(.+)\s*$/ and $target->{arch} = $1;
+		/^Target-Arch-Packages:\s*(.+)\s*$/ and $target->{arch_packages} = $1;
+		/^Target-Features:\s*(.+)\s*$/ and $target->{features} = [ split(/\s+/, $1) ];
+		/^Target-Depends:\s*(.+)\s*$/ and $target->{depends} = [ split(/\s+/, $1) ];
+		/^Target-Description:/ and $target->{desc} = get_multiline(*FILE);
+		/^Target-Optimization:\s*(.+)\s*$/ and $target->{cflags} = $1;
+		/^CPU-Type:\s*(.+)\s*$/ and $target->{cputype} = $1;
+		/^Linux-Version:\s*(.+)\s*$/ and $target->{version} = $1;
+		/^Linux-Testing-Version:\s*(.+)\s*$/ and $target->{testing_version} = $1;
+		/^Linux-Release:\s*(.+)\s*$/ and $target->{release} = $1;
+		/^Linux-Kernel-Arch:\s*(.+)\s*$/ and $target->{karch} = $1;
+		/^Default-Subtarget:\s*(.+)\s*$/ and $target->{def_subtarget} = $1;
+		/^Default-Packages:\s*(.+)\s*$/ and $target->{packages} = [ split(/\s+/, $1) ];
+		/^Target-Profile:\s*(.+)\s*$/ and do {
+			$profile = {
+				id => $1,
+				name => $1,
+				has_image_metadata => 0,
+				supported_devices => [],
+				priority => 999,
+				packages => [],
+				default => "y if TARGET_ALL_PROFILES"
+			};
+			$1 =~ /^DEVICE_/ and $target->{has_devices} = 1;
+			push @{$target->{profiles}}, $profile;
+		};
+		/^Target-Profile-Name:\s*(.+)\s*$/ and $profile->{name} = $1;
+		/^Target-Profile-hasImageMetadata:\s*(\d+)\s*$/ and $profile->{has_image_metadata} = $1;
+		/^Target-Profile-SupportedDevices:\s*(.+)\s*$/ and $profile->{supported_devices} = [ split(/\s+/, $1) ];
+		/^Target-Profile-Priority:\s*(\d+)\s*$/ and do {
+			$profile->{priority} = $1;
+			$target->{sort} = 1;
+		};
+		/^Target-Profile-Packages:\s*(.*)\s*$/ and $profile->{packages} = [ split(/\s+/, $1) ];
+		/^Target-Profile-Description:\s*(.*)\s*/ and $profile->{desc} = get_multiline(*FILE);
+		/^Target-Profile-Broken:\s*(.+)\s*$/ and do {
+			$profile->{broken} = 1;
+			$profile->{default} = "n";
+		};
+		/^Target-Profile-Default:\s*(.+)\s*$/ and $profile->{default} = $1;
+	}
+	close FILE;
+	foreach my $target (@target) {
+		if (@{$target->{subtargets}} > 0) {
+			$target->{profiles} = [];
+			next;
+		}
+		@{$target->{profiles}} > 0 or $target->{profiles} = [
+			{
+				id => 'Default',
+				name => 'Default',
+				packages => []
+			}
+		];
+
+		$target->{sort} and @{$target->{profiles}} = sort {
+			$a->{priority} <=> $b->{priority} or
+			$a->{name} cmp $b->{name};
+		} @{$target->{profiles}};
+	}
+	return @target;
+}
+
+sub clear_packages() {
+	%package = ();
+	%vpackage = ();
+	%srcpackage = ();
+	%category = ();
+	%overrides = ();
+	%usernames = ();
+	%groupnames = ();
+}
+
+sub parse_package_metadata($) {
+	my $file = shift;
+	my $pkg;
+	my $src;
+	my $override;
+	my %ignore = map { $_ => 1 } @ignore;
+
+	open FILE, "<$file" or do {
+		warn "Cannot open '$file': $!\n";
+		return undef;
+	};
+	while (<FILE>) {
+		chomp;
+		/^Source-Makefile: \s*((?:package\/)?((?:.+\/)?([^\/]+))\/Makefile)\s*$/ and do {
+			$src = {
+				makefile => $1,
+				path => $2,
+				name => $3,
+				ignore => $ignore{$3},
+				packages => [],
+				buildtypes => [],
+				builddepends => [],
+			};
+			$srcpackage{$3} = $src;
+			$override = "";
+			undef $pkg;
+		};
+		/^Override: \s*(.+?)\s*$/ and do {
+			$override = $1;
+			$overrides{$src->{name}} = 1;
+		};
+		next unless $src;
+		/^Package:\s*(.+?)\s*$/ and do {
+			$pkg = {};
+			$pkg->{src} = $src;
+			$pkg->{name} = $1;
+			$pkg->{title} = "";
+			$pkg->{depends} = [];
+			$pkg->{mdepends} = [];
+			$pkg->{provides} = [$1];
+			$pkg->{tristate} = 1;
+			$pkg->{override} = $override;
+			$package{$1} = $pkg;
+			push @{$src->{packages}}, $pkg;
+
+			$vpackage{$1} or $vpackage{$1} = [];
+			unshift @{$vpackage{$1}}, $pkg;
+		};
+		/^Build-Depends: \s*(.+)\s*$/ and $src->{builddepends} = [ split /\s+/, $1 ];
+		/^Build-Depends\/(\w+): \s*(.+)\s*$/ and $src->{"builddepends/$1"} = [ split /\s+/, $2 ];
+		/^Build-Types:\s*(.+)\s*$/ and $src->{buildtypes} = [ split /\s+/, $1 ];
+		next unless $pkg;
+		/^Version: \s*(.+)\s*$/ and $pkg->{version} = $1;
+		/^Title: \s*(.+)\s*$/ and $pkg->{title} = $1;
+		/^Menu: \s*(.+)\s*$/ and $pkg->{menu} = $1;
+		/^Submenu: \s*(.+)\s*$/ and $pkg->{submenu} = $1;
+		/^Submenu-Depends: \s*(.+)\s*$/ and $pkg->{submenudep} = $1;
+		/^Source: \s*(.+)\s*$/ and $pkg->{source} = $1;
+		/^License: \s*(.+)\s*$/ and $pkg->{license} = $1;
+		/^LicenseFiles: \s*(.+)\s*$/ and $pkg->{licensefiles} = $1;
+		/^CPE-ID: \s*(.+)\s*$/ and $pkg->{cpe_id} = $1;
+		/^URL: \s*(.+)\s*$/ and $pkg->{url} = $1;
+		/^ABI-Version: \s*(.+)\s*$/ and $pkg->{abi_version} = $1;
+		/^Default: \s*(.+)\s*$/ and $pkg->{default} = $1;
+		/^Provides: \s*(.+)\s*$/ and do {
+			my @vpkg = split /\s+/, $1;
+			@{$pkg->{provides}} = ($pkg->{name}, @vpkg);
+			foreach my $vpkg (@vpkg) {
+				next if ($vpkg eq $pkg->{name});
+				$vpackage{$vpkg} or $vpackage{$vpkg} = [];
+				push @{$vpackage{$vpkg}}, $pkg;
+			}
+		};
+		/^Menu-Depends: \s*(.+)\s*$/ and $pkg->{mdepends} = [ split /\s+/, $1 ];
+		/^Depends: \s*(.+)\s*$/ and $pkg->{depends} = [ split /\s+/, $1 ];
+		/^Conflicts: \s*(.+)\s*$/ and $pkg->{conflicts} = [ split /\s+/, $1 ];
+		/^Hidden: \s*(.+)\s*$/ and $pkg->{hidden} = 1;
+		/^Build-Variant: \s*([\w\-]+)\s*/ and $pkg->{variant} = $1;
+		/^Default-Variant: .*/ and $pkg->{variant_default} = 1;
+		/^Build-Only: \s*(.+)\s*$/ and $pkg->{buildonly} = 1;
+		/^Repository:\s*(.+?)\s*$/ and $pkg->{repository} = $1;
+		/^Category: \s*(.+)\s*$/ and do {
+			$pkg->{category} = $1;
+			defined $category{$1} or $category{$1} = {};
+			defined $category{$1}{$src->{name}} or $category{$1}{$src->{name}} = [];
+			push @{$category{$1}{$src->{name}}}, $pkg;
+		};
+		/^Description: \s*(.*)\s*$/ and $pkg->{description} = "\t\t $1\n". get_multiline(*FILE, "\t\t ");
+		/^Type: \s*(.+)\s*$/ and do {
+			$pkg->{type} = [ split /\s+/, $1 ];
+			undef $pkg->{tristate};
+			foreach my $type (@{$pkg->{type}}) {
+				$type =~ /ipkg/ and $pkg->{tristate} = 1;
+			}
+		};
+		/^Config:\s*(.*)\s*$/ and $pkg->{config} = "$1\n".get_multiline(*FILE, "\t");
+		/^Prereq-Check:/ and $pkg->{prereq} = 1;
+		/^Maintainer: \s*(.+)\s*$/ and $pkg->{maintainer} = [ split /, /, $1 ];
+		/^Require-User:\s*(.*?)\s*$/ and do {
+			my @ugspecs = split /\s+/, $1;
+
+			for my $ugspec (@ugspecs) {
+				my @ugspec = split /:/, $ugspec, 3;
+				if ($ugspec[0]) {
+					parse_package_metadata_usergroup($src->{makefile}, "user", \%usernames, \%userids, $ugspec[0]) or return 0;
+				}
+				if ($ugspec[1]) {
+					parse_package_metadata_usergroup($src->{makefile}, "group", \%groupnames, \%groupids, $ugspec[1]) or return 0;
+				}
+				if ($ugspec[2]) {
+					my @addngroups = split /,/, $ugspec[2];
+					for my $addngroup (@addngroups) {
+						parse_package_metadata_usergroup($src->{makefile}, "group", \%groupnames, \%groupids, $addngroup) or return 0;
+					}
+				}
+			}
+		};
+	}
+	close FILE;
+	return 1;
+}
+
+sub parse_package_manifest_metadata($) {
+	my $file = shift;
+	my $pkg;
+	my %pkgs;
+
+	open FILE, "<$file" or do {
+		warn "Cannot open '$file': $!\n";
+		return undef;
+	};
+
+	while (<FILE>) {
+		chomp;
+		/^Package:\s*(.+?)\s*$/ and do {
+			$pkg = {};
+			$pkg->{name} = $1;
+			$pkg->{depends} = [];
+			$pkgs{$1} = $pkg;
+		};
+		/^Version:\s*(.+)\s*$/ and $pkg->{version} = $1;
+		/^Depends:\s*(.+)\s*$/ and $pkg->{depends} = [ split /\s+/, $1 ];
+		/^Source:\s*(.+)\s*$/ and $pkg->{source} = $1;
+		/^SourceName:\s*(.+)\s*$/ and $pkg->{sourcename} = $1;
+		/^License:\s*(.+)\s*$/ and $pkg->{license} = $1;
+		/^LicenseFiles:\s*(.+)\s*$/ and $pkg->{licensefiles} = $1;
+		/^Section:\s*(.+)\s*$/ and $pkg->{section} = $1;
+		/^SourceDateEpoch: \s*(.+)\s*$/ and $pkg->{sourcedateepoch} = $1;
+		/^CPE-ID:\s*(.+)\s*$/ and $pkg->{cpe_id} = $1;
+		/^URL:\s*(.+)\s*$/ and $pkg->{url} = $1;
+		/^Architecture:\s*(.+)\s*$/ and $pkg->{architecture} = $1;
+		/^Installed-Size:\s*(.+)\s*$/ and $pkg->{installedsize} = $1;
+		/^Filename:\s*(.+)\s*$/ and $pkg->{filename} = $1;
+		/^Size:\s*(\d+)\s*$/ and $pkg->{size} = $1;
+		/^SHA256sum:\s*(.*)\s*$/ and $pkg->{sha256sum} = $1;
+	}
+
+	close FILE;
+	return %pkgs;
+}
+
+1;
diff --git a/scripts/mkhash.c b/scripts/mkhash.c
new file mode 100644
index 0000000..a28d5fd
--- /dev/null
+++ b/scripts/mkhash.c
@@ -0,0 +1,854 @@
+/*
+ * Copyright (C) 2016 Felix Fietkau <nbd@nbd.name>
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ * -- MD5 code:
+ *
+ * This is an OpenSSL-compatible implementation of the RSA Data Security, Inc.
+ * MD5 Message-Digest Algorithm (RFC 1321).
+ *
+ * Homepage:
+ * http://openwall.info/wiki/people/solar/software/public-domain-source-code/md5
+ *
+ * Author:
+ * Alexander Peslyak, better known as Solar Designer <solar at openwall.com>
+ *
+ * This software was written by Alexander Peslyak in 2001.  No copyright is
+ * claimed, and the software is hereby placed in the public domain.
+ * In case this attempt to disclaim copyright and place the software in the
+ * public domain is deemed null and void, then the software is
+ * Copyright (c) 2001 Alexander Peslyak and it is hereby released to the
+ * general public under the following terms:
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted.
+ *
+ * There's ABSOLUTELY NO WARRANTY, express or implied.
+ *
+ * (This is a heavily cut-down "BSD license".)
+ *
+ * This differs from Colin Plumb's older public domain implementation in that
+ * no exactly 32-bit integer data type is required (any 32-bit or wider
+ * unsigned integer data type will do), there's no compile-time endianness
+ * configuration, and the function prototypes match OpenSSL's.  No code from
+ * Colin Plumb's implementation has been reused; this comment merely compares
+ * the properties of the two independent implementations.
+ *
+ * The primary goals of this implementation are portability and ease of use.
+ * It is meant to be fast, but not as fast as possible.  Some known
+ * optimizations are not included to reduce source code size and avoid
+ * compile-time configuration.
+ *
+ * -- SHA256 Code:
+ *
+ * Copyright 2005 Colin Percival
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+
+
+#ifndef __FreeBSD__
+#include <endian.h>
+#else
+#include <sys/endian.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <stdint.h>
+#include <stdbool.h>
+#include <unistd.h>
+#include <sys/stat.h>
+
+#define ARRAY_SIZE(_n) (sizeof(_n) / sizeof((_n)[0]))
+
+#ifndef __FreeBSD__
+static void
+be32enc(void *buf, uint32_t u)
+{
+	uint8_t *p = buf;
+
+	p[0] = ((uint8_t) ((u >> 24) & 0xff));
+	p[1] = ((uint8_t) ((u >> 16) & 0xff));
+	p[2] = ((uint8_t) ((u >> 8) & 0xff));
+	p[3] = ((uint8_t) (u & 0xff));
+}
+
+static void
+be64enc(void *buf, uint64_t u)
+{
+	uint8_t *p = buf;
+
+	be32enc(p, ((uint32_t) (u >> 32)));
+	be32enc(p + 4, ((uint32_t) (u & 0xffffffffULL)));
+}
+
+
+static uint16_t
+be16dec(const void *buf)
+{
+	const uint8_t *p = buf;
+
+	return (((uint16_t) p[0]) << 8) | p[1];
+}
+
+static uint32_t
+be32dec(const void *buf)
+{
+	const uint8_t *p = buf;
+
+	return (((uint32_t) be16dec(p)) << 16) | be16dec(p + 2);
+}
+#endif
+
+#define MD5_DIGEST_LENGTH	16
+
+typedef struct MD5_CTX {
+	uint32_t lo, hi;
+	uint32_t a, b, c, d;
+	unsigned char buffer[64];
+} MD5_CTX;
+
+/*
+ * The basic MD5 functions.
+ *
+ * F and G are optimized compared to their RFC 1321 definitions for
+ * architectures that lack an AND-NOT instruction, just like in Colin Plumb's
+ * implementation.
+ */
+#define F(x, y, z)			((z) ^ ((x) & ((y) ^ (z))))
+#define G(x, y, z)			((y) ^ ((z) & ((x) ^ (y))))
+#define H(x, y, z)			(((x) ^ (y)) ^ (z))
+#define H2(x, y, z)			((x) ^ ((y) ^ (z)))
+#define I(x, y, z)			((y) ^ ((x) | ~(z)))
+
+/*
+ * The MD5 transformation for all four rounds.
+ */
+#define STEP(f, a, b, c, d, x, t, s) \
+	(a) += f((b), (c), (d)) + (x) + (t); \
+	(a) = (((a) << (s)) | (((a) & 0xffffffff) >> (32 - (s)))); \
+	(a) += (b);
+
+/*
+ * SET reads 4 input bytes in little-endian byte order and stores them
+ * in a properly aligned word in host byte order.
+ */
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+#define SET(n) \
+	(*(uint32_t *)&ptr[(n) * 4])
+#define GET(n) \
+	SET(n)
+#else
+#define SET(n) \
+	(block[(n)] = \
+	(uint32_t)ptr[(n) * 4] | \
+	((uint32_t)ptr[(n) * 4 + 1] << 8) | \
+	((uint32_t)ptr[(n) * 4 + 2] << 16) | \
+	((uint32_t)ptr[(n) * 4 + 3] << 24))
+#define GET(n) \
+	(block[(n)])
+#endif
+
+/*
+ * This processes one or more 64-byte data blocks, but does NOT update
+ * the bit counters.  There are no alignment requirements.
+ */
+static const void *MD5_body(MD5_CTX *ctx, const void *data, unsigned long size)
+{
+	const unsigned char *ptr;
+	uint32_t a, b, c, d;
+	uint32_t saved_a, saved_b, saved_c, saved_d;
+#if __BYTE_ORDER != __LITTLE_ENDIAN
+	uint32_t block[16];
+#endif
+
+	ptr = (const unsigned char *)data;
+
+	a = ctx->a;
+	b = ctx->b;
+	c = ctx->c;
+	d = ctx->d;
+
+	do {
+		saved_a = a;
+		saved_b = b;
+		saved_c = c;
+		saved_d = d;
+
+/* Round 1 */
+		STEP(F, a, b, c, d, SET(0), 0xd76aa478, 7)
+		STEP(F, d, a, b, c, SET(1), 0xe8c7b756, 12)
+		STEP(F, c, d, a, b, SET(2), 0x242070db, 17)
+		STEP(F, b, c, d, a, SET(3), 0xc1bdceee, 22)
+		STEP(F, a, b, c, d, SET(4), 0xf57c0faf, 7)
+		STEP(F, d, a, b, c, SET(5), 0x4787c62a, 12)
+		STEP(F, c, d, a, b, SET(6), 0xa8304613, 17)
+		STEP(F, b, c, d, a, SET(7), 0xfd469501, 22)
+		STEP(F, a, b, c, d, SET(8), 0x698098d8, 7)
+		STEP(F, d, a, b, c, SET(9), 0x8b44f7af, 12)
+		STEP(F, c, d, a, b, SET(10), 0xffff5bb1, 17)
+		STEP(F, b, c, d, a, SET(11), 0x895cd7be, 22)
+		STEP(F, a, b, c, d, SET(12), 0x6b901122, 7)
+		STEP(F, d, a, b, c, SET(13), 0xfd987193, 12)
+		STEP(F, c, d, a, b, SET(14), 0xa679438e, 17)
+		STEP(F, b, c, d, a, SET(15), 0x49b40821, 22)
+
+/* Round 2 */
+		STEP(G, a, b, c, d, GET(1), 0xf61e2562, 5)
+		STEP(G, d, a, b, c, GET(6), 0xc040b340, 9)
+		STEP(G, c, d, a, b, GET(11), 0x265e5a51, 14)
+		STEP(G, b, c, d, a, GET(0), 0xe9b6c7aa, 20)
+		STEP(G, a, b, c, d, GET(5), 0xd62f105d, 5)
+		STEP(G, d, a, b, c, GET(10), 0x02441453, 9)
+		STEP(G, c, d, a, b, GET(15), 0xd8a1e681, 14)
+		STEP(G, b, c, d, a, GET(4), 0xe7d3fbc8, 20)
+		STEP(G, a, b, c, d, GET(9), 0x21e1cde6, 5)
+		STEP(G, d, a, b, c, GET(14), 0xc33707d6, 9)
+		STEP(G, c, d, a, b, GET(3), 0xf4d50d87, 14)
+		STEP(G, b, c, d, a, GET(8), 0x455a14ed, 20)
+		STEP(G, a, b, c, d, GET(13), 0xa9e3e905, 5)
+		STEP(G, d, a, b, c, GET(2), 0xfcefa3f8, 9)
+		STEP(G, c, d, a, b, GET(7), 0x676f02d9, 14)
+		STEP(G, b, c, d, a, GET(12), 0x8d2a4c8a, 20)
+
+/* Round 3 */
+		STEP(H, a, b, c, d, GET(5), 0xfffa3942, 4)
+		STEP(H2, d, a, b, c, GET(8), 0x8771f681, 11)
+		STEP(H, c, d, a, b, GET(11), 0x6d9d6122, 16)
+		STEP(H2, b, c, d, a, GET(14), 0xfde5380c, 23)
+		STEP(H, a, b, c, d, GET(1), 0xa4beea44, 4)
+		STEP(H2, d, a, b, c, GET(4), 0x4bdecfa9, 11)
+		STEP(H, c, d, a, b, GET(7), 0xf6bb4b60, 16)
+		STEP(H2, b, c, d, a, GET(10), 0xbebfbc70, 23)
+		STEP(H, a, b, c, d, GET(13), 0x289b7ec6, 4)
+		STEP(H2, d, a, b, c, GET(0), 0xeaa127fa, 11)
+		STEP(H, c, d, a, b, GET(3), 0xd4ef3085, 16)
+		STEP(H2, b, c, d, a, GET(6), 0x04881d05, 23)
+		STEP(H, a, b, c, d, GET(9), 0xd9d4d039, 4)
+		STEP(H2, d, a, b, c, GET(12), 0xe6db99e5, 11)
+		STEP(H, c, d, a, b, GET(15), 0x1fa27cf8, 16)
+		STEP(H2, b, c, d, a, GET(2), 0xc4ac5665, 23)
+
+/* Round 4 */
+		STEP(I, a, b, c, d, GET(0), 0xf4292244, 6)
+		STEP(I, d, a, b, c, GET(7), 0x432aff97, 10)
+		STEP(I, c, d, a, b, GET(14), 0xab9423a7, 15)
+		STEP(I, b, c, d, a, GET(5), 0xfc93a039, 21)
+		STEP(I, a, b, c, d, GET(12), 0x655b59c3, 6)
+		STEP(I, d, a, b, c, GET(3), 0x8f0ccc92, 10)
+		STEP(I, c, d, a, b, GET(10), 0xffeff47d, 15)
+		STEP(I, b, c, d, a, GET(1), 0x85845dd1, 21)
+		STEP(I, a, b, c, d, GET(8), 0x6fa87e4f, 6)
+		STEP(I, d, a, b, c, GET(15), 0xfe2ce6e0, 10)
+		STEP(I, c, d, a, b, GET(6), 0xa3014314, 15)
+		STEP(I, b, c, d, a, GET(13), 0x4e0811a1, 21)
+		STEP(I, a, b, c, d, GET(4), 0xf7537e82, 6)
+		STEP(I, d, a, b, c, GET(11), 0xbd3af235, 10)
+		STEP(I, c, d, a, b, GET(2), 0x2ad7d2bb, 15)
+		STEP(I, b, c, d, a, GET(9), 0xeb86d391, 21)
+
+		a += saved_a;
+		b += saved_b;
+		c += saved_c;
+		d += saved_d;
+
+		ptr += 64;
+	} while (size -= 64);
+
+	ctx->a = a;
+	ctx->b = b;
+	ctx->c = c;
+	ctx->d = d;
+
+	return ptr;
+}
+
+void MD5_begin(MD5_CTX *ctx)
+{
+	ctx->a = 0x67452301;
+	ctx->b = 0xefcdab89;
+	ctx->c = 0x98badcfe;
+	ctx->d = 0x10325476;
+
+	ctx->lo = 0;
+	ctx->hi = 0;
+}
+
+static void
+MD5_hash(const void *data, size_t size, MD5_CTX *ctx)
+{
+	uint32_t saved_lo;
+	unsigned long used, available;
+
+	saved_lo = ctx->lo;
+	if ((ctx->lo = (saved_lo + size) & 0x1fffffff) < saved_lo)
+		ctx->hi++;
+	ctx->hi += size >> 29;
+
+	used = saved_lo & 0x3f;
+
+	if (used) {
+		available = 64 - used;
+
+		if (size < available) {
+			memcpy(&ctx->buffer[used], data, size);
+			return;
+		}
+
+		memcpy(&ctx->buffer[used], data, available);
+		data = (const unsigned char *)data + available;
+		size -= available;
+		MD5_body(ctx, ctx->buffer, 64);
+	}
+
+	if (size >= 64) {
+		data = MD5_body(ctx, data, size & ~((size_t) 0x3f));
+		size &= 0x3f;
+	}
+
+	memcpy(ctx->buffer, data, size);
+}
+
+static void
+MD5_end(void *resbuf, MD5_CTX *ctx)
+{
+	unsigned char *result = resbuf;
+	unsigned long used, available;
+
+	used = ctx->lo & 0x3f;
+
+	ctx->buffer[used++] = 0x80;
+
+	available = 64 - used;
+
+	if (available < 8) {
+		memset(&ctx->buffer[used], 0, available);
+		MD5_body(ctx, ctx->buffer, 64);
+		used = 0;
+		available = 64;
+	}
+
+	memset(&ctx->buffer[used], 0, available - 8);
+
+	ctx->lo <<= 3;
+	ctx->buffer[56] = ctx->lo;
+	ctx->buffer[57] = ctx->lo >> 8;
+	ctx->buffer[58] = ctx->lo >> 16;
+	ctx->buffer[59] = ctx->lo >> 24;
+	ctx->buffer[60] = ctx->hi;
+	ctx->buffer[61] = ctx->hi >> 8;
+	ctx->buffer[62] = ctx->hi >> 16;
+	ctx->buffer[63] = ctx->hi >> 24;
+
+	MD5_body(ctx, ctx->buffer, 64);
+
+	result[0] = ctx->a;
+	result[1] = ctx->a >> 8;
+	result[2] = ctx->a >> 16;
+	result[3] = ctx->a >> 24;
+	result[4] = ctx->b;
+	result[5] = ctx->b >> 8;
+	result[6] = ctx->b >> 16;
+	result[7] = ctx->b >> 24;
+	result[8] = ctx->c;
+	result[9] = ctx->c >> 8;
+	result[10] = ctx->c >> 16;
+	result[11] = ctx->c >> 24;
+	result[12] = ctx->d;
+	result[13] = ctx->d >> 8;
+	result[14] = ctx->d >> 16;
+	result[15] = ctx->d >> 24;
+
+	memset(ctx, 0, sizeof(*ctx));
+}
+
+#define SHA256_BLOCK_LENGTH		64
+#define SHA256_DIGEST_LENGTH		32
+#define SHA256_DIGEST_STRING_LENGTH	(SHA256_DIGEST_LENGTH * 2 + 1)
+
+typedef struct SHA256Context {
+	uint32_t state[8];
+	uint64_t count;
+	uint8_t buf[SHA256_BLOCK_LENGTH];
+} SHA256_CTX;
+
+#if BYTE_ORDER == BIG_ENDIAN
+
+/* Copy a vector of big-endian uint32_t into a vector of bytes */
+#define be32enc_vect(dst, src, len)	\
+	memcpy((void *)dst, (const void *)src, (size_t)len)
+
+/* Copy a vector of bytes into a vector of big-endian uint32_t */
+#define be32dec_vect(dst, src, len)	\
+	memcpy((void *)dst, (const void *)src, (size_t)len)
+
+#else /* BYTE_ORDER != BIG_ENDIAN */
+
+/*
+ * Encode a length len/4 vector of (uint32_t) into a length len vector of
+ * (unsigned char) in big-endian form.  Assumes len is a multiple of 4.
+ */
+static void
+be32enc_vect(unsigned char *dst, const uint32_t *src, size_t len)
+{
+	size_t i;
+
+	for (i = 0; i < len / 4; i++)
+		be32enc(dst + i * 4, src[i]);
+}
+
+/*
+ * Decode a big-endian length len vector of (unsigned char) into a length
+ * len/4 vector of (uint32_t).  Assumes len is a multiple of 4.
+ */
+static void
+be32dec_vect(uint32_t *dst, const unsigned char *src, size_t len)
+{
+	size_t i;
+
+	for (i = 0; i < len / 4; i++)
+		dst[i] = be32dec(src + i * 4);
+}
+
+#endif /* BYTE_ORDER != BIG_ENDIAN */
+
+
+/* Elementary functions used by SHA256 */
+#define Ch(x, y, z)	((x & (y ^ z)) ^ z)
+#define Maj(x, y, z)	((x & (y | z)) | (y & z))
+#define ROTR(x, n)	((x >> n) | (x << (32 - n)))
+
+/*
+ * SHA256 block compression function.  The 256-bit state is transformed via
+ * the 512-bit input block to produce a new state.
+ */
+static void
+SHA256_Transform(uint32_t * state, const unsigned char block[64])
+{
+	/* SHA256 round constants. */
+	static const uint32_t K[64] = {
+		0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5,
+		0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
+		0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3,
+		0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
+		0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc,
+		0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
+		0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7,
+		0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
+		0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13,
+		0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
+		0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3,
+		0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
+		0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5,
+		0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
+		0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208,
+		0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
+	};
+	uint32_t W[64];
+	uint32_t S[8];
+	int i;
+
+#define S0(x)		(ROTR(x, 2) ^ ROTR(x, 13) ^ ROTR(x, 22))
+#define S1(x)		(ROTR(x, 6) ^ ROTR(x, 11) ^ ROTR(x, 25))
+#define s0(x)		(ROTR(x, 7) ^ ROTR(x, 18) ^ (x >> 3))
+#define s1(x)		(ROTR(x, 17) ^ ROTR(x, 19) ^ (x >> 10))
+
+/* SHA256 round function */
+#define RND(a, b, c, d, e, f, g, h, k)			\
+	h += S1(e) + Ch(e, f, g) + k;			\
+	d += h;						\
+	h += S0(a) + Maj(a, b, c);
+
+/* Adjusted round function for rotating state */
+#define RNDr(S, W, i, ii)			\
+	RND(S[(64 - i) % 8], S[(65 - i) % 8],	\
+	    S[(66 - i) % 8], S[(67 - i) % 8],	\
+	    S[(68 - i) % 8], S[(69 - i) % 8],	\
+	    S[(70 - i) % 8], S[(71 - i) % 8],	\
+	    W[i + ii] + K[i + ii])
+
+/* Message schedule computation */
+#define MSCH(W, ii, i)				\
+	W[i + ii + 16] = s1(W[i + ii + 14]) + W[i + ii + 9] + s0(W[i + ii + 1]) + W[i + ii]
+
+	/* 1. Prepare the first part of the message schedule W. */
+	be32dec_vect(W, block, 64);
+
+	/* 2. Initialize working variables. */
+	memcpy(S, state, 32);
+
+	/* 3. Mix. */
+	for (i = 0; i < 64; i += 16) {
+		RNDr(S, W, 0, i);
+		RNDr(S, W, 1, i);
+		RNDr(S, W, 2, i);
+		RNDr(S, W, 3, i);
+		RNDr(S, W, 4, i);
+		RNDr(S, W, 5, i);
+		RNDr(S, W, 6, i);
+		RNDr(S, W, 7, i);
+		RNDr(S, W, 8, i);
+		RNDr(S, W, 9, i);
+		RNDr(S, W, 10, i);
+		RNDr(S, W, 11, i);
+		RNDr(S, W, 12, i);
+		RNDr(S, W, 13, i);
+		RNDr(S, W, 14, i);
+		RNDr(S, W, 15, i);
+
+		if (i == 48)
+			break;
+		MSCH(W, 0, i);
+		MSCH(W, 1, i);
+		MSCH(W, 2, i);
+		MSCH(W, 3, i);
+		MSCH(W, 4, i);
+		MSCH(W, 5, i);
+		MSCH(W, 6, i);
+		MSCH(W, 7, i);
+		MSCH(W, 8, i);
+		MSCH(W, 9, i);
+		MSCH(W, 10, i);
+		MSCH(W, 11, i);
+		MSCH(W, 12, i);
+		MSCH(W, 13, i);
+		MSCH(W, 14, i);
+		MSCH(W, 15, i);
+	}
+
+#undef S0
+#undef s0
+#undef S1
+#undef s1
+#undef RND
+#undef RNDr
+#undef MSCH
+
+	/* 4. Mix local working variables into global state */
+	for (i = 0; i < 8; i++)
+		state[i] += S[i];
+}
+
+static unsigned char PAD[64] = {
+	0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+/* Add padding and terminating bit-count. */
+static void
+SHA256_Pad(SHA256_CTX * ctx)
+{
+	size_t r;
+
+	/* Figure out how many bytes we have buffered. */
+	r = (ctx->count >> 3) & 0x3f;
+
+	/* Pad to 56 mod 64, transforming if we finish a block en route. */
+	if (r < 56) {
+		/* Pad to 56 mod 64. */
+		memcpy(&ctx->buf[r], PAD, 56 - r);
+	} else {
+		/* Finish the current block and mix. */
+		memcpy(&ctx->buf[r], PAD, 64 - r);
+		SHA256_Transform(ctx->state, ctx->buf);
+
+		/* The start of the final block is all zeroes. */
+		memset(&ctx->buf[0], 0, 56);
+	}
+
+	/* Add the terminating bit-count. */
+	be64enc(&ctx->buf[56], ctx->count);
+
+	/* Mix in the final block. */
+	SHA256_Transform(ctx->state, ctx->buf);
+}
+
+/* SHA-256 initialization.  Begins a SHA-256 operation. */
+static void
+SHA256_Init(SHA256_CTX * ctx)
+{
+
+	/* Zero bits processed so far */
+	ctx->count = 0;
+
+	/* Magic initialization constants */
+	ctx->state[0] = 0x6A09E667;
+	ctx->state[1] = 0xBB67AE85;
+	ctx->state[2] = 0x3C6EF372;
+	ctx->state[3] = 0xA54FF53A;
+	ctx->state[4] = 0x510E527F;
+	ctx->state[5] = 0x9B05688C;
+	ctx->state[6] = 0x1F83D9AB;
+	ctx->state[7] = 0x5BE0CD19;
+}
+
+/* Add bytes into the hash */
+static void
+SHA256_Update(SHA256_CTX * ctx, const void *in, size_t len)
+{
+	uint64_t bitlen;
+	uint32_t r;
+	const unsigned char *src = in;
+
+	/* Number of bytes left in the buffer from previous updates */
+	r = (ctx->count >> 3) & 0x3f;
+
+	/* Convert the length into a number of bits */
+	bitlen = len << 3;
+
+	/* Update number of bits */
+	ctx->count += bitlen;
+
+	/* Handle the case where we don't need to perform any transforms */
+	if (len < 64 - r) {
+		memcpy(&ctx->buf[r], src, len);
+		return;
+	}
+
+	/* Finish the current block */
+	memcpy(&ctx->buf[r], src, 64 - r);
+	SHA256_Transform(ctx->state, ctx->buf);
+	src += 64 - r;
+	len -= 64 - r;
+
+	/* Perform complete blocks */
+	while (len >= 64) {
+		SHA256_Transform(ctx->state, src);
+		src += 64;
+		len -= 64;
+	}
+
+	/* Copy left over data into buffer */
+	memcpy(ctx->buf, src, len);
+}
+
+/*
+ * SHA-256 finalization.  Pads the input data, exports the hash value,
+ * and clears the context state.
+ */
+static void
+SHA256_Final(unsigned char digest[static SHA256_DIGEST_LENGTH], SHA256_CTX *ctx)
+{
+	/* Add padding */
+	SHA256_Pad(ctx);
+
+	/* Write the hash */
+	be32enc_vect(digest, ctx->state, SHA256_DIGEST_LENGTH);
+
+	/* Clear the context state */
+	memset(ctx, 0, sizeof(*ctx));
+}
+
+static void *hash_buf(FILE *f, int *len)
+{
+	static char buf[1024];
+
+	*len = fread(buf, 1, sizeof(buf), f);
+
+	return *len > 0 ? buf : NULL;
+}
+
+static char *hash_string(unsigned char *buf, int len)
+{
+	static char str[SHA256_DIGEST_LENGTH * 2 + 1];
+	int i;
+
+	if (len * 2 + 1 > sizeof(str))
+		return NULL;
+
+	for (i = 0; i < len; i++)
+		sprintf(&str[i * 2], "%02x", buf[i]);
+
+	return str;
+}
+
+static const char *md5_hash(FILE *f)
+{
+	MD5_CTX ctx;
+	unsigned char val[MD5_DIGEST_LENGTH];
+	void *buf;
+	int len;
+
+	MD5_begin(&ctx);
+	while ((buf = hash_buf(f, &len)) != NULL)
+		MD5_hash(buf, len, &ctx);
+	MD5_end(val, &ctx);
+
+	return hash_string(val, MD5_DIGEST_LENGTH);
+}
+
+static const char *sha256_hash(FILE *f)
+{
+	SHA256_CTX ctx;
+	unsigned char val[SHA256_DIGEST_LENGTH];
+	void *buf;
+	int len;
+
+	SHA256_Init(&ctx);
+	while ((buf = hash_buf(f, &len)) != NULL)
+		SHA256_Update(&ctx, buf, len);
+	SHA256_Final(val, &ctx);
+
+	return hash_string(val, SHA256_DIGEST_LENGTH);
+}
+
+
+struct hash_type {
+	const char *name;
+	const char *(*func)(FILE *f);
+	int len;
+};
+
+struct hash_type types[] = {
+	{ "md5", md5_hash, MD5_DIGEST_LENGTH },
+	{ "sha256", sha256_hash, SHA256_DIGEST_LENGTH },
+};
+
+
+static int usage(const char *progname)
+{
+	int i;
+
+	fprintf(stderr, "Usage: %s <hash type> [options] [<file>...]\n"
+		"Options:\n"
+		"	-n		Print filename(s)\n"
+		"	-N		Suppress trailing newline\n"
+		"\n"
+		"Supported hash types:", progname);
+
+	for (i = 0; i < ARRAY_SIZE(types); i++)
+		fprintf(stderr, "%s %s", i ? "," : "", types[i].name);
+
+	fprintf(stderr, "\n");
+	return 1;
+}
+
+static struct hash_type *get_hash_type(const char *name)
+{
+	int i;
+
+	for (i = 0; i < ARRAY_SIZE(types); i++) {
+		struct hash_type *t = &types[i];
+
+		if (!strcmp(t->name, name))
+			return t;
+	}
+	return NULL;
+}
+
+
+static int hash_file(struct hash_type *t, const char *filename, bool add_filename,
+	bool no_newline)
+{
+	const char *str;
+
+	if (!filename || !strcmp(filename, "-")) {
+		str = t->func(stdin);
+	} else {
+		struct stat path_stat;
+		stat(filename, &path_stat);
+		if (S_ISDIR(path_stat.st_mode)) {
+			fprintf(stderr, "Failed to open '%s': Is a directory\n", filename);
+			return 1;
+		}
+
+		FILE *f = fopen(filename, "r");
+
+		if (!f) {
+			fprintf(stderr, "Failed to open '%s'\n", filename);
+			return 1;
+		}
+		str = t->func(f);
+		fclose(f);
+	}
+
+	if (!str) {
+		fprintf(stderr, "Failed to generate hash\n");
+		return 1;
+	}
+
+	if (add_filename)
+		printf("%s %s%s", str, filename ? filename : "-",
+			no_newline ? "" : "\n");
+	else
+		printf("%s%s", str, no_newline ? "" : "\n");
+	return 0;
+}
+
+
+int main(int argc, char **argv)
+{
+	struct hash_type *t;
+	const char *progname = argv[0];
+	int i, ch;
+	bool add_filename = false, no_newline = false;
+
+	while ((ch = getopt(argc, argv, "nN")) != -1) {
+		switch (ch) {
+		case 'n':
+			add_filename = true;
+			break;
+		case 'N':
+			no_newline = true;
+			break;
+		default:
+			return usage(progname);
+		}
+	}
+
+	argc -= optind;
+	argv += optind;
+
+	if (argc < 1)
+		return usage(progname);
+
+	t = get_hash_type(argv[0]);
+	if (!t)
+		return usage(progname);
+
+	if (argc < 2)
+		return hash_file(t, NULL, add_filename, no_newline);
+
+	for (i = 0; i < argc - 1; i++) {
+		int ret = hash_file(t, argv[1 + i], add_filename, no_newline);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
diff --git a/scripts/mkits-qsdk-ipq-image.sh b/scripts/mkits-qsdk-ipq-image.sh
new file mode 100755
index 0000000..066e8df
--- /dev/null
+++ b/scripts/mkits-qsdk-ipq-image.sh
@@ -0,0 +1,59 @@
+#!/usr/bin/env bash
+#
+# Licensed under the terms of the GNU GPL License version 2 or later.
+# Author: Piotr Dymacz <pepe2k@gmail.com>, based on mkits.sh.
+#
+# Qualcomm SDK (QSDK) sysupgrade compatible images for IPQ40xx, IPQ806x
+# and IPQ807x use FIT format together with 'dumpimage' tool from U-Boot
+# for verifying and extracting them. Based on 'images' sections names,
+# corresponding mtd partitions are flashed.
+# This is a simple script for generating FIT images tree source files,
+# compatible with the QSDK sysupgrade format. Resulting images can be
+# used for initial (factory -> OpenWrt) installation and would work
+# both in CLI and GUI. The script is also universal in a way it allows
+# to include as many sections as needed.
+#
+
+usage() {
+	echo "Usage: `basename $0` output img0_name img0_file [[img1_name img1_file] ...]"
+	exit 1
+}
+
+# We need at least 3 arguments
+[ "$#" -lt 3 ] && usage
+
+# Target output file
+OUTPUT="$1"; shift
+
+# Create a default, fully populated DTS file
+echo "\
+/dts-v1/;
+
+/ {
+	description = \"OpenWrt factory image\";
+	#address-cells = <1>;
+
+	images {" > ${OUTPUT}
+
+while [ -n "$1" -a -n "$2" ]; do
+	[ -f "$2" ] || usage
+
+	name="$1"; shift
+	file="$1"; shift
+
+	echo \
+"		${name} {
+			description = \"${name}\";
+			data = /incbin/(\"${file}\");
+			type = \"Firmware\";
+			arch = \"ARM\";
+			compression = \"none\";
+			hash@1 {
+				algo = \"crc32\";
+			};
+		};" >> ${OUTPUT}
+done
+
+echo \
+"	};
+};" >> ${OUTPUT}
diff --git a/scripts/mkits-zyxel-fit-filogic.sh b/scripts/mkits-zyxel-fit-filogic.sh
new file mode 100755
index 0000000..319e187
--- /dev/null
+++ b/scripts/mkits-zyxel-fit-filogic.sh
@@ -0,0 +1,40 @@
+#!/usr/bin/env bash
+#
+# Licensed under the terms of the GNU GPL License version 2 or later.
+# Author: David Bauer <mail@david-bauer.net>, based on mkits-zyxel-factory.sh.
+
+usage() {
+	echo "Usage: `basename $0` output file compat-models"
+	exit 1
+}
+
+# We need at least 3 arguments
+[ "$#" -lt 3 ] && usage
+
+# Target output file
+OUTPUT="$1"; shift
+FILE="$1"; shift
+MODELS="$1"; shift
+
+# Create a default, fully populated DTS file
+echo "\
+/dts-v1/;
+
+/ {
+	timestamp = <0x684090B4>;
+	description = \"Zyxel FIT (Flattened Image Tree)\";
+	compat-models = [${MODELS}];
+	fw_version = \"9.99(###.1)\";
+	#address-cells = <1>;
+
+	images {
+		ubi {
+			data = /incbin/(\"${FILE}\");
+			type = \"firmware\";
+			compression = \"none\";
+			hash {
+				algo = \"sha256\";
+			};
+		};
+	};
+};" > ${OUTPUT}
diff --git a/scripts/mkits-zyxel-fit.sh b/scripts/mkits-zyxel-fit.sh
new file mode 100755
index 0000000..8ace194
--- /dev/null
+++ b/scripts/mkits-zyxel-fit.sh
@@ -0,0 +1,38 @@
+#!/usr/bin/env bash
+#
+# Licensed under the terms of the GNU GPL License version 2 or later.
+# Author: David Bauer <mail@david-bauer.net>, based on mkits-zyxel-factory.sh.
+
+usage() {
+	echo "Usage: `basename $0` output file compat-models"
+	exit 1
+}
+
+# We need at least 3 arguments
+[ "$#" -lt 3 ] && usage
+
+# Target output file
+OUTPUT="$1"; shift
+FILE="$1"; shift
+MODELS="$1"; shift
+
+# Create a default, fully populated DTS file
+echo "\
+/dts-v1/;
+
+/ {
+	description = \"Zyxel FIT (Flattened Image Tree)\";
+	compat-models = [${MODELS}];
+	#address-cells = <1>;
+
+	images {
+		firmware {
+			data = /incbin/(\"${FILE}\");
+			type = \"firmware\";
+			compression = \"none\";
+			hash@1 {
+				algo = \"sha1\";
+			};
+		};
+	};
+};" > ${OUTPUT}
diff --git a/scripts/mkits.sh b/scripts/mkits.sh
new file mode 100755
index 0000000..46ab5ee
--- /dev/null
+++ b/scripts/mkits.sh
@@ -0,0 +1,240 @@
+#!/bin/sh
+#
+# Licensed under the terms of the GNU GPL License version 2 or later.
+#
+# Author: Peter Tyser <ptyser@xes-inc.com>
+#
+# U-Boot firmware supports the booting of images in the Flattened Image
+# Tree (FIT) format.  The FIT format uses a device tree structure to
+# describe a kernel image, device tree blob, ramdisk, etc.  This script
+# creates an Image Tree Source (.its file) which can be passed to the
+# 'mkimage' utility to generate an Image Tree Blob (.itb file).  The .itb
+# file can then be booted by U-Boot (or other bootloaders which support
+# FIT images).  See doc/uImage.FIT/howto.txt in U-Boot source code for
+# additional information on FIT images.
+#
+
+usage() {
+	printf "Usage: %s -A arch -C comp -a addr -e entry" "$(basename "$0")"
+	printf " -v version -k kernel [-D name -n address -d dtb] -o its_file"
+
+	printf "\n\t-A ==> set architecture to 'arch'"
+	printf "\n\t-C ==> set compression type 'comp'"
+	printf "\n\t-c ==> set config name 'config'"
+	printf "\n\t-a ==> set load address to 'addr' (hex)"
+	printf "\n\t-e ==> set entry point to 'entry' (hex)"
+	printf "\n\t-f ==> set device tree compatible string"
+	printf "\n\t-i ==> include initrd Blob 'initrd'"
+	printf "\n\t-v ==> set kernel version to 'version'"
+	printf "\n\t-k ==> include kernel image 'kernel'"
+	printf "\n\t-D ==> human friendly Device Tree Blob 'name'"
+	printf "\n\t-n ==> fdt unit-address 'address'"
+	printf "\n\t-d ==> include Device Tree Blob 'dtb'"
+	printf "\n\t-r ==> include RootFS blob 'rootfs'"
+	printf "\n\t-H ==> specify hash algo instead of SHA1"
+	printf "\n\t-l ==> legacy mode character (@ etc otherwise -)"
+	printf "\n\t-o ==> create output file 'its_file'"
+	printf "\n\t-O ==> create config with dt overlay 'name:dtb'"
+	printf "\n\t-s ==> set FDT load address to 'addr' (hex)"
+	printf "\n\t\t(can be specified more than once)\n"
+	exit 1
+}
+
+REFERENCE_CHAR='-'
+FDTNUM=1
+ROOTFSNUM=1
+INITRDNUM=1
+HASH=sha1
+LOADABLES=
+DTOVERLAY=
+DTADDR=
+
+while getopts ":A:a:c:C:D:d:e:f:i:k:l:n:o:O:v:r:s:H:" OPTION
+do
+	case $OPTION in
+		A ) ARCH=$OPTARG;;
+		a ) LOAD_ADDR=$OPTARG;;
+		c ) CONFIG=$OPTARG;;
+		C ) COMPRESS=$OPTARG;;
+		D ) DEVICE=$OPTARG;;
+		d ) DTB=$OPTARG;;
+		e ) ENTRY_ADDR=$OPTARG;;
+		f ) COMPATIBLE=$OPTARG;;
+		i ) INITRD=$OPTARG;;
+		k ) KERNEL=$OPTARG;;
+		l ) REFERENCE_CHAR=$OPTARG;;
+		n ) FDTNUM=$OPTARG;;
+		o ) OUTPUT=$OPTARG;;
+		O ) DTOVERLAY="$DTOVERLAY ${OPTARG}";;
+		r ) ROOTFS=$OPTARG;;
+		s ) FDTADDR=$OPTARG;;
+		H ) HASH=$OPTARG;;
+		v ) VERSION=$OPTARG;;
+		* ) echo "Invalid option passed to '$0' (options:$*)"
+		usage;;
+	esac
+done
+
+# Make sure user entered all required parameters
+if [ -z "${ARCH}" ] || [ -z "${COMPRESS}" ] || [ -z "${LOAD_ADDR}" ] || \
+	[ -z "${ENTRY_ADDR}" ] || [ -z "${VERSION}" ] || [ -z "${KERNEL}" ] || \
+	[ -z "${OUTPUT}" ] || [ -z "${CONFIG}" ]; then
+	usage
+fi
+
+ARCH_UPPER=$(echo "$ARCH" | tr '[:lower:]' '[:upper:]')
+
+if [ -n "${COMPATIBLE}" ]; then
+	COMPATIBLE_PROP="compatible = \"${COMPATIBLE}\";"
+fi
+
+[ "$FDTADDR" ] && {
+	DTADDR="$FDTADDR"
+}
+
+# Conditionally create fdt information
+if [ -n "${DTB}" ]; then
+	FDT_NODE="
+		fdt${REFERENCE_CHAR}$FDTNUM {
+			description = \"${ARCH_UPPER} OpenWrt ${DEVICE} device tree blob\";
+			${COMPATIBLE_PROP}
+			data = /incbin/(\"${DTB}\");
+			type = \"flat_dt\";
+			${DTADDR:+load = <${DTADDR}>;}
+			arch = \"${ARCH}\";
+			compression = \"none\";
+			hash${REFERENCE_CHAR}1 {
+				algo = \"crc32\";
+			};
+			hash${REFERENCE_CHAR}2 {
+				algo = \"${HASH}\";
+			};
+		};
+"
+	FDT_PROP="fdt = \"fdt${REFERENCE_CHAR}$FDTNUM\";"
+fi
+
+if [ -n "${INITRD}" ]; then
+	INITRD_NODE="
+		initrd${REFERENCE_CHAR}$INITRDNUM {
+			description = \"${ARCH_UPPER} OpenWrt ${DEVICE} initrd\";
+			${COMPATIBLE_PROP}
+			data = /incbin/(\"${INITRD}\");
+			type = \"ramdisk\";
+			arch = \"${ARCH}\";
+			os = \"linux\";
+			hash${REFERENCE_CHAR}1 {
+				algo = \"crc32\";
+			};
+			hash${REFERENCE_CHAR}2 {
+				algo = \"${HASH}\";
+			};
+		};
+"
+	INITRD_PROP="ramdisk=\"initrd${REFERENCE_CHAR}${INITRDNUM}\";"
+fi
+
+
+if [ -n "${ROOTFS}" ]; then
+	dd if="${ROOTFS}" of="${ROOTFS}.pagesync" bs=4096 conv=sync
+	ROOTFS_NODE="
+		rootfs${REFERENCE_CHAR}$ROOTFSNUM {
+			description = \"${ARCH_UPPER} OpenWrt ${DEVICE} rootfs\";
+			${COMPATIBLE_PROP}
+			data = /incbin/(\"${ROOTFS}.pagesync\");
+			type = \"filesystem\";
+			arch = \"${ARCH}\";
+			compression = \"none\";
+			hash${REFERENCE_CHAR}1 {
+				algo = \"crc32\";
+			};
+			hash${REFERENCE_CHAR}2 {
+				algo = \"${HASH}\";
+			};
+		};
+"
+	LOADABLES="${LOADABLES:+$LOADABLES, }\"rootfs${REFERENCE_CHAR}${ROOTFSNUM}\""
+fi
+
+# add DT overlay blobs
+FDTOVERLAY_NODE=""
+OVCONFIGS=""
+[ "$DTOVERLAY" ] && for overlay in $DTOVERLAY ; do
+	overlay_blob=${overlay##*:}
+	ovname=${overlay%%:*}
+	ovnode="fdt-$ovname"
+	ovsize=$(wc -c "$overlay_blob" | awk '{print $1}')
+	echo "$ovname ($overlay_blob) : $ovsize" >&2
+	FDTOVERLAY_NODE="$FDTOVERLAY_NODE
+
+		$ovnode {
+			description = \"${ARCH_UPPER} OpenWrt ${DEVICE} device tree overlay $ovname\";
+			${COMPATIBLE_PROP}
+			data = /incbin/(\"${overlay_blob}\");
+			type = \"flat_dt\";
+			arch = \"${ARCH}\";
+			compression = \"none\";
+			hash${REFERENCE_CHAR}1 {
+				algo = \"crc32\";
+			};
+			hash${REFERENCE_CHAR}2 {
+				algo = \"${HASH}\";
+			};
+		};
+"
+	OVCONFIGS="$OVCONFIGS
+
+		$ovname {
+			description = \"OpenWrt ${DEVICE} overlay $ovname\";
+			fdt = \"$ovnode\";
+			${COMPATIBLE_PROP}
+		};
+	"
+done
+
+# Create a default, fully populated DTS file
+DATA="/dts-v1/;
+
+/ {
+	description = \"${ARCH_UPPER} OpenWrt FIT (Flattened Image Tree)\";
+	#address-cells = <1>;
+
+	images {
+		kernel${REFERENCE_CHAR}1 {
+			description = \"${ARCH_UPPER} OpenWrt Linux-${VERSION}\";
+			data = /incbin/(\"${KERNEL}\");
+			type = \"kernel\";
+			arch = \"${ARCH}\";
+			os = \"linux\";
+			compression = \"${COMPRESS}\";
+			load = <${LOAD_ADDR}>;
+			entry = <${ENTRY_ADDR}>;
+			hash${REFERENCE_CHAR}1 {
+				algo = \"crc32\";
+			};
+			hash${REFERENCE_CHAR}2 {
+				algo = \"$HASH\";
+			};
+		};
+${INITRD_NODE}
+${FDT_NODE}
+${FDTOVERLAY_NODE}
+${ROOTFS_NODE}
+	};
+
+	configurations {
+		default = \"${CONFIG}\";
+		${CONFIG} {
+			description = \"OpenWrt ${DEVICE}\";
+			kernel = \"kernel${REFERENCE_CHAR}1\";
+			${FDT_PROP}
+			${LOADABLES:+loadables = ${LOADABLES};}
+			${COMPATIBLE_PROP}
+			${INITRD_PROP}
+		};
+		${OVCONFIGS}
+	};
+};"
+
+# Write .its file to disk
+echo "$DATA" > "${OUTPUT}"
diff --git a/scripts/moxa-encode-fw.py b/scripts/moxa-encode-fw.py
new file mode 100755
index 0000000..48d139b
--- /dev/null
+++ b/scripts/moxa-encode-fw.py
@@ -0,0 +1,109 @@
+#! /usr/bin/env python3
+# SPDX-License-Identifier: GPL-2.0-or-later
+
+import argparse
+import struct
+
+from binascii import crc32
+from dataclasses import dataclass
+from itertools import cycle
+from typing import List
+
+
+def xor(data: bytes) -> bytes:
+    passphrase = "Seek AGREEMENT for the date of completion.\0"
+    pw = cycle(bytearray(passphrase.encode('ascii')))
+    return bytearray(b ^ next(pw) for b in data)
+
+
+def add_fw_header(data: bytes, magic: int, hwid: int, build_id: int,
+                  offsets: List[int]) -> bytes:
+    unknown_1 = 0x01
+    unknown_2 = 0x0000
+    unknown_3 = 0x00000000
+    unknown_4 = 0x01000000
+    file_crc = crc(data, 0)
+
+    header_struct = struct.Struct('>QIBBHIIIIII' + 'I' * len(offsets))
+    header_size = header_struct.size
+    file_size = header_size + len(data)
+
+    header_offsets = map(lambda x: x + header_size, offsets)
+
+    header_data = header_struct.pack(magic, file_size, unknown_1, len(offsets),
+                                     unknown_2, hwid, build_id, unknown_3,
+                                     build_id, unknown_4, *header_offsets,
+                                     file_crc)
+    return header_data + data
+
+
+def add_file_header(data: bytes, filename: str, build_id: int) -> bytes:
+    unknown1 = 0x01000000
+    unknown2 = 0x00000000
+    file_crc = crc(data, 0)
+
+    header_struct = struct.Struct(">16sIIIII")
+    file_size = header_struct.size + len(data)
+
+    header_data = header_struct.pack(filename.encode('ascii'), file_size,
+                                     unknown1, build_id, unknown2, file_crc)
+    return header_data + data
+
+
+def crc(data: bytes, init_val: int) -> int:
+    return 0xffffffff ^ (crc32(data, 0xffffffff ^ init_val))
+
+
+@dataclass
+class Partition:
+    name: str
+    size: int
+
+
+def main():
+    partitions = [
+        Partition(name='kernel', size=2048 * 1024),
+        Partition(name='root', size=9216 * 1024),
+        Partition(name='userdisk', size=3076 * 1024),
+    ]
+
+    parser = argparse.ArgumentParser(prog='moxa-encode-fw',
+                                     description='MOXA IW firmware encoder')
+    parser.add_argument('-i', '--input', required=True, type=str, help='Firmware file')
+    parser.add_argument('-o', '--output', required=True, type=str, help="Output path for encoded firmware file")
+    parser.add_argument('-m', '--magic', required=True, type=lambda x: int(x,0), help="Magic for firmware header")
+    parser.add_argument('-d', '--hwid', required=True, type=lambda x: int(x,0), help="Hardware id of device")
+    parser.add_argument('-b', '--buildid', required=True, type=lambda x: int(x,0), help="Build id of firmware")
+    args = parser.parse_args()
+
+    with open(args.input, 'rb') as input_file:
+        firmware = bytearray(input_file.read())
+
+    offsets = []
+    pos_input = 0
+    pos_output = 0
+    firmware_seg = bytearray()
+
+    for partition in partitions:
+        part_data = firmware[pos_input:pos_input + partition.size]
+
+        # just to make sure that no partition is empty
+        if len(part_data) == 0:
+            part_data = bytearray([0x00])
+
+        header = add_file_header(part_data, partition.name, args.buildid)
+        firmware_seg += header
+
+        offsets.append(pos_output)
+        pos_input += partition.size
+        pos_output += len(header)
+
+    moxa_firmware = add_fw_header(firmware_seg, args.magic, args.hwid, args.buildid, offsets)
+
+    encrypted = xor(moxa_firmware)
+    with open(args.output, 'wb') as output_file:
+        output_file.write(encrypted)
+
+
+if __name__ == '__main__':
+    main()
diff --git a/scripts/multixz.sh b/scripts/multixz.sh
new file mode 100755
index 0000000..4646f85
--- /dev/null
+++ b/scripts/multixz.sh
@@ -0,0 +1,44 @@
+#!/bin/sh
+if [ -z "$1" -o -z "$2" ]; then
+    echo "Usage: $0 [-d] file_name block_size [dict_size] [preset] [filter] [lc] [lp] [pb] [nice]"
+    echo "The output is file_name.mxz if -d is not specified, or filename.dec if -d is specified"
+    echo "Compression algorithm: LZMA2; Check algorithm: CRC32; Filter: empty string or arm"
+else
+    set -e
+    if [ "$1" != "-d" ]; then
+	FNAME="$1"
+	split -b "$2" "$FNAME" "$FNAME.part"
+	FILTER=
+	OPT=
+	if [ -n "$4" ]; then
+	    OPT="$OPT,preset=$4"
+	fi
+	if [ -n "$3" ]; then
+	    OPT="$OPT,dict=$3"
+	fi
+	if [ -n "$5" ]; then
+	    FILTER=--$5
+	fi
+	if [ -n "$6" ]; then
+	    OPT="$OPT,lc=$6"
+	fi
+	if [ -n "$7" ]; then
+	    OPT="$OPT,lp=$7"
+	fi
+	if [ -n "$8" ]; then
+	    OPT="$OPT,pb=$8"
+	fi
+	if [ -n "$9" ]; then
+	    OPT="$OPT,nice=$9"
+	fi
+	(set -x; xz --check=crc32 $FILTER --lzma2=$OPT -vfk "$FNAME.part"?? || \
+	    xz.exe --check=crc32 $FILTER --lzma2=$OPT -vfk "$FNAME.part"??)
+	cat "$FNAME.part"??.xz > "$FNAME.mxz"
+	rm -f "$FNAME.part"*
+	du -sh "$FNAME" "$FNAME.mxz"
+    else
+	FNAME="$2"
+	(xz -dc "$FNAME" || xz.exe -dc "$FNAME") > "$FNAME.dec"
+	du -sh "$FNAME" "$FNAME.dec"
+    fi
+fi
diff --git a/scripts/netgear-encrypted-factory.py b/scripts/netgear-encrypted-factory.py
new file mode 100755
index 0000000..40cfd9d
--- /dev/null
+++ b/scripts/netgear-encrypted-factory.py
@@ -0,0 +1,79 @@
+#!/usr/bin/env python3
+
+import argparse
+import re
+import struct
+import subprocess
+import zlib
+
+
+def main():
+    parser = argparse.ArgumentParser()
+    parser.add_argument('--input-file', type=str, required=True)
+    parser.add_argument('--output-file', type=str, required=True)
+    parser.add_argument('--model', type=str, required=True)
+    parser.add_argument('--region', type=str, required=True)
+    parser.add_argument('--version', type=str, required=True)
+    parser.add_argument('--hw-id-list', type=str)
+    parser.add_argument('--model-list', type=str)
+    parser.add_argument('--encryption-block-size', type=str, required=True)
+    parser.add_argument('--openssl-bin', type=str, required=True)
+    parser.add_argument('--key', type=str, required=True)
+    parser.add_argument('--iv', type=str, required=True)
+    args = parser.parse_args()
+
+    assert re.match(r'V[0-9]\.[0-9]\.[0-9]\.[0-9]',
+                    args.version), 'Version must start with Vx.x.x.x'
+    encryption_block_size = int(args.encryption_block_size, 0)
+    assert (encryption_block_size > 0 and encryption_block_size % 16 ==
+            0), 'Encryption block size must be a multiple of the AES block size (16)'
+
+    hw_id_list = args.hw_id_list.split(';') if args.hw_id_list else []
+    model_list = args.model_list.split(';') if args.model_list else []
+    hw_info = ';'.join(hw_id_list + model_list)
+
+    image = open(args.input_file, 'rb').read()
+    image_enc = []
+    for i in range(0, len(image), encryption_block_size):
+        chunk = image[i:i + encryption_block_size]
+        chunk += b'\x00' * ((-len(chunk)) % 16)  # pad to AES block size (16)
+        res = subprocess.run([
+            args.openssl_bin,
+            'enc',
+            '-aes-256-cbc',
+            '-nosalt',
+            '-nopad',
+            '-K', args.key,
+            '-iv', args.iv
+        ],
+            check=True, input=chunk, stdout=subprocess.PIPE)
+        image_enc.append(res.stdout)
+    image_enc = b''.join(image_enc)
+
+    image_with_header = struct.pack(
+        '>32s32s64s64sIBBB13s200s100s12sII',
+        args.model.encode('ascii'),
+        args.region.encode('ascii'),
+        args.version.encode('ascii'),
+        b'Thu Jan 1 00:00:00 1970',  # static date for reproducibility
+        0,  # product hw model
+        0,  # model index
+        len(hw_id_list),
+        len(model_list),
+        b'',  # reserved
+        hw_info.encode('ascii'),
+        b'',  # reserved
+        b'encrpted_img',
+        len(image_enc),
+        encryption_block_size,
+    ) + image_enc
+
+    checksum = zlib.crc32(image_with_header, 0xffffffff) ^ 0xffffffff
+
+    with open(args.output_file, 'wb') as outfile:
+        outfile.write(image_with_header)
+        outfile.write(struct.pack('>I', checksum))
+
+
+if __name__ == "__main__":
+    main()
diff --git a/scripts/noop.sh b/scripts/noop.sh
new file mode 100755
index 0000000..96b71fe
--- /dev/null
+++ b/scripts/noop.sh
@@ -0,0 +1,2 @@
+#!/bin/sh
+# This script does nothing, intentionally.
diff --git a/scripts/om-fwupgradecfg-gen.sh b/scripts/om-fwupgradecfg-gen.sh
new file mode 100755
index 0000000..c385841
--- /dev/null
+++ b/scripts/om-fwupgradecfg-gen.sh
@@ -0,0 +1,93 @@
+#!/bin/sh
+#
+# Copyright (C) 2011 OpenWrt.org
+#
+# This is free software, licensed under the GNU General Public License v2.
+# See /LICENSE for more information.
+#
+
+usage() {
+	echo "Usage: $0 <OM2P|OM5P|OM5PAC|MR600|MR900|MR1750|A60|A42|A62|PA300|PA1200|PA2200> <out file path> <kernel path> <rootfs path>"
+	rm -f $CFG_OUT
+	exit 1
+}
+
+[ "$#" -lt 4 ] && usage
+
+CE_TYPE=$1
+CFG_OUT=$2
+KERNEL_PATH=$3
+ROOTFS_PATH=$4
+
+case $CE_TYPE in
+	PA300|\
+	OM2P)
+		MAX_PART_SIZE=7168
+		KERNEL_FLASH_ADDR=0x1c0000
+		SIZE_FACTOR=1
+		SIZE_FORMAT="%d"
+		;;
+	OM5P|OM5PAC|MR600|MR900|MR1750|A60)
+		MAX_PART_SIZE=7808
+		KERNEL_FLASH_ADDR=0xb0000
+		SIZE_FACTOR=1
+		SIZE_FORMAT="%d"
+		;;
+	A42|PA1200)
+		MAX_PART_SIZE=15616
+		KERNEL_FLASH_ADDR=0x180000
+		SIZE_FACTOR=1024
+		SIZE_FORMAT="0x%08x"
+		;;
+	A62|PA2200)
+		MAX_PART_SIZE=15552
+		KERNEL_FLASH_ADDR=0x1a0000
+		SIZE_FACTOR=1024
+		SIZE_FORMAT="0x%08x"
+		;;
+	*)
+		echo "Error - unsupported ce type: $CE_TYPE"
+		exit 1
+		;;
+esac
+
+CHECK_BS=65536
+
+KERNEL_SIZE=$(stat -c%s "$KERNEL_PATH")
+KERNEL_MD5=$($MKHASH md5 $KERNEL_PATH)
+KERNEL_SHA256=$($MKHASH sha256 $KERNEL_PATH)
+KERNEL_PART_SIZE_KB=$((KERNEL_SIZE / 1024))
+KERNEL_PART_SIZE=$(printf $SIZE_FORMAT $(($KERNEL_PART_SIZE_KB * $SIZE_FACTOR)))
+
+ROOTFS_FLASH_ADDR=$(addr=$(($KERNEL_FLASH_ADDR + ($KERNEL_PART_SIZE_KB * 1024))); printf "0x%x" $addr)
+ROOTFS_SIZE=$(stat -c%s "$ROOTFS_PATH")
+ROOTFS_SQUASHFS_SIZE=$((ROOTFS_SIZE-4))
+ROOTFS_CHECK_BLOCKS=$((ROOTFS_SQUASHFS_SIZE / CHECK_BS))
+ROOTFS_MD5=$(dd if=$ROOTFS_PATH bs=$CHECK_BS count=$ROOTFS_CHECK_BLOCKS 2>&- | $MKHASH md5)
+ROOTFS_MD5_FULL=$($MKHASH md5 $ROOTFS_PATH)
+ROOTFS_SHA256_FULL=$($MKHASH sha256 $ROOTFS_PATH)
+ROOTFS_CHECK_SIZE=$(printf '0x%x' $ROOTFS_SQUASHFS_SIZE)
+ROOTFS_PART_SIZE_KB=$(($MAX_PART_SIZE - $KERNEL_PART_SIZE_KB))
+ROOTFS_PART_SIZE=$(printf $SIZE_FORMAT $(($ROOTFS_PART_SIZE_KB * $SIZE_FACTOR)))
+
+cat << EOF > $CFG_OUT
+[vmlinux]
+filename=kernel
+md5sum=$KERNEL_MD5
+filemd5sum=$KERNEL_MD5
+filesha256sum=$KERNEL_SHA256
+flashaddr=$KERNEL_FLASH_ADDR
+checksize=0x0
+cmd_success=setenv bootseq 1,2; setenv kernel_size_1 $KERNEL_PART_SIZE; saveenv
+cmd_fail=reset
+
+[rootfs]
+filename=rootfs
+md5sum=$ROOTFS_MD5
+filemd5sum=$ROOTFS_MD5_FULL
+filesha256sum=$ROOTFS_SHA256_FULL
+flashaddr=$ROOTFS_FLASH_ADDR
+checksize=$ROOTFS_CHECK_SIZE
+cmd_success=setenv bootseq 1,2; setenv kernel_size_1 $KERNEL_PART_SIZE; setenv rootfs_size_1 $ROOTFS_PART_SIZE; saveenv
+cmd_fail=reset
+EOF
diff --git a/scripts/package-metadata.pl b/scripts/package-metadata.pl
new file mode 100755
index 0000000..82bd436
--- /dev/null
+++ b/scripts/package-metadata.pl
@@ -0,0 +1,833 @@
+#!/usr/bin/env perl
+use FindBin;
+use lib "$FindBin::Bin";
+use strict;
+use metadata;
+use Getopt::Long;
+use Time::Piece;
+use JSON::PP;
+
+my %board;
+
+sub version_to_num($) {
+	my $str = shift;
+	my $num = 0;
+
+	if (defined($str) && $str =~ /^\d+(?:\.\d+)+$/)
+	{
+		my @n = (split(/\./, $str), 0, 0, 0, 0);
+		$num = ($n[0] << 24) | ($n[1] << 16) | ($n[2] << 8) | $n[3];
+	}
+
+	return $num;
+}
+
+sub version_filter_list(@) {
+	my $cmpver = version_to_num(shift @_);
+	my @items;
+
+	foreach my $item (@_)
+	{
+		if ($item =~ s/@(lt|le|gt|ge|eq|ne)(\d+(?:\.\d+)+)\b//)
+		{
+			my $op = $1;
+			my $symver = version_to_num($2);
+
+			if ($symver > 0 && $cmpver > 0)
+			{
+				next unless (($op eq 'lt' && $cmpver <  $symver) ||
+				             ($op eq 'le' && $cmpver <= $symver) ||
+				             ($op eq 'gt' && $cmpver >  $symver) ||
+				             ($op eq 'ge' && $cmpver >= $symver) ||
+				             ($op eq 'eq' && $cmpver == $symver) ||
+				             ($op eq 'ne' && $cmpver != $symver));
+			}
+		}
+
+		push @items, $item;
+	}
+
+	return @items;
+}
+
+sub gen_kconfig_overrides() {
+	my %config;
+	my %kconfig;
+	my $package;
+	my $pkginfo = shift @ARGV;
+	my $cfgfile = shift @ARGV;
+	my $patchver = shift @ARGV;
+
+	# parameter 2: build system config
+	open FILE, "<$cfgfile" or return;
+	while (<FILE>) {
+		/^(CONFIG_.+?)=(.+)$/ and $config{$1} = 1;
+	}
+	close FILE;
+
+	# parameter 1: package metadata
+	open FILE, "<$pkginfo" or return;
+	while (<FILE>) {
+		/^Package:\s*(.+?)\s*$/ and $package = $1;
+		/^Kernel-Config:\s*(.+?)\s*$/ and do {
+			my @config = split /\s+/, $1;
+			foreach my $config (version_filter_list($patchver, @config)) {
+				my $val = 'm';
+				my $override;
+				if ($config =~ /^(.+?)=(.+)$/) {
+					$config = $1;
+					$override = 1;
+					$val = $2;
+				}
+				if ($config{"CONFIG_PACKAGE_$package"} and ($config ne 'n')) {
+					next if $kconfig{$config} eq 'y';
+					$kconfig{$config} = $val;
+				} elsif (!$override) {
+					$kconfig{$config} or $kconfig{$config} = 'n';
+				}
+			}
+		};
+	};
+	close FILE;
+
+	foreach my $kconfig (sort keys %kconfig) {
+		if ($kconfig{$kconfig} eq 'n') {
+			print "# $kconfig is not set\n";
+		} else {
+			print "$kconfig=$kconfig{$kconfig}\n";
+		}
+	}
+}
+
+my %dep_check;
+sub __find_package_dep($$) {
+	my $pkg = shift;
+	my $name = shift;
+	my $deps = $pkg->{depends};
+
+	return 0 unless defined $deps;
+	foreach my $vpkg (@{$deps}) {
+		foreach my $dep (@{$vpackage{$vpkg}}) {
+			next if $dep_check{$dep->{name}};
+			$dep_check{$dep->{name}} = 1;
+			return 1 if $dep->{name} eq $name;
+			return 1 if (__find_package_dep($dep, $name) == 1);
+		}
+	}
+	return 0;
+}
+
+# wrapper to avoid infinite recursion
+sub find_package_dep($$) {
+	my $pkg = shift;
+	my $name = shift;
+
+	%dep_check = ();
+	return __find_package_dep($pkg, $name);
+}
+
+sub package_depends($$) {
+	my $a = shift;
+	my $b = shift;
+	my $ret;
+
+	return 0 if ($a->{submenu} ne $b->{submenu});
+	if (find_package_dep($a, $b->{name}) == 1) {
+		$ret = 1;
+	} elsif (find_package_dep($b, $a->{name}) == 1) {
+		$ret = -1;
+	} else {
+		return 0;
+	}
+	return $ret;
+}
+
+sub mconf_depends {
+	my $pkgname = shift;
+	my $depends = shift;
+	my $only_dep = shift;
+	my $res;
+	my $dep = shift;
+	my $seen = shift;
+	my $parent_condition = shift;
+	$dep or $dep = {};
+	$seen or $seen = {};
+	my @t_depends;
+
+	$depends or return;
+	my @depends = @$depends;
+	foreach my $depend (@depends) {
+		my $m = "depends on";
+		my $flags = "";
+		$depend =~ s/^([@\+]+)// and $flags = $1;
+		my $condition = $parent_condition;
+
+		$depend = $2 if	$depend =~ /^(.+):(.+)$/ and $dep->{$1} eq 'select';
+
+		next if $condition eq $depend;
+		next if $seen->{"$parent_condition:$depend"};
+		next if $seen->{":$depend"};
+		$seen->{"$parent_condition:$depend"} = 1;
+		if ($depend =~ /^(.+):(.+)$/) {
+			if ($1 ne "PACKAGE_$pkgname") {
+				if ($condition) {
+					$condition = "$condition && $1";
+				} else {
+					$condition = $1;
+				}
+			}
+			$depend = $2;
+		}
+		if ($flags =~ /\+/) {
+			my $vdep = $vpackage{$depend};
+			if ($vdep) {
+				my @vdeps;
+
+				foreach my $v (@$vdep) {
+					next if $v->{buildonly};
+					if ($v->{variant_default}) {
+						unshift @vdeps, $v->{name};
+					} else {
+						push @vdeps, $v->{name};
+					}
+				}
+
+				$depend = shift @vdeps;
+
+				if (@vdeps > 1) {
+					$condition = ($condition ? "$condition && " : '') . join("&&", map { "PACKAGE_$_<PACKAGE_$pkgname" } @vdeps);
+				} elsif (@vdeps > 0) {
+					$condition = ($condition ? "$condition && " : '') . "PACKAGE_${vdeps[0]}<PACKAGE_$pkgname";
+				}
+			}
+
+			# Menuconfig will not treat 'select FOO' as a real dependency
+			# thus if FOO depends on other config options, these dependencies
+			# will not be checked. To fix this, we simply emit all of FOO's
+			# depends here as well.
+			$package{$depend} and push @t_depends, [ $package{$depend}->{depends}, $condition ];
+
+			$m = "select";
+			next if $only_dep;
+
+			$flags =~ /@/ or $depend = "PACKAGE_$depend";
+		} else {
+			my $vdep = $vpackage{$depend};
+			if ($vdep && @$vdep > 0) {
+				$depend = join("||", map { "PACKAGE_".$_->{name} } @$vdep);
+			} else {
+				$flags =~ /@/ or $depend = "PACKAGE_$depend";
+			}
+		}
+
+		if ($condition) {
+			if ($m =~ /select/) {
+				next if $depend eq $condition;
+				$depend = "$depend if $condition";
+			} else {
+				next if $dep->{"$depend if $condition"};
+				$depend = "!($condition) || $depend" unless $dep->{$condition} eq 'select';
+			}
+		}
+		$dep->{$depend} =~ /select/ or $dep->{$depend} = $m;
+	}
+
+	foreach my $tdep (@t_depends) {
+		mconf_depends($pkgname, $tdep->[0], 1, $dep, $seen, $tdep->[1]);
+	}
+
+	foreach my $depend (sort keys %$dep) {
+		my $m = $dep->{$depend};
+		$res .= "\t\t$m $depend\n";
+	}
+	return $res;
+}
+
+sub mconf_conflicts {
+	my $pkgname = shift;
+	my $depends = shift;
+	my $res = "";
+
+	foreach my $depend (@$depends) {
+		next unless $package{$depend};
+		$res .= "\t\tdepends on m || (PACKAGE_$depend != y)\n";
+	}
+	return $res;
+}
+
+sub print_package_config_category($) {
+	my $cat = shift;
+	my %menus;
+	my %menu_dep;
+
+	return unless $category{$cat};
+
+	print "menu \"$cat\"\n\n";
+	my %spkg = %{$category{$cat}};
+
+	foreach my $spkg (sort {uc($a) cmp uc($b)} keys %spkg) {
+		foreach my $pkg (@{$spkg{$spkg}}) {
+			next if $pkg->{buildonly};
+			my $menu = $pkg->{submenu};
+			if ($menu) {
+				$menu_dep{$menu} or $menu_dep{$menu} = $pkg->{submenudep};
+			} else {
+				$menu = 'undef';
+			}
+			$menus{$menu} or $menus{$menu} = [];
+			push @{$menus{$menu}}, $pkg;
+		}
+	}
+	my @menus = sort {
+		($a eq 'undef' ?  1 : 0) or
+		($b eq 'undef' ? -1 : 0) or
+		($a cmp $b)
+	} keys %menus;
+
+	foreach my $menu (@menus) {
+		my @pkgs = sort {
+			package_depends($a, $b) or
+			($a->{name} cmp $b->{name})
+		} @{$menus{$menu}};
+		if ($menu ne 'undef') {
+			$menu_dep{$menu} and print "if $menu_dep{$menu}\n";
+			print "menu \"$menu\"\n";
+		}
+		foreach my $pkg (@pkgs) {
+			next if $pkg->{src}{ignore};
+			my $title = $pkg->{name};
+			my $c = (72 - length($pkg->{name}) - length($pkg->{title}));
+			if ($c > 0) {
+				$title .= ("." x $c). " ". $pkg->{title};
+			}
+			$title = "\"$title\"";
+			print "\t";
+			$pkg->{menu} and print "menu";
+			print "config PACKAGE_".$pkg->{name}."\n";
+			$pkg->{hidden} and $title = "";
+			print "\t\t".($pkg->{tristate} ? 'tristate' : 'bool')." $title\n";
+			print "\t\tdefault y if DEFAULT_".$pkg->{name}."\n";
+			unless ($pkg->{hidden}) {
+				my @def = ("ALL");
+				if (!exists($pkg->{repository})) {
+					push @def, "ALL_NONSHARED";
+				}
+				if ($pkg->{name} =~ /^kmod-/) {
+					push @def, "ALL_KMODS";
+				}
+				$pkg->{default} ||= "m if " . join("||", @def);
+			}
+			if ($pkg->{default}) {
+				foreach my $default (split /\s*,\s*/, $pkg->{default}) {
+					print "\t\tdefault $default\n";
+				}
+			}
+			print mconf_depends($pkg->{name}, $pkg->{depends}, 0);
+			print mconf_depends($pkg->{name}, $pkg->{mdepends}, 0);
+			print mconf_conflicts($pkg->{name}, $pkg->{conflicts});
+			print "\t\thelp\n";
+			print $pkg->{description};
+			print "\n";
+
+			$pkg->{config} and print $pkg->{config}."\n";
+		}
+		if ($menu ne 'undef') {
+			print "endmenu\n";
+			$menu_dep{$menu} and print "endif\n";
+		}
+	}
+	print "endmenu\n\n";
+
+	undef $category{$cat};
+}
+
+sub print_package_overrides() {
+	keys %overrides > 0 or return;
+	print "\tconfig OVERRIDE_PKGS\n";
+	print "\t\tstring\n";
+	print "\t\tdefault \"".join(" ", sort keys %overrides)."\"\n\n";
+}
+
+sub gen_package_config() {
+	parse_package_metadata($ARGV[0]) or exit 1;
+	print "menuconfig IMAGEOPT\n\tbool \"Image configuration\"\n\tdefault n\n";
+	print "source \"package/*/image-config.in\"\n";
+	if (scalar glob "package/feeds/*/*/image-config.in") {
+	    print "source \"package/feeds/*/*/image-config.in\"\n";
+	}
+	print_package_config_category 'Base system';
+	foreach my $cat (sort {uc($a) cmp uc($b)} keys %category) {
+		print_package_config_category $cat;
+	}
+	print_package_overrides();
+}
+
+sub and_condition($) {
+	my $condition = shift;
+	my @spl_and = split('\&\&', $condition);
+	if (@spl_and == 1) {
+		return "\$(CONFIG_$spl_and[0])";
+	}
+	return "\$(and " . join (',', map("\$(CONFIG_$_)", @spl_and)) . ")";
+}
+
+sub gen_condition ($) {
+	my $condition = shift;
+	# remove '!()', just as include/package-pack.mk does
+	$condition =~ s/[()!]//g;
+	return join("", map(and_condition($_), split('\|\|', $condition)));
+}
+
+sub get_conditional_dep($$) {
+	my $condition = shift;
+	my $depstr = shift;
+	if ($condition) {
+		if ($condition =~ /^!(.+)/) {
+			return "\$(if " . gen_condition($1) . ",,$depstr)";
+		} else {
+			return "\$(if " . gen_condition($condition) . ",$depstr)";
+		}
+	} else {
+		return $depstr;
+	}
+}
+
+sub gen_package_mk() {
+	my $line;
+
+	parse_package_metadata($ARGV[0]) or exit 1;
+	foreach my $srcname (sort {uc($a) cmp uc($b)} keys %srcpackage) {
+		my $src = $srcpackage{$srcname};
+		my $variant_default;
+		my %deplines = ('' => {});
+
+		foreach my $pkg (@{$src->{packages}}) {
+			foreach my $dep (@{$pkg->{depends}}) {
+				next if ($dep =~ /@/);
+
+				my $condition;
+
+				$dep =~ s/\+//g;
+				if ($dep =~ /^(.+):(.+)/) {
+					$condition = $1;
+					$dep = $2;
+				}
+
+				my $vpkg_dep = $vpackage{$dep};
+				unless (defined $vpkg_dep) {
+					warn sprintf "WARNING: Makefile '%s' has a dependency on '%s', which does not exist\n",
+						$src->{makefile}, $dep;
+					next;
+				}
+
+				# Filter out self-depends
+				my @vdeps = grep { $srcname ne $_->{src}{name} } @{$vpkg_dep};
+
+				foreach my $vdep (@vdeps) {
+					my $depstr = sprintf '$(curdir)/%s/compile', $vdep->{src}{path};
+					if (@vdeps > 1) {
+						$depstr = sprintf '$(if $(CONFIG_PACKAGE_%s),%s)', $vdep->{name}, $depstr;
+					}
+					my $depline = get_conditional_dep($condition, $depstr);
+					if ($depline) {
+						$deplines{''}{$depline}++;
+					}
+				}
+			}
+
+			my $config = '';
+			$config = sprintf '$(CONFIG_PACKAGE_%s)', $pkg->{name} unless $pkg->{buildonly};
+
+			$pkg->{prereq} and printf "prereq-%s += %s\n", $config, $src->{path};
+
+			next if $pkg->{buildonly};
+
+			printf "package-%s += %s\n", $config, $src->{path};
+
+			if ($pkg->{variant}) {
+				if (!defined($variant_default) or $pkg->{variant_default}) {
+					$variant_default = $pkg->{variant};
+				}
+				printf "\$(curdir)/%s/variants += \$(if %s,%s)\n", $src->{path}, $config, $pkg->{variant};
+			}
+		}
+
+		if (defined($variant_default)) {
+			printf "\$(curdir)/%s/default-variant := %s\n", $src->{path}, $variant_default;
+		}
+
+		unless (grep {!$_->{buildonly}} @{$src->{packages}}) {
+			printf "package- += %s\n", $src->{path};
+		}
+
+		if (@{$src->{buildtypes}} > 0) {
+			printf "buildtypes-%s = %s\n", $src->{path}, join(' ', @{$src->{buildtypes}});
+		}
+
+		foreach my $type ('', @{$src->{buildtypes}}) {
+			my $suffix = '';
+
+			$suffix = "/$type" if $type;
+
+			next unless $src->{"builddepends$suffix"};
+
+			defined $deplines{$suffix} or $deplines{$suffix} = {};
+
+			foreach my $dep (@{$src->{"builddepends$suffix"}}) {
+				my $depsuffix = "";
+				my $deptype = "";
+				my $condition;
+
+				if ($dep =~ /^(.+):(.+)/) {
+					$condition = $1;
+					$dep = $2;
+				}
+				if ($dep =~ /^(.+)\/(.+)/) {
+					$dep = $1;
+					$deptype = $2;
+					$depsuffix = "/$2";
+				}
+
+				next if $srcname.$suffix eq $dep.$depsuffix;
+
+				my $src_dep = $srcpackage{$dep};
+				unless (defined($src_dep) && (!$deptype || grep { $_ eq $deptype } @{$src_dep->{buildtypes}})) {
+					warn sprintf "WARNING: Makefile '%s' has a build dependency on '%s', which does not exist\n",
+						$src->{makefile}, $dep.$depsuffix;
+					next;
+				}
+
+				my $depstr = sprintf '$(curdir)/%s/compile', $src_dep->{path}.$depsuffix;
+				my $depline = get_conditional_dep($condition, $depstr);
+				if ($depline) {
+					$deplines{$suffix}{$depline}++;
+				}
+			}
+		}
+
+		foreach my $suffix (sort keys %deplines) {
+			my $depline = join(" ", sort keys %{$deplines{$suffix}});
+			if ($depline) {
+				$line .= sprintf "\$(curdir)/%s/compile += %s\n", $src->{path}.$suffix, $depline;
+			}
+		}
+	}
+
+	if ($line ne "") {
+		print "\n$line";
+	}
+}
+
+sub gen_package_source() {
+	parse_package_metadata($ARGV[0]) or exit 1;
+	foreach my $name (sort {uc($a) cmp uc($b)} keys %package) {
+		my $pkg = $package{$name};
+		if ($pkg->{name} && $pkg->{source}) {
+			print "$pkg->{name}: ";
+			print "$pkg->{source}\n";
+		}
+	}
+}
+
+sub gen_package_auxiliary() {
+	parse_package_metadata($ARGV[0]) or exit 1;
+	foreach my $name (sort {uc($a) cmp uc($b)} keys %package) {
+		my $pkg = $package{$name};
+		if ($pkg->{name} && $pkg->{repository}) {
+			print "Package/$name/subdir = $pkg->{repository}\n";
+		}
+		my %depends;
+		foreach my $dep (@{$pkg->{depends} || []}) {
+			if ($dep =~ m!^\+?(?:[^:]+:)?([^@]+)$!) {
+				$depends{$1}++;
+			}
+		}
+		my @depends = sort keys %depends;
+		if (@depends > 0) {
+			foreach my $n (@{$pkg->{provides}}) {
+				print "Package/$n/depends = @depends\n";
+			}
+		}
+	}
+}
+
+sub gen_package_license($) {
+	my $level = shift;
+	parse_package_metadata($ARGV[0]) or exit 1;
+	foreach my $name (sort {uc($a) cmp uc($b)} keys %package) {
+		my $pkg = $package{$name};
+		if ($pkg->{name}) {
+			if ($pkg->{license}) {
+				print "$pkg->{name}: ";
+				print "$pkg->{license}\n";
+				if ($pkg->{licensefiles} && $level == 0) {
+					print "\tFiles: $pkg->{licensefiles}\n";
+				}
+			} else {
+				if ($level == 1) {
+					print "$pkg->{name}: Missing license! ";
+					print "Please fix $pkg->{src}{makefile}\n";
+				}
+			}
+		}
+	}
+}
+
+sub gen_version_filtered_list() {
+	foreach my $item (version_filter_list(@ARGV)) {
+		print "$item\n";
+	}
+}
+
+sub gen_usergroup_list() {
+	parse_package_metadata($ARGV[0]) or exit 1;
+	for my $name (keys %usernames) {
+		print "user $name $usernames{$name}{id} $usernames{$name}{makefile}\n";
+	}
+	for my $name (keys %groupnames) {
+		print "group $name $groupnames{$name}{id} $groupnames{$name}{makefile}\n";
+	}
+}
+
+sub gen_package_manifest_json() {
+	my $json;
+	parse_package_metadata($ARGV[0]) or exit 1;
+	foreach my $name (sort {uc($a) cmp uc($b)} keys %package) {
+		my %depends;
+		my $pkg = $package{$name};
+		foreach my $dep (@{$pkg->{depends} || []}) {
+			if ($dep =~ m!^\+?(?:[^:]+:)?([^@]+)$!) {
+				$depends{$1}++;
+			}
+		}
+		my @depends = sort keys %depends;
+		my $pkg_deps = join ' ', map { qq/"$_",/ } @depends;
+		$pkg_deps =~ s/\,$//;
+
+		my $pkg_maintainer = join ' ', map { qq/"$_",/ } @{$pkg->{maintainer} || []};
+		$pkg_maintainer =~ s/\,$//;
+
+		$json = <<"END_JSON";
+${json}{
+"name":"$name",
+"version":"$pkg->{version}",
+"category":"$pkg->{category}",
+"license":"$pkg->{license}",
+"cpe_id":"$pkg->{cpe_id}",
+"maintainer": [$pkg_maintainer],
+"depends":[$pkg_deps]},
+END_JSON
+	}
+
+	$json =~ s/[\n\r]//g;
+	$json =~ s/\,$//;
+	print "[$json]";
+}
+
+sub image_manifest_packages($)
+{
+	my %packages;
+	my $imgmanifest = shift;
+
+	open FILE, "<$imgmanifest" or return;
+	while (<FILE>) {
+		/^(.+?) - (.+)$/ and $packages{$1} = $2;
+	}
+	close FILE;
+
+	return %packages;
+}
+
+sub dump_cyclonedxsbom_json {
+	my (@components) = @_;
+
+	my $uuid = sprintf(
+	    "%04x%04x-%04x-%04x-%04x-%04x%04x%04x",
+	    rand(0xffff), rand(0xffff), rand(0xffff),
+	    rand(0x0fff) | 0x4000,
+	    rand(0x3fff) | 0x8000,
+	    rand(0xffff), rand(0xffff), rand(0xffff)
+	);
+
+	my $cyclonedx = {
+		bomFormat => "CycloneDX",
+		specVersion => "1.4",
+		serialNumber => "urn:uuid:$uuid",
+		version => 1,
+		metadata => {
+			timestamp => gmtime->datetime . 'Z',
+		},
+		"components" => [@components],
+	};
+
+	return encode_json($cyclonedx);
+}
+
+sub gen_image_cyclonedxsbom() {
+	my $pkginfo = shift @ARGV;
+	my $imgmanifest = shift @ARGV;
+	my @components;
+	my %image_packages;
+
+	%image_packages = image_manifest_packages($imgmanifest);
+	%image_packages or exit 1;
+	parse_package_metadata($pkginfo) or exit 1;
+
+	$package{"kernel"} = {
+		license => "GPL-2.0",
+		cpe_id  => "cpe:/o:linux:linux_kernel",
+		name    => "kernel",
+		category  => "operating-system",
+	};
+
+	my %abimap;
+	my @abipkgs = grep { defined $package{$_}->{abi_version} } keys %package;
+	foreach my $name (@abipkgs) {
+		my $pkg = $package{$name};
+		my $abipkg = $name . $pkg->{abi_version};
+		$abimap{$abipkg} = $name;
+	}
+
+	foreach my $name (sort {uc($a) cmp uc($b)} keys %image_packages) {
+		my $pkg = $package{$name};
+		if (!$pkg) {
+			$pkg = $package{$abimap{$name}};
+			next if !$pkg;
+		}
+
+		my @licenses;
+		my @license = split(/\s+/, $pkg->{license});
+		foreach my $lic (@license) {
+			push @licenses, (
+				{ "license" => { "name" => $lic } }
+			);
+		}
+		my $type;
+		if ($pkg->{category}) {
+			my $category = $pkg->{category};
+			my %cat_type = (
+				"operating-system"        => "operating-system",
+				"Firmware"        => "firmware",
+				"Libraries"       => "library"
+			);
+
+			if ($cat_type{$category}) {
+				$type = $cat_type{$category};
+			} else {
+				$type = "application";
+			}
+		}
+
+		my $version = $pkg->{version};
+		if ($image_packages{$name}) {
+			$version = $image_packages{$name};
+		}
+		$version =~ s/-r\d+$// if $version;
+		if ($name =~ /^(kernel|kmod-)/ and $version =~ /^(\d+\.\d+\.\d+)/) {
+			$version = $1;
+		}
+
+		push @components, {
+			name => $pkg->{name},
+			version => $version,
+			@licenses > 0 ? (licenses => [ @licenses ]) : (),
+			$pkg->{cpe_id} ? (cpe => $pkg->{cpe_id}.":".$version) : (),
+			$type ? (type => $type) : (),
+			$version ? (version => $version) : (),
+		};
+	}
+
+	print dump_cyclonedxsbom_json(@components);
+}
+
+sub gen_package_cyclonedxsbom() {
+	my $pkgmanifest = shift @ARGV;
+	my @components;
+	my %mpkgs;
+
+	%mpkgs = parse_package_manifest_metadata($pkgmanifest);
+	%mpkgs or exit 1;
+
+	foreach my $name (sort {uc($a) cmp uc($b)} keys %mpkgs) {
+		my $pkg = $mpkgs{$name};
+
+		my @licenses;
+		my @license = split(/\s+/, $pkg->{license});
+		foreach my $lic (@license) {
+			push @licenses, (
+				{ "license" => { "name" => $lic } }
+			);
+		}
+
+		my $type;
+		if ($pkg->{section}) {
+			my $section = $pkg->{section};
+			my %section_type = (
+				"firmware" => "firmware",
+				"libs" => "library"
+			);
+
+			if ($section_type{$section}) {
+				$type = $section_type{$section};
+			} else {
+				$type = "application";
+			}
+		}
+
+		my $version = $pkg->{version};
+		$version =~ s/-r\d+$// if $version;
+		if ($name =~ /^(kernel|kmod-)/ and $version =~ /^(\d+\.\d+\.\d+)/) {
+			$version = $1;
+		}
+
+		push @components, {
+			name => $name,
+			version => $version,
+			@licenses > 0 ? (licenses => [ @licenses ]) : (),
+			$pkg->{cpe_id} ? (cpe => $pkg->{cpe_id}.":".$version) : (),
+			$type ? (type => $type) : (),
+			$version ? (version => $version) : (),
+		};
+	}
+
+	print dump_cyclonedxsbom_json(@components);
+}
+
+sub parse_command() {
+	GetOptions("ignore=s", \@ignore);
+	my $cmd = shift @ARGV;
+	for ($cmd) {
+		/^mk$/ and return gen_package_mk();
+		/^config$/ and return gen_package_config();
+		/^kconfig/ and return gen_kconfig_overrides();
+		/^source$/ and return gen_package_source();
+		/^pkgaux$/ and return gen_package_auxiliary();
+		/^pkgmanifestjson$/ and return gen_package_manifest_json();
+		/^imgcyclonedxsbom$/ and return gen_image_cyclonedxsbom();
+		/^pkgcyclonedxsbom$/ and return gen_package_cyclonedxsbom();
+		/^license$/ and return gen_package_license(0);
+		/^licensefull$/ and return gen_package_license(1);
+		/^usergroup$/ and return gen_usergroup_list();
+		/^version_filter$/ and return gen_version_filtered_list();
+	}
+	die <<EOF
+Available Commands:
+	$0 mk [file]					Package metadata in makefile format
+	$0 config [file] 				Package metadata in Kconfig format
+	$0 kconfig [file] [config] [patchver]	Kernel config overrides
+	$0 source [file] 				Package source file information
+	$0 pkgaux [file]				Package auxiliary variables in makefile format
+	$0 pkgmanifestjson [file]			Package manifests in JSON format
+	$0 imgcyclonedxsbom <file> [manifest]	Image package manifest in CycloneDX SBOM JSON format
+	$0 pkgcyclonedxsbom <file>			Package manifest in CycloneDX SBOM JSON format
+	$0 license [file] 				Package license information
+	$0 licensefull [file] 			Package license information (full list)
+	$0 usergroup [file]				Package usergroup allocation list
+	$0 version_filter [patchver] [list...]	Filter list of version tagged strings
+
+Options:
+	--ignore <name>				Ignore the source package <name>
+EOF
+}
+
+parse_command();
diff --git a/scripts/pad_image b/scripts/pad_image
new file mode 100755
index 0000000..871063a
--- /dev/null
+++ b/scripts/pad_image
@@ -0,0 +1,100 @@
+#!/usr/bin/env bash
+
+function usage {
+  echo "Usage: prepare_image image_type kernel_image rootfs_image header_size"
+  echo "Pad root and kernel image to the correct size and append the jffs2 start marker as needed"
+  exit 1
+}
+
+function pad_file {
+	echo "Padding $1 to size $2"
+	dd if=$1 of=$1.paddingtempfile bs=$2 count=1 conv=sync &> /dev/null
+	mv $1.paddingtempfile $1
+}
+
+#filesize filestart padding
+function calc_pad {
+	[  $((($1 + $2) & ($3 - 1))) == 0 ] && {
+		echo $1
+		return 0
+        }
+	echo $(((($1 + $2) | ($3 - 1)) + 1 - $2))
+}
+
+function prep_squash {
+	echo "kernel_size: $kernel_size"
+	echo "header_size: $header_size"
+	kernel_pad_size=$(calc_pad $kernel_size $header_size 32)
+	kernel_end=$(($header_size + $kernel_pad_size))
+	pad_file $kernel_image $kernel_pad_size
+
+	#4k
+	rootfs_pad_size=$(calc_pad $rootfs_size $kernel_end 4096)
+	pad_file $rootfs_image $rootfs_pad_size
+	echo -ne '\xde\xad\xc0\xde' >> $rootfs_image
+	
+	#8k
+	rootfs_pad_size=$(calc_pad $rootfs_size $kernel_end 8192)
+	[ $rootfs_pad_size == rootfs_old_padsize ] || {
+		pad_file $rootfs_image $rootfs_pad_size
+		rootfs_old_padsize=$rootfs_pad_size
+		echo -ne '\xde\xad\xc0\xde' >> $rootfs_image
+	}
+
+	#64k
+	rootfs_pad_size=$(calc_pad $rootfs_size $kernel_end 65536)
+	[ $rootfs_pad_size == rootfs_old_padsize ] || {
+		pad_file $rootfs_image $rootfs_pad_size
+		rootfs_old_padsize=$rootfs_pad_size
+		echo -ne '\xde\xad\xc0\xde' >> $rootfs_image
+	}
+
+	#128k
+	rootfs_pad_size=$(calc_pad $rootfs_size $kernel_end 131072)
+	[ $rootfs_pad_size == rootfs_old_padsize ] || {
+		pad_file $rootfs_image $rootfs_pad_size
+		rootfs_old_padsize=$rootfs_pad_size
+		echo -ne '\xde\xad\xc0\xde' >> $rootfs_image
+	}
+	
+}
+
+function prep_jffs2 {
+	kernel_pad_size=$(calc_pad $kernel_size $header_size $1)
+	pad_file $kernel_image $kernel_pad_size
+}
+
+image_type=$1
+kernel_image=$2
+rootfs_image=$3
+header_size=$4
+
+if [ -z "$image_type" ] || [ -z "$rootfs_image" ] || [ -z "$kernel_image" ] || [ -z "$header_size" ]; then
+	usage
+fi
+
+if [ ! -e "$rootfs_image" ] || [ -z "$kernel_image" ]; then
+	echo "input file not found"
+	exit 1
+fi
+
+kernel_size=$(stat -c "%s" "$kernel_image")
+rootfs_size=$(stat -c "%s" "$rootfs_image")
+
+if [ $kernel_size == 0 ] || [ $rootfs_size == 0 ]; then
+	echo "kernel or rootfs empty"
+	exit 1
+fi
+
+case $image_type in
+	squashfs )
+		prep_squash ;;
+	jffs2-64k )
+		prep_jffs2 65536 ;;
+	jffs2-128k )
+		prep_jffs2 131072 ;;
+	* )
+		echo "Unknown image type"
+		exit 1 ;;
+esac
+
diff --git a/scripts/patch-kernel.sh b/scripts/patch-kernel.sh
new file mode 100755
index 0000000..52750dd
--- /dev/null
+++ b/scripts/patch-kernel.sh
@@ -0,0 +1,54 @@
+#!/bin/sh
+# A little script I whipped up to make it easy to
+# patch source trees and have sane error handling
+# -Erik
+#
+# (c) 2002 Erik Andersen <andersen@codepoet.org>
+
+# Set directories from arguments, or use defaults.
+targetdir=${1-.}
+patchdir=${2-../kernel-patches}
+patchpattern=${3-*}
+
+if [ ! -d "${targetdir}" ] ; then
+    echo "Aborting.  '${targetdir}' is not a directory."
+    exit 1
+fi
+if [ ! -d "${patchdir}" ] ; then
+    echo "Aborting.  '${patchdir}' is not a directory."
+    exit 1
+fi
+    
+for i in ${patchdir}/${patchpattern} ; do 
+    case "$i" in
+	*.gz)
+	type="gzip"; uncomp="gunzip -dc"; ;; 
+	*.bz)
+	type="bzip"; uncomp="bunzip -dc"; ;; 
+	*.bz2)
+	type="bzip2"; uncomp="bunzip2 -dc"; ;; 
+	*.zip)
+	type="zip"; uncomp="unzip -d"; ;; 
+	*.Z)
+	type="compress"; uncomp="uncompress -c"; ;; 
+	*)
+	type="plaintext"; uncomp="cat"; ;; 
+    esac
+    [ -d "${i}" ] && echo "Ignoring subdirectory ${i}" && continue	
+    echo ""
+    echo "Applying ${i} using ${type}: " 
+    ${uncomp} ${i} | ${PATCH:-patch} -f -p1 -d ${targetdir}
+    if [ $? != 0 ] ; then
+        echo "Patch failed!  Please fix $i!"
+	exit 1
+    fi
+done
+
+# Check for rejects...
+if [ "`find $targetdir/ '(' -name '*.rej' -o -name '.*.rej' ')' -print`" ] ; then
+    echo "Aborting.  Reject files found."
+    exit 1
+fi
+
+# Remove backup files
+find $targetdir/ '(' -name '*.orig' -o -name '.*.orig' ')' -exec rm -f {} \;
diff --git a/scripts/patch-specs.sh b/scripts/patch-specs.sh
new file mode 100755
index 0000000..2262e21
--- /dev/null
+++ b/scripts/patch-specs.sh
@@ -0,0 +1,90 @@
+#!/usr/bin/env bash
+
+DIR="$1"
+
+if [ -d "$DIR" ]; then
+	DIR="$(cd "$DIR"; pwd)"
+else
+	echo "Usage: $0 toolchain-dir"
+	exit 1
+fi
+
+echo -n "Locating cpp ... "
+for bin in bin usr/bin usr/local/bin; do
+	for cmd in "$DIR/$bin/"*-cpp; do
+		if [ -x "$cmd" ]; then
+			echo "$cmd"
+			CPP="$cmd"
+			break
+		fi
+	done
+done
+
+if [ ! -x "$CPP" ]; then
+	echo "Can't locate a cpp executable in '$DIR' !"
+	exit 1
+fi
+
+patch_specs() {
+	local found=0
+
+	for lib in $(STAGING_DIR="$DIR" "$CPP" -x c -v /dev/null 2>&1 | sed -ne 's#:# #g; s#^LIBRARY_PATH=##p'); do
+		if [ -d "$lib" ]; then
+			grep -qs "STAGING_DIR" "$lib/specs" && rm -f "$lib/specs"
+			if [ $found -lt 1 ]; then
+				echo -n "Patching specs ... "
+				STAGING_DIR="$DIR" "$CPP" -dumpspecs | awk '
+					mode ~ "link" {
+						sub(/(%@?\{L.\})/, "& -L %:getenv(STAGING_DIR /usr/lib) -rpath-link %:getenv(STAGING_DIR /usr/lib)")
+					}
+					mode ~ "cpp" {
+						$0 = $0 " -idirafter %:getenv(STAGING_DIR /usr/include)"
+					}
+					{
+						print $0
+						mode = ""
+					}
+					/^\*cpp:/ {
+						mode = "cpp"
+					}
+					/^\*link.*:/ {
+						mode = "link"
+					}
+				' > "$lib/specs"
+				echo "ok"
+				found=1
+			fi
+		fi
+	done
+
+	[ $found -gt 0 ]
+	return $?
+}
+
+
+VERSION="$(STAGING_DIR="$DIR" "$CPP" --version | sed -ne 's/^.* (.*) //; s/ .*$//; 1p')"
+VERSION="${VERSION:-unknown}"
+
+case "${VERSION##* }" in
+	2.*|3.*|4.0.*|4.1.*|4.2.*)
+		echo "The compiler version does not support getenv() in spec files."
+		echo -n "Wrapping binaries instead ... "
+
+		if "${0%/*}/ext-toolchain.sh" --toolchain "$DIR" --wrap "${CPP%/*}"; then
+			echo "ok"
+			exit 0
+		else
+			echo "failed"
+			exit $?
+		fi
+	;;
+	*)
+		if patch_specs; then
+			echo "Toolchain successfully patched."
+			exit 0
+		else
+			echo "Failed to locate library directory!"
+			exit 1
+		fi
+	;;
+esac
diff --git a/scripts/portable_date.sh b/scripts/portable_date.sh
new file mode 100755
index 0000000..84b5638
--- /dev/null
+++ b/scripts/portable_date.sh
@@ -0,0 +1,11 @@
+#!/bin/sh
+
+case $(uname) in
+	NetBSD|OpenBSD|DragonFly|FreeBSD|Darwin)
+		date -j -f "%Y-%m-%d %H:%M:%S %z" "$1" "$2" 2>/dev/null
+		;;
+	*)
+		date -d "$1" "$2"
+esac
+
+exit $?
diff --git a/scripts/prepare-dm-create-script.sh b/scripts/prepare-dm-create-script.sh
new file mode 100755
index 0000000..d10deb4
--- /dev/null
+++ b/scripts/prepare-dm-create-script.sh
@@ -0,0 +1,74 @@
+#!/bin/bash
+
+TOPDIR=$1
+STAGING_DIR_HOST=$2
+squashfs=$3
+dm_crypt_cfg=$4
+
+SECTOR_SIZE=512
+BLOCK_SIZE=4096
+
+align_up() {
+    local offset=$1
+    local size=$2
+
+    echo $(((($offset + ($size - 1)) / $size) * $size))
+}
+
+verity_get_meta() {
+    local needle="$1"
+    local haystack="$2"
+
+    echo "$haystack" | grep "$needle" | cut -d: -f2 | tr -d '[ \t]'
+}
+
+
+if [[ "$dm_crypt_cfg" == "y" ]]; then
+CRYPT_KEY_SIZE=32
+FIXED_KEY=2f54e5b40c9de5e4700d52f5d3938c1fd19a1d5e05b9dcf74c34a653b4b73ff5
+RANDOM_KEY=$(openssl rand -hex $CRYPT_KEY_SIZE)
+
+openssl enc -aes-$(($CRYPT_KEY_SIZE * 8))-ecb -e -in $squashfs -out "$squashfs"_enc -K $RANDOM_KEY
+mv "$squashfs"_enc $squashfs
+
+ENCRYPTED_KEY=$(echo -ne $RANDOM_KEY | perl -ne 'print pack "H*", $_' | openssl enc \
+                -aes-$(($CRYPT_KEY_SIZE * 8))-ecb -K $FIXED_KEY -nopad | perl -ne 'print unpack "H*", $_' | tr -d '\n')
+fi
+
+VERITY_HASH_OFFSET=$(align_up $(stat --format=%s $squashfs) $BLOCK_SIZE)
+VERITY_HASH_BLOCKS=$(($VERITY_HASH_OFFSET / $BLOCK_SIZE))
+#echo "hash-blocks:"${VERITY_HASH_BLOCKS}
+
+VERITY_META="$(veritysetup format \
+    --hash-offset=$VERITY_HASH_OFFSET \
+    "$squashfs" "$squashfs")"
+
+#echo "VERITY_META= :"${VERITY_META}
+
+VERITY_SALT=$(verity_get_meta Salt "$VERITY_META")
+VERITY_ROOT=$(verity_get_meta Root "$VERITY_META")
+
+ROOT_SECTORS=$(($VERITY_HASH_OFFSET / $SECTOR_SIZE))
+
+ROOT_VERITY="$VERITY_ROOT $VERITY_SALT"
+SMASH_DM_MOD_CREATE="ROOT_SECTORS=$ROOT_SECTORS:HASH_BLOCKS=$VERITY_HASH_BLOCKS:HASHSALT=$ROOT_VERITY"
+if [[ "$dm_crypt_cfg" == "y" ]]; then
+SMASH_DM_MOD_CREATE="$SMASH_DM_MOD_CREATE:ENCRYPTED_KEY=$ENCRYPTED_KEY"
+fi
+DM_SIZE=`echo ${#SMASH_DM_MOD_CREATE}`
+
+DM_ALIGN_SIZE=0x20000
+ROOT_FS_SIZE=$(stat -c%s "$squashfs")
+
+BLOCK_MUL_SIZE=$(($ROOT_FS_SIZE / $DM_ALIGN_SIZE))
+
+if [ $(($ROOT_FS_SIZE % $DM_ALIGN_SIZE)) -eq 0 ]; then
+    APPEND_POS=$ROOT_FS_SIZE
+else
+    APPEND_POS=$((($BLOCK_MUL_SIZE + 1) * $DM_ALIGN_SIZE))
+fi
+
+FILL_SIZE=$(($APPEND_POS - $ROOT_FS_SIZE))
+dd if=/dev/zero of=$squashfs conv=notrunc bs=1 seek=$ROOT_FS_SIZE count=$FILL_SIZE
+echo -n -e "DM_SIZE=$DM_SIZE:"${SMASH_DM_MOD_CREATE} | dd of=$squashfs conv=notrunc bs=1 seek=$APPEND_POS
+
diff --git a/scripts/projectsmirrors.json b/scripts/projectsmirrors.json
new file mode 100644
index 0000000..9bc2d7a
--- /dev/null
+++ b/scripts/projectsmirrors.json
@@ -0,0 +1,70 @@
+{
+	"@SF": [
+		"https://downloads.sourceforge.net"
+	],
+	"@DEBIAN": [
+		"https://ftp.debian.org/debian",
+		"https://mirror.leaseweb.com/debian",
+		"https://mirror.netcologne.de/debian",
+		"https://mirrors.tuna.tsinghua.edu.cn/debian",
+		"https://mirrors.ustc.edu.cn/debian"
+	],
+	"@APACHE": [
+		"https://dlcdn.apache.org",
+		"https://mirror.aarnet.edu.au/pub/apache",
+		"https://mirror.csclub.uwaterloo.ca/apache",
+		"https://archive.apache.org/dist",
+		"https://mirror.cogentco.com/pub/apache",
+		"https://mirror.navercorp.com/apache",
+		"https://ftp.jaist.ac.jp/pub/apache",
+		"https://apache.cs.utah.edu/apache.org",
+		"http://apache.mirrors.ovh.net/ftp.apache.org/dist",
+		"https://mirrors.tuna.tsinghua.edu.cn/apache",
+		"https://mirrors.ustc.edu.cn/apache"
+	],
+        "@GITHUB": [
+                "https://raw.githubusercontent.com"
+        ],
+	"@GNU": [
+		"https://ftpmirror.gnu.org",
+		"https://mirror.csclub.uwaterloo.ca/gnu",
+		"https://mirror.netcologne.de/gnu",
+		"https://ftp.kddilabs.jp/GNU/gnu",
+		"https://www.nic.funet.fi/pub/gnu/gnu",
+		"https://mirror.navercorp.com/gnu",
+		"https://mirrors.rit.edu/gnu",
+		"https://ftp.gnu.org/gnu",
+		"https://mirrors.tuna.tsinghua.edu.cn/gnu",
+		"https://mirrors.ustc.edu.cn/gnu"
+	],
+	"@SAVANNAH": [
+		"https://download.savannah.nongnu.org/releases",
+		"https://mirror.netcologne.de/savannah",
+		"https://mirror.csclub.uwaterloo.ca/nongnu",
+		"https://ftp.acc.umu.se/mirror/gnu.org/savannah",
+		"https://nongnu.uib.no",
+		"https://cdimage.debian.org/mirror/gnu.org/savannah"
+	],
+	"@KERNEL": [
+		"https://cdn.kernel.org/pub",
+		"https://mirrors.mit.edu/kernel",
+		"http://ftp.nara.wide.ad.jp/pub/kernel.org",
+		"http://www.ring.gr.jp/archives/linux/kernel.org",
+		"https://ftp.riken.jp/Linux/kernel.org",
+		"https://www.mirrorservice.org/sites/ftp.kernel.org/pub",
+		"https://mirrors.ustc.edu.cn/kernel.org"
+	],
+	"@GNOME": [
+		"https://download.gnome.org/sources",
+		"https://mirror.csclub.uwaterloo.ca/gnome/sources",
+		"https://ftp.acc.umu.se/pub/GNOME/sources",
+		"http://ftp.cse.buffalo.edu/pub/Gnome/sources",
+		"http://ftp.nara.wide.ad.jp/pub/X11/GNOME/sources",
+		"https://mirrors.ustc.edu.cn/gnome/sources"
+	],
+	"@OPENWRT": [
+		"https://sources.cdn.openwrt.org",
+		"https://sources.openwrt.org",
+		"https://mirror2.openwrt.org/sources"
+	]
+}
diff --git a/scripts/proman.sh b/scripts/proman.sh
new file mode 100755
index 0000000..8294e49
--- /dev/null
+++ b/scripts/proman.sh
@@ -0,0 +1,308 @@
+#!/bin/sh
+set -e
+
+if [ $# -eq 0 -o $# -eq 1 -a "$1" = help ]; then
+	echo "Usage: $0 <add | del | list | help> [arguments...]"
+	echo "   run $0 help <command>  to show help for <command>"
+	exit 0
+fi
+
+HELP=false
+if [ "$1" = help ]; then
+	HELP=true
+	shift
+fi
+
+die () {
+	if [ $# -gt 0 ]; then
+		echo "$@"
+	fi
+	exit 1
+}
+
+row() {
+	FMT="%s"
+	for c in `seq 2 $#`; do
+		FMT="%-15s $FMT"
+	done
+	printf "$FMT\n" "$@"
+}
+
+MMP_DIR=target/linux/mmp
+
+case "$1" in
+	add)
+		RET=0
+		if [ $# -lt 5 ]; then
+			echo "Insufficient arguments"
+			RET=1
+			HELP=true
+		fi
+		if $HELP; then
+			echo "$0 add <template_profile> <chip> <name> <code> [kernel_config_suffix]"
+			echo "   Add a new profile named <chip>p<code> based on template config file <template_profile> and specified params"
+			echo "   Model name would be <name><code>, BLF_PREFIX and FOTA_PREFIX would be p<code>"
+			echo "Sample usage: $0 add asr1803p401 asr1802s NEZAS 201"
+			exit $RET
+		fi
+		TMPL="config/defconfig_$2"
+		[ -s "$TMPL" ] || die "Invalid template file $TMPL"
+		TMPL_CHIP="$(sed 's/^CONFIG_TARGET_mmp_\([^_]*\)=y$/\1/;t;d' "$TMPL")"
+		[ -n "$TMPL_CHIP" ] || die "No chip found in $TMPL"
+		TMPL_MODL="$(sed "s/^CONFIG_TARGET_mmp_${TMPL_CHIP}_\\([^_]*\)=y$/\\1/;t;d" "$TMPL")"
+		[ -n "$TMPL_MODL" ] || die "No model found in $TMPL"
+		TMPL_NAME="$(echo "$TMPL_MODL" | sed 's/[0-9].*$//')"
+		TMPL_NAME_L="$(echo "$TMPL_NAME" | tr A-Z a-z)"
+		TMPL_CODE="$(echo "$TMPL_MODL" | sed 's/^[^0-9]*//')"
+		TMPL_CONF=CONFIG_TARGET_mmp_${TMPL_CHIP}_${TMPL_MODL}
+		TMPL_KCFG="$(echo $MMP_DIR/$TMPL_CHIP/config-* | sed 's/ .*//')"
+		[ -s "$TMPL_KCFG" ] || die "No kernel config found for $TMPL_CHIP"
+		TMPL_OEMF=$MMP_DIR/$TMPL_CHIP/oem_fs
+		[ -d "$TMPL_OEMF" ] || die "No oem_fs found for $TMPL_CHIP"
+		CHIP=$3
+		[ -n "$CHIP" ] || die "Empty chip"
+		NAME=$4
+		[ -n "$NAME" ] || die "Empty name"
+		NAME_L="$(echo "$NAME" | tr A-Z a-z)"
+		CODE=$5
+		[ -n "$CODE" ] || die "Empty code"
+		CONF=CONFIG_TARGET_mmp_${CHIP}_$NAME$CODE
+		KCONF=$6
+		TGTF="config/defconfig_${CHIP}p$CODE"
+		[ ! -e "$TGTF" ] || die "$TGTF already exists"
+		KCFG=$MMP_DIR/$CHIP/`basename $TMPL_KCFG`
+		OEMF=$MMP_DIR/$CHIP/oem_fs
+		TGTM=$MMP_DIR/$CHIP/target.mk
+		CPFX="$(echo $CHIP | sed 's/[0-9].*//')"
+		CSFX="$(echo $CHIP | sed 's/^[^0-9]*//')"
+		[ "$CPFX$CSFX" = "$CHIP" ] || die "Unexpected $CHIP"
+		CPFX_U="$(echo $CPFX | tr a-z A-Z)"
+		TMPL_CPFX="$(echo $TMPL_CHIP | sed 's/[0-9].*//')"
+		TMPL_CSFX="$(echo $TMPL_CHIP | sed 's/^[^0-9]*//')"
+		[ "$TMPL_CPFX$TMPL_CSFX" = "$TMPL_CHIP" ] || die "Unexpected $TMPL_CHIP"
+		TMPL_CPFX_U="$(echo $TMPL_CPFX | tr a-z A-Z)"
+		set -x
+		set -e
+		mkdir -p $MMP_DIR/$CHIP $MMP_DIR/$CHIP/profiles
+		[ -s $KCFG ] || cp $TMPL_KCFG $KCFG
+		[ -d $OEMF ] || cp -a $TMPL_OEMF $OEMF
+		if [ ! -s $TGTM ] || [ `grep -Fc $CHIP $TGTM` -ne 3 ]; then
+			cp $MMP_DIR/$TMPL_CHIP/target.mk $TGTM
+			sed -i "s/$TMPL_CHIP/$CHIP/g;s/$TMPL_CPFX_U/$CPFX_U/g;s/$TMPL_CSFX/$CSFX/g" $TGTM
+		fi
+		if ! grep -Fq 'define Model' $MMP_DIR/$CHIP/profiles/*.mk 2>/dev/null; then
+			PROF=$MMP_DIR/$CHIP/profiles/$CPFX.mk
+			cp $MMP_DIR/$TMPL_CHIP/profiles/*.mk $PROF
+			sed -i '/^define Profile/,/\$(eval \$(call Model,/d' $PROF
+		else
+			PROF=`grep -Fl 'define Model' $MMP_DIR/$CHIP/profiles/*.mk | head -1`
+		fi
+		sed -i ":n;/^$/{N;s/^\\n$//;t n;};/^\n*define Profile\/$NAME$CODE$/,/^endef/d;/,$NAME$CODE[,)]/d" $PROF
+		cat >> $PROF << EOF
+
+define Profile/$NAME$CODE
+  NAME:=$CPFX_U `echo $NAME | awk '{printf("%c%s\n", substr($0,1,1), tolower(substr($0,2)))}'` Profile $CODE
+endef
+\$(eval \$(call Profile,$NAME$CODE))
+\$(eval \$(call Model,$NAME$CODE,${CHIP}p$CODE))
+EOF
+		[ -d $MMP_DIR/$TMPL_CHIP/$TMPL_MODL ] && cp -a $MMP_DIR/$TMPL_CHIP/$TMPL_MODL $MMP_DIR/$CHIP/$NAME$CODE
+		SUBT="$(sed 's/^\s*\<SUBTARGETS\>[^=]*=\s*//;t n;d;:n;s/ \+$//;s/ \+/\n/g' $MMP_DIR/Makefile)"
+		if ! echo "$SUBT" | grep -Fx $CHIP; then
+			sed -i "/^\\s*\\<SUBTARGETS\\>[^=]*=/{s/ *$/ $CHIP/}" $MMP_DIR/Makefile
+		fi
+		TMPL_RULE="$(awk "/^ifeq \\(\\\$\\($TMPL_CONF\\),y\\)/{++x;print;next};x{print;if(\$1~/^if/)++x;else if(\$1==\"endif\")--x}" rules.mk)"
+		if [ -n "$TMPL_RULE" ]; then
+			awk "/^ifeq \\(\\\$\\(CONFIG_TARGET_mmp_/{++x;if(/$CONF\\),y\\)/)y=NR};!y{print};x{if(\$1~/^if/&&!/CONFIG_TARGET_mmp_/)++x;else if(\$1==\"endif\"){--x;if(!x){getline;if(/^ *$/)n=NR;else n=NR-1;if(y){m=n-y+1;y=0;if(n<NR)print}else print}}};END{print n-m >\"rules.mk.nr\"}" rules.mk > rules.mk.tmp
+			#diff -u rules.mk rules.mk.tmp || true
+			LNUM="$(cat rules.mk.nr)"
+			head -$LNUM rules.mk.tmp > rules.mk
+			echo "$TMPL_RULE" | sed "s/$TMPL_CHIP/$CHIP/g;s/$TMPL_MODL/$NAME$CODE/g;s/$TMPL_CODE/$CODE/g" >> rules.mk
+			echo >> rules.mk
+			tail -n +`expr $LNUM + 1` rules.mk.tmp >> rules.mk
+			#diff -u rules.mk.tmp rules.mk || true
+			rm rules.mk.tmp rules.mk.nr
+		else
+			echo "Warning: rules.mk not updated"
+		fi
+		TMPL_IMG_MKFL="$(awk "/^ifeq \\(\\\$\\(CONFIG_TARGET_mmp_$TMPL_CHIP\\),y\\)/{++x;print;next};x{print;if(\$1~/^if/)++x;else if(\$1==\"endif\")--x}" $MMP_DIR/image/Makefile)"
+		if [ -n "$TMPL_IMG_MKFL" ]; then
+			awk "/^ifeq \\(\\\$\\(CONFIG_TARGET_mmp_/{++x;if(/CONFIG_TARGET_mmp_$CHIP\\),y\\)/)y=NR};!y{print};x{if(\$1~/^if/&&!/CONFIG_TARGET_mmp_/)++x;else if(\$1==\"endif\"){--x;if(!x){getline;if(/^ *$/)n=NR;else n=NR-1;if(y){m=n-y+1;y=0;if(n<NR)print}else print}}};END{print n-m >\"image.mk.nr\"}" $MMP_DIR/image/Makefile > image.mk.tmp
+			#diff -u $MMP_DIR/image/Makefile image.mk.tmp || true
+			LNUM="$(cat image.mk.nr)"
+			head -$LNUM image.mk.tmp > $MMP_DIR/image/Makefile
+			echo "$TMPL_IMG_MKFL" | sed "s/$TMPL_CHIP/$CHIP/g;s/$TMPL_MODL/$NAME$CODE/g;s/$TMPL_NAME/$NAME/g;s/$TMPL_CODE/$CODE/g" >> $MMP_DIR/image/Makefile
+			echo >> $MMP_DIR/image/Makefile
+			tail -n +`expr $LNUM + 1` image.mk.tmp >> $MMP_DIR/image/Makefile
+			#diff -u image.mk.tmp $MMP_DIR/image/Makefile || true
+			rm image.mk.tmp image.mk.nr
+		else
+			echo "Warning: image/Makefile not updated"
+		fi
+		cp $TMPL $TGTF
+		if [ $TMPL_CHIP = $CHIP ]; then
+			sed -i "s/$NAME$CODE/$TMPL_MODL/g;t;s/$TMPL_MODL/$NAME$CODE/g;s/p$TMPL_CODE/p$CODE/g" $TGTF
+		else
+			sed -i "s/$CHIP/$TMPL_CHIP/g;t;s/$TMPL_CHIP/$CHIP/g;s/$TMPL_MODL/$NAME$CODE/g;s/$TMPL_NAME_L/$NAME_L/g;s/p$TMPL_CODE/p$CODE/g" $TGTF
+		fi
+		#diff -u $TMPL $TGTF || true
+		make prepare-tmpinfo
+		./scripts/config/conf --defconfig=$TGTF -w $TGTF.new Config.in
+		#diff -u $TGTF $TGTF.new || true
+		mv $TGTF.new $TGTF
+		set +x
+		sh $0 list | grep -F ${CHIP}p$CODE
+		;;
+	list)
+		if $HELP; then
+			echo "$0 list"
+			echo "   show the list of defined profiles, as well as the"
+			echo "   list of unused target configurations in uboot /obm"
+			exit 0
+		fi
+		[ -d "$MMP_DIR" ] || die "Please run under openwrt directory"
+		PROFILES=
+		MODELS=
+		for c in $MMP_DIR/*/profiles/*.mk; do
+			[ -s "$c" ] || die "Invalid file $c"
+			CHIP_DIR="$(echo "$c" | sed "s=^$MMP_DIR/==;s=/profiles.*==")"
+			echo "$CHIP_DIR" | grep -Fvq / || die "Slash in $CHIP_DIR"
+			for d in `sed "s/^.*(call Profile,//;t n;d;:n;s/).*//" "$c"`; do
+				MODELS="$MODELS $d"
+				p="$(sed "s/^.*(call Model,$d,//;t n;d;:n;s/).*//" "$c")"
+				[ -n "$p" ] || die "Missing model $d"
+				PROFILES="$PROFILES $p"
+				eval "PROFILE_$d=$p"
+				eval "MODEL_$p=$d"
+				eval "CHIP_$p=$CHIP_DIR"
+			done
+		done
+		UBOOT_CONF_LIST="$(sed -n '/^define uboot\//,/^endef$/{s/^ *DEPS:=@//;t n;d;:n;s/[()]//g;s/||/\n/g;s/ //g;p}' package/boot/uboot-mmp/Makefile | sed 's/^/CONFIG_/')"
+		OBM_CONF_LIST="$(sed -n '/^define obm\//,/^endef$/{s/^ *DEPS:=@//;t n;d;:n;s/[()]//g;s/||/\n/g;s/ //g;p}' package/boot/obm-mmp/Makefile | sed 's/^/CONFIG_/')"
+		for p in $PROFILES; do
+			eval "CONF=CONFIG_TARGET_mmp_\${CHIP_$p}_\${MODEL_$p}"
+			CFG="$(grep -Fxl $CONF=y config/* | sed 's=^.*/defconfig_==')"
+			if [ -z "$CFG" ]; then
+				echo "Warning: profile $p has no config"
+			elif [ "$CFG" != $p ]; then
+				CFG="$(echo -n "$CFG" | tr '\n' ',')"
+				echo "Warning: profile $p has config: $CFG"
+				echo "$CFG" | grep -q "^$p," || echo "         without $p"
+			fi
+			eval "CONFIG_$p=\"$CFG\""
+			if [ -z "$CFG" ]; then
+				VAL=XXX
+			else
+				if [ "$CFG" != $p ]; then
+					CFG="$(echo "$CFG" | sed 's/,.*//')"
+				fi
+				if eval grep -Fxq CONFIG_TARGET_mmp_\${CHIP_$p}=y config/defconfig_$CFG; then
+					VAL=OO
+				else
+					VAL=XO
+				fi
+				if eval grep -Fxq CONFIG_TARGET_ARCH_PACKAGES=\\\"\${CHIP_$p}\\\" config/defconfig_$CFG; then
+					VAL=${VAL}O
+				else
+					VAL=${VAL}X
+				fi
+			fi
+			if true; then
+				if grep -Fxq "ifeq (\$($CONF),y)" rules.mk; then
+					VAL=${VAL}O
+					c="$(sed -n "/^ifeq (\\\$($CONF),y)$/,/^endif$/{s/.* ARCH_PROFILE.*= *//;t n;d;:n;p}" rules.mk)"
+					if [ "$c" = "$p" ]; then
+						VAL=${VAL}O
+					else
+						VAL=${VAL}X
+					fi
+					c="$(sed -n "/^ifeq (\\\$($CONF),y)$/,/^endif$/{s/.* BLF_PREFIX.*= *//;t n;d;:n;p}" rules.mk)"
+					eval "BLF_$p=\"$c\""
+					c="$(sed -n "/^ifeq (\\\$($CONF),y)$/,/^endif$/{s/.* FOTA_PREFIX.*= *//;t n;d;:n;p}" rules.mk)"
+					eval "FOTA_$p=\"$c\""
+					c="$(sed -n "/^ifeq (\\\$($CONF),y)$/,/^endif$/{s/.* KERNEL_CONFIG_FILE_NAME_APPENDIX.*= *//;t n;d;:n;p}" rules.mk)"
+					eval "KCONF_$p=\"$c\""
+				else
+					VAL=${VAL}XX
+				fi
+			fi
+			if echo "$UBOOT_CONF_LIST" | grep -Fxq "$CONF"; then
+				VAL=${VAL}O
+			else
+				VAL=${VAL}X
+			fi
+			if echo "$OBM_CONF_LIST" | grep -Fxq "$CONF"; then
+				VAL=${VAL}O
+			else
+				VAL=${VAL}X
+			fi
+			eval "VALID_$p=$VAL"
+		done
+		row Profile Chip Model BLF/FOTA/Kconf Config "Validity(O/X)"
+		for p in $PROFILES; do
+			eval row \$p \${CHIP_$p} \${MODEL_$p} \"\${BLF_$p}/\${FOTA_$p}/\${KCONF_$p}\" \"\${CONFIG_$p}\" \${VALID_$p}
+		done
+		for c in `echo "$UBOOT_CONF_LIST
+$OBM_CONF_LIST" | sort | uniq | sed 's/^\s*CONFIG_TARGET_mmp_//'`; do
+			d="$(echo "$c" | sed 's/^[^_]*_//')"
+			if [ -n "$(eval echo "\$PROFILE_$d")" ]; then
+				continue
+			fi
+			CHIP_DIR="$(echo "$c" | sed 's/_.*//')"
+			CONF="CONFIG_TARGET_mmp_$c"
+			CFG="$(grep -Fxl $CONF=y config/* | sed 's=^.*/defconfig_==')"
+			if [ -z "$CFG" ]; then
+				VAL=XXX
+			else
+				CFG="$(echo "$CFG" | head -1)"
+				if grep -Fxq CONFIG_TARGET_mmp_$CHIP_DIR=y config/defconfig_$CFG; then
+					VAL=OO
+				else
+					VAL=XO
+				fi
+				if grep -Fxq "CONFIG_TARGET_ARCH_PACKAGES=\"$CHIP_DIR\"" config/defconfig_$CFG; then
+					VAL=${VAL}O
+				else
+					VAL=${VAL}X
+				fi
+			fi
+			if true; then
+				if grep -Fxq "ifeq (\$($CONF),y)" rules.mk; then
+					VAL=${VAL}O
+					c="$(sed -n "/^ifeq (\\\$($CONF),y)$/,/^endif$/{s/.* ARCH_PROFILE.*= *//;t n;d;:n;p}" rules.mk)"
+					if [ "$c" = "$CFG" ]; then
+						VAL=${VAL}O
+					else
+						VAL=${VAL}X
+					fi
+					c="$(sed -n "/^ifeq (\\\$($CONF),y)$/,/^endif$/{s/.* BLF_PREFIX.*= *//;t n;d;:n;p}" rules.mk)"
+					BLF="$c"
+					c="$(sed -n "/^ifeq (\\\$($CONF),y)$/,/^endif$/{s/.* FOTA_PREFIX.*= *//;t n;d;:n;p}" rules.mk)"
+					FOTA="$c"
+					c="$(sed -n "/^ifeq (\\\$($CONF),y)$/,/^endif$/{s/.* KERNEL_CONFIG_FILE_NAME_APPENDIX.*= *//;t n;d;:n;p}" rules.mk)"
+					KCONF="$c"
+				else
+					VAL=${VAL}XX
+					BLF=
+					FOTA=
+					KCONF=
+				fi
+			fi
+			if echo "$UBOOT_CONF_LIST" | grep -Fxq "$CONF"; then
+				VAL=${VAL}O
+			else
+				VAL=${VAL}X
+			fi
+			if echo "$OBM_CONF_LIST" | grep -Fxq "$CONF"; then
+				VAL=${VAL}O
+			else
+				VAL=${VAL}X
+			fi
+			row "" "$CHIP_DIR" "$d" "$BLF/$FOTA/$KCONF" "$CFG" "$VAL"
+		done
+		;;
+
+	*)
+		die "Unknown command $1"
+		;;
+esac
diff --git a/scripts/qemustart b/scripts/qemustart
new file mode 100755
index 0000000..6c4c9be
--- /dev/null
+++ b/scripts/qemustart
@@ -0,0 +1,343 @@
+#!/usr/bin/env bash
+
+SELF="$0"
+
+# Linux bridge for connecting lan and wan network of guest machines
+BR_LAN="${BR_LAN:-br-lan}"
+BR_WAN="${BR_WAN:-br-wan}"
+
+# Host network interface providing internet access for guest machines
+IF_INET="${IF_INET:-eth0}"
+
+# qemu-bridge-helper does two things here
+#
+# - create tap interface
+# - add the tap interface to bridge
+#
+# as such it requires CAP_NET_ADMIN to do its job.  It will be convenient to
+# have it as a root setuid program.  Be aware of the security risks implied
+#
+# the helper has an acl list which defaults to deny all bridge.  we need to add
+# $BR_LAN and $BR_WAN to its allow list
+#
+#	# sudo vim /etc/qemu/bridge.conf
+#	allow br-lan
+#	allow br-wan
+#
+# Other allowed directives can be 'allow all', 'deny all', 'include xxx',  See
+# qemu-bridge-helper.c of qemu source code for details.
+#
+# The helper can be provided by package qemu-system-common on debian, or
+# qemu-kvm-common on rhel
+#
+HELPER="${HELPER:-/usr/libexec/qemu-bridge-helper}"
+
+### end of global settings
+
+__errmsg() {
+	echo "$*" >&2
+}
+
+do_setup() {
+	# setup bridge for LAN network
+	sudo ip link add dev "$BR_LAN" type bridge
+	sudo ip link set dev "$BR_LAN" up
+	sudo ip addr add 192.168.1.3/24 dev "$BR_LAN"
+
+	# setup bridge for WAN network
+	#
+	# minimal dnsmasq config for configuring guest wan network with dhcp
+	#
+	#	# sudo apt-get install dnsmasq
+	#	# sudo vi /etc/dnsmasq.conf
+	#	interface=br-wan
+	#	dhcp-range=192.168.7.50,192.168.7.150,255.255.255.0,30m
+	#
+	sudo ip link add dev "$BR_WAN" type bridge
+	sudo ip link set dev "$BR_WAN" up
+	sudo ip addr add 192.168.7.1/24 dev "$BR_WAN"
+
+	# guest internet access
+	sudo sysctl -w "net.ipv4.ip_forward=1"
+	sudo sysctl -w "net.ipv4.conf.$BR_WAN.proxy_arp=1"
+	while sudo iptables -t nat -D POSTROUTING -o "$IF_INET" -j MASQUERADE 2>/dev/null; do true; done
+	      sudo iptables -t nat -A POSTROUTING -o "$IF_INET" -j MASQUERADE
+}
+
+check_setup_() {
+	ip link show "$BR_LAN" >/dev/null || return 1
+	ip link show "$BR_WAN" >/dev/null || return 1
+	[ -x "$HELPER" ] || {
+		__errmsg "helper $HELPER is not an executable"
+		return 1
+	}
+}
+
+check_setup() {
+	[ -n "$o_network" ] || return 0
+	check_setup_ || {
+		__errmsg "please check the script content to see the environment requirement"
+		return 1
+	}
+}
+#do_setup; check_setup; exit $?
+
+usage() {
+	cat >&2 <<EOF
+Usage: $SELF [-h|--help]
+       $SELF <target>
+         [<subtarget> [<extra-qemu-options>]]
+         [--kernel <kernel>]
+         [--rootfs <rootfs>]
+         [--machine <machine>]
+         [-n|--network]
+
+<subtarget> will default to "generic" and must be specified if
+<extra-qemu-options> are present
+
+e.g. <subtarget> for malta can be le, be, le64, be64, le-glibc, le64-glibc, etc
+
+<kernel>, <rootfs> can be required or optional arguments to qemu depending on
+the actual <target> in use.  They will default to files under bin/targets/
+
+Examples
+
+  $SELF x86 64
+  $SELF x86 64 --machine q35,accel=kvm -device virtio-balloon-pci
+  $SELF x86 64 -incoming tcp:0:4444
+  $SELF x86 64-glibc
+  $SELF malta be -m 64
+  $SELF malta le64
+  $SELF malta be-glibc
+  $SELF armsr armv7 \\
+                --machine virt,highmem=off \\
+                --kernel bin/targets/armsr/armv7/openwrt-armsr-armv7-generic-kernel.bin \\
+                --rootfs bin/targets/armsr/armv7/openwrt-armsr-armv7-generic-ext4-rootfs.img
+EOF
+}
+
+rand_mac() {
+	hexdump -n 3 -e '"52:54:00" 3/1 ":%02x"' /dev/urandom
+}
+
+parse_args() {
+	o_network=
+	o_qemu_extra=()
+	while [ "$#" -gt 0 ]; do
+		# Cmdline options for the script itself SHOULD try to be
+		# prefixed with two dashes to distinguish them from those for
+		# qemu executables.
+		#
+		# Also note that qemu accepts both --opt and -opt
+		case "$1" in
+			--kernel) o_kernel="$2"; shift 2 ;;
+			--rootfs) o_rootfs="$2"; shift 2 ;;
+			--machine|-machine|-M) o_mach="$2"; shift 2 ;;
+			--network|-n) o_network=1; shift ;;
+			--help|-h)
+				usage
+				exit 0
+				;;
+			*)
+				if [ -z "$o_target" ]; then
+					o_target="$1"
+				elif [ -z "$o_subtarget" ]; then
+					o_subtarget="$1"
+				else
+					o_qemu_extra+=("$1")
+				fi
+				shift
+				;;
+		esac
+	done
+
+	MAC_LAN="$(rand_mac)"
+	MAC_WAN="$(rand_mac)"
+	[ -n "$o_target" ] || {
+		usage
+		return 1
+	}
+	[ -n "$o_subtarget" ] || o_subtarget="generic"
+	eval "$(grep ^CONFIG_BINARY_FOLDER= .config 2>/dev/null)"
+	o_bindir="${CONFIG_BINARY_FOLDER:-bin}/targets/$o_target/$o_subtarget"
+}
+
+start_qemu_armsr() {
+	local kernel="$o_kernel"
+	local rootfs="$o_rootfs"
+	local mach="${o_mach:-virt}"
+	local cpu
+	local qemu_exe
+
+	case "${o_subtarget%-*}" in
+		armv7)
+			qemu_exe="qemu-system-arm"
+			cpu="cortex-a15"
+			[ -n "$kernel" ] || kernel="$o_bindir/openwrt-$o_target-${o_subtarget%-*}-generic-initramfs-kernel.bin"
+			;;
+		armv8)
+			qemu_exe="qemu-system-aarch64"
+			cpu="cortex-a57"
+			[ -n "$kernel" ] || kernel="$o_bindir/openwrt-$o_target-${o_subtarget%-*}-generic-initramfs-kernel.bin"
+			;;
+		*)
+			__errmsg "target $o_target: unknown subtarget $o_subtarget"
+			return 1
+			;;
+	esac
+	[ -z "$rootfs" ] || {
+		if [ ! -f "$rootfs" -a -s "$rootfs.gz" ]; then
+			gunzip "$rootfs.gz"
+		fi
+		o_qemu_extra+=( \
+			"-drive" "file=$rootfs,format=raw,if=virtio" \
+			"-append" "root=/dev/vda rootwait" \
+		)
+	}
+
+	[ -z "$o_network" ] || {
+		o_qemu_extra+=( \
+			"-netdev" "bridge,id=lan,br=$BR_LAN,helper=$HELPER" \
+			    "-device" "virtio-net-pci,id=devlan,netdev=lan,mac=$MAC_LAN" \
+			"-netdev" "bridge,id=wan,br=$BR_WAN,helper=$HELPER" "-device" \
+			    "virtio-net-pci,id=devwan,netdev=wan,mac=$MAC_WAN" \
+		)
+	}
+
+	"$qemu_exe" -machine "$mach" -cpu "$cpu" -nographic \
+		-kernel "$kernel" \
+		"${o_qemu_extra[@]}"
+}
+
+start_qemu_malta() {
+	local is64
+	local isel
+	local qemu_exe
+	local cpu
+	local rootfs="$o_rootfs"
+	local kernel="$o_kernel"
+	local mach="${o_mach:-malta}"
+
+	# o_subtarget can be le, be, le64, be64, le-glibc, le64-glibc, etc..
+	is64="$(echo $o_subtarget | grep -o 64)"
+	[ "$(echo "$o_subtarget" | grep -o '^..')" = "le" ] && isel="el"
+	qemu_exe="qemu-system-mips$is64$isel"
+	[ -n "$is64" ] && cpu="MIPS64R2-generic" || cpu="24Kc"
+
+	[ -n "$kernel" ] || kernel="$o_bindir/openwrt-malta-${o_subtarget%-*}-vmlinux-initramfs.elf"
+
+	[ -z "$rootfs" ] || {
+		if [ ! -f "$rootfs" -a -s "$rootfs.gz" ]; then
+			gunzip "$rootfs.gz"
+		fi
+		o_qemu_extra+=( \
+			"-drive" "file=$rootfs,format=raw" \
+			"-append" "root=/dev/sda rootwait" \
+		)
+	}
+
+	# NOTE: order of wan, lan -device arguments matters as it will affect which
+	# one will be actually used as the wan, lan network interface inside the
+	# guest machine
+	[ -z "$o_network" ] || {
+		o_qemu_extra+=(
+			-netdev bridge,id=wan,br="$BR_WAN,helper=$HELPER" -device pcnet,netdev=wan,mac="$MAC_WAN"
+			-netdev bridge,id=lan,br="$BR_LAN,helper=$HELPER" -device pcnet,netdev=lan,mac="$MAC_LAN"
+		)
+	}
+
+	"$qemu_exe" -machine "$mach" -cpu "$cpu" -nographic \
+		-kernel "$kernel" \
+		"${o_qemu_extra[@]}"
+}
+
+start_qemu_x86() {
+	local qemu_exe
+	local kernel="$o_kernel"
+	local rootfs="$o_rootfs"
+	local mach="${o_mach:-pc}"
+
+	[ -n "$rootfs" ] || {
+		rootfs="$o_bindir/openwrt-$o_target-${o_subtarget%-*}-generic-squashfs-combined.img"
+		if [ ! -f "$rootfs" -a -s "$rootfs.gz" ]; then
+			gunzip "$rootfs.gz"
+		fi
+	}
+	#
+	# generic: 32-bit, pentium4 (CONFIG_MPENTIUM4), kvm guest, virtio
+	# legacy: 32-bit, i486 (CONFIG_M486)
+	# 64: 64-bit, kvm guest, virtio
+	#
+	case "${o_subtarget%-*}" in
+		legacy)			qemu_exe="qemu-system-i386"	;;
+		generic|64)		qemu_exe="qemu-system-x86_64"	;;
+		*)
+			__errmsg "target $o_target: unknown subtarget $o_subtarget"
+			return 1
+			;;
+	esac
+
+	[ -n "$kernel" ] && {
+	    o_qemu_extra+=( \
+		"-kernel" "$kernel" \
+		"-append" "root=/dev/vda console=ttyS0 rootwait" \
+	    )
+	}
+
+	[ -z "$o_network" ] || {
+		case "${o_subtarget%-*}" in
+			legacy)
+				o_qemu_extra+=(
+					-netdev "bridge,id=lan,br=$BR_LAN,helper=$HELPER" -device "e1000,id=devlan,netdev=lan,mac=$MAC_LAN"
+					-netdev "bridge,id=wan,br=$BR_WAN,helper=$HELPER" -device "e1000,id=devwan,netdev=wan,mac=$MAC_WAN"
+				)
+				;;
+			generic|64)
+				o_qemu_extra+=(
+					-netdev "bridge,id=lan,br=$BR_LAN,helper=$HELPER" -device "virtio-net-pci,id=devlan,netdev=lan,mac=$MAC_LAN"
+					-netdev "bridge,id=wan,br=$BR_WAN,helper=$HELPER" -device "virtio-net-pci,id=devwan,netdev=wan,mac=$MAC_WAN"
+				)
+				;;
+		esac
+	}
+
+	case "${o_subtarget%-*}" in
+		legacy)
+			# use IDE (PATA) disk instead of AHCI (SATA).  Refer to link
+			# [1] for related discussions
+			#
+			# To use AHCI interface
+			#
+			#	-device ich9-ahci,id=ahci \
+			#	-device ide-hd,drive=drv0,bus=ahci.0 \
+			#	-drive "file=$rootfs,format=raw,id=drv0,if=none" \
+			#
+			# [1] https://dev.openwrt.org/ticket/17947
+			"$qemu_exe" -machine "$mach" -nographic \
+				-device ide-hd,drive=drv0 \
+				-drive "file=$rootfs,format=raw,id=drv0,if=none" \
+				"${o_qemu_extra[@]}"
+			;;
+		generic|64)
+			"$qemu_exe" -machine "$mach" -nographic \
+				-drive "file=$rootfs,format=raw,if=virtio" \
+				"${o_qemu_extra[@]}"
+			;;
+	esac
+}
+
+start_qemu() {
+	case "$o_target" in
+		armsr)		start_qemu_armsr	;;
+		malta)		start_qemu_malta	;;
+		x86)		start_qemu_x86		;;
+		*)
+			__errmsg "target $o_target is not supported yet"
+			return 1
+			;;
+	esac
+}
+
+parse_args "$@" \
+	&& check_setup \
+	&& start_qemu
diff --git a/scripts/redboot-script.pl b/scripts/redboot-script.pl
new file mode 100755
index 0000000..e2d1264
--- /dev/null
+++ b/scripts/redboot-script.pl
@@ -0,0 +1,111 @@
+#!/usr/bin/env perl
+#
+# Script for generating redboot configs, based on brcmImage.pl
+#
+# Copyright (C) 2015 Álvaro Fernández Rojas <noltari@gmail.com>
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+#
+
+use strict;
+use Getopt::Std;
+use File::stat;
+
+my $version = "0.1";
+my %arg = (
+	o => 'redboot.script',
+	s => 0x1000,
+	f => 0xbe430000,
+	a => 0x80010000,
+	l => 0x7c0000,
+	t => 20,
+);
+my $prog = $0;
+$prog =~ s/^.*\///;
+getopts("r:k:o:s:f:a:l:t:vh", \%arg);
+
+die "usage: $prog ~opts~
+
+  -r <file>	: input rootfs file
+  -k <file>	: input kernel file
+  -o <file>	: output image file, default $arg{o}
+  -s <size_kb>	: redboot script size, default ".sprintf('%d', parse_num($arg{s}))."
+  -f <baseaddr>	: flash base, default ".sprintf('0x%x', parse_num($arg{f}))."
+  -a <loadaddr>	: Kernel load address, default ".sprintf('0x%x', parse_num($arg{a}))."
+  -l <linux_kb>	: linux partition size, default ".sprintf('0x%x', parse_num($arg{l}))."
+  -t <timeout> 	: redboot script timeout, default ".sprintf('%d', parse_num($arg{t}))."
+  -v		: be more verbose
+  -h		: help, version $version
+
+EXAMPLES:
+    $prog -k kern -r rootfs
+" if $arg{h} || !$arg{k} || !$arg{r};
+
+sub parse_num
+{
+	my $num = @_[0];
+	if (index(lc($num), lc("0x")) == 0) {
+		return hex($num);
+	} else {
+		return $num + 0;
+	}
+}
+
+sub gen_script
+{
+	my $kernel_off = parse_num($arg{s});
+	my $kernel_addr = parse_num($arg{f});
+	my $kernel_len = stat($arg{k})->size;
+
+	my $rootfs_off = $kernel_off + $kernel_len;
+	my $rootfs_addr = $kernel_addr + $kernel_len;
+	my $rootfs_len = parse_num($arg{l}) - $kernel_len;
+	my $rootfs_size = stat($arg{r})->size;
+
+	my $load_addr = parse_num($arg{a});
+
+	my $timeout = parse_num($arg{t});
+
+	if ($arg{v}) {
+		printf "kernel_off: 0x%x(%u)\n", $kernel_off, $kernel_off;
+		printf "kernel_addr: 0x%x(%u)\n", $kernel_addr, $kernel_addr;
+		printf "kernel_len: 0x%x(%u)\n", $kernel_len, $kernel_len;
+
+		printf "rootfs_off: 0x%x(%u)\n", $rootfs_off, $rootfs_off;
+		printf "rootfs_addr: 0x%x(%u)\n", $rootfs_addr, $rootfs_addr;
+		printf "rootfs_len: 0x%x(%u)\n", $rootfs_len, $rootfs_len;
+		printf "rootfs_size: 0x%x(%u)\n", $rootfs_size, $rootfs_size;
+	}
+
+	open(FO, ">$arg{o}");
+	printf FO "fis init -f\n";
+	printf FO "\n";
+	printf FO "fconfig boot_script true\n";
+	printf FO "fconfig boot_script_data\n";
+	printf FO "fis load -b 0x%x -d kernel\n", $load_addr;
+	printf FO "exec -c \"noinitrd\" 0x%x\n", $load_addr;
+	printf FO "\n";
+	printf FO "fconfig boot_script_timeout %d\n", $timeout;
+	printf FO "\n";
+	printf FO "fis create -o 0x%x -f 0x%x -l 0x%x kernel\n", $kernel_off, $kernel_addr, $kernel_len;
+	printf FO "\n";
+	printf FO "fis create -o 0x%x -s 0x%x -f 0x%x -l 0x%x rootfs\n", $rootfs_off, $rootfs_size, $rootfs_addr, $rootfs_len;
+	printf FO "\n";
+	printf FO "reset\n";
+	close FO;
+}
+
+# MAIN
+gen_script();
diff --git a/scripts/relink-lib.sh b/scripts/relink-lib.sh
new file mode 100755
index 0000000..5367b70
--- /dev/null
+++ b/scripts/relink-lib.sh
@@ -0,0 +1,14 @@
+#!/bin/sh
+[ $# -lt 4 -o -z "$1" -o -z "$2" -o -z "$3" -o -z "$4" ] && {
+	echo "Usage: $0 <cross> <reference> <pic .a> <destination>"
+	exit 1
+}
+
+cross="$1"; shift
+ref="$1"; shift
+pic="$1"; shift
+dest="$1"; shift
+
+SYMBOLS="$(${cross}nm "$ref" | grep -E '........ [TW] ' | awk '$3 {printf "-u%s ", $3}')"
+set -x
+${cross}gcc -nostdlib -nostartfiles -shared -Wl,--gc-sections -o "$dest" $SYMBOLS "$pic" "$@"
diff --git a/scripts/remote-gdb b/scripts/remote-gdb
new file mode 100755
index 0000000..d8e2336
--- /dev/null
+++ b/scripts/remote-gdb
@@ -0,0 +1,89 @@
+#!/usr/bin/env perl
+
+use strict;
+use warnings;
+use FindBin '$Bin';
+use File::Temp 'tempfile';
+
+@ARGV == 2 || do {
+	die "Usage: $0 <corefile|host:port> <executable>\n";
+	exit 1;
+};
+
+if( opendir SD, "$Bin/../staging_dir" )
+{
+	my ( $tid, $arch, $libc, @arches );
+
+	if( $ARGV[1] =~ m!\btarget-(.+?)_([^/_]+libc|musl)_?([^/]*).*\b!i )
+	{
+		print("Using target $1 ($2, $3)\n");
+		($arch, $libc) = ($1, $2);
+	}
+	else
+	{
+		# Find arches
+		print("Choose target:\n");
+
+		while( defined( my $e = readdir SD ) )
+		{
+			if( -d "$Bin/../staging_dir/$e" && $e =~ /^target-(.+?)_([^\/_]+libc|musl)_?([^\/]*).*/i )
+			{
+				push @arches, [ $1, $2 ];
+				printf(" %2d) %s (%s %s)\n", @arches + 0, $1, $2, $3);
+			}
+		}
+
+		if( @arches > 1 )
+		{
+			# Query arch
+			do {
+				print("Target? > ");
+				chomp($tid = <STDIN>);
+			} while( !defined($tid) || $tid !~ /^\d+$/ || $tid < 1 || $tid > @arches );
+
+			($arch, $libc) = @{$arches[$tid-1]};
+		}
+		else
+		{
+			($arch, $libc) = @{$arches[0]};
+		}
+	}
+
+	closedir SD;
+
+	# Find gdb
+	my ($gdb) = glob("$Bin/../staging_dir/toolchain-${arch}_*_${libc}*/bin/*-gdb");
+	if( defined($gdb) && -x $gdb )
+	{
+		my ( $fh, $fp ) = tempfile();
+
+		# Find sysroot
+		my ($sysroot) = glob("$Bin/../staging_dir/target-${arch}_${libc}*/root-*/");
+
+		print $fh "set sysroot $sysroot\n" if $sysroot;
+		my $cmd = "target extended-remote";
+		-f $ARGV[0] and $cmd = "core-file";
+		print $fh "$cmd $ARGV[0]\n";
+
+		# History settings
+		print $fh "set history filename $Bin/../tmp/.gdb_history\n";
+		print $fh "set history size 100000000\n";
+		print $fh "set history save on\n";
+
+		my $file = -f "$sysroot/$ARGV[1]" ? "$sysroot/$ARGV[1]" : $ARGV[1];
+		system($gdb, '-x', $fp, $file);
+
+		close($fh);
+		unlink($fp);
+	}
+	else
+	{
+		print("No gdb found! Make sure that CONFIG_GDB is set!\n");
+		exit(1);
+	}
+}
+else
+{
+	print("No staging_dir found! You need to compile at least once!\n");
+	exit(1);
+}
diff --git a/scripts/rstrip.sh b/scripts/rstrip.sh
new file mode 100755
index 0000000..2aa7e96
--- /dev/null
+++ b/scripts/rstrip.sh
@@ -0,0 +1,50 @@
+#!/usr/bin/env bash
+# 
+# Copyright (C) 2006 OpenWrt.org
+#
+# This is free software, licensed under the GNU General Public License v2.
+# See /LICENSE for more information.
+#
+SELF=${0##*/}
+
+[ -z "$STRIP" ] && {
+  echo "$SELF: strip command not defined (STRIP variable not set)"
+  exit 1
+}
+
+TARGETS=$*
+
+[ -z "$TARGETS" ] && {
+  echo "$SELF: no directories / files specified"
+  echo "usage: $SELF [PATH...]"
+  exit 1
+}
+
+find $TARGETS -not -path \*/lib/firmware/\* -a -type f -a -exec file {} \; | \
+  sed -n -e 's/^\(.*\):.*ELF.*\(executable\|relocatable\|shared object\).*,.*/\1:\2/p' | \
+(
+  IFS=":"
+  while read F S; do
+    echo "$SELF: $F: $S"
+	[ "${S}" = "relocatable" ] && {
+		[ "${F##*.}" == "o" ] && continue
+		eval "$STRIP_KMOD $F"
+	} || {
+		b=$(stat -c '%a' $F)
+		[ -z "$PATCHELF" ] || [ -z "$TOPDIR" ] || {
+			old_rpath="$($PATCHELF --print-rpath $F)"; new_rpath=""
+			for path in $old_rpath; do
+				case "$path" in
+					/lib/[^/]*|/usr/lib/[^/]*|\$ORIGIN/*|\$ORIGIN) new_rpath="${new_rpath:+$new_rpath:}$path" ;;
+					*) echo "$SELF: $F: removing rpath $path" ;;
+				esac
+			done
+			[ "$new_rpath" = "$old_rpath" ] || $PATCHELF --set-rpath "$new_rpath" $F
+		}
+		eval "$STRIP $F"
+		a=$(stat -c '%a' $F)
+		[ "$a" = "$b" ] || chmod $b $F
+	}
+  done
+  true
+)
diff --git a/scripts/sercomm-crypto.py b/scripts/sercomm-crypto.py
new file mode 100755
index 0000000..bed3e49
--- /dev/null
+++ b/scripts/sercomm-crypto.py
@@ -0,0 +1,86 @@
+#!/usr/bin/env python3
+
+import argparse
+import binascii
+import hashlib
+import os
+import struct
+
+def create_header(key, version, iv, random, size):
+	header = struct.pack('32s32s32s32s32s', key, version, iv, random, size)
+
+	return header
+
+def create_output(args):
+	in_st = os.stat(args.input_file)
+	in_size = in_st.st_size
+
+	key = "".encode('ascii')
+	version = args.version.encode('ascii')
+	iv = "".encode('ascii')
+	random = "".encode('ascii')
+	size = str(in_size).encode('ascii')
+	header = create_header(key, version, iv, random, size)
+
+	out_f = open(args.output_file, 'w+b')
+	out_f.write(header)
+	out_f.close()
+
+	md5 = hashlib.md5()
+	md5.update(header[0x60:0x80])
+	md5.update(header[0x20:0x40])
+	md5_1 = md5.digest()
+
+	md5 = hashlib.md5()
+	md5.update(header[0x80:0xA0])
+	md5.update(header[0x20:0x40])
+	md5_2 = md5.digest()
+
+	key = md5_1 + md5_2
+
+	key_f = open(args.key_file, 'w+b')
+	key_f.write(binascii.hexlify(bytearray(key)))
+	key_f.close()
+
+	print("AES 256 CBC Key:", binascii.hexlify(bytearray(key)))
+
+def main():
+	global args
+
+	parser = argparse.ArgumentParser(description='')
+
+	parser.add_argument('--input-file',
+		dest='input_file',
+		action='store',
+		type=str,
+		help='Input file')
+
+	parser.add_argument('--key-file',
+		dest='key_file',
+		action='store',
+		type=str,
+		help='AES 256 CBC Key File')
+
+	parser.add_argument('--output-file',
+		dest='output_file',
+		action='store',
+		type=str,
+		help='Output file')
+
+	parser.add_argument('--version',
+		dest='version',
+		action='store',
+		type=str,
+		help='Version')
+
+	args = parser.parse_args()
+
+	if ((not args.input_file) or
+	    (not args.key_file) or
+	    (not args.output_file) or
+	    (not args.version)):
+		parser.print_help()
+
+	create_output(args)
+
+main()
diff --git a/scripts/sercomm-kernel-header.py b/scripts/sercomm-kernel-header.py
new file mode 100755
index 0000000..40bcbb1
--- /dev/null
+++ b/scripts/sercomm-kernel-header.py
@@ -0,0 +1,121 @@
+#!/usr/bin/env python3
+"""
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# sercomm-kernel-header.py: Creates Sercomm kernel header
+#
+# Copyright © 2022 Mikhail Zhilkin
+"""
+
+import argparse
+import binascii
+import os
+import struct
+
+KERNEL_HEADER_SIZE = 0x100
+PADDING = 0xff
+ROOTFS_FAKE_HEADER = "UBI#"
+
+def auto_int(x):
+	return int(x, 0)
+
+def create_kernel_header(args):
+	out_file = open(args.header_file, "wb")
+	header = get_kernel_header(args)
+	out_file.write(header)
+	out_file.close()
+
+def get_kernel_header(args):
+	header = bytearray([PADDING] * KERNEL_HEADER_SIZE)
+
+	struct.pack_into('<L', header, 0xc, 0xffffff02)
+	struct.pack_into('<L', header, 0x1c, 0x0)
+	struct.pack_into('<L', header, 0x34, 0x0)
+	struct.pack_into('<L', header, 0x10, args.kernel_offset)
+	struct.pack_into('<L', header, 0x28, args.rootfs_offset)
+
+	if (args.rootfs_file):
+		if (args.rootfs_checking_size):
+			rootfs_size = args.rootfs_checking_size
+		else:
+			rootfs_size = os.path.getsize(args.rootfs_file)
+		buf = open(args.rootfs_file,'rb').read(rootfs_size)
+		crc = binascii.crc32(buf) & 0xffffffff
+	else:
+		rootfs_size = len(ROOTFS_FAKE_HEADER)
+		crc = binascii.crc32(str.encode(ROOTFS_FAKE_HEADER)) & \
+		      0xffffffff
+	struct.pack_into('<L', header, 0x2c, rootfs_size)
+	struct.pack_into('<L', header, 0x30, crc)
+
+	kernel_size = os.path.getsize(args.kernel_file)
+	struct.pack_into('<L', header, 0x14, kernel_size)
+
+	kernel_end_offset = args.kernel_offset + kernel_size
+	struct.pack_into('<L', header, 0x4, kernel_end_offset)
+
+	buf = open(args.kernel_file,'rb').read()
+	crc = binascii.crc32(buf) & 0xffffffff
+	struct.pack_into('<L', header, 0x18, crc)
+
+	crc = binascii.crc32(header) & 0xffffffff
+	struct.pack_into('<L', header, 0x8, crc)
+
+	struct.pack_into('<L', header, 0x0, 0x726553)
+
+	return header
+
+def main():
+	global args
+
+	parser = argparse.ArgumentParser(description='This script generates \
+		a kernel header for the Sercomm mt7621 devices')
+
+	parser.add_argument('--kernel-image',
+		dest='kernel_file',
+		action='store',
+		type=str,
+		help='Path to a Kernel binary image')
+
+	parser.add_argument('--kernel-offset',
+		dest='kernel_offset',
+		action='store',
+		type=auto_int,
+		help='Kernel offset')
+
+	parser.add_argument('--rootfs-offset',
+		dest='rootfs_offset',
+		action='store',
+		type=auto_int,
+		help='RootFS offset')
+
+	parser.add_argument('--output-header',
+		dest='header_file',
+		action='store',
+		type=str,
+		help='Output kernel header file')
+
+	parser.add_argument('--rootfs-image',
+		dest='rootfs_file',
+		action='store',
+		type=str,
+		help='Path to RootFS binary image (optional)')
+
+	parser.add_argument('--rootfs-checking-size',
+		dest='rootfs_checking_size',
+		action='store',
+		type=auto_int,
+		help='Bytes count for CRC calculation (optional)')
+
+	args = parser.parse_args()
+
+	if ((not args.kernel_file) or
+	    (not args.kernel_offset) or
+	    (not args.rootfs_offset) or
+	    (not args.header_file)):
+		parser.print_help()
+		exit()
+
+	create_kernel_header(args)
+
+main()
diff --git a/scripts/sercomm-partition-tag.py b/scripts/sercomm-partition-tag.py
new file mode 100755
index 0000000..ef49af8
--- /dev/null
+++ b/scripts/sercomm-partition-tag.py
@@ -0,0 +1,81 @@
+#!/usr/bin/env python3
+
+import argparse
+import os
+import struct
+
+def create_header(args, size):
+	header = struct.pack('32s32s32s32s32s',
+		args.part_name.encode('ascii'),
+		str(size).encode('ascii'),
+		args.part_version.encode('ascii'),
+		"".encode('ascii'),
+		args.rootfs_version.encode('ascii'))
+
+	return header
+
+def create_output(args):
+	in_st = os.stat(args.input_file)
+	in_size = in_st.st_size
+
+	header = create_header(args, in_size)
+	print(header)
+
+	in_f = open(args.input_file, 'r+b')
+	in_bytes = in_f.read(in_size)
+	in_f.close()
+
+	out_f = open(args.output_file, 'w+b')
+	out_f.write(header)
+	out_f.write(in_bytes)
+	out_f.close()
+
+def main():
+	global args
+
+	parser = argparse.ArgumentParser(description='')
+
+	parser.add_argument('--input-file',
+		dest='input_file',
+		action='store',
+		type=str,
+		help='Input file')
+
+	parser.add_argument('--output-file',
+		dest='output_file',
+		action='store',
+		type=str,
+		help='Output file')
+
+	parser.add_argument('--part-name',
+		dest='part_name',
+		action='store',
+		type=str,
+		help='Partition Name')
+
+	parser.add_argument('--part-version',
+		dest='part_version',
+		action='store',
+		type=str,
+		help='Partition Version')
+
+	parser.add_argument('--rootfs-version',
+		dest='rootfs_version',
+		action='store',
+		type=str,
+		help='RootFS lib version')
+
+	args = parser.parse_args()
+
+	if not args.rootfs_version:
+		args.rootfs_version = ""
+
+	if ((not args.input_file) or
+	    (not args.output_file) or
+	    (not args.part_name) or
+	    (not args.part_version)):
+		parser.print_help()
+
+	create_output(args)
+
+main()
diff --git a/scripts/sercomm-payload.py b/scripts/sercomm-payload.py
new file mode 100755
index 0000000..2390d5d
--- /dev/null
+++ b/scripts/sercomm-payload.py
@@ -0,0 +1,72 @@
+#!/usr/bin/env python3
+
+import argparse
+import hashlib
+import os
+
+def create_output(args):
+	in_st = os.stat(args.input_file)
+	in_size = in_st.st_size
+
+	in_f = open(args.input_file, 'r+b')
+	in_bytes = in_f.read(in_size)
+	in_f.close()
+
+	if (args.pid_file):
+		pid_st = os.stat(args.pid_file)
+		pid_size = pid_st.st_size
+
+		pid_f = open(args.pid_file, 'r+b')
+		pid_bytes = pid_f.read(pid_size)
+		pid_f.close()
+	else:
+		pid_bytes = bytes.fromhex(args.pid)
+
+	sha256 = hashlib.sha256()
+	sha256.update(in_bytes)
+
+	out_f = open(args.output_file, 'w+b')
+	out_f.write(pid_bytes)
+	out_f.write(sha256.digest())
+	out_f.write(in_bytes)
+	out_f.close()
+
+def main():
+	global args
+
+	parser = argparse.ArgumentParser(description='')
+
+	parser.add_argument('--input-file',
+		dest='input_file',
+		action='store',
+		type=str,
+		help='Input file')
+
+	parser.add_argument('--output-file',
+		dest='output_file',
+		action='store',
+		type=str,
+		help='Output file')
+
+	parser.add_argument('--pid-file',
+		dest='pid_file',
+		action='store',
+		type=str,
+		help='Sercomm PID file')
+
+	parser.add_argument('--pid',
+		dest='pid',
+		action='store',
+		type=str,
+		help='Sercomm PID')
+
+	args = parser.parse_args()
+
+	if ((not args.input_file) or
+	    (not args.output_file) or
+	    (not args.pid_file and not args.pid)):
+		parser.print_help()
+
+	create_output(args)
+
+main()
diff --git a/scripts/sercomm-pid.py b/scripts/sercomm-pid.py
new file mode 100755
index 0000000..2c246fc
--- /dev/null
+++ b/scripts/sercomm-pid.py
@@ -0,0 +1,108 @@
+#!/usr/bin/env python3
+"""
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# sercomm-pid.py: Creates Sercomm device PID
+#
+# Copyright © 2022 Mikhail Zhilkin
+"""
+
+import argparse
+import binascii
+import struct
+
+PID_SIZE	= 0x70
+PADDING		= 0x30
+PADDING_TAIL	= 0x0
+
+def auto_int(x):
+	return int(x, 0)
+
+def create_pid_file(args):
+	pid_file = open(args.pid_file, "wb")
+	buf = get_pid(args)
+	pid_file.write(buf)
+	pid_file.close()
+
+def get_pid(args):
+	buf = bytearray([PADDING] * PID_SIZE)
+
+	if not args.hw_id:
+		enc = args.hw_version.rjust(14, '0').encode('ascii')
+		struct.pack_into('>14s', buf, 0x0, enc)
+	else:
+		enc = args.hw_version.rjust(8, '0').encode('ascii')
+		struct.pack_into('>8s', buf, 0x0, enc)
+
+		enc = binascii.hexlify(args.hw_id.encode()).upper()
+		struct.pack_into('>6s', buf, 0x8, enc)
+
+	enc = args.sw_version.rjust(4, '0').encode('ascii')
+	struct.pack_into('>4s', buf, 0x64, enc)
+
+	if (args.extra_padd_size):
+		tail = bytearray([PADDING_TAIL] * args.extra_padd_size)
+		if (args.extra_padd_byte):
+			struct.pack_into ('<i', tail, 0x0,
+					  args.extra_padd_byte)
+		elif not args.hw_id:
+			tail[0] = 0x0D
+			tail[1] = 0x0A
+		buf += tail
+
+	return buf
+
+def main():
+	global args
+
+	parser = argparse.ArgumentParser(description='This script \
+		generates firmware PID for the Sercomm-based devices')
+
+	parser.add_argument('--hw-version',
+		dest='hw_version',
+		action='store',
+		type=str,
+		help='Sercomm hardware version')
+
+	parser.add_argument('--hw-id',
+		dest='hw_id',
+		action='store',
+		type=str,
+		help='Sercomm hardware ID')
+
+	parser.add_argument('--sw-version',
+		dest='sw_version',
+		action='store',
+		type=str,
+		help='Sercomm software version')
+
+	parser.add_argument('--pid-file',
+		dest='pid_file',
+		action='store',
+		type=str,
+		help='Output PID file')
+
+	parser.add_argument('--extra-padding-size',
+		dest='extra_padd_size',
+		action='store',
+		type=auto_int,
+		help='Size of extra NULL padding at the end of the file \
+			(optional)')
+
+	parser.add_argument('--extra-padding-first-byte',
+		dest='extra_padd_byte',
+		action='store',
+		type=auto_int,
+		help='First byte of extra padding (optional)')
+
+	args = parser.parse_args()
+
+	if ((not args.hw_version) or
+	    (not args.sw_version) or 
+	    (not args.pid_file)):
+		parser.print_help()
+		exit()
+
+	create_pid_file(args)
+
+main()
diff --git a/scripts/sign_images.sh b/scripts/sign_images.sh
new file mode 100755
index 0000000..f37c2f5
--- /dev/null
+++ b/scripts/sign_images.sh
@@ -0,0 +1,27 @@
+#!/bin/sh
+
+# directory where search for images
+TOP_DIR="${TOP_DIR:-./bin/targets}"
+# key to sign images
+BUILD_KEY="${BUILD_KEY:-key-build}" # TODO unify naming?
+# remove other signatures (added e.g.  by buildbot)
+REMOVE_OTER_SIGNATURES="${REMOVE_OTER_SIGNATURES:-1}"
+
+# find all sysupgrade images in TOP_DIR
+# factory images don't need signatures as non OpenWrt system doesn't check them anyway
+for image in $(find $TOP_DIR -type f -name "*-sysupgrade.bin"); do
+	# check if image actually support metadata
+	if fwtool -i /dev/null "$image"; then
+		# remove all previous signatures
+		if [ -n "$REMOVE_OTER_SIGNATURES" ]; then
+			while [ "$?" = 0 ]; do
+				fwtool -t -s /dev/null "$image"
+			done
+		fi
+		# run same operation as build root does for signing
+		cp "$BUILD_KEY.ucert" "$image.ucert"
+		usign -S -m "$image" -s "$BUILD_KEY" -x "$image.sig"
+		ucert -A -c "$image.ucert" -x "$image.sig"
+		fwtool -S "$image.ucert" "$image"
+	fi
+done
diff --git a/scripts/size_compare.sh b/scripts/size_compare.sh
new file mode 100755
index 0000000..8417d25
--- /dev/null
+++ b/scripts/size_compare.sh
@@ -0,0 +1,157 @@
+# SPDX-License-Identifier: GPL-2.0-or-later
+#
+# Copyright (C) 2020 Paul Spooren <mail@aparcar.org>
+#
+###
+### size_compare - compare size of OpenWrt packages against upstream
+###
+### The script compares locally compiled package with the package indexes
+### available upstream. This way the storage impact of optimizations or
+### feature modifications is easy to see.
+###
+### If no environmental variables are set the script reads the current
+### .config file. The evaluated env variables are the following:
+###
+###   TARGET SUBTARGET ARCH PACKAGES BIN_DIR BASE_URL CHECK_INSTALLED
+###
+### Usage:
+###   ./scripts/size_compare.sh
+###
+### Options:
+###   -p --package-size 	Check IPK package size and not installed size
+###   -h --help 		This message
+
+eval "$(grep \
+	-e ^CONFIG_TARGET_BOARD= \
+	-e ^CONFIG_TARGET_SUBTARGET= \
+	-e ^CONFIG_TARGET_ARCH_PACKAGES= \
+	-e ^CONFIG_BINARY_FOLDER= \
+	.config 2>/dev/null \
+)"
+CONFIG_PACKAGES=$(sed -n 's/^CONFIG_PACKAGE_\(.*\)=y$/\1/p' .config | tr '\n' ' ')
+
+TARGET=${TARGET:-$CONFIG_TARGET_BOARD}
+SUBTARGET=${SUBTARGET:-$CONFIG_TARGET_SUBTARGET}
+ARCH=${ARCH:-$CONFIG_TARGET_ARCH_PACKAGES}
+PACKAGES=${PACKAGES:-$CONFIG_PACKAGES}
+BIN_DIR=${CONFIG_BINARY_FOLDER:-./bin}
+BASE_URL="${BASE_URL:-https://downloads.openwrt.org/snapshots}"
+CHECK_INSTALLED="${CHECK_INSTALLED:-y}"
+
+TARGET_URL="$BASE_URL/targets/$TARGET/$SUBTARGET/packages/Packages.gz"
+CONFIG_URL="$BASE_URL/targets/$TARGET/$SUBTARGET/config.buildinfo"
+PACKAGES_URL="$BASE_URL/packages/$ARCH/base/Packages.gz"
+
+if command -v curl > /dev/null; then
+	DOWNLOAD_METHOD="curl"
+else
+	DOWNLOAD_METHOD="wget --output-document=-"
+fi
+
+help() {
+    sed -rn 's/^### ?//;T;p' "$0"
+}
+
+package_size () {
+	FOUND_PACKAGE=
+	if [ -z "$CHECK_INSTALLED" ]; then
+		SEARCH_PATTERN="Size"
+	else
+		SEARCH_PATTERN="Installed-Size"
+	fi
+	while IFS= read -r line; do
+		if [ "$line" = "Package: $2" ]; then
+			FOUND_PACKAGE=y
+		fi
+		if [ -n "$FOUND_PACKAGE" ]; then
+			case $line in
+				"$SEARCH_PATTERN"*)
+					echo "$line" | cut -d ' ' -f 2
+					break
+					;;
+			esac
+		fi
+	done < "$1"
+}
+
+compare_sizes () {
+	TOTAL_DIFF="0"
+	for PACKAGE in $PACKAGES; do
+		if [ "$PACKAGE" = "libc" ]; then
+			continue
+		fi
+		PACKAGE_FILE=$(find "$BIN_DIR/packages/$ARCH/" \
+			"$BIN_DIR/targets/$TARGET/$SUBTARGET/" \
+			-name "${PACKAGE}_*.ipk" 2>/dev/null | head -n1)
+
+		if [ -z "$PACKAGE_FILE" ]; then
+			continue
+		fi
+		if [ -z "$CHECK_INSTALLED" ]; then
+			SIZE_LOCAL=$(stat -c '%s' "$PACKAGE_FILE")
+		else
+			SIZE_LOCAL=$(tar tzvf "$PACKAGE_FILE" ./data.tar.gz | awk '{ print $3 }')
+		fi
+		SIZE_UPSTREAM=$(package_size "$TMP_INDEX" "$PACKAGE")
+		SIZE_DIFF="$((SIZE_LOCAL - SIZE_UPSTREAM))"
+		TOTAL_DIFF="$((TOTAL_DIFF + SIZE_DIFF))"
+		if [ "$SIZE_DIFF" -gt 0 ]; then
+			SIZE_DIFF="+$SIZE_DIFF"
+		fi
+		if [ -n "$SIZE_UPSTREAM" ]; then
+			echo "${SIZE_DIFF}	${SIZE_LOCAL}	${SIZE_UPSTREAM}	$PACKAGE"
+		else
+			echo "$PACKAGE is missing upstream"
+		fi
+	done
+	echo "~~~~~~~	total change	${TOTAL_DIFF}"
+}
+
+if [ "$1" = "-h" ]; then
+    help
+    exit 0
+fi
+
+if [ "$1" = "-p" ]; then
+    CHECK_INSTALLED=
+fi
+
+echo "Compare packages of $TARGET/$SUBTARGET/$ARCH":
+echo "$PACKAGES"
+echo
+
+echo "Checking configuration difference"
+TMP_CONFIG=$(mktemp /tmp/config.XXXXXX)
+sed -n 's/^	\+config \(.*\)/\1/p' config/Config-build.in config/Config-devel.in > "${TMP_CONFIG}-FOCUS"
+sort .config | grep -f "${TMP_CONFIG}-FOCUS" | grep -v "^#" | sort > "${TMP_CONFIG}-LOCAL"
+mv .config .config.bak
+"$DOWNLOAD_METHOD" "$CONFIG_URL" > .config
+make defconfig > /dev/null 2> /dev/null
+grep -f "${TMP_CONFIG}-FOCUS" .config | grep -v "^#" | sort > "${TMP_CONFIG}-UPSTREAM"
+mv .config.bak .config
+
+echo
+echo " --- start config diff ---"
+diff -u "${TMP_CONFIG}-LOCAL" "${TMP_CONFIG}-UPSTREAM"
+echo " --- end config diff ---"
+rm "${TMP_CONFIG}-FOCUS" "${TMP_CONFIG}-UPSTREAM" "${TMP_CONFIG}-LOCAL"
+echo
+
+if [ -z "$CHECK_INSTALLED" ]; then
+	echo "Checking IPK package size"
+else
+	echo "Checking installed size"
+fi
+echo
+
+echo "Fetching latest package indexes..."
+TMP_INDEX=$(mktemp /tmp/size_compare_package_index.XXXXXX)
+"$DOWNLOAD_METHOD" "$TARGET_URL" | gzip -d > "$TMP_INDEX" || exit 1
+"$DOWNLOAD_METHOD" "$PACKAGES_URL" | gzip -d >> "$TMP_INDEX" || exit 1
+echo
+
+echo "Comparing package sizes..."
+echo "Change 	Local	Remote 	Package"
+compare_sizes | sort -g -r
+
+rm "$TMP_INDEX"
diff --git a/scripts/slugimage.pl b/scripts/slugimage.pl
new file mode 100755
index 0000000..366b239
--- /dev/null
+++ b/scripts/slugimage.pl
@@ -0,0 +1,1217 @@
+#!/usr/bin/env perl
+# 
+# SlugImage : Manipulate NSLU2 firmware images
+#             Dwayne Fontenot (jacques)
+#             Rod Whitby (rwhitby)
+#	      www.nslu2-linux.org
+#
+# Copyright (c) 2004, 2006, Dwayne Fontenot & Rod Whitby
+# All rights reserved.
+# 
+# Redistribution and use in source and binary forms, with or without
+# modification, are permitted provided that the following conditions
+# are met:
+# 
+# Redistributions of source code must retain the above copyright
+# notice, this list of conditions and the following disclaimer.
+# Redistributions in binary form must reproduce the above copyright
+# notice, this list of conditions and the following disclaimer in the
+# documentation and/or other materials provided with the distribution.
+# Neither the name of the NSLU2-Linux Development Team nor the names
+# of its contributors may be used to endorse or promote products
+# derived from this software without specific prior written
+# permission.
+# 
+# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
+# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+# POSSIBILITY OF SUCH DAMAGE.
+#
+
+use strict;
+use warnings;
+
+use Getopt::Long qw(:config no_ignore_case);
+use File::Temp qw(tempfile);
+
+my($debug) = 0;
+my($quiet) = 0;
+my($flash_start)    = 0x50000000;
+my($flash_len)      = 0x00800000;
+my($block_size)     = 0x00020000;
+my($kernel_offset)  = 0x00060000;
+my($kernel_size)    = 0x00100000;
+my($ramdisk_offset) = 0x00160000;
+my(@cleanup);
+
+# The last 70 bytes of the SercommRedBootTrailer (i.e. excluding MAC
+# address).  Needed to create an image with an empty RedBoot partition
+# since the Sercomm upgrade tool checks for this trailer.
+# http://www.nslu2-linux.org/wiki/Info/SercommRedBootTrailer
+my @sercomm_redboot_trailer = (0x4573, 0x4372, 0x4d6f, 0x006d, 0x0001,
+       0x0400, 0x3170, 0x5895, 0x0010, 0x0000, 0x0000, 0x0000, 0x0000,
+       0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000,
+       0x0000, 0x0000, 0x0001, 0x0000, 0x0000, 0x0000, 0x0003, 0x2300,
+       0x0063, 0x0000, 0x7320, 0x7245, 0x6f43, 0x6d4d);
+
+# There's a 16 byte Sercomm trailer at the end of the flash. It is used
+# by RedBoot to detect a Sercomm flash layout and to configure the
+# Sercomm upgrade system.
+# http://www.nslu2-linux.org/wiki/Info/SercommFlashTrailer
+my @sercomm_flash_trailer = (0x0100, 0x0000, 0x6323, 0xf790, 0x5265,
+                             0x4f63, 0x4d6d, 0xb400);
+
+# Take $data, and pad it out to $total_len bytes, appending 0xff's.
+sub padBytes {
+    my($data,$total_len) = @_;
+
+    # 0xFF is used to pad, as it's the erase value of the flash.
+    my($pad_char) = pack("C",0xff);
+    my($pad_len) = $total_len - length($data);
+
+    # A request for negative padding is indicative of a logic error ...
+    if (length($data) > $total_len) {
+	die sprintf("padBytes error: data (%d) is longer than total_len (%d)", length($data), $total_len);
+    }
+
+    return $data . ($pad_char x $pad_len);
+}
+
+# Return the next multiple of block_size larger than or equal to $data_len.
+sub paddedSize {
+    my($data_len) = @_;
+
+    use integer;
+    return (($data_len - 1) / $block_size) * $block_size + $block_size;
+}
+
+# Return the number of block_size blocks required to hold $data_len.
+sub numBlocks {
+    my($data_len) = @_;
+
+    use integer;
+    return (($data_len - 1) / $block_size) + 1;
+}
+
+# Pack the name, address, size and optional skip regions of a partition entry into binary form.
+sub createPartitionEntry {
+    my($name, $flash_base, $size, $skips) = @_;
+    my $entry;
+
+    my($zero_long) = 0x0000;
+
+    # Pack the partition entry according to the format that RedBoot (and the MTD partition parsing code) requires.
+    $entry = pack("a16N5x212N2",$name,$flash_base,$zero_long,$size,$zero_long,$zero_long,$zero_long,$zero_long);
+
+    # Optionally put a skip header into the padding area.
+    if (defined $skips) {
+	my $i = scalar(@$skips);
+	foreach my $region (@$skips) {
+	    substr($entry, -8 - 12*$i, 12) =
+		pack("a4N2", "skip", $region->{'offset'}, $region->{'size'});
+	    $i--;
+	}
+    }
+
+    return $entry;
+}
+
+# Parse partition entry and return anon array ref [$name, $offset, $size, $skip] or return 0 on partition terminator.
+sub parsePartitionEntry {
+    my($partition_entry) = @_;
+
+    my($entry_len) = 0x100;
+    length($partition_entry) eq $entry_len or die "parsePartitionEntry: partition entry length is not $entry_len!\n";
+
+    # Unpack the partition table entry, saving those values in which we are interested.
+    my($name, $flash_base, $size, $dummy_long, $padding, $skips);
+    ($name, $flash_base, $dummy_long, $size, $dummy_long, $dummy_long, $padding, $dummy_long, $dummy_long) =
+	unpack("a16N5a212N2",$partition_entry);
+
+    # A partition entry starting with 0xFF terminates the table.
+    if (unpack("C", $name) eq 0xff) {
+	# %%% FIXME: This should only skip, not terminate. %%%
+	$debug and print "Found terminator for <FIS directory>\n";
+	return 0;
+    }
+
+    # Remove trailing nulls from the partition name.
+    $name =~ s/\000+//;
+
+    # Extract the skip regions out of the padding area.
+    $padding =~ s/^\000+//;
+    $padding =~ s/\000*skip(........)\000*/$1/g;
+    $padding =~ s/\000+$//;
+
+    # Store the skip regions in an array for later use.
+    while (length($padding)) {
+	my $region = {};
+	($region->{'offset'}, $region->{'size'}) =
+	    unpack("N2", $padding);
+	$debug and printf("Found skip region at 0x%05X, size 0x%05X\n",
+			  $region->{'offset'}, $region->{'size'});
+	push(@$skips, $region);
+	$padding = substr($padding,8);
+    }
+
+    return [$name, $flash_base - $flash_start, $size, $skips];
+}
+
+# Return partition table from data is one exists, otherwise return 0.
+sub findPartitionTable {
+    my($data_buf) = @_;
+
+    unpack("a7", $data_buf) eq 'RedBoot' or return 0;
+    return substr($data_buf, 0, 0x1000)
+}
+
+# Parse partition table and return array of anonymous array references ([$name, $offset, $size, $skips], ...).
+sub parsePartitionTable {
+    my($partition_table) = @_;
+
+    my(@partitions, $fields_ref);
+    my($entry_len) = 0x100;
+    my($partition_count) = 0;
+
+    # Loop through the fixed size partition table entries, and store the entries in @partitions.
+    # %%% FIXME: This doesn't handle the case of a completely full partition table. %%%
+    while ($fields_ref = parsePartitionEntry(substr($partition_table, $partition_count * $entry_len, $entry_len))) {
+	$debug and printf("Found <%s> at 0x%08X (%s)%s\n", $fields_ref->[0], $fields_ref->[1],
+			  ($fields_ref->[2] >= $block_size ?
+			   sprintf("%d blocks", numBlocks($fields_ref->[2])) :
+			   sprintf("0x%05X bytes", $fields_ref->[2])),
+			  (defined $fields_ref->[3] ?
+			   sprintf(" [%s]",
+				   join(", ",
+					map { sprintf("0x%05X/0x%05X", $_->{'offset'},$_->{'size'}) }
+					@{$fields_ref->[3]})) :
+			   ""));
+	$partitions[$partition_count++] = $fields_ref;
+    }
+    return(@partitions);
+}
+
+# Create an empty jffs2 block.
+sub jffs2Block {
+    return padBytes(pack("N3", 0x19852003, 0x0000000c, 0xf060dc98), $block_size);
+}
+
+# Write out $data to $filename,
+sub writeOut {
+    my($data, $filename) = @_;
+
+    open FILE,">$filename" or die "Can't open file \"$filename\": $!\n";
+
+    if (defined($data)) { print FILE $data;}
+
+    close FILE or die "Can't close file \"$filename\": $!\n";
+}
+
+# Not used at the moment.
+sub trailerData {
+    my($product_id)       = 0x0001;
+    my($protocol_id)      = 0x0000;
+    my($firmware_version) = 0x2325;
+    my($unknown1)         = 0x90f7;
+    my($magic_number)     = 'eRcOmM';
+    my($unknown2)         = 0x00b9;
+
+    return pack("n4a6n",$product_id,$protocol_id,$firmware_version,$unknown1,$magic_number,$unknown2);
+}
+
+# Print the contents of the Sercomm RedBoot trailer.
+sub printRedbootTrailer {
+    my($redboot_data) = @_;
+
+    my($correct_redboot_len) = 0x40000;
+    my($redboot_data_len) = length($redboot_data);
+
+    if ($redboot_data_len != $correct_redboot_len) {
+	printf("Redboot length (0x%08X) is not 0x%08X\n", $redboot_data_len, $correct_redboot_len);
+	return;
+    }
+
+    # The trailer is the last 80 bytes of the redboot partition.
+    my($redboot_trailer) = substr($redboot_data, -80);
+
+    writeOut($redboot_trailer, 'RedbootTrailer');
+
+    my($mac_addr0, $mac_addr1, $mac_addr2, $unknown, $prefix, $ver_ctrl, $down_ctrl, $hid, $hver, $prodid, $prodidmask,
+       $protid, $protidmask, $funcid, $funcidmask, $fver, $cseg, $csize, $postfix) =
+	   unpack("n3Na7n2a32n10a7",$redboot_trailer);
+
+    printf("MAC address is %04X%04X%04X\n", $mac_addr0, $mac_addr1, $mac_addr2);
+    printf("unknown: %08X\n", $unknown);
+    printf("%s:%04X:%04X:%s\n", $prefix, $ver_ctrl, $down_ctrl, $postfix);
+    printf("VerControl: %04X\nDownControl: %04X\n", $ver_ctrl, $down_ctrl);
+    printf("hid: %04X %04X %04X %04X %04X %04X %04X %04X %04X %04X %04X %04X %04X %04X %04X %04X\n", unpack("n16", $hid));
+    printf("Hver: %04X\nProdID: %04X\nProtID: %04X\nFuncID: %04X\nFver: %04X\nCseg: %04X\nCsize: %04X\n",
+	   $hver, $prodid, $protid, $funcid, $fver, $cseg, $csize);
+}
+
+# remove the optional Loader partition
+sub removeOptionalLoader {
+    my($partitions_ref) = @_;
+
+    my $index;
+    my $count = 0;
+    map {
+	if (not defined $index) {
+	    if ($_->{'name'} eq "Loader") {
+		$index = $count;
+	    }
+	    $count++;
+	}
+    } @$partitions_ref;
+    
+    defined $index or die "Cannot find the Loader partition\n";
+
+    splice(@$partitions_ref, $index, 1);
+
+    # Set fixed offsets and sizes for Kernel and Ramdisk
+    map {
+	if ($_->{'name'} eq 'Kernel') {
+	    $_->{'offset'}   = $kernel_offset;
+	    $_->{'size'}     = $kernel_size;
+	    $_->{'variable'} = 0;
+	}
+	if ($_->{'name'} eq 'Ramdisk') {
+	    $_->{'offset'}   = $ramdisk_offset;
+	}
+    } @$partitions_ref;
+
+    return;
+}
+
+
+# populate @partitions based on the firmware's partition table
+sub spliceFirmwarePartitions {
+    my($firmware_buf, $partitions_ref) = @_;
+
+    # we know that partition table, if it exists, begins at start of 'FIS directory' and has max length 0x1000
+    my($partition_table);
+    map {
+	$_->{'name'} eq 'FIS directory' and
+	    $partition_table = findPartitionTable(substr($firmware_buf, $_->{'offset'}, $_->{'size'}));
+    } @$partitions_ref;
+
+    # return 0 here if no partition table in FIS directory
+    return if not $partition_table;
+
+    my @new_partitions = parsePartitionTable($partition_table);
+
+    # Remove the optional second stage bootloader if it is not found in the FIS directory.
+    if (not grep { $_->[0] eq 'Loader' } @new_partitions) {
+	removeOptionalLoader($partitions_ref);
+    }
+
+    my($partition_count) = 0;
+    my($splice) = 0;
+    map {
+
+	# Skip pseudo partitions.
+	while (($partition_count < scalar(@$partitions_ref)) and
+	       $partitions_ref->[$partition_count]->{'pseudo'}) {
+	    $debug and printf("Skipped <%s> (pseudo partition)\n", $partitions_ref->[$partition_count]->{'name'});
+	    $partition_count++;
+	}
+
+	# If we are in a variable area, and we haven't reached the end of it,
+	# then splice in another partition for use by the later code.
+	if ($splice and ($partitions_ref->[$partition_count]->{'name'} ne $_->[0])) {
+	    $debug and printf("Splicing new partition <%s> before <%s>\n",
+			      $_->[0], $partitions_ref->[$partition_count]->{'name'});
+	    splice(@{$partitions_ref}, $partition_count, 0, ({'name' => "",'variable'=>1,'header'=>0}));
+	}
+
+	my $partition = $partitions_ref->[$partition_count];
+
+	# Variable partitions can be overridden by the real FIS directory
+	if ($partition->{'variable'}) {
+
+	    # Only override the filename if the partition name is not set or doesn't match
+	    if ($partition->{'name'} ne $_->[0]) {
+
+		if (length($partition->{'name'})) {
+		    $debug and printf("Overwriting <%s> with <%s>\n",
+				      $partitions_ref->[$partition_count]->{'name'}, $_->[0]);
+		}
+
+		$partition->{'name'} = $_->[0];
+		$partition->{'file'} = $_->[0];
+	    }
+
+	    # Set the offset, size and skips based on the real partition table
+	    $partition->{'offset'} = $_->[1];
+	    $partition->{'size'}   = $_->[2];
+	    $partition->{'skips'}  = $_->[3];
+
+	    $debug and printf("Locating <%s> at 0x%08X (%s)\n",
+			      $partition->{'name'}, $partition->{'offset'},
+			      ($partition->{'size'} >= $block_size ?
+			       sprintf("%d blocks", numBlocks($partition->{'size'})) :
+			       sprintf("0x%05X bytes", $partition->{'size'})));
+
+	    $splice = 1;
+	}
+
+	# Fixed partitions cannot be overridden
+	else {
+	    ($partition->{'name'} eq $_->[0]) or
+		die "Unexpected partition <",$_->[0],"> (expecting <",$partition->{'name'},">)\n";
+
+	    $debug and printf("Locating <%s> at 0x%08X (%s)\n",
+			      $partition->{'name'}, $partition->{'offset'},
+			      ($partition->{'size'} >= $block_size ?
+			       sprintf("%d blocks", numBlocks($partition->{'size'})) :
+			       sprintf("0x%05X bytes", $partition->{'size'})));
+	    
+	    $splice = 0;
+	}
+	
+	$partition_count++;
+
+    } @new_partitions;
+
+    return;
+}
+
+# Read in an 8MB firmware file, and store the data into @partitions.
+# Note that the data is only stored in a partition if 'offset' and 'size' are defined,
+# and it does not already have data stored in it.
+sub readInFirmware {
+    my($filename, $partitions_ref) = @_;
+
+    my($firmware_buf);
+
+    open FILE,$filename or die "Can't find firmware image \"$filename\": $!\n";
+    read FILE,$firmware_buf,$flash_len or die "Can't read $flash_len bytes from \"$filename\": $!\n";
+    close FILE or die "Can't close \"$filename\": $!\n";
+
+    $debug and printf("Read 0x%08X bytes from \"%s\"\n", length($firmware_buf), $filename);
+
+    spliceFirmwarePartitions($firmware_buf, $partitions_ref);
+
+    # Read the parts of the firmware file into the partitions table.
+    map {
+	if (defined $_->{'offset'} and defined $_->{'size'}) {
+
+	    if (defined $_->{'data'}) {
+		$debug and printf("Not overwriting data in <%s>\n", $_->{'name'});
+	    }
+	    else {
+
+		# Slurp up the data, based on whether a header and/or data is present or not
+		if ($_->{'header'}) {
+
+		    # Read the length, and grab the data based on the length.
+		    my($data_len) = unpack("N", substr($firmware_buf, $_->{'offset'}));
+
+		    # A length of 0xFFFFFFFF means that the area is not initialised
+		    if ($data_len != 0xFFFFFFFF) {
+			$debug and printf("Found header size of 0x%08X bytes for <%s>\n", $data_len, $_->{'name'});
+			$_->{'data'} = substr($firmware_buf, $_->{'offset'} + $_->{'header'}, $data_len);
+		    }
+		}
+		elsif ($_->{'pseudo'} and not defined $_->{'file'} and
+		       (substr($firmware_buf, $_->{'offset'}, $_->{'size'}) eq
+			(pack("C", 0xff) x $_->{'size'}))) {
+		    $debug and printf("Skipping empty pseudo partition <%s>\n", $_->{'name'});
+		}
+		else {
+
+		    # Grab the whole partition, using the maximum size.
+		    $_->{'data'} = substr($firmware_buf, $_->{'offset'}, $_->{'size'});
+		}
+
+		# If skip regions are defined, remove them from the data.
+		if (defined $_->{'skips'}) {
+		    my $removed = 0;
+		    foreach my $region (@{$_->{'skips'}}) {
+			if (($region->{'offset'} > 0) or
+			    not ($_->{'header'} > 0)) {
+			    $debug and printf("Removing 0x%05X bytes from offset 0x%05X\n",
+					      $region->{'size'}, $region->{'offset'});
+			    $region->{'data'} = substr($_->{'data'}, $region->{'offset'} - $removed, $region->{'size'}, '');
+			}
+			$removed += $region->{'size'};
+		    }
+		}
+
+		$quiet or defined $_->{'data'} and printf("Read %s into <%s>\n",
+							  (length($_->{'data'}) >= $block_size ?
+							   sprintf("%d blocks", numBlocks(length($_->{'data'}))) :
+							   sprintf("0x%05X bytes", length($_->{'data'}))), $_->{'name'});
+	    }
+	}
+    } @$partitions_ref;
+}
+
+# Write the partition data stored in memory out into the files associated with each.
+sub writeOutFirmwareParts {
+    my(@partitions) = @_;
+
+    # Write out the parts of the firmware file.
+    map {
+
+	# We can only write if 'data' and 'file' are defined.
+	if (defined $_->{'file'} and defined $_->{'data'} and length($_->{'data'})) {
+	    writeOut($_->{'data'}, $_->{'file'});
+	    $quiet or printf("Wrote 0x%08X bytes from <%s> into \"%s\"\n",
+			      length($_->{'data'}), $_->{'name'}, $_->{'file'});
+	}
+	else {
+	    $debug and printf("Skipping <%s> (%s)\n", $_->{'name'},
+			      (not defined $_->{'file'}) ?
+			      "no filename specified" :
+			      "no data to write");
+	}
+
+    } @partitions;
+
+    return;
+}
+
+# Read in the partition data from the files associated with each and store in memory.
+sub readInFirmwareParts {
+    my(@partitions) = (@_);
+    
+    undef $/; # we want to slurp
+
+    map {
+
+	my $file = $_->{'file'};
+	if (defined $file) {
+	    open FILE,$file or die "Can't find firmware part \"$file\": $!\n";
+
+	    # Slurp in the data
+	    $_->{'data'} = <FILE>;
+
+	    # close the file
+	    close FILE or die "Can't close file \"$file\": $!\n";
+
+	    # Optionally byteswap the data
+	    if ($_->{'byteswap'}) {
+		# Byte swap the data (which has to be padded to a multiple of 4 bytes).
+		$_->{'data'} = pack("N*", unpack("V*", $_->{'data'}.pack("CCC", 0)));
+	    }
+
+	    # Keep track of the actual size.
+	    my $size;
+
+	    if ($_->{'header'}) {
+		if ($_->{'pseudo'}) {
+		    $size = $_->{'header'} + length($_->{'data'});
+		}
+		else {
+		    $size = paddedSize($_->{'header'} + length($_->{'data'}));
+		}
+	    }
+	    elsif (not $_->{'pseudo'}) {
+		$size = paddedSize(length($_->{'data'}));
+	    }
+	    else {
+		$size = length($_->{'data'});
+	    }
+
+	    # Check to make sure the file contents are not too large.
+	    if (defined $_->{'size'} and ($size > $_->{'size'})) {
+		die sprintf("Ran out of flash space in <%s> - %s too large.\n", $_->{'name'},
+			    sprintf("0x%05X bytes", ($size - $_->{'size'})));
+	    }
+
+	    # If the partition does not have a fixed size, the calculate the size.
+	    if (not defined $_->{'size'}) {
+		$_->{'size'} = $size;
+	    }
+
+	    # Keep the user appraised ...
+	    $quiet or printf("Read 0x%08X bytes from \"%s\" into <%s> (%s / %s)%s\n",
+			     length($_->{'data'}), $_->{'file'}, $_->{'name'},
+			     ($size >= $block_size ?
+			      sprintf("%d blocks", numBlocks($size)) :
+			      sprintf("0x%05X bytes", $size)),
+			     ($_->{'size'} >= $block_size ?
+			      sprintf("%d blocks", numBlocks($_->{'size'})) :
+			      sprintf("0x%05X bytes", $_->{'size'})),
+			     ($_->{'byteswap'} ? " (byte-swapped)" : ""));
+	}
+
+    } @partitions;
+
+    return;
+}
+
+# layoutPartitions : this function must be ugly - it needs to verify RedBoot, SysConf, Kernel, Ramdisk, and
+#     FIS directory partitions exist, are in the correct order, and do not have more data than can fit in
+#     their lengths (fixed for all but Ramdisk, which has a minimum length of one block).
+#     If Rootdisk and/or Userdisk exist, it must also verify that their block padded lengths are not
+#     too great for the available space.
+# input : an array of hashes, some of which are populated with data
+# output: same reference with start and size (partition not data) also populated. this populated structure
+#         can then be passed to buildPartitionTable() to generate the actual partition table data
+sub layoutPartitions {
+    my(@partitions) = @_;
+
+    # Find the kernel partition, and save a pointer to it for later use
+    my $kernel;
+    map { ($_->{'name'} eq "Kernel") && ($kernel = $_); } @partitions;
+    $kernel or die "Couldn't find the kernel partition\n";
+
+    # Find the last variable size partition, and save a pointer to it for later use
+    my $lastdisk;
+    my $directory_offset;
+    my $curdisk = $partitions[0];
+    map {
+	if (not defined $lastdisk) {
+	    if ($_->{'name'} eq "FIS directory") {
+		$lastdisk = $curdisk;
+		$directory_offset = $_->{'offset'};
+	    }
+	    else {
+		$curdisk = $_;
+	    }
+	}
+    } @partitions;
+
+    $lastdisk or die "Couldn't find the last variable size partition\n";
+
+    $debug and printf("Last variable size partition is <%s>\n", $lastdisk->{'name'});
+
+    #
+    # here we go through the $partitions array ref and fill in all the values
+    #
+
+    # This points to where the next partition should be placed.
+    my $pointer = $flash_start;
+
+    map {
+
+	$debug and printf("Pointer is 0x%08X\n", $pointer);
+
+	# Determine the start and offset of the current partition.
+	if (defined $_->{'offset'}) {
+	    $_->{'start'} = $flash_start + $_->{'offset'};
+	    # Check for running past the defined start of the partition.
+	    if (($pointer > $_->{'start'}) and not $_->{'pseudo'}) {
+		die sprintf("Ran out of flash space before <%s> - %s too large.\n", $_->{'name'},
+			    sprintf("0x%05X bytes", ($pointer - $_->{'start'})));
+	    }
+	}
+
+	# If offset is not defined, then calculate it.
+	else {
+	    $_->{'start'} = $pointer;
+	    $_->{'offset'} = $_->{'start'} - $flash_start;
+	}
+
+	my $size = defined $_->{'data'} ? length($_->{'data'}) : 0;
+
+	# Add skip regions for the partitions with headers.
+	if ($_->{'header'} > 0) {
+	    # Define the skip region for the initial Sercomm header.
+	    push(@{$_->{'skips'}},
+		 { 'offset' => 0, 'size' => $_->{'header'}, 'data' => undef });
+	    # Allow for the Sercomm header to be prepended to the data.
+	    $size += $_->{'header'};
+
+	    # Determine if the partition overlaps the ramdisk boundary.
+	    if (($_->{'offset'} < $ramdisk_offset) and
+		(($_->{'offset'} + $size) > $ramdisk_offset)) {
+		# Define the skip region for the inline Sercomm header.
+		push(@{$_->{'skips'}},
+		     { 'offset' => ($ramdisk_offset - $_->{'offset'}), 'size' => 16,
+		       'data' => pack("N4", $block_size) });
+		# Allow for the Sercomm header to be inserted in the data.
+		$size += 16;
+	    }
+	}
+
+	# Partitions without headers cannot have skip regions.
+	elsif (($_->{'offset'} <= $ramdisk_offset) and
+	       (($_->{'offset'} + $size) > $ramdisk_offset)) {
+	    # Pad the kernel until it extends past the ramdisk offset.
+	    push(@{$kernel->{'skips'}},
+		 { 'offset' => ($ramdisk_offset - $kernel->{'offset'}), 'size' => 16,
+		   'data' => pack("N4", $block_size) });
+	    $kernel->{'size'} = $ramdisk_offset - $kernel->{'offset'} + $block_size;
+	    $kernel->{'data'} = padBytes($kernel->{'data'},
+					 $kernel->{'size'} - $kernel->{'header'} - 16);
+	    $_->{'offset'} = $ramdisk_offset + $block_size;
+	    $_->{'start'} = $flash_start + $_->{'offset'};
+	    $pointer = $_->{'start'};
+	    $debug and printf("Extending kernel partition past ramdisk offset.\n");
+	}
+
+	# If this is the last variable size partition, then fill the rest of the space.
+	if ($_->{'name'} eq $lastdisk->{'name'}) {
+	    $_->{'size'} = paddedSize($directory_offset + $flash_start - $pointer);
+	    $debug and printf("Padding last variable partition <%s> to 0x%08X bytes\n", $_->{'name'}, $_->{'size'});
+	}
+
+	die sprintf("Partition size not defined in <%s>.\n", $_->{'name'})
+	    unless defined $_->{'size'};
+
+	# Extend to another block if required.
+	if ($size > $_->{'size'}) {
+	    if ($_->{'name'} eq $lastdisk->{'name'}) {
+		die sprintf("Ran out of flash space in <%s> - %s too large.\n", $_->{'name'},
+			    sprintf("0x%05X bytes", ($size - $_->{'size'})));
+	    }
+	    $_->{'size'} = $size;
+	    printf("Extending partition <%s> to 0x%08X bytes\n", $_->{'name'}, $_->{'size'});
+	}
+
+	# Keep the user appraised ...
+	$debug and printf("Allocated <%s> from 0x%08X to 0x%08X (%s / %s)\n",
+			  $_->{'name'}, $_->{'start'}, $_->{'start'} + $_->{'size'},
+			  ($size >= $block_size ?
+			   sprintf("%d blocks", numBlocks($size)) :
+			   sprintf("0x%05X bytes", $size)),
+			  ($_->{'size'} >= $block_size ?
+			   sprintf("%d blocks", numBlocks($_->{'size'})) :
+			   sprintf("0x%05X bytes", $_->{'size'})));
+
+	# Check to make sure we have not run out of room.
+	if (($_->{'start'} + $_->{'size'}) > ($flash_start + $flash_len)) {
+	    die "Ran out of flash space in <", $_->{'name'}, ">\n";
+	}
+
+	$debug and printf("Moving pointer from 0x%08X to 0x%08X (0x%08X + 0x%08X)\n",
+			  $pointer, paddedSize($_->{'start'} + $_->{'size'}),
+			  $_->{'start'}, $_->{'size'});
+
+	# Move the pointer up, in preparation for the next partition.
+	$pointer = paddedSize($_->{'start'} + $_->{'size'});
+
+    } @partitions;
+
+    return;
+}
+
+sub buildPartitionTable {
+    my(@partitions) = @_;
+
+    my($flash_start) = 0x50000000;
+    my($partition_data) = '';
+
+    map {
+
+	# Collate the partition data for all known partitions.
+	if (not $_->{'pseudo'} and defined $_->{'offset'} and defined $_->{'size'}) {
+
+	    # Pack and append the binary table entry for this partition.
+	    $partition_data .= createPartitionEntry($_->{'name'}, $_->{'offset'} + $flash_start,
+						    $_->{'size'}, $_->{'skips'});
+
+	    # If this is the FIS directory, then write the partition table data into it.
+	    if ($_->{'name'} eq "FIS directory") {
+		# Explicitly terminate the partition data.
+		$partition_data .= pack("C",0xff) x 0x100;
+		$_->{'data'} = padBytes($partition_data, $_->{'size'});
+	    }
+
+	    my $size = length($_->{'data'});
+
+	    # Keep the user appraised ...
+	    $debug and printf("Table entry <%s> from 0x%08X to 0x%08X (%s / %s)%s\n",
+			      $_->{'name'}, $_->{'start'}, $_->{'start'} + $_->{'size'},
+			      ($size >= $block_size ?
+			       sprintf("%d blocks", numBlocks($size)) :
+			       sprintf("0x%05X bytes", $size)),
+			      ($_->{'size'} >= $block_size ?
+			       sprintf("%d blocks", numBlocks($_->{'size'})) :
+			       sprintf("0x%05X bytes", $_->{'size'})),
+			      (defined $_->{'skips'} ?
+			       sprintf("\nTable entry <%s> skip %s", $_->{'name'},
+				       join(", ",
+					    map { sprintf("0x%08X to 0x%08X", $_->{'offset'},
+							  $_->{'offset'} + $_->{'size'} - 1) }
+					    @{$_->{'skips'}})) :
+			       "")
+			      );
+	}
+	else {
+	    $debug and print "No table entry required for <", $_->{'name'}, ">\n";
+	}
+
+    } @partitions;
+
+    return;
+}
+
+sub writeOutFirmware {
+    my($filename, @partitions) = @_;
+
+    # Clear the image to start.
+    my $image_buf = "";
+
+    map {
+
+	# We can only write a partition if it has an offset, a size, and some data to write.
+	if (defined $_->{'offset'} and defined $_->{'size'} and defined $_->{'data'}) {
+
+	    # Keep track of the end of the image.
+	    my $end_point = length($image_buf);
+
+	    # If the next partition is well past the end of the current image, then pad it.
+	    if ($_->{'offset'} > $end_point) {
+		$image_buf .= padBytes("", $_->{'offset'} - $end_point);
+		$quiet or printf("Padded %s before <%s> in \"%s\"\n",
+				 ((length($image_buf) - $end_point) >= $block_size ?
+				  sprintf("%d blocks", numBlocks(length($image_buf) - $end_point)) :
+				  sprintf("0x%05X bytes", length($image_buf) - $end_point)),
+				 $_->{'name'}, $filename);
+	    }
+
+	    # If the next partition is before the end of the current image, then rewind.
+	    elsif ($_->{'offset'} < $end_point) {
+		$debug and printf("Rewound %s before <%s> in \"%s\"\n",
+				  (($end_point - $_->{'offset'}) >= $block_size ?
+				   sprintf("%d blocks", numBlocks($end_point - $_->{'offset'})) :
+				   sprintf("0x%05X bytes", $end_point - $_->{'offset'})),
+				  $_->{'name'}, $filename);
+# 		if (($end_point - $_->{'offset'}) >= $block_size) {
+# 		    die "Allocation error: rewound a full block or more ...\n";
+# 		}
+	    }
+
+	    # If skip regions are defined, add them to the data.
+	    if (defined $_->{'skips'}) {
+		my $added = 0;
+		foreach my $region (@{$_->{'skips'}}) {
+		    if (($region->{'offset'} > 0) or
+			not ($_->{'header'} > 0)) {
+			$debug and printf("Inserted 0x%05X bytes (at offset 0x%05X) into <%s>\n",
+					  $region->{'size'}, $region->{'offset'}, $_->{'name'});
+			substr($_->{'data'},
+			       $region->{'offset'} + $added - $_->{'header'},
+			       0, $region->{'data'});
+			$added += $region->{'size'};
+		    }
+		}
+	    }
+
+	    # Splice the data into the image at the appropriate place, padding as required.
+	    substr($image_buf, $_->{'offset'}, $_->{'size'},
+		   $_->{'header'} ?
+		   padBytes(pack("N4",length($_->{'data'})).$_->{'data'}, $_->{'size'}) :
+		   padBytes($_->{'data'}, $_->{'size'}));
+	    
+	    # Keep the user appraised ...
+	    $quiet or printf("Wrote %s (0x%08X to 0x%08X) from <%s> into \"%s\"\n",
+			     ($_->{'size'} >= $block_size ?
+			      sprintf("%2d blocks", numBlocks($_->{'size'})) :
+			      sprintf("0x%05X bytes", $_->{'size'})),
+			     $_->{'offset'}, $_->{'offset'}+$_->{'size'}, $_->{'name'}, $filename);
+	}
+
+	# If we are not able to write a partition, then give debug information about why.
+	else {
+	    $debug and printf("Skipping <%s> (%s)\n", $_->{'name'},
+			      (not defined $_->{'offset'}) ? "no offset defined" :
+			      ((not defined $_->{'size'}) ? "no size defined" :
+			       "no data available"));
+	}
+
+    } @partitions;
+
+    # Write the image to the specified file.
+    writeOut($image_buf, $filename);
+
+    return;
+}
+
+# checkPartitionTable: sanity check partition table - for testing but might evolve into setting @partitions
+#    so that we can write out jffs2 partitions from a read image
+#    currently not nearly paranoid enough
+sub checkPartitionTable {
+    my($data) = @_;
+
+    my($pointer) = 0;
+    my($entry);
+
+    my($name, $flash_base, $size, $done, $dummy_long, $padding);
+    do {
+	$entry = substr($data, $pointer, 0x100);
+
+	($name,$flash_base,$dummy_long,$size,$dummy_long,$dummy_long,$padding,$dummy_long,$dummy_long) = unpack("a16N5x212N2",$entry);
+	$name =~ s/\0//g;
+	$debug and printf("pointer: %d\tname: %s%sflash_base: 0x%08X\tsize: 0x%08X\n",
+			  $pointer, $name, (" " x (16 - length($name))), $flash_base, $size);
+	$pointer += 0x100;
+	$debug and printf("terminator: 0x%08X\n", unpack("C", substr($data, $pointer, 1)));
+	if (unpack("C", substr($data, $pointer, 1)) eq 0xff) {
+	    $done = 1;
+	}
+    } until $done;
+}
+
+sub printPartitions {
+    my(@partitions) = @_;
+
+    my($offset, $size, $skips);
+    map {
+#	defined $_->{'size'} ? $size = $_->{'size'} : $size = undef;
+
+	if (defined  $_->{'size'}) {
+	    $size = $_->{'size'};
+	}
+	else {
+	    $size = undef;
+	}
+	if (defined  $_->{'offset'}) {
+	    $offset = $_->{'offset'};
+	}
+	else {
+	    $offset = undef;
+	}
+	if (defined  $_->{'skips'}) {
+	    $skips = $_->{'skips'};
+	}
+	else {
+	    $skips = undef;
+	}
+	printf("%s%s", $_->{'name'}, (" " x (16 - length($_->{'name'}))));
+	if (defined $offset) { printf("0x%08X\t", $offset); } else { printf("(undefined)\t");   };
+	if (defined $size)   { printf("0x%08X", $size); } else { printf("(undefined)"); };
+	if (defined $skips) {
+	    printf("\t[%s]",
+		   join(", ",
+			map { sprintf("0x%05X/0x%05X", $_->{'offset'}, $_->{'size'}); }
+			@$skips));
+	}
+	printf("\n");
+    } @partitions;
+}
+
+sub defaultPartitions {
+
+    return ({'name'=>'RedBoot',          'file'=>'RedBoot',
+	     'offset'=>0x00000000,        'size'=>0x00040000,
+	     'variable'=>0, 'header'=>0,  'pseudo'=>0, 'data'=>undef, 'byteswap'=>0},
+	    {'name'=>'EthAddr',           'file'=>undef,
+	     'offset'=>0x0003ffb0,        'size'=>0x00000006,
+	     'variable'=>0, 'header'=>0,  'pseudo'=>1, 'data'=>undef, 'byteswap'=>0},
+	    {'name'=>'SysConf',           'file'=>'SysConf',
+	     'offset'=>0x00040000,        'size'=>0x00020000,
+	     'variable'=>0, 'header'=>0,  'pseudo'=>0, 'data'=>undef, 'byteswap'=>0},
+	    {'name'=>'Loader',            'file'=>'apex.bin',
+	     'offset'=>undef,             'size'=>undef,
+	     'variable'=>1, 'header'=>16, 'pseudo'=>0, 'data'=>undef, 'byteswap'=>0},
+	    {'name'=>'Kernel',            'file'=>'vmlinuz',
+	     'offset'=>undef,             'size'=>undef,
+	     'variable'=>1, 'header'=>16, 'pseudo'=>0, 'data'=>undef, 'byteswap'=>0},
+	    {'name'=>'Ramdisk',           'file'=>'ramdisk.gz',
+	     'offset'=>undef,             'size'=>undef,
+	     'variable'=>1, 'header'=>16, 'pseudo'=>0, 'data'=>undef, 'byteswap'=>0},
+	    {'name'=>'FIS directory',     'file'=>undef,
+	     'offset'=>0x007e0000,        'size'=>0x00020000,
+	     'variable'=>0, 'header'=>0,  'pseudo'=>0, 'data'=>undef, 'byteswap'=>0},
+	    {'name'=>'Loader config',	  'file'=>undef,
+	     'offset'=>0x007f8000,        'size'=>0x00004000,
+	     'variable'=>0, 'header'=>0,  'pseudo'=>1, 'data'=>undef, 'byteswap'=>0},
+	    {'name'=>'Microcode',	  'file'=>'NPE-B',
+	     'offset'=>0x007fc000,        'size'=>0x00003fe0,
+	     'variable'=>0, 'header'=>16, 'pseudo'=>1, 'data'=>undef, 'byteswap'=>0},
+	    {'name'=>'Trailer',           'file'=>'Trailer',
+	     'offset'=>0x007ffff0,        'size'=>0x00000010,
+	     'variable'=>0, 'header'=>0,  'pseudo'=>1, 'data'=>undef, 'byteswap'=>0});
+}
+
+# Main routine starts here ...
+
+my($unpack, $pack, $little, $fatflash, $input, $output, $redboot);
+my($kernel, $sysconf, $ramdisk, $fisdir);
+my($microcode, $trailer, $ethaddr, $loader);
+
+END {
+    # Remove temporary files
+    for my $file (@cleanup) {
+	unlink $file;
+    }
+}
+
+if (!GetOptions("d|debug"       => \$debug,
+		"q|quiet"       => \$quiet,
+		"u|unpack"      => \$unpack,
+		"p|pack"        => \$pack,
+		"l|little"      => \$little,
+		"F|fatflash"    => \$fatflash,
+		"i|input=s"     => \$input,
+		"o|output=s"    => \$output,
+		"b|redboot=s"   => \$redboot,
+		"k|kernel=s"    => \$kernel,
+		"s|sysconf=s"   => \$sysconf,
+		"r|ramdisk=s"   => \$ramdisk,
+		"f|fisdir=s"    => \$fisdir,
+		"m|microcode=s" => \$microcode,
+		"t|trailer=s"   => \$trailer,
+		"e|ethaddr=s"   => \$ethaddr,
+		"L|loader=s"    => \$loader,
+		) or (not defined $pack and not defined $unpack)) {
+    print "Usage: slugimage <options>\n";
+    print "\n";
+    print "  [-d|--debug]			Turn on debugging output\n";
+    print "  [-q|--quiet]			Turn off status messages\n";
+    print "  [-u|--unpack]			Unpack a firmware image\n";
+    print "  [-p|--pack]			Pack a firmware image\n";
+    print "  [-l|--little]			Convert Kernel and Ramdisk to little-endian\n";
+    print "  [-F|--fatflash]			Generate an image for 16MB flash\n";
+    print "  [-i|--input]     <file>		Input firmware image filename\n";
+    print "  [-o|--output]    <file>		Output firmware image filename\n";
+    print "  [-b|--redboot]   <file>		Input/Output RedBoot filename\n";
+    print "  [-s|--sysconf]   <file>		Input/Output SysConf filename\n";
+    print "  [-L|--loader]    <file>		Second stage boot loader filename\n";
+    print "  [-k|--kernel]    <file>		Input/Output Kernel filename\n";
+    print "  [-r|--ramdisk]   <file>		Input/Output Ramdisk filename(s)\n";
+    print "  [-f|--fisdir]    <file>		Input/Output FIS directory filename\n";
+    print "  [-m|--microcode] <file>		Input/Output Microcode filename\n";
+    print "  [-t|--trailer]   <file>		Input/Output Trailer filename\n";
+    print "  [-e|--ethaddr]   <AABBCCDDEEFF>	Set the Ethernet address\n";
+
+    # %%% TODO %%% Document --ramdisk syntax
+
+    exit 1;
+}
+
+my(@partitions) = defaultPartitions();
+
+if ($pack) {
+    die "Output filename must be specified\n" unless defined $output;
+
+    # If we're creating an image and no RedBoot, SysConf partition is
+    # explicitly specified, simply write an empty one as the upgrade tools
+    # don't touch RedBoot and SysConf anyway.  If no Trailer is specified,
+    # put in one.
+    if (not defined $redboot and not -e "RedBoot") {
+	$redboot = tempfile();
+	open TMP, ">$redboot" or die "Cannot open file $redboot: $!";
+	push @cleanup, $redboot;
+	# The RedBoot partition is 256 * 1024 = 262144; the trailer we add
+	# is 70 bytes.
+	print TMP "\0"x(262144-70);
+	# Upgrade tools check for an appropriate Sercomm trailer.
+	for my $i (@sercomm_redboot_trailer) {
+	    print TMP pack "S", $i;
+	}
+	close TMP;
+    }
+    if (not defined $sysconf and not -e "SysConf") {
+	$sysconf = tempfile();
+	open TMP, ">$sysconf" or die "Cannot open file $sysconf: $!";
+	push @cleanup, $sysconf;
+	# The SysConf partition is 128 * 1024 = 131072
+	print TMP "\0"x131072;
+	close TMP;
+    }
+    if (not defined $trailer and not -e "Trailer") {
+	$trailer = tempfile();
+	open TMP, ">$trailer" or die "Cannot open file $trailer: $!";
+	push @cleanup, $trailer;
+	for my $i (@sercomm_flash_trailer) {
+	    print TMP pack "S", $i;
+	}
+	close TMP;
+    }
+
+    # If the microcode was not specified, then don't complain that it's missing.
+    if (not defined $microcode and not -e "NPE-B") {
+	map { ($_->{'name'} eq 'Microcode') && ($_->{'file'} = undef);   } @partitions;
+    }
+}
+
+# Go through the partition options, and set the names and files in @partitions
+if (defined $redboot)   { map { ($_->{'name'} eq 'RedBoot')	  && ($_->{'file'} = $redboot);   } @partitions; }
+if (defined $sysconf)   { map { ($_->{'name'} eq 'SysConf')	  && ($_->{'file'} = $sysconf);   } @partitions; }
+if (defined $loader)    { map { ($_->{'name'} eq 'Loader')	  && ($_->{'file'} = $loader);    } @partitions; }
+if (defined $kernel)    { map { ($_->{'name'} eq 'Kernel')	  && ($_->{'file'} = $kernel);    } @partitions; }
+if (defined $fisdir)    { map { ($_->{'name'} eq 'FIS directory') && ($_->{'file'} = $fisdir);    } @partitions; }
+if (defined $microcode) { map { ($_->{'name'} eq 'Microcode')	  && ($_->{'file'} = $microcode); } @partitions; }
+if (defined $trailer)   { map { ($_->{'name'} eq 'Trailer')	  && ($_->{'file'} = $trailer);   } @partitions; }
+
+if (defined $little)  {
+    map {
+	if (($_->{'name'} eq 'Loader') or
+	    ($_->{'name'} eq 'Kernel') or
+	    ($_->{'name'} eq 'Ramdisk')) {
+	    $_->{'byteswap'} = 1;
+	}
+    } @partitions;
+}
+
+if (defined $fatflash)  {
+    $flash_len = 0x01000000;
+    map {
+	if (($_->{'name'} eq 'FIS directory') or
+	    ($_->{'name'} eq 'Loader config') or
+	    ($_->{'name'} eq 'Microcode') or
+	    ($_->{'name'} eq 'Trailer')) {
+	    $_->{'offset'} += 0x00800000;
+	}
+    } @partitions;
+}
+
+if (defined $ethaddr) {
+    map {
+	if ($_->{'name'} eq 'EthAddr') {
+	    $ethaddr =~ s/://g;
+	    if (($ethaddr !~ m/^[0-9A-Fa-f]+$/) or (length($ethaddr) != 12)) {
+		die "Invalid ethernet address specification: '".$ethaddr."'\n";
+	    }
+	    $_->{'data'} = pack("H12", $ethaddr);
+	}
+    } @partitions;
+}
+
+if (defined $ramdisk) {
+
+    # A single filename is used for the ramdisk filename
+    if ($ramdisk !~ m/[:,]/) {
+	map { ($_->{'name'} eq 'Ramdisk') && ($_->{'file'} = $ramdisk); } @partitions;
+    }
+
+    # otherwise, it's a list of name:file mappings
+    else {
+	my @mappings = split(',', $ramdisk);
+
+	# Find the index of the Ramdisk entry
+	my $index;
+	my $count = 0;
+	map {
+	    if (not defined $index) {
+		if ($_->{'name'} eq "Ramdisk") {
+		    $index = $count;
+		}
+		$count++;
+	    }
+	} @partitions;
+
+	defined $index or die "Cannot find the Ramdisk partition\n";
+
+	# Replace the Ramdisk entry with the new mappings
+	splice(@partitions, $index, 1, map {
+
+	    # Preserve the information from the ramdisk entry
+	    my %entry = %{$partitions[$index]};
+
+	    # Parse the mapping
+	    ($_ =~ m/^([^:]+):([^:]+)(:([^:]+))?$/) or die "Invalid syntax in --ramdisk\n";
+	    $entry{'name'} = $1; $entry{'file'} = $2; my $size = $4;
+
+	    # If the mapping is not for the ramdisk, then undefine its attributes
+	    if ($entry{'name'} ne 'Ramdisk') {
+		$entry{'offset'} = undef;
+		$entry{'size'} = undef;
+		$entry{'variable'} = 1;
+		$entry{'header'} = 0;
+		$entry{'pseudo'} = 0;
+		$entry{'data'} = undef;
+		$entry{'byteswap'} = 0;
+	    }
+
+	    # Support specification of the number of blocks for empty jffs2
+	    if ($entry{'file'} =~ m/^[0-9]+$/) {
+		$size = $entry{'file'};
+		$entry{'file'} = undef;
+	    }
+
+	    # If the user has specified a size, then respect their wishes
+	    if (defined $size) {
+		$entry{'size'} = $size * $block_size;
+		# Create an empty partition of the requested size.
+		$entry{'data'} = padBytes("", $entry{'size'} - $entry{'header'});
+	    }
+
+	    \%entry;
+
+	} @mappings);
+    }
+}
+
+# Read in the firmware image
+if ($input) {
+    if ($debug) {
+	print "Initial partition map:\n";
+	printPartitions(@partitions);
+    }
+    
+    my $result = readInFirmware($input, \@partitions);
+
+    if ($debug) {
+	print "After reading firmware:\n";
+	printPartitions(@partitions);
+    }
+}
+
+# Unpack the firmware if requested
+if ($unpack) {
+    die "Input filename must be specified\n" unless defined $input;
+
+#    map {
+#	($_->{'name'} eq 'FIS directory') and @partitions = checkPartitionTable($_->{'data'});
+#    } @partitions;
+
+    writeOutFirmwareParts(@partitions);
+
+}
+
+# Pack the firmware if requested
+if ($pack) {
+
+    if (!defined $loader) {
+	removeOptionalLoader(\@partitions);
+    }
+
+    if ($debug) {
+	print "Initial partition map:\n";
+	printPartitions(@partitions);
+    }
+    
+    my $result = readInFirmwareParts(@partitions);
+
+    if ($debug) {
+	print "after readInFirmwareParts():\n";
+	printPartitions(@partitions);
+# 	map {
+# 	    ($_->{'name'} eq 'RedBoot') && (printRedbootTrailer($_->{'data'}));
+# 	} @partitions;
+    }
+    
+    layoutPartitions(@partitions);
+
+    if ($debug) {
+ 	print "after layoutPartitions():\n";
+ 	printPartitions(@partitions);
+    }
+    
+    buildPartitionTable(@partitions);
+
+    if ($debug) {
+ 	print "after buildPartitionTable():\n";
+ 	printPartitions(@partitions);
+
+#  	my($lastblock);
+#  	map {
+#  	    if ($_->{'name'} eq 'FIS directory') {
+#  		$lastblock = $_->{'data'};
+#  	    }
+#  	} @partitions;
+
+#  	print "checkPartitionTable():\n";
+#  	checkPartitionTable($lastblock);
+    }
+    
+    writeOutFirmware($output, @partitions);
+
+}
+
+exit 0;
diff --git a/scripts/spelling.txt b/scripts/spelling.txt
new file mode 100644
index 0000000..c45e9af
--- /dev/null
+++ b/scripts/spelling.txt
@@ -0,0 +1,1517 @@
+# Originally from Debian's Lintian tool. Various false positives have been
+# removed, and various additions have been made as they've been discovered
+# in the kernel source.
+#
+# License: GPLv2
+#
+# The format of each line is:
+# mistake||correction
+#
+abandonning||abandoning
+abigious||ambiguous
+abitrate||arbitrate
+abnornally||abnormally
+abnrormal||abnormal
+abord||abort
+aboslute||absolute
+abov||above
+abreviated||abbreviated
+absense||absence
+absolut||absolute
+absoulte||absolute
+acccess||access
+acceess||access
+acceleratoin||acceleration
+accelleration||acceleration
+accesing||accessing
+accesnt||accent
+accessable||accessible
+accesss||access
+accidentaly||accidentally
+accidentually||accidentally
+acclerated||accelerated
+accoding||according
+accomodate||accommodate
+accomodates||accommodates
+accordign||according
+accoring||according
+accout||account
+accquire||acquire
+accquired||acquired
+accross||across
+accumalate||accumulate
+accumalator||accumulator
+acessable||accessible
+acess||access
+acessing||accessing
+achitecture||architecture
+acient||ancient
+acitions||actions
+acitve||active
+acknowldegement||acknowledgment
+acknowledgement||acknowledgment
+ackowledge||acknowledge
+ackowledged||acknowledged
+acording||according
+activete||activate
+actived||activated
+actualy||actually
+acumulating||accumulating
+acumulative||accumulative
+acumulator||accumulator
+acutally||actually
+adapater||adapter
+addional||additional
+additionaly||additionally
+additonal||additional
+addres||address
+adddress||address
+addreses||addresses
+addresss||address
+addrress||address
+aditional||additional
+aditionally||additionally
+aditionaly||additionally
+adminstrative||administrative
+adress||address
+adresses||addresses
+adrresses||addresses
+advertisment||advertisement
+adviced||advised
+afecting||affecting
+againt||against
+agaist||against
+aggreataon||aggregation
+aggreation||aggregation
+albumns||albums
+alegorical||allegorical
+algined||aligned
+algorith||algorithm
+algorithmical||algorithmically
+algoritm||algorithm
+algoritms||algorithms
+algorithmn||algorithm
+algorrithm||algorithm
+algorritm||algorithm
+aligment||alignment
+alignement||alignment
+allign||align
+alligned||aligned
+alllocate||allocate
+alloated||allocated
+allocatote||allocate
+allocatrd||allocated
+allocte||allocate
+allpication||application
+alocate||allocate
+alogirhtms||algorithms
+alogrithm||algorithm
+alot||a lot
+alow||allow
+alows||allows
+alreay||already
+alredy||already
+altough||although
+alue||value
+ambigious||ambiguous
+ambigous||ambiguous
+amoung||among
+amout||amount
+amplifer||amplifier
+amplifyer||amplifier
+an union||a union
+an user||a user
+an userspace||a userspace
+an one||a one
+analysator||analyzer
+ang||and
+anniversery||anniversary
+annoucement||announcement
+anomolies||anomalies
+anomoly||anomaly
+anway||anyway
+aplication||application
+appearence||appearance
+applicaion||application
+appliction||application
+applictions||applications
+applys||applies
+appplications||applications
+appropiate||appropriate
+appropriatly||appropriately
+approriate||appropriate
+approriately||appropriately
+apropriate||appropriate
+aquainted||acquainted
+aquired||acquired
+aquisition||acquisition
+arbitary||arbitrary
+architechture||architecture
+arguement||argument
+arguements||arguments
+aritmetic||arithmetic
+arne't||aren't
+arraival||arrival
+artifical||artificial
+artillary||artillery
+asign||assign
+asser||assert
+assertation||assertion
+assertting||asserting
+assiged||assigned
+assigment||assignment
+assigments||assignments
+assistent||assistant
+assocation||association
+associcated||associated
+assotiated||associated
+asssert||assert
+assum||assume
+assumtpion||assumption
+asuming||assuming
+asycronous||asynchronous
+asynchnous||asynchronous
+asynchromous||asynchronous
+asymetric||asymmetric
+asymmeric||asymmetric
+atomatically||automatically
+atomicly||atomically
+atempt||attempt
+attachement||attachment
+attatch||attach
+attched||attached
+attemp||attempt
+attemps||attempts
+attemping||attempting
+attepmpt||attempt
+attnetion||attention
+attruibutes||attributes
+authentification||authentication
+authenicated||authenticated
+automaticaly||automatically
+automaticly||automatically
+automatize||automate
+automatized||automated
+automatizes||automates
+autonymous||autonomous
+auxillary||auxiliary
+auxilliary||auxiliary
+avaiable||available
+avaible||available
+availabe||available
+availabled||available
+availablity||availability
+availaible||available
+availale||available
+availavility||availability
+availble||available
+availiable||available
+availible||available
+avalable||available
+avaliable||available
+aysnc||async
+backgroud||background
+backword||backward
+backwords||backwards
+bahavior||behavior
+bakup||backup
+baloon||balloon
+baloons||balloons
+bandwith||bandwidth
+banlance||balance
+batery||battery
+beacuse||because
+becasue||because
+becomming||becoming
+becuase||because
+beeing||being
+befor||before
+begining||beginning
+beter||better
+betweeen||between
+bianries||binaries
+bitmast||bitmask
+boardcast||broadcast
+borad||board
+boundry||boundary
+brievely||briefly
+brigde||bridge
+broadcase||broadcast
+broadcat||broadcast
+bufer||buffer
+bufufer||buffer
+cacluated||calculated
+caculate||calculate
+caculation||calculation
+cadidate||candidate
+cahces||caches
+calender||calendar
+calescing||coalescing
+calle||called
+callibration||calibration
+callled||called
+callser||caller
+calucate||calculate
+calulate||calculate
+cancelation||cancellation
+cancle||cancel
+capabilites||capabilities
+capabilties||capabilities
+capabilty||capability
+capabitilies||capabilities
+capablity||capability
+capatibilities||capabilities
+capapbilities||capabilities
+caputure||capture
+carefuly||carefully
+cariage||carriage
+catagory||category
+cehck||check
+challange||challenge
+challanges||challenges
+chache||cache
+chanell||channel
+changable||changeable
+chanined||chained
+channle||channel
+channnel||channel
+charachter||character
+charachters||characters
+charactor||character
+charater||character
+charaters||characters
+charcter||character
+chcek||check
+chck||check
+checksumed||checksummed
+checksuming||checksumming
+childern||children
+childs||children
+chiled||child
+chked||checked
+chnage||change
+chnages||changes
+chnnel||channel
+choosen||chosen
+chouse||chose
+circumvernt||circumvent
+claread||cleared
+clared||cleared
+closeing||closing
+clustred||clustered
+cnfiguration||configuration
+coexistance||coexistence
+colescing||coalescing
+collapsable||collapsible
+colorfull||colorful
+comand||command
+comit||commit
+commerical||commercial
+comming||coming
+comminucation||communication
+commited||committed
+commiting||committing
+committ||commit
+commoditiy||commodity
+comsume||consume
+comsumer||consumer
+comsuming||consuming
+compability||compatibility
+compaibility||compatibility
+comparsion||comparison
+compatability||compatibility
+compatable||compatible
+compatibililty||compatibility
+compatibiliy||compatibility
+compatibilty||compatibility
+compatiblity||compatibility
+competion||completion
+compilant||compliant
+compleatly||completely
+completition||completion
+completly||completely
+complient||compliant
+componnents||components
+compoment||component
+comppatible||compatible
+compres||compress
+compresion||compression
+comression||compression
+comunication||communication
+conbination||combination
+conditionaly||conditionally
+conditon||condition
+condtion||condition
+conected||connected
+conector||connector
+configration||configuration
+configuartion||configuration
+configuation||configuration
+configued||configured
+configuratoin||configuration
+configuraton||configuration
+configuretion||configuration
+configutation||configuration
+conider||consider
+conjuction||conjunction
+connecetd||connected
+connectinos||connections
+connetor||connector
+connnection||connection
+connnections||connections
+consistancy||consistency
+consistant||consistent
+containes||contains
+containts||contains
+contaisn||contains
+contant||contact
+contence||contents
+contiguos||contiguous
+continious||continuous
+continous||continuous
+continously||continuously
+continueing||continuing
+contraints||constraints
+contruct||construct
+contol||control
+contoller||controller
+controled||controlled
+controler||controller
+controll||control
+contruction||construction
+contry||country
+conuntry||country
+convertion||conversion
+convertor||converter
+convienient||convenient
+convinient||convenient
+corected||corrected
+correponding||corresponding
+correponds||corresponds
+correspoding||corresponding
+cotrol||control
+cound||could
+couter||counter
+coutner||counter
+cryptocraphic||cryptographic
+cunter||counter
+curently||currently
+cylic||cyclic
+dafault||default
+deafult||default
+deamon||daemon
+debouce||debounce
+decendant||descendant
+decendants||descendants
+decompres||decompress
+decsribed||described
+decription||description
+dectected||detected
+defailt||default
+deferal||deferral
+deffered||deferred
+defferred||deferred
+definate||definite
+definately||definitely
+defintion||definition
+defintions||definitions
+defualt||default
+defult||default
+deintializing||deinitializing
+deintialize||deinitialize
+deintialized||deinitialized
+deivce||device
+delared||declared
+delare||declare
+delares||declares
+delaring||declaring
+delemiter||delimiter
+delievered||delivered
+demodualtor||demodulator
+demension||dimension
+dependancies||dependencies
+dependancy||dependency
+dependant||dependent
+dependend||dependent
+depreacted||deprecated
+depreacte||deprecate
+desactivate||deactivate
+desciptor||descriptor
+desciptors||descriptors
+descripto||descriptor
+descripton||description
+descrition||description
+descritptor||descriptor
+desctiptor||descriptor
+desriptor||descriptor
+desriptors||descriptors
+desination||destination
+destionation||destination
+destoried||destroyed
+destory||destroy
+destoryed||destroyed
+destorys||destroys
+destroied||destroyed
+detabase||database
+deteced||detected
+develope||develop
+developement||development
+developped||developed
+developpement||development
+developper||developer
+developpment||development
+deveolpment||development
+devided||divided
+deviece||device
+diable||disable
+dicline||decline
+dictionnary||dictionary
+didnt||didn't
+diferent||different
+differrence||difference
+diffrent||different
+differenciate||differentiate
+diffrentiate||differentiate
+difinition||definition
+digial||digital
+dimention||dimension
+dimesions||dimensions
+disgest||digest
+dispalying||displaying
+diplay||display
+directon||direction
+direectly||directly
+diregard||disregard
+disassocation||disassociation
+disapear||disappear
+disapeared||disappeared
+disappared||disappeared
+disbale||disable
+disbaled||disabled
+disble||disable
+disbled||disabled
+disconnet||disconnect
+discontinous||discontinuous
+disharge||discharge
+disnabled||disabled
+dispertion||dispersion
+dissapears||disappears
+dissconect||disconnect
+distiction||distinction
+divisable||divisible
+divsiors||divisors
+docuentation||documentation
+documantation||documentation
+documentaion||documentation
+documment||document
+doesnt||doesn't
+donwload||download
+donwloading||downloading
+dorp||drop
+dosen||doesn
+downlad||download
+downlads||downloads
+droped||dropped
+droput||dropout
+druing||during
+dynmaic||dynamic
+eanable||enable
+eanble||enable
+easilly||easily
+ecspecially||especially
+edditable||editable
+editting||editing
+efective||effective
+effectivness||effectiveness
+efficently||efficiently
+ehther||ether
+eigth||eight
+elementry||elementary
+eletronic||electronic
+embeded||embedded
+enabledi||enabled
+enbale||enable
+enble||enable
+enchanced||enhanced
+encorporating||incorporating
+encrupted||encrypted
+encrypiton||encryption
+encryptio||encryption
+endianess||endianness
+enhaced||enhanced
+enlightnment||enlightenment
+enqueing||enqueuing
+entires||entries
+entites||entities
+entrys||entries
+enocded||encoded
+enterily||entirely
+enviroiment||environment
+enviroment||environment
+environement||environment
+environent||environment
+eqivalent||equivalent
+equiped||equipped
+equivelant||equivalent
+equivilant||equivalent
+eror||error
+errorr||error
+estbalishment||establishment
+etsablishment||establishment
+etsbalishment||establishment
+excecutable||executable
+exceded||exceeded
+exceeed||exceed
+excellant||excellent
+execeeded||exceeded
+execeeds||exceeds
+exeed||exceed
+exeuction||execution
+existance||existence
+existant||existent
+exixt||exist
+exlcude||exclude
+exlcusive||exclusive
+exmaple||example
+expecially||especially
+experies||expires
+explicite||explicit
+explicitely||explicitly
+explict||explicit
+explictely||explicitly
+explictly||explicitly
+expresion||expression
+exprimental||experimental
+extened||extended
+extensability||extensibility
+extention||extension
+extenstion||extension
+extracter||extractor
+faied||failed
+faield||failed
+falied||failed
+faild||failed
+failded||failed
+failer||failure
+faill||fail
+failied||failed
+faillure||failure
+failue||failure
+failuer||failure
+failng||failing
+faireness||fairness
+falied||failed
+faliure||failure
+fallbck||fallback
+familar||familiar
+fatser||faster
+feauture||feature
+feautures||features
+fetaure||feature
+fetaures||features
+fileystem||filesystem
+fimware||firmware
+firmare||firmware
+firmaware||firmware
+firware||firmware
+finanize||finalize
+findn||find
+finilizes||finalizes
+finsih||finish
+flusing||flushing
+folloing||following
+followign||following
+followings||following
+follwing||following
+fonud||found
+forseeable||foreseeable
+forse||force
+fortan||fortran
+forwardig||forwarding
+frambuffer||framebuffer
+framming||framing
+framwork||framework
+frequncy||frequency
+frequancy||frequency
+frome||from
+fucntion||function
+fuction||function
+fuctions||functions
+fullill||fulfill
+funcation||function
+funcion||function
+functionallity||functionality
+functionaly||functionally
+functionnality||functionality
+functonality||functionality
+funtion||function
+funtions||functions
+furthur||further
+futhermore||furthermore
+futrue||future
+gatable||gateable
+gateing||gating
+gauage||gauge
+gaurenteed||guaranteed
+generiously||generously
+genereate||generate
+genereted||generated
+genric||generic
+globel||global
+grabing||grabbing
+grahical||graphical
+grahpical||graphical
+grapic||graphic
+grranted||granted
+guage||gauge
+guarenteed||guaranteed
+guarentee||guarantee
+halfs||halves
+hander||handler
+handfull||handful
+hanlde||handle
+hanled||handled
+happend||happened
+harware||hardware
+havind||having
+heirarchically||hierarchically
+helpfull||helpful
+hexdecimal||hexadecimal
+hybernate||hibernate
+hierachy||hierarchy
+hierarchie||hierarchy
+homogenous||homogeneous
+howver||however
+hsould||should
+hypervior||hypervisor
+hypter||hyper
+identidier||identifier
+iligal||illegal
+illigal||illegal
+illgal||illegal
+iomaped||iomapped
+imblance||imbalance
+immeadiately||immediately
+immedaite||immediate
+immedate||immediate
+immediatelly||immediately
+immediatly||immediately
+immidiate||immediate
+immutible||immutable
+impelentation||implementation
+impementated||implemented
+implemantation||implementation
+implemenation||implementation
+implementaiton||implementation
+implementated||implemented
+implemention||implementation
+implementd||implemented
+implemetation||implementation
+implemntation||implementation
+implentation||implementation
+implmentation||implementation
+implmenting||implementing
+incative||inactive
+incomming||incoming
+incompatabilities||incompatibilities
+incompatable||incompatible
+incompatble||incompatible
+inconsistant||inconsistent
+increas||increase
+incremeted||incremented
+incrment||increment
+inculde||include
+indendation||indentation
+indended||intended
+independant||independent
+independantly||independently
+independed||independent
+indiate||indicate
+indicat||indicate
+inexpect||inexpected
+inferface||interface
+infomation||information
+informatiom||information
+informations||information
+informtion||information
+infromation||information
+ingore||ignore
+inital||initial
+initalized||initialized
+initalised||initialized
+initalise||initialize
+initalize||initialize
+initation||initiation
+initators||initiators
+initialiazation||initialization
+initializationg||initialization
+initializiation||initialization
+initialze||initialize
+initialzed||initialized
+initialzing||initializing
+initilization||initialization
+initilize||initialize
+initliaze||initialize
+initilized||initialized
+inofficial||unofficial
+inrerface||interface
+insititute||institute
+instace||instance
+instal||install
+instanciate||instantiate
+instanciated||instantiated
+insufficent||insufficient
+inteface||interface
+integreated||integrated
+integrety||integrity
+integrey||integrity
+intendet||intended
+intented||intended
+interanl||internal
+interchangable||interchangeable
+interferring||interfering
+interger||integer
+intermittant||intermittent
+internel||internal
+interoprability||interoperability
+interuupt||interrupt
+interupt||interrupt
+interupts||interrupts
+interrface||interface
+interrrupt||interrupt
+interrup||interrupt
+interrups||interrupts
+interruptted||interrupted
+interupted||interrupted
+interupt||interrupt
+intial||initial
+intialisation||initialisation
+intialised||initialised
+intialise||initialise
+intialization||initialization
+intialized||initialized
+intialize||initialize
+intregral||integral
+intrerrupt||interrupt
+intrrupt||interrupt
+intterrupt||interrupt
+intuative||intuitive
+inavlid||invalid
+invaid||invalid
+invaild||invalid
+invailid||invalid
+invald||invalid
+invalde||invalid
+invalide||invalid
+invalidiate||invalidate
+invalud||invalid
+invididual||individual
+invokation||invocation
+invokations||invocations
+ireelevant||irrelevant
+irrelevent||irrelevant
+isnt||isn't
+isssue||issue
+issus||issues
+iteraions||iterations
+iternations||iterations
+itertation||iteration
+itslef||itself
+jave||java
+jeffies||jiffies
+jumpimng||jumping
+juse||just
+jus||just
+kown||known
+langage||language
+langauage||language
+langauge||language
+langugage||language
+lauch||launch
+layed||laid
+legnth||length
+leightweight||lightweight
+lengh||length
+lenght||length
+lenth||length
+lesstiff||lesstif
+libaries||libraries
+libary||library
+librairies||libraries
+libraris||libraries
+licenceing||licencing
+limted||limited
+logaritmic||logarithmic
+loggging||logging
+loggin||login
+logile||logfile
+loobpack||loopback
+loosing||losing
+losted||lost
+maangement||management
+machinary||machinery
+maibox||mailbox
+maintainance||maintenance
+maintainence||maintenance
+maintan||maintain
+makeing||making
+mailformed||malformed
+malplaced||misplaced
+malplace||misplace
+managable||manageable
+managment||management
+mangement||management
+manoeuvering||maneuvering
+manufaucturing||manufacturing
+mappping||mapping
+matchs||matches
+mathimatical||mathematical
+mathimatic||mathematic
+mathimatics||mathematics
+maximium||maximum
+maxium||maximum
+mechamism||mechanism
+meetign||meeting
+memeory||memory
+memmber||member
+memoery||memory
+ment||meant
+mergable||mergeable
+mesage||message
+messags||messages
+messgaes||messages
+messsage||message
+messsages||messages
+metdata||metadata
+micropone||microphone
+microprocesspr||microprocessor
+migrateable||migratable
+milliseonds||milliseconds
+minium||minimum
+minimam||minimum
+miniumum||minimum
+minumum||minimum
+misalinged||misaligned
+miscelleneous||miscellaneous
+misformed||malformed
+mispelled||misspelled
+mispelt||misspelt
+mising||missing
+mismactch||mismatch
+missign||missing
+missmanaged||mismanaged
+missmatch||mismatch
+misssing||missing
+miximum||maximum
+mmnemonic||mnemonic
+mnay||many
+modfiy||modify
+modulues||modules
+momery||memory
+memomry||memory
+monitring||monitoring
+monochorome||monochrome
+monochromo||monochrome
+monocrome||monochrome
+mopdule||module
+mroe||more
+mulitplied||multiplied
+multidimensionnal||multidimensional
+multipe||multiple
+multple||multiple
+mumber||number
+muticast||multicast
+mutilcast||multicast
+mutiple||multiple
+mutli||multi
+nams||names
+navagating||navigating
+nead||need
+neccecary||necessary
+neccesary||necessary
+neccessary||necessary
+necesary||necessary
+neded||needed
+negaive||negative
+negoitation||negotiation
+negotation||negotiation
+nerver||never
+nescessary||necessary
+nessessary||necessary
+noticable||noticeable
+notication||notification
+notications||notifications
+notifcations||notifications
+notifed||notified
+notity||notify
+numebr||number
+numner||number
+obtaion||obtain
+obusing||abusing
+occassionally||occasionally
+occationally||occasionally
+occurance||occurrence
+occurances||occurrences
+occured||occurred
+occurence||occurrence
+occure||occurred
+occured||occurred
+occuring||occurring
+offser||offset
+offet||offset
+offlaod||offload
+offloded||offloaded
+offseting||offsetting
+omited||omitted
+omiting||omitting
+omitt||omit
+ommiting||omitting
+ommitted||omitted
+onself||oneself
+ony||only
+operatione||operation
+opertaions||operations
+optionnal||optional
+optmizations||optimizations
+orientatied||orientated
+orientied||oriented
+orignal||original
+originial||original
+otherise||otherwise
+ouput||output
+oustanding||outstanding
+overaall||overall
+overhread||overhead
+overlaping||overlapping
+overide||override
+overrided||overridden
+overriden||overridden
+overun||overrun
+overwritting||overwriting
+overwriten||overwritten
+pacakge||package
+pachage||package
+packacge||package
+packege||package
+packge||package
+packtes||packets
+pakage||package
+paket||packet
+pallette||palette
+paln||plan
+paramameters||parameters
+paramaters||parameters
+paramater||parameter
+parametes||parameters
+parametised||parametrised
+paramter||parameter
+paramters||parameters
+parmaters||parameters
+particuarly||particularly
+particularily||particularly
+partion||partition
+partions||partitions
+partiton||partition
+pased||passed
+passin||passing
+pathes||paths
+pattrns||patterns
+pecularities||peculiarities
+peformance||performance
+peforming||performing
+peice||piece
+pendantic||pedantic
+peprocessor||preprocessor
+perfoming||performing
+perfomring||performing
+periperal||peripheral
+peripherial||peripheral
+permissons||permissions
+peroid||period
+persistance||persistence
+persistant||persistent
+phoneticly||phonetically
+plalform||platform
+platfoem||platform
+platfrom||platform
+plattform||platform
+pleaes||please
+ploting||plotting
+plugable||pluggable
+poinnter||pointer
+pointeur||pointer
+poiter||pointer
+posible||possible
+positon||position
+possibilites||possibilities
+potocol||protocol
+powerfull||powerful
+pramater||parameter
+preamle||preamble
+preample||preamble
+preapre||prepare
+preceeded||preceded
+preceeding||preceding
+preceed||precede
+precendence||precedence
+precission||precision
+preemptable||preemptible
+prefered||preferred
+prefferably||preferably
+premption||preemption
+prepaired||prepared
+preperation||preparation
+preprare||prepare
+pressre||pressure
+primative||primitive
+princliple||principle
+priorty||priority
+privilaged||privileged
+privilage||privilege
+priviledge||privilege
+priviledges||privileges
+probaly||probably
+procceed||proceed
+proccesors||processors
+procesed||processed
+proces||process
+procesing||processing
+processessing||processing
+processess||processes
+processpr||processor
+processsed||processed
+processsing||processing
+procteted||protected
+prodecure||procedure
+progamming||programming
+progams||programs
+progess||progress
+programers||programmers
+programm||program
+programms||programs
+progresss||progress
+prohibitted||prohibited
+prohibitting||prohibiting
+promiscous||promiscuous
+promps||prompts
+pronnounced||pronounced
+prononciation||pronunciation
+pronouce||pronounce
+pronunce||pronounce
+propery||property
+propigate||propagate
+propigation||propagation
+propogate||propagate
+prosess||process
+protable||portable
+protcol||protocol
+protecion||protection
+protedcted||protected
+protocoll||protocol
+promixity||proximity
+psudo||pseudo
+psuedo||pseudo
+psychadelic||psychedelic
+pwoer||power
+queing||queuing
+quering||querying
+queus||queues
+randomally||randomly
+raoming||roaming
+reasearcher||researcher
+reasearchers||researchers
+reasearch||research
+receieve||receive
+recepient||recipient
+recevied||received
+receving||receiving
+recieved||received
+recieve||receive
+reciever||receiver
+recieves||receives
+recogniced||recognised
+recognizeable||recognizable
+recommanded||recommended
+recyle||recycle
+redircet||redirect
+redirectrion||redirection
+redundacy||redundancy
+reename||rename
+refcounf||refcount
+refence||reference
+refered||referred
+referenace||reference
+refering||referring
+refernces||references
+refernnce||reference
+refrence||reference
+registed||registered
+registerd||registered
+registeration||registration
+registeresd||registered
+registerred||registered
+registes||registers
+registraration||registration
+regsiter||register
+regster||register
+regualar||regular
+reguator||regulator
+regulamentations||regulations
+reigstration||registration
+releated||related
+relevent||relevant
+reloade||reload
+remoote||remote
+remore||remote
+removeable||removable
+repectively||respectively
+replacable||replaceable
+replacments||replacements
+replys||replies
+reponse||response
+representaion||representation
+reqeust||request
+reqister||register
+requestied||requested
+requiere||require
+requirment||requirement
+requred||required
+requried||required
+requst||request
+requsted||requested
+reregisteration||reregistration
+reseting||resetting
+reseved||reserved
+reseverd||reserved
+resizeable||resizable
+resouce||resource
+resouces||resources
+resoures||resources
+responce||response
+resrouce||resource
+ressizes||resizes
+ressource||resource
+ressources||resources
+restesting||retesting
+resumbmitting||resubmitting
+retransmited||retransmitted
+retreived||retrieved
+retreive||retrieve
+retreiving||retrieving
+retrive||retrieve
+retrived||retrieved
+retrun||return
+retun||return
+retuned||returned
+reudce||reduce
+reuest||request
+reuqest||request
+reutnred||returned
+revsion||revision
+rmeoved||removed
+rmeove||remove
+rmeoves||removes
+rountine||routine
+routins||routines
+rquest||request
+runing||running
+runned||ran
+runnning||running
+runtine||runtime
+sacrifying||sacrificing
+safly||safely
+safty||safety
+savable||saveable
+scaleing||scaling
+scaned||scanned
+scaning||scanning
+scarch||search
+schdule||schedule
+seach||search
+searchs||searches
+secquence||sequence
+secund||second
+segement||segment
+semaphone||semaphore
+senario||scenario
+senarios||scenarios
+sentivite||sensitive
+separatly||separately
+sepcify||specify
+seperated||separated
+seperately||separately
+seperate||separate
+seperatly||separately
+seperator||separator
+sepperate||separate
+seqeunce||sequence
+seqeuncer||sequencer
+seqeuencer||sequencer
+sequece||sequence
+sequencial||sequential
+serivce||service
+serveral||several
+servive||service
+setts||sets
+settting||setting
+shapshot||snapshot
+shotdown||shutdown
+shoud||should
+shouldnt||shouldn't
+shoule||should
+shrinked||shrunk
+siginificantly||significantly
+signabl||signal
+significanly||significantly
+similary||similarly
+similiar||similar
+simlar||similar
+simliar||similar
+simpified||simplified
+singaled||signaled
+singal||signal
+singed||signed
+sleeped||slept
+sliped||slipped
+softwares||software
+speach||speech
+specfic||specific
+specfield||specified
+speciefied||specified
+specifc||specific
+specifed||specified
+specificatin||specification
+specificaton||specification
+specifing||specifying
+specifiying||specifying
+speficied||specified
+speicify||specify
+speling||spelling
+spinlcok||spinlock
+spinock||spinlock
+splitted||split
+spreaded||spread
+spurrious||spurious
+sructure||structure
+stablilization||stabilization
+staically||statically
+staion||station
+standardss||standards
+standartization||standardization
+standart||standard
+standy||standby
+stardard||standard
+staticly||statically
+statuss||status
+stoped||stopped
+stoping||stopping
+stoppped||stopped
+straming||streaming
+struc||struct
+structres||structures
+stuct||struct
+strucuture||structure
+stucture||structure
+sturcture||structure
+subdirectoires||subdirectories
+suble||subtle
+substract||subtract
+submition||submission
+suceed||succeed
+succesfully||successfully
+succesful||successful
+successed||succeeded
+successfull||successful
+successfuly||successfully
+sucessfully||successfully
+sucess||success
+superflous||superfluous
+superseeded||superseded
+suplied||supplied
+suported||supported
+suport||support
+supportet||supported
+suppored||supported
+supportin||supporting
+suppoted||supported
+suppported||supported
+suppport||support
+supress||suppress
+surpressed||suppressed
+surpresses||suppresses
+susbsystem||subsystem
+suspeneded||suspended
+suspsend||suspend
+suspicously||suspiciously
+swaping||swapping
+switchs||switches
+swith||switch
+swithable||switchable
+swithc||switch
+swithced||switched
+swithcing||switching
+swithed||switched
+swithing||switching
+swtich||switch
+syfs||sysfs
+symetric||symmetric
+synax||syntax
+synchonized||synchronized
+synchronuously||synchronously
+syncronize||synchronize
+syncronized||synchronized
+syncronizing||synchronizing
+syncronus||synchronous
+syste||system
+sytem||system
+sythesis||synthesis
+taht||that
+tansmit||transmit
+targetted||targeted
+targetting||targeting
+taskelt||tasklet
+teh||the
+temorary||temporary
+temproarily||temporarily
+temperture||temperature
+thead||thread
+therfore||therefore
+thier||their
+threds||threads
+threee||three
+threshhold||threshold
+thresold||threshold
+throught||through
+trackling||tracking
+troughput||throughput
+thses||these
+tiggers||triggers
+tiggered||triggered
+tipically||typically
+timeing||timing
+timout||timeout
+tmis||this
+toogle||toggle
+torerable||tolerable
+traking||tracking
+tramsmitted||transmitted
+tramsmit||transmit
+tranasction||transaction
+tranfer||transfer
+transcevier||transceiver
+transciever||transceiver
+transferd||transferred
+transfered||transferred
+transfering||transferring
+transision||transition
+transmittd||transmitted
+transormed||transformed
+trasfer||transfer
+trasmission||transmission
+treshold||threshold
+trigerred||triggered
+trigerring||triggering
+trun||turn
+tunning||tuning
+ture||true
+tyep||type
+udpate||update
+uesd||used
+uknown||unknown
+usccess||success
+usupported||unsupported
+uncommited||uncommitted
+unconditionaly||unconditionally
+undeflow||underflow
+underun||underrun
+unecessary||unnecessary
+unexecpted||unexpected
+unexepected||unexpected
+unexpcted||unexpected
+unexpectd||unexpected
+unexpeted||unexpected
+unexpexted||unexpected
+unfortunatelly||unfortunately
+unifiy||unify
+uniterrupted||uninterrupted
+unintialized||uninitialized
+unitialized||uninitialized
+unkmown||unknown
+unknonw||unknown
+unknow||unknown
+unkown||unknown
+unamed||unnamed
+uneeded||unneeded
+unneded||unneeded
+unneccecary||unnecessary
+unneccesary||unnecessary
+unneccessary||unnecessary
+unnecesary||unnecessary
+unneedingly||unnecessarily
+unnsupported||unsupported
+unmached||unmatched
+unregester||unregister
+unresgister||unregister
+unrgesiter||unregister
+unsinged||unsigned
+unstabel||unstable
+unsolicitied||unsolicited
+unsuccessfull||unsuccessful
+unsuported||unsupported
+untill||until
+unuseful||useless
+unvalid||invalid
+upate||update
+upsupported||unsupported
+usefule||useful
+usefull||useful
+usege||usage
+usera||users
+usualy||usually
+usupported||unsupported
+utilites||utilities
+utillities||utilities
+utilties||utilities
+utiltity||utility
+utitity||utility
+utitlty||utility
+vaid||valid
+vaild||valid
+valide||valid
+variantions||variations
+varible||variable
+varient||variant
+vaule||value
+verbse||verbose
+veify||verify
+verisons||versions
+verison||version
+verson||version
+vicefersa||vice-versa
+virtal||virtual
+virtaul||virtual
+virtiual||virtual
+visiters||visitors
+vitual||virtual
+vunerable||vulnerable
+wakeus||wakeups
+wathdog||watchdog
+wating||waiting
+wiat||wait
+wether||whether
+whataver||whatever
+whcih||which
+whenver||whenever
+wheter||whether
+whe||when
+wierd||weird
+wiil||will
+wirte||write
+withing||within
+wnat||want
+workarould||workaround
+writeing||writing
+writting||writing
+wtih||with
+zombe||zombie
+zomebie||zombie
diff --git a/scripts/srecimage.pl b/scripts/srecimage.pl
new file mode 100755
index 0000000..b9e2a84
--- /dev/null
+++ b/scripts/srecimage.pl
@@ -0,0 +1,57 @@
+#!/usr/bin/env perl
+#
+# srecimage.pl - script to convert a binary image into srec
+# Copyright (c) 2015 - Jo-Philipp Wich <jo@mein.io>
+#
+# This script is in the public domain.
+
+use strict;
+
+my ($input, $output, $offset) = @ARGV;
+
+if (!defined($input) || !-f $input || !defined($output) ||
+    !defined($offset) || $offset !~ /^(0x)?[a-fA-F0-9]+$/) {
+	die "Usage: $0 <input file> <output file> <load address>\n";
+}
+
+sub srec
+{
+	my ($type, $addr, $data, $len) = @_;
+	my @addrtypes = qw(%04X %04X %06X %08X %08X %04X %06X %08X %06X %04X);
+	my $addrstr = sprintf $addrtypes[$type], $addr;
+
+	$len = length($data) if ($len <= 0);
+	$len += 1 + (length($addrstr) / 2);
+
+	my $sum = $len;
+
+	foreach my $byte (unpack('C*', pack('H*', $addrstr)), unpack('C*', $data))
+	{
+		$sum += $byte;
+	}
+
+	return sprintf "S%d%02X%s%s%02X\r\n",
+	       $type, $len, $addrstr, uc(unpack('H*', $data)), ~($sum & 0xFF) & 0xFF;
+}
+
+
+open(IN, '<:raw', $input) || die "Unable to open $input: $!\n";
+open(OUT, '>:raw', $output) || die "Unable to open $output: $!\n";
+
+my ($basename) = $output =~ m!([^/]+)$!;
+
+print OUT srec(0, 0, $basename, 0);
+
+my $off = hex($offset);
+my $len;
+
+while (defined($len = read(IN, my $buf, 16)) && $len > 0)
+{
+	print OUT srec(3, $off, $buf, $len);
+	$off += $len;
+}
+
+print OUT srec(7, hex($offset), "", 0);
+
+close OUT;
+close IN;
diff --git a/scripts/strip-kmod.sh b/scripts/strip-kmod.sh
new file mode 100755
index 0000000..e5b2868
--- /dev/null
+++ b/scripts/strip-kmod.sh
@@ -0,0 +1,55 @@
+#!/bin/sh
+[ -n "$CROSS" ] || {
+	echo "The variable CROSS must be set to point to the cross-compiler prefix"
+	exit 1
+}
+
+MODULE="$1"
+
+[ "$#" -ne 1 ] && {
+	echo "Usage: $0 <module>"
+	exit 1
+}
+
+ARGS=
+if [ -n "$KEEP_SYMBOLS" ]; then
+	ARGS="-X --strip-debug"
+else
+	ARGS="-x -G __this_module --strip-unneeded"
+fi
+
+if [ -z "$KEEP_BUILD_ID" ]; then
+	ARGS="$ARGS -R .note.gnu.build-id"
+fi
+
+${CROSS}objcopy \
+	-R .comment \
+	-R .pdr \
+	-R .mdebug.abi32 \
+	-R .gnu.attributes \
+	-R .reginfo \
+	-R .MIPS.abiflags \
+	-R .note.GNU-stack \
+	$ARGS \
+	"$MODULE" "$MODULE.tmp"
+
+[ -n "$NO_RENAME" ] && {
+	mv "${MODULE}.tmp" "$MODULE"
+	exit 0
+}
+
+${CROSS}nm "$MODULE.tmp" | awk '
+BEGIN {
+	n = 0
+}
+
+$3 && $2 ~ /[brtd]/ && $3 !~ /\$LC/ && !def[$3] {
+	print "--redefine-sym "$3"=_"n;
+	n = n + 1
+	def[$3] = 1
+}
+' > "$MODULE.tmp1"
+
+${CROSS}objcopy $(cat ${MODULE}.tmp1) ${MODULE}.tmp ${MODULE}.out
+mv "${MODULE}.out" "${MODULE}"
+rm -f "${MODULE}".t*
diff --git a/scripts/symlink-tree.sh b/scripts/symlink-tree.sh
new file mode 100755
index 0000000..6ed91df
--- /dev/null
+++ b/scripts/symlink-tree.sh
@@ -0,0 +1,51 @@
+#!/bin/sh
+# Create a new openwrt tree with symlinks pointing at the current tree
+# Usage: ./scripts/symlink-tree.sh <destination>
+
+FILES="
+	BSDmakefile
+	config
+	Config.in
+	LICENSE
+	Makefile
+	README
+	dl
+	feeds.conf.default
+	include
+	package
+	rules.mk
+	scripts
+	target
+	toolchain
+	tools"
+
+OPTIONAL_FILES="
+	.git"
+
+if [ -f feeds.conf ] ; then
+	FILES="$FILES feeds.conf"
+fi
+
+if [ -z "$1" ]; then
+	echo "Syntax: $0 <destination>" >&2
+	exit 1
+fi
+
+if [ -e "$1" ]; then
+	echo "Error: $1 already exists" >&2
+	exit 1
+fi
+
+set -e # fail if any commands fails
+mkdir -p dl "$1"
+for file in $FILES; do
+	[ -e "$PWD/$file" ] || {
+		echo "ERROR: $file does not exist in the current tree" >&2
+		exit 1
+	}
+	ln -s "$PWD/$file" "$1/"
+done
+for file in $OPTIONAL_FILES; do
+	[ -e "$PWD/$file" ] && ln -s "$PWD/$file" "$1/"
+done
+exit 0
diff --git a/scripts/sysupgrade-tar.sh b/scripts/sysupgrade-tar.sh
new file mode 100755
index 0000000..b93b258
--- /dev/null
+++ b/scripts/sysupgrade-tar.sh
@@ -0,0 +1,84 @@
+#!/bin/sh
+
+. $TOPDIR/scripts/functions.sh
+
+board=""
+kernel=""
+rootfs=""
+outfile=""
+err=""
+
+while [ "$1" ]; do
+	case "$1" in
+	"--board")
+		board="$2"
+		shift
+		shift
+		continue
+		;;
+	"--kernel")
+		kernel="$2"
+		shift
+		shift
+		continue
+		;;
+	"--rootfs")
+		rootfs="$2"
+		shift
+		shift
+		continue
+		;;
+	*)
+		if [ ! "$outfile" ]; then
+			outfile=$1
+			shift
+			continue
+		fi
+		;;
+	esac
+done
+
+if [ ! -n "$board" -o ! -r "$kernel" -a  ! -r "$rootfs" -o ! "$outfile" ]; then
+	echo "syntax: $0 [--board boardname] [--kernel kernelimage] [--rootfs rootfs] out"
+	exit 1
+fi
+
+tmpdir="$( mktemp -d 2> /dev/null )"
+if [ -z "$tmpdir" ]; then
+	# try OSX signature
+	tmpdir="$( mktemp -t 'ubitmp' -d )"
+fi
+
+if [ -z "$tmpdir" ]; then
+	exit 1
+fi
+
+mkdir -p "${tmpdir}/sysupgrade-${board}"
+echo "BOARD=${board}" > "${tmpdir}/sysupgrade-${board}/CONTROL"
+if [ -n "${rootfs}" ]; then
+	case "$( get_fs_type ${rootfs} )" in
+	"squashfs")
+		dd if="${rootfs}" of="${tmpdir}/sysupgrade-${board}/root" bs=1024 conv=sync
+		;;
+	*)
+		cp "${rootfs}" "${tmpdir}/sysupgrade-${board}/root"
+		;;
+	esac
+fi
+[ -z "${kernel}" ] || cp "${kernel}" "${tmpdir}/sysupgrade-${board}/kernel"
+
+mtime=""
+if [ -n "$SOURCE_DATE_EPOCH" ]; then
+	mtime="--mtime=@${SOURCE_DATE_EPOCH}"
+fi
+
+(cd "$tmpdir"; tar --sort=name --owner=0 --group=0 --numeric-owner -cvf sysupgrade.tar sysupgrade-${board} ${mtime})
+err="$?"
+if [ -e "$tmpdir/sysupgrade.tar" ]; then
+	cp "$tmpdir/sysupgrade.tar" "$outfile"
+else
+	err=2
+fi
+rm -rf "$tmpdir"
+
+exit $err
diff --git a/scripts/target-metadata.pl b/scripts/target-metadata.pl
new file mode 100755
index 0000000..629abc5
--- /dev/null
+++ b/scripts/target-metadata.pl
@@ -0,0 +1,464 @@
+#!/usr/bin/env perl
+use FindBin;
+use lib "$FindBin::Bin";
+use strict;
+use metadata;
+use Getopt::Long;
+
+sub target_config_features(@) {
+	my $ret;
+
+	while ($_ = shift @_) {
+		/^arm_v(\w+)$/ and $ret .= "\tselect arm_v$1\n";
+		/^audio$/ and $ret .= "\tselect AUDIO_SUPPORT\n";
+		/^boot-part$/ and $ret .= "\tselect USES_BOOT_PART\n";
+		/^broken$/ and $ret .= "\tdepends on BROKEN\n";
+		/^cpiogz$/ and $ret .= "\tselect USES_CPIOGZ\n";
+		/^display$/ and $ret .= "\tselect DISPLAY_SUPPORT\n";
+		/^dt$/ and $ret .= "\tselect USES_DEVICETREE\n";
+		/^dt-overlay$/ and $ret .= "\tselect HAS_DT_OVERLAY_SUPPORT\n";
+		/^emmc$/ and $ret .= "\tselect EMMC_SUPPORT\n";
+		/^ext4$/ and $ret .= "\tselect USES_EXT4\n";
+		/^fpu$/ and $ret .= "\tselect HAS_FPU\n";
+		/^gpio$/ and $ret .= "\tselect GPIO_SUPPORT\n";
+		/^jffs2$/ and $ret .= "\tselect USES_JFFS2\n";
+		/^jffs2_nand$/ and $ret .= "\tselect USES_JFFS2_NAND\n";
+		/^legacy-sdcard$/ and $ret .= "\tselect LEGACY_SDCARD_SUPPORT\n";
+		/^low_mem$/ and $ret .= "\tselect LOW_MEMORY_FOOTPRINT\n";
+		/^minor$/ and $ret .= "\tselect USES_MINOR\n";
+		/^mips16$/ and $ret .= "\tselect HAS_MIPS16\n";
+		/^nand$/ and $ret .= "\tselect NAND_SUPPORT\n";
+		/^nommu$/ and $ret .= "\tselect NOMMU\n";
+		/^pci$/ and $ret .= "\tselect PCI_SUPPORT\n";
+		/^pcie$/ and $ret .= "\tselect PCIE_SUPPORT\n";
+		/^pcmcia$/ and $ret .= "\tselect PCMCIA_SUPPORT\n";
+		/^powerpc64$/ and $ret .= "\tselect powerpc64\n";
+		/^pwm$/ and $ret .= "\select PWM_SUPPORT\n";
+		/^ramdisk$/ and $ret .= "\tselect USES_INITRAMFS\n";
+		/^rfkill$/ and $ret .= "\tselect RFKILL_SUPPORT\n";
+		/^rootfs-part$/ and $ret .= "\tselect USES_ROOTFS_PART\n";
+		/^rtc$/ and $ret .= "\tselect RTC_SUPPORT\n";
+		/^separate_ramdisk$/ and $ret .= "\tselect USES_INITRAMFS\n\tselect USES_SEPARATE_INITRAMFS\n";
+		/^small_flash$/ and $ret .= "\tselect SMALL_FLASH\n";
+		/^spe_fpu$/ and $ret .= "\tselect HAS_SPE_FPU\n";
+		/^squashfs$/ and $ret .= "\tselect USES_SQUASHFS\n";
+		/^targz$/ and $ret .= "\tselect USES_TARGZ\n";
+		/^testing-kernel$/ and $ret .= "\tselect HAS_TESTING_KERNEL\n";
+		/^ubifs$/ and $ret .= "\tselect USES_UBIFS\n";
+		/^usb$/ and $ret .= "\tselect USB_SUPPORT\n";
+		/^usbgadget$/ and $ret .= "\tselect USB_GADGET_SUPPORT\n";
+		/^virtio$/ and $ret .= "\tselect VIRTIO_SUPPORT\n";
+	}
+	return $ret;
+}
+
+sub target_name($) {
+	my $target = shift;
+	my $parent = $target->{parent};
+	if ($parent) {
+		return $target->{parent}->{name}." - ".$target->{name};
+	} else {
+		return $target->{name};
+	}
+}
+
+sub kver($) {
+	my $v = shift;
+	$v =~ tr/\./_/;
+	if (substr($v,0,2) eq "2_") {
+		$v =~ /(\d+_\d+_\d+)(_\d+)?/ and $v = $1;
+	} else {
+		$v =~ /(\d+_\d+)(_\d+)?/ and $v = $1;
+	}
+	return $v;
+}
+
+sub print_target($) {
+	my $target = shift;
+	my $features = target_config_features(@{$target->{features}});
+	my $help = $target->{desc};
+	my $confstr;
+
+	chomp $features;
+	$features .= "\n";
+	if ($help =~ /\w+/) {
+		$help =~ s/^\s*/\t  /mg;
+		$help = "\thelp\n$help";
+	} else {
+		undef $help;
+	}
+
+	my $v = kver($target->{version});
+	my $tv = kver($target->{testing_version});
+	$tv or $tv = $v;
+	if (@{$target->{subtargets}} == 0) {
+	$confstr = <<EOF;
+config TARGET_$target->{conf}
+	bool "$target->{name}"
+	select LINUX_$v if !TESTING_KERNEL
+	select LINUX_$tv if TESTING_KERNEL
+EOF
+	}
+	else {
+		$confstr = <<EOF;
+config TARGET_$target->{conf}
+	bool "$target->{name}"
+EOF
+	}
+	if ($target->{subtarget}) {
+		$confstr .= "\tdepends on TARGET_$target->{boardconf}\n";
+	}
+	if (@{$target->{subtargets}} > 0) {
+		$confstr .= "\tselect HAS_SUBTARGETS\n";
+		grep { /broken/ } @{$target->{features}} and $confstr .= "\tdepends on BROKEN\n";
+	} else {
+		$confstr .= $features;
+		if ($target->{arch} =~ /\w/) {
+			$confstr .= "\tselect $target->{arch}\n";
+		}
+		if ($target->{has_devices}) {
+			$confstr .= "\tselect HAS_DEVICES\n";
+		}
+	}
+
+	foreach my $dep (@{$target->{depends}}) {
+		my $mode = "depends on";
+		my $flags;
+		my $name;
+
+		$dep =~ /^([@\+\-]+)(.+)$/;
+		$flags = $1;
+		$name = $2;
+
+		next if $name =~ /:/;
+		$flags =~ /-/ and $mode = "deselect";
+		$flags =~ /\+/ and $mode = "select";
+		$flags =~ /@/ and $confstr .= "\t$mode $name\n";
+	}
+	$confstr .= "$help\n\n";
+	print $confstr;
+}
+
+sub merge_package_lists($$) {
+	my $list1 = shift;
+	my $list2 = shift;
+	my @l = ();
+	my %pkgs;
+
+	foreach my $pkg (@$list1, @$list2) {
+		$pkgs{$pkg =~ s/^~//r} = 1;
+	}
+	foreach my $pkg (keys %pkgs) {
+		push @l, $pkg unless ($pkg =~ /^-/ or $pkgs{"-$pkg"});
+	}
+	return sort(@l);
+}
+
+sub gen_target_config() {
+	my $file = shift @ARGV;
+	my @target = parse_target_metadata($file);
+	my %defaults;
+
+	my @target_sort = sort {
+		target_name($a) cmp target_name($b);
+	} @target;
+
+	foreach my $target (@target_sort) {
+		next if @{$target->{subtargets}} > 0;
+		print <<EOF;
+config DEFAULT_TARGET_$target->{conf}
+	bool
+	depends on TARGET_PER_DEVICE_ROOTFS
+	default y if TARGET_$target->{conf}
+EOF
+		foreach my $pkg (@{$target->{packages}}) {
+			print "\tselect DEFAULT_$pkg if TARGET_PER_DEVICE_ROOTFS\n";
+		}
+	}
+
+	print <<EOF;
+choice
+	prompt "Target System"
+	default TARGET_mmp
+	reset if !DEVEL
+	
+EOF
+
+	foreach my $target (@target_sort) {
+		next if $target->{subtarget};
+		print_target($target);
+	}
+
+	print <<EOF;
+endchoice
+
+choice
+	prompt "Subtarget" if HAS_SUBTARGETS
+EOF
+	foreach my $target (@target) {
+		next unless $target->{def_subtarget};
+		print <<EOF;
+	default TARGET_$target->{conf}_$target->{def_subtarget} if TARGET_$target->{conf}
+EOF
+	}
+	print <<EOF;
+
+EOF
+	foreach my $target (@target) {
+		next unless $target->{subtarget};
+		print_target($target);
+	}
+
+print <<EOF;
+endchoice
+
+choice
+	prompt "Target Profile"
+	default TARGET_MULTI_PROFILE if BUILDBOT
+
+EOF
+	foreach my $target (@target) {
+		my $profile = $target->{profiles}->[0];
+		$profile or next;
+		print <<EOF;
+	default TARGET_$target->{conf}_$profile->{id} if TARGET_$target->{conf} && !BUILDBOT
+EOF
+	}
+
+	print <<EOF;
+
+config TARGET_MULTI_PROFILE
+	bool "Multiple devices"
+	depends on HAS_DEVICES
+	help
+	Instead of only building a single image, or all images, this allows you
+	to select images to be built for multiple devices in one build.
+
+EOF
+
+	foreach my $target (@target) {
+		my $profiles = $target->{profiles};
+		foreach my $profile (@{$target->{profiles}}) {
+			print <<EOF;
+config TARGET_$target->{conf}_$profile->{id}
+	bool "$profile->{name}"
+	depends on TARGET_$target->{conf}
+EOF
+			$profile->{broken} and print "\tdepends on BROKEN\n";
+			my @pkglist = merge_package_lists($target->{packages}, $profile->{packages});
+			foreach my $pkg (@pkglist) {
+				print "\tselect DEFAULT_$pkg\n";
+				$defaults{$pkg} = 1;
+			}
+			my $help = $profile->{desc};
+			if ($help =~ /\w+/) {
+				$help =~ s/^\s*/\t  /mg;
+				$help = "\thelp\n$help";
+			} else {
+				undef $help;
+			}
+			print "$help\n";
+		}
+	}
+
+	print <<EOF;
+endchoice
+
+menu "Target Devices"
+	depends on TARGET_MULTI_PROFILE
+
+	config TARGET_ALL_PROFILES
+		bool "Enable all profiles by default"
+		default BUILDBOT
+
+	config TARGET_PER_DEVICE_ROOTFS
+		bool "Use a per-device root filesystem that adds profile packages"
+		default BUILDBOT
+		help
+		When disabled, all device packages from all selected devices
+		will be included in all images by default. (Marked as <*>) You will
+		still be able to manually deselect any/all packages.
+		When enabled, each device builds it's own image, including only the
+		profile packages for that device.  (Marked as {M}) You will be able
+		to change a package to included in all images by marking as {*}, but
+		will not be able to disable a profile package completely.
+		
+		To get the most use of this setting, you must set in a .config stub
+		before calling "make defconfig".  Selecting TARGET_MULTI_PROFILE and
+		then manually selecting (via menuconfig for instance) this option
+		will have pre-defaulted all profile packages to included, making this
+		option appear to have had no effect.
+
+EOF
+	foreach my $target (@target) {
+		my @profiles = sort {
+			my $x = $a->{name};
+			my $y = $b->{name};
+			"\L$x" cmp "\L$y";
+		} @{$target->{profiles}};
+		foreach my $profile (@profiles) {
+			next unless $profile->{id} =~ /^DEVICE_/;
+			print <<EOF;
+menuconfig TARGET_DEVICE_$target->{conf}_$profile->{id}
+	bool "$profile->{name}"
+	depends on TARGET_$target->{conf}
+	default $profile->{default}
+EOF
+			$profile->{broken} and print "\tdepends on BROKEN\n";
+			my @pkglist = merge_package_lists($target->{packages}, $profile->{packages});
+			foreach my $pkg (@pkglist) {
+				print "\tselect DEFAULT_$pkg if !TARGET_PER_DEVICE_ROOTFS\n";
+				print "\tselect MODULE_DEFAULT_$pkg if TARGET_PER_DEVICE_ROOTFS\n";
+				$defaults{$pkg} = 1;
+			}
+
+			print <<EOF;
+
+
+	config TARGET_DEVICE_PACKAGES_$target->{conf}_$profile->{id}
+		string "$profile->{name} additional packages"
+		default ""
+		depends on TARGET_PER_DEVICE_ROOTFS
+		depends on TARGET_DEVICE_$target->{conf}_$profile->{id}
+
+EOF
+		}
+	}
+
+	print <<EOF;
+
+endmenu
+
+config HAS_SUBTARGETS
+	bool
+
+config HAS_DEVICES
+	bool
+
+config TARGET_BOARD
+	string
+
+EOF
+	foreach my $target (@target) {
+		$target->{subtarget} or	print "\t\tdefault \"".$target->{board}."\" if TARGET_".$target->{conf}."\n";
+	}
+	print <<EOF;
+config TARGET_SUBTARGET
+	string
+	default "generic" if !HAS_SUBTARGETS
+
+EOF
+
+	foreach my $target (@target) {
+		foreach my $subtarget (@{$target->{subtargets}}) {
+			print "\t\tdefault \"$subtarget\" if TARGET_".$target->{conf}."_$subtarget\n";
+		}
+	}
+	print <<EOF;
+config TARGET_PROFILE
+	string
+EOF
+	foreach my $target (@target) {
+		my $profiles = $target->{profiles};
+		foreach my $profile (@$profiles) {
+			print "\tdefault \"$profile->{id}\" if TARGET_$target->{conf}_$profile->{id}\n";
+		}
+	}
+
+	print <<EOF;
+
+config TARGET_ARCH_PACKAGES
+	string
+	
+EOF
+	foreach my $target (@target) {
+		next if @{$target->{subtargets}} > 0;
+		print "\t\tdefault \"".($target->{arch_packages} || $target->{board})."\" if TARGET_".$target->{conf}."\n";
+	}
+	print <<EOF;
+
+config DEFAULT_TARGET_OPTIMIZATION
+	string
+EOF
+	foreach my $target (@target) {
+		next if @{$target->{subtargets}} > 0;
+		print "\tdefault \"".$target->{cflags}."\" if TARGET_".$target->{conf}."\n";
+	}
+	print "\tdefault \"-Os -pipe -funit-at-a-time\"\n";
+	print <<EOF;
+
+config CPU_TYPE
+	string
+EOF
+	foreach my $target (@target) {
+		next if @{$target->{subtargets}} > 0;
+		print "\tdefault \"".$target->{cputype}."\" if TARGET_".$target->{conf}."\n";
+	}
+	print "\tdefault \"\"\n";
+
+	my %kver;
+	foreach my $target (@target) {
+		foreach my $tv ($target->{version}, $target->{testing_version}) {
+			next unless $tv;
+			my $v = kver($tv);
+			next if $kver{$v};
+			$kver{$v} = 1;
+			print <<EOF;
+
+config LINUX_$v
+	bool
+
+EOF
+		}
+	}
+	foreach my $def (sort keys %defaults) {
+		print <<EOF;
+	config DEFAULT_$def
+		bool
+
+	config MODULE_DEFAULT_$def
+		tristate
+		depends on TARGET_PER_DEVICE_ROOTFS
+		depends on m
+		default m if DEFAULT_$def
+		select PACKAGE_$def
+
+EOF
+	}
+}
+
+sub gen_profile_mk() {
+	my $file = shift @ARGV;
+	my $target = shift @ARGV;
+	my @targets = parse_target_metadata($file);
+	foreach my $cur (@targets) {
+		next unless $cur->{id} eq $target;
+		my @profile_ids_unique =  do { my %seen; grep { !$seen{$_}++} map { $_->{id} } @{$cur->{profiles}}};
+		print "PROFILE_NAMES = ".join(" ", @profile_ids_unique)."\n";
+		foreach my $profile (@{$cur->{profiles}}) {
+			print $profile->{id}.'_NAME:='.$profile->{name}."\n";
+			print $profile->{id}.'_HAS_IMAGE_METADATA:='.$profile->{has_image_metadata}."\n";
+			if (defined($profile->{supported_devices}) and @{$profile->{supported_devices}} > 0) {
+				print $profile->{id}.'_SUPPORTED_DEVICES:='.join(' ', @{$profile->{supported_devices}})."\n";
+			}
+			print $profile->{id}.'_PACKAGES:='.join(' ', @{$profile->{packages}})."\n";
+		}
+	}
+}
+
+sub parse_command() {
+	GetOptions("ignore=s", \@ignore);
+	my $cmd = shift @ARGV;
+	for ($cmd) {
+		/^config$/ and return gen_target_config();
+		/^profile_mk$/ and return gen_profile_mk();
+	}
+	die <<EOF
+Available Commands:
+	$0 config [file] 			Target metadata in Kconfig format
+	$0 profile_mk [file] [target]		Profile metadata in makefile format
+
+EOF
+}
+
+parse_command();
diff --git a/scripts/time.pl b/scripts/time.pl
new file mode 100755
index 0000000..6f10170
--- /dev/null
+++ b/scripts/time.pl
@@ -0,0 +1,65 @@
+#!/usr/bin/env perl
+
+use strict;
+use warnings;
+use Config;
+
+if (@ARGV < 2) {
+	die "Usage: $0 <prefix> <command...>\n";
+}
+
+sub gettime {
+	my ($sec, $usec);
+
+	eval {
+		require Time::HiRes;
+		($sec, $usec) = Time::HiRes::gettimeofday();
+	};
+
+	unless (defined($sec) && defined($usec)) {
+		my $tv_t = ($Config{'longsize'} == 8) ? 'qq' : 'll';
+		my $tv = pack $tv_t, 0, 0;
+
+		eval {
+			require 'syscall.ph';
+			syscall(SYS_gettimeofday(), $tv, 0);
+		};
+
+		($sec, $usec) = unpack $tv_t, $tv;
+	}
+
+	return ($sec, $usec);
+}
+
+my ($prefix, @cmd) = @ARGV;
+my ($sec, $usec) = gettime();
+my $pid = fork();
+
+if (!defined($pid)) {
+	die "$0: Failure to fork(): $!\n";
+}
+elsif ($pid == 0) {
+	exec(@cmd);
+	die "$0: Failure to exec(): $!\n";
+}
+else {
+	$SIG{'INT'} = 'IGNORE';
+	$SIG{'QUIT'} = 'IGNORE';
+
+	if (waitpid($pid, 0) == -1) {
+		die "$0: Failure to waitpid(): $!\n";
+	}
+
+	my $exitcode = $? >> 8;
+	my ($sec2, $usec2) = gettime();
+	my (undef, undef, $cuser, $csystem) = times();
+
+	printf STDOUT "%s#%.2f#%.2f#%.2f\n",
+		$prefix, $cuser, $csystem,
+		($sec2 - $sec) + ($usec2 - $usec) / 1000000;
+
+	$SIG{'INT'} = 'DEFAULT';
+	$SIG{'QUIT'} = 'DEFAULT';
+
+	exit $exitcode;
+}
diff --git a/scripts/timestamp.pl b/scripts/timestamp.pl
new file mode 100755
index 0000000..e24d814
--- /dev/null
+++ b/scripts/timestamp.pl
@@ -0,0 +1,69 @@
+#!/usr/bin/env perl
+# 
+# Copyright (C) 2006 OpenWrt.org
+#
+# This is free software, licensed under the GNU General Public License v2.
+# See /LICENSE for more information.
+#
+
+use strict;
+
+sub get_ts($$) {
+	my $path = shift;
+	my $options = shift;
+	my $ts = 0;
+	my $fn = "";
+	$path .= "/" if( -d $path);
+	open FIND, "find $path -type f -and -not -path \\*/.svn\\* -and -not -path \\*CVS\\* $options 2>/dev/null |";
+	while (<FIND>) {
+		chomp;
+		my $file = $_;
+		next if -l $file;
+		my $mt = (stat $file)[9];
+		if ($mt > $ts) {
+			$ts = $mt;
+			$fn = $file;
+		}
+	}
+	close FIND;
+	return ($ts, $fn);
+}
+
+(@ARGV > 0) or push @ARGV, ".";
+my $ts = 0;
+my $n = ".";
+my %options;
+while (@ARGV > 0) {
+	my $path = shift @ARGV;
+	if ($path =~ /^-x/) {
+		my $str = shift @ARGV;
+		$options{"findopts"} .= " -and -not -path '".$str."'"
+	} elsif ($path =~ /^-f/) {
+		$options{"findopts"} .= " -follow";
+	} elsif ($path =~ /^-n/) {
+		my $arg = $ARGV[0];
+		$options{$path} = $arg;
+	} elsif ($path =~ /^-/) {
+		$options{$path} = 1;
+	} else {
+		my ($tmp, $fname) = get_ts($path, $options{"findopts"});
+		if ($tmp > $ts) {
+			if ($options{'-F'}) {
+				$n = $fname;
+			} else {
+				$n = $path;
+			}
+			$ts = $tmp;
+		}
+	}
+}
+
+if ($options{"-n"}) {
+	exit ($n eq $options{"-n"} ? 0 : 1);
+} elsif ($options{"-p"}) {
+	print "$n\n";
+} elsif ($options{"-t"}) {
+	print "$ts\n";
+} else {
+	print "$n\t$ts\n";
+}
diff --git a/scripts/ubinize-image.sh b/scripts/ubinize-image.sh
new file mode 100755
index 0000000..d8b8cd3
--- /dev/null
+++ b/scripts/ubinize-image.sh
@@ -0,0 +1,170 @@
+#!/bin/sh
+
+. $TOPDIR/scripts/functions.sh
+
+part=""
+ubootenv=""
+ubinize_param=""
+kernel=""
+rootfs=""
+outfile=""
+err=""
+ubinize_seq=""
+
+ubivol() {
+	local volid="$1"
+	local name="$2"
+	local image="$3"
+	local autoresize="$4"
+	local size="$5"
+	local voltype="${6:-dynamic}"
+	echo "[$name]"
+	echo "mode=ubi"
+	echo "vol_id=$volid"
+	echo "vol_type=$voltype"
+	echo "vol_name=$name"
+	if [ "$image" ]; then
+		echo "image=$image"
+		[ -n "$size" ] && echo "vol_size=${size}"
+	else
+		echo "vol_size=1MiB"
+	fi
+	if [ "$autoresize" ]; then
+		echo "vol_flags=autoresize"
+	fi
+}
+
+ubilayout() {
+	local vol_id=0
+	local rootsize
+	local autoresize
+	local rootfs_type
+	local voltype
+
+	rootfs_type="$( get_fs_type "$2" )"
+	if [ "$1" = "ubootenv" ]; then
+		ubivol $vol_id ubootenv
+		vol_id=$(( vol_id + 1 ))
+		ubivol $vol_id ubootenv2
+		vol_id=$(( vol_id + 1 ))
+	fi
+	for part in $parts; do
+		name="${part%%=*}"
+		prev="$part"
+		part="${part#*=}"
+		voltype=dynamic
+		[ "$prev" = "$part" ] && part=
+
+		image="${part%%=*}"
+		if [ "${image#:}" != "$image" ]; then
+			voltype=static
+			image="${image#:}"
+		fi
+		prev="$part"
+		part="${part#*=}"
+		[ "$prev" = "$part" ] && part=
+
+		size="$part"
+		if [ -z "$size" ]; then
+			size="$( round_up "$( stat -c%s "$image" )" 1024 )"
+		else
+			size="${size}MiB"
+		fi
+
+		ubivol $vol_id "$name" "$image" "" "${size}" "$voltype"
+		vol_id=$(( vol_id + 1 ))
+	done
+	if [ "$3" ]; then
+		ubivol $vol_id kernel "$3"
+		vol_id=$(( vol_id + 1 ))
+	fi
+
+	if [ "$2" ]; then
+		case "$rootfs_type" in
+		"ubifs")
+			autoresize=1
+			;;
+		"squashfs")
+			# squashfs uses 1k block size, ensure we do not
+			# violate that
+			rootsize="$( round_up "$( stat -c%s "$2" )" 1024 )"
+			;;
+		esac
+		ubivol $vol_id rootfs "$2" "$autoresize" "$rootsize"
+
+		vol_id=$(( vol_id + 1 ))
+		[ "$rootfs_type" = "ubifs" ] || ubivol $vol_id rootfs_data "" 1
+	fi
+}
+
+set_ubinize_seq() {
+	if [ -n "$SOURCE_DATE_EPOCH" ] ; then
+		ubinize_seq="-Q $SOURCE_DATE_EPOCH"
+	fi
+}
+
+while [ "$1" ]; do
+	case "$1" in
+	"--uboot-env")
+		ubootenv="ubootenv"
+		shift
+		continue
+		;;
+	"--kernel")
+		kernel="$2"
+		shift
+		shift
+		continue
+		;;
+	"--rootfs")
+		rootfs="$2"
+		shift
+		shift
+		continue
+		;;
+	"--part")
+		parts="$parts $2"
+		shift
+		shift
+		continue
+		;;
+	"-"*)
+		ubinize_param="$*"
+		break
+		;;
+	*)
+		if [ ! "$outfile" ]; then
+			outfile=$1
+			shift
+			continue
+		fi
+		;;
+	esac
+done
+
+if [ ! -r "$rootfs" ] && [ ! -r "$kernel" ] && [ ! "$parts" ] && [ ! "$outfile" ]; then
+	echo "syntax: $0 [--uboot-env] [--part <name>=<file>] [--kernel kernelimage] [--rootfs rootfsimage] out [ubinize opts]"
+	exit 1
+fi
+
+ubinize="$( command -v ubinize )"
+if [ ! -x "$ubinize" ]; then
+	echo "ubinize tool not found or not usable"
+	exit 1
+fi
+
+ubinizecfg="$( mktemp 2> /dev/null )"
+if [ -z "$ubinizecfg" ]; then
+	# try OSX signature
+	ubinizecfg="$( mktemp -t 'ubitmp' )"
+fi
+ubilayout "$ubootenv" "$rootfs" "$kernel" > "$ubinizecfg"
+
+set_ubinize_seq
+cat "$ubinizecfg"
+ubinize $ubinize_seq -o "$outfile" $ubinize_param "$ubinizecfg"
+err="$?"
+[ ! -e "$outfile" ] && err=2
+rm "$ubinizecfg"
+
+exit $err
diff --git a/scripts/xxdi.pl b/scripts/xxdi.pl
new file mode 100755
index 0000000..f7bb3c2
--- /dev/null
+++ b/scripts/xxdi.pl
@@ -0,0 +1,66 @@
+#!/usr/bin/env perl
+#
+# xxdi.pl - perl implementation of 'xxd -i' mode
+#
+# Copyright 2013 Greg Kroah-Hartman <gregkh@linuxfoundation.org>
+# Copyright 2013 Linux Foundation
+#
+# Released under the GPLv2.
+#
+# Implements the "basic" functionality of 'xxd -i' in perl to keep build
+# systems from having to build/install/rely on vim-core, which not all
+# distros want to do.  But everyone has perl, so use it instead.
+#
+
+use strict;
+use warnings;
+
+my $indata;
+my $var_name = "stdin";
+my $full_output = (@ARGV > 0 && $ARGV[0] eq '-i') ? shift @ARGV : undef;
+
+{
+	local $/;
+	my $fh;
+
+	if (@ARGV) {
+		$var_name = $ARGV[0];
+		open($fh, '<:raw', $var_name) || die("xxdi.pl: Unable to open $var_name: $!\n");
+	} elsif (! -t STDIN) {
+		$fh = \*STDIN;
+		undef $full_output;
+	} else {
+		die "usage: xxdi.pl [-i] [infile]\n";
+	}
+
+	$indata = readline $fh;
+
+	close $fh;
+}
+
+my $len_data = length($indata);
+my $num_digits_per_line = 12;
+my $outdata = "";
+
+# Use the variable name of the file we read from, converting '/' and '.
+# to '_', or, if this is stdin, just use "stdin" as the name.
+$var_name =~ s/\//_/g;
+$var_name =~ s/\./_/g;
+$var_name = "__$var_name" if $var_name =~ /^\d/;
+
+$outdata = "unsigned char $var_name\[] = { " if $full_output;
+
+for (my $key= 0; $key < $len_data; $key++) {
+	if ($key % $num_digits_per_line == 0) {
+		$outdata = substr($outdata, 0, -1)."\n  ";
+	}
+	$outdata .= sprintf("0x%.2x, ", ord(substr($indata, $key, 1)));
+}
+
+$outdata = substr($outdata, 0, -2);
+$outdata .= "\n";
+
+$outdata .= "};\nunsigned int $var_name\_len = $len_data;\n" if $full_output;
+
+binmode STDOUT;
+print $outdata;