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
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
This commit is contained in:
42
scripts/arm-magic.sh
Executable file
42
scripts/arm-magic.sh
Executable file
@@ -0,0 +1,42 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# Empty/wrong machtype-workaround generator
|
||||
#
|
||||
# Copyright (C) 2006-2012 Imre Kaloz <kaloz@openwrt.org>
|
||||
# based on linux/arch/arm/boot/compressed/head-xscale.S
|
||||
#
|
||||
# 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
|
||||
|
||||
# NOTE: for now it's for only IXP4xx in big endian mode
|
||||
|
||||
# list of supported boards, in "boardname machtypeid" format
|
||||
for board in "avila 526" "gateway7001 731" "nslu2 597" "nas100d 865" "wg302v1 889" "wg302v2 890" "pronghorn 928" "pronghornmetro 1040" "compex 1273" "wrt300nv2 1077" "loft 849" "dsmg600 964" "fsg3 1091" "ap1000 1543" "tw2662 1658" "tw5334 1664" "ixdpg425 604" "cambria 1468" "sidewinder 1041" "ap42x 4418"
|
||||
do
|
||||
set -- $board
|
||||
hexid=$(printf %x\\n $2)
|
||||
if [ "$2" -lt "256" ]; then
|
||||
# we have a low machtypeid, we just need a "mov" (e3a)
|
||||
printf "\xe3\xa0\x10\x$hexid" > $BIN_DIR/$IMG_PREFIX-$1-zImage
|
||||
else
|
||||
# we have a high machtypeid, we need a "mov" (e3a) and an "orr" (e38)
|
||||
if [ "$2" -lt "4096" ]; then
|
||||
printf "\xe3\xa0\x10\x$(echo $hexid|cut -b "2 3")\xe3\x81\x1c\x$(echo $hexid|cut -b 1)" > $BIN_DIR/$IMG_PREFIX-$1-zImage
|
||||
else
|
||||
printf "\xe3\xa0\x10\x$(echo $hexid|cut -b "3 4")\xe3\x81\x1c\x$(echo $hexid|cut -b "1 2")" > $BIN_DIR/$IMG_PREFIX-$1-zImage
|
||||
fi
|
||||
fi
|
||||
# generate the image
|
||||
cat $BIN_DIR/$IMG_PREFIX-zImage >> $BIN_DIR/$IMG_PREFIX-$1-zImage
|
||||
done
|
||||
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
154
scripts/config/Makefile
Normal file
154
scripts/config/Makefile
Normal file
@@ -0,0 +1,154 @@
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
# ===========================================================================
|
||||
# OpenWrt configuration targets
|
||||
|
||||
.PHONY: clean all
|
||||
all: conf mconf
|
||||
clean:
|
||||
rm -f *.o lxdialog/*.o *.moc .*.cmd $(clean-files)
|
||||
|
||||
# This clean-files definition is here to ensure that temporary files from the
|
||||
# previous version are removed by make config-clean.
|
||||
# It should be emptied after the end of support for OpenWrt 19.07.
|
||||
clean-files := zconf.tab.c zconf.lex.c zconf.hash.c .tmp_qtcheck
|
||||
|
||||
# ===========================================================================
|
||||
# Variables needed by the upstream Makefile
|
||||
|
||||
# Avoids displaying 'UPD mconf-cfg' in an otherwise quiet make menuconfig
|
||||
kecho:=true
|
||||
|
||||
CONFIG_SHELL:=$(SHELL)
|
||||
srctree:=.
|
||||
src:=.
|
||||
obj:=.
|
||||
Q:=$(if $V,,@)
|
||||
cmd = $(cmd_$(1))
|
||||
|
||||
# some definitions taken from ../Kbuild.include
|
||||
dot-target = $(dir $@).$(notdir $@)
|
||||
squote := '
|
||||
escsq = $(subst $(squote),'\$(squote)',$1)
|
||||
define filechk
|
||||
$(Q)set -e; \
|
||||
mkdir -p $(dir $@); \
|
||||
trap "rm -f $(dot-target).tmp" EXIT; \
|
||||
{ $(filechk_$(1)); } > $(dot-target).tmp; \
|
||||
if [ ! -r $@ ] || ! cmp -s $@ $(dot-target).tmp; then \
|
||||
$(kecho) ' UPD $@'; \
|
||||
mv -f $(dot-target).tmp $@; \
|
||||
fi
|
||||
endef
|
||||
cmd-check = $(if $(strip $(cmd_$@)),,1)
|
||||
make-cmd = $(call escsq,$(subst $(pound),$$(pound),$(subst $$,$$$$,$(cmd_$(1)))))
|
||||
newer-prereqs = $(filter-out $(PHONY),$?)
|
||||
if_changed = $(if $(newer-prereqs)$(cmd-check), \
|
||||
$(cmd); \
|
||||
printf '%s\n' 'cmd_$@ := $(make-cmd)' > $(dot-target).cmd, @:)
|
||||
|
||||
### 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 = $(shell . $(obj)/nconf-cfg && echo $$libs)
|
||||
HOSTCFLAGS_nconf.o = $(shell . $(obj)/nconf-cfg && echo $$cflags)
|
||||
HOSTCFLAGS_nconf.gui.o = $(shell . $(obj)/nconf-cfg && echo $$cflags)
|
||||
|
||||
$(obj)/nconf.o $(obj)/nconf.gui.o: $(obj)/nconf-cfg
|
||||
|
||||
# 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 = $(shell . $(obj)/mconf-cfg && echo $$libs)
|
||||
$(foreach f, mconf.o $(lxdialog), \
|
||||
$(eval HOSTCFLAGS_$f = $$(shell . $(obj)/mconf-cfg && echo $$$$cflags)))
|
||||
|
||||
$(addprefix $(obj)/, mconf.o $(lxdialog)): $(obj)/mconf-cfg
|
||||
|
||||
# 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 = $(shell . $(obj)/qconf-cfg && echo $$libs)
|
||||
HOSTCXXFLAGS_qconf.o = $(shell . $(obj)/qconf-cfg && echo $$cflags)
|
||||
HOSTCXXFLAGS_qconf-moc.o = $(shell . $(obj)/qconf-cfg && echo $$cflags)
|
||||
|
||||
$(obj)/qconf.o: $(obj)/qconf-cfg
|
||||
|
||||
quiet_cmd_moc = MOC $@
|
||||
cmd_moc = $(shell . $(obj)/qconf-cfg && echo $$moc) $< -o $@
|
||||
|
||||
$(obj)/qconf-moc.cc: $(src)/qconf.h $(obj)/qconf-cfg FORCE
|
||||
$(call if_changed,moc)
|
||||
|
||||
targets += qconf-moc.cc
|
||||
|
||||
# check if necessary packages are available, and configure build flags
|
||||
filechk_conf_cfg = $(CONFIG_SHELL) $<
|
||||
|
||||
$(obj)/%conf-cfg: $(src)/%conf-cfg.sh FORCE
|
||||
$(call filechk,conf_cfg)
|
||||
|
||||
clean-files += *conf-cfg
|
||||
|
||||
# ===========================================================================
|
||||
# OpenWrt rules and final adjustments that need to be made after reading the
|
||||
# full upstream Makefile
|
||||
|
||||
clean-files += $(targets) $(hostprogs)
|
||||
|
||||
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
|
||||
|
||||
$(foreach f,$(conf-objs) $(filter-out $(common-objs),$(mconf-objs) \
|
||||
$(qconf-objs) \
|
||||
$(nconf-objs)), \
|
||||
$(eval $(obj)/$f: CFLAGS+=$$(HOSTCFLAGS_$f)))
|
||||
|
||||
$(foreach f,$(qconf-cxxobjs), \
|
||||
$(eval $(obj)/$f: CXXFLAGS+=$$(HOSTCXXFLAGS_$f)))
|
||||
|
||||
$(obj)/conf: $(addprefix $(obj)/,$(conf-objs))
|
||||
|
||||
# The *conf-cfg file is used (then filtered out) as the first prerequisite to
|
||||
# avoid sourcing it before the script is built, when trying to compute CFLAGS
|
||||
# for the actual first prerequisite. This avoids errors like:
|
||||
# '/bin/sh: ./mconf-cfg: No such file or directory'
|
||||
$(obj)/mconf: mconf-cfg $(addprefix $(obj)/,$(mconf-objs))
|
||||
$(CC) -o $@ $(filter-out mconf-cfg,$^) $(HOSTLDLIBS_mconf)
|
||||
|
||||
$(obj)/nconf: nconf-cfg $(addprefix $(obj)/,$(nconf-objs))
|
||||
$(CC) -o $@ $(filter-out nconf-cfg,$^) $(HOSTLDLIBS_nconf)
|
||||
|
||||
$(obj)/qconf: qconf-cfg $(addprefix $(obj)/,$(qconf-cxxobjs) $(qconf-objs))
|
||||
$(CXX) -o $@ $(filter-out qconf-cfg,$^) $(HOSTLDLIBS_qconf)
|
||||
26
scripts/config/README
Normal file
26
scripts/config/README
Normal file
@@ -0,0 +1,26 @@
|
||||
These files were taken from the Linux 5.14 Kernel Configuration System and
|
||||
modified for the OpenWrt Buildroot:
|
||||
- Removed nconf, 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-5.14/scripts/kconfig
|
||||
915
scripts/config/conf.c
Normal file
915
scripts/config/conf.c
Normal file
@@ -0,0 +1,915 @@
|
||||
// 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,
|
||||
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_y2m,
|
||||
def_m2y,
|
||||
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_mod_or_yes(enum conf_def_mode mode)
|
||||
{
|
||||
struct symbol *sym;
|
||||
int i;
|
||||
tristate old_val = (mode == def_y2m) ? yes : mod;
|
||||
tristate new_val = (mode == def_y2m) ? mod : yes;
|
||||
|
||||
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_choice_value(sym, child->sym);
|
||||
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) {
|
||||
const char *str;
|
||||
|
||||
if (sym->type == S_STRING) {
|
||||
str = sym_get_string_value(sym);
|
||||
str = sym_escape_string_value(str);
|
||||
printf("%s%s=%s\n", CONFIG_, sym->name, str);
|
||||
free((void *)str);
|
||||
} else {
|
||||
str = sym_get_string_value(sym);
|
||||
printf("%s%s=%s\n", CONFIG_, sym->name, str);
|
||||
}
|
||||
}
|
||||
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},
|
||||
{"fatalrecursive",no_argument, NULL, 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(" -s, --silent Do not print log.\n");
|
||||
printf(" --fatalrecursive Treat recursive depenendencies as a fatal 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(" (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:sw:", long_opts, NULL)) != -1) {
|
||||
switch (opt) {
|
||||
case 'h':
|
||||
conf_usage(progname);
|
||||
exit(1);
|
||||
break;
|
||||
case 's':
|
||||
conf_set_message_callback(NULL);
|
||||
break;
|
||||
case fatalrecursive:
|
||||
recursive_is_error = 1;
|
||||
continue;
|
||||
case 'r':
|
||||
input_file = optarg;
|
||||
break;
|
||||
case 'w':
|
||||
output_file = optarg;
|
||||
break;
|
||||
case 0:
|
||||
input_mode = input_mode_opt;
|
||||
switch (input_mode) {
|
||||
case syncconfig:
|
||||
/*
|
||||
* syncconfig is invoked during the build stage.
|
||||
* Suppress distracting
|
||||
* "configuration written to ..."
|
||||
*/
|
||||
conf_set_message_callback(NULL);
|
||||
sync_kconfig = 1;
|
||||
break;
|
||||
case defconfig:
|
||||
case savedefconfig:
|
||||
defconfig_file = optarg;
|
||||
break;
|
||||
case randconfig:
|
||||
set_randconfig_seed();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (ac == optind) {
|
||||
fprintf(stderr, "%s: Kconfig file missing\n", av[0]);
|
||||
conf_usage(progname);
|
||||
exit(1);
|
||||
}
|
||||
conf_parse(av[optind]);
|
||||
//zconfdump(stdout);
|
||||
|
||||
switch (input_mode) {
|
||||
case defconfig:
|
||||
if (conf_read(defconfig_file)) {
|
||||
fprintf(stderr,
|
||||
"***\n"
|
||||
"*** Can't find default configuration \"%s\"!\n"
|
||||
"***\n",
|
||||
defconfig_file);
|
||||
exit(1);
|
||||
}
|
||||
break;
|
||||
case savedefconfig:
|
||||
case syncconfig:
|
||||
case oldaskconfig:
|
||||
case oldconfig:
|
||||
case listnewconfig:
|
||||
case helpnewconfig:
|
||||
case olddefconfig:
|
||||
case yes2modconfig:
|
||||
case mod2yesconfig:
|
||||
case 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_mod_or_yes(def_y2m);
|
||||
break;
|
||||
case mod2yesconfig:
|
||||
conf_rewrite_mod_or_yes(def_m2y);
|
||||
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;
|
||||
}
|
||||
1162
scripts/config/confdata.c
Normal file
1162
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
327
scripts/config/expr.h
Normal file
327
scripts/config/expr.h
Normal file
@@ -0,0 +1,327 @@
|
||||
/* 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;
|
||||
int index;
|
||||
};
|
||||
|
||||
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 */
|
||||
521
scripts/config/lexer.l
Normal file
521
scripts/config/lexer.l
Normal file
@@ -0,0 +1,521 @@
|
||||
/* 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_-]
|
||||
|
||||
%%
|
||||
int str = 0;
|
||||
int ts, i;
|
||||
|
||||
#.* /* 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;
|
||||
\"|\' {
|
||||
str = 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 (str == 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]+ {
|
||||
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>";
|
||||
}
|
||||
4201
scripts/config/lexer.lex.c
Normal file
4201
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
|
||||
158
scripts/config/lkc.h
Normal file
158
scripts/config/lkc.h
Normal file
@@ -0,0 +1,158 @@
|
||||
/* 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, ...);
|
||||
const 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_root_menu(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);
|
||||
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_set_choice_value(struct symbol *ch, struct symbol *chval)
|
||||
{
|
||||
return sym_set_tristate_value(chval, yes);
|
||||
}
|
||||
|
||||
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);
|
||||
const char * sym_escape_string_value(const char *in);
|
||||
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 */
|
||||
}
|
||||
238
scripts/config/lxdialog/dialog.h
Normal file
238
scripts/config/lxdialog/dialog.h
Normal file
@@ -0,0 +1,238 @@
|
||||
/* 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>
|
||||
|
||||
/*
|
||||
* Colors in ncurses 1.9.9e do not work properly since foreground and
|
||||
* background colors are OR'd rather than separately masked. This version
|
||||
* of dialog was hacked to work with ncurses 1.9.9e, making it incompatible
|
||||
* with standard curses. The simplest fix (to make this work with standard
|
||||
* curses) uses the wbkgdset() function, not used in the original hack.
|
||||
* Turn it off if we're building with 1.9.9e, since it just confuses things.
|
||||
*/
|
||||
#if defined(NCURSES_VERSION) && defined(_NEED_WRAP) && !defined(GCC_PRINTFLIKE)
|
||||
#define OLD_NCURSES 1
|
||||
#undef wbkgdset
|
||||
#define wbkgdset(w,p) /*nothing */
|
||||
#else
|
||||
#define OLD_NCURSES 0
|
||||
#endif
|
||||
|
||||
#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);
|
||||
|
||||
|
||||
typedef void (*update_text_fn)(char *buf, size_t start, size_t end, void
|
||||
*_data);
|
||||
int dialog_textbox(const char *title, char *tbuf, int initial_height,
|
||||
int initial_width, int *keys, int *_vscroll, int *_hscroll,
|
||||
update_text_fn update_text, 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);
|
||||
|
||||
/*
|
||||
* This is the base for fictitious keys, which activate
|
||||
* the buttons.
|
||||
*
|
||||
* Mouse-generated keys are the following:
|
||||
* -- the first 32 are used as numbers, in addition to '0'-'9'
|
||||
* -- the lowercase are used to signal mouse-enter events (M_EVENT + 'o')
|
||||
* -- uppercase chars are used to invoke the button (M_EVENT + 'O')
|
||||
*/
|
||||
#define M_EVENT (KEY_MAX+1)
|
||||
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 */
|
||||
}
|
||||
424
scripts/config/lxdialog/menubox.c
Normal file
424
scripts/config/lxdialog/menubox.c
Normal file
@@ -0,0 +1,424 @@
|
||||
// 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);
|
||||
#if OLD_NCURSES
|
||||
{
|
||||
int i;
|
||||
for (i = 0; i < menu_width; i++)
|
||||
waddch(win, ' ');
|
||||
}
|
||||
#else
|
||||
wclrtoeol(win);
|
||||
#endif
|
||||
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 */
|
||||
}
|
||||
395
scripts/config/lxdialog/textbox.c
Normal file
395
scripts/config/lxdialog/textbox.c
Normal file
@@ -0,0 +1,395 @@
|
||||
// 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 void back_lines(int n);
|
||||
static void print_page(WINDOW *win, int height, int width, update_text_fn
|
||||
update_text, void *data);
|
||||
static void print_line(WINDOW *win, int row, int width);
|
||||
static char *get_line(void);
|
||||
static void print_position(WINDOW * win);
|
||||
|
||||
static int hscroll;
|
||||
static int begin_reached, end_reached, page_length;
|
||||
static char *buf;
|
||||
static char *page;
|
||||
|
||||
/*
|
||||
* refresh window content
|
||||
*/
|
||||
static void refresh_text_box(WINDOW *dialog, WINDOW *box, int boxh, int boxw,
|
||||
int cur_y, int cur_x, update_text_fn update_text,
|
||||
void *data)
|
||||
{
|
||||
print_page(box, boxh, boxw, update_text, data);
|
||||
print_position(dialog);
|
||||
wmove(dialog, cur_y, cur_x); /* Restore cursor position */
|
||||
wrefresh(dialog);
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Display text from a file in a dialog box.
|
||||
*
|
||||
* keys is a null-terminated array
|
||||
* update_text() may not add or remove any '\n' or '\0' in tbuf
|
||||
*/
|
||||
int dialog_textbox(const char *title, char *tbuf, int initial_height,
|
||||
int initial_width, int *keys, int *_vscroll, int *_hscroll,
|
||||
update_text_fn update_text, 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, update_text,
|
||||
data);
|
||||
|
||||
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, update_text,
|
||||
data);
|
||||
}
|
||||
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, update_text, data);
|
||||
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, update_text, data);
|
||||
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, update_text, data);
|
||||
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, update_text, data);
|
||||
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, update_text, data);
|
||||
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, update_text, data);
|
||||
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, update_text, data);
|
||||
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:
|
||||
for (i = 0; keys[i]; i++) {
|
||||
if (key == keys[i]) {
|
||||
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;
|
||||
}
|
||||
|
||||
/*
|
||||
* 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++;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Print a new page of text.
|
||||
*/
|
||||
static void print_page(WINDOW *win, int height, int width, update_text_fn
|
||||
update_text, void *data)
|
||||
{
|
||||
int i, passed_end = 0;
|
||||
|
||||
if (update_text) {
|
||||
char *end;
|
||||
|
||||
for (i = 0; i < height; i++)
|
||||
get_line();
|
||||
end = page;
|
||||
back_lines(height);
|
||||
update_text(buf, page - buf, end - buf, data);
|
||||
}
|
||||
|
||||
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 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 */
|
||||
#if OLD_NCURSES
|
||||
{
|
||||
int x = getcurx(win);
|
||||
int i;
|
||||
for (i = 0; i < width - x; i++)
|
||||
waddch(win, ' ');
|
||||
}
|
||||
#else
|
||||
wclrtoeol(win);
|
||||
#endif
|
||||
}
|
||||
|
||||
/*
|
||||
* 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 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);
|
||||
}
|
||||
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 */
|
||||
}
|
||||
52
scripts/config/mconf-cfg.sh
Executable file
52
scripts/config/mconf-cfg.sh
Executable file
@@ -0,0 +1,52 @@
|
||||
#!/bin/sh
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
PKG="ncursesw"
|
||||
PKG2="ncurses"
|
||||
|
||||
if [ -n "$(command -v pkg-config)" ]; then
|
||||
if pkg-config --exists $PKG; then
|
||||
echo cflags=\"$(pkg-config --cflags $PKG)\"
|
||||
echo libs=\"$(pkg-config --libs $PKG)\"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if pkg-config --exists $PKG2; then
|
||||
echo cflags=\"$(pkg-config --cflags $PKG2)\"
|
||||
echo libs=\"$(pkg-config --libs $PKG2)\"
|
||||
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 cflags=\"-D_GNU_SOURCE -I/usr/include/ncursesw\"
|
||||
echo libs=\"-lncursesw\"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ -f /usr/include/ncurses/ncurses.h ]; then
|
||||
echo cflags=\"-D_GNU_SOURCE -I/usr/include/ncurses\"
|
||||
echo libs=\"-lncurses\"
|
||||
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 cflags=\"-D_GNU_SOURCE\"
|
||||
echo libs=\"-lncurses\"
|
||||
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 pkg-config to find the"
|
||||
echo >&2 "* ncurses installed in a non-default location."
|
||||
echo >&2 "*"
|
||||
exit 1
|
||||
1050
scripts/config/mconf.c
Normal file
1050
scripts/config/mconf.c
Normal file
File diff suppressed because it is too large
Load Diff
878
scripts/config/menu.c
Normal file
878
scripts/config/menu.c
Normal file
@@ -0,0 +1,878 @@
|
||||
// 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_root_menu(struct menu *menu)
|
||||
{
|
||||
return &rootmenu;
|
||||
}
|
||||
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
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->parent;
|
||||
for (i = 0; menu != &rootmenu && i < 8; menu = menu->parent) {
|
||||
bool accessible = menu_is_visible(menu);
|
||||
|
||||
submenu[i++] = menu;
|
||||
if (location == NULL && accessible)
|
||||
location = menu;
|
||||
}
|
||||
if (head && location) {
|
||||
jump = xmalloc(sizeof(struct jump_key));
|
||||
|
||||
if (menu_is_visible(prop->menu)) {
|
||||
/*
|
||||
* There is not enough room to put the hint at the
|
||||
* beginning of the "Prompt" line. Put the hint on the
|
||||
* last "Location" line even when it would belong on
|
||||
* the former.
|
||||
*/
|
||||
jump->target = prop->menu;
|
||||
} else
|
||||
jump->target = location;
|
||||
|
||||
if (list_empty(head))
|
||||
jump->index = 0;
|
||||
else
|
||||
jump->index = list_entry(head->prev, struct jump_key,
|
||||
entries)->index + 1;
|
||||
|
||||
list_add_tail(&jump->entries, head);
|
||||
}
|
||||
|
||||
if (i > 0) {
|
||||
str_printf(r, " Location:\n");
|
||||
for (j = 4; --i >= 0; j += 2) {
|
||||
menu = submenu[i];
|
||||
if (jump && menu == location)
|
||||
jump->offset = strlen(r->s);
|
||||
str_printf(r, "%*c-> %s", j, ' ',
|
||||
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);
|
||||
}
|
||||
50
scripts/config/nconf-cfg.sh
Executable file
50
scripts/config/nconf-cfg.sh
Executable file
@@ -0,0 +1,50 @@
|
||||
#!/bin/sh
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
PKG="ncursesw menuw panelw"
|
||||
PKG2="ncurses menu panel"
|
||||
|
||||
if [ -n "$(command -v pkg-config)" ]; then
|
||||
if pkg-config --exists $PKG; then
|
||||
echo cflags=\"$(pkg-config --cflags $PKG)\"
|
||||
echo libs=\"$(pkg-config --libs $PKG)\"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if pkg-config --exists $PKG2; then
|
||||
echo cflags=\"$(pkg-config --cflags $PKG2)\"
|
||||
echo libs=\"$(pkg-config --libs $PKG2)\"
|
||||
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 cflags=\"-D_GNU_SOURCE -I/usr/include/ncursesw\"
|
||||
echo libs=\"-lncursesw -lmenuw -lpanelw\"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ -f /usr/include/ncurses/ncurses.h ]; then
|
||||
echo cflags=\"-D_GNU_SOURCE -I/usr/include/ncurses\"
|
||||
echo libs=\"-lncurses -lmenu -lpanel\"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ -f /usr/include/ncurses.h ]; then
|
||||
echo cflags=\"-D_GNU_SOURCE\"
|
||||
echo libs=\"-lncurses -lmenu -lpanel\"
|
||||
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 pkg-config to find the"
|
||||
echo >&2 "* ncurses installed in a non-default location."
|
||||
echo >&2 "*"
|
||||
exit 1
|
||||
1562
scripts/config/nconf.c
Normal file
1562
scripts/config/nconf.c
Normal file
File diff suppressed because it is too large
Load Diff
614
scripts/config/nconf.gui.c
Normal file
614
scripts/config/nconf.gui.c
Normal file
@@ -0,0 +1,614 @@
|
||||
// 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();
|
||||
}
|
||||
|
||||
/* layman's scrollable window... */
|
||||
void show_scroll_win(WINDOW *main_window,
|
||||
const char *title,
|
||||
const char *text)
|
||||
{
|
||||
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;
|
||||
|
||||
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 */
|
||||
do {
|
||||
|
||||
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;
|
||||
}
|
||||
if (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;
|
||||
} while (res);
|
||||
|
||||
del_panel(panel);
|
||||
delwin(win);
|
||||
refresh_all_windows(main_window);
|
||||
}
|
||||
83
scripts/config/nconf.h
Normal file
83
scripts/config/nconf.h
Normal file
@@ -0,0 +1,83 @@
|
||||
/* 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);
|
||||
|
||||
/* 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);
|
||||
void show_scroll_win(WINDOW *main_window,
|
||||
const char *title,
|
||||
const char *text);
|
||||
2189
scripts/config/parser.tab.c
Normal file
2189
scripts/config/parser.tab.c
Normal file
File diff suppressed because it is too large
Load Diff
133
scripts/config/parser.tab.h
Normal file
133
scripts/config/parser.tab.h
Normal file
@@ -0,0 +1,133 @@
|
||||
/* A Bison parser, made by GNU Bison 3.7.6. */
|
||||
|
||||
/* 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
575
scripts/config/preprocess.c
Normal file
575
scripts/config/preprocess.c
Normal file
@@ -0,0 +1,575 @@
|
||||
// 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[256];
|
||||
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++;
|
||||
}
|
||||
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);
|
||||
}
|
||||
24
scripts/config/qconf-cfg.sh
Executable file
24
scripts/config/qconf-cfg.sh
Executable file
@@ -0,0 +1,24 @@
|
||||
#!/bin/sh
|
||||
# SPDX-License-Identifier: GPL-2.0-only
|
||||
|
||||
PKG="Qt5Core Qt5Gui Qt5Widgets"
|
||||
|
||||
if [ -z "$(command -v pkg-config)" ]; then
|
||||
echo >&2 "*"
|
||||
echo >&2 "* 'make xconfig' requires 'pkg-config'. Please install it."
|
||||
echo >&2 "*"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if pkg-config --exists $PKG; then
|
||||
echo cflags=\"-std=c++11 -fPIC $(pkg-config --cflags $PKG)\"
|
||||
echo libs=\"$(pkg-config --libs $PKG)\"
|
||||
echo moc=\"$(pkg-config --variable=host_bins Qt5Core)/moc\"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo >&2 "*"
|
||||
echo >&2 "* Could not find Qt5 via pkg-config."
|
||||
echo >&2 "* Please install Qt5 and make sure it's in PKG_CONFIG_PATH"
|
||||
echo >&2 "*"
|
||||
exit 1
|
||||
1916
scripts/config/qconf.cc
Normal file
1916
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;
|
||||
};
|
||||
1298
scripts/config/symbol.c
Normal file
1298
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 */
|
||||
const 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
|
||||
331
scripts/dl_cleanup.py
Executable file
331
scripts/dl_cleanup.py
Executable file
@@ -0,0 +1,331 @@
|
||||
#!/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",
|
||||
".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))
|
||||
428
scripts/dl_github_archive.py
Executable file
428
scripts/dl_github_archive.py
Executable file
@@ -0,0 +1,428 @@
|
||||
#!/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('.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.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."""
|
||||
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._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')
|
||||
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()
|
||||
365
scripts/download.pl
Executable file
365
scripts/download.pl
Executable file
@@ -0,0 +1,365 @@
|
||||
#!/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;
|
||||
|
||||
@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 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) {
|
||||
push @mirrors, "https://downloads.sourceforge.net/$1";
|
||||
}
|
||||
} elsif ($mirror =~ /^\@OPENWRT$/) {
|
||||
# use OpenWrt source server directly
|
||||
} elsif ($mirror =~ /^\@DEBIAN\/(.+)$/) {
|
||||
push @mirrors, "https://ftp.debian.org/debian/$1";
|
||||
push @mirrors, "https://mirror.leaseweb.com/debian/$1";
|
||||
push @mirrors, "https://mirror.netcologne.de/debian/$1";
|
||||
push @mirrors, "https://mirrors.tuna.tsinghua.edu.cn/debian/$1";
|
||||
push @mirrors, "https://mirrors.ustc.edu.cn/debian/$1"
|
||||
} elsif ($mirror =~ /^\@APACHE\/(.+)$/) {
|
||||
push @mirrors, "https://dlcdn.apache.org/$1";
|
||||
push @mirrors, "https://mirror.netcologne.de/apache.org/$1";
|
||||
push @mirrors, "https://mirror.aarnet.edu.au/pub/apache/$1";
|
||||
push @mirrors, "https://mirror.csclub.uwaterloo.ca/apache/$1";
|
||||
push @mirrors, "https://archive.apache.org/dist/$1";
|
||||
push @mirrors, "http://mirror.cogentco.com/pub/apache/$1";
|
||||
push @mirrors, "http://mirror.navercorp.com/apache/$1";
|
||||
push @mirrors, "http://ftp.jaist.ac.jp/pub/apache/$1";
|
||||
push @mirrors, "ftp://apache.cs.utah.edu/apache.org/$1";
|
||||
push @mirrors, "ftp://apache.mirrors.ovh.net/ftp.apache.org/dist/$1";
|
||||
push @mirrors, "https://mirrors.tuna.tsinghua.edu.cn/apache/$1";
|
||||
push @mirrors, "https://mirrors.ustc.edu.cn/apache/$1";
|
||||
} elsif ($mirror =~ /^\@GITHUB\/(.+)$/) {
|
||||
# give github a few more tries (different mirrors)
|
||||
for (1 .. 5) {
|
||||
push @mirrors, "https://raw.githubusercontent.com/$1";
|
||||
}
|
||||
} elsif ($mirror =~ /^\@GNU\/(.+)$/) {
|
||||
push @mirrors, "https://ftpmirror.gnu.org/$1";
|
||||
push @mirrors, "https://mirror.csclub.uwaterloo.ca/gnu/$1";
|
||||
push @mirrors, "https://mirror.netcologne.de/gnu/$1";
|
||||
push @mirrors, "http://ftp.kddilabs.jp/GNU/gnu/$1";
|
||||
push @mirrors, "http://www.nic.funet.fi/pub/gnu/gnu/$1";
|
||||
push @mirrors, "http://mirror.internode.on.net/pub/gnu/$1";
|
||||
push @mirrors, "http://mirror.navercorp.com/gnu/$1";
|
||||
push @mirrors, "ftp://mirrors.rit.edu/gnu/$1";
|
||||
push @mirrors, "ftp://download.xs4all.nl/pub/gnu/$1";
|
||||
push @mirrors, "https://ftp.gnu.org/gnu/$1";
|
||||
push @mirrors, "https://mirrors.tuna.tsinghua.edu.cn/gnu/$1";
|
||||
push @mirrors, "https://mirrors.ustc.edu.cn/gnu/$1";
|
||||
} elsif ($mirror =~ /^\@SAVANNAH\/(.+)$/) {
|
||||
push @mirrors, "https://download.savannah.nongnu.org/releases/$1";
|
||||
push @mirrors, "https://mirror.netcologne.de/savannah/$1";
|
||||
push @mirrors, "https://mirror.csclub.uwaterloo.ca/nongnu/$1";
|
||||
push @mirrors, "http://ftp.acc.umu.se/mirror/gnu.org/savannah/$1";
|
||||
push @mirrors, "http://nongnu.uib.no/$1";
|
||||
push @mirrors, "http://ftp.igh.cnrs.fr/pub/nongnu/$1";
|
||||
push @mirrors, "ftp://cdimage.debian.org/mirror/gnu.org/savannah/$1";
|
||||
push @mirrors, "ftp://ftp.acc.umu.se/mirror/gnu.org/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) {
|
||||
push @mirrors, "https://cdn.kernel.org/pub/$dir";
|
||||
push @mirrors, "https://download.xs4all.nl/ftp.kernel.org/pub/$dir";
|
||||
push @mirrors, "https://mirrors.mit.edu/kernel/$dir";
|
||||
push @mirrors, "http://ftp.nara.wide.ad.jp/pub/kernel.org/$dir";
|
||||
push @mirrors, "http://www.ring.gr.jp/archives/linux/kernel.org/$dir";
|
||||
push @mirrors, "ftp://ftp.riken.jp/Linux/kernel.org/$dir";
|
||||
push @mirrors, "ftp://www.mirrorservice.org/sites/ftp.kernel.org/pub/$dir";
|
||||
push @mirrors, "https://mirrors.tuna.tsinghua.edu.cn/kernel/$dir";
|
||||
push @mirrors, "https://mirrors.ustc.edu.cn/kernel.org/$dir";
|
||||
}
|
||||
} elsif ($mirror =~ /^\@GNOME\/(.+)$/) {
|
||||
push @mirrors, "https://download.gnome.org/sources/$1";
|
||||
push @mirrors, "https://mirror.csclub.uwaterloo.ca/gnome/sources/$1";
|
||||
push @mirrors, "http://ftp.acc.umu.se/pub/GNOME/sources/$1";
|
||||
push @mirrors, "http://ftp.kaist.ac.kr/gnome/sources/$1";
|
||||
push @mirrors, "http://www.mirrorservice.org/sites/ftp.gnome.org/pub/GNOME/sources/$1";
|
||||
push @mirrors, "http://mirror.internode.on.net/pub/gnome/sources/$1";
|
||||
push @mirrors, "http://ftp.belnet.be/ftp.gnome.org/sources/$1";
|
||||
push @mirrors, "ftp://ftp.cse.buffalo.edu/pub/Gnome/sources/$1";
|
||||
push @mirrors, "ftp://ftp.nara.wide.ad.jp/pub/X11/GNOME/sources/$1";
|
||||
push @mirrors, "https://mirrors.ustc.edu.cn/gnome/sources/$1";
|
||||
} else {
|
||||
push @mirrors, $mirror;
|
||||
}
|
||||
}
|
||||
|
||||
push @mirrors, 'https://sources.cdn.openwrt.org';
|
||||
push @mirrors, 'https://sources.openwrt.org';
|
||||
push @mirrors, 'https://mirror2.openwrt.org/sources';
|
||||
|
||||
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
|
||||
624
scripts/ext-toolchain.sh
Executable file
624
scripts/ext-toolchain.sh
Executable file
@@ -0,0 +1,624 @@
|
||||
#!/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=""
|
||||
|
||||
|
||||
# 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
|
||||
}
|
||||
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
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 --depth 1 '%s' '%s' && cd '%s' && git fetch --depth=1 origin '%s' && git -c advice.detachedHead=false checkout '%s' && cd -",
|
||||
'update' => "git pull --ff-only",
|
||||
'update_force' => "git pull --ff-only || (git reset --hard HEAD; git pull --ff-only; exit 1)",
|
||||
'post_update' => "git submodule update --init --recursive --depth 1",
|
||||
'controldir' => ".git",
|
||||
'revision' => "git rev-parse --short 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 --short HEAD | tr -d '\n'"},
|
||||
'src-gitsvn' => {
|
||||
'init' => "git svn clone -r HEAD '%s' '%s'",
|
||||
'update' => "git svn rebase",
|
||||
'controldir' => ".git",
|
||||
'revision' => "git rev-parse --short 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.\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 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 master@{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=$(stat -c "%s" "$tmp_dir"/data.tar.gz)
|
||||
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_"]:
|
||||
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=(",", ":")))
|
||||
83
scripts/json_overview_image_info.py
Executable file
83
scripts/json_overview_image_info.py
Executable file
@@ -0,0 +1,83 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
from os import getenv, environ
|
||||
from pathlib import Path
|
||||
from subprocess import run, PIPE
|
||||
from sys import argv
|
||||
import json
|
||||
|
||||
if len(argv) != 2:
|
||||
print("JSON info files script requires output file as argument")
|
||||
exit(1)
|
||||
|
||||
output_path = Path(argv[1])
|
||||
|
||||
assert getenv("WORK_DIR"), "$WORK_DIR required"
|
||||
|
||||
work_dir = Path(getenv("WORK_DIR"))
|
||||
|
||||
output = {}
|
||||
|
||||
|
||||
def get_initial_output(image_info):
|
||||
# preserve existing profiles.json
|
||||
if output_path.is_file():
|
||||
profiles = json.loads(output_path.read_text())
|
||||
if profiles["version_code"] == image_info["version_code"]:
|
||||
return profiles
|
||||
return image_info
|
||||
|
||||
|
||||
for json_file in work_dir.glob("*.json"):
|
||||
image_info = json.loads(json_file.read_text())
|
||||
|
||||
if not output:
|
||||
output = get_initial_output(image_info)
|
||||
|
||||
# get first and only profile in json file
|
||||
device_id, profile = next(iter(image_info["profiles"].items()))
|
||||
if device_id not in output["profiles"]:
|
||||
output["profiles"][device_id] = profile
|
||||
else:
|
||||
output["profiles"][device_id]["images"].extend(profile["images"])
|
||||
|
||||
# make image lists unique by name, keep last/latest
|
||||
for device_id, profile in output.get("profiles", {}).items():
|
||||
profile["images"] = list({e["name"]: e for e in profile["images"]}.values())
|
||||
|
||||
|
||||
if output:
|
||||
(
|
||||
default_packages,
|
||||
output["arch_packages"],
|
||||
linux_version,
|
||||
linux_release,
|
||||
linux_vermagic,
|
||||
) = run(
|
||||
[
|
||||
"make",
|
||||
"--no-print-directory",
|
||||
"-C",
|
||||
"target/linux/",
|
||||
"val.DEFAULT_PACKAGES",
|
||||
"val.ARCH_PACKAGES",
|
||||
"val.LINUX_VERSION",
|
||||
"val.LINUX_RELEASE",
|
||||
"val.LINUX_VERMAGIC",
|
||||
"V=s",
|
||||
],
|
||||
stdout=PIPE,
|
||||
check=True,
|
||||
env=environ.copy().update({"TOPDIR": Path().cwd()}),
|
||||
universal_newlines=True,
|
||||
).stdout.splitlines()
|
||||
|
||||
output["default_packages"] = sorted(default_packages.split())
|
||||
output["linux_kernel"] = {
|
||||
"version": linux_version,
|
||||
"release": linux_release,
|
||||
"vermagic": linux_vermagic,
|
||||
}
|
||||
output_path.write_text(json.dumps(output, sort_keys=True, separators=(",", ":")))
|
||||
else:
|
||||
print("JSON info file script could not find any JSON files for target")
|
||||
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]);
|
||||
64
scripts/linksys-image.sh
Executable file
64
scripts/linksys-image.sh
Executable file
@@ -0,0 +1,64 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# Copyright (C) 2018 Oceanic Systems (UK) Ltd
|
||||
#
|
||||
# This is free software, licensed under the GNU General Public License v2.
|
||||
# See /LICENSE for more information.
|
||||
#
|
||||
# Maintained by: Ryan Pannell <ryan [at] o s u k l .com> <github.com/Escalion>
|
||||
#
|
||||
# Write Linksys signature for factory image
|
||||
# This is appended to the factory image and is tested by the Linksys Upgrader - as observed in civic.
|
||||
# The footer is 256 bytes. The format is:
|
||||
# .LINKSYS. This is detected by the Linksys upgrader before continuing with upgrade. (9 bytes)
|
||||
# <VERSION> The version number of upgrade. Not checked so use arbitrary value (8 bytes)
|
||||
# <TYPE> Model of target device, padded (0x20) to (15 bytes)
|
||||
# <CRC> CRC checksum of the image to flash (8 byte)
|
||||
# <padding> Padding ('0' + 0x20 *7) (8 bytes)
|
||||
# <signature> Signature of signer. Not checked so use arbitrary value (16 bytes)
|
||||
# <padding> Padding (0x00) (192 bytes)
|
||||
|
||||
## version history
|
||||
# * version 1: initial commit
|
||||
|
||||
set -e
|
||||
|
||||
ME="${0##*/}"
|
||||
|
||||
usage() {
|
||||
echo "Usage: $ME <type> <in filename>"
|
||||
[ "$IMG_OUT" ] && rm -f "$IMG_OUT"
|
||||
exit 1
|
||||
}
|
||||
|
||||
[ "$#" -lt 3 ] && usage
|
||||
|
||||
TYPE=$1
|
||||
|
||||
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"
|
||||
|
||||
IMG_IN=$2
|
||||
IMG_OUT="${IMG_IN}.new"
|
||||
|
||||
[ ! -f "$IMG_IN" ] && echo "$ME: Not a valid image: $IMG_IN" && usage
|
||||
|
||||
dd if="${IMG_IN}" of="${IMG_TMP_OUT}"
|
||||
CRC=$(printf "%08X" $(dd if="${IMG_IN}" bs=$(stat -c%s "${IMG_IN}") count=1|cksum| cut -d ' ' -f1))
|
||||
|
||||
printf ".LINKSYS.01000409%-15s%-8s%-8s%-16s" "${TYPE}" "${CRC}" "0" "K0000000F0246434" >> "${IMG_TMP_OUT}"
|
||||
|
||||
dd if=/dev/zero bs=1 count=192 conv=notrunc >> "${IMG_TMP_OUT}"
|
||||
|
||||
cp "${IMG_TMP_OUT}" "${IMG_OUT}"
|
||||
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
|
||||
358
scripts/metadata.pm
Normal file
358
scripts/metadata.pm
Normal file
@@ -0,0 +1,358 @@
|
||||
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;
|
||||
/^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;
|
||||
/^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}"
|
||||
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()
|
||||
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
|
||||
831
scripts/package-metadata.pl
Executable file
831
scripts/package-metadata.pl
Executable file
@@ -0,0 +1,831 @@
|
||||
#!/usr/bin/env perl
|
||||
use FindBin;
|
||||
use lib "$FindBin::Bin";
|
||||
use strict;
|
||||
use metadata;
|
||||
use Getopt::Long;
|
||||
use Time::Piece;
|
||||
use JSON::PP;
|
||||
|
||||
my %board;
|
||||
|
||||
sub version_to_num($) {
|
||||
my $str = shift;
|
||||
my $num = 0;
|
||||
|
||||
if (defined($str) && $str =~ /^\d+(?:\.\d+)+$/)
|
||||
{
|
||||
my @n = (split(/\./, $str), 0, 0, 0, 0);
|
||||
$num = ($n[0] << 24) | ($n[1] << 16) | ($n[2] << 8) | $n[3];
|
||||
}
|
||||
|
||||
return $num;
|
||||
}
|
||||
|
||||
sub version_filter_list(@) {
|
||||
my $cmpver = version_to_num(shift @_);
|
||||
my @items;
|
||||
|
||||
foreach my $item (@_)
|
||||
{
|
||||
if ($item =~ s/@(lt|le|gt|ge|eq|ne)(\d+(?:\.\d+)+)\b//)
|
||||
{
|
||||
my $op = $1;
|
||||
my $symver = version_to_num($2);
|
||||
|
||||
if ($symver > 0 && $cmpver > 0)
|
||||
{
|
||||
next unless (($op eq 'lt' && $cmpver < $symver) ||
|
||||
($op eq 'le' && $cmpver <= $symver) ||
|
||||
($op eq 'gt' && $cmpver > $symver) ||
|
||||
($op eq 'ge' && $cmpver >= $symver) ||
|
||||
($op eq 'eq' && $cmpver == $symver) ||
|
||||
($op eq 'ne' && $cmpver != $symver));
|
||||
}
|
||||
}
|
||||
|
||||
push @items, $item;
|
||||
}
|
||||
|
||||
return @items;
|
||||
}
|
||||
|
||||
sub gen_kconfig_overrides() {
|
||||
my %config;
|
||||
my %kconfig;
|
||||
my $package;
|
||||
my $pkginfo = shift @ARGV;
|
||||
my $cfgfile = shift @ARGV;
|
||||
my $patchver = shift @ARGV;
|
||||
|
||||
# parameter 2: build system config
|
||||
open FILE, "<$cfgfile" or return;
|
||||
while (<FILE>) {
|
||||
/^(CONFIG_.+?)=(.+)$/ and $config{$1} = 1;
|
||||
}
|
||||
close FILE;
|
||||
|
||||
# parameter 1: package metadata
|
||||
open FILE, "<$pkginfo" or return;
|
||||
while (<FILE>) {
|
||||
/^Package:\s*(.+?)\s*$/ and $package = $1;
|
||||
/^Kernel-Config:\s*(.+?)\s*$/ and do {
|
||||
my @config = split /\s+/, $1;
|
||||
foreach my $config (version_filter_list($patchver, @config)) {
|
||||
my $val = 'm';
|
||||
my $override;
|
||||
if ($config =~ /^(.+?)=(.+)$/) {
|
||||
$config = $1;
|
||||
$override = 1;
|
||||
$val = $2;
|
||||
}
|
||||
if ($config{"CONFIG_PACKAGE_$package"} and ($config ne 'n')) {
|
||||
next if $kconfig{$config} eq 'y';
|
||||
$kconfig{$config} = $val;
|
||||
} elsif (!$override) {
|
||||
$kconfig{$config} or $kconfig{$config} = 'n';
|
||||
}
|
||||
}
|
||||
};
|
||||
};
|
||||
close FILE;
|
||||
|
||||
foreach my $kconfig (sort keys %kconfig) {
|
||||
if ($kconfig{$kconfig} eq 'n') {
|
||||
print "# $kconfig is not set\n";
|
||||
} else {
|
||||
print "$kconfig=$kconfig{$kconfig}\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
my %dep_check;
|
||||
sub __find_package_dep($$) {
|
||||
my $pkg = shift;
|
||||
my $name = shift;
|
||||
my $deps = $pkg->{depends};
|
||||
|
||||
return 0 unless defined $deps;
|
||||
foreach my $vpkg (@{$deps}) {
|
||||
foreach my $dep (@{$vpackage{$vpkg}}) {
|
||||
next if $dep_check{$dep->{name}};
|
||||
$dep_check{$dep->{name}} = 1;
|
||||
return 1 if $dep->{name} eq $name;
|
||||
return 1 if (__find_package_dep($dep, $name) == 1);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
# wrapper to avoid infinite recursion
|
||||
sub find_package_dep($$) {
|
||||
my $pkg = shift;
|
||||
my $name = shift;
|
||||
|
||||
%dep_check = ();
|
||||
return __find_package_dep($pkg, $name);
|
||||
}
|
||||
|
||||
sub package_depends($$) {
|
||||
my $a = shift;
|
||||
my $b = shift;
|
||||
my $ret;
|
||||
|
||||
return 0 if ($a->{submenu} ne $b->{submenu});
|
||||
if (find_package_dep($a, $b->{name}) == 1) {
|
||||
$ret = 1;
|
||||
} elsif (find_package_dep($b, $a->{name}) == 1) {
|
||||
$ret = -1;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
return $ret;
|
||||
}
|
||||
|
||||
sub mconf_depends {
|
||||
my $pkgname = shift;
|
||||
my $depends = shift;
|
||||
my $only_dep = shift;
|
||||
my $res;
|
||||
my $dep = shift;
|
||||
my $seen = shift;
|
||||
my $parent_condition = shift;
|
||||
$dep or $dep = {};
|
||||
$seen or $seen = {};
|
||||
my @t_depends;
|
||||
|
||||
$depends or return;
|
||||
my @depends = @$depends;
|
||||
foreach my $depend (@depends) {
|
||||
my $m = "depends on";
|
||||
my $flags = "";
|
||||
$depend =~ s/^([@\+]+)// and $flags = $1;
|
||||
my $condition = $parent_condition;
|
||||
|
||||
next if $condition eq $depend;
|
||||
next if $seen->{"$parent_condition:$depend"};
|
||||
next if $seen->{":$depend"};
|
||||
$seen->{"$parent_condition:$depend"} = 1;
|
||||
if ($depend =~ /^(.+):(.+)$/) {
|
||||
if ($1 ne "PACKAGE_$pkgname") {
|
||||
if ($condition) {
|
||||
$condition = "$condition && $1";
|
||||
} else {
|
||||
$condition = $1;
|
||||
}
|
||||
}
|
||||
$depend = $2;
|
||||
}
|
||||
if ($flags =~ /\+/) {
|
||||
my $vdep = $vpackage{$depend};
|
||||
if ($vdep) {
|
||||
my @vdeps;
|
||||
|
||||
foreach my $v (@$vdep) {
|
||||
next if $v->{buildonly};
|
||||
if ($v->{variant_default}) {
|
||||
unshift @vdeps, $v->{name};
|
||||
} else {
|
||||
push @vdeps, $v->{name};
|
||||
}
|
||||
}
|
||||
|
||||
$depend = shift @vdeps;
|
||||
|
||||
if (@vdeps > 1) {
|
||||
$condition = ($condition ? "$condition && " : '') . join("&&", map { "PACKAGE_$_<PACKAGE_$pkgname" } @vdeps);
|
||||
} elsif (@vdeps > 0) {
|
||||
$condition = ($condition ? "$condition && " : '') . "PACKAGE_${vdeps[0]}<PACKAGE_$pkgname";
|
||||
}
|
||||
}
|
||||
|
||||
# Menuconfig will not treat 'select FOO' as a real dependency
|
||||
# thus if FOO depends on other config options, these dependencies
|
||||
# will not be checked. To fix this, we simply emit all of FOO's
|
||||
# depends here as well.
|
||||
$package{$depend} and push @t_depends, [ $package{$depend}->{depends}, $condition ];
|
||||
|
||||
$m = "select";
|
||||
next if $only_dep;
|
||||
|
||||
$flags =~ /@/ or $depend = "PACKAGE_$depend";
|
||||
} else {
|
||||
my $vdep = $vpackage{$depend};
|
||||
if ($vdep && @$vdep > 0) {
|
||||
$depend = join("||", map { "PACKAGE_".$_->{name} } @$vdep);
|
||||
} else {
|
||||
$flags =~ /@/ or $depend = "PACKAGE_$depend";
|
||||
}
|
||||
}
|
||||
|
||||
if ($condition) {
|
||||
if ($m =~ /select/) {
|
||||
next if $depend eq $condition;
|
||||
$depend = "$depend if $condition";
|
||||
} else {
|
||||
next if $dep->{"$depend if $condition"};
|
||||
$depend = "!($condition) || $depend" unless $dep->{$condition} eq 'select';
|
||||
}
|
||||
}
|
||||
$dep->{$depend} =~ /select/ or $dep->{$depend} = $m;
|
||||
}
|
||||
|
||||
foreach my $tdep (@t_depends) {
|
||||
mconf_depends($pkgname, $tdep->[0], 1, $dep, $seen, $tdep->[1]);
|
||||
}
|
||||
|
||||
foreach my $depend (keys %$dep) {
|
||||
my $m = $dep->{$depend};
|
||||
$res .= "\t\t$m $depend\n";
|
||||
}
|
||||
return $res;
|
||||
}
|
||||
|
||||
sub mconf_conflicts {
|
||||
my $pkgname = shift;
|
||||
my $depends = shift;
|
||||
my $res = "";
|
||||
|
||||
foreach my $depend (@$depends) {
|
||||
next unless $package{$depend};
|
||||
$res .= "\t\tdepends on m || (PACKAGE_$depend != y)\n";
|
||||
}
|
||||
return $res;
|
||||
}
|
||||
|
||||
sub print_package_config_category($) {
|
||||
my $cat = shift;
|
||||
my %menus;
|
||||
my %menu_dep;
|
||||
|
||||
return unless $category{$cat};
|
||||
|
||||
print "menu \"$cat\"\n\n";
|
||||
my %spkg = %{$category{$cat}};
|
||||
|
||||
foreach my $spkg (sort {uc($a) cmp uc($b)} keys %spkg) {
|
||||
foreach my $pkg (@{$spkg{$spkg}}) {
|
||||
next if $pkg->{buildonly};
|
||||
my $menu = $pkg->{submenu};
|
||||
if ($menu) {
|
||||
$menu_dep{$menu} or $menu_dep{$menu} = $pkg->{submenudep};
|
||||
} else {
|
||||
$menu = 'undef';
|
||||
}
|
||||
$menus{$menu} or $menus{$menu} = [];
|
||||
push @{$menus{$menu}}, $pkg;
|
||||
}
|
||||
}
|
||||
my @menus = sort {
|
||||
($a eq 'undef' ? 1 : 0) or
|
||||
($b eq 'undef' ? -1 : 0) or
|
||||
($a cmp $b)
|
||||
} keys %menus;
|
||||
|
||||
foreach my $menu (@menus) {
|
||||
my @pkgs = sort {
|
||||
package_depends($a, $b) or
|
||||
($a->{name} cmp $b->{name})
|
||||
} @{$menus{$menu}};
|
||||
if ($menu ne 'undef') {
|
||||
$menu_dep{$menu} and print "if $menu_dep{$menu}\n";
|
||||
print "menu \"$menu\"\n";
|
||||
}
|
||||
foreach my $pkg (@pkgs) {
|
||||
next if $pkg->{src}{ignore};
|
||||
my $title = $pkg->{name};
|
||||
my $c = (72 - length($pkg->{name}) - length($pkg->{title}));
|
||||
if ($c > 0) {
|
||||
$title .= ("." x $c). " ". $pkg->{title};
|
||||
}
|
||||
$title = "\"$title\"";
|
||||
print "\t";
|
||||
$pkg->{menu} and print "menu";
|
||||
print "config PACKAGE_".$pkg->{name}."\n";
|
||||
$pkg->{hidden} and $title = "";
|
||||
print "\t\t".($pkg->{tristate} ? 'tristate' : 'bool')." $title\n";
|
||||
print "\t\tdefault y if DEFAULT_".$pkg->{name}."\n";
|
||||
unless ($pkg->{hidden}) {
|
||||
my @def = ("ALL");
|
||||
if (!exists($pkg->{repository})) {
|
||||
push @def, "ALL_NONSHARED";
|
||||
}
|
||||
if ($pkg->{name} =~ /^kmod-/) {
|
||||
push @def, "ALL_KMODS";
|
||||
}
|
||||
$pkg->{default} ||= "m if " . join("||", @def);
|
||||
}
|
||||
if ($pkg->{default}) {
|
||||
foreach my $default (split /\s*,\s*/, $pkg->{default}) {
|
||||
print "\t\tdefault $default\n";
|
||||
}
|
||||
}
|
||||
print mconf_depends($pkg->{name}, $pkg->{depends}, 0);
|
||||
print mconf_depends($pkg->{name}, $pkg->{mdepends}, 0);
|
||||
print mconf_conflicts($pkg->{name}, $pkg->{conflicts});
|
||||
print "\t\thelp\n";
|
||||
print $pkg->{description};
|
||||
print "\n";
|
||||
|
||||
$pkg->{config} and print $pkg->{config}."\n";
|
||||
}
|
||||
if ($menu ne 'undef') {
|
||||
print "endmenu\n";
|
||||
$menu_dep{$menu} and print "endif\n";
|
||||
}
|
||||
}
|
||||
print "endmenu\n\n";
|
||||
|
||||
undef $category{$cat};
|
||||
}
|
||||
|
||||
sub print_package_overrides() {
|
||||
keys %overrides > 0 or return;
|
||||
print "\tconfig OVERRIDE_PKGS\n";
|
||||
print "\t\tstring\n";
|
||||
print "\t\tdefault \"".join(" ", sort keys %overrides)."\"\n\n";
|
||||
}
|
||||
|
||||
sub gen_package_config() {
|
||||
parse_package_metadata($ARGV[0]) or exit 1;
|
||||
print "menuconfig IMAGEOPT\n\tbool \"Image configuration\"\n\tdefault n\n";
|
||||
print "source \"package/*/image-config.in\"\n";
|
||||
if (scalar glob "package/feeds/*/*/image-config.in") {
|
||||
print "source \"package/feeds/*/*/image-config.in\"\n";
|
||||
}
|
||||
print_package_config_category 'Base system';
|
||||
foreach my $cat (sort {uc($a) cmp uc($b)} keys %category) {
|
||||
print_package_config_category $cat;
|
||||
}
|
||||
print_package_overrides();
|
||||
}
|
||||
|
||||
sub and_condition($) {
|
||||
my $condition = shift;
|
||||
my @spl_and = split('\&\&', $condition);
|
||||
if (@spl_and == 1) {
|
||||
return "\$(CONFIG_$spl_and[0])";
|
||||
}
|
||||
return "\$(and " . join (',', map("\$(CONFIG_$_)", @spl_and)) . ")";
|
||||
}
|
||||
|
||||
sub gen_condition ($) {
|
||||
my $condition = shift;
|
||||
# remove '!()', just as include/package-ipkg.mk does
|
||||
$condition =~ s/[()!]//g;
|
||||
return join("", map(and_condition($_), split('\|\|', $condition)));
|
||||
}
|
||||
|
||||
sub get_conditional_dep($$) {
|
||||
my $condition = shift;
|
||||
my $depstr = shift;
|
||||
if ($condition) {
|
||||
if ($condition =~ /^!(.+)/) {
|
||||
return "\$(if " . gen_condition($1) . ",,$depstr)";
|
||||
} else {
|
||||
return "\$(if " . gen_condition($condition) . ",$depstr)";
|
||||
}
|
||||
} else {
|
||||
return $depstr;
|
||||
}
|
||||
}
|
||||
|
||||
sub gen_package_mk() {
|
||||
my $line;
|
||||
|
||||
parse_package_metadata($ARGV[0]) or exit 1;
|
||||
foreach my $srcname (sort {uc($a) cmp uc($b)} keys %srcpackage) {
|
||||
my $src = $srcpackage{$srcname};
|
||||
my $variant_default;
|
||||
my %deplines = ('' => {});
|
||||
|
||||
foreach my $pkg (@{$src->{packages}}) {
|
||||
foreach my $dep (@{$pkg->{depends}}) {
|
||||
next if ($dep =~ /@/);
|
||||
|
||||
my $condition;
|
||||
|
||||
$dep =~ s/\+//g;
|
||||
if ($dep =~ /^(.+):(.+)/) {
|
||||
$condition = $1;
|
||||
$dep = $2;
|
||||
}
|
||||
|
||||
my $vpkg_dep = $vpackage{$dep};
|
||||
unless (defined $vpkg_dep) {
|
||||
warn sprintf "WARNING: Makefile '%s' has a dependency on '%s', which does not exist\n",
|
||||
$src->{makefile}, $dep;
|
||||
next;
|
||||
}
|
||||
|
||||
# Filter out self-depends
|
||||
my @vdeps = grep { $srcname ne $_->{src}{name} } @{$vpkg_dep};
|
||||
|
||||
foreach my $vdep (@vdeps) {
|
||||
my $depstr = sprintf '$(curdir)/%s/compile', $vdep->{src}{path};
|
||||
if (@vdeps > 1) {
|
||||
$depstr = sprintf '$(if $(CONFIG_PACKAGE_%s),%s)', $vdep->{name}, $depstr;
|
||||
}
|
||||
my $depline = get_conditional_dep($condition, $depstr);
|
||||
if ($depline) {
|
||||
$deplines{''}{$depline}++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
my $config = '';
|
||||
$config = sprintf '$(CONFIG_PACKAGE_%s)', $pkg->{name} unless $pkg->{buildonly};
|
||||
|
||||
$pkg->{prereq} and printf "prereq-%s += %s\n", $config, $src->{path};
|
||||
|
||||
next if $pkg->{buildonly};
|
||||
|
||||
printf "package-%s += %s\n", $config, $src->{path};
|
||||
|
||||
if ($pkg->{variant}) {
|
||||
if (!defined($variant_default) or $pkg->{variant_default}) {
|
||||
$variant_default = $pkg->{variant};
|
||||
}
|
||||
printf "\$(curdir)/%s/variants += \$(if %s,%s)\n", $src->{path}, $config, $pkg->{variant};
|
||||
}
|
||||
}
|
||||
|
||||
if (defined($variant_default)) {
|
||||
printf "\$(curdir)/%s/default-variant := %s\n", $src->{path}, $variant_default;
|
||||
}
|
||||
|
||||
unless (grep {!$_->{buildonly}} @{$src->{packages}}) {
|
||||
printf "package- += %s\n", $src->{path};
|
||||
}
|
||||
|
||||
if (@{$src->{buildtypes}} > 0) {
|
||||
printf "buildtypes-%s = %s\n", $src->{path}, join(' ', @{$src->{buildtypes}});
|
||||
}
|
||||
|
||||
foreach my $type ('', @{$src->{buildtypes}}) {
|
||||
my $suffix = '';
|
||||
|
||||
$suffix = "/$type" if $type;
|
||||
|
||||
next unless $src->{"builddepends$suffix"};
|
||||
|
||||
defined $deplines{$suffix} or $deplines{$suffix} = {};
|
||||
|
||||
foreach my $dep (@{$src->{"builddepends$suffix"}}) {
|
||||
my $depsuffix = "";
|
||||
my $deptype = "";
|
||||
my $condition;
|
||||
|
||||
if ($dep =~ /^(.+):(.+)/) {
|
||||
$condition = $1;
|
||||
$dep = $2;
|
||||
}
|
||||
if ($dep =~ /^(.+)\/(.+)/) {
|
||||
$dep = $1;
|
||||
$deptype = $2;
|
||||
$depsuffix = "/$2";
|
||||
}
|
||||
|
||||
next if $srcname.$suffix eq $dep.$depsuffix;
|
||||
|
||||
my $src_dep = $srcpackage{$dep};
|
||||
unless (defined($src_dep) && (!$deptype || grep { $_ eq $deptype } @{$src_dep->{buildtypes}})) {
|
||||
warn sprintf "WARNING: Makefile '%s' has a build dependency on '%s', which does not exist\n",
|
||||
$src->{makefile}, $dep.$depsuffix;
|
||||
next;
|
||||
}
|
||||
|
||||
my $depstr = sprintf '$(curdir)/%s/compile', $src_dep->{path}.$depsuffix;
|
||||
my $depline = get_conditional_dep($condition, $depstr);
|
||||
if ($depline) {
|
||||
$deplines{$suffix}{$depline}++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach my $suffix (sort keys %deplines) {
|
||||
my $depline = join(" ", sort keys %{$deplines{$suffix}});
|
||||
if ($depline) {
|
||||
$line .= sprintf "\$(curdir)/%s/compile += %s\n", $src->{path}.$suffix, $depline;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ($line ne "") {
|
||||
print "\n$line";
|
||||
}
|
||||
}
|
||||
|
||||
sub gen_package_source() {
|
||||
parse_package_metadata($ARGV[0]) or exit 1;
|
||||
foreach my $name (sort {uc($a) cmp uc($b)} keys %package) {
|
||||
my $pkg = $package{$name};
|
||||
if ($pkg->{name} && $pkg->{source}) {
|
||||
print "$pkg->{name}: ";
|
||||
print "$pkg->{source}\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub gen_package_auxiliary() {
|
||||
parse_package_metadata($ARGV[0]) or exit 1;
|
||||
foreach my $name (sort {uc($a) cmp uc($b)} keys %package) {
|
||||
my $pkg = $package{$name};
|
||||
if ($pkg->{name} && $pkg->{repository}) {
|
||||
print "Package/$name/subdir = $pkg->{repository}\n";
|
||||
}
|
||||
my %depends;
|
||||
foreach my $dep (@{$pkg->{depends} || []}) {
|
||||
if ($dep =~ m!^\+?(?:[^:]+:)?([^@]+)$!) {
|
||||
$depends{$1}++;
|
||||
}
|
||||
}
|
||||
my @depends = sort keys %depends;
|
||||
if (@depends > 0) {
|
||||
foreach my $n (@{$pkg->{provides}}) {
|
||||
print "Package/$n/depends = @depends\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub gen_package_license($) {
|
||||
my $level = shift;
|
||||
parse_package_metadata($ARGV[0]) or exit 1;
|
||||
foreach my $name (sort {uc($a) cmp uc($b)} keys %package) {
|
||||
my $pkg = $package{$name};
|
||||
if ($pkg->{name}) {
|
||||
if ($pkg->{license}) {
|
||||
print "$pkg->{name}: ";
|
||||
print "$pkg->{license}\n";
|
||||
if ($pkg->{licensefiles} && $level == 0) {
|
||||
print "\tFiles: $pkg->{licensefiles}\n";
|
||||
}
|
||||
} else {
|
||||
if ($level == 1) {
|
||||
print "$pkg->{name}: Missing license! ";
|
||||
print "Please fix $pkg->{src}{makefile}\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sub gen_version_filtered_list() {
|
||||
foreach my $item (version_filter_list(@ARGV)) {
|
||||
print "$item\n";
|
||||
}
|
||||
}
|
||||
|
||||
sub gen_usergroup_list() {
|
||||
parse_package_metadata($ARGV[0]) or exit 1;
|
||||
for my $name (keys %usernames) {
|
||||
print "user $name $usernames{$name}{id} $usernames{$name}{makefile}\n";
|
||||
}
|
||||
for my $name (keys %groupnames) {
|
||||
print "group $name $groupnames{$name}{id} $groupnames{$name}{makefile}\n";
|
||||
}
|
||||
}
|
||||
|
||||
sub gen_package_manifest_json() {
|
||||
my $json;
|
||||
parse_package_metadata($ARGV[0]) or exit 1;
|
||||
foreach my $name (sort {uc($a) cmp uc($b)} keys %package) {
|
||||
my %depends;
|
||||
my $pkg = $package{$name};
|
||||
foreach my $dep (@{$pkg->{depends} || []}) {
|
||||
if ($dep =~ m!^\+?(?:[^:]+:)?([^@]+)$!) {
|
||||
$depends{$1}++;
|
||||
}
|
||||
}
|
||||
my @depends = sort keys %depends;
|
||||
my $pkg_deps = join ' ', map { qq/"$_",/ } @depends;
|
||||
$pkg_deps =~ s/\,$//;
|
||||
|
||||
my $pkg_maintainer = join ' ', map { qq/"$_",/ } @{$pkg->{maintainer} || []};
|
||||
$pkg_maintainer =~ s/\,$//;
|
||||
|
||||
$json = <<"END_JSON";
|
||||
${json}{
|
||||
"name":"$name",
|
||||
"version":"$pkg->{version}",
|
||||
"category":"$pkg->{category}",
|
||||
"license":"$pkg->{license}",
|
||||
"cpe_id":"$pkg->{cpe_id}",
|
||||
"maintainer": [$pkg_maintainer],
|
||||
"depends":[$pkg_deps]},
|
||||
END_JSON
|
||||
}
|
||||
|
||||
$json =~ s/[\n\r]//g;
|
||||
$json =~ s/\,$//;
|
||||
print "[$json]";
|
||||
}
|
||||
|
||||
sub image_manifest_packages($)
|
||||
{
|
||||
my %packages;
|
||||
my $imgmanifest = shift;
|
||||
|
||||
open FILE, "<$imgmanifest" or return;
|
||||
while (<FILE>) {
|
||||
/^(.+?) - (.+)$/ and $packages{$1} = $2;
|
||||
}
|
||||
close FILE;
|
||||
|
||||
return %packages;
|
||||
}
|
||||
|
||||
sub dump_cyclonedxsbom_json {
|
||||
my (@components) = @_;
|
||||
|
||||
my $uuid = sprintf(
|
||||
"%04x%04x-%04x-%04x-%04x-%04x%04x%04x",
|
||||
rand(0xffff), rand(0xffff), rand(0xffff),
|
||||
rand(0x0fff) | 0x4000,
|
||||
rand(0x3fff) | 0x8000,
|
||||
rand(0xffff), rand(0xffff), rand(0xffff)
|
||||
);
|
||||
|
||||
my $cyclonedx = {
|
||||
bomFormat => "CycloneDX",
|
||||
specVersion => "1.4",
|
||||
serialNumber => "urn:uuid:$uuid",
|
||||
version => 1,
|
||||
metadata => {
|
||||
timestamp => gmtime->datetime . 'Z',
|
||||
},
|
||||
"components" => [@components],
|
||||
};
|
||||
|
||||
return encode_json($cyclonedx);
|
||||
}
|
||||
|
||||
sub gen_image_cyclonedxsbom() {
|
||||
my $pkginfo = shift @ARGV;
|
||||
my $imgmanifest = shift @ARGV;
|
||||
my @components;
|
||||
my %image_packages;
|
||||
|
||||
%image_packages = image_manifest_packages($imgmanifest);
|
||||
%image_packages or exit 1;
|
||||
parse_package_metadata($pkginfo) or exit 1;
|
||||
|
||||
$package{"kernel"} = {
|
||||
license => "GPL-2.0",
|
||||
cpe_id => "cpe:/o:linux:linux_kernel",
|
||||
name => "kernel",
|
||||
category => "operating-system",
|
||||
};
|
||||
|
||||
my %abimap;
|
||||
my @abipkgs = grep { defined $package{$_}->{abi_version} } keys %package;
|
||||
foreach my $name (@abipkgs) {
|
||||
my $pkg = $package{$name};
|
||||
my $abipkg = $name . $pkg->{abi_version};
|
||||
$abimap{$abipkg} = $name;
|
||||
}
|
||||
|
||||
foreach my $name (sort {uc($a) cmp uc($b)} keys %image_packages) {
|
||||
my $pkg = $package{$name};
|
||||
if (!$pkg) {
|
||||
$pkg = $package{$abimap{$name}};
|
||||
next if !$pkg;
|
||||
}
|
||||
|
||||
my @licenses;
|
||||
my @license = split(/\s+/, $pkg->{license});
|
||||
foreach my $lic (@license) {
|
||||
push @licenses, (
|
||||
{ "license" => { "name" => $lic } }
|
||||
);
|
||||
}
|
||||
my $type;
|
||||
if ($pkg->{category}) {
|
||||
my $category = $pkg->{category};
|
||||
my %cat_type = (
|
||||
"operating-system" => "operating-system",
|
||||
"Firmware" => "firmware",
|
||||
"Libraries" => "library"
|
||||
);
|
||||
|
||||
if ($cat_type{$category}) {
|
||||
$type = $cat_type{$category};
|
||||
} else {
|
||||
$type = "application";
|
||||
}
|
||||
}
|
||||
|
||||
my $version = $pkg->{version};
|
||||
if ($image_packages{$name}) {
|
||||
$version = $image_packages{$name};
|
||||
}
|
||||
$version =~ s/-\d+$// if $version;
|
||||
if ($name =~ /^(kernel|kmod-)/ and $version =~ /^(\d+\.\d+\.\d+)/) {
|
||||
$version = $1;
|
||||
}
|
||||
|
||||
push @components, {
|
||||
name => $pkg->{name},
|
||||
version => $version,
|
||||
@licenses > 0 ? (licenses => [ @licenses ]) : (),
|
||||
$pkg->{cpe_id} ? (cpe => $pkg->{cpe_id}.":".$version) : (),
|
||||
$type ? (type => $type) : (),
|
||||
$version ? (version => $version) : (),
|
||||
};
|
||||
}
|
||||
|
||||
print dump_cyclonedxsbom_json(@components);
|
||||
}
|
||||
|
||||
sub gen_package_cyclonedxsbom() {
|
||||
my $pkgmanifest = shift @ARGV;
|
||||
my @components;
|
||||
my %mpkgs;
|
||||
|
||||
%mpkgs = parse_package_manifest_metadata($pkgmanifest);
|
||||
%mpkgs or exit 1;
|
||||
|
||||
foreach my $name (sort {uc($a) cmp uc($b)} keys %mpkgs) {
|
||||
my $pkg = $mpkgs{$name};
|
||||
|
||||
my @licenses;
|
||||
my @license = split(/\s+/, $pkg->{license});
|
||||
foreach my $lic (@license) {
|
||||
push @licenses, (
|
||||
{ "license" => { "name" => $lic } }
|
||||
);
|
||||
}
|
||||
|
||||
my $type;
|
||||
if ($pkg->{section}) {
|
||||
my $section = $pkg->{section};
|
||||
my %section_type = (
|
||||
"firmware" => "firmware",
|
||||
"libs" => "library"
|
||||
);
|
||||
|
||||
if ($section_type{$section}) {
|
||||
$type = $section_type{$section};
|
||||
} else {
|
||||
$type = "application";
|
||||
}
|
||||
}
|
||||
|
||||
my $version = $pkg->{version};
|
||||
$version =~ s/-\d+$// if $version;
|
||||
if ($name =~ /^(kernel|kmod-)/ and $version =~ /^(\d+\.\d+\.\d+)/) {
|
||||
$version = $1;
|
||||
}
|
||||
|
||||
push @components, {
|
||||
name => $name,
|
||||
version => $version,
|
||||
@licenses > 0 ? (licenses => [ @licenses ]) : (),
|
||||
$pkg->{cpe_id} ? (cpe => $pkg->{cpe_id}.":".$version) : (),
|
||||
$type ? (type => $type) : (),
|
||||
$version ? (version => $version) : (),
|
||||
};
|
||||
}
|
||||
|
||||
print dump_cyclonedxsbom_json(@components);
|
||||
}
|
||||
|
||||
sub parse_command() {
|
||||
GetOptions("ignore=s", \@ignore);
|
||||
my $cmd = shift @ARGV;
|
||||
for ($cmd) {
|
||||
/^mk$/ and return gen_package_mk();
|
||||
/^config$/ and return gen_package_config();
|
||||
/^kconfig/ and return gen_kconfig_overrides();
|
||||
/^source$/ and return gen_package_source();
|
||||
/^pkgaux$/ and return gen_package_auxiliary();
|
||||
/^pkgmanifestjson$/ and return gen_package_manifest_json();
|
||||
/^imgcyclonedxsbom$/ and return gen_image_cyclonedxsbom();
|
||||
/^pkgcyclonedxsbom$/ and return gen_package_cyclonedxsbom();
|
||||
/^license$/ and return gen_package_license(0);
|
||||
/^licensefull$/ and return gen_package_license(1);
|
||||
/^usergroup$/ and return gen_usergroup_list();
|
||||
/^version_filter$/ and return gen_version_filtered_list();
|
||||
}
|
||||
die <<EOF
|
||||
Available Commands:
|
||||
$0 mk [file] Package metadata in makefile format
|
||||
$0 config [file] Package metadata in Kconfig format
|
||||
$0 kconfig [file] [config] [patchver] Kernel config overrides
|
||||
$0 source [file] Package source file information
|
||||
$0 pkgaux [file] Package auxiliary variables in makefile format
|
||||
$0 pkgmanifestjson [file] Package manifests in JSON format
|
||||
$0 imgcyclonedxsbom <file> [manifest] Image package manifest in CycloneDX SBOM JSON format
|
||||
$0 pkgcyclonedxsbom <file> Package manifest in CycloneDX SBOM JSON format
|
||||
$0 license [file] Package license information
|
||||
$0 licensefull [file] Package license information (full list)
|
||||
$0 usergroup [file] Package usergroup allocation list
|
||||
$0 version_filter [patchver] [list...] Filter list of version tagged strings
|
||||
|
||||
Options:
|
||||
--ignore <name> Ignore the source package <name>
|
||||
EOF
|
||||
}
|
||||
|
||||
parse_command();
|
||||
100
scripts/pad_image
Executable file
100
scripts/pad_image
Executable file
@@ -0,0 +1,100 @@
|
||||
#!/usr/bin/env bash
|
||||
|
||||
function usage {
|
||||
echo "Usage: prepare_image image_type kernel_image rootfs_image header_size"
|
||||
echo "Pad root and kernel image to the correct size and append the jffs2 start marker as needed"
|
||||
exit 1
|
||||
}
|
||||
|
||||
function pad_file {
|
||||
echo "Padding $1 to size $2"
|
||||
dd if=$1 of=$1.paddingtempfile bs=$2 count=1 conv=sync &> /dev/null
|
||||
mv $1.paddingtempfile $1
|
||||
}
|
||||
|
||||
#filesize filestart padding
|
||||
function calc_pad {
|
||||
[ $((($1 + $2) & ($3 - 1))) == 0 ] && {
|
||||
echo $1
|
||||
return 0
|
||||
}
|
||||
echo $(((($1 + $2) | ($3 - 1)) + 1 - $2))
|
||||
}
|
||||
|
||||
function prep_squash {
|
||||
echo "kernel_size: $kernel_size"
|
||||
echo "header_size: $header_size"
|
||||
kernel_pad_size=$(calc_pad $kernel_size $header_size 32)
|
||||
kernel_end=$(($header_size + $kernel_pad_size))
|
||||
pad_file $kernel_image $kernel_pad_size
|
||||
|
||||
#4k
|
||||
rootfs_pad_size=$(calc_pad $rootfs_size $kernel_end 4096)
|
||||
pad_file $rootfs_image $rootfs_pad_size
|
||||
echo -ne '\xde\xad\xc0\xde' >> $rootfs_image
|
||||
|
||||
#8k
|
||||
rootfs_pad_size=$(calc_pad $rootfs_size $kernel_end 8192)
|
||||
[ $rootfs_pad_size == rootfs_old_padsize ] || {
|
||||
pad_file $rootfs_image $rootfs_pad_size
|
||||
rootfs_old_padsize=$rootfs_pad_size
|
||||
echo -ne '\xde\xad\xc0\xde' >> $rootfs_image
|
||||
}
|
||||
|
||||
#64k
|
||||
rootfs_pad_size=$(calc_pad $rootfs_size $kernel_end 65536)
|
||||
[ $rootfs_pad_size == rootfs_old_padsize ] || {
|
||||
pad_file $rootfs_image $rootfs_pad_size
|
||||
rootfs_old_padsize=$rootfs_pad_size
|
||||
echo -ne '\xde\xad\xc0\xde' >> $rootfs_image
|
||||
}
|
||||
|
||||
#128k
|
||||
rootfs_pad_size=$(calc_pad $rootfs_size $kernel_end 131072)
|
||||
[ $rootfs_pad_size == rootfs_old_padsize ] || {
|
||||
pad_file $rootfs_image $rootfs_pad_size
|
||||
rootfs_old_padsize=$rootfs_pad_size
|
||||
echo -ne '\xde\xad\xc0\xde' >> $rootfs_image
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function prep_jffs2 {
|
||||
kernel_pad_size=$(calc_pad $kernel_size $header_size $1)
|
||||
pad_file $kernel_image $kernel_pad_size
|
||||
}
|
||||
|
||||
image_type=$1
|
||||
kernel_image=$2
|
||||
rootfs_image=$3
|
||||
header_size=$4
|
||||
|
||||
if [ -z "$image_type" ] || [ -z "$rootfs_image" ] || [ -z "$kernel_image" ] || [ -z "$header_size" ]; then
|
||||
usage
|
||||
fi
|
||||
|
||||
if [ ! -e "$rootfs_image" ] || [ -z "$kernel_image" ]; then
|
||||
echo "input file not found"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
kernel_size=$(stat -c "%s" "$kernel_image")
|
||||
rootfs_size=$(stat -c "%s" "$rootfs_image")
|
||||
|
||||
if [ $kernel_size == 0 ] || [ $rootfs_size == 0 ]; then
|
||||
echo "kernel or rootfs empty"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
case $image_type in
|
||||
squashfs )
|
||||
prep_squash ;;
|
||||
jffs2-64k )
|
||||
prep_jffs2 65536 ;;
|
||||
jffs2-128k )
|
||||
prep_jffs2 131072 ;;
|
||||
* )
|
||||
echo "Unknown image type"
|
||||
exit 1 ;;
|
||||
esac
|
||||
|
||||
54
scripts/patch-kernel.sh
Executable file
54
scripts/patch-kernel.sh
Executable file
@@ -0,0 +1,54 @@
|
||||
#! /bin/sh
|
||||
# A little script I whipped up to make it easy to
|
||||
# patch source trees and have sane error handling
|
||||
# -Erik
|
||||
#
|
||||
# (c) 2002 Erik Andersen <andersen@codepoet.org>
|
||||
|
||||
# Set directories from arguments, or use defaults.
|
||||
targetdir=${1-.}
|
||||
patchdir=${2-../kernel-patches}
|
||||
patchpattern=${3-*}
|
||||
|
||||
if [ ! -d "${targetdir}" ] ; then
|
||||
echo "Aborting. '${targetdir}' is not a directory."
|
||||
exit 1
|
||||
fi
|
||||
if [ ! -d "${patchdir}" ] ; then
|
||||
echo "Aborting. '${patchdir}' is not a directory."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
for i in ${patchdir}/${patchpattern} ; do
|
||||
case "$i" in
|
||||
*.gz)
|
||||
type="gzip"; uncomp="gunzip -dc"; ;;
|
||||
*.bz)
|
||||
type="bzip"; uncomp="bunzip -dc"; ;;
|
||||
*.bz2)
|
||||
type="bzip2"; uncomp="bunzip2 -dc"; ;;
|
||||
*.zip)
|
||||
type="zip"; uncomp="unzip -d"; ;;
|
||||
*.Z)
|
||||
type="compress"; uncomp="uncompress -c"; ;;
|
||||
*)
|
||||
type="plaintext"; uncomp="cat"; ;;
|
||||
esac
|
||||
[ -d "${i}" ] && echo "Ignoring subdirectory ${i}" && continue
|
||||
echo ""
|
||||
echo "Applying ${i} using ${type}: "
|
||||
${uncomp} ${i} | ${PATCH:-patch} -f -p1 -d ${targetdir}
|
||||
if [ $? != 0 ] ; then
|
||||
echo "Patch failed! Please fix $i!"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
# Check for rejects...
|
||||
if [ "`find $targetdir/ '(' -name '*.rej' -o -name '.*.rej' ')' -print`" ] ; then
|
||||
echo "Aborting. Reject files found."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Remove backup files
|
||||
find $targetdir/ '(' -name '*.orig' -o -name '.*.orig' ')' -exec rm -f {} \;
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user