Initial commit
Some checks failed
Build Kernel / Build all affected Kernels (push) Has been cancelled
Build all core packages / Build all core packages for selected target (push) Has been cancelled
Build and Push prebuilt tools container / Build and Push all prebuilt containers (push) Has been cancelled
Build Toolchains / Build Toolchains for each target (push) Has been cancelled
Build host tools / Build host tools for linux and macos based systems (push) Has been cancelled
Coverity scan build / Coverity x86/64 build (push) Has been cancelled
Some checks failed
Build Kernel / Build all affected Kernels (push) Has been cancelled
Build all core packages / Build all core packages for selected target (push) Has been cancelled
Build and Push prebuilt tools container / Build and Push all prebuilt containers (push) Has been cancelled
Build Toolchains / Build Toolchains for each target (push) Has been cancelled
Build host tools / Build host tools for linux and macos based systems (push) Has been cancelled
Coverity scan build / Coverity x86/64 build (push) Has been cancelled
This commit is contained in:
162
scripts/brcmImage.pl
Executable file
162
scripts/brcmImage.pl
Executable file
@@ -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);
|
||||
|
||||
212
scripts/bundle-libraries.sh
Executable file
212
scripts/bundle-libraries.sh
Executable file
@@ -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
|
||||
86
scripts/cameo-imghdr.py
Executable file
86
scripts/cameo-imghdr.py
Executable file
@@ -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)
|
||||
117
scripts/cameo-tag.py
Executable file
117
scripts/cameo-tag.py
Executable file
@@ -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))
|
||||
72
scripts/cfe-bin-header.py
Executable file
72
scripts/cfe-bin-header.py
Executable file
@@ -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()
|
||||
139
scripts/cfe-partition-tag.py
Executable file
139
scripts/cfe-partition-tag.py
Executable file
@@ -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()
|
||||
149
scripts/cfe-wfi-tag.py
Executable file
149
scripts/cfe-wfi-tag.py
Executable file
@@ -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()
|
||||
14
scripts/check-toolchain-clean.sh
Executable file
14
scripts/check-toolchain-clean.sh
Executable file
@@ -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
|
||||
6872
scripts/checkpatch.pl
Executable file
6872
scripts/checkpatch.pl
Executable file
File diff suppressed because it is too large
Load Diff
25
scripts/clean-package.sh
Executable file
25
scripts/clean-package.sh
Executable file
@@ -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
|
||||
177
scripts/cleanfile
Executable file
177
scripts/cleanfile
Executable file
@@ -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);
|
||||
}
|
||||
259
scripts/cleanpatch
Executable file
259
scripts/cleanpatch
Executable file
@@ -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);
|
||||
}
|
||||
78
scripts/combined-ext-image.sh
Executable file
78
scripts/combined-ext-image.sh
Executable file
@@ -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}"
|
||||
34
scripts/combined-image.sh
Executable file
34
scripts/combined-image.sh
Executable file
@@ -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"
|
||||
11
scripts/command_all.sh
Executable file
11
scripts/command_all.sh
Executable file
@@ -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
|
||||
1702
scripts/config.guess
vendored
Executable file
1702
scripts/config.guess
vendored
Executable file
File diff suppressed because it is too large
Load Diff
666
scripts/config.rpath
Executable file
666
scripts/config.rpath
Executable file
@@ -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
|
||||
1864
scripts/config.sub
vendored
Executable file
1864
scripts/config.sub
vendored
Executable file
File diff suppressed because it is too large
Load Diff
14
scripts/config/.gitignore
vendored
Normal file
14
scripts/config/.gitignore
vendored
Normal file
@@ -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
|
||||
279
scripts/config/Kbuild.include
Normal file
279
scripts/config/Kbuild.include
Normal file
@@ -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
|
||||
125
scripts/config/Makefile
Normal file
125
scripts/config/Makefile
Normal file
@@ -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)))
|
||||
27
scripts/config/README
Normal file
27
scripts/config/README
Normal file
@@ -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
|
||||
909
scripts/config/conf.c
Normal file
909
scripts/config/conf.c
Normal file
@@ -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;
|
||||
}
|
||||
1288
scripts/config/confdata.c
Normal file
1288
scripts/config/confdata.c
Normal file
File diff suppressed because it is too large
Load Diff
1303
scripts/config/expr.c
Normal file
1303
scripts/config/expr.c
Normal file
File diff suppressed because it is too large
Load Diff
326
scripts/config/expr.h
Normal file
326
scripts/config/expr.h
Normal file
@@ -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 */
|
||||
328
scripts/config/images.c
Normal file
328
scripts/config/images.c
Normal file
@@ -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",
|
||||
" ",
|
||||
" ",
|
||||
" ",
|
||||
" ",
|
||||
" ",
|
||||
" ",
|
||||
" ",
|
||||
" ",
|
||||
" ",
|
||||
" ",
|
||||
" ",
|
||||
" "};
|
||||
33
scripts/config/images.h
Normal file
33
scripts/config/images.h
Normal file
@@ -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 */
|
||||
9
scripts/config/internal.h
Normal file
9
scripts/config/internal.h
Normal file
@@ -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 */
|
||||
522
scripts/config/lexer.l
Normal file
522
scripts/config/lexer.l
Normal file
@@ -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>";
|
||||
}
|
||||
4203
scripts/config/lexer.lex.c
Normal file
4203
scripts/config/lexer.lex.c
Normal file
File diff suppressed because it is too large
Load Diff
132
scripts/config/list.h
Normal file
132
scripts/config/list.h
Normal file
@@ -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
|
||||
153
scripts/config/lkc.h
Normal file
153
scripts/config/lkc.h
Normal file
@@ -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 */
|
||||
53
scripts/config/lkc_proto.h
Normal file
53
scripts/config/lkc_proto.h
Normal file
@@ -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);
|
||||
319
scripts/config/lxdialog/checklist.c
Normal file
319
scripts/config/lxdialog/checklist.c
Normal file
@@ -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 */
|
||||
}
|
||||
207
scripts/config/lxdialog/dialog.h
Normal file
207
scripts/config/lxdialog/dialog.h
Normal file
@@ -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);
|
||||
289
scripts/config/lxdialog/inputbox.c
Normal file
289
scripts/config/lxdialog/inputbox.c
Normal file
@@ -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 */
|
||||
}
|
||||
416
scripts/config/lxdialog/menubox.c
Normal file
416
scripts/config/lxdialog/menubox.c
Normal file
@@ -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 */
|
||||
}
|
||||
358
scripts/config/lxdialog/textbox.c
Normal file
358
scripts/config/lxdialog/textbox.c
Normal file
@@ -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;
|
||||
}
|
||||
700
scripts/config/lxdialog/util.c
Normal file
700
scripts/config/lxdialog/util.c
Normal file
@@ -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);
|
||||
}
|
||||
101
scripts/config/lxdialog/yesno.c
Normal file
101
scripts/config/lxdialog/yesno.c
Normal file
@@ -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 */
|
||||
}
|
||||
55
scripts/config/mconf-cfg.sh
Executable file
55
scripts/config/mconf-cfg.sh
Executable file
@@ -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
|
||||
1060
scripts/config/mconf.c
Normal file
1060
scripts/config/mconf.c
Normal file
File diff suppressed because it is too large
Load Diff
867
scripts/config/menu.c
Normal file
867
scripts/config/menu.c
Normal file
@@ -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 = ¤t_entry->list;
|
||||
current_menu = current_entry;
|
||||
return current_menu;
|
||||
}
|
||||
|
||||
void menu_end_menu(void)
|
||||
{
|
||||
last_entry_ptr = ¤t_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 = ¤t_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);
|
||||
}
|
||||
53
scripts/config/nconf-cfg.sh
Executable file
53
scripts/config/nconf-cfg.sh
Executable file
@@ -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
|
||||
1659
scripts/config/nconf.c
Normal file
1659
scripts/config/nconf.c
Normal file
File diff suppressed because it is too large
Load Diff
641
scripts/config/nconf.gui.c
Normal file
641
scripts/config/nconf.gui.c
Normal file
@@ -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;
|
||||
}
|
||||
88
scripts/config/nconf.h
Normal file
88
scripts/config/nconf.h
Normal file
@@ -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);
|
||||
2176
scripts/config/parser.tab.c
Normal file
2176
scripts/config/parser.tab.c
Normal file
File diff suppressed because it is too large
Load Diff
135
scripts/config/parser.tab.h
Normal file
135
scripts/config/parser.tab.h
Normal file
@@ -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 */
|
||||
718
scripts/config/parser.y
Normal file
718
scripts/config/parser.y
Normal file
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
578
scripts/config/preprocess.c
Normal file
578
scripts/config/preprocess.c
Normal file
@@ -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);
|
||||
}
|
||||
40
scripts/config/qconf-cfg.sh
Executable file
40
scripts/config/qconf-cfg.sh
Executable file
@@ -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
|
||||
1928
scripts/config/qconf.cc
Normal file
1928
scripts/config/qconf.cc
Normal file
File diff suppressed because it is too large
Load Diff
275
scripts/config/qconf.h
Normal file
275
scripts/config/qconf.h
Normal file
@@ -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;
|
||||
};
|
||||
1253
scripts/config/symbol.c
Normal file
1253
scripts/config/symbol.c
Normal file
File diff suppressed because it is too large
Load Diff
129
scripts/config/util.c
Normal file
129
scripts/config/util.c
Normal file
@@ -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);
|
||||
}
|
||||
68
scripts/const_structs.checkpatch
Normal file
68
scripts/const_structs.checkpatch
Normal file
@@ -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
|
||||
211
scripts/deptest.sh
Executable file
211
scripts/deptest.sh
Executable file
@@ -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
|
||||
17
scripts/diffconfig.sh
Executable file
17
scripts/diffconfig.sh
Executable file
@@ -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
|
||||
332
scripts/dl_cleanup.py
Executable file
332
scripts/dl_cleanup.py
Executable file
@@ -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))
|
||||
434
scripts/dl_github_archive.py
Executable file
434
scripts/dl_github_archive.py
Executable file
@@ -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()
|
||||
329
scripts/download.pl
Executable file
329
scripts/download.pl
Executable file
@@ -0,0 +1,329 @@
|
||||
#!/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 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") {
|
||||
system("mkdir", "-p", "$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";
|
||||
system("mv", "$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;
|
||||
189
scripts/dump-target-info.pl
Executable file
189
scripts/dump-target-info.pl
Executable file
@@ -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";
|
||||
}
|
||||
226
scripts/env
Executable file
226
scripts/env
Executable file
@@ -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
|
||||
646
scripts/ext-toolchain.sh
Executable file
646
scripts/ext-toolchain.sh
Executable file
@@ -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
|
||||
114
scripts/ext-tools.sh
Executable file
114
scripts/ext-tools.sh
Executable file
@@ -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
|
||||
936
scripts/feeds
Executable file
936
scripts/feeds
Executable file
@@ -0,0 +1,936 @@
|
||||
#!/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 '%s' '%s' && cd '%s' && git checkout -b '%s' '%s' && cd -",
|
||||
'update' => "git pull --ff-only",
|
||||
'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-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_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 $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'};
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
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 $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) != 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('ahif', \%opts);
|
||||
%argv_feeds = map { $_ => 1 } @ARGV;
|
||||
|
||||
if ($opts{h}) {
|
||||
usage();
|
||||
return 0;
|
||||
}
|
||||
|
||||
-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}) == 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.
|
||||
-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();
|
||||
135
scripts/fixup-makefile.pl
Executable file
135
scripts/fixup-makefile.pl
Executable file
@@ -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";
|
||||
342
scripts/flashing/adam2flash-502T.pl
Executable file
342
scripts/flashing/adam2flash-502T.pl
Executable file
@@ -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();
|
||||
209
scripts/flashing/adam2flash-fritzbox.pl
Executable file
209
scripts/flashing/adam2flash-fritzbox.pl
Executable file
@@ -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();
|
||||
174
scripts/flashing/adam2flash.pl
Executable file
174
scripts/flashing/adam2flash.pl
Executable file
@@ -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();
|
||||
170
scripts/flashing/adsl2mue_flash.pl
Executable file
170
scripts/flashing/adsl2mue_flash.pl
Executable file
@@ -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();
|
||||
44
scripts/flashing/eva_ramboot.py
Executable file
44
scripts/flashing/eva_ramboot.py
Executable file
@@ -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()
|
||||
66
scripts/flashing/flash.sh
Executable file
66
scripts/flashing/flash.sh
Executable file
@@ -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
|
||||
283
scripts/flashing/jungo-image.py
Executable file
283
scripts/flashing/jungo-image.py
Executable file
@@ -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()
|
||||
|
||||
48
scripts/functions.sh
Normal file
48
scripts/functions.sh
Normal file
@@ -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))"
|
||||
}
|
||||
33
scripts/gen-dependencies.sh
Executable file
33
scripts/gen-dependencies.sh
Executable file
@@ -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
|
||||
60
scripts/gen_image_generic.sh
Executable file
60
scripts/gen_image_generic.sh
Executable file
@@ -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"
|
||||
35
scripts/get_source_date_epoch.sh
Executable file
35
scripts/get_source_date_epoch.sh
Executable file
@@ -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"
|
||||
59
scripts/getver.sh
Executable file
59
scripts/getver.sh
Executable file
@@ -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"
|
||||
200
scripts/ipkg-build
Executable file
200
scripts/ipkg-build
Executable file
@@ -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"
|
||||
31
scripts/ipkg-make-index.sh
Executable file
31
scripts/ipkg-make-index.sh
Executable file
@@ -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
|
||||
20
scripts/ipkg-remove
Executable file
20
scripts/ipkg-remove
Executable file
@@ -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
|
||||
84
scripts/json_add_image_info.py
Executable file
84
scripts/json_add_image_info.py
Executable file
@@ -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=(",", ":")))
|
||||
70
scripts/json_overview_image_info.py
Executable file
70
scripts/json_overview_image_info.py
Executable file
@@ -0,0 +1,70 @@
|
||||
#!/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"] = run(
|
||||
[
|
||||
"make",
|
||||
"--no-print-directory",
|
||||
"-C",
|
||||
"target/linux/",
|
||||
"val.DEFAULT_PACKAGES",
|
||||
"val.ARCH_PACKAGES",
|
||||
"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_path.write_text(json.dumps(output, sort_keys=True, separators=(",", ":")))
|
||||
else:
|
||||
print("JSON info file script could not find any JSON files for target")
|
||||
189
scripts/kconfig.pl
Executable file
189
scripts/kconfig.pl
Executable file
@@ -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]);
|
||||
263
scripts/kernel_bump.sh
Executable file
263
scripts/kernel_bump.sh
Executable file
@@ -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
|
||||
21
scripts/make-ipkg-dir.sh
Executable file
21
scripts/make-ipkg-dir.sh
Executable file
@@ -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"
|
||||
2
scripts/md5sum
Executable file
2
scripts/md5sum
Executable file
@@ -0,0 +1,2 @@
|
||||
#!/bin/sh
|
||||
cat "$@" | md5
|
||||
360
scripts/metadata.pm
Normal file
360
scripts/metadata.pm
Normal file
@@ -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;
|
||||
854
scripts/mkhash.c
Normal file
854
scripts/mkhash.c
Normal file
@@ -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;
|
||||
}
|
||||
59
scripts/mkits-qsdk-ipq-image.sh
Executable file
59
scripts/mkits-qsdk-ipq-image.sh
Executable file
@@ -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}
|
||||
40
scripts/mkits-zyxel-fit-filogic.sh
Executable file
40
scripts/mkits-zyxel-fit-filogic.sh
Executable file
@@ -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}
|
||||
38
scripts/mkits-zyxel-fit.sh
Executable file
38
scripts/mkits-zyxel-fit.sh
Executable file
@@ -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}
|
||||
240
scripts/mkits.sh
Executable file
240
scripts/mkits.sh
Executable file
@@ -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}"
|
||||
109
scripts/moxa-encode-fw.py
Executable file
109
scripts/moxa-encode-fw.py
Executable file
@@ -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()
|
||||
79
scripts/netgear-encrypted-factory.py
Executable file
79
scripts/netgear-encrypted-factory.py
Executable file
@@ -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()
|
||||
2
scripts/noop.sh
Executable file
2
scripts/noop.sh
Executable file
@@ -0,0 +1,2 @@
|
||||
#!/bin/sh
|
||||
# This script does nothing, intentionally.
|
||||
93
scripts/om-fwupgradecfg-gen.sh
Executable file
93
scripts/om-fwupgradecfg-gen.sh
Executable file
@@ -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
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user