aboutsummaryrefslogtreecommitdiff
path: root/package/utils
diff options
context:
space:
mode:
Diffstat (limited to 'package/utils')
-rw-r--r--package/utils/adb/Makefile14
-rw-r--r--package/utils/adb/patches/001-create_Makefile.patch2
-rw-r--r--package/utils/adb/patches/010-mbedtls.patch342
-rw-r--r--package/utils/adb/patches/010-openssl-1.1.patch28
-rw-r--r--package/utils/audit/Makefile15
-rw-r--r--package/utils/bcm27xx-utils/Makefile8
-rw-r--r--package/utils/bcm27xx-utils/patches/0001-raspinfo-adapt-to-OpenWrt.patch9
-rw-r--r--package/utils/bcm27xx-utils/patches/0002-cmake-disable-piolib.patch24
-rw-r--r--package/utils/busybox/Config-defaults.in24
-rw-r--r--package/utils/busybox/Makefile25
-rw-r--r--package/utils/busybox/config/Config.in11
-rw-r--r--package/utils/busybox/config/archival/Config.in12
-rw-r--r--package/utils/busybox/config/console-tools/Config.in30
-rw-r--r--package/utils/busybox/config/coreutils/Config.in187
-rw-r--r--package/utils/busybox/config/debianutils/Config.in6
-rw-r--r--package/utils/busybox/config/e2fsprogs/Config.in6
-rw-r--r--package/utils/busybox/config/editors/Config.in12
-rw-r--r--package/utils/busybox/config/findutils/Config.in17
-rw-r--r--package/utils/busybox/config/init/Config.in6
-rw-r--r--package/utils/busybox/config/klibc-utils/Config.in4
-rw-r--r--package/utils/busybox/config/loginutils/Config.in26
-rw-r--r--package/utils/busybox/config/mailutils/Config.in6
-rw-r--r--package/utils/busybox/config/miscutils/Config.in89
-rw-r--r--package/utils/busybox/config/modutils/Config.in6
-rw-r--r--package/utils/busybox/config/networking/Config.in64
-rw-r--r--package/utils/busybox/config/networking/udhcp/Config.in11
-rw-r--r--package/utils/busybox/config/printutils/Config.in6
-rw-r--r--package/utils/busybox/config/procps/Config.in42
-rw-r--r--package/utils/busybox/config/runit/Config.in20
-rw-r--r--package/utils/busybox/config/shell/Config.in11
-rw-r--r--package/utils/busybox/config/sysklogd/Config.in8
-rw-r--r--package/utils/busybox/config/util-linux/Config.in117
-rw-r--r--package/utils/busybox/files/busybox-history-file.sh2
-rwxr-xr-xpackage/utils/busybox/files/cron2
-rw-r--r--package/utils/busybox/patches/001-fix-non-x86-build.patch12
-rw-r--r--package/utils/busybox/patches/002-upstream-fix-hexdump.patch49
-rw-r--r--package/utils/busybox/patches/200-udhcpc_reduce_msgs.patch4
-rw-r--r--package/utils/busybox/patches/201-udhcpc_changed_ifindex.patch2
-rw-r--r--package/utils/busybox/patches/301-ip-link-fix-netlink-msg-size.patch2
-rw-r--r--package/utils/busybox/patches/530-nslookup-ensure-unique-transaction-IDs-for-the-DNS-queries.patch2
-rw-r--r--package/utils/busybox/patches/600-loginutils-login.c-libselinux-get_default_context-ex.patch51
-rw-r--r--package/utils/checkpolicy/Makefile4
-rw-r--r--package/utils/cli/Makefile35
-rw-r--r--package/utils/cli/docs/MODULE-API.md364
-rwxr-xr-xpackage/utils/cli/files/usr/sbin/cli750
-rw-r--r--package/utils/cli/files/usr/share/ucode/cli/cache.uc60
-rw-r--r--package/utils/cli/files/usr/share/ucode/cli/color.uc65
-rw-r--r--package/utils/cli/files/usr/share/ucode/cli/context-call.uc126
-rw-r--r--package/utils/cli/files/usr/share/ucode/cli/context.uc708
-rw-r--r--package/utils/cli/files/usr/share/ucode/cli/datamodel.uc175
-rw-r--r--package/utils/cli/files/usr/share/ucode/cli/modules/network.uc138
-rw-r--r--package/utils/cli/files/usr/share/ucode/cli/modules/service.uc174
-rw-r--r--package/utils/cli/files/usr/share/ucode/cli/object-editor.uc660
-rw-r--r--package/utils/cli/files/usr/share/ucode/cli/types.uc195
-rw-r--r--package/utils/cli/files/usr/share/ucode/cli/utils.uc26
-rw-r--r--package/utils/ct-bugcheck/Makefile2
-rw-r--r--package/utils/debugcc/Makefile1
-rw-r--r--package/utils/dns320l-mcu/Makefile38
-rw-r--r--package/utils/dns320l-mcu/files/dns320l-mcu.init14
-rw-r--r--package/utils/dtc/Makefile9
-rw-r--r--package/utils/dtc/patches/010-both-libraries.patch48
-rw-r--r--package/utils/e2fsprogs/Makefile26
-rw-r--r--package/utils/f2fs-tools/Makefile3
-rw-r--r--package/utils/f2fs-tools/patches/100-f2fs-tools-use-stdbool.h-instead-of-bool.patch33
-rw-r--r--package/utils/fbtest/src/fbtest.c16
-rw-r--r--package/utils/firmware-utils/Makefile7
-rw-r--r--package/utils/fitblk/Makefile6
-rw-r--r--package/utils/fitblk/files/fit.sh73
-rw-r--r--package/utils/fritz-tools/Makefile2
-rw-r--r--package/utils/fritz-tools/src/fritz_cal_extract.c250
-rw-r--r--package/utils/jsonfilter/Makefile6
-rw-r--r--package/utils/mdadm/Makefile4
-rw-r--r--package/utils/mdadm/patches/010-falloc.patch36
-rw-r--r--package/utils/mdadm/patches/020-basename.patch30
-rw-r--r--package/utils/mdadm/patches/030-fix-monitor-tv_sec.patch14
-rw-r--r--package/utils/mdadm/patches/040-udev.patch26
-rw-r--r--package/utils/mdadm/patches/050-pie.patch33
-rw-r--r--package/utils/mdadm/patches/060-gcc14.patch24
-rw-r--r--package/utils/mdadm/patches/100-cross_compile.patch2
-rw-r--r--package/utils/mdadm/patches/200-reduce_size.patch4
-rw-r--r--package/utils/mtd-utils/Makefile25
-rw-r--r--package/utils/mtd-utils/files/ubihealthd.defaults18
-rw-r--r--package/utils/mtd-utils/files/ubihealthd.init27
-rw-r--r--package/utils/mtd-utils/patches/100-fix_includes.patch10
-rw-r--r--package/utils/mtd-utils/patches/130-lzma_jffs2.patch5038
-rw-r--r--package/utils/nilfs-utils/Makefile204
-rw-r--r--package/utils/omnia-eeprom/Makefile52
-rw-r--r--package/utils/omnia-mcutool/Makefile54
-rw-r--r--package/utils/policycoreutils/Makefile10
-rw-r--r--package/utils/policycoreutils/patches/0001-policycoreutils-run_init-define-_GNU_SOURCE.patch29
-rw-r--r--package/utils/provision/Makefile33
-rwxr-xr-xpackage/utils/provision/files/usr/sbin/provision107
-rw-r--r--package/utils/provision/files/usr/share/ucode/provision.uc189
-rw-r--r--package/utils/px5g-mbedtls/Makefile2
-rw-r--r--package/utils/px5g-mbedtls/px5g-mbedtls.c69
-rw-r--r--package/utils/secilc/Makefile5
-rw-r--r--package/utils/spidev_test/Makefile7
-rw-r--r--package/utils/ucode-mod-bpf/src/bpf.c66
-rw-r--r--package/utils/ucode-mod-pkgen/Makefile44
-rwxr-xr-xpackage/utils/ucode-mod-pkgen/files/pkgen252
-rw-r--r--package/utils/ucode-mod-pkgen/src/CMakeLists.txt35
-rw-r--r--package/utils/ucode-mod-pkgen/src/pk.h45
-rw-r--r--package/utils/ucode-mod-pkgen/src/pkcs12.c612
-rw-r--r--package/utils/ucode-mod-pkgen/src/ucode.c598
-rw-r--r--package/utils/ucode-mod-uline/Makefile32
-rw-r--r--package/utils/ucode-mod-uline/src/CMakeLists.txt44
-rw-r--r--package/utils/ucode-mod-uline/src/private.h206
-rw-r--r--package/utils/ucode-mod-uline/src/ucode.c908
-rw-r--r--package/utils/ucode-mod-uline/src/uline.c945
-rw-r--r--package/utils/ucode-mod-uline/src/uline.h152
-rw-r--r--package/utils/ucode-mod-uline/src/utf8.c340
-rw-r--r--package/utils/ucode-mod-uline/src/vt100.c108
-rw-r--r--package/utils/ucode/Makefile31
-rw-r--r--package/utils/ucode/patches/100-ubus-fix-uc_ubus_have_uloop-for-eloop-uloop-combinat.patch26
-rw-r--r--package/utils/ucode/patches/100-ucode-add-padding-to-uc_resource_ext_t.patch21
-rw-r--r--package/utils/usbgadget/Makefile12
-rw-r--r--package/utils/usbgadget/files/usbgadget.init9
-rw-r--r--package/utils/usbmode/data/3426-1f014
-rw-r--r--package/utils/util-linux/Makefile162
-rw-r--r--package/utils/util-linux/patches/001-meson-properly-handle-gettext-non-existence.patch28
-rw-r--r--package/utils/yafut/Makefile7
-rw-r--r--package/utils/zyxel-bootconfig/Makefile4
-rw-r--r--package/utils/zyxel-bootconfig/files/95_apply_bootconfig2
123 files changed, 10299 insertions, 5774 deletions
diff --git a/package/utils/adb/Makefile b/package/utils/adb/Makefile
index c207c333b2..ced887e968 100644
--- a/package/utils/adb/Makefile
+++ b/package/utils/adb/Makefile
@@ -2,16 +2,14 @@ include $(TOPDIR)/rules.mk
#Based on adb package from AUR https://aur.archlinux.org/packages/adb/ , reused Makefile
PKG_NAME:=adb
-PKG_VERSION:=android.5.0.2_r1
-PKG_RELEASE:=3
+PKG_SOURCE_VERSION:=6fe92d1a3fb17545d82d020a3c995f32e6b71f9d
+PKG_VERSION:=5.0.2~$(call version_abbrev,$(PKG_SOURCE_VERSION))
+PKG_RELEASE:=4
PKG_SOURCE_PROTO:=git
PKG_SOURCE_URL:=https://android.googlesource.com/platform/system/core
-PKG_SOURCE_VERSION:=6fe92d1a3fb17545d82d020a3c995f32e6b71f9d
-PKG_MIRROR_HASH:=a9b4b86602dfc0d4fc9e1d0f78dc83e648a931fb04f5a4be9b1f0054a8cebf7e
-PKG_SOURCE_SUBDIR:=$(PKG_NAME)-$(PKG_SOURCE_VERSION)
-PKG_SOURCE:=$(PKG_SOURCE_SUBDIR).tar.xz
-PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_SOURCE_SUBDIR)
+PKG_MIRROR_HASH:=2ff96b4342cd05f475083207a4927635548c6693771c12a24cfa99f175fdb10a
+
PKG_MAINTAINER:=Henryk Heisig <hyniu@o2.pl>
PKG_CPE_ID:=cpe:/a:google:android_debug_bridge
@@ -27,7 +25,7 @@ define Package/adb
CATEGORY:=Utilities
TITLE:=Android Debug Bridge CLI tool
URL:=http://tools.android.com/
- DEPENDS:=+zlib +libopenssl +libpthread
+ DEPENDS:=+zlib +libmbedtls +libpthread
endef
define Package/adb/description
diff --git a/package/utils/adb/patches/001-create_Makefile.patch b/package/utils/adb/patches/001-create_Makefile.patch
index d7fa00cb4c..80db5e7f1a 100644
--- a/package/utils/adb/patches/001-create_Makefile.patch
+++ b/package/utils/adb/patches/001-create_Makefile.patch
@@ -35,7 +35,7 @@
+CPPFLAGS+= -I../include
+CPPFLAGS+= -D_FILE_OFFSET_BITS=64
+
-+LIBS+= -lcrypto -lpthread -lz
++LIBS+= -lmbedcrypto -lpthread -lz
+
+OBJS= $(SRCS:.c=.o)
+
diff --git a/package/utils/adb/patches/010-mbedtls.patch b/package/utils/adb/patches/010-mbedtls.patch
new file mode 100644
index 0000000000..084df20c08
--- /dev/null
+++ b/package/utils/adb/patches/010-mbedtls.patch
@@ -0,0 +1,342 @@
+--- a/adb/adb_auth_host.c
++++ b/adb/adb_auth_host.c
+@@ -39,15 +39,13 @@
+
+ #include <cutils/list.h>
+
+-#include <openssl/evp.h>
+-#include <openssl/objects.h>
+-#include <openssl/pem.h>
+-#include <openssl/rsa.h>
+-#include <openssl/sha.h>
+-
+-#if defined(OPENSSL_IS_BORINGSSL)
+-#include <openssl/base64.h>
+-#endif
++#include <mbedtls/rsa.h>
++#include <mbedtls/sha1.h>
++#include <mbedtls/base64.h>
++#include <mbedtls/entropy.h>
++#include <mbedtls/ctr_drbg.h>
++#include <mbedtls/pk.h>
++#include <mbedtls/pem.h>
+
+ #define TRACE_TAG TRACE_AUTH
+
+@@ -57,56 +55,92 @@
+
+ struct adb_private_key {
+ struct listnode node;
+- RSA *rsa;
++ mbedtls_pk_context pk;
+ };
+
+ static struct listnode key_list;
++static mbedtls_ctr_drbg_context ctr_drbg;
+
+
+ /* Convert OpenSSL RSA private key to android pre-computed RSAPublicKey format */
+-static int RSA_to_RSAPublicKey(RSA *rsa, RSAPublicKey *pkey)
++static int RSA_to_RSAPublicKey(mbedtls_pk_context *pk, RSAPublicKey *pkey)
+ {
+ int ret = 1;
+ unsigned int i;
++ mbedtls_mpi r32, rr, r, rem, n, n0inv, e, tmp;
++ mbedtls_rsa_context *rsa;
++ unsigned char buf[sizeof(uint32_t)];
++
++ if (mbedtls_pk_get_type(pk) != MBEDTLS_PK_RSA) {
++ return 0;
++ }
+
+- BN_CTX* ctx = BN_CTX_new();
+- BIGNUM* r32 = BN_new();
+- BIGNUM* rr = BN_new();
+- BIGNUM* r = BN_new();
+- BIGNUM* rem = BN_new();
+- BIGNUM* n = BN_new();
+- BIGNUM* n0inv = BN_new();
++ rsa = mbedtls_pk_rsa(*pk);
++ if (!rsa) {
++ return 0;
++ }
+
+- if (RSA_size(rsa) != RSANUMBYTES) {
++ mbedtls_mpi_init(&r32);
++ mbedtls_mpi_init(&rr);
++ mbedtls_mpi_init(&r);
++ mbedtls_mpi_init(&rem);
++ mbedtls_mpi_init(&n);
++ mbedtls_mpi_init(&n0inv);
++ mbedtls_mpi_init(&e);
++ mbedtls_mpi_init(&tmp);
++
++ if (mbedtls_rsa_get_len(rsa) != RSANUMBYTES) {
+ ret = 0;
+ goto out;
+ }
+
+- BN_set_bit(r32, 32);
+- BN_copy(n, rsa->n);
+- BN_set_bit(r, RSANUMWORDS * 32);
+- BN_mod_sqr(rr, r, n, ctx);
+- BN_div(NULL, rem, n, r32, ctx);
+- BN_mod_inverse(n0inv, rem, r32, ctx);
++ mbedtls_rsa_export(rsa, &n, NULL, NULL, NULL, &e);
++
++ mbedtls_mpi_lset(&r32, 1);
++ mbedtls_mpi_shift_l(&r32, 32);
++ mbedtls_mpi_lset(&r, 1);
++ mbedtls_mpi_shift_l(&r, RSANUMWORDS * 32);
++ mbedtls_mpi_mul_mpi(&rr, &r, &r);
++ mbedtls_mpi_mod_mpi(&rr, &rr, &n);
++ mbedtls_mpi_div_mpi(NULL, &rem, &n, &r32);
++ mbedtls_mpi_inv_mod(&n0inv, &rem, &r32);
+
+ pkey->len = RSANUMWORDS;
+- pkey->n0inv = 0 - BN_get_word(n0inv);
++
++ mbedtls_mpi_write_binary(&n0inv, buf, sizeof(buf));
++ uint32_t n0inv_val = ((buf[0] << 24) | (buf[1] << 16) |
++ (buf[2] << 8) | buf[3]);
++ pkey->n0inv = 0 - n0inv_val;
++
+ for (i = 0; i < RSANUMWORDS; i++) {
+- BN_div(rr, rem, rr, r32, ctx);
+- pkey->rr[i] = BN_get_word(rem);
+- BN_div(n, rem, n, r32, ctx);
+- pkey->n[i] = BN_get_word(rem);
++ mbedtls_mpi_div_mpi(&tmp, &rem, &rr, &r32);
++ mbedtls_mpi_copy(&rr, &tmp);
++
++ mbedtls_mpi_write_binary(&rem, buf, sizeof(buf));
++ pkey->rr[i] = ((buf[0] << 24) | (buf[1] << 16) |
++ (buf[2] << 8) | buf[3]);
++
++ mbedtls_mpi_div_mpi(&tmp, &rem, &n, &r32);
++ mbedtls_mpi_copy(&n, &tmp);
++
++ mbedtls_mpi_write_binary(&rem, buf, sizeof(buf));
++ pkey->n[i] = ((buf[0] << 24) | (buf[1] << 16) |
++ (buf[2] << 8) | buf[3]);
+ }
+- pkey->exponent = BN_get_word(rsa->e);
++
++ mbedtls_mpi_write_binary(&e, buf, sizeof(buf));
++ pkey->exponent = ((buf[0] << 24) | (buf[1] << 16) |
++ (buf[2] << 8) | buf[3]);
+
+ out:
+- BN_free(n0inv);
+- BN_free(n);
+- BN_free(rem);
+- BN_free(r);
+- BN_free(rr);
+- BN_free(r32);
+- BN_CTX_free(ctx);
++ mbedtls_mpi_free(&tmp);
++ mbedtls_mpi_free(&e);
++ mbedtls_mpi_free(&n0inv);
++ mbedtls_mpi_free(&n);
++ mbedtls_mpi_free(&rem);
++ mbedtls_mpi_free(&r);
++ mbedtls_mpi_free(&rr);
++ mbedtls_mpi_free(&r32);
+
+ return ret;
+ }
+@@ -133,7 +167,7 @@ static void get_user_info(char *buf, siz
+ buf[len - 1] = '\0';
+ }
+
+-static int write_public_keyfile(RSA *private_key, const char *private_key_path)
++static int write_public_keyfile(mbedtls_pk_context *private_key, const char *private_key_path)
+ {
+ RSAPublicKey pkey;
+ FILE *outfile = NULL;
+@@ -161,16 +195,7 @@ static int write_public_keyfile(RSA *pri
+
+ D("Writing public key to '%s'\n", path);
+
+-#if defined(OPENSSL_IS_BORINGSSL)
+- if (!EVP_EncodedLength(&encoded_length, sizeof(pkey))) {
+- D("Public key too large to base64 encode");
+- goto out;
+- }
+-#else
+- /* While we switch from OpenSSL to BoringSSL we have to implement
+- * |EVP_EncodedLength| here. */
+ encoded_length = 1 + ((sizeof(pkey) + 2) / 3 * 4);
+-#endif
+
+ encoded = malloc(encoded_length);
+ if (encoded == NULL) {
+@@ -178,7 +203,12 @@ static int write_public_keyfile(RSA *pri
+ goto out;
+ }
+
+- encoded_length = EVP_EncodeBlock(encoded, (uint8_t*) &pkey, sizeof(pkey));
++ if (mbedtls_base64_encode(encoded, encoded_length, &encoded_length,
++ (unsigned char*)&pkey, sizeof(pkey)) != 0) {
++ D("Base64 encoding failed");
++ goto out;
++ }
++
+ get_user_info(info, sizeof(info));
+
+ if (fwrite(encoded, encoded_length, 1, outfile) != 1 ||
+@@ -201,23 +231,25 @@ static int write_public_keyfile(RSA *pri
+
+ static int generate_key(const char *file)
+ {
+- EVP_PKEY* pkey = EVP_PKEY_new();
+- BIGNUM* exponent = BN_new();
+- RSA* rsa = RSA_new();
++ mbedtls_pk_context pk;
+ mode_t old_mask;
+ FILE *f = NULL;
+ int ret = 0;
+
+ D("generate_key '%s'\n", file);
+
+- if (!pkey || !exponent || !rsa) {
+- D("Failed to allocate key\n");
++ mbedtls_pk_init(&pk);
++
++ if (mbedtls_pk_setup(&pk, mbedtls_pk_info_from_type(MBEDTLS_PK_RSA)) != 0) {
++ D("Failed to setup key context\n");
+ goto out;
+ }
+
+- BN_set_word(exponent, RSA_F4);
+- RSA_generate_key_ex(rsa, 2048, exponent, NULL);
+- EVP_PKEY_set1_RSA(pkey, rsa);
++ if (mbedtls_rsa_gen_key(mbedtls_pk_rsa(pk), mbedtls_ctr_drbg_random, &ctr_drbg,
++ 2048, 65537) != 0) {
++ D("Failed to generate key\n");
++ goto out;
++ }
+
+ old_mask = umask(077);
+
+@@ -230,12 +262,20 @@ static int generate_key(const char *file
+
+ umask(old_mask);
+
+- if (!PEM_write_PrivateKey(f, pkey, NULL, NULL, 0, NULL, NULL)) {
+- D("Failed to write key\n");
++ unsigned char buf[16000];
++ size_t len;
++
++ if (mbedtls_pk_write_key_pem(&pk, buf, sizeof(buf)) != 0) {
++ D("Failed to write key to buffer\n");
+ goto out;
+ }
+
+- if (!write_public_keyfile(rsa, file)) {
++ if (fwrite(buf, strlen((char*)buf), 1, f) != 1) {
++ D("Failed to write buffer to file\n");
++ goto out;
++ }
++
++ if (!write_public_keyfile(&pk, file)) {
+ D("Failed to write public key\n");
+ goto out;
+ }
+@@ -243,44 +283,32 @@ static int generate_key(const char *file
+ ret = 1;
+
+ out:
+- if (f)
++ if (f) {
+ fclose(f);
+- EVP_PKEY_free(pkey);
+- RSA_free(rsa);
+- BN_free(exponent);
++ }
++ mbedtls_pk_free(&pk);
+ return ret;
+ }
+
+ static int read_key(const char *file, struct listnode *list)
+ {
+ struct adb_private_key *key;
+- FILE *f;
+-
+- D("read_key '%s'\n", file);
+-
+- f = fopen(file, "r");
+- if (!f) {
+- D("Failed to open '%s'\n", file);
+- return 0;
+- }
+
+ key = malloc(sizeof(*key));
+ if (!key) {
+ D("Failed to alloc key\n");
+- fclose(f);
+ return 0;
+ }
+- key->rsa = RSA_new();
+
+- if (!PEM_read_RSAPrivateKey(f, &key->rsa, NULL, NULL)) {
++ mbedtls_pk_init(&key->pk);
++
++ if (mbedtls_pk_parse_keyfile(&key->pk, file, NULL, mbedtls_ctr_drbg_random, &ctr_drbg) != 0) {
+ D("Failed to read key\n");
+- fclose(f);
+- RSA_free(key->rsa);
++ mbedtls_pk_free(&key->pk);
+ free(key);
+ return 0;
+ }
+
+- fclose(f);
+ list_add_tail(list, &key->node);
+ return 1;
+ }
+@@ -373,15 +401,20 @@ static void get_vendor_keys(struct listn
+
+ int adb_auth_sign(void *node, void *token, size_t token_size, void *sig)
+ {
+- unsigned int len;
+ struct adb_private_key *key = node_to_item(node, struct adb_private_key, node);
++ unsigned char hash[20];
++ size_t sig_len;
++
++ mbedtls_sha1((unsigned char*)token, token_size, hash);
+
+- if (!RSA_sign(NID_sha1, token, token_size, sig, &len, key->rsa)) {
++ if (mbedtls_pk_sign(&key->pk, MBEDTLS_MD_SHA1, hash, sizeof(hash),
++ sig, mbedtls_pk_get_len(&key->pk), &sig_len,
++ mbedtls_ctr_drbg_random, &ctr_drbg) != 0) {
+ return 0;
+ }
+
+- D("adb_auth_sign len=%d\n", len);
+- return (int)len;
++ D("adb_auth_sign len=%d\n", (int)sig_len);
++ return (int)sig_len;
+ }
+
+ void *adb_auth_nextkey(void *current)
+@@ -439,10 +472,19 @@ int adb_auth_get_userkey(unsigned char *
+ void adb_auth_init(void)
+ {
+ int ret;
++ mbedtls_entropy_context entropy;
+
+ D("adb_auth_init\n");
+
+ list_init(&key_list);
++ mbedtls_entropy_init(&entropy);
++ mbedtls_ctr_drbg_init(&ctr_drbg);
++
++ if (mbedtls_ctr_drbg_seed(&ctr_drbg, mbedtls_entropy_func, &entropy,
++ (const unsigned char *)"adb_auth", 8) != 0) {
++ D("Failed to seed RNG\n");
++ return;
++ }
+
+ ret = get_user_key(&key_list);
+ if (!ret) {
diff --git a/package/utils/adb/patches/010-openssl-1.1.patch b/package/utils/adb/patches/010-openssl-1.1.patch
deleted file mode 100644
index e4df372a34..0000000000
--- a/package/utils/adb/patches/010-openssl-1.1.patch
+++ /dev/null
@@ -1,28 +0,0 @@
---- a/adb/adb_auth_host.c
-+++ b/adb/adb_auth_host.c
-@@ -83,7 +83,13 @@ static int RSA_to_RSAPublicKey(RSA *rsa,
- }
-
- BN_set_bit(r32, 32);
-+#if OPENSSL_VERSION_NUMBER >= 0x10100000L
-+ const BIGNUM *rsa_n, *rsa_e;
-+ RSA_get0_key(rsa, &rsa_n, &rsa_e, NULL);
-+ BN_copy(n, rsa_n);
-+#else
- BN_copy(n, rsa->n);
-+#endif
- BN_set_bit(r, RSANUMWORDS * 32);
- BN_mod_sqr(rr, r, n, ctx);
- BN_div(NULL, rem, n, r32, ctx);
-@@ -97,7 +103,11 @@ static int RSA_to_RSAPublicKey(RSA *rsa,
- BN_div(n, rem, n, r32, ctx);
- pkey->n[i] = BN_get_word(rem);
- }
-+#if OPENSSL_VERSION_NUMBER >= 0x10100000L
-+ pkey->exponent = BN_get_word(rsa_e);
-+#else
- pkey->exponent = BN_get_word(rsa->e);
-+#endif
-
- out:
- BN_free(n0inv);
diff --git a/package/utils/audit/Makefile b/package/utils/audit/Makefile
index e36e3ebd53..58fc8ef4e9 100644
--- a/package/utils/audit/Makefile
+++ b/package/utils/audit/Makefile
@@ -6,14 +6,17 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=audit-userspace
-PKG_VERSION:=3.1.4
+PKG_VERSION:=3.1.5
PKG_RELEASE:=1
-PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
-PKG_SOURCE_URL:=https://github.com/linux-audit/audit-userspace/archive/refs/tags/v$(PKG_VERSION).tar.gz?
-PKG_HASH:=aec501760acd13ebbe00e78b9b59f795d16a430b1d673628e346cd18905c594b
+
+PKG_SOURCE_PROTO:=git
+PKG_SOURCE_URL:=https://github.com/linux-audit/$(PKG_NAME).git
+PKG_SOURCE_VERSION:=v$(PKG_VERSION)
+PKG_MIRROR_HASH:=60f479476d4f1b0beadbe3e516ea67490d38ca9b01db53e56f52b1731340d5bb
+
PKG_MAINTAINER:=Thomas Petazzoni <thomas.petazzoni@bootlin.com>
-PKG_LICENSE:=GPL-2.0-or-later
-PKG_LICENSE_FILES:=COPYING
+PKG_LICENSE:=GPL-2.0-or-later LGPL-2.1-or-later
+PKG_LICENSE_FILES:=COPYING COPYING.LIB
PKG_CPE_ID:=cpe:/a:linux_audit_project:linux_audit
PKG_CONFIG_DEPENDS:=CONFIG_KERNEL_IO_URING
diff --git a/package/utils/bcm27xx-utils/Makefile b/package/utils/bcm27xx-utils/Makefile
index 9e113914e7..f1aafd666a 100644
--- a/package/utils/bcm27xx-utils/Makefile
+++ b/package/utils/bcm27xx-utils/Makefile
@@ -3,13 +3,13 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=bcm27xx-utils
-PKG_VERSION:=2024-01-18
+PKG_VERSION:=2025.03.14
PKG_RELEASE:=1
PKG_SOURCE_PROTO:=git
PKG_SOURCE_URL:=https://github.com/raspberrypi/utils.git
-PKG_SOURCE_VERSION:=e65f5ec102e74218cda7da9fdc8b1caa0fd1127d
-PKG_MIRROR_HASH:=ea946fc4a86875c5d1efc35b2bc80f6b5482afc3d1ea13853b69abc2b4a2eee6
+PKG_SOURCE_VERSION:=685afa8c0d6f2310eaefe1b528627a8bf3154ca0
+PKG_MIRROR_HASH:=04528742fc5b55ba31f448a27588a9df707cbcc27823c1d219b7f877dd4ac200
PKG_FLAGS:=nonshared
PKG_BUILD_FLAGS:=no-lto
@@ -46,6 +46,8 @@ define Package/bcm27xx-utils/install
$(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/bin/eepflash.sh $(1)/usr/bin
$(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/bin/eepmake $(1)/usr/bin
+ $(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/bin/kdtc $(1)/usr/bin
+
$(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/bin/otpset $(1)/usr/bin
$(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/bin/overlaycheck $(1)/usr/bin
diff --git a/package/utils/bcm27xx-utils/patches/0001-raspinfo-adapt-to-OpenWrt.patch b/package/utils/bcm27xx-utils/patches/0001-raspinfo-adapt-to-OpenWrt.patch
index 9dd6d99626..26f8f6e4e3 100644
--- a/package/utils/bcm27xx-utils/patches/0001-raspinfo-adapt-to-OpenWrt.patch
+++ b/package/utils/bcm27xx-utils/patches/0001-raspinfo-adapt-to-OpenWrt.patch
@@ -242,6 +242,15 @@ Signed-off-by: Álvaro Fernández Rojas <noltari@gmail.com>
else
echo "vcdbg not found"
fi
+@@ -275,7 +111,7 @@ echo "dmesg log"
+ echo "---------"
+ echo
+
+-sudo dmesg | sed -e "s/\([0-9a-fA-F]\{1,4\}:\)\{7,7\}[0-9a-fA-F]\{1,4\}/y.y.y.y.y.y.y.y/g" | sed -e "s/[0-9a-fA-F]\{1,4\}:\(:[0-9a-fA-F]\{1,4\}\)\{1,4\}/y::y.y.y.y/g" | sed -e "s/\([0-9a-fA-F]\{2,2\}\:\)\{5,5\}[0-9a-fA-F]\{2,2\}/m.m.m.m/g"
++dmesg | sed -e "s/\([0-9a-fA-F]\{1,4\}:\)\{7,7\}[0-9a-fA-F]\{1,4\}/y.y.y.y.y.y.y.y/g" | sed -e "s/[0-9a-fA-F]\{1,4\}:\(:[0-9a-fA-F]\{1,4\}\)\{1,4\}/y::y.y.y.y/g" | sed -e "s/\([0-9a-fA-F]\{2,2\}\:\)\{5,5\}[0-9a-fA-F]\{2,2\}/m.m.m.m/g"
+
+
+ if grep -q "^Revision\s*:\s*[ 123][0-9a-fA-F][0-9a-fA-F][0-9a-fA-F]1[13457][0-9a-fA-F]$" /proc/cpuinfo
@@ -284,5 +120,9 @@ echo
echo "EEPROM"
echo "------"
diff --git a/package/utils/bcm27xx-utils/patches/0002-cmake-disable-piolib.patch b/package/utils/bcm27xx-utils/patches/0002-cmake-disable-piolib.patch
new file mode 100644
index 0000000000..37f8a3a7e7
--- /dev/null
+++ b/package/utils/bcm27xx-utils/patches/0002-cmake-disable-piolib.patch
@@ -0,0 +1,24 @@
+From 5249e68da31d11e0beaf9fd76a6d17ac04198b26 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?=C3=81lvaro=20Fern=C3=A1ndez=20Rojas?= <noltari@gmail.com>
+Date: Sat, 28 Dec 2024 09:42:00 +0100
+Subject: [PATCH] cmake: disable piolib
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Signed-off-by: Álvaro Fernández Rojas <noltari@gmail.com>
+---
+ CMakeLists.txt | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/CMakeLists.txt
++++ b/CMakeLists.txt
+@@ -10,7 +10,7 @@ add_subdirectory(otpset)
+ add_subdirectory(overlaycheck)
+ add_subdirectory(ovmerge)
+ add_subdirectory(pinctrl)
+-add_subdirectory(piolib)
++# add_subdirectory(piolib)
+ add_subdirectory(raspinfo)
+ add_subdirectory(vcgencmd)
+ add_subdirectory(vclog)
diff --git a/package/utils/busybox/Config-defaults.in b/package/utils/busybox/Config-defaults.in
index 515bea3d1d..820c498eea 100644
--- a/package/utils/busybox/Config-defaults.in
+++ b/package/utils/busybox/Config-defaults.in
@@ -28,6 +28,9 @@ config BUSYBOX_DEFAULT_FEATURE_COMPRESS_USAGE
config BUSYBOX_DEFAULT_LFS
bool
default y
+config BUSYBOX_DEFAULT_TIME64
+ bool
+ default n
config BUSYBOX_DEFAULT_PAM
bool
default n
@@ -252,10 +255,10 @@ config BUSYBOX_DEFAULT_FEATURE_EDITING_HISTORY
default 256
config BUSYBOX_DEFAULT_FEATURE_EDITING_SAVEHISTORY
bool
- default n
+ default y
config BUSYBOX_DEFAULT_FEATURE_EDITING_SAVE_ON_EXIT
bool
- default n
+ default y
config BUSYBOX_DEFAULT_FEATURE_REVERSE_SEARCH
bool
default n
@@ -1181,6 +1184,9 @@ config BUSYBOX_DEFAULT_FEATURE_FIND_EXEC
config BUSYBOX_DEFAULT_FEATURE_FIND_EXEC_PLUS
bool
default n
+config BUSYBOX_DEFAULT_FEATURE_FIND_EXEC_OK
+ bool
+ default n
config BUSYBOX_DEFAULT_FEATURE_FIND_USER
bool
default y
@@ -1801,7 +1807,7 @@ config BUSYBOX_DEFAULT_FEATURE_SETPRIV_CAPABILITY_NAMES
default n
config BUSYBOX_DEFAULT_SETSID
bool
- default n
+ default y
config BUSYBOX_DEFAULT_SWAPON
bool
default y
@@ -2051,6 +2057,9 @@ config BUSYBOX_DEFAULT_FLASH_UNLOCK
config BUSYBOX_DEFAULT_FLASHCP
bool
default n
+config BUSYBOX_DEFAULT_GETFATTR
+ bool
+ default n
config BUSYBOX_DEFAULT_HDPARM
bool
default n
@@ -2457,6 +2466,9 @@ config BUSYBOX_DEFAULT_FEATURE_IP_ADDRESS
config BUSYBOX_DEFAULT_FEATURE_IP_LINK
bool
default y
+config BUSYBOX_DEFAULT_FEATURE_IP_LINK_CAN
+ bool
+ default n
config BUSYBOX_DEFAULT_FEATURE_IP_ROUTE
bool
default y
@@ -2682,6 +2694,9 @@ config BUSYBOX_DEFAULT_ZCIP
config BUSYBOX_DEFAULT_UDHCPD
bool
default n
+config BUSYBOX_DEFAULT_FEATURE_UDHCPD_BOOTP
+ bool
+ default n
config BUSYBOX_DEFAULT_FEATURE_UDHCPD_BASE_IP_ON_MAC
bool
default n
@@ -3042,9 +3057,6 @@ config BUSYBOX_DEFAULT_ASH_PRINTF
config BUSYBOX_DEFAULT_ASH_TEST
bool
default y
-config BUSYBOX_DEFAULT_ASH_SLEEP
- bool
- default n
config BUSYBOX_DEFAULT_ASH_HELP
bool
default n
diff --git a/package/utils/busybox/Makefile b/package/utils/busybox/Makefile
index 4bddd5201d..63c1dceee3 100644
--- a/package/utils/busybox/Makefile
+++ b/package/utils/busybox/Makefile
@@ -5,18 +5,22 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=busybox
-PKG_VERSION:=1.36.1
-PKG_RELEASE:=1
+PKG_VERSION:=1.37.0
+PKG_RELEASE:=5
PKG_FLAGS:=essential
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.bz2
PKG_SOURCE_URL:=https://www.busybox.net/downloads \
- http://sources.buildroot.net
-PKG_HASH:=b8cc24c9574d809e7279c3be349795c5d5ceb6fdf19ca709f80cde50e47de314
+ https://sources.buildroot.net/$(PKG_NAME)
+PKG_HASH:=3311dff32e746499f4df0d5df04d7eb396382d7e108bb9250e7b519b837043a4
PKG_BUILD_DEPENDS:=BUSYBOX_CONFIG_PAM:libpam
PKG_BUILD_PARALLEL:=1
PKG_BUILD_FLAGS:=lto
+ifeq ($(CONFIG_SOFT_FLOAT),)
+ PKG_BUILD_FLAGS+=no-mips16
+endif
+
PKG_CHECK_FORMAT_SECURITY:=0
PKG_LICENSE:=GPL-2.0
@@ -44,7 +48,7 @@ define Package/busybox/Default
MAINTAINER:=Felix Fietkau <nbd@nbd.name>
TITLE:=Core utilities for embedded Linux
URL:=http://busybox.net/
- DEPENDS:=+BUSYBOX_CONFIG_PAM:libpam +BUSYBOX_CONFIG_NTPD:jsonfilter
+ DEPENDS:= +USE_GLIBC:libcrypt-compat +BUSYBOX_CONFIG_PAM:libpam +BUSYBOX_CONFIG_NTPD:jsonfilter +(USE_GLIBC&&BUSYBOX_CONFIG_FEATURE_MOUNT_NFS)||(USE_GLIBC&&BUSYBOX_CONFIG_FEATURE_INETD_RPC):libtirpc
USERID:=ntp=123:ntp=123
endef
@@ -100,6 +104,13 @@ ifeq ($(CONFIG_USE_GLIBC),y)
LDLIBS += $(call BUSYBOX_IF_ENABLED,NSLOOKUP,resolv)
endif
+ifneq ($(CONFIG_BUSYBOX_$(BUSYBOX_SYM)_FEATURE_MOUNT_NFS)$(CONFIG_BUSYBOX_$(BUSYBOX_SYM)_FEATURE_INETD_RPC),)
+ifndef CONFIG_USE_MUSL
+ TARGET_CFLAGS += -I$(STAGING_DIR)/usr/include/tirpc
+ LDLIBS += tirpc
+endif
+endif
+
ifeq ($(BUILD_VARIANT),selinux)
LDLIBS += selinux sepol
endif
@@ -107,7 +118,7 @@ endif
MAKE_VARS :=
MAKE_FLAGS += \
EXTRA_CFLAGS="$(TARGET_CFLAGS) $(TARGET_CPPFLAGS)" \
- EXTRA_LDFLAGS="$(TARGET_LDFLAGS)" \
+ EXTRA_LDFLAGS="$(TARGET_LDFLAGS) $(TARGET_CFLAGS)" \
LDLIBS="$(LDLIBS)" \
LD="$(TARGET_CC)" \
SKIP_STRIP=y
@@ -139,6 +150,8 @@ define Package/busybox/install
$(INSTALL_DIR) $(1)/etc/init.d
$(INSTALL_DIR) $(1)/usr/sbin
$(CP) $(PKG_INSTALL_DIR)/* $(1)/
+ $(INSTALL_DIR) $(1)/etc/profile.d
+ $(INSTALL_BIN) ./files/busybox-history-file.sh $(1)/etc/profile.d
ifneq ($(CONFIG_BUSYBOX_$(BUSYBOX_SYM)_FEATURE_SYSLOG)$(CONFIG_BUSYBOX_$(BUSYBOX_SYM)_FEATURE_SYSLOGD_CFG),)
touch $(1)/etc/syslog.conf
endif
diff --git a/package/utils/busybox/config/Config.in b/package/utils/busybox/config/Config.in
index f30629839e..f1bd8ef12f 100644
--- a/package/utils/busybox/config/Config.in
+++ b/package/utils/busybox/config/Config.in
@@ -107,6 +107,17 @@ config BUSYBOX_CONFIG_LFS
programs that can benefit from large file support include dd, gzip,
cp, mount, tar.
+config BUSYBOX_CONFIG_TIME64
+ bool "Support 64bit wide time types"
+ default BUSYBOX_DEFAULT_TIME64
+ depends on BUSYBOX_CONFIG_LFS
+ help
+ Make times later than 2038 representable for several libc syscalls
+ (stat, clk_gettime etc.). Note this switch is specific to glibc
+ and has no effect on platforms that already use 64bit wide time types
+ (i.e. all 64bit archs and some selected 32bit archs (currently riscv
+ and x32)).
+
config BUSYBOX_CONFIG_PAM
bool "Support PAM (Pluggable Authentication Modules)"
default BUSYBOX_DEFAULT_PAM
diff --git a/package/utils/busybox/config/archival/Config.in b/package/utils/busybox/config/archival/Config.in
index ac2b3d2056..a3eebdce0f 100644
--- a/package/utils/busybox/config/archival/Config.in
+++ b/package/utils/busybox/config/archival/Config.in
@@ -80,7 +80,7 @@ config BUSYBOX_CONFIG_FEATURE_GUNZIP_LONG_OPTIONS
default BUSYBOX_DEFAULT_FEATURE_GUNZIP_LONG_OPTIONS
depends on (BUSYBOX_CONFIG_GUNZIP || BUSYBOX_CONFIG_ZCAT) && BUSYBOX_CONFIG_LONG_OPTS
config BUSYBOX_CONFIG_BUNZIP2
- bool "bunzip2 (8.7 kb)"
+ bool "bunzip2 (9.1 kb)"
default BUSYBOX_DEFAULT_BUNZIP2
select BUSYBOX_CONFIG_FEATURE_BZIP2_DECOMPRESS
help
@@ -94,13 +94,13 @@ config BUSYBOX_CONFIG_BUNZIP2
should probably say N here.
config BUSYBOX_CONFIG_BZCAT
- bool "bzcat (8.7 kb)"
+ bool "bzcat (9 kb)"
default BUSYBOX_DEFAULT_BZCAT
select BUSYBOX_CONFIG_FEATURE_BZIP2_DECOMPRESS
help
Alias to "bunzip2 -c".
config BUSYBOX_CONFIG_UNLZMA
- bool "unlzma (7.5 kb)"
+ bool "unlzma (7.8 kb)"
default BUSYBOX_DEFAULT_UNLZMA
help
unlzma is a compression utility using the Lempel-Ziv-Markov chain
@@ -109,7 +109,7 @@ config BUSYBOX_CONFIG_UNLZMA
compressors.
config BUSYBOX_CONFIG_LZCAT
- bool "lzcat (7.5 kb)"
+ bool "lzcat (7.8 kb)"
default BUSYBOX_DEFAULT_LZCAT
help
Alias to "unlzma -c".
@@ -229,7 +229,7 @@ config BUSYBOX_CONFIG_DPKG
This implementation of dpkg has a number of limitations,
you should use the official dpkg if possible.
config BUSYBOX_CONFIG_DPKG_DEB
- bool "dpkg-deb (30 kb)"
+ bool "dpkg-deb (29 kb)"
default BUSYBOX_DEFAULT_DPKG_DEB
select BUSYBOX_CONFIG_FEATURE_SEAMLESS_GZ
help
@@ -282,7 +282,7 @@ config BUSYBOX_CONFIG_FEATURE_GZIP_DECOMPRESS
This will be automatically selected if gunzip or zcat is
enabled.
config BUSYBOX_CONFIG_LZOP
- bool "lzop (12 kb)"
+ bool "lzop (13 kb)"
default BUSYBOX_DEFAULT_LZOP
help
Lzop compression/decompresion.
diff --git a/package/utils/busybox/config/console-tools/Config.in b/package/utils/busybox/config/console-tools/Config.in
index cf224e5e2e..3516178212 100644
--- a/package/utils/busybox/config/console-tools/Config.in
+++ b/package/utils/busybox/config/console-tools/Config.in
@@ -7,39 +7,39 @@
menu "Console Utilities"
config BUSYBOX_CONFIG_CHVT
- bool "chvt (2 kb)"
+ bool "chvt (2.2 kb)"
default BUSYBOX_DEFAULT_CHVT
help
This program is used to change to another terminal.
Example: chvt 4 (change to terminal /dev/tty4)
config BUSYBOX_CONFIG_CLEAR
- bool "clear (tiny)"
+ bool "clear (371 bytes)"
default BUSYBOX_DEFAULT_CLEAR
help
This program clears the terminal screen.
config BUSYBOX_CONFIG_DEALLOCVT
- bool "deallocvt (1.9 kb)"
+ bool "deallocvt (2.2 kb)"
default BUSYBOX_DEFAULT_DEALLOCVT
help
This program deallocates unused virtual consoles.
config BUSYBOX_CONFIG_DUMPKMAP
- bool "dumpkmap (1.6 kb)"
+ bool "dumpkmap (1.9 kb)"
default BUSYBOX_DEFAULT_DUMPKMAP
help
This program dumps the kernel's keyboard translation table to
stdout, in binary format. You can then use loadkmap to load it.
config BUSYBOX_CONFIG_FGCONSOLE
- bool "fgconsole (1.5 kb)"
+ bool "fgconsole (1.8 kb)"
default BUSYBOX_DEFAULT_FGCONSOLE
help
This program prints active (foreground) console number.
config BUSYBOX_CONFIG_KBD_MODE
- bool "kbd_mode (4.1 kb)"
+ bool "kbd_mode (4.3 kb)"
default BUSYBOX_DEFAULT_KBD_MODE
help
This program reports and sets keyboard mode.
config BUSYBOX_CONFIG_LOADFONT
- bool "loadfont (5.2 kb)"
+ bool "loadfont (5.4 kb)"
default BUSYBOX_DEFAULT_LOADFONT
help
This program loads a console font from standard input.
@@ -78,25 +78,25 @@ config BUSYBOX_CONFIG_FEATURE_LOADFONT_RAW
default BUSYBOX_DEFAULT_FEATURE_LOADFONT_RAW
depends on BUSYBOX_CONFIG_LOADFONT || BUSYBOX_CONFIG_SETFONT
config BUSYBOX_CONFIG_LOADKMAP
- bool "loadkmap (1.8 kb)"
+ bool "loadkmap (2.1 kb)"
default BUSYBOX_DEFAULT_LOADKMAP
help
This program loads a keyboard translation table from
standard input.
config BUSYBOX_CONFIG_OPENVT
- bool "openvt (7.2 kb)"
+ bool "openvt (7.4 kb)"
default BUSYBOX_DEFAULT_OPENVT
help
This program is used to start a command on an unused
virtual terminal.
config BUSYBOX_CONFIG_RESET
- bool "reset (345 bytes)"
+ bool "reset (676 bytes)"
default BUSYBOX_DEFAULT_RESET
help
This program is used to reset the terminal screen, if it
gets messed up.
config BUSYBOX_CONFIG_RESIZE
- bool "resize (903 bytes)"
+ bool "resize (1.2 kb)"
default BUSYBOX_DEFAULT_RESIZE
help
This program is used to (re)set the width and height of your current
@@ -112,7 +112,7 @@ config BUSYBOX_CONFIG_FEATURE_RESIZE_PRINT
E.g.:
COLUMNS=80;LINES=44;export COLUMNS LINES;
config BUSYBOX_CONFIG_SETCONSOLE
- bool "setconsole (3.6 kb)"
+ bool "setconsole (3.8 kb)"
default BUSYBOX_DEFAULT_SETCONSOLE
help
Redirect writes to /dev/console to another device,
@@ -125,18 +125,18 @@ config BUSYBOX_CONFIG_FEATURE_SETCONSOLE_LONG_OPTIONS
default BUSYBOX_DEFAULT_FEATURE_SETCONSOLE_LONG_OPTIONS
depends on BUSYBOX_CONFIG_SETCONSOLE && BUSYBOX_CONFIG_LONG_OPTS
config BUSYBOX_CONFIG_SETKEYCODES
- bool "setkeycodes (2.1 kb)"
+ bool "setkeycodes (2.4 kb)"
default BUSYBOX_DEFAULT_SETKEYCODES
help
This program loads entries into the kernel's scancode-to-keycode
map, allowing unusual keyboards to generate usable keycodes.
config BUSYBOX_CONFIG_SETLOGCONS
- bool "setlogcons (1.8 kb)"
+ bool "setlogcons (2 kb)"
default BUSYBOX_DEFAULT_SETLOGCONS
help
This program redirects the output console of kernel messages.
config BUSYBOX_CONFIG_SHOWKEY
- bool "showkey (4.7 kb)"
+ bool "showkey (4.9 kb)"
default BUSYBOX_DEFAULT_SHOWKEY
help
Shows keys pressed.
diff --git a/package/utils/busybox/config/coreutils/Config.in b/package/utils/busybox/config/coreutils/Config.in
index 983740be6e..8ba5afc5e4 100644
--- a/package/utils/busybox/config/coreutils/Config.in
+++ b/package/utils/busybox/config/coreutils/Config.in
@@ -48,14 +48,14 @@ config BUSYBOX_CONFIG_FEATURE_HUMAN_READABLE
Allow df, du, and ls to have human readable output.
config BUSYBOX_CONFIG_BASENAME
- bool "basename (438 bytes)"
+ bool "basename (3.7 kb)"
default BUSYBOX_DEFAULT_BASENAME
help
basename is used to strip the directory and suffix from filenames,
leaving just the filename itself. Enable this option if you wish
to enable the 'basename' utility.
config BUSYBOX_CONFIG_CAT
- bool "cat (5.6 kb)"
+ bool "cat (5.8 kb)"
default BUSYBOX_DEFAULT_CAT
help
cat is used to concatenate files and print them to the standard
@@ -96,20 +96,20 @@ config BUSYBOX_CONFIG_FEATURE_CHOWN_LONG_OPTIONS
default BUSYBOX_DEFAULT_FEATURE_CHOWN_LONG_OPTIONS
depends on BUSYBOX_CONFIG_CHOWN && BUSYBOX_CONFIG_LONG_OPTS
config BUSYBOX_CONFIG_CHROOT
- bool "chroot (3.7 kb)"
+ bool "chroot (4 kb)"
default BUSYBOX_DEFAULT_CHROOT
help
chroot is used to change the root directory and run a command.
The default command is '/bin/sh'.
config BUSYBOX_CONFIG_CKSUM
- bool "cksum (4.1 kb)"
+ bool "cksum (4.3 kb)"
default BUSYBOX_DEFAULT_CKSUM
config BUSYBOX_CONFIG_CRC32
- bool "crc32 (4.1 kb)"
+ bool "crc32 (4.2 kb)"
default BUSYBOX_DEFAULT_CRC32
config BUSYBOX_CONFIG_COMM
- bool "comm (4.2 kb)"
+ bool "comm (4.4 kb)"
default BUSYBOX_DEFAULT_COMM
help
comm is used to compare two files line by line and return
@@ -133,7 +133,7 @@ config BUSYBOX_CONFIG_FEATURE_CP_REFLINK
default BUSYBOX_DEFAULT_FEATURE_CP_REFLINK
depends on BUSYBOX_CONFIG_FEATURE_CP_LONG_OPTIONS
config BUSYBOX_CONFIG_CUT
- bool "cut (5.8 kb)"
+ bool "cut (6.7 kb)"
default BUSYBOX_DEFAULT_CUT
help
cut is used to print selected parts of lines from
@@ -146,7 +146,7 @@ config BUSYBOX_CONFIG_FEATURE_CUT_REGEX
help
Allow regex based delimiters.
config BUSYBOX_CONFIG_DATE
- bool "date (7 kb)"
+ bool "date (7.2 kb)"
default BUSYBOX_DEFAULT_DATE
help
date is used to set the system date or display the
@@ -183,7 +183,7 @@ config BUSYBOX_CONFIG_FEATURE_DATE_COMPAT
the same format. With it on, 'date DATE' additionally supports
MMDDhhmm[[YY]YY][.ss] format.
config BUSYBOX_CONFIG_DD
- bool "dd (7.5 kb)"
+ bool "dd (8.3 kb)"
default BUSYBOX_DEFAULT_DD
help
dd copies a file (from standard input to standard output,
@@ -227,7 +227,7 @@ config BUSYBOX_CONFIG_FEATURE_DD_STATUS
help
Enable support for status=noxfer/none option.
config BUSYBOX_CONFIG_DF
- bool "df (6.8 kb)"
+ bool "df (7.1 kb)"
default BUSYBOX_DEFAULT_DF
help
df reports the amount of disk space used and available
@@ -262,26 +262,26 @@ config BUSYBOX_CONFIG_FEATURE_SKIP_ROOTFS
Otherwise, choose Y.
config BUSYBOX_CONFIG_DIRNAME
- bool "dirname (329 bytes)"
+ bool "dirname (611 bytes)"
default BUSYBOX_DEFAULT_DIRNAME
help
dirname is used to strip a non-directory suffix from
a file name.
config BUSYBOX_CONFIG_DOS2UNIX
- bool "dos2unix (5.2 kb)"
+ bool "dos2unix (5.5 kb)"
default BUSYBOX_DEFAULT_DOS2UNIX
help
dos2unix is used to convert a text file from DOS format to
UNIX format, and vice versa.
config BUSYBOX_CONFIG_UNIX2DOS
- bool "unix2dos (5.2 kb)"
+ bool "unix2dos (5.5 kb)"
default BUSYBOX_DEFAULT_UNIX2DOS
help
unix2dos is used to convert a text file from UNIX format to
DOS format, and vice versa.
config BUSYBOX_CONFIG_DU
- bool "du (6.3 kb)"
+ bool "du (6.5 kb)"
default BUSYBOX_DEFAULT_DU
help
du is used to report the amount of disk space used
@@ -292,7 +292,7 @@ config BUSYBOX_CONFIG_FEATURE_DU_DEFAULT_BLOCKSIZE_1K
default BUSYBOX_DEFAULT_FEATURE_DU_DEFAULT_BLOCKSIZE_1K
depends on BUSYBOX_CONFIG_DU
config BUSYBOX_CONFIG_ECHO
- bool "echo (1.8 kb)"
+ bool "echo (2 kb)"
default BUSYBOX_DEFAULT_ECHO
help
echo prints a specified string to stdout.
@@ -303,25 +303,25 @@ config BUSYBOX_CONFIG_FEATURE_FANCY_ECHO
default BUSYBOX_DEFAULT_FEATURE_FANCY_ECHO
depends on BUSYBOX_CONFIG_ECHO || BUSYBOX_CONFIG_ASH_ECHO || BUSYBOX_CONFIG_HUSH_ECHO
config BUSYBOX_CONFIG_ENV
- bool "env (4 kb)"
+ bool "env (4.3 kb)"
default BUSYBOX_DEFAULT_ENV
help
env is used to set an environment variable and run
a command; without options it displays the current
environment.
config BUSYBOX_CONFIG_EXPAND
- bool "expand (5.1 kb)"
+ bool "expand (5.3 kb)"
default BUSYBOX_DEFAULT_EXPAND
help
By default, convert all tabs to spaces.
config BUSYBOX_CONFIG_UNEXPAND
- bool "unexpand (5.3 kb)"
+ bool "unexpand (5.5 kb)"
default BUSYBOX_DEFAULT_UNEXPAND
help
By default, convert only leading sequences of blanks to tabs.
config BUSYBOX_CONFIG_EXPR
- bool "expr (6.6 kb)"
+ bool "expr (6.8 kb)"
default BUSYBOX_DEFAULT_EXPR
help
expr is used to calculate numbers and print the result
@@ -336,22 +336,22 @@ config BUSYBOX_CONFIG_EXPR_MATH_SUPPORT_64
the applet slightly larger, but will allow computation with very
large numbers.
config BUSYBOX_CONFIG_FACTOR
- bool "factor (2.7 kb)"
+ bool "factor (3.2 kb)"
default BUSYBOX_DEFAULT_FACTOR
help
factor factorizes integers
config BUSYBOX_CONFIG_FALSE
- bool "false (tiny)"
+ bool "false (314 bytes)"
default BUSYBOX_DEFAULT_FALSE
help
false returns an exit code of FALSE (1).
config BUSYBOX_CONFIG_FOLD
- bool "fold (4.6 kb)"
+ bool "fold (4.8 kb)"
default BUSYBOX_DEFAULT_FOLD
help
Wrap text to fit a specific width.
config BUSYBOX_CONFIG_HEAD
- bool "head (3.8 kb)"
+ bool "head (4 kb)"
default BUSYBOX_DEFAULT_HEAD
help
head is used to print the first specified number of lines
@@ -362,19 +362,19 @@ config BUSYBOX_CONFIG_FEATURE_FANCY_HEAD
default BUSYBOX_DEFAULT_FEATURE_FANCY_HEAD
depends on BUSYBOX_CONFIG_HEAD
config BUSYBOX_CONFIG_HOSTID
- bool "hostid (286 bytes)"
+ bool "hostid (566 bytes)"
default BUSYBOX_DEFAULT_HOSTID
help
hostid prints the numeric identifier (in hexadecimal) for
the current host.
config BUSYBOX_CONFIG_ID
- bool "id (7 kb)"
+ bool "id (7.1 kb)"
default BUSYBOX_DEFAULT_ID
help
id displays the current user and group ID names.
config BUSYBOX_CONFIG_GROUPS
- bool "groups (6.7 kb)"
+ bool "groups (6.8 kb)"
default BUSYBOX_DEFAULT_GROUPS
help
Print the group names associated with current user id.
@@ -389,17 +389,17 @@ config BUSYBOX_CONFIG_FEATURE_INSTALL_LONG_OPTIONS
default BUSYBOX_DEFAULT_FEATURE_INSTALL_LONG_OPTIONS
depends on BUSYBOX_CONFIG_INSTALL && BUSYBOX_CONFIG_LONG_OPTS
config BUSYBOX_CONFIG_LINK
- bool "link (3.2 kb)"
+ bool "link (3.5 kb)"
default BUSYBOX_DEFAULT_LINK
help
link creates hard links between files.
config BUSYBOX_CONFIG_LN
- bool "ln (4.9 kb)"
+ bool "ln (5.1 kb)"
default BUSYBOX_DEFAULT_LN
help
ln is used to create hard or soft links between files.
config BUSYBOX_CONFIG_LOGNAME
- bool "logname (1.1 kb)"
+ bool "logname (1.4 kb)"
default BUSYBOX_DEFAULT_LOGNAME
help
logname is used to print the current user's login name.
@@ -468,31 +468,31 @@ config BUSYBOX_CONFIG_FEATURE_LS_COLOR_IS_DEFAULT
configurable, and the output may not be legible on
many output screens.
config BUSYBOX_CONFIG_MD5SUM
- bool "md5sum (6.5 kb)"
+ bool "md5sum (6.7 kb)"
default BUSYBOX_DEFAULT_MD5SUM
help
Compute and check MD5 message digest
config BUSYBOX_CONFIG_SHA1SUM
- bool "sha1sum (5.9 kb)"
+ bool "sha1sum (6.7 kb)"
default BUSYBOX_DEFAULT_SHA1SUM
help
Compute and check SHA1 message digest
config BUSYBOX_CONFIG_SHA256SUM
- bool "sha256sum (7 kb)"
+ bool "sha256sum (8.2 kb)"
default BUSYBOX_DEFAULT_SHA256SUM
help
Compute and check SHA256 message digest
config BUSYBOX_CONFIG_SHA512SUM
- bool "sha512sum (7.4 kb)"
+ bool "sha512sum (7.3 kb)"
default BUSYBOX_DEFAULT_SHA512SUM
help
Compute and check SHA512 message digest
config BUSYBOX_CONFIG_SHA3SUM
- bool "sha3sum (6.1 kb)"
+ bool "sha3sum (6.3 kb)"
default BUSYBOX_DEFAULT_SHA3SUM
help
Compute and check SHA3 message digest
@@ -509,24 +509,24 @@ config BUSYBOX_CONFIG_FEATURE_MD5_SHA1_SUM_CHECK
against pre-calculated hash values.
-s and -w are useful options when verifying checksums.
config BUSYBOX_CONFIG_MKDIR
- bool "mkdir (4.5 kb)"
+ bool "mkdir (4.7 kb)"
default BUSYBOX_DEFAULT_MKDIR
help
mkdir is used to create directories with the specified names.
config BUSYBOX_CONFIG_MKFIFO
- bool "mkfifo (3.8 kb)"
+ bool "mkfifo (4 kb)"
default BUSYBOX_DEFAULT_MKFIFO
help
mkfifo is used to create FIFOs (named pipes).
The 'mknod' program can also create FIFOs.
config BUSYBOX_CONFIG_MKNOD
- bool "mknod (4.5 kb)"
+ bool "mknod (4.6 kb)"
default BUSYBOX_DEFAULT_MKNOD
help
mknod is used to create FIFOs or block/character special
files with the specified names.
config BUSYBOX_CONFIG_MKTEMP
- bool "mktemp (4.2 kb)"
+ bool "mktemp (4.5 kb)"
default BUSYBOX_DEFAULT_MKTEMP
help
mktemp is used to create unique temporary files
@@ -536,22 +536,22 @@ config BUSYBOX_CONFIG_MV
help
mv is used to move or rename files or directories.
config BUSYBOX_CONFIG_NICE
- bool "nice (2.1 kb)"
+ bool "nice (2.3 kb)"
default BUSYBOX_DEFAULT_NICE
help
nice runs a program with modified scheduling priority.
config BUSYBOX_CONFIG_NL
- bool "nl (4.6 kb)"
+ bool "nl (4.9 kb)"
default BUSYBOX_DEFAULT_NL
help
nl is used to number lines of files.
config BUSYBOX_CONFIG_NOHUP
- bool "nohup (2 kb)"
+ bool "nohup (2.2 kb)"
default BUSYBOX_DEFAULT_NOHUP
help
run a command immune to hangups, with output to a non-tty.
config BUSYBOX_CONFIG_NPROC
- bool "nproc (3.7 kb)"
+ bool "nproc (3.9 kb)"
default BUSYBOX_DEFAULT_NPROC
help
Print number of CPUs
@@ -561,29 +561,29 @@ config BUSYBOX_CONFIG_OD
help
od is used to dump binary files in octal and other formats.
config BUSYBOX_CONFIG_PASTE
- bool "paste (4.9 kb)"
+ bool "paste (5.1 kb)"
default BUSYBOX_DEFAULT_PASTE
help
paste is used to paste lines of different files together
and write the result to stdout
config BUSYBOX_CONFIG_PRINTENV
- bool "printenv (1.3 kb)"
+ bool "printenv (1.6 kb)"
default BUSYBOX_DEFAULT_PRINTENV
help
printenv is used to print all or part of environment.
config BUSYBOX_CONFIG_PRINTF
- bool "printf (3.8 kb)"
+ bool "printf (4.1 kb)"
default BUSYBOX_DEFAULT_PRINTF
help
printf is used to format and print specified strings.
It's similar to 'echo' except it has more options.
config BUSYBOX_CONFIG_PWD
- bool "pwd (3.7 kb)"
+ bool "pwd (4 kb)"
default BUSYBOX_DEFAULT_PWD
help
pwd is used to print the current directory.
config BUSYBOX_CONFIG_READLINK
- bool "readlink (4 kb)"
+ bool "readlink (4.8 kb)"
default BUSYBOX_DEFAULT_READLINK
help
This program reads a symbolic link and returns the name
@@ -596,49 +596,50 @@ config BUSYBOX_CONFIG_FEATURE_READLINK_FOLLOW
help
Enable the readlink option (-f).
config BUSYBOX_CONFIG_REALPATH
- bool "realpath (1.6 kb)"
+ bool "realpath (2.5 kb)"
default BUSYBOX_DEFAULT_REALPATH
help
Return the canonicalized absolute pathname.
This isn't provided by GNU shellutils, but where else does it belong.
config BUSYBOX_CONFIG_RM
- bool "rm (5.4 kb)"
+ bool "rm (5.5 kb)"
default BUSYBOX_DEFAULT_RM
help
rm is used to remove files or directories.
config BUSYBOX_CONFIG_RMDIR
- bool "rmdir (3.5 kb)"
+ bool "rmdir (3.8 kb)"
default BUSYBOX_DEFAULT_RMDIR
help
rmdir is used to remove empty directories.
config BUSYBOX_CONFIG_SEQ
- bool "seq (3.8 kb)"
+ bool "seq (4 kb)"
default BUSYBOX_DEFAULT_SEQ
help
print a sequence of numbers
config BUSYBOX_CONFIG_SHRED
- bool "shred (4.9 kb)"
+ bool "shred (5.5 kb)"
default BUSYBOX_DEFAULT_SHRED
help
Overwrite a file to hide its contents, and optionally delete it
config BUSYBOX_CONFIG_SHUF
- bool "shuf (5.4 kb)"
+ bool "shuf (6 kb)"
default BUSYBOX_DEFAULT_SHUF
help
Generate random permutations
config BUSYBOX_CONFIG_SLEEP
- bool "sleep (2 kb)"
+ bool "sleep (2.4 kb)"
default BUSYBOX_DEFAULT_SLEEP
help
sleep is used to pause for a specified number of seconds.
- It comes in 3 versions:
+ It comes in 2 versions:
- small: takes one integer parameter
- - fancy: takes multiple integer arguments with suffixes:
- sleep 1d 2h 3m 15s
- - fancy with fractional numbers:
- sleep 2.3s 4.5h sleeps for 16202.3 seconds
- Last one is "the most compatible" with coreutils sleep,
- but it adds around 1k of code.
+ - fancy:
+ * takes multiple integer arguments with suffixes:
+ sleep 1d 2h 3m 15s
+ * allows fractional numbers:
+ sleep 2.3s 4.5h sleeps for 16202.3 seconds
+ fancy is more compatible with coreutils sleep, but it adds around
+ 1k of code.
config BUSYBOX_CONFIG_FEATURE_FANCY_SLEEP
bool "Enable multiple arguments and s/m/h/d suffixes"
@@ -647,7 +648,7 @@ config BUSYBOX_CONFIG_FEATURE_FANCY_SLEEP
help
Allow sleep to pause for specified minutes, hours, and days.
config BUSYBOX_CONFIG_SORT
- bool "sort (7.7 kb)"
+ bool "sort (8.1 kb)"
default BUSYBOX_DEFAULT_SORT
help
sort is used to sort lines of text in specified files.
@@ -672,7 +673,7 @@ config BUSYBOX_CONFIG_FEATURE_SORT_OPTIMIZE_MEMORY
Attempt to use less memory (by storing only one copy
of duplicated lines, and such). Useful if you work on huge files.
config BUSYBOX_CONFIG_SPLIT
- bool "split (5 kb)"
+ bool "split (5.2 kb)"
default BUSYBOX_DEFAULT_SPLIT
help
Split a file into pieces.
@@ -708,17 +709,17 @@ config BUSYBOX_CONFIG_FEATURE_STAT_FILESYSTEM
Without this, stat will not support the '-f' option to display
information about filesystem status.
config BUSYBOX_CONFIG_STTY
- bool "stty (8.9 kb)"
+ bool "stty (9.2 kb)"
default BUSYBOX_DEFAULT_STTY
help
stty is used to change and print terminal line settings.
config BUSYBOX_CONFIG_SUM
- bool "sum (4 kb)"
+ bool "sum (4.2 kb)"
default BUSYBOX_DEFAULT_SUM
help
checksum and count the blocks in a file
config BUSYBOX_CONFIG_SYNC
- bool "sync (3.8 kb)"
+ bool "sync (4 kb)"
default BUSYBOX_DEFAULT_SYNC
help
sync is used to flush filesystem buffers.
@@ -730,17 +731,17 @@ config BUSYBOX_CONFIG_FEATURE_SYNC_FANCY
sync -d FILE... executes fdatasync() on each FILE.
sync -f FILE... executes syncfs() on each FILE.
config BUSYBOX_CONFIG_FSYNC
- bool "fsync (3.6 kb)"
+ bool "fsync (3.8 kb)"
default BUSYBOX_DEFAULT_FSYNC
help
fsync is used to flush file-related cached blocks to disk.
config BUSYBOX_CONFIG_TAC
- bool "tac (3.9 kb)"
+ bool "tac (4.1 kb)"
default BUSYBOX_DEFAULT_TAC
help
tac is used to concatenate and print files in reverse.
config BUSYBOX_CONFIG_TAIL
- bool "tail (6.8 kb)"
+ bool "tail (7.2 kb)"
default BUSYBOX_DEFAULT_TAIL
help
tail is used to print the last specified number of lines
@@ -758,7 +759,7 @@ config BUSYBOX_CONFIG_FEATURE_FANCY_TAIL
-v Always output headers giving file names
-F Same as -f, but keep retrying
config BUSYBOX_CONFIG_TEE
- bool "tee (4.2 kb)"
+ bool "tee (4.4 kb)"
default BUSYBOX_DEFAULT_TEE
help
tee is used to read from standard input and write
@@ -771,7 +772,7 @@ config BUSYBOX_CONFIG_FEATURE_TEE_USE_BLOCK_IO
help
Enable this option for a faster tee, at expense of size.
config BUSYBOX_CONFIG_TEST
- bool "test (4.1 kb)"
+ bool "test (4.4 kb)"
default BUSYBOX_DEFAULT_TEST
help
test is used to check file types and compare values,
@@ -797,13 +798,13 @@ config BUSYBOX_CONFIG_FEATURE_TEST_64
help
Enable 64-bit support in test.
config BUSYBOX_CONFIG_TIMEOUT
- bool "timeout (6 kb)"
+ bool "timeout (6.5 kb)"
default BUSYBOX_DEFAULT_TIMEOUT
help
Runs a program and watches it. If it does not terminate in
specified number of seconds, it is sent a signal.
config BUSYBOX_CONFIG_TOUCH
- bool "touch (5.9 kb)"
+ bool "touch (6.1 kb)"
default BUSYBOX_DEFAULT_TOUCH
help
touch is used to create or change the access and/or
@@ -816,7 +817,7 @@ config BUSYBOX_CONFIG_FEATURE_TOUCH_SUSV3
help
Enable touch to use a reference file or a given date/time argument.
config BUSYBOX_CONFIG_TR
- bool "tr (5.1 kb)"
+ bool "tr (5.3 kb)"
default BUSYBOX_DEFAULT_TR
help
tr is used to squeeze, and/or delete characters from standard
@@ -841,29 +842,29 @@ config BUSYBOX_CONFIG_FEATURE_TR_EQUIV
useful for cases when no other way of expressing a character
is possible.
config BUSYBOX_CONFIG_TRUE
- bool "true (tiny)"
+ bool "true (311 bytes)"
default BUSYBOX_DEFAULT_TRUE
help
true returns an exit code of TRUE (0).
config BUSYBOX_CONFIG_TRUNCATE
- bool "truncate (4.2 kb)"
+ bool "truncate (4.4 kb)"
default BUSYBOX_DEFAULT_TRUNCATE
help
truncate truncates files to a given size. If a file does
not exist, it is created unless told otherwise.
config BUSYBOX_CONFIG_TSORT
- bool "tsort (0.7 kb)"
+ bool "tsort (2.6 kb)"
default BUSYBOX_DEFAULT_TSORT
help
tsort performs a topological sort.
config BUSYBOX_CONFIG_TTY
- bool "tty (3.6 kb)"
+ bool "tty (3.9 kb)"
default BUSYBOX_DEFAULT_TTY
help
tty is used to print the name of the current terminal to
standard output.
config BUSYBOX_CONFIG_UNAME
- bool "uname (3.9 kb)"
+ bool "uname (4.2 kb)"
default BUSYBOX_DEFAULT_UNAME
help
uname is used to print system information.
@@ -877,47 +878,47 @@ config BUSYBOX_CONFIG_UNAME_OSNAME
default BUSYBOX_DEFAULT_UNAME_OSNAME "GNU/Linux".
config BUSYBOX_CONFIG_BB_ARCH
- bool "arch (1.1 kb)"
+ bool "arch (1.4 kb)"
default BUSYBOX_DEFAULT_BB_ARCH
help
Same as uname -m.
config BUSYBOX_CONFIG_UNIQ
- bool "uniq (4.9 kb)"
+ bool "uniq (5.1 kb)"
default BUSYBOX_DEFAULT_UNIQ
help
uniq is used to remove duplicate lines from a sorted file.
config BUSYBOX_CONFIG_UNLINK
- bool "unlink (3.2 kb)"
+ bool "unlink (3.5 kb)"
default BUSYBOX_DEFAULT_UNLINK
help
unlink deletes a file by calling unlink()
config BUSYBOX_CONFIG_USLEEP
- bool "usleep (1.3 kb)"
+ bool "usleep (1.6 kb)"
default BUSYBOX_DEFAULT_USLEEP
help
usleep is used to pause for a specified number of microseconds.
config BUSYBOX_CONFIG_UUDECODE
- bool "uudecode (5.8 kb)"
+ bool "uudecode (5.9 kb)"
default BUSYBOX_DEFAULT_UUDECODE
help
uudecode is used to decode a uuencoded file.
config BUSYBOX_CONFIG_BASE32
- bool "base32 (4.9 kb)"
+ bool "base32 (5.5 kb)"
default BUSYBOX_DEFAULT_BASE32
help
Base32 encode and decode
config BUSYBOX_CONFIG_BASE64
- bool "base64 (4.9 kb)"
+ bool "base64 (5.3 kb)"
default BUSYBOX_DEFAULT_BASE64
help
Base64 encode and decode
config BUSYBOX_CONFIG_UUENCODE
- bool "uuencode (4.4 kb)"
+ bool "uuencode (4.7 kb)"
default BUSYBOX_DEFAULT_UUENCODE
help
uuencode is used to uuencode a file.
config BUSYBOX_CONFIG_WC
- bool "wc (4.5 kb)"
+ bool "wc (4.7 kb)"
default BUSYBOX_DEFAULT_WC
help
wc is used to print the number of bytes, words, and lines,
@@ -930,33 +931,33 @@ config BUSYBOX_CONFIG_FEATURE_WC_LARGE
help
Use "unsigned long long" for counter variables.
config BUSYBOX_CONFIG_WHO
- bool "who (3.9 kb)"
+ bool "who (5.6 kb)"
default BUSYBOX_DEFAULT_WHO
depends on BUSYBOX_CONFIG_FEATURE_UTMP
help
Print users currently logged on.
config BUSYBOX_CONFIG_W
- bool "w (3.8 kb)"
+ bool "w (5.5 kb)"
default BUSYBOX_DEFAULT_W
depends on BUSYBOX_CONFIG_FEATURE_UTMP
help
Print users currently logged on.
config BUSYBOX_CONFIG_USERS
- bool "users (3.4 kb)"
+ bool "users (3.6 kb)"
default BUSYBOX_DEFAULT_USERS
depends on BUSYBOX_CONFIG_FEATURE_UTMP
help
Print users currently logged on.
config BUSYBOX_CONFIG_WHOAMI
- bool "whoami (3.2 kb)"
+ bool "whoami (3.5 kb)"
default BUSYBOX_DEFAULT_WHOAMI
help
whoami is used to print the username of the current
user id (same as id -un).
config BUSYBOX_CONFIG_YES
- bool "yes (1.2 kb)"
+ bool "yes (1.5 kb)"
default BUSYBOX_DEFAULT_YES
help
yes is used to repeatedly output a specific string, or
diff --git a/package/utils/busybox/config/debianutils/Config.in b/package/utils/busybox/config/debianutils/Config.in
index 15abb08e63..69bdbb654d 100644
--- a/package/utils/busybox/config/debianutils/Config.in
+++ b/package/utils/busybox/config/debianutils/Config.in
@@ -7,12 +7,12 @@
menu "Debian Utilities"
config BUSYBOX_CONFIG_PIPE_PROGRESS
- bool "pipe_progress (275 bytes)"
+ bool "pipe_progress (576 bytes)"
default BUSYBOX_DEFAULT_PIPE_PROGRESS
help
Display a dot to indicate pipe activity.
config BUSYBOX_CONFIG_RUN_PARTS
- bool "run-parts (6.1 kb)"
+ bool "run-parts (6.2 kb)"
default BUSYBOX_DEFAULT_RUN_PARTS
help
run-parts is a utility designed to run all the scripts in a directory.
@@ -61,7 +61,7 @@ config BUSYBOX_CONFIG_FEATURE_START_STOP_DAEMON_FANCY
-v|--verbose
-N|--nicelevel N
config BUSYBOX_CONFIG_WHICH
- bool "which (3.8 kb)"
+ bool "which (4 kb)"
default BUSYBOX_DEFAULT_WHICH
help
which is used to find programs in your PATH and
diff --git a/package/utils/busybox/config/e2fsprogs/Config.in b/package/utils/busybox/config/e2fsprogs/Config.in
index 4a9aa2ab4e..ed6220a2d1 100644
--- a/package/utils/busybox/config/e2fsprogs/Config.in
+++ b/package/utils/busybox/config/e2fsprogs/Config.in
@@ -7,19 +7,19 @@
menu "Linux Ext2 FS Progs"
config BUSYBOX_CONFIG_CHATTR
- bool "chattr (3.8 kb)"
+ bool "chattr (4.1 kb)"
default BUSYBOX_DEFAULT_CHATTR
help
chattr changes the file attributes on a second extended file system.
config BUSYBOX_CONFIG_FSCK
- bool "fsck (7.4 kb)"
+ bool "fsck (7.6 kb)"
default BUSYBOX_DEFAULT_FSCK
help
fsck is used to check and optionally repair one or more filesystems.
In actuality, fsck is simply a front-end for the various file system
checkers (fsck.fstype) available under Linux.
config BUSYBOX_CONFIG_LSATTR
- bool "lsattr (5.5 kb)"
+ bool "lsattr (5.7 kb)"
default BUSYBOX_DEFAULT_LSATTR
help
lsattr lists the file attributes on a second extended file system.
diff --git a/package/utils/busybox/config/editors/Config.in b/package/utils/busybox/config/editors/Config.in
index dc80a4ec0d..0e9bda0fba 100644
--- a/package/utils/busybox/config/editors/Config.in
+++ b/package/utils/busybox/config/editors/Config.in
@@ -7,7 +7,7 @@
menu "Editors"
config BUSYBOX_CONFIG_AWK
- bool "awk (23 kb)"
+ bool "awk (24 kb)"
default BUSYBOX_DEFAULT_AWK
help
Awk is used as a pattern scanning and processing language.
@@ -31,7 +31,7 @@ config BUSYBOX_CONFIG_FEATURE_AWK_GNU_EXTENSIONS
This enables the use of awk library files.
Example: awk -f mylib.awk -e '{print myfunction($1);}' ...
config BUSYBOX_CONFIG_CMP
- bool "cmp (4.9 kb)"
+ bool "cmp (5.3 kb)"
default BUSYBOX_DEFAULT_CMP
help
cmp is used to compare two files and returns the result
@@ -57,14 +57,14 @@ config BUSYBOX_CONFIG_FEATURE_DIFF_DIR
This option enables support for directory and subdirectory
comparison.
config BUSYBOX_CONFIG_ED
- bool "ed (21 kb)"
+ bool "ed (16 kb)"
default BUSYBOX_DEFAULT_ED
help
The original 1970's Unix text editor, from the days of teletypes.
Small, simple, evil. Part of SUSv3. If you're not already using
this, you don't need it.
config BUSYBOX_CONFIG_PATCH
- bool "patch (9.4 kb)"
+ bool "patch (9.6 kb)"
default BUSYBOX_DEFAULT_PATCH
help
Apply a unified diff formatted patch.
@@ -75,7 +75,7 @@ config BUSYBOX_CONFIG_SED
sed is used to perform text transformations on a file
or input from a pipeline.
config BUSYBOX_CONFIG_VI
- bool "vi (23 kb)"
+ bool "vi (26 kb)"
default BUSYBOX_DEFAULT_VI
help
'vi' is a text editor. More specifically, it is the One True
@@ -134,7 +134,7 @@ config BUSYBOX_CONFIG_FEATURE_VI_SEARCH
config BUSYBOX_CONFIG_FEATURE_VI_REGEX_SEARCH
bool "Enable regex in search and replace"
- default BUSYBOX_DEFAULT_FEATURE_VI_REGEX_SEARCH # Uses GNU regex, which may be unavailable. FIXME
+ default BUSYBOX_DEFAULT_FEATURE_VI_REGEX_SEARCH
depends on BUSYBOX_CONFIG_FEATURE_VI_SEARCH
depends on USE_GLIBC
help
diff --git a/package/utils/busybox/config/findutils/Config.in b/package/utils/busybox/config/findutils/Config.in
index 805d44f149..8ea35b7c55 100644
--- a/package/utils/busybox/config/findutils/Config.in
+++ b/package/utils/busybox/config/findutils/Config.in
@@ -7,7 +7,7 @@
menu "Finding Utilities"
config BUSYBOX_CONFIG_FIND
- bool "find (14 kb)"
+ bool "find (16 kb)"
default BUSYBOX_DEFAULT_FIND
help
find is used to search your system to find specified files.
@@ -136,6 +136,13 @@ config BUSYBOX_CONFIG_FEATURE_FIND_EXEC_PLUS
Without this option, -exec + is a synonym for -exec ;
(IOW: it works correctly, but without expected speedup)
+config BUSYBOX_CONFIG_FEATURE_FIND_EXEC_OK
+ bool "Enable -ok: execute confirmed commands"
+ default BUSYBOX_DEFAULT_FEATURE_FIND_EXEC_OK
+ depends on BUSYBOX_CONFIG_FEATURE_FIND_EXEC
+ help
+ Support the 'find -ok' option which prompts before executing.
+
config BUSYBOX_CONFIG_FEATURE_FIND_USER
bool "Enable -user: username/uid matching"
default BUSYBOX_DEFAULT_FEATURE_FIND_USER
@@ -234,19 +241,19 @@ config BUSYBOX_CONFIG_FEATURE_FIND_LINKS
help
Support the 'find -links' option for matching number of links.
config BUSYBOX_CONFIG_GREP
- bool "grep (8.6 kb)"
+ bool "grep (8.9 kb)"
default BUSYBOX_DEFAULT_GREP
help
grep is used to search files for a specified pattern.
config BUSYBOX_CONFIG_EGREP
- bool "egrep (7.8 kb)"
+ bool "egrep (8 kb)"
default BUSYBOX_DEFAULT_EGREP
help
Alias to "grep -E".
config BUSYBOX_CONFIG_FGREP
- bool "fgrep (7.8 kb)"
+ bool "fgrep (8 kb)"
default BUSYBOX_DEFAULT_FGREP
help
Alias to "grep -F".
@@ -260,7 +267,7 @@ config BUSYBOX_CONFIG_FEATURE_GREP_CONTEXT
context surrounding our matching lines.
Print the specified number of context lines (-C).
config BUSYBOX_CONFIG_XARGS
- bool "xargs (7.2 kb)"
+ bool "xargs (7.6 kb)"
default BUSYBOX_DEFAULT_XARGS
help
xargs is used to execute a specified command for
diff --git a/package/utils/busybox/config/init/Config.in b/package/utils/busybox/config/init/Config.in
index fc6c916a68..56cc2ef1da 100644
--- a/package/utils/busybox/config/init/Config.in
+++ b/package/utils/busybox/config/init/Config.in
@@ -45,19 +45,19 @@ config BUSYBOX_CONFIG_FEATURE_BOOTCHARTD_CONFIG_FILE
Enable reading and parsing of $PWD/bootchartd.conf
and /etc/bootchartd.conf files.
config BUSYBOX_CONFIG_HALT
- bool "halt (4 kb)"
+ bool "halt (4.3 kb)"
default BUSYBOX_DEFAULT_HALT
help
Stop all processes and halt the system.
config BUSYBOX_CONFIG_POWEROFF
- bool "poweroff (4 kb)"
+ bool "poweroff (4.3 kb)"
default BUSYBOX_DEFAULT_POWEROFF
help
Stop all processes and power off the system.
config BUSYBOX_CONFIG_REBOOT
- bool "reboot (4 kb)"
+ bool "reboot (4.3 kb)"
default BUSYBOX_DEFAULT_REBOOT
help
Stop all processes and reboot the system.
diff --git a/package/utils/busybox/config/klibc-utils/Config.in b/package/utils/busybox/config/klibc-utils/Config.in
index 06b9681bc9..6fb5bbf669 100644
--- a/package/utils/busybox/config/klibc-utils/Config.in
+++ b/package/utils/busybox/config/klibc-utils/Config.in
@@ -17,12 +17,12 @@ config BUSYBOX_CONFIG_NUKE
help
Alias to "rm -rf".
config BUSYBOX_CONFIG_RESUME
- bool "resume (3.2 kb)"
+ bool "resume (3.6 kb)"
default BUSYBOX_DEFAULT_RESUME
help
Resume from saved "suspend-to-disk" image
config BUSYBOX_CONFIG_RUN_INIT
- bool "run-init (7.7 kb)"
+ bool "run-init (8 kb)"
default BUSYBOX_DEFAULT_RUN_INIT
help
The run-init utility is used from initramfs to select a new
diff --git a/package/utils/busybox/config/loginutils/Config.in b/package/utils/busybox/config/loginutils/Config.in
index 465fa519cb..95422f1037 100644
--- a/package/utils/busybox/config/loginutils/Config.in
+++ b/package/utils/busybox/config/loginutils/Config.in
@@ -93,18 +93,18 @@ config BUSYBOX_CONFIG_USE_BB_CRYPT_SHA
user which has password encrypted with these algorithms.
config BUSYBOX_CONFIG_ADD_SHELL
- bool "add-shell (3.1 kb)"
+ bool "add-shell (3.3 kb)"
default BUSYBOX_DEFAULT_ADD_SHELL if BUSYBOX_CONFIG_DESKTOP
help
Add shells to /etc/shells.
config BUSYBOX_CONFIG_REMOVE_SHELL
- bool "remove-shell (3 kb)"
+ bool "remove-shell (3.3 kb)"
default BUSYBOX_DEFAULT_REMOVE_SHELL if BUSYBOX_CONFIG_DESKTOP
help
Remove shells from /etc/shells.
config BUSYBOX_CONFIG_ADDGROUP
- bool "addgroup (8.6 kb)"
+ bool "addgroup (8.8 kb)"
default BUSYBOX_DEFAULT_ADDGROUP
select BUSYBOX_CONFIG_LONG_OPTS
help
@@ -160,7 +160,7 @@ config BUSYBOX_CONFIG_LAST_SYSTEM_ID
help
Last valid system uid or gid for adduser and addgroup
config BUSYBOX_CONFIG_CHPASSWD
- bool "chpasswd (18 kb)"
+ bool "chpasswd (19 kb)"
default BUSYBOX_DEFAULT_CHPASSWD
help
Reads a file of user name and password pairs from standard input
@@ -173,27 +173,27 @@ config BUSYBOX_CONFIG_FEATURE_DEFAULT_PASSWD_ALGO
help
Possible choices are "d[es]", "m[d5]", "s[ha256]" or "sha512".
config BUSYBOX_CONFIG_CRYPTPW
- bool "cryptpw (14 kb)"
+ bool "cryptpw (15 kb)"
default BUSYBOX_DEFAULT_CRYPTPW
help
Encrypts the given password with the crypt(3) libc function
using the given salt.
config BUSYBOX_CONFIG_MKPASSWD
- bool "mkpasswd (15 kb)"
+ bool "mkpasswd (16 kb)"
default BUSYBOX_DEFAULT_MKPASSWD
help
Encrypts the given password with the crypt(3) libc function
using the given salt. Debian has this utility under mkpasswd
name. Busybox provides mkpasswd as an alias for cryptpw.
config BUSYBOX_CONFIG_DELUSER
- bool "deluser (9.1 kb)"
+ bool "deluser (9.3 kb)"
default BUSYBOX_DEFAULT_DELUSER
help
Utility for deleting a user account.
config BUSYBOX_CONFIG_DELGROUP
- bool "delgroup (6.4 kb)"
+ bool "delgroup (6.6 kb)"
default BUSYBOX_DEFAULT_DELGROUP
help
Utility for deleting a group account.
@@ -206,7 +206,7 @@ config BUSYBOX_CONFIG_FEATURE_DEL_USER_FROM_GROUP
If called with two non-option arguments, deluser
or delgroup will remove an user from a specified group.
config BUSYBOX_CONFIG_GETTY
- bool "getty (10 kb)"
+ bool "getty (11 kb)"
default BUSYBOX_DEFAULT_GETTY
select BUSYBOX_CONFIG_FEATURE_SYSLOG
help
@@ -224,7 +224,7 @@ config BUSYBOX_CONFIG_GETTY
read -r login
exec /bin/login "$login"
config BUSYBOX_CONFIG_LOGIN
- bool "login (24 kb)"
+ bool "login (25 kb)"
default BUSYBOX_DEFAULT_LOGIN
select BUSYBOX_CONFIG_FEATURE_SYSLOG
help
@@ -269,7 +269,7 @@ config BUSYBOX_CONFIG_FEATURE_SECURETTY
The file contains the device names of tty lines (one per line,
without leading /dev/) on which root is allowed to login.
config BUSYBOX_CONFIG_PASSWD
- bool "passwd (21 kb)"
+ bool "passwd (22 kb)"
default BUSYBOX_DEFAULT_PASSWD
select BUSYBOX_CONFIG_FEATURE_SYSLOG
help
@@ -312,14 +312,14 @@ config BUSYBOX_CONFIG_FEATURE_SU_BLANK_PW_NEEDS_SECURE_TTY
default BUSYBOX_DEFAULT_FEATURE_SU_BLANK_PW_NEEDS_SECURE_TTY
depends on BUSYBOX_CONFIG_SU
config BUSYBOX_CONFIG_SULOGIN
- bool "sulogin (17 kb)"
+ bool "sulogin (18 kb)"
default BUSYBOX_DEFAULT_SULOGIN
select BUSYBOX_CONFIG_FEATURE_SYSLOG
help
sulogin is invoked when the system goes into single user
mode (this is done through an entry in inittab).
config BUSYBOX_CONFIG_VLOCK
- bool "vlock (17 kb)"
+ bool "vlock (18 kb)"
default BUSYBOX_DEFAULT_VLOCK
help
Build the "vlock" applet which allows you to lock (virtual) terminals.
diff --git a/package/utils/busybox/config/mailutils/Config.in b/package/utils/busybox/config/mailutils/Config.in
index ea7ae6d649..146478a872 100644
--- a/package/utils/busybox/config/mailutils/Config.in
+++ b/package/utils/busybox/config/mailutils/Config.in
@@ -9,12 +9,12 @@ config BUSYBOX_CONFIG_FEATURE_MIME_CHARSET
Default charset of the message.
config BUSYBOX_CONFIG_MAKEMIME
- bool "makemime (5.4 kb)"
+ bool "makemime (5.6 kb)"
default BUSYBOX_DEFAULT_MAKEMIME
help
Create MIME-formatted messages.
config BUSYBOX_CONFIG_POPMAILDIR
- bool "popmaildir (10 kb)"
+ bool "popmaildir (11 kb)"
default BUSYBOX_DEFAULT_POPMAILDIR
help
Simple yet powerful POP3 mail popper. Delivers content
@@ -30,7 +30,7 @@ config BUSYBOX_CONFIG_FEATURE_POPMAILDIR_DELIVERY
Allow to use a custom program for message actual delivery
(-M "prog [args...]").
config BUSYBOX_CONFIG_REFORMIME
- bool "reformime (7.5 kb)"
+ bool "reformime (7.6 kb)"
default BUSYBOX_DEFAULT_REFORMIME
help
Parse MIME-formatted messages.
diff --git a/package/utils/busybox/config/miscutils/Config.in b/package/utils/busybox/config/miscutils/Config.in
index e15e318fe0..3d3a6d86fd 100644
--- a/package/utils/busybox/config/miscutils/Config.in
+++ b/package/utils/busybox/config/miscutils/Config.in
@@ -7,13 +7,13 @@
menu "Miscellaneous Utilities"
config BUSYBOX_CONFIG_ADJTIMEX
- bool "adjtimex (4.7 kb)"
+ bool "adjtimex (4.9 kb)"
default BUSYBOX_DEFAULT_ADJTIMEX
help
Adjtimex reads and optionally sets adjustment parameters for
the Linux clock adjustment algorithm.
config BUSYBOX_CONFIG_ASCII
- bool "ascii"
+ bool "ascii (784 bytes)"
default BUSYBOX_DEFAULT_ASCII
help
Print ascii table.
@@ -39,7 +39,7 @@ config BUSYBOX_CONFIG_FEATURE_COMPRESS_BBCONFIG
and have very little memory, this might not be a win. Otherwise,
you probably want this.
config BUSYBOX_CONFIG_BC
- bool "bc (45 kb)"
+ bool "bc (38 kb)"
default BUSYBOX_DEFAULT_BC
select BUSYBOX_CONFIG_FEATURE_DC_BIG
help
@@ -63,7 +63,7 @@ config BUSYBOX_CONFIG_BC
5) "read()" accepts expressions, not only numeric literals.
config BUSYBOX_CONFIG_DC
- bool "dc (36 kb)"
+ bool "dc (29 kb)"
default BUSYBOX_DEFAULT_DC
help
dc is a reverse-polish notation command-line calculator which
@@ -125,7 +125,7 @@ config BUSYBOX_CONFIG_FEATURE_BC_LONG_OPTIONS
endif
config BUSYBOX_CONFIG_BEEP
- bool "beep (2.4 kb)"
+ bool "beep (2.7 kb)"
default BUSYBOX_DEFAULT_BEEP
help
The beep applets beeps in a given freq/Hz.
@@ -146,7 +146,7 @@ config BUSYBOX_CONFIG_FEATURE_BEEP_LENGTH_MS
help
Length in ms for default beep.
config BUSYBOX_CONFIG_CHAT
- bool "chat (6.3 kb)"
+ bool "chat (6.7 kb)"
default BUSYBOX_DEFAULT_CHAT
help
Simple chat utility.
@@ -220,7 +220,7 @@ config BUSYBOX_CONFIG_CONSPY
or conspy -nd NUM screenshot of console num
or conspy -cs NUM poor man's GNU screen like
config BUSYBOX_CONFIG_CROND
- bool "crond (14 kb)"
+ bool "crond (15 kb)"
default BUSYBOX_DEFAULT_CROND
select BUSYBOX_CONFIG_FEATURE_SYSLOG
help
@@ -330,7 +330,7 @@ config BUSYBOX_CONFIG_FEATURE_DEVFS
/dev/loop0. If your /dev directory has normal names instead of
devfs names, you don't want this.
config BUSYBOX_CONFIG_DEVMEM
- bool "devmem (2.5 kb)"
+ bool "devmem (2.7 kb)"
default BUSYBOX_DEFAULT_DEVMEM
help
devmem is a small program that reads and writes from physical
@@ -380,6 +380,11 @@ config BUSYBOX_CONFIG_FLASHCP
help
The flashcp binary, inspired by mtd-utils as of git head 5eceb74f7.
This utility is used to copy images into a MTD device.
+config BUSYBOX_CONFIG_GETFATTR
+ bool "getfattr (12.3 kb)"
+ default BUSYBOX_DEFAULT_GETFATTR
+ help
+ Get extended attributes on files
config BUSYBOX_CONFIG_HDPARM
bool "hdparm (25 kb)"
default BUSYBOX_DEFAULT_HDPARM
@@ -437,36 +442,36 @@ config BUSYBOX_CONFIG_FEATURE_HDPARM_HDIO_GETSET_DMA
help
Enable the 'hdparm -d' option to get/set using_dma flag.
config BUSYBOX_CONFIG_HEXEDIT
- bool "hexedit (21 kb)"
+ bool "hexedit (15 kb)"
default BUSYBOX_DEFAULT_HEXEDIT
help
Edit file in hexadecimal.
config BUSYBOX_CONFIG_I2CGET
- bool "i2cget (5.5 kb)"
+ bool "i2cget (5.7 kb)"
default BUSYBOX_DEFAULT_I2CGET
help
Read from I2C/SMBus chip registers.
config BUSYBOX_CONFIG_I2CSET
- bool "i2cset (6.7 kb)"
+ bool "i2cset (6.9 kb)"
default BUSYBOX_DEFAULT_I2CSET
help
Set I2C registers.
config BUSYBOX_CONFIG_I2CDUMP
- bool "i2cdump (7.1 kb)"
+ bool "i2cdump (7.2 kb)"
default BUSYBOX_DEFAULT_I2CDUMP
help
Examine I2C registers.
config BUSYBOX_CONFIG_I2CDETECT
- bool "i2cdetect (7.1 kb)"
+ bool "i2cdetect (7.3 kb)"
default BUSYBOX_DEFAULT_I2CDETECT
help
Detect I2C chips.
config BUSYBOX_CONFIG_I2CTRANSFER
- bool "i2ctransfer (4.0 kb)"
+ bool "i2ctransfer (5.5 kb)"
default BUSYBOX_DEFAULT_I2CTRANSFER
help
Send user-defined I2C messages in one transfer.
@@ -579,7 +584,7 @@ config BUSYBOX_CONFIG_LOCK
help
Small utility for using locks in scripts
config BUSYBOX_CONFIG_LSSCSI
- bool "lsscsi (2.5 kb)"
+ bool "lsscsi (2.9 kb)"
default BUSYBOX_DEFAULT_LSSCSI
help
lsscsi is a utility for displaying information about SCSI buses in the
@@ -587,7 +592,7 @@ config BUSYBOX_CONFIG_LSSCSI
This version uses sysfs (/sys/bus/scsi/devices) only.
config BUSYBOX_CONFIG_MAKEDEVS
- bool "makedevs (9.2 kb)"
+ bool "makedevs (9.4 kb)"
default BUSYBOX_DEFAULT_MAKEDEVS
help
'makedevs' is a utility used to create a batch of devices with
@@ -623,7 +628,7 @@ config BUSYBOX_CONFIG_MAN
help
Format and display manual pages.
config BUSYBOX_CONFIG_MICROCOM
- bool "microcom (5.7 kb)"
+ bool "microcom (5.9 kb)"
default BUSYBOX_DEFAULT_MICROCOM
help
The poor man's minicom utility for chatting with serial port devices.
@@ -635,36 +640,36 @@ config BUSYBOX_CONFIG_MIM
Run a script from a Makefile-like specification file.
Unlike 'make' dependencies aren't supported.
config BUSYBOX_CONFIG_MT
- bool "mt (2.5 kb)"
+ bool "mt (2.7 kb)"
default BUSYBOX_DEFAULT_MT
help
mt is used to control tape devices. You can use the mt utility
to advance or rewind a tape past a specified number of archive
files on the tape.
config BUSYBOX_CONFIG_NANDWRITE
- bool "nandwrite (4.8 kb)"
+ bool "nandwrite (5 kb)"
default BUSYBOX_DEFAULT_NANDWRITE
help
Write to the specified MTD device, with bad blocks awareness
config BUSYBOX_CONFIG_NANDDUMP
- bool "nanddump (5.2 kb)"
+ bool "nanddump (5.4 kb)"
default BUSYBOX_DEFAULT_NANDDUMP
help
Dump the content of raw NAND chip
config BUSYBOX_CONFIG_PARTPROBE
- bool "partprobe (3.5 kb)"
+ bool "partprobe (3.7 kb)"
default BUSYBOX_DEFAULT_PARTPROBE
help
Ask kernel to rescan partition table.
config BUSYBOX_CONFIG_RAIDAUTORUN
- bool "raidautorun (1.3 kb)"
+ bool "raidautorun (1.6 kb)"
default BUSYBOX_DEFAULT_RAIDAUTORUN
help
raidautorun tells the kernel md driver to
search and start RAID arrays.
config BUSYBOX_CONFIG_READAHEAD
- bool "readahead (1.5 kb)"
+ bool "readahead (1.7 kb)"
default BUSYBOX_DEFAULT_READAHEAD
depends on BUSYBOX_CONFIG_LFS
help
@@ -691,7 +696,7 @@ config BUSYBOX_CONFIG_RFKILL
rfkill block|unblock wlan : block/unblock all wlan(wifi) devices
config BUSYBOX_CONFIG_RUNLEVEL
- bool "runlevel (559 bytes)"
+ bool "runlevel (837 bytes)"
default BUSYBOX_DEFAULT_RUNLEVEL
depends on BUSYBOX_CONFIG_FEATURE_UTMP
help
@@ -700,50 +705,50 @@ config BUSYBOX_CONFIG_RUNLEVEL
This applet uses utmp but does not rely on busybox supporing
utmp on purpose. It is used by e.g. emdebian via /etc/init.d/rc.
config BUSYBOX_CONFIG_RX
- bool "rx (2.9 kb)"
+ bool "rx (3.2 kb)"
default BUSYBOX_DEFAULT_RX
help
Receive files using the Xmodem protocol.
config BUSYBOX_CONFIG_SEEDRNG
- bool "seedrng (1.3 kb)"
+ bool "seedrng (9.1 kb)"
default BUSYBOX_DEFAULT_SEEDRNG
help
Seed the kernel RNG from seed files, meant to be called
once during startup, once during shutdown, and optionally
at some periodic interval in between.
config BUSYBOX_CONFIG_SETFATTR
- bool "setfattr (3.7 kb)"
+ bool "setfattr (3.9 kb)"
default BUSYBOX_DEFAULT_SETFATTR
help
Set/delete extended attributes on files
config BUSYBOX_CONFIG_SETSERIAL
- bool "setserial (6.9 kb)"
+ bool "setserial (7.1 kb)"
default BUSYBOX_DEFAULT_SETSERIAL
help
Retrieve or set Linux serial port.
config BUSYBOX_CONFIG_STRINGS
- bool "strings (4.6 kb)"
+ bool "strings (4.8 kb)"
default BUSYBOX_DEFAULT_STRINGS
help
strings prints the printable character sequences for each file
specified.
config BUSYBOX_CONFIG_TIME
- bool "time (6.8 kb)"
+ bool "time (8.1 kb)"
default BUSYBOX_DEFAULT_TIME
help
The time command runs the specified program with the given arguments.
When the command finishes, time writes a message to standard output
giving timing statistics about this program run.
config BUSYBOX_CONFIG_TREE
- bool "tree (0.6 kb)"
+ bool "tree (2.5 kb)"
default BUSYBOX_DEFAULT_TREE
help
List files and directories in a tree structure.
config BUSYBOX_CONFIG_TS
- bool "ts (450 bytes)"
+ bool "ts (4.4 kb)"
default BUSYBOX_DEFAULT_TS
config BUSYBOX_CONFIG_TTYSIZE
- bool "ttysize (432 bytes)"
+ bool "ttysize (718 bytes)"
default BUSYBOX_DEFAULT_TTYSIZE
help
A replacement for "stty size". Unlike stty, can report only width,
@@ -751,52 +756,52 @@ config BUSYBOX_CONFIG_TTYSIZE
error, but returns default 80x24.
Usage in shell scripts: width=`ttysize w`.
config BUSYBOX_CONFIG_UBIATTACH
- bool "ubiattach (4.2 kb)"
+ bool "ubiattach (4.5 kb)"
default BUSYBOX_DEFAULT_UBIATTACH
help
Attach MTD device to an UBI device.
config BUSYBOX_CONFIG_UBIDETACH
- bool "ubidetach (4.1 kb)"
+ bool "ubidetach (4.3 kb)"
default BUSYBOX_DEFAULT_UBIDETACH
help
Detach MTD device from an UBI device.
config BUSYBOX_CONFIG_UBIMKVOL
- bool "ubimkvol (5.3 kb)"
+ bool "ubimkvol (5.5 kb)"
default BUSYBOX_DEFAULT_UBIMKVOL
help
Create a UBI volume.
config BUSYBOX_CONFIG_UBIRMVOL
- bool "ubirmvol (4.9 kb)"
+ bool "ubirmvol (5.1 kb)"
default BUSYBOX_DEFAULT_UBIRMVOL
help
Delete a UBI volume.
config BUSYBOX_CONFIG_UBIRSVOL
- bool "ubirsvol (4.2 kb)"
+ bool "ubirsvol (4.4 kb)"
default BUSYBOX_DEFAULT_UBIRSVOL
help
Resize a UBI volume.
config BUSYBOX_CONFIG_UBIUPDATEVOL
- bool "ubiupdatevol (5.2 kb)"
+ bool "ubiupdatevol (5.6 kb)"
default BUSYBOX_DEFAULT_UBIUPDATEVOL
help
Update a UBI volume.
config BUSYBOX_CONFIG_UBIRENAME
- bool "ubirename (2.4 kb)"
+ bool "ubirename (2.7 kb)"
default BUSYBOX_DEFAULT_UBIRENAME
help
Utility to rename UBI volumes
config BUSYBOX_CONFIG_VOLNAME
- bool "volname (1.6 kb)"
+ bool "volname (1.9 kb)"
default BUSYBOX_DEFAULT_VOLNAME
help
Prints a CD-ROM volume name.
config BUSYBOX_CONFIG_WATCHDOG
- bool "watchdog (5.3 kb)"
+ bool "watchdog (5.7 kb)"
default BUSYBOX_DEFAULT_WATCHDOG
help
The watchdog utility is used with hardware or software watchdog
diff --git a/package/utils/busybox/config/modutils/Config.in b/package/utils/busybox/config/modutils/Config.in
index e3538081ea..58a0e5b767 100644
--- a/package/utils/busybox/config/modutils/Config.in
+++ b/package/utils/busybox/config/modutils/Config.in
@@ -47,7 +47,7 @@ config BUSYBOX_CONFIG_INSMOD
help
insmod is used to load specified modules in the running kernel.
config BUSYBOX_CONFIG_LSMOD
- bool "lsmod (1.9 kb)"
+ bool "lsmod (2.1 kb)"
default BUSYBOX_DEFAULT_LSMOD
help
lsmod is used to display a list of loaded modules.
@@ -66,7 +66,7 @@ config BUSYBOX_CONFIG_MODINFO
help
Show information about a Linux Kernel module
config BUSYBOX_CONFIG_MODPROBE
- bool "modprobe (28 kb)"
+ bool "modprobe (27 kb)"
default BUSYBOX_DEFAULT_MODPROBE
help
Handle the loading of modules, and their dependencies on a high
@@ -83,7 +83,7 @@ config BUSYBOX_CONFIG_FEATURE_MODPROBE_BLACKLIST
hardware autodetection scripts to load modules like evdev, frame
buffer drivers etc.
config BUSYBOX_CONFIG_RMMOD
- bool "rmmod (3.3 kb)"
+ bool "rmmod (3.5 kb)"
default BUSYBOX_DEFAULT_RMMOD
help
rmmod is used to unload specified modules from the kernel.
diff --git a/package/utils/busybox/config/networking/Config.in b/package/utils/busybox/config/networking/Config.in
index 861e4f9bbf..fd2b4efd85 100644
--- a/package/utils/busybox/config/networking/Config.in
+++ b/package/utils/busybox/config/networking/Config.in
@@ -90,12 +90,12 @@ config BUSYBOX_CONFIG_ARP
help
Manipulate the system ARP cache.
config BUSYBOX_CONFIG_ARPING
- bool "arping (9 kb)"
+ bool "arping (9.1 kb)"
default BUSYBOX_DEFAULT_ARPING
help
Ping hosts by ARP packets.
config BUSYBOX_CONFIG_BRCTL
- bool "brctl (4.7 kb)"
+ bool "brctl (9.9 kb)"
default BUSYBOX_DEFAULT_BRCTL
help
Manage ethernet bridges.
@@ -120,12 +120,12 @@ config BUSYBOX_CONFIG_FEATURE_BRCTL_SHOW
Add support for option which prints the current config:
show
config BUSYBOX_CONFIG_DNSD
- bool "dnsd (9.8 kb)"
+ bool "dnsd (10 kb)"
default BUSYBOX_DEFAULT_DNSD
help
Small and static DNS server daemon.
config BUSYBOX_CONFIG_ETHER_WAKE
- bool "ether-wake (4.9 kb)"
+ bool "ether-wake (5.2 kb)"
default BUSYBOX_DEFAULT_ETHER_WAKE
help
Send a magic packet to wake up sleeping machines.
@@ -167,13 +167,13 @@ config BUSYBOX_CONFIG_FEATURE_FTPD_AUTHENTICATION
of the user it was started under, and does not require login.
Take care to not launch it under root.
config BUSYBOX_CONFIG_FTPGET
- bool "ftpget (7.8 kb)"
+ bool "ftpget (7.9 kb)"
default BUSYBOX_DEFAULT_FTPGET
help
Retrieve a remote file via FTP.
config BUSYBOX_CONFIG_FTPPUT
- bool "ftpput (7.5 kb)"
+ bool "ftpput (7.6 kb)"
default BUSYBOX_DEFAULT_FTPPUT
help
Store a remote file via FTP.
@@ -183,13 +183,13 @@ config BUSYBOX_CONFIG_FEATURE_FTPGETPUT_LONG_OPTIONS
default BUSYBOX_DEFAULT_FEATURE_FTPGETPUT_LONG_OPTIONS
depends on BUSYBOX_CONFIG_LONG_OPTS && (BUSYBOX_CONFIG_FTPGET || BUSYBOX_CONFIG_FTPPUT)
config BUSYBOX_CONFIG_HOSTNAME
- bool "hostname (5.5 kb)"
+ bool "hostname (5.8 kb)"
default BUSYBOX_DEFAULT_HOSTNAME
help
Show or set the system's host name.
config BUSYBOX_CONFIG_DNSDOMAINNAME
- bool "dnsdomainname (3.6 kb)"
+ bool "dnsdomainname (3.8 kb)"
default BUSYBOX_DEFAULT_DNSDOMAINNAME
help
Alias to "hostname -d".
@@ -408,7 +408,7 @@ config BUSYBOX_CONFIG_IFENSLAVE
Userspace application to bind several interfaces
to a logical interface (use with kernel bonding driver).
config BUSYBOX_CONFIG_IFPLUGD
- bool "ifplugd (10 kb)"
+ bool "ifplugd (11 kb)"
default BUSYBOX_DEFAULT_IFPLUGD
help
Network interface plug detection daemon.
@@ -569,7 +569,7 @@ config BUSYBOX_CONFIG_IP
trying to be portable, it's better to use "ip CMD" forms.
config BUSYBOX_CONFIG_IPADDR
- bool "ipaddr (14 kb)"
+ bool "ipaddr (15 kb)"
default BUSYBOX_DEFAULT_IPADDR
select BUSYBOX_CONFIG_FEATURE_IP_ADDRESS
help
@@ -590,7 +590,7 @@ config BUSYBOX_CONFIG_IPROUTE
Short form of "ip route"
config BUSYBOX_CONFIG_IPTUNNEL
- bool "iptunnel (9.6 kb)"
+ bool "iptunnel (9.8 kb)"
default BUSYBOX_DEFAULT_IPTUNNEL
select BUSYBOX_CONFIG_FEATURE_IP_TUNNEL
help
@@ -604,7 +604,7 @@ config BUSYBOX_CONFIG_IPRULE
Short form of "ip rule"
config BUSYBOX_CONFIG_IPNEIGH
- bool "ipneigh (8.3 kb)"
+ bool "ipneigh (8.6 kb)"
default BUSYBOX_DEFAULT_IPNEIGH
select BUSYBOX_CONFIG_FEATURE_IP_NEIGH
help
@@ -624,6 +624,12 @@ config BUSYBOX_CONFIG_FEATURE_IP_LINK
help
Configure network devices with "ip".
+config BUSYBOX_CONFIG_FEATURE_IP_LINK_CAN
+ bool "ip link set type can"
+ default BUSYBOX_DEFAULT_FEATURE_IP_LINK_CAN
+ help
+ Configure CAN devices with "ip".
+
config BUSYBOX_CONFIG_FEATURE_IP_ROUTE
bool "ip route"
default BUSYBOX_DEFAULT_FEATURE_IP_ROUTE
@@ -669,7 +675,7 @@ config BUSYBOX_CONFIG_FEATURE_IP_RARE_PROTOCOLS
Ethernet, wireless, infrared, ppp/slip, ip tunnelling
link types are supported without this option selected.
config BUSYBOX_CONFIG_IPCALC
- bool "ipcalc (4.4 kb)"
+ bool "ipcalc (4.6 kb)"
default BUSYBOX_DEFAULT_IPCALC
help
ipcalc takes an IP address and netmask and calculates the
@@ -688,14 +694,14 @@ config BUSYBOX_CONFIG_FEATURE_IPCALC_FANCY
Adds the options hostname, prefix and silent to the output of
"ipcalc".
config BUSYBOX_CONFIG_FAKEIDENTD
- bool "fakeidentd (8.7 kb)"
+ bool "fakeidentd (9 kb)"
default BUSYBOX_DEFAULT_FAKEIDENTD
select BUSYBOX_CONFIG_FEATURE_SYSLOG
help
fakeidentd listens on the ident port and returns a predefined
fake value on any query.
config BUSYBOX_CONFIG_NAMEIF
- bool "nameif (6.6 kb)"
+ bool "nameif (6.9 kb)"
default BUSYBOX_DEFAULT_NAMEIF
select BUSYBOX_CONFIG_FEATURE_SYSLOG
help
@@ -723,7 +729,7 @@ config BUSYBOX_CONFIG_FEATURE_NAMEIF_EXTENDED
new_interface_name mac=00:80:C8:38:91:B5
new_interface_name 00:80:C8:38:91:B5
config BUSYBOX_CONFIG_NBDCLIENT
- bool "nbd-client (6 kb)"
+ bool "nbd-client (6.3 kb)"
default BUSYBOX_DEFAULT_NBDCLIENT
help
Network block device client
@@ -792,7 +798,7 @@ config BUSYBOX_CONFIG_FEATURE_NETSTAT_PRG
Add support for -p flag to print out PID and program name.
+700 bytes of code.
config BUSYBOX_CONFIG_NSLOOKUP
- bool "nslookup (9.7 kb)"
+ bool "nslookup (10 kb)"
default BUSYBOX_DEFAULT_NSLOOKUP
help
nslookup is a tool to query Internet name servers.
@@ -807,7 +813,7 @@ config BUSYBOX_CONFIG_FEATURE_NSLOOKUP_LONG_OPTIONS
default BUSYBOX_DEFAULT_FEATURE_NSLOOKUP_LONG_OPTIONS
depends on BUSYBOX_CONFIG_FEATURE_NSLOOKUP_BIG && BUSYBOX_CONFIG_LONG_OPTS
config BUSYBOX_CONFIG_NTPD
- bool "ntpd (22 kb)"
+ bool "ntpd (23 kb)"
default BUSYBOX_DEFAULT_NTPD
help
The NTP client/server daemon.
@@ -855,22 +861,22 @@ config BUSYBOX_CONFIG_FEATURE_FANCY_PING
or terminate with SIGALRM in 5 seconds otherwise.
No command-line options will be recognized.
config BUSYBOX_CONFIG_PSCAN
- bool "pscan (6 kb)"
+ bool "pscan (6.2 kb)"
default BUSYBOX_DEFAULT_PSCAN
help
Simple network port scanner.
config BUSYBOX_CONFIG_ROUTE
- bool "route (8.7 kb)"
+ bool "route (9 kb)"
default BUSYBOX_DEFAULT_ROUTE
help
Route displays or manipulates the kernel's IP routing tables.
config BUSYBOX_CONFIG_SLATTACH
- bool "slattach (6.2 kb)"
+ bool "slattach (6.3 kb)"
default BUSYBOX_DEFAULT_SLATTACH
help
slattach configures serial line as SLIP network interface.
config BUSYBOX_CONFIG_SSL_CLIENT
- bool "ssl_client (25 kb)"
+ bool "ssl_client (28 kb)"
default BUSYBOX_DEFAULT_SSL_CLIENT
select BUSYBOX_CONFIG_TLS
help
@@ -929,7 +935,7 @@ config BUSYBOX_CONFIG_FEATURE_TELNET_WIDTH
default BUSYBOX_DEFAULT_FEATURE_TELNET_WIDTH
depends on BUSYBOX_CONFIG_TELNET
config BUSYBOX_CONFIG_TELNETD
- bool "telnetd (12 kb)"
+ bool "telnetd (13 kb)"
default BUSYBOX_DEFAULT_TELNETD
select BUSYBOX_CONFIG_FEATURE_SYSLOG
help
@@ -1076,7 +1082,7 @@ config BUSYBOX_CONFIG_TRACEROUTE
Utility to trace the route of IP packets.
config BUSYBOX_CONFIG_TRACEROUTE6
- bool "traceroute6 (13 kb)"
+ bool "traceroute6 (12 kb)"
default BUSYBOX_DEFAULT_TRACEROUTE6
depends on BUSYBOX_CONFIG_FEATURE_IPV6
help
@@ -1095,7 +1101,7 @@ config BUSYBOX_CONFIG_FEATURE_TRACEROUTE_USE_ICMP
default BUSYBOX_DEFAULT_FEATURE_TRACEROUTE_USE_ICMP
depends on BUSYBOX_CONFIG_TRACEROUTE || BUSYBOX_CONFIG_TRACEROUTE6
config BUSYBOX_CONFIG_TUNCTL
- bool "tunctl (6.2 kb)"
+ bool "tunctl (6.4 kb)"
default BUSYBOX_DEFAULT_TUNCTL
help
tunctl creates or deletes tun devices.
@@ -1108,12 +1114,12 @@ config BUSYBOX_CONFIG_FEATURE_TUNCTL_UG
Allow to specify owner and group of newly created interface.
340 bytes of pure bloat. Say no here.
config BUSYBOX_CONFIG_VCONFIG
- bool "vconfig (2.3 kb)"
+ bool "vconfig (2.6 kb)"
default BUSYBOX_DEFAULT_VCONFIG
help
Creates, removes, and configures VLAN interfaces
config BUSYBOX_CONFIG_WGET
- bool "wget (38 kb)"
+ bool "wget (41 kb)"
default BUSYBOX_DEFAULT_WGET
help
wget is a utility for non-interactive download of files from HTTP
@@ -1233,12 +1239,12 @@ config BUSYBOX_CONFIG_FEATURE_WGET_OPENSSL
By default TLS verification is performed, unless
--no-check-certificate option is passed.
config BUSYBOX_CONFIG_WHOIS
- bool "whois (6.3 kb)"
+ bool "whois (6.5 kb)"
default BUSYBOX_DEFAULT_WHOIS
help
whois is a client for the whois directory service
config BUSYBOX_CONFIG_ZCIP
- bool "zcip (8.4 kb)"
+ bool "zcip (8.7 kb)"
default BUSYBOX_DEFAULT_ZCIP
select BUSYBOX_CONFIG_FEATURE_SYSLOG
help
diff --git a/package/utils/busybox/config/networking/udhcp/Config.in b/package/utils/busybox/config/networking/udhcp/Config.in
index 6757f1efc9..3c88e285a1 100644
--- a/package/utils/busybox/config/networking/udhcp/Config.in
+++ b/package/utils/busybox/config/networking/udhcp/Config.in
@@ -11,6 +11,13 @@ config BUSYBOX_CONFIG_UDHCPD
udhcpd is a DHCP server geared primarily toward embedded systems,
while striving to be fully functional and RFC compliant.
+config BUSYBOX_CONFIG_FEATURE_UDHCPD_BOOTP
+ bool "Answer to BOOTP requests as well"
+ default BUSYBOX_DEFAULT_FEATURE_UDHCPD_BOOTP
+ depends on BUSYBOX_CONFIG_UDHCPD
+ help
+ Support old BOOTP protocol too.
+
config BUSYBOX_CONFIG_FEATURE_UDHCPD_BASE_IP_ON_MAC
bool "Select IP address based on client MAC"
default BUSYBOX_DEFAULT_FEATURE_UDHCPD_BASE_IP_ON_MAC
@@ -44,7 +51,7 @@ config BUSYBOX_CONFIG_DHCPD_LEASES_FILE
of the file. Normally it is safe to leave it untouched.
config BUSYBOX_CONFIG_DUMPLEASES
- bool "dumpleases (5.1 kb)"
+ bool "dumpleases (5.3 kb)"
default BUSYBOX_DEFAULT_DUMPLEASES
help
dumpleases displays the leases written out by the udhcpd.
@@ -52,7 +59,7 @@ config BUSYBOX_CONFIG_DUMPLEASES
by the absolute time that it expires in seconds from epoch.
config BUSYBOX_CONFIG_DHCPRELAY
- bool "dhcprelay (5.2 kb)"
+ bool "dhcprelay (5.5 kb)"
default BUSYBOX_DEFAULT_DHCPRELAY
help
dhcprelay listens for DHCP requests on one or more interfaces
diff --git a/package/utils/busybox/config/printutils/Config.in b/package/utils/busybox/config/printutils/Config.in
index c53ee19d4e..a320c87ebc 100644
--- a/package/utils/busybox/config/printutils/Config.in
+++ b/package/utils/busybox/config/printutils/Config.in
@@ -7,18 +7,18 @@
menu "Print Utilities"
config BUSYBOX_CONFIG_LPD
- bool "lpd (5.5 kb)"
+ bool "lpd (5.7 kb)"
default BUSYBOX_DEFAULT_LPD
help
lpd is a print spooling daemon.
config BUSYBOX_CONFIG_LPR
- bool "lpr (9.9 kb)"
+ bool "lpr (10 kb)"
default BUSYBOX_DEFAULT_LPR
help
lpr sends files (or standard input) to a print spooling daemon.
config BUSYBOX_CONFIG_LPQ
- bool "lpq (9.9 kb)"
+ bool "lpq (10 kb)"
default BUSYBOX_DEFAULT_LPQ
help
lpq is a print spool queue examination and manipulation program.
diff --git a/package/utils/busybox/config/procps/Config.in b/package/utils/busybox/config/procps/Config.in
index 0501daf8fd..1524f61dc7 100644
--- a/package/utils/busybox/config/procps/Config.in
+++ b/package/utils/busybox/config/procps/Config.in
@@ -22,26 +22,26 @@ config BUSYBOX_CONFIG_FEATURE_SHOW_THREADS
and 'h' command in top.
config BUSYBOX_CONFIG_FREE
- bool "free (3.1 kb)"
+ bool "free (3.8 kb)"
default BUSYBOX_DEFAULT_FREE
help
free displays the total amount of free and used physical and swap
memory in the system, as well as the buffers used by the kernel.
The shared memory column should be ignored; it is obsolete.
config BUSYBOX_CONFIG_FUSER
- bool "fuser (7 kb)"
+ bool "fuser (7.3 kb)"
default BUSYBOX_DEFAULT_FUSER
help
fuser lists all PIDs (Process IDs) that currently have a given
file open. fuser can also list all PIDs that have a given network
(TCP or UDP) port open.
config BUSYBOX_CONFIG_IOSTAT
- bool "iostat (7.6 kb)"
+ bool "iostat (8 kb)"
default BUSYBOX_DEFAULT_IOSTAT
help
Report CPU and I/O statistics
config BUSYBOX_CONFIG_KILL
- bool "kill (3.1 kb)"
+ bool "kill (3.4 kb)"
default BUSYBOX_DEFAULT_KILL
help
The command kill sends the specified signal to the specified
@@ -49,7 +49,7 @@ config BUSYBOX_CONFIG_KILL
signal is sent.
config BUSYBOX_CONFIG_KILLALL
- bool "killall (5.6 kb)"
+ bool "killall (5.9 kb)"
default BUSYBOX_DEFAULT_KILLALL
help
killall sends a signal to all processes running any of the
@@ -57,7 +57,7 @@ config BUSYBOX_CONFIG_KILLALL
sent.
config BUSYBOX_CONFIG_KILLALL5
- bool "killall5 (5.3 kb)"
+ bool "killall5 (5.6 kb)"
default BUSYBOX_DEFAULT_KILLALL5
help
The SystemV killall command. killall5 sends a signal
@@ -65,34 +65,34 @@ config BUSYBOX_CONFIG_KILLALL5
in its own session, so it won't kill the shell that is running
the script it was called from.
config BUSYBOX_CONFIG_LSOF
- bool "lsof (3.4 kb)"
+ bool "lsof (3.7 kb)"
default BUSYBOX_DEFAULT_LSOF
help
Show open files in the format of:
PID <TAB> /path/to/executable <TAB> /path/to/opened/file
config BUSYBOX_CONFIG_MPSTAT
- bool "mpstat (9.8 kb)"
+ bool "mpstat (10 kb)"
default BUSYBOX_DEFAULT_MPSTAT
help
Per-processor statistics
config BUSYBOX_CONFIG_NMETER
- bool "nmeter (11 kb)"
+ bool "nmeter (12 kb)"
default BUSYBOX_DEFAULT_NMETER
help
Prints selected system stats continuously, one line per update.
config BUSYBOX_CONFIG_PGREP
- bool "pgrep (6.5 kb)"
+ bool "pgrep (6.8 kb)"
default BUSYBOX_DEFAULT_PGREP
help
Look for processes by name.
config BUSYBOX_CONFIG_PKILL
- bool "pkill (7.5 kb)"
+ bool "pkill (7.8 kb)"
default BUSYBOX_DEFAULT_PKILL
help
Send signals to processes by name.
config BUSYBOX_CONFIG_PIDOF
- bool "pidof (6.3 kb)"
+ bool "pidof (6.5 kb)"
default BUSYBOX_DEFAULT_PIDOF
help
Pidof finds the process id's (pids) of the named programs. It prints
@@ -114,12 +114,12 @@ config BUSYBOX_CONFIG_FEATURE_PIDOF_OMIT
The special pid %PPID can be used to name the parent process
of the pidof, in other words the calling shell or shell script.
config BUSYBOX_CONFIG_PMAP
- bool "pmap (6 kb)"
+ bool "pmap (6.2 kb)"
default BUSYBOX_DEFAULT_PMAP
help
Display processes' memory mappings.
config BUSYBOX_CONFIG_POWERTOP
- bool "powertop (9.6 kb)"
+ bool "powertop (9.9 kb)"
default BUSYBOX_DEFAULT_POWERTOP
help
Analyze power consumption on Intel-based laptops
@@ -132,7 +132,7 @@ config BUSYBOX_CONFIG_FEATURE_POWERTOP_INTERACTIVE
Without this, powertop will only refresh display every 10 seconds.
No keyboard commands will work, only ^C to terminate.
config BUSYBOX_CONFIG_PS
- bool "ps (11 kb)"
+ bool "ps (12 kb)"
default BUSYBOX_DEFAULT_PS
help
ps gives a snapshot of the current processes.
@@ -172,23 +172,23 @@ config BUSYBOX_CONFIG_FEATURE_PS_ADDITIONAL_COLUMNS
default BUSYBOX_DEFAULT_FEATURE_PS_ADDITIONAL_COLUMNS
depends on (BUSYBOX_CONFIG_PS || BUSYBOX_CONFIG_MINIPS) && BUSYBOX_CONFIG_DESKTOP
config BUSYBOX_CONFIG_PSTREE
- bool "pstree (9.3 kb)"
+ bool "pstree (9.4 kb)"
default BUSYBOX_DEFAULT_PSTREE
help
Display a tree of processes.
config BUSYBOX_CONFIG_PWDX
- bool "pwdx (3.7 kb)"
+ bool "pwdx (3.9 kb)"
default BUSYBOX_DEFAULT_PWDX
help
Report current working directory of a process
config BUSYBOX_CONFIG_SMEMCAP
- bool "smemcap (2.5 kb)"
+ bool "smemcap (3 kb)"
default BUSYBOX_DEFAULT_SMEMCAP
help
smemcap is a tool for capturing process data for smem,
a memory usage statistic tool.
config BUSYBOX_CONFIG_BB_SYSCTL
- bool "sysctl (7.4 kb)"
+ bool "sysctl (7.9 kb)"
default BUSYBOX_DEFAULT_BB_SYSCTL
help
Configure kernel parameters at runtime.
@@ -254,7 +254,7 @@ config BUSYBOX_CONFIG_FEATURE_TOPMEM
help
Enable 's' in top (gives lots of memory info).
config BUSYBOX_CONFIG_UPTIME
- bool "uptime (3.7 kb)"
+ bool "uptime (4 kb)"
default BUSYBOX_DEFAULT_UPTIME
help
uptime gives a one line display of the current time, how long
@@ -268,7 +268,7 @@ config BUSYBOX_CONFIG_FEATURE_UPTIME_UTMP_SUPPORT
help
Display the number of users currently logged on.
config BUSYBOX_CONFIG_WATCH
- bool "watch (4.4 kb)"
+ bool "watch (5.2 kb)"
default BUSYBOX_DEFAULT_WATCH
help
watch is used to execute a program periodically, showing
diff --git a/package/utils/busybox/config/runit/Config.in b/package/utils/busybox/config/runit/Config.in
index 2c701f2136..3a95619bde 100644
--- a/package/utils/busybox/config/runit/Config.in
+++ b/package/utils/busybox/config/runit/Config.in
@@ -7,44 +7,44 @@
menu "Runit Utilities"
config BUSYBOX_CONFIG_CHPST
- bool "chpst (9 kb)"
+ bool "chpst (9.2 kb)"
default BUSYBOX_DEFAULT_CHPST
help
chpst changes the process state according to the given options, and
execs specified program.
config BUSYBOX_CONFIG_SETUIDGID
- bool "setuidgid (4 kb)"
+ bool "setuidgid (4.2 kb)"
default BUSYBOX_DEFAULT_SETUIDGID
help
Sets soft resource limits as specified by options
config BUSYBOX_CONFIG_ENVUIDGID
- bool "envuidgid (3.9 kb)"
+ bool "envuidgid (4.1 kb)"
default BUSYBOX_DEFAULT_ENVUIDGID
help
Sets $UID to account's uid and $GID to account's gid
config BUSYBOX_CONFIG_ENVDIR
- bool "envdir (2.5 kb)"
+ bool "envdir (2.9 kb)"
default BUSYBOX_DEFAULT_ENVDIR
help
Sets various environment variables as specified by files
in the given directory
config BUSYBOX_CONFIG_SOFTLIMIT
- bool "softlimit (4.5 kb)"
+ bool "softlimit (4.7 kb)"
default BUSYBOX_DEFAULT_SOFTLIMIT
help
Sets soft resource limits as specified by options
config BUSYBOX_CONFIG_RUNSV
- bool "runsv (7.8 kb)"
+ bool "runsv (8.2 kb)"
default BUSYBOX_DEFAULT_RUNSV
help
runsv starts and monitors a service and optionally an appendant log
service.
config BUSYBOX_CONFIG_RUNSVDIR
- bool "runsvdir (6.3 kb)"
+ bool "runsvdir (6.6 kb)"
default BUSYBOX_DEFAULT_RUNSVDIR
help
runsvdir starts a runsv process for each subdirectory, or symlink to
@@ -60,7 +60,7 @@ config BUSYBOX_CONFIG_FEATURE_RUNSVDIR_LOG
message (viewable via top/ps). Otherwise (feature is off
or no parameter), error messages go to stderr only.
config BUSYBOX_CONFIG_SV
- bool "sv (8.5 kb)"
+ bool "sv (8.7 kb)"
default BUSYBOX_DEFAULT_SV
help
sv reports the current status and controls the state of services
@@ -75,14 +75,14 @@ config BUSYBOX_CONFIG_SV_DEFAULT_SERVICE_DIR
Defaults to "/var/service"
config BUSYBOX_CONFIG_SVC
- bool "svc (8.4 kb)"
+ bool "svc (8.7 kb)"
default BUSYBOX_DEFAULT_SVC
help
svc controls the state of services monitored by the runsv supervisor.
It is compatible with daemontools command with the same name.
config BUSYBOX_CONFIG_SVOK
- bool "svok (1.5 kb)"
+ bool "svok (1.8 kb)"
default BUSYBOX_DEFAULT_SVOK
help
svok checks whether runsv supervisor is running.
diff --git a/package/utils/busybox/config/shell/Config.in b/package/utils/busybox/config/shell/Config.in
index a68e9114ef..848609a840 100644
--- a/package/utils/busybox/config/shell/Config.in
+++ b/package/utils/busybox/config/shell/Config.in
@@ -87,7 +87,7 @@ config BUSYBOX_CONFIG_SHELL_ASH
depends on !BUSYBOX_CONFIG_NOMMU
config BUSYBOX_CONFIG_ASH
- bool "ash (78 kb)"
+ bool "ash (80 kb)"
default BUSYBOX_DEFAULT_ASH
depends on !BUSYBOX_CONFIG_NOMMU
select BUSYBOX_CONFIG_SHELL_ASH
@@ -201,11 +201,6 @@ config BUSYBOX_CONFIG_ASH_TEST
default BUSYBOX_DEFAULT_ASH_TEST
depends on BUSYBOX_CONFIG_SHELL_ASH
-config BUSYBOX_CONFIG_ASH_SLEEP
- bool "sleep builtin"
- default BUSYBOX_DEFAULT_ASH_SLEEP
- depends on BUSYBOX_CONFIG_SHELL_ASH
-
config BUSYBOX_CONFIG_ASH_HELP
bool "help builtin"
default BUSYBOX_DEFAULT_ASH_HELP
@@ -227,7 +222,7 @@ config BUSYBOX_CONFIG_ASH_CMDCMD
endif # ash options
config BUSYBOX_CONFIG_CTTYHACK
- bool "cttyhack (2.4 kb)"
+ bool "cttyhack (2.7 kb)"
default BUSYBOX_DEFAULT_CTTYHACK
help
One common problem reported on the mailing list is the "can't
@@ -270,7 +265,7 @@ config BUSYBOX_CONFIG_CTTYHACK
# getty 115200 $(cttyhack)
config BUSYBOX_CONFIG_HUSH
- bool "hush (68 kb)"
+ bool "hush (70 kb)"
default BUSYBOX_DEFAULT_HUSH
select BUSYBOX_CONFIG_SHELL_HUSH
help
diff --git a/package/utils/busybox/config/sysklogd/Config.in b/package/utils/busybox/config/sysklogd/Config.in
index 1aa2ea005f..5f982ed9c2 100644
--- a/package/utils/busybox/config/sysklogd/Config.in
+++ b/package/utils/busybox/config/sysklogd/Config.in
@@ -7,7 +7,7 @@
menu "System Logging Utilities"
config BUSYBOX_CONFIG_KLOGD
- bool "klogd (5.7 kb)"
+ bool "klogd (6.2 kb)"
default BUSYBOX_DEFAULT_KLOGD
help
klogd is a utility which intercepts and logs all
@@ -35,7 +35,7 @@ config BUSYBOX_CONFIG_FEATURE_KLOGD_KLOGCTL
If in doubt, say 'Y'.
config BUSYBOX_CONFIG_LOGGER
- bool "logger (6.3 kb)"
+ bool "logger (6.5 kb)"
default BUSYBOX_DEFAULT_LOGGER
select BUSYBOX_CONFIG_FEATURE_SYSLOG
help
@@ -44,7 +44,7 @@ config BUSYBOX_CONFIG_LOGGER
they can be logged. This is generally used to help locate
problems that occur within programs and scripts.
config BUSYBOX_CONFIG_LOGREAD
- bool "logread (4.8 kb)"
+ bool "logread (5 kb)"
default BUSYBOX_DEFAULT_LOGREAD
help
If you enabled Circular Buffer support, you almost
@@ -64,7 +64,7 @@ config BUSYBOX_CONFIG_FEATURE_LOGREAD_REDUCED_LOCKING
contention at some minor memory expense.
config BUSYBOX_CONFIG_SYSLOGD
- bool "syslogd (13 kb)"
+ bool "syslogd (14 kb)"
default BUSYBOX_DEFAULT_SYSLOGD
help
The syslogd utility is used to record logs of all the
diff --git a/package/utils/busybox/config/util-linux/Config.in b/package/utils/busybox/config/util-linux/Config.in
index e3e59f1506..18ba619db0 100644
--- a/package/utils/busybox/config/util-linux/Config.in
+++ b/package/utils/busybox/config/util-linux/Config.in
@@ -7,7 +7,7 @@
menu "Linux System Utilities"
config BUSYBOX_CONFIG_ACPID
- bool "acpid (9 kb)"
+ bool "acpid (9.3 kb)"
default BUSYBOX_DEFAULT_ACPID
help
acpid listens to ACPI events coming either in textual form from
@@ -28,7 +28,7 @@ config BUSYBOX_CONFIG_FEATURE_ACPID_COMPAT
help
Accept and ignore compatibility options -g -m -s -S -v.
config BUSYBOX_CONFIG_BLKDISCARD
- bool "blkdiscard (4.3 kb)"
+ bool "blkdiscard (4.6 kb)"
default BUSYBOX_DEFAULT_BLKDISCARD
help
blkdiscard discards sectors on a given device.
@@ -46,23 +46,23 @@ config BUSYBOX_CONFIG_FEATURE_BLKID_TYPE
help
Show TYPE="filesystem type"
config BUSYBOX_CONFIG_BLOCKDEV
- bool "blockdev (2.3 kb)"
+ bool "blockdev (2.6 kb)"
default BUSYBOX_DEFAULT_BLOCKDEV
help
Performs some ioctls with block devices.
config BUSYBOX_CONFIG_CAL
- bool "cal (5.8 kb)"
+ bool "cal (6.1 kb)"
default BUSYBOX_DEFAULT_CAL
help
cal is used to display a monthly calendar.
config BUSYBOX_CONFIG_CHRT
- bool "chrt (4.7 kb)"
+ bool "chrt (5.1 kb)"
default BUSYBOX_DEFAULT_CHRT
help
Manipulate real-time attributes of a process.
This requires sched_{g,s}etparam support in your libc.
config BUSYBOX_CONFIG_DMESG
- bool "dmesg (3.7 kb)"
+ bool "dmesg (3.9 kb)"
default BUSYBOX_DEFAULT_DMESG
help
dmesg is used to examine or control the kernel ring buffer. When the
@@ -94,7 +94,7 @@ config BUSYBOX_CONFIG_FEATURE_DMESG_PRETTY
<6>BIOS-provided physical RAM map:
<6> BIOS-e820: 0000000000000000 - 000000000009f000 (usable)
config BUSYBOX_CONFIG_EJECT
- bool "eject (4 kb)"
+ bool "eject (4.3 kb)"
default BUSYBOX_DEFAULT_EJECT
help
Used to eject cdroms. (defaults to /dev/cdrom)
@@ -107,17 +107,17 @@ config BUSYBOX_CONFIG_FEATURE_EJECT_SCSI
Add the -s option to eject, this allows to eject SCSI-Devices and
usb-storage devices.
config BUSYBOX_CONFIG_FALLOCATE
- bool "fallocate (4.1 kb)"
+ bool "fallocate (4.3 kb)"
default BUSYBOX_DEFAULT_FALLOCATE
help
Preallocate space for files.
config BUSYBOX_CONFIG_FATATTR
- bool "fatattr (1.9 kb)"
+ bool "fatattr (2.2 kb)"
default BUSYBOX_DEFAULT_FATATTR
help
fatattr lists or changes the file attributes on a fat file system.
config BUSYBOX_CONFIG_FBSET
- bool "fbset (5.9 kb)"
+ bool "fbset (6.2 kb)"
default BUSYBOX_DEFAULT_FBSET
help
fbset is used to show or change the settings of a Linux frame buffer
@@ -144,12 +144,12 @@ config BUSYBOX_CONFIG_FEATURE_FBSET_READMODE
default BUSYBOX_DEFAULT_FEATURE_FBSET_READMODE /etc/fb.modes, which can be used to set frame buffer
device to pre-defined video modes.
config BUSYBOX_CONFIG_FDFORMAT
- bool "fdformat (4.4 kb)"
+ bool "fdformat (4.7 kb)"
default BUSYBOX_DEFAULT_FDFORMAT
help
fdformat is used to low-level format a floppy disk.
config BUSYBOX_CONFIG_FDISK
- bool "fdisk (37 kb)"
+ bool "fdisk (31 kb)"
default BUSYBOX_DEFAULT_FDISK
help
The fdisk utility is used to divide hard disks into one or more
@@ -222,18 +222,18 @@ config BUSYBOX_CONFIG_FEATURE_FDISK_ADVANCED
partition, and similarly evil things. Unless you have a very good
reason you would be wise to leave this disabled.
config BUSYBOX_CONFIG_FINDFS
- bool "findfs (12 kb)"
+ bool "findfs (11 kb)"
default BUSYBOX_DEFAULT_FINDFS
select BUSYBOX_CONFIG_VOLUMEID
help
Prints the name of a filesystem with given label or UUID.
config BUSYBOX_CONFIG_FLOCK
- bool "flock (6.3 kb)"
+ bool "flock (6.5 kb)"
default BUSYBOX_DEFAULT_FLOCK
help
Manage locks from shell scripts
config BUSYBOX_CONFIG_FDFLUSH
- bool "fdflush (1.3 kb)"
+ bool "fdflush (1.6 kb)"
default BUSYBOX_DEFAULT_FDFLUSH
help
fdflush is only needed when changing media on slightly-broken
@@ -245,7 +245,7 @@ config BUSYBOX_CONFIG_FDFLUSH
leave this disabled.
config BUSYBOX_CONFIG_FREERAMDISK
- bool "freeramdisk (1.3 kb)"
+ bool "freeramdisk (1.6 kb)"
default BUSYBOX_DEFAULT_FREERAMDISK
help
Linux allows you to create ramdisks. This utility allows you to
@@ -265,18 +265,18 @@ config BUSYBOX_CONFIG_FSCK_MINIX
check for and attempt to repair any corruption that occurs to a minix
filesystem.
config BUSYBOX_CONFIG_FSFREEZE
- bool "fsfreeze (3.5 kb)"
+ bool "fsfreeze (3.7 kb)"
default BUSYBOX_DEFAULT_FSFREEZE
select BUSYBOX_CONFIG_LONG_OPTS
help
Halt new accesses and flush writes on a mounted filesystem.
config BUSYBOX_CONFIG_FSTRIM
- bool "fstrim (4.4 kb)"
+ bool "fstrim (4.6 kb)"
default BUSYBOX_DEFAULT_FSTRIM
help
Discard unused blocks on a mounted filesystem.
config BUSYBOX_CONFIG_GETOPT
- bool "getopt (5.8 kb)"
+ bool "getopt (6 kb)"
default BUSYBOX_DEFAULT_GETOPT
help
The getopt utility is used to break up (parse) options in command
@@ -293,26 +293,27 @@ config BUSYBOX_CONFIG_FEATURE_GETOPT_LONG
help
Enable support for long options (option -l).
config BUSYBOX_CONFIG_HEXDUMP
- bool "hexdump (8.6 kb)"
+ bool "hexdump (8.7 kb)"
default BUSYBOX_DEFAULT_HEXDUMP
help
The hexdump utility is used to display binary data in a readable
way that is comparable to the output from most hex editors.
config BUSYBOX_CONFIG_HD
- bool "hd (7.8 kb)"
+ bool "hd (8.3 kb)"
default BUSYBOX_DEFAULT_HD
help
hd is an alias to hexdump -C.
config BUSYBOX_CONFIG_XXD
- bool "xxd (8.9 kb)"
+ bool "xxd (11 kb)"
default BUSYBOX_DEFAULT_XXD
help
The xxd utility is used to display binary data in a readable
way that is comparable to the output from most hex editors.
config BUSYBOX_CONFIG_HWCLOCK
- bool "hwclock (5.8 kb)"
+ bool "hwclock (5.9 kb)"
default BUSYBOX_DEFAULT_HWCLOCK
+ select BUSYBOX_CONFIG_LONG_OPTS
help
The hwclock utility is used to read and set the hardware clock
on a system. This is primarily used to set the current time on
@@ -331,26 +332,26 @@ config BUSYBOX_CONFIG_FEATURE_HWCLOCK_ADJTIME_FHS
pathname.com/fhs/pub/fhs-2.3.html#VARLIBHWCLOCKSTATEDIRECTORYFORHWCLO
config BUSYBOX_CONFIG_IONICE
- bool "ionice (3.8 kb)"
+ bool "ionice (4 kb)"
default BUSYBOX_DEFAULT_IONICE
help
Set/set program io scheduling class and priority
Requires kernel >= 2.6.13
config BUSYBOX_CONFIG_IPCRM
- bool "ipcrm (3.2 kb)"
+ bool "ipcrm (3.5 kb)"
default BUSYBOX_DEFAULT_IPCRM
help
The ipcrm utility allows the removal of System V interprocess
communication (IPC) objects and the associated data structures
from the system.
config BUSYBOX_CONFIG_IPCS
- bool "ipcs (11 kb)"
+ bool "ipcs (12 kb)"
default BUSYBOX_DEFAULT_IPCS
help
The ipcs utility is used to provide information on the currently
allocated System V interprocess (IPC) objects in the system.
config BUSYBOX_CONFIG_LAST
- bool "last (6.1 kb)"
+ bool "last (7.4 kb)"
default BUSYBOX_DEFAULT_LAST
depends on BUSYBOX_CONFIG_FEATURE_WTMP
help
@@ -364,14 +365,14 @@ config BUSYBOX_CONFIG_FEATURE_LAST_FANCY
'last' displays detailed information about the last users that
logged into the system (mimics sysvinit last). +900 bytes.
config BUSYBOX_CONFIG_LOSETUP
- bool "losetup (5.5 kb)"
+ bool "losetup (6.2 kb)"
default BUSYBOX_DEFAULT_LOSETUP
help
losetup is used to associate or detach a loop device with a regular
file or block device, and to query the status of a loop device. This
version does not currently support enabling data encryption.
config BUSYBOX_CONFIG_LSPCI
- bool "lspci (6.3 kb)"
+ bool "lspci (6.4 kb)"
default BUSYBOX_DEFAULT_LSPCI
help
lspci is a utility for displaying information about PCI buses in the
@@ -379,7 +380,7 @@ config BUSYBOX_CONFIG_LSPCI
This version uses sysfs (/sys/bus/pci/devices) only.
config BUSYBOX_CONFIG_LSUSB
- bool "lsusb (4.2 kb)"
+ bool "lsusb (4.4 kb)"
default BUSYBOX_DEFAULT_LSUSB
help
lsusb is a utility for displaying information about USB buses in the
@@ -387,7 +388,7 @@ config BUSYBOX_CONFIG_LSUSB
This version uses sysfs (/sys/bus/usb/devices) only.
config BUSYBOX_CONFIG_MDEV
- bool "mdev (17 kb)"
+ bool "mdev (20 kb)"
default BUSYBOX_DEFAULT_MDEV
help
mdev is a mini-udev implementation for dynamically creating device
@@ -454,7 +455,7 @@ config BUSYBOX_CONFIG_FEATURE_MDEV_DAEMON
resources than registering mdev as hotplug helper or using the
uevent applet.
config BUSYBOX_CONFIG_MESG
- bool "mesg (1.4 kb)"
+ bool "mesg (1.8 kb)"
default BUSYBOX_DEFAULT_MESG
help
Mesg controls access to your terminal by others. It is typically
@@ -505,18 +506,18 @@ config BUSYBOX_CONFIG_MKFS_REISER
Utility to create ReiserFS filesystems.
Note: this applet needs a lot of testing and polishing.
config BUSYBOX_CONFIG_MKDOSFS
- bool "mkdosfs (7.2 kb)"
+ bool "mkdosfs (7.6 kb)"
default BUSYBOX_DEFAULT_MKDOSFS
help
Utility to create FAT32 filesystems.
config BUSYBOX_CONFIG_MKFS_VFAT
- bool "mkfs.vfat (7.2 kb)"
+ bool "mkfs.vfat (7.6 kb)"
default BUSYBOX_DEFAULT_MKFS_VFAT
help
Alias to "mkdosfs".
config BUSYBOX_CONFIG_MKSWAP
- bool "mkswap (6.3 kb)"
+ bool "mkswap (6.6 kb)"
default BUSYBOX_DEFAULT_MKSWAP
help
The mkswap utility is used to configure a file or disk partition as
@@ -535,7 +536,7 @@ config BUSYBOX_CONFIG_FEATURE_MKSWAP_UUID
help
Generate swap spaces with universally unique identifiers.
config BUSYBOX_CONFIG_MORE
- bool "more (7 kb)"
+ bool "more (7.2 kb)"
default BUSYBOX_DEFAULT_MORE
help
more is a simple utility which allows you to read text one screen
@@ -544,7 +545,7 @@ config BUSYBOX_CONFIG_MORE
you will probably find this utility very helpful. If you don't have
any need to reading text files, you can leave this disabled.
config BUSYBOX_CONFIG_MOUNT
- bool "mount (23 kb)"
+ bool "mount (24 kb)"
default BUSYBOX_DEFAULT_MOUNT
help
All files and filesystems in Unix are arranged into one big directory
@@ -634,7 +635,7 @@ config BUSYBOX_CONFIG_FEATURE_MOUNT_OTHERTAB
help
Support mount -T (specifying an alternate fstab)
config BUSYBOX_CONFIG_MOUNTPOINT
- bool "mountpoint (4.9 kb)"
+ bool "mountpoint (5.1 kb)"
default BUSYBOX_DEFAULT_MOUNTPOINT
help
mountpoint checks if the directory is a mountpoint.
@@ -659,12 +660,12 @@ config BUSYBOX_CONFIG_NOLOGIN_DEPENDENCIES
If you know these will be available externally you can
disable this option.
config BUSYBOX_CONFIG_NSENTER
- bool "nsenter (6.5 kb)"
+ bool "nsenter (6.8 kb)"
default BUSYBOX_DEFAULT_NSENTER
help
Run program with namespaces of other processes.
config BUSYBOX_CONFIG_PIVOT_ROOT
- bool "pivot_root (1.1 kb)"
+ bool "pivot_root (1.4 kb)"
default BUSYBOX_DEFAULT_PIVOT_ROOT
help
The pivot_root utility swaps the mount points for the root filesystem
@@ -675,7 +676,7 @@ config BUSYBOX_CONFIG_PIVOT_ROOT
Note: This is for initrd in linux 2.4. Under initramfs (introduced
in linux 2.6) use switch_root instead.
config BUSYBOX_CONFIG_RDATE
- bool "rdate (5.6 kb)"
+ bool "rdate (5.9 kb)"
default BUSYBOX_DEFAULT_RDATE
help
The rdate utility allows you to synchronize the date and time of your
@@ -683,44 +684,44 @@ config BUSYBOX_CONFIG_RDATE
the RFC868 protocol, which is built into the inetd daemon on most
systems.
config BUSYBOX_CONFIG_RDEV
- bool "rdev (1.8 kb)"
+ bool "rdev (2.1 kb)"
default BUSYBOX_DEFAULT_RDEV
help
Print the device node associated with the filesystem mounted at '/'.
config BUSYBOX_CONFIG_READPROFILE
- bool "readprofile (7.1 kb)"
+ bool "readprofile (7.5 kb)"
default BUSYBOX_DEFAULT_READPROFILE
help
This allows you to parse /proc/profile for basic profiling.
config BUSYBOX_CONFIG_RENICE
- bool "renice (4.2 kb)"
+ bool "renice (4.4 kb)"
default BUSYBOX_DEFAULT_RENICE
help
Renice alters the scheduling priority of one or more running
processes.
config BUSYBOX_CONFIG_REV
- bool "rev (4.4 kb)"
+ bool "rev (4.6 kb)"
default BUSYBOX_DEFAULT_REV
help
Reverse lines of a file or files.
config BUSYBOX_CONFIG_RTCWAKE
- bool "rtcwake (6.8 kb)"
+ bool "rtcwake (7.5 kb)"
default BUSYBOX_DEFAULT_RTCWAKE
help
Enter a system sleep state until specified wakeup time.
config BUSYBOX_CONFIG_SCRIPT
- bool "script (8.6 kb)"
+ bool "script (8.8 kb)"
default BUSYBOX_DEFAULT_SCRIPT
help
The script makes typescript of terminal session.
config BUSYBOX_CONFIG_SCRIPTREPLAY
- bool "scriptreplay (2.4 kb)"
+ bool "scriptreplay (2.6 kb)"
default BUSYBOX_DEFAULT_SCRIPTREPLAY
help
This program replays a typescript, using timing information
given by script -t.
config BUSYBOX_CONFIG_SETARCH
- bool "setarch (3.6 kb)"
+ bool "setarch (3.8 kb)"
default BUSYBOX_DEFAULT_SETARCH
help
The linux32 utility is used to create a 32bit environment for the
@@ -729,18 +730,18 @@ config BUSYBOX_CONFIG_SETARCH
(like amd64/x86, ppc64/ppc, sparc64/sparc, etc...).
config BUSYBOX_CONFIG_LINUX32
- bool "linux32 (3.3 kb)"
+ bool "linux32 (3.6 kb)"
default BUSYBOX_DEFAULT_LINUX32
help
Alias to "setarch linux32".
config BUSYBOX_CONFIG_LINUX64
- bool "linux64 (3.3 kb)"
+ bool "linux64 (3.5 kb)"
default BUSYBOX_DEFAULT_LINUX64
help
Alias to "setarch linux64".
config BUSYBOX_CONFIG_SETPRIV
- bool "setpriv (6.6 kb)"
+ bool "setpriv (6.9 kb)"
default BUSYBOX_DEFAULT_SETPRIV
select BUSYBOX_CONFIG_LONG_OPTS
help
@@ -775,7 +776,7 @@ config BUSYBOX_CONFIG_FEATURE_SETPRIV_CAPABILITY_NAMES
this option allows using the human-readable names in addition to
the index-based names.
config BUSYBOX_CONFIG_SETSID
- bool "setsid (3.6 kb)"
+ bool "setsid (3.8 kb)"
default BUSYBOX_DEFAULT_SETSID
help
setsid runs a program in a new session
@@ -818,7 +819,7 @@ config BUSYBOX_CONFIG_FEATURE_SWAPONOFF_LABEL
This allows for specifying a device by label or uuid, rather than by
name. This feature utilizes the same functionality as blkid/findfs.
config BUSYBOX_CONFIG_SWITCH_ROOT
- bool "switch_root (5.5 kb)"
+ bool "switch_root (5.7 kb)"
default BUSYBOX_DEFAULT_SWITCH_ROOT
help
The switch_root utility is used from initramfs to select a new
@@ -837,7 +838,7 @@ config BUSYBOX_CONFIG_SWITCH_ROOT
list of active mount points. That's why.
config BUSYBOX_CONFIG_TASKSET
- bool "taskset (4.2 kb)"
+ bool "taskset (5.6 kb)"
default BUSYBOX_DEFAULT_TASKSET
help
Retrieve or set a processes's CPU affinity.
@@ -860,7 +861,7 @@ config BUSYBOX_CONFIG_FEATURE_TASKSET_CPULIST
Add support for taking/printing affinity as CPU list when '-c'
option is used. For example, it prints '0-3,7' instead of mask '8f'.
config BUSYBOX_CONFIG_UEVENT
- bool "uevent (3.1 kb)"
+ bool "uevent (3.5 kb)"
default BUSYBOX_DEFAULT_UEVENT
help
uevent is a netlink listener for kernel uevent notifications
@@ -881,14 +882,14 @@ config BUSYBOX_CONFIG_FEATURE_UMOUNT_ALL
help
Support -a option to unmount all currently mounted filesystems.
config BUSYBOX_CONFIG_UNSHARE
- bool "unshare (7.2 kb)"
+ bool "unshare (7.3 kb)"
default BUSYBOX_DEFAULT_UNSHARE
depends on !BUSYBOX_CONFIG_NOMMU
select BUSYBOX_CONFIG_LONG_OPTS
help
Run program with some namespaces unshared from parent.
config BUSYBOX_CONFIG_WALL
- bool "wall (2.6 kb)"
+ bool "wall (2.9 kb)"
default BUSYBOX_DEFAULT_WALL
depends on BUSYBOX_CONFIG_FEATURE_UTMP
help
diff --git a/package/utils/busybox/files/busybox-history-file.sh b/package/utils/busybox/files/busybox-history-file.sh
new file mode 100644
index 0000000000..f286009627
--- /dev/null
+++ b/package/utils/busybox/files/busybox-history-file.sh
@@ -0,0 +1,2 @@
+export HISTFILE=/tmp/.busybox_ash_history
+
diff --git a/package/utils/busybox/files/cron b/package/utils/busybox/files/cron
index 4efdfa52ca..2ddffa61e4 100755
--- a/package/utils/busybox/files/cron
+++ b/package/utils/busybox/files/cron
@@ -28,7 +28,7 @@ start_service() {
ln -s /etc/crontabs /var/spool/cron/ 2>/dev/null
procd_open_instance
- procd_set_param command "$PROG" -f -c /etc/crontabs -l "${loglevel:-5}"
+ procd_set_param command "$PROG" -f -c /etc/crontabs -l "${loglevel:-7}"
for crontab in /etc/crontabs/*; do
procd_set_param file "$crontab"
done
diff --git a/package/utils/busybox/patches/001-fix-non-x86-build.patch b/package/utils/busybox/patches/001-fix-non-x86-build.patch
new file mode 100644
index 0000000000..7a47292474
--- /dev/null
+++ b/package/utils/busybox/patches/001-fix-non-x86-build.patch
@@ -0,0 +1,12 @@
+--- a/libbb/hash_md5_sha.c
++++ b/libbb/hash_md5_sha.c
+@@ -1313,7 +1313,9 @@ unsigned FAST_FUNC sha1_end(sha1_ctx_t *
+ hash_size = 8;
+ if (ctx->process_block == sha1_process_block64
+ #if ENABLE_SHA1_HWACCEL
++# if defined(__GNUC__) && (defined(__i386__) || defined(__x86_64__))
+ || ctx->process_block == sha1_process_block64_shaNI
++# endif
+ #endif
+ ) {
+ hash_size = 5;
diff --git a/package/utils/busybox/patches/002-upstream-fix-hexdump.patch b/package/utils/busybox/patches/002-upstream-fix-hexdump.patch
new file mode 100644
index 0000000000..a123fe3a5f
--- /dev/null
+++ b/package/utils/busybox/patches/002-upstream-fix-hexdump.patch
@@ -0,0 +1,49 @@
+From 87e60dcf0f7ef917b73353d8605188a420bd91f9 Mon Sep 17 00:00:00 2001
+From: Natanael Copa <ncopa@alpinelinux.org>
+Date: Mon, 28 Oct 2024 15:26:21 +0100
+Subject: hexdump: fix regression with -n4 -e '"%u"'
+
+Fix bug introduced in busybox 1.37.0 that broke kernel builds.
+
+Fixes commit e2287f99fe6f (od: for !DESKTOP, match output more closely
+to GNU coreutils 9.1, implement -s)
+
+function old new delta
+rewrite 967 976 +9
+
+Signed-off-by: Natanael Copa <ncopa@alpinelinux.org>
+Signed-off-by: Denys Vlasenko <vda.linux@googlemail.com>
+---
+ libbb/dump.c | 6 ++++--
+ testsuite/hexdump.tests | 6 ++++++
+ 2 files changed, 10 insertions(+), 2 deletions(-)
+
+--- a/libbb/dump.c
++++ b/libbb/dump.c
+@@ -198,9 +198,11 @@ static NOINLINE void rewrite(priv_dumper
+ if (!e)
+ goto DO_BAD_CONV_CHAR;
+ pr->flags = F_INT;
+- if (e > int_convs + 1) /* not d or i? */
+- pr->flags = F_UINT;
+ byte_count_str = "\010\004\002\001";
++ if (e > int_convs + 1) { /* not d or i? */
++ pr->flags = F_UINT;
++ byte_count_str++;
++ }
+ goto DO_BYTE_COUNT;
+ } else
+ if (strchr(int_convs, *p1)) { /* %d etc */
+--- a/testsuite/hexdump.tests
++++ b/testsuite/hexdump.tests
+@@ -82,4 +82,10 @@ testing "hexdump -e /2 %d" \
+ "\x80\x81\x82\x83\x84\x85\x86\x87\x88\x89\x8a\x8b\x8c\x8d\x8e\x8f"\
+ "\xf0\xf1\xf2\xf3\xf4\xf5\xf6\xf7\xf8\xf9\xfa\xfb\xfc\xfd\xfe\xff"\
+
++testing "hexdump -n4 -e '\"%u\"'" \
++ "hexdump -n4 -e '\"%u\"'" \
++ "12345678" \
++ "" \
++ "\x4e\x61\xbc\x00AAAA"
++
+ exit $FAILCOUNT
diff --git a/package/utils/busybox/patches/200-udhcpc_reduce_msgs.patch b/package/utils/busybox/patches/200-udhcpc_reduce_msgs.patch
index c0f234ee42..595eaa6afe 100644
--- a/package/utils/busybox/patches/200-udhcpc_reduce_msgs.patch
+++ b/package/utils/busybox/patches/200-udhcpc_reduce_msgs.patch
@@ -1,6 +1,6 @@
--- a/networking/udhcp/dhcpc.c
+++ b/networking/udhcp/dhcpc.c
-@@ -722,6 +722,7 @@ static int bcast_or_ucast(struct dhcp_pa
+@@ -711,6 +711,7 @@ static int bcast_or_ucast(struct dhcp_pa
static NOINLINE int send_discover(uint32_t requested)
{
struct dhcp_packet packet;
@@ -8,7 +8,7 @@
/* Fill in: op, htype, hlen, cookie, chaddr fields,
* xid field, message type option:
-@@ -736,6 +737,7 @@ static NOINLINE int send_discover(uint32
+@@ -725,6 +726,7 @@ static NOINLINE int send_discover(uint32
*/
add_client_options(&packet);
diff --git a/package/utils/busybox/patches/201-udhcpc_changed_ifindex.patch b/package/utils/busybox/patches/201-udhcpc_changed_ifindex.patch
index a4bda992c4..94745cf8d9 100644
--- a/package/utils/busybox/patches/201-udhcpc_changed_ifindex.patch
+++ b/package/utils/busybox/patches/201-udhcpc_changed_ifindex.patch
@@ -1,6 +1,6 @@
--- a/networking/udhcp/dhcpc.c
+++ b/networking/udhcp/dhcpc.c
-@@ -1384,6 +1384,12 @@ int udhcpc_main(int argc UNUSED_PARAM, c
+@@ -1374,6 +1374,12 @@ int udhcpc_main(int argc UNUSED_PARAM, c
struct pollfd pfds[2];
struct dhcp_packet packet;
diff --git a/package/utils/busybox/patches/301-ip-link-fix-netlink-msg-size.patch b/package/utils/busybox/patches/301-ip-link-fix-netlink-msg-size.patch
index f4c0a80922..9a3ddb52fd 100644
--- a/package/utils/busybox/patches/301-ip-link-fix-netlink-msg-size.patch
+++ b/package/utils/busybox/patches/301-ip-link-fix-netlink-msg-size.patch
@@ -1,6 +1,6 @@
--- a/networking/libiproute/iplink.c
+++ b/networking/libiproute/iplink.c
-@@ -683,7 +683,7 @@ static int do_add_or_delete(char **argv,
+@@ -953,7 +953,7 @@ static int do_add_or_delete(char **argv,
}
xrtnl_open(&rth);
ll_init_map(&rth);
diff --git a/package/utils/busybox/patches/530-nslookup-ensure-unique-transaction-IDs-for-the-DNS-queries.patch b/package/utils/busybox/patches/530-nslookup-ensure-unique-transaction-IDs-for-the-DNS-queries.patch
index caa5ee78f3..bbd7b277fe 100644
--- a/package/utils/busybox/patches/530-nslookup-ensure-unique-transaction-IDs-for-the-DNS-queries.patch
+++ b/package/utils/busybox/patches/530-nslookup-ensure-unique-transaction-IDs-for-the-DNS-queries.patch
@@ -29,7 +29,7 @@ Forwarded: http://lists.busybox.net/pipermail/busybox/2022-October/089911.html
---
--- a/networking/nslookup.c
+++ b/networking/nslookup.c
-@@ -978,6 +978,10 @@ int nslookup_main(int argc UNUSED_PARAM,
+@@ -1370,6 +1370,10 @@ int nslookup_main(int argc UNUSED_PARAM,
}
}
diff --git a/package/utils/busybox/patches/600-loginutils-login.c-libselinux-get_default_context-ex.patch b/package/utils/busybox/patches/600-loginutils-login.c-libselinux-get_default_context-ex.patch
new file mode 100644
index 0000000000..8c26ee001b
--- /dev/null
+++ b/package/utils/busybox/patches/600-loginutils-login.c-libselinux-get_default_context-ex.patch
@@ -0,0 +1,51 @@
+From 850a6d031039237b0b13d8fab9f10a7cd4752907 Mon Sep 17 00:00:00 2001
+From: Dominick Grift <dominick.grift@defensec.nl>
+Date: Sat, 5 Apr 2025 13:40:26 +0200
+Subject: [PATCH] loginutils/login.c: libselinux get_default_context() expects
+ seuser
+
+Use getseuserbyname() to get the seuser associated with username and use that
+instead with get_default_context()
+
+>From get_default_context.3:
+"These functions takes a SELinux user identity that must be defined in the SELinux policy as their input, not a Linux username."
+
+Fixes: #19075
+Upstream-Status: Submitted [https://lists.busybox.net/pipermail/busybox/2025-April/091407.html]
+Signed-off-by: Dominick Grift <dominick.grift@defensec.nl>
+---
+ loginutils/login.c | 11 ++++++++++-
+ 1 file changed, 10 insertions(+), 1 deletion(-)
+
+--- a/loginutils/login.c
++++ b/loginutils/login.c
+@@ -183,12 +183,16 @@ static void die_if_nologin(void)
+ static void initselinux(char *username, char *full_tty,
+ security_context_t *user_sid)
+ {
++ char *seuser = NULL, *level = NULL;
+ security_context_t old_tty_sid, new_tty_sid;
+
+ if (!is_selinux_enabled())
+ return;
+
+- if (get_default_context(username, NULL, user_sid)) {
++ if (getseuserbyname(username, &seuser, &level)) {
++ bb_error_msg_and_die("can't get seuser for %s", username);
++ }
++ if (get_default_context(seuser, NULL, user_sid)) {
+ bb_error_msg_and_die("can't get SID for %s", username);
+ }
+ if (getfilecon(full_tty, &old_tty_sid) < 0) {
+@@ -201,6 +205,11 @@ static void initselinux(char *username,
+ if (setfilecon(full_tty, new_tty_sid) != 0) {
+ bb_perror_msg_and_die("chsid(%s, %s) failed", full_tty, new_tty_sid);
+ }
++
++ if (ENABLE_FEATURE_CLEAN_UP) {
++ free(seuser);
++ free(level);
++ }
+ }
+ #endif
+
diff --git a/package/utils/checkpolicy/Makefile b/package/utils/checkpolicy/Makefile
index 4ebf97bb3f..179127bf1a 100644
--- a/package/utils/checkpolicy/Makefile
+++ b/package/utils/checkpolicy/Makefile
@@ -6,12 +6,12 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=checkpolicy
-PKG_VERSION:=3.5
+PKG_VERSION:=3.8.1
PKG_RELEASE:=1
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
PKG_SOURCE_URL:=https://github.com/SELinuxProject/selinux/releases/download/$(PKG_VERSION)
-PKG_HASH:=7aa48ab2222a0b9881111d6d7f70c3014d3d9338827d9e02df105a68c0df5dbc
+PKG_HASH:=7b477c516e2693d8b6c511386323177f1d7db51c2e04eb6d0de8ca2b36120e5d
PKG_INSTALL:=1
PKG_BUILD_DEPENDS:=libselinux
HOST_BUILD_DEPENDS:=libselinux/host
diff --git a/package/utils/cli/Makefile b/package/utils/cli/Makefile
new file mode 100644
index 0000000000..65eae06309
--- /dev/null
+++ b/package/utils/cli/Makefile
@@ -0,0 +1,35 @@
+#
+# Copyright (C) 2025 OpenWrt.org
+#
+# This is free software, licensed under the GNU General Public License v2.
+# See /LICENSE for more information.
+#
+
+include $(TOPDIR)/rules.mk
+
+PKG_NAME:=cli
+PKG_RELEASE:=$(AUTORELEASE)
+
+PKG_LICENSE:=GPL-2.0
+PKG_MAINTAINER:=Felix Fietkau <nbd@nbd.name>
+
+include $(INCLUDE_DIR)/package.mk
+
+define Package/cli
+ SECTION:=utils
+ CATEGORY:=Utilities
+ TITLE:=OpenWrt CLI
+ DEPENDS:=+ucode +ucode-mod-uline \
+ +ucode-mod-ubus +ucode-mod-uloop \
+ +ucode-mod-fs +ucode-mod-rtnl
+endef
+
+define Build/Compile
+ :
+endef
+
+define Package/cli/install
+ $(CP) ./files/* $(1)/
+endef
+
+$(eval $(call BuildPackage,cli))
diff --git a/package/utils/cli/docs/MODULE-API.md b/package/utils/cli/docs/MODULE-API.md
new file mode 100644
index 0000000000..ac6e2ce760
--- /dev/null
+++ b/package/utils/cli/docs/MODULE-API.md
@@ -0,0 +1,364 @@
+ Design of the `cli` module API
+
+## Structure:
+The cli is organized as a set of *nodes*, which are ucode objects objects describing *entries*.
+Each *entry* can either implement a *command*, or select another *node*, optionally with *parameters*.
+Additionally, it contains helptext and full *parameter* descriptions, including everything needed for tab completion.
+The initial *node* on startup is `Root`, representing the main menu.
+
+## Simple example:
+
+### Code:
+```
+const Example = {
+ hello: {
+ help: "Example command",
+ args: [
+ {
+ name: "name",
+ type: "string",
+ min: 3,
+ max: 16,
+ required: true,
+ }
+ ],
+ call: function(ctx, argv, named) {
+ return ctx.ok("Hello, " + argv[0]);
+ },
+ },
+ hello2: {
+ help: "Example command (named_args version)",
+ named_args: {
+ name: {
+ required: true,
+ args: {
+ type: "string",
+ min: 3,
+ max: 16,
+ }
+ }
+ },
+ call: function(ctx, argv, named) {
+ return ctx.ok("Hello, " + named.name);
+ },
+ }
+};
+
+const Root = {
+ example: {
+ help: "Example node",
+ select_node: "Example",
+ }
+};
+
+model.add_nodes({ Root, Example });
+```
+### Example interaction:
+```
+root@OpenWrt:~# cli
+Welcome to the OpenWrt CLI. Press '?' for help on commands/arguments
+cli> example
+cli example> hello
+Error: Missing argument 1: name
+cli example> hello foo
+Hello, foo
+cli example> hello2
+Error: Missing argument: name
+cli example> hello2 name foo2
+Hello, foo2
+cli example>
+```
+
+## API documentation:
+
+Each module is placed in `/usr/share/ucode/cli/modules` on the root filesystem.
+When included by the cli code, the scope contains the `model` variable, which is the main cli API object. This variable is also present in the scope of the other callback functions described below.
+
+### `model` methods:
+- `model.warn(msg)`: Pass a warning to the user (similar to the ucode `warn` function).
+- `model.exception(e)`: Print an exception with stack trace.
+- `model.add_module(path)`: Load a single module from `path`
+- `model.add_modules(path)`: Load multiple modules from the `path` wildcard pattern
+- `model.add_node(name, obj)`: Add a single node under the given name
+- `model.add_nodes(nodes)`: Add multiple nodes with taking `name` and `obj` from the `nodes` object.
+- `model.add_type(name, info)`: Add a data type with validation information,
+- `model.add_types(types)`: Add multiple data types, taking `name` and `info` from the `types` object.
+- `model.status_msg(msg)`: Print an asynchronous status message (should not be used from within a node `call` or `select` function).
+
+### Properties of an `entry` inside a `node`:
+Each entry must have at least `help` and either `call` or `select_node` set.
+- `help`: Helptext describing the command
+- `call: function(ctx, argv, named)`: main command handler function of the entry.
+ - `this`: pointer to the `entry`
+ - `ctx`: call context object (see below)
+ - `argv`: array of positional arguments after the command name
+ - `named`: object of named parameters passed to the command
+ - Return value: either `ctx.ok(msg)` for successfull calls, or the result of an error function (see below).
+- `select_node`: (string) name of the *node* that this entry points to. Mutually exclusive with implementing `call`.
+- `select: function(ctx, argv, named)`: function for selecting another node.
+ - `this`: pointer to the *entry*
+ - `ctx`: node context object (see below)
+ - `argv`, `named`: see `call`
+ - Return value: either `ctx.set(prompt, data)`, `true`, or the result of an error function (see below).
+- `args`: array of positional *arguments* (see *argument* property description)
+- `named_args`: object of named *parameters* (see *parameter* property description)
+- `available: function(ctx)`: function indicating if the entry can be used (affects tab completion and running commands)
+ - `this`: pointer to the *entry*
+ - `ctx`: node context object (see below)
+ - Return value: `true` if available, `false` otherwise.
+- `validate: function (ctx, argv, named)`: validate command arguments
+ - Function parameters: see `call`
+
+### Named *parameter* properties:
+- `help`: Description of the named parameter's purpose
+- `args`: Either an array of *argument* objects, or an object with a single *argument* (see below). If not set, paramter will not take any arguments, and its value will be `true` if the parameter was specified on the command line.
+- `available: function(ctx, argv, named)`: function indicating if the named parameter can be used (affects tab completion and argument validation). May depend on *arguments*/*parameters* specified before this one.
+- `multiple` (bool): indicates if an argument may be specified multiple times. Turns the value in `named` into an array.
+- `required` (bool): Parameter must be specified for the command
+- `default`: default value for the parameter.
+- `allow_empty`: empty values are allowed and can be specified on the command line using `-param_name` instead of `param_name`. The value in the `named` object will be `null` in that case.
+
+### Positional *argument* properties:
+- `name`: Short name of the *argument*
+- `help`: Longer description of the *argument* (used in helptext/completion)
+- `type`: data type name (see below)
+- `required` (bool): Value must not be empty
+- `value`: possible values for tab completion, one of:
+ - array of objects with the following contents:
+ - `name`: value string
+ - `help`: help text for this value
+ - `function(ctx, argv, named)` returning the above.
+- extra properties specific to the data type (see below)
+
+### Default data types:
+- `int`: Integer value. The valid range can be specified using the `min` and `max` properties.
+- `string`: String value. The valid string length can be specified using the `min` and `max` properties.
+- `bool`: Boolean value. Converts `"1"` and `"0"` to `true` and `false`
+- `enum`: String value that must match one entry of the list provided via the `value` property. Case-insensitive match can be enabled using the `ignore_case` property.
+- `path`: Local filesystem path. When the `new_path` property is set, only match directories for a file to be created.
+- `host`: Host name or IP address
+- `macaddr`: MAC address
+- `ipv4`: IPv4 address
+- `ipv6`: IPv6 address
+- `cidr4`: IPv4 address with netmask size, e.g. 192.168.1.1/24. Allows `auto` as value if the `allow_auto` property is set.
+
+### `call` context:
+Passed as `ctx` argument to entry `call` functions.
+- `ctx.data`: Object containing any data passed via `ctx.set()` from a `select` context.
+- `ctx.ok(msg)`: Indicates successful call, passes the message `msg` to the user.
+- `ctx.select(...args)`: After completion, switch to a different *node* by running the command chain provided as function argument (only entries with `.select_node` are supported).
+- `ctx.string(name, val)`: Passes a string to the caller as return value.
+- `ctx.list(name, val)`: Passes a list of values to the caller as return value. `val` must be an array.
+- `ctx.table(name, val)`: Passes a table as value to the caller. `val` can be an array `[ column_1, column_2 ]`, where each member of the outer array describes a row in the table. It can also be an object, where the property name is the first column value, and the value the second column value.
+- `ctx.multi_table(name, val)`: Passes multiple tables to the caller. Can be an array of `[ title, table ]`, or an object.
+- Error functions (see below)
+
+### `select` context:
+- `ctx.data`: Object containing any data passed via parent `ctx.set` calls.
+- `ctx.set(prompt, data)`: Modify the prompt and `ctx.data` for the child context. The string given in `prompt` is appended to the existing prompt. The data given in the `data` object is merged with the previous `ctx.data` value.
+- Error functions (see below)
+
+### Error functions:
+All error messages accept a format string in `msg`, with arguments added after it.
+- `ctx.invalid_argument(msg, ...args)`: Indicates that invalid arguments were provided.
+- `ctx.missing_argument(msg, ...args)`: Indicates that an expected argument was missing.
+- `ctx.command_failed(msg, ...args)`: Indicates that the command failed.
+- `ctx.not_found(msg, ...args)`: Indicates that a given entry was not found.
+- `ctx.unknown_error(msg, ...args)`: Indicates that the command failed for unknown or unspecified reasons.
+- `ctx.error(id, msg, ...args)`: Generic error message with `id` specifying a machine readable error type string.
+
+## Editor API documentation
+The editor API provides a layer of abstraction above node entries/calls in order to make it easy to edit properties of an object based on an attribute list, as well as create/destroy/show object instances using a consistent user interface.
+
+### Simple example:
+```
+import * as editor from "cli.object-editor";
+
+let changed = false;
+let data = {
+ things: {
+ foo: {
+ label_str: [ "bar" ],
+ id: 31337,
+ }
+ },
+};
+
+const thing_editor = {
+ change_cb: function(ctx) {
+ changed = true;
+ },
+ named_args: {
+ label: {
+ help: "Thing label",
+ attribute: "label_str",
+ multiple: true,
+ args: {
+ type: "string",
+ min: 2,
+ max: 16
+ },
+ },
+ id: {
+ help: "Thing id",
+ required: true,
+ args: {
+ type: "int",
+ min: 1,
+ },
+ },
+ },
+};
+const ExampleThing = editor.new(thing_editor);
+
+let Example = {
+ dump: {
+ help: "Dump current data",
+ call: function(ctx, argv, named) {
+ return ctx.json("Data", {
+ changed,
+ data
+ });
+ },
+ }
+};
+const example_editor = {
+ change_cb: function(ctx) {
+ changed = true;
+ },
+ types: {
+ thing: {
+ node_name: "ExampleThing",
+ node: ExampleThing,
+ object: "things",
+ },
+ },
+};
+editor.edit_create_destroy(example_editor, Example);
+
+const Root = {
+ example: {
+ help: "Example node",
+ select_node: "Example",
+ select: function(ctx, argv, named) {
+ return ctx.set(null, {
+ object_edit: data,
+ });
+ }
+ }
+};
+
+model.add_nodes({ Root, Example, ExampleThing });
+```
+### Example interaction:
+```
+root@OpenWrt:~# cli
+Welcome to the OpenWrt CLI. Press '?' for help on commands/arguments
+cli> example
+cli example> dump
+Data: {
+ "changed": false,
+ "data": {
+ "things": {
+ "foo": {
+ "label_str": [
+ "bar"
+ ],
+ "id": 31337
+ }
+ }
+ }
+}
+cli example> thing foo set id 1337
+cli example> create thing bar id 168 label l1 label l2
+Added thing 'bar'
+cli example> thing bar show
+Values:
+ id: 168
+ label: l1, l2
+cli example> thing bar remove label 1
+cli example> thing bar show
+Values:
+ id: 168
+ label: l2
+cli example> dump
+Data: {
+ "changed": true,
+ "data": {
+ "things": {
+ "foo": {
+ "label_str": [
+ "bar"
+ ],
+ "id": 1337
+ },
+ "bar": {
+ "id": 168,
+ "label_str": [
+ "l2"
+ ]
+ }
+ }
+ }
+}
+cli example> destroy thing foo
+Deleted thing 'foo'
+cli example>
+```
+### API documentation
+Prelude: `import * as editor from "cli.object-editor";`
+
+#### Object editor:
+For editing an object, the following user commands are defined:
+- `set`: Changes property values
+- `show` Show all values
+
+If properties with `multiple: true` are defined, the following commands are also defined:
+- `add`: Add values to properties
+- `remove` Remove specific values from properties
+
+##### Variant 1 (editor-only node):
+`const Node = editor.new(editor_data)`
+
+##### Variant 2 (merge with existing entries):
+`let Node = {};`
+`editor.new(editor_data, Node);`
+
+The editor code assumes that the *node* that selects the editor node uses `ctx.set()` to set the `edit` field in `ctx.data` to the object being edited.
+
+#### `editor_data` properties:
+- `change_cb: function(ctx)`: Called whenever a property is changed by the user
+- `named_args`: Parameters for editing properties (based on *entry* `named_args`, see below)
+- `add`, `set`, `show`, `remove`: Object for overriding fields of the commands defined by the editor. Primarily used to override the helptext.
+
+#### Instance editor `named_args` entry properties:
+All *entry* `named_args` properties are supported, but the meaning is extended slightly:
+- `multiple`: Property array values can be added/removed
+- `default`: Default value when creating the object
+- `allow_empty`: Property can be deleted
+- `required`: Property is mandatory in the object.
+
+#### Object instance editor:
+For managing object instances, the following user commands are defined:
+- `create <type> <name> <...>`: Create a new instance. Also takes parameter values to be set on the object.
+- `destroy <type> <name>`: Delete an instance.
+- `list <type>` List all instances of a given type.
+
+The instance editor code assumes that the *node* that selects the editor node uses `ctx.set()` to set the `object_edit` field in `ctx.data` to the object being edited.
+
+##### Variant 1 (editor-only node):
+`const Node = editor.edit_create_destroy(instance_data);`
+
+##### Variant 2 (merge with existing entries):
+`let Node = {};`
+`editor.edit_create_destroy(instance_data, Node);`
+
+#### `instance_data` properties:
+- `change_cb: function(ctx)`: Called whenever an instance is added or deleted
+- `types`: Metadata about instances types (see below)
+
+#### `instance_data.types` object properties:
+- `node_name`: name of the *editor node* belonging to the object instance.
+- `node`: The *editor node* itself.
+- `object`: Name of the type specific container object inside the object pointed to by `ctx.data.object_edit`.
+
diff --git a/package/utils/cli/files/usr/sbin/cli b/package/utils/cli/files/usr/sbin/cli
new file mode 100755
index 0000000000..90889f996a
--- /dev/null
+++ b/package/utils/cli/files/usr/sbin/cli
@@ -0,0 +1,750 @@
+#!/usr/bin/env ucode
+// SPDX-License-Identifier: GPL-2.0-or-later
+// Copyright (C) 2025 Felix Fietkau <nbd@nbd.name>
+'use strict';
+import * as datamodel from "cli.datamodel";
+import { bold, color_fg } from "cli.color";
+import * as uline from "uline";
+import { basename, stdin } from "fs";
+
+let history = [];
+let history_edit;
+let history_idx = -1;
+let cur_line;
+let interactive, script_mode, raw_mode;
+
+while (length(ARGV) > 0) {
+ let cmd = ARGV[0];
+ if (substr(cmd, 0, 1) != "-")
+ break;
+
+ shift(ARGV);
+ switch (cmd) {
+ case '-i':
+ interactive = true;
+ break;
+ case '-s':
+ script_mode = true;
+ break;
+ case '-R':
+ raw_mode = true;
+ break;
+ }
+}
+
+let el;
+let model = datamodel.new({
+ getpass: uline.getpass,
+ poll_key: (timeout) => el.poll_key(timeout),
+ status_msg: (msg) => {
+ el.hide_prompt();
+ warn(msg + "\n");
+ el.refresh_prompt();
+ },
+ opt_pretty_print: !raw_mode
+});
+let uloop = model.uloop;
+model.add_modules();
+let ctx = model.context();
+let parser = uline.arg_parser({
+ line_separator: ";"
+});
+let base_prompt = [ "cli" ];
+
+model.add_nodes({
+ Root: {
+ exit: {
+ help: "Exit the CLI",
+ call: function(ctx) {
+ el.close();
+ uloop.end();
+ interactive = false;
+ return ctx.ok();
+ }
+ }
+ }
+});
+
+model.init();
+
+function update_prompt() {
+ el.set_state({
+ prompt: bold(join(" ", [ ...base_prompt, ...ctx.prompt ]) + "> "),
+ });
+}
+
+let cur_completion, tab_arg, tab_arg_len, tab_prefix, tab_suffix, tab_prefix_len, tab_quote, tab_ctx;
+
+function max_len(list, len)
+{
+ for (let entry in list)
+ if (length(entry) > len)
+ len = length(entry);
+ return len + 3;
+}
+
+function sort_completion(data)
+{
+ let categories = {};
+ for (let entry in data) {
+ let cat = entry.category ?? " ";
+ categories[cat] ??= [];
+ push(categories[cat], entry);
+ }
+ return categories;
+}
+
+function val_str(val)
+{
+ if (type(val) == "array")
+ return join(", ", val);
+ return val;
+}
+
+function helptext_list_str(cur, str)
+{
+ let data = cur.value;
+ let categories = sort_completion(data);
+ let cat_len = max_len(keys(categories));
+ let has_categories = length(categories) > 1 || !categories[" "];
+ let len = max_len(map(data, (v) => v.name), 10);
+
+ if (has_categories || str == null)
+ str = "";
+
+ for (let cat, cdata in categories) {
+ if (has_categories && cat != " ") {
+ if (length(str) > 0)
+ str += "\n";
+ str += `${cat}:\n`;
+ }
+
+ for (let val in cdata) {
+ let name = val.name;
+ let help = val.help ?? "";
+ let extra = [];
+ if (val.multiple)
+ push(extra, "multiple");
+ if (val.required)
+ push(extra, "required");
+ if (val.default)
+ push(extra, "default: " + val_str(val.default));
+ if (length(extra) > 0)
+ help += " (" + join(", ", extra) + ")";
+ if (length(help) > 0)
+ name += ":";
+ str += sprintf(" %-" + len + "s %s\n", name, help);
+ }
+ }
+
+ return str;
+}
+
+function helptext(cur) {
+ if (!cur) {
+ el.set_hint(`\n No help information available\n`);
+ return true;
+ }
+
+ let str = `${cur.help}: `;
+ let data = cur.value;
+ if (type(data) != "array") {
+ str += `<${cur.type}>\n`;
+ } else if (length(data) > 0) {
+ str += "\n";
+ str = helptext_list_str(cur, str);
+ } else {
+ str += " (no match)\n";
+ }
+ el.set_hint(str);
+ return true;
+}
+
+function completion_ctx(arg_info)
+{
+ let cur_ctx = ctx;
+ for (let args in arg_info.args) {
+ let sel = cur_ctx.select(args, true);
+ if (!length(args))
+ cur_ctx = sel;
+ if (type(sel) != "object" || sel.errors)
+ return;
+ }
+
+ return cur_ctx;
+}
+
+function completion_replace_arg(val, incomplete, skip_space)
+{
+ let ref = substr(tab_prefix, -tab_prefix_len);
+ val = parser.escape(val, ref);
+
+ if (incomplete) {
+ let last = substr(val, -1);
+ if (last == '"' || last == "'")
+ val = substr(val, 0, -1);
+ } else if (!skip_space) {
+ val += " ";
+ }
+
+ let line = tab_prefix;
+ if (tab_prefix_len)
+ line = substr(tab_prefix, 0, -tab_prefix_len);
+ line += val;
+ let pos = length(line);
+ line += tab_suffix;
+ el.set_state({ line, pos });
+}
+
+function completion_check_prefix(data)
+{
+ let prefix = data[0].name;
+ let prefix_len = length(prefix);
+
+ for (let entry in data) {
+ entry = entry.name;
+ if (prefix_len > length(entry))
+ prefix_len = length(entry);
+ }
+ prefix = substr(prefix, 0, prefix_len);
+
+ for (let entry in data) {
+ entry = substr(entry.name, 0, prefix_len);
+ while (entry != prefix) {
+ prefix_len--;
+ prefix = substr(prefix, 0, prefix_len);
+ entry = substr(entry, 0, prefix_len);
+ }
+ }
+
+ completion_replace_arg(prefix, true);
+}
+
+function completion(count) {
+ if (count < 2) {
+ let line_data = el.get_line();
+ let line = line_data.line;
+ let pos = line_data.pos;
+ tab_suffix = substr(line, pos);
+ if (length(tab_suffix) > 0 &&
+ substr(tab_suffix, 0, 1) != " ") {
+ let idx = index(tab_suffix, " ");
+ if (idx < 0 || !idx)
+ pos += length(tab_suffix);
+ else
+ pos += idx;
+
+ tab_suffix = substr(line, pos);
+ }
+ tab_prefix = substr(line, 0, pos);
+
+ let arg_info = parser.parse(tab_prefix);
+ let is_open = arg_info.missing != null;
+ if (arg_info.missing == "\\\"")
+ tab_quote = "\"";
+ else
+ tab_quote = arg_info.missing ?? "";
+ let args = pop(arg_info.args);
+ let arg_pos = pop(arg_info.pos);
+
+ if (!is_open && substr(tab_prefix, -1) == " ")
+ push(args, "");
+ let tab_arg_pos = arg_pos[length(args) - 1];
+ tab_arg = args[length(args) - 1];
+ if (tab_arg_pos)
+ tab_prefix_len = tab_arg_pos[1] - tab_arg_pos[0];
+ else
+ tab_prefix_len = 0;
+
+ tab_ctx = completion_ctx(arg_info);
+ if (!tab_ctx)
+ return;
+
+ cur_completion = tab_ctx.complete([...args]);
+ }
+
+ if (!tab_ctx)
+ return;
+
+ if (count < 0 || (cur_completion && cur_completion.force_helptext))
+ return helptext(cur_completion);
+
+ let cur = cur_completion;
+ if (!cur || !cur.value) {
+ if (!tab_prefix_len) {
+ el.set_hint("");
+ return;
+ }
+
+ cur = {
+ value: [{
+ name: tab_arg,
+ }]
+ };
+ }
+
+ let data = cur.value;
+ if (length(data) == 0) {
+ el.set_hint(` (no match)`);
+ return;
+ }
+
+ if (length(data) == 1) {
+ completion_replace_arg(data[0].name, data[0].incomplete);
+ el.set_hint("");
+ el.reset_key_input();
+ return;
+ }
+
+ if (count == 1)
+ completion_check_prefix(data);
+
+ if (count > 1) {
+ let idx = (count - 2) % length(data);
+ completion_replace_arg(data[idx].name, false, true);
+ }
+
+ let win = el.get_window();
+ let str = "";
+ let x = 0;
+
+ let categories = sort_completion(data);
+ let cat_len = max_len(keys(categories));
+ let len = max_len(map(data, (v) => v.name));
+ let has_categories = length(categories) > 1 || !categories[" "];
+
+ for (let cat, cdata in categories) {
+ let cat_start = cat != " ";
+ if (cat_start)
+ cat += ": ";
+
+ if (x) {
+ str += "\n";
+ x = 0;
+ }
+ for (let entry in cdata) {
+ let add;
+
+ if (!x && has_categories)
+ add = sprintf(" %-"+cat_len+"s", cat);
+ else
+ add = " ";
+ cat = "";
+
+ let name = entry.name;
+ if (entry.incomplete)
+ name += "...";
+ add += sprintf("%-"+len+"s", name);
+ str += add;
+ x += length(add);
+
+ if (x + length(add) < win.x)
+ continue;
+
+ str += "\n";
+ x = 0;
+ }
+ }
+ el.set_hint(str);
+}
+
+function format_entry(val)
+{
+ if (type(val) == "bool")
+ val = val ? "yes" : "no";
+ return val;
+}
+
+function format_multiline(prefix, val)
+{
+ let prefix2 = replace(prefix, /./g, " ");
+ let prefix_len = length(prefix);
+ let win = el.get_window();
+ let x = 0;
+
+ if (type(val) != "array")
+ val = [ val ];
+ if (length(val) == 0)
+ val = [ "<none>" ];
+
+ for (let cur in val) {
+ cur = format_entry(cur);
+ let cur_lines = split(cur, "\n");
+ if (length(cur_lines) > 1) {
+ if (x) {
+ warn(',\n');
+ x = 0;
+ }
+
+ cur = join("\n" + prefix2, cur_lines);
+ warn(cur);
+ x = win.x;
+ prefix = null;
+ continue;
+ }
+
+ if (x && (x + length(cur) > win.x - 3)) {
+ warn(',\n');
+ x = 0;
+ }
+
+ if (!x) {
+ warn(prefix ?? prefix2);
+ prefix = null;
+ x = prefix_len;
+ } else {
+ warn(', ');
+ x += 2;
+ }
+
+ warn(cur);
+ x += length(cur);
+ }
+ warn('\n');
+}
+
+function format_table(table)
+{
+ let data = table;
+
+ let len = max_len(map(data, (v) => v[0]), 8);
+ for (let line in data) {
+ let name = line[0];
+ let val = line[1];
+ let prefix = sprintf(" %-" + len + "s ", name + ":");
+ format_multiline(prefix, val);
+ }
+}
+
+function convert_table(val)
+{
+ if (type(val) == "array")
+ return val;
+
+ let data = [];
+ for (let name in sort(keys(val)))
+ push(data, [ name, val[name] ]);
+
+ return data;
+}
+
+function convert_multi_table(val)
+{
+ if (type(val) != "array") {
+ let data = [];
+ for (let name in sort(keys(val)))
+ push(data, [ val[name], name ]);
+ val = data;
+ }
+
+ for (let line in val)
+ line[0] = convert_table(line[0]);
+
+ return val;
+}
+
+function format_result(res)
+{
+ if (!res) {
+ warn(color_fg("red", "Unknown command") + "\n");
+ return;
+ }
+ if (!res.ok) {
+ for (let err in res.errors) {
+ warn(color_fg("red", "Error: "+ err.msg) + "\n");
+ }
+ if (!length(res.errors))
+ warn(color_fg("red", "Failed") + "\n");
+ return;
+ }
+
+ if (res.status_msg)
+ warn(color_fg("green", res.status_msg) + "\n");
+
+ if (res.name)
+ warn(res.name + ": ");
+
+ let data = res.data;
+ switch (res.type) {
+ case "multi_table":
+ data = convert_multi_table(data);
+ warn("\n");
+ for (let table in data) {
+ if (table[1])
+ warn("\n" + table[1] + ":\n");
+ format_table(table[0]);
+ warn("\n");
+ }
+ break;
+ case "table":
+ data = convert_table(data);
+ warn("\n");
+ format_table(data);
+ break;
+ case "list":
+ warn("\n");
+ for (let entry in data)
+ warn(" - " + entry + "\n");
+ break;
+ case "string":
+ warn(res.data + "\n");
+ break;
+ case "json":
+ warn(sprintf("%.J\n", res.data));
+ break;
+ }
+}
+
+function line_history_reset()
+{
+ history_idx = -1;
+ history_edit = null;
+ cur_line = null;
+}
+
+function line_history(dir)
+{
+ let min_idx = cur_line == null ? 0 : -1;
+ let new_idx = history_idx + dir;
+
+ if (new_idx < min_idx || new_idx >= length(history))
+ return;
+
+ let line = el.get_line().line;
+ let cur_history = history_edit ?? history;
+ if (history_idx == -1)
+ cur_line = line;
+ else if (cur_history[history_idx] != line) {
+ history_edit ??= [ ...history ];
+ history_edit[history_idx] = line;
+ cur_history = history_edit;
+ }
+
+ history_idx = new_idx;
+ if (history_idx < 0)
+ line = cur_line;
+ else
+ line = cur_history[history_idx];
+ let pos = length(line);
+ el.set_state({ line, pos });
+
+}
+let rev_search, rev_search_results, rev_search_index;
+
+function reverse_search_update(line)
+{
+ if (line) {
+ rev_search = line;
+ rev_search_results = filter(history, (l) => index(l, line) >= 0);
+ rev_search_index = 0;
+ }
+
+ let prompt = "reverse-search: ";
+ if (line && !length(rev_search_results))
+ prompt = "failing " + prompt;
+
+ el.set_state({
+ line2_prompt: prompt,
+ });
+
+ if (line && length(rev_search_results)) {
+ line = rev_search_results[0];
+ let pos = length(line);
+ el.set_state({ line, pos });
+ }
+}
+
+function reverse_search_reset() {
+ if (rev_search == null)
+ return;
+ rev_search = null;
+ rev_search_results = null;
+ rev_search_index = 0;
+ el.set_state({
+ line2_prompt: null
+ });
+}
+
+function reverse_search()
+{
+ if (rev_search == null) {
+ reverse_search_update("");
+ return;
+ }
+
+ if (!length(rev_search_results))
+ return;
+
+ rev_search_index = (rev_search_index + 1) % length(rev_search_results);
+ let line = rev_search_results[rev_search_index];
+ let pos = length(line);
+ el.set_state({ line, pos });
+}
+
+function line_cb(line)
+{
+ reverse_search_reset();
+ line_history_reset();
+ unshift(history, line);
+
+ let arg_info = parser.parse(line);
+ if (!arg_info)
+ return;
+ for (let cmd in arg_info.args) {
+ let orig_cmd = [ ...cmd ];
+
+ // convenience hack
+ if (cmd[0] == "cd" && cmd[1] == "..") {
+ shift(cmd);
+ cmd[0] = "up";
+ } else if (cmd[0] == "ls") {
+ let compl = ctx.complete([""]);
+ if (!compl)
+ continue;
+
+ warn(helptext_list_str(compl));
+ continue;
+ }
+
+ let cur_ctx = ctx.select(cmd);
+ if (type(cur_ctx) != "object" || cur_ctx.errors) {
+ format_result(cur_ctx);
+ break;
+ }
+
+ if (!length(cmd)) {
+ ctx = cur_ctx;
+ update_prompt();
+ continue;
+ }
+
+ try {
+ let res = cur_ctx.call(cmd);
+ format_result(res);
+ if (res && res.ctx) {
+ ctx = res.ctx;
+ update_prompt();
+ }
+ } catch (e) {
+ model.exception(e);
+ }
+ }
+}
+
+const cb = {
+ eof: () => { warn(`\n`); uloop.end(); },
+ line_check: (line) => parser.check(line) == null,
+ line2_cursor: () => {
+ reverse_search_reset();
+ return false;
+ },
+ line2_update: reverse_search_update,
+ key_input: (c, count) => {
+ try {
+ switch(c) {
+ case "?":
+ if (parser.check(el.get_line().line) != null)
+ return false;
+ completion(-1);
+ return true;
+ case "\t":
+ reverse_search_reset();
+ completion(count);
+ return true;
+ case '\x03':
+ if (count < 2) {
+ el.set_state({ line: "", pos: 0 });
+ } else if (ctx.prev) {
+ warn(`\n`);
+ let cur_ctx = ctx.select([ "main" ]);
+ if (cur_ctx && !cur_ctx.errors)
+ ctx = cur_ctx;
+ update_prompt();
+ } else {
+ warn(`\n`);
+ el.poll_stop();
+ uloop.end();
+ }
+ return true;
+ case "\x12":
+ reverse_search();
+ return true;
+ }
+ } catch (e) {
+ warn(`${e}\n${e.stacktrace[0].context}`);
+ }
+ },
+ cursor_up: () => {
+ try {
+ line_history(1);
+ } catch (e) {
+ el.set_hint(`${e}\n${e.stacktrace[0].context}`);
+ }
+ },
+ cursor_down: () => {
+ try {
+ line_history(-1);
+ } catch (e) {
+ el.set_hint(`${e}\n${e.stacktrace[0].context}`);
+ }
+ },
+};
+el = uline.new({
+ utf8: true,
+ cb,
+ key_input_list: [ "?", "\t", "\x03", "\x12" ]
+});
+
+if (SCRIPT_NAME != "cli") {
+ let cur_ctx = ctx.select([ basename(SCRIPT_NAME) ]);
+ if (cur_ctx && cur_ctx != ctx && !cur_ctx.errors) {
+ ctx = cur_ctx;
+ delete ctx.prev;
+ ctx.node.exit = model.node.Root.exit;
+ base_prompt = [];
+ }
+}
+
+while (length(ARGV) > 0) {
+ let cmd = ARGV;
+ let idx = index(ARGV, ":");
+ if (idx >= 0) {
+ cmd = slice(ARGV, 0, idx);
+ ARGV = slice(ARGV, idx + 1);
+ } else {
+ ARGV = [];
+ }
+ interactive ??= false;
+
+ let orig_cmd = [ ...cmd ];
+ let cur_ctx = ctx.select(cmd);
+ if (type(cur_ctx) != "object" || cur_ctx.errors) {
+ format_result(cur_ctx);
+ break;
+ }
+
+ if (!length(cmd)) {
+ ctx = cur_ctx;
+ continue;
+ }
+
+ let res = cur_ctx.call(cmd);
+ format_result(res);
+}
+
+if (script_mode) {
+ el.close();
+ while (!stdin.error()) {
+ let line = stdin.read("line");
+ line_cb(line);
+ }
+ exit(0);
+}
+
+if (interactive != false) {
+ warn("Welcome to the OpenWrt CLI. Press '?' for help on commands/arguments\n");
+ update_prompt();
+ el.set_uloop(line_cb);
+ uloop.run();
+ exit(0);
+}
diff --git a/package/utils/cli/files/usr/share/ucode/cli/cache.uc b/package/utils/cli/files/usr/share/ucode/cli/cache.uc
new file mode 100644
index 0000000000..27cc049f73
--- /dev/null
+++ b/package/utils/cli/files/usr/share/ucode/cli/cache.uc
@@ -0,0 +1,60 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+// Copyright (C) 2025 Felix Fietkau <nbd@nbd.name>
+'use strict';
+
+const CACHE_DEFAULT_TIMEOUT = 5;
+
+function cache_get(key, fn, timeout)
+{
+ let now = time();
+ let entry = this.entries[key];
+ if (entry) {
+ if (now < entry.timeout)
+ return entry.data;
+
+ if (!fn)
+ delete this.entries[key];
+ }
+
+ if (!fn)
+ return;
+
+ let data = fn();
+ if (!entry)
+ this.entries[key] = entry = {};
+ timeout ??= CACHE_DEFAULT_TIMEOUT;
+ entry.timeout = now + timeout;
+ entry.data = data;
+
+ return data;
+}
+
+function cache_remove(key)
+{
+ delete this.entries[key];
+}
+
+function cache_gc() {
+ let now = time();
+ for (let key, entry in this.entries)
+ if (now > entry.timeout)
+ delete this.entries[key];
+}
+
+const cache_proto = {
+ get: cache_get,
+ remove: cache_remove,
+ gc: cache_gc,
+};
+
+export function new(model) {
+ model.cache_proto ??= { model, ...cache_proto };
+ let cache = proto({
+ entries: {},
+ }, model.cache_proto);
+ cache.gc_interval = model.uloop.interval(10000, () => {
+ cache.gc();
+ });
+
+ return cache;
+};
diff --git a/package/utils/cli/files/usr/share/ucode/cli/color.uc b/package/utils/cli/files/usr/share/ucode/cli/color.uc
new file mode 100644
index 0000000000..39e5863533
--- /dev/null
+++ b/package/utils/cli/files/usr/share/ucode/cli/color.uc
@@ -0,0 +1,65 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+// Copyright (C) 2025 Felix Fietkau <nbd@nbd.name>
+'use strict';
+
+const color_codes = {
+ black: 30,
+ red: 31,
+ green: 32,
+ yellow: 33,
+ blue: 34,
+ magenta: 35,
+ cyan: 36,
+ white: 37,
+ default: 39
+};
+
+function color_str(n)
+{
+ return "\e["+n+"m";
+}
+
+function color_code(str)
+{
+ let n = 0;
+ if (substr(str, 0, 7) == "bright_") {
+ str = substr(str, 7);
+ n += 60;
+ }
+ if (!color_codes[str])
+ return;
+
+ n += color_codes[str];
+ return n;
+}
+
+export function color_fg(name, str)
+{
+ let n = color_code(name);
+ if (!n)
+ return str;
+
+ let ret = color_str(n);
+ if (str != null)
+ ret += str + color_str(39);
+
+ return ret;
+};
+
+export function color_bg(name, str)
+{
+ let n = color_code(name);
+ if (!n)
+ return str;
+
+ let ret = color_str(n + 10);
+ if (str != null)
+ ret += str + color_str(49);
+
+ return ret;
+};
+
+export function bold(str)
+{
+ return color_str(1) + str + color_str(0);
+};
diff --git a/package/utils/cli/files/usr/share/ucode/cli/context-call.uc b/package/utils/cli/files/usr/share/ucode/cli/context-call.uc
new file mode 100644
index 0000000000..0977cda741
--- /dev/null
+++ b/package/utils/cli/files/usr/share/ucode/cli/context-call.uc
@@ -0,0 +1,126 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+// Copyright (C) 2025 Felix Fietkau <nbd@nbd.name>
+'use strict';
+
+function default_result()
+{
+ return {
+ errors: [],
+ ok: false
+ };
+}
+
+function context_clone()
+{
+ let ret = { ...this };
+ ret.result = default_result();
+ ret.data = { ...ret.data };
+ return proto(ret, proto(this));
+}
+
+function call_select(...args)
+{
+ this.result.ctx = this.node_ctx.select(args);
+}
+
+function call_ok(msg)
+{
+ this.result.ok = true;
+ if (msg)
+ this.result.status_msg = msg;
+ return true;
+}
+
+function call_error(code, msg, ...args)
+{
+ msg ??= "Unknown error";
+ msg = sprintf(msg, ...args);
+ let error = {
+ code, msg, args
+ };
+ push(this.result.errors, error);
+}
+
+function call_generic(ctx, name, type, val)
+{
+ ctx.result.type = type;
+ ctx.result.name = name;
+ ctx.result.data = val;
+ return ctx.ok();
+}
+
+function call_multi_table(name, val)
+{
+ return call_generic(this, name, "multi_table", val);
+}
+
+function call_table(name, val)
+{
+ return call_generic(this, name, "table", val);
+}
+
+function call_list(name, val)
+{
+ return call_generic(this, name, "list", val);
+}
+
+function call_string(name, val)
+{
+ return call_generic(this, name, "string", val);
+}
+
+function call_json(name, val)
+{
+ return call_generic(this, name, "json", val);
+}
+
+function call_apply_defaults(named_args, args)
+{
+ let entry = this.entry;
+ named_args ??= entry.named_args;
+ args ??= this.named_args;
+ for (let name, arg in named_args)
+ if (arg.default != null && !(name in args))
+ args[name] ??= arg.default;
+}
+
+export const callctx_error_proto = {
+ missing_argument: function(msg, ...args) {
+ return this.error("MISSING_ARGUMENT", msg ?? "Missing argument", ...args);
+ },
+ invalid_argument: function(msg, ...args) {
+ return this.error("INVALID_ARGUMENT", msg ?? "Invalid argument", ...args);
+ },
+ unknown_error: function(msg, ...args) {
+ return this.error("UNKNOWN_ERROR", msg ?? "Unknown error", ...args);
+ },
+ not_found: function(msg, ...args) {
+ return this.error("NOT_FOUND", msg ?? "Not found", ...args);
+ },
+ command_failed: function(msg, ...args) {
+ return this.error("COMMAND_FAILEDu", msg ?? "Command failed", ...args);
+ },
+};
+
+const callctx_proto = {
+ clone: context_clone,
+ select: call_select,
+ apply_defaults: call_apply_defaults,
+ ok: call_ok,
+ list: call_list,
+ table: call_table,
+ multi_table: call_multi_table,
+ string: call_string,
+ json: call_json,
+
+ error: call_error,
+ ...callctx_error_proto,
+};
+
+export function new(model, ctx) {
+ let node_ctx = ctx;
+ let data = ctx.data;
+ model.callctx_proto ??= { model, ...callctx_proto };
+ let result = default_result();
+ return proto({ node_ctx, data, result }, model.callctx_proto);
+};
diff --git a/package/utils/cli/files/usr/share/ucode/cli/context.uc b/package/utils/cli/files/usr/share/ucode/cli/context.uc
new file mode 100644
index 0000000000..53b5f57047
--- /dev/null
+++ b/package/utils/cli/files/usr/share/ucode/cli/context.uc
@@ -0,0 +1,708 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+// Copyright (C) 2025 Felix Fietkau <nbd@nbd.name>
+'use strict';
+
+import * as callctx from "cli.context-call";
+
+function prefix_match(prefix, str, icase)
+{
+ if (icase) {
+ str = lc(str);
+ prefix = lc(prefix);
+ }
+ return substr(str, 0, length(prefix)) == prefix;
+}
+
+function context_clone()
+{
+ let ret = { ...this };
+ ret.prompt = [ ...ret.prompt ];
+ ret.data = { ...ret.data };
+ ret.hooks = {};
+ return proto(ret, proto(this));
+}
+
+function context_entries()
+{
+ return keys(this.node)
+}
+
+function context_help(entry)
+{
+ if (entry)
+ return this.node[entry].help;
+
+ let ret = {};
+ for (let name, val in this.node)
+ ret[name] = val.help ?? "";
+
+ return ret;
+}
+
+function context_add_hook(type, cb)
+{
+ this.hooks[type] ??= [];
+ push(this.hooks[type], cb);
+}
+
+function context_select_error(code, msg, ...args)
+{
+ msg ??= "Unknown error";
+ msg = sprintf(msg, ...args);
+ let error = {
+ code, msg, args
+ };
+ this.errors ??= [];
+ push(this.errors, error);
+}
+
+function context_set(prompt, data)
+{
+ if (prompt)
+ this.cur_prompt = prompt;
+ if (data)
+ this.data = { ...this.data, ...data };
+ return true;
+}
+
+const context_select_proto = {
+ add_hook: context_add_hook,
+ error: context_select_error,
+ set: context_set,
+ ...callctx.callctx_error_proto,
+};
+
+function __context_select(ctx, name, args)
+{
+ let entry = ctx.node[name];
+ if (!entry || !entry.select_node)
+ return;
+
+ let node = ctx.model.node[entry.select_node];
+ if (!node)
+ return;
+
+ let ret = proto(ctx.clone(), ctx.model.context_select_proto);
+ ret.cur_prompt = name;
+ ret.node = node;
+ try {
+ if (entry.select &&
+ !call(entry.select, entry, ctx.model.scope, ret, args))
+ ret.errors ??= [];
+ } catch (e) {
+ ctx.model.exception(e);
+ return;
+ }
+
+ push(ret.prompt, ret.cur_prompt);
+ ret.prev = ctx;
+ proto(ret, proto(ctx));
+
+ return ret;
+}
+
+function context_run_hooks(ctx, name)
+{
+ try {
+ while (length(ctx.hooks[name]) > 0) {
+ let hook = ctx.hooks[name][0];
+
+ let ret = call(hook, ctx, ctx.model.scope);
+ if (!ret)
+ return false;
+
+ shift(ctx.hooks.exit);
+ }
+ } catch (e) {
+ ctx.model.exception(e);
+ return false;
+ }
+
+ return true;
+}
+
+function context_prev(ctx, skip_hooks)
+{
+ if (!skip_hooks && !context_run_hooks(ctx, "exit"))
+ return;
+ return ctx.prev;
+}
+
+function context_top(ctx, skip_hooks)
+{
+ while (ctx && ctx.prev)
+ ctx = context_prev(ctx, skip_hooks);
+ return ctx;
+}
+
+function prepare_spec(e, ctx, spec, argv)
+{
+ if (type(spec) != "function")
+ return spec;
+
+ return call(spec, e, ctx.model.scope, ctx, argv);
+}
+
+function prepare_default(e, ctx, spec, argv, named_args)
+{
+ if (type(spec) != "object" || type(spec.default) != "function")
+ return;
+
+ try {
+ spec.default = call(spec.default, e, ctx.model.scope, ctx, argv, named_args, spec);
+ } catch (e) {
+ model.exception(e);
+ }
+}
+
+function prepare_attr_spec(e, ctx, spec, argv, named_args)
+{
+ if (type(spec) != "object")
+ return spec;
+
+ let t = ctx.model.types[spec.type];
+ if (t)
+ spec = { ...t, ...spec };
+ else
+ spec = { ...spec };
+
+ prepare_default(e, ctx, spec, argv, named_args, spec);
+ if (type(spec.value) == "function")
+ try {
+ spec.value = call(spec.value, e, ctx.model.scope, ctx, argv, named_args, spec);
+ } catch (e) {
+ ctx.model.exception(e);
+ spec.value = [];
+ }
+
+ return spec;
+}
+
+function parse_arg(ctx, name, spec, val)
+{
+ let t;
+
+ if (val == null) {
+ ctx.invalid_argument("Missing argument %s", name);
+ return;
+ }
+
+ if (type(spec) == "object" && spec.type)
+ t = ctx.model.types[spec.type];
+ if (!t) {
+ ctx.invalid_argument("Invalid type in argument: %s", name);
+ return;
+ }
+
+ if (!t.parse)
+ return val;
+
+ return call(t.parse, spec, ctx.model.scope, ctx, name, val);
+}
+
+const context_defaults = {
+ up: [ "Return to previous node", context_prev ],
+ exit: [ "Return to previous node", context_prev ],
+ main: [ "Return to main node", context_top ],
+};
+
+const context_default_order = [ "up", "exit", "main" ];
+
+function context_select(args, completion)
+{
+ let ctx = this;
+
+ while (length(args) > completion ? 1 : 0) {
+ let name = args[0];
+ let entry = ctx.node[name];
+
+ if (!entry) {
+ let e = context_defaults[name];
+ if (!e)
+ return ctx;
+
+ shift(args);
+ ctx = e[1](ctx, completion);
+ if (!ctx)
+ return;
+
+ continue;
+ }
+
+ if (!entry.select_node)
+ return ctx;
+
+ let num_args = length(entry.args);
+ if (completion && num_args + 1 >= length(args))
+ return ctx;
+
+ shift(args);
+ let argv = [];
+ let parse_ctx = callctx.new(this.model, ctx);
+ if (num_args > 0) {
+ let cur_argv = slice(args, 0, num_args);
+ for (let i = 0; i < num_args; i++) {
+ let arg = shift(args);
+ let spec = entry.args[i];
+
+ spec = prepare_attr_spec(entry, ctx, spec, cur_argv, {});
+ if (arg != null)
+ arg = parse_arg(parse_ctx, spec.name, spec, arg);
+
+ if (arg != null)
+ push(argv, arg);
+ }
+
+ }
+
+ if (entry.no_subcommands && length(args) > 0)
+ parse_ctx.invalid_argument("command %s does not support subcommands", name);
+
+ if (length(parse_ctx.result.errors) > 0) {
+ ctx = ctx.clone();
+ ctx.errors = parse_ctx.result.errors;
+ return ctx;
+ }
+
+ ctx = __context_select(ctx, name, argv);
+ if (type(ctx) != "object" || ctx.errors)
+ break;
+ }
+
+ return ctx;
+}
+
+function complete_named_params(ctx, entry, obj, name, argv, named_params)
+{
+ let data = [];
+ let empty = "";
+
+ if (substr(name, 0, 1) == "-") {
+ empty = "-";
+ name = substr(name, 1);
+ }
+
+ let defaults = {};
+ callctx.new(ctx.model, ctx).apply_defaults(obj, defaults);
+ for (let cur_name in sort(keys(obj))) {
+ let val = obj[cur_name];
+
+ if (!prefix_match(name, cur_name) || val.no_complete)
+ continue;
+
+ if (empty && !(val.allow_empty ?? entry.allow_empty))
+ continue;
+
+ if (!val.multiple && named_params[cur_name] != null)
+ continue;
+
+ if (type(val.available) == "function" &&
+ !call(val.available, val, ctx.model.scope, ctx, argv, named_params))
+ continue;
+
+ val = {
+ name: empty + cur_name,
+ ...val,
+ };
+ push(data, val);
+ }
+
+ return {
+ type: "keywords",
+ name: "parameter",
+ help: "Parameter name",
+ value: data
+ };
+}
+
+function complete_param(e, ctx, cur, val, args, named_args)
+{
+ cur = prepare_attr_spec(e, ctx, cur, args, named_args);
+
+ if (type(cur.value) == "object") {
+ let ret = [];
+ for (let key in sort(keys(cur.value)))
+ if (prefix_match(val, key, cur.ignore_case))
+ push(ret, {
+ name: key,
+ help: cur.value[key]
+ });
+
+ cur.value = ret;
+ return cur;
+ }
+
+ if (type(cur.value) == "array") {
+ cur.value = map(sort(filter(cur.value, (v) => prefix_match(val, v, cur.ignore_case))), (v) => ({ name: v }));
+ return cur;
+ }
+
+ let type_info = ctx.model.types[cur.type];
+ if (!type_info || !type_info.complete)
+ return cur;
+
+ cur.value = call(type_info.complete, cur, ctx.model.scope, ctx, val);
+
+ return cur;
+}
+
+function complete_arg_list(e, ctx, arg_info, args, base_args, named_args)
+{
+ let cur_idx = length(args) - 1;
+ let cur = arg_info[cur_idx];
+ let val;
+
+ for (let i = 0; i <= cur_idx; i++)
+ val = shift(args);
+
+ let ret = complete_param(e, ctx, cur, val, base_args, named_args);
+ if (!cur.prefix_separator)
+ return ret;
+
+ let prefix_len = length(val);
+ let vals = [];
+ let match_prefix;
+ for (let cur_val in ret.value) {
+ let cur_str = cur_val.name;
+ let cur_suffix = substr(cur_str, prefix_len);
+ let idx = index(cur_suffix, cur.prefix_separator);
+ if (idx < 0) {
+ push(vals, cur_val);
+ continue;
+ }
+
+ let cur_prefix = substr(cur_str, 0, prefix_len + idx + 1);
+ if (cur_prefix == match_prefix)
+ continue;
+
+ match_prefix = cur_prefix;
+ push(vals, {
+ ...cur_val,
+ name: cur_prefix,
+ incomplete: true
+ });
+ }
+ ret.value = vals;
+
+ return ret;
+}
+
+function handle_empty_param(entry, spec, name, argv, named_args)
+{
+ if (substr(name, 0, 1) != "-")
+ return;
+
+ name = substr(name, 1);
+ let cur = spec[name];
+ if (!cur)
+ return;
+
+ if (cur.default == null &&
+ !(cur.allow_empty ?? entry.allow_empty))
+ return;
+
+ if (cur.required) {
+ cur = { ...cur };
+ prepare_default(e, ctx, cur, argv, named_args, cur);
+ named_args[name] = cur.default;
+ } else {
+ named_args[name] = null;
+ }
+ return true;
+}
+
+
+function default_complete(ctx, args)
+{
+ let num_args = length(this.args);
+ let named_args = {};
+ let cur_args;
+
+ if (length(args) <= num_args)
+ return complete_arg_list(this, ctx, this.args, args, [ ...args ], named_args);
+
+ let spec = prepare_spec(this, ctx, this.named_args, args);
+ if (!spec)
+ return;
+
+ let base_args = slice(args, 0, num_args);
+ for (let i = 0; i < num_args; i++)
+ shift(args);
+
+ while (length(args) > 0) {
+ let name = args[0];
+
+ if (length(args) == 1)
+ return complete_named_params(ctx, this, spec, name, base_args, named_args);
+
+ shift(args);
+ let cur = spec[name];
+ if (!cur) {
+ if (handle_empty_param(this, spec, name, base_args, named_args))
+ continue;
+ return;
+ }
+
+ if (!cur.args) {
+ named_args[name] = true;
+ continue;
+ }
+
+ let val;
+ let cur_spec = cur.args;
+ if (type(cur_spec) != "array") {
+ cur_spec = [{
+ name,
+ help: cur.help,
+ ...cur_spec
+ }];
+ named_args[name] = shift(args);
+ val = [ named_args[name] ];
+ } else {
+ let num_args = length(cur_spec);
+ let val = [];
+ for (let i = 0; i < num_args; i++)
+ push(val, shift(args));
+ named_args[name] = val;
+ }
+
+ if (!length(args))
+ return complete_arg_list(this, ctx, cur_spec, val, base_args, named_args);
+ }
+}
+
+function context_complete(args)
+{
+ let ctx = this.select(args, true);
+ if (!ctx || ctx.errors)
+ return;
+
+ if (ctx != this) {
+ ctx = ctx.clone();
+ ctx.skip_default_complete = true;
+ }
+
+ if (length(args) > 1) {
+ let name = shift(args);
+ let entry = ctx.node[name];
+ if (!entry)
+ return;
+
+ try {
+ if (!entry.available || call(entry.available, entry, ctx.model.scope, ctx, args))
+ return call(entry.complete ?? default_complete, entry, ctx.model.scope, ctx, args);
+ } catch (e) {
+ this.model.exception(e);
+ }
+ return;
+ }
+
+ let name = shift(args) ?? "";
+ let prefix_len = length(name);
+ let data = [];
+ let default_data = {};
+ for (let cur_name in sort(keys(ctx.node))) {
+ let val = ctx.node[cur_name];
+
+ if (substr(cur_name, 0, prefix_len) != name)
+ continue;
+
+ if (val.available && !call(val.available, val, ctx.model.scope, ctx, args))
+ continue;
+
+ let cur = {
+ name: cur_name,
+ help: val.help,
+ category: val.select_node ? "Object" : "Action",
+ };
+ if (context_defaults[cur_name])
+ default_data[cur_name] = cur;
+ else
+ push(data, cur);
+ }
+
+ for (let cur_name in context_default_order) {
+ if (substr(cur_name, 0, prefix_len) != name)
+ continue;
+
+ let val = default_data[cur_name];
+ if (!val) {
+ if (!ctx.prev || ctx.skip_default_complete)
+ continue;
+ val = {
+ name: cur_name,
+ help: context_defaults[cur_name][0],
+ category: "Navigation",
+ };
+ }
+
+ push(data, val);
+ }
+
+ return {
+ type: "enum",
+ name: "command",
+ help: "Command",
+ value: data
+ };
+}
+
+function context_call(args)
+{
+ let ctx = this.select(args);
+ if (!ctx || !length(args))
+ return;
+
+ let name = shift(args);
+ let entry = ctx.node[name];
+ if (!entry)
+ return;
+
+ if (!entry.call)
+ return;
+
+ let named_args = {};
+ let num_args = length(entry.args);
+ let cur_argv = slice(args, 0, num_args);
+ let argv = [];
+ let skip = {};
+
+ ctx = callctx.new(this.model, ctx);
+ ctx.entry = entry;
+ ctx.named_args = named_args;
+
+ for (let i = 0; i < num_args; i++) {
+ let arg = shift(args);
+ let spec = entry.args[i];
+
+ spec = prepare_attr_spec(entry, ctx, spec, cur_argv, named_args);
+ if (arg != null)
+ arg = parse_arg(ctx, spec.name, spec, arg);
+
+ if (spec.required && !length(arg)) {
+ if (spec.default)
+ arg = spec.default;
+ else
+ ctx.missing_argument("Missing argument %d: %s", i + 1, spec.name);
+ }
+
+ if (arg != null)
+ push(argv, arg);
+ }
+
+ let spec = prepare_spec(entry, ctx, entry.named_args, argv) ?? {};
+ let defaults = {};
+ ctx.apply_defaults(spec, defaults);
+ while (length(args) > 0) {
+ let name = shift(args);
+ let cur = spec[name];
+ try {
+ if (cur && type(cur.available) == "function" &&
+ !call(cur.available, cur, ctx.model.scope, ctx, argv, { ...defaults, ...named_args }))
+ cur = null;
+ } catch (e) {
+ ctx.model.exception(e);
+ continue;
+ }
+
+ if (!cur) {
+ if (handle_empty_param(entry, spec, name, argv, named_args))
+ continue;
+ ctx.invalid_argument("Invalid argument: %s", name);
+ return ctx.result;
+ }
+
+ if (!cur.args) {
+ named_args[name] = true;
+ continue;
+ }
+
+ let val;
+ let cur_spec = cur.args;
+ if (type(cur.args) == "array") {
+ val = [];
+ for (let spec in cur.args) {
+ spec = prepare_attr_spec(entry, ctx, spec, argv, named_args);
+ let cur = parse_arg(ctx, name, spec, shift(args));
+ if (cur == null)
+ return ctx.result;
+
+ push(val, cur);
+ }
+ } else {
+ let spec = prepare_attr_spec(entry, ctx, cur.args, argv, named_args);
+ val = parse_arg(ctx, name, spec, shift(args));
+ if (val == null)
+ return ctx.result;
+ }
+ if (cur.multiple) {
+ named_args[name] ??= [];
+ push(named_args[name], val);
+ } else {
+ named_args[name] = val;
+ }
+ }
+
+ for (let name, arg in spec) {
+ if (!arg.required || named_args[name] != null)
+ continue;
+
+ try {
+ if (type(arg.available) == "function" &&
+ !call(arg.available, arg, ctx.model.scope, ctx, argv, named_args))
+ continue;
+ } catch (e) {
+ ctx.model.exception(e);
+ continue;
+ }
+
+ let spec = { ...arg };
+ prepare_default(entry, ctx, spec, argv, named_args);
+ if (spec.default != null)
+ named_args[name] = spec.default;
+ else
+ ctx.missing_argument("Missing argument: %s", name);
+ }
+
+ if (length(ctx.result.errors) > 0)
+ return ctx.result;
+
+ if (entry.available && !call(entry.available, entry, ctx.model.scope, ctx))
+ return ctx.result;
+
+ try {
+ if (!entry.validate || call(entry.validate, entry, ctx.model.scope, ctx, argv, named_args))
+ call(entry.call, entry, ctx.model.scope, ctx, argv, named_args);
+ } catch (e) {
+ this.model.exception(e);
+ return;
+ }
+ return ctx.result;
+}
+
+const context_proto = {
+ clone: context_clone,
+ entries: context_entries,
+ help: context_help,
+ select: context_select,
+ call: context_call,
+ complete: context_complete,
+ add_hook: context_add_hook,
+};
+
+export function new(model) {
+ model.context_proto ??= {
+ model,
+ ...context_proto
+ };
+ model.context_select_proto ??= {
+ model,
+ ...context_select_proto
+ };
+ return proto({
+ prompt: [],
+ node: model.node.Root,
+ hooks: {},
+ data: {}
+ }, model.context_proto);
+};
diff --git a/package/utils/cli/files/usr/share/ucode/cli/datamodel.uc b/package/utils/cli/files/usr/share/ucode/cli/datamodel.uc
new file mode 100644
index 0000000000..f6d9454dd7
--- /dev/null
+++ b/package/utils/cli/files/usr/share/ucode/cli/datamodel.uc
@@ -0,0 +1,175 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+// Copyright (C) 2025 Felix Fietkau <nbd@nbd.name>
+'use strict';
+
+import * as context from "cli.context";
+import * as cache from "cli.cache";
+import * as libubus from "ubus";
+import * as uloop from "uloop";
+import { glob, dirname } from "fs";
+let types = require("cli.types");
+
+uloop.init();
+let ubus = libubus.connect();
+
+function status_msg(msg)
+{
+ if (this.cb.status_msg)
+ call(this.cb.status_msg, this, this.scope, msg);
+}
+
+function poll_key(keys, prompt)
+{
+ if (!model.cb.poll_key)
+ return;
+
+ if (prompt)
+ warn(prompt);
+
+ while (1) {
+ let key = lc(model.cb.poll_key());
+ if (!key || key == "\x03")
+ return;
+
+ if (index(keys, key) >= 0)
+ return key;
+ }
+}
+
+function merge_object(obj, add)
+{
+ for (let name, entry in add)
+ obj[name] = entry;
+}
+
+function add_node(name, node)
+{
+ let obj = this.node;
+
+ if (obj[name])
+ merge_object(obj[name], node);
+ else
+ obj[name] = { ...node };
+
+ return obj[name];
+}
+
+function add_nodes(add)
+{
+ for (let name, val in add)
+ this.add_node(name, val);
+}
+
+function add_hook(name, val)
+{
+ let obj = this.hooks;
+
+ if (type(val) == "function")
+ val = [ val ];
+ obj[name] ??= [];
+ push(obj[name], ...val);
+}
+
+function add_hooks(add)
+{
+ for (let name, val in add)
+ this.add_hook(name, val);
+}
+
+function add_type(name, val)
+{
+ this.type[name] = val;
+}
+
+function add_types(add)
+{
+ for (let name, val in add)
+ this.add_type(name, val);
+}
+
+function add_module(path)
+{
+ if (substr(path, 0, 1) != "/")
+ path = dirname(sourcepath()) + "/modules/" + path;
+
+ let mod;
+ try {
+ let fn = loadfile(path, {
+ raw_mode: true,
+ strict_declarations: true,
+ });
+ mod = call(fn, this, this.scope);
+ } catch (e) {
+ this.warn(`${e}\n${e.stacktrace[0].context}\nFailed to open module ${path}.\n`);
+ return;
+ }
+}
+
+function add_modules(path)
+{
+ path ??= "*.uc";
+ if (substr(path, 0, 1) != "/")
+ path = dirname(sourcepath()) + "/modules/" + path;
+
+ for (let mod in glob(path))
+ this.add_module(mod);
+}
+
+function run_hook(name, ...args)
+{
+ let hooks = this.hooks[name];
+ if (!hooks)
+ return;
+
+ for (let hook in hooks)
+ call(hook, this, this.scope, ...args);
+}
+
+function init()
+{
+ this.run_hook("init");
+}
+
+function context_new()
+{
+ return context.new(this);
+}
+
+function exception(e)
+{
+ this.warn(`${e}\n${e.stacktrace[0].context}`);
+}
+
+const data_proto = {
+ warn, exception,
+ poll_key,
+ add_module,
+ add_modules,
+ add_node,
+ add_nodes,
+ add_type,
+ add_types,
+ add_hook,
+ add_hooks,
+ run_hook,
+ init,
+ status_msg,
+ context: context_new,
+};
+
+export function new(cb) {
+ cb ??= {};
+ let model = proto({
+ libubus, ubus, uloop,
+ cb,
+ hooks: {},
+ node: {
+ Root: {}
+ },
+ warnings: {},
+ types: { ...types },
+ }, data_proto);
+ model.scope = proto({ model }, global);
+ model.cache = cache.new(model);
+ return model;
+};
diff --git a/package/utils/cli/files/usr/share/ucode/cli/modules/network.uc b/package/utils/cli/files/usr/share/ucode/cli/modules/network.uc
new file mode 100644
index 0000000000..3d5ef5b058
--- /dev/null
+++ b/package/utils/cli/files/usr/share/ucode/cli/modules/network.uc
@@ -0,0 +1,138 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+// Copyright (C) 2025 Felix Fietkau <nbd@nbd.name>
+'use strict';
+
+import { time_format } from "cli.utils";
+
+function get_interfaces()
+{
+ let data = model.ubus.call("network.interface", "dump");
+ if (!data)
+ return {};
+
+ let ret = {};
+ for (let iface in data.interface)
+ ret[iface.interface] = iface;
+
+ return ret;
+}
+
+function interface_validate(ctx, argv)
+{
+ let name = argv[0];
+ if (!name)
+ return ctx.missing_argument("Missing argument: %s", "name");
+
+ if (index(get_interfaces(), name) < 0)
+ return ctx.not_found("Interface not found: %s", name);
+
+ return true;
+}
+
+const interface_args = [
+ {
+ name: "interface",
+ help: "Interface name",
+ type: "enum",
+ value: (ctx) => keys(get_interfaces())
+ }
+];
+
+function interface_status(data)
+{
+ if (data.up)
+ return "up";
+ if (!data.autostart)
+ return "down";
+ if (!data.available)
+ return "unavailable";
+ return "pending";
+}
+
+const Network = {
+ list: {
+ help: "List interfaces",
+ call: function(ctx, argv) {
+ return ctx.list("Interfaces", keys(get_interfaces()));
+ }
+ },
+ reload: {
+ help: "Reload network config",
+ call: function(ctx, argv) {
+ model.ubus.call("network", "reload");
+ return ctx.ok("Configuration reloaded");
+ }
+ },
+ restart: {
+ help: "Restart interface",
+ validate: interface_validate,
+ args: interface_args,
+ call: function(ctx, argv) {
+ let name = shift(argv);
+ model.ubus.call("network.interface."+name, "down");
+ model.ubus.call("network.interface."+name, "up");
+ return ctx.ok("Interface restarted");
+ }
+ },
+ start: {
+ help: "Start interface",
+ validate: interface_validate,
+ args: interface_args,
+ call: function(ctx, argv) {
+ let name = shift(argv);
+ model.ubus.call("network.interface."+name, "up");
+ return ctx.ok("Interface started");
+ }
+ },
+ stop: {
+ help: "Stop interface",
+ validate: interface_validate,
+ args: interface_args,
+ call: function(ctx, argv) {
+ let name = shift(argv);
+ model.ubus.call("network.interface."+name, "down");
+ return ctx.ok("Interface stopped");
+ }
+ },
+ status: {
+ help: "Interface status",
+ args: interface_args,
+ call: function(ctx, argv) {
+ let name = shift(argv);
+ let status = get_interfaces();
+ if (!name) {
+ let data = {};
+ for (let iface, ifdata in status)
+ data[iface] = interface_status(ifdata);
+
+ return ctx.table("Status", data);
+ }
+
+ let ifdata = status[name];
+ let data = {
+ Status: interface_status(ifdata),
+ };
+ if (ifdata.up)
+ data.Uptime = time_format(ifdata.uptime);
+
+ if (length(ifdata["ipv4-address"]) > 0)
+ data.IPv4 = join(", ", map(ifdata["ipv4-address"], (v) => v.address + "/" + v.mask));
+ if (length(ifdata["ipv6-address"]) > 0)
+ data.IPv6 = join(", ", map(ifdata["ipv6-address"], (v) => v.address + "/" + v.mask));
+ if (length(ifdata["dns-server"]) > 0)
+ data.DNS = join(", ", ifdata["dns-server"]);
+ if (length(ifdata["route"]) > 0)
+ data.Routes = join(", ", map(ifdata["route"], (v) => (v.mask == 0 ? "Default" : `${v.target}/${v.mask}`) + ": " + v.nexthop));
+ return ctx.table("Status", data);
+ }
+ }
+};
+
+const Root = {
+ network: {
+ help: "Network interface configuration",
+ select_node: "Network",
+ }
+};
+
+model.add_nodes({ Root, Network });
diff --git a/package/utils/cli/files/usr/share/ucode/cli/modules/service.uc b/package/utils/cli/files/usr/share/ucode/cli/modules/service.uc
new file mode 100644
index 0000000000..a280f6559e
--- /dev/null
+++ b/package/utils/cli/files/usr/share/ucode/cli/modules/service.uc
@@ -0,0 +1,174 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+// Copyright (C) 2025 Felix Fietkau <nbd@nbd.name>
+'use strict';
+
+import { glob, access, basename } from "fs";
+
+function get_services()
+{
+ return model.cache.get("init_service_list", () => {
+ let services = glob("/etc/init.d/*");
+ services = filter(services, (v) => !system([ "grep", "-q", "start_service()", v ]));
+ services = map(services, basename);
+ return sort(services);
+ });
+}
+
+function get_service_status( name)
+{
+ return model.ubus.call("service", "list", (name ? { name } : null));
+}
+
+function service_running(name)
+{
+ let status = get_service_status(name);
+ return !!(status && status[name]);
+}
+
+function __service_cmd(name, cmd)
+{
+ return system([ "/etc/init.d/" + name, cmd ]) == 0;
+}
+
+function service_cmd(ctx, name, cmd, msg)
+{
+ if (__service_cmd(name, cmd))
+ return ctx.ok(msg);
+ else
+ return ctx.command_failed("Command failed");
+}
+
+function service_validate(ctx, argv)
+{
+ let name = argv[0];
+ if (!name)
+ return ctx.missing_argument("Missing argument: %s", "name");
+
+ if (index(get_services(), name) < 0)
+ return ctx.not_found("Service not found: %s", name);
+
+ return true;
+}
+
+const service_args = [
+ {
+ name: "name",
+ help: "Service name",
+ type: "enum",
+ value: (ctx) => get_services()
+ }
+];
+
+const service_settings = {
+ enabled: {
+ help: "Service enabled at system boot",
+ },
+ disabled: {
+ help: "Service disabled at system boot",
+ }
+};
+
+const SystemService = {
+ list: {
+ help: "List services",
+ call: function(ctx, argv) {
+ return ctx.list("Services", get_services());
+ }
+ },
+ reload: {
+ help: "Reload service",
+ validate: service_validate,
+ args: service_args,
+ call: function(ctx, argv) {
+ return service_cmd(ctx, shift(argv), "reload", "Service reloaded");
+ }
+ },
+ restart: {
+ help: "Restart service",
+ validate: service_validate,
+ args: service_args,
+ call: function(ctx, argv) {
+ return service_cmd(ctx, shift(argv), "restart", "Service restarted");
+ }
+ },
+ set: {
+ help: "Change service settings",
+ validate: service_validate,
+ args: service_args,
+ named_args: service_settings,
+ call: function(ctx, argv, param) {
+ if (!length(param))
+ return ctx.invalid_argument("No settings provided");
+
+ if (param.enabled && param.disabled)
+ return ctx.invalid_argument("enabled and disabled cannot be set at the same time");
+
+ if (param.enabled && !__service_cmd(name, "enable"))
+ ctx.command_failed("Command failed: %s", "enable");
+
+ if (param.disabled && !__service_cmd(name, "disable"))
+ ctx.command_failed("Command failed: %s", "disable");
+
+ return ctx.ok("Settings changed");
+ }
+ },
+ start: {
+ help: "Start service",
+ validate: service_validate,
+ args: service_args,
+ call: function(ctx, argv) {
+ let name = shift(argv);
+
+ if (service_running(name))
+ return ctx.invalid_argument("Service already running", name);
+
+ return service_cmd(ctx, name, "start", "Service started");
+ }
+ },
+ stop: {
+ help: "Stop service",
+ validate: service_validate,
+ args: service_args,
+ call: function(ctx, argv) {
+ let name = shift(argv);
+
+ if (!service_running(name))
+ return ctx.invalid_argument("Service not running", name);
+
+ return service_cmd(ctx, name, "stop", "Service stopped");
+ }
+ },
+ status: {
+ help: "Service status",
+ args: service_args,
+ call: function(ctx, argv) {
+ let name = shift(argv);
+ if (!name) {
+ let data = {};
+ for (let service in get_services()) {
+ let running = service_running(service);
+ data[service] = running ? "running" : "not running";
+ }
+ return ctx.table("Status", data);
+ }
+
+ if (index(get_services(), name) < 0)
+ return ctx.not_found("Service not found: %s", name);
+
+ let data = {
+ "Running": service_running(name),
+ "Enabled": __service_cmd(name, "enabled"),
+ };
+ return ctx.table("Status", data);
+ }
+ }
+};
+
+const Root = {
+ service: {
+ help: "System service configuration",
+ select_node: "SystemService",
+ }
+};
+
+model.add_nodes({ Root, SystemService });
diff --git a/package/utils/cli/files/usr/share/ucode/cli/object-editor.uc b/package/utils/cli/files/usr/share/ucode/cli/object-editor.uc
new file mode 100644
index 0000000000..639313b72f
--- /dev/null
+++ b/package/utils/cli/files/usr/share/ucode/cli/object-editor.uc
@@ -0,0 +1,660 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+// Copyright (C) 2025 Felix Fietkau <nbd@nbd.name>
+'use strict';
+
+function __get_edit_object(ctx, entry, argv, name)
+{
+ if (type(entry.edit_object) == "function")
+ return call(entry.edit_object, entry, ctx.model.scope, ctx, argv);
+
+ if (name)
+ return ctx.data.edit[name];
+
+ return ctx.data.edit;
+}
+
+function get_edit_object(ctx, entry, argv, name)
+{
+ let obj = __get_edit_object(ctx, entry, argv, name);
+ if (!obj)
+ ctx.invalid_argument();
+
+ return obj;
+}
+
+function get_param_object(ctx, obj, spec, argv)
+{
+ if (type(spec.get_object) != "function")
+ return obj;
+
+ return call(spec.get_object, spec, ctx.model.scope, ctx, spec, obj, argv);
+}
+
+function call_change_cb(ctx, entry, argv, named)
+{
+ if (!length(named) || type(entry.change_cb) != "function")
+ return;
+
+ call(entry.change_cb, entry, ctx.model.scope, ctx, argv);
+}
+
+function check_duplicate(ctx, val, new_val)
+{
+ for (let i = 0; i < length(new_val); i++) {
+ let v = new_val[i];
+ if ((val && index(val, v) >= 0) ||
+ (i && index(slice(new_val, 0, i), v) >= 0)) {
+ ctx.invalid_argument("Duplicate value: %s", v);
+ return true;
+ }
+ }
+}
+
+export function add_call(ctx, argv, named)
+{
+ let spec = this.named_args;
+ let obj = get_edit_object(ctx, this, argv);
+ if (!obj)
+ return;
+
+ for (let name, val in named) {
+ let cur = spec[name];
+ if (type(cur.add) == "function") {
+ call(cur.add, cur, ctx.model.scope, ctx, val);
+ continue;
+ }
+
+ if (cur.attribute)
+ name = cur.attribute;
+
+ let cur_obj = get_param_object(ctx, obj, cur, argv);
+ cur_obj[name] ??= [];
+ if (!cur.allow_duplicate &&
+ check_duplicate(ctx, obj[name], val))
+ return;
+ push(cur_obj[name], ...val);
+ }
+ call_change_cb(ctx, this, argv, named);
+ return ctx.ok();
+};
+
+export function set_call(ctx, argv, named)
+{
+ let spec = this.named_args;
+ let obj = get_edit_object(ctx, this, argv);
+ if (!obj)
+ return;
+
+ for (let name, val in named) {
+ let cur = spec[name];
+ if (!cur)
+ continue;
+
+ if (type(cur.set) == "function") {
+ call(cur.set, cur, ctx.model.scope, ctx, val);
+ continue;
+ }
+
+ if (cur.attribute)
+ name = cur.attribute;
+
+ let cur_obj = get_param_object(ctx, obj, cur, argv);
+ if (val == null) {
+ delete cur_obj[name];
+ continue;
+ }
+
+ if (cur.multiple && !cur.allow_duplicate &&
+ check_duplicate(ctx, obj[name], val))
+ return;
+ cur_obj[name] = val;
+ }
+ call_change_cb(ctx, this, argv, named);
+ return ctx.ok();
+};
+
+export function remove_call(ctx, argv, named)
+{
+ let spec = this.named_args;
+ let obj = get_edit_object(ctx, this, argv);
+ if (!obj)
+ return;
+
+ for (let name, val in named) {
+ let cur = spec[name];
+ if (type(cur.remove) == "function") {
+ call(cur.remove, cur, ctx.model.scope, ctx, val);
+ continue;
+ }
+
+ if (cur.attribute)
+ name = cur.attribute;
+
+ let cur_obj = get_param_object(ctx, obj, cur, argv);
+ let data = cur_obj[name];
+ if (!data)
+ continue;
+
+ for (let idx in val) {
+ let orig_idx = idx;
+ if (idx != "" + +idx) {
+ let cur_idx = index(data, idx);
+ if (cur_idx >= 0)
+ idx = cur_idx + 1;
+ else
+ idx = null;
+ } else if (+idx > length(data))
+ idx = null;
+ if (idx == null) {
+ ctx.invalid_argument('Invalid value: %s', orig_idx);
+ continue;
+ }
+ data[+idx - 1] = null;
+ }
+
+ cur_obj[name] = filter(data, (v) => v != null);
+ if (cur.attribute_allow_empty && !length(cur_obj[name]))
+ delete cur_obj[name];
+ }
+ if (length(ctx.result.errors) > 0)
+ return;
+ call_change_cb(ctx, this, argv, named);
+ return ctx.ok();
+};
+
+export function show_call(ctx, argv, named)
+{
+ let obj = get_edit_object(ctx, this, argv);
+ if (!obj)
+ return;
+
+ let data = {};
+ for (let name, spec in this.attribute_info) {
+ let val;
+ if (type(spec.get) == "function") {
+ val = call(spec.get, spec, ctx.model.scope, ctx);
+ } else {
+ let cur_obj = get_param_object(ctx, obj, spec, argv);
+ val = cur_obj[spec.attribute ?? name];
+ }
+ val ??= spec.default;
+
+ if (val != null)
+ data[name] = val;
+ }
+
+ return ctx.table("Values", data);
+};
+
+function param_values(ctx, argv, named_args, spec)
+{
+ let obj = get_edit_object(ctx, this, argv);
+ if (!obj)
+ return;
+
+ let values;
+ if (type(spec.get) == "function")
+ values = call(spec.get, spec, ctx.model.scope, ctx);
+ else {
+ let cur_obj = get_param_object(ctx, obj, spec, argv);
+ values = cur_obj[spec.attribute];
+ }
+
+ let ret = {};
+ let idx = 0;
+ for (let value in values)
+ ret["" + (++idx)] = value;
+
+ return ret;
+}
+
+function add_params(orig_params)
+{
+ let params = {};
+
+ for (let name, val in orig_params) {
+ if (!val.multiple)
+ continue;
+
+ val = { ...val };
+ delete val.required;
+ delete val.allow_empty;
+ params[name] = val;
+ }
+
+ return params;
+}
+
+function set_params(orig_params)
+{
+ let params = {};
+
+ for (let name, val in orig_params) {
+ val = { ...val };
+ if (!val.required)
+ val.allow_empty = true;
+ else
+ delete val.allow_empty;
+
+ delete val.required;
+ params[name] = val;
+ }
+
+ return params;
+}
+
+function remove_params(orig_params)
+{
+ let params = {};
+
+ for (let name, val in orig_params) {
+ if (!val.multiple)
+ continue;
+
+ val = { ...val };
+ val.attribute_allow_empty = val.allow_empty;
+ delete val.required;
+ delete val.allow_empty;
+ val.args = {
+ type: "enum",
+ get_object: val.get_object,
+ attribute: val.attribute ?? name,
+ no_validate: true,
+ value: param_values,
+ force_helptext: true,
+ };
+
+ params[name] = val;
+ }
+
+ return params;
+}
+
+export function new(info, node)
+{
+ let params = info.named_args;
+ let ret = {
+ add: {
+ help: "Add list parameter entries",
+ args: info.args,
+ named_args: add_params(params),
+ call: add_call,
+ edit_object: info.edit_object,
+ change_cb: info.change_cb,
+ ...(info.add ?? {}),
+ },
+ show: {
+ help: "Show parameter values",
+ args: info.args,
+ call: show_call,
+ attribute_info: params,
+ ...(info.show ?? {}),
+ },
+ set: {
+ help: "Set parameter values",
+ args: info.args,
+ named_args: set_params(params),
+ call: set_call,
+ edit_object: info.edit_object,
+ change_cb: info.change_cb,
+ ...(info.set ?? {}),
+ },
+ remove: {
+ help: "Remove parameter values",
+ args: info.args,
+ named_args: remove_params(params),
+ call: remove_call,
+ edit_object: info.edit_object,
+ change_cb: info.change_cb,
+ ...(info.remove ?? {}),
+ }
+ };
+
+ if (!length(ret.add.named_args)) {
+ delete ret.add;
+ delete ret.remove;
+ }
+
+ if (node)
+ for (let cmd, val in ret)
+ node[cmd] = val;
+
+ return ret;
+};
+
+export function object_destroy_call(ctx, argv, named)
+{
+ let type_info, type_name;
+ let info = this.object_info;
+ if (info.types) {
+ type_name = shift(argv);
+ if (!type_name)
+ return ctx.invalid_argument();
+
+ type_info = info.types[type_name];
+ } else {
+ type_info = info.type;
+ type_name = type_info.name;
+ }
+ if (!type_info)
+ return ctx.invalid_argument();
+
+ let obj_name = type_info.object ?? type_name;
+
+ let name = shift(argv);
+ if (type_info.delete) {
+ if (!call(type_info.delete, info, ctx.model.scope, ctx, type, name))
+ return;
+ } else {
+ let obj = ctx.data.object_edit[obj_name];
+ if (!obj)
+ return ctx.unknown_error();
+
+ if (!obj[name])
+ return ctx.not_found();
+
+ delete obj[name];
+ }
+
+ if (info.change_cb)
+ call(info.change_cb, info, ctx.model.scope, ctx, argv);
+
+ return ctx.ok(`Deleted ${type_name} '${name}'`);
+};
+
+const create_edit_param = {
+ help: "Edit object after creating",
+};
+
+export function object_create_params(node)
+{
+ if (!node.show)
+ return {};
+
+ let orig_params = node.show.attribute_info;
+ let params = {};
+
+ for (let name, val in orig_params) {
+ if (val.change_only)
+ continue;
+
+ params[name] = val;
+ }
+ params.edit ??= create_edit_param;
+
+ return params;
+};
+
+export function object_create_call(ctx, argv, named)
+{
+ let type_info, type_name;
+ let info = this.object_info;
+ if (info.types) {
+ type_name = shift(argv);
+ if (!type_name)
+ return ctx.invalid_argument();
+
+ type_info = info.types[type_name];
+ } else {
+ type_info = info.type;
+ type_name = type_info.name;
+ }
+ if (!type_info)
+ return ctx.invalid_argument();
+
+ let obj_name = type_info.object ?? type_name;
+
+ let name = shift(argv);
+ let obj, data;
+ if (type_info.add) {
+ data = call(type_info.add, info, ctx.model.scope, ctx, type_name, name, named);
+ if (!data)
+ return;
+ } else {
+ data = {};
+ }
+
+ let entry = type_info.node.set;
+ if (entry) {
+ ctx.apply_defaults();
+ let subctx = ctx.clone();
+ subctx.data.name = name;
+ subctx.data.edit = data;
+
+ try {
+ call(entry.call, entry, ctx.model.scope, subctx, argv, named);
+ } catch (e) {
+ ctx.model.exception(e);
+ return ctx.unknown_error();
+ }
+
+ if (!subctx.result.ok) {
+ ctx.result = subctx.result;
+ return;
+ }
+ }
+
+ if (type_info.insert) {
+ if (!call(type_info.insert, info, ctx.model.scope, ctx, type_name, name, data, named))
+ return;
+ } else {
+ ctx.data.object_edit[obj_name] ??= {};
+ obj = ctx.data.object_edit[obj_name];
+ obj[name] = data;
+ }
+
+ if (named.edit)
+ ctx.select(info.type ? "edit" : type_name, name);
+
+ return ctx.ok(`Added ${type_name} '${name}'`);
+};
+
+function object_lookup(ctx, entry, type_name)
+{
+ let info = entry.object_info;
+ let type_info = info.types ? info.types[type_name] : info.type;
+ if (!type_info)
+ return {};
+
+ if (type_info.get_object) {
+ let objs = call(type_info.get_object, info, ctx.model.scope, ctx, type_name);
+ if (type(objs) != "object")
+ objs = {};
+
+ return objs;
+ }
+
+ let obj_name = type_info.object ?? (info.types ? type_name : type_info.name);
+
+ return ctx.data.object_edit[obj_name];
+}
+
+function object_values(ctx, entry, type_name)
+{
+ let obj = object_lookup(ctx, entry, type_name);
+ if (!obj)
+ return [];
+
+ return keys(obj);
+}
+
+export function object_list_call(ctx, argv, named)
+{
+ let info = this.object_info;
+ let type_name = info.types ? argv[0] : info.type.name;
+ return ctx.list(type_name + " list", object_values(ctx, this, type_name));
+};
+
+function object_show_call_single(ctx, entry, type_info, type_name, name)
+{
+ let obj = object_lookup(ctx, entry, type_name);
+ if (!obj)
+ return;
+
+ entry = obj[name];
+ if (!entry)
+ return;
+
+ let callctx = ctx.clone();
+ callctx.data.name = name;
+ callctx.data.edit = entry;
+
+ call(type_info.node.show.call, type_info.node.show, ctx.model.scope, callctx, [], {});
+ if (callctx.result.ok)
+ return callctx.result.data;
+}
+
+export function object_show_call(ctx, argv, named)
+{
+ let info = this.object_info;
+ let type_name = info.type.name;
+
+ if (argv[0]) {
+ let data = object_show_call_single(ctx, this, info.type, type_name, argv[0]);
+ if (!data)
+ return;
+ return ctx.table("Values", data);
+ }
+
+ let ret = {};
+ for (let name in object_values(ctx, this, type_name)) {
+ let data = object_show_call_single(ctx, this, info.type, type_name, name);
+ if (!data)
+ continue;
+ ret[type_name + " " + name] = data;
+ }
+
+ return ctx.multi_table(type_name + " list", ret);
+};
+
+export function edit_create_destroy(info, node)
+{
+ let type_arg = [];
+ if (info.types)
+ type_arg = [{
+ name: "type",
+ help: "Type",
+ type: "enum",
+ required: true,
+ value: keys(info.types),
+ }];
+
+ let name_arg = {
+ name: "name",
+ help: "Name",
+ type: "string",
+ required: true,
+ };
+ let delete_name_arg = {
+ ...name_arg,
+ type: "enum",
+ value: function(ctx, argv) {
+ return object_values(ctx, this, argv[0]);
+ }
+ };
+ let show_name_arg = {
+ ...delete_name_arg,
+ required: false,
+ };
+
+ let create_params = {};
+ if (info.types) {
+ for (let name, val in info.types)
+ create_params[name] = object_create_params(val.node);
+ } else {
+ create_params = object_create_params(info.type.node);
+ }
+
+ let types_info = info.types ? "(" + join(", ", keys(info.types)) + ")" : info.type.name;
+ let cmds = {
+ destroy: {
+ object_info: info,
+ help: "Delete " + types_info,
+ args: [ ...type_arg, delete_name_arg ],
+ call: object_destroy_call,
+ },
+ list: {
+ object_info: info,
+ help: "List " + types_info,
+ args: [ ...type_arg ],
+ call: object_list_call,
+ },
+ create: {
+ object_info: info,
+ help: "Create " + types_info,
+ args: [ ...type_arg, name_arg ],
+ type_params: create_params,
+ named_args: function(ctx, argv) {
+ if (!this.object_info.types)
+ return this.type_params;
+ if (!argv[0])
+ return;
+ return this.type_params[argv[0]];
+ },
+ call: object_create_call,
+ },
+ };
+
+
+ let info_types = info.types;
+ if (!info_types) {
+ info_types = {};
+ info_types[info.type.name] = info.type;
+ cmds.show = {
+ object_info: info,
+ help: "Show " + types_info,
+ args: [ show_name_arg ],
+ call: object_show_call,
+ };
+ }
+
+ for (let name, val in info_types) {
+ let cmd_name = info.types ? name : "edit";
+ cmds[cmd_name] = {
+ object_name: name,
+ object_info: info,
+ help: "Edit " + name,
+ args: [
+ {
+ ...name_arg,
+ type: "enum",
+ value: function(ctx, argv) {
+ return object_values(ctx, this, this.object_name);
+ }
+ }
+ ],
+ select_node: val.node_name,
+ select: function(ctx, argv) {
+ let name = argv[0];
+ if (!name) {
+ ctx.missing_argument();
+ return;
+ }
+
+ let obj = object_lookup(ctx, this, this.object_name);
+ if (!obj) {
+ ctx.invalid_argument("Object not found");
+ return;
+ }
+
+ let entry = obj[name];
+ if (!entry) {
+ ctx.invalid_argument(`${name} not found: %s`, name);
+ return;
+ }
+
+ return ctx.set(`${this.object_name} "${name}"`, {
+ name,
+ edit: entry,
+ object_edit: entry,
+ });
+ }
+ };
+ }
+
+ if (node)
+ for (let cmd, val in cmds)
+ node[cmd] = val;
+
+ return cmds;
+};
diff --git a/package/utils/cli/files/usr/share/ucode/cli/types.uc b/package/utils/cli/files/usr/share/ucode/cli/types.uc
new file mode 100644
index 0000000000..62f9683b45
--- /dev/null
+++ b/package/utils/cli/files/usr/share/ucode/cli/types.uc
@@ -0,0 +1,195 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+// Copyright (C) 2025 Felix Fietkau <nbd@nbd.name>
+'use strict';
+
+import { access, basename, dirname, opendir, stat } from "fs";
+
+function is_directory(path)
+{
+ let s = stat(path);
+ return s && s.type == "directory";
+}
+
+const types = {
+ bool: {
+ value: [ "0", "1" ],
+ parse: function(ctx, name, val) {
+ if (val == "1")
+ return true;
+ if (val == "0")
+ return false;
+ ctx.invalid_argument("value for %s must be 0 or 1", name);
+ return;
+ },
+ },
+ int: {
+ parse: function(ctx, name, strval) {
+ let val = +strval;
+ if (substr(strval, 0, 1) == "-")
+ strval = substr(strval, 1);
+ if (match(strval, /[^0-9]/)) {
+ ctx.invalid_argument("value for %s is not a number", name);
+ return;
+ }
+ if ((this.min == null || val >= this.min) &&
+ (this.max == null || val <= this.max))
+ return val;
+ if (this.min != null && this.max != null)
+ ctx.invalid_argument(`value for %s must be between ${this.min} and ${this.max}`, name);
+ else if (this.min != null)
+ ctx.invalid_argument(`value for %s must be at least ${this.min}`, name);
+ else
+ ctx.invalid_argument(`value for %s must not be bigger than ${this.max}`, name);
+ return;
+ }
+ },
+ string: {
+ parse: function(ctx, name, val) {
+ let len = length(val);
+ if ((this.min == null || len >= this.min) &&
+ (this.max == null || len <= this.max))
+ return val;
+ if (this.min != null && this.max != null)
+ ctx.invalid_argument(`String value %s must be between ${this.min} and ${this.max} characters`, name);
+ else if (this.min != null)
+ ctx.invalid_argument(`String value %s must be at least ${this.min} characters long`, name);
+ else
+ ctx.invalid_argument(`String value %s must not be longer than ${this.max} characters`, name);
+ return;
+ }
+ },
+ json: {
+ parse: function(ctx, name, val) {
+ try {
+ val = json(val);
+ } catch (e) {
+ return ctx.invalid_argument('Invalid JSON data');
+ }
+ if (this.data_type != null && type(val) != this.data_type)
+ ctx.invalid_argument(`Invalid data type: %s, expected: %s`, type(val), this.data_type);
+ return val;
+ }
+ },
+ enum: {
+ parse: function(ctx, name, val) {
+ if (this.no_validate)
+ return val;
+
+ let list = this.value;
+ if (type(list) == "object")
+ list = keys(list);
+ if (this.ignore_case) {
+ val = lc(val);
+ val = filter(list, (v) => val == lc(v))[0];
+ } else {
+ val = filter(list, (v) => val == v)[0];
+ }
+
+ if (val == null)
+ ctx.invalid_argument("Invalid value for %s", name);
+
+ return val;
+ }
+ },
+ path: {
+ complete: function(ctx, val) {
+ let ret = [];
+
+ let dir = split(val, "/");
+ let prefix = pop(dir);
+ push(dir, "");
+ dir = join("/", dir);
+ let prefix_len = length(prefix);
+ let d = opendir(length(dir) ? dir : ".");
+ if (!d)
+ return ret;
+
+ let cur;
+ while (cur = d.read()) {
+ if (cur == "." || cur == "..")
+ continue;
+
+ if (substr(cur, 0, prefix_len) != prefix)
+ continue;
+
+ let path = dir + cur;
+ let incomplete = false;
+ if (is_directory(path)) {
+ path += "/";
+ incomplete = true;
+ }
+
+ push(ret, { name: path, incomplete });
+ }
+
+ return ret;
+ },
+ parse: function(ctx, name, val) {
+ if (this.new_path) {
+ let dir = dirname(val);
+ let s = stat(dir);
+ if (!is_directory(dir)) {
+ ctx.invalid_argument("Path '%s' is not a directory", dir);
+ return;
+ }
+ } else {
+ if (!access(val, "r")) {
+ ctx.invalid_argument("Path '%s' does not exist", val);
+ return;
+ }
+ }
+ return val;
+ }
+ },
+ host: {
+ parse: function(ctx, name, val) {
+ if (length(iptoarr(val)) != 0)
+ return val;
+ if (length(val) > 255)
+ return;
+ let labels = split(val, ".");
+ if (length(filter(labels, label => !match(label, /^([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9-]{0,61}[a-zA-Z0-9])$/))) == 0 && length(labels) > 0)
+ return val;
+ ctx.invalid_argument("value for %s is not an valid IP or hostname", name);
+ return;
+ }
+ },
+ macaddr: {
+ parse: function(ctx, name, val) {
+ val = lc(val);
+ let arr = split(val, ":");
+ if (length(arr) != 6 || length(filter(arr, (v) => !match(v, /^[0-9a-f][0-9a-f]$/))))
+ return ctx.invalid_argument("value for %s is not an MAC address", name);
+ return val;
+ }
+ },
+ ipv4: {
+ parse: function(ctx, name, val) {
+ if (length(iptoarr(val)) == 4)
+ return val;
+ ctx.invalid_argument("value for %s is not an IPv4", name);
+ return;
+ }
+ },
+ ipv6: {
+ parse: function(ctx, name, val) {
+ if (length(iptoarr(val)) == 16)
+ return val;
+ ctx.invalid_argument("value for %s is not an IPv6", name);
+ return;
+ }
+ },
+ cidr4: {
+ parse: function(ctx, name, val) {
+ let m = split(val, '/', 2);
+ if (m && +m[1] <= 32 &&
+ ((m[0] == "auto" && this.allow_auto) ||
+ length(iptoarr(m[0])) == 4))
+ return val;
+ ctx.invalid_argument("value for %s is not cidr4 (e.g. 192.168.1.1/24)", name);
+ return;
+ }
+ },
+};
+
+return types;
diff --git a/package/utils/cli/files/usr/share/ucode/cli/utils.uc b/package/utils/cli/files/usr/share/ucode/cli/utils.uc
new file mode 100644
index 0000000000..f299ad361d
--- /dev/null
+++ b/package/utils/cli/files/usr/share/ucode/cli/utils.uc
@@ -0,0 +1,26 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+// Copyright (C) 2025 Felix Fietkau <nbd@nbd.name>
+'use strict';
+
+export function time_format(val)
+{
+ let ret = `${val % 60}s`;
+
+ val /= 60;
+ if (!val)
+ return ret;
+
+ ret = `${val % 60}m ${ret}`;
+
+ val /= 60;
+ if (!val)
+ return ret;
+
+ ret = `${val % 24 }h ${ret}`;
+
+ val /= 24;
+ if (!val)
+ return ret;
+
+ return `${val}d ${ret}`;
+};
diff --git a/package/utils/ct-bugcheck/Makefile b/package/utils/ct-bugcheck/Makefile
index 5deb1e0d35..d8b35df1ea 100644
--- a/package/utils/ct-bugcheck/Makefile
+++ b/package/utils/ct-bugcheck/Makefile
@@ -9,7 +9,7 @@ include $(TOPDIR)/rules.mk
include $(INCLUDE_DIR)/kernel.mk
PKG_NAME:=ct-bugcheck
-PKG_RELEASE:=2016-07-21
+PKG_RELEASE:=2016.07.21
include $(INCLUDE_DIR)/package.mk
diff --git a/package/utils/debugcc/Makefile b/package/utils/debugcc/Makefile
index 3e26cea7e5..c3821cd8c6 100644
--- a/package/utils/debugcc/Makefile
+++ b/package/utils/debugcc/Makefile
@@ -11,6 +11,7 @@ PKG_MIRROR_HASH:=4cd7a770a05db28f496a60eb9fe015a4af677bba05053b4d4be21adcf95e52e
PKG_LICENSE:=BSD-3-Clause
PKG_LICENSE_FILES:=LICENSE
+PKG_FLAGS:=nonshared
PKG_MAINTAINER:=Christian Marangi <ansuelsmth@gmail.com>
diff --git a/package/utils/dns320l-mcu/Makefile b/package/utils/dns320l-mcu/Makefile
new file mode 100644
index 0000000000..018f81109c
--- /dev/null
+++ b/package/utils/dns320l-mcu/Makefile
@@ -0,0 +1,38 @@
+include $(TOPDIR)/rules.mk
+
+PKG_NAME:=dns320l-mcu
+PKG_RELEASE:=2
+
+PKG_SOURCE_PROTO:=git
+PKG_SOURCE_URL=https://github.com/wigyori/dns320l-daemon.git
+PKG_SOURCE_DATE:=2025-05-03
+PKG_SOURCE_VERSION:=11fcf3bbc98cc1efc64479ffbea8fb86d91c57c2
+PKG_MIRROR_HASH:=4e16dc098aeb5845b0aa977b6f34b67c549bea83e194bf841c9061fb9385fa5c
+PKG_MAINTAINER:=Zoltan HERPAI <wigyori@uid0.hu>
+PKG_LICENSE:=GPL-3.0+
+
+PKG_FLAGS:=nonshared
+
+include $(INCLUDE_DIR)/package.mk
+
+define Package/dns320l-mcu
+ SECTION:=utils
+ CATEGORY:=Utilities
+ TITLE:=Utility to control the MCU on DNS-320L
+ DEPENDS:=@TARGET_kirkwood
+ URL:=https://github.com/wigyori/dns320l-mcu
+endef
+
+define Build/Compile
+ $(MAKE) -C $(PKG_BUILD_DIR) \
+ CC="$(TARGET_CC)" \
+ CFLAGS="$(TARGET_CFLAGS) -Wall"
+endef
+
+define Package/dns320l-mcu/install
+ $(INSTALL_DIR) $(1)/usr/bin $(1)/etc/init.d
+ $(INSTALL_BIN) ./files/dns320l-mcu.init $(1)/etc/init.d/dns320l-mcu
+ $(INSTALL_BIN) $(PKG_BUILD_DIR)/dns320l-daemon $(1)/usr/bin/dns320l-mcu
+endef
+
+$(eval $(call BuildPackage,dns320l-mcu))
diff --git a/package/utils/dns320l-mcu/files/dns320l-mcu.init b/package/utils/dns320l-mcu/files/dns320l-mcu.init
new file mode 100644
index 0000000000..eb1c579907
--- /dev/null
+++ b/package/utils/dns320l-mcu/files/dns320l-mcu.init
@@ -0,0 +1,14 @@
+#!/bin/sh /etc/rc.common
+# Copyright (c) 2024 OpenWrt.org
+
+START=99
+
+USE_PROCD=1
+PROG=/usr/bin/dns320l-mcu
+
+start_service() {
+ procd_open_instance
+ procd_set_param command "$PROG"
+ procd_set_param respawn
+ procd_close_instance
+}
diff --git a/package/utils/dtc/Makefile b/package/utils/dtc/Makefile
index ea970e200a..8b5eeccfa5 100644
--- a/package/utils/dtc/Makefile
+++ b/package/utils/dtc/Makefile
@@ -5,11 +5,11 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=dtc
-PKG_VERSION:=1.7.0
-PKG_RELEASE:=3
+PKG_VERSION:=1.7.1
+PKG_RELEASE:=1
-PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.xz
-PKG_HASH:=29edce3d302a15563d8663198bbc398c5a0554765c83830d0d4c0409d21a16c4
+PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
+PKG_HASH:=9532f10098455711a4da37816fd567dfc8523bb01f59ad6c44887a112e553d9e
PKG_SOURCE_URL:=@KERNEL/software/utils/dtc
PKG_MAINTAINER:=Yousong Zhou <yszhou4tech@gmail.com>
@@ -88,6 +88,7 @@ define Package/libfdt/install
endef
MESON_ARGS += \
+ -Dtests=false \
-Dtools=true \
-Dyaml=disabled \
-Dvalgrind=disabled \
diff --git a/package/utils/dtc/patches/010-both-libraries.patch b/package/utils/dtc/patches/010-both-libraries.patch
deleted file mode 100644
index ccc547b349..0000000000
--- a/package/utils/dtc/patches/010-both-libraries.patch
+++ /dev/null
@@ -1,48 +0,0 @@
-From da39ee0e68b6d9293133a7c41c6cf73354dce337 Mon Sep 17 00:00:00 2001
-From: Rosen Penev <rosenp@gmail.com>
-Date: Wed, 21 Feb 2024 13:57:56 -0800
-Subject: [PATCH] libfdt: rework shared/static libraries
-
-Instead of creating 2 libraries manualy, just call both_libraries and
-link to the appropriate one as requested.
-
-Fixes compilation when passing -Ddefault_libraries=both as the
-static_library name is duplicated.
-
-Signed-off-by: Rosen Penev <rosenp@gmail.com>
-Signed-off-by: David Gibson <david@gibson.dropbear.id.au>
----
- libfdt/meson.build | 17 +++++------------
- 1 file changed, 5 insertions(+), 12 deletions(-)
-
---- a/libfdt/meson.build
-+++ b/libfdt/meson.build
-@@ -16,7 +16,7 @@ sources = files(
- 'fdt_wip.c',
- )
-
--libfdt = library(
-+libfdt = both_libraries(
- 'fdt', sources,
- version: '1.6.0',
- link_args: ['-Wl,--no-undefined', version_script],
-@@ -24,17 +24,12 @@ libfdt = library(
- install: true,
- )
-
--libfdt_a = static_library(
-- 'fdt', sources,
-- install: true,
--)
--
- libfdt_inc = include_directories('.')
-
- if static_build
-- link_with = libfdt_a
-+ link_with = libfdt.get_static_lib()
- else
-- link_with = libfdt
-+ link_with = libfdt.get_shared_lib()
- endif
-
- libfdt_dep = declare_dependency(
diff --git a/package/utils/e2fsprogs/Makefile b/package/utils/e2fsprogs/Makefile
index 9e2f2fafe5..bebfc8dab2 100644
--- a/package/utils/e2fsprogs/Makefile
+++ b/package/utils/e2fsprogs/Makefile
@@ -8,18 +8,18 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=e2fsprogs
-PKG_VERSION:=1.47.0
-PKG_RELEASE:=2
+PKG_VERSION:=1.47.2
+PKG_RELEASE:=1
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.xz
PKG_SOURCE_URL:=@KERNEL/linux/kernel/people/tytso/e2fsprogs/v$(PKG_VERSION)/
-PKG_HASH:=144af53f2bbd921cef6f8bea88bb9faddca865da3fbc657cc9b4d2001097d5db
+PKG_HASH:=08242e64ca0e8194d9c1caad49762b19209a06318199b63ce74ae4ef2d74e63c
PKG_LICENSE:=GPL-2.0
PKG_LICENSE_FILES:=NOTICE
PKG_CPE_ID:=cpe:/a:e2fsprogs_project:e2fsprogs
-PKG_BUILD_DEPENDS:=util-linux e2fsprogs/host
+PKG_BUILD_DEPENDS:=util-linux
PKG_INSTALL:=1
PKG_BUILD_PARALLEL:=1
@@ -225,23 +225,6 @@ define Build/InstallDev
$(CP) $(PKG_BUILD_DIR)/lib/e2p/e2p.h $(1)/usr/include/e2p
endef
-define Host/Compile
- $(MAKE) $(PKG_JOBS) -C $(HOST_BUILD_DIR)/lib/ss mk_cmds
- $(MAKE) $(PKG_JOBS) -C $(HOST_BUILD_DIR)/lib/et compile_et
-endef
-
-define Host/Install
- $(INSTALL_DIR) $(1)/share/et
- $(CP) $(HOST_BUILD_DIR)/lib/et/et_[ch].awk $(1)/share/et/
- $(INSTALL_DIR) $(1)/share/ss
- $(CP) $(HOST_BUILD_DIR)/lib/ss/ct_c.{sed,awk} $(1)/share/ss/
- $(INSTALL_DIR) $(1)/bin
- $(CP) \
- $(HOST_BUILD_DIR)/lib/et/compile_et \
- $(HOST_BUILD_DIR)/lib/ss/mk_cmds \
- $(1)/bin/
-endef
-
define Package/e2fsprogs/conffiles
/etc/e2fsck.conf
endef
@@ -354,4 +337,3 @@ $(eval $(call BuildPackage,filefrag))
$(eval $(call BuildPackage,debugfs))
$(eval $(call BuildPackage,chattr))
$(eval $(call BuildPackage,lsattr))
-$(eval $(call HostBuild))
diff --git a/package/utils/f2fs-tools/Makefile b/package/utils/f2fs-tools/Makefile
index d5dc1a6d78..7a1c2dbcfb 100644
--- a/package/utils/f2fs-tools/Makefile
+++ b/package/utils/f2fs-tools/Makefile
@@ -9,7 +9,7 @@ include $(TOPDIR)/rules.mk
PKG_NAME:=f2fs-tools
PKG_VERSION:=1.16.0
-PKG_RELEASE:=2
+PKG_RELEASE:=4
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
PKG_SOURCE_URL:=https://git.kernel.org/pub/scm/linux/kernel/git/jaegeuk/f2fs-tools.git/snapshot/
@@ -137,6 +137,7 @@ define Package/f2fsck/install
$(LN) ../sbin/fsck.f2fs $(1)/usr/sbin/dump.f2fs
$(LN) ../sbin/fsck.f2fs $(1)/usr/sbin/sload.f2fs
$(LN) ../sbin/fsck.f2fs $(1)/usr/sbin/resize.f2fs
+ $(LN) ../sbin/fsck.f2fs $(1)/usr/sbin/f2fslabel
endef
Package/f2fsck-selinux/install = $(Package/f2fsck/install)
diff --git a/package/utils/f2fs-tools/patches/100-f2fs-tools-use-stdbool.h-instead-of-bool.patch b/package/utils/f2fs-tools/patches/100-f2fs-tools-use-stdbool.h-instead-of-bool.patch
new file mode 100644
index 0000000000..fc3d8009aa
--- /dev/null
+++ b/package/utils/f2fs-tools/patches/100-f2fs-tools-use-stdbool.h-instead-of-bool.patch
@@ -0,0 +1,33 @@
+From 6617d15a660becc23825007ab3fc2d270b5b250f Mon Sep 17 00:00:00 2001
+From: Jaegeuk Kim <jaegeuk@kernel.org>
+Date: Thu, 24 Oct 2024 20:33:38 +0000
+Subject: [PATCH] f2fs-tools: use stdbool.h instead of bool
+
+The existing bool definition is broken for c23, where bool is now a keyword.
+
+Signed-off-by: Elliott Hughes <enh@google.com>
+Signed-off-by: Jaegeuk Kim <jaegeuk@kernel.org>
+---
+ include/f2fs_fs.h | 4 +---
+ 1 file changed, 1 insertion(+), 3 deletions(-)
+
+--- a/include/f2fs_fs.h
++++ b/include/f2fs_fs.h
+@@ -26,6 +26,7 @@
+ #include <stddef.h>
+ #include <string.h>
+ #include <time.h>
++#include <stdbool.h>
+
+ #ifdef HAVE_CONFIG_H
+ #include <config.h>
+@@ -103,9 +104,6 @@ typedef uint16_t u16;
+ typedef uint8_t u8;
+ typedef u32 block_t;
+ typedef u32 nid_t;
+-#ifndef bool
+-typedef u8 bool;
+-#endif
+ typedef unsigned long pgoff_t;
+ typedef unsigned short umode_t;
+
diff --git a/package/utils/fbtest/src/fbtest.c b/package/utils/fbtest/src/fbtest.c
index 021b80303c..9a9bc6494c 100644
--- a/package/utils/fbtest/src/fbtest.c
+++ b/package/utils/fbtest/src/fbtest.c
@@ -2,7 +2,7 @@
* fbtest - fbtest.c
* test program for the tuxbox-framebuffer device
* tests all GTX/eNX supported modes
- *
+ *
* (c) 2003 Carsten Juttner (carjay@gmx.net)
*
* This program is free software; you can redistribute it and/or modify
@@ -18,7 +18,7 @@
* 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.
- *
+ *
******************************************************************************
* $Id: fbtest.c,v 1.5 2005/01/14 23:14:41 carjay Exp $
******************************************************************************/
@@ -187,12 +187,12 @@ int setmode(int fbd, const struct pixelformat *pixf,const struct vidsize *vids){
int stat;
stat = ioctl (fbd, FBIOGET_VSCREENINFO,&var);
if (stat<0) return -2;
-
+
var.xres= vids->width;
var.xres_virtual = vids->width;
var.yres= vids->height;
var.yres_virtual = vids->height;
-
+
var.bits_per_pixel = pixf->bpp;
var.red = pixf->red;
var.green = pixf->green;
@@ -254,7 +254,7 @@ void drawrect(void *videoram, struct rect *r, const struct pixelformat *pixf, co
pmem +=((vids->width*bpp)>>3); // skip one whole line, actually should be taken from "fix-info"
}
}
-
+
// create quick little test image, 4 colours from table
void draw4field(void *videoram, const struct pixelformat *pixf, const struct vidsize *vids){
struct rect r;
@@ -314,7 +314,7 @@ int main (int argc,char **argv){
int optchar,fmode=-1,smode=-1,clear=1;
int i_cmap,i_size,i_pix;
extern char *optarg;
-
+
if (argc!=0&&argc>4) usage(argv[0]);
while ( (optchar = getopt (argc,argv,"f:s:n"))!= -1){
int i,height,width;
@@ -359,7 +359,7 @@ int main (int argc,char **argv){
usage (argv[0]);
}
}
-
+
fbd = open (FBDEV, O_RDWR);
if (fbd<0){
perror ("Error opening framebuffer device");
@@ -396,7 +396,7 @@ int main (int argc,char **argv){
printf ("%dx%d ",vidsizetable[i_size].width,vidsizetable[i_size].height);
fflush(stdout);
if ((i_size%4)==3) printf ("\n");
-
+
// try to set mode
stat = setmode(fbd,&pixelformattable[i_pix],&vidsizetable[i_size]);
if (stat==-2) perror ("fbtest: could not get fb_var-screeninfo from fb-device");
diff --git a/package/utils/firmware-utils/Makefile b/package/utils/firmware-utils/Makefile
index 3a6467503c..65b7048f96 100644
--- a/package/utils/firmware-utils/Makefile
+++ b/package/utils/firmware-utils/Makefile
@@ -7,10 +7,11 @@ PKG_RELEASE:=1
PKG_SOURCE_PROTO:=git
PKG_SOURCE_URL=$(PROJECT_GIT)/project/firmware-utils.git
-PKG_SOURCE_DATE:=2024-03-23
-PKG_SOURCE_VERSION:=6b242991a995c41769977efb010dc9f4e4ec3da2
-PKG_MIRROR_HASH:=00ff2661fda2eb299ccbfaa07dff8fbcf46b3441a6e9f6f46eaa119e66dbe142
+PKG_SOURCE_DATE:=2024-10-16
+PKG_SOURCE_VERSION:=88fbd52666e3b3f83ebab40f95b84f265824a729
+PKG_MIRROR_HASH:=4809421286257a91b2f29e79b7bbd0852a72c6e82169b340036cca5703881232
+PKG_FLAGS:=nonshared
PKG_BUILD_DEPENDS:=openssl zlib
include $(INCLUDE_DIR)/package.mk
diff --git a/package/utils/fitblk/Makefile b/package/utils/fitblk/Makefile
index 4da4dc46f1..b8f881937e 100644
--- a/package/utils/fitblk/Makefile
+++ b/package/utils/fitblk/Makefile
@@ -1,7 +1,7 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=fitblk
-PKG_RELEASE:=1
+PKG_RELEASE:=2
PKG_LICENSE:=GPL-2.0-only
PKG_MAINTAINER:=Daniel Golle <daniel@makrotopia.org>
@@ -16,7 +16,7 @@ define Package/fitblk
SECTION:=base
CATEGORY:=Base system
TITLE:=fitblk firmware release tool
- DEPENDS:=@!LINUX_5_15
+ DEPENDS:=+fit-check-sign
endef
define Package/fitblk/description
@@ -36,6 +36,8 @@ endef
define Package/fitblk/install
$(INSTALL_DIR) $(1)/usr/sbin
$(INSTALL_BIN) $(PKG_BUILD_DIR)/fitblk $(1)/usr/sbin/
+ $(INSTALL_DIR) $(1)/lib/upgrade
+ $(INSTALL_DATA) ./files/fit.sh $(1)/lib/upgrade
endef
$(eval $(call BuildPackage,fitblk))
diff --git a/package/utils/fitblk/files/fit.sh b/package/utils/fitblk/files/fit.sh
new file mode 100644
index 0000000000..839389bed4
--- /dev/null
+++ b/package/utils/fitblk/files/fit.sh
@@ -0,0 +1,73 @@
+export_fitblk_bootdev() {
+ [ -e /sys/firmware/devicetree/base/chosen/rootdisk ] || return
+
+ local rootdisk="$(cat /sys/firmware/devicetree/base/chosen/rootdisk)"
+ local handle bootdev
+
+ for handle in /sys/class/mtd/mtd*/of_node/volumes/*/phandle; do
+ [ ! -e "$handle" ] && continue
+ if [ "$rootdisk" = "$(cat "$handle")" ]; then
+ if [ -e "${handle%/phandle}/volname" ]; then
+ export CI_KERNPART="$(cat "${handle%/phandle}/volname")"
+ elif [ -e "${handle%/phandle}/volid" ]; then
+ export CI_KERNVOLID="$(cat "${handle%/phandle}/volid")"
+ else
+ return
+ fi
+ export CI_UBIPART="$(cat "${handle%%/of_node*}/name")"
+ export CI_METHOD="ubi"
+ return
+ fi
+ done
+
+ for handle in /sys/class/mtd/mtd*/of_node/phandle; do
+ [ ! -e "$handle" ] && continue
+ if [ "$rootdisk" = "$(cat $handle)" ]; then
+ bootdev="${handle%/of_node/phandle}"
+ bootdev="${bootdev#/sys/class/mtd/}"
+ export PART_NAME="/dev/$bootdev"
+ export CI_METHOD="default"
+ return
+ fi
+ done
+
+ for handle in /sys/class/block/*/of_node/phandle; do
+ [ ! -e "$handle" ] && continue
+ if [ "$rootdisk" = "$(cat $handle)" ]; then
+ bootdev="${handle%/of_node/phandle}"
+ bootdev="${bootdev#/sys/class/block/}"
+ export EMMC_KERN_DEV="/dev/$bootdev"
+ export CI_METHOD="emmc"
+ return
+ fi
+ done
+}
+
+fit_do_upgrade() {
+ export_fitblk_bootdev
+ [ -n "$CI_METHOD" ] || return 1
+ [ -e /dev/fit0 ] && fitblk /dev/fit0
+ [ -e /dev/fitrw ] && fitblk /dev/fitrw
+
+ case "$CI_METHOD" in
+ emmc)
+ emmc_do_upgrade "$1"
+ ;;
+ default)
+ default_do_upgrade "$1"
+ ;;
+ ubi)
+ nand_do_upgrade "$1"
+ ;;
+ esac
+}
+
+fit_check_image() {
+ local magic="$(get_magic_long "$1")"
+ [ "$magic" != "d00dfeed" ] && {
+ echo "Invalid image type."
+ return 74
+ }
+
+ fit_check_sign -f "$1" >/dev/null || return 74
+}
diff --git a/package/utils/fritz-tools/Makefile b/package/utils/fritz-tools/Makefile
index 6e20b56ff8..b43fe20e9e 100644
--- a/package/utils/fritz-tools/Makefile
+++ b/package/utils/fritz-tools/Makefile
@@ -1,7 +1,7 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=fritz-tools
-PKG_RELEASE:=2
+PKG_RELEASE:=3
CMAKE_INSTALL:=1
include $(INCLUDE_DIR)/package.mk
diff --git a/package/utils/fritz-tools/src/fritz_cal_extract.c b/package/utils/fritz-tools/src/fritz_cal_extract.c
index 2978d86e0c..7e5f4118f5 100644
--- a/package/utils/fritz-tools/src/fritz_cal_extract.c
+++ b/package/utils/fritz-tools/src/fritz_cal_extract.c
@@ -8,6 +8,9 @@
* that is Not copyrighted -- provided to the public domain
* Version 1.4 11 December 2005 Mark Adler
*
+ * Modifications to also handle calibration data in reversed byte order
+ * (c) 2024 by <dzsoftware@posteo.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
@@ -28,106 +31,126 @@
#include <assert.h>
#include <unistd.h>
#include <stdint.h>
+#include <stdbool.h>
#include <stdlib.h>
#include <endian.h>
#include <errno.h>
#include "zlib.h"
#define CHUNK 1024
+#define DEFAULT_BUFFERSIZE (129 * 1024)
+
+#define MIN(a,b) (((a)<(b))?(a):(b))
-static inline size_t special_min(size_t a, size_t b)
+/* Reverse byte order in data buffer.
+ * 'top' is position of last valid data byte = (datasize - 1)
+ */
+static void buffer_reverse(unsigned char *data, unsigned int top)
{
- return a == 0 ? b : (a < b ? a : b);
+ register unsigned char swapbyte;
+ const unsigned int center = top / 2;
+
+ for (unsigned int bottom = 0; bottom < center; ++bottom, --top) {
+ swapbyte = data[bottom];
+ data[bottom] = data[top];
+ data[top] = swapbyte;
+ }
}
-/* Decompress from file source to file dest until stream ends or EOF.
- inf() returns Z_OK on success, Z_MEM_ERROR if memory could not be
- allocated for processing, Z_DATA_ERROR if the deflate data is
- invalid or incomplete, Z_VERSION_ERROR if the version of zlib.h and
- the version of the library linked do not match, or Z_ERRNO if there
- is an error reading or writing the files. */
-static int inf(FILE *source, FILE *dest, size_t limit, size_t skip)
+/* Decompress from file source to data buffer until stream ends
+ * or *limit bytes have been written to buffer.
+ *
+ * On call, 'limit' must reference a variable containing the intended
+ * number of bytes to retrieve (must be <= allocated buffer size).
+ *
+ * Return values (success):
+ * Z_END_STREAM if complete data was retrieved (*limit == size of complete data),
+ * or Z_OK if data was retrieved up to limit (*limit == original value).
+ *
+ * Return values (failure):
+ * Z_MEM_ERROR if memory could not be allocated for processing,
+ * Z_DATA_ERROR if the deflate data is invalid or incomplete,
+ * Z_VERSION_ERROR if the version of zlib.h and the version of the
+ * library linked do not match, or
+ * Z_ERRNO if there is an error reading or writing the files.
+ */
+static int inflate_to_buffer(FILE *source, unsigned char *buf, size_t *limit)
{
- int ret;
- size_t have;
- z_stream strm;
- unsigned char in[CHUNK];
- unsigned char out[CHUNK];
-
- /* allocate inflate state */
- strm.zalloc = Z_NULL;
- strm.zfree = Z_NULL;
- strm.opaque = Z_NULL;
- strm.avail_in = 0;
- strm.next_in = Z_NULL;
- ret = inflateInit(&strm);
- if (ret != Z_OK)
- return ret;
-
- /* decompress until deflate stream ends or end of file */
- do {
- strm.avail_in = fread(in, 1, CHUNK, source);
- if (ferror(source)) {
- (void)inflateEnd(&strm);
- return Z_ERRNO;
- }
- if (strm.avail_in == 0)
- break;
- strm.next_in = in;
-
- /* run inflate() on input until output buffer not full */
- do {
- strm.avail_out = CHUNK;
- strm.next_out = out;
- ret = inflate(&strm, Z_NO_FLUSH);
- assert(ret != Z_STREAM_ERROR); /* state not clobbered */
- switch (ret) {
- case Z_NEED_DICT:
- ret = Z_DATA_ERROR; /* and fall through */
- case Z_DATA_ERROR:
- case Z_MEM_ERROR:
- (void)inflateEnd(&strm);
- return ret;
- }
- have = special_min(limit, CHUNK - strm.avail_out) - skip;
- if (fwrite(&out[skip], have, 1, dest) != 1 || ferror(dest)) {
- (void)inflateEnd(&strm);
- return Z_ERRNO;
- }
- skip = 0;
- limit -= have;
- } while (strm.avail_out == 0 && limit > 0);
-
- /* done when inflate() says it's done */
- } while (ret != Z_STREAM_END && limit > 0);
-
- /* clean up and return */
- (void)inflateEnd(&strm);
- return (limit == 0 ? Z_OK : (ret == Z_STREAM_END ? Z_OK : Z_DATA_ERROR));
+ int ret;
+ z_stream strm;
+ unsigned char in[CHUNK];
+
+ /* allocate inflate state */
+ strm.zalloc = Z_NULL;
+ strm.zfree = Z_NULL;
+ strm.opaque = Z_NULL;
+ strm.avail_in = 0;
+ strm.next_in = Z_NULL;
+ ret = inflateInit(&strm);
+ if (ret != Z_OK)
+ return ret;
+
+ /* set data buffer as stream output */
+ strm.avail_out = *limit;
+ strm.next_out = buf;
+
+ /* decompress until deflate stream ends or end of file */
+ do {
+ strm.avail_in = fread(in, 1, CHUNK, source);
+ if (ferror(source)) {
+ (void)inflateEnd(&strm);
+ return Z_ERRNO;
+ }
+ if (strm.avail_in == 0)
+ break;
+ strm.next_in = in;
+
+ /* run inflate(), fill data buffer with all available output */
+ ret = inflate(&strm, Z_FINISH);
+ assert(ret != Z_STREAM_ERROR); /* state not clobbered */
+
+ switch (ret) {
+ case Z_NEED_DICT:
+ ret = Z_DATA_ERROR; /* and fall through */
+ case Z_DATA_ERROR:
+ case Z_MEM_ERROR:
+ (void)inflateEnd(&strm);
+ return ret;
+ }
+ /* done when inflate() says it's done or limit reached */
+ } while (ret != Z_STREAM_END && strm.avail_out > 0);
+
+ /* set limit to end of retrieved data */
+ assert(strm.total_out <= *limit);
+ *limit = strm.total_out;
+
+ /* clean up and return */
+ (void)inflateEnd(&strm);
+ return (ret == Z_STREAM_END ? Z_STREAM_END : (strm.avail_out == 0 ? Z_OK : Z_DATA_ERROR));
}
/* report a zlib or i/o error */
static void zerr(int ret)
{
- switch (ret) {
- case Z_ERRNO:
- if (ferror(stdin))
- fputs("error reading stdin\n", stderr);
- if (ferror(stdout))
- fputs("error writing stdout\n", stderr);
- break;
- case Z_STREAM_ERROR:
- fputs("invalid compression level\n", stderr);
- break;
- case Z_DATA_ERROR:
- fputs("invalid or incomplete deflate data\n", stderr);
- break;
- case Z_MEM_ERROR:
- fputs("out of memory\n", stderr);
- break;
- case Z_VERSION_ERROR:
- fputs("zlib version mismatch!\n", stderr);
- }
+ switch (ret) {
+ case Z_ERRNO:
+ if (ferror(stdin))
+ fputs("error reading stdin\n", stderr);
+ if (ferror(stdout))
+ fputs("error writing stdout\n", stderr);
+ break;
+ case Z_STREAM_ERROR:
+ fputs("invalid compression level\n", stderr);
+ break;
+ case Z_DATA_ERROR:
+ fputs("invalid or incomplete deflate data\n", stderr);
+ break;
+ case Z_MEM_ERROR:
+ fputs("out of memory\n", stderr);
+ break;
+ case Z_VERSION_ERROR:
+ fputs("zlib version mismatch!\n", stderr);
+ }
}
static unsigned int get_num(char *str)
@@ -140,7 +163,8 @@ static unsigned int get_num(char *str)
static void usage(void)
{
- fprintf(stderr, "Usage: fritz_cal_extract [-s seek offset] [-i skip] [-o output file] [-l limit] [infile] -e entry_id\n"
+ fprintf(stderr, "Usage: fritz_cal_extract -e entry_id [-s seek offset] [-l limit]\n"
+ "\t[-r reverse extracted data] [-i skip n bytes] [-o output file] [infile]\n"
"Finds and extracts zlib compressed calibration data in the EVA loader\n");
exit(EXIT_FAILURE);
}
@@ -154,15 +178,18 @@ struct cal_entry {
int main(int argc, char **argv)
{
struct cal_entry cal = { .len = 0 };
+ unsigned char *buf = NULL;
FILE *in = stdin;
FILE *out = stdout;
+ size_t datasize = DEFAULT_BUFFERSIZE;
size_t limit = 0, skip = 0;
int initial_offset = 0;
int entry = -1;
+ bool reversed = false, limit_was_set = true;
int ret;
int opt;
- while ((opt = getopt(argc, argv, "s:e:o:l:i:")) != -1) {
+ while ((opt = getopt(argc, argv, "s:e:o:l:i:r")) != -1) {
switch (opt) {
case 's':
initial_offset = (int)get_num(optarg);
@@ -199,6 +226,9 @@ int main(int argc, char **argv)
goto out_bad;
}
break;
+ case 'r':
+ reversed = true;
+ break;
default: /* '?' */
usage();
}
@@ -243,11 +273,50 @@ int main(int argc, char **argv)
goto out_bad;
}
- ret = inf(in, out, limit, skip);
- if (ret == Z_OK)
- goto out;
+ /* Set boundaries. Only keep default datasize if we need complete data
+ * for reversal and didn't set a higher limit. */
+ if (!limit) {
+ limit_was_set = false;
+ limit = datasize - skip;
+ }
+ datasize = (reversed && datasize >= limit + skip) ? datasize : (limit + skip);
+
+ /* Create data buffer. */
+ buf = malloc(datasize);
+ assert(buf != NULL);
+
+ ret = inflate_to_buffer(in, buf, &datasize);
+
+ if ((reversed || !limit_was_set) && ret != Z_STREAM_END) { /* didn't read to stream end */
+ fprintf(stderr, "Failed: Data exceeds buffer size of %u. Refusing to reverse"
+ " or store incomplete data."
+ " Use a higher limit [-l] to increase buffer size.\n",
+ (unsigned int) datasize);
+ goto out_bad;
+ }
+
+ ret = (ret == Z_STREAM_END) ? Z_OK : ret; /* normalize return value */
+ if (ret != Z_OK) {
+ zerr(ret);
+ goto out_bad;
+ }
+
+ if (reversed)
+ buffer_reverse(buf, datasize - 1);
+
+ if (datasize <= skip) {
+ fprintf(stderr, "Failed to skip %u bytes, total data size is %u!\n",
+ (unsigned int)skip, (unsigned int)datasize);
+ goto out_bad;
+ }
+
+ limit = MIN(limit, datasize - skip);
+ if (fwrite(&buf[skip], limit, 1, out) != 1 || ferror(out)) {
+ fprintf(stderr, "Failed to write data buffer to output file");
+ goto out_bad;
+ }
- zerr(ret);
+ goto out;
out_bad:
ret = EXIT_FAILURE;
@@ -257,5 +326,6 @@ out:
fclose(in);
if (out)
fclose(out);
+ free(buf);
return ret;
}
diff --git a/package/utils/jsonfilter/Makefile b/package/utils/jsonfilter/Makefile
index d249ea1a36..928ba1207b 100644
--- a/package/utils/jsonfilter/Makefile
+++ b/package/utils/jsonfilter/Makefile
@@ -5,9 +5,9 @@ PKG_RELEASE:=1
PKG_SOURCE_PROTO:=git
PKG_SOURCE_URL=$(PROJECT_GIT)/project/jsonpath.git
-PKG_SOURCE_DATE:=2024-01-23
-PKG_SOURCE_VERSION:=594cfa86469c005972ba750614f5b3f1af84d0f6
-PKG_MIRROR_HASH:=2f455f04fbfcdb4c81cccd23475b47395f847db44aa4bd9a1007b9aa0ab7fd19
+PKG_SOURCE_DATE:=2025-04-18
+PKG_SOURCE_VERSION:=8a86fb78235b5d7925b762b7b0934517890cc034
+PKG_MIRROR_HASH:=06b763387d00faae0e62af68626588ff2b8f12e0e6821950ae5422033df32757
CMAKE_INSTALL:=1
PKG_MAINTAINER:=Jo-Philipp Wich <jo@mein.io>
diff --git a/package/utils/mdadm/Makefile b/package/utils/mdadm/Makefile
index 8070003394..b521daaefc 100644
--- a/package/utils/mdadm/Makefile
+++ b/package/utils/mdadm/Makefile
@@ -8,12 +8,12 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=mdadm
-PKG_VERSION:=4.2
+PKG_VERSION:=4.3
PKG_RELEASE:=2
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.xz
PKG_SOURCE_URL:=@KERNEL/linux/utils/raid/mdadm
-PKG_HASH:=461c215670864bb74a4d1a3620684aa2b2f8296dffa06743f26dda5557acf01d
+PKG_HASH:=416727ae1f1080ea6e3090cea36dd076826fc369151e36ab736557ba92196f9f
PKG_MAINTAINER:=Felix Fietkau <nbd@nbd.name>
PKG_CPE_ID:=cpe:/a:mdadm_project:mdadm
diff --git a/package/utils/mdadm/patches/010-falloc.patch b/package/utils/mdadm/patches/010-falloc.patch
new file mode 100644
index 0000000000..2f2a9b87e1
--- /dev/null
+++ b/package/utils/mdadm/patches/010-falloc.patch
@@ -0,0 +1,36 @@
+From 52bead95d2957437c691891fcdc49bd6afccdd49 Mon Sep 17 00:00:00 2001
+From: Fabrice Fontaine <fontaine.fabrice@gmail.com>
+Date: Fri, 12 Apr 2024 18:45:13 +0200
+Subject: Create.c: fix uclibc build
+
+Define FALLOC_FL_ZERO_RANGE if needed as FALLOC_FL_ZERO_RANGE is only
+defined for aarch64 on uclibc-ng resulting in the following or1k build
+failure since commit 577fd10486d8d1472a6b559066f344ac30a3a391:
+
+Create.c: In function 'write_zeroes_fork':
+Create.c:155:35: error: 'FALLOC_FL_ZERO_RANGE' undeclared (first use in this function)
+ 155 | if (fallocate(fd, FALLOC_FL_ZERO_RANGE | FALLOC_FL_KEEP_SIZE,
+ | ^~~~~~~~~~~~~~~~~~~~
+
+Fixes:
+ - http://autobuild.buildroot.org/results/0e04bcdb591ca5642053e1f7e31384f06581e989
+
+Signed-off-by: Fabrice Fontaine <fontaine.fabrice@gmail.com>
+Signed-off-by: Mariusz Tkaczyk <mariusz.tkaczyk@linux.intel.com>
+---
+ Create.c | 4 ++++
+ 1 file changed, 4 insertions(+)
+
+--- a/Create.c
++++ b/Create.c
+@@ -32,6 +32,10 @@
+ #include <sys/signalfd.h>
+ #include <sys/wait.h>
+
++#ifndef FALLOC_FL_ZERO_RANGE
++#define FALLOC_FL_ZERO_RANGE 16
++#endif
++
+ static int round_size_and_verify(unsigned long long *size, int chunk)
+ {
+ if (*size == 0)
diff --git a/package/utils/mdadm/patches/020-basename.patch b/package/utils/mdadm/patches/020-basename.patch
new file mode 100644
index 0000000000..d4f7aa5963
--- /dev/null
+++ b/package/utils/mdadm/patches/020-basename.patch
@@ -0,0 +1,30 @@
+--- a/Monitor.c
++++ b/Monitor.c
+@@ -27,6 +27,7 @@
+ #include "md_p.h"
+ #include "md_u.h"
+ #include <sys/wait.h>
++#include <libgen.h>
+ #include <limits.h>
+ #include <syslog.h>
+
+--- a/platform-intel.c
++++ b/platform-intel.c
+@@ -28,6 +28,7 @@
+ #include <sys/mman.h>
+ #include <sys/types.h>
+ #include <sys/stat.h>
++#include <libgen.h>
+ #include <limits.h>
+
+ #define NVME_SUBSYS_PATH "/sys/devices/virtual/nvme-subsystem/"
+--- a/super-intel.c
++++ b/super-intel.c
+@@ -27,6 +27,7 @@
+ #include <scsi/sg.h>
+ #include <ctype.h>
+ #include <dirent.h>
++#include <libgen.h>
+
+ /* MPB == Metadata Parameter Block */
+ #define MPB_SIGNATURE "Intel Raid ISM Cfg Sig. "
diff --git a/package/utils/mdadm/patches/030-fix-monitor-tv_sec.patch b/package/utils/mdadm/patches/030-fix-monitor-tv_sec.patch
new file mode 100644
index 0000000000..a0fe9fd7c3
--- /dev/null
+++ b/package/utils/mdadm/patches/030-fix-monitor-tv_sec.patch
@@ -0,0 +1,14 @@
+--- a/monitor.c
++++ b/monitor.c
+@@ -449,9 +449,9 @@ static int read_and_act(struct active_ar
+ }
+
+ gettimeofday(&tv, NULL);
+- dprintf("(%d): %ld.%06ld state:%s prev:%s action:%s prev: %s start:%llu\n",
++ dprintf("(%d): %lld.%06ld state:%s prev:%s action:%s prev: %s start:%llu\n",
+ a->info.container_member,
+- tv.tv_sec, tv.tv_usec,
++ (long long)tv.tv_sec, (long)tv.tv_usec,
+ array_states[a->curr_state],
+ array_states[a->prev_state],
+ sync_actions[a->curr_action],
diff --git a/package/utils/mdadm/patches/040-udev.patch b/package/utils/mdadm/patches/040-udev.patch
new file mode 100644
index 0000000000..0d497aa016
--- /dev/null
+++ b/package/utils/mdadm/patches/040-udev.patch
@@ -0,0 +1,26 @@
+From 1750758c7ff526e3560433f6235e5cfa35cf646a Mon Sep 17 00:00:00 2001
+From: Mariusz Tkaczyk <mariusz.tkaczyk@linux.intel.com>
+Date: Wed, 6 Mar 2024 15:50:55 +0100
+Subject: udev.c: Do not require libudev.h if DNO_LIBUDEV
+
+libudev may not be presented at all, do not require it.
+
+Reported-by: Boian Bonev <bbonev@ipacct.com>
+Signed-off-by: Mariusz Tkaczyk <mariusz.tkaczyk@linux.intel.com>
+---
+ udev.c | 3 +++
+ 1 file changed, 3 insertions(+)
+
+--- a/udev.c
++++ b/udev.c
+@@ -26,7 +26,10 @@
+ #include <signal.h>
+ #include <limits.h>
+ #include <syslog.h>
++
++#ifndef NO_LIBUDEV
+ #include <libudev.h>
++#endif
+
+ static char *unblock_path;
+
diff --git a/package/utils/mdadm/patches/050-pie.patch b/package/utils/mdadm/patches/050-pie.patch
new file mode 100644
index 0000000000..59c96f51ba
--- /dev/null
+++ b/package/utils/mdadm/patches/050-pie.patch
@@ -0,0 +1,33 @@
+From 893a55831e5abbcd15b171db66fa1f389fb61506 Mon Sep 17 00:00:00 2001
+From: Fabrice Fontaine <fontaine.fabrice@gmail.com>
+Date: Tue, 7 May 2024 19:32:16 +0200
+Subject: Makefile: Move -pie to LDFLAGS
+
+Move -pie from LDLIBS to LDFLAGS and make LDFLAGS configurable to allow
+the user to drop it by setting their own LDFLAGS (e.g. PIE could be
+enabled or disabled by the buildsystem such as buildroot).
+
+Suggested-by: Mariusz Tkaczyk <mariusz.tkaczyk@linux.intel.com>
+Signed-off-by: Fabrice Fontaine <fontaine.fabrice@gmail.com>
+Signed-off-by: Mariusz Tkaczyk <mariusz.tkaczyk@linux.intel.com>
+---
+ Makefile | 4 ++--
+ 1 file changed, 2 insertions(+), 2 deletions(-)
+
+--- a/Makefile
++++ b/Makefile
+@@ -132,12 +132,12 @@ CFLAGS += -DUSE_PTHREADS
+ MON_LDFLAGS += -pthread
+ endif
+
+-LDFLAGS = -Wl,-z,now,-z,noexecstack
++LDFLAGS ?= -pie -Wl,-z,now,-z,noexecstack
+
+ # If you want a static binary, you might uncomment these
+ # LDFLAGS += -static
+ # STRIP = -s
+-LDLIBS = -ldl -pie
++LDLIBS = -ldl
+
+ # To explicitly disable libudev, set -DNO_LIBUDEV in CXFLAGS
+ ifeq (, $(findstring -DNO_LIBUDEV, $(CXFLAGS)))
diff --git a/package/utils/mdadm/patches/060-gcc14.patch b/package/utils/mdadm/patches/060-gcc14.patch
new file mode 100644
index 0000000000..545a40ac0a
--- /dev/null
+++ b/package/utils/mdadm/patches/060-gcc14.patch
@@ -0,0 +1,24 @@
+From 8bda86099089b44129ef6206764f9de47a45f0db Mon Sep 17 00:00:00 2001
+From: Alexander Kanavin <alex@linutronix.de>
+Date: Tue, 12 Mar 2024 11:01:50 +0100
+Subject: [PATCH] util.c: add limits.h include for NAME_MAX definition
+
+Add limits.h include for NAME_MAX definition.
+
+Signed-off-by: Alexander Kanavin <alex@linutronix.de>
+Signed-off-by: Mariusz Tkaczyk <mariusz.tkaczyk@linux.intel.com>
+---
+ util.c | 2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+--- a/util.c
++++ b/util.c
+@@ -36,7 +36,7 @@
+ #include <ctype.h>
+ #include <dirent.h>
+ #include <dlfcn.h>
+-
++#include <limits.h>
+
+ /*
+ * following taken from linux/blkpg.h because they aren't
diff --git a/package/utils/mdadm/patches/100-cross_compile.patch b/package/utils/mdadm/patches/100-cross_compile.patch
index 790d7755b0..bd71c083e5 100644
--- a/package/utils/mdadm/patches/100-cross_compile.patch
+++ b/package/utils/mdadm/patches/100-cross_compile.patch
@@ -1,6 +1,6 @@
--- a/Makefile
+++ b/Makefile
-@@ -99,7 +99,7 @@ DLM:=$(shell [ -f /usr/include/libdlm.h
+@@ -115,7 +115,7 @@ DLM:=$(shell [ -f /usr/include/libdlm.h
DIRFLAGS = -DMAP_DIR=\"$(MAP_DIR)\" -DMAP_FILE=\"$(MAP_FILE)\"
DIRFLAGS += -DMDMON_DIR=\"$(MDMON_DIR)\"
DIRFLAGS += -DFAILED_SLOTS_DIR=\"$(FAILED_SLOTS_DIR)\"
diff --git a/package/utils/mdadm/patches/200-reduce_size.patch b/package/utils/mdadm/patches/200-reduce_size.patch
index 163e125c22..d14fae716e 100644
--- a/package/utils/mdadm/patches/200-reduce_size.patch
+++ b/package/utils/mdadm/patches/200-reduce_size.patch
@@ -1,6 +1,6 @@
--- a/Incremental.c
+++ b/Incremental.c
-@@ -983,6 +983,10 @@ static int array_try_spare(char *devname
+@@ -985,6 +985,10 @@ static int array_try_spare(char *devname
goto next;
}
@@ -13,7 +13,7 @@
/* domain test fails */
--- a/util.c
+++ b/util.c
-@@ -1147,7 +1147,9 @@ void wait_for(char *dev, int fd)
+@@ -1192,7 +1192,9 @@ void wait_for(char *dev, int fd)
struct superswitch *superlist[] =
{
&super0, &super1,
diff --git a/package/utils/mtd-utils/Makefile b/package/utils/mtd-utils/Makefile
index fd1cb75e51..0dc8831251 100644
--- a/package/utils/mtd-utils/Makefile
+++ b/package/utils/mtd-utils/Makefile
@@ -8,15 +8,14 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=mtd-utils
-PKG_VERSION:=2.1.6
+PKG_VERSION:=2.2.1
PKG_RELEASE:=1
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.bz2
PKG_SOURCE_URL:=https://infraroot.at/pub/mtd/
-PKG_HASH:=c1d853bc4adf83bcabd2792fc95af33bdd8643c97e8f7b3f0180af36af76f0e5
+PKG_HASH:=f7ae20b2eb79ee83441468f0b99d897024cd96ff853eea59106fb1952065c803
PKG_INSTALL:=1
-PKG_FIXUP:=autoreconf
PKG_FLAGS:=nonshared
PKG_BUILD_FLAGS:=gc-sections
@@ -59,24 +58,34 @@ endef
MAKE_FLAGS += LDLIBS+="$(LIBGCC_S)"
CONFIGURE_ARGS += \
- --disable-tests \
+ --enable-tests \
--without-crypto \
--without-xattr \
--without-zstd \
- --without-lzo
+ --without-lzo \
+ --without-zlib
+
+define Package/ubi-utils/conffiles
+/etc/config/ubihealthd
+endef
define Package/ubi-utils/install
$(INSTALL_DIR) $(1)/usr/sbin
$(INSTALL_BIN) \
- $(PKG_INSTALL_DIR)/usr/sbin/{ubiattach,ubicrc32,ubiblock,ubidetach,ubiformat,ubimkvol} $(1)/usr/sbin/
+ $(PKG_INSTALL_DIR)/usr/sbin/{ubiattach,ubicrc32,ubiblock,ubidetach,ubiformat,ubihealthd} $(1)/usr/sbin/
$(INSTALL_BIN) \
- $(PKG_INSTALL_DIR)/usr/sbin/{ubinfo,ubinize,ubirename,ubirmvol,ubirsvol,ubiupdatevol} $(1)/usr/sbin/
+ $(PKG_INSTALL_DIR)/usr/sbin/{ubimkvol,ubinfo,ubinize,ubirename,ubirmvol,ubirsvol,ubiupdatevol} $(1)/usr/sbin/
+ $(INSTALL_DIR) $(1)/etc/init.d
+ $(INSTALL_BIN) ./files/ubihealthd.init $(1)/etc/init.d/ubihealthd
+ $(INSTALL_DIR) $(1)/etc/uci-defaults
+ $(INSTALL_DATA) ./files/ubihealthd.defaults $(1)/etc/uci-defaults/ubihealthd
endef
define Package/nand-utils/install
$(INSTALL_DIR) $(1)/usr/sbin
$(INSTALL_BIN) \
- $(PKG_INSTALL_DIR)/usr/sbin/{flash_erase,nanddump,nandwrite,nandtest,mtdinfo} $(1)/usr/sbin/
+ $(PKG_INSTALL_DIR)/usr/sbin/{flash_erase,nanddump,nandwrite,nandtest,mtdinfo} \
+ $(PKG_INSTALL_DIR)/usr/lib/mtd-utils/nandbiterrs $(1)/usr/sbin/
endef
$(eval $(call BuildPackage,ubi-utils))
diff --git a/package/utils/mtd-utils/files/ubihealthd.defaults b/package/utils/mtd-utils/files/ubihealthd.defaults
new file mode 100644
index 0000000000..5222961bd4
--- /dev/null
+++ b/package/utils/mtd-utils/files/ubihealthd.defaults
@@ -0,0 +1,18 @@
+#!/bin/sh
+
+[ -e "/etc/config/ubihealthd" ] && exit 0
+[ ! -e "/sys/class/ubi" ] && exit 0
+
+touch "/etc/config/ubihealthd"
+
+for ubidev in /sys/class/ubi/*/total_eraseblocks; do
+ ubidev="${ubidev%/*}"
+ ubidev="${ubidev##*/}"
+ uci batch <<EOF
+set ubihealthd.$ubidev=ubi-device
+set ubihealthd.$ubidev.device="/dev/$ubidev"
+set ubihealthd.$ubidev.enable=1
+EOF
+done
+
+uci commit ubihealthd
diff --git a/package/utils/mtd-utils/files/ubihealthd.init b/package/utils/mtd-utils/files/ubihealthd.init
new file mode 100644
index 0000000000..c5ec97b22e
--- /dev/null
+++ b/package/utils/mtd-utils/files/ubihealthd.init
@@ -0,0 +1,27 @@
+#!/bin/sh /etc/rc.common
+
+START=99
+
+USE_PROCD=1
+PROG=/usr/sbin/ubihealthd
+
+ubihealthd_instance() {
+ local cfg="$1"
+ local device interval enable
+
+ config_get_bool enable "$cfg" "enable" 1
+ [ "$enable" = "1" ] || return 0
+
+ config_get device "$cfg" "device"
+ config_get interval "$cfg" "interval"
+
+ procd_open_instance
+ procd_set_param command "$PROG" -f -d "$device"
+ [ -n "$interval" ] && procd_append_param command -i "$interval"
+ procd_close_instance
+}
+
+start_service() {
+ config_load ubihealthd
+ config_foreach ubihealthd_instance ubi-device
+}
diff --git a/package/utils/mtd-utils/patches/100-fix_includes.patch b/package/utils/mtd-utils/patches/100-fix_includes.patch
deleted file mode 100644
index cc75052c5e..0000000000
--- a/package/utils/mtd-utils/patches/100-fix_includes.patch
+++ /dev/null
@@ -1,10 +0,0 @@
---- a/lib/libfec.c
-+++ b/lib/libfec.c
-@@ -45,6 +45,7 @@
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
-+#include <sys/types.h>
- #include "libfec.h"
-
- /*
diff --git a/package/utils/mtd-utils/patches/130-lzma_jffs2.patch b/package/utils/mtd-utils/patches/130-lzma_jffs2.patch
deleted file mode 100644
index db683063d5..0000000000
--- a/package/utils/mtd-utils/patches/130-lzma_jffs2.patch
+++ /dev/null
@@ -1,5038 +0,0 @@
---- a/jffsX-utils/Makemodule.am
-+++ b/jffsX-utils/Makemodule.am
-@@ -4,7 +4,10 @@ mkfs_jffs2_SOURCES = \
- jffsX-utils/compr_zlib.c \
- jffsX-utils/compr.h \
- jffsX-utils/rbtree.c \
-- jffsX-utils/compr_lzo.c \
-+ jffsX-utils/compr_lzma.c \
-+ jffsX-utils/lzma/LzFind.c \
-+ jffsX-utils/lzma/LzmaEnc.c \
-+ jffsX-utils/lzma/LzmaDec.c \
- jffsX-utils/compr.c \
- jffsX-utils/compr_rtime.c \
- jffsX-utils/compr.h \
-@@ -12,8 +15,13 @@ mkfs_jffs2_SOURCES = \
- jffsX-utils/summary.h \
- include/linux/jffs2.h \
- include/mtd/jffs2-user.h
-+
-+if !WITHOUT_LZO
-+mkfs_jffs2_SOURCES += jffsX-utils/compr_lzo.c
-+endif
-+
- mkfs_jffs2_LDADD = libmtd.a $(ZLIB_LIBS) $(LZO_LIBS)
--mkfs_jffs2_CPPFLAGS = $(AM_CPPFLAGS) $(ZLIB_CFLAGS) $(LZO_CFLAGS)
-+mkfs_jffs2_CPPFLAGS = $(AM_CPPFLAGS) $(ZLIB_CFLAGS) $(LZO_CFLAGS) -I./include/linux/lzma
-
- jffs2reader_SOURCES = jffsX-utils/jffs2reader.c include/mtd/jffs2-user.h
- jffs2reader_LDADD = libmtd.a $(ZLIB_LIBS) $(LZO_LIBS)
---- a/jffsX-utils/compr.c
-+++ b/jffsX-utils/compr.c
-@@ -520,6 +520,9 @@ int jffs2_compressors_init(void)
- #ifdef CONFIG_JFFS2_LZO
- jffs2_lzo_init();
- #endif
-+#ifdef CONFIG_JFFS2_LZMA
-+ jffs2_lzma_init();
-+#endif
- return 0;
- }
-
-@@ -534,5 +537,8 @@ int jffs2_compressors_exit(void)
- #ifdef CONFIG_JFFS2_LZO
- jffs2_lzo_exit();
- #endif
-+#ifdef CONFIG_JFFS2_LZMA
-+ jffs2_lzma_exit();
-+#endif
- return 0;
- }
---- a/jffsX-utils/compr.h
-+++ b/jffsX-utils/compr.h
-@@ -18,13 +18,14 @@
-
- #define CONFIG_JFFS2_ZLIB
- #define CONFIG_JFFS2_RTIME
--#define CONFIG_JFFS2_LZO
-+#define CONFIG_JFFS2_LZMA
-
- #define JFFS2_RUBINMIPS_PRIORITY 10
- #define JFFS2_DYNRUBIN_PRIORITY 20
- #define JFFS2_RTIME_PRIORITY 50
--#define JFFS2_ZLIB_PRIORITY 60
--#define JFFS2_LZO_PRIORITY 80
-+#define JFFS2_LZMA_PRIORITY 70
-+#define JFFS2_ZLIB_PRIORITY 80
-+#define JFFS2_LZO_PRIORITY 90
-
- #define JFFS2_COMPR_MODE_NONE 0
- #define JFFS2_COMPR_MODE_PRIORITY 1
-@@ -115,5 +116,10 @@ void jffs2_rtime_exit(void);
- int jffs2_lzo_init(void);
- void jffs2_lzo_exit(void);
- #endif
-+#ifdef CONFIG_JFFS2_LZMA
-+int jffs2_lzma_init(void);
-+void jffs2_lzma_exit(void);
-+#endif
-+
-
- #endif /* __JFFS2_COMPR_H__ */
---- /dev/null
-+++ b/jffsX-utils/compr_lzma.c
-@@ -0,0 +1,128 @@
-+/*
-+ * JFFS2 -- Journalling Flash File System, Version 2.
-+ *
-+ * For licensing information, see the file 'LICENCE' in this directory.
-+ *
-+ * JFFS2 wrapper to the LZMA C SDK
-+ *
-+ */
-+
-+#include <linux/lzma.h>
-+#include "compr.h"
-+
-+#ifdef __KERNEL__
-+ static DEFINE_MUTEX(deflate_mutex);
-+#endif
-+
-+CLzmaEncHandle *p;
-+Byte propsEncoded[LZMA_PROPS_SIZE];
-+SizeT propsSize = sizeof(propsEncoded);
-+
-+STATIC void lzma_free_workspace(void)
-+{
-+ LzmaEnc_Destroy(p, &lzma_alloc, &lzma_alloc);
-+}
-+
-+STATIC int INIT lzma_alloc_workspace(CLzmaEncProps *props)
-+{
-+ if ((p = (CLzmaEncHandle *)LzmaEnc_Create(&lzma_alloc)) == NULL)
-+ {
-+ PRINT_ERROR("Failed to allocate lzma deflate workspace\n");
-+ return -ENOMEM;
-+ }
-+
-+ if (LzmaEnc_SetProps(p, props) != SZ_OK)
-+ {
-+ lzma_free_workspace();
-+ return -1;
-+ }
-+
-+ if (LzmaEnc_WriteProperties(p, propsEncoded, &propsSize) != SZ_OK)
-+ {
-+ lzma_free_workspace();
-+ return -1;
-+ }
-+
-+ return 0;
-+}
-+
-+STATIC int jffs2_lzma_compress(unsigned char *data_in, unsigned char *cpage_out,
-+ uint32_t *sourcelen, uint32_t *dstlen)
-+{
-+ SizeT compress_size = (SizeT)(*dstlen);
-+ int ret;
-+
-+ #ifdef __KERNEL__
-+ mutex_lock(&deflate_mutex);
-+ #endif
-+
-+ ret = LzmaEnc_MemEncode(p, cpage_out, &compress_size, data_in, *sourcelen,
-+ 0, NULL, &lzma_alloc, &lzma_alloc);
-+
-+ #ifdef __KERNEL__
-+ mutex_unlock(&deflate_mutex);
-+ #endif
-+
-+ if (ret != SZ_OK)
-+ return -1;
-+
-+ *dstlen = (uint32_t)compress_size;
-+
-+ return 0;
-+}
-+
-+STATIC int jffs2_lzma_decompress(unsigned char *data_in, unsigned char *cpage_out,
-+ uint32_t srclen, uint32_t destlen)
-+{
-+ int ret;
-+ SizeT dl = (SizeT)destlen;
-+ SizeT sl = (SizeT)srclen;
-+ ELzmaStatus status;
-+
-+ ret = LzmaDecode(cpage_out, &dl, data_in, &sl, propsEncoded,
-+ propsSize, LZMA_FINISH_ANY, &status, &lzma_alloc);
-+
-+ if (ret != SZ_OK || status == LZMA_STATUS_NOT_FINISHED || dl != (SizeT)destlen)
-+ return -1;
-+
-+ return 0;
-+}
-+
-+static struct jffs2_compressor jffs2_lzma_comp = {
-+ .priority = JFFS2_LZMA_PRIORITY,
-+ .name = "lzma",
-+ .compr = JFFS2_COMPR_LZMA,
-+ .compress = &jffs2_lzma_compress,
-+ .decompress = &jffs2_lzma_decompress,
-+ .disabled = 0,
-+};
-+
-+int INIT jffs2_lzma_init(void)
-+{
-+ int ret;
-+ CLzmaEncProps props;
-+ LzmaEncProps_Init(&props);
-+
-+ props.dictSize = LZMA_BEST_DICT(0x2000);
-+ props.level = LZMA_BEST_LEVEL;
-+ props.lc = LZMA_BEST_LC;
-+ props.lp = LZMA_BEST_LP;
-+ props.pb = LZMA_BEST_PB;
-+ props.fb = LZMA_BEST_FB;
-+
-+ ret = lzma_alloc_workspace(&props);
-+ if (ret < 0)
-+ return ret;
-+
-+ ret = jffs2_register_compressor(&jffs2_lzma_comp);
-+ if (ret)
-+ lzma_free_workspace();
-+
-+ return ret;
-+}
-+
-+void jffs2_lzma_exit(void)
-+{
-+ jffs2_unregister_compressor(&jffs2_lzma_comp);
-+ lzma_free_workspace();
-+}
---- a/include/linux/jffs2.h
-+++ b/include/linux/jffs2.h
-@@ -47,6 +47,7 @@
- #define JFFS2_COMPR_DYNRUBIN 0x05
- #define JFFS2_COMPR_ZLIB 0x06
- #define JFFS2_COMPR_LZO 0x07
-+#define JFFS2_COMPR_LZMA 0x08
- /* Compatibility flags. */
- #define JFFS2_COMPAT_MASK 0xc000 /* What do to if an unknown nodetype is found */
- #define JFFS2_NODE_ACCURATE 0x2000
---- /dev/null
-+++ b/include/linux/lzma.h
-@@ -0,0 +1,61 @@
-+#ifndef __LZMA_H__
-+#define __LZMA_H__
-+
-+#ifdef __KERNEL__
-+ #include <linux/kernel.h>
-+ #include <linux/sched.h>
-+ #include <linux/slab.h>
-+ #include <linux/vmalloc.h>
-+ #include <linux/init.h>
-+ #define LZMA_MALLOC vmalloc
-+ #define LZMA_FREE vfree
-+ #define PRINT_ERROR(msg) printk(KERN_WARNING #msg)
-+ #define INIT __init
-+ #define STATIC static
-+#else
-+ #include <stdint.h>
-+ #include <stdlib.h>
-+ #include <stdio.h>
-+ #include <unistd.h>
-+ #include <string.h>
-+ #include <errno.h>
-+ #include <linux/jffs2.h>
-+ #ifndef PAGE_SIZE
-+ extern int page_size;
-+ #define PAGE_SIZE page_size
-+ #endif
-+ #define LZMA_MALLOC malloc
-+ #define LZMA_FREE free
-+ #define PRINT_ERROR(msg) fprintf(stderr, msg)
-+ #define INIT
-+ #define STATIC static
-+#endif
-+
-+#include "lzma/LzmaDec.h"
-+#include "lzma/LzmaEnc.h"
-+
-+#define LZMA_BEST_LEVEL (9)
-+#define LZMA_BEST_LC (0)
-+#define LZMA_BEST_LP (0)
-+#define LZMA_BEST_PB (0)
-+#define LZMA_BEST_FB (273)
-+
-+#define LZMA_BEST_DICT(n) (((int)((n) / 2)) * 2)
-+
-+static void *p_lzma_malloc(void *p, size_t size)
-+{
-+ if (size == 0)
-+ return NULL;
-+
-+ return LZMA_MALLOC(size);
-+}
-+
-+static void p_lzma_free(void *p, void *address)
-+{
-+ if (address != NULL)
-+ LZMA_FREE(address);
-+}
-+
-+static ISzAlloc lzma_alloc = {p_lzma_malloc, p_lzma_free};
-+
-+#endif
---- /dev/null
-+++ b/include/linux/lzma/LzFind.h
-@@ -0,0 +1,116 @@
-+/* LzFind.h -- Match finder for LZ algorithms
-+2008-04-04
-+Copyright (c) 1999-2008 Igor Pavlov
-+You can use any of the following license options:
-+ 1) GNU Lesser General Public License (GNU LGPL)
-+ 2) Common Public License (CPL)
-+ 3) Common Development and Distribution License (CDDL) Version 1.0
-+ 4) Igor Pavlov, as the author of this code, expressly permits you to
-+ statically or dynamically link your code (or bind by name) to this file,
-+ while you keep this file unmodified.
-+*/
-+
-+#ifndef __LZFIND_H
-+#define __LZFIND_H
-+
-+#include "Types.h"
-+
-+typedef UInt32 CLzRef;
-+
-+typedef struct _CMatchFinder
-+{
-+ Byte *buffer;
-+ UInt32 pos;
-+ UInt32 posLimit;
-+ UInt32 streamPos;
-+ UInt32 lenLimit;
-+
-+ UInt32 cyclicBufferPos;
-+ UInt32 cyclicBufferSize; /* it must be = (historySize + 1) */
-+
-+ UInt32 matchMaxLen;
-+ CLzRef *hash;
-+ CLzRef *son;
-+ UInt32 hashMask;
-+ UInt32 cutValue;
-+
-+ Byte *bufferBase;
-+ ISeqInStream *stream;
-+ int streamEndWasReached;
-+
-+ UInt32 blockSize;
-+ UInt32 keepSizeBefore;
-+ UInt32 keepSizeAfter;
-+
-+ UInt32 numHashBytes;
-+ int directInput;
-+ int btMode;
-+ /* int skipModeBits; */
-+ int bigHash;
-+ UInt32 historySize;
-+ UInt32 fixedHashSize;
-+ UInt32 hashSizeSum;
-+ UInt32 numSons;
-+ SRes result;
-+ UInt32 crc[256];
-+} CMatchFinder;
-+
-+#define Inline_MatchFinder_GetPointerToCurrentPos(p) ((p)->buffer)
-+#define Inline_MatchFinder_GetIndexByte(p, index) ((p)->buffer[(Int32)(index)])
-+
-+#define Inline_MatchFinder_GetNumAvailableBytes(p) ((p)->streamPos - (p)->pos)
-+
-+int MatchFinder_NeedMove(CMatchFinder *p);
-+Byte *MatchFinder_GetPointerToCurrentPos(CMatchFinder *p);
-+void MatchFinder_MoveBlock(CMatchFinder *p);
-+void MatchFinder_ReadIfRequired(CMatchFinder *p);
-+
-+void MatchFinder_Construct(CMatchFinder *p);
-+
-+/* Conditions:
-+ historySize <= 3 GB
-+ keepAddBufferBefore + matchMaxLen + keepAddBufferAfter < 511MB
-+*/
-+int MatchFinder_Create(CMatchFinder *p, UInt32 historySize,
-+ UInt32 keepAddBufferBefore, UInt32 matchMaxLen, UInt32 keepAddBufferAfter,
-+ ISzAlloc *alloc);
-+void MatchFinder_Free(CMatchFinder *p, ISzAlloc *alloc);
-+void MatchFinder_Normalize3(UInt32 subValue, CLzRef *items, UInt32 numItems);
-+void MatchFinder_ReduceOffsets(CMatchFinder *p, UInt32 subValue);
-+
-+UInt32 * GetMatchesSpec1(UInt32 lenLimit, UInt32 curMatch, UInt32 pos, const Byte *buffer, CLzRef *son,
-+ UInt32 _cyclicBufferPos, UInt32 _cyclicBufferSize, UInt32 _cutValue,
-+ UInt32 *distances, UInt32 maxLen);
-+
-+/*
-+Conditions:
-+ Mf_GetNumAvailableBytes_Func must be called before each Mf_GetMatchLen_Func.
-+ Mf_GetPointerToCurrentPos_Func's result must be used only before any other function
-+*/
-+
-+typedef void (*Mf_Init_Func)(void *object);
-+typedef Byte (*Mf_GetIndexByte_Func)(void *object, Int32 index);
-+typedef UInt32 (*Mf_GetNumAvailableBytes_Func)(void *object);
-+typedef const Byte * (*Mf_GetPointerToCurrentPos_Func)(void *object);
-+typedef UInt32 (*Mf_GetMatches_Func)(void *object, UInt32 *distances);
-+typedef void (*Mf_Skip_Func)(void *object, UInt32);
-+
-+typedef struct _IMatchFinder
-+{
-+ Mf_Init_Func Init;
-+ Mf_GetIndexByte_Func GetIndexByte;
-+ Mf_GetNumAvailableBytes_Func GetNumAvailableBytes;
-+ Mf_GetPointerToCurrentPos_Func GetPointerToCurrentPos;
-+ Mf_GetMatches_Func GetMatches;
-+ Mf_Skip_Func Skip;
-+} IMatchFinder;
-+
-+void MatchFinder_CreateVTable(CMatchFinder *p, IMatchFinder *vTable);
-+
-+void MatchFinder_Init(CMatchFinder *p);
-+UInt32 Bt3Zip_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances);
-+UInt32 Hc3Zip_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances);
-+void Bt3Zip_MatchFinder_Skip(CMatchFinder *p, UInt32 num);
-+void Hc3Zip_MatchFinder_Skip(CMatchFinder *p, UInt32 num);
-+
-+#endif
---- /dev/null
-+++ b/include/linux/lzma/LzHash.h
-@@ -0,0 +1,56 @@
-+/* LzHash.h -- HASH functions for LZ algorithms
-+2008-03-26
-+Copyright (c) 1999-2008 Igor Pavlov
-+Read LzFind.h for license options */
-+
-+#ifndef __LZHASH_H
-+#define __LZHASH_H
-+
-+#define kHash2Size (1 << 10)
-+#define kHash3Size (1 << 16)
-+#define kHash4Size (1 << 20)
-+
-+#define kFix3HashSize (kHash2Size)
-+#define kFix4HashSize (kHash2Size + kHash3Size)
-+#define kFix5HashSize (kHash2Size + kHash3Size + kHash4Size)
-+
-+#define HASH2_CALC hashValue = cur[0] | ((UInt32)cur[1] << 8);
-+
-+#define HASH3_CALC { \
-+ UInt32 temp = p->crc[cur[0]] ^ cur[1]; \
-+ hash2Value = temp & (kHash2Size - 1); \
-+ hashValue = (temp ^ ((UInt32)cur[2] << 8)) & p->hashMask; }
-+
-+#define HASH4_CALC { \
-+ UInt32 temp = p->crc[cur[0]] ^ cur[1]; \
-+ hash2Value = temp & (kHash2Size - 1); \
-+ hash3Value = (temp ^ ((UInt32)cur[2] << 8)) & (kHash3Size - 1); \
-+ hashValue = (temp ^ ((UInt32)cur[2] << 8) ^ (p->crc[cur[3]] << 5)) & p->hashMask; }
-+
-+#define HASH5_CALC { \
-+ UInt32 temp = p->crc[cur[0]] ^ cur[1]; \
-+ hash2Value = temp & (kHash2Size - 1); \
-+ hash3Value = (temp ^ ((UInt32)cur[2] << 8)) & (kHash3Size - 1); \
-+ hash4Value = (temp ^ ((UInt32)cur[2] << 8) ^ (p->crc[cur[3]] << 5)); \
-+ hashValue = (hash4Value ^ (p->crc[cur[4]] << 3)) & p->hashMask; \
-+ hash4Value &= (kHash4Size - 1); }
-+
-+/* #define HASH_ZIP_CALC hashValue = ((cur[0] | ((UInt32)cur[1] << 8)) ^ p->crc[cur[2]]) & 0xFFFF; */
-+#define HASH_ZIP_CALC hashValue = ((cur[2] | ((UInt32)cur[0] << 8)) ^ p->crc[cur[1]]) & 0xFFFF;
-+
-+
-+#define MT_HASH2_CALC \
-+ hash2Value = (p->crc[cur[0]] ^ cur[1]) & (kHash2Size - 1);
-+
-+#define MT_HASH3_CALC { \
-+ UInt32 temp = p->crc[cur[0]] ^ cur[1]; \
-+ hash2Value = temp & (kHash2Size - 1); \
-+ hash3Value = (temp ^ ((UInt32)cur[2] << 8)) & (kHash3Size - 1); }
-+
-+#define MT_HASH4_CALC { \
-+ UInt32 temp = p->crc[cur[0]] ^ cur[1]; \
-+ hash2Value = temp & (kHash2Size - 1); \
-+ hash3Value = (temp ^ ((UInt32)cur[2] << 8)) & (kHash3Size - 1); \
-+ hash4Value = (temp ^ ((UInt32)cur[2] << 8) ^ (p->crc[cur[3]] << 5)) & (kHash4Size - 1); }
-+
-+#endif
---- /dev/null
-+++ b/include/linux/lzma/LzmaDec.h
-@@ -0,0 +1,232 @@
-+/* LzmaDec.h -- LZMA Decoder
-+2008-04-29
-+Copyright (c) 1999-2008 Igor Pavlov
-+You can use any of the following license options:
-+ 1) GNU Lesser General Public License (GNU LGPL)
-+ 2) Common Public License (CPL)
-+ 3) Common Development and Distribution License (CDDL) Version 1.0
-+ 4) Igor Pavlov, as the author of this code, expressly permits you to
-+ statically or dynamically link your code (or bind by name) to this file,
-+ while you keep this file unmodified.
-+*/
-+
-+#ifndef __LZMADEC_H
-+#define __LZMADEC_H
-+
-+#include "Types.h"
-+
-+/* #define _LZMA_PROB32 */
-+/* _LZMA_PROB32 can increase the speed on some CPUs,
-+ but memory usage for CLzmaDec::probs will be doubled in that case */
-+
-+#ifdef _LZMA_PROB32
-+#define CLzmaProb UInt32
-+#else
-+#define CLzmaProb UInt16
-+#endif
-+
-+
-+/* ---------- LZMA Properties ---------- */
-+
-+#define LZMA_PROPS_SIZE 5
-+
-+typedef struct _CLzmaProps
-+{
-+ unsigned lc, lp, pb;
-+ UInt32 dicSize;
-+} CLzmaProps;
-+
-+/* LzmaProps_Decode - decodes properties
-+Returns:
-+ SZ_OK
-+ SZ_ERROR_UNSUPPORTED - Unsupported properties
-+*/
-+
-+SRes LzmaProps_Decode(CLzmaProps *p, const Byte *data, unsigned size);
-+
-+
-+/* ---------- LZMA Decoder state ---------- */
-+
-+/* LZMA_REQUIRED_INPUT_MAX = number of required input bytes for worst case.
-+ Num bits = log2((2^11 / 31) ^ 22) + 26 < 134 + 26 = 160; */
-+
-+#define LZMA_REQUIRED_INPUT_MAX 20
-+
-+typedef struct
-+{
-+ CLzmaProps prop;
-+ CLzmaProb *probs;
-+ Byte *dic;
-+ const Byte *buf;
-+ UInt32 range, code;
-+ SizeT dicPos;
-+ SizeT dicBufSize;
-+ UInt32 processedPos;
-+ UInt32 checkDicSize;
-+ unsigned state;
-+ UInt32 reps[4];
-+ unsigned remainLen;
-+ int needFlush;
-+ int needInitState;
-+ UInt32 numProbs;
-+ unsigned tempBufSize;
-+ Byte tempBuf[LZMA_REQUIRED_INPUT_MAX];
-+} CLzmaDec;
-+
-+#define LzmaDec_Construct(p) { (p)->dic = 0; (p)->probs = 0; }
-+
-+void LzmaDec_Init(CLzmaDec *p);
-+
-+/* There are two types of LZMA streams:
-+ 0) Stream with end mark. That end mark adds about 6 bytes to compressed size.
-+ 1) Stream without end mark. You must know exact uncompressed size to decompress such stream. */
-+
-+typedef enum
-+{
-+ LZMA_FINISH_ANY, /* finish at any point */
-+ LZMA_FINISH_END /* block must be finished at the end */
-+} ELzmaFinishMode;
-+
-+/* ELzmaFinishMode has meaning only if the decoding reaches output limit !!!
-+
-+ You must use LZMA_FINISH_END, when you know that current output buffer
-+ covers last bytes of block. In other cases you must use LZMA_FINISH_ANY.
-+
-+ If LZMA decoder sees end marker before reaching output limit, it returns SZ_OK,
-+ and output value of destLen will be less than output buffer size limit.
-+ You can check status result also.
-+
-+ You can use multiple checks to test data integrity after full decompression:
-+ 1) Check Result and "status" variable.
-+ 2) Check that output(destLen) = uncompressedSize, if you know real uncompressedSize.
-+ 3) Check that output(srcLen) = compressedSize, if you know real compressedSize.
-+ You must use correct finish mode in that case. */
-+
-+typedef enum
-+{
-+ LZMA_STATUS_NOT_SPECIFIED, /* use main error code instead */
-+ LZMA_STATUS_FINISHED_WITH_MARK, /* stream was finished with end mark. */
-+ LZMA_STATUS_NOT_FINISHED, /* stream was not finished */
-+ LZMA_STATUS_NEEDS_MORE_INPUT, /* you must provide more input bytes */
-+ LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK /* there is probability that stream was finished without end mark */
-+} ELzmaStatus;
-+
-+/* ELzmaStatus is used only as output value for function call */
-+
-+
-+/* ---------- Interfaces ---------- */
-+
-+/* There are 3 levels of interfaces:
-+ 1) Dictionary Interface
-+ 2) Buffer Interface
-+ 3) One Call Interface
-+ You can select any of these interfaces, but don't mix functions from different
-+ groups for same object. */
-+
-+
-+/* There are two variants to allocate state for Dictionary Interface:
-+ 1) LzmaDec_Allocate / LzmaDec_Free
-+ 2) LzmaDec_AllocateProbs / LzmaDec_FreeProbs
-+ You can use variant 2, if you set dictionary buffer manually.
-+ For Buffer Interface you must always use variant 1.
-+
-+LzmaDec_Allocate* can return:
-+ SZ_OK
-+ SZ_ERROR_MEM - Memory allocation error
-+ SZ_ERROR_UNSUPPORTED - Unsupported properties
-+*/
-+
-+SRes LzmaDec_AllocateProbs(CLzmaDec *p, const Byte *props, unsigned propsSize, ISzAlloc *alloc);
-+void LzmaDec_FreeProbs(CLzmaDec *p, ISzAlloc *alloc);
-+
-+SRes LzmaDec_Allocate(CLzmaDec *state, const Byte *prop, unsigned propsSize, ISzAlloc *alloc);
-+void LzmaDec_Free(CLzmaDec *state, ISzAlloc *alloc);
-+
-+/* ---------- Dictionary Interface ---------- */
-+
-+/* You can use it, if you want to eliminate the overhead for data copying from
-+ dictionary to some other external buffer.
-+ You must work with CLzmaDec variables directly in this interface.
-+
-+ STEPS:
-+ LzmaDec_Constr()
-+ LzmaDec_Allocate()
-+ for (each new stream)
-+ {
-+ LzmaDec_Init()
-+ while (it needs more decompression)
-+ {
-+ LzmaDec_DecodeToDic()
-+ use data from CLzmaDec::dic and update CLzmaDec::dicPos
-+ }
-+ }
-+ LzmaDec_Free()
-+*/
-+
-+/* LzmaDec_DecodeToDic
-+
-+ The decoding to internal dictionary buffer (CLzmaDec::dic).
-+ You must manually update CLzmaDec::dicPos, if it reaches CLzmaDec::dicBufSize !!!
-+
-+finishMode:
-+ It has meaning only if the decoding reaches output limit (dicLimit).
-+ LZMA_FINISH_ANY - Decode just dicLimit bytes.
-+ LZMA_FINISH_END - Stream must be finished after dicLimit.
-+
-+Returns:
-+ SZ_OK
-+ status:
-+ LZMA_STATUS_FINISHED_WITH_MARK
-+ LZMA_STATUS_NOT_FINISHED
-+ LZMA_STATUS_NEEDS_MORE_INPUT
-+ LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK
-+ SZ_ERROR_DATA - Data error
-+*/
-+
-+SRes LzmaDec_DecodeToDic(CLzmaDec *p, SizeT dicLimit,
-+ const Byte *src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status);
-+
-+
-+/* ---------- Buffer Interface ---------- */
-+
-+/* It's zlib-like interface.
-+ See LzmaDec_DecodeToDic description for information about STEPS and return results,
-+ but you must use LzmaDec_DecodeToBuf instead of LzmaDec_DecodeToDic and you don't need
-+ to work with CLzmaDec variables manually.
-+
-+finishMode:
-+ It has meaning only if the decoding reaches output limit (*destLen).
-+ LZMA_FINISH_ANY - Decode just destLen bytes.
-+ LZMA_FINISH_END - Stream must be finished after (*destLen).
-+*/
-+
-+SRes LzmaDec_DecodeToBuf(CLzmaDec *p, Byte *dest, SizeT *destLen,
-+ const Byte *src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status);
-+
-+
-+/* ---------- One Call Interface ---------- */
-+
-+/* LzmaDecode
-+
-+finishMode:
-+ It has meaning only if the decoding reaches output limit (*destLen).
-+ LZMA_FINISH_ANY - Decode just destLen bytes.
-+ LZMA_FINISH_END - Stream must be finished after (*destLen).
-+
-+Returns:
-+ SZ_OK
-+ status:
-+ LZMA_STATUS_FINISHED_WITH_MARK
-+ LZMA_STATUS_NOT_FINISHED
-+ LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK
-+ SZ_ERROR_DATA - Data error
-+ SZ_ERROR_MEM - Memory allocation error
-+ SZ_ERROR_UNSUPPORTED - Unsupported properties
-+ SZ_ERROR_INPUT_EOF - It needs more bytes in input buffer (src).
-+*/
-+
-+SRes LzmaDecode(Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen,
-+ const Byte *propData, unsigned propSize, ELzmaFinishMode finishMode,
-+ ELzmaStatus *status, ISzAlloc *alloc);
-+
-+#endif
---- /dev/null
-+++ b/include/linux/lzma/LzmaEnc.h
-@@ -0,0 +1,74 @@
-+/* LzmaEnc.h -- LZMA Encoder
-+2008-04-27
-+Copyright (c) 1999-2008 Igor Pavlov
-+Read LzFind.h for license options */
-+
-+#ifndef __LZMAENC_H
-+#define __LZMAENC_H
-+
-+#include "Types.h"
-+
-+#define LZMA_PROPS_SIZE 5
-+
-+typedef struct _CLzmaEncProps
-+{
-+ int level; /* 0 <= level <= 9 */
-+ UInt32 dictSize; /* (1 << 12) <= dictSize <= (1 << 27) for 32-bit version
-+ (1 << 12) <= dictSize <= (1 << 30) for 64-bit version
-+ default = (1 << 24) */
-+ int lc; /* 0 <= lc <= 8, default = 3 */
-+ int lp; /* 0 <= lp <= 4, default = 0 */
-+ int pb; /* 0 <= pb <= 4, default = 2 */
-+ int algo; /* 0 - fast, 1 - normal, default = 1 */
-+ int fb; /* 5 <= fb <= 273, default = 32 */
-+ int btMode; /* 0 - hashChain Mode, 1 - binTree mode - normal, default = 1 */
-+ int numHashBytes; /* 2, 3 or 4, default = 4 */
-+ UInt32 mc; /* 1 <= mc <= (1 << 30), default = 32 */
-+ unsigned writeEndMark; /* 0 - do not write EOPM, 1 - write EOPM, default = 0 */
-+ int numThreads; /* 1 or 2, default = 2 */
-+} CLzmaEncProps;
-+
-+void LzmaEncProps_Init(CLzmaEncProps *p);
-+void LzmaEncProps_Normalize(CLzmaEncProps *p);
-+UInt32 LzmaEncProps_GetDictSize(const CLzmaEncProps *props2);
-+
-+
-+/* ---------- CLzmaEncHandle Interface ---------- */
-+
-+/* LzmaEnc_* functions can return the following exit codes:
-+Returns:
-+ SZ_OK - OK
-+ SZ_ERROR_MEM - Memory allocation error
-+ SZ_ERROR_PARAM - Incorrect paramater in props
-+ SZ_ERROR_WRITE - Write callback error.
-+ SZ_ERROR_PROGRESS - some break from progress callback
-+ SZ_ERROR_THREAD - errors in multithreading functions (only for Mt version)
-+*/
-+
-+typedef void * CLzmaEncHandle;
-+
-+CLzmaEncHandle LzmaEnc_Create(ISzAlloc *alloc);
-+void LzmaEnc_Destroy(CLzmaEncHandle p, ISzAlloc *alloc, ISzAlloc *allocBig);
-+SRes LzmaEnc_SetProps(CLzmaEncHandle p, const CLzmaEncProps *props);
-+SRes LzmaEnc_WriteProperties(CLzmaEncHandle p, Byte *properties, SizeT *size);
-+SRes LzmaEnc_Encode(CLzmaEncHandle p, ISeqOutStream *outStream, ISeqInStream *inStream,
-+ ICompressProgress *progress, ISzAlloc *alloc, ISzAlloc *allocBig);
-+SRes LzmaEnc_MemEncode(CLzmaEncHandle p, Byte *dest, SizeT *destLen, const Byte *src, SizeT srcLen,
-+ int writeEndMark, ICompressProgress *progress, ISzAlloc *alloc, ISzAlloc *allocBig);
-+
-+/* ---------- One Call Interface ---------- */
-+
-+/* LzmaEncode
-+Return code:
-+ SZ_OK - OK
-+ SZ_ERROR_MEM - Memory allocation error
-+ SZ_ERROR_PARAM - Incorrect paramater
-+ SZ_ERROR_OUTPUT_EOF - output buffer overflow
-+ SZ_ERROR_THREAD - errors in multithreading functions (only for Mt version)
-+*/
-+
-+SRes LzmaEncode(Byte *dest, SizeT *destLen, const Byte *src, SizeT srcLen,
-+ const CLzmaEncProps *props, Byte *propsEncoded, SizeT *propsSize, int writeEndMark,
-+ ICompressProgress *progress, ISzAlloc *alloc, ISzAlloc *allocBig);
-+
-+#endif
---- /dev/null
-+++ b/include/linux/lzma/Types.h
-@@ -0,0 +1,130 @@
-+/* Types.h -- Basic types
-+2008-04-11
-+Igor Pavlov
-+Public domain */
-+
-+#ifndef __7Z_TYPES_H
-+#define __7Z_TYPES_H
-+
-+#define SZ_OK 0
-+
-+#define SZ_ERROR_DATA 1
-+#define SZ_ERROR_MEM 2
-+#define SZ_ERROR_CRC 3
-+#define SZ_ERROR_UNSUPPORTED 4
-+#define SZ_ERROR_PARAM 5
-+#define SZ_ERROR_INPUT_EOF 6
-+#define SZ_ERROR_OUTPUT_EOF 7
-+#define SZ_ERROR_READ 8
-+#define SZ_ERROR_WRITE 9
-+#define SZ_ERROR_PROGRESS 10
-+#define SZ_ERROR_FAIL 11
-+#define SZ_ERROR_THREAD 12
-+
-+#define SZ_ERROR_ARCHIVE 16
-+#define SZ_ERROR_NO_ARCHIVE 17
-+
-+typedef int SRes;
-+
-+#ifndef RINOK
-+#define RINOK(x) { int __result__ = (x); if (__result__ != 0) return __result__; }
-+#endif
-+
-+typedef unsigned char Byte;
-+typedef short Int16;
-+typedef unsigned short UInt16;
-+
-+#ifdef _LZMA_UINT32_IS_ULONG
-+typedef long Int32;
-+typedef unsigned long UInt32;
-+#else
-+typedef int Int32;
-+typedef unsigned int UInt32;
-+#endif
-+
-+/* #define _SZ_NO_INT_64 */
-+/* define it if your compiler doesn't support 64-bit integers */
-+
-+#ifdef _SZ_NO_INT_64
-+
-+typedef long Int64;
-+typedef unsigned long UInt64;
-+
-+#else
-+
-+#if defined(_MSC_VER) || defined(__BORLANDC__)
-+typedef __int64 Int64;
-+typedef unsigned __int64 UInt64;
-+#else
-+typedef long long int Int64;
-+typedef unsigned long long int UInt64;
-+#endif
-+
-+#endif
-+
-+#ifdef _LZMA_NO_SYSTEM_SIZE_T
-+typedef UInt32 SizeT;
-+#else
-+#include <stddef.h>
-+typedef size_t SizeT;
-+#endif
-+
-+typedef int Bool;
-+#define True 1
-+#define False 0
-+
-+
-+#ifdef _MSC_VER
-+
-+#if _MSC_VER >= 1300
-+#define MY_NO_INLINE __declspec(noinline)
-+#else
-+#define MY_NO_INLINE
-+#endif
-+
-+#define MY_CDECL __cdecl
-+#define MY_STD_CALL __stdcall
-+#define MY_FAST_CALL MY_NO_INLINE __fastcall
-+
-+#else
-+
-+#define MY_CDECL
-+#define MY_STD_CALL
-+#define MY_FAST_CALL
-+
-+#endif
-+
-+
-+/* The following interfaces use first parameter as pointer to structure */
-+
-+typedef struct
-+{
-+ SRes (*Read)(void *p, void *buf, size_t *size);
-+ /* if (input(*size) != 0 && output(*size) == 0) means end_of_stream.
-+ (output(*size) < input(*size)) is allowed */
-+} ISeqInStream;
-+
-+typedef struct
-+{
-+ size_t (*Write)(void *p, const void *buf, size_t size);
-+ /* Returns: result - the number of actually written bytes.
-+ (result < size) means error */
-+} ISeqOutStream;
-+
-+typedef struct
-+{
-+ SRes (*Progress)(void *p, UInt64 inSize, UInt64 outSize);
-+ /* Returns: result. (result != SZ_OK) means break.
-+ Value (UInt64)(Int64)-1 for size means unknown value. */
-+} ICompressProgress;
-+
-+typedef struct
-+{
-+ void *(*Alloc)(void *p, size_t size);
-+ void (*Free)(void *p, void *address); /* address can be 0 */
-+} ISzAlloc;
-+
-+#define IAlloc_Alloc(p, size) (p)->Alloc((p), size)
-+#define IAlloc_Free(p, a) (p)->Free((p), a)
-+
-+#endif
---- /dev/null
-+++ b/jffsX-utils/lzma/LzFind.c
-@@ -0,0 +1,753 @@
-+/* LzFind.c -- Match finder for LZ algorithms
-+2008-04-04
-+Copyright (c) 1999-2008 Igor Pavlov
-+Read LzFind.h for license options */
-+
-+#include <string.h>
-+
-+#include "LzFind.h"
-+#include "LzHash.h"
-+
-+#define kEmptyHashValue 0
-+#define kMaxValForNormalize ((UInt32)0xFFFFFFFF)
-+#define kNormalizeStepMin (1 << 10) /* it must be power of 2 */
-+#define kNormalizeMask (~(kNormalizeStepMin - 1))
-+#define kMaxHistorySize ((UInt32)3 << 30)
-+
-+#define kStartMaxLen 3
-+
-+static void LzInWindow_Free(CMatchFinder *p, ISzAlloc *alloc)
-+{
-+ if (!p->directInput)
-+ {
-+ alloc->Free(alloc, p->bufferBase);
-+ p->bufferBase = 0;
-+ }
-+}
-+
-+/* keepSizeBefore + keepSizeAfter + keepSizeReserv must be < 4G) */
-+
-+static int LzInWindow_Create(CMatchFinder *p, UInt32 keepSizeReserv, ISzAlloc *alloc)
-+{
-+ UInt32 blockSize = p->keepSizeBefore + p->keepSizeAfter + keepSizeReserv;
-+ if (p->directInput)
-+ {
-+ p->blockSize = blockSize;
-+ return 1;
-+ }
-+ if (p->bufferBase == 0 || p->blockSize != blockSize)
-+ {
-+ LzInWindow_Free(p, alloc);
-+ p->blockSize = blockSize;
-+ p->bufferBase = (Byte *)alloc->Alloc(alloc, (size_t)blockSize);
-+ }
-+ return (p->bufferBase != 0);
-+}
-+
-+Byte *MatchFinder_GetPointerToCurrentPos(CMatchFinder *p) { return p->buffer; }
-+static Byte MatchFinder_GetIndexByte(CMatchFinder *p, Int32 index) { return p->buffer[index]; }
-+
-+static UInt32 MatchFinder_GetNumAvailableBytes(CMatchFinder *p) { return p->streamPos - p->pos; }
-+
-+void MatchFinder_ReduceOffsets(CMatchFinder *p, UInt32 subValue)
-+{
-+ p->posLimit -= subValue;
-+ p->pos -= subValue;
-+ p->streamPos -= subValue;
-+}
-+
-+static void MatchFinder_ReadBlock(CMatchFinder *p)
-+{
-+ if (p->streamEndWasReached || p->result != SZ_OK)
-+ return;
-+ for (;;)
-+ {
-+ Byte *dest = p->buffer + (p->streamPos - p->pos);
-+ size_t size = (p->bufferBase + p->blockSize - dest);
-+ if (size == 0)
-+ return;
-+ p->result = p->stream->Read(p->stream, dest, &size);
-+ if (p->result != SZ_OK)
-+ return;
-+ if (size == 0)
-+ {
-+ p->streamEndWasReached = 1;
-+ return;
-+ }
-+ p->streamPos += (UInt32)size;
-+ if (p->streamPos - p->pos > p->keepSizeAfter)
-+ return;
-+ }
-+}
-+
-+void MatchFinder_MoveBlock(CMatchFinder *p)
-+{
-+ memmove(p->bufferBase,
-+ p->buffer - p->keepSizeBefore,
-+ (size_t)(p->streamPos - p->pos + p->keepSizeBefore));
-+ p->buffer = p->bufferBase + p->keepSizeBefore;
-+}
-+
-+int MatchFinder_NeedMove(CMatchFinder *p)
-+{
-+ /* if (p->streamEndWasReached) return 0; */
-+ return ((size_t)(p->bufferBase + p->blockSize - p->buffer) <= p->keepSizeAfter);
-+}
-+
-+void MatchFinder_ReadIfRequired(CMatchFinder *p)
-+{
-+ if (p->streamEndWasReached)
-+ return;
-+ if (p->keepSizeAfter >= p->streamPos - p->pos)
-+ MatchFinder_ReadBlock(p);
-+}
-+
-+static void MatchFinder_CheckAndMoveAndRead(CMatchFinder *p)
-+{
-+ if (MatchFinder_NeedMove(p))
-+ MatchFinder_MoveBlock(p);
-+ MatchFinder_ReadBlock(p);
-+}
-+
-+static void MatchFinder_SetDefaultSettings(CMatchFinder *p)
-+{
-+ p->cutValue = 32;
-+ p->btMode = 1;
-+ p->numHashBytes = 4;
-+ /* p->skipModeBits = 0; */
-+ p->directInput = 0;
-+ p->bigHash = 0;
-+}
-+
-+#define kCrcPoly 0xEDB88320
-+
-+void MatchFinder_Construct(CMatchFinder *p)
-+{
-+ UInt32 i;
-+ p->bufferBase = 0;
-+ p->directInput = 0;
-+ p->hash = 0;
-+ MatchFinder_SetDefaultSettings(p);
-+
-+ for (i = 0; i < 256; i++)
-+ {
-+ UInt32 r = i;
-+ int j;
-+ for (j = 0; j < 8; j++)
-+ r = (r >> 1) ^ (kCrcPoly & ~((r & 1) - 1));
-+ p->crc[i] = r;
-+ }
-+}
-+
-+static void MatchFinder_FreeThisClassMemory(CMatchFinder *p, ISzAlloc *alloc)
-+{
-+ alloc->Free(alloc, p->hash);
-+ p->hash = 0;
-+}
-+
-+void MatchFinder_Free(CMatchFinder *p, ISzAlloc *alloc)
-+{
-+ MatchFinder_FreeThisClassMemory(p, alloc);
-+ LzInWindow_Free(p, alloc);
-+}
-+
-+static CLzRef* AllocRefs(UInt32 num, ISzAlloc *alloc)
-+{
-+ size_t sizeInBytes = (size_t)num * sizeof(CLzRef);
-+ if (sizeInBytes / sizeof(CLzRef) != num)
-+ return 0;
-+ return (CLzRef *)alloc->Alloc(alloc, sizeInBytes);
-+}
-+
-+int MatchFinder_Create(CMatchFinder *p, UInt32 historySize,
-+ UInt32 keepAddBufferBefore, UInt32 matchMaxLen, UInt32 keepAddBufferAfter,
-+ ISzAlloc *alloc)
-+{
-+ UInt32 sizeReserv;
-+ if (historySize > kMaxHistorySize)
-+ {
-+ MatchFinder_Free(p, alloc);
-+ return 0;
-+ }
-+ sizeReserv = historySize >> 1;
-+ if (historySize > ((UInt32)2 << 30))
-+ sizeReserv = historySize >> 2;
-+ sizeReserv += (keepAddBufferBefore + matchMaxLen + keepAddBufferAfter) / 2 + (1 << 19);
-+
-+ p->keepSizeBefore = historySize + keepAddBufferBefore + 1;
-+ p->keepSizeAfter = matchMaxLen + keepAddBufferAfter;
-+ /* we need one additional byte, since we use MoveBlock after pos++ and before dictionary using */
-+ if (LzInWindow_Create(p, sizeReserv, alloc))
-+ {
-+ UInt32 newCyclicBufferSize = (historySize /* >> p->skipModeBits */) + 1;
-+ UInt32 hs;
-+ p->matchMaxLen = matchMaxLen;
-+ {
-+ p->fixedHashSize = 0;
-+ if (p->numHashBytes == 2)
-+ hs = (1 << 16) - 1;
-+ else
-+ {
-+ hs = historySize - 1;
-+ hs |= (hs >> 1);
-+ hs |= (hs >> 2);
-+ hs |= (hs >> 4);
-+ hs |= (hs >> 8);
-+ hs >>= 1;
-+ /* hs >>= p->skipModeBits; */
-+ hs |= 0xFFFF; /* don't change it! It's required for Deflate */
-+ if (hs > (1 << 24))
-+ {
-+ if (p->numHashBytes == 3)
-+ hs = (1 << 24) - 1;
-+ else
-+ hs >>= 1;
-+ }
-+ }
-+ p->hashMask = hs;
-+ hs++;
-+ if (p->numHashBytes > 2) p->fixedHashSize += kHash2Size;
-+ if (p->numHashBytes > 3) p->fixedHashSize += kHash3Size;
-+ if (p->numHashBytes > 4) p->fixedHashSize += kHash4Size;
-+ hs += p->fixedHashSize;
-+ }
-+
-+ {
-+ UInt32 prevSize = p->hashSizeSum + p->numSons;
-+ UInt32 newSize;
-+ p->historySize = historySize;
-+ p->hashSizeSum = hs;
-+ p->cyclicBufferSize = newCyclicBufferSize;
-+ p->numSons = (p->btMode ? newCyclicBufferSize * 2 : newCyclicBufferSize);
-+ newSize = p->hashSizeSum + p->numSons;
-+ if (p->hash != 0 && prevSize == newSize)
-+ return 1;
-+ MatchFinder_FreeThisClassMemory(p, alloc);
-+ p->hash = AllocRefs(newSize, alloc);
-+ if (p->hash != 0)
-+ {
-+ p->son = p->hash + p->hashSizeSum;
-+ return 1;
-+ }
-+ }
-+ }
-+ MatchFinder_Free(p, alloc);
-+ return 0;
-+}
-+
-+static void MatchFinder_SetLimits(CMatchFinder *p)
-+{
-+ UInt32 limit = kMaxValForNormalize - p->pos;
-+ UInt32 limit2 = p->cyclicBufferSize - p->cyclicBufferPos;
-+ if (limit2 < limit)
-+ limit = limit2;
-+ limit2 = p->streamPos - p->pos;
-+ if (limit2 <= p->keepSizeAfter)
-+ {
-+ if (limit2 > 0)
-+ limit2 = 1;
-+ }
-+ else
-+ limit2 -= p->keepSizeAfter;
-+ if (limit2 < limit)
-+ limit = limit2;
-+ {
-+ UInt32 lenLimit = p->streamPos - p->pos;
-+ if (lenLimit > p->matchMaxLen)
-+ lenLimit = p->matchMaxLen;
-+ p->lenLimit = lenLimit;
-+ }
-+ p->posLimit = p->pos + limit;
-+}
-+
-+void MatchFinder_Init(CMatchFinder *p)
-+{
-+ UInt32 i;
-+ for(i = 0; i < p->hashSizeSum; i++)
-+ p->hash[i] = kEmptyHashValue;
-+ p->cyclicBufferPos = 0;
-+ p->buffer = p->bufferBase;
-+ p->pos = p->streamPos = p->cyclicBufferSize;
-+ p->result = SZ_OK;
-+ p->streamEndWasReached = 0;
-+ MatchFinder_ReadBlock(p);
-+ MatchFinder_SetLimits(p);
-+}
-+
-+static UInt32 MatchFinder_GetSubValue(CMatchFinder *p)
-+{
-+ return (p->pos - p->historySize - 1) & kNormalizeMask;
-+}
-+
-+void MatchFinder_Normalize3(UInt32 subValue, CLzRef *items, UInt32 numItems)
-+{
-+ UInt32 i;
-+ for (i = 0; i < numItems; i++)
-+ {
-+ UInt32 value = items[i];
-+ if (value <= subValue)
-+ value = kEmptyHashValue;
-+ else
-+ value -= subValue;
-+ items[i] = value;
-+ }
-+}
-+
-+static void MatchFinder_Normalize(CMatchFinder *p)
-+{
-+ UInt32 subValue = MatchFinder_GetSubValue(p);
-+ MatchFinder_Normalize3(subValue, p->hash, p->hashSizeSum + p->numSons);
-+ MatchFinder_ReduceOffsets(p, subValue);
-+}
-+
-+static void MatchFinder_CheckLimits(CMatchFinder *p)
-+{
-+ if (p->pos == kMaxValForNormalize)
-+ MatchFinder_Normalize(p);
-+ if (!p->streamEndWasReached && p->keepSizeAfter == p->streamPos - p->pos)
-+ MatchFinder_CheckAndMoveAndRead(p);
-+ if (p->cyclicBufferPos == p->cyclicBufferSize)
-+ p->cyclicBufferPos = 0;
-+ MatchFinder_SetLimits(p);
-+}
-+
-+static UInt32 * Hc_GetMatchesSpec(UInt32 lenLimit, UInt32 curMatch, UInt32 pos, const Byte *cur, CLzRef *son,
-+ UInt32 _cyclicBufferPos, UInt32 _cyclicBufferSize, UInt32 cutValue,
-+ UInt32 *distances, UInt32 maxLen)
-+{
-+ son[_cyclicBufferPos] = curMatch;
-+ for (;;)
-+ {
-+ UInt32 delta = pos - curMatch;
-+ if (cutValue-- == 0 || delta >= _cyclicBufferSize)
-+ return distances;
-+ {
-+ const Byte *pb = cur - delta;
-+ curMatch = son[_cyclicBufferPos - delta + ((delta > _cyclicBufferPos) ? _cyclicBufferSize : 0)];
-+ if (pb[maxLen] == cur[maxLen] && *pb == *cur)
-+ {
-+ UInt32 len = 0;
-+ while(++len != lenLimit)
-+ if (pb[len] != cur[len])
-+ break;
-+ if (maxLen < len)
-+ {
-+ *distances++ = maxLen = len;
-+ *distances++ = delta - 1;
-+ if (len == lenLimit)
-+ return distances;
-+ }
-+ }
-+ }
-+ }
-+}
-+
-+UInt32 * GetMatchesSpec1(UInt32 lenLimit, UInt32 curMatch, UInt32 pos, const Byte *cur, CLzRef *son,
-+ UInt32 _cyclicBufferPos, UInt32 _cyclicBufferSize, UInt32 cutValue,
-+ UInt32 *distances, UInt32 maxLen)
-+{
-+ CLzRef *ptr0 = son + (_cyclicBufferPos << 1) + 1;
-+ CLzRef *ptr1 = son + (_cyclicBufferPos << 1);
-+ UInt32 len0 = 0, len1 = 0;
-+ for (;;)
-+ {
-+ UInt32 delta = pos - curMatch;
-+ if (cutValue-- == 0 || delta >= _cyclicBufferSize)
-+ {
-+ *ptr0 = *ptr1 = kEmptyHashValue;
-+ return distances;
-+ }
-+ {
-+ CLzRef *pair = son + ((_cyclicBufferPos - delta + ((delta > _cyclicBufferPos) ? _cyclicBufferSize : 0)) << 1);
-+ const Byte *pb = cur - delta;
-+ UInt32 len = (len0 < len1 ? len0 : len1);
-+ if (pb[len] == cur[len])
-+ {
-+ if (++len != lenLimit && pb[len] == cur[len])
-+ while(++len != lenLimit)
-+ if (pb[len] != cur[len])
-+ break;
-+ if (maxLen < len)
-+ {
-+ *distances++ = maxLen = len;
-+ *distances++ = delta - 1;
-+ if (len == lenLimit)
-+ {
-+ *ptr1 = pair[0];
-+ *ptr0 = pair[1];
-+ return distances;
-+ }
-+ }
-+ }
-+ if (pb[len] < cur[len])
-+ {
-+ *ptr1 = curMatch;
-+ ptr1 = pair + 1;
-+ curMatch = *ptr1;
-+ len1 = len;
-+ }
-+ else
-+ {
-+ *ptr0 = curMatch;
-+ ptr0 = pair;
-+ curMatch = *ptr0;
-+ len0 = len;
-+ }
-+ }
-+ }
-+}
-+
-+static void SkipMatchesSpec(UInt32 lenLimit, UInt32 curMatch, UInt32 pos, const Byte *cur, CLzRef *son,
-+ UInt32 _cyclicBufferPos, UInt32 _cyclicBufferSize, UInt32 cutValue)
-+{
-+ CLzRef *ptr0 = son + (_cyclicBufferPos << 1) + 1;
-+ CLzRef *ptr1 = son + (_cyclicBufferPos << 1);
-+ UInt32 len0 = 0, len1 = 0;
-+ for (;;)
-+ {
-+ UInt32 delta = pos - curMatch;
-+ if (cutValue-- == 0 || delta >= _cyclicBufferSize)
-+ {
-+ *ptr0 = *ptr1 = kEmptyHashValue;
-+ return;
-+ }
-+ {
-+ CLzRef *pair = son + ((_cyclicBufferPos - delta + ((delta > _cyclicBufferPos) ? _cyclicBufferSize : 0)) << 1);
-+ const Byte *pb = cur - delta;
-+ UInt32 len = (len0 < len1 ? len0 : len1);
-+ if (pb[len] == cur[len])
-+ {
-+ while(++len != lenLimit)
-+ if (pb[len] != cur[len])
-+ break;
-+ {
-+ if (len == lenLimit)
-+ {
-+ *ptr1 = pair[0];
-+ *ptr0 = pair[1];
-+ return;
-+ }
-+ }
-+ }
-+ if (pb[len] < cur[len])
-+ {
-+ *ptr1 = curMatch;
-+ ptr1 = pair + 1;
-+ curMatch = *ptr1;
-+ len1 = len;
-+ }
-+ else
-+ {
-+ *ptr0 = curMatch;
-+ ptr0 = pair;
-+ curMatch = *ptr0;
-+ len0 = len;
-+ }
-+ }
-+ }
-+}
-+
-+#define MOVE_POS \
-+ ++p->cyclicBufferPos; \
-+ p->buffer++; \
-+ if (++p->pos == p->posLimit) MatchFinder_CheckLimits(p);
-+
-+#define MOVE_POS_RET MOVE_POS return offset;
-+
-+static void MatchFinder_MovePos(CMatchFinder *p) { MOVE_POS; }
-+
-+#define GET_MATCHES_HEADER2(minLen, ret_op) \
-+ UInt32 lenLimit; UInt32 hashValue; const Byte *cur; UInt32 curMatch; \
-+ lenLimit = p->lenLimit; { if (lenLimit < minLen) { MatchFinder_MovePos(p); ret_op; }} \
-+ cur = p->buffer;
-+
-+#define GET_MATCHES_HEADER(minLen) GET_MATCHES_HEADER2(minLen, return 0)
-+#define SKIP_HEADER(minLen) GET_MATCHES_HEADER2(minLen, continue)
-+
-+#define MF_PARAMS(p) p->pos, p->buffer, p->son, p->cyclicBufferPos, p->cyclicBufferSize, p->cutValue
-+
-+#define GET_MATCHES_FOOTER(offset, maxLen) \
-+ offset = (UInt32)(GetMatchesSpec1(lenLimit, curMatch, MF_PARAMS(p), \
-+ distances + offset, maxLen) - distances); MOVE_POS_RET;
-+
-+#define SKIP_FOOTER \
-+ SkipMatchesSpec(lenLimit, curMatch, MF_PARAMS(p)); MOVE_POS;
-+
-+static UInt32 Bt2_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances)
-+{
-+ UInt32 offset;
-+ GET_MATCHES_HEADER(2)
-+ HASH2_CALC;
-+ curMatch = p->hash[hashValue];
-+ p->hash[hashValue] = p->pos;
-+ offset = 0;
-+ GET_MATCHES_FOOTER(offset, 1)
-+}
-+
-+UInt32 Bt3Zip_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances)
-+{
-+ UInt32 offset;
-+ GET_MATCHES_HEADER(3)
-+ HASH_ZIP_CALC;
-+ curMatch = p->hash[hashValue];
-+ p->hash[hashValue] = p->pos;
-+ offset = 0;
-+ GET_MATCHES_FOOTER(offset, 2)
-+}
-+
-+static UInt32 Bt3_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances)
-+{
-+ UInt32 hash2Value, delta2, maxLen, offset;
-+ GET_MATCHES_HEADER(3)
-+
-+ HASH3_CALC;
-+
-+ delta2 = p->pos - p->hash[hash2Value];
-+ curMatch = p->hash[kFix3HashSize + hashValue];
-+
-+ p->hash[hash2Value] =
-+ p->hash[kFix3HashSize + hashValue] = p->pos;
-+
-+
-+ maxLen = 2;
-+ offset = 0;
-+ if (delta2 < p->cyclicBufferSize && *(cur - delta2) == *cur)
-+ {
-+ for (; maxLen != lenLimit; maxLen++)
-+ if (cur[(ptrdiff_t)maxLen - delta2] != cur[maxLen])
-+ break;
-+ distances[0] = maxLen;
-+ distances[1] = delta2 - 1;
-+ offset = 2;
-+ if (maxLen == lenLimit)
-+ {
-+ SkipMatchesSpec(lenLimit, curMatch, MF_PARAMS(p));
-+ MOVE_POS_RET;
-+ }
-+ }
-+ GET_MATCHES_FOOTER(offset, maxLen)
-+}
-+
-+static UInt32 Bt4_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances)
-+{
-+ UInt32 hash2Value, hash3Value, delta2, delta3, maxLen, offset;
-+ GET_MATCHES_HEADER(4)
-+
-+ HASH4_CALC;
-+
-+ delta2 = p->pos - p->hash[ hash2Value];
-+ delta3 = p->pos - p->hash[kFix3HashSize + hash3Value];
-+ curMatch = p->hash[kFix4HashSize + hashValue];
-+
-+ p->hash[ hash2Value] =
-+ p->hash[kFix3HashSize + hash3Value] =
-+ p->hash[kFix4HashSize + hashValue] = p->pos;
-+
-+ maxLen = 1;
-+ offset = 0;
-+ if (delta2 < p->cyclicBufferSize && *(cur - delta2) == *cur)
-+ {
-+ distances[0] = maxLen = 2;
-+ distances[1] = delta2 - 1;
-+ offset = 2;
-+ }
-+ if (delta2 != delta3 && delta3 < p->cyclicBufferSize && *(cur - delta3) == *cur)
-+ {
-+ maxLen = 3;
-+ distances[offset + 1] = delta3 - 1;
-+ offset += 2;
-+ delta2 = delta3;
-+ }
-+ if (offset != 0)
-+ {
-+ for (; maxLen != lenLimit; maxLen++)
-+ if (cur[(ptrdiff_t)maxLen - delta2] != cur[maxLen])
-+ break;
-+ distances[offset - 2] = maxLen;
-+ if (maxLen == lenLimit)
-+ {
-+ SkipMatchesSpec(lenLimit, curMatch, MF_PARAMS(p));
-+ MOVE_POS_RET;
-+ }
-+ }
-+ if (maxLen < 3)
-+ maxLen = 3;
-+ GET_MATCHES_FOOTER(offset, maxLen)
-+}
-+
-+static UInt32 Hc4_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances)
-+{
-+ UInt32 hash2Value, hash3Value, delta2, delta3, maxLen, offset;
-+ GET_MATCHES_HEADER(4)
-+
-+ HASH4_CALC;
-+
-+ delta2 = p->pos - p->hash[ hash2Value];
-+ delta3 = p->pos - p->hash[kFix3HashSize + hash3Value];
-+ curMatch = p->hash[kFix4HashSize + hashValue];
-+
-+ p->hash[ hash2Value] =
-+ p->hash[kFix3HashSize + hash3Value] =
-+ p->hash[kFix4HashSize + hashValue] = p->pos;
-+
-+ maxLen = 1;
-+ offset = 0;
-+ if (delta2 < p->cyclicBufferSize && *(cur - delta2) == *cur)
-+ {
-+ distances[0] = maxLen = 2;
-+ distances[1] = delta2 - 1;
-+ offset = 2;
-+ }
-+ if (delta2 != delta3 && delta3 < p->cyclicBufferSize && *(cur - delta3) == *cur)
-+ {
-+ maxLen = 3;
-+ distances[offset + 1] = delta3 - 1;
-+ offset += 2;
-+ delta2 = delta3;
-+ }
-+ if (offset != 0)
-+ {
-+ for (; maxLen != lenLimit; maxLen++)
-+ if (cur[(ptrdiff_t)maxLen - delta2] != cur[maxLen])
-+ break;
-+ distances[offset - 2] = maxLen;
-+ if (maxLen == lenLimit)
-+ {
-+ p->son[p->cyclicBufferPos] = curMatch;
-+ MOVE_POS_RET;
-+ }
-+ }
-+ if (maxLen < 3)
-+ maxLen = 3;
-+ offset = (UInt32)(Hc_GetMatchesSpec(lenLimit, curMatch, MF_PARAMS(p),
-+ distances + offset, maxLen) - (distances));
-+ MOVE_POS_RET
-+}
-+
-+UInt32 Hc3Zip_MatchFinder_GetMatches(CMatchFinder *p, UInt32 *distances)
-+{
-+ UInt32 offset;
-+ GET_MATCHES_HEADER(3)
-+ HASH_ZIP_CALC;
-+ curMatch = p->hash[hashValue];
-+ p->hash[hashValue] = p->pos;
-+ offset = (UInt32)(Hc_GetMatchesSpec(lenLimit, curMatch, MF_PARAMS(p),
-+ distances, 2) - (distances));
-+ MOVE_POS_RET
-+}
-+
-+static void Bt2_MatchFinder_Skip(CMatchFinder *p, UInt32 num)
-+{
-+ do
-+ {
-+ SKIP_HEADER(2)
-+ HASH2_CALC;
-+ curMatch = p->hash[hashValue];
-+ p->hash[hashValue] = p->pos;
-+ SKIP_FOOTER
-+ }
-+ while (--num != 0);
-+}
-+
-+void Bt3Zip_MatchFinder_Skip(CMatchFinder *p, UInt32 num)
-+{
-+ do
-+ {
-+ SKIP_HEADER(3)
-+ HASH_ZIP_CALC;
-+ curMatch = p->hash[hashValue];
-+ p->hash[hashValue] = p->pos;
-+ SKIP_FOOTER
-+ }
-+ while (--num != 0);
-+}
-+
-+static void Bt3_MatchFinder_Skip(CMatchFinder *p, UInt32 num)
-+{
-+ do
-+ {
-+ UInt32 hash2Value;
-+ SKIP_HEADER(3)
-+ HASH3_CALC;
-+ curMatch = p->hash[kFix3HashSize + hashValue];
-+ p->hash[hash2Value] =
-+ p->hash[kFix3HashSize + hashValue] = p->pos;
-+ SKIP_FOOTER
-+ }
-+ while (--num != 0);
-+}
-+
-+static void Bt4_MatchFinder_Skip(CMatchFinder *p, UInt32 num)
-+{
-+ do
-+ {
-+ UInt32 hash2Value, hash3Value;
-+ SKIP_HEADER(4)
-+ HASH4_CALC;
-+ curMatch = p->hash[kFix4HashSize + hashValue];
-+ p->hash[ hash2Value] =
-+ p->hash[kFix3HashSize + hash3Value] = p->pos;
-+ p->hash[kFix4HashSize + hashValue] = p->pos;
-+ SKIP_FOOTER
-+ }
-+ while (--num != 0);
-+}
-+
-+static void Hc4_MatchFinder_Skip(CMatchFinder *p, UInt32 num)
-+{
-+ do
-+ {
-+ UInt32 hash2Value, hash3Value;
-+ SKIP_HEADER(4)
-+ HASH4_CALC;
-+ curMatch = p->hash[kFix4HashSize + hashValue];
-+ p->hash[ hash2Value] =
-+ p->hash[kFix3HashSize + hash3Value] =
-+ p->hash[kFix4HashSize + hashValue] = p->pos;
-+ p->son[p->cyclicBufferPos] = curMatch;
-+ MOVE_POS
-+ }
-+ while (--num != 0);
-+}
-+
-+void Hc3Zip_MatchFinder_Skip(CMatchFinder *p, UInt32 num)
-+{
-+ do
-+ {
-+ SKIP_HEADER(3)
-+ HASH_ZIP_CALC;
-+ curMatch = p->hash[hashValue];
-+ p->hash[hashValue] = p->pos;
-+ p->son[p->cyclicBufferPos] = curMatch;
-+ MOVE_POS
-+ }
-+ while (--num != 0);
-+}
-+
-+void MatchFinder_CreateVTable(CMatchFinder *p, IMatchFinder *vTable)
-+{
-+ vTable->Init = (Mf_Init_Func)MatchFinder_Init;
-+ vTable->GetIndexByte = (Mf_GetIndexByte_Func)MatchFinder_GetIndexByte;
-+ vTable->GetNumAvailableBytes = (Mf_GetNumAvailableBytes_Func)MatchFinder_GetNumAvailableBytes;
-+ vTable->GetPointerToCurrentPos = (Mf_GetPointerToCurrentPos_Func)MatchFinder_GetPointerToCurrentPos;
-+ if (!p->btMode)
-+ {
-+ vTable->GetMatches = (Mf_GetMatches_Func)Hc4_MatchFinder_GetMatches;
-+ vTable->Skip = (Mf_Skip_Func)Hc4_MatchFinder_Skip;
-+ }
-+ else if (p->numHashBytes == 2)
-+ {
-+ vTable->GetMatches = (Mf_GetMatches_Func)Bt2_MatchFinder_GetMatches;
-+ vTable->Skip = (Mf_Skip_Func)Bt2_MatchFinder_Skip;
-+ }
-+ else if (p->numHashBytes == 3)
-+ {
-+ vTable->GetMatches = (Mf_GetMatches_Func)Bt3_MatchFinder_GetMatches;
-+ vTable->Skip = (Mf_Skip_Func)Bt3_MatchFinder_Skip;
-+ }
-+ else
-+ {
-+ vTable->GetMatches = (Mf_GetMatches_Func)Bt4_MatchFinder_GetMatches;
-+ vTable->Skip = (Mf_Skip_Func)Bt4_MatchFinder_Skip;
-+ }
-+}
---- /dev/null
-+++ b/jffsX-utils/lzma/LzmaDec.c
-@@ -0,0 +1,1014 @@
-+/* LzmaDec.c -- LZMA Decoder
-+2008-04-29
-+Copyright (c) 1999-2008 Igor Pavlov
-+Read LzmaDec.h for license options */
-+
-+#include "LzmaDec.h"
-+
-+#include <string.h>
-+
-+#define kNumTopBits 24
-+#define kTopValue ((UInt32)1 << kNumTopBits)
-+
-+#define kNumBitModelTotalBits 11
-+#define kBitModelTotal (1 << kNumBitModelTotalBits)
-+#define kNumMoveBits 5
-+
-+#define RC_INIT_SIZE 5
-+
-+#define NORMALIZE if (range < kTopValue) { range <<= 8; code = (code << 8) | (*buf++); }
-+
-+#define IF_BIT_0(p) ttt = *(p); NORMALIZE; bound = (range >> kNumBitModelTotalBits) * ttt; if (code < bound)
-+#define UPDATE_0(p) range = bound; *(p) = (CLzmaProb)(ttt + ((kBitModelTotal - ttt) >> kNumMoveBits));
-+#define UPDATE_1(p) range -= bound; code -= bound; *(p) = (CLzmaProb)(ttt - (ttt >> kNumMoveBits));
-+#define GET_BIT2(p, i, A0, A1) IF_BIT_0(p) \
-+ { UPDATE_0(p); i = (i + i); A0; } else \
-+ { UPDATE_1(p); i = (i + i) + 1; A1; }
-+#define GET_BIT(p, i) GET_BIT2(p, i, ; , ;)
-+
-+#define TREE_GET_BIT(probs, i) { GET_BIT((probs + i), i); }
-+#define TREE_DECODE(probs, limit, i) \
-+ { i = 1; do { TREE_GET_BIT(probs, i); } while (i < limit); i -= limit; }
-+
-+/* #define _LZMA_SIZE_OPT */
-+
-+#ifdef _LZMA_SIZE_OPT
-+#define TREE_6_DECODE(probs, i) TREE_DECODE(probs, (1 << 6), i)
-+#else
-+#define TREE_6_DECODE(probs, i) \
-+ { i = 1; \
-+ TREE_GET_BIT(probs, i); \
-+ TREE_GET_BIT(probs, i); \
-+ TREE_GET_BIT(probs, i); \
-+ TREE_GET_BIT(probs, i); \
-+ TREE_GET_BIT(probs, i); \
-+ TREE_GET_BIT(probs, i); \
-+ i -= 0x40; }
-+#endif
-+
-+#define NORMALIZE_CHECK if (range < kTopValue) { if (buf >= bufLimit) return DUMMY_ERROR; range <<= 8; code = (code << 8) | (*buf++); }
-+
-+#define IF_BIT_0_CHECK(p) ttt = *(p); NORMALIZE_CHECK; bound = (range >> kNumBitModelTotalBits) * ttt; if (code < bound)
-+#define UPDATE_0_CHECK range = bound;
-+#define UPDATE_1_CHECK range -= bound; code -= bound;
-+#define GET_BIT2_CHECK(p, i, A0, A1) IF_BIT_0_CHECK(p) \
-+ { UPDATE_0_CHECK; i = (i + i); A0; } else \
-+ { UPDATE_1_CHECK; i = (i + i) + 1; A1; }
-+#define GET_BIT_CHECK(p, i) GET_BIT2_CHECK(p, i, ; , ;)
-+#define TREE_DECODE_CHECK(probs, limit, i) \
-+ { i = 1; do { GET_BIT_CHECK(probs + i, i) } while(i < limit); i -= limit; }
-+
-+
-+#define kNumPosBitsMax 4
-+#define kNumPosStatesMax (1 << kNumPosBitsMax)
-+
-+#define kLenNumLowBits 3
-+#define kLenNumLowSymbols (1 << kLenNumLowBits)
-+#define kLenNumMidBits 3
-+#define kLenNumMidSymbols (1 << kLenNumMidBits)
-+#define kLenNumHighBits 8
-+#define kLenNumHighSymbols (1 << kLenNumHighBits)
-+
-+#define LenChoice 0
-+#define LenChoice2 (LenChoice + 1)
-+#define LenLow (LenChoice2 + 1)
-+#define LenMid (LenLow + (kNumPosStatesMax << kLenNumLowBits))
-+#define LenHigh (LenMid + (kNumPosStatesMax << kLenNumMidBits))
-+#define kNumLenProbs (LenHigh + kLenNumHighSymbols)
-+
-+
-+#define kNumStates 12
-+#define kNumLitStates 7
-+
-+#define kStartPosModelIndex 4
-+#define kEndPosModelIndex 14
-+#define kNumFullDistances (1 << (kEndPosModelIndex >> 1))
-+
-+#define kNumPosSlotBits 6
-+#define kNumLenToPosStates 4
-+
-+#define kNumAlignBits 4
-+#define kAlignTableSize (1 << kNumAlignBits)
-+
-+#define kMatchMinLen 2
-+#define kMatchSpecLenStart (kMatchMinLen + kLenNumLowSymbols + kLenNumMidSymbols + kLenNumHighSymbols)
-+
-+#define IsMatch 0
-+#define IsRep (IsMatch + (kNumStates << kNumPosBitsMax))
-+#define IsRepG0 (IsRep + kNumStates)
-+#define IsRepG1 (IsRepG0 + kNumStates)
-+#define IsRepG2 (IsRepG1 + kNumStates)
-+#define IsRep0Long (IsRepG2 + kNumStates)
-+#define PosSlot (IsRep0Long + (kNumStates << kNumPosBitsMax))
-+#define SpecPos (PosSlot + (kNumLenToPosStates << kNumPosSlotBits))
-+#define Align (SpecPos + kNumFullDistances - kEndPosModelIndex)
-+#define LenCoder (Align + kAlignTableSize)
-+#define RepLenCoder (LenCoder + kNumLenProbs)
-+#define Literal (RepLenCoder + kNumLenProbs)
-+
-+#define LZMA_BASE_SIZE 1846
-+#define LZMA_LIT_SIZE 768
-+
-+#define LzmaProps_GetNumProbs(p) ((UInt32)LZMA_BASE_SIZE + (LZMA_LIT_SIZE << ((p)->lc + (p)->lp)))
-+
-+#if Literal != LZMA_BASE_SIZE
-+StopCompilingDueBUG
-+#endif
-+
-+/*
-+#define LZMA_STREAM_WAS_FINISHED_ID (-1)
-+#define LZMA_SPEC_LEN_OFFSET (-3)
-+*/
-+
-+Byte kLiteralNextStates[kNumStates * 2] =
-+{
-+ 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 4, 5,
-+ 7, 7, 7, 7, 7, 7, 7, 10, 10, 10, 10, 10
-+};
-+
-+#define LZMA_DIC_MIN (1 << 12)
-+
-+/* First LZMA-symbol is always decoded.
-+And it decodes new LZMA-symbols while (buf < bufLimit), but "buf" is without last normalization
-+Out:
-+ Result:
-+ 0 - OK
-+ 1 - Error
-+ p->remainLen:
-+ < kMatchSpecLenStart : normal remain
-+ = kMatchSpecLenStart : finished
-+ = kMatchSpecLenStart + 1 : Flush marker
-+ = kMatchSpecLenStart + 2 : State Init Marker
-+*/
-+
-+static int MY_FAST_CALL LzmaDec_DecodeReal(CLzmaDec *p, SizeT limit, const Byte *bufLimit)
-+{
-+ CLzmaProb *probs = p->probs;
-+
-+ unsigned state = p->state;
-+ UInt32 rep0 = p->reps[0], rep1 = p->reps[1], rep2 = p->reps[2], rep3 = p->reps[3];
-+ unsigned pbMask = ((unsigned)1 << (p->prop.pb)) - 1;
-+ unsigned lpMask = ((unsigned)1 << (p->prop.lp)) - 1;
-+ unsigned lc = p->prop.lc;
-+
-+ Byte *dic = p->dic;
-+ SizeT dicBufSize = p->dicBufSize;
-+ SizeT dicPos = p->dicPos;
-+
-+ UInt32 processedPos = p->processedPos;
-+ UInt32 checkDicSize = p->checkDicSize;
-+ unsigned len = 0;
-+
-+ const Byte *buf = p->buf;
-+ UInt32 range = p->range;
-+ UInt32 code = p->code;
-+
-+ do
-+ {
-+ CLzmaProb *prob;
-+ UInt32 bound;
-+ unsigned ttt;
-+ unsigned posState = processedPos & pbMask;
-+
-+ prob = probs + IsMatch + (state << kNumPosBitsMax) + posState;
-+ IF_BIT_0(prob)
-+ {
-+ unsigned symbol;
-+ UPDATE_0(prob);
-+ prob = probs + Literal;
-+ if (checkDicSize != 0 || processedPos != 0)
-+ prob += (LZMA_LIT_SIZE * (((processedPos & lpMask) << lc) +
-+ (dic[(dicPos == 0 ? dicBufSize : dicPos) - 1] >> (8 - lc))));
-+
-+ if (state < kNumLitStates)
-+ {
-+ symbol = 1;
-+ do { GET_BIT(prob + symbol, symbol) } while (symbol < 0x100);
-+ }
-+ else
-+ {
-+ unsigned matchByte = p->dic[(dicPos - rep0) + ((dicPos < rep0) ? dicBufSize : 0)];
-+ unsigned offs = 0x100;
-+ symbol = 1;
-+ do
-+ {
-+ unsigned bit;
-+ CLzmaProb *probLit;
-+ matchByte <<= 1;
-+ bit = (matchByte & offs);
-+ probLit = prob + offs + bit + symbol;
-+ GET_BIT2(probLit, symbol, offs &= ~bit, offs &= bit)
-+ }
-+ while (symbol < 0x100);
-+ }
-+ dic[dicPos++] = (Byte)symbol;
-+ processedPos++;
-+
-+ state = kLiteralNextStates[state];
-+ /* if (state < 4) state = 0; else if (state < 10) state -= 3; else state -= 6; */
-+ continue;
-+ }
-+ else
-+ {
-+ UPDATE_1(prob);
-+ prob = probs + IsRep + state;
-+ IF_BIT_0(prob)
-+ {
-+ UPDATE_0(prob);
-+ state += kNumStates;
-+ prob = probs + LenCoder;
-+ }
-+ else
-+ {
-+ UPDATE_1(prob);
-+ if (checkDicSize == 0 && processedPos == 0)
-+ return SZ_ERROR_DATA;
-+ prob = probs + IsRepG0 + state;
-+ IF_BIT_0(prob)
-+ {
-+ UPDATE_0(prob);
-+ prob = probs + IsRep0Long + (state << kNumPosBitsMax) + posState;
-+ IF_BIT_0(prob)
-+ {
-+ UPDATE_0(prob);
-+ dic[dicPos] = dic[(dicPos - rep0) + ((dicPos < rep0) ? dicBufSize : 0)];
-+ dicPos++;
-+ processedPos++;
-+ state = state < kNumLitStates ? 9 : 11;
-+ continue;
-+ }
-+ UPDATE_1(prob);
-+ }
-+ else
-+ {
-+ UInt32 distance;
-+ UPDATE_1(prob);
-+ prob = probs + IsRepG1 + state;
-+ IF_BIT_0(prob)
-+ {
-+ UPDATE_0(prob);
-+ distance = rep1;
-+ }
-+ else
-+ {
-+ UPDATE_1(prob);
-+ prob = probs + IsRepG2 + state;
-+ IF_BIT_0(prob)
-+ {
-+ UPDATE_0(prob);
-+ distance = rep2;
-+ }
-+ else
-+ {
-+ UPDATE_1(prob);
-+ distance = rep3;
-+ rep3 = rep2;
-+ }
-+ rep2 = rep1;
-+ }
-+ rep1 = rep0;
-+ rep0 = distance;
-+ }
-+ state = state < kNumLitStates ? 8 : 11;
-+ prob = probs + RepLenCoder;
-+ }
-+ {
-+ unsigned limit, offset;
-+ CLzmaProb *probLen = prob + LenChoice;
-+ IF_BIT_0(probLen)
-+ {
-+ UPDATE_0(probLen);
-+ probLen = prob + LenLow + (posState << kLenNumLowBits);
-+ offset = 0;
-+ limit = (1 << kLenNumLowBits);
-+ }
-+ else
-+ {
-+ UPDATE_1(probLen);
-+ probLen = prob + LenChoice2;
-+ IF_BIT_0(probLen)
-+ {
-+ UPDATE_0(probLen);
-+ probLen = prob + LenMid + (posState << kLenNumMidBits);
-+ offset = kLenNumLowSymbols;
-+ limit = (1 << kLenNumMidBits);
-+ }
-+ else
-+ {
-+ UPDATE_1(probLen);
-+ probLen = prob + LenHigh;
-+ offset = kLenNumLowSymbols + kLenNumMidSymbols;
-+ limit = (1 << kLenNumHighBits);
-+ }
-+ }
-+ TREE_DECODE(probLen, limit, len);
-+ len += offset;
-+ }
-+
-+ if (state >= kNumStates)
-+ {
-+ UInt32 distance;
-+ prob = probs + PosSlot +
-+ ((len < kNumLenToPosStates ? len : kNumLenToPosStates - 1) << kNumPosSlotBits);
-+ TREE_6_DECODE(prob, distance);
-+ if (distance >= kStartPosModelIndex)
-+ {
-+ unsigned posSlot = (unsigned)distance;
-+ int numDirectBits = (int)(((distance >> 1) - 1));
-+ distance = (2 | (distance & 1));
-+ if (posSlot < kEndPosModelIndex)
-+ {
-+ distance <<= numDirectBits;
-+ prob = probs + SpecPos + distance - posSlot - 1;
-+ {
-+ UInt32 mask = 1;
-+ unsigned i = 1;
-+ do
-+ {
-+ GET_BIT2(prob + i, i, ; , distance |= mask);
-+ mask <<= 1;
-+ }
-+ while(--numDirectBits != 0);
-+ }
-+ }
-+ else
-+ {
-+ numDirectBits -= kNumAlignBits;
-+ do
-+ {
-+ NORMALIZE
-+ range >>= 1;
-+
-+ {
-+ UInt32 t;
-+ code -= range;
-+ t = (0 - ((UInt32)code >> 31)); /* (UInt32)((Int32)code >> 31) */
-+ distance = (distance << 1) + (t + 1);
-+ code += range & t;
-+ }
-+ /*
-+ distance <<= 1;
-+ if (code >= range)
-+ {
-+ code -= range;
-+ distance |= 1;
-+ }
-+ */
-+ }
-+ while (--numDirectBits != 0);
-+ prob = probs + Align;
-+ distance <<= kNumAlignBits;
-+ {
-+ unsigned i = 1;
-+ GET_BIT2(prob + i, i, ; , distance |= 1);
-+ GET_BIT2(prob + i, i, ; , distance |= 2);
-+ GET_BIT2(prob + i, i, ; , distance |= 4);
-+ GET_BIT2(prob + i, i, ; , distance |= 8);
-+ }
-+ if (distance == (UInt32)0xFFFFFFFF)
-+ {
-+ len += kMatchSpecLenStart;
-+ state -= kNumStates;
-+ break;
-+ }
-+ }
-+ }
-+ rep3 = rep2;
-+ rep2 = rep1;
-+ rep1 = rep0;
-+ rep0 = distance + 1;
-+ if (checkDicSize == 0)
-+ {
-+ if (distance >= processedPos)
-+ return SZ_ERROR_DATA;
-+ }
-+ else if (distance >= checkDicSize)
-+ return SZ_ERROR_DATA;
-+ state = (state < kNumStates + kNumLitStates) ? kNumLitStates : kNumLitStates + 3;
-+ /* state = kLiteralNextStates[state]; */
-+ }
-+
-+ len += kMatchMinLen;
-+
-+ {
-+ SizeT rem = limit - dicPos;
-+ unsigned curLen = ((rem < len) ? (unsigned)rem : len);
-+ SizeT pos = (dicPos - rep0) + ((dicPos < rep0) ? dicBufSize : 0);
-+
-+ processedPos += curLen;
-+
-+ len -= curLen;
-+ if (pos + curLen <= dicBufSize)
-+ {
-+ Byte *dest = dic + dicPos;
-+ ptrdiff_t src = (ptrdiff_t)pos - (ptrdiff_t)dicPos;
-+ const Byte *lim = dest + curLen;
-+ dicPos += curLen;
-+ do
-+ *(dest) = (Byte)*(dest + src);
-+ while (++dest != lim);
-+ }
-+ else
-+ {
-+ do
-+ {
-+ dic[dicPos++] = dic[pos];
-+ if (++pos == dicBufSize)
-+ pos = 0;
-+ }
-+ while (--curLen != 0);
-+ }
-+ }
-+ }
-+ }
-+ while (dicPos < limit && buf < bufLimit);
-+ NORMALIZE;
-+ p->buf = buf;
-+ p->range = range;
-+ p->code = code;
-+ p->remainLen = len;
-+ p->dicPos = dicPos;
-+ p->processedPos = processedPos;
-+ p->reps[0] = rep0;
-+ p->reps[1] = rep1;
-+ p->reps[2] = rep2;
-+ p->reps[3] = rep3;
-+ p->state = state;
-+
-+ return SZ_OK;
-+}
-+
-+static void MY_FAST_CALL LzmaDec_WriteRem(CLzmaDec *p, SizeT limit)
-+{
-+ if (p->remainLen != 0 && p->remainLen < kMatchSpecLenStart)
-+ {
-+ Byte *dic = p->dic;
-+ SizeT dicPos = p->dicPos;
-+ SizeT dicBufSize = p->dicBufSize;
-+ unsigned len = p->remainLen;
-+ UInt32 rep0 = p->reps[0];
-+ if (limit - dicPos < len)
-+ len = (unsigned)(limit - dicPos);
-+
-+ if (p->checkDicSize == 0 && p->prop.dicSize - p->processedPos <= len)
-+ p->checkDicSize = p->prop.dicSize;
-+
-+ p->processedPos += len;
-+ p->remainLen -= len;
-+ while (len-- != 0)
-+ {
-+ dic[dicPos] = dic[(dicPos - rep0) + ((dicPos < rep0) ? dicBufSize : 0)];
-+ dicPos++;
-+ }
-+ p->dicPos = dicPos;
-+ }
-+}
-+
-+/* LzmaDec_DecodeReal2 decodes LZMA-symbols and sets p->needFlush and p->needInit, if required. */
-+
-+static int MY_FAST_CALL LzmaDec_DecodeReal2(CLzmaDec *p, SizeT limit, const Byte *bufLimit)
-+{
-+ do
-+ {
-+ SizeT limit2 = limit;
-+ if (p->checkDicSize == 0)
-+ {
-+ UInt32 rem = p->prop.dicSize - p->processedPos;
-+ if (limit - p->dicPos > rem)
-+ limit2 = p->dicPos + rem;
-+ }
-+ RINOK(LzmaDec_DecodeReal(p, limit2, bufLimit));
-+ if (p->processedPos >= p->prop.dicSize)
-+ p->checkDicSize = p->prop.dicSize;
-+ LzmaDec_WriteRem(p, limit);
-+ }
-+ while (p->dicPos < limit && p->buf < bufLimit && p->remainLen < kMatchSpecLenStart);
-+
-+ if (p->remainLen > kMatchSpecLenStart)
-+ {
-+ p->remainLen = kMatchSpecLenStart;
-+ }
-+ return 0;
-+}
-+
-+typedef enum
-+{
-+ DUMMY_ERROR, /* unexpected end of input stream */
-+ DUMMY_LIT,
-+ DUMMY_MATCH,
-+ DUMMY_REP
-+} ELzmaDummy;
-+
-+static ELzmaDummy LzmaDec_TryDummy(const CLzmaDec *p, const Byte *buf, SizeT inSize)
-+{
-+ UInt32 range = p->range;
-+ UInt32 code = p->code;
-+ const Byte *bufLimit = buf + inSize;
-+ CLzmaProb *probs = p->probs;
-+ unsigned state = p->state;
-+ ELzmaDummy res;
-+
-+ {
-+ CLzmaProb *prob;
-+ UInt32 bound;
-+ unsigned ttt;
-+ unsigned posState = (p->processedPos) & ((1 << p->prop.pb) - 1);
-+
-+ prob = probs + IsMatch + (state << kNumPosBitsMax) + posState;
-+ IF_BIT_0_CHECK(prob)
-+ {
-+ UPDATE_0_CHECK
-+
-+ /* if (bufLimit - buf >= 7) return DUMMY_LIT; */
-+
-+ prob = probs + Literal;
-+ if (p->checkDicSize != 0 || p->processedPos != 0)
-+ prob += (LZMA_LIT_SIZE *
-+ ((((p->processedPos) & ((1 << (p->prop.lp)) - 1)) << p->prop.lc) +
-+ (p->dic[(p->dicPos == 0 ? p->dicBufSize : p->dicPos) - 1] >> (8 - p->prop.lc))));
-+
-+ if (state < kNumLitStates)
-+ {
-+ unsigned symbol = 1;
-+ do { GET_BIT_CHECK(prob + symbol, symbol) } while (symbol < 0x100);
-+ }
-+ else
-+ {
-+ unsigned matchByte = p->dic[p->dicPos - p->reps[0] +
-+ ((p->dicPos < p->reps[0]) ? p->dicBufSize : 0)];
-+ unsigned offs = 0x100;
-+ unsigned symbol = 1;
-+ do
-+ {
-+ unsigned bit;
-+ CLzmaProb *probLit;
-+ matchByte <<= 1;
-+ bit = (matchByte & offs);
-+ probLit = prob + offs + bit + symbol;
-+ GET_BIT2_CHECK(probLit, symbol, offs &= ~bit, offs &= bit)
-+ }
-+ while (symbol < 0x100);
-+ }
-+ res = DUMMY_LIT;
-+ }
-+ else
-+ {
-+ unsigned len;
-+ UPDATE_1_CHECK;
-+
-+ prob = probs + IsRep + state;
-+ IF_BIT_0_CHECK(prob)
-+ {
-+ UPDATE_0_CHECK;
-+ state = 0;
-+ prob = probs + LenCoder;
-+ res = DUMMY_MATCH;
-+ }
-+ else
-+ {
-+ UPDATE_1_CHECK;
-+ res = DUMMY_REP;
-+ prob = probs + IsRepG0 + state;
-+ IF_BIT_0_CHECK(prob)
-+ {
-+ UPDATE_0_CHECK;
-+ prob = probs + IsRep0Long + (state << kNumPosBitsMax) + posState;
-+ IF_BIT_0_CHECK(prob)
-+ {
-+ UPDATE_0_CHECK;
-+ NORMALIZE_CHECK;
-+ return DUMMY_REP;
-+ }
-+ else
-+ {
-+ UPDATE_1_CHECK;
-+ }
-+ }
-+ else
-+ {
-+ UPDATE_1_CHECK;
-+ prob = probs + IsRepG1 + state;
-+ IF_BIT_0_CHECK(prob)
-+ {
-+ UPDATE_0_CHECK;
-+ }
-+ else
-+ {
-+ UPDATE_1_CHECK;
-+ prob = probs + IsRepG2 + state;
-+ IF_BIT_0_CHECK(prob)
-+ {
-+ UPDATE_0_CHECK;
-+ }
-+ else
-+ {
-+ UPDATE_1_CHECK;
-+ }
-+ }
-+ }
-+ state = kNumStates;
-+ prob = probs + RepLenCoder;
-+ }
-+ {
-+ unsigned limit, offset;
-+ CLzmaProb *probLen = prob + LenChoice;
-+ IF_BIT_0_CHECK(probLen)
-+ {
-+ UPDATE_0_CHECK;
-+ probLen = prob + LenLow + (posState << kLenNumLowBits);
-+ offset = 0;
-+ limit = 1 << kLenNumLowBits;
-+ }
-+ else
-+ {
-+ UPDATE_1_CHECK;
-+ probLen = prob + LenChoice2;
-+ IF_BIT_0_CHECK(probLen)
-+ {
-+ UPDATE_0_CHECK;
-+ probLen = prob + LenMid + (posState << kLenNumMidBits);
-+ offset = kLenNumLowSymbols;
-+ limit = 1 << kLenNumMidBits;
-+ }
-+ else
-+ {
-+ UPDATE_1_CHECK;
-+ probLen = prob + LenHigh;
-+ offset = kLenNumLowSymbols + kLenNumMidSymbols;
-+ limit = 1 << kLenNumHighBits;
-+ }
-+ }
-+ TREE_DECODE_CHECK(probLen, limit, len);
-+ len += offset;
-+ }
-+
-+ if (state < 4)
-+ {
-+ unsigned posSlot;
-+ prob = probs + PosSlot +
-+ ((len < kNumLenToPosStates ? len : kNumLenToPosStates - 1) <<
-+ kNumPosSlotBits);
-+ TREE_DECODE_CHECK(prob, 1 << kNumPosSlotBits, posSlot);
-+ if (posSlot >= kStartPosModelIndex)
-+ {
-+ int numDirectBits = ((posSlot >> 1) - 1);
-+
-+ /* if (bufLimit - buf >= 8) return DUMMY_MATCH; */
-+
-+ if (posSlot < kEndPosModelIndex)
-+ {
-+ prob = probs + SpecPos + ((2 | (posSlot & 1)) << numDirectBits) - posSlot - 1;
-+ }
-+ else
-+ {
-+ numDirectBits -= kNumAlignBits;
-+ do
-+ {
-+ NORMALIZE_CHECK
-+ range >>= 1;
-+ code -= range & (((code - range) >> 31) - 1);
-+ /* if (code >= range) code -= range; */
-+ }
-+ while (--numDirectBits != 0);
-+ prob = probs + Align;
-+ numDirectBits = kNumAlignBits;
-+ }
-+ {
-+ unsigned i = 1;
-+ do
-+ {
-+ GET_BIT_CHECK(prob + i, i);
-+ }
-+ while(--numDirectBits != 0);
-+ }
-+ }
-+ }
-+ }
-+ }
-+ NORMALIZE_CHECK;
-+ return res;
-+}
-+
-+
-+static void LzmaDec_InitRc(CLzmaDec *p, const Byte *data)
-+{
-+ p->code = ((UInt32)data[1] << 24) | ((UInt32)data[2] << 16) | ((UInt32)data[3] << 8) | ((UInt32)data[4]);
-+ p->range = 0xFFFFFFFF;
-+ p->needFlush = 0;
-+}
-+
-+static void LzmaDec_InitDicAndState(CLzmaDec *p, Bool initDic, Bool initState)
-+{
-+ p->needFlush = 1;
-+ p->remainLen = 0;
-+ p->tempBufSize = 0;
-+
-+ if (initDic)
-+ {
-+ p->processedPos = 0;
-+ p->checkDicSize = 0;
-+ p->needInitState = 1;
-+ }
-+ if (initState)
-+ p->needInitState = 1;
-+}
-+
-+void LzmaDec_Init(CLzmaDec *p)
-+{
-+ p->dicPos = 0;
-+ LzmaDec_InitDicAndState(p, True, True);
-+}
-+
-+static void LzmaDec_InitStateReal(CLzmaDec *p)
-+{
-+ UInt32 numProbs = Literal + ((UInt32)LZMA_LIT_SIZE << (p->prop.lc + p->prop.lp));
-+ UInt32 i;
-+ CLzmaProb *probs = p->probs;
-+ for (i = 0; i < numProbs; i++)
-+ probs[i] = kBitModelTotal >> 1;
-+ p->reps[0] = p->reps[1] = p->reps[2] = p->reps[3] = 1;
-+ p->state = 0;
-+ p->needInitState = 0;
-+}
-+
-+SRes LzmaDec_DecodeToDic(CLzmaDec *p, SizeT dicLimit, const Byte *src, SizeT *srcLen,
-+ ELzmaFinishMode finishMode, ELzmaStatus *status)
-+{
-+ SizeT inSize = *srcLen;
-+ (*srcLen) = 0;
-+ LzmaDec_WriteRem(p, dicLimit);
-+
-+ *status = LZMA_STATUS_NOT_SPECIFIED;
-+
-+ while (p->remainLen != kMatchSpecLenStart)
-+ {
-+ int checkEndMarkNow;
-+
-+ if (p->needFlush != 0)
-+ {
-+ for (; inSize > 0 && p->tempBufSize < RC_INIT_SIZE; (*srcLen)++, inSize--)
-+ p->tempBuf[p->tempBufSize++] = *src++;
-+ if (p->tempBufSize < RC_INIT_SIZE)
-+ {
-+ *status = LZMA_STATUS_NEEDS_MORE_INPUT;
-+ return SZ_OK;
-+ }
-+ if (p->tempBuf[0] != 0)
-+ return SZ_ERROR_DATA;
-+
-+ LzmaDec_InitRc(p, p->tempBuf);
-+ p->tempBufSize = 0;
-+ }
-+
-+ checkEndMarkNow = 0;
-+ if (p->dicPos >= dicLimit)
-+ {
-+ if (p->remainLen == 0 && p->code == 0)
-+ {
-+ *status = LZMA_STATUS_MAYBE_FINISHED_WITHOUT_MARK;
-+ return SZ_OK;
-+ }
-+ if (finishMode == LZMA_FINISH_ANY)
-+ {
-+ *status = LZMA_STATUS_NOT_FINISHED;
-+ return SZ_OK;
-+ }
-+ if (p->remainLen != 0)
-+ {
-+ *status = LZMA_STATUS_NOT_FINISHED;
-+ return SZ_ERROR_DATA;
-+ }
-+ checkEndMarkNow = 1;
-+ }
-+
-+ if (p->needInitState)
-+ LzmaDec_InitStateReal(p);
-+
-+ if (p->tempBufSize == 0)
-+ {
-+ SizeT processed;
-+ const Byte *bufLimit;
-+ if (inSize < LZMA_REQUIRED_INPUT_MAX || checkEndMarkNow)
-+ {
-+ int dummyRes = LzmaDec_TryDummy(p, src, inSize);
-+ if (dummyRes == DUMMY_ERROR)
-+ {
-+ memcpy(p->tempBuf, src, inSize);
-+ p->tempBufSize = (unsigned)inSize;
-+ (*srcLen) += inSize;
-+ *status = LZMA_STATUS_NEEDS_MORE_INPUT;
-+ return SZ_OK;
-+ }
-+ if (checkEndMarkNow && dummyRes != DUMMY_MATCH)
-+ {
-+ *status = LZMA_STATUS_NOT_FINISHED;
-+ return SZ_ERROR_DATA;
-+ }
-+ bufLimit = src;
-+ }
-+ else
-+ bufLimit = src + inSize - LZMA_REQUIRED_INPUT_MAX;
-+ p->buf = src;
-+ if (LzmaDec_DecodeReal2(p, dicLimit, bufLimit) != 0)
-+ return SZ_ERROR_DATA;
-+ processed = p->buf - src;
-+ (*srcLen) += processed;
-+ src += processed;
-+ inSize -= processed;
-+ }
-+ else
-+ {
-+ unsigned rem = p->tempBufSize, lookAhead = 0;
-+ while (rem < LZMA_REQUIRED_INPUT_MAX && lookAhead < inSize)
-+ p->tempBuf[rem++] = src[lookAhead++];
-+ p->tempBufSize = rem;
-+ if (rem < LZMA_REQUIRED_INPUT_MAX || checkEndMarkNow)
-+ {
-+ int dummyRes = LzmaDec_TryDummy(p, p->tempBuf, rem);
-+ if (dummyRes == DUMMY_ERROR)
-+ {
-+ (*srcLen) += lookAhead;
-+ *status = LZMA_STATUS_NEEDS_MORE_INPUT;
-+ return SZ_OK;
-+ }
-+ if (checkEndMarkNow && dummyRes != DUMMY_MATCH)
-+ {
-+ *status = LZMA_STATUS_NOT_FINISHED;
-+ return SZ_ERROR_DATA;
-+ }
-+ }
-+ p->buf = p->tempBuf;
-+ if (LzmaDec_DecodeReal2(p, dicLimit, p->buf) != 0)
-+ return SZ_ERROR_DATA;
-+ lookAhead -= (rem - (unsigned)(p->buf - p->tempBuf));
-+ (*srcLen) += lookAhead;
-+ src += lookAhead;
-+ inSize -= lookAhead;
-+ p->tempBufSize = 0;
-+ }
-+ }
-+ if (p->code == 0)
-+ *status = LZMA_STATUS_FINISHED_WITH_MARK;
-+ return (p->code == 0) ? SZ_OK : SZ_ERROR_DATA;
-+}
-+
-+SRes LzmaDec_DecodeToBuf(CLzmaDec *p, Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen, ELzmaFinishMode finishMode, ELzmaStatus *status)
-+{
-+ SizeT outSize = *destLen;
-+ SizeT inSize = *srcLen;
-+ *srcLen = *destLen = 0;
-+ for (;;)
-+ {
-+ SizeT inSizeCur = inSize, outSizeCur, dicPos;
-+ ELzmaFinishMode curFinishMode;
-+ SRes res;
-+ if (p->dicPos == p->dicBufSize)
-+ p->dicPos = 0;
-+ dicPos = p->dicPos;
-+ if (outSize > p->dicBufSize - dicPos)
-+ {
-+ outSizeCur = p->dicBufSize;
-+ curFinishMode = LZMA_FINISH_ANY;
-+ }
-+ else
-+ {
-+ outSizeCur = dicPos + outSize;
-+ curFinishMode = finishMode;
-+ }
-+
-+ res = LzmaDec_DecodeToDic(p, outSizeCur, src, &inSizeCur, curFinishMode, status);
-+ src += inSizeCur;
-+ inSize -= inSizeCur;
-+ *srcLen += inSizeCur;
-+ outSizeCur = p->dicPos - dicPos;
-+ memcpy(dest, p->dic + dicPos, outSizeCur);
-+ dest += outSizeCur;
-+ outSize -= outSizeCur;
-+ *destLen += outSizeCur;
-+ if (res != 0)
-+ return res;
-+ if (outSizeCur == 0 || outSize == 0)
-+ return SZ_OK;
-+ }
-+}
-+
-+void LzmaDec_FreeProbs(CLzmaDec *p, ISzAlloc *alloc)
-+{
-+ alloc->Free(alloc, p->probs);
-+ p->probs = 0;
-+}
-+
-+static void LzmaDec_FreeDict(CLzmaDec *p, ISzAlloc *alloc)
-+{
-+ alloc->Free(alloc, p->dic);
-+ p->dic = 0;
-+}
-+
-+void LzmaDec_Free(CLzmaDec *p, ISzAlloc *alloc)
-+{
-+ LzmaDec_FreeProbs(p, alloc);
-+ LzmaDec_FreeDict(p, alloc);
-+}
-+
-+SRes LzmaProps_Decode(CLzmaProps *p, const Byte *data, unsigned size)
-+{
-+ UInt32 dicSize;
-+ Byte d;
-+
-+ if (size < LZMA_PROPS_SIZE)
-+ return SZ_ERROR_UNSUPPORTED;
-+ else
-+ dicSize = data[1] | ((UInt32)data[2] << 8) | ((UInt32)data[3] << 16) | ((UInt32)data[4] << 24);
-+
-+ if (dicSize < LZMA_DIC_MIN)
-+ dicSize = LZMA_DIC_MIN;
-+ p->dicSize = dicSize;
-+
-+ d = data[0];
-+ if (d >= (9 * 5 * 5))
-+ return SZ_ERROR_UNSUPPORTED;
-+
-+ p->lc = d % 9;
-+ d /= 9;
-+ p->pb = d / 5;
-+ p->lp = d % 5;
-+
-+ return SZ_OK;
-+}
-+
-+static SRes LzmaDec_AllocateProbs2(CLzmaDec *p, const CLzmaProps *propNew, ISzAlloc *alloc)
-+{
-+ UInt32 numProbs = LzmaProps_GetNumProbs(propNew);
-+ if (p->probs == 0 || numProbs != p->numProbs)
-+ {
-+ LzmaDec_FreeProbs(p, alloc);
-+ p->probs = (CLzmaProb *)alloc->Alloc(alloc, numProbs * sizeof(CLzmaProb));
-+ p->numProbs = numProbs;
-+ if (p->probs == 0)
-+ return SZ_ERROR_MEM;
-+ }
-+ return SZ_OK;
-+}
-+
-+SRes LzmaDec_AllocateProbs(CLzmaDec *p, const Byte *props, unsigned propsSize, ISzAlloc *alloc)
-+{
-+ CLzmaProps propNew;
-+ RINOK(LzmaProps_Decode(&propNew, props, propsSize));
-+ RINOK(LzmaDec_AllocateProbs2(p, &propNew, alloc));
-+ p->prop = propNew;
-+ return SZ_OK;
-+}
-+
-+SRes LzmaDec_Allocate(CLzmaDec *p, const Byte *props, unsigned propsSize, ISzAlloc *alloc)
-+{
-+ CLzmaProps propNew;
-+ SizeT dicBufSize;
-+ RINOK(LzmaProps_Decode(&propNew, props, propsSize));
-+ RINOK(LzmaDec_AllocateProbs2(p, &propNew, alloc));
-+ dicBufSize = propNew.dicSize;
-+ if (p->dic == 0 || dicBufSize != p->dicBufSize)
-+ {
-+ LzmaDec_FreeDict(p, alloc);
-+ p->dic = (Byte *)alloc->Alloc(alloc, dicBufSize);
-+ if (p->dic == 0)
-+ {
-+ LzmaDec_FreeProbs(p, alloc);
-+ return SZ_ERROR_MEM;
-+ }
-+ }
-+ p->dicBufSize = dicBufSize;
-+ p->prop = propNew;
-+ return SZ_OK;
-+}
-+
-+SRes LzmaDecode(Byte *dest, SizeT *destLen, const Byte *src, SizeT *srcLen,
-+ const Byte *propData, unsigned propSize, ELzmaFinishMode finishMode,
-+ ELzmaStatus *status, ISzAlloc *alloc)
-+{
-+ CLzmaDec p;
-+ SRes res;
-+ SizeT inSize = *srcLen;
-+ SizeT outSize = *destLen;
-+ *srcLen = *destLen = 0;
-+ if (inSize < RC_INIT_SIZE)
-+ return SZ_ERROR_INPUT_EOF;
-+
-+ LzmaDec_Construct(&p);
-+ res = LzmaDec_AllocateProbs(&p, propData, propSize, alloc);
-+ if (res != 0)
-+ return res;
-+ p.dic = dest;
-+ p.dicBufSize = outSize;
-+
-+ LzmaDec_Init(&p);
-+
-+ *srcLen = inSize;
-+ res = LzmaDec_DecodeToDic(&p, outSize, src, srcLen, finishMode, status);
-+
-+ if (res == SZ_OK && *status == LZMA_STATUS_NEEDS_MORE_INPUT)
-+ res = SZ_ERROR_INPUT_EOF;
-+
-+ (*destLen) = p.dicPos;
-+ LzmaDec_FreeProbs(&p, alloc);
-+ return res;
-+}
---- /dev/null
-+++ b/jffsX-utils/lzma/LzmaEnc.c
-@@ -0,0 +1,2335 @@
-+/* LzmaEnc.c -- LZMA Encoder
-+2008-04-28
-+Copyright (c) 1999-2008 Igor Pavlov
-+Read LzmaEnc.h for license options */
-+
-+#if defined(SHOW_STAT) || defined(SHOW_STAT2)
-+#include <stdio.h>
-+#endif
-+
-+#include <string.h>
-+
-+#include "LzmaEnc.h"
-+
-+#include "LzFind.h"
-+#ifdef COMPRESS_MF_MT
-+#include "LzFindMt.h"
-+#endif
-+
-+/* #define SHOW_STAT */
-+/* #define SHOW_STAT2 */
-+
-+#ifdef SHOW_STAT
-+static int ttt = 0;
-+#endif
-+
-+#define kBlockSizeMax ((1 << LZMA_NUM_BLOCK_SIZE_BITS) - 1)
-+
-+#define kBlockSize (9 << 10)
-+#define kUnpackBlockSize (1 << 18)
-+#define kMatchArraySize (1 << 21)
-+#define kMatchRecordMaxSize ((LZMA_MATCH_LEN_MAX * 2 + 3) * LZMA_MATCH_LEN_MAX)
-+
-+#define kNumMaxDirectBits (31)
-+
-+#define kNumTopBits 24
-+#define kTopValue ((UInt32)1 << kNumTopBits)
-+
-+#define kNumBitModelTotalBits 11
-+#define kBitModelTotal (1 << kNumBitModelTotalBits)
-+#define kNumMoveBits 5
-+#define kProbInitValue (kBitModelTotal >> 1)
-+
-+#define kNumMoveReducingBits 4
-+#define kNumBitPriceShiftBits 4
-+#define kBitPrice (1 << kNumBitPriceShiftBits)
-+
-+void LzmaEncProps_Init(CLzmaEncProps *p)
-+{
-+ p->level = 5;
-+ p->dictSize = p->mc = 0;
-+ p->lc = p->lp = p->pb = p->algo = p->fb = p->btMode = p->numHashBytes = p->numThreads = -1;
-+ p->writeEndMark = 0;
-+}
-+
-+void LzmaEncProps_Normalize(CLzmaEncProps *p)
-+{
-+ int level = p->level;
-+ if (level < 0) level = 5;
-+ p->level = level;
-+ if (p->dictSize == 0) p->dictSize = (level <= 5 ? (1 << (level * 2 + 14)) : (level == 6 ? (1 << 25) : (1 << 26)));
-+ if (p->lc < 0) p->lc = 3;
-+ if (p->lp < 0) p->lp = 0;
-+ if (p->pb < 0) p->pb = 2;
-+ if (p->algo < 0) p->algo = (level < 5 ? 0 : 1);
-+ if (p->fb < 0) p->fb = (level < 7 ? 32 : 64);
-+ if (p->btMode < 0) p->btMode = (p->algo == 0 ? 0 : 1);
-+ if (p->numHashBytes < 0) p->numHashBytes = 4;
-+ if (p->mc == 0) p->mc = (16 + (p->fb >> 1)) >> (p->btMode ? 0 : 1);
-+ if (p->numThreads < 0) p->numThreads = ((p->btMode && p->algo) ? 2 : 1);
-+}
-+
-+UInt32 LzmaEncProps_GetDictSize(const CLzmaEncProps *props2)
-+{
-+ CLzmaEncProps props = *props2;
-+ LzmaEncProps_Normalize(&props);
-+ return props.dictSize;
-+}
-+
-+/* #define LZMA_LOG_BSR */
-+/* Define it for Intel's CPU */
-+
-+
-+#ifdef LZMA_LOG_BSR
-+
-+#define kDicLogSizeMaxCompress 30
-+
-+#define BSR2_RET(pos, res) { unsigned long i; _BitScanReverse(&i, (pos)); res = (i + i) + ((pos >> (i - 1)) & 1); }
-+
-+UInt32 GetPosSlot1(UInt32 pos)
-+{
-+ UInt32 res;
-+ BSR2_RET(pos, res);
-+ return res;
-+}
-+#define GetPosSlot2(pos, res) { BSR2_RET(pos, res); }
-+#define GetPosSlot(pos, res) { if (pos < 2) res = pos; else BSR2_RET(pos, res); }
-+
-+#else
-+
-+#define kNumLogBits (9 + (int)sizeof(size_t) / 2)
-+#define kDicLogSizeMaxCompress ((kNumLogBits - 1) * 2 + 7)
-+
-+static void LzmaEnc_FastPosInit(Byte *g_FastPos)
-+{
-+ int c = 2, slotFast;
-+ g_FastPos[0] = 0;
-+ g_FastPos[1] = 1;
-+
-+ for (slotFast = 2; slotFast < kNumLogBits * 2; slotFast++)
-+ {
-+ UInt32 k = (1 << ((slotFast >> 1) - 1));
-+ UInt32 j;
-+ for (j = 0; j < k; j++, c++)
-+ g_FastPos[c] = (Byte)slotFast;
-+ }
-+}
-+
-+#define BSR2_RET(pos, res) { UInt32 i = 6 + ((kNumLogBits - 1) & \
-+ (0 - (((((UInt32)1 << (kNumLogBits + 6)) - 1) - pos) >> 31))); \
-+ res = p->g_FastPos[pos >> i] + (i * 2); }
-+/*
-+#define BSR2_RET(pos, res) { res = (pos < (1 << (kNumLogBits + 6))) ? \
-+ p->g_FastPos[pos >> 6] + 12 : \
-+ p->g_FastPos[pos >> (6 + kNumLogBits - 1)] + (6 + (kNumLogBits - 1)) * 2; }
-+*/
-+
-+#define GetPosSlot1(pos) p->g_FastPos[pos]
-+#define GetPosSlot2(pos, res) { BSR2_RET(pos, res); }
-+#define GetPosSlot(pos, res) { if (pos < kNumFullDistances) res = p->g_FastPos[pos]; else BSR2_RET(pos, res); }
-+
-+#endif
-+
-+
-+#define LZMA_NUM_REPS 4
-+
-+typedef unsigned CState;
-+
-+typedef struct _COptimal
-+{
-+ UInt32 price;
-+
-+ CState state;
-+ int prev1IsChar;
-+ int prev2;
-+
-+ UInt32 posPrev2;
-+ UInt32 backPrev2;
-+
-+ UInt32 posPrev;
-+ UInt32 backPrev;
-+ UInt32 backs[LZMA_NUM_REPS];
-+} COptimal;
-+
-+#define kNumOpts (1 << 12)
-+
-+#define kNumLenToPosStates 4
-+#define kNumPosSlotBits 6
-+#define kDicLogSizeMin 0
-+#define kDicLogSizeMax 32
-+#define kDistTableSizeMax (kDicLogSizeMax * 2)
-+
-+
-+#define kNumAlignBits 4
-+#define kAlignTableSize (1 << kNumAlignBits)
-+#define kAlignMask (kAlignTableSize - 1)
-+
-+#define kStartPosModelIndex 4
-+#define kEndPosModelIndex 14
-+#define kNumPosModels (kEndPosModelIndex - kStartPosModelIndex)
-+
-+#define kNumFullDistances (1 << (kEndPosModelIndex / 2))
-+
-+#ifdef _LZMA_PROB32
-+#define CLzmaProb UInt32
-+#else
-+#define CLzmaProb UInt16
-+#endif
-+
-+#define LZMA_PB_MAX 4
-+#define LZMA_LC_MAX 8
-+#define LZMA_LP_MAX 4
-+
-+#define LZMA_NUM_PB_STATES_MAX (1 << LZMA_PB_MAX)
-+
-+
-+#define kLenNumLowBits 3
-+#define kLenNumLowSymbols (1 << kLenNumLowBits)
-+#define kLenNumMidBits 3
-+#define kLenNumMidSymbols (1 << kLenNumMidBits)
-+#define kLenNumHighBits 8
-+#define kLenNumHighSymbols (1 << kLenNumHighBits)
-+
-+#define kLenNumSymbolsTotal (kLenNumLowSymbols + kLenNumMidSymbols + kLenNumHighSymbols)
-+
-+#define LZMA_MATCH_LEN_MIN 2
-+#define LZMA_MATCH_LEN_MAX (LZMA_MATCH_LEN_MIN + kLenNumSymbolsTotal - 1)
-+
-+#define kNumStates 12
-+
-+typedef struct
-+{
-+ CLzmaProb choice;
-+ CLzmaProb choice2;
-+ CLzmaProb low[LZMA_NUM_PB_STATES_MAX << kLenNumLowBits];
-+ CLzmaProb mid[LZMA_NUM_PB_STATES_MAX << kLenNumMidBits];
-+ CLzmaProb high[kLenNumHighSymbols];
-+} CLenEnc;
-+
-+typedef struct
-+{
-+ CLenEnc p;
-+ UInt32 prices[LZMA_NUM_PB_STATES_MAX][kLenNumSymbolsTotal];
-+ UInt32 tableSize;
-+ UInt32 counters[LZMA_NUM_PB_STATES_MAX];
-+} CLenPriceEnc;
-+
-+typedef struct _CRangeEnc
-+{
-+ UInt32 range;
-+ Byte cache;
-+ UInt64 low;
-+ UInt64 cacheSize;
-+ Byte *buf;
-+ Byte *bufLim;
-+ Byte *bufBase;
-+ ISeqOutStream *outStream;
-+ UInt64 processed;
-+ SRes res;
-+} CRangeEnc;
-+
-+typedef struct _CSeqInStreamBuf
-+{
-+ ISeqInStream funcTable;
-+ const Byte *data;
-+ SizeT rem;
-+} CSeqInStreamBuf;
-+
-+static SRes MyRead(void *pp, void *data, size_t *size)
-+{
-+ size_t curSize = *size;
-+ CSeqInStreamBuf *p = (CSeqInStreamBuf *)pp;
-+ if (p->rem < curSize)
-+ curSize = p->rem;
-+ memcpy(data, p->data, curSize);
-+ p->rem -= curSize;
-+ p->data += curSize;
-+ *size = curSize;
-+ return SZ_OK;
-+}
-+
-+typedef struct
-+{
-+ CLzmaProb *litProbs;
-+
-+ CLzmaProb isMatch[kNumStates][LZMA_NUM_PB_STATES_MAX];
-+ CLzmaProb isRep[kNumStates];
-+ CLzmaProb isRepG0[kNumStates];
-+ CLzmaProb isRepG1[kNumStates];
-+ CLzmaProb isRepG2[kNumStates];
-+ CLzmaProb isRep0Long[kNumStates][LZMA_NUM_PB_STATES_MAX];
-+
-+ CLzmaProb posSlotEncoder[kNumLenToPosStates][1 << kNumPosSlotBits];
-+ CLzmaProb posEncoders[kNumFullDistances - kEndPosModelIndex];
-+ CLzmaProb posAlignEncoder[1 << kNumAlignBits];
-+
-+ CLenPriceEnc lenEnc;
-+ CLenPriceEnc repLenEnc;
-+
-+ UInt32 reps[LZMA_NUM_REPS];
-+ UInt32 state;
-+} CSaveState;
-+
-+typedef struct _CLzmaEnc
-+{
-+ IMatchFinder matchFinder;
-+ void *matchFinderObj;
-+
-+ #ifdef COMPRESS_MF_MT
-+ Bool mtMode;
-+ CMatchFinderMt matchFinderMt;
-+ #endif
-+
-+ CMatchFinder matchFinderBase;
-+
-+ #ifdef COMPRESS_MF_MT
-+ Byte pad[128];
-+ #endif
-+
-+ UInt32 optimumEndIndex;
-+ UInt32 optimumCurrentIndex;
-+
-+ Bool longestMatchWasFound;
-+ UInt32 longestMatchLength;
-+ UInt32 numDistancePairs;
-+
-+ COptimal opt[kNumOpts];
-+
-+ #ifndef LZMA_LOG_BSR
-+ Byte g_FastPos[1 << kNumLogBits];
-+ #endif
-+
-+ UInt32 ProbPrices[kBitModelTotal >> kNumMoveReducingBits];
-+ UInt32 matchDistances[LZMA_MATCH_LEN_MAX * 2 + 2 + 1];
-+ UInt32 numFastBytes;
-+ UInt32 additionalOffset;
-+ UInt32 reps[LZMA_NUM_REPS];
-+ UInt32 state;
-+
-+ UInt32 posSlotPrices[kNumLenToPosStates][kDistTableSizeMax];
-+ UInt32 distancesPrices[kNumLenToPosStates][kNumFullDistances];
-+ UInt32 alignPrices[kAlignTableSize];
-+ UInt32 alignPriceCount;
-+
-+ UInt32 distTableSize;
-+
-+ unsigned lc, lp, pb;
-+ unsigned lpMask, pbMask;
-+
-+ CLzmaProb *litProbs;
-+
-+ CLzmaProb isMatch[kNumStates][LZMA_NUM_PB_STATES_MAX];
-+ CLzmaProb isRep[kNumStates];
-+ CLzmaProb isRepG0[kNumStates];
-+ CLzmaProb isRepG1[kNumStates];
-+ CLzmaProb isRepG2[kNumStates];
-+ CLzmaProb isRep0Long[kNumStates][LZMA_NUM_PB_STATES_MAX];
-+
-+ CLzmaProb posSlotEncoder[kNumLenToPosStates][1 << kNumPosSlotBits];
-+ CLzmaProb posEncoders[kNumFullDistances - kEndPosModelIndex];
-+ CLzmaProb posAlignEncoder[1 << kNumAlignBits];
-+
-+ CLenPriceEnc lenEnc;
-+ CLenPriceEnc repLenEnc;
-+
-+ unsigned lclp;
-+
-+ Bool fastMode;
-+
-+ CRangeEnc rc;
-+
-+ Bool writeEndMark;
-+ UInt64 nowPos64;
-+ UInt32 matchPriceCount;
-+ Bool finished;
-+ Bool multiThread;
-+
-+ SRes result;
-+ UInt32 dictSize;
-+ UInt32 matchFinderCycles;
-+
-+ ISeqInStream *inStream;
-+ CSeqInStreamBuf seqBufInStream;
-+
-+ CSaveState saveState;
-+} CLzmaEnc;
-+
-+static void LzmaEnc_SaveState(CLzmaEncHandle pp)
-+{
-+ CLzmaEnc *p = (CLzmaEnc *)pp;
-+ CSaveState *dest = &p->saveState;
-+ int i;
-+ dest->lenEnc = p->lenEnc;
-+ dest->repLenEnc = p->repLenEnc;
-+ dest->state = p->state;
-+
-+ for (i = 0; i < kNumStates; i++)
-+ {
-+ memcpy(dest->isMatch[i], p->isMatch[i], sizeof(p->isMatch[i]));
-+ memcpy(dest->isRep0Long[i], p->isRep0Long[i], sizeof(p->isRep0Long[i]));
-+ }
-+ for (i = 0; i < kNumLenToPosStates; i++)
-+ memcpy(dest->posSlotEncoder[i], p->posSlotEncoder[i], sizeof(p->posSlotEncoder[i]));
-+ memcpy(dest->isRep, p->isRep, sizeof(p->isRep));
-+ memcpy(dest->isRepG0, p->isRepG0, sizeof(p->isRepG0));
-+ memcpy(dest->isRepG1, p->isRepG1, sizeof(p->isRepG1));
-+ memcpy(dest->isRepG2, p->isRepG2, sizeof(p->isRepG2));
-+ memcpy(dest->posEncoders, p->posEncoders, sizeof(p->posEncoders));
-+ memcpy(dest->posAlignEncoder, p->posAlignEncoder, sizeof(p->posAlignEncoder));
-+ memcpy(dest->reps, p->reps, sizeof(p->reps));
-+ memcpy(dest->litProbs, p->litProbs, (0x300 << p->lclp) * sizeof(CLzmaProb));
-+}
-+
-+static void LzmaEnc_RestoreState(CLzmaEncHandle pp)
-+{
-+ CLzmaEnc *dest = (CLzmaEnc *)pp;
-+ const CSaveState *p = &dest->saveState;
-+ int i;
-+ dest->lenEnc = p->lenEnc;
-+ dest->repLenEnc = p->repLenEnc;
-+ dest->state = p->state;
-+
-+ for (i = 0; i < kNumStates; i++)
-+ {
-+ memcpy(dest->isMatch[i], p->isMatch[i], sizeof(p->isMatch[i]));
-+ memcpy(dest->isRep0Long[i], p->isRep0Long[i], sizeof(p->isRep0Long[i]));
-+ }
-+ for (i = 0; i < kNumLenToPosStates; i++)
-+ memcpy(dest->posSlotEncoder[i], p->posSlotEncoder[i], sizeof(p->posSlotEncoder[i]));
-+ memcpy(dest->isRep, p->isRep, sizeof(p->isRep));
-+ memcpy(dest->isRepG0, p->isRepG0, sizeof(p->isRepG0));
-+ memcpy(dest->isRepG1, p->isRepG1, sizeof(p->isRepG1));
-+ memcpy(dest->isRepG2, p->isRepG2, sizeof(p->isRepG2));
-+ memcpy(dest->posEncoders, p->posEncoders, sizeof(p->posEncoders));
-+ memcpy(dest->posAlignEncoder, p->posAlignEncoder, sizeof(p->posAlignEncoder));
-+ memcpy(dest->reps, p->reps, sizeof(p->reps));
-+ memcpy(dest->litProbs, p->litProbs, (0x300 << dest->lclp) * sizeof(CLzmaProb));
-+}
-+
-+SRes LzmaEnc_SetProps(CLzmaEncHandle pp, const CLzmaEncProps *props2)
-+{
-+ CLzmaEnc *p = (CLzmaEnc *)pp;
-+ CLzmaEncProps props = *props2;
-+ LzmaEncProps_Normalize(&props);
-+
-+ if (props.lc > LZMA_LC_MAX || props.lp > LZMA_LP_MAX || props.pb > LZMA_PB_MAX ||
-+ props.dictSize > (1 << kDicLogSizeMaxCompress) || props.dictSize > (1 << 30))
-+ return SZ_ERROR_PARAM;
-+ p->dictSize = props.dictSize;
-+ p->matchFinderCycles = props.mc;
-+ {
-+ unsigned fb = props.fb;
-+ if (fb < 5)
-+ fb = 5;
-+ if (fb > LZMA_MATCH_LEN_MAX)
-+ fb = LZMA_MATCH_LEN_MAX;
-+ p->numFastBytes = fb;
-+ }
-+ p->lc = props.lc;
-+ p->lp = props.lp;
-+ p->pb = props.pb;
-+ p->fastMode = (props.algo == 0);
-+ p->matchFinderBase.btMode = props.btMode;
-+ {
-+ UInt32 numHashBytes = 4;
-+ if (props.btMode)
-+ {
-+ if (props.numHashBytes < 2)
-+ numHashBytes = 2;
-+ else if (props.numHashBytes < 4)
-+ numHashBytes = props.numHashBytes;
-+ }
-+ p->matchFinderBase.numHashBytes = numHashBytes;
-+ }
-+
-+ p->matchFinderBase.cutValue = props.mc;
-+
-+ p->writeEndMark = props.writeEndMark;
-+
-+ #ifdef COMPRESS_MF_MT
-+ /*
-+ if (newMultiThread != _multiThread)
-+ {
-+ ReleaseMatchFinder();
-+ _multiThread = newMultiThread;
-+ }
-+ */
-+ p->multiThread = (props.numThreads > 1);
-+ #endif
-+
-+ return SZ_OK;
-+}
-+
-+static const int kLiteralNextStates[kNumStates] = {0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 4, 5};
-+static const int kMatchNextStates[kNumStates] = {7, 7, 7, 7, 7, 7, 7, 10, 10, 10, 10, 10};
-+static const int kRepNextStates[kNumStates] = {8, 8, 8, 8, 8, 8, 8, 11, 11, 11, 11, 11};
-+static const int kShortRepNextStates[kNumStates]= {9, 9, 9, 9, 9, 9, 9, 11, 11, 11, 11, 11};
-+
-+/*
-+ void UpdateChar() { Index = kLiteralNextStates[Index]; }
-+ void UpdateMatch() { Index = kMatchNextStates[Index]; }
-+ void UpdateRep() { Index = kRepNextStates[Index]; }
-+ void UpdateShortRep() { Index = kShortRepNextStates[Index]; }
-+*/
-+
-+#define IsCharState(s) ((s) < 7)
-+
-+
-+#define GetLenToPosState(len) (((len) < kNumLenToPosStates + 1) ? (len) - 2 : kNumLenToPosStates - 1)
-+
-+#define kInfinityPrice (1 << 30)
-+
-+static void RangeEnc_Construct(CRangeEnc *p)
-+{
-+ p->outStream = 0;
-+ p->bufBase = 0;
-+}
-+
-+#define RangeEnc_GetProcessed(p) ((p)->processed + ((p)->buf - (p)->bufBase) + (p)->cacheSize)
-+
-+#define RC_BUF_SIZE (1 << 16)
-+static int RangeEnc_Alloc(CRangeEnc *p, ISzAlloc *alloc)
-+{
-+ if (p->bufBase == 0)
-+ {
-+ p->bufBase = (Byte *)alloc->Alloc(alloc, RC_BUF_SIZE);
-+ if (p->bufBase == 0)
-+ return 0;
-+ p->bufLim = p->bufBase + RC_BUF_SIZE;
-+ }
-+ return 1;
-+}
-+
-+static void RangeEnc_Free(CRangeEnc *p, ISzAlloc *alloc)
-+{
-+ alloc->Free(alloc, p->bufBase);
-+ p->bufBase = 0;
-+}
-+
-+static void RangeEnc_Init(CRangeEnc *p)
-+{
-+ /* Stream.Init(); */
-+ p->low = 0;
-+ p->range = 0xFFFFFFFF;
-+ p->cacheSize = 1;
-+ p->cache = 0;
-+
-+ p->buf = p->bufBase;
-+
-+ p->processed = 0;
-+ p->res = SZ_OK;
-+}
-+
-+static void RangeEnc_FlushStream(CRangeEnc *p)
-+{
-+ size_t num;
-+ if (p->res != SZ_OK)
-+ return;
-+ num = p->buf - p->bufBase;
-+ if (num != p->outStream->Write(p->outStream, p->bufBase, num))
-+ p->res = SZ_ERROR_WRITE;
-+ p->processed += num;
-+ p->buf = p->bufBase;
-+}
-+
-+static void MY_FAST_CALL RangeEnc_ShiftLow(CRangeEnc *p)
-+{
-+ if ((UInt32)p->low < (UInt32)0xFF000000 || (int)(p->low >> 32) != 0)
-+ {
-+ Byte temp = p->cache;
-+ do
-+ {
-+ Byte *buf = p->buf;
-+ *buf++ = (Byte)(temp + (Byte)(p->low >> 32));
-+ p->buf = buf;
-+ if (buf == p->bufLim)
-+ RangeEnc_FlushStream(p);
-+ temp = 0xFF;
-+ }
-+ while (--p->cacheSize != 0);
-+ p->cache = (Byte)((UInt32)p->low >> 24);
-+ }
-+ p->cacheSize++;
-+ p->low = (UInt32)p->low << 8;
-+}
-+
-+static void RangeEnc_FlushData(CRangeEnc *p)
-+{
-+ int i;
-+ for (i = 0; i < 5; i++)
-+ RangeEnc_ShiftLow(p);
-+}
-+
-+static void RangeEnc_EncodeDirectBits(CRangeEnc *p, UInt32 value, int numBits)
-+{
-+ do
-+ {
-+ p->range >>= 1;
-+ p->low += p->range & (0 - ((value >> --numBits) & 1));
-+ if (p->range < kTopValue)
-+ {
-+ p->range <<= 8;
-+ RangeEnc_ShiftLow(p);
-+ }
-+ }
-+ while (numBits != 0);
-+}
-+
-+static void RangeEnc_EncodeBit(CRangeEnc *p, CLzmaProb *prob, UInt32 symbol)
-+{
-+ UInt32 ttt = *prob;
-+ UInt32 newBound = (p->range >> kNumBitModelTotalBits) * ttt;
-+ if (symbol == 0)
-+ {
-+ p->range = newBound;
-+ ttt += (kBitModelTotal - ttt) >> kNumMoveBits;
-+ }
-+ else
-+ {
-+ p->low += newBound;
-+ p->range -= newBound;
-+ ttt -= ttt >> kNumMoveBits;
-+ }
-+ *prob = (CLzmaProb)ttt;
-+ if (p->range < kTopValue)
-+ {
-+ p->range <<= 8;
-+ RangeEnc_ShiftLow(p);
-+ }
-+}
-+
-+static void LitEnc_Encode(CRangeEnc *p, CLzmaProb *probs, UInt32 symbol)
-+{
-+ symbol |= 0x100;
-+ do
-+ {
-+ RangeEnc_EncodeBit(p, probs + (symbol >> 8), (symbol >> 7) & 1);
-+ symbol <<= 1;
-+ }
-+ while (symbol < 0x10000);
-+}
-+
-+static void LitEnc_EncodeMatched(CRangeEnc *p, CLzmaProb *probs, UInt32 symbol, UInt32 matchByte)
-+{
-+ UInt32 offs = 0x100;
-+ symbol |= 0x100;
-+ do
-+ {
-+ matchByte <<= 1;
-+ RangeEnc_EncodeBit(p, probs + (offs + (matchByte & offs) + (symbol >> 8)), (symbol >> 7) & 1);
-+ symbol <<= 1;
-+ offs &= ~(matchByte ^ symbol);
-+ }
-+ while (symbol < 0x10000);
-+}
-+
-+static void LzmaEnc_InitPriceTables(UInt32 *ProbPrices)
-+{
-+ UInt32 i;
-+ for (i = (1 << kNumMoveReducingBits) / 2; i < kBitModelTotal; i += (1 << kNumMoveReducingBits))
-+ {
-+ const int kCyclesBits = kNumBitPriceShiftBits;
-+ UInt32 w = i;
-+ UInt32 bitCount = 0;
-+ int j;
-+ for (j = 0; j < kCyclesBits; j++)
-+ {
-+ w = w * w;
-+ bitCount <<= 1;
-+ while (w >= ((UInt32)1 << 16))
-+ {
-+ w >>= 1;
-+ bitCount++;
-+ }
-+ }
-+ ProbPrices[i >> kNumMoveReducingBits] = ((kNumBitModelTotalBits << kCyclesBits) - 15 - bitCount);
-+ }
-+}
-+
-+
-+#define GET_PRICE(prob, symbol) \
-+ p->ProbPrices[((prob) ^ (((-(int)(symbol))) & (kBitModelTotal - 1))) >> kNumMoveReducingBits];
-+
-+#define GET_PRICEa(prob, symbol) \
-+ ProbPrices[((prob) ^ ((-((int)(symbol))) & (kBitModelTotal - 1))) >> kNumMoveReducingBits];
-+
-+#define GET_PRICE_0(prob) p->ProbPrices[(prob) >> kNumMoveReducingBits]
-+#define GET_PRICE_1(prob) p->ProbPrices[((prob) ^ (kBitModelTotal - 1)) >> kNumMoveReducingBits]
-+
-+#define GET_PRICE_0a(prob) ProbPrices[(prob) >> kNumMoveReducingBits]
-+#define GET_PRICE_1a(prob) ProbPrices[((prob) ^ (kBitModelTotal - 1)) >> kNumMoveReducingBits]
-+
-+static UInt32 LitEnc_GetPrice(const CLzmaProb *probs, UInt32 symbol, UInt32 *ProbPrices)
-+{
-+ UInt32 price = 0;
-+ symbol |= 0x100;
-+ do
-+ {
-+ price += GET_PRICEa(probs[symbol >> 8], (symbol >> 7) & 1);
-+ symbol <<= 1;
-+ }
-+ while (symbol < 0x10000);
-+ return price;
-+};
-+
-+static UInt32 LitEnc_GetPriceMatched(const CLzmaProb *probs, UInt32 symbol, UInt32 matchByte, UInt32 *ProbPrices)
-+{
-+ UInt32 price = 0;
-+ UInt32 offs = 0x100;
-+ symbol |= 0x100;
-+ do
-+ {
-+ matchByte <<= 1;
-+ price += GET_PRICEa(probs[offs + (matchByte & offs) + (symbol >> 8)], (symbol >> 7) & 1);
-+ symbol <<= 1;
-+ offs &= ~(matchByte ^ symbol);
-+ }
-+ while (symbol < 0x10000);
-+ return price;
-+};
-+
-+
-+static void RcTree_Encode(CRangeEnc *rc, CLzmaProb *probs, int numBitLevels, UInt32 symbol)
-+{
-+ UInt32 m = 1;
-+ int i;
-+ for (i = numBitLevels; i != 0 ;)
-+ {
-+ UInt32 bit;
-+ i--;
-+ bit = (symbol >> i) & 1;
-+ RangeEnc_EncodeBit(rc, probs + m, bit);
-+ m = (m << 1) | bit;
-+ }
-+};
-+
-+static void RcTree_ReverseEncode(CRangeEnc *rc, CLzmaProb *probs, int numBitLevels, UInt32 symbol)
-+{
-+ UInt32 m = 1;
-+ int i;
-+ for (i = 0; i < numBitLevels; i++)
-+ {
-+ UInt32 bit = symbol & 1;
-+ RangeEnc_EncodeBit(rc, probs + m, bit);
-+ m = (m << 1) | bit;
-+ symbol >>= 1;
-+ }
-+}
-+
-+static UInt32 RcTree_GetPrice(const CLzmaProb *probs, int numBitLevels, UInt32 symbol, UInt32 *ProbPrices)
-+{
-+ UInt32 price = 0;
-+ symbol |= (1 << numBitLevels);
-+ while (symbol != 1)
-+ {
-+ price += GET_PRICEa(probs[symbol >> 1], symbol & 1);
-+ symbol >>= 1;
-+ }
-+ return price;
-+}
-+
-+static UInt32 RcTree_ReverseGetPrice(const CLzmaProb *probs, int numBitLevels, UInt32 symbol, UInt32 *ProbPrices)
-+{
-+ UInt32 price = 0;
-+ UInt32 m = 1;
-+ int i;
-+ for (i = numBitLevels; i != 0; i--)
-+ {
-+ UInt32 bit = symbol & 1;
-+ symbol >>= 1;
-+ price += GET_PRICEa(probs[m], bit);
-+ m = (m << 1) | bit;
-+ }
-+ return price;
-+}
-+
-+
-+static void LenEnc_Init(CLenEnc *p)
-+{
-+ unsigned i;
-+ p->choice = p->choice2 = kProbInitValue;
-+ for (i = 0; i < (LZMA_NUM_PB_STATES_MAX << kLenNumLowBits); i++)
-+ p->low[i] = kProbInitValue;
-+ for (i = 0; i < (LZMA_NUM_PB_STATES_MAX << kLenNumMidBits); i++)
-+ p->mid[i] = kProbInitValue;
-+ for (i = 0; i < kLenNumHighSymbols; i++)
-+ p->high[i] = kProbInitValue;
-+}
-+
-+static void LenEnc_Encode(CLenEnc *p, CRangeEnc *rc, UInt32 symbol, UInt32 posState)
-+{
-+ if (symbol < kLenNumLowSymbols)
-+ {
-+ RangeEnc_EncodeBit(rc, &p->choice, 0);
-+ RcTree_Encode(rc, p->low + (posState << kLenNumLowBits), kLenNumLowBits, symbol);
-+ }
-+ else
-+ {
-+ RangeEnc_EncodeBit(rc, &p->choice, 1);
-+ if (symbol < kLenNumLowSymbols + kLenNumMidSymbols)
-+ {
-+ RangeEnc_EncodeBit(rc, &p->choice2, 0);
-+ RcTree_Encode(rc, p->mid + (posState << kLenNumMidBits), kLenNumMidBits, symbol - kLenNumLowSymbols);
-+ }
-+ else
-+ {
-+ RangeEnc_EncodeBit(rc, &p->choice2, 1);
-+ RcTree_Encode(rc, p->high, kLenNumHighBits, symbol - kLenNumLowSymbols - kLenNumMidSymbols);
-+ }
-+ }
-+}
-+
-+static void LenEnc_SetPrices(CLenEnc *p, UInt32 posState, UInt32 numSymbols, UInt32 *prices, UInt32 *ProbPrices)
-+{
-+ UInt32 a0 = GET_PRICE_0a(p->choice);
-+ UInt32 a1 = GET_PRICE_1a(p->choice);
-+ UInt32 b0 = a1 + GET_PRICE_0a(p->choice2);
-+ UInt32 b1 = a1 + GET_PRICE_1a(p->choice2);
-+ UInt32 i = 0;
-+ for (i = 0; i < kLenNumLowSymbols; i++)
-+ {
-+ if (i >= numSymbols)
-+ return;
-+ prices[i] = a0 + RcTree_GetPrice(p->low + (posState << kLenNumLowBits), kLenNumLowBits, i, ProbPrices);
-+ }
-+ for (; i < kLenNumLowSymbols + kLenNumMidSymbols; i++)
-+ {
-+ if (i >= numSymbols)
-+ return;
-+ prices[i] = b0 + RcTree_GetPrice(p->mid + (posState << kLenNumMidBits), kLenNumMidBits, i - kLenNumLowSymbols, ProbPrices);
-+ }
-+ for (; i < numSymbols; i++)
-+ prices[i] = b1 + RcTree_GetPrice(p->high, kLenNumHighBits, i - kLenNumLowSymbols - kLenNumMidSymbols, ProbPrices);
-+}
-+
-+static void MY_FAST_CALL LenPriceEnc_UpdateTable(CLenPriceEnc *p, UInt32 posState, UInt32 *ProbPrices)
-+{
-+ LenEnc_SetPrices(&p->p, posState, p->tableSize, p->prices[posState], ProbPrices);
-+ p->counters[posState] = p->tableSize;
-+}
-+
-+static void LenPriceEnc_UpdateTables(CLenPriceEnc *p, UInt32 numPosStates, UInt32 *ProbPrices)
-+{
-+ UInt32 posState;
-+ for (posState = 0; posState < numPosStates; posState++)
-+ LenPriceEnc_UpdateTable(p, posState, ProbPrices);
-+}
-+
-+static void LenEnc_Encode2(CLenPriceEnc *p, CRangeEnc *rc, UInt32 symbol, UInt32 posState, Bool updatePrice, UInt32 *ProbPrices)
-+{
-+ LenEnc_Encode(&p->p, rc, symbol, posState);
-+ if (updatePrice)
-+ if (--p->counters[posState] == 0)
-+ LenPriceEnc_UpdateTable(p, posState, ProbPrices);
-+}
-+
-+
-+
-+
-+static void MovePos(CLzmaEnc *p, UInt32 num)
-+{
-+ #ifdef SHOW_STAT
-+ ttt += num;
-+ printf("\n MovePos %d", num);
-+ #endif
-+ if (num != 0)
-+ {
-+ p->additionalOffset += num;
-+ p->matchFinder.Skip(p->matchFinderObj, num);
-+ }
-+}
-+
-+static UInt32 ReadMatchDistances(CLzmaEnc *p, UInt32 *numDistancePairsRes)
-+{
-+ UInt32 lenRes = 0, numDistancePairs;
-+ numDistancePairs = p->matchFinder.GetMatches(p->matchFinderObj, p->matchDistances);
-+ #ifdef SHOW_STAT
-+ printf("\n i = %d numPairs = %d ", ttt, numDistancePairs / 2);
-+ if (ttt >= 61994)
-+ ttt = ttt;
-+
-+ ttt++;
-+ {
-+ UInt32 i;
-+ for (i = 0; i < numDistancePairs; i += 2)
-+ printf("%2d %6d | ", p->matchDistances[i], p->matchDistances[i + 1]);
-+ }
-+ #endif
-+ if (numDistancePairs > 0)
-+ {
-+ lenRes = p->matchDistances[numDistancePairs - 2];
-+ if (lenRes == p->numFastBytes)
-+ {
-+ UInt32 numAvail = p->matchFinder.GetNumAvailableBytes(p->matchFinderObj) + 1;
-+ const Byte *pby = p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - 1;
-+ UInt32 distance = p->matchDistances[numDistancePairs - 1] + 1;
-+ if (numAvail > LZMA_MATCH_LEN_MAX)
-+ numAvail = LZMA_MATCH_LEN_MAX;
-+
-+ {
-+ const Byte *pby2 = pby - distance;
-+ for (; lenRes < numAvail && pby[lenRes] == pby2[lenRes]; lenRes++);
-+ }
-+ }
-+ }
-+ p->additionalOffset++;
-+ *numDistancePairsRes = numDistancePairs;
-+ return lenRes;
-+}
-+
-+
-+#define MakeAsChar(p) (p)->backPrev = (UInt32)(-1); (p)->prev1IsChar = False;
-+#define MakeAsShortRep(p) (p)->backPrev = 0; (p)->prev1IsChar = False;
-+#define IsShortRep(p) ((p)->backPrev == 0)
-+
-+static UInt32 GetRepLen1Price(CLzmaEnc *p, UInt32 state, UInt32 posState)
-+{
-+ return
-+ GET_PRICE_0(p->isRepG0[state]) +
-+ GET_PRICE_0(p->isRep0Long[state][posState]);
-+}
-+
-+static UInt32 GetPureRepPrice(CLzmaEnc *p, UInt32 repIndex, UInt32 state, UInt32 posState)
-+{
-+ UInt32 price;
-+ if (repIndex == 0)
-+ {
-+ price = GET_PRICE_0(p->isRepG0[state]);
-+ price += GET_PRICE_1(p->isRep0Long[state][posState]);
-+ }
-+ else
-+ {
-+ price = GET_PRICE_1(p->isRepG0[state]);
-+ if (repIndex == 1)
-+ price += GET_PRICE_0(p->isRepG1[state]);
-+ else
-+ {
-+ price += GET_PRICE_1(p->isRepG1[state]);
-+ price += GET_PRICE(p->isRepG2[state], repIndex - 2);
-+ }
-+ }
-+ return price;
-+}
-+
-+static UInt32 GetRepPrice(CLzmaEnc *p, UInt32 repIndex, UInt32 len, UInt32 state, UInt32 posState)
-+{
-+ return p->repLenEnc.prices[posState][len - LZMA_MATCH_LEN_MIN] +
-+ GetPureRepPrice(p, repIndex, state, posState);
-+}
-+
-+static UInt32 Backward(CLzmaEnc *p, UInt32 *backRes, UInt32 cur)
-+{
-+ UInt32 posMem = p->opt[cur].posPrev;
-+ UInt32 backMem = p->opt[cur].backPrev;
-+ p->optimumEndIndex = cur;
-+ do
-+ {
-+ if (p->opt[cur].prev1IsChar)
-+ {
-+ MakeAsChar(&p->opt[posMem])
-+ p->opt[posMem].posPrev = posMem - 1;
-+ if (p->opt[cur].prev2)
-+ {
-+ p->opt[posMem - 1].prev1IsChar = False;
-+ p->opt[posMem - 1].posPrev = p->opt[cur].posPrev2;
-+ p->opt[posMem - 1].backPrev = p->opt[cur].backPrev2;
-+ }
-+ }
-+ {
-+ UInt32 posPrev = posMem;
-+ UInt32 backCur = backMem;
-+
-+ backMem = p->opt[posPrev].backPrev;
-+ posMem = p->opt[posPrev].posPrev;
-+
-+ p->opt[posPrev].backPrev = backCur;
-+ p->opt[posPrev].posPrev = cur;
-+ cur = posPrev;
-+ }
-+ }
-+ while (cur != 0);
-+ *backRes = p->opt[0].backPrev;
-+ p->optimumCurrentIndex = p->opt[0].posPrev;
-+ return p->optimumCurrentIndex;
-+}
-+
-+#define LIT_PROBS(pos, prevByte) (p->litProbs + ((((pos) & p->lpMask) << p->lc) + ((prevByte) >> (8 - p->lc))) * 0x300)
-+
-+static UInt32 GetOptimum(CLzmaEnc *p, UInt32 position, UInt32 *backRes)
-+{
-+ UInt32 numAvailableBytes, lenMain, numDistancePairs;
-+ const Byte *data;
-+ UInt32 reps[LZMA_NUM_REPS];
-+ UInt32 repLens[LZMA_NUM_REPS];
-+ UInt32 repMaxIndex, i;
-+ UInt32 *matchDistances;
-+ Byte currentByte, matchByte;
-+ UInt32 posState;
-+ UInt32 matchPrice, repMatchPrice;
-+ UInt32 lenEnd;
-+ UInt32 len;
-+ UInt32 normalMatchPrice;
-+ UInt32 cur;
-+ if (p->optimumEndIndex != p->optimumCurrentIndex)
-+ {
-+ const COptimal *opt = &p->opt[p->optimumCurrentIndex];
-+ UInt32 lenRes = opt->posPrev - p->optimumCurrentIndex;
-+ *backRes = opt->backPrev;
-+ p->optimumCurrentIndex = opt->posPrev;
-+ return lenRes;
-+ }
-+ p->optimumCurrentIndex = p->optimumEndIndex = 0;
-+
-+ numAvailableBytes = p->matchFinder.GetNumAvailableBytes(p->matchFinderObj);
-+
-+ if (!p->longestMatchWasFound)
-+ {
-+ lenMain = ReadMatchDistances(p, &numDistancePairs);
-+ }
-+ else
-+ {
-+ lenMain = p->longestMatchLength;
-+ numDistancePairs = p->numDistancePairs;
-+ p->longestMatchWasFound = False;
-+ }
-+
-+ data = p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - 1;
-+ if (numAvailableBytes < 2)
-+ {
-+ *backRes = (UInt32)(-1);
-+ return 1;
-+ }
-+ if (numAvailableBytes > LZMA_MATCH_LEN_MAX)
-+ numAvailableBytes = LZMA_MATCH_LEN_MAX;
-+
-+ repMaxIndex = 0;
-+ for (i = 0; i < LZMA_NUM_REPS; i++)
-+ {
-+ UInt32 lenTest;
-+ const Byte *data2;
-+ reps[i] = p->reps[i];
-+ data2 = data - (reps[i] + 1);
-+ if (data[0] != data2[0] || data[1] != data2[1])
-+ {
-+ repLens[i] = 0;
-+ continue;
-+ }
-+ for (lenTest = 2; lenTest < numAvailableBytes && data[lenTest] == data2[lenTest]; lenTest++);
-+ repLens[i] = lenTest;
-+ if (lenTest > repLens[repMaxIndex])
-+ repMaxIndex = i;
-+ }
-+ if (repLens[repMaxIndex] >= p->numFastBytes)
-+ {
-+ UInt32 lenRes;
-+ *backRes = repMaxIndex;
-+ lenRes = repLens[repMaxIndex];
-+ MovePos(p, lenRes - 1);
-+ return lenRes;
-+ }
-+
-+ matchDistances = p->matchDistances;
-+ if (lenMain >= p->numFastBytes)
-+ {
-+ *backRes = matchDistances[numDistancePairs - 1] + LZMA_NUM_REPS;
-+ MovePos(p, lenMain - 1);
-+ return lenMain;
-+ }
-+ currentByte = *data;
-+ matchByte = *(data - (reps[0] + 1));
-+
-+ if (lenMain < 2 && currentByte != matchByte && repLens[repMaxIndex] < 2)
-+ {
-+ *backRes = (UInt32)-1;
-+ return 1;
-+ }
-+
-+ p->opt[0].state = (CState)p->state;
-+
-+ posState = (position & p->pbMask);
-+
-+ {
-+ const CLzmaProb *probs = LIT_PROBS(position, *(data - 1));
-+ p->opt[1].price = GET_PRICE_0(p->isMatch[p->state][posState]) +
-+ (!IsCharState(p->state) ?
-+ LitEnc_GetPriceMatched(probs, currentByte, matchByte, p->ProbPrices) :
-+ LitEnc_GetPrice(probs, currentByte, p->ProbPrices));
-+ }
-+
-+ MakeAsChar(&p->opt[1]);
-+
-+ matchPrice = GET_PRICE_1(p->isMatch[p->state][posState]);
-+ repMatchPrice = matchPrice + GET_PRICE_1(p->isRep[p->state]);
-+
-+ if (matchByte == currentByte)
-+ {
-+ UInt32 shortRepPrice = repMatchPrice + GetRepLen1Price(p, p->state, posState);
-+ if (shortRepPrice < p->opt[1].price)
-+ {
-+ p->opt[1].price = shortRepPrice;
-+ MakeAsShortRep(&p->opt[1]);
-+ }
-+ }
-+ lenEnd = ((lenMain >= repLens[repMaxIndex]) ? lenMain : repLens[repMaxIndex]);
-+
-+ if (lenEnd < 2)
-+ {
-+ *backRes = p->opt[1].backPrev;
-+ return 1;
-+ }
-+
-+ p->opt[1].posPrev = 0;
-+ for (i = 0; i < LZMA_NUM_REPS; i++)
-+ p->opt[0].backs[i] = reps[i];
-+
-+ len = lenEnd;
-+ do
-+ p->opt[len--].price = kInfinityPrice;
-+ while (len >= 2);
-+
-+ for (i = 0; i < LZMA_NUM_REPS; i++)
-+ {
-+ UInt32 repLen = repLens[i];
-+ UInt32 price;
-+ if (repLen < 2)
-+ continue;
-+ price = repMatchPrice + GetPureRepPrice(p, i, p->state, posState);
-+ do
-+ {
-+ UInt32 curAndLenPrice = price + p->repLenEnc.prices[posState][repLen - 2];
-+ COptimal *opt = &p->opt[repLen];
-+ if (curAndLenPrice < opt->price)
-+ {
-+ opt->price = curAndLenPrice;
-+ opt->posPrev = 0;
-+ opt->backPrev = i;
-+ opt->prev1IsChar = False;
-+ }
-+ }
-+ while (--repLen >= 2);
-+ }
-+
-+ normalMatchPrice = matchPrice + GET_PRICE_0(p->isRep[p->state]);
-+
-+ len = ((repLens[0] >= 2) ? repLens[0] + 1 : 2);
-+ if (len <= lenMain)
-+ {
-+ UInt32 offs = 0;
-+ while (len > matchDistances[offs])
-+ offs += 2;
-+ for (; ; len++)
-+ {
-+ COptimal *opt;
-+ UInt32 distance = matchDistances[offs + 1];
-+
-+ UInt32 curAndLenPrice = normalMatchPrice + p->lenEnc.prices[posState][len - LZMA_MATCH_LEN_MIN];
-+ UInt32 lenToPosState = GetLenToPosState(len);
-+ if (distance < kNumFullDistances)
-+ curAndLenPrice += p->distancesPrices[lenToPosState][distance];
-+ else
-+ {
-+ UInt32 slot;
-+ GetPosSlot2(distance, slot);
-+ curAndLenPrice += p->alignPrices[distance & kAlignMask] + p->posSlotPrices[lenToPosState][slot];
-+ }
-+ opt = &p->opt[len];
-+ if (curAndLenPrice < opt->price)
-+ {
-+ opt->price = curAndLenPrice;
-+ opt->posPrev = 0;
-+ opt->backPrev = distance + LZMA_NUM_REPS;
-+ opt->prev1IsChar = False;
-+ }
-+ if (len == matchDistances[offs])
-+ {
-+ offs += 2;
-+ if (offs == numDistancePairs)
-+ break;
-+ }
-+ }
-+ }
-+
-+ cur = 0;
-+
-+ #ifdef SHOW_STAT2
-+ if (position >= 0)
-+ {
-+ unsigned i;
-+ printf("\n pos = %4X", position);
-+ for (i = cur; i <= lenEnd; i++)
-+ printf("\nprice[%4X] = %d", position - cur + i, p->opt[i].price);
-+ }
-+ #endif
-+
-+ for (;;)
-+ {
-+ UInt32 numAvailableBytesFull, newLen, numDistancePairs;
-+ COptimal *curOpt;
-+ UInt32 posPrev;
-+ UInt32 state;
-+ UInt32 curPrice;
-+ Bool nextIsChar;
-+ const Byte *data;
-+ Byte currentByte, matchByte;
-+ UInt32 posState;
-+ UInt32 curAnd1Price;
-+ COptimal *nextOpt;
-+ UInt32 matchPrice, repMatchPrice;
-+ UInt32 numAvailableBytes;
-+ UInt32 startLen;
-+
-+ cur++;
-+ if (cur == lenEnd)
-+ return Backward(p, backRes, cur);
-+
-+ numAvailableBytesFull = p->matchFinder.GetNumAvailableBytes(p->matchFinderObj);
-+ newLen = ReadMatchDistances(p, &numDistancePairs);
-+ if (newLen >= p->numFastBytes)
-+ {
-+ p->numDistancePairs = numDistancePairs;
-+ p->longestMatchLength = newLen;
-+ p->longestMatchWasFound = True;
-+ return Backward(p, backRes, cur);
-+ }
-+ position++;
-+ curOpt = &p->opt[cur];
-+ posPrev = curOpt->posPrev;
-+ if (curOpt->prev1IsChar)
-+ {
-+ posPrev--;
-+ if (curOpt->prev2)
-+ {
-+ state = p->opt[curOpt->posPrev2].state;
-+ if (curOpt->backPrev2 < LZMA_NUM_REPS)
-+ state = kRepNextStates[state];
-+ else
-+ state = kMatchNextStates[state];
-+ }
-+ else
-+ state = p->opt[posPrev].state;
-+ state = kLiteralNextStates[state];
-+ }
-+ else
-+ state = p->opt[posPrev].state;
-+ if (posPrev == cur - 1)
-+ {
-+ if (IsShortRep(curOpt))
-+ state = kShortRepNextStates[state];
-+ else
-+ state = kLiteralNextStates[state];
-+ }
-+ else
-+ {
-+ UInt32 pos;
-+ const COptimal *prevOpt;
-+ if (curOpt->prev1IsChar && curOpt->prev2)
-+ {
-+ posPrev = curOpt->posPrev2;
-+ pos = curOpt->backPrev2;
-+ state = kRepNextStates[state];
-+ }
-+ else
-+ {
-+ pos = curOpt->backPrev;
-+ if (pos < LZMA_NUM_REPS)
-+ state = kRepNextStates[state];
-+ else
-+ state = kMatchNextStates[state];
-+ }
-+ prevOpt = &p->opt[posPrev];
-+ if (pos < LZMA_NUM_REPS)
-+ {
-+ UInt32 i;
-+ reps[0] = prevOpt->backs[pos];
-+ for (i = 1; i <= pos; i++)
-+ reps[i] = prevOpt->backs[i - 1];
-+ for (; i < LZMA_NUM_REPS; i++)
-+ reps[i] = prevOpt->backs[i];
-+ }
-+ else
-+ {
-+ UInt32 i;
-+ reps[0] = (pos - LZMA_NUM_REPS);
-+ for (i = 1; i < LZMA_NUM_REPS; i++)
-+ reps[i] = prevOpt->backs[i - 1];
-+ }
-+ }
-+ curOpt->state = (CState)state;
-+
-+ curOpt->backs[0] = reps[0];
-+ curOpt->backs[1] = reps[1];
-+ curOpt->backs[2] = reps[2];
-+ curOpt->backs[3] = reps[3];
-+
-+ curPrice = curOpt->price;
-+ nextIsChar = False;
-+ data = p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - 1;
-+ currentByte = *data;
-+ matchByte = *(data - (reps[0] + 1));
-+
-+ posState = (position & p->pbMask);
-+
-+ curAnd1Price = curPrice + GET_PRICE_0(p->isMatch[state][posState]);
-+ {
-+ const CLzmaProb *probs = LIT_PROBS(position, *(data - 1));
-+ curAnd1Price +=
-+ (!IsCharState(state) ?
-+ LitEnc_GetPriceMatched(probs, currentByte, matchByte, p->ProbPrices) :
-+ LitEnc_GetPrice(probs, currentByte, p->ProbPrices));
-+ }
-+
-+ nextOpt = &p->opt[cur + 1];
-+
-+ if (curAnd1Price < nextOpt->price)
-+ {
-+ nextOpt->price = curAnd1Price;
-+ nextOpt->posPrev = cur;
-+ MakeAsChar(nextOpt);
-+ nextIsChar = True;
-+ }
-+
-+ matchPrice = curPrice + GET_PRICE_1(p->isMatch[state][posState]);
-+ repMatchPrice = matchPrice + GET_PRICE_1(p->isRep[state]);
-+
-+ if (matchByte == currentByte && !(nextOpt->posPrev < cur && nextOpt->backPrev == 0))
-+ {
-+ UInt32 shortRepPrice = repMatchPrice + GetRepLen1Price(p, state, posState);
-+ if (shortRepPrice <= nextOpt->price)
-+ {
-+ nextOpt->price = shortRepPrice;
-+ nextOpt->posPrev = cur;
-+ MakeAsShortRep(nextOpt);
-+ nextIsChar = True;
-+ }
-+ }
-+
-+ {
-+ UInt32 temp = kNumOpts - 1 - cur;
-+ if (temp < numAvailableBytesFull)
-+ numAvailableBytesFull = temp;
-+ }
-+ numAvailableBytes = numAvailableBytesFull;
-+
-+ if (numAvailableBytes < 2)
-+ continue;
-+ if (numAvailableBytes > p->numFastBytes)
-+ numAvailableBytes = p->numFastBytes;
-+ if (!nextIsChar && matchByte != currentByte) /* speed optimization */
-+ {
-+ /* try Literal + rep0 */
-+ UInt32 temp;
-+ UInt32 lenTest2;
-+ const Byte *data2 = data - (reps[0] + 1);
-+ UInt32 limit = p->numFastBytes + 1;
-+ if (limit > numAvailableBytesFull)
-+ limit = numAvailableBytesFull;
-+
-+ for (temp = 1; temp < limit && data[temp] == data2[temp]; temp++);
-+ lenTest2 = temp - 1;
-+ if (lenTest2 >= 2)
-+ {
-+ UInt32 state2 = kLiteralNextStates[state];
-+ UInt32 posStateNext = (position + 1) & p->pbMask;
-+ UInt32 nextRepMatchPrice = curAnd1Price +
-+ GET_PRICE_1(p->isMatch[state2][posStateNext]) +
-+ GET_PRICE_1(p->isRep[state2]);
-+ /* for (; lenTest2 >= 2; lenTest2--) */
-+ {
-+ UInt32 curAndLenPrice;
-+ COptimal *opt;
-+ UInt32 offset = cur + 1 + lenTest2;
-+ while (lenEnd < offset)
-+ p->opt[++lenEnd].price = kInfinityPrice;
-+ curAndLenPrice = nextRepMatchPrice + GetRepPrice(p, 0, lenTest2, state2, posStateNext);
-+ opt = &p->opt[offset];
-+ if (curAndLenPrice < opt->price)
-+ {
-+ opt->price = curAndLenPrice;
-+ opt->posPrev = cur + 1;
-+ opt->backPrev = 0;
-+ opt->prev1IsChar = True;
-+ opt->prev2 = False;
-+ }
-+ }
-+ }
-+ }
-+
-+ startLen = 2; /* speed optimization */
-+ {
-+ UInt32 repIndex;
-+ for (repIndex = 0; repIndex < LZMA_NUM_REPS; repIndex++)
-+ {
-+ UInt32 lenTest;
-+ UInt32 lenTestTemp;
-+ UInt32 price;
-+ const Byte *data2 = data - (reps[repIndex] + 1);
-+ if (data[0] != data2[0] || data[1] != data2[1])
-+ continue;
-+ for (lenTest = 2; lenTest < numAvailableBytes && data[lenTest] == data2[lenTest]; lenTest++);
-+ while (lenEnd < cur + lenTest)
-+ p->opt[++lenEnd].price = kInfinityPrice;
-+ lenTestTemp = lenTest;
-+ price = repMatchPrice + GetPureRepPrice(p, repIndex, state, posState);
-+ do
-+ {
-+ UInt32 curAndLenPrice = price + p->repLenEnc.prices[posState][lenTest - 2];
-+ COptimal *opt = &p->opt[cur + lenTest];
-+ if (curAndLenPrice < opt->price)
-+ {
-+ opt->price = curAndLenPrice;
-+ opt->posPrev = cur;
-+ opt->backPrev = repIndex;
-+ opt->prev1IsChar = False;
-+ }
-+ }
-+ while (--lenTest >= 2);
-+ lenTest = lenTestTemp;
-+
-+ if (repIndex == 0)
-+ startLen = lenTest + 1;
-+
-+ /* if (_maxMode) */
-+ {
-+ UInt32 lenTest2 = lenTest + 1;
-+ UInt32 limit = lenTest2 + p->numFastBytes;
-+ UInt32 nextRepMatchPrice;
-+ if (limit > numAvailableBytesFull)
-+ limit = numAvailableBytesFull;
-+ for (; lenTest2 < limit && data[lenTest2] == data2[lenTest2]; lenTest2++);
-+ lenTest2 -= lenTest + 1;
-+ if (lenTest2 >= 2)
-+ {
-+ UInt32 state2 = kRepNextStates[state];
-+ UInt32 posStateNext = (position + lenTest) & p->pbMask;
-+ UInt32 curAndLenCharPrice =
-+ price + p->repLenEnc.prices[posState][lenTest - 2] +
-+ GET_PRICE_0(p->isMatch[state2][posStateNext]) +
-+ LitEnc_GetPriceMatched(LIT_PROBS(position + lenTest, data[lenTest - 1]),
-+ data[lenTest], data2[lenTest], p->ProbPrices);
-+ state2 = kLiteralNextStates[state2];
-+ posStateNext = (position + lenTest + 1) & p->pbMask;
-+ nextRepMatchPrice = curAndLenCharPrice +
-+ GET_PRICE_1(p->isMatch[state2][posStateNext]) +
-+ GET_PRICE_1(p->isRep[state2]);
-+
-+ /* for (; lenTest2 >= 2; lenTest2--) */
-+ {
-+ UInt32 curAndLenPrice;
-+ COptimal *opt;
-+ UInt32 offset = cur + lenTest + 1 + lenTest2;
-+ while (lenEnd < offset)
-+ p->opt[++lenEnd].price = kInfinityPrice;
-+ curAndLenPrice = nextRepMatchPrice + GetRepPrice(p, 0, lenTest2, state2, posStateNext);
-+ opt = &p->opt[offset];
-+ if (curAndLenPrice < opt->price)
-+ {
-+ opt->price = curAndLenPrice;
-+ opt->posPrev = cur + lenTest + 1;
-+ opt->backPrev = 0;
-+ opt->prev1IsChar = True;
-+ opt->prev2 = True;
-+ opt->posPrev2 = cur;
-+ opt->backPrev2 = repIndex;
-+ }
-+ }
-+ }
-+ }
-+ }
-+ }
-+ /* for (UInt32 lenTest = 2; lenTest <= newLen; lenTest++) */
-+ if (newLen > numAvailableBytes)
-+ {
-+ newLen = numAvailableBytes;
-+ for (numDistancePairs = 0; newLen > matchDistances[numDistancePairs]; numDistancePairs += 2);
-+ matchDistances[numDistancePairs] = newLen;
-+ numDistancePairs += 2;
-+ }
-+ if (newLen >= startLen)
-+ {
-+ UInt32 normalMatchPrice = matchPrice + GET_PRICE_0(p->isRep[state]);
-+ UInt32 offs, curBack, posSlot;
-+ UInt32 lenTest;
-+ while (lenEnd < cur + newLen)
-+ p->opt[++lenEnd].price = kInfinityPrice;
-+
-+ offs = 0;
-+ while (startLen > matchDistances[offs])
-+ offs += 2;
-+ curBack = matchDistances[offs + 1];
-+ GetPosSlot2(curBack, posSlot);
-+ for (lenTest = /*2*/ startLen; ; lenTest++)
-+ {
-+ UInt32 curAndLenPrice = normalMatchPrice + p->lenEnc.prices[posState][lenTest - LZMA_MATCH_LEN_MIN];
-+ UInt32 lenToPosState = GetLenToPosState(lenTest);
-+ COptimal *opt;
-+ if (curBack < kNumFullDistances)
-+ curAndLenPrice += p->distancesPrices[lenToPosState][curBack];
-+ else
-+ curAndLenPrice += p->posSlotPrices[lenToPosState][posSlot] + p->alignPrices[curBack & kAlignMask];
-+
-+ opt = &p->opt[cur + lenTest];
-+ if (curAndLenPrice < opt->price)
-+ {
-+ opt->price = curAndLenPrice;
-+ opt->posPrev = cur;
-+ opt->backPrev = curBack + LZMA_NUM_REPS;
-+ opt->prev1IsChar = False;
-+ }
-+
-+ if (/*_maxMode && */lenTest == matchDistances[offs])
-+ {
-+ /* Try Match + Literal + Rep0 */
-+ const Byte *data2 = data - (curBack + 1);
-+ UInt32 lenTest2 = lenTest + 1;
-+ UInt32 limit = lenTest2 + p->numFastBytes;
-+ UInt32 nextRepMatchPrice;
-+ if (limit > numAvailableBytesFull)
-+ limit = numAvailableBytesFull;
-+ for (; lenTest2 < limit && data[lenTest2] == data2[lenTest2]; lenTest2++);
-+ lenTest2 -= lenTest + 1;
-+ if (lenTest2 >= 2)
-+ {
-+ UInt32 state2 = kMatchNextStates[state];
-+ UInt32 posStateNext = (position + lenTest) & p->pbMask;
-+ UInt32 curAndLenCharPrice = curAndLenPrice +
-+ GET_PRICE_0(p->isMatch[state2][posStateNext]) +
-+ LitEnc_GetPriceMatched(LIT_PROBS(position + lenTest, data[lenTest - 1]),
-+ data[lenTest], data2[lenTest], p->ProbPrices);
-+ state2 = kLiteralNextStates[state2];
-+ posStateNext = (posStateNext + 1) & p->pbMask;
-+ nextRepMatchPrice = curAndLenCharPrice +
-+ GET_PRICE_1(p->isMatch[state2][posStateNext]) +
-+ GET_PRICE_1(p->isRep[state2]);
-+
-+ /* for (; lenTest2 >= 2; lenTest2--) */
-+ {
-+ UInt32 offset = cur + lenTest + 1 + lenTest2;
-+ UInt32 curAndLenPrice;
-+ COptimal *opt;
-+ while (lenEnd < offset)
-+ p->opt[++lenEnd].price = kInfinityPrice;
-+ curAndLenPrice = nextRepMatchPrice + GetRepPrice(p, 0, lenTest2, state2, posStateNext);
-+ opt = &p->opt[offset];
-+ if (curAndLenPrice < opt->price)
-+ {
-+ opt->price = curAndLenPrice;
-+ opt->posPrev = cur + lenTest + 1;
-+ opt->backPrev = 0;
-+ opt->prev1IsChar = True;
-+ opt->prev2 = True;
-+ opt->posPrev2 = cur;
-+ opt->backPrev2 = curBack + LZMA_NUM_REPS;
-+ }
-+ }
-+ }
-+ offs += 2;
-+ if (offs == numDistancePairs)
-+ break;
-+ curBack = matchDistances[offs + 1];
-+ if (curBack >= kNumFullDistances)
-+ GetPosSlot2(curBack, posSlot);
-+ }
-+ }
-+ }
-+ }
-+}
-+
-+#define ChangePair(smallDist, bigDist) (((bigDist) >> 7) > (smallDist))
-+
-+static UInt32 GetOptimumFast(CLzmaEnc *p, UInt32 *backRes)
-+{
-+ UInt32 numAvailableBytes = p->matchFinder.GetNumAvailableBytes(p->matchFinderObj);
-+ UInt32 lenMain, numDistancePairs;
-+ const Byte *data;
-+ UInt32 repLens[LZMA_NUM_REPS];
-+ UInt32 repMaxIndex, i;
-+ UInt32 *matchDistances;
-+ UInt32 backMain;
-+
-+ if (!p->longestMatchWasFound)
-+ {
-+ lenMain = ReadMatchDistances(p, &numDistancePairs);
-+ }
-+ else
-+ {
-+ lenMain = p->longestMatchLength;
-+ numDistancePairs = p->numDistancePairs;
-+ p->longestMatchWasFound = False;
-+ }
-+
-+ data = p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - 1;
-+ if (numAvailableBytes > LZMA_MATCH_LEN_MAX)
-+ numAvailableBytes = LZMA_MATCH_LEN_MAX;
-+ if (numAvailableBytes < 2)
-+ {
-+ *backRes = (UInt32)(-1);
-+ return 1;
-+ }
-+
-+ repMaxIndex = 0;
-+
-+ for (i = 0; i < LZMA_NUM_REPS; i++)
-+ {
-+ const Byte *data2 = data - (p->reps[i] + 1);
-+ UInt32 len;
-+ if (data[0] != data2[0] || data[1] != data2[1])
-+ {
-+ repLens[i] = 0;
-+ continue;
-+ }
-+ for (len = 2; len < numAvailableBytes && data[len] == data2[len]; len++);
-+ if (len >= p->numFastBytes)
-+ {
-+ *backRes = i;
-+ MovePos(p, len - 1);
-+ return len;
-+ }
-+ repLens[i] = len;
-+ if (len > repLens[repMaxIndex])
-+ repMaxIndex = i;
-+ }
-+ matchDistances = p->matchDistances;
-+ if (lenMain >= p->numFastBytes)
-+ {
-+ *backRes = matchDistances[numDistancePairs - 1] + LZMA_NUM_REPS;
-+ MovePos(p, lenMain - 1);
-+ return lenMain;
-+ }
-+
-+ backMain = 0; /* for GCC */
-+ if (lenMain >= 2)
-+ {
-+ backMain = matchDistances[numDistancePairs - 1];
-+ while (numDistancePairs > 2 && lenMain == matchDistances[numDistancePairs - 4] + 1)
-+ {
-+ if (!ChangePair(matchDistances[numDistancePairs - 3], backMain))
-+ break;
-+ numDistancePairs -= 2;
-+ lenMain = matchDistances[numDistancePairs - 2];
-+ backMain = matchDistances[numDistancePairs - 1];
-+ }
-+ if (lenMain == 2 && backMain >= 0x80)
-+ lenMain = 1;
-+ }
-+
-+ if (repLens[repMaxIndex] >= 2)
-+ {
-+ if (repLens[repMaxIndex] + 1 >= lenMain ||
-+ (repLens[repMaxIndex] + 2 >= lenMain && (backMain > (1 << 9))) ||
-+ (repLens[repMaxIndex] + 3 >= lenMain && (backMain > (1 << 15))))
-+ {
-+ UInt32 lenRes;
-+ *backRes = repMaxIndex;
-+ lenRes = repLens[repMaxIndex];
-+ MovePos(p, lenRes - 1);
-+ return lenRes;
-+ }
-+ }
-+
-+ if (lenMain >= 2 && numAvailableBytes > 2)
-+ {
-+ UInt32 i;
-+ numAvailableBytes = p->matchFinder.GetNumAvailableBytes(p->matchFinderObj);
-+ p->longestMatchLength = ReadMatchDistances(p, &p->numDistancePairs);
-+ if (p->longestMatchLength >= 2)
-+ {
-+ UInt32 newDistance = matchDistances[p->numDistancePairs - 1];
-+ if ((p->longestMatchLength >= lenMain && newDistance < backMain) ||
-+ (p->longestMatchLength == lenMain + 1 && !ChangePair(backMain, newDistance)) ||
-+ (p->longestMatchLength > lenMain + 1) ||
-+ (p->longestMatchLength + 1 >= lenMain && lenMain >= 3 && ChangePair(newDistance, backMain)))
-+ {
-+ p->longestMatchWasFound = True;
-+ *backRes = (UInt32)(-1);
-+ return 1;
-+ }
-+ }
-+ data = p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - 1;
-+ for (i = 0; i < LZMA_NUM_REPS; i++)
-+ {
-+ UInt32 len;
-+ const Byte *data2 = data - (p->reps[i] + 1);
-+ if (data[1] != data2[1] || data[2] != data2[2])
-+ {
-+ repLens[i] = 0;
-+ continue;
-+ }
-+ for (len = 2; len < numAvailableBytes && data[len] == data2[len]; len++);
-+ if (len + 1 >= lenMain)
-+ {
-+ p->longestMatchWasFound = True;
-+ *backRes = (UInt32)(-1);
-+ return 1;
-+ }
-+ }
-+ *backRes = backMain + LZMA_NUM_REPS;
-+ MovePos(p, lenMain - 2);
-+ return lenMain;
-+ }
-+ *backRes = (UInt32)(-1);
-+ return 1;
-+}
-+
-+static void WriteEndMarker(CLzmaEnc *p, UInt32 posState)
-+{
-+ UInt32 len;
-+ RangeEnc_EncodeBit(&p->rc, &p->isMatch[p->state][posState], 1);
-+ RangeEnc_EncodeBit(&p->rc, &p->isRep[p->state], 0);
-+ p->state = kMatchNextStates[p->state];
-+ len = LZMA_MATCH_LEN_MIN;
-+ LenEnc_Encode2(&p->lenEnc, &p->rc, len - LZMA_MATCH_LEN_MIN, posState, !p->fastMode, p->ProbPrices);
-+ RcTree_Encode(&p->rc, p->posSlotEncoder[GetLenToPosState(len)], kNumPosSlotBits, (1 << kNumPosSlotBits) - 1);
-+ RangeEnc_EncodeDirectBits(&p->rc, (((UInt32)1 << 30) - 1) >> kNumAlignBits, 30 - kNumAlignBits);
-+ RcTree_ReverseEncode(&p->rc, p->posAlignEncoder, kNumAlignBits, kAlignMask);
-+}
-+
-+static SRes CheckErrors(CLzmaEnc *p)
-+{
-+ if (p->result != SZ_OK)
-+ return p->result;
-+ if (p->rc.res != SZ_OK)
-+ p->result = SZ_ERROR_WRITE;
-+ if (p->matchFinderBase.result != SZ_OK)
-+ p->result = SZ_ERROR_READ;
-+ if (p->result != SZ_OK)
-+ p->finished = True;
-+ return p->result;
-+}
-+
-+static SRes Flush(CLzmaEnc *p, UInt32 nowPos)
-+{
-+ /* ReleaseMFStream(); */
-+ p->finished = True;
-+ if (p->writeEndMark)
-+ WriteEndMarker(p, nowPos & p->pbMask);
-+ RangeEnc_FlushData(&p->rc);
-+ RangeEnc_FlushStream(&p->rc);
-+ return CheckErrors(p);
-+}
-+
-+static void FillAlignPrices(CLzmaEnc *p)
-+{
-+ UInt32 i;
-+ for (i = 0; i < kAlignTableSize; i++)
-+ p->alignPrices[i] = RcTree_ReverseGetPrice(p->posAlignEncoder, kNumAlignBits, i, p->ProbPrices);
-+ p->alignPriceCount = 0;
-+}
-+
-+static void FillDistancesPrices(CLzmaEnc *p)
-+{
-+ UInt32 tempPrices[kNumFullDistances];
-+ UInt32 i, lenToPosState;
-+ for (i = kStartPosModelIndex; i < kNumFullDistances; i++)
-+ {
-+ UInt32 posSlot = GetPosSlot1(i);
-+ UInt32 footerBits = ((posSlot >> 1) - 1);
-+ UInt32 base = ((2 | (posSlot & 1)) << footerBits);
-+ tempPrices[i] = RcTree_ReverseGetPrice(p->posEncoders + base - posSlot - 1, footerBits, i - base, p->ProbPrices);
-+ }
-+
-+ for (lenToPosState = 0; lenToPosState < kNumLenToPosStates; lenToPosState++)
-+ {
-+ UInt32 posSlot;
-+ const CLzmaProb *encoder = p->posSlotEncoder[lenToPosState];
-+ UInt32 *posSlotPrices = p->posSlotPrices[lenToPosState];
-+ for (posSlot = 0; posSlot < p->distTableSize; posSlot++)
-+ posSlotPrices[posSlot] = RcTree_GetPrice(encoder, kNumPosSlotBits, posSlot, p->ProbPrices);
-+ for (posSlot = kEndPosModelIndex; posSlot < p->distTableSize; posSlot++)
-+ posSlotPrices[posSlot] += ((((posSlot >> 1) - 1) - kNumAlignBits) << kNumBitPriceShiftBits);
-+
-+ {
-+ UInt32 *distancesPrices = p->distancesPrices[lenToPosState];
-+ UInt32 i;
-+ for (i = 0; i < kStartPosModelIndex; i++)
-+ distancesPrices[i] = posSlotPrices[i];
-+ for (; i < kNumFullDistances; i++)
-+ distancesPrices[i] = posSlotPrices[GetPosSlot1(i)] + tempPrices[i];
-+ }
-+ }
-+ p->matchPriceCount = 0;
-+}
-+
-+static void LzmaEnc_Construct(CLzmaEnc *p)
-+{
-+ RangeEnc_Construct(&p->rc);
-+ MatchFinder_Construct(&p->matchFinderBase);
-+ #ifdef COMPRESS_MF_MT
-+ MatchFinderMt_Construct(&p->matchFinderMt);
-+ p->matchFinderMt.MatchFinder = &p->matchFinderBase;
-+ #endif
-+
-+ {
-+ CLzmaEncProps props;
-+ LzmaEncProps_Init(&props);
-+ LzmaEnc_SetProps(p, &props);
-+ }
-+
-+ #ifndef LZMA_LOG_BSR
-+ LzmaEnc_FastPosInit(p->g_FastPos);
-+ #endif
-+
-+ LzmaEnc_InitPriceTables(p->ProbPrices);
-+ p->litProbs = 0;
-+ p->saveState.litProbs = 0;
-+}
-+
-+CLzmaEncHandle LzmaEnc_Create(ISzAlloc *alloc)
-+{
-+ void *p;
-+ p = alloc->Alloc(alloc, sizeof(CLzmaEnc));
-+ if (p != 0)
-+ LzmaEnc_Construct((CLzmaEnc *)p);
-+ return p;
-+}
-+
-+static void LzmaEnc_FreeLits(CLzmaEnc *p, ISzAlloc *alloc)
-+{
-+ alloc->Free(alloc, p->litProbs);
-+ alloc->Free(alloc, p->saveState.litProbs);
-+ p->litProbs = 0;
-+ p->saveState.litProbs = 0;
-+}
-+
-+static void LzmaEnc_Destruct(CLzmaEnc *p, ISzAlloc *alloc, ISzAlloc *allocBig)
-+{
-+ #ifdef COMPRESS_MF_MT
-+ MatchFinderMt_Destruct(&p->matchFinderMt, allocBig);
-+ #endif
-+ MatchFinder_Free(&p->matchFinderBase, allocBig);
-+ LzmaEnc_FreeLits(p, alloc);
-+ RangeEnc_Free(&p->rc, alloc);
-+}
-+
-+void LzmaEnc_Destroy(CLzmaEncHandle p, ISzAlloc *alloc, ISzAlloc *allocBig)
-+{
-+ LzmaEnc_Destruct((CLzmaEnc *)p, alloc, allocBig);
-+ alloc->Free(alloc, p);
-+}
-+
-+static SRes LzmaEnc_CodeOneBlock(CLzmaEnc *p, Bool useLimits, UInt32 maxPackSize, UInt32 maxUnpackSize)
-+{
-+ UInt32 nowPos32, startPos32;
-+ if (p->inStream != 0)
-+ {
-+ p->matchFinderBase.stream = p->inStream;
-+ p->matchFinder.Init(p->matchFinderObj);
-+ p->inStream = 0;
-+ }
-+
-+ if (p->finished)
-+ return p->result;
-+ RINOK(CheckErrors(p));
-+
-+ nowPos32 = (UInt32)p->nowPos64;
-+ startPos32 = nowPos32;
-+
-+ if (p->nowPos64 == 0)
-+ {
-+ UInt32 numDistancePairs;
-+ Byte curByte;
-+ if (p->matchFinder.GetNumAvailableBytes(p->matchFinderObj) == 0)
-+ return Flush(p, nowPos32);
-+ ReadMatchDistances(p, &numDistancePairs);
-+ RangeEnc_EncodeBit(&p->rc, &p->isMatch[p->state][0], 0);
-+ p->state = kLiteralNextStates[p->state];
-+ curByte = p->matchFinder.GetIndexByte(p->matchFinderObj, 0 - p->additionalOffset);
-+ LitEnc_Encode(&p->rc, p->litProbs, curByte);
-+ p->additionalOffset--;
-+ nowPos32++;
-+ }
-+
-+ if (p->matchFinder.GetNumAvailableBytes(p->matchFinderObj) != 0)
-+ for (;;)
-+ {
-+ UInt32 pos, len, posState;
-+
-+ if (p->fastMode)
-+ len = GetOptimumFast(p, &pos);
-+ else
-+ len = GetOptimum(p, nowPos32, &pos);
-+
-+ #ifdef SHOW_STAT2
-+ printf("\n pos = %4X, len = %d pos = %d", nowPos32, len, pos);
-+ #endif
-+
-+ posState = nowPos32 & p->pbMask;
-+ if (len == 1 && pos == 0xFFFFFFFF)
-+ {
-+ Byte curByte;
-+ CLzmaProb *probs;
-+ const Byte *data;
-+
-+ RangeEnc_EncodeBit(&p->rc, &p->isMatch[p->state][posState], 0);
-+ data = p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - p->additionalOffset;
-+ curByte = *data;
-+ probs = LIT_PROBS(nowPos32, *(data - 1));
-+ if (IsCharState(p->state))
-+ LitEnc_Encode(&p->rc, probs, curByte);
-+ else
-+ LitEnc_EncodeMatched(&p->rc, probs, curByte, *(data - p->reps[0] - 1));
-+ p->state = kLiteralNextStates[p->state];
-+ }
-+ else
-+ {
-+ RangeEnc_EncodeBit(&p->rc, &p->isMatch[p->state][posState], 1);
-+ if (pos < LZMA_NUM_REPS)
-+ {
-+ RangeEnc_EncodeBit(&p->rc, &p->isRep[p->state], 1);
-+ if (pos == 0)
-+ {
-+ RangeEnc_EncodeBit(&p->rc, &p->isRepG0[p->state], 0);
-+ RangeEnc_EncodeBit(&p->rc, &p->isRep0Long[p->state][posState], ((len == 1) ? 0 : 1));
-+ }
-+ else
-+ {
-+ UInt32 distance = p->reps[pos];
-+ RangeEnc_EncodeBit(&p->rc, &p->isRepG0[p->state], 1);
-+ if (pos == 1)
-+ RangeEnc_EncodeBit(&p->rc, &p->isRepG1[p->state], 0);
-+ else
-+ {
-+ RangeEnc_EncodeBit(&p->rc, &p->isRepG1[p->state], 1);
-+ RangeEnc_EncodeBit(&p->rc, &p->isRepG2[p->state], pos - 2);
-+ if (pos == 3)
-+ p->reps[3] = p->reps[2];
-+ p->reps[2] = p->reps[1];
-+ }
-+ p->reps[1] = p->reps[0];
-+ p->reps[0] = distance;
-+ }
-+ if (len == 1)
-+ p->state = kShortRepNextStates[p->state];
-+ else
-+ {
-+ LenEnc_Encode2(&p->repLenEnc, &p->rc, len - LZMA_MATCH_LEN_MIN, posState, !p->fastMode, p->ProbPrices);
-+ p->state = kRepNextStates[p->state];
-+ }
-+ }
-+ else
-+ {
-+ UInt32 posSlot;
-+ RangeEnc_EncodeBit(&p->rc, &p->isRep[p->state], 0);
-+ p->state = kMatchNextStates[p->state];
-+ LenEnc_Encode2(&p->lenEnc, &p->rc, len - LZMA_MATCH_LEN_MIN, posState, !p->fastMode, p->ProbPrices);
-+ pos -= LZMA_NUM_REPS;
-+ GetPosSlot(pos, posSlot);
-+ RcTree_Encode(&p->rc, p->posSlotEncoder[GetLenToPosState(len)], kNumPosSlotBits, posSlot);
-+
-+ if (posSlot >= kStartPosModelIndex)
-+ {
-+ UInt32 footerBits = ((posSlot >> 1) - 1);
-+ UInt32 base = ((2 | (posSlot & 1)) << footerBits);
-+ UInt32 posReduced = pos - base;
-+
-+ if (posSlot < kEndPosModelIndex)
-+ RcTree_ReverseEncode(&p->rc, p->posEncoders + base - posSlot - 1, footerBits, posReduced);
-+ else
-+ {
-+ RangeEnc_EncodeDirectBits(&p->rc, posReduced >> kNumAlignBits, footerBits - kNumAlignBits);
-+ RcTree_ReverseEncode(&p->rc, p->posAlignEncoder, kNumAlignBits, posReduced & kAlignMask);
-+ p->alignPriceCount++;
-+ }
-+ }
-+ p->reps[3] = p->reps[2];
-+ p->reps[2] = p->reps[1];
-+ p->reps[1] = p->reps[0];
-+ p->reps[0] = pos;
-+ p->matchPriceCount++;
-+ }
-+ }
-+ p->additionalOffset -= len;
-+ nowPos32 += len;
-+ if (p->additionalOffset == 0)
-+ {
-+ UInt32 processed;
-+ if (!p->fastMode)
-+ {
-+ if (p->matchPriceCount >= (1 << 7))
-+ FillDistancesPrices(p);
-+ if (p->alignPriceCount >= kAlignTableSize)
-+ FillAlignPrices(p);
-+ }
-+ if (p->matchFinder.GetNumAvailableBytes(p->matchFinderObj) == 0)
-+ break;
-+ processed = nowPos32 - startPos32;
-+ if (useLimits)
-+ {
-+ if (processed + kNumOpts + 300 >= maxUnpackSize ||
-+ RangeEnc_GetProcessed(&p->rc) + kNumOpts * 2 >= maxPackSize)
-+ break;
-+ }
-+ else if (processed >= (1 << 15))
-+ {
-+ p->nowPos64 += nowPos32 - startPos32;
-+ return CheckErrors(p);
-+ }
-+ }
-+ }
-+ p->nowPos64 += nowPos32 - startPos32;
-+ return Flush(p, nowPos32);
-+}
-+
-+#define kBigHashDicLimit ((UInt32)1 << 24)
-+
-+static SRes LzmaEnc_Alloc(CLzmaEnc *p, UInt32 keepWindowSize, ISzAlloc *alloc, ISzAlloc *allocBig)
-+{
-+ UInt32 beforeSize = kNumOpts;
-+ Bool btMode;
-+ if (!RangeEnc_Alloc(&p->rc, alloc))
-+ return SZ_ERROR_MEM;
-+ btMode = (p->matchFinderBase.btMode != 0);
-+ #ifdef COMPRESS_MF_MT
-+ p->mtMode = (p->multiThread && !p->fastMode && btMode);
-+ #endif
-+
-+ {
-+ unsigned lclp = p->lc + p->lp;
-+ if (p->litProbs == 0 || p->saveState.litProbs == 0 || p->lclp != lclp)
-+ {
-+ LzmaEnc_FreeLits(p, alloc);
-+ p->litProbs = (CLzmaProb *)alloc->Alloc(alloc, (0x300 << lclp) * sizeof(CLzmaProb));
-+ p->saveState.litProbs = (CLzmaProb *)alloc->Alloc(alloc, (0x300 << lclp) * sizeof(CLzmaProb));
-+ if (p->litProbs == 0 || p->saveState.litProbs == 0)
-+ {
-+ LzmaEnc_FreeLits(p, alloc);
-+ return SZ_ERROR_MEM;
-+ }
-+ p->lclp = lclp;
-+ }
-+ }
-+
-+ p->matchFinderBase.bigHash = (p->dictSize > kBigHashDicLimit);
-+
-+ if (beforeSize + p->dictSize < keepWindowSize)
-+ beforeSize = keepWindowSize - p->dictSize;
-+
-+ #ifdef COMPRESS_MF_MT
-+ if (p->mtMode)
-+ {
-+ RINOK(MatchFinderMt_Create(&p->matchFinderMt, p->dictSize, beforeSize, p->numFastBytes, LZMA_MATCH_LEN_MAX, allocBig));
-+ p->matchFinderObj = &p->matchFinderMt;
-+ MatchFinderMt_CreateVTable(&p->matchFinderMt, &p->matchFinder);
-+ }
-+ else
-+ #endif
-+ {
-+ if (!MatchFinder_Create(&p->matchFinderBase, p->dictSize, beforeSize, p->numFastBytes, LZMA_MATCH_LEN_MAX, allocBig))
-+ return SZ_ERROR_MEM;
-+ p->matchFinderObj = &p->matchFinderBase;
-+ MatchFinder_CreateVTable(&p->matchFinderBase, &p->matchFinder);
-+ }
-+ return SZ_OK;
-+}
-+
-+static void LzmaEnc_Init(CLzmaEnc *p)
-+{
-+ UInt32 i;
-+ p->state = 0;
-+ for(i = 0 ; i < LZMA_NUM_REPS; i++)
-+ p->reps[i] = 0;
-+
-+ RangeEnc_Init(&p->rc);
-+
-+
-+ for (i = 0; i < kNumStates; i++)
-+ {
-+ UInt32 j;
-+ for (j = 0; j < LZMA_NUM_PB_STATES_MAX; j++)
-+ {
-+ p->isMatch[i][j] = kProbInitValue;
-+ p->isRep0Long[i][j] = kProbInitValue;
-+ }
-+ p->isRep[i] = kProbInitValue;
-+ p->isRepG0[i] = kProbInitValue;
-+ p->isRepG1[i] = kProbInitValue;
-+ p->isRepG2[i] = kProbInitValue;
-+ }
-+
-+ {
-+ UInt32 num = 0x300 << (p->lp + p->lc);
-+ for (i = 0; i < num; i++)
-+ p->litProbs[i] = kProbInitValue;
-+ }
-+
-+ {
-+ for (i = 0; i < kNumLenToPosStates; i++)
-+ {
-+ CLzmaProb *probs = p->posSlotEncoder[i];
-+ UInt32 j;
-+ for (j = 0; j < (1 << kNumPosSlotBits); j++)
-+ probs[j] = kProbInitValue;
-+ }
-+ }
-+ {
-+ for(i = 0; i < kNumFullDistances - kEndPosModelIndex; i++)
-+ p->posEncoders[i] = kProbInitValue;
-+ }
-+
-+ LenEnc_Init(&p->lenEnc.p);
-+ LenEnc_Init(&p->repLenEnc.p);
-+
-+ for (i = 0; i < (1 << kNumAlignBits); i++)
-+ p->posAlignEncoder[i] = kProbInitValue;
-+
-+ p->longestMatchWasFound = False;
-+ p->optimumEndIndex = 0;
-+ p->optimumCurrentIndex = 0;
-+ p->additionalOffset = 0;
-+
-+ p->pbMask = (1 << p->pb) - 1;
-+ p->lpMask = (1 << p->lp) - 1;
-+}
-+
-+static void LzmaEnc_InitPrices(CLzmaEnc *p)
-+{
-+ if (!p->fastMode)
-+ {
-+ FillDistancesPrices(p);
-+ FillAlignPrices(p);
-+ }
-+
-+ p->lenEnc.tableSize =
-+ p->repLenEnc.tableSize =
-+ p->numFastBytes + 1 - LZMA_MATCH_LEN_MIN;
-+ LenPriceEnc_UpdateTables(&p->lenEnc, 1 << p->pb, p->ProbPrices);
-+ LenPriceEnc_UpdateTables(&p->repLenEnc, 1 << p->pb, p->ProbPrices);
-+}
-+
-+static SRes LzmaEnc_AllocAndInit(CLzmaEnc *p, UInt32 keepWindowSize, ISzAlloc *alloc, ISzAlloc *allocBig)
-+{
-+ UInt32 i;
-+ for (i = 0; i < (UInt32)kDicLogSizeMaxCompress; i++)
-+ if (p->dictSize <= ((UInt32)1 << i))
-+ break;
-+ p->distTableSize = i * 2;
-+
-+ p->finished = False;
-+ p->result = SZ_OK;
-+ RINOK(LzmaEnc_Alloc(p, keepWindowSize, alloc, allocBig));
-+ LzmaEnc_Init(p);
-+ LzmaEnc_InitPrices(p);
-+ p->nowPos64 = 0;
-+ return SZ_OK;
-+}
-+
-+static SRes LzmaEnc_Prepare(CLzmaEncHandle pp, ISeqInStream *inStream, ISeqOutStream *outStream,
-+ ISzAlloc *alloc, ISzAlloc *allocBig)
-+{
-+ CLzmaEnc *p = (CLzmaEnc *)pp;
-+ p->inStream = inStream;
-+ p->rc.outStream = outStream;
-+ return LzmaEnc_AllocAndInit(p, 0, alloc, allocBig);
-+}
-+
-+static SRes LzmaEnc_PrepareForLzma2(CLzmaEncHandle pp,
-+ ISeqInStream *inStream, UInt32 keepWindowSize,
-+ ISzAlloc *alloc, ISzAlloc *allocBig)
-+{
-+ CLzmaEnc *p = (CLzmaEnc *)pp;
-+ p->inStream = inStream;
-+ return LzmaEnc_AllocAndInit(p, keepWindowSize, alloc, allocBig);
-+}
-+
-+static void LzmaEnc_SetInputBuf(CLzmaEnc *p, const Byte *src, SizeT srcLen)
-+{
-+ p->seqBufInStream.funcTable.Read = MyRead;
-+ p->seqBufInStream.data = src;
-+ p->seqBufInStream.rem = srcLen;
-+}
-+
-+static SRes LzmaEnc_MemPrepare(CLzmaEncHandle pp, const Byte *src, SizeT srcLen,
-+ UInt32 keepWindowSize, ISzAlloc *alloc, ISzAlloc *allocBig)
-+{
-+ CLzmaEnc *p = (CLzmaEnc *)pp;
-+ LzmaEnc_SetInputBuf(p, src, srcLen);
-+ p->inStream = &p->seqBufInStream.funcTable;
-+ return LzmaEnc_AllocAndInit(p, keepWindowSize, alloc, allocBig);
-+}
-+
-+static void LzmaEnc_Finish(CLzmaEncHandle pp)
-+{
-+ #ifdef COMPRESS_MF_MT
-+ CLzmaEnc *p = (CLzmaEnc *)pp;
-+ if (p->mtMode)
-+ MatchFinderMt_ReleaseStream(&p->matchFinderMt);
-+ #endif
-+}
-+
-+typedef struct _CSeqOutStreamBuf
-+{
-+ ISeqOutStream funcTable;
-+ Byte *data;
-+ SizeT rem;
-+ Bool overflow;
-+} CSeqOutStreamBuf;
-+
-+static size_t MyWrite(void *pp, const void *data, size_t size)
-+{
-+ CSeqOutStreamBuf *p = (CSeqOutStreamBuf *)pp;
-+ if (p->rem < size)
-+ {
-+ size = p->rem;
-+ p->overflow = True;
-+ }
-+ memcpy(p->data, data, size);
-+ p->rem -= size;
-+ p->data += size;
-+ return size;
-+}
-+
-+
-+static UInt32 LzmaEnc_GetNumAvailableBytes(CLzmaEncHandle pp)
-+{
-+ const CLzmaEnc *p = (CLzmaEnc *)pp;
-+ return p->matchFinder.GetNumAvailableBytes(p->matchFinderObj);
-+}
-+
-+static const Byte *LzmaEnc_GetCurBuf(CLzmaEncHandle pp)
-+{
-+ const CLzmaEnc *p = (CLzmaEnc *)pp;
-+ return p->matchFinder.GetPointerToCurrentPos(p->matchFinderObj) - p->additionalOffset;
-+}
-+
-+static SRes LzmaEnc_CodeOneMemBlock(CLzmaEncHandle pp, Bool reInit,
-+ Byte *dest, size_t *destLen, UInt32 desiredPackSize, UInt32 *unpackSize)
-+{
-+ CLzmaEnc *p = (CLzmaEnc *)pp;
-+ UInt64 nowPos64;
-+ SRes res;
-+ CSeqOutStreamBuf outStream;
-+
-+ outStream.funcTable.Write = MyWrite;
-+ outStream.data = dest;
-+ outStream.rem = *destLen;
-+ outStream.overflow = False;
-+
-+ p->writeEndMark = False;
-+ p->finished = False;
-+ p->result = SZ_OK;
-+
-+ if (reInit)
-+ LzmaEnc_Init(p);
-+ LzmaEnc_InitPrices(p);
-+ nowPos64 = p->nowPos64;
-+ RangeEnc_Init(&p->rc);
-+ p->rc.outStream = &outStream.funcTable;
-+
-+ res = LzmaEnc_CodeOneBlock(pp, True, desiredPackSize, *unpackSize);
-+
-+ *unpackSize = (UInt32)(p->nowPos64 - nowPos64);
-+ *destLen -= outStream.rem;
-+ if (outStream.overflow)
-+ return SZ_ERROR_OUTPUT_EOF;
-+
-+ return res;
-+}
-+
-+SRes LzmaEnc_Encode(CLzmaEncHandle pp, ISeqOutStream *outStream, ISeqInStream *inStream, ICompressProgress *progress,
-+ ISzAlloc *alloc, ISzAlloc *allocBig)
-+{
-+ CLzmaEnc *p = (CLzmaEnc *)pp;
-+ SRes res = SZ_OK;
-+
-+ #ifdef COMPRESS_MF_MT
-+ Byte allocaDummy[0x300];
-+ int i = 0;
-+ for (i = 0; i < 16; i++)
-+ allocaDummy[i] = (Byte)i;
-+ #endif
-+
-+ RINOK(LzmaEnc_Prepare(pp, inStream, outStream, alloc, allocBig));
-+
-+ for (;;)
-+ {
-+ res = LzmaEnc_CodeOneBlock(pp, False, 0, 0);
-+ if (res != SZ_OK || p->finished != 0)
-+ break;
-+ if (progress != 0)
-+ {
-+ res = progress->Progress(progress, p->nowPos64, RangeEnc_GetProcessed(&p->rc));
-+ if (res != SZ_OK)
-+ {
-+ res = SZ_ERROR_PROGRESS;
-+ break;
-+ }
-+ }
-+ }
-+ LzmaEnc_Finish(pp);
-+ return res;
-+}
-+
-+SRes LzmaEnc_WriteProperties(CLzmaEncHandle pp, Byte *props, SizeT *size)
-+{
-+ CLzmaEnc *p = (CLzmaEnc *)pp;
-+ int i;
-+ UInt32 dictSize = p->dictSize;
-+ if (*size < LZMA_PROPS_SIZE)
-+ return SZ_ERROR_PARAM;
-+ *size = LZMA_PROPS_SIZE;
-+ props[0] = (Byte)((p->pb * 5 + p->lp) * 9 + p->lc);
-+
-+ for (i = 11; i <= 30; i++)
-+ {
-+ if (dictSize <= ((UInt32)2 << i))
-+ {
-+ dictSize = (2 << i);
-+ break;
-+ }
-+ if (dictSize <= ((UInt32)3 << i))
-+ {
-+ dictSize = (3 << i);
-+ break;
-+ }
-+ }
-+
-+ for (i = 0; i < 4; i++)
-+ props[1 + i] = (Byte)(dictSize >> (8 * i));
-+ return SZ_OK;
-+}
-+
-+SRes LzmaEnc_MemEncode(CLzmaEncHandle pp, Byte *dest, SizeT *destLen, const Byte *src, SizeT srcLen,
-+ int writeEndMark, ICompressProgress *progress, ISzAlloc *alloc, ISzAlloc *allocBig)
-+{
-+ SRes res;
-+ CLzmaEnc *p = (CLzmaEnc *)pp;
-+
-+ CSeqOutStreamBuf outStream;
-+
-+ LzmaEnc_SetInputBuf(p, src, srcLen);
-+
-+ outStream.funcTable.Write = MyWrite;
-+ outStream.data = dest;
-+ outStream.rem = *destLen;
-+ outStream.overflow = False;
-+
-+ p->writeEndMark = writeEndMark;
-+ res = LzmaEnc_Encode(pp, &outStream.funcTable, &p->seqBufInStream.funcTable,
-+ progress, alloc, allocBig);
-+
-+ *destLen -= outStream.rem;
-+ if (outStream.overflow)
-+ return SZ_ERROR_OUTPUT_EOF;
-+ return res;
-+}
-+
-+SRes LzmaEncode(Byte *dest, SizeT *destLen, const Byte *src, SizeT srcLen,
-+ const CLzmaEncProps *props, Byte *propsEncoded, SizeT *propsSize, int writeEndMark,
-+ ICompressProgress *progress, ISzAlloc *alloc, ISzAlloc *allocBig)
-+{
-+ CLzmaEnc *p = (CLzmaEnc *)LzmaEnc_Create(alloc);
-+ SRes res;
-+ if (p == 0)
-+ return SZ_ERROR_MEM;
-+
-+ res = LzmaEnc_SetProps(p, props);
-+ if (res == SZ_OK)
-+ {
-+ res = LzmaEnc_WriteProperties(p, propsEncoded, propsSize);
-+ if (res == SZ_OK)
-+ res = LzmaEnc_MemEncode(p, dest, destLen, src, srcLen,
-+ writeEndMark, progress, alloc, allocBig);
-+ }
-+
-+ LzmaEnc_Destroy(p, alloc, allocBig);
-+ return res;
-+}
---- a/jffsX-utils/mkfs.jffs2.c
-+++ b/jffsX-utils/mkfs.jffs2.c
-@@ -1668,11 +1668,11 @@ int main(int argc, char **argv)
- }
- erase_block_size *= units;
-
-- /* If it's less than 8KiB, they're not allowed */
-- if (erase_block_size < 0x2000) {
-- fprintf(stderr, "Erase size 0x%x too small. Increasing to 8KiB minimum\n",
-+ /* If it's less than 4KiB, they're not allowed */
-+ if (erase_block_size < 0x1000) {
-+ fprintf(stderr, "Erase size 0x%x too small. Increasing to 4KiB minimum\n",
- erase_block_size);
-- erase_block_size = 0x2000;
-+ erase_block_size = 0x1000;
- }
- break;
- }
diff --git a/package/utils/nilfs-utils/Makefile b/package/utils/nilfs-utils/Makefile
new file mode 100644
index 0000000000..17b23b9e25
--- /dev/null
+++ b/package/utils/nilfs-utils/Makefile
@@ -0,0 +1,204 @@
+#
+# Copyright (C) 2025 OpenWrt.org
+#
+# This is free software, licensed under the GNU General Public License v2.
+# See /LICENSE for more information.
+#
+
+include $(TOPDIR)/rules.mk
+
+PKG_NAME:=nilfs-utils
+PKG_VERSION:=2.2.11
+PKG_RELEASE:=1
+PKG_URL:=https://nilfs.sourceforge.io
+
+PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.bz2
+PKG_SOURCE_URL:=https://nilfs.sourceforge.io/download/
+PKG_HASH:=8602897ff0d2c49be9bc76311f0b102088e58b6de4f749009403de06ff2c34cd
+
+PKG_MAINTAINER:=Pavlo Samko <bulldozerbsg@gmail.com>
+PKG_LICENSE:=GPL-2.0
+PKG_LICENSE_FILES:=COPYING
+PKG_CPE_ID:=cpe:/a:nilf:nilfs
+
+PKG_FIXUP:=autoreconf
+PKG_INSTALL:=1
+
+include $(INCLUDE_DIR)/package.mk
+
+define Package/nilfs-mkfs
+ SECTION:=utils
+ CATEGORY:=Utilities
+ SUBMENU:=Filesystem
+ TITLE:=create a NILFS2 filesystem
+ DEPENDS:=+libuuid +libblkid
+endef
+
+define Package/nilfs-cleanerd
+ SECTION:=utils
+ CATEGORY:=Utilities
+ SUBMENU:=Filesystem
+ TITLE:=NILFS2 garbage collector daemon
+ DEPENDS:=+libuuid @KERNEL_POSIX_MQUEUE
+endef
+
+define Package/nilfs-mount
+ SECTION:=utils
+ CATEGORY:=Utilities
+ SUBMENU:=Filesystem
+ TITLE:=mount, unmount a NILFS2 file system
+ DEPENDS:=+libmount +libblkid +nilfs-cleanerd
+endef
+
+define Package/libnilfs
+ SECTION:=libs
+ CATEGORY:=Libraries
+ TITLE:=libnilfs
+endef
+
+define Package/libnilfsgc
+ SECTION:=libs
+ CATEGORY:=Libraries
+ TITLE:=libnilfsgc
+ DEPENDS:=+libnilfs
+endef
+
+define Package/libnilfscleaner
+ SECTION:=libs
+ CATEGORY:=Libraries
+ TITLE:=libnilfscleaner
+ DEPENDS:=+libuuid +nilfs-cleanerd @KERNEL_POSIX_MQUEUE
+endef
+
+define Package/nilfs-clean
+ SECTION:=utils
+ CATEGORY:=Utilities
+ SUBMENU:=Filesystem
+ TITLE:=run garbage collector on NILFS file system
+ DEPENDS:=+libnilfs +libnilfscleaner
+endef
+
+define Package/nilfs-resize
+ SECTION:=utils
+ CATEGORY:=Utilities
+ SUBMENU:=Filesystem
+ TITLE:=resize NILFS file system volume size
+ DEPENDS:=+libnilfs +libnilfsgc
+endef
+
+define Package/nilfs-tune
+ SECTION:=utils
+ CATEGORY:=Utilities
+ SUBMENU:=Filesystem
+ TITLE:=adjust tunable file system parameters on NILFS file system
+ DEPENDS:=+libnilfs
+endef
+
+define Package/nilfs-checkpoint
+ SECTION:=utils
+ CATEGORY:=Utilities
+ SUBMENU:=Filesystem
+ TITLE:=make, change, remove, list a NILFS2 checkpoint
+ DEPENDS:=+libnilfs
+endef
+
+define Package/nilfs-dumpseg
+ SECTION:=utils
+ CATEGORY:=Utilities
+ SUBMENU:=Filesystem
+ TITLE:=print segment information of NILFS2
+ DEPENDS:=+libnilfs
+endef
+
+define Package/nilfs-lssu
+ SECTION:=utils
+ CATEGORY:=Utilities
+ SUBMENU:=Filesystem
+ TITLE:=list usage state of NILFS2 segments
+ DEPENDS:=+libnilfs +libnilfsgc
+endef
+
+# libmount is used and support of the selinux context mount options
+# depends on the libmount that distro provides.
+CONFIGURE_ARGS += \
+ --without-selinux
+
+define Package/nilfs-mkfs/install
+ $(INSTALL_DIR) $(1)/sbin
+ $(CP) $(PKG_INSTALL_DIR)/sbin/mkfs.nilfs2 $(1)/sbin/
+endef
+
+define Package/nilfs-cleanerd/install
+ $(INSTALL_DIR) $(1)/etc
+ $(CP) $(PKG_INSTALL_DIR)/etc/nilfs_cleanerd.conf $(1)/etc/
+
+ $(INSTALL_DIR) $(1)/sbin
+ $(CP) $(PKG_INSTALL_DIR)/sbin/nilfs_cleanerd $(1)/sbin/
+endef
+
+define Package/nilfs-mount/install
+ $(INSTALL_DIR) $(1)/sbin
+ $(CP) $(PKG_INSTALL_DIR)/sbin/mount.nilfs2 $(1)/sbin/
+ $(CP) $(PKG_INSTALL_DIR)/sbin/umount.nilfs2 $(1)/sbin/
+endef
+
+define Package/libnilfs/install
+ $(INSTALL_DIR) $(1)/usr/lib
+ $(CP) $(PKG_INSTALL_DIR)/usr/lib/libnilfs.so* $(1)/usr/lib/
+endef
+
+define Package/libnilfsgc/install
+ $(INSTALL_DIR) $(1)/usr/lib
+ $(CP) $(PKG_INSTALL_DIR)/usr/lib/libnilfsgc.so* $(1)/usr/lib/
+endef
+
+define Package/libnilfscleaner/install
+ $(INSTALL_DIR) $(1)/usr/lib
+ $(CP) $(PKG_INSTALL_DIR)/usr/lib/libnilfscleaner.so* $(1)/usr/lib/
+endef
+
+define Package/nilfs-clean/install
+ $(INSTALL_DIR) $(1)/usr/sbin
+ $(CP) $(PKG_INSTALL_DIR)/usr/sbin/nilfs-clean $(1)/usr/sbin/
+endef
+
+define Package/nilfs-resize/install
+ $(INSTALL_DIR) $(1)/usr/sbin
+ $(CP) $(PKG_INSTALL_DIR)/usr/sbin/nilfs-resize $(1)/usr/sbin/
+endef
+
+define Package/nilfs-tune/install
+ $(INSTALL_DIR) $(1)/usr/sbin
+ $(CP) $(PKG_INSTALL_DIR)/usr/sbin/nilfs-tune $(1)/usr/sbin/
+endef
+
+define Package/nilfs-checkpoint/install
+ $(INSTALL_DIR) $(1)/usr/bin
+ $(CP) $(PKG_INSTALL_DIR)/usr/bin/chcp $(1)/usr/bin/
+ $(CP) $(PKG_INSTALL_DIR)/usr/bin/lscp $(1)/usr/bin/
+ $(CP) $(PKG_INSTALL_DIR)/usr/bin/mkcp $(1)/usr/bin/
+ $(CP) $(PKG_INSTALL_DIR)/usr/bin/rmcp $(1)/usr/bin/
+endef
+
+define Package/nilfs-dumpseg/install
+ $(INSTALL_DIR) $(1)/usr/bin
+ $(CP) $(PKG_INSTALL_DIR)/usr/bin/dumpseg $(1)/usr/bin/
+endef
+
+define Package/nilfs-lssu/install
+ $(INSTALL_DIR) $(1)/usr/bin
+ $(CP) $(PKG_INSTALL_DIR)/usr/bin/lssu $(1)/usr/bin/
+endef
+
+$(eval $(call BuildPackage,nilfs-mkfs))
+$(eval $(call BuildPackage,nilfs-cleanerd))
+$(eval $(call BuildPackage,nilfs-mount))
+$(eval $(call BuildPackage,libnilfs))
+$(eval $(call BuildPackage,libnilfsgc))
+$(eval $(call BuildPackage,libnilfscleaner))
+$(eval $(call BuildPackage,nilfs-clean))
+$(eval $(call BuildPackage,nilfs-resize))
+$(eval $(call BuildPackage,nilfs-tune))
+$(eval $(call BuildPackage,nilfs-checkpoint))
+$(eval $(call BuildPackage,nilfs-dumpseg))
+$(eval $(call BuildPackage,nilfs-lssu))
diff --git a/package/utils/omnia-eeprom/Makefile b/package/utils/omnia-eeprom/Makefile
new file mode 100644
index 0000000000..44108c0cb4
--- /dev/null
+++ b/package/utils/omnia-eeprom/Makefile
@@ -0,0 +1,52 @@
+#
+# Copyright (C) 2024 Marek Behún <kabel@kernel.org>
+#
+# This is free software, licensed under the GNU General Public License v2.
+# See /LICENSE for more information.
+#
+
+include $(TOPDIR)/rules.mk
+
+PKG_NAME:=omnia-eeprom
+PKG_VERSION:=0.1
+PKG_RELEASE:=1
+
+PKG_SOURCE:=$(PKG_NAME)-v$(PKG_VERSION).tar.bz2
+PKG_SOURCE_URL:=https://gitlab.nic.cz/turris/omnia-eeprom/-/archive/v$(PKG_VERSION)/
+PKG_HASH:=6f949d0b8080adca8bae088774ce615b563ba6ec2807cce97ee6769b4eee7bbf
+PKG_FLAGS:=nonshared
+
+PKG_MAINTAINER:=Marek Behun <kabel@kernel.org>
+PKG_LICENSE:=GPL-2.0-or-later
+PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)-v$(PKG_VERSION)
+
+include $(INCLUDE_DIR)/package.mk
+
+define Package/omnia-eeprom
+ SECTION:=utils
+ CATEGORY:=Utilities
+ URL:=https://gitlab.nic.cz/turris/omnia-eeprom
+ TITLE:=CZ.NIC Turris Omnia EEPROM accessing utility
+ DEPENDS:=@TARGET_mvebu_cortexa9 +kmod-eeprom-at24
+endef
+
+define Package/omnia-eeprom/description
+This package contains the omnia-eeprom utility, which allows you to display
+and update EEPROM fields on the Turris Omnia router.
+The EEPROM is normally not meant to be updated by users, but there are some
+exceptions where it might be useful.
+One such example is to change the DDR3 speed from the default 1600K mode to
+1333H mode, in order to solve random crashes that occur on some boards with
+newer versions of the U-Boot bootloader (because of bugs in newer versions of
+the DDR training algorithm).
+endef
+
+MAKE_VARS += OMNIA_EEPROM_VERSION="v$(PKG_VERSION)"
+TARGET_CFLAGS += -Wall
+
+define Package/omnia-eeprom/install
+ $(INSTALL_DIR) $(1)/usr/bin
+ $(INSTALL_BIN) $(PKG_BUILD_DIR)/omnia-eeprom $(1)/usr/bin/
+endef
+
+$(eval $(call BuildPackage,omnia-eeprom))
diff --git a/package/utils/omnia-mcutool/Makefile b/package/utils/omnia-mcutool/Makefile
new file mode 100644
index 0000000000..66f8ffc6d4
--- /dev/null
+++ b/package/utils/omnia-mcutool/Makefile
@@ -0,0 +1,54 @@
+#
+# Copyright (C) 2016-2024 CZ.NIC z.s.p.o. (http://www.nic.cz/)
+#
+# This is free software, licensed under the GNU General Public License v2.
+# See /LICENSE for more information.
+#
+
+include $(TOPDIR)/rules.mk
+
+PKG_NAME:=omnia-mcutool
+PKG_VERSION_REAL:=0.3-rc3
+PKG_RELEASE:=1
+
+PKG_SOURCE_PROTO:=git
+PKG_SOURCE_URL=https://gitlab.nic.cz/turris/$(PKG_NAME)
+PKG_SOURCE_DATE:=2024-08-05
+PKG_SOURCE_VERSION:=3833ade1377076a5c8e394d7afe7679716af0107
+PKG_MIRROR_HASH:=63cfaa388cffc8a5a7d08c14d6428d1bb33ae7aebf62a1764fd57f8bc94f9144
+
+PKG_MAINTAINER:=Marek Mojik <marek.mojik@nic.cz>
+PKG_LICENSE:=GPL-2.0-or-later
+
+include $(INCLUDE_DIR)/package.mk
+
+define Package/omnia-mcutool
+ SECTION:=utils
+ CATEGORY:=Utilities
+ URL:=https://gitlab.nic.cz/turris/$(PKG_NAME)
+ TITLE:=CZ.NIC Turris Omnia MCU utility
+ DEPENDS:=+libopenssl +omnia-mcu-firmware
+endef
+
+define Package/omnia-mcutool/description
+The omnia-mcutool utility is mainly used to upgrade the firmware on the
+microcontroller on the Turris Omnia router. It can also show state of MCU
+settings and configure MCU options (GPIOs, LEDs, power).
+endef
+
+TARGET_LDFLAGS += -lcrypto
+
+define Build/Compile
+ $(MAKE) -C $(PKG_BUILD_DIR) \
+ CC="$(TARGET_CC)" \
+ CFLAGS="$(TARGET_CFLAGS) -Wall" \
+ LDFLAGS="$(TARGET_LDFLAGS)" \
+ MCUTOOL_VERSION="$(PKG_VERSION_REAL)"
+endef
+
+define Package/omnia-mcutool/install
+ $(INSTALL_DIR) $(1)/usr/bin
+ $(INSTALL_BIN) $(PKG_BUILD_DIR)/omnia-mcutool $(1)/usr/bin/
+endef
+
+$(eval $(call BuildPackage,omnia-mcutool))
diff --git a/package/utils/policycoreutils/Makefile b/package/utils/policycoreutils/Makefile
index f5027c5ece..639e6c4480 100644
--- a/package/utils/policycoreutils/Makefile
+++ b/package/utils/policycoreutils/Makefile
@@ -6,12 +6,12 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=policycoreutils
-PKG_VERSION:=3.5
-PKG_RELEASE:=1
+PKG_VERSION:=3.8.1
+PKG_RELEASE:=2
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
PKG_SOURCE_URL:=https://github.com/SELinuxProject/selinux/releases/download/$(PKG_VERSION)
-PKG_HASH:=78453e1529fbbf800e88860094d555e781ce1fba11a7ef77b5aabb43e1173276
+PKG_HASH:=eef23196b501d141cb95f5fc52ef1a7289f459b65e4415ea0fe9aeedc5d80ef2
PKG_INSTALL:=1
HOST_BUILD_DEPENDS:=libsemanage/host gettext-full/host
PKG_BUILD_DEPENDS:=BUSYBOX_CONFIG_PAM:libpam gettext-full/host
@@ -75,11 +75,11 @@ ALTS_setfiles:=300:/sbin/restorecon:/sbin/policycoreutils-setfiles 300:/sbin/set
DEPENDS_genhomedircon:=+libsemanage $(INTL_DEPENDS)
DEPENDS_load_policy:=+libselinux $(INTL_DEPENDS)
-DEPENDS_newrole:=+libselinux +libaudit +BUSYBOX_CONFIG_PAM:libpam $(INTL_DEPENDS)
+DEPENDS_newrole:= +USE_GLIBC:libcrypt-compat +libselinux +libaudit +BUSYBOX_CONFIG_PAM:libpam $(INTL_DEPENDS)
DEPENDS_open_init_pty:=$(INTL_DEPENDS)
DEPENDS_pp:=+libsepol $(INTL_DEPENDS)
DEPENDS_restorecon_xattr:=+libselinux +libsepol +libaudit $(INTL_DEPENDS)
-DEPENDS_run_init:=+libselinux +libaudit +BUSYBOX_CONFIG_PAM:libpam $(INTL_DEPENDS)
+DEPENDS_run_init:= +USE_GLIBC:libcrypt-compat +libselinux +libaudit +BUSYBOX_CONFIG_PAM:libpam $(INTL_DEPENDS)
DEPENDS_secon:=+libselinux $(INTL_DEPENDS)
DEPENDS_semanage:=+libsemanage
DEPENDS_semodule:=+libsemanage $(INTL_DEPENDS)
diff --git a/package/utils/policycoreutils/patches/0001-policycoreutils-run_init-define-_GNU_SOURCE.patch b/package/utils/policycoreutils/patches/0001-policycoreutils-run_init-define-_GNU_SOURCE.patch
new file mode 100644
index 0000000000..40f505a33b
--- /dev/null
+++ b/package/utils/policycoreutils/patches/0001-policycoreutils-run_init-define-_GNU_SOURCE.patch
@@ -0,0 +1,29 @@
+From 2ffd51650fb1886d6466f652d2e626b1bb6a5ce3 Mon Sep 17 00:00:00 2001
+From: Robert Marko <robimarko@gmail.com>
+Date: Thu, 1 May 2025 21:15:11 +0200
+Subject: [PATCH] policycoreutils: run_init: define _GNU_SOURCE
+
+Trying to compile run_init with musl will fail with:
+run_init.c: In function 'authenticate_via_shadow_passwd':
+run_init.c:206:40: error: implicit declaration of function 'getpass' [-Wimplicit-function-declaration]
+ 206 | if (!(unencrypted_password_s = getpass(PASSWORD_PROMPT))) {
+
+This is because getpass in musl is guarded only for _GNU_SOURCE, so
+define _GNU_SOURCE for run_init.
+
+Signed-off-by: Robert Marko <robimarko@gmail.com>
+---
+ run_init/run_init.c | 2 ++
+ 1 file changed, 2 insertions(+)
+
+--- a/run_init/run_init.c
++++ b/run_init/run_init.c
+@@ -37,6 +37,8 @@
+ *
+ *************************************************************************/
+
++#define _GNU_SOURCE
++
+ #include <stdio.h>
+ #include <stdlib.h> /* for malloc(), realloc(), free() */
+ #include <pwd.h> /* for getpwuid() */
diff --git a/package/utils/provision/Makefile b/package/utils/provision/Makefile
new file mode 100644
index 0000000000..49c57379b6
--- /dev/null
+++ b/package/utils/provision/Makefile
@@ -0,0 +1,33 @@
+#
+# Copyright (C) 2025 OpenWrt.org
+#
+# This is free software, licensed under the GNU General Public License v2.
+# See /LICENSE for more information.
+#
+
+include $(TOPDIR)/rules.mk
+
+PKG_NAME:=provision
+PKG_RELEASE:=$(AUTORELEASE)
+
+PKG_LICENSE:=GPL-2.0
+PKG_MAINTAINER:=Felix Fietkau <nbd@nbd.name>
+
+include $(INCLUDE_DIR)/package.mk
+
+define Package/provision
+ SECTION:=utils
+ CATEGORY:=Utilities
+ TITLE:=Utility for managing device provisioning data
+ DEPENDS:=+ucode +ucode-mod-fs +ucode-mod-struct
+endef
+
+define Build/Compile
+ :
+endef
+
+define Package/provision/install
+ $(CP) ./files/* $(1)/
+endef
+
+$(eval $(call BuildPackage,provision))
diff --git a/package/utils/provision/files/usr/sbin/provision b/package/utils/provision/files/usr/sbin/provision
new file mode 100755
index 0000000000..49542e5b8d
--- /dev/null
+++ b/package/utils/provision/files/usr/sbin/provision
@@ -0,0 +1,107 @@
+#!/usr/bin/env ucode
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2025 Felix Fietkau <nbd@nbd.name>
+ */
+'use strict';
+import { basename } from "fs";
+import * as provision from "provision";
+
+const usage_message = `Usage: ${basename(sourcepath())} <command> [<args>]
+
+ Commands:
+ - get [<path>]: Get string value at <path> (or all if no path given)
+ - set <path> <value> Set string value at <path> to <value>
+ - get_json [<path>]: Get JSON data at <path> (or all if no path given)
+ - set_json <path> <value> Set JSON value at <path> to <value>
+ - delete <path> Delete value at <path>
+ - reset Clear provision data
+ - create Create provisioning partition
+ - destroy Destroy provisioning partition
+
+`;
+
+
+function usage()
+{
+ warn(usage_message);
+ exit(1);
+}
+
+if (!length(ARGV))
+ usage();
+
+const create_error_msg = `Provisioning partition could not be created.
+This is only supported on devices with UBI for now.
+If there was not enough space, please reflash using the sysugrade -P parameter
+`;
+
+let ctx;
+if (ARGV[0] == "create") {
+ ctx = provision.create();
+ if (!ctx) {
+ warn(create_error_msg);
+ exit(1);
+ }
+ ctx.reset();
+ ctx.commit();
+ exit(0);
+} else {
+ ctx = provision.open();
+ if (!ctx) {
+ warn(`Provisioning partition not found. Try ${basename(sourcepath())} enable\n`);
+ exit(1);
+ }
+}
+ctx.init();
+
+let cmd = shift(ARGV);
+switch (cmd) {
+case "get":
+ let val = ctx.get(ARGV[0]);
+ val ??= "";
+ print(val + "\n");
+ break;
+case "get_json":
+ printf("%.J\n", ctx.get(ARGV[0]));
+ break;
+case "set_json":
+ if (length(ARGV) != 2)
+ usage();
+ ARGV[1] = json(ARGV[1]);
+ if (ARGV[1] == null) {
+ warn('Invalid JSON argument\n');
+ exit(1);
+ }
+ // fallthrough
+case "set":
+ if (length(ARGV) != 2)
+ usage();
+
+ if (!ctx.set(ARGV[0], ARGV[1])) {
+ warn('Set failed\n');
+ exit(1);
+ }
+
+ ctx.commit();
+ break;
+case "delete":
+ if (length(ARGV) != 1)
+ usage();
+
+ if (!ctx.set(ARGV[0])) {
+ warn('Delete failed\n');
+ exit(1);
+ }
+ ctx.commit();
+ break;
+case "reset":
+ ctx.reset();
+ ctx.commit();
+ break;
+case "destroy":
+ ctx.destroy();
+ break;
+default:
+ usage();
+}
diff --git a/package/utils/provision/files/usr/share/ucode/provision.uc b/package/utils/provision/files/usr/share/ucode/provision.uc
new file mode 100644
index 0000000000..3f43331d00
--- /dev/null
+++ b/package/utils/provision/files/usr/share/ucode/provision.uc
@@ -0,0 +1,189 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright (C) 2025 Felix Fietkau <nbd@nbd.name>
+ */
+'use strict';
+import * as struct from "struct";
+import * as fs from "fs";
+
+const MAGIC = 0xf09f8697;
+const HDR_LEN = 9;
+
+let hdr = struct.new(">LLc");
+
+const ubi_proto = {
+ read: function() {
+ let file = fs.open(this.dev);
+ if (!file)
+ return;
+
+ let hdr_data = file.read(HDR_LEN);
+ if (!hdr_data)
+ return;
+
+ hdr_data = hdr.unpack(hdr_data);
+ if (!hdr_data)
+ return;
+
+ if (hdr_data[0] != MAGIC)
+ return;
+
+ if (hdr_data[1] > 131072 || hdr_data[2] != 0)
+ return;
+
+ let data = file.read(hdr_data[1]);
+ if (length(data) != hdr_data[1])
+ return;
+
+ return data;
+ },
+ commit: function(data) {
+ let len = HDR_LEN + length(data);
+
+ let file = fs.popen(`ubiupdatevol ${this.dev} -s ${len} -`, "w");
+ file.write(hdr.pack(MAGIC, length(data), 0));
+ file.write(data);
+
+ return file.close() == 0;
+ },
+ destroy: function() {
+ let dev = replace(this.dev, /_\d+$/, "");
+ return system(`ubirmvol ${dev} -N provisioning`) == 0;
+ }
+};
+
+function open_ubi()
+{
+ let found = fs.glob("/sys/class/ubi/*/name");
+ found = filter(found, (v) => trim(fs.readfile(v)) == "provisioning");
+ if (!length(found))
+ return;
+
+ let dev_name = fs.basename(fs.dirname(found[0]));
+
+ return proto({
+ dev: "/dev/" + dev_name,
+ }, ubi_proto);
+}
+
+function create_ubi()
+{
+ let ctx = open_ubi();
+ if (ctx)
+ return ctx;
+
+ let found = fs.glob("/sys/class/ubi/*/name");
+ found = filter(found, (v) => substr(fs.readfile(v), 0, 6) == "rootfs");
+ if (!length(found))
+ return;
+
+ let dev = fs.basename(fs.dirname(found[0]));
+ dev = "/dev/" + replace(dev, /_\d+$/, "");
+ if (system(`ubimkvol ${dev} -N provisioning -s 131072`) != 0)
+ return;
+
+ return open_ubi();
+}
+
+function data_path_get(data, path, create)
+{
+ if (!data)
+ return;
+
+ if (!length(path))
+ return data;
+
+ if (type(path) == "string")
+ path = split(path, ".");
+
+ let last = data;
+ let last_name;
+ for (let name in path) {
+ switch (type(data)) {
+ case "object":
+ last = data;
+ last_name = name;
+ data = data[name];
+ break;
+ case "array":
+ last = data;
+ last_name = name;
+ data = data[+name];
+ break;
+ default:
+ return;
+ }
+
+ if (data == null && create)
+ data = last[last_name] = {};
+ }
+
+ return data;
+}
+
+const provision_proto = {
+ init: function() {
+ this.data = this.backend.read();
+ try {
+ this.data = json(this.data);
+ } catch(e) {
+ this.data = null;
+ }
+ if (!this.data)
+ this.reset();
+ return true;
+ },
+ get: function(path) {
+ return data_path_get(this.data, path);
+ },
+ set: function(path, value) {
+ if (!length(path))
+ return;
+
+ if (type(path) == "string")
+ path = split(path, ".");
+ let name = pop(path);
+ let data = data_path_get(this.data, path, true);
+ if (type(data) != "object")
+ return;
+
+ if (value == null)
+ delete data[name];
+ else
+ data[name] = value;
+ return true;
+ },
+ reset: function() {
+ this.data = {};
+ return true;
+ },
+ commit: function() {
+ if (!this.data)
+ return;
+
+ return this.backend.commit("" + this.data);
+ },
+ destroy: function() {
+ return this.backend.destroy();
+ }
+};
+
+function __open(backend)
+{
+ if (!backend)
+ return;
+
+ return proto({
+ backend,
+ }, provision_proto);
+}
+
+export function create()
+{
+ return __open(create_ubi());
+};
+
+export function open()
+{
+ return __open(open_ubi());
+};
diff --git a/package/utils/px5g-mbedtls/Makefile b/package/utils/px5g-mbedtls/Makefile
index 14c9f684a9..6addbbab12 100644
--- a/package/utils/px5g-mbedtls/Makefile
+++ b/package/utils/px5g-mbedtls/Makefile
@@ -8,7 +8,7 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=px5g-mbedtls
-PKG_RELEASE:=10
+PKG_RELEASE:=11
PKG_LICENSE:=LGPL-2.1
PKG_BUILD_FLAGS:=no-mips16
diff --git a/package/utils/px5g-mbedtls/px5g-mbedtls.c b/package/utils/px5g-mbedtls/px5g-mbedtls.c
index 85abe7dc73..b1c809f834 100644
--- a/package/utils/px5g-mbedtls/px5g-mbedtls.c
+++ b/package/utils/px5g-mbedtls/px5g-mbedtls.c
@@ -38,8 +38,13 @@
#include <mbedtls/ecp.h>
#include <mbedtls/rsa.h>
#include <mbedtls/pk.h>
+#include <mbedtls/asn1.h>
+#include <mbedtls/oid.h>
-#define PX5G_VERSION "0.2"
+#define SET_OID(x, oid) \
+ do { x.len = MBEDTLS_OID_SIZE(oid); x.p = (unsigned char *) oid; } while (0)
+
+#define PX5G_VERSION "0.3"
#define PX5G_COPY "Copyright (c) 2009 Steven Barth <steven@midlink.org>"
#define PX5G_LICENSE "Licensed under the GNU Lesser General Public License v2.1"
@@ -71,7 +76,7 @@ static void write_file(const char *path, size_t len, bool pem, bool cert)
fprintf(stderr, "No data to write\n");
exit(1);
}
-
+
if (cert)
mode |= S_IRGRP | S_IROTH;
@@ -193,6 +198,16 @@ int selfsigned(char **arg)
mbedtls_pk_context key;
mbedtls_x509write_cert cert;
mbedtls_mpi serial;
+ mbedtls_x509_san_list *san_list = NULL, *san_prev = NULL, *san_cur = NULL;
+ /*support
+ - MBEDTLS_X509_SAN_DNS_NAME
+ - MBEDTLS_X509_SAN_IP_ADDRESS
+ - MBEDTLS_X509_SAN_RFC822_NAME
+ - MBEDTLS_X509_SAN_UNIFORM_RESOURCE_IDENTIFIER
+ */
+ mbedtls_asn1_sequence *eku = NULL, *ext_key_usage = NULL;
+ char *sanval, *santype;
+ uint8_t ipaddr[16] = { 0 };
char *subject = "";
unsigned int ksize = 512;
@@ -267,8 +282,56 @@ int selfsigned(char **arg)
oldc = delim + 1;
} while(*delim);
arg++;
+ } else if (!strcmp(*arg, "-addext") && arg[1]) {
+ mbedtls_asn1_sequence **tail = &eku;
+ if (!strncmp(arg[1], "extendedKeyUsage=", strlen("extendedKeyUsage="))) {
+ ext_key_usage = calloc(1, sizeof(mbedtls_asn1_sequence));
+ ext_key_usage->buf.tag = MBEDTLS_ASN1_OID;
+ if (!strncmp(arg[1] + strlen("extendedKeyUsage="), "serverAuth", strlen("serverAuth"))) {
+ SET_OID(ext_key_usage->buf, MBEDTLS_OID_SERVER_AUTH);
+ } else if (!strncmp(arg[1] + strlen("extendedKeyUsage="), "any", strlen("any"))) {
+ SET_OID(ext_key_usage->buf, MBEDTLS_OID_ANY_EXTENDED_KEY_USAGE);
+ } // there are other extendedKeyUsage OIDs but none conceivably useful here
+ *tail = ext_key_usage;
+ tail = &ext_key_usage->next;
+ arg++;
+ } else if (!strncmp(arg[1], "subjectAltName=", strlen("subjectAltName=")) && strchr(arg[1], ':') != NULL) {
+ santype = strchr(arg[1], '=') + 1;
+ sanval = strchr(arg[1], ':') + 1;
+ //build sAN list
+ san_cur = calloc(1, sizeof(mbedtls_x509_san_list));
+ san_cur->next = NULL;
+ if (!strncmp(santype, "DNS:", strlen("DNS:"))) {
+ san_cur->node.type = MBEDTLS_X509_SAN_DNS_NAME;
+ san_cur->node.san.unstructured_name.p = (unsigned char *) sanval;
+ san_cur->node.san.unstructured_name.len = strlen(sanval);
+ } else if (!strncmp(santype, "EMAIL:", strlen("EMAIL:"))) {
+ san_cur->node.type = MBEDTLS_X509_SAN_RFC822_NAME;
+ san_cur->node.san.unstructured_name.p = (unsigned char *) sanval;
+ san_cur->node.san.unstructured_name.len = strlen(sanval);
+ } else if (!strncmp(santype, "IP:", strlen("IP:"))) {
+ san_cur->node.type = MBEDTLS_X509_SAN_IP_ADDRESS;
+ mbedtls_x509_crt_parse_cn_inet_pton(sanval, ipaddr);
+ san_cur->node.san.unstructured_name.p = (unsigned char *) ipaddr;
+ san_cur->node.san.unstructured_name.len = sizeof(ipaddr);
+ } else if (!strncmp(santype, "URI:", strlen("URI:"))) {
+ san_cur->node.type = MBEDTLS_X509_SAN_UNIFORM_RESOURCE_IDENTIFIER;
+ san_cur->node.san.unstructured_name.p = (unsigned char *) sanval;
+ san_cur->node.san.unstructured_name.len = strlen(sanval);
+ }
+ else fprintf(stderr, "No match to subjectAltName content type.\n");
+ arg++;
+ }
}
arg++;
+
+ //set the pointers in our san_list linked list
+ if (san_prev == NULL) {
+ san_list = san_cur;
+ } else {
+ san_prev->next = san_cur;
+ }
+ san_prev = san_cur;
}
gen_key(&key, rsa, ksize, exp, curve, pem);
@@ -295,6 +358,8 @@ int selfsigned(char **arg)
mbedtls_x509write_crt_set_basic_constraints(&cert, 0, -1);
mbedtls_x509write_crt_set_subject_key_identifier(&cert);
mbedtls_x509write_crt_set_authority_key_identifier(&cert);
+ mbedtls_x509write_crt_set_subject_alternative_name(&cert, san_list);
+ mbedtls_x509write_crt_set_ext_key_usage(&cert, ext_key_usage);
_urandom(NULL, (void *) buf, 8);
for (len = 0; len < 8; len++)
diff --git a/package/utils/secilc/Makefile b/package/utils/secilc/Makefile
index 5469039e22..0cb808b2e1 100644
--- a/package/utils/secilc/Makefile
+++ b/package/utils/secilc/Makefile
@@ -6,16 +6,15 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=secilc
-PKG_VERSION:=3.5
+PKG_VERSION:=3.8.1
PKG_RELEASE:=1
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
PKG_SOURCE_URL:=https://github.com/SELinuxProject/selinux/releases/download/$(PKG_VERSION)
-PKG_HASH:=3eebc5a1f97847fa530cf90654b9f3b8f21a13c9ea3d07495325651580cd3373
+PKG_HASH:=3db2974dd9a3c8403ada0392deff267b0398a74b4e7a0b051af76457270848d1
HOST_BUILD_DEPENDS:=libsepol/host
PKG_MAINTAINER:=Dominick Grift <dominick.grift@defensec.nl>
-PKG_CPE_ID:=cpe:/a:selinuxproject:secilc
PKG_LICENSE:=BSD-2-Clause
PKG_LICENSE_FILES:=COPYING
diff --git a/package/utils/spidev_test/Makefile b/package/utils/spidev_test/Makefile
index fef5c8f646..f0e60f8b15 100644
--- a/package/utils/spidev_test/Makefile
+++ b/package/utils/spidev_test/Makefile
@@ -9,10 +9,13 @@ include $(TOPDIR)/rules.mk
include $(INCLUDE_DIR)/kernel.mk
PKG_NAME:=spidev-test
-PKG_RELEASE:=$(LINUX_VERSION)
+PKG_VERSION:=$(LINUX_VERSION)
+PKG_RELEASE:=1
PKG_BUILD_DIR:=$(LINUX_DIR)/tools/spi-$(TARGET_DIR_NAME)
PKG_BUILD_PARALLEL:=1
+PKG_LICENSE:=GPL-2.0-only
+
include $(INCLUDE_DIR)/package.mk
define Package/spidev-test
@@ -20,7 +23,7 @@ define Package/spidev-test
CATEGORY:=Utilities
DEPENDS:=+kmod-spi-dev
TITLE:=SPI testing utility
- VERSION:=$(LINUX_VERSION)-$(PKG_RELEASE)
+ VERSION:=$(LINUX_VERSION)-r$(PKG_RELEASE)
URL:=http://www.kernel.org
endef
diff --git a/package/utils/ucode-mod-bpf/src/bpf.c b/package/utils/ucode-mod-bpf/src/bpf.c
index 415215e54e..33641ddfb2 100644
--- a/package/utils/ucode-mod-bpf/src/bpf.c
+++ b/package/utils/ucode-mod-bpf/src/bpf.c
@@ -17,7 +17,6 @@
#define err_return(err, ...) do { set_error(err, __VA_ARGS__); return NULL; } while(0)
#define TRUE ucv_boolean_new(true)
-static uc_resource_type_t *module_type, *map_type, *map_iter_type, *program_type;
static uc_value_t *registry;
static uc_vm_t *debug_vm;
@@ -184,21 +183,38 @@ uc_bpf_open_module(uc_vm_t *vm, size_t nargs)
err_return(errno, NULL);
}
- return uc_resource_new(module_type, obj);
+ return ucv_resource_create(vm, "bpf.module", obj);
}
static uc_value_t *
-uc_bpf_map_create(int fd, unsigned int key_size, unsigned int val_size, bool close)
+uc_bpf_map_create(uc_vm_t *vm, uc_value_t *mod, int fd, unsigned int key_size,
+ unsigned int val_size, bool close)
{
struct uc_bpf_map *uc_map;
+ uc_value_t *res;
- uc_map = xalloc(sizeof(*uc_map));
+ res = ucv_resource_create_ex(vm, "bpf.map", (void **)&uc_map, 1, sizeof(*uc_map));
+ ucv_resource_value_set(res, 0, ucv_get(mod));
uc_map->fd.fd = fd;
uc_map->key_size = key_size;
uc_map->val_size = val_size;
uc_map->fd.close = close;
- return uc_resource_new(map_type, uc_map);
+ return res;
+}
+
+static uc_value_t *
+uc_bpf_prog_create(uc_vm_t *vm, uc_value_t *mod, int fd, bool close)
+{
+ struct uc_bpf_fd *uc_fd;
+ uc_value_t *res;
+
+ res = ucv_resource_create_ex(vm, "bpf.program", (void **)&uc_fd, 1, sizeof(*uc_fd));
+ ucv_resource_value_set(res, 0, ucv_get(mod));
+ uc_fd->fd = fd;
+ uc_fd->close = close;
+
+ return res;
}
static uc_value_t *
@@ -223,14 +239,13 @@ uc_bpf_open_map(uc_vm_t *vm, size_t nargs)
err_return(errno, NULL);
}
- return uc_bpf_map_create(fd, info.key_size, info.value_size, true);
+ return uc_bpf_map_create(vm, NULL, fd, info.key_size, info.value_size, true);
}
static uc_value_t *
uc_bpf_open_program(uc_vm_t *vm, size_t nargs)
{
uc_value_t *path = uc_fn_arg(0);
- struct uc_bpf_fd *f;
int fd;
if (ucv_type(path) != UC_STRING)
@@ -240,11 +255,7 @@ uc_bpf_open_program(uc_vm_t *vm, size_t nargs)
if (fd < 0)
err_return(errno, NULL);
- f = xalloc(sizeof(*f));
- f->fd = fd;
- f->close = true;
-
- return uc_resource_new(program_type, f);
+ return uc_bpf_prog_create(vm, NULL, fd, true);
}
static uc_value_t *
@@ -284,7 +295,7 @@ uc_bpf_module_get_map(uc_vm_t *vm, size_t nargs)
if (fd < 0)
err_return(EINVAL, NULL);
- return uc_bpf_map_create(fd, bpf_map__key_size(map), bpf_map__value_size(map), false);
+ return uc_bpf_map_create(vm, _uc_fn_this_res(vm), fd, bpf_map__key_size(map), bpf_map__value_size(map), false);
}
static uc_value_t *
@@ -311,7 +322,6 @@ uc_bpf_module_get_program(uc_vm_t *vm, size_t nargs)
struct bpf_object *obj = uc_fn_thisval("bpf.module");
struct bpf_program *prog;
uc_value_t *name = uc_fn_arg(0);
- struct uc_bpf_fd *f;
int fd;
if (!obj || !name || ucv_type(name) != UC_STRING)
@@ -325,10 +335,7 @@ uc_bpf_module_get_program(uc_vm_t *vm, size_t nargs)
if (fd < 0)
err_return(EINVAL, NULL);
- f = xalloc(sizeof(*f));
- f->fd = fd;
-
- return uc_resource_new(program_type, f);
+ return uc_bpf_prog_create(vm, _uc_fn_this_res(vm), fd, false);
}
static void *
@@ -493,16 +500,18 @@ uc_bpf_map_iterator(uc_vm_t *vm, size_t nargs)
{
struct uc_bpf_map *map = uc_fn_thisval("bpf.map");
struct uc_bpf_map_iter *iter;
+ uc_value_t *res;
if (!map)
err_return(EINVAL, NULL);
- iter = xalloc(sizeof(*iter) + map->key_size);
+ res = ucv_resource_create_ex(vm, "bpf.map_iter", (void **)&iter, 1, sizeof(*iter) + map->key_size);
+ ucv_resource_value_set(res, 0, ucv_get(_uc_fn_this_res(vm)));
iter->fd = map->fd.fd;
iter->key_size = map->key_size;
iter->has_next = !bpf_map_get_next_key(iter->fd, NULL, &iter->key);
- return uc_resource_new(map_iter_type, iter);
+ return res;
}
static uc_value_t *
@@ -611,7 +620,7 @@ uc_bpf_map_pin(uc_vm_t *vm, size_t nargs)
static uc_value_t *
uc_bpf_set_tc_hook(uc_value_t *ifname, uc_value_t *type, uc_value_t *prio,
- int fd)
+ uc_value_t *classid, int fd)
{
DECLARE_LIBBPF_OPTS(bpf_tc_hook, hook);
DECLARE_LIBBPF_OPTS(bpf_tc_opts, attach_tc,
@@ -648,6 +657,7 @@ uc_bpf_set_tc_hook(uc_value_t *ifname, uc_value_t *type, uc_value_t *prio,
goto out;
attach_tc.prog_fd = fd;
+ attach_tc.classid = ucv_int64_get(classid);
if (bpf_tc_attach(&hook, &attach_tc) < 0)
goto error;
@@ -667,11 +677,12 @@ uc_bpf_program_tc_attach(uc_vm_t *vm, size_t nargs)
uc_value_t *ifname = uc_fn_arg(0);
uc_value_t *type = uc_fn_arg(1);
uc_value_t *prio = uc_fn_arg(2);
+ uc_value_t *classid = uc_fn_arg(3);
if (!f)
err_return(EINVAL, NULL);
- return uc_bpf_set_tc_hook(ifname, type, prio, f->fd);
+ return uc_bpf_set_tc_hook(ifname, type, prio, classid, f->fd);
}
static uc_value_t *
@@ -681,7 +692,7 @@ uc_bpf_tc_detach(uc_vm_t *vm, size_t nargs)
uc_value_t *type = uc_fn_arg(1);
uc_value_t *prio = uc_fn_arg(2);
- return uc_bpf_set_tc_hook(ifname, type, prio, -1);
+ return uc_bpf_set_tc_hook(ifname, type, prio, NULL, -1);
}
static int
@@ -777,7 +788,6 @@ static void uc_bpf_fd_free(void *ptr)
if (f->close)
close(f->fd);
- free(f);
}
static const uc_function_list_t map_iter_fns[] = {
@@ -807,8 +817,8 @@ void uc_module_init(uc_vm_t *vm, uc_value_t *scope)
registry = ucv_array_new(vm);
uc_vm_registry_set(vm, "bpf.registry", registry);
- module_type = uc_type_declare(vm, "bpf.module", module_fns, module_free);
- map_type = uc_type_declare(vm, "bpf.map", map_fns, uc_bpf_fd_free);
- map_iter_type = uc_type_declare(vm, "bpf.map_iter", map_iter_fns, free);
- program_type = uc_type_declare(vm, "bpf.program", prog_fns, uc_bpf_fd_free);
+ uc_type_declare(vm, "bpf.module", module_fns, module_free);
+ uc_type_declare(vm, "bpf.map", map_fns, uc_bpf_fd_free);
+ uc_type_declare(vm, "bpf.map_iter", map_iter_fns, NULL);
+ uc_type_declare(vm, "bpf.program", prog_fns, uc_bpf_fd_free);
}
diff --git a/package/utils/ucode-mod-pkgen/Makefile b/package/utils/ucode-mod-pkgen/Makefile
new file mode 100644
index 0000000000..918aa22a90
--- /dev/null
+++ b/package/utils/ucode-mod-pkgen/Makefile
@@ -0,0 +1,44 @@
+include $(TOPDIR)/rules.mk
+
+PKG_NAME:=ucode-mod-pkgen
+PKG_RELEASE:=1
+PKG_LICENSE:=GPL-2.0-or-later
+PKG_MAINTAINER:=Felix Fietkau <nbd@nbd.name>
+
+include $(INCLUDE_DIR)/package.mk
+include $(INCLUDE_DIR)/cmake.mk
+
+CMAKE_INSTALL := 1
+
+define Package/ucode-mod-pkgen
+ SECTION:=utils
+ CATEGORY:=Utilities
+ TITLE:=ucode module for generating public keys/certificates
+ DEPENDS:=+libucode +libmbedtls
+endef
+
+define Package/ucode-mod-pkgen/description
+The pkgen module provides functionality for generating cryptographic keys and
+(self-)signed certificates. It supports exporting PEM/DER format files, as
+well as PKCS#12 bundle for client cert/key pairs with CA.
+endef
+
+define Package/pkgen
+ SECTION:=utils
+ CATEGORY:=Utilities
+ TITLE:=ucode script for generating public keys/certificates
+ DEPENDS:=+ucode +ucode-mod-pkgen +ucode-mod-fs
+endef
+
+define Package/ucode-mod-pkgen/install
+ $(INSTALL_DIR) $(1)/usr/lib/ucode
+ $(CP) $(PKG_INSTALL_DIR)/usr/lib/ucode/pkgen.so $(1)/usr/lib/ucode/
+endef
+
+define Package/pkgen/install
+ $(INSTALL_DIR) $(1)/usr/bin
+ $(INSTALL_BIN) ./files/pkgen $(1)/usr/bin
+endef
+
+$(eval $(call BuildPackage,ucode-mod-pkgen))
+$(eval $(call BuildPackage,pkgen))
diff --git a/package/utils/ucode-mod-pkgen/files/pkgen b/package/utils/ucode-mod-pkgen/files/pkgen
new file mode 100755
index 0000000000..e37e5f5329
--- /dev/null
+++ b/package/utils/ucode-mod-pkgen/files/pkgen
@@ -0,0 +1,252 @@
+#!/usr/bin/env ucode
+'use strict';
+
+import { basename, readfile, writefile, stdin } from "fs";
+let pk = require("pkgen");
+let valid_from = "20240101000000";
+let valid_to = "21001231235959";
+let subject, password, password_stdin;
+let keytype = "ec";
+let keylen = 2048;
+let keyexp = 65537;
+let keycurve = "secp256r1";
+let no_ca;
+let legacy;
+
+const usage_message = `Usage: ${basename(sourcepath())} [<options>] <command> [<arguments>]
+
+Commands:
+ ca <ca.pem>: Create a new CA.
+ (creates ca.pem, ca.key, ca.serial)
+
+ cert <ca.pem> <cert.pem>: Create a new certificate/key using the CA
+ from ca.pem. (creates cert.pem and ca.key)
+
+ cert_p12 <ca.pem> <cert.p12>: Create a new PKCS#12 certificate/key
+ using the CA from ca.pem. (creates ca.p12)
+
+ selfsigned <cert.pem>: Create a self-signed certificate
+ (creates cert.pem)
+
+Options:
+ -C <curve> Set EC curve type (default: ${keycurve})
+ Possible values: secp521r1, secp384r1, secp256r1,
+ secp256k1, secp224r1, secp224k1, secp192r1,
+ secp192k1
+ -E <exponent> Set RSA key exponent (default: ${keyexp})
+ -L <len> Set RSA key length (default: ${keylen})
+ -N Omit CA certificate for PKCS#12 files
+ -p <password> Set PKCS#12 password to <password>
+ -P Read PKCS#12 password from stdin
+ (default: random password, printed to stdout)
+ -s <name> Set subject for generated certificate to <name>.
+ -t rsa|ec Set key type to rsa or ec (default: ec)
+ -V <from> <to> Set validity for generated certificates.
+ (default: ${valid_from} ${valid_to})
+ -W Use weaker PKCS#12 encryption for
+ compatibility with Windows and Apple systems
+
+`;
+
+function perror(msg) {
+ let err = pk.errno() == -1 ? "Invalid arguments" : pk.error();
+ warn(`${msg}: ${err}\n`);
+ exit(1);
+}
+
+function usage() {
+ warn(usage_message);
+ exit(1);
+}
+
+function check_pem_path(pem_file) {
+ if (substr(pem_file, -4) != ".pem") {
+ warn(`Path with .pem extension expected\n`);
+ exit(1);
+ }
+
+ return pem_file;
+}
+
+
+function gen_key() {
+ let key = pk.generate_key({
+ type: keytype,
+ curve: keycurve,
+ size: keylen,
+ exponent: keyexp,
+ });
+
+ if (!key)
+ perror("Failed to generate CA key");
+
+ return key;
+}
+
+function gen_cert(key, args) {
+ let cert = pk.generate_cert({
+ subject_name: subject,
+ subject_key: key,
+ validity: [ valid_from, valid_to ],
+ ...args
+ });
+
+ if (!cert)
+ perror("Failed to generate certificate");
+
+ cert = cert.pem();
+ if (!cert)
+ perror("Failed to complete certificate");
+
+ return cert;
+}
+
+function gen_client_cert(ca_file, ca_data, key) {
+ let ca_base = substr(ca_file, 0, -4);
+ let ca_info = pk.cert_info(ca_data);
+ if (!length(ca_info))
+ perror("Failed to load CA certificate");
+
+ let ca_key = pk.load_key(readfile(ca_base + ".key"));
+ if (!ca_key)
+ perror("Failed to load CA key");
+ let ca_serial = +readfile(ca_base + ".serial");
+ if (!ca_serial)
+ perror("Failed to load CA serial");
+
+ let cert = gen_cert(key, {
+ serial: ++ca_serial,
+ issuer_name: ca_info[0].subject,
+ issuer_key: ca_key,
+ });
+ writefile(ca_base + ".serial", "" + ca_serial);
+
+ return cert;
+}
+
+let cmds = {
+ ca: function(args) {
+ let ca_file = check_pem_path(shift(args));
+ let ca_base = substr(ca_file, 0, -4);
+
+ let key = gen_key();
+ let ca_cert = gen_cert(key, {
+ ca: true,
+ serial: 1,
+ issuer_name: subject,
+ issuer_key: key,
+ key_usage: [ "key_cert_sign" ],
+ });
+
+ writefile(ca_file, ca_cert);
+ writefile(ca_base + ".key", key.pem());
+ writefile(ca_base + ".serial", "1");
+ },
+
+ cert: function (args) {
+ let ca_file = check_pem_path(shift(args));
+ let crt_file = check_pem_path(shift(args));
+ let crt_base = substr(crt_file, 0, -4);
+
+ let key = gen_key();
+ let ca_data = readfile(ca_file);
+ let cert = gen_client_cert(ca_file, ca_data, key);
+
+ writefile(crt_base + ".key", key.pem());
+ writefile(crt_file, cert);
+ },
+
+ cert_p12: function (args) {
+ let ca_file = check_pem_path(shift(args));
+ let p12_file = shift(args);
+ if (!p12_file)
+ usage();
+
+ let key = gen_key();
+ let ca_data = readfile(ca_file);
+ let cert = gen_client_cert(ca_file, ca_data, key);
+
+ if (password_stdin)
+ password = rtrim(stdin.read("line"));
+ else if (!password)
+ print((password = hexenc(readfile("/dev/urandom", 8))) + "\n");
+
+ let p12 = pk.generate_pkcs12({
+ password, cert, key, legacy,
+ extra: no_ca ? null : [ ca_data ],
+ });
+
+ writefile(p12_file, p12);
+ },
+
+ selfsigned: function(args) {
+ let crt_file = check_pem_path(shift(args));
+ let crt_base = substr(crt_file, 0, -4);
+
+ let key = gen_key();
+ let cert = gen_cert(key, {
+ serial: 1,
+ issuer_name: subject,
+ issuer_key: key,
+ });
+
+ writefile(crt_base + ".key", key.pem());
+ writefile(crt_file, cert);
+ },
+};
+
+while (substr(ARGV[0], 0, 1) == "-") {
+ let opt = substr(shift(ARGV), 1);
+ switch (opt) {
+ case 'C':
+ keycurve = shift(ARGV);
+ break;
+ case 'L':
+ keylen = +shift(ARGV);
+ break;
+ case 'N':
+ no_ca = true;
+ break;
+ case 'p':
+ password = shift(ARGV);
+ if (password_stdin)
+ usage();
+ break;
+ case 'P':
+ password_stdin = true;
+ if (password)
+ usage();
+ break;
+ case 's':
+ subject = shift(ARGV);
+ break;
+ case 't':
+ keytype = shift(ARGV);
+ if (keytype != "rsa" && keytype != "ec") {
+ warn(`Unsupported key type ${keytype}\n`);
+ exit(1);
+ }
+ break;
+ case 'V':
+ valid_from = shift(ARGV);
+ valid_to = shift(ARGV);
+ break;
+ case 'W':
+ legacy = true;
+ break;
+ default:
+ usage();
+ break;
+ }
+}
+
+let cmd = shift(ARGV);
+if (!cmd || !cmds[cmd])
+ usage();
+
+if (subject == null) {
+ warn(`Missing -s option\n`);
+ exit(1);
+}
+
+cmds[cmd](ARGV);
diff --git a/package/utils/ucode-mod-pkgen/src/CMakeLists.txt b/package/utils/ucode-mod-pkgen/src/CMakeLists.txt
new file mode 100644
index 0000000000..8794755160
--- /dev/null
+++ b/package/utils/ucode-mod-pkgen/src/CMakeLists.txt
@@ -0,0 +1,35 @@
+cmake_minimum_required(VERSION 3.13)
+
+PROJECT(ucode-pkgen C)
+ADD_DEFINITIONS(-Os -ggdb -Wall -Werror --std=gnu99 -ffunction-sections -fwrapv -D_GNU_SOURCE)
+
+IF(CMAKE_C_COMPILER_VERSION VERSION_GREATER 6)
+ ADD_DEFINITIONS(-Wextra -Werror=implicit-function-declaration)
+ ADD_DEFINITIONS(-Wformat -Werror=format-security -Werror=format-nonliteral)
+ENDIF()
+ADD_DEFINITIONS(-Wmissing-declarations -Wno-error=unused-variable -Wno-unused-parameter)
+
+IF(APPLE)
+ SET(UCODE_MODULE_LINK_OPTIONS "LINKER:-undefined,dynamic_lookup")
+ELSE()
+ SET(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "-Wl,--gc-sections")
+ENDIF()
+
+IF(DEBUG)
+ ADD_DEFINITIONS(-DDEBUG -g3 -O0)
+ELSE()
+ ADD_DEFINITIONS(-DNDEBUG)
+ENDIF()
+
+FIND_LIBRARY(mbedtls NAMES mbedtls)
+FIND_LIBRARY(ucode NAMES ucode)
+FIND_PATH(mbedtls_include_dir NAMES mbedtls/pk.h)
+FIND_PATH(ucode_include_dir NAMES ucode/module.h)
+INCLUDE_DIRECTORIES(${mbedtls_include_dir} ${ucode_include_dir})
+
+ADD_LIBRARY(pkgen_lib MODULE ucode.c pkcs12.c)
+SET_TARGET_PROPERTIES(pkgen_lib PROPERTIES OUTPUT_NAME pkgen PREFIX "")
+TARGET_LINK_OPTIONS(pkgen_lib PRIVATE ${UCODE_MODULE_LINK_OPTIONS})
+TARGET_LINK_LIBRARIES(pkgen_lib ${mbedtls})
+
+INSTALL(TARGETS pkgen_lib LIBRARY DESTINATION lib/ucode)
diff --git a/package/utils/ucode-mod-pkgen/src/pk.h b/package/utils/ucode-mod-pkgen/src/pk.h
new file mode 100644
index 0000000000..9e2b316b6d
--- /dev/null
+++ b/package/utils/ucode-mod-pkgen/src/pk.h
@@ -0,0 +1,45 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2024 Felix Fietkau <nbd@nbd.name>
+ */
+#ifndef __UCODE_PK_H
+#define __UCODE_PK_H
+
+#include <ucode/lib.h>
+#include <ucode/vm.h>
+
+#include <mbedtls/bignum.h>
+#include <mbedtls/pk.h>
+#include <mbedtls/oid.h>
+#include <mbedtls/error.h>
+#include <mbedtls/version.h>
+
+#if MBEDTLS_VERSION_MAJOR < 3
+#define MBEDTLS_LEGACY
+#endif
+
+int random_cb(void *ctx, unsigned char *out, size_t len);
+uc_value_t *uc_generate_pkcs12(uc_vm_t *vm, size_t nargs);
+int64_t get_int_arg(uc_value_t *obj, const char *key, int64_t defval);
+extern int mbedtls_errno;
+extern char buf[32 * 1024];
+
+#define C(ret) \
+ ({ \
+ int __ret = (ret); \
+ mbedtls_errno = __ret < 0 ? __ret : 0; \
+ __ret; \
+ })
+
+#define CHECK(ret) do { \
+ if (C(ret) < 0) \
+ return NULL; \
+} while (0)
+#define CHECK_INT(ret) do { \
+ if (C(ret) < 0) \
+ return -1; \
+} while (0)
+
+#define INVALID_ARG() do { C(-1); return NULL; } while (0)
+
+#endif
diff --git a/package/utils/ucode-mod-pkgen/src/pkcs12.c b/package/utils/ucode-mod-pkgen/src/pkcs12.c
new file mode 100644
index 0000000000..7d23b9d35b
--- /dev/null
+++ b/package/utils/ucode-mod-pkgen/src/pkcs12.c
@@ -0,0 +1,612 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2024 Felix Fietkau <nbd@nbd.name>
+ */
+#include "pk.h"
+#include <mbedtls/x509_crt.h>
+#include <mbedtls/ecp.h>
+#include <mbedtls/rsa.h>
+#include <mbedtls/asn1write.h>
+#include <mbedtls/pkcs5.h>
+#include <mbedtls/pkcs12.h>
+#include <mbedtls/base64.h>
+#include <mbedtls/sha1.h>
+
+#define OID_TAG(n) MBEDTLS_OID_##n, MBEDTLS_OID_SIZE(MBEDTLS_OID_##n)
+
+#ifndef MBEDTLS_OID_AES_256_CBC
+#define MBEDTLS_OID_AES_256_CBC MBEDTLS_OID_AES "\x2a"
+#endif
+
+#define MBEDTLS_OID_PKCS9_LOCAL_KEY_ID MBEDTLS_OID_PKCS9 "\x15"
+#define MBEDTLS_OID_PKCS9_CERT_TYPE MBEDTLS_OID_PKCS9 "\x16"
+#define MBEDTLS_OID_PKCS9_CERT_TYPE_X509 MBEDTLS_OID_PKCS9_CERT_TYPE "\x01"
+
+#define MBEDTLS_OID_PKCS12_KEY_BAG MBEDTLS_OID_PKCS12 "\x0a\x01\x01"
+#define MBEDTLS_OID_PKCS12_SHROUDED_KEY_BAG MBEDTLS_OID_PKCS12 "\x0a\x01\x02"
+#define MBEDTLS_OID_PKCS12_CERT_BAG MBEDTLS_OID_PKCS12 "\x0a\x01\x03"
+
+#ifndef MBEDTLS_OID_PKCS7
+#define MBEDTLS_OID_PKCS7 MBEDTLS_OID_PKCS "\x07"
+#define MBEDTLS_OID_PKCS7_DATA MBEDTLS_OID_PKCS7 "\x01"
+#define MBEDTLS_OID_PKCS7_ENCRYPTED_DATA MBEDTLS_OID_PKCS7 "\x06"
+#endif
+
+#define NUM_ITER 2048
+#define SALT_LEN 16
+#define IV_LEN 16
+#define IV_LEN_LEGACY 8
+#define KEY_LEN 32
+#define CERT_HASH_LEN 20
+
+#define CONTEXT_TAG(n) (MBEDTLS_ASN1_CONTEXT_SPECIFIC | MBEDTLS_ASN1_CONSTRUCTED | (n))
+#define SEQUENCE_TAG (MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE)
+#define SET_TAG (MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SET)
+
+#define CTX_ADD(ctx, n) \
+ do { \
+ int __n = (n); \
+ if ((ctx)->p - (ctx)->start <= __n) \
+ return -1; \
+ (ctx)->p -= __n; \
+ } while (0)
+
+struct pkcs12_ctx {
+ uint8_t *p, *start;
+
+ uint8_t cert_hash[20];
+ uint8_t *key_cert_hash;
+
+ uint8_t salt[SALT_LEN];
+ uint8_t iv[IV_LEN];
+
+ const char *password;
+ uint8_t *pwd;
+ size_t pwd_len;
+
+ bool legacy;
+};
+
+#ifdef MBEDTLS_LEGACY
+static inline int
+mbedtls_pkcs5_pbkdf2_hmac_ext(mbedtls_md_type_t md_alg, const unsigned char *password,
+ size_t plen, const unsigned char *salt, size_t slen,
+ unsigned int iteration_count,
+ uint32_t key_length, unsigned char *output)
+{
+ mbedtls_md_context_t md_ctx;
+ const mbedtls_md_info_t *md_info;
+ int ret;
+
+ md_info = mbedtls_md_info_from_type(md_alg);
+ if (!md_info)
+ return MBEDTLS_ERR_PKCS5_FEATURE_UNAVAILABLE;
+
+ mbedtls_md_init(&md_ctx);
+ ret = mbedtls_md_setup(&md_ctx, md_info, 1);
+ if (ret)
+ goto out;
+
+ ret = mbedtls_pkcs5_pbkdf2_hmac(&md_ctx, password, plen, salt, slen,
+ iteration_count, key_length, output);
+
+out:
+ mbedtls_md_free(&md_ctx);
+ return ret;
+}
+#endif
+
+static void
+uc_p12_add_tag(struct pkcs12_ctx *ctx, uint8_t *end, uint8_t tag)
+{
+ mbedtls_asn1_write_len(&ctx->p, ctx->start, end - ctx->p);
+ mbedtls_asn1_write_tag(&ctx->p, ctx->start, tag);
+}
+
+static void
+uc_p12_add_sequence(struct pkcs12_ctx *ctx, uint8_t *end)
+{
+ uc_p12_add_tag(ctx, end, SEQUENCE_TAG);
+}
+
+static void
+uc_p12_add_algo(struct pkcs12_ctx *ctx, const char *oid, size_t oid_len, size_t par_len)
+{
+ mbedtls_asn1_write_algorithm_identifier(&ctx->p, ctx->start, oid, oid_len, par_len);
+}
+
+static void
+uc_p12_add_attribute(struct pkcs12_ctx *ctx, uint8_t *end, const char *oid, size_t oid_len)
+{
+ uc_p12_add_tag(ctx, end, SET_TAG);
+ mbedtls_asn1_write_oid(&ctx->p, ctx->start, oid, oid_len);
+ uc_p12_add_sequence(ctx, end);
+}
+
+static void
+uc_p12_add_localkeyid(struct pkcs12_ctx *ctx, bool key)
+{
+ uint8_t *end = ctx->p;
+
+ ctx->p -= CERT_HASH_LEN;
+ if (key)
+ ctx->key_cert_hash = ctx->p;
+ else if (ctx->key_cert_hash)
+ memcpy(ctx->p, ctx->key_cert_hash, CERT_HASH_LEN);
+ uc_p12_add_tag(ctx, end, MBEDTLS_ASN1_OCTET_STRING);
+ uc_p12_add_attribute(ctx, end, OID_TAG(PKCS9_LOCAL_KEY_ID));
+}
+
+static void
+uc_p12_add_bag(struct pkcs12_ctx *ctx, uint8_t *data_end, uint8_t *end, const char *oid, size_t oid_len)
+{
+ uc_p12_add_tag(ctx, data_end, CONTEXT_TAG(0));
+ mbedtls_asn1_write_oid(&ctx->p, ctx->start, oid, oid_len);
+ uc_p12_add_sequence(ctx, end);
+}
+
+static int
+uc_p12_enc_setup(struct pkcs12_ctx *ctx, mbedtls_cipher_context_t *cipher)
+{
+ const mbedtls_cipher_info_t *cipher_info;
+ uint8_t key[KEY_LEN];
+ int ret;
+
+ random_cb(NULL, ctx->iv, IV_LEN);
+ ret = mbedtls_pkcs5_pbkdf2_hmac_ext(MBEDTLS_MD_SHA256,
+ (void *)ctx->password, strlen(ctx->password),
+ ctx->salt, SALT_LEN, NUM_ITER,
+ KEY_LEN, key);
+ if (ret < 0)
+ return ret;
+
+ cipher_info = mbedtls_cipher_info_from_type(MBEDTLS_CIPHER_AES_256_CBC);
+ ret = mbedtls_cipher_setup(cipher, cipher_info);
+ if (ret < 0)
+ return ret;
+
+ return mbedtls_cipher_setkey(cipher, key, 8 * KEY_LEN, MBEDTLS_ENCRYPT);
+}
+
+static int
+uc_p12_enc_legacy(struct pkcs12_ctx *ctx, mbedtls_cipher_context_t *cipher)
+{
+ const mbedtls_cipher_info_t *cipher_info;
+ uint8_t key[24];
+ int ret;
+
+ ret = mbedtls_pkcs12_derivation(key, sizeof(key), ctx->pwd, ctx->pwd_len,
+ ctx->salt, SALT_LEN, MBEDTLS_MD_SHA1,
+ MBEDTLS_PKCS12_DERIVE_KEY, NUM_ITER);
+ if (ret < 0)
+ return ret;
+
+ ret = mbedtls_pkcs12_derivation(ctx->iv, IV_LEN_LEGACY, ctx->pwd, ctx->pwd_len,
+ ctx->salt, SALT_LEN, MBEDTLS_MD_SHA1,
+ MBEDTLS_PKCS12_DERIVE_IV, NUM_ITER);
+ if (ret < 0)
+ return ret;
+
+ cipher_info = mbedtls_cipher_info_from_type(MBEDTLS_CIPHER_DES_EDE3_CBC);
+ ret = mbedtls_cipher_setup(cipher, cipher_info);
+ if (ret < 0)
+ return ret;
+
+ return mbedtls_cipher_setkey(cipher, key, 8 * sizeof(key), MBEDTLS_ENCRYPT);
+}
+
+static int
+uc_p12_encrypt(struct pkcs12_ctx *ctx, uint8_t *end)
+{
+ mbedtls_cipher_context_t cipher;
+ size_t iv_len = ctx->legacy ? IV_LEN_LEGACY : IV_LEN;
+ size_t out_len = 0;
+ int ret;
+
+ random_cb(NULL, ctx->salt, SALT_LEN);
+
+ if (ctx->legacy)
+ ret = uc_p12_enc_legacy(ctx, &cipher);
+ else
+ ret = uc_p12_enc_setup(ctx, &cipher);
+ if (ret < 0)
+ goto out;
+
+ ret = mbedtls_cipher_set_padding_mode(&cipher, MBEDTLS_PADDING_PKCS7);
+ if (ret < 0)
+ goto out;
+
+ ret = mbedtls_cipher_crypt(&cipher, ctx->iv, iv_len, ctx->p, end - ctx->p,
+ (void *)buf, &out_len);
+
+ if (ret < 0)
+ goto out;
+
+ CTX_ADD(ctx, out_len - (end - ctx->p));
+ memcpy(ctx->p, buf, out_len);
+ uc_p12_add_tag(ctx, end, MBEDTLS_ASN1_OCTET_STRING);
+
+out:
+ mbedtls_cipher_free(&cipher);
+
+ return ret;
+}
+
+static int
+uc_p12_add_enc_params(struct pkcs12_ctx *ctx)
+{
+ uint8_t *par_end = ctx->p;
+ uint8_t *kdf_end;
+
+ if (ctx->legacy) {
+ mbedtls_asn1_write_int(&ctx->p, ctx->start, NUM_ITER);
+
+ CTX_ADD(ctx, SALT_LEN);
+ memcpy(ctx->p, ctx->salt, SALT_LEN);
+ uc_p12_add_tag(ctx, ctx->p + SALT_LEN, MBEDTLS_ASN1_OCTET_STRING);
+
+ uc_p12_add_sequence(ctx, par_end);
+
+ uc_p12_add_algo(ctx, OID_TAG(PKCS12_PBE_SHA1_DES3_EDE_CBC), par_end - ctx->p);
+ } else {
+ CTX_ADD(ctx, IV_LEN);
+ memcpy(ctx->p, ctx->iv, IV_LEN);
+
+ uc_p12_add_tag(ctx, par_end, MBEDTLS_ASN1_OCTET_STRING);
+ uc_p12_add_algo(ctx, OID_TAG(AES_256_CBC), par_end - ctx->p);
+
+ kdf_end = ctx->p;
+ uc_p12_add_algo(ctx, OID_TAG(HMAC_SHA256), 0);
+ mbedtls_asn1_write_int(&ctx->p, ctx->start, NUM_ITER);
+ CTX_ADD(ctx, SALT_LEN);
+ memcpy(ctx->p, ctx->salt, SALT_LEN);
+ uc_p12_add_tag(ctx, ctx->p + SALT_LEN, MBEDTLS_ASN1_OCTET_STRING);
+ uc_p12_add_sequence(ctx, kdf_end);
+
+ uc_p12_add_algo(ctx, OID_TAG(PKCS5_PBKDF2), kdf_end - ctx->p);
+ uc_p12_add_sequence(ctx, par_end);
+
+ uc_p12_add_algo(ctx, OID_TAG(PKCS5_PBES2), par_end - ctx->p);
+ }
+
+ return 0;
+}
+
+static int
+uc_p12_add_cert(struct pkcs12_ctx *ctx, uc_value_t *arg, bool ca)
+{
+ const char *str = ucv_string_get(arg), *str_end;
+ uint8_t *bag_end, *end;
+ size_t len;
+ int ret;
+
+#define START_TAG "-----BEGIN CERTIFICATE-----"
+#define END_TAG "-----END CERTIFICATE-----"
+
+ if (!str)
+ return -1;
+
+ str = strstr(str, START_TAG);
+ if (!str)
+ return -1;
+
+ str += sizeof(START_TAG);
+ str_end = strstr(str, END_TAG);
+ if (!str_end)
+ return -1;
+
+ if ((size_t)(str_end - str) > sizeof(buf) / 2)
+ return -1;
+
+ ret = mbedtls_base64_decode((void *)buf, sizeof(buf) / 2, &len,
+ (const void *)str, str_end - str);
+ if (ret)
+ return ret;
+
+ bag_end = ctx->p;
+ if (!ca && ctx->key_cert_hash) {
+ mbedtls_sha1((const void *)buf, len, ctx->key_cert_hash);
+ uc_p12_add_localkeyid(ctx, false);
+ uc_p12_add_tag(ctx, bag_end, SET_TAG);
+ }
+
+ end = ctx->p;
+ CTX_ADD(ctx, len);
+ memcpy(ctx->p, buf, len);
+ uc_p12_add_tag(ctx, end, MBEDTLS_ASN1_OCTET_STRING);
+
+ /* CertBag */
+ uc_p12_add_tag(ctx, end, CONTEXT_TAG(0));
+ mbedtls_asn1_write_oid(&ctx->p, ctx->start, OID_TAG(PKCS9_CERT_TYPE_X509));
+ uc_p12_add_sequence(ctx, end);
+
+ uc_p12_add_bag(ctx, end, bag_end, OID_TAG(PKCS12_CERT_BAG));
+
+ return 0;
+}
+
+static int
+uc_p12_add_key(struct pkcs12_ctx *ctx, uc_value_t *arg)
+{
+ mbedtls_pk_context *pk = ucv_resource_data(arg, "mbedtls.pk");
+ uint8_t *bag_end, *end;
+ uint8_t *param = NULL;
+ size_t param_len = 0;
+ const char *oid;
+ size_t oid_len = 0;
+ int ret, len;
+
+ if (!pk)
+ return -1;
+
+ bag_end = ctx->p;
+ uc_p12_add_localkeyid(ctx, true);
+ uc_p12_add_tag(ctx, bag_end, SET_TAG);
+
+ end = ctx->p;
+ len = mbedtls_pk_write_key_der(pk, (void *)ctx->start, end - ctx->start);
+ if (len < 0)
+ return len;
+
+ ctx->p -= len;
+
+ /* Convert EC key contents to PKCS#8 style */
+ if (mbedtls_pk_get_type(pk) == MBEDTLS_PK_ECKEY) {
+ mbedtls_ecp_group_id grp_id;
+ mbedtls_asn1_buf tag_buf;
+ uint8_t *pkey_start, *pkey_end;
+ size_t seq_len, pkey_len, param_tag_len;
+ uint8_t *p = ctx->p;
+ uint8_t *_end = end;
+ uint8_t *_start;
+ int version;
+
+ ret = mbedtls_asn1_get_tag(&p, end, &seq_len, SEQUENCE_TAG);
+ if (ret < 0)
+ return ret;
+
+ _start = p;
+ _end = p + seq_len;
+ ret = mbedtls_asn1_get_int(&p, _end, &version);
+ if (ret < 0)
+ return ret;
+
+ /* private key */
+ ret = mbedtls_asn1_get_tag(&p, _end, &pkey_len, MBEDTLS_ASN1_OCTET_STRING);
+ if (ret < 0)
+ return ret;
+ pkey_start = p;
+ p += pkey_len;
+ pkey_end = p;
+
+ /* parameters */
+ ret = mbedtls_asn1_get_tag(&p, _end, &param_len, CONTEXT_TAG(0));
+ if (ret < 0)
+ return ret;
+
+ param = memcpy(buf, p, param_len);
+ p += param_len;
+
+ /* overwrite parameters */
+ param_tag_len = p - pkey_end;
+ ctx->p += param_tag_len;
+ _start += param_tag_len;
+ memmove(ctx->p, ctx->p - param_tag_len, p - ctx->p);
+
+ /* replace sequence tag */
+ ctx->p = _start;
+ uc_p12_add_sequence(ctx, end);
+
+ /* check for Curve25519 or Curve448 */
+ tag_buf = (mbedtls_asn1_buf){
+ .p = (uint8_t *)buf,
+ .len = param_len,
+ };
+ tag_buf.tag = *tag_buf.p;
+ ret = mbedtls_asn1_get_tag(&tag_buf.p, tag_buf.p + param_len, &tag_buf.len, tag_buf.tag);
+ if (ret < 0)
+ return ret;
+
+ oid = MBEDTLS_OID_EC_ALG_UNRESTRICTED;
+ oid_len = MBEDTLS_OID_SIZE(MBEDTLS_OID_EC_ALG_UNRESTRICTED);
+
+#ifdef MBEDTLS_LEGACY
+ (void)pkey_start;
+ (void)grp_id;
+#else
+ ret = mbedtls_oid_get_ec_grp_algid(&tag_buf, &grp_id);
+ if (!ret && (grp_id == MBEDTLS_ECP_DP_CURVE25519 ||
+ grp_id == MBEDTLS_ECP_DP_CURVE448)) {
+ ctx->p = end - pkey_len;
+ memmove(ctx->p, pkey_start, pkey_len);
+ uc_p12_add_tag(ctx, end, MBEDTLS_ASN1_OCTET_STRING);
+ }
+#endif
+ } else {
+ mbedtls_oid_get_oid_by_pk_alg(mbedtls_pk_get_type(pk), &oid, &oid_len);
+ }
+
+ uc_p12_add_tag(ctx, end, MBEDTLS_ASN1_OCTET_STRING);
+
+ /* KeyBag */
+ if (param_len) {
+ CTX_ADD(ctx, param_len);
+ memcpy(ctx->p, param, param_len);
+ }
+ uc_p12_add_algo(ctx, oid, oid_len, param_len);
+ mbedtls_asn1_write_int(&ctx->p, ctx->start, 0);
+ uc_p12_add_sequence(ctx, end);
+
+ ret = uc_p12_encrypt(ctx, end);
+ if (ret < 0)
+ return ret;
+
+ ret = uc_p12_add_enc_params(ctx);
+ if (ret < 0)
+ return ret;
+
+ uc_p12_add_sequence(ctx, end);
+
+ uc_p12_add_bag(ctx, end, bag_end, OID_TAG(PKCS12_SHROUDED_KEY_BAG));
+
+ return 0;
+}
+
+static int
+uc_p12_add_authsafe(struct pkcs12_ctx *ctx, uc_value_t *arg)
+{
+ uc_value_t *extra;
+ uint8_t *end = ctx->p;
+ size_t len;
+ int ret;
+
+ /* authSafe contents */
+ extra = ucv_object_get(arg, "extra", NULL);
+ if (extra) {
+ size_t len;
+
+ if (ucv_type(extra) != UC_ARRAY)
+ return -1;
+
+ len = ucv_array_length(extra);
+ for (size_t i = 0; i < len; i++) {
+ ret = uc_p12_add_cert(ctx, ucv_array_get(extra, i), true);
+ if (ret < 0)
+ return ret;
+ }
+ }
+
+ ret = uc_p12_add_key(ctx, ucv_object_get(arg, "key", NULL));
+ if (ret < 0)
+ return ret;
+
+ ret = uc_p12_add_cert(ctx, ucv_object_get(arg, "cert", NULL), false);
+ if (ret < 0)
+ return ret;
+
+ /* encrypted */
+ uc_p12_add_sequence(ctx, end);
+
+ ret = uc_p12_encrypt(ctx, end);
+ if (ret < 0)
+ return ret;
+
+ uc_p12_add_tag(ctx, end, CONTEXT_TAG(0));
+
+ ret = uc_p12_add_enc_params(ctx);
+ if (ret < 0)
+ return ret;
+
+ mbedtls_asn1_write_oid(&ctx->p, ctx->start, OID_TAG(PKCS7_DATA));
+ uc_p12_add_sequence(ctx, end);
+
+ mbedtls_asn1_write_int(&ctx->p, ctx->start, 0);
+ uc_p12_add_sequence(ctx, end);
+ uc_p12_add_tag(ctx, end, CONTEXT_TAG(0));
+ mbedtls_asn1_write_oid(&ctx->p, ctx->start, OID_TAG(PKCS7_ENCRYPTED_DATA));
+ uc_p12_add_sequence(ctx, end);
+
+ /* authSafe contents */
+ uc_p12_add_sequence(ctx, end);
+
+ /* authSafe header */
+ len = end - ctx->p;
+ uc_p12_add_tag(ctx, end, MBEDTLS_ASN1_OCTET_STRING);
+ uc_p12_add_tag(ctx, end, CONTEXT_TAG(0));
+ mbedtls_asn1_write_oid(&ctx->p, ctx->start, OID_TAG(PKCS7_DATA));
+ uc_p12_add_sequence(ctx, end);
+
+ return len;
+}
+
+static void *
+uc_p12_add_mac_digest_info(struct pkcs12_ctx *ctx)
+{
+ uint8_t *end = ctx->p;
+ size_t len = 20;
+
+ ctx->p -= len;
+ uc_p12_add_tag(ctx, end, MBEDTLS_ASN1_OCTET_STRING);
+ uc_p12_add_algo(ctx, OID_TAG(DIGEST_ALG_SHA1), 0);
+ uc_p12_add_sequence(ctx, end);
+
+ return end - len;
+}
+
+uc_value_t *uc_generate_pkcs12(uc_vm_t *vm, size_t nargs)
+{
+ uc_value_t *arg = uc_fn_arg(0), *pwd_arg;
+ mbedtls_md_context_t hmac;
+ uint8_t *end = (uint8_t *)&buf[sizeof(buf)];
+ struct pkcs12_ctx ctx = {
+ .start = (uint8_t *)&buf[sizeof(buf) / 2],
+ .p = end,
+ };
+ uint8_t *hash, *data;
+ uint8_t key[20], *salt;
+ size_t salt_len = 8;
+ int data_len;
+ uc_value_t *ret = NULL;
+ const char *passwd;
+
+ if (ucv_type(arg) != UC_OBJECT)
+ INVALID_ARG();
+
+ ctx.legacy = ucv_is_truish(ucv_object_get(arg, "legacy", NULL));
+
+ mbedtls_md_init(&hmac);
+ pwd_arg = ucv_object_get(arg, "password", NULL);
+ passwd = ucv_string_get(pwd_arg);
+ if (!passwd)
+ INVALID_ARG();
+
+ /* password is expected to be a UTF-16 string */
+ ctx.password = passwd;
+ ctx.pwd_len = 2 * strlen(passwd) + 2;
+ ctx.pwd = malloc(ctx.pwd_len);
+ for (size_t i = 0; i < ctx.pwd_len; i += 2) {
+ ctx.pwd[i] = 0;
+ ctx.pwd[i + 1] = passwd[i / 2];
+ }
+
+ /* MacData */
+ mbedtls_asn1_write_int(&ctx.p, ctx.start, NUM_ITER);
+
+ ctx.p -= salt_len;
+ salt = ctx.p;
+ random_cb(NULL, salt, salt_len);
+ uc_p12_add_tag(&ctx, salt + salt_len, MBEDTLS_ASN1_OCTET_STRING);
+
+ hash = uc_p12_add_mac_digest_info(&ctx);
+ uc_p12_add_sequence(&ctx, end);
+
+ data = ctx.p;
+ data_len = uc_p12_add_authsafe(&ctx, arg);
+ if (C(data_len) < 0)
+ goto out;
+ data -= data_len;
+
+ mbedtls_asn1_write_int(&ctx.p, ctx.start, 3);
+ uc_p12_add_sequence(&ctx, end);
+
+ if (C(mbedtls_pkcs12_derivation(key, sizeof(key), ctx.pwd, ctx.pwd_len,
+ salt, salt_len, MBEDTLS_MD_SHA1,
+ MBEDTLS_PKCS12_DERIVE_MAC_KEY, NUM_ITER)))
+ goto out;
+
+ if (C(mbedtls_md_setup(&hmac, mbedtls_md_info_from_type(MBEDTLS_MD_SHA1), 1)))
+ goto out;
+ if (C(mbedtls_md_hmac_starts(&hmac, key, sizeof(key))))
+ goto out;
+ if (C(mbedtls_md_hmac_update(&hmac, data, data_len)))
+ goto out;
+ if (C(mbedtls_md_hmac_finish(&hmac, hash)))
+ goto out;
+
+ ret = ucv_string_new_length((char *)ctx.p, end - ctx.p);
+
+out:
+ free(ctx.pwd);
+ mbedtls_md_free(&hmac);
+ return ret;
+}
diff --git a/package/utils/ucode-mod-pkgen/src/ucode.c b/package/utils/ucode-mod-pkgen/src/ucode.c
new file mode 100644
index 0000000000..4b7779f136
--- /dev/null
+++ b/package/utils/ucode-mod-pkgen/src/ucode.c
@@ -0,0 +1,598 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2024 Felix Fietkau <nbd@nbd.name>
+ */
+#include <sys/types.h>
+#include <sys/random.h>
+
+#include <stdint.h>
+#include <limits.h>
+#include <errno.h>
+
+#include <mbedtls/entropy.h>
+#include <mbedtls/x509_crt.h>
+#include <mbedtls/ecp.h>
+#include <mbedtls/rsa.h>
+
+#include <ucode/module.h>
+
+#include "pk.h"
+
+/* mbedtls < 3.x compat */
+#ifdef MBEDTLS_LEGACY
+#define mbedtls_pk_parse_key(pk, key, keylen, passwd, passwdlen, random, random_ctx) \
+ mbedtls_pk_parse_key(pk, key, keylen, passwd, passwdlen)
+#endif
+
+static uc_resource_type_t *uc_pk_type, *uc_crt_type;
+static uc_value_t *registry;
+char buf[32 * 1024];
+int mbedtls_errno;
+
+struct uc_cert_wr {
+ mbedtls_x509write_cert crt; /* must be first */
+ mbedtls_mpi mpi;
+ unsigned int reg;
+};
+
+static unsigned int uc_reg_add(uc_value_t *val)
+{
+ size_t i = 0;
+
+ while (ucv_array_get(registry, i))
+ i++;
+
+ ucv_array_set(registry, i, ucv_get(val));
+
+ return i;
+}
+
+int random_cb(void *ctx, unsigned char *out, size_t len)
+{
+#ifdef linux
+ if (getrandom(out, len, 0) != (ssize_t) len)
+ return MBEDTLS_ERR_ENTROPY_SOURCE_FAILED;
+#else
+ static FILE *f;
+
+ if (!f)
+ f = fopen("/dev/urandom", "r");
+ if (fread(out, len, 1, f) != 1)
+ return MBEDTLS_ERR_ENTROPY_SOURCE_FAILED;
+#endif
+
+ return 0;
+}
+
+int64_t get_int_arg(uc_value_t *obj, const char *key, int64_t defval)
+{
+ uc_value_t *uval = ucv_object_get(obj, key, NULL);
+ int64_t val;
+
+ if (!uval)
+ return defval;
+
+ val = ucv_int64_get(uval);
+ if (errno || val < 0 || val > INT_MAX)
+ return INT_MIN;
+
+ return val ? val : defval;
+}
+
+static int
+gen_rsa_key(mbedtls_pk_context *pk, uc_value_t *arg)
+{
+ int64_t key_size, exp;
+
+ key_size = get_int_arg(arg, "size", 2048);
+ exp = get_int_arg(arg, "exponent", 65537);
+ if (key_size < 0 || exp < 0)
+ return -1;
+
+ return mbedtls_rsa_gen_key(mbedtls_pk_rsa(*pk), random_cb, NULL, key_size, exp);
+}
+
+static int
+gen_ec_key(mbedtls_pk_context *pk, uc_value_t *arg)
+{
+ mbedtls_ecp_group_id curve;
+ const char *c_name;
+ uc_value_t *c_arg;
+
+ c_arg = ucv_object_get(arg, "curve", NULL);
+ if (c_arg && ucv_type(c_arg) != UC_STRING)
+ return -1;
+
+ c_name = ucv_string_get(c_arg);
+ if (!c_name)
+ curve = MBEDTLS_ECP_DP_SECP256R1;
+ else {
+ const mbedtls_ecp_curve_info *curve_info;
+ curve_info = mbedtls_ecp_curve_info_from_name(c_name);
+ if (!curve_info)
+ return MBEDTLS_ERR_PK_UNKNOWN_NAMED_CURVE;
+
+ curve = curve_info->grp_id;
+ }
+
+ return mbedtls_ecp_gen_key(curve, mbedtls_pk_ec(*pk), random_cb, NULL);
+}
+
+static void free_pk(void *pk)
+{
+ if (!pk)
+ return;
+
+ mbedtls_pk_free(pk);
+ free(pk);
+}
+
+static void free_crt(void *ptr)
+{
+ struct uc_cert_wr *crt = ptr;
+
+ if (!crt)
+ return;
+
+ mbedtls_x509write_crt_free(&crt->crt);
+ mbedtls_mpi_free(&crt->mpi);
+ ucv_array_set(registry, crt->reg, NULL);
+ free(crt);
+}
+
+static uc_value_t *
+uc_generate_key(uc_vm_t *vm, size_t nargs)
+{
+ uc_value_t *cur, *arg = uc_fn_arg(0);
+ mbedtls_pk_type_t pk_type;
+ mbedtls_pk_context *pk;
+ const char *type;
+ int ret;
+
+ if (ucv_type(arg) != UC_OBJECT)
+ INVALID_ARG();
+
+ cur = ucv_object_get(arg, "type", NULL);
+ type = ucv_string_get(cur);
+ if (!type)
+ INVALID_ARG();
+
+ if (!strcmp(type, "rsa"))
+ pk_type = MBEDTLS_PK_RSA;
+ else if (!strcmp(type, "ec"))
+ pk_type = MBEDTLS_PK_ECKEY;
+ else
+ INVALID_ARG();
+
+ pk = calloc(1, sizeof(*pk));
+ mbedtls_pk_init(pk);
+ mbedtls_pk_setup(pk, mbedtls_pk_info_from_type(pk_type));
+ switch (pk_type) {
+ case MBEDTLS_PK_RSA:
+ ret = C(gen_rsa_key(pk, arg));
+ break;
+ case MBEDTLS_PK_ECKEY:
+ ret = C(gen_ec_key(pk, arg));
+ break;
+ default:
+ ret = -1;
+ }
+
+ if (ret) {
+ free_pk(pk);
+ return NULL;
+ }
+
+ return uc_resource_new(uc_pk_type, pk);
+}
+
+static uc_value_t *
+uc_load_key(uc_vm_t *vm, size_t nargs)
+{
+ uc_value_t *keystr = uc_fn_arg(0);
+ uc_value_t *pub = uc_fn_arg(1);
+ uc_value_t *passwd = uc_fn_arg(2);
+ mbedtls_pk_context *pk;
+ int ret;
+
+ if (ucv_type(keystr) != UC_STRING ||
+ (pub && ucv_type(pub) != UC_BOOLEAN) ||
+ (passwd && ucv_type(passwd) != UC_STRING))
+ INVALID_ARG();
+
+ pk = calloc(1, sizeof(*pk));
+ mbedtls_pk_init(pk);
+ if (ucv_is_truish(pub))
+ ret = C(mbedtls_pk_parse_public_key(pk, (const uint8_t *)ucv_string_get(keystr),
+ ucv_string_length(keystr) + 1));
+ else
+ ret = C(mbedtls_pk_parse_key(pk, (const uint8_t *)ucv_string_get(keystr),
+ ucv_string_length(keystr) + 1,
+ (const uint8_t *)ucv_string_get(passwd),
+ ucv_string_length(passwd) + 1,
+ random_cb, NULL));
+ if (ret) {
+ free_pk(pk);
+ return NULL;
+ }
+
+ return uc_resource_new(uc_pk_type, pk);
+}
+
+static void
+uc_cert_info_add(uc_value_t *info, const char *name, int len)
+{
+ uc_value_t *val;
+
+ if (len < 0)
+ return;
+
+ val = ucv_string_new_length(buf, len);
+ ucv_object_add(info, name, ucv_get(val));
+}
+
+static void
+uc_cert_info_add_name(uc_value_t *info, const char *name, mbedtls_x509_name *dn)
+{
+ int len;
+
+ len = mbedtls_x509_dn_gets(buf, sizeof(buf), dn);
+ uc_cert_info_add(info, name, len);
+}
+
+static void
+uc_cert_info_add_time(uc_value_t *info, const char *name, mbedtls_x509_time *t)
+{
+ int len;
+
+ len = snprintf(buf, sizeof(buf), "%04d%02d%02d%02d%02d%02d",
+ t->year, t->mon, t->day, t->hour, t->min, t->sec);
+ uc_cert_info_add(info, name, len);
+}
+
+static uc_value_t *
+uc_cert_info(uc_vm_t *vm, size_t nargs)
+{
+ uc_value_t *arg = uc_fn_arg(0);
+ mbedtls_x509_crt crt, *cur;
+ uc_value_t *ret = NULL;
+
+ if (ucv_type(arg) != UC_STRING)
+ return NULL;
+
+ mbedtls_x509_crt_init(&crt);
+ if (C(mbedtls_x509_crt_parse(&crt, (const void *)ucv_string_get(arg), ucv_string_length(arg) + 1)) < 0)
+ goto out;
+
+ ret = ucv_array_new(vm);
+ for (cur = &crt; cur && cur->version != 0; cur = cur->next) {
+ uc_value_t *info = ucv_object_new(vm);
+ int len;
+
+ ucv_array_push(ret, info);
+ ucv_object_add(info, "version", ucv_int64_new(cur->version));
+
+ uc_cert_info_add_name(info, "issuer", &cur->issuer);
+ uc_cert_info_add_name(info, "subject", &cur->issuer);
+ uc_cert_info_add_time(info, "valid_from", &cur->valid_from);
+ uc_cert_info_add_time(info, "valid_to", &cur->valid_to);
+
+ len = mbedtls_x509_serial_gets(buf, sizeof(buf), &cur->serial);
+ uc_cert_info_add(info, "serial", len);
+ }
+
+out:
+ mbedtls_x509_crt_free(&crt);
+ return ret;
+}
+
+static uc_value_t *
+uc_pk_pem(uc_vm_t *vm, size_t nargs)
+{
+ mbedtls_pk_context *pk = uc_fn_thisval("mbedtls.pk");
+ uc_value_t *pub = uc_fn_arg(0);
+
+ if (!pk)
+ return NULL;
+
+ if (ucv_is_truish(pub))
+ CHECK(mbedtls_pk_write_pubkey_pem(pk, (void *)buf, sizeof(buf)));
+ else
+ CHECK(mbedtls_pk_write_key_pem(pk, (void *)buf, sizeof(buf)));
+
+ return ucv_string_new(buf);
+}
+
+static uc_value_t *
+uc_pk_der(uc_vm_t *vm, size_t nargs)
+{
+ mbedtls_pk_context *pk = uc_fn_thisval("mbedtls.pk");
+ uc_value_t *pub = uc_fn_arg(0);
+ int len;
+
+ if (!pk)
+ return NULL;
+
+ if (ucv_is_truish(pub))
+ len = mbedtls_pk_write_pubkey_der(pk, (void *)buf, sizeof(buf));
+ else
+ len = mbedtls_pk_write_key_der(pk, (void *)buf, sizeof(buf));
+ if (len < 0)
+ CHECK(len);
+
+ return ucv_string_new_length(buf + sizeof(buf) - len, len);
+}
+
+static uc_value_t *
+uc_crt_pem(uc_vm_t *vm, size_t nargs)
+{
+ mbedtls_x509write_cert *crt = uc_fn_thisval("mbedtls.crt");
+ if (!crt)
+ return NULL;
+
+ CHECK(mbedtls_x509write_crt_pem(crt, (void *)buf, sizeof(buf), random_cb, NULL));
+
+ return ucv_string_new(buf);
+}
+
+static uc_value_t *
+uc_crt_der(uc_vm_t *vm, size_t nargs)
+{
+ mbedtls_x509write_cert *crt = uc_fn_thisval("mbedtls.crt");
+ int len;
+
+ if (!crt)
+ return NULL;
+
+ len = mbedtls_x509write_crt_der(crt, (void *)buf, sizeof(buf), random_cb, NULL);
+ if (len < 0)
+ CHECK(len);
+
+ return ucv_string_new_length(buf, len);
+}
+
+static int
+uc_cert_set_validity(mbedtls_x509write_cert *crt, uc_value_t *arg)
+{
+ uc_value_t *from = ucv_array_get(arg, 0);
+ uc_value_t *to = ucv_array_get(arg, 1);
+
+ if (ucv_type(from) != UC_STRING || ucv_type(to) != UC_STRING)
+ return -1;
+
+ return mbedtls_x509write_crt_set_validity(crt, ucv_string_get(from), ucv_string_get(to));
+}
+
+static int
+uc_cert_init(mbedtls_x509write_cert *crt, mbedtls_mpi *mpi, uc_value_t *reg, uc_value_t *arg)
+{
+ uc_value_t *cur;
+ int64_t serial;
+ int path_len;
+ int version;
+ bool ca;
+
+ mbedtls_mpi_init(mpi);
+ mbedtls_x509write_crt_init(crt);
+ mbedtls_x509write_crt_set_md_alg(crt, MBEDTLS_MD_SHA256);
+
+ ca = ucv_is_truish(ucv_object_get(arg, "ca", NULL));
+ path_len = get_int_arg(arg, "max_pathlen", ca ? -1 : 0);
+ if (path_len < -1)
+ return -1;
+
+ version = get_int_arg(arg, "version", 3);
+ if (version < 0 || version > 3)
+ return -1;
+
+ serial = get_int_arg(arg, "serial", 1);
+ if (serial < 0)
+ return -1;
+
+ mbedtls_mpi_lset(mpi, serial);
+ mbedtls_x509write_crt_set_serial(crt, mpi);
+ mbedtls_x509write_crt_set_version(crt, version - 1);
+ CHECK_INT(mbedtls_x509write_crt_set_basic_constraints(crt, ca, path_len));
+
+ cur = ucv_object_get(arg, "subject_name", NULL);
+ if (ucv_type(cur) == UC_STRING)
+ CHECK_INT(mbedtls_x509write_crt_set_subject_name(crt, ucv_string_get(cur)));
+ else
+ return -1;
+ cur = ucv_object_get(arg, "subject_key", NULL);
+ if (cur) {
+ mbedtls_pk_context *key = ucv_resource_data(cur, "mbedtls.pk");
+ if (!key)
+ return -1;
+
+ ucv_array_set(reg, 0, ucv_get(cur));
+ mbedtls_x509write_crt_set_subject_key(crt, key);
+ mbedtls_x509write_crt_set_subject_key_identifier(crt);
+ } else
+ return -1;
+
+ cur = ucv_object_get(arg, "issuer_name", NULL);
+ if (ucv_type(cur) == UC_STRING)
+ CHECK_INT(mbedtls_x509write_crt_set_issuer_name(crt, ucv_string_get(cur)));
+ else
+ return -1;
+ cur = ucv_object_get(arg, "issuer_key", NULL);
+ if (cur) {
+ mbedtls_pk_context *key = ucv_resource_data(cur, "mbedtls.pk");
+ if (!key)
+ return -1;
+
+ ucv_array_set(reg, 1, ucv_get(cur));
+ mbedtls_x509write_crt_set_issuer_key(crt, key);
+ mbedtls_x509write_crt_set_authority_key_identifier(crt);
+ } else
+ return -1;
+
+ cur = ucv_object_get(arg, "validity", NULL);
+ if (ucv_type(cur) != UC_ARRAY || ucv_array_length(cur) != 2)
+ return -1;
+ if (uc_cert_set_validity(crt, cur))
+ return -1;
+
+ cur = ucv_object_get(arg, "key_usage", NULL);
+ if (ucv_type(cur) == UC_ARRAY) {
+ static const struct {
+ const char *name;
+ uint8_t val;
+ } key_flags[] = {
+ { "digital_signature", MBEDTLS_X509_KU_DIGITAL_SIGNATURE },
+ { "non_repudiation", MBEDTLS_X509_KU_NON_REPUDIATION },
+ { "key_encipherment", MBEDTLS_X509_KU_KEY_ENCIPHERMENT },
+ { "data_encipherment", MBEDTLS_X509_KU_DATA_ENCIPHERMENT },
+ { "key_agreement", MBEDTLS_X509_KU_KEY_AGREEMENT },
+ { "key_cert_sign", MBEDTLS_X509_KU_KEY_CERT_SIGN },
+ { "crl_sign", MBEDTLS_X509_KU_CRL_SIGN },
+ };
+ uint8_t key_usage = 0;
+ size_t len = ucv_array_length(cur);
+
+ for (size_t i = 0; i < len; i++) {
+ uc_value_t *val = ucv_array_get(cur, i);
+ const char *str;
+ size_t k;
+
+ str = ucv_string_get(val);
+ if (!str)
+ return -1;
+
+ for (k = 0; k < ARRAY_SIZE(key_flags); k++)
+ if (!strcmp(str, key_flags[k].name))
+ break;
+ if (k == ARRAY_SIZE(key_flags))
+ return -1;
+
+ key_usage |= key_flags[k].val;
+ }
+ CHECK_INT(mbedtls_x509write_crt_set_key_usage(crt, key_usage));
+ } else if (cur)
+ return -1;
+
+#ifndef MBEDTLS_LEGACY
+ cur = ucv_object_get(arg, "ext_key_usage", NULL);
+ if (ucv_type(cur) == UC_ARRAY && ucv_array_length(cur)) {
+ static const struct {
+ const char *name;
+ struct mbedtls_asn1_buf val;
+ } key_flags[] = {
+#define __oid(name, val) \
+ { \
+ name, \
+ { \
+ .tag = MBEDTLS_ASN1_OID, \
+ .len = sizeof(MBEDTLS_OID_##val), \
+ .p = (uint8_t *)MBEDTLS_OID_##val, \
+ } \
+ }
+ __oid("server_auth", SERVER_AUTH),
+ __oid("client_auth", CLIENT_AUTH),
+ __oid("code_signing", CODE_SIGNING),
+ __oid("email_protection", EMAIL_PROTECTION),
+ __oid("time_stamping", TIME_STAMPING),
+ __oid("ocsp_signing", OCSP_SIGNING),
+ __oid("any", ANY_EXTENDED_KEY_USAGE)
+ };
+ struct mbedtls_asn1_sequence *elem;
+ size_t len = ucv_array_length(cur);
+
+ elem = calloc(len, sizeof(*elem));
+ for (size_t i = 0; i < len; i++) {
+ uc_value_t *val = ucv_array_get(cur, i);
+ const char *str;
+ size_t k;
+
+ str = ucv_string_get(val);
+ if (!str)
+ return -1;
+
+ for (k = 0; k < ARRAY_SIZE(key_flags); k++)
+ if (!strcmp(str, key_flags[k].name))
+ break;
+
+ if (k == ARRAY_SIZE(key_flags)) {
+ free(elem);
+ return -1;
+ }
+ elem[i].buf = key_flags[k].val;
+ if (i + 1 < len)
+ elem[i].next = &elem[i + 1];
+ }
+
+ CHECK_INT(mbedtls_x509write_crt_set_ext_key_usage(crt, elem));
+ } else if (cur)
+ return -1;
+#endif
+
+ return 0;
+}
+
+static uc_value_t *
+uc_generate_cert(uc_vm_t *vm, size_t nargs)
+{
+ struct uc_cert_wr *crt;
+ uc_value_t *arg = uc_fn_arg(0);
+ uc_value_t *reg;
+
+ if (ucv_type(arg) != UC_OBJECT)
+ return NULL;
+
+ reg = ucv_array_new(vm);
+ crt = calloc(1, sizeof(*crt));
+ if (C(uc_cert_init(&crt->crt, &crt->mpi, reg, arg))) {
+ free(crt);
+ return NULL;
+ }
+
+ crt->reg = uc_reg_add(reg);
+
+ return uc_resource_new(uc_crt_type, crt);
+}
+
+static uc_value_t *
+uc_mbedtls_error(uc_vm_t *vm, size_t nargs)
+{
+ mbedtls_strerror(mbedtls_errno, buf, sizeof(buf));
+
+ return ucv_string_new(buf);
+}
+
+static uc_value_t *
+uc_mbedtls_errno(uc_vm_t *vm, size_t nargs)
+{
+ return ucv_int64_new(mbedtls_errno);
+}
+
+
+static const uc_function_list_t pk_fns[] = {
+ { "pem", uc_pk_pem },
+ { "der", uc_pk_der },
+};
+
+static const uc_function_list_t crt_fns[] = {
+ { "pem", uc_crt_pem },
+ { "der", uc_crt_der },
+};
+
+static const uc_function_list_t global_fns[] = {
+ { "load_key", uc_load_key },
+ { "cert_info", uc_cert_info },
+ { "generate_key", uc_generate_key },
+ { "generate_cert", uc_generate_cert },
+ { "generate_pkcs12", uc_generate_pkcs12 },
+ { "errno", uc_mbedtls_errno },
+ { "error", uc_mbedtls_error },
+};
+
+void uc_module_init(uc_vm_t *vm, uc_value_t *scope)
+{
+ uc_pk_type = uc_type_declare(vm, "mbedtls.pk", pk_fns, free_pk);
+ uc_crt_type = uc_type_declare(vm, "mbedtls.crt", crt_fns, free_crt);
+ uc_function_list_register(scope, global_fns);
+
+ registry = ucv_array_new(vm);
+ uc_vm_registry_set(vm, "pkgen.registry", registry);
+}
diff --git a/package/utils/ucode-mod-uline/Makefile b/package/utils/ucode-mod-uline/Makefile
new file mode 100644
index 0000000000..50aecd4f7f
--- /dev/null
+++ b/package/utils/ucode-mod-uline/Makefile
@@ -0,0 +1,32 @@
+include $(TOPDIR)/rules.mk
+
+PKG_NAME:=ucode-mod-uline
+PKG_RELEASE:=$(AUTORELEASE)
+PKG_LICENSE:=GPL-2.0-or-later
+PKG_MAINTAINER:=Felix Fietkau <nbd@nbd.name>
+
+include $(INCLUDE_DIR)/package.mk
+include $(INCLUDE_DIR)/cmake.mk
+
+CMAKE_INSTALL := 1
+
+define Package/ucode-mod-uline
+ SECTION:=utils
+ CATEGORY:=Utilities
+ TITLE:=ucode module for terminal line editing
+ DEPENDS:=+libucode +libubox
+endef
+
+CMAKE_OPTIONS += -DUSE_SYSTEM_WCHAR=ON
+
+define Package/ucode-mod-uline/description
+This module provides similar functionality as libreadline for ucode, without
+depending on other libraries like ncurses.
+endef
+
+define Package/ucode-mod-uline/install
+ $(INSTALL_DIR) $(1)/usr/lib/ucode
+ $(CP) $(PKG_INSTALL_DIR)/usr/lib/ucode/uline.so $(1)/usr/lib/ucode/
+endef
+
+$(eval $(call BuildPackage,ucode-mod-uline))
diff --git a/package/utils/ucode-mod-uline/src/CMakeLists.txt b/package/utils/ucode-mod-uline/src/CMakeLists.txt
new file mode 100644
index 0000000000..efa2d80f90
--- /dev/null
+++ b/package/utils/ucode-mod-uline/src/CMakeLists.txt
@@ -0,0 +1,44 @@
+cmake_minimum_required(VERSION 3.13)
+
+PROJECT(uline C)
+ADD_DEFINITIONS(-Os -ggdb -Wall -Werror --std=gnu99 -ffunction-sections -fwrapv -D_GNU_SOURCE -Wno-error=unused-function -Wno-parentheses -Wno-sign-compare)
+
+OPTION(USE_SYSTEM_WCHAR "Use system multibyte implementation for UTF-8" OFF)
+IF(CMAKE_C_COMPILER_VERSION VERSION_GREATER 6)
+ ADD_DEFINITIONS(-Wextra -Werror=implicit-function-declaration)
+ ADD_DEFINITIONS(-Wformat -Werror=format-security -Werror=format-nonliteral)
+ENDIF()
+ADD_DEFINITIONS(-Wmissing-declarations -Wno-error=unused-variable -Wno-unused-parameter)
+
+IF(APPLE)
+ SET(UCODE_MODULE_LINK_OPTIONS "LINKER:-undefined,dynamic_lookup")
+ELSE()
+ SET(CMAKE_SHARED_LIBRARY_LINK_C_FLAGS "-Wl,--gc-sections")
+ENDIF()
+
+IF(DEBUG)
+ ADD_DEFINITIONS(-DDEBUG -g3 -O0)
+ELSE()
+ ADD_DEFINITIONS(-DNDEBUG)
+ENDIF()
+
+FIND_LIBRARY(ucode NAMES ucode)
+FIND_LIBRARY(libubox NAMES ubox)
+FIND_PATH(uloop_include_dir NAMES libubox/uloop.h)
+FIND_PATH(ucode_include_dir NAMES ucode/module.h)
+INCLUDE_DIRECTORIES(${ucode_include_dir} ${uloop_include_dir})
+
+ADD_LIBRARY(uline STATIC uline.c utf8.c vt100.c)
+set_property(TARGET uline PROPERTY POSITION_INDEPENDENT_CODE ON)
+IF(USE_SYSTEM_WCHAR)
+ TARGET_COMPILE_DEFINITIONS(uline PUBLIC USE_SYSTEM_WCHAR)
+ENDIF()
+
+ADD_LIBRARY(uline_lib MODULE ucode.c)
+SET_TARGET_PROPERTIES(uline_lib PROPERTIES OUTPUT_NAME uline PREFIX "")
+TARGET_LINK_OPTIONS(uline_lib PRIVATE ${UCODE_MODULE_LINK_OPTIONS})
+TARGET_LINK_LIBRARIES(uline_lib uline ${libubox})
+
+install(FILES uline.h DESTINATION include)
+INSTALL(TARGETS uline LIBRARY DESTINATION lib)
+INSTALL(TARGETS uline_lib LIBRARY DESTINATION lib/ucode)
diff --git a/package/utils/ucode-mod-uline/src/private.h b/package/utils/ucode-mod-uline/src/private.h
new file mode 100644
index 0000000000..e53ec22e03
--- /dev/null
+++ b/package/utils/ucode-mod-uline/src/private.h
@@ -0,0 +1,206 @@
+// SPDX-License-Identifier: ISC
+/*
+ * Copyright (C) 2025 Felix Fietkau <nbd@nbd.name>
+ */
+#ifndef __EDITLINE_PRIVATE_H
+#define __EDITLINE_PRIVATE_H
+
+#include <stdio.h>
+
+#define KEY_NUL 0 // ^@ Null character
+#define KEY_SOH 1 // ^A Start of heading, = console interrupt
+#define KEY_STX 2 // ^B Start of text, maintenance mode on HP console
+#define KEY_ETX 3 // ^C End of text
+#define KEY_EOT 4 // ^D End of transmission, not the same as ETB
+#define KEY_ENQ 5 // ^E Enquiry, goes with ACK; old HP flow control
+#define KEY_ACK 6 // ^F Acknowledge, clears ENQ logon hand
+#define KEY_BEL 7 // ^G Bell, rings the bell
+#define KEY_BS 8 // ^H Backspace, works on HP terminals/computers
+#define KEY_HT 9 // ^I Horizontal tab, move to next tab stop
+#define KEY_LF 10 // ^J Line Feed
+#define KEY_VT 11 // ^K Vertical tab
+#define KEY_FF 12 // ^L Form Feed, page eject
+#define KEY_CR 13 // ^M Carriage Return
+#define KEY_SO 14 // ^N Shift Out, alternate character set
+#define KEY_SI 15 // ^O Shift In, resume defaultn character set
+#define KEY_DLE 16 // ^P Data link escape
+#define KEY_DC1 17 // ^Q XON, with XOFF to pause listings; "okay to send"
+#define KEY_DC2 18 // ^R Device control 2, block-mode flow control
+#define KEY_DC3 19 // ^S XOFF, with XON is TERM=18 flow control
+#define KEY_DC4 20 // ^T Device control 4
+#define KEY_NAK 21 // ^U Negative acknowledge
+#define KEY_SYN 22 // ^V Synchronous idle
+#define KEY_ETB 23 // ^W End transmission block, not the same as EOT
+#define KEY_CAN 24 // ^X Cancel line, MPE echoes !!!
+#define KEY_EM 25 // ^Y End of medium, Control-Y interrupt
+#define KEY_SUB 26 // ^Z Substitute
+#define KEY_ESC 27 // ^[ Escape, next character is not echoed
+#define KEY_FS 28 // ^\ File separator
+#define KEY_GS 29 // ^] Group separator
+#define KEY_RS 30 // ^^ Record separator, block-mode terminator
+#define KEY_US 31 // ^_ Unit separator
+#define KEY_DEL 127 // Delete (not a real control character)
+
+// Types of escape code
+enum vt100_escape {
+ VT100_INCOMPLETE,
+ VT100_UNKNOWN,
+ VT100_IGNORE,
+ VT100_CURSOR_UP,
+ VT100_CURSOR_DOWN,
+ VT100_CURSOR_LEFT,
+ VT100_CURSOR_WORD_LEFT,
+ VT100_CURSOR_RIGHT,
+ VT100_CURSOR_WORD_RIGHT,
+ VT100_CURSOR_POS,
+ VT100_HOME,
+ VT100_END,
+ VT100_INSERT,
+ VT100_DELETE,
+ VT100_DELETE_LEFT,
+ VT100_DELETE_LEFT_WORD,
+ VT100_PAGE_UP,
+ VT100_PAGE_DOWN,
+};
+
+ssize_t utf8_nsyms(const char *str, size_t len);
+enum vt100_escape vt100_esc_decode(const char *str, uint32_t *data);
+
+// helpers:
+void __vt100_csi_num(FILE *out, int num, char code);
+void __vt100_csi2(FILE *out, char c1, char c2);
+void __vt100_esc(FILE *out, char c);
+static inline void __vt100_sgr(FILE *out, int code)
+{
+ __vt100_csi2(out, code + '0', 'm');
+}
+
+
+static inline void vt100_attr_reset(FILE *out)
+{
+ __vt100_sgr(out, 0);
+}
+
+static inline void vt100_attr_bright(FILE *out)
+{
+ __vt100_sgr(out, 1);
+}
+
+static inline void vt100_attr_dim(FILE *out)
+{
+ __vt100_sgr(out, 2);
+}
+
+static inline void vt100_attr_underscore(FILE *out)
+{
+ __vt100_sgr(out, 4);
+}
+
+static inline void vt100_attr_blink(FILE *out)
+{
+ __vt100_sgr(out, 5);
+}
+
+static inline void vt100_attr_reverse(FILE *out)
+{
+ __vt100_sgr(out, 7);
+}
+
+static inline void vt100_attr_hidden(FILE *out)
+{
+ __vt100_sgr(out, 8);
+}
+
+static inline void vt100_erase_line(FILE *out)
+{
+ __vt100_csi2(out, '2', 'K');
+}
+
+static inline void vt100_clear_screen(FILE *out)
+{
+ __vt100_csi2(out, '2', 'J');
+}
+
+static inline void vt100_cursor_save(FILE *out)
+{
+ __vt100_esc(out, '7');
+}
+
+static inline void vt100_cursor_restore(FILE *out)
+{
+ __vt100_esc(out, '8');
+}
+
+static inline void vt100_scroll_up(FILE *out)
+{
+ __vt100_esc(out, 'D');
+}
+
+static inline void vt100_scroll_down(FILE *out)
+{
+ __vt100_esc(out, 'M');
+}
+
+static inline void vt100_next_line(FILE *out)
+{
+ __vt100_esc(out, 'E');
+}
+
+static inline void vt100_cursor_up(FILE *out, int count)
+{
+ __vt100_csi_num(out, count, 'A');
+}
+
+static inline void vt100_cursor_down(FILE *out, int count)
+{
+ __vt100_csi_num(out, count, 'B');
+}
+
+static inline void vt100_cursor_forward(FILE *out, int count)
+{
+ __vt100_csi_num(out, count, 'C');
+}
+
+static inline void vt100_cursor_back(FILE *out, int count)
+{
+ __vt100_csi_num(out, count, 'D');
+}
+
+static inline void vt100_cursor_home(FILE *out)
+{
+ __vt100_csi2(out, 'H', 0);
+}
+
+static inline void vt100_erase(FILE *out, int count)
+{
+ __vt100_csi_num(out, count, 'P');
+}
+
+static inline void vt100_erase_down(FILE *out)
+{
+ __vt100_csi2(out, 'J', 0);
+}
+
+static inline void vt100_erase_right(FILE *out)
+{
+ __vt100_csi2(out, 'K', 0);
+}
+
+static inline void vt100_ding(FILE *out)
+{
+ fputc(7, out);
+ fflush(out);
+}
+
+static inline void vt100_request_window_size(FILE *out)
+{
+ fputs(
+ "\e7" /* save cursor position */
+ "\e[r" /* reset margins */
+ "\e[999;999H" /* move cursor to bottom right */
+ "\e[6n" /* report cursor position */
+ "\e8", /* restore cursor position */
+ out);
+}
+
+#endif
diff --git a/package/utils/ucode-mod-uline/src/ucode.c b/package/utils/ucode-mod-uline/src/ucode.c
new file mode 100644
index 0000000000..bf53a636ed
--- /dev/null
+++ b/package/utils/ucode-mod-uline/src/ucode.c
@@ -0,0 +1,908 @@
+// SPDX-License-Identifier: ISC
+/*
+ * Copyright (C) 2025 Felix Fietkau <nbd@nbd.name>
+ */
+#include <stdio.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+#include <poll.h>
+
+#include <ucode/module.h>
+#include <libubox/list.h>
+#include <libubox/uloop.h>
+
+#include "uline.h"
+
+static uc_value_t *registry;
+static uc_resource_type_t *state_type, *argp_type;
+
+enum {
+ STATE_RES,
+ STATE_CB,
+ STATE_INPUT,
+ STATE_OUTPUT,
+ STATE_POLL_CB,
+};
+
+struct uc_uline_state {
+ struct uloop_fd fd;
+
+ struct uline_state s;
+ int registry_index;
+
+ uc_vm_t *vm;
+ uc_value_t *state, *cb, *res, *poll_cb;
+
+ uc_value_t *line;
+
+ uint32_t input_mask[256 / 32];
+};
+
+struct uc_arg_parser {
+ char line_sep;
+};
+
+static unsigned int
+registry_set(uc_vm_t *vm, uc_value_t *val)
+{
+ uc_value_t *registry;
+ size_t i, len;
+
+ registry = uc_vm_registry_get(vm, "uline.registry");
+ len = ucv_array_length(registry);
+ for (i = 0; i < len; i++)
+ if (ucv_array_get(registry, i) == NULL)
+ break;
+
+ ucv_array_set(registry, i, ucv_get(val));
+ return i;
+}
+
+static uc_value_t *
+uc_uline_poll(uc_vm_t *vm, size_t nargs)
+{
+ struct uc_uline_state *us = uc_fn_thisval("uline.state");
+ uc_value_t *val;
+
+ if (!us)
+ return NULL;
+
+ uline_poll(&us->s);
+ val = us->line;
+ us->line = NULL;
+
+ return val;
+}
+
+static uc_value_t *
+uc_uline_poll_key(uc_vm_t *vm, size_t nargs)
+{
+ struct uc_uline_state *us = uc_fn_thisval("uline.state");
+ uc_value_t *timeout_arg = uc_fn_arg(0);
+ struct pollfd pfd = {};
+ int timeout, len;
+ char c;
+
+ if (!us)
+ return NULL;
+
+ if (ucv_type(timeout_arg) == UC_INTEGER)
+ timeout = ucv_int64_get(timeout_arg);
+ else
+ timeout = -1;
+
+ pfd.fd = us->s.input;
+ pfd.events = POLLIN;
+ poll(&pfd, 1, timeout);
+ if (!(pfd.revents & POLLIN))
+ return NULL;
+
+ do {
+ len = read(pfd.fd, &c, 1);
+ } while (len < 0 && errno == EINTR);
+
+ if (len != 1)
+ return NULL;
+
+ return ucv_string_new_length(&c, 1);
+}
+
+static uc_value_t *
+uc_uline_poll_stop(uc_vm_t *vm, size_t nargs)
+{
+ struct uc_uline_state *us = uc_fn_thisval("uline.state");
+
+ if (!us)
+ return NULL;
+
+ us->s.stop = true;
+
+ return NULL;
+}
+
+static uc_value_t *
+uc_uline_get_window(uc_vm_t *vm, size_t nargs)
+{
+ struct uc_uline_state *us = uc_fn_thisval("uline.state");
+ uc_value_t *val;
+
+ if (!us)
+ return NULL;
+
+ val = ucv_object_new(vm);
+ ucv_object_add(val, "x", ucv_int64_new(us->s.cols));
+ ucv_object_add(val, "y", ucv_int64_new(us->s.rows));
+ return val;
+}
+
+static uc_value_t *
+uc_uline_get_line(uc_vm_t *vm, size_t nargs)
+{
+ struct uc_uline_state *us = uc_fn_thisval("uline.state");
+ uc_value_t *line2 = uc_fn_arg(0);
+ uc_value_t *state;
+ const char *line;
+ size_t len;
+
+ if (!us)
+ return NULL;
+
+ state = ucv_object_new(vm);
+ if (ucv_is_truish(line2))
+ uline_get_line2(&us->s, &line, &len);
+ else
+ uline_get_line(&us->s, &line, &len);
+ ucv_object_add(state, "line", ucv_string_new_length(line, len));
+ ucv_object_add(state, "pos", ucv_int64_new(us->s.line.pos));
+
+ return state;
+}
+
+static uc_value_t *
+uc_uline_set_state(uc_vm_t *vm, size_t nargs)
+{
+ struct uc_uline_state *us = uc_fn_thisval("uline.state");
+ uc_value_t *state = uc_fn_arg(0);
+ uc_value_t *arg;
+ bool found;
+
+ if (!us || ucv_type(state) != UC_OBJECT)
+ return NULL;
+
+ if ((arg = ucv_object_get(state, "prompt", NULL)) != NULL) {
+ if (ucv_type(arg) != UC_STRING)
+ return NULL;
+
+ uline_set_prompt(&us->s, ucv_string_get(arg));
+ }
+
+ if ((arg = ucv_object_get(state, "line", NULL)) != NULL) {
+ if (ucv_type(arg) != UC_STRING)
+ return NULL;
+
+ uline_set_line(&us->s, ucv_string_get(arg), ucv_string_length(arg));
+ }
+
+ if ((arg = ucv_object_get(state, "pos", NULL)) != NULL) {
+ if (ucv_type(arg) != UC_INTEGER)
+ return NULL;
+
+ uline_set_cursor(&us->s, ucv_int64_get(arg));
+ }
+
+ arg = ucv_object_get(state, "line2_prompt", &found);
+ if (found) {
+ if (!arg)
+ uline_set_line2_prompt(&us->s, NULL);
+ else if (ucv_type(arg) == UC_STRING)
+ uline_set_line2_prompt(&us->s, ucv_string_get(arg));
+ else
+ return NULL;
+ }
+
+ if ((arg = ucv_object_get(state, "line2", NULL)) != NULL) {
+ if (ucv_type(arg) != UC_STRING)
+ return NULL;
+
+ uline_set_line2(&us->s, ucv_string_get(arg), ucv_string_length(arg));
+ }
+
+ if ((arg = ucv_object_get(state, "line2_pos", NULL)) != NULL) {
+ if (ucv_type(arg) != UC_INTEGER)
+ return NULL;
+
+ uline_set_line2_cursor(&us->s, ucv_int64_get(arg));
+ }
+
+ return ucv_boolean_new(true);
+}
+
+static uc_value_t *
+uc_uline_set_hint(uc_vm_t *vm, size_t nargs)
+{
+ struct uc_uline_state *us = uc_fn_thisval("uline.state");
+ uc_value_t *arg = uc_fn_arg(0);
+
+ if (!us || ucv_type(arg) != UC_STRING)
+ return NULL;
+
+ uline_set_hint(&us->s, ucv_string_get(arg), ucv_string_length(arg));
+
+ return ucv_boolean_new(true);
+}
+
+static uc_value_t *
+uc_uline_set_uloop(uc_vm_t *vm, size_t nargs)
+{
+ struct uc_uline_state *us = uc_fn_thisval("uline.state");
+ uc_value_t *cb = uc_fn_arg(0);
+
+ if (!us || (cb && !ucv_is_callable(cb)))
+ return NULL;
+
+ us->poll_cb = cb;
+ ucv_array_set(us->state, STATE_POLL_CB, ucv_get(cb));
+ if (cb) {
+ uloop_fd_add(&us->fd, ULOOP_READ);
+ us->fd.cb(&us->fd, 0);
+ } else {
+ uloop_fd_delete(&us->fd);
+ }
+
+ return ucv_boolean_new(true);
+}
+
+static uc_value_t *
+uc_uline_reset_key_input(uc_vm_t *vm, size_t nargs)
+{
+ struct uc_uline_state *us = uc_fn_thisval("uline.state");
+
+ us->s.repeat_char = 0;
+
+ return ucv_boolean_new(true);
+}
+
+static uc_value_t *
+uc_uline_hide_prompt(uc_vm_t *vm, size_t nargs)
+{
+ struct uc_uline_state *us = uc_fn_thisval("uline.state");
+
+ if (!us)
+ return NULL;
+
+ uline_hide_prompt(&us->s);
+
+ return ucv_boolean_new(true);
+}
+
+static uc_value_t *
+uc_uline_refresh_prompt(uc_vm_t *vm, size_t nargs)
+{
+ struct uc_uline_state *us = uc_fn_thisval("uline.state");
+
+ if (!us)
+ return NULL;
+
+ uline_refresh_prompt(&us->s);
+
+ return ucv_boolean_new(true);
+}
+
+static bool
+cb_prepare(struct uc_uline_state *us, const char *name)
+{
+ uc_value_t *func;
+
+ func = ucv_object_get(us->cb, name, NULL);
+ if (!func)
+ return false;
+
+ uc_vm_stack_push(us->vm, ucv_get(us->res));
+ uc_vm_stack_push(us->vm, ucv_get(func));
+ return true;
+}
+
+static uc_value_t *
+cb_call_ret(struct uc_uline_state *us, size_t args, ...)
+{
+ uc_vm_t *vm = us->vm;
+ va_list ap;
+
+ va_start(ap, args);
+ for (size_t i = 0; i < args; i++)
+ uc_vm_stack_push(vm, ucv_get(va_arg(ap, void *)));
+ va_end(ap);
+
+ if (uc_vm_call(vm, true, args) == EXCEPTION_NONE)
+ return uc_vm_stack_pop(vm);
+
+ return NULL;
+}
+#define cb_call(...) ucv_put(cb_call_ret(__VA_ARGS__))
+
+static bool
+uc_uline_cb_line(struct uline_state *s, const char *str, size_t len)
+{
+ struct uc_uline_state *us = container_of(s, struct uc_uline_state, s);
+ bool complete = true;
+ uc_value_t *ret;
+
+ if (cb_prepare(us, "line_check")) {
+ ret = cb_call_ret(us, 1, ucv_string_new_length(str, len));
+ complete = ucv_is_truish(ret);
+ ucv_put(ret);
+ }
+
+ s->stop = complete;
+ if (complete)
+ us->line = ucv_string_new_length(str, len);
+
+ return complete;
+}
+
+static void
+uc_uline_cb_event(struct uline_state *s, enum uline_event ev)
+{
+ struct uc_uline_state *us = container_of(s, struct uc_uline_state, s);
+ static const char * const ev_types[] = {
+ [EDITLINE_EV_CURSOR_UP] = "cursor_up",
+ [EDITLINE_EV_CURSOR_DOWN] = "cursor_down",
+ [EDITLINE_EV_WINDOW_CHANGED] = "window_changed",
+ [EDITLINE_EV_EOF] = "eof",
+ [EDITLINE_EV_INTERRUPT] = "interrupt",
+ };
+
+ if (ev > ARRAY_SIZE(ev_types) || !ev_types[ev])
+ return;
+
+ if (!cb_prepare(us, ev_types[ev]))
+ return;
+
+ if (ev == EDITLINE_EV_WINDOW_CHANGED)
+ cb_call(us, 2, ucv_int64_new(s->cols), ucv_int64_new(s->rows));
+ else
+ cb_call(us, 0);
+}
+
+static void uc_uline_poll_cb(struct uloop_fd *fd, unsigned int events)
+{
+ struct uc_uline_state *us = container_of(fd, struct uc_uline_state, fd);
+ uc_value_t *res = ucv_get(us->res);
+ uc_value_t *val;
+
+ while (!uloop_cancelled && ucv_resource_data(res, NULL) && us->poll_cb) {
+ uline_poll(&us->s);
+
+ val = us->line;
+ if (!val)
+ break;
+
+ us->line = NULL;
+ if (!ucv_is_callable(us->poll_cb))
+ break;
+
+ uc_vm_stack_push(us->vm, ucv_get(res));
+ uc_vm_stack_push(us->vm, ucv_get(us->poll_cb));
+ cb_call(us, 1, val);
+ }
+ ucv_put(res);
+}
+
+static bool
+uc_uline_cb_key_input(struct uline_state *s, unsigned char c, unsigned int count)
+{
+ struct uc_uline_state *us = container_of(s, struct uc_uline_state, s);
+ uc_value_t *ret;
+ bool retval;
+
+ if (!(us->input_mask[c / 32] & (1 << (c % 32))))
+ return false;
+
+ if (!cb_prepare(us, "key_input"))
+ return false;
+
+ ret = cb_call_ret(us, 2, ucv_string_new_length((char *)&c, 1), ucv_int64_new(count));
+ retval = ucv_is_truish(ret);
+ ucv_put(ret);
+
+ return retval;
+}
+
+static void
+uc_uline_cb_line2_update(struct uline_state *s, const char *str, size_t len)
+{
+ struct uc_uline_state *us = container_of(s, struct uc_uline_state, s);
+
+ if (cb_prepare(us, "line2_update"))
+ cb_call(us, 1, ucv_string_new_length(str, len));
+}
+
+static bool
+uc_uline_cb_line2_cursor(struct uline_state *s)
+{
+ struct uc_uline_state *us = container_of(s, struct uc_uline_state, s);
+ uc_value_t *retval;
+ bool ret = true;
+
+ if (cb_prepare(us, "line2_cursor")) {
+ retval = cb_call_ret(us, 0);
+ ret = ucv_is_truish(retval);
+ ucv_put(retval);
+ }
+
+ return ret;
+}
+
+static bool
+uc_uline_cb_line2_newline(struct uline_state *s, const char *str, size_t len)
+{
+ struct uc_uline_state *us = container_of(s, struct uc_uline_state, s);
+ uc_value_t *retval;
+ bool ret = false;
+
+ if (cb_prepare(us, "line2_newline")) {
+ retval = cb_call_ret(us, 1, ucv_string_new_length(str, len));
+ ret = ucv_is_truish(retval);
+ ucv_put(retval);
+ }
+
+ return ret;
+}
+
+static uc_value_t *
+uc_uline_new(uc_vm_t *vm, size_t nargs)
+{
+ static const struct uline_cb uline_cb = {
+#define _CB(_type) ._type = uc_uline_cb_##_type
+ _CB(key_input),
+ _CB(line),
+ _CB(event),
+ _CB(line2_update),
+ _CB(line2_cursor),
+ _CB(line2_newline),
+#undef _CB
+ };
+ uc_value_t *data = uc_fn_arg(0);
+ struct uc_uline_state *us;
+ FILE *input, *output;
+ uc_value_t *arg, *cb, *state, *res;
+
+ if (ucv_type(data) != UC_OBJECT)
+ return NULL;
+
+ cb = ucv_object_get(data, "cb", NULL);
+ if (ucv_type(cb) != UC_OBJECT)
+ return NULL;
+
+ state = ucv_array_new(vm);
+ ucv_array_set(state, 0, ucv_get(cb));
+ if ((arg = ucv_object_get(data, "input", NULL)) != NULL) {
+ input = ucv_resource_data(arg, "fs.file");
+ ucv_array_set(state, STATE_INPUT, ucv_get(arg));
+ } else {
+ input = stdin;
+ }
+
+ if ((arg = ucv_object_get(data, "output", NULL)) != NULL) {
+ output = ucv_resource_data(arg, "fs.file");
+ ucv_array_set(state, STATE_OUTPUT, ucv_get(arg));
+ } else {
+ output = stdout;
+ }
+
+ if (!input || !output) {
+ input = output = NULL;
+ return NULL;
+ }
+
+ us = calloc(1, sizeof(*us));
+ us->vm = vm;
+ us->state = ucv_array_new(vm);
+ ucv_array_set(us->state, STATE_CB, ucv_get(cb));
+ us->cb = cb;
+ us->registry_index = registry_set(vm, state);
+
+ if ((arg = ucv_object_get(data, "key_input_list", NULL)) != NULL) {
+ uc_value_t *val;
+ size_t len;
+
+ if (ucv_type(arg) != UC_ARRAY)
+ goto free;
+
+ len = ucv_array_length(arg);
+ for (size_t i = 0; i < len; i++) {
+ unsigned char c;
+
+ val = ucv_array_get(arg, i);
+ if (ucv_type(val) != UC_STRING || ucv_string_length(val) != 1)
+ goto free;
+
+ c = ucv_string_get(val)[0];
+ us->input_mask[c / 32] |= 1 << (c % 32);
+ }
+ }
+
+ res = ucv_resource_new(state_type, us);
+ ucv_array_set(us->state, STATE_RES, ucv_get(res));
+ us->res = res;
+ us->fd.fd = fileno(input);
+ us->fd.cb = uc_uline_poll_cb;
+
+ uline_init(&us->s, &uline_cb, us->fd.fd, output, true);
+
+ return res;
+
+free:
+ free(us);
+ return NULL;
+}
+
+static void free_state(void *ptr)
+{
+ struct uc_uline_state *us = ptr;
+ uc_value_t *registry;
+
+ if (!us)
+ return;
+
+ uloop_fd_delete(&us->fd);
+ registry = uc_vm_registry_get(us->vm, "uline.registry");
+ ucv_array_set(registry, us->registry_index, NULL);
+ uline_free(&us->s);
+ free(us);
+}
+
+static uc_value_t *
+uc_uline_close(uc_vm_t *vm, size_t nargs)
+{
+ struct uline_state **s = uc_fn_this("uline.state");
+
+ if (!s || !*s)
+ return NULL;
+
+ free_state(*s);
+ *s = NULL;
+
+ return NULL;
+}
+
+static bool
+skip_space(const char **str, const char *end)
+{
+ while (*str < end && isspace(**str))
+ (*str)++;
+ return *str < end;
+}
+
+static void
+add_str(uc_stringbuf_t **buf, const char *str, const char *next)
+{
+ if (str == next)
+ return;
+
+ if (!*buf)
+ *buf = ucv_stringbuf_new();
+ ucv_stringbuf_addstr(*buf, str, next - str);
+}
+
+static void
+uc_uline_add_pos(uc_vm_t *vm, uc_value_t *list, ssize_t start, ssize_t end)
+{
+ uc_value_t *val = ucv_array_new(vm);
+ ucv_array_push(val, ucv_int64_new(start));
+ ucv_array_push(val, ucv_int64_new(end));
+ ucv_array_push(list, val);
+}
+
+static uc_value_t *
+uc_uline_parse_args(uc_vm_t *vm, size_t nargs, bool check)
+{
+ struct uc_arg_parser *argp = uc_fn_thisval("uline.argp");
+ uc_value_t *list = NULL, *pos_list = NULL;
+ uc_value_t *args = NULL, *pos_args = NULL;
+ uc_value_t *str_arg = uc_fn_arg(0);
+ uc_stringbuf_t *buf = NULL;
+ uc_value_t *missing = NULL;
+ uc_value_t *ret;
+ const char *start, *str, *end;
+ ssize_t start_idx = -1, end_idx = 0;
+ enum {
+ UNQUOTED,
+ BACKSLASH,
+ SINGLE_QUOTE,
+ DOUBLE_QUOTE,
+ DOUBLE_QUOTE_BACKSLASH,
+ } state = UNQUOTED;
+ static const char * const state_str[] = {
+ [BACKSLASH] = "\\",
+ [SINGLE_QUOTE] = "'",
+ [DOUBLE_QUOTE] = "\"",
+ [DOUBLE_QUOTE_BACKSLASH] = "\\\"",
+ };
+#define UNQUOTE_TOKENS " \t\r\n'\"\\"
+ char unquote_tok[] = UNQUOTE_TOKENS "\x00";
+ unquote_tok[strlen(UNQUOTE_TOKENS)] = argp->line_sep;
+
+ if (!argp || ucv_type(str_arg) != UC_STRING)
+ return NULL;
+
+ if (!check) {
+ list = ucv_array_new(vm);
+ pos_list = ucv_array_new(vm);
+ if (argp->line_sep) {
+ args = ucv_array_new(vm);
+ pos_args = ucv_array_new(vm);
+ ucv_array_push(args, list);
+ ucv_array_push(pos_args, pos_list);
+ } else {
+ args = list;
+ pos_args = pos_list;
+ }
+ }
+
+ start = str = ucv_string_get(str_arg);
+ end = str + ucv_string_length(str_arg);
+ skip_space(&str, end);
+
+ while (*str && str < end) {
+ const char *next;
+
+ switch (state) {
+ case UNQUOTED:
+ if (isspace(*str)) {
+ skip_space(&str, end);
+ if (!buf)
+ continue;
+
+ ucv_array_push(list, ucv_stringbuf_finish(buf));
+ uc_uline_add_pos(vm, pos_list, start_idx, end_idx);
+ start_idx = -1;
+ buf = NULL;
+ continue;
+ }
+
+ if (start_idx < 0)
+ start_idx = str - start;
+ next = str + strcspn(str, unquote_tok);
+ if (list)
+ add_str(&buf, str, next);
+ str = next;
+ end_idx = str - start;
+
+ switch (*str) {
+ case 0:
+ continue;
+ case '\'':
+ state = SINGLE_QUOTE;
+ break;
+ case '"':
+ state = DOUBLE_QUOTE;
+ break;
+ case '\\':
+ state = BACKSLASH;
+ break;
+ default:
+ if (argp->line_sep &&
+ *str == argp->line_sep) {
+ str++;
+ if (list) {
+ if (buf) {
+ ucv_array_push(list, ucv_stringbuf_finish(buf));
+ uc_uline_add_pos(vm, pos_list, start_idx, end_idx);
+ start_idx = -1;
+ }
+
+ buf = NULL;
+ list = ucv_array_new(vm);
+ ucv_array_push(args, list);
+
+ pos_list = ucv_array_new(vm);
+ ucv_array_push(pos_args, pos_list);
+ }
+ }
+ continue;
+ }
+ if (!buf)
+ buf = ucv_stringbuf_new();
+ str++;
+ break;
+
+ case BACKSLASH:
+ case DOUBLE_QUOTE_BACKSLASH:
+ if (start_idx < 0)
+ start_idx = str - start;
+ if (list && *str != '\n')
+ add_str(&buf, str, str + 1);
+ str++;
+ state--;
+ end_idx = str - start;
+ break;
+
+ case SINGLE_QUOTE:
+ if (start_idx < 0)
+ start_idx = str - start;
+ next = str + strcspn(str, "'");
+ if (list)
+ add_str(&buf, str, next);
+ str = next;
+
+ if (*str == '\'') {
+ state = UNQUOTED;
+ str++;
+ }
+ end_idx = str - start;
+ break;
+
+ case DOUBLE_QUOTE:
+ if (start_idx < 0)
+ start_idx = str - start;
+ next = str + strcspn(str, "\"\\");
+ if (list)
+ add_str(&buf, str, next);
+ str = next;
+
+ if (*str == '"') {
+ state = UNQUOTED;
+ str++;
+ } else if (*str == '\\') {
+ state = DOUBLE_QUOTE_BACKSLASH;
+ str++;
+ }
+ end_idx = str - start;
+ }
+ }
+
+ if (buf) {
+ ucv_array_push(list, ucv_stringbuf_finish(buf));
+ uc_uline_add_pos(vm, pos_list, start_idx, end_idx);
+ }
+
+ if (state_str[state])
+ missing = ucv_string_new(state_str[state]);
+
+ if (!list)
+ return missing;
+
+ ret = ucv_object_new(vm);
+ ucv_object_add(ret, "args", args);
+ ucv_object_add(ret, "pos", pos_args);
+ if (missing)
+ ucv_object_add(ret, "missing", missing);
+
+ return ret;
+}
+
+static uc_value_t *
+uc_uline_arg_parser(uc_vm_t *vm, size_t nargs)
+{
+ uc_value_t *opts = uc_fn_arg(0);
+ struct uc_arg_parser *argp;
+ uc_value_t *a;
+ char sep = 0;
+
+ if ((a = ucv_object_get(opts, "line_separator", NULL)) != NULL) {
+ if (ucv_type(a) != UC_STRING || ucv_string_length(a) != 1)
+ return NULL;
+
+ sep = ucv_string_get(a)[0];
+ }
+
+ argp = calloc(1, sizeof(*argp));
+ argp->line_sep = sep;
+
+ return ucv_resource_new(argp_type, argp);
+}
+
+static uc_value_t *
+uc_uline_argp_parse(uc_vm_t *vm, size_t nargs)
+{
+ return uc_uline_parse_args(vm, nargs, false);
+}
+
+static uc_value_t *
+uc_uline_argp_check(uc_vm_t *vm, size_t nargs)
+{
+ return uc_uline_parse_args(vm, nargs, true);
+}
+
+static uc_value_t *
+uc_uline_argp_escape(uc_vm_t *vm, size_t nargs)
+{
+ uc_value_t *arg = uc_fn_arg(0);
+ uc_value_t *ref_arg = uc_fn_arg(1);
+ const char *str, *next;
+ uc_stringbuf_t *buf;
+ char ref = 0;
+
+ if (ucv_type(arg) != UC_STRING)
+ return NULL;
+
+ if (ucv_type(ref_arg) == UC_STRING)
+ ref = ucv_string_get(ref_arg)[0];
+
+ str = ucv_string_get(arg);
+ if (ref != '"' && ref != '\'') {
+ next = str + strcspn(str, "\n\t '\"");
+ if (*next)
+ ref = '"';
+ }
+ if (ref != '"' && ref != '\'')
+ return ucv_string_new(str);
+
+ buf = ucv_stringbuf_new();
+ ucv_stringbuf_addstr(buf, &ref, 1);
+
+ while (*str) {
+ next = strchr(str, ref);
+ if (!next) {
+ ucv_stringbuf_addstr(buf, str, strlen(str));
+ break;
+ }
+
+ if (next - str)
+ ucv_stringbuf_addstr(buf, str, next - str);
+ if (ref == '\'')
+ ucv_stringbuf_addstr(buf, "'\\''", 4);
+ else
+ ucv_stringbuf_addstr(buf, "\\\"", 2);
+ str = next + 1;
+ }
+
+ ucv_stringbuf_addstr(buf, &ref, 1);
+
+ return ucv_stringbuf_finish(buf);
+}
+
+static uc_value_t *
+uc_uline_getpass(uc_vm_t *vm, size_t nargs)
+{
+ uc_value_t *prompt = uc_fn_arg(0);
+ char *pw;
+
+ if (ucv_type(prompt) != UC_STRING)
+ return NULL;
+
+ pw = getpass(ucv_string_get(prompt));
+ if (!pw)
+ return NULL;
+
+ return ucv_string_new(pw);
+}
+
+static const uc_function_list_t argp_fns[] = {
+ { "parse", uc_uline_argp_parse },
+ { "check", uc_uline_argp_check },
+ { "escape", uc_uline_argp_escape },
+};
+
+static const uc_function_list_t state_fns[] = {
+ { "close", uc_uline_close },
+ { "poll", uc_uline_poll },
+ { "poll_stop", uc_uline_poll_stop },
+ { "poll_key", uc_uline_poll_key },
+ { "reset_key_input", uc_uline_reset_key_input },
+ { "get_line", uc_uline_get_line },
+ { "get_window", uc_uline_get_window },
+ { "set_hint", uc_uline_set_hint },
+ { "set_state", uc_uline_set_state },
+ { "set_uloop", uc_uline_set_uloop },
+ { "hide_prompt", uc_uline_hide_prompt },
+ { "refresh_prompt", uc_uline_refresh_prompt },
+};
+
+static const uc_function_list_t global_fns[] = {
+ { "new", uc_uline_new },
+ { "arg_parser", uc_uline_arg_parser },
+ { "getpass", uc_uline_getpass },
+};
+
+void uc_module_init(uc_vm_t *vm, uc_value_t *scope)
+{
+ uc_function_list_register(scope, global_fns);
+
+ state_type = uc_type_declare(vm, "uline.state", state_fns, free_state);
+ argp_type = uc_type_declare(vm, "uline.argp", argp_fns, free);
+ registry = ucv_array_new(vm);
+ uc_vm_registry_set(vm, "uline.registry", registry);
+}
diff --git a/package/utils/ucode-mod-uline/src/uline.c b/package/utils/ucode-mod-uline/src/uline.c
new file mode 100644
index 0000000000..3d0ba8a587
--- /dev/null
+++ b/package/utils/ucode-mod-uline/src/uline.c
@@ -0,0 +1,945 @@
+// SPDX-License-Identifier: ISC
+/*
+ * Copyright (C) 2025 Felix Fietkau <nbd@nbd.name>
+ */
+#include <sys/types.h>
+#include <sys/ioctl.h>
+
+#include <stdint.h>
+#include <stdio.h>
+#include <errno.h>
+#include <signal.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <locale.h>
+
+#include <libubox/list.h>
+
+#include "uline.h"
+#include "private.h"
+
+#define LINEBUF_CHUNK 64
+
+static int sigwinch_count;
+
+static size_t
+nsyms(struct uline_state *s, const char *buf, size_t len)
+{
+ if (!s->utf8)
+ return len;
+ return utf8_nsyms(buf, len);
+}
+
+static inline bool
+is_utf8_cont(unsigned char c)
+{
+ return (c & 0xc0) == 0x80;
+}
+
+static size_t
+utf8_move_left(const char *line, size_t pos)
+{
+ if (!pos)
+ return 0;
+ do {
+ pos--;
+ } while (pos > 0 && is_utf8_cont(line[pos]));
+
+ return pos;
+}
+
+static size_t
+utf8_move_right(const char *line, size_t pos, size_t len)
+{
+ if (pos == len)
+ return pos;
+
+ do {
+ pos++;
+ } while (pos < len && is_utf8_cont(line[pos]));
+
+ return pos;
+}
+
+static char *
+linebuf_extend(struct linebuf *l, size_t size)
+{
+ size_t tailroom = l->bufsize - l->len;
+ char *buf;
+
+ if (l->buf && tailroom > size)
+ goto out;
+
+ size -= tailroom;
+ size += LINEBUF_CHUNK - 1;
+ size -= size % LINEBUF_CHUNK;
+
+ buf = realloc(l->buf, l->bufsize + size);
+ if (!buf)
+ return NULL;
+
+ l->buf = buf;
+ l->bufsize += size;
+
+out:
+ return l->buf + l->len;
+}
+
+static void
+linebuf_free(struct linebuf *line)
+{
+ free(line->buf);
+ free(line->prompt);
+}
+
+static void
+update_window_size(struct uline_state *s, bool init)
+{
+ unsigned int cols = 80, rows = 25;
+#ifdef TIOCGWINSZ
+ struct winsize ws = {};
+
+ if (s->ioctl_winsize &&
+ !ioctl(fileno(s->output), TIOCGWINSZ, &ws) &&
+ ws.ws_col && ws.ws_row) {
+ cols = ws.ws_col;
+ rows = ws.ws_row;
+ } else
+#endif
+ {
+ s->ioctl_winsize = false;
+ }
+
+ s->sigwinch_count = sigwinch_count;
+ if (s->cols == cols && s->rows == rows)
+ return;
+
+ s->cols = cols;
+ s->rows = rows;
+ s->full_update = true;
+ s->cb->event(s, EDITLINE_EV_WINDOW_CHANGED);
+}
+
+static void
+handle_sigwinch(int signal)
+{
+ sigwinch_count++;
+}
+
+static void
+reset_input_state(struct uline_state *s)
+{
+ s->utf8_cont = 0;
+ s->esc_idx = -1;
+}
+
+static void
+termios_set_native_mode(struct uline_state *s)
+{
+ struct termios t = s->orig_termios;
+
+ if (!s->has_termios)
+ return;
+
+ t.c_iflag = 0;
+ t.c_oflag = OPOST | ONLCR;
+ t.c_lflag = 0;
+ t.c_cc[VMIN] = 1;
+ t.c_cc[VTIME] = 0;
+
+ tcsetattr(s->input, TCSADRAIN, &t);
+}
+
+static void
+termios_set_orig_mode(struct uline_state *s)
+{
+ if (!s->has_termios)
+ return;
+
+ tcsetattr(s->input, TCSADRAIN, &s->orig_termios);
+}
+
+static bool
+check_utf8(struct uline_state *s, unsigned char c)
+{
+ if (!s->utf8)
+ return false;
+ if (s->utf8_cont)
+ return true;
+ return (c & 0xc0) == 0xc0;
+}
+
+static bool
+handle_utf8(struct uline_state *s, unsigned char c)
+{
+ if (!s->utf8)
+ return false;
+
+ if (!s->utf8_cont) {
+ if ((c & 0xc0) != 0xc0)
+ return false;
+
+ c &= 0xf0;
+ c <<= 1;
+ while (c & 0x80) {
+ c <<= 1;
+ s->utf8_cont++;
+ }
+
+ return true;
+ }
+
+ if ((c & 0xc0) != 0x80) {
+ // invalid utf-8
+ s->utf8_cont = 0;
+ return false;
+ }
+
+ s->utf8_cont--;
+
+ return s->utf8_cont;
+}
+
+static bool
+linebuf_insert(struct linebuf *line, char *c, size_t len)
+{
+ char *dest;
+ ssize_t tail;
+
+ if (!linebuf_extend(line, len + 1))
+ return false;
+
+ dest = &line->buf[line->pos];
+ tail = line->len - line->pos;
+ if (tail > 0)
+ memmove(dest + len, dest, tail);
+ else
+ dest[len] = 0;
+
+ if (line->update_pos > line->pos)
+ line->update_pos = line->pos;
+
+ memcpy(dest, c, len);
+ line->len += len;
+ line->pos += len;
+ line->buf[line->len] = 0;
+
+ return true;
+}
+
+static void
+linebuf_delete(struct linebuf *line, size_t len)
+{
+ char *dest = &line->buf[line->pos];
+ ssize_t tail = line->len - line->pos;
+ size_t max_len = line->len - line->pos;
+
+ if (line->update_pos > line->pos)
+ line->update_pos = line->pos;
+
+ if (len > max_len)
+ len = max_len;
+
+ memmove(dest, dest + len, tail + 1);
+ line->len -= len;
+}
+
+static struct pos
+pos_convert(struct uline_state *s, ssize_t offset)
+{
+ struct pos pos;
+ pos.y = offset / s->cols;
+ pos.x = offset - (pos.y * s->cols);
+ return pos;
+}
+
+static void
+pos_add(struct uline_state *s, struct pos *pos, struct pos add)
+{
+ pos->x += add.x;
+ pos->y += add.y;
+ if (pos->x >= (int16_t)s->cols) {
+ pos->x -= s->cols;
+ pos->y++;
+ }
+ if (pos->x < 0) {
+ pos->x += s->cols;
+ pos->y--;
+ }
+ if (pos->y < 0)
+ pos->y = 0;
+}
+
+static void
+pos_add_ofs(struct uline_state *s, struct pos *pos, size_t offset)
+{
+ pos_add(s, pos, pos_convert(s, offset));
+}
+
+static void
+pos_add_newline(struct uline_state *s, struct pos *pos)
+{
+ pos->x = 0;
+ pos->y++;
+}
+
+static void
+__pos_add_string(struct uline_state *s, struct pos *pos, const char *str, size_t len)
+{
+ const char *next;
+
+ while ((next = memchr(str, KEY_ESC, len)) != NULL) {
+ size_t cur_len = next - str;
+
+ pos_add_ofs(s, pos, nsyms(s, str, cur_len));
+ next++;
+
+ if (*next == '[' || *next == 'O') {
+ next++;
+ while (*next <= 63)
+ next++;
+ }
+ next++;
+ len -= next - str;
+ str = next;
+ }
+
+ pos_add_ofs(s, pos, nsyms(s, str, len));
+}
+
+static void
+pos_add_string(struct uline_state *s, struct pos *pos, const char *str, size_t len)
+{
+ const char *next;
+
+ if (!len)
+ return;
+
+ while ((next = memchr(str, '\n', len)) != NULL) {
+ size_t cur_len = next - str;
+ if (cur_len)
+ __pos_add_string(s, pos, str, cur_len);
+ pos_add_newline(s, pos);
+ len -= cur_len + 1;
+ str = next + 1;
+ }
+
+ if (len)
+ __pos_add_string(s, pos, str, len);
+}
+
+static struct pos
+pos_diff(struct pos start, struct pos end)
+{
+ struct pos diff = {
+ .x = end.x - start.x,
+ .y = end.y - start.y
+ };
+
+ return diff;
+}
+
+static void
+set_cursor(struct uline_state *s, struct pos pos)
+{
+ struct pos diff = pos_diff(s->cursor_pos, pos);
+
+ if (diff.x > 0)
+ vt100_cursor_forward(s->output, diff.x);
+ else if (diff.x < 0)
+ vt100_cursor_back(s->output, -diff.x);
+
+ if (diff.y > 0)
+ vt100_cursor_down(s->output, diff.y);
+ else if (diff.y < 0)
+ vt100_cursor_up(s->output, -diff.y);
+
+ s->cursor_pos = pos;
+}
+
+static void
+display_output_string(struct uline_state *s, const char *str,
+ size_t len)
+{
+ fwrite(str, len, 1, s->output);
+ pos_add_string(s, &s->cursor_pos, str, len);
+}
+
+static void
+display_update_line(struct uline_state *s, struct linebuf *line,
+ struct pos *pos)
+{
+ char *start = line->buf;
+ char *end = line->buf + line->len;
+ struct pos update_pos;
+ size_t prompt_len = 0;
+
+ if (line->prompt)
+ prompt_len = strlen(line->prompt);
+
+ if (s->full_update) {
+ display_output_string(s, line->prompt, prompt_len);
+ *pos = s->cursor_pos;
+ line->update_pos = 0;
+ } else {
+ pos_add_string(s, pos, line->prompt, prompt_len);
+ }
+
+ update_pos = *pos;
+ if (line->update_pos) {
+ start += line->update_pos;
+ pos_add_string(s, &update_pos, line->buf, line->update_pos);
+ }
+ set_cursor(s, update_pos);
+ vt100_erase_right(s->output);
+ line->update_pos = line->len;
+
+ if (end - start <= 0)
+ return;
+
+ display_output_string(s, start, end - start);
+ if (s->cursor_pos.x == 0 && end[-1] != '\n')
+ vt100_next_line(s->output);
+}
+
+static void
+display_update(struct uline_state *s)
+{
+ struct pos edit_pos, end_diff;
+ struct pos base_pos = {};
+ struct linebuf *line = &s->line;
+
+ if (s->full_update) {
+ set_cursor(s, (struct pos){});
+ fputc(KEY_CR, s->output);
+ vt100_erase_down(s->output);
+ }
+
+ display_update_line(s, line, &base_pos);
+
+ if (s->line2) {
+ line = s->line2;
+
+ if (s->cursor_pos.x != 0) {
+ vt100_next_line(s->output);
+ pos_add_newline(s, &s->cursor_pos);
+ }
+
+ base_pos = s->cursor_pos;
+ display_update_line(s, s->line2, &base_pos);
+ }
+
+ edit_pos = base_pos;
+ pos_add_string(s, &edit_pos, line->buf, line->pos);
+
+ end_diff = pos_diff(s->end_pos, s->cursor_pos);
+ s->end_pos = s->cursor_pos;
+
+ if (end_diff.y != 0)
+ vt100_erase_down(s->output);
+ else
+ vt100_erase_right(s->output);
+
+ set_cursor(s, edit_pos);
+ fflush(s->output);
+
+ s->full_update = false;
+}
+
+static bool
+delete_symbol(struct uline_state *s, struct linebuf *line)
+{
+ size_t len = 1;
+
+ if (line->pos == line->len)
+ return false;
+
+ if (s->utf8) {
+ len = utf8_move_right(line->buf, line->pos, line->len);
+ len -= line->pos;
+ }
+
+ linebuf_delete(line, len);
+ return true;
+}
+
+static bool
+move_left(struct uline_state *s, struct linebuf *line)
+{
+ if (!line->pos)
+ return false;
+ if (s->utf8)
+ line->pos = utf8_move_left(line->buf, line->pos);
+ else
+ line->pos--;
+ return true;
+}
+
+static bool
+move_word_left(struct uline_state *s, struct linebuf *line)
+{
+ char *buf = line->buf;
+ size_t pos;
+
+ if (!move_left(s, line))
+ return false;
+
+ pos = line->pos;
+ // remove trailing spaces
+ while (pos > 0 && isspace(buf[pos]))
+ pos--;
+
+ // skip word
+ while (pos > 0 && !isspace(buf[pos]))
+ pos--;
+ if (isspace(buf[pos]))
+ pos++;
+
+ line->pos = pos;
+
+ return true;
+}
+
+static bool
+move_right(struct uline_state *s, struct linebuf *line)
+{
+ if (line->pos >= line->len)
+ return false;
+ if (s->utf8)
+ line->pos = utf8_move_right(line->buf, line->pos, line->len);
+ else
+ line->pos++;
+ return true;
+}
+
+static bool
+move_word_right(struct uline_state *s, struct linebuf *line)
+{
+ char *buf = line->buf;
+ size_t pos = line->pos;
+
+ if (pos == line->len)
+ return false;
+
+ // skip word
+ while (!isspace(buf[pos]) && pos < line->len)
+ pos++;
+
+ // skip trailing whitespace
+ while (isspace(buf[pos]) && pos < line->len)
+ pos++;
+
+ line->pos = pos;
+
+ return true;
+}
+
+static bool
+process_esc(struct uline_state *s, enum vt100_escape esc, uint32_t data)
+{
+ struct linebuf *line = &s->line;
+
+ if (s->line2 &&
+ (esc == VT100_DELETE ||
+ (s->cb->line2_cursor && s->cb->line2_cursor(s))))
+ line = s->line2;
+
+ switch (esc) {
+ case VT100_CURSOR_LEFT:
+ return move_left(s, line);
+ case VT100_CURSOR_WORD_LEFT:
+ return move_word_left(s, line);
+ case VT100_CURSOR_RIGHT:
+ return move_right(s, line);
+ case VT100_CURSOR_WORD_RIGHT:
+ return move_word_right(s, line);
+ case VT100_CURSOR_POS:
+ if (s->rows == (data & 0xffff) &&
+ s->cols == data >> 16)
+ return false;
+ s->rows = data & 0xffff;
+ s->cols = data >> 16;
+ s->full_update = true;
+ s->cb->event(s, EDITLINE_EV_WINDOW_CHANGED);
+ return true;
+ case VT100_HOME:
+ line->pos = 0;
+ return true;
+ case VT100_END:
+ line->pos = line->len;
+ return true;
+ case VT100_CURSOR_UP:
+ s->cb->event(s, EDITLINE_EV_CURSOR_UP);
+ return true;
+ case VT100_CURSOR_DOWN:
+ s->cb->event(s, EDITLINE_EV_CURSOR_DOWN);
+ return true;
+ case VT100_DELETE:
+ return delete_symbol(s, line);
+ default:
+ vt100_ding(s->output);
+ return false;
+ }
+}
+
+static bool
+process_backword(struct uline_state *s, struct linebuf *line)
+{
+ size_t pos, len;
+
+ pos = line->pos - 1;
+ if (!move_word_left(s, line))
+ return false;
+
+ len = pos + 1 - line->pos;
+ linebuf_delete(line, len);
+
+ return true;
+}
+
+static void
+linebuf_reset(struct linebuf *line)
+{
+ line->pos = 0;
+ line->len = 0;
+ line->buf[0] = 0;
+ line->update_pos = 0;
+}
+
+static void
+free_line2(struct uline_state *s)
+{
+ if (!s->line2)
+ return;
+
+ linebuf_free(s->line2);
+ free(s->line2);
+ s->line2 = NULL;
+}
+
+static bool
+process_newline(struct uline_state *s, bool drop)
+{
+ bool ret;
+
+ if (drop)
+ goto reset;
+
+ termios_set_orig_mode(s);
+ if (s->line2 && s->cb->line2_newline &&
+ s->cb->line2_newline(s, s->line2->buf, s->line2->len)) {
+ termios_set_native_mode(s);
+ return true;
+ }
+
+ free_line2(s);
+ ret = s->cb->line(s, s->line.buf, s->line.len);
+ termios_set_native_mode(s);
+ if (!ret) {
+ linebuf_insert(&s->line, "\n", 1);
+ return true;
+ }
+
+reset:
+ vt100_next_line(s->output);
+ vt100_erase_down(s->output);
+ s->cursor_pos = (struct pos) {};
+ s->full_update = true;
+ fflush(s->output);
+ if (!s->line.len)
+ return true;
+
+ linebuf_reset(&s->line);
+
+ return true;
+}
+
+static bool
+process_ctrl(struct uline_state *s, char c)
+{
+ struct linebuf *line = s->line2 ? s->line2 : &s->line;
+
+ switch (c) {
+ case KEY_LF:
+ case KEY_CR:
+ return process_newline(s, false);
+ case KEY_ETX:
+ s->cb->event(s, EDITLINE_EV_INTERRUPT);
+ process_newline(s, true);
+ s->stop = true;
+ return true;
+ case KEY_EOT:
+ if (s->line.len)
+ return false;
+ s->cb->event(s, EDITLINE_EV_EOF);
+ s->stop = true;
+ return true;
+ case KEY_BS:
+ case KEY_DEL:
+ if (!move_left(s, line))
+ return false;
+
+ delete_symbol(s, line);
+ if (s->line2 && s->cb->line2_update)
+ s->cb->line2_update(s, line->buf, line->len);
+ return true;
+ case KEY_FF:
+ vt100_cursor_home(s->output);
+ vt100_erase_down(s->output);
+ s->full_update = true;
+ return true;
+ case KEY_NAK:
+ linebuf_reset(line);
+ return true;
+ case KEY_SOH:
+ return process_esc(s, VT100_HOME, 0);
+ case KEY_ENQ:
+ return process_esc(s, VT100_END, 0);
+ case KEY_VT:
+ // TODO: kill
+ return false;
+ case KEY_EM:
+ // TODO: yank
+ return false;
+ case KEY_ETB:
+ return process_backword(s, line);
+ case KEY_ESC:
+ s->esc_idx = 0;
+ return false;
+ case KEY_SUB:
+ kill(getpid(), SIGTSTP);
+ return false;
+ default:
+ return false;
+ }
+}
+
+static void
+check_key_repeat(struct uline_state *s, char c)
+{
+ if (s->repeat_char != c)
+ s->repeat_count = 0;
+
+ s->repeat_char = c;
+ s->repeat_count++;
+}
+
+static void
+process_char(struct uline_state *s, char c)
+{
+ enum vt100_escape esc;
+ uint32_t data = 0;
+
+ check_key_repeat(s, c);
+ if (s->esc_idx >= 0) {
+ s->esc_seq[s->esc_idx++] = c;
+ s->esc_seq[s->esc_idx] = 0;
+ esc = vt100_esc_decode(s->esc_seq, &data);
+ if (esc == VT100_INCOMPLETE &&
+ s->esc_idx < (int)sizeof(s->esc_seq) - 1)
+ return;
+
+ s->esc_idx = -1;
+ if (!process_esc(s, esc, data))
+ return;
+ } else if (s->cb->key_input &&
+ !check_utf8(s, (unsigned char )c) &&
+ s->cb->key_input(s, c, s->repeat_count)) {
+ goto out;
+ } else if ((unsigned char)c < 32 || c == 127) {
+ if (!process_ctrl(s, c))
+ return;
+ } else {
+ struct linebuf *line = s->line2 ? s->line2 : &s->line;
+
+ if (!linebuf_insert(line, &c, 1) ||
+ handle_utf8(s, (unsigned char )c))
+ return;
+
+ if (s->line2 && s->cb->line2_update)
+ s->cb->line2_update(s, line->buf, line->len);
+ }
+
+out:
+ if (s->stop)
+ return;
+
+ display_update(s);
+}
+
+void uline_poll(struct uline_state *s)
+{
+ int ret;
+ char c;
+
+ uline_refresh_prompt(s);
+ s->stop = false;
+ while (!s->stop) {
+ ret = read(s->input, &c, 1);
+ if (ret < 0) {
+ if (errno == EINTR)
+ continue;
+ if (errno == EAGAIN)
+ return;
+ ret = 0;
+ }
+
+ if (!ret) {
+ s->cb->event(s, EDITLINE_EV_EOF);
+ termios_set_orig_mode(s);
+ return;
+ }
+
+ if (s->sigwinch_count != sigwinch_count)
+ update_window_size(s, false);
+
+ process_char(s, c);
+ }
+}
+
+void uline_set_prompt(struct uline_state *s, const char *str)
+{
+ if (s->line.prompt && !strcmp(s->line.prompt, str))
+ return;
+
+ free(s->line.prompt);
+ s->line.prompt = strdup(str);
+ s->full_update = true;
+}
+
+void uline_set_line2_prompt(struct uline_state *s, const char *str)
+{
+ if (!!str != !!s->line2) {
+ if (!str)
+ free_line2(s);
+ else
+ s->line2 = calloc(1, sizeof(*s->line2));
+ }
+
+ if (!str || (s->line2->prompt && !strcmp(s->line2->prompt, str)))
+ return;
+
+ free(s->line2->prompt);
+ s->line2->prompt = strdup(str);
+ s->full_update = true;
+}
+
+static void
+__uline_set_line(struct uline_state *s, struct linebuf *line, const char *str, size_t len)
+{
+ size_t i, prev_len = line->len;
+
+ line->len = 0;
+ linebuf_extend(line, len);
+ for (i = 0; i < prev_len && i < len; i++) {
+ if (line->buf[i] != str[i])
+ break;
+ }
+ if (i > prev_len)
+ i--;
+ if (s->utf8) {
+ // move back to the beginning of the utf-8 symbol
+ while (i > 0 && (str[i] & 0xc0) == 0x80)
+ i--;
+ }
+ line->update_pos = i;
+
+ memcpy(line->buf, str, len);
+ line->len = len;
+ if (line->pos > line->len)
+ line->pos = line->len;
+}
+
+void uline_set_line(struct uline_state *s, const char *str, size_t len)
+{
+ __uline_set_line(s, &s->line, str, len);
+}
+
+void uline_set_line2(struct uline_state *s, const char *str, size_t len)
+{
+ if (!s->line2)
+ return;
+ __uline_set_line(s, s->line2, str, len);
+}
+
+void uline_hide_prompt(struct uline_state *s)
+{
+ set_cursor(s, (struct pos){});
+ vt100_erase_down(s->output);
+ s->full_update = true;
+ fflush(s->output);
+}
+
+void uline_refresh_prompt(struct uline_state *s)
+{
+ termios_set_native_mode(s);
+ display_update(s);
+}
+
+void uline_set_hint(struct uline_state *s, const char *str, size_t len)
+{
+ struct pos prev_pos = s->cursor_pos;
+
+ if (len) {
+ vt100_next_line(s->output);
+ pos_add_newline(s, &s->cursor_pos);
+ }
+ vt100_erase_down(s->output);
+
+ if (len) {
+ fwrite(str, len, 1, s->output);
+ pos_add_string(s, &s->cursor_pos, str, len);
+ }
+
+ if (s->cursor_pos.y >= (int16_t)s->rows) {
+ if (s->cursor_pos.x > 0)
+ vt100_next_line(s->output);
+ s->cursor_pos = (struct pos){};
+ s->full_update = true;
+ } else {
+ set_cursor(s, prev_pos);
+ }
+ fflush(s->output);
+}
+
+void uline_init(struct uline_state *s, const struct uline_cb *cb,
+ int in_fd, FILE *out_stream, bool utf8)
+{
+ struct sigaction sa = {
+ .sa_handler = handle_sigwinch,
+ };
+ s->cb = cb;
+ s->utf8 = utf8;
+ s->input = in_fd;
+ s->output = out_stream;
+ s->ioctl_winsize = true;
+ reset_input_state(s);
+
+#ifdef USE_SYSTEM_WCHAR
+ if (utf8)
+ setlocale(LC_CTYPE, "C.UTF-8");
+#endif
+
+ sigaction(SIGWINCH, &sa, NULL);
+ s->full_update = true;
+
+ if (!tcgetattr(s->input, &s->orig_termios)) {
+ s->has_termios = true;
+ termios_set_native_mode(s);
+ }
+
+ update_window_size(s, true);
+ if (!s->ioctl_winsize) {
+ vt100_request_window_size(s->output);
+ fflush(s->output);
+ }
+}
+
+void uline_free(struct uline_state *s)
+{
+ free_line2(s);
+ termios_set_orig_mode(s);
+ linebuf_free(&s->line);
+}
diff --git a/package/utils/ucode-mod-uline/src/uline.h b/package/utils/ucode-mod-uline/src/uline.h
new file mode 100644
index 0000000000..514675e799
--- /dev/null
+++ b/package/utils/ucode-mod-uline/src/uline.h
@@ -0,0 +1,152 @@
+// SPDX-License-Identifier: ISC
+/*
+ * Copyright (C) 2025 Felix Fietkau <nbd@nbd.name>
+ */
+#ifndef __EDITLINE_H
+#define __EDITLINE_H
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <termios.h>
+#include <stdio.h>
+
+#include <libubox/utils.h>
+
+struct uline_state;
+
+struct linebuf {
+ char *buf;
+ size_t len;
+ size_t bufsize;
+
+ char *prompt;
+ size_t pos;
+ size_t update_pos;
+};
+
+struct pos {
+ int16_t x;
+ int16_t y;
+};
+
+enum uline_event {
+ EDITLINE_EV_CURSOR_UP,
+ EDITLINE_EV_CURSOR_DOWN,
+
+ EDITLINE_EV_WINDOW_CHANGED,
+ EDITLINE_EV_LINE_INPUT,
+
+ EDITLINE_EV_INTERRUPT,
+ EDITLINE_EV_EOF,
+};
+
+struct uline_cb {
+ // called on every key input. return true if handled by callback
+ bool (*key_input)(struct uline_state *s, unsigned char c, unsigned int count);
+
+ void (*event)(struct uline_state *s, enum uline_event ev);
+
+ // line: called on newline, returns true to accept the line, false to keep
+ // editing a multi-line string
+ bool (*line)(struct uline_state *s, const char *str, size_t len);
+
+ // called on any changes to the buffer of the secondary line editor
+ void (*line2_update)(struct uline_state *s, const char *str, size_t len);
+
+ // called on cursor button press during line2 editing
+ // return true to handle in line2, false to handle in primary line
+ bool (*line2_cursor)(struct uline_state *s);
+
+ // called on newline on the secondary line editor
+ // return true to ignore, false to process as primary line newline event
+ bool (*line2_newline)(struct uline_state *s, const char *str, size_t len);
+};
+
+struct uline_state {
+ const struct uline_cb *cb;
+
+ int input;
+ FILE *output;
+
+ int sigwinch_count;
+
+ struct termios orig_termios;
+ bool has_termios;
+
+ struct linebuf line;
+ struct linebuf *line2;
+
+ unsigned int repeat_count;
+ char repeat_char;
+
+ unsigned int rows, cols;
+ struct pos cursor_pos;
+ struct pos end_pos;
+ bool ioctl_winsize;
+ bool full_update;
+ bool stop;
+
+ bool utf8;
+
+ char esc_seq[32];
+ int8_t esc_idx;
+ uint8_t utf8_cont;
+};
+
+void uline_init(struct uline_state *s, const struct uline_cb *cb,
+ int in_fd, FILE *out_stream, bool utf8);
+void uline_poll(struct uline_state *s);
+
+void uline_set_line(struct uline_state *s, const char *str, size_t len);
+void uline_set_prompt(struct uline_state *s, const char *str);
+static inline void
+uline_set_cursor(struct uline_state *s, size_t pos)
+{
+ s->line.pos = pos;
+ if (s->line.pos > s->line.len)
+ s->line.pos = s->line.len;
+}
+static inline void
+uline_get_line(struct uline_state *s, const char **str, size_t *len)
+{
+ if (s->line.buf) {
+ *str = s->line.buf;
+ *len = s->line.len;
+ } else{
+ *str = "";
+ *len = 0;
+ }
+}
+
+
+
+void uline_set_line2(struct uline_state *s, const char *str, size_t len);
+void uline_set_line2_prompt(struct uline_state *s, const char *str);
+static inline void
+uline_set_line2_cursor(struct uline_state *s, size_t pos)
+{
+ if (!s->line2)
+ return;
+
+ s->line2->pos = pos;
+ if (s->line2->pos > s->line2->len)
+ s->line2->pos = s->line2->len;
+}
+static inline void
+uline_get_line2(struct uline_state *s, const char **str, size_t *len)
+{
+ if (s->line2 && s->line2->buf) {
+ *str = s->line2->buf;
+ *len = s->line2->len;
+ } else{
+ *str = "";
+ *len = 0;
+ }
+}
+
+void uline_set_hint(struct uline_state *s, const char *str, size_t len);
+void uline_hide_prompt(struct uline_state *s);
+void uline_refresh_prompt(struct uline_state *s);
+void uline_free(struct uline_state *s);
+
+#endif
diff --git a/package/utils/ucode-mod-uline/src/utf8.c b/package/utils/ucode-mod-uline/src/utf8.c
new file mode 100644
index 0000000000..c2e696820b
--- /dev/null
+++ b/package/utils/ucode-mod-uline/src/utf8.c
@@ -0,0 +1,340 @@
+// SPDX-License-Identifier: ISC
+/*
+ * Copyright (C) 2025 Felix Fietkau <nbd@nbd.name>
+ */
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+#include <unistd.h>
+#include <stdint.h>
+#include <wchar.h>
+
+#include "private.h"
+
+#ifndef USE_SYSTEM_WCHAR
+/*
+ * adapted from musl code:
+ *
+ * Copyright © 2005-2020 Rich Felker, et al.
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be
+ * included in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#undef MB_CUR_MAX
+#define MB_CUR_MAX 4
+
+static const unsigned char table[] = {
+16,16,16,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,16,33,16,16,16,34,35,36,
+37,38,39,40,16,16,41,16,16,16,16,16,16,16,16,16,16,16,42,43,16,16,44,16,16,16,
+16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,
+16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,
+16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,
+16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,
+16,16,16,16,16,16,16,16,16,16,45,16,46,47,48,49,16,16,16,16,16,16,16,16,16,16,
+16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,
+16,16,16,16,16,16,16,50,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,
+16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,51,16,16,52,
+53,16,54,55,56,16,16,16,16,16,16,57,16,16,58,16,59,60,61,62,63,64,65,66,67,68,
+69,70,16,71,72,73,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,
+16,74,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,
+16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,
+16,16,16,75,76,16,16,16,77,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,
+16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,
+16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,
+16,16,16,16,16,16,16,78,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,
+16,16,79,80,16,16,16,16,16,16,16,81,16,16,16,16,16,82,83,84,16,16,16,16,16,85,
+86,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,
+16,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,255,255,
+255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
+255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
+255,255,255,255,255,255,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,248,3,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,254,255,255,255,255,191,182,0,0,0,0,0,0,0,63,0,255,23,0,0,0,0,0,248,255,
+255,0,0,1,0,0,0,0,0,0,0,0,0,0,0,192,191,159,61,0,0,0,128,2,0,0,0,255,255,255,
+7,0,0,0,0,0,0,0,0,0,0,192,255,1,0,0,0,0,0,0,248,15,32,0,0,192,251,239,62,0,0,
+0,0,0,14,0,0,0,0,0,0,0,0,0,0,0,0,0,0,248,255,255,255,255,
+255,7,0,0,0,0,0,0,20,254,33,254,0,12,0,0,0,2,0,0,0,0,0,0,16,30,32,0,0,12,0,0,
+64,6,0,0,0,0,0,0,16,134,57,2,0,0,0,35,0,6,0,0,0,0,0,0,16,190,33,0,0,12,0,0,
+252,2,0,0,0,0,0,0,144,30,32,64,0,12,0,0,0,4,0,0,0,0,0,0,0,1,32,0,0,0,0,0,0,17,
+0,0,0,0,0,0,192,193,61,96,0,12,0,0,0,2,0,0,0,0,0,0,144,64,48,0,0,12,0,0,0,3,0,
+0,0,0,0,0,24,30,32,0,0,12,0,0,0,0,0,0,0,0,0,0,0,0,4,92,0,0,0,0,0,0,0,0,0,0,0,
+242,7,128,127,0,0,0,0,0,0,0,0,0,0,0,0,242,31,0,63,0,0,0,0,0,0,0,0,0,3,0,0,160,
+2,0,0,0,0,0,0,254,127,223,224,255,254,255,255,255,31,64,0,0,0,0,0,0,0,0,0,0,0,
+0,224,253,102,0,0,0,195,1,0,30,0,100,32,0,32,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,255,255,255,255,255,255,255,255,255,
+255,255,255,255,255,255,255,0,0,0,0,0,0,0,0,0,0,0,224,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,28,0,0,0,28,0,0,0,12,0,0,0,12,0,0,0,0,0,0,0,176,63,64,254,
+15,32,0,0,0,0,0,120,0,0,0,0,0,0,0,0,0,0,0,0,0,0,96,0,0,0,0,2,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,135,1,4,14,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,
+128,9,0,0,0,0,0,0,64,127,229,31,248,159,0,0,0,0,0,0,255,127,0,0,0,0,0,0,0,0,
+15,0,0,0,0,0,208,23,4,0,0,0,0,248,15,0,3,0,0,0,60,59,0,0,0,0,0,0,64,163,3,0,0,
+0,0,0,0,240,207,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,247,255,253,33,16,
+3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,255,255,255,
+251,0,248,0,0,0,124,0,0,0,0,0,0,223,255,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,
+255,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,3,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,128,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,0,0,0,0,
+0,60,0,0,0,0,0,0,0,0,0,0,0,0,0,6,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,128,247,63,0,0,0,192,0,0,0,0,0,0,0,0,0,0,3,0,68,8,0,0,96,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,48,0,0,0,255,255,3,128,0,0,0,0,192,63,0,0,128,255,3,0,
+0,0,0,0,7,0,0,0,0,0,200,51,0,0,0,0,32,0,0,
+0,0,0,0,0,0,126,102,0,8,16,0,0,0,0,0,16,0,0,0,0,0,0,157,193,2,0,0,0,0,48,64,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,32,33,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,255,255,255,255,255,255,0,0,0,
+64,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,255,255,0,0,255,
+255,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,128,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,14,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,32,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,1,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,192,7,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,110,240,0,
+0,0,0,0,135,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,96,0,0,0,0,0,0,0,240,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,192,255,1,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,0,0,0,0,0,0,255,127,0,0,0,0,0,0,128,
+3,0,0,0,0,0,120,38,0,32,0,0,0,0,0,0,7,0,0,0,128,239,31,0,0,0,0,0,0,0,8,0,3,0,
+0,0,0,0,192,127,0,30,0,0,0,0,0,0,0,0,0,0,0,128,211,64,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,128,248,7,0,0,3,0,0,0,0,0,0,24,1,0,0,0,192,31,31,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,255,92,0,0,64,0,0,0,0,0,0,0,0,0,0,248,133,13,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,60,176,1,0,0,48,0,0,0,0,0,0,0,0,0,0,
+248,167,1,0,0,0,0,0,0,0,0,0,0,0,0,40,191,0,0,0,0,0,0,0,0,0,0,0,0,224,188,15,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,128,255,6,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,240,12,1,0,0,0,254,7,0,0,0,0,248,121,128,0,126,14,0,0,0,0,0,252,
+127,3,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,127,191,0,0,0,0,0,0,0,0,0,0,252,255,
+255,252,109,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,126,180,191,0,0,0,0,0,0,0,0,0,163,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,24,0,0,0,0,0,0,0,255,
+1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,31,0,0,0,0,0,0,0,127,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,128,0,0,0,0,0,0,0,128,7,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,96,15,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,3,248,255,231,15,0,0,0,60,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,28,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,
+255,255,255,255,127,248,255,255,255,255,255,31,32,0,16,0,0,248,254,255,0,0,0,
+0,0,0,0,0,0,0,127,255,255,249,219,7,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,127,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,240,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,127,0,0,0,0,0,0,0,0,0,0,0,0,0,240,7,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,};
+
+static const unsigned char wtable[] = {
+16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,18,16,16,16,16,16,16,16,16,
+16,16,16,16,16,16,16,16,16,19,16,20,21,22,16,16,16,23,16,16,24,25,26,27,28,17,
+17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,29,
+17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,
+17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,
+17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,
+17,17,17,17,17,17,17,17,30,16,16,16,16,31,16,16,17,17,17,17,17,17,17,17,17,17,
+17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,
+17,17,17,17,17,17,17,32,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,
+16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,17,17,16,16,16,33,
+34,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,
+16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,
+16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,
+16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,
+16,16,16,16,16,16,16,16,35,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,17,
+17,17,17,17,17,17,36,17,17,37,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,
+16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,17,38,39,16,16,
+16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,
+16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,16,
+16,16,16,16,16,16,16,40,41,42,43,44,45,46,47,16,48,49,16,16,16,16,
+16,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,255,255,
+255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
+255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
+255,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,12,0,6,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,30,9,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,96,0,0,48,0,0,0,0,0,0,255,15,0,0,0,0,128,0,0,8,
+0,2,12,0,96,48,64,16,0,0,4,44,36,32,12,0,0,0,1,0,0,0,80,184,0,0,0,0,0,0,0,224,
+0,0,0,1,128,0,0,0,0,0,0,0,0,0,0,0,24,0,0,0,0,0,0,33,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,255,255,255,251,255,255,255,255,255,255,255,
+255,255,255,15,0,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
+255,255,255,255,255,255,255,255,255,255,255,63,0,0,0,255,15,255,255,255,255,
+255,255,255,127,254,255,255,255,255,255,255,255,255,255,127,254,255,255,255,
+255,255,255,255,255,255,255,255,255,224,255,255,255,255,255,254,255,255,255,
+255,255,255,255,255,255,255,127,255,255,255,255,255,7,255,255,255,255,15,0,
+255,255,255,255,255,127,255,255,255,255,255,0,255,255,255,255,255,255,255,255,
+255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
+255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,0,
+0,0,0,0,0,0,0,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
+255,31,255,255,255,255,255,255,127,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,
+255,255,31,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
+255,15,0,0,0,0,0,0,0,0,0,0,0,0,0,255,3,0,0,255,255,255,255,247,255,127,15,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,254,255,255,255,255,255,255,255,255,255,255,
+255,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,127,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,15,0,0,0,255,255,255,255,255,255,255,255,255,255,255,
+255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
+255,0,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
+255,255,255,255,255,255,255,255,255,255,255,255,7,0,255,255,255,127,0,0,0,0,0,
+0,7,0,240,0,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
+255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
+255,255,255,255,255,255,255,255,255,255,255,255,255,255,
+15,16,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,128,0,0,0,0,0,0,0,0,0,0,
+0,0,0,0,0,0,0,0,0,0,0,0,0,64,254,7,0,0,0,0,0,0,0,0,0,0,0,0,7,0,255,255,255,
+255,255,15,255,1,3,0,63,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,255,255,255,255,
+1,224,191,255,255,255,255,255,255,255,255,223,255,255,15,0,255,255,255,255,
+255,135,15,0,255,255,17,255,255,255,255,255,255,255,255,127,253,255,255,255,
+255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
+159,255,255,255,255,255,255,255,63,0,120,255,255,255,0,0,4,0,0,96,0,16,0,0,0,
+0,0,0,0,0,0,0,248,255,255,255,255,255,255,255,255,255,255,0,0,0,0,0,0,255,255,
+255,255,255,255,255,255,63,16,39,0,0,24,240,7,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,255,15,0,
+0,0,224,255,255,255,255,255,255,255,255,255,255,255,255,123,252,255,255,255,
+255,231,199,255,255,255,231,255,255,255,255,255,255,0,0,0,0,0,0,0,0,0,0,0,0,0,
+0,15,7,7,0,63,0,0,0,0,0,0,0,0,0,0,0,0,0,
+};
+
+/* Upper 6 state bits are a negative integer offset to bound-check next byte */
+/* equivalent to: ( (b-0x80) | (b+offset) ) & ~0x3f */
+#define OOB(c,b) (((((b)>>3)-0x10)|(((b)>>3)+((int32_t)(c)>>26))) & ~7)
+
+/* Interval [a,b). Either a must be 80 or b must be c0, lower 3 bits clear. */
+#define R(a,b) ((uint32_t)((a==0x80 ? 0x40u-b : 0u-a) << 23))
+#define FAILSTATE R(0x80,0x80)
+
+#define SA 0xc2u
+#define SB 0xf4u
+
+/* Arbitrary encoding for representing code units instead of characters. */
+#define CODEUNIT(c) (0xdfff & (signed char)(c))
+#define IS_CODEUNIT(c) ((unsigned)(c)-0xdf80 < 0x80)
+
+static int
+internal_mbtowc(wchar_t *restrict wc, const char *restrict src, size_t n)
+{
+#define C(x) ( x<2 ? -1 : ( R(0x80,0xc0) | x ) )
+#define D(x) C((x+16))
+#define E(x) ( ( x==0 ? R(0xa0,0xc0) : \
+ x==0xd ? R(0x80,0xa0) : \
+ R(0x80,0xc0) ) \
+ | ( R(0x80,0xc0) >> 6 ) \
+ | x )
+#define F(x) ( ( x>=5 ? 0 : \
+ x==0 ? R(0x90,0xc0) : \
+ x==4 ? R(0x80,0x90) : \
+ R(0x80,0xc0) ) \
+ | ( R(0x80,0xc0) >> 6 ) \
+ | ( R(0x80,0xc0) >> 12 ) \
+ | x )
+
+ static const uint32_t bittab[] = {
+ C(0x2),C(0x3),C(0x4),C(0x5),C(0x6),C(0x7),
+ C(0x8),C(0x9),C(0xa),C(0xb),C(0xc),C(0xd),C(0xe),C(0xf),
+ D(0x0),D(0x1),D(0x2),D(0x3),D(0x4),D(0x5),D(0x6),D(0x7),
+ D(0x8),D(0x9),D(0xa),D(0xb),D(0xc),D(0xd),D(0xe),D(0xf),
+ E(0x0),E(0x1),E(0x2),E(0x3),E(0x4),E(0x5),E(0x6),E(0x7),
+ E(0x8),E(0x9),E(0xa),E(0xb),E(0xc),E(0xd),E(0xe),E(0xf),
+ F(0x0),F(0x1),F(0x2),F(0x3),F(0x4)
+ };
+ unsigned c;
+ const unsigned char *s = (const void *)src;
+ wchar_t dummy;
+
+ if (!s) return 0;
+ if (!n) goto ilseq;
+ if (!wc) wc = &dummy;
+
+ if (*s < 0x80) return !!(*wc = *s);
+ if (MB_CUR_MAX==1) return (*wc = CODEUNIT(*s)), 1;
+ if (*s-SA > SB-SA) goto ilseq;
+ c = bittab[*s++-SA];
+
+ /* Avoid excessive checks against n: If shifting the state n-1
+ * times does not clear the high bit, then the value of n is
+ * insufficient to read a character */
+ if (n<4 && ((c<<(6*n-6)) & (1U<<31))) goto ilseq;
+
+ if (OOB(c,*s)) goto ilseq;
+ c = c<<6 | *s++-0x80;
+ if (!(c&(1U<<31))) {
+ *wc = c;
+ return 2;
+ }
+
+ if (*s-0x80u >= 0x40) goto ilseq;
+ c = c<<6 | *s++-0x80;
+ if (!(c&(1U<<31))) {
+ *wc = c;
+ return 3;
+ }
+
+ if (*s-0x80u >= 0x40) goto ilseq;
+ *wc = c<<6 | *s++-0x80;
+ return 4;
+
+ilseq:
+ errno = EILSEQ;
+ return -1;
+}
+
+static int internal_wcwidth(wchar_t wc)
+{
+ if (wc < 0xff)
+ return (wc+1 & 0x7f) >= 0x21 ? 1 : wc ? -1 : 0;
+ if ((wc & 0xfffeffffU) < 0xfffe) {
+ if ((table[table[wc>>8]*32+((wc&255)>>3)]>>(wc&7))&1)
+ return 0;
+ if ((wtable[wtable[wc>>8]*32+((wc&255)>>3)]>>(wc&7))&1)
+ return 2;
+ return 1;
+ }
+ if ((wc & 0xfffe) == 0xfffe)
+ return -1;
+ if (wc-0x20000U < 0x20000)
+ return 2;
+ if (wc == 0xe0001 || wc-0xe0020U < 0x5f || wc-0xe0100U < 0xef)
+ return 0;
+ return 1;
+}
+
+#define mbtowc internal_mbtowc
+#define wcwidth internal_wcwidth
+
+#endif
+
+ssize_t utf8_nsyms(const char *str, size_t len)
+{
+ size_t nsyms = 0;
+ size_t ofs = 0;
+
+ while (ofs < len) {
+ wchar_t sym;
+ int ret;
+
+ ret = mbtowc(&sym, str + ofs, len - ofs);
+ if (ret <= 0) {
+ ret = 1;
+ sym = 'A';
+ } else if ((size_t)ret > len) {
+ ret = len;
+ }
+
+ ofs += ret;
+ ret = wcwidth(sym);
+ if (ret < 0)
+ continue;
+
+ nsyms += ret;
+ }
+
+ return nsyms;
+}
diff --git a/package/utils/ucode-mod-uline/src/vt100.c b/package/utils/ucode-mod-uline/src/vt100.c
new file mode 100644
index 0000000000..f81b11d3ad
--- /dev/null
+++ b/package/utils/ucode-mod-uline/src/vt100.c
@@ -0,0 +1,108 @@
+// SPDX-License-Identifier: ISC
+/*
+ * Copyright (C) 2025 Felix Fietkau <nbd@nbd.name>
+ */
+#include <string.h>
+#include <stdlib.h>
+#include "uline.h"
+#include "private.h"
+
+enum vt100_escape vt100_esc_decode(const char *str, uint32_t *data)
+{
+ unsigned long code, code2;
+ char *err;
+
+ switch (*(str++)) {
+ case 0:
+ return VT100_INCOMPLETE;
+ case '[':
+ case 'O':
+ switch (*(str++)) {
+ case 0:
+ return VT100_INCOMPLETE;
+ case 'A':
+ return VT100_CURSOR_UP;
+ case 'B':
+ return VT100_CURSOR_DOWN;
+ case 'C':
+ return VT100_CURSOR_RIGHT;
+ case 'D':
+ return VT100_CURSOR_LEFT;
+ case 'F':
+ return VT100_END;
+ case 'H':
+ return VT100_HOME;
+ case '5':
+ switch (*str) {
+ case 'C':
+ return VT100_CURSOR_WORD_RIGHT;
+ case 'D':
+ return VT100_CURSOR_WORD_LEFT;
+ default:
+ break;
+ }
+ /* fallthrough */
+ case '0' ... '4':
+ case '6' ... '9':
+ str--;
+ code = strtoul(str, &err, 10);
+ switch (*err) {
+ case 0:
+ return VT100_INCOMPLETE;
+ case '~':
+ switch (code) {
+ case 1:
+ return VT100_HOME;
+ case 3:
+ return VT100_DELETE;
+ case 4:
+ return VT100_END;
+ case 200:
+ case 201:
+ // paste start/end
+ return VT100_IGNORE;
+ default:
+ return VT100_UNKNOWN;
+ }
+ case ';':
+ code2 = strtoul(err + 1, &err, 10);
+ switch (*err) {
+ case 0:
+ return VT100_INCOMPLETE;
+ case 'R':
+ *data = (code2 << 16) | (code & 0xffff);
+ return VT100_CURSOR_POS;
+ default:
+ return VT100_UNKNOWN;
+ }
+ default:
+ return VT100_UNKNOWN;
+ }
+ default:
+ return VT100_UNKNOWN;
+ }
+ default:
+ return VT100_UNKNOWN;
+ }
+}
+
+void __vt100_csi_num(FILE *out, int num, char code)
+{
+ fprintf(out, "\e[%d%c", num, code);
+}
+
+void __vt100_esc(FILE *out, char c)
+{
+ char seq[] = "\eX";
+ seq[1] = c;
+ fputs(seq, out);
+}
+
+void __vt100_csi2(FILE *out, char c1, char c2)
+{
+ char seq[] = "\e[XX";
+
+ seq[2] = c1;
+ seq[3] = c2;
+ fputs(seq, out);
+}
diff --git a/package/utils/ucode/Makefile b/package/utils/ucode/Makefile
index f9db6b060e..8ae1b91c1f 100644
--- a/package/utils/ucode/Makefile
+++ b/package/utils/ucode/Makefile
@@ -12,29 +12,33 @@ PKG_RELEASE:=1
PKG_SOURCE_PROTO:=git
PKG_SOURCE_URL=https://github.com/jow-/ucode.git
-PKG_SOURCE_DATE:=2024-04-07
-PKG_SOURCE_VERSION:=5507654a498a339c44b642f62e203e1d5fb1f725
-PKG_MIRROR_HASH:=40d3df5308faaf3cddfca4ebbcd9ee7fff98cf7e7d406dc177972a7abf0ca16b
+PKG_SOURCE_DATE:=2025-07-18
+PKG_SOURCE_VERSION:=3f64c8089bf3ea4847c96b91df09fbfcaec19e1d
+PKG_MIRROR_HASH:=55fbff7c527e1fadbda2e038636f39419649841ee63a5f3cdb50b9714b13420c
PKG_MAINTAINER:=Jo-Philipp Wich <jo@mein.io>
PKG_LICENSE:=ISC
PKG_ABI_VERSION:=20230711
+PKG_BUILD_DEPENDS:=libmd
HOST_BUILD_DEPENDS:=libjson-c/host
include $(INCLUDE_DIR)/package.mk
include $(INCLUDE_DIR)/host-build.mk
include $(INCLUDE_DIR)/cmake.mk
-CMAKE_OPTIONS += -DSOVERSION=$(PKG_ABI_VERSION)
+CMAKE_OPTIONS += \
+ -DSOVERSION=$(PKG_ABI_VERSION)
-ifeq ($(HOST_OS),Darwin)
- CMAKE_HOST_OPTIONS += \
+CMAKE_HOST_OPTIONS += \
-DCMAKE_SKIP_RPATH=FALSE \
- -DCMAKE_MACOSX_RPATH=1 \
-DCMAKE_INSTALL_RPATH="${STAGING_DIR_HOSTPKG}/lib"
+
+ifeq ($(HOST_OS),Darwin)
+ CMAKE_HOST_OPTIONS += \
+ -DCMAKE_MACOSX_RPATH=1
else
CMAKE_HOST_OPTIONS += \
- -DSOVERSION=$(PKG_ABI_VERSION)
+ -DUSE_RPATH="${STAGING_DIR_HOSTPKG}/lib"
endif
CMAKE_HOST_OPTIONS += \
@@ -48,7 +52,8 @@ CMAKE_HOST_OPTIONS += \
-DUCI_SUPPORT=OFF \
-DULOOP_SUPPORT=OFF \
-DDEBUG_SUPPORT=ON \
- -DLOG_SUPPORT=OFF
+ -DLOG_SUPPORT=OFF \
+ -DDIGEST_SUPPORT=OFF
define Package/ucode/default
@@ -156,6 +161,10 @@ $(eval $(call UcodeModule, \
The rtnl plugin provides access to the Linux routing netlink API.))
$(eval $(call UcodeModule, \
+ socket, SOCKET_SUPPORT, , \
+ The socket plugin provides access to IPv4, IPv6, Unix and packet socket APIs.))
+
+$(eval $(call UcodeModule, \
struct, STRUCT_SUPPORT, , \
The struct plugin implements Python 3 compatible struct.pack/unpack functionality.))
@@ -171,6 +180,10 @@ $(eval $(call UcodeModule, \
uloop, ULOOP_SUPPORT, +libubox, \
The uloop module allows ucode scripts to interact with OpenWrt uloop event loop implementation.))
+$(eval $(call UcodeModule, \
+ digest, DIGEST_SUPPORT, , \
+ The digest module allows ucode scripts to use libmd digests.))
+
$(eval $(call BuildPackage,libucode))
$(eval $(call BuildPackage,ucode))
diff --git a/package/utils/ucode/patches/100-ubus-fix-uc_ubus_have_uloop-for-eloop-uloop-combinat.patch b/package/utils/ucode/patches/100-ubus-fix-uc_ubus_have_uloop-for-eloop-uloop-combinat.patch
deleted file mode 100644
index a1659be3c8..0000000000
--- a/package/utils/ucode/patches/100-ubus-fix-uc_ubus_have_uloop-for-eloop-uloop-combinat.patch
+++ /dev/null
@@ -1,26 +0,0 @@
-From: Felix Fietkau <nbd@nbd.name>
-Date: Wed, 1 May 2024 18:40:19 +0200
-Subject: [PATCH] ubus: fix uc_ubus_have_uloop for eloop+uloop combination
-
-When uloop has been integrated with eloop (in hostapd/wpa_supplicant),
-uloop_cancelling will return false, since uloop_run is not being called.
-This leads to ubus.defer() calling uloop_run in a context where it can
-prevent the other event loop from running.
-
-Fix this by detecting event loop integration via uloop_fd_set_cb being set
-
-Signed-off-by: Felix Fietkau <nbd@nbd.name>
----
-
---- a/lib/ubus.c
-+++ b/lib/ubus.c
-@@ -665,6 +665,9 @@ uc_ubus_have_uloop(void)
- bool prev = uloop_cancelled;
- bool active;
-
-+ if (uloop_fd_set_cb)
-+ return true;
-+
- uloop_cancelled = true;
- active = uloop_cancelling();
- uloop_cancelled = prev;
diff --git a/package/utils/ucode/patches/100-ucode-add-padding-to-uc_resource_ext_t.patch b/package/utils/ucode/patches/100-ucode-add-padding-to-uc_resource_ext_t.patch
new file mode 100644
index 0000000000..f8a8cf29cc
--- /dev/null
+++ b/package/utils/ucode/patches/100-ucode-add-padding-to-uc_resource_ext_t.patch
@@ -0,0 +1,21 @@
+From: Felix Fietkau <nbd@nbd.name>
+Date: Mon, 21 Jul 2025 21:07:17 +0200
+Subject: [PATCH] ucode: add padding to uc_resource_ext_t
+
+This ensures that user data structures tied to the ext resource are aligned
+to 64 bit, as usually guaranteed by the memory allocator.
+
+Signed-off-by: Felix Fietkau <nbd@nbd.name>
+---
+
+--- a/include/ucode/types.h
++++ b/include/ucode/types.h
+@@ -213,6 +213,8 @@ typedef struct {
+ uint32_t persistent:1;
+ uint32_t uvcount:8;
+ uint32_t datasize:20;
++
++ uint32_t _pad;
+ } uc_resource_ext_t;
+
+ uc_declare_vector(uc_resource_types_t, uc_resource_type_t *);
diff --git a/package/utils/usbgadget/Makefile b/package/utils/usbgadget/Makefile
index d3a68ea9df..ee45ea4222 100644
--- a/package/utils/usbgadget/Makefile
+++ b/package/utils/usbgadget/Makefile
@@ -1,7 +1,7 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=usbgadget
-PKG_RELEASE:=1
+PKG_RELEASE:=2
PKG_LICENSE:=BSD-2-Clause
@@ -12,10 +12,14 @@ include $(INCLUDE_DIR)/package.mk
define Package/$(PKG_NAME)
SECTION:=utils
CATEGORY:=Utilities
- DEPENDS:=@USB_GADGET_SUPPORT +kmod-usb-gadget +kmod-usb-lib-composite
+ DEPENDS:=@USB_GADGET_SUPPORT +kmod-usb-gadget +kmod-fs-configfs +kmod-usb-lib-composite
TITLE:=init script to create USB gadgets
endef
+define Package/$(PKG_NAME)/conffiles
+/etc/config/usbgadget
+endef
+
define Build/Compile
endef
@@ -35,7 +39,7 @@ define GadgetPreset
SECTION:=utils
CATEGORY:=Utilities
TITLE+= $(2) gadget preset
- DEPENDS+= $(3)
+ DEPENDS+= +usbgadget $(3)
endef
define Package/$(PKG_NAME)-$(1)/description
@@ -51,4 +55,4 @@ define GadgetPreset
endef
$(eval $(call GadgetPreset,ncm,CDC-NCM,+kmod-usb-gadget-ncm))
-$(eval $(call GadgetPreset,acm,CDC-ACM,+kmod-usb-gadget-serial)) \ No newline at end of file
+$(eval $(call GadgetPreset,acm,CDC-ACM,+kmod-usb-gadget-serial))
diff --git a/package/utils/usbgadget/files/usbgadget.init b/package/utils/usbgadget/files/usbgadget.init
index f2e105caae..384ab548b6 100644
--- a/package/utils/usbgadget/files/usbgadget.init
+++ b/package/utils/usbgadget/files/usbgadget.init
@@ -35,6 +35,9 @@ setup_gadget() {
local strings_path="${gadget_path}/strings/0x409"
mkdir "$strings_path"
apply_configs "$strings_path" "$cfg"
+
+ local os_desc_path="${gadget_path}/os_desc"
+ apply_configs "$os_desc_path" "$cfg"
}
setup_configuration() {
@@ -54,6 +57,8 @@ setup_configuration() {
local strings_path="${cfg_path}/strings/0x409"
mkdir "$strings_path"
apply_configs "$strings_path" "$cfg"
+
+ ln -s "$cfg_path" "${GADGET_FS}/${GADGET_PREFIX}${gadget}/os_desc"
}
setup_function() {
@@ -78,6 +83,9 @@ setup_function() {
apply_configs "$fun_path" "$cfg"
+ local os_desc_path="$(find ${fun_path}/os_desc/* -type d)"
+ apply_configs "$os_desc_path" "$cfg"
+
ln -s "$fun_path" "$cfg_path"
}
@@ -134,6 +142,7 @@ stop() {
for gadget_path in ${GADGET_FS}/${GADGET_PREFIX}* ; do
[ -d "$gadget_path" ] || continue
echo "" > ${gadget_path}/UDC
+ find ${gadget_path}/os_desc -maxdepth 1 -type l -exec rm '{}' ';'
find ${gadget_path}/configs -maxdepth 2 -type l -exec rm '{}' ';'
rmdir ${gadget_path}/configs/*/strings/*
rmdir ${gadget_path}/configs/*
diff --git a/package/utils/usbmode/data/3426-1f01 b/package/utils/usbmode/data/3426-1f01
new file mode 100644
index 0000000000..cd5b7e7055
--- /dev/null
+++ b/package/utils/usbmode/data/3426-1f01
@@ -0,0 +1,4 @@
+# Huawei E5785
+TargetVendor=0x3426
+TargetProduct=0x14db
+HuaweiNewMode=1
diff --git a/package/utils/util-linux/Makefile b/package/utils/util-linux/Makefile
index ec6925f0ea..52dca78e3d 100644
--- a/package/utils/util-linux/Makefile
+++ b/package/utils/util-linux/Makefile
@@ -8,22 +8,14 @@
include $(TOPDIR)/rules.mk
PKG_NAME:=util-linux
-PKG_VERSION:=2.39.3
-PKG_RELEASE:=1
+PKG_VERSION:=2.41.1
+PKG_RELEASE:=2
PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.xz
-PKG_SOURCE_URL:=@KERNEL/linux/utils/$(PKG_NAME)/v2.39
-PKG_HASH:=7b6605e48d1a49f43cc4b4cfc59f313d0dd5402fa40b96810bd572e167dfed0f
+PKG_SOURCE_URL:=@KERNEL/linux/utils/$(PKG_NAME)/v2.41
+PKG_HASH:=be9ad9a276f4305ab7dd2f5225c8be1ff54352f565ff4dede9628c1aaa7dec57
PKG_CPE_ID:=cpe:/a:kernel:util-linux
-PKG_LICENSE:=GPL-2.0-only
-PKG_LICENSE_FILES:= COPYING \
- libblkid/COPYING \
- libmount/COPYING \
- Documentation/licenses/COPYING.GPLv2 \
- Documentation/licenses/COPYING.LGPLv2.1 \
- libuuid/COPYING \
- Documentation/licenses/COPYING.BSD-3
PKG_INSTALL:=1
@@ -44,6 +36,8 @@ $(call Package/util-linux/Default)
SECTION:=libs
CATEGORY:=Libraries
ABI_VERSION:=1
+ LICENSE:=LGPL-2.1-or-later
+ LICENSE_FILES:=Documentation/licenses/COPYING.LGPL-2.1-or-later
endef
define Package/libblkid/description
@@ -60,6 +54,8 @@ $(call Package/util-linux/Default)
SECTION:=libs
CATEGORY:=Libraries
ABI_VERSION:=1
+ LICENSE:=LGPL-2.1-or-later
+ LICENSE_FILES:=Documentation/licenses/COPYING.LGPL-2.1-or-later
endef
define Package/libfdisk/description
@@ -73,6 +69,8 @@ $(call Package/util-linux/Default)
SECTION:=libs
CATEGORY:=Libraries
ABI_VERSION:=1
+ LICENSE:=LGPL-2.1-or-later
+ LICENSE_FILES:=Documentation/licenses/COPYING.LGPL-2.1-or-later
endef
define Package/libmount/description
@@ -86,6 +84,8 @@ $(call Package/util-linux/Default)
SECTION:=libs
CATEGORY:=Libraries
ABI_VERSION:=1
+ LICENSE:=BSD-3-Clause
+ LICENSE_FILES:=Documentation/licenses/COPYING.BSD-3-Clause
endef
define Package/libuuid/description
@@ -101,6 +101,8 @@ $(call Package/util-linux/Default)
SECTION:=libs
CATEGORY:=Libraries
ABI_VERSION:=1
+ LICENSE:=LGPL-2.1-or-later
+ LICENSE_FILES:=Documentation/licenses/COPYING.LGPL-2.1-or-later
endef
define Package/libsmartcols/description
@@ -111,6 +113,7 @@ define Package/agetty
$(call Package/util-linux/Default)
TITLE:=alternative Linux getty
SUBMENU=Terminal
+ LICENSE:=Public-Domain
endef
define Package/agetty/description
@@ -123,6 +126,8 @@ $(call Package/util-linux/Default)
TITLE:=discard sectors on a device
SUBMENU=Disc
DEPENDS:=libblkid
+ LICENSE:=GPL-2.0-or-later
+ LICENSE_FILES:=Documentation/licenses/COPYING.GPL-2.0-or-later
endef
define Package/blkdiscard/description
@@ -136,6 +141,8 @@ $(call Package/util-linux/Default)
TITLE:=locate and print block device attributes
DEPENDS:= +libblkid +libuuid
SUBMENU=Disc
+ LICENSE:=LGPL-2.1-or-later
+ LICENSE_FILES:=Documentation/licenses/COPYING.LGPL-2.1-or-later
endef
define Package/blkid/description
@@ -147,6 +154,8 @@ define Package/blockdev
$(call Package/util-linux/Default)
TITLE:=call block device ioctls from the command line
SUBMENU=Disc
+ LICENSE:=GPL-2.0-or-later
+ LICENSE_FILES:=Documentation/licenses/COPYING.GPL-2.0-or-later
endef
define Package/blockdev/description
@@ -157,6 +166,8 @@ define Package/cal
$(call Package/util-linux/Default)
TITLE:=display a calendar
DEPENDS:= +libncurses
+ LICENSE=BSD-4-Clause-UC
+ LICENSE_FILES:=Documentation/licenses/COPYING.BSD-4-Clause-UC
endef
define Package/cal/description
@@ -168,6 +179,8 @@ $(call Package/util-linux/Default)
TITLE:=display or manipulate disk partition table
DEPENDS:= +libblkid +libncurses +libsmartcols +libfdisk +libmount
SUBMENU:=Disc
+ LICENSE:=GPL-2.0-or-later
+ LICENSE_FILES:=Documentation/licenses/COPYING.GPL-2.0-or-later
endef
define Package/cfdisk/description
@@ -178,6 +191,8 @@ define Package/colrm
$(call Package/util-linux/Default)
TITLE:=colrm removes selected columns from a file
DEPENDS:=
+ LICENSE=BSD-4-Clause-UC
+ LICENSE_FILES:=Documentation/licenses/COPYING.BSD-4-Clause-UC
endef
define Package/colrm/description
@@ -189,6 +204,8 @@ define Package/dmesg
$(call Package/util-linux/Default)
TITLE:=print or control the kernel ring buffer
DEPENDS:= +libncursesw
+ LICENSE:=GPL-2.0-or-later
+ LICENSE_FILES:=Documentation/licenses/COPYING.GPL-2.0-or-later
endef
define Package/dmesg/description
@@ -200,6 +217,8 @@ $(call Package/util-linux/Default)
TITLE:=eject removable media
DEPENDS:= +libblkid +libmount +libuuid
SUBMENU=Disc
+ LICENSE:=GPL-2.0-or-later
+ LICENSE_FILES:=Documentation/licenses/COPYING.GPL-2.0-or-later
endef
define Package/eject/description
@@ -212,6 +231,8 @@ $(call Package/util-linux/Default)
TITLE:=manipulate disk partition table
DEPENDS:= +libblkid +libsmartcols +libfdisk +libncursesw
SUBMENU=Disc
+ LICENSE:=GPL-1.0-or-later
+ LICENSE_FILES:=Documentation/licenses/COPYING.GPL-1.0-or-later
endef
define Package/fdisk/description
@@ -223,6 +244,8 @@ $(call Package/util-linux/Default)
TITLE:=find a filesystem by label or UUID
DEPENDS:= +libblkid
SUBMENU=Disc
+ LICENSE:=GPL-1.0-or-later
+ LICENSE_FILES:=Documentation/licenses/COPYING.GPL-1.0-or-later
endef
define Package/findfs/description
@@ -234,6 +257,8 @@ define Package/flock
$(call Package/util-linux/Default)
TITLE:=manage locks from shell scripts
ALTERNATIVES:=200:/usr/bin/flock:/usr/bin/util-linux-flock
+ LICENSE:=MIT
+ LICENSE_FILES:=Documentation/licenses/COPYING.MIT
endef
define Package/flock/description
@@ -245,6 +270,8 @@ $(call Package/util-linux/Default)
TITLE:=discard unused blocks on a mounted filesystem
DEPENDS:= +libblkid +libuuid +libsmartcols +libmount
SUBMENU=Filesystem
+ LICENSE:=GPL-2.0-or-later
+ LICENSE_FILES:=Documentation/licenses/COPYING.GPL-2.0-or-later
endef
define Package/fstrim/description
@@ -256,6 +283,8 @@ endef
define Package/getopt
$(call Package/util-linux/Default)
TITLE:=parse command options (enhanced)
+ LICENSE:=GPL-2.0-or-later
+ LICENSE_FILES:=Documentation/licenses/COPYING.GPL-2.0-or-later
endef
define Package/getopt/description
@@ -266,6 +295,8 @@ endef
define Package/hwclock
$(call Package/util-linux/Default)
TITLE:=query or set the hardware clock
+ LICENSE:=GPL-3.0-or-later
+ LICENSE_FILES:=Documentation/licenses/COPYING.GPL-3.0-or-later
endef
define Package/hwclock/description
@@ -275,6 +306,8 @@ endef
define Package/ipcs
$(call Package/util-linux/Default)
TITLE:=show information on IPC facilities
+ LICENSE:=GPL-2.0-or-later
+ LICENSE_FILES:=Documentation/licenses/COPYING.GPL-2.0-or-later
endef
define Package/ipcs/description
@@ -284,10 +317,24 @@ define Package/ipcs/description
semaphore arrays.
endef
+define Package/last
+$(call Package/util-linux/Default)
+ TITLE:=display history of user logins and logout sessions
+ LICENSE=BSD-4-Clause-UC
+ LICENSE_FILES:=Documentation/licenses/COPYING.BSD-4-Clause-UC
+endef
+
+define Package/last/description
+ last utility displays a history of user login and logout sessions, system reboots,
+ and other events recorded in /var/log/wtmp (or a specified file)
+endef
+
define Package/logger
$(call Package/util-linux/Default)
TITLE:=a shell command interface to the syslog system log module
ALTERNATIVES:=200:/usr/bin/logger:/usr/bin/util-linux-logger
+ LICENSE=BSD-4-Clause-UC
+ LICENSE_FILES:=Documentation/licenses/COPYING.BSD-4-Clause-UC
endef
define Package/logger/description
@@ -298,6 +345,8 @@ endef
define Package/look
$(call Package/util-linux/Default)
TITLE:=display lines beginning with a given string
+ LICENSE=BSD-4-Clause-UC
+ LICENSE_FILES:=Documentation/licenses/COPYING.BSD-4-Clause-UC
endef
define Package/look/description
@@ -308,6 +357,8 @@ define Package/losetup
$(call Package/util-linux/Default)
TITLE:=set up and control loop devices
DEPENDS:= +libsmartcols
+ LICENSE:=GPL-2.0-or-later
+ LICENSE_FILES:=Documentation/licenses/COPYING.GPL-2.0-or-later
endef
define Package/losetup/description
@@ -318,8 +369,10 @@ endef
define Package/lsblk
$(call Package/util-linux/Default)
TITLE:=list block devices
- DEPENDS:= +libblkid +libmount +libsmartcols
+ DEPENDS:= +libblkid +libmount +libsmartcols +libncurses
SUBMENU=Disc
+ LICENSE:=GPL-2.0-or-later
+ LICENSE_FILES:=Documentation/licenses/COPYING.GPL-2.0-or-later
endef
define Package/lsblk/description
@@ -330,6 +383,8 @@ define Package/lscpu
$(call Package/util-linux/Default)
TITLE:=display information about the CPU architecture
DEPENDS:= +libsmartcols
+ LICENSE:=GPL-2.0-or-later
+ LICENSE_FILES:=Documentation/licenses/COPYING.GPL-2.0-or-later
endef
define Package/lscpu/description
@@ -340,6 +395,8 @@ define Package/lslocks
$(call Package/util-linux/Default)
TITLE:=list local system locks
DEPENDS:= +libmount +libsmartcols
+ LICENSE:=GPL-2.0-or-later
+ LICENSE_FILES:=Documentation/licenses/COPYING.GPL-2.0-or-later
endef
define Package/lslocks/description
@@ -350,6 +407,8 @@ define Package/lsns
$(call Package/util-linux/Default)
TITLE:=list system namespaces
DEPENDS:= +libblkid +libmount +libsmartcols
+ LICENSE:=GPL-2.0-or-later
+ LICENSE_FILES:=Documentation/licenses/COPYING.GPL-2.0-or-later
endef
define Package/lsns/description
@@ -360,6 +419,7 @@ define Package/more
$(call Package/util-linux/Default)
TITLE:=filter for paging through text one screenful at a time
DEPENDS:= +libncurses
+ LICENSE:=BSD-4.3TAHOE
endef
define Package/more/description
@@ -369,6 +429,7 @@ endef
define Package/mcookie
$(call Package/util-linux/Default)
TITLE:=generate magic cookies for xauth
+ LICENSE:=Public-Domain
endef
define Package/mcookie/description
@@ -380,6 +441,8 @@ define Package/mount-utils
$(call Package/util-linux/Default)
TITLE:=related (u)mount utilities
DEPENDS+= +libmount +libsmartcols
+ LICENSE:=GPL-2.0-or-later
+ LICENSE_FILES:=Documentation/licenses/COPYING.GPL-2.0-or-later
endef
define Package/mount-utils/description
@@ -389,6 +452,8 @@ endef
define Package/namei
$(call Package/util-linux/Default)
TITLE:=follow a pathname until a terminal point is found
+ LICENSE:=GPL-2.0-or-later
+ LICENSE_FILES:=Documentation/licenses/COPYING.GPL-2.0-or-later
endef
define Package/namei/description
@@ -399,6 +464,8 @@ endef
define Package/nsenter
$(call Package/util-linux/Default)
TITLE:=enter a namespace
+ LICENSE:=GPL-2.0-only
+ LICENSE_FILES:=Documentation/licenses/COPYING.GPL-2.0-only
endef
define Package/nsenter/description
@@ -409,6 +476,8 @@ define Package/prlimit
$(call Package/util-linux/Default)
TITLE:=get and set process resource limits
DEPENDS:= +libsmartcols
+ LICENSE:=GPL-2.0-or-later
+ LICENSE_FILES:=Documentation/licenses/COPYING.GPL-2.0-or-later
endef
define Package/prlimit/description
@@ -419,6 +488,8 @@ endef
define Package/rename
$(call Package/util-linux/Default)
TITLE:=rename files
+ LICENSE:=GPL-2.0-or-later
+ LICENSE_FILES:=Documentation/licenses/COPYING.GPL-2.0-or-later
endef
define Package/rename/description
@@ -429,6 +500,8 @@ endef
define Package/rev
$(call Package/util-linux/Default)
TITLE:=Reverse lines characterwise
+ LICENSE=BSD-4-Clause-UC
+ LICENSE_FILES:=Documentation/licenses/COPYING.BSD-4-Clause-UC
endef
define Package/rev/description
@@ -442,6 +515,8 @@ $(call Package/util-linux/Default)
TITLE:=inform kernel about the presence and numbering of on-disk partitions
DEPENDS:= +libblkid +libsmartcols
SUBMENU=Disc
+ LICENSE:=GPL-2.0-or-later
+ LICENSE_FILES:=Documentation/licenses/COPYING.GPL-2.0-or-later
endef
define Package/partx-utils/description
@@ -452,6 +527,8 @@ define Package/script-utils
$(call Package/util-linux/Default)
TITLE:=make and replay typescript of terminal session
SUBMENU=Terminal
+ LICENSE=BSD-4-Clause-UC
+ LICENSE_FILES:=Documentation/licenses/COPYING.BSD-4-Clause-UC
endef
define Package/script-utils/description
@@ -463,6 +540,8 @@ $(call Package/util-linux/Default)
TITLE:=set terminal attributes
DEPENDS:= +libncurses
SUBMENU:=Terminal
+ LICENSE:=GPL-2.0-or-later
+ LICENSE_FILES:=Documentation/licenses/COPYING.GPL-2.0-or-later
endef
define Package/setterm/description
@@ -475,6 +554,8 @@ $(call Package/util-linux/Default)
TITLE:=partition table manipulator for Linux
SUBMENU=Disc
DEPENDS:= +libblkid +libfdisk +libsmartcols +libncursesw
+ LICENSE:=GPL-1.0-or-later
+ LICENSE_FILES:=Documentation/licenses/COPYING.GPL-1.0-or-later
endef
define Package/sfdisk/description
@@ -487,6 +568,8 @@ $(call Package/util-linux/Default)
TITLE:=swap space management utilities
DEPENDS+= +libblkid
SUBMENU:=Filesystem
+ LICENSE:=GPL-2.0-or-later
+ LICENSE_FILES:=Documentation/licenses/COPYING.GPL-2.0-or-later
endef
define Package/swap-utils/description
@@ -497,6 +580,8 @@ define Package/taskset
$(call Package/util-linux/Default)
TITLE:=set or retrieve a process's CPU affinity
ALTERNATIVES:=200:/usr/bin/taskset:/usr/bin/util-linux-taskset
+ LICENSE:=GPL-2.0-only
+ LICENSE_FILES:=Documentation/licenses/COPYING.GPL-2.0-only
endef
define Package/taskset/description
@@ -506,6 +591,8 @@ endef
define Package/unshare
$(call Package/util-linux/Default)
TITLE:=unshare userspace tool
+ LICENSE:=GPL-2.0-or-later
+ LICENSE_FILES:=Documentation/licenses/COPYING.GPL-2.0-or-later
endef
define Package/unshare/description
@@ -516,6 +603,8 @@ define Package/uuidd
$(call Package/util-linux/Default)
TITLE:=UUID generation daemon
DEPENDS:= +libuuid
+ LICENSE:=GPL-1.0-or-later
+ LICENSE_FILES:=Documentation/licenses/COPYING.GPL-1.0-or-later
endef
define Package/uuidd/description
@@ -529,6 +618,8 @@ define Package/uuidgen
$(call Package/util-linux/Default)
TITLE:=create a new UUID value
DEPENDS:= +libuuid
+ LICENSE:=GPL-1.0-or-later
+ LICENSE_FILES:=Documentation/licenses/COPYING.GPL-1.0-or-later
endef
define Package/uuidgen/description
@@ -542,6 +633,8 @@ define Package/wall
$(call Package/util-linux/Default)
TITLE:=send a message to everybody's terminal
SUBMENU=Terminal
+ LICENSE=BSD-4-Clause-UC
+ LICENSE_FILES:=Documentation/licenses/COPYING.BSD-4-Clause-UC
endef
define Package/wall/description
@@ -549,9 +642,28 @@ define Package/wall/description
set to yes
endef
+define Package/wdctl
+$(call Package/util-linux/Default)
+ TITLE:=show hardware watchdog status
+ DEPENDS:= +libsmartcols
+ LICENSE:=GPL-2.0-or-later
+ LICENSE_FILES:=Documentation/licenses/COPYING.GPL-2.0-or-later
+endef
+
+define Package/wdctl/description
+ Show hardware watchdog status. The default device is /dev/watchdog. If more
+ than one device is specified then the output is separated by one blank line.
+ If the device is already used or user has no permissions to read from the
+ device, then wdctl reads data from sysfs. In this case information about
+ supported features (flags) might be missing. Note that the number of supported
+ watchdog features is hardware specific.
+endef
+
define Package/whereis
$(call Package/util-linux/Default)
TITLE:=locate the binary, source, and manual page files for a command
+ LICENSE=BSD-4-Clause-UC
+ LICENSE_FILES:=Documentation/licenses/COPYING.BSD-4-Clause-UC
endef
define Package/whereis/description
@@ -563,6 +675,8 @@ $(call Package/util-linux/Default)
TITLE:=wipe a signature from a device
DEPENDS:= +libblkid +libsmartcols
SUBMENU:=Disc
+ LICENSE:=GPL-2.0-or-later
+ LICENSE_FILES:=Documentation/licenses/COPYING.GPL-2.0-or-later
endef
define Package/wipefs/description
@@ -581,6 +695,7 @@ MESON_ARGS += \
-Dlibuser=disabled \
-Duse-tty-group=false \
-Duse-tls=false \
+ -Dtranslate-docs=disabled \
-Dbuild-python=disabled \
-Dbuild-zramctl=disabled \
-Dbuild-fsck=disabled \
@@ -593,8 +708,6 @@ MESON_ARGS += \
-Dbuild-minix=disabled \
-Dbuild-fdformat=disabled \
-Dbuild-lslogins=disabled \
- -Dbuild-wdctl=disabled \
- -Dbuild-cal=disabled \
-Dbuild-switch_root=disabled \
-Dbuild-pivot_root=disabled \
-Dbuild-lsmem=disabled \
@@ -605,7 +718,6 @@ MESON_ARGS += \
-Dbuild-rfkill=disabled \
-Dbuild-tunelp=disabled \
-Dbuild-kill=disabled \
- -Dbuild-last=disabled \
-Dbuild-utmpdump=disabled \
-Dbuild-line=disabled \
-Dbuild-mesg=disabled \
@@ -623,6 +735,7 @@ MESON_ARGS += \
-Dbuild-write=disabled \
-Dbuild-bash-completion=disabled \
-Dbuild-pylibmount=disabled \
+ -Dbuild-liblastlog2=disabled \
-Dreadline=disabled \
-Dmagic=disabled \
-Dncursesw=enabled
@@ -760,6 +873,11 @@ define Package/ipcs/install
$(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/bin/ipcs $(1)/usr/bin/
endef
+define Package/last/install
+ $(INSTALL_DIR) $(1)/usr/bin
+ $(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/bin/last $(1)/usr/bin/
+endef
+
define Package/logger/install
$(INSTALL_DIR) $(1)/usr/bin
$(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/bin/logger $(1)/usr/bin/util-linux-logger
@@ -878,12 +996,12 @@ endef
define Package/uuidd/install
$(INSTALL_DIR) $(1)/usr/sbin
- $(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/sbin//uuidd $(1)/usr/sbin/
+ $(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/sbin/uuidd $(1)/usr/sbin/
endef
define Package/uuidgen/install
$(INSTALL_DIR) $(1)/usr/bin
- $(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/bin//uuidgen $(1)/usr/bin/
+ $(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/bin/uuidgen $(1)/usr/bin/
endef
define Package/wall/install
@@ -891,6 +1009,12 @@ define Package/wall/install
$(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/bin/wall $(1)/usr/bin/
endef
+# Install to /bin instead of /usr/bin to not conflict wdctl from wifidog
+define Package/wdctl/install
+ $(INSTALL_DIR) $(1)/bin
+ $(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/bin/wdctl $(1)/bin/
+endef
+
define Package/whereis/install
$(INSTALL_DIR) $(1)/usr/bin
$(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/bin/whereis $(1)/usr/bin/
@@ -924,6 +1048,7 @@ $(eval $(call BuildPackage,fstrim))
$(eval $(call BuildPackage,getopt))
$(eval $(call BuildPackage,hwclock))
$(eval $(call BuildPackage,ipcs))
+$(eval $(call BuildPackage,last))
$(eval $(call BuildPackage,logger))
$(eval $(call BuildPackage,look))
$(eval $(call BuildPackage,losetup))
@@ -949,5 +1074,6 @@ $(eval $(call BuildPackage,unshare))
$(eval $(call BuildPackage,uuidd))
$(eval $(call BuildPackage,uuidgen))
$(eval $(call BuildPackage,wall))
+$(eval $(call BuildPackage,wdctl))
$(eval $(call BuildPackage,whereis))
$(eval $(call BuildPackage,wipefs))
diff --git a/package/utils/util-linux/patches/001-meson-properly-handle-gettext-non-existence.patch b/package/utils/util-linux/patches/001-meson-properly-handle-gettext-non-existence.patch
deleted file mode 100644
index f3d49d8d2d..0000000000
--- a/package/utils/util-linux/patches/001-meson-properly-handle-gettext-non-existence.patch
+++ /dev/null
@@ -1,28 +0,0 @@
-From b8bed37a1493b913bf5bda938487ae0c06c11ce7 Mon Sep 17 00:00:00 2001
-From: =?UTF-8?q?Thomas=20Wei=C3=9Fschuh?= <thomas@t-8ch.de>
-Date: Sat, 5 Aug 2023 08:57:28 +0200
-Subject: [PATCH] meson: properly handle gettext non-existence
-MIME-Version: 1.0
-Content-Type: text/plain; charset=UTF-8
-Content-Transfer-Encoding: 8bit
-
-Commit e91a49c9747f ("meson: don't build po if no gettext")
-tried to add the possibility to build util-linux without gettext.
-
-Unfortunately by default the call to find_program() would abort the
-build if the program is not found.
-Avoid aborting the build.
-
-Signed-off-by: Thomas Weißschuh <thomas@t-8ch.de>
----
- po/meson.build | 2 +-
- 1 file changed, 1 insertion(+), 1 deletion(-)
-
---- a/po/meson.build
-+++ b/po/meson.build
-@@ -1,4 +1,4 @@
--if not find_program('gettext').found()
-+if not find_program('gettext', required : false).found()
- subdir_done()
- endif
-
diff --git a/package/utils/yafut/Makefile b/package/utils/yafut/Makefile
index 37df2d8172..42e91ef8a6 100644
--- a/package/utils/yafut/Makefile
+++ b/package/utils/yafut/Makefile
@@ -5,12 +5,13 @@ PKG_RELEASE:=1
PKG_SOURCE_PROTO:=git
PKG_SOURCE_URL=https://github.com/kempniu/yafut.git
-PKG_MIRROR_HASH:=591eb3ffa334aaebf9f8155ade90b674896223bcabc1039deda57c2aeafb0525
-PKG_SOURCE_DATE:=2023-03-31
-PKG_SOURCE_VERSION:=16435e89d449f953712983315e1a89cdb678620d
+PKG_MIRROR_HASH:=f5c76edc81477d2b68a7b032487d10fa361c8eaeecfc36908fde2ae828b7e822
+PKG_SOURCE_DATE:=2025-02-18
+PKG_SOURCE_VERSION:=e342c93981dc255bba58c17925b5c8983d7dacf8
PKG_LICENSE:=GPL-2.0
PKG_LICENSE_FILES:=LICENSE
+PKG_FLAGS:=nonshared
include $(INCLUDE_DIR)/package.mk
include $(INCLUDE_DIR)/cmake.mk
diff --git a/package/utils/zyxel-bootconfig/Makefile b/package/utils/zyxel-bootconfig/Makefile
index eb2dd79b82..4e1a74aeaa 100644
--- a/package/utils/zyxel-bootconfig/Makefile
+++ b/package/utils/zyxel-bootconfig/Makefile
@@ -15,12 +15,12 @@ include $(INCLUDE_DIR)/package.mk
define Package/zyxel-bootconfig
SECTION:=utils
CATEGORY:=Base system
- TITLE:=Utility for handling ZyXEL Bootconfig settings
+ TITLE:=Utility for handling Zyxel Bootconfig settings
MAINTAINER:=David Bauer <mail@david-bauer.net>
endef
define Package/zyxel-bootconfig/description
- This package contains an utility that allows handling ZyXEL Bootconfig settings.
+ This package contains an utility that allows handling Zyxel Bootconfig settings.
endef
define Build/Compile
diff --git a/package/utils/zyxel-bootconfig/files/95_apply_bootconfig b/package/utils/zyxel-bootconfig/files/95_apply_bootconfig
index c98bc8fbe2..b109b3c95c 100644
--- a/package/utils/zyxel-bootconfig/files/95_apply_bootconfig
+++ b/package/utils/zyxel-bootconfig/files/95_apply_bootconfig
@@ -5,7 +5,7 @@ apply_bootconfig() {
zyxel,nwa50ax|\
zyxel,nwa50ax-pro|\
zyxel,nwa55axe)
- mtd_idx=$(find_mtd_index "bootconfig")
+ mtd_idx=$(find_mtd_index "bootconfig")
zyxel-bootconfig "/dev/mtd$mtd_idx" set-image-status 0 valid
zyxel-bootconfig "/dev/mtd$mtd_idx" set-active-image 0
;;