From patchwork Thu Jul 1 06:15:44 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sean Anderson X-Patchwork-Id: 1499276 X-Patchwork-Delegate: trini@ti.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=lists.denx.de (client-ip=2a01:238:438b:c500:173d:9f52:ddab:ee01; helo=phobos.denx.de; envelope-from=u-boot-bounces@lists.denx.de; receiver=) Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20161025 header.b=PRwa4hkX; dkim-atps=neutral Received: from phobos.denx.de (phobos.denx.de [IPv6:2a01:238:438b:c500:173d:9f52:ddab:ee01]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 4GFnzt4kYqz9sWk for ; Thu, 1 Jul 2021 16:16:58 +1000 (AEST) Received: from h2850616.stratoserver.net (localhost [IPv6:::1]) by phobos.denx.de (Postfix) with ESMTP id 6FE3783264; Thu, 1 Jul 2021 08:16:28 +0200 (CEST) Authentication-Results: phobos.denx.de; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=u-boot-bounces@lists.denx.de Authentication-Results: phobos.denx.de; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="PRwa4hkX"; dkim-atps=neutral Received: by phobos.denx.de (Postfix, from userid 109) id D946E8323D; Thu, 1 Jul 2021 08:16:24 +0200 (CEST) X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on phobos.denx.de X-Spam-Level: X-Spam-Status: No, score=-2.0 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,FREEMAIL_FROM,SPF_HELO_NONE autolearn=unavailable autolearn_force=no version=3.4.2 Received: from mail-qk1-x736.google.com (mail-qk1-x736.google.com [IPv6:2607:f8b0:4864:20::736]) (using TLSv1.3 with cipher TLS_AES_128_GCM_SHA256 (128/128 bits)) (No client certificate requested) by phobos.denx.de (Postfix) with ESMTPS id 8C1CA82C11 for ; Thu, 1 Jul 2021 08:16:16 +0200 (CEST) Authentication-Results: phobos.denx.de; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=seanga2@gmail.com Received: by mail-qk1-x736.google.com with SMTP id y29so4956008qky.12 for ; Wed, 30 Jun 2021 23:16:16 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=vt6/GvTlZmB8nTwvtPZZQyvTUYIF8LL5OBL5udGEzwA=; b=PRwa4hkXWFR640si+vYvIE2HJjCUPWwywrHeXjOvNITbcyw9O4mQSMoz+QCr2klZMO aQkG6NPQwo/5IAYTNH5Frqi4SZc+IUmwTB004UUG+szY2FZlccn2KzlHKLt7dojjS/HK c7H4weGLWJz1dmr/rXCPGVFRSd4qxgq+0G20/Lan2RAlbIP9xomXjZ4ErcPNHgdniUV+ qQIqiYxbnwCAKjZnAQrP7zJ+xYGLJc8Tc7y289a792CqnZ8L6XtCbRwlSbIAg0ariPgg sV+j6fKRImZ8viUiltbRH9mc4i05MzQHDu+r/RFC3s59Ypc8vZweZMM08NwUwTFSrKrE TcLA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=vt6/GvTlZmB8nTwvtPZZQyvTUYIF8LL5OBL5udGEzwA=; b=lIPnUpycA4/AkBVEu23VYxuVw3x8g30yYWtNUYHZPV55PgH8GYzJ6u1P66NCeQ0jj5 stI0IfCanva+pRJiwm6XU8IIpRKQluFnJVtATUGB3zcMIGFtQEQ6gut80KJxMJDHYZ2A eLOochmZNOpTB728psfysKj8ua7057BXv09+yDEMoJ9E576ralN7bhFBAilftDN8qr4Y x5kF9ksdj89DhWWq/xbZUkJD6EJCvpgFtxrEgAHYPltl0ZWJ7XZeHr0JkPwNZ8AfjYu5 p9VrdvZcxp0d3KspZ78m++/4yL6AmS56ONnW1Y2cngs3St1i666yD0nKjpTSsBJzjQDK 4F8A== X-Gm-Message-State: AOAM532lkCCiU9iwrC2WvAShkygbgztbxCEefoP/bY37Q8B6xsYJkFZD 74H5seqxalv12EokxRb3YLfigqqsKjA= X-Google-Smtp-Source: ABdhPJxa+aUXUHacReZMSybw7CzA+S0BaONXfmQX8Zit9aDIT5VILMj6LGIDc7HqOZkypPczxBeoDA== X-Received: by 2002:a37:2fc3:: with SMTP id v186mr40004398qkh.28.1625120175279; Wed, 30 Jun 2021 23:16:15 -0700 (PDT) Received: from godwin.fios-router.home (pool-74-96-87-9.washdc.fios.verizon.net. [74.96.87.9]) by smtp.gmail.com with ESMTPSA id g21sm1684673qts.90.2021.06.30.23.16.14 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 30 Jun 2021 23:16:15 -0700 (PDT) From: Sean Anderson To: u-boot@lists.denx.de, Tom Rini Cc: =?utf-8?q?Marek_Beh=C3=BAn?= , Wolfgang Denk , Simon Glass , Roland Gaudig , Heinrich Schuchardt , Kostas Michalopoulos , Sean Anderson Subject: [RFC PATCH 01/28] Add Zlib License Date: Thu, 1 Jul 2021 02:15:44 -0400 Message-Id: <20210701061611.957918-2-seanga2@gmail.com> X-Mailer: git-send-email 2.32.0 In-Reply-To: <20210701061611.957918-1-seanga2@gmail.com> References: <20210701061611.957918-1-seanga2@gmail.com> MIME-Version: 1.0 X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.34 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" X-Virus-Scanned: clamav-milter 0.103.2 at phobos.denx.de X-Virus-Status: Clean This adds the Zlib License which is compatible with GPLv2 as long as its terms are met. Files originally licensed as Zlib should use the "GPL-2.0+ AND Zlib" SPDX identifier. Signed-off-by: Sean Anderson Reviewed-by: Simon Glass --- Licenses/README | 1 + Licenses/zlib.txt | 14 ++++++++++++++ 2 files changed, 15 insertions(+) create mode 100644 Licenses/zlib.txt diff --git a/Licenses/README b/Licenses/README index d09f0fe2cb..d5af353520 100644 --- a/Licenses/README +++ b/Licenses/README @@ -152,4 +152,5 @@ IBM PIBS (PowerPC Initialization and IBM-pibs ibm-pibs.txt ISC License ISC Y isc.txt https://spdx.org/licenses/ISC MIT License MIT Y mit.txt https://spdx.org/licenses/MIT.html SIL OPEN FONT LICENSE (OFL-1.1) OFL-1.1 Y OFL.txt https://spdx.org/licenses/OFL-1.1.html +zlib License Zlib Y zlib.txt https://spdx.org/licenses/Zlib.html X11 License X11 x11.txt https://spdx.org/licenses/X11.html diff --git a/Licenses/zlib.txt b/Licenses/zlib.txt new file mode 100644 index 0000000000..7ea8dde871 --- /dev/null +++ b/Licenses/zlib.txt @@ -0,0 +1,14 @@ +This software is provided 'as-is', without any express or implied warranty. In +no event will the authors be held liable for any damages arising from the use +of this software. + +Permission is granted to anyone to use this software for any purpose, including +commercial applications, and to alter it and redistribute it freely, subject +to the following restrictions: + 1. The origin of this software must not be misrepresented; you must not +claim that you wrote the original software. If you use this software in a +product, an acknowledgment in the product documentation would be appreciated +but is not required. + 2. Altered source versions must be plainly marked as such, and must not be +misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. From patchwork Thu Jul 1 06:15:45 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sean Anderson X-Patchwork-Id: 1499280 X-Patchwork-Delegate: trini@ti.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=lists.denx.de (client-ip=2a01:238:438b:c500:173d:9f52:ddab:ee01; helo=phobos.denx.de; envelope-from=u-boot-bounces@lists.denx.de; receiver=) Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20161025 header.b=IvSFlJ6C; dkim-atps=neutral Received: from phobos.denx.de (phobos.denx.de [IPv6:2a01:238:438b:c500:173d:9f52:ddab:ee01]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 4GFp0c1f7Jz9sWX for ; Thu, 1 Jul 2021 16:17:36 +1000 (AEST) Received: from h2850616.stratoserver.net (localhost [IPv6:::1]) by phobos.denx.de (Postfix) with ESMTP id D76DE83232; Thu, 1 Jul 2021 08:16:39 +0200 (CEST) Authentication-Results: phobos.denx.de; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=u-boot-bounces@lists.denx.de Authentication-Results: phobos.denx.de; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="IvSFlJ6C"; dkim-atps=neutral Received: by phobos.denx.de (Postfix, from userid 109) id 0F4E683286; Thu, 1 Jul 2021 08:16:36 +0200 (CEST) X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on phobos.denx.de X-Spam-Level: X-Spam-Status: No, score=-2.0 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,FREEMAIL_FROM,SPF_HELO_NONE autolearn=unavailable autolearn_force=no version=3.4.2 Received: from mail-qv1-xf2a.google.com (mail-qv1-xf2a.google.com [IPv6:2607:f8b0:4864:20::f2a]) (using TLSv1.3 with cipher TLS_AES_128_GCM_SHA256 (128/128 bits)) (No client certificate requested) by phobos.denx.de (Postfix) with ESMTPS id 71D0083203 for ; Thu, 1 Jul 2021 08:16:18 +0200 (CEST) Authentication-Results: phobos.denx.de; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=seanga2@gmail.com Received: by mail-qv1-xf2a.google.com with SMTP id x6so2434090qvx.4 for ; Wed, 30 Jun 2021 23:16:18 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=oL/OuK/6rpzoNn/yJ+i3SdFFWzwJndrGsoDTZennVo0=; b=IvSFlJ6CjIrAZX6TAPRn2qOvxOhxuUtptCRPJ+FpdwgUr5cmVleqbRcdMZE8PsDI3+ oJPRsRVFwPlRP64w4mCDEESOMnlztnyJu0Hm6zOYdduUjUqrrBKJIVJ+xPg7DSMlv816 Tq/KHqlfIVWcniq0IwzT/GRl6lUG9s9sHS10+qZQvPgL1aDGPcrxzpLmondNpvcd4C+X hh8guchK0LmEEfh047C2wDBAaRM4KuqWHAvxCU1u98VP2krv/WFtz0hgIMnYdxtFLOuO 3sBW1//bCCnAjazZ/kjeV/eJ1mUnHpVw+uDYPZs3505EZQaH5CDpg/XuiB9CJ4juwApM 4m8A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=oL/OuK/6rpzoNn/yJ+i3SdFFWzwJndrGsoDTZennVo0=; b=C67tsYNE2SNZaWG1LkPSP6wDkSfY3yS3leHtr0D2mN+jKrU2Qg3Gxr/LiGQR4iMmZZ NuTRYM4oMR8l00jY6Pd94JJKeFOIgBtVajEJdxnM+IZt5BRTF4ahrLa0BMbt0+wreifb ivNVhNrmzaY5tRoBPk8V2tahyJ9kH6ausNlHwpM8G+PNl++LFBFMlUhAKAjB791vwMcu N5ikYZndmCYtnPvrNxtAhiNBeNdK5GTWdlUEUjBsEFxyC5e2uPSPCKgFc6rsOzR1q7Wv FeT/D8iM7kXEl+T8NndYTqujXuRMsU4+F4o65Z3RV0C1JhINH3Mn+ZZ13tGHqgjDsnw6 A7hA== X-Gm-Message-State: AOAM530V/7VQuKVuhfEfaSxsiBv+jAkoNglGuNxA8sXNiLoG8klPnXfP 5KuqgvhOCldNM+4tSVuaTEn9LvG5HCs= X-Google-Smtp-Source: ABdhPJx8GWppar8G2MfwGbt7wZoaif7TaDNOlVfQfeg80UXyUgUgucRJGFUrqSBAq8s2HIj9P2DUsg== X-Received: by 2002:a05:6214:18c2:: with SMTP id cy2mr37419768qvb.40.1625120176226; Wed, 30 Jun 2021 23:16:16 -0700 (PDT) Received: from godwin.fios-router.home (pool-74-96-87-9.washdc.fios.verizon.net. [74.96.87.9]) by smtp.gmail.com with ESMTPSA id g21sm1684673qts.90.2021.06.30.23.16.15 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 30 Jun 2021 23:16:15 -0700 (PDT) From: Sean Anderson To: u-boot@lists.denx.de, Tom Rini Cc: =?utf-8?q?Marek_Beh=C3=BAn?= , Wolfgang Denk , Simon Glass , Roland Gaudig , Heinrich Schuchardt , Kostas Michalopoulos , Sean Anderson Subject: [RFC PATCH 02/28] cli: Add LIL shell Date: Thu, 1 Jul 2021 02:15:45 -0400 Message-Id: <20210701061611.957918-3-seanga2@gmail.com> X-Mailer: git-send-email 2.32.0 In-Reply-To: <20210701061611.957918-1-seanga2@gmail.com> References: <20210701061611.957918-1-seanga2@gmail.com> MIME-Version: 1.0 X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.34 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" X-Virus-Scanned: clamav-milter 0.103.2 at phobos.denx.de X-Virus-Status: Clean This is the LIL programming language [1] as originally written by Kostas Michalopoulos . LIL is a stripped-down TCL variant. Many syntax features are very similar to shell: => echo Hello from $stdout Hello from serial,vidconsole LIL supports functions, scoped variables, and command substitution. For a short example, running the following script func fact {n} { if {$n} { expr {$n * [fact [expr {$n - 1}]]} } { return 1 } } echo [fact 10] echo [fact 20] would result in the output fact 3628800 2432902008176640000 For more examples, please refer to the "test: Add tests for LIL" later in this series. I have made several mostly-mechanical modifications to the source before importing it to reduce patches to functional changes. If you are interested in what has been changed before this commit, refer to my lil git repository [2]. In addition, some functions and features have been removed from this patch as I have better determined what is necessary for U-Boot and what is not. If there is sufficient interest, I can add those changes to the repository as well. [1] http://runtimeterror.com/tech/lil/ [2] https://github.com/Forty-Bot/lil/commits/master Signed-off-by: Sean Anderson Reviewed-by: Simon Glass --- MAINTAINERS | 6 + cmd/Kconfig | 11 +- common/Makefile | 1 + common/cli_lil.c | 2991 +++++++++++++++++++++++++++++++++++++++++++++ include/cli_lil.h | 111 ++ 5 files changed, 3119 insertions(+), 1 deletion(-) create mode 100644 common/cli_lil.c create mode 100644 include/cli_lil.h diff --git a/MAINTAINERS b/MAINTAINERS index 2d267aeff2..0184de5f93 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -766,6 +766,12 @@ S: Maintained T: git https://source.denx.de/u-boot/custodians/u-boot-i2c.git F: drivers/i2c/ +LIL SHELL +M: Sean Anderson +S: Maintained +F: common/cli_lil.c +F: include/cli_lil.h + LOGGING M: Simon Glass S: Maintained diff --git a/cmd/Kconfig b/cmd/Kconfig index 9e8b69258f..8bccc572af 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -23,6 +23,15 @@ config HUSH_PARSER If disabled, you get the old, much simpler behaviour with a somewhat smaller memory footprint. +config LIL + bool "Use LIL shell" + depends on CMDLINE + help + This options enables the "Little Interpreted Language" (LIL) shell as + command line interpreter, thus enabling powerful command line syntax + like `proc name {args} {body}' functions or `echo [some command]` + command substitution ("tcl scripts"). + config CMDLINE_EDITING bool "Enable command line editing" depends on CMDLINE @@ -1379,7 +1388,7 @@ config CMD_ECHO config CMD_ITEST bool "itest" - default y + default y if !LIL help Return true/false on integer compare. diff --git a/common/Makefile b/common/Makefile index 829ea5fb42..dce04b305e 100644 --- a/common/Makefile +++ b/common/Makefile @@ -10,6 +10,7 @@ obj-y += main.o obj-y += exports.o obj-$(CONFIG_HASH) += hash.o obj-$(CONFIG_HUSH_PARSER) += cli_hush.o +obj-$(CONFIG_LIL) += cli_lil.o obj-$(CONFIG_AUTOBOOT) += autoboot.o # This option is not just y/n - it can have a numeric value diff --git a/common/cli_lil.c b/common/cli_lil.c new file mode 100644 index 0000000000..c8c6986fe1 --- /dev/null +++ b/common/cli_lil.c @@ -0,0 +1,2991 @@ +// SPDX-License-Identifier: GPL-2.0+ AND Zlib +/* + * LIL - Little Interpreted Language + * Copyright (C) 2021 Sean Anderson + * Copyright (C) 2010-2013 Kostas Michalopoulos + * + * This file originated from the LIL project, licensed under Zlib. All + * modifications are licensed under GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include + +/* Enable pools for reusing values, lists and environments. This will use more memory and + * will rely on the runtime/OS to free the pools once the program ends, but will cause + * considerably less memory fragmentation and improve the script execution performance. */ +/*#define LIL_ENABLE_POOLS*/ + +/* Enable limiting recursive calls to lil_parse - this can be used to avoid call stack + * overflows and is also useful when running through an automated fuzzer like AFL */ +/*#define LIL_ENABLE_RECLIMIT 10000*/ + +#define ERROR_NOERROR 0 +#define ERROR_DEFAULT 1 +#define ERROR_FIXHEAD 2 + +#define CALLBACKS 8 +#define HASHMAP_CELLS 256 +#define HASHMAP_CELLMASK 0xFF + +struct hashentry { + char *k; + void *v; +}; + +struct hashcell { + struct hashentry *e; + size_t c; +}; + +struct hashmap { + struct hashcell cell[HASHMAP_CELLS]; +}; + +struct lil_value { + size_t l; +#ifdef LIL_ENABLE_POOLS + size_t c; +#endif + char *d; +}; + +struct lil_var { + char *n; + struct lil_env *env; + struct lil_value *v; +}; + +struct lil_env { + struct lil_env *parent; + struct lil_func *func; + struct lil_var **var; + size_t vars; + struct hashmap varmap; + struct lil_value *retval; + int retval_set; + int breakrun; +}; + +struct lil_list { + struct lil_value **v; + size_t c; + size_t cap; +}; + +struct lil_func { + char *name; + struct lil_value *code; + struct lil_list *argnames; + lil_func_proc_t proc; +}; + +struct lil { + const char *code; /* need save on parse */ + const char *rootcode; + size_t clen; /* need save on parse */ + size_t head; /* need save on parse */ + int ignoreeol; + struct lil_func **cmd; + size_t cmds; + struct hashmap cmdmap; + struct lil_env *env; + struct lil_env *rootenv; + struct lil_env *downenv; + struct lil_value *empty; + int error; + size_t err_head; + char *err_msg; + lil_callback_proc_t callback[CALLBACKS]; + size_t parse_depth; +}; + +struct expreval { + const char *code; + size_t len, head; + ssize_t ival; + int error; +}; + +static struct lil_value *next_word(struct lil *lil); +static void register_stdcmds(struct lil *lil); + +#ifdef LIL_ENABLE_POOLS +static struct lil_value **pool; +static int poolsize, poolcap; +static struct lil_list **listpool; +static size_t listpoolsize, listpoolcap; +static struct lil_env **envpool; +static size_t envpoolsize, envpoolcap; +#endif + +static char *strclone(const char *s) +{ + size_t len = strlen(s) + 1; + char *ns = malloc(len); + + if (!ns) + return NULL; + memcpy(ns, s, len); + return ns; +} + +static unsigned long hm_hash(const char *key) +{ + unsigned long hash = 5381; + int c; + + while ((c = *key++)) + hash = ((hash << 5) + hash) + c; + return hash; +} + +static void hm_init(struct hashmap *hm) +{ + memset(hm, 0, sizeof(struct hashmap)); +} + +static void hm_destroy(struct hashmap *hm) +{ + size_t i, j; + + for (i = 0; i < HASHMAP_CELLS; i++) { + for (j = 0; j < hm->cell[i].c; j++) + free(hm->cell[i].e[j].k); + free(hm->cell[i].e); + } +} + +static void hm_put(struct hashmap *hm, const char *key, void *value) +{ + struct hashcell *cell = hm->cell + (hm_hash(key) & HASHMAP_CELLMASK); + size_t i; + + for (i = 0; i < cell->c; i++) { + if (!strcmp(key, cell->e[i].k)) { + cell->e[i].v = value; + return; + } + } + + cell->e = realloc(cell->e, sizeof(struct hashentry) * (cell->c + 1)); + cell->e[cell->c].k = strclone(key); + cell->e[cell->c].v = value; + cell->c++; +} + +static void *hm_get(struct hashmap *hm, const char *key) +{ + struct hashcell *cell = hm->cell + (hm_hash(key) & HASHMAP_CELLMASK); + size_t i; + + for (i = 0; i < cell->c; i++) + if (!strcmp(key, cell->e[i].k)) + return cell->e[i].v; + return NULL; +} + +static int hm_has(struct hashmap *hm, const char *key) +{ + struct hashcell *cell = hm->cell + (hm_hash(key) & HASHMAP_CELLMASK); + size_t i; + + for (i = 0; i < cell->c; i++) + if (!strcmp(key, cell->e[i].k)) + return 1; + return 0; +} + +#ifdef LIL_ENABLE_POOLS +static struct lil_value *alloc_from_pool(void) +{ + if (poolsize > 0) { + poolsize--; + return pool[poolsize]; + } else { + struct lil_value *val = calloc(1, sizeof(struct lil_value)); + + return val; + } +} + +static void release_to_pool(struct lil_value *val) +{ + if (poolsize == poolcap) { + poolcap = poolcap ? (poolcap + poolcap / 2) : 64; + pool = realloc(pool, sizeof(struct lil_value *) * poolcap); + } + pool[poolsize++] = val; +} + +static void ensure_capacity(struct lil_value *val, size_t cap) +{ + if (val->c < cap) { + val->c = cap + 128; + val->d = realloc(val->d, val->c); + } +} +#endif + +static struct lil_value *alloc_value_len(const char *str, size_t len) +{ +#ifdef LIL_ENABLE_POOLS + struct lil_value *val = alloc_from_pool(); +#else + struct lil_value *val = calloc(1, sizeof(struct lil_value)); +#endif + + if (!val) + return NULL; + if (str) { + val->l = len; +#ifdef LIL_ENABLE_POOLS + ensure_capacity(val, len + 1); +#else + val->d = malloc(len + 1); + if (!val->d) { + free(val); + return NULL; + } +#endif + memcpy(val->d, str, len); + val->d[len] = 0; + } else { + val->l = 0; +#ifdef LIL_ENABLE_POOLS + ensure_capacity(val, 1); + val->d[0] = '\0'; +#else + val->d = NULL; +#endif + } + return val; +} + +static struct lil_value *alloc_value(const char *str) +{ + return alloc_value_len(str, str ? strlen(str) : 0); +} + +struct lil_value *lil_clone_value(struct lil_value *src) +{ + struct lil_value *val; + + if (!src) + return NULL; +#ifdef LIL_ENABLE_POOLS + val = alloc_from_pool(); +#else + val = calloc(1, sizeof(struct lil_value)); +#endif + if (!val) + return NULL; + + val->l = src->l; + if (src->l) { +#ifdef LIL_ENABLE_POOLS + ensure_capacity(val, val->l + 1); +#else + val->d = malloc(val->l + 1); + if (!val->d) { + free(val); + return NULL; + } +#endif + memcpy(val->d, src->d, val->l + 1); + } else { +#ifdef LIL_ENABLE_POOLS + ensure_capacity(val, 1); + val->d[0] = '\0'; +#else + val->d = NULL; +#endif + } + return val; +} + +int lil_append_char(struct lil_value *val, char ch) +{ +#ifdef LIL_ENABLE_POOLS + ensure_capacity(val, val->l + 2); + val->d[val->l++] = ch; + val->d[val->l] = '\0'; +#else + char *new = realloc(val->d, val->l + 2); + + if (!new) + return 0; + + new[val->l++] = ch; + new[val->l] = 0; + val->d = new; +#endif + return 1; +} + +int lil_append_string_len(struct lil_value *val, const char *s, size_t len) +{ +#ifndef LIL_ENABLE_POOLS + char *new; +#endif + + if (!s || !s[0]) + return 1; + +#ifdef LIL_ENABLE_POOLS + ensure_capacity(val, val->l + len + 1); + memcpy(val->d + val->l, s, len + 1); +#else + new = realloc(val->d, val->l + len + 1); + if (!new) + return 0; + + memcpy(new + val->l, s, len + 1); + val->d = new; +#endif + val->l += len; + return 1; +} + +int lil_append_string(struct lil_value *val, const char *s) +{ + return lil_append_string_len(val, s, strlen(s)); +} + +int lil_append_val(struct lil_value *val, struct lil_value *v) +{ +#ifndef LIL_ENABLE_POOLS + char *new; +#endif + + if (!v || !v->l) + return 1; + +#ifdef LIL_ENABLE_POOLS + ensure_capacity(val, val->l + v->l + 1); + memcpy(val->d + val->l, v->d, v->l + 1); +#else + new = realloc(val->d, val->l + v->l + 1); + if (!new) + return 0; + + memcpy(new + val->l, v->d, v->l + 1); + val->d = new; +#endif + val->l += v->l; + return 1; +} + +void lil_free_value(struct lil_value *val) +{ + if (!val) + return; + +#ifdef LIL_ENABLE_POOLS + release_to_pool(val); +#else + free(val->d); + free(val); +#endif +} + +struct lil_list *lil_alloc_list(void) +{ + struct lil_list *list; + +#ifdef LIL_ENABLE_POOLS + if (listpoolsize > 0) + return listpool[--listpoolsize]; +#endif + list = calloc(1, sizeof(struct lil_list)); + list->v = NULL; + return list; +} + +void lil_free_list(struct lil_list *list) +{ + size_t i; + + if (!list) + return; + + for (i = 0; i < list->c; i++) + lil_free_value(list->v[i]); + +#ifdef LIL_ENABLE_POOLS + list->c = 0; + if (listpoolsize == listpoolcap) { + listpoolcap = + listpoolcap ? (listpoolcap + listpoolcap / 2) : 32; + listpool = realloc(listpool, + sizeof(struct lil_list *) * listpoolcap); + } + listpool[listpoolsize++] = list; +#else + free(list->v); + free(list); +#endif +} + +void lil_list_append(struct lil_list *list, struct lil_value *val) +{ + if (list->c == list->cap) { + size_t cap = list->cap ? (list->cap + list->cap / 2) : 32; + struct lil_value **nv = + realloc(list->v, sizeof(struct lil_value *) * cap); + + if (!nv) + return; + + list->cap = cap; + list->v = nv; + } + list->v[list->c++] = val; +} + +size_t lil_list_size(struct lil_list *list) +{ + return list->c; +} + +struct lil_value *lil_list_get(struct lil_list *list, size_t index) +{ + return index >= list->c ? NULL : list->v[index]; +} + +static int needs_escape(const char *str) +{ + size_t i; + + if (!str || !str[0]) + return 1; + + for (i = 0; str[i]; i++) + if (ispunct(str[i]) || isspace(str[i])) + return 1; + + return 0; +} + +struct lil_value *lil_list_to_value(struct lil_list *list, int do_escape) +{ + struct lil_value *val = alloc_value(NULL); + size_t i, j; + + for (i = 0; i < list->c; i++) { + int escape = + do_escape ? needs_escape(lil_to_string(list->v[i])) : 0; + + if (i) + lil_append_char(val, ' '); + + if (escape) { + lil_append_char(val, '{'); + for (j = 0; j < list->v[i]->l; j++) { + if (list->v[i]->d[j] == '{') + lil_append_string(val, "}\"\\o\"{"); + else if (list->v[i]->d[j] == '}') + lil_append_string(val, "}\"\\c\"{"); + else + lil_append_char(val, list->v[i]->d[j]); + } + lil_append_char(val, '}'); + } else { + lil_append_val(val, list->v[i]); + } + } + return val; +} + +struct lil_env *lil_alloc_env(struct lil_env *parent) +{ + struct lil_env *env; + +#ifdef LIL_ENABLE_POOLS + if (envpoolsize > 0) { + size_t i, j; + + env = envpool[--envpoolsize]; + env->parent = parent; + env->func = NULL; + env->var = NULL; + env->vars = 0; + env->retval = NULL; + env->retval_set = 0; + env->breakrun = 0; + for (i = 0; i < HASHMAP_CELLS; i++) { + for (j = 0; j < env->varmap.cell[i].c; j++) + free(env->varmap.cell[i].e[j].k); + env->varmap.cell[i].c = 0; + } + return env; + } +#endif + env = calloc(1, sizeof(struct lil_env)); + env->parent = parent; + return env; +} + +void lil_free_env(struct lil_env *env) +{ + size_t i; + + if (!env) + return; + + lil_free_value(env->retval); +#ifdef LIL_ENABLE_POOLS + for (i = 0; i < env->vars; i++) { + free(env->var[i]->n); + lil_free_value(env->var[i]->v); + free(env->var[i]); + } + free(env->var); + + if (envpoolsize == envpoolcap) { + envpoolcap = envpoolcap ? (envpoolcap + envpoolcap / 2) : 64; + envpool = + realloc(envpool, sizeof(struct lil_env *) * envpoolcap); + } + envpool[envpoolsize++] = env; +#else + hm_destroy(&env->varmap); + for (i = 0; i < env->vars; i++) { + free(env->var[i]->n); + lil_free_value(env->var[i]->v); + free(env->var[i]); + } + free(env->var); + free(env); +#endif +} + +static struct lil_var *lil_find_local_var(struct lil *lil, struct lil_env *env, + const char *name) +{ + return hm_get(&env->varmap, name); +} + +static struct lil_var *lil_find_var(struct lil *lil, struct lil_env *env, + const char *name) +{ + struct lil_var *r = lil_find_local_var(lil, env, name); + + if (r) + return r; + + if (env == lil->rootenv) + return NULL; + + return lil_find_var(lil, lil->rootenv, name); +} + +static struct lil_func *lil_find_cmd(struct lil *lil, const char *name) +{ + return hm_get(&lil->cmdmap, name); +} + +static struct lil_func *add_func(struct lil *lil, const char *name) +{ + struct lil_func *cmd; + struct lil_func **ncmd; + + cmd = lil_find_cmd(lil, name); + if (cmd) { + if (cmd->argnames) + lil_free_list(cmd->argnames); + lil_free_value(cmd->code); + cmd->argnames = NULL; + cmd->code = NULL; + cmd->proc = NULL; + return cmd; + } + + cmd = calloc(1, sizeof(struct lil_func)); + cmd->name = strclone(name); + + ncmd = realloc(lil->cmd, sizeof(struct lil_func *) * (lil->cmds + 1)); + if (!ncmd) { + free(cmd); + return NULL; + } + + lil->cmd = ncmd; + ncmd[lil->cmds++] = cmd; + hm_put(&lil->cmdmap, name, cmd); + return cmd; +} + +static void del_func(struct lil *lil, struct lil_func *cmd) +{ + size_t i, index = lil->cmds; + + for (i = 0; i < lil->cmds; i++) { + if (lil->cmd[i] == cmd) { + index = i; + break; + } + } + if (index == lil->cmds) + return; + + hm_put(&lil->cmdmap, cmd->name, 0); + if (cmd->argnames) + lil_free_list(cmd->argnames); + + lil_free_value(cmd->code); + free(cmd->name); + free(cmd); + lil->cmds--; + for (i = index; i < lil->cmds; i++) + lil->cmd[i] = lil->cmd[i + 1]; +} + +int lil_register(struct lil *lil, const char *name, lil_func_proc_t proc) +{ + struct lil_func *cmd = add_func(lil, name); + + if (!cmd) + return 0; + cmd->proc = proc; + return 1; +} + +struct lil_var *lil_set_var(struct lil *lil, const char *name, + struct lil_value *val, int local) +{ + struct lil_var **nvar; + struct lil_env *env = + local == LIL_SETVAR_GLOBAL ? lil->rootenv : lil->env; + int freeval = 0; + + if (!name[0]) + return NULL; + + if (local != LIL_SETVAR_LOCAL_NEW) { + struct lil_var *var = lil_find_var(lil, env, name); + + if (local == LIL_SETVAR_LOCAL_ONLY && var && + var->env == lil->rootenv && var->env != env) + var = NULL; + + if (((!var && env == lil->rootenv) || + (var && var->env == lil->rootenv)) && + lil->callback[LIL_CALLBACK_SETVAR]) { + lil_setvar_callback_proc_t proc = + (lil_setvar_callback_proc_t) + lil->callback[LIL_CALLBACK_SETVAR]; + struct lil_value *newval = val; + int r = proc(lil, name, &newval); + + if (r < 0) { + return NULL; + } else if (r > 0) { + val = newval; + freeval = 1; + } + } + + if (var) { + lil_free_value(var->v); + var->v = freeval ? val : lil_clone_value(val); + return var; + } + } + + nvar = realloc(env->var, sizeof(struct lil_var *) * (env->vars + 1)); + if (!nvar) { + /* TODO: report memory error */ + return NULL; + } + + env->var = nvar; + nvar[env->vars] = calloc(1, sizeof(struct lil_var)); + nvar[env->vars]->n = strclone(name); + nvar[env->vars]->env = env; + nvar[env->vars]->v = freeval ? val : lil_clone_value(val); + hm_put(&env->varmap, name, nvar[env->vars]); + return nvar[env->vars++]; +} + +struct lil_value *lil_get_var(struct lil *lil, const char *name) +{ + return lil_get_var_or(lil, name, lil->empty); +} + +struct lil_value *lil_get_var_or(struct lil *lil, const char *name, + struct lil_value *defvalue) +{ + struct lil_var *var = lil_find_var(lil, lil->env, name); + struct lil_value *retval = var ? var->v : defvalue; + + if (lil->callback[LIL_CALLBACK_GETVAR] && + (!var || var->env == lil->rootenv)) { + lil_getvar_callback_proc_t proc = + (lil_getvar_callback_proc_t) + lil->callback[LIL_CALLBACK_GETVAR]; + struct lil_value *newretval = retval; + + if (proc(lil, name, &newretval)) + retval = newretval; + } + return retval; +} + +struct lil_env *lil_push_env(struct lil *lil) +{ + struct lil_env *env = lil_alloc_env(lil->env); + + lil->env = env; + return env; +} + +void lil_pop_env(struct lil *lil) +{ + if (lil->env->parent) { + struct lil_env *next = lil->env->parent; + + lil_free_env(lil->env); + lil->env = next; + } +} + +struct lil *lil_new(void) +{ + struct lil *lil = calloc(1, sizeof(struct lil)); + + lil->rootenv = lil->env = lil_alloc_env(NULL); + lil->empty = alloc_value(NULL); + hm_init(&lil->cmdmap); + register_stdcmds(lil); + return lil; +} + +static int islilspecial(char ch) +{ + return ch == '$' || ch == '{' || ch == '}' || ch == '[' || ch == ']' || + ch == '"' || ch == '\'' || ch == ';'; +} + +static int eolchar(char ch) +{ + return ch == '\n' || ch == '\r' || ch == ';'; +} + +static int ateol(struct lil *lil) +{ + return !(lil->ignoreeol) && eolchar(lil->code[lil->head]); +} + +static void lil_skip_spaces(struct lil *lil) +{ + while (lil->head < lil->clen) { + if (lil->code[lil->head] == '#') { + if (lil->code[lil->head + 1] == '#' && + lil->code[lil->head + 2] != '#') { + lil->head += 2; + while (lil->head < lil->clen) { + if ((lil->code[lil->head] == '#') && + (lil->code[lil->head + 1] == '#') && + (lil->code[lil->head + 2] != '#')) { + lil->head += 2; + break; + } + lil->head++; + } + } else { + while (lil->head < lil->clen && + !eolchar(lil->code[lil->head])) + lil->head++; + } + } else if (lil->code[lil->head] == '\\' && + eolchar(lil->code[lil->head + 1])) { + lil->head++; + while (lil->head < lil->clen && + eolchar(lil->code[lil->head])) + lil->head++; + } else if (eolchar(lil->code[lil->head])) { + if (lil->ignoreeol) + lil->head++; + else + break; + } else if (isspace(lil->code[lil->head])) { + lil->head++; + } else { + break; + } + } +} + +static struct lil_value *get_bracketpart(struct lil *lil) +{ + size_t cnt = 1; + struct lil_value *val, *cmd = alloc_value(NULL); + int save_eol = lil->ignoreeol; + + lil->ignoreeol = 0; + lil->head++; + while (lil->head < lil->clen) { + if (lil->code[lil->head] == '[') { + lil->head++; + cnt++; + lil_append_char(cmd, '['); + } else if (lil->code[lil->head] == ']') { + lil->head++; + if (--cnt == 0) + break; + else + lil_append_char(cmd, ']'); + } else { + lil_append_char(cmd, lil->code[lil->head++]); + } + } + + val = lil_parse_value(lil, cmd, 0); + lil_free_value(cmd); + lil->ignoreeol = save_eol; + return val; +} + +static struct lil_value *get_dollarpart(struct lil *lil) +{ + struct lil_value *val, *name, *tmp; + + lil->head++; + name = next_word(lil); + tmp = alloc_value("set "); + lil_append_val(tmp, name); + lil_free_value(name); + + val = lil_parse_value(lil, tmp, 0); + lil_free_value(tmp); + return val; +} + +static struct lil_value *next_word(struct lil *lil) +{ + struct lil_value *val; + size_t start; + + lil_skip_spaces(lil); + if (lil->code[lil->head] == '$') { + val = get_dollarpart(lil); + } else if (lil->code[lil->head] == '{') { + size_t cnt = 1; + + lil->head++; + val = alloc_value(NULL); + while (lil->head < lil->clen) { + if (lil->code[lil->head] == '{') { + lil->head++; + cnt++; + lil_append_char(val, '{'); + } else if (lil->code[lil->head] == '}') { + lil->head++; + if (--cnt == 0) + break; + else + lil_append_char(val, '}'); + } else { + lil_append_char(val, lil->code[lil->head++]); + } + } + } else if (lil->code[lil->head] == '[') { + val = get_bracketpart(lil); + } else if (lil->code[lil->head] == '"' || + lil->code[lil->head] == '\'') { + char sc = lil->code[lil->head++]; + + val = alloc_value(NULL); + while (lil->head < lil->clen) { + if (lil->code[lil->head] == '[' || + lil->code[lil->head] == '$') { + struct lil_value *tmp = + lil->code[lil->head] == '$' ? + get_dollarpart(lil) : + get_bracketpart(lil); + + lil_append_val(val, tmp); + lil_free_value(tmp); + lil->head--; /* avoid skipping the char below */ + } else if (lil->code[lil->head] == '\\') { + lil->head++; + switch (lil->code[lil->head]) { + case 'b': + lil_append_char(val, '\b'); + break; + case 't': + lil_append_char(val, '\t'); + break; + case 'n': + lil_append_char(val, '\n'); + break; + case 'v': + lil_append_char(val, '\v'); + break; + case 'f': + lil_append_char(val, '\f'); + break; + case 'r': + lil_append_char(val, '\r'); + break; + case '0': + lil_append_char(val, 0); + break; + case 'a': + lil_append_char(val, '\a'); + break; + case 'c': + lil_append_char(val, '}'); + break; + case 'o': + lil_append_char(val, '{'); + break; + default: + lil_append_char(val, + lil->code[lil->head]); + break; + } + } else if (lil->code[lil->head] == sc) { + lil->head++; + break; + } else { + lil_append_char(val, lil->code[lil->head]); + } + lil->head++; + } + } else { + start = lil->head; + while (lil->head < lil->clen && + !isspace(lil->code[lil->head]) && + !islilspecial(lil->code[lil->head])) + lil->head++; + val = alloc_value_len(lil->code + start, lil->head - start); + } + return val ? val : alloc_value(NULL); +} + +static struct lil_list *substitute(struct lil *lil) +{ + struct lil_list *words = lil_alloc_list(); + + lil_skip_spaces(lil); + while (lil->head < lil->clen && !ateol(lil) && !lil->error) { + struct lil_value *w = alloc_value(NULL); + + do { + size_t head = lil->head; + struct lil_value *wp = next_word(lil); + + if (head == + lil->head) { /* something wrong, the parser can't proceed */ + lil_free_value(w); + lil_free_value(wp); + lil_free_list(words); + return NULL; + } + + lil_append_val(w, wp); + lil_free_value(wp); + } while (lil->head < lil->clen && + !eolchar(lil->code[lil->head]) && + !isspace(lil->code[lil->head]) && !lil->error); + lil_skip_spaces(lil); + + lil_list_append(words, w); + } + + return words; +} + +struct lil_list *lil_subst_to_list(struct lil *lil, struct lil_value *code) +{ + const char *save_code = lil->code; + size_t save_clen = lil->clen; + size_t save_head = lil->head; + int save_igeol = lil->ignoreeol; + struct lil_list *words; + + lil->code = lil_to_string(code); + lil->clen = code->l; + lil->head = 0; + lil->ignoreeol = 1; + + words = substitute(lil); + if (!words) + words = lil_alloc_list(); + + lil->code = save_code; + lil->clen = save_clen; + lil->head = save_head; + lil->ignoreeol = save_igeol; + return words; +} + +struct lil_value *lil_subst_to_value(struct lil *lil, struct lil_value *code) +{ + struct lil_list *words = lil_subst_to_list(lil, code); + struct lil_value *val; + + val = lil_list_to_value(words, 0); + lil_free_list(words); + return val; +} + +static struct lil_value *run_cmd(struct lil *lil, struct lil_func *cmd, + struct lil_list *words) +{ + struct lil_value *r; + + if (cmd->proc) { + size_t shead = lil->head; + + r = cmd->proc(lil, words->c - 1, words->v + 1); + if (lil->error == ERROR_FIXHEAD) { + lil->error = ERROR_DEFAULT; + lil->err_head = shead; + } + } else { + lil_push_env(lil); + lil->env->func = cmd; + + if (cmd->argnames->c == 1 && + !strcmp(lil_to_string(cmd->argnames->v[0]), "args")) { + struct lil_value *args = lil_list_to_value(words, 1); + + lil_set_var(lil, "args", args, LIL_SETVAR_LOCAL_NEW); + lil_free_value(args); + } else { + size_t i; + + for (i = 0; i < cmd->argnames->c; i++) { + struct lil_value *val; + + if (i < words->c - 1) + val = words->v[i + 1]; + else + val = lil->empty; + + lil_set_var(lil, + lil_to_string(cmd->argnames->v[i]), + val, LIL_SETVAR_LOCAL_NEW); + } + } + r = lil_parse_value(lil, cmd->code, 1); + + lil_pop_env(lil); + } + + return r; +} + +struct lil_value *lil_parse(struct lil *lil, const char *code, size_t codelen, + int funclevel) +{ + const char *save_code = lil->code; + size_t save_clen = lil->clen; + size_t save_head = lil->head; + struct lil_value *val = NULL; + struct lil_list *words = NULL; + + if (!save_code) + lil->rootcode = code; + lil->code = code; + lil->clen = codelen ? codelen : strlen(code); + lil->head = 0; + + lil_skip_spaces(lil); + lil->parse_depth++; +#ifdef LIL_ENABLE_RECLIMIT + if (lil->parse_depth > LIL_ENABLE_RECLIMIT) { + lil_set_error(lil, "Too many recursive calls"); + goto cleanup; + } +#endif + + if (lil->parse_depth == 1) + lil->error = 0; + + if (funclevel) + lil->env->breakrun = 0; + + while (lil->head < lil->clen && !lil->error) { + if (words) + lil_free_list(words); + + if (val) + lil_free_value(val); + val = NULL; + + words = substitute(lil); + if (!words || lil->error) + goto cleanup; + + if (words->c) { + struct lil_func *cmd = + lil_find_cmd(lil, lil_to_string(words->v[0])); + + if (!cmd) { + if (words->v[0]->l) { + char msg[64]; + + snprintf(msg, sizeof(msg), + "unknown function %s", + words->v[0]->d); + lil_set_error_at(lil, lil->head, msg); + goto cleanup; + } + } else { + val = run_cmd(lil, cmd, words); + } + + if (lil->env->breakrun) + goto cleanup; + } + + lil_skip_spaces(lil); + while (ateol(lil)) + lil->head++; + lil_skip_spaces(lil); + } + +cleanup: + if (lil->error && lil->callback[LIL_CALLBACK_ERROR] && + lil->parse_depth == 1) { + lil_error_callback_proc_t proc = + (lil_error_callback_proc_t) + lil->callback[LIL_CALLBACK_ERROR]; + + proc(lil, lil->err_head, lil->err_msg); + } + + if (words) + lil_free_list(words); + lil->code = save_code; + lil->clen = save_clen; + lil->head = save_head; + + if (funclevel && lil->env->retval_set) { + if (val) + lil_free_value(val); + val = lil->env->retval; + lil->env->retval = NULL; + lil->env->retval_set = 0; + lil->env->breakrun = 0; + } + + lil->parse_depth--; + return val ? val : alloc_value(NULL); +} + +struct lil_value *lil_parse_value(struct lil *lil, struct lil_value *val, + int funclevel) +{ + if (!val || !val->d || !val->l) + return alloc_value(NULL); + + return lil_parse(lil, val->d, val->l, funclevel); +} + +void lil_callback(struct lil *lil, int cb, lil_callback_proc_t proc) +{ + if (cb < 0 || cb > CALLBACKS) + return; + + lil->callback[cb] = proc; +} + +void lil_set_error(struct lil *lil, const char *msg) +{ + if (lil->error) + return; + + free(lil->err_msg); + lil->error = ERROR_FIXHEAD; + lil->err_head = 0; + lil->err_msg = strclone(msg ? msg : ""); +} + +void lil_set_error_at(struct lil *lil, size_t pos, const char *msg) +{ + if (lil->error) + return; + + free(lil->err_msg); + lil->error = ERROR_DEFAULT; + lil->err_head = pos; + lil->err_msg = strclone(msg ? msg : ""); +} + +int lil_error(struct lil *lil, const char **msg, size_t *pos) +{ + if (!lil->error) + return 0; + + *msg = lil->err_msg; + *pos = lil->err_head; + lil->error = ERROR_NOERROR; + + return 1; +} + +#define EERR_NO_ERROR 0 +#define EERR_SYNTAX_ERROR 1 +#define EERR_DIVISION_BY_ZERO 3 +#define EERR_INVALID_EXPRESSION 4 + +static void ee_expr(struct expreval *ee); + +static int ee_invalidpunct(int ch) +{ + return ispunct(ch) && ch != '!' && ch != '~' && ch != '(' && + ch != ')' && ch != '-' && ch != '+'; +} + +static void ee_skip_spaces(struct expreval *ee) +{ + while (ee->head < ee->len && isspace(ee->code[ee->head])) + ee->head++; +} + +static void ee_numeric_element(struct expreval *ee) +{ + ee_skip_spaces(ee); + ee->ival = 0; + while (ee->head < ee->len) { + if (!isdigit(ee->code[ee->head])) + break; + + ee->ival = ee->ival * 10 + (ee->code[ee->head] - '0'); + ee->head++; + } +} + +static void ee_element(struct expreval *ee) +{ + if (isdigit(ee->code[ee->head])) { + ee_numeric_element(ee); + return; + } + + /* + * for anything else that might creep in (usually from strings), we set + * the value to 1 so that strings evaluate as "true" when used in + * conditional expressions + */ + ee->ival = 1; + ee->error = EERR_INVALID_EXPRESSION; /* special flag, will be cleared */ +} + +static void ee_paren(struct expreval *ee) +{ + ee_skip_spaces(ee); + if (ee->code[ee->head] == '(') { + ee->head++; + ee_expr(ee); + ee_skip_spaces(ee); + + if (ee->code[ee->head] == ')') + ee->head++; + else + ee->error = EERR_SYNTAX_ERROR; + } else { + ee_element(ee); + } +} + +static void ee_unary(struct expreval *ee) +{ + ee_skip_spaces(ee); + if (ee->head < ee->len && !ee->error && + (ee->code[ee->head] == '-' || ee->code[ee->head] == '+' || + ee->code[ee->head] == '~' || ee->code[ee->head] == '!')) { + char op = ee->code[ee->head++]; + + ee_unary(ee); + if (ee->error) + return; + + switch (op) { + case '-': + ee->ival = -ee->ival; + break; + case '+': + /* ignore it, doesn't change a thing */ + break; + case '~': + ee->ival = ~ee->ival; + break; + case '!': + ee->ival = !ee->ival; + break; + } + } else { + ee_paren(ee); + } +} + +static void ee_muldiv(struct expreval *ee) +{ + ee_unary(ee); + if (ee->error) + return; + + ee_skip_spaces(ee); + while (ee->head < ee->len && !ee->error && + !ee_invalidpunct(ee->code[ee->head + 1]) && + (ee->code[ee->head] == '*' || ee->code[ee->head] == '/' || + ee->code[ee->head] == '\\' || ee->code[ee->head] == '%')) { + ssize_t oival = ee->ival; + + switch (ee->code[ee->head]) { + case '*': + ee->head++; + ee_unary(ee); + if (ee->error) + return; + + ee->ival = ee->ival * oival; + break; + case '%': + ee->head++; + ee_unary(ee); + if (ee->error) + return; + + if (ee->ival == 0) + ee->error = EERR_DIVISION_BY_ZERO; + else + ee->ival = oival % ee->ival; + break; + case '/': + ee->head++; + ee_unary(ee); + if (ee->error) + return; + + if (ee->ival == 0) + ee->error = EERR_DIVISION_BY_ZERO; + else + ee->ival = oival / ee->ival; + break; + case '\\': + ee->head++; + ee_unary(ee); + if (ee->error) + return; + + if (ee->ival == 0) + ee->error = EERR_DIVISION_BY_ZERO; + else + ee->ival = oival / ee->ival; + break; + } + + ee_skip_spaces(ee); + } +} + +static void ee_addsub(struct expreval *ee) +{ + ee_muldiv(ee); + ee_skip_spaces(ee); + + while (ee->head < ee->len && !ee->error && + !ee_invalidpunct(ee->code[ee->head + 1]) && + (ee->code[ee->head] == '+' || ee->code[ee->head] == '-')) { + ssize_t oival = ee->ival; + + switch (ee->code[ee->head]) { + case '+': + ee->head++; + ee_muldiv(ee); + if (ee->error) + return; + + ee->ival = ee->ival + oival; + break; + case '-': + ee->head++; + ee_muldiv(ee); + if (ee->error) + return; + + ee->ival = oival - ee->ival; + break; + } + + ee_skip_spaces(ee); + } +} + +static void ee_shift(struct expreval *ee) +{ + ee_addsub(ee); + ee_skip_spaces(ee); + + while (ee->head < ee->len && !ee->error && + ((ee->code[ee->head] == '<' && ee->code[ee->head + 1] == '<') || + (ee->code[ee->head] == '>' && ee->code[ee->head + 1] == '>'))) { + ssize_t oival = ee->ival; + + ee->head++; + switch (ee->code[ee->head]) { + case '<': + ee->head++; + ee_addsub(ee); + if (ee->error) + return; + + ee->ival = oival << ee->ival; + break; + case '>': + ee->head++; + ee_addsub(ee); + if (ee->error) + return; + + ee->ival = oival >> ee->ival; + break; + } + + ee_skip_spaces(ee); + } +} + +static void ee_compare(struct expreval *ee) +{ + ee_shift(ee); + ee_skip_spaces(ee); + + while (ee->head < ee->len && !ee->error && + ((ee->code[ee->head] == '<' && + !ee_invalidpunct(ee->code[ee->head + 1])) || + (ee->code[ee->head] == '>' && + !ee_invalidpunct(ee->code[ee->head + 1])) || + (ee->code[ee->head] == '<' && ee->code[ee->head + 1] == '=') || + (ee->code[ee->head] == '>' && ee->code[ee->head + 1] == '='))) { + ssize_t oival = ee->ival; + int op = 4; + + if (ee->code[ee->head] == '<' && + !ee_invalidpunct(ee->code[ee->head + 1])) + op = 1; + else if (ee->code[ee->head] == '>' && + !ee_invalidpunct(ee->code[ee->head + 1])) + op = 2; + else if (ee->code[ee->head] == '<' && + ee->code[ee->head + 1] == '=') + op = 3; + + ee->head += op > 2 ? 2 : 1; + + switch (op) { + case 1: + ee_shift(ee); + if (ee->error) + return; + + ee->ival = (oival < ee->ival) ? 1 : 0; + break; + case 2: + ee_shift(ee); + if (ee->error) + return; + + ee->ival = (oival > ee->ival) ? 1 : 0; + break; + case 3: + ee_shift(ee); + if (ee->error) + return; + + ee->ival = (oival <= ee->ival) ? 1 : 0; + break; + case 4: + ee_shift(ee); + if (ee->error) + return; + + ee->ival = (oival >= ee->ival) ? 1 : 0; + break; + } + + ee_skip_spaces(ee); + } +} + +static void ee_equals(struct expreval *ee) +{ + ee_compare(ee); + ee_skip_spaces(ee); + + while (ee->head < ee->len && !ee->error && + ((ee->code[ee->head] == '=' && ee->code[ee->head + 1] == '=') || + (ee->code[ee->head] == '!' && ee->code[ee->head + 1] == '='))) { + ssize_t oival = ee->ival; + int op = ee->code[ee->head] == '=' ? 1 : 2; + + ee->head += 2; + + switch (op) { + case 1: + ee_compare(ee); + if (ee->error) + return; + + ee->ival = (oival == ee->ival) ? 1 : 0; + break; + case 2: + ee_compare(ee); + if (ee->error) + return; + + ee->ival = (oival != ee->ival) ? 1 : 0; + break; + } + + ee_skip_spaces(ee); + } +} + +static void ee_bitand(struct expreval *ee) +{ + ee_equals(ee); + ee_skip_spaces(ee); + + while (ee->head < ee->len && !ee->error && + (ee->code[ee->head] == '&' && + !ee_invalidpunct(ee->code[ee->head + 1]))) { + ssize_t oival = ee->ival; + ee->head++; + + ee_equals(ee); + if (ee->error) + return; + + ee->ival = oival & ee->ival; + + ee_skip_spaces(ee); + } +} + +static void ee_bitor(struct expreval *ee) +{ + ee_bitand(ee); + ee_skip_spaces(ee); + + while (ee->head < ee->len && !ee->error && + (ee->code[ee->head] == '|' && + !ee_invalidpunct(ee->code[ee->head + 1]))) { + ssize_t oival = ee->ival; + + ee->head++; + + ee_bitand(ee); + if (ee->error) + return; + + ee->ival = oival | ee->ival; + + ee_skip_spaces(ee); + } +} + +static void ee_logand(struct expreval *ee) +{ + ee_bitor(ee); + ee_skip_spaces(ee); + + while (ee->head < ee->len && !ee->error && + (ee->code[ee->head] == '&' && ee->code[ee->head + 1] == '&')) { + ssize_t oival = ee->ival; + + ee->head += 2; + + ee_bitor(ee); + if (ee->error) + return; + + ee->ival = (oival && ee->ival) ? 1 : 0; + + ee_skip_spaces(ee); + } +} + +static void ee_logor(struct expreval *ee) +{ + ee_logand(ee); + ee_skip_spaces(ee); + + while (ee->head < ee->len && !ee->error && + (ee->code[ee->head] == '|' && ee->code[ee->head + 1] == '|')) { + ssize_t oival = ee->ival; + + ee->head += 2; + + ee_logand(ee); + if (ee->error) + return; + + ee->ival = (oival || ee->ival) ? 1 : 0; + + ee_skip_spaces(ee); + } +} + +static void ee_expr(struct expreval *ee) +{ + ee_logor(ee); + if (ee->error == EERR_INVALID_EXPRESSION) { + /* + * invalid expression doesn't really matter, it is only used to + * stop the expression parsing. + */ + ee->error = EERR_NO_ERROR; + ee->ival = 1; + } +} + +struct lil_value *lil_eval_expr(struct lil *lil, struct lil_value *code) +{ + struct expreval ee; + + code = lil_subst_to_value(lil, code); + if (lil->error) + return NULL; + + ee.code = lil_to_string(code); + if (!ee.code[0]) { + /* + * an empty expression equals to 0 so that it can be used as a + * false value in conditionals + */ + lil_free_value(code); + return lil_alloc_integer(0); + } + + ee.head = 0; + ee.len = code->l; + ee.ival = 0; + ee.error = 0; + + ee_expr(&ee); + lil_free_value(code); + if (ee.error) { + switch (ee.error) { + case EERR_DIVISION_BY_ZERO: + lil_set_error(lil, "division by zero in expression"); + break; + case EERR_SYNTAX_ERROR: + lil_set_error(lil, "expression syntax error"); + break; + } + return NULL; + } + return lil_alloc_integer(ee.ival); +} + +struct lil_value *lil_unused_name(struct lil *lil, const char *part) +{ + char *name = malloc(strlen(part) + 64); + struct lil_value *val; + size_t i; + + for (i = 0; i < (size_t)-1; i++) { + sprintf(name, "!!un!%s!%09u!nu!!", part, (unsigned int)i); + if (lil_find_cmd(lil, name)) + continue; + + if (lil_find_var(lil, lil->env, name)) + continue; + + val = lil_alloc_string(name); + free(name); + return val; + } + return NULL; +} + +struct lil_value *lil_arg(struct lil_value **argv, size_t index) +{ + return argv ? argv[index] : NULL; +} + +const char *lil_to_string(struct lil_value *val) +{ + return (val && val->l) ? val->d : ""; +} + +ssize_t lil_to_integer(struct lil_value *val) +{ + return simple_strtol(lil_to_string(val), NULL, 0); +} + +int lil_to_boolean(struct lil_value *val) +{ + const char *s = lil_to_string(val); + size_t i, dots = 0; + + if (!s[0]) + return 0; + + for (i = 0; s[i]; i++) { + if (s[i] != '0' && s[i] != '.') + return 1; + + if (s[i] == '.') { + if (dots) + return 1; + + dots = 1; + } + } + + return 0; +} + +struct lil_value *lil_alloc_string(const char *str) +{ + return alloc_value(str); +} + +struct lil_value *lil_alloc_string_len(const char *str, size_t len) +{ + return alloc_value_len(str, len); +} + +struct lil_value *lil_alloc_integer(ssize_t num) +{ + char buff[128]; + + sprintf(buff, "%zd", num); + return alloc_value(buff); +} + +void lil_free(struct lil *lil) +{ + size_t i; + + if (!lil) + return; + + free(lil->err_msg); + lil_free_value(lil->empty); + while (lil->env) { + struct lil_env *next = lil->env->parent; + + lil_free_env(lil->env); + lil->env = next; + } + + for (i = 0; i < lil->cmds; i++) { + if (lil->cmd[i]->argnames) + lil_free_list(lil->cmd[i]->argnames); + + lil_free_value(lil->cmd[i]->code); + free(lil->cmd[i]->name); + free(lil->cmd[i]); + } + + hm_destroy(&lil->cmdmap); + free(lil->cmd); + free(lil); +} + +static struct lil_value *fnc_reflect(struct lil *lil, size_t argc, + struct lil_value **argv) +{ + struct lil_func *func; + const char *type; + size_t i; + struct lil_value *r; + + if (!argc) + return NULL; + + type = lil_to_string(argv[0]); + if (!strcmp(type, "version")) + return lil_alloc_string(LIL_VERSION_STRING); + + if (!strcmp(type, "args")) { + if (argc < 2) + return NULL; + func = lil_find_cmd(lil, lil_to_string(argv[1])); + if (!func || !func->argnames) + return NULL; + return lil_list_to_value(func->argnames, 1); + } + + if (!strcmp(type, "body")) { + if (argc < 2) + return NULL; + + func = lil_find_cmd(lil, lil_to_string(argv[1])); + if (!func || func->proc) + return NULL; + + return lil_clone_value(func->code); + } + + if (!strcmp(type, "func-count")) + return lil_alloc_integer(lil->cmds); + + if (!strcmp(type, "funcs")) { + struct lil_list *funcs = lil_alloc_list(); + + for (i = 0; i < lil->cmds; i++) + lil_list_append(funcs, + lil_alloc_string(lil->cmd[i]->name)); + + r = lil_list_to_value(funcs, 1); + lil_free_list(funcs); + return r; + } + + if (!strcmp(type, "vars")) { + struct lil_list *vars = lil_alloc_list(); + struct lil_env *env = lil->env; + + while (env) { + for (i = 0; i < env->vars; i++) { + struct lil_value *var = + lil_alloc_string(env->var[i]->n); + + lil_list_append(vars, var); + } + env = env->parent; + } + + r = lil_list_to_value(vars, 1); + lil_free_list(vars); + return r; + } + + if (!strcmp(type, "globals")) { + struct lil_list *vars = lil_alloc_list(); + + for (i = 0; i < lil->rootenv->vars; i++) { + struct lil_value *var = + lil_alloc_string(lil->rootenv->var[i]->n); + + lil_list_append(vars, var); + } + + r = lil_list_to_value(vars, 1); + lil_free_list(vars); + return r; + } + + if (!strcmp(type, "has-func")) { + const char *target; + + if (argc == 1) + return NULL; + + target = lil_to_string(argv[1]); + return hm_has(&lil->cmdmap, target) ? lil_alloc_string("1") : + NULL; + } + + if (!strcmp(type, "has-var")) { + const char *target; + struct lil_env *env = lil->env; + + if (argc == 1) + return NULL; + + target = lil_to_string(argv[1]); + while (env) { + if (hm_has(&env->varmap, target)) + return lil_alloc_string("1"); + env = env->parent; + } + return NULL; + } + + if (!strcmp(type, "has-global")) { + const char *target; + + if (argc == 1) + return NULL; + + target = lil_to_string(argv[1]); + for (i = 0; i < lil->rootenv->vars; i++) + if (!strcmp(target, lil->rootenv->var[i]->n)) + return lil_alloc_string("1"); + return NULL; + } + + if (!strcmp(type, "error")) + return lil->err_msg ? lil_alloc_string(lil->err_msg) : NULL; + + if (!strcmp(type, "this")) { + struct lil_env *env = lil->env; + + while (env != lil->rootenv && !env->func) + env = env->parent; + + if (env == lil->rootenv) + return lil_alloc_string(lil->rootcode); + + return env->func ? env->func->code : NULL; + } + + if (!strcmp(type, "name")) { + struct lil_env *env = lil->env; + + while (env != lil->rootenv && !env->func) + env = env->parent; + + if (env == lil->rootenv) + return NULL; + + return env->func ? lil_alloc_string(env->func->name) : NULL; + } + + return NULL; +} + +static struct lil_value *fnc_func(struct lil *lil, size_t argc, + struct lil_value **argv) +{ + struct lil_value *name; + struct lil_func *cmd; + struct lil_list *fargs; + + if (argc < 1) + return NULL; + + if (argc >= 3) { + name = lil_clone_value(argv[0]); + fargs = lil_subst_to_list(lil, argv[1]); + cmd = add_func(lil, lil_to_string(argv[0])); + cmd->argnames = fargs; + cmd->code = lil_clone_value(argv[2]); + } else { + name = lil_unused_name(lil, "anonymous-function"); + if (argc < 2) { + struct lil_value *tmp = lil_alloc_string("args"); + + fargs = lil_subst_to_list(lil, tmp); + lil_free_value(tmp); + cmd = add_func(lil, lil_to_string(name)); + cmd->argnames = fargs; + cmd->code = lil_clone_value(argv[0]); + } else { + fargs = lil_subst_to_list(lil, argv[0]); + cmd = add_func(lil, lil_to_string(name)); + cmd->argnames = fargs; + cmd->code = lil_clone_value(argv[1]); + } + } + + return name; +} + +static struct lil_value *fnc_rename(struct lil *lil, size_t argc, + struct lil_value **argv) +{ + struct lil_value *r; + struct lil_func *func; + const char *oldname; + const char *newname; + + if (argc < 2) + return NULL; + + oldname = lil_to_string(argv[0]); + newname = lil_to_string(argv[1]); + func = lil_find_cmd(lil, oldname); + if (!func) { + char *msg = malloc(24 + strlen(oldname)); + + sprintf(msg, "unknown function '%s'", oldname); + lil_set_error_at(lil, lil->head, msg); + free(msg); + return NULL; + } + + r = lil_alloc_string(func->name); + if (newname[0]) { + hm_put(&lil->cmdmap, oldname, 0); + hm_put(&lil->cmdmap, newname, func); + free(func->name); + func->name = strclone(newname); + } else { + del_func(lil, func); + } + + return r; +} + +static struct lil_value *fnc_unusedname(struct lil *lil, size_t argc, + struct lil_value **argv) +{ + return lil_unused_name(lil, argc > 0 ? lil_to_string(argv[0]) : + "unusedname"); +} + +static struct lil_value *fnc_quote(struct lil *lil, size_t argc, + struct lil_value **argv) +{ + struct lil_value *r; + size_t i; + + if (argc < 1) + return NULL; + + r = alloc_value(NULL); + for (i = 0; i < argc; i++) { + if (i) + lil_append_char(r, ' '); + lil_append_val(r, argv[i]); + } + + return r; +} + +static struct lil_value *fnc_set(struct lil *lil, size_t argc, + struct lil_value **argv) +{ + size_t i = 0; + struct lil_var *var = NULL; + int access = LIL_SETVAR_LOCAL; + + if (!argc) + return NULL; + + if (!strcmp(lil_to_string(argv[0]), "global")) { + i = 1; + access = LIL_SETVAR_GLOBAL; + } + + while (i < argc) { + if (argc == i + 1) { + struct lil_value *val = + lil_get_var(lil, lil_to_string(argv[i])); + + return lil_clone_value(val); + } + + var = lil_set_var(lil, lil_to_string(argv[i]), argv[i + 1], + access); + i += 2; + } + + return var ? lil_clone_value(var->v) : NULL; +} + +static struct lil_value *fnc_local(struct lil *lil, size_t argc, + struct lil_value **argv) +{ + size_t i; + + for (i = 0; i < argc; i++) { + const char *varname = lil_to_string(argv[i]); + + if (!lil_find_local_var(lil, lil->env, varname)) + lil_set_var(lil, varname, lil->empty, + LIL_SETVAR_LOCAL_NEW); + } + + return NULL; +} + +static struct lil_value *fnc_eval(struct lil *lil, size_t argc, + struct lil_value **argv) +{ + if (argc == 1) + return lil_parse_value(lil, argv[0], 0); + + if (argc > 1) { + struct lil_value *val = alloc_value(NULL), *r; + size_t i; + + for (i = 0; i < argc; i++) { + if (i) + lil_append_char(val, ' '); + lil_append_val(val, argv[i]); + } + + r = lil_parse_value(lil, val, 0); + lil_free_value(val); + return r; + } + + return NULL; +} + +static struct lil_value *fnc_topeval(struct lil *lil, size_t argc, + struct lil_value **argv) +{ + struct lil_env *thisenv = lil->env; + struct lil_env *thisdownenv = lil->downenv; + struct lil_value *r; + + lil->env = lil->rootenv; + lil->downenv = thisenv; + + r = fnc_eval(lil, argc, argv); + lil->downenv = thisdownenv; + lil->env = thisenv; + return r; +} + +static struct lil_value *fnc_upeval(struct lil *lil, size_t argc, + struct lil_value **argv) +{ + struct lil_env *thisenv = lil->env; + struct lil_env *thisdownenv = lil->downenv; + struct lil_value *r; + + if (lil->rootenv == thisenv) + return fnc_eval(lil, argc, argv); + + lil->env = thisenv->parent; + lil->downenv = thisenv; + + r = fnc_eval(lil, argc, argv); + lil->env = thisenv; + lil->downenv = thisdownenv; + return r; +} + +static struct lil_value *fnc_downeval(struct lil *lil, size_t argc, + struct lil_value **argv) +{ + struct lil_value *r; + struct lil_env *upenv = lil->env; + struct lil_env *downenv = lil->downenv; + + if (!downenv) + return fnc_eval(lil, argc, argv); + + lil->downenv = NULL; + lil->env = downenv; + + r = fnc_eval(lil, argc, argv); + lil->downenv = downenv; + lil->env = upenv; + return r; +} + +static struct lil_value *fnc_count(struct lil *lil, size_t argc, + struct lil_value **argv) +{ + struct lil_list *list; + char buff[64]; + + if (!argc) + return alloc_value("0"); + + list = lil_subst_to_list(lil, argv[0]); + sprintf(buff, "%u", (unsigned int)list->c); + lil_free_list(list); + return alloc_value(buff); +} + +static struct lil_value *fnc_index(struct lil *lil, size_t argc, + struct lil_value **argv) +{ + struct lil_list *list; + size_t index; + struct lil_value *r; + + if (argc < 2) + return NULL; + + list = lil_subst_to_list(lil, argv[0]); + index = (size_t)lil_to_integer(argv[1]); + if (index >= list->c) + r = NULL; + else + r = lil_clone_value(list->v[index]); + lil_free_list(list); + return r; +} + +static struct lil_value *fnc_indexof(struct lil *lil, size_t argc, + struct lil_value **argv) +{ + struct lil_list *list; + size_t index; + struct lil_value *r = NULL; + + if (argc < 2) + return NULL; + + list = lil_subst_to_list(lil, argv[0]); + for (index = 0; index < list->c; index++) { + if (!strcmp(lil_to_string(list->v[index]), + lil_to_string(argv[1]))) { + r = lil_alloc_integer(index); + break; + } + } + + lil_free_list(list); + return r; +} + +static struct lil_value *fnc_append(struct lil *lil, size_t argc, + struct lil_value **argv) +{ + struct lil_list *list; + struct lil_value *r; + size_t i, base = 1; + int access = LIL_SETVAR_LOCAL; + const char *varname; + + if (argc < 2) + return NULL; + + varname = lil_to_string(argv[0]); + if (!strcmp(varname, "global")) { + if (argc < 3) + return NULL; + + varname = lil_to_string(argv[1]); + base = 2; + access = LIL_SETVAR_GLOBAL; + } + + list = lil_subst_to_list(lil, lil_get_var(lil, varname)); + for (i = base; i < argc; i++) + lil_list_append(list, lil_clone_value(argv[i])); + + r = lil_list_to_value(list, 1); + lil_free_list(list); + lil_set_var(lil, varname, r, access); + return r; +} + +static struct lil_value *fnc_slice(struct lil *lil, size_t argc, + struct lil_value **argv) +{ + struct lil_list *list, *slice; + size_t i; + ssize_t from, to; + struct lil_value *r; + + if (argc < 1) + return NULL; + if (argc < 2) + return lil_clone_value(argv[0]); + + from = lil_to_integer(argv[1]); + if (from < 0) + from = 0; + + list = lil_subst_to_list(lil, argv[0]); + to = argc > 2 ? lil_to_integer(argv[2]) : (ssize_t)list->c; + if (to > (ssize_t)list->c) + to = list->c; + else if (to < from) + to = from; + + slice = lil_alloc_list(); + for (i = (size_t)from; i < (size_t)to; i++) + lil_list_append(slice, lil_clone_value(list->v[i])); + lil_free_list(list); + + r = lil_list_to_value(slice, 1); + lil_free_list(slice); + return r; +} + +static struct lil_value *fnc_filter(struct lil *lil, size_t argc, + struct lil_value **argv) +{ + struct lil_list *list, *filtered; + size_t i; + struct lil_value *r; + const char *varname = "x"; + int base = 0; + + if (argc < 1) + return NULL; + + if (argc < 2) + return lil_clone_value(argv[0]); + + if (argc > 2) { + base = 1; + varname = lil_to_string(argv[0]); + } + + list = lil_subst_to_list(lil, argv[base]); + filtered = lil_alloc_list(); + for (i = 0; i < list->c && !lil->env->breakrun; i++) { + lil_set_var(lil, varname, list->v[i], LIL_SETVAR_LOCAL_ONLY); + r = lil_eval_expr(lil, argv[base + 1]); + if (lil_to_boolean(r)) + lil_list_append(filtered, lil_clone_value(list->v[i])); + lil_free_value(r); + } + lil_free_list(list); + + r = lil_list_to_value(filtered, 1); + lil_free_list(filtered); + return r; +} + +static struct lil_value *fnc_list(struct lil *lil, size_t argc, + struct lil_value **argv) +{ + struct lil_list *list = lil_alloc_list(); + struct lil_value *r; + size_t i; + + for (i = 0; i < argc; i++) + lil_list_append(list, lil_clone_value(argv[i])); + + r = lil_list_to_value(list, 1); + lil_free_list(list); + return r; +} + +static struct lil_value *fnc_subst(struct lil *lil, size_t argc, + struct lil_value **argv) +{ + if (argc < 1) + return NULL; + + return lil_subst_to_value(lil, argv[0]); +} + +static struct lil_value *fnc_concat(struct lil *lil, size_t argc, + struct lil_value **argv) +{ + struct lil_list *list; + struct lil_value *r, *tmp; + size_t i; + + if (argc < 1) + return NULL; + + r = lil_alloc_string(""); + for (i = 0; i < argc; i++) { + list = lil_subst_to_list(lil, argv[i]); + tmp = lil_list_to_value(list, 1); + lil_free_list(list); + lil_append_val(r, tmp); + lil_free_value(tmp); + } + return r; +} + +static struct lil_value *fnc_foreach(struct lil *lil, size_t argc, + struct lil_value **argv) +{ + struct lil_list *list, *rlist; + struct lil_value *r; + size_t i, listidx = 0, codeidx = 1; + const char *varname = "i"; + + if (argc < 2) + return NULL; + + if (argc >= 3) { + varname = lil_to_string(argv[0]); + listidx = 1; + codeidx = 2; + } + + rlist = lil_alloc_list(); + list = lil_subst_to_list(lil, argv[listidx]); + for (i = 0; i < list->c; i++) { + struct lil_value *rv; + + lil_set_var(lil, varname, list->v[i], LIL_SETVAR_LOCAL_ONLY); + rv = lil_parse_value(lil, argv[codeidx], 0); + if (rv->l) + lil_list_append(rlist, rv); + else + lil_free_value(rv); + + if (lil->env->breakrun || lil->error) + break; + } + + r = lil_list_to_value(rlist, 1); + lil_free_list(list); + lil_free_list(rlist); + return r; +} + +static struct lil_value *fnc_return(struct lil *lil, size_t argc, + struct lil_value **argv) +{ + lil->env->breakrun = 1; + lil_free_value(lil->env->retval); + lil->env->retval = argc < 1 ? NULL : lil_clone_value(argv[0]); + lil->env->retval_set = 1; + return argc < 1 ? NULL : lil_clone_value(argv[0]); +} + +static struct lil_value *fnc_result(struct lil *lil, size_t argc, + struct lil_value **argv) +{ + if (argc > 0) { + lil_free_value(lil->env->retval); + lil->env->retval = lil_clone_value(argv[0]); + lil->env->retval_set = 1; + } + + return lil->env->retval_set ? lil_clone_value(lil->env->retval) : NULL; +} + +static struct lil_value *fnc_expr(struct lil *lil, size_t argc, + struct lil_value **argv) +{ + if (argc == 1) + return lil_eval_expr(lil, argv[0]); + + if (argc > 1) { + struct lil_value *val = alloc_value(NULL), *r; + size_t i; + + for (i = 0; i < argc; i++) { + if (i) + lil_append_char(val, ' '); + lil_append_val(val, argv[i]); + } + + r = lil_eval_expr(lil, val); + lil_free_value(val); + return r; + } + + return NULL; +} + +static struct lil_value *real_inc(struct lil *lil, const char *varname, + ssize_t v) +{ + struct lil_value *pv = lil_get_var(lil, varname); + + pv = lil_alloc_integer(lil_to_integer(pv) + v); + lil_set_var(lil, varname, pv, LIL_SETVAR_LOCAL); + return pv; +} + +static struct lil_value *fnc_inc(struct lil *lil, size_t argc, + struct lil_value **argv) +{ + if (argc < 1) + return NULL; + + return real_inc(lil, lil_to_string(argv[0]), + argc > 1 ? lil_to_integer(argv[1]) : 1); +} + +static struct lil_value *fnc_dec(struct lil *lil, size_t argc, + struct lil_value **argv) +{ + if (argc < 1) + return NULL; + + return real_inc(lil, lil_to_string(argv[0]), + -(argc > 1 ? lil_to_integer(argv[1]) : 1)); +} + +static struct lil_value *fnc_if(struct lil *lil, size_t argc, + struct lil_value **argv) +{ + struct lil_value *val, *r = NULL; + int base = 0, not = 0, v; + + if (argc < 1) + return NULL; + + if (!strcmp(lil_to_string(argv[0]), "not")) + base = not = 1; + + if (argc < (size_t)base + 2) + return NULL; + + val = lil_eval_expr(lil, argv[base]); + if (!val || lil->error) + return NULL; + + v = lil_to_boolean(val); + if (not) + v = !v; + + if (v) + r = lil_parse_value(lil, argv[base + 1], 0); + else if (argc > (size_t)base + 2) + r = lil_parse_value(lil, argv[base + 2], 0); + + lil_free_value(val); + return r; +} + +static struct lil_value *fnc_while(struct lil *lil, size_t argc, + struct lil_value **argv) +{ + struct lil_value *val, *r = NULL; + int base = 0, not = 0, v; + + if (argc < 1) + return NULL; + + if (!strcmp(lil_to_string(argv[0]), "not")) + base = not = 1; + + if (argc < (size_t)base + 2) + return NULL; + + while (!lil->error && !lil->env->breakrun) { + val = lil_eval_expr(lil, argv[base]); + if (!val || lil->error) + return NULL; + + v = lil_to_boolean(val); + if (not) + v = !v; + + if (!v) { + lil_free_value(val); + break; + } + + if (r) + lil_free_value(r); + r = lil_parse_value(lil, argv[base + 1], 0); + lil_free_value(val); + } + + return r; +} + +static struct lil_value *fnc_for(struct lil *lil, size_t argc, + struct lil_value **argv) +{ + struct lil_value *val, *r = NULL; + + if (argc < 4) + return NULL; + + lil_free_value(lil_parse_value(lil, argv[0], 0)); + while (!lil->error && !lil->env->breakrun) { + val = lil_eval_expr(lil, argv[1]); + if (!val || lil->error) + return NULL; + + if (!lil_to_boolean(val)) { + lil_free_value(val); + break; + } + + if (r) + lil_free_value(r); + r = lil_parse_value(lil, argv[3], 0); + lil_free_value(val); + lil_free_value(lil_parse_value(lil, argv[2], 0)); + } + + return r; +} + +static struct lil_value *fnc_char(struct lil *lil, size_t argc, + struct lil_value **argv) +{ + char s[2]; + + if (!argc) + return NULL; + + s[0] = (char)lil_to_integer(argv[0]); + s[1] = 0; + return lil_alloc_string(s); +} + +static struct lil_value *fnc_charat(struct lil *lil, size_t argc, + struct lil_value **argv) +{ + size_t index; + char chstr[2]; + const char *str; + + if (argc < 2) + return NULL; + + str = lil_to_string(argv[0]); + index = (size_t)lil_to_integer(argv[1]); + if (index >= strlen(str)) + return NULL; + + chstr[0] = str[index]; + chstr[1] = 0; + return lil_alloc_string(chstr); +} + +static struct lil_value *fnc_codeat(struct lil *lil, size_t argc, + struct lil_value **argv) +{ + size_t index; + const char *str; + + if (argc < 2) + return NULL; + + str = lil_to_string(argv[0]); + index = (size_t)lil_to_integer(argv[1]); + if (index >= strlen(str)) + return NULL; + + return lil_alloc_integer(str[index]); +} + +static struct lil_value *fnc_substr(struct lil *lil, size_t argc, + struct lil_value **argv) +{ + const char *str; + struct lil_value *r; + size_t start, end, i, slen; + + if (argc < 2) + return NULL; + + str = lil_to_string(argv[0]); + if (!str[0]) + return NULL; + + slen = strlen(str); + start = simple_strtol(lil_to_string(argv[1]), NULL, 0); + end = argc > 2 ? simple_strtol(lil_to_string(argv[2]), NULL, 0) : slen; + if (end > slen) + end = slen; + + if (start >= end) + return NULL; + + r = lil_alloc_string(""); + for (i = start; i < end; i++) + lil_append_char(r, str[i]); + return r; +} + +static struct lil_value *fnc_strpos(struct lil *lil, size_t argc, + struct lil_value **argv) +{ + const char *hay; + const char *str; + size_t min = 0; + + if (argc < 2) + return lil_alloc_integer(-1); + + hay = lil_to_string(argv[0]); + if (argc > 2) { + min = simple_strtol(lil_to_string(argv[2]), NULL, 0); + if (min >= strlen(hay)) + return lil_alloc_integer(-1); + } + + str = strstr(hay + min, lil_to_string(argv[1])); + if (!str) + return lil_alloc_integer(-1); + + return lil_alloc_integer(str - hay); +} + +static struct lil_value *fnc_length(struct lil *lil, size_t argc, + struct lil_value **argv) +{ + size_t i, total = 0; + + for (i = 0; i < argc; i++) { + if (i) + total++; + total += strlen(lil_to_string(argv[i])); + } + + return lil_alloc_integer((ssize_t)total); +} + +static struct lil_value *real_trim(const char *str, const char *chars, int left, + int right) +{ + int base = 0; + struct lil_value *r = NULL; + + if (left) { + while (str[base] && strchr(chars, str[base])) + base++; + if (!right) + r = lil_alloc_string(str[base] ? str + base : NULL); + } + + if (right) { + size_t len; + char *s; + + s = strclone(str + base); + len = strlen(s); + while (len && strchr(chars, s[len - 1])) + len--; + + s[len] = 0; + r = lil_alloc_string(s); + free(s); + } + + return r; +} + +static struct lil_value *fnc_trim(struct lil *lil, size_t argc, + struct lil_value **argv) +{ + const char *chars; + + if (!argc) + return NULL; + + if (argc < 2) + chars = " \f\n\r\t\v"; + else + chars = lil_to_string(argv[1]); + + return real_trim(lil_to_string(argv[0]), chars, 1, 1); +} + +static struct lil_value *fnc_ltrim(struct lil *lil, size_t argc, + struct lil_value **argv) +{ + const char *chars; + + if (!argc) + return NULL; + + if (argc < 2) + chars = " \f\n\r\t\v"; + else + chars = lil_to_string(argv[1]); + + return real_trim(lil_to_string(argv[0]), chars, 1, 0); +} + +static struct lil_value *fnc_rtrim(struct lil *lil, size_t argc, + struct lil_value **argv) +{ + const char *chars; + + if (!argc) + return NULL; + + if (argc < 2) + chars = " \f\n\r\t\v"; + else + chars = lil_to_string(argv[1]); + + return real_trim(lil_to_string(argv[0]), chars, 0, 1); +} + +static struct lil_value *fnc_strcmp(struct lil *lil, size_t argc, + struct lil_value **argv) +{ + if (argc < 2) + return NULL; + + return lil_alloc_integer(strcmp(lil_to_string(argv[0]), + lil_to_string(argv[1]))); +} + +static struct lil_value *fnc_streq(struct lil *lil, size_t argc, + struct lil_value **argv) +{ + if (argc < 2) + return NULL; + + return lil_alloc_integer(strcmp(lil_to_string(argv[0]), + lil_to_string(argv[1])) ? 0 : 1); +} + +static struct lil_value *fnc_repstr(struct lil *lil, size_t argc, + struct lil_value **argv) +{ + const char *from; + const char *to; + char *src; + const char *sub; + size_t idx; + size_t fromlen; + size_t tolen; + size_t srclen; + struct lil_value *r; + + if (argc < 1) + return NULL; + + if (argc < 3) + return lil_clone_value(argv[0]); + + from = lil_to_string(argv[1]); + to = lil_to_string(argv[2]); + if (!from[0]) + return NULL; + + src = strclone(lil_to_string(argv[0])); + srclen = strlen(src); + fromlen = strlen(from); + tolen = strlen(to); + while ((sub = strstr(src, from))) { + char *newsrc = malloc(srclen - fromlen + tolen + 1); + + idx = sub - src; + if (idx) + memcpy(newsrc, src, idx); + + memcpy(newsrc + idx, to, tolen); + memcpy(newsrc + idx + tolen, src + idx + fromlen, + srclen - idx - fromlen); + srclen = srclen - fromlen + tolen; + free(src); + src = newsrc; + src[srclen] = 0; + } + + r = lil_alloc_string(src); + free(src); + return r; +} + +static struct lil_value *fnc_split(struct lil *lil, size_t argc, + struct lil_value **argv) +{ + struct lil_list *list; + const char *sep = " "; + size_t i; + struct lil_value *val; + const char *str; + + if (argc == 0) + return NULL; + if (argc > 1) { + sep = lil_to_string(argv[1]); + if (!sep || !sep[0]) + return lil_clone_value(argv[0]); + } + + val = lil_alloc_string(""); + str = lil_to_string(argv[0]); + list = lil_alloc_list(); + for (i = 0; str[i]; i++) { + if (strchr(sep, str[i])) { + lil_list_append(list, val); + val = lil_alloc_string(""); + } else { + lil_append_char(val, str[i]); + } + } + + lil_list_append(list, val); + val = lil_list_to_value(list, 1); + lil_free_list(list); + return val; +} + +static struct lil_value *fnc_try(struct lil *lil, size_t argc, + struct lil_value **argv) +{ + struct lil_value *r; + + if (argc < 1) + return NULL; + + if (lil->error) + return NULL; + + r = lil_parse_value(lil, argv[0], 0); + if (lil->error) { + lil->error = ERROR_NOERROR; + lil_free_value(r); + + if (argc > 1) + r = lil_parse_value(lil, argv[1], 0); + else + r = 0; + } + return r; +} + +static struct lil_value *fnc_error(struct lil *lil, size_t argc, + struct lil_value **argv) +{ + lil_set_error(lil, argc > 0 ? lil_to_string(argv[0]) : NULL); + return NULL; +} + +static struct lil_value *fnc_lmap(struct lil *lil, size_t argc, + struct lil_value **argv) +{ + struct lil_list *list; + size_t i; + + if (argc < 2) + return NULL; + + list = lil_subst_to_list(lil, argv[0]); + for (i = 1; i < argc; i++) + lil_set_var(lil, lil_to_string(argv[i]), + lil_list_get(list, i - 1), LIL_SETVAR_LOCAL); + lil_free_list(list); + return NULL; +} + +static void register_stdcmds(struct lil *lil) +{ + lil_register(lil, "append", fnc_append); + lil_register(lil, "char", fnc_char); + lil_register(lil, "charat", fnc_charat); + lil_register(lil, "codeat", fnc_codeat); + lil_register(lil, "concat", fnc_concat); + lil_register(lil, "count", fnc_count); + lil_register(lil, "dec", fnc_dec); + lil_register(lil, "downeval", fnc_downeval); + lil_register(lil, "error", fnc_error); + lil_register(lil, "eval", fnc_eval); + lil_register(lil, "expr", fnc_expr); + lil_register(lil, "filter", fnc_filter); + lil_register(lil, "for", fnc_for); + lil_register(lil, "foreach", fnc_foreach); + lil_register(lil, "func", fnc_func); + lil_register(lil, "if", fnc_if); + lil_register(lil, "inc", fnc_inc); + lil_register(lil, "index", fnc_index); + lil_register(lil, "indexof", fnc_indexof); + lil_register(lil, "length", fnc_length); + lil_register(lil, "list", fnc_list); + lil_register(lil, "lmap", fnc_lmap); + lil_register(lil, "local", fnc_local); + lil_register(lil, "ltrim", fnc_ltrim); + lil_register(lil, "quote", fnc_quote); + lil_register(lil, "reflect", fnc_reflect); + lil_register(lil, "rename", fnc_rename); + lil_register(lil, "repstr", fnc_repstr); + lil_register(lil, "result", fnc_result); + lil_register(lil, "return", fnc_return); + lil_register(lil, "rtrim", fnc_rtrim); + lil_register(lil, "set", fnc_set); + lil_register(lil, "slice", fnc_slice); + lil_register(lil, "split", fnc_split); + lil_register(lil, "strcmp", fnc_strcmp); + lil_register(lil, "streq", fnc_streq); + lil_register(lil, "strpos", fnc_strpos); + lil_register(lil, "subst", fnc_subst); + lil_register(lil, "substr", fnc_substr); + lil_register(lil, "topeval", fnc_topeval); + lil_register(lil, "trim", fnc_trim); + lil_register(lil, "try", fnc_try); + lil_register(lil, "unusedname", fnc_unusedname); + lil_register(lil, "upeval", fnc_upeval); + lil_register(lil, "while", fnc_while); +} diff --git a/include/cli_lil.h b/include/cli_lil.h new file mode 100644 index 0000000000..c72977ea5c --- /dev/null +++ b/include/cli_lil.h @@ -0,0 +1,111 @@ +/* SPDX-License-Identifier: GPL-2.0+ AND Zlib */ +/* + * LIL - Little Interpreted Language + * Copyright (C) 2021 Sean Anderson + * Copyright (C) 2010-2013 Kostas Michalopoulos + * + * This file originated from the LIL project, licensed under Zlib. All + * modifications are licensed under GPL-2.0+ + */ + +#ifndef __LIL_H_INCLUDED__ +#define __LIL_H_INCLUDED__ + +#define LIL_VERSION_STRING "0.1" + +#define LIL_SETVAR_GLOBAL 0 +#define LIL_SETVAR_LOCAL 1 +#define LIL_SETVAR_LOCAL_NEW 2 +#define LIL_SETVAR_LOCAL_ONLY 3 + +#define LIL_CALLBACK_EXIT 0 +#define LIL_CALLBACK_WRITE 1 +#define LIL_CALLBACK_READ 2 +#define LIL_CALLBACK_STORE 3 +#define LIL_CALLBACK_SOURCE 4 +#define LIL_CALLBACK_ERROR 5 +#define LIL_CALLBACK_SETVAR 6 +#define LIL_CALLBACK_GETVAR 7 + +#include +#include + +struct lil_value; +struct lil_func; +struct lil_var; +struct lil_env; +struct lil_list; +struct lil; +typedef struct lil_value *(*lil_func_proc_t)(struct lil *lil, size_t argc, + struct lil_value **argv); +typedef void (*lil_exit_callback_proc_t)(struct lil *lil, + struct lil_value *arg); +typedef void (*lil_write_callback_proc_t)(struct lil *lil, const char *msg); +typedef char *(*lil_read_callback_proc_t)(struct lil *lil, const char *name); +typedef char *(*lil_source_callback_proc_t)(struct lil *lil, const char *name); +typedef void (*lil_store_callback_proc_t)(struct lil *lil, const char *name, + const char *data); +typedef void (*lil_error_callback_proc_t)(struct lil *lil, size_t pos, + const char *msg); +typedef int (*lil_setvar_callback_proc_t)(struct lil *lil, const char *name, + struct lil_value **value); +typedef int (*lil_getvar_callback_proc_t)(struct lil *lil, const char *name, + struct lil_value **value); +typedef void (*lil_callback_proc_t)(void); + +struct lil *lil_new(void); +void lil_free(struct lil *lil); + +int lil_register(struct lil *lil, const char *name, lil_func_proc_t proc); + +struct lil_value *lil_parse(struct lil *lil, const char *code, size_t codelen, + int funclevel); +struct lil_value *lil_parse_value(struct lil *lil, struct lil_value *val, + int funclevel); + +void lil_callback(struct lil *lil, int cb, lil_callback_proc_t proc); + +void lil_set_error(struct lil *lil, const char *msg); +void lil_set_error_at(struct lil *lil, size_t pos, const char *msg); +int lil_error(struct lil *lil, const char **msg, size_t *pos); + +const char *lil_to_string(struct lil_value *val); +ssize_t lil_to_integer(struct lil_value *val); +int lil_to_boolean(struct lil_value *val); + +struct lil_value *lil_alloc_string(const char *str); +struct lil_value *lil_alloc_integer(ssize_t num); +void lil_free_value(struct lil_value *val); + +struct lil_value *lil_clone_value(struct lil_value *src); +int lil_append_char(struct lil_value *val, char ch); +int lil_append_string(struct lil_value *val, const char *s); +int lil_append_val(struct lil_value *val, struct lil_value *v); + +struct lil_list *lil_alloc_list(void); +void lil_free_list(struct lil_list *list); +void lil_list_append(struct lil_list *list, struct lil_value *val); +size_t lil_list_size(struct lil_list *list); +struct lil_value *lil_list_get(struct lil_list *list, size_t index); +struct lil_value *lil_list_to_value(struct lil_list *list, int do_escape); + +struct lil_list *lil_subst_to_list(struct lil *lil, struct lil_value *code); +struct lil_value *lil_subst_to_value(struct lil *lil, struct lil_value *code); + +struct lil_env *lil_alloc_env(struct lil_env *parent); +void lil_free_env(struct lil_env *env); +struct lil_env *lil_push_env(struct lil *lil); +void lil_pop_env(struct lil *lil); + +struct lil_var *lil_set_var(struct lil *lil, const char *name, + struct lil_value *val, int local); +struct lil_value *lil_get_var(struct lil *lil, const char *name); +struct lil_value *lil_get_var_or(struct lil *lil, const char *name, + struct lil_value *defvalue); + +struct lil_value *lil_eval_expr(struct lil *lil, struct lil_value *code); +struct lil_value *lil_unused_name(struct lil *lil, const char *part); + +struct lil_value *lil_arg(struct lil_value **argv, size_t index); + +#endif From patchwork Thu Jul 1 06:15:46 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sean Anderson X-Patchwork-Id: 1499275 X-Patchwork-Delegate: trini@ti.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=lists.denx.de (client-ip=85.214.62.61; helo=phobos.denx.de; envelope-from=u-boot-bounces@lists.denx.de; receiver=) Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20161025 header.b=hYHRUEkb; dkim-atps=neutral Received: from phobos.denx.de (phobos.denx.de [85.214.62.61]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 4GFnzX3jB9z9sWX for ; Thu, 1 Jul 2021 16:16:40 +1000 (AEST) Received: from h2850616.stratoserver.net (localhost [IPv6:::1]) by phobos.denx.de (Postfix) with ESMTP id 52B578323D; Thu, 1 Jul 2021 08:16:26 +0200 (CEST) Authentication-Results: phobos.denx.de; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=u-boot-bounces@lists.denx.de Authentication-Results: phobos.denx.de; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="hYHRUEkb"; dkim-atps=neutral Received: by phobos.denx.de (Postfix, from userid 109) id 5F817831EA; Thu, 1 Jul 2021 08:16:24 +0200 (CEST) X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on phobos.denx.de X-Spam-Level: X-Spam-Status: No, score=-2.0 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,FREEMAIL_FROM,SPF_HELO_NONE autolearn=ham autolearn_force=no version=3.4.2 Received: from mail-qk1-x72b.google.com (mail-qk1-x72b.google.com [IPv6:2607:f8b0:4864:20::72b]) (using TLSv1.3 with cipher TLS_AES_128_GCM_SHA256 (128/128 bits)) (No client certificate requested) by phobos.denx.de (Postfix) with ESMTPS id 42026831EA for ; Thu, 1 Jul 2021 08:16:18 +0200 (CEST) Authentication-Results: phobos.denx.de; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=seanga2@gmail.com Received: by mail-qk1-x72b.google.com with SMTP id j184so5002575qkd.6 for ; Wed, 30 Jun 2021 23:16:18 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=y69m50NIties87+pJv5ckXaLI1xTsNsDmfkBDE+KLww=; b=hYHRUEkb4ZVTf+paKLMApJy5Qu2iJpMoVOrgGseWfFYQHdBUcTKLX6HRiKYVPQNG+V MTI6PwqJemkrb5hQWtYGQ1ArEJ22b+IfniSHbsAnbktPZqpahV1tIDycvdKELEj8i5FA LWlhIITXv+xDeW0rvPRzi4nwWIXn55tSaJ5P1FGxwDFSs2nVIb4pSPhA2gpLJw9GFIY+ 1Rc6CfowqVHfPmLURSGuv0YI0nfh7xPbchL/8gfpbAAHXu+SF1PtCqU0GURQ5FEfYOw+ 92rcgMOEDIwZayllGxUB/ZlfpAt6Fj+xaUakl8U3CrOwAhKhZtjgtcl+ZVQigaqEV+p+ Ko5A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=y69m50NIties87+pJv5ckXaLI1xTsNsDmfkBDE+KLww=; b=ez5825IK7iD/ygejwGgzuIGKgTf/jiA0py6ZM/Fo+oFAxzvqLoHdAQ2T9O9cCVZkW4 /eRzLjDTUyrvQ3di4T+HGuzBrz3BG1gKisdF4W2LdLlJJ9UJoxvD4E+kS7dJZ2pgor0/ GDkS4OcnBn7Cfvl6dOEWxHI57KFBF2A0PA0OgOcwgbT+OGHk0/1YZw0OC6hNapBUM3kD F8I7b5Q+ZmTRylstLtf9sl+bf6tuRfZIkR2B5x4vYj5+vP70EJFTrUh8tsTOAMtjGZIB n4uHTMXral9JkhWxCI2VGrJWzhifpKp853Q3DYLwSpW0AMiRyJWZrUsXMF1UmV/06O0H EEew== X-Gm-Message-State: AOAM533oPYLJdLznlzxS/2AUypm53lFP89je7rIhc6VAhO4j7V0bLsNe vp4OOmVHDiu1AHOep2ZYSxMwWO06p2E= X-Google-Smtp-Source: ABdhPJyjitTtXr+Bi9uQEfYIoG3QDngPIkaceY1CdCrEq8d9dWcgnFuNCwN/31iLnTNypL0fTgBTig== X-Received: by 2002:a05:620a:12da:: with SMTP id e26mr7275825qkl.333.1625120176951; Wed, 30 Jun 2021 23:16:16 -0700 (PDT) Received: from godwin.fios-router.home (pool-74-96-87-9.washdc.fios.verizon.net. [74.96.87.9]) by smtp.gmail.com with ESMTPSA id g21sm1684673qts.90.2021.06.30.23.16.16 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 30 Jun 2021 23:16:16 -0700 (PDT) From: Sean Anderson To: u-boot@lists.denx.de, Tom Rini Cc: =?utf-8?q?Marek_Beh=C3=BAn?= , Wolfgang Denk , Simon Glass , Roland Gaudig , Heinrich Schuchardt , Kostas Michalopoulos , Sean Anderson Subject: [RFC PATCH 03/28] cli: lil: Replace strclone with strdup Date: Thu, 1 Jul 2021 02:15:46 -0400 Message-Id: <20210701061611.957918-4-seanga2@gmail.com> X-Mailer: git-send-email 2.32.0 In-Reply-To: <20210701061611.957918-1-seanga2@gmail.com> References: <20210701061611.957918-1-seanga2@gmail.com> MIME-Version: 1.0 X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.34 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" X-Virus-Scanned: clamav-milter 0.103.2 at phobos.denx.de X-Virus-Status: Clean Apparently strdup is not portable, so LIL used its own. Use strdup. Signed-off-by: Sean Anderson --- common/cli_lil.c | 27 ++++++++------------------- 1 file changed, 8 insertions(+), 19 deletions(-) diff --git a/common/cli_lil.c b/common/cli_lil.c index c8c6986fe1..294d79e86e 100644 --- a/common/cli_lil.c +++ b/common/cli_lil.c @@ -123,17 +123,6 @@ static struct lil_env **envpool; static size_t envpoolsize, envpoolcap; #endif -static char *strclone(const char *s) -{ - size_t len = strlen(s) + 1; - char *ns = malloc(len); - - if (!ns) - return NULL; - memcpy(ns, s, len); - return ns; -} - static unsigned long hm_hash(const char *key) { unsigned long hash = 5381; @@ -173,7 +162,7 @@ static void hm_put(struct hashmap *hm, const char *key, void *value) } cell->e = realloc(cell->e, sizeof(struct hashentry) * (cell->c + 1)); - cell->e[cell->c].k = strclone(key); + cell->e[cell->c].k = strdup(key); cell->e[cell->c].v = value; cell->c++; } @@ -606,7 +595,7 @@ static struct lil_func *add_func(struct lil *lil, const char *name) } cmd = calloc(1, sizeof(struct lil_func)); - cmd->name = strclone(name); + cmd->name = strdup(name); ncmd = realloc(lil->cmd, sizeof(struct lil_func *) * (lil->cmds + 1)); if (!ncmd) { @@ -705,7 +694,7 @@ struct lil_var *lil_set_var(struct lil *lil, const char *name, env->var = nvar; nvar[env->vars] = calloc(1, sizeof(struct lil_var)); - nvar[env->vars]->n = strclone(name); + nvar[env->vars]->n = strdup(name); nvar[env->vars]->env = env; nvar[env->vars]->v = freeval ? val : lil_clone_value(val); hm_put(&env->varmap, name, nvar[env->vars]); @@ -1207,7 +1196,7 @@ void lil_set_error(struct lil *lil, const char *msg) free(lil->err_msg); lil->error = ERROR_FIXHEAD; lil->err_head = 0; - lil->err_msg = strclone(msg ? msg : ""); + lil->err_msg = strdup(msg ? msg : ""); } void lil_set_error_at(struct lil *lil, size_t pos, const char *msg) @@ -1218,7 +1207,7 @@ void lil_set_error_at(struct lil *lil, size_t pos, const char *msg) free(lil->err_msg); lil->error = ERROR_DEFAULT; lil->err_head = pos; - lil->err_msg = strclone(msg ? msg : ""); + lil->err_msg = strdup(msg ? msg : ""); } int lil_error(struct lil *lil, const char **msg, size_t *pos) @@ -2016,7 +2005,7 @@ static struct lil_value *fnc_rename(struct lil *lil, size_t argc, hm_put(&lil->cmdmap, oldname, 0); hm_put(&lil->cmdmap, newname, func); free(func->name); - func->name = strclone(newname); + func->name = strdup(newname); } else { del_func(lil, func); } @@ -2728,7 +2717,7 @@ static struct lil_value *real_trim(const char *str, const char *chars, int left, size_t len; char *s; - s = strclone(str + base); + s = strdup(str + base); len = strlen(s); while (len && strchr(chars, s[len - 1])) len--; @@ -2833,7 +2822,7 @@ static struct lil_value *fnc_repstr(struct lil *lil, size_t argc, if (!from[0]) return NULL; - src = strclone(lil_to_string(argv[0])); + src = strdup(lil_to_string(argv[0])); srclen = strlen(src); fromlen = strlen(from); tolen = strlen(to); From patchwork Thu Jul 1 06:15:47 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sean Anderson X-Patchwork-Id: 1499277 X-Patchwork-Delegate: trini@ti.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=lists.denx.de (client-ip=2a01:238:438b:c500:173d:9f52:ddab:ee01; helo=phobos.denx.de; envelope-from=u-boot-bounces@lists.denx.de; receiver=) Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20161025 header.b=kTKKDpva; dkim-atps=neutral Received: from phobos.denx.de (phobos.denx.de [IPv6:2a01:238:438b:c500:173d:9f52:ddab:ee01]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 4GFnzz2Cqrz9sWX for ; Thu, 1 Jul 2021 16:17:03 +1000 (AEST) Received: from h2850616.stratoserver.net (localhost [IPv6:::1]) by phobos.denx.de (Postfix) with ESMTP id 42E8283275; Thu, 1 Jul 2021 08:16:31 +0200 (CEST) Authentication-Results: phobos.denx.de; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=u-boot-bounces@lists.denx.de Authentication-Results: phobos.denx.de; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="kTKKDpva"; dkim-atps=neutral Received: by phobos.denx.de (Postfix, from userid 109) id DEB668326F; Thu, 1 Jul 2021 08:16:28 +0200 (CEST) X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on phobos.denx.de X-Spam-Level: X-Spam-Status: No, score=-2.0 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,FREEMAIL_FROM,SPF_HELO_NONE autolearn=ham autolearn_force=no version=3.4.2 Received: from mail-qk1-x732.google.com (mail-qk1-x732.google.com [IPv6:2607:f8b0:4864:20::732]) (using TLSv1.3 with cipher TLS_AES_128_GCM_SHA256 (128/128 bits)) (No client certificate requested) by phobos.denx.de (Postfix) with ESMTPS id 0DF208320A for ; Thu, 1 Jul 2021 08:16:19 +0200 (CEST) Authentication-Results: phobos.denx.de; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=seanga2@gmail.com Received: by mail-qk1-x732.google.com with SMTP id y29so4956079qky.12 for ; Wed, 30 Jun 2021 23:16:18 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=90YfR0LP5TFiDKzfZwBM/49yhqrZT3qiPN/l7r+cWeo=; b=kTKKDpvabZoBB+/msDe1YPgs/sXtB840DDVgtff/lO8qC7cC0qRsRD5lc1K5NMzLmp DxIWeto46RxyrMTjNSOfE+3j4LAJ6s2rXpHV6uOIhzhX87fJNj2dZh97kEXu0AA4aFSQ omPzkxrfUKXccKnCGvqL8TCMJB6gdY0uavlAf1RaWnQ54n1cTiLXM0wWz6Wo7AaHsFmV t8HPNQTlCwKKNSarB2W3TsMG77oEYCqDAOw18lVKjNaEVHV1zJkcmqxjfFTrdUeOTgO5 uQEojF+hb+yYykpc+ADTkZvSxiBE7i3SlPeN8k0J+/9m1Lh7D2o0kPVLTzNCU1jLBeIG TnuQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=90YfR0LP5TFiDKzfZwBM/49yhqrZT3qiPN/l7r+cWeo=; b=lg9h+uqvMZz7DUPnarvtia4LsXjjRlcnl6Q084oJtkacEn5YQS0/4Fyh8dmIgArzkB ehMFyInuwmhqEVnAE731e/H8iptVD0+htqGQGbOufHI0M3SOSEtJ1vvEAr8bURzjV/4A RYs5MsdCFFdD3minohG8APzAsyJQ2niPaiBKbRaCE4iINTKjD21JJss9JI9HJHCpVqHb bF34bc1Frr3qcpvByHz7VvqJW56f22iCQTxfZ5PAqySAOacMj7IvBhWA923rkardZLSZ WTbdj8gxldcyrSEDtkuClJvhHNi/qjiAWbxDy54lMgXrbQZPt+9MyxU/fTbgt6gtMmGw /nAw== X-Gm-Message-State: AOAM531n8Y1GgrgxOHLKPK1u9hDb4MSeRfWLZj86xIb5+ivA2VHKVQP6 khRgMf/CorWWx3kJ6KBqZs6HFw8kIuU= X-Google-Smtp-Source: ABdhPJxdDzJ+KxjyK44CsyjIhE2M30YD4RdqaCJ3iYFN8LmQDuGpq+tAqBIBK7rGN1k1rt48JkNu2A== X-Received: by 2002:a37:84c3:: with SMTP id g186mr39446610qkd.276.1625120177697; Wed, 30 Jun 2021 23:16:17 -0700 (PDT) Received: from godwin.fios-router.home (pool-74-96-87-9.washdc.fios.verizon.net. [74.96.87.9]) by smtp.gmail.com with ESMTPSA id g21sm1684673qts.90.2021.06.30.23.16.17 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 30 Jun 2021 23:16:17 -0700 (PDT) From: Sean Anderson To: u-boot@lists.denx.de, Tom Rini Cc: =?utf-8?q?Marek_Beh=C3=BAn?= , Wolfgang Denk , Simon Glass , Roland Gaudig , Heinrich Schuchardt , Kostas Michalopoulos , Sean Anderson Subject: [RFC PATCH 04/28] cli: lil: Remove most functions by default Date: Thu, 1 Jul 2021 02:15:47 -0400 Message-Id: <20210701061611.957918-5-seanga2@gmail.com> X-Mailer: git-send-email 2.32.0 In-Reply-To: <20210701061611.957918-1-seanga2@gmail.com> References: <20210701061611.957918-1-seanga2@gmail.com> MIME-Version: 1.0 X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.34 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" X-Virus-Scanned: clamav-milter 0.103.2 at phobos.denx.de X-Virus-Status: Clean This helps reduce the size impact of LIL to approximately that of hush. The builtin functions have been chosen to roughly match the functionality present in hush. In addition, arithmetic is removed from expr to reduce size further. Signed-off-by: Sean Anderson Reviewed-by: Simon Glass --- cmd/Kconfig | 10 ++++++ common/cli_lil.c | 83 +++++++++++++++++++++++++++--------------------- 2 files changed, 57 insertions(+), 36 deletions(-) diff --git a/cmd/Kconfig b/cmd/Kconfig index 8bccc572af..0a7b73cb6d 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -32,6 +32,16 @@ config LIL like `proc name {args} {body}' functions or `echo [some command]` command substitution ("tcl scripts"). +if LIL + +config LIL_FULL + bool "Enable all LIL features" + help + This enables all LIL builtin functions, as well as expression support + for arithmetic and bitwise operations. + +endif + config CMDLINE_EDITING bool "Enable command line editing" depends on CMDLINE diff --git a/common/cli_lil.c b/common/cli_lil.c index 294d79e86e..a2e5cdf35a 100644 --- a/common/cli_lil.c +++ b/common/cli_lil.c @@ -1321,7 +1321,7 @@ static void ee_unary(struct expreval *ee) static void ee_muldiv(struct expreval *ee) { ee_unary(ee); - if (ee->error) + if (ee->error || !IS_ENABLED(CONFIG_LIL_FULL)) return; ee_skip_spaces(ee); @@ -1382,8 +1382,10 @@ static void ee_muldiv(struct expreval *ee) static void ee_addsub(struct expreval *ee) { ee_muldiv(ee); - ee_skip_spaces(ee); + if (!IS_ENABLED(CONFIG_LIL_FULL)) + return; + ee_skip_spaces(ee); while (ee->head < ee->len && !ee->error && !ee_invalidpunct(ee->code[ee->head + 1]) && (ee->code[ee->head] == '+' || ee->code[ee->head] == '-')) { @@ -1415,8 +1417,10 @@ static void ee_addsub(struct expreval *ee) static void ee_shift(struct expreval *ee) { ee_addsub(ee); - ee_skip_spaces(ee); + if (!IS_ENABLED(CONFIG_LIL_FULL)) + return; + ee_skip_spaces(ee); while (ee->head < ee->len && !ee->error && ((ee->code[ee->head] == '<' && ee->code[ee->head + 1] == '<') || (ee->code[ee->head] == '>' && ee->code[ee->head + 1] == '>'))) { @@ -1545,8 +1549,10 @@ static void ee_equals(struct expreval *ee) static void ee_bitand(struct expreval *ee) { ee_equals(ee); - ee_skip_spaces(ee); + if (!IS_ENABLED(CONFIG_LIL_FULL)) + return; + ee_skip_spaces(ee); while (ee->head < ee->len && !ee->error && (ee->code[ee->head] == '&' && !ee_invalidpunct(ee->code[ee->head + 1]))) { @@ -1566,8 +1572,10 @@ static void ee_bitand(struct expreval *ee) static void ee_bitor(struct expreval *ee) { ee_bitand(ee); - ee_skip_spaces(ee); + if (!IS_ENABLED(CONFIG_LIL_FULL)) + return; + ee_skip_spaces(ee); while (ee->head < ee->len && !ee->error && (ee->code[ee->head] == '|' && !ee_invalidpunct(ee->code[ee->head + 1]))) { @@ -2932,49 +2940,52 @@ static struct lil_value *fnc_lmap(struct lil *lil, size_t argc, static void register_stdcmds(struct lil *lil) { - lil_register(lil, "append", fnc_append); - lil_register(lil, "char", fnc_char); - lil_register(lil, "charat", fnc_charat); - lil_register(lil, "codeat", fnc_codeat); - lil_register(lil, "concat", fnc_concat); - lil_register(lil, "count", fnc_count); lil_register(lil, "dec", fnc_dec); - lil_register(lil, "downeval", fnc_downeval); - lil_register(lil, "error", fnc_error); lil_register(lil, "eval", fnc_eval); lil_register(lil, "expr", fnc_expr); - lil_register(lil, "filter", fnc_filter); lil_register(lil, "for", fnc_for); lil_register(lil, "foreach", fnc_foreach); lil_register(lil, "func", fnc_func); lil_register(lil, "if", fnc_if); lil_register(lil, "inc", fnc_inc); - lil_register(lil, "index", fnc_index); - lil_register(lil, "indexof", fnc_indexof); - lil_register(lil, "length", fnc_length); - lil_register(lil, "list", fnc_list); - lil_register(lil, "lmap", fnc_lmap); lil_register(lil, "local", fnc_local); - lil_register(lil, "ltrim", fnc_ltrim); - lil_register(lil, "quote", fnc_quote); - lil_register(lil, "reflect", fnc_reflect); - lil_register(lil, "rename", fnc_rename); - lil_register(lil, "repstr", fnc_repstr); - lil_register(lil, "result", fnc_result); lil_register(lil, "return", fnc_return); - lil_register(lil, "rtrim", fnc_rtrim); lil_register(lil, "set", fnc_set); - lil_register(lil, "slice", fnc_slice); - lil_register(lil, "split", fnc_split); lil_register(lil, "strcmp", fnc_strcmp); - lil_register(lil, "streq", fnc_streq); - lil_register(lil, "strpos", fnc_strpos); - lil_register(lil, "subst", fnc_subst); - lil_register(lil, "substr", fnc_substr); - lil_register(lil, "topeval", fnc_topeval); - lil_register(lil, "trim", fnc_trim); lil_register(lil, "try", fnc_try); - lil_register(lil, "unusedname", fnc_unusedname); - lil_register(lil, "upeval", fnc_upeval); lil_register(lil, "while", fnc_while); + + if (IS_ENABLED(CONFIG_LIL_FULL)) { + lil_register(lil, "append", fnc_append); + lil_register(lil, "char", fnc_char); + lil_register(lil, "charat", fnc_charat); + lil_register(lil, "codeat", fnc_codeat); + lil_register(lil, "concat", fnc_concat); + lil_register(lil, "count", fnc_count); + lil_register(lil, "downeval", fnc_downeval); + lil_register(lil, "error", fnc_error); + lil_register(lil, "filter", fnc_filter); + lil_register(lil, "index", fnc_index); + lil_register(lil, "indexof", fnc_indexof); + lil_register(lil, "length", fnc_length); + lil_register(lil, "list", fnc_list); + lil_register(lil, "lmap", fnc_lmap); + lil_register(lil, "ltrim", fnc_ltrim); + lil_register(lil, "quote", fnc_quote); + lil_register(lil, "reflect", fnc_reflect); + lil_register(lil, "rename", fnc_rename); + lil_register(lil, "repstr", fnc_repstr); + lil_register(lil, "result", fnc_result); + lil_register(lil, "rtrim", fnc_rtrim); + lil_register(lil, "slice", fnc_slice); + lil_register(lil, "split", fnc_split); + lil_register(lil, "streq", fnc_streq); + lil_register(lil, "strpos", fnc_strpos); + lil_register(lil, "subst", fnc_subst); + lil_register(lil, "substr", fnc_substr); + lil_register(lil, "topeval", fnc_topeval); + lil_register(lil, "trim", fnc_trim); + lil_register(lil, "unusedname", fnc_unusedname); + lil_register(lil, "upeval", fnc_upeval); + } } From patchwork Thu Jul 1 06:15:48 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sean Anderson X-Patchwork-Id: 1499279 X-Patchwork-Delegate: trini@ti.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=lists.denx.de (client-ip=2a01:238:438b:c500:173d:9f52:ddab:ee01; helo=phobos.denx.de; envelope-from=u-boot-bounces@lists.denx.de; receiver=) Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20161025 header.b=lnNUNmOI; dkim-atps=neutral Received: from phobos.denx.de (phobos.denx.de [IPv6:2a01:238:438b:c500:173d:9f52:ddab:ee01]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 4GFp0N74nVz9sWX for ; Thu, 1 Jul 2021 16:17:24 +1000 (AEST) Received: from h2850616.stratoserver.net (localhost [IPv6:::1]) by phobos.denx.de (Postfix) with ESMTP id 8E3378321B; Thu, 1 Jul 2021 08:16:37 +0200 (CEST) Authentication-Results: phobos.denx.de; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=u-boot-bounces@lists.denx.de Authentication-Results: phobos.denx.de; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="lnNUNmOI"; dkim-atps=neutral Received: by phobos.denx.de (Postfix, from userid 109) id 448868327E; Thu, 1 Jul 2021 08:16:34 +0200 (CEST) X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on phobos.denx.de X-Spam-Level: X-Spam-Status: No, score=-2.0 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,FREEMAIL_FROM,SPF_HELO_NONE autolearn=unavailable autolearn_force=no version=3.4.2 Received: from mail-qv1-xf32.google.com (mail-qv1-xf32.google.com [IPv6:2607:f8b0:4864:20::f32]) (using TLSv1.3 with cipher TLS_AES_128_GCM_SHA256 (128/128 bits)) (No client certificate requested) by phobos.denx.de (Postfix) with ESMTPS id AE2BD83219 for ; Thu, 1 Jul 2021 08:16:19 +0200 (CEST) Authentication-Results: phobos.denx.de; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=seanga2@gmail.com Received: by mail-qv1-xf32.google.com with SMTP id d2so2430074qvh.2 for ; Wed, 30 Jun 2021 23:16:19 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=7SNLHIKfEYOaaD41OLk3wJrt7d9q4lXdU/HLuTIEbFU=; b=lnNUNmOIhM7xkeKYhpgLHm8Qo6xgrhL5Klcv3toFFYRQgiZFa5f+v9jLdVkU1fxn3g E7KcasIhZI+BfIiI4EAGL8rKkWoaqW5iLIWwOyyUjT2z8KPqsK9ezjI1wMtop+7HSor2 yv254wChE5fn5UQlcdbDBhtxwXQcW8fSfyohIPgX9WSzT6SWttUynqI5PSCDz7LRETJp YkNo3hVx3pAc8uMBtQOQrO4nxm3rvFfseq/n+ZtQvo0TNXvfD4F82LrHLoPyghCwjuiI rT5GcwQEiYsA6/vhwi8mJLuWCdoxiBjqesh7SAtfXAxx9ekwpVHAFGF0iNsFzEAm3UO1 vhUg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=7SNLHIKfEYOaaD41OLk3wJrt7d9q4lXdU/HLuTIEbFU=; b=AQ1DgSuLRgOnk90Z2PLWRRji/RZk+zm6OIqszDAtlXHaUgSPA4/f9GowB1Fi+/fe8G 7b94VUO39ERqaNiCQlDnTF0f63jEyWncMfnmtXBAVmQd/KLA4j/zIMyVlEhKuAfmsV8q 9vq98jP+IqePhMaRd3aWibFzxS8tjSlH3z3n1gvQQn+clDS3Kzaqap4qJWnoWJUBo0kh /YVgSJYN3AAk1Qqvtmdr9+P0FwiIAS6rKlRDSxamjonCdAiXHQL5KmzoiX5M2wo/U/IU zGySCznaTEYpUEDYvFrPute6kLYGe6Qli7GO2xyFVIor4YOKr1Mn8U6Uxltibnj4Go1g ifdg== X-Gm-Message-State: AOAM530qdN7n6yQSKTaHESQQVeLRWFtegavV+y8NqgnfGvCeuO7YOEFC OqpOg590AKwwQFDoSYg6CXOJFBQNLEo= X-Google-Smtp-Source: ABdhPJwFChiy8UqkTOxEujY0UfKCOO0lKJQwe8QeAgHD56uct30LJTSr2wV06dmVcWuE6/Zucl91zg== X-Received: by 2002:a05:6214:5088:: with SMTP id kk8mr41163083qvb.44.1625120178434; Wed, 30 Jun 2021 23:16:18 -0700 (PDT) Received: from godwin.fios-router.home (pool-74-96-87-9.washdc.fios.verizon.net. [74.96.87.9]) by smtp.gmail.com with ESMTPSA id g21sm1684673qts.90.2021.06.30.23.16.17 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 30 Jun 2021 23:16:18 -0700 (PDT) From: Sean Anderson To: u-boot@lists.denx.de, Tom Rini Cc: =?utf-8?q?Marek_Beh=C3=BAn?= , Wolfgang Denk , Simon Glass , Roland Gaudig , Heinrich Schuchardt , Kostas Michalopoulos , Sean Anderson Subject: [RFC PATCH 05/28] cli: lil: Rename some functions to be more like TCL Date: Thu, 1 Jul 2021 02:15:48 -0400 Message-Id: <20210701061611.957918-6-seanga2@gmail.com> X-Mailer: git-send-email 2.32.0 In-Reply-To: <20210701061611.957918-1-seanga2@gmail.com> References: <20210701061611.957918-1-seanga2@gmail.com> MIME-Version: 1.0 X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.34 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" X-Virus-Scanned: clamav-milter 0.103.2 at phobos.denx.de X-Virus-Status: Clean Several functions have different names than they do in TCL. To make things easier for those familiar with TCL, rename them to their TCL equivalents. At the moment, this is only done for functions not used by LIL_FULL. Some functions need more substantive work to conform them to TCL. For example, in TCL, there is a `string` function with a subcommand of `compare`, which is the same as the top-level function `compare`. Several functions also have no TCL equivalent. Do we need these? TODO: do this for all functions Signed-off-by: Sean Anderson --- common/cli_lil.c | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/common/cli_lil.c b/common/cli_lil.c index a2e5cdf35a..a6c77ee19c 100644 --- a/common/cli_lil.c +++ b/common/cli_lil.c @@ -1829,10 +1829,10 @@ static struct lil_value *fnc_reflect(struct lil *lil, size_t argc, return lil_clone_value(func->code); } - if (!strcmp(type, "func-count")) + if (!strcmp(type, "proc-count")) return lil_alloc_integer(lil->cmds); - if (!strcmp(type, "funcs")) { + if (!strcmp(type, "procs")) { struct lil_list *funcs = lil_alloc_list(); for (i = 0; i < lil->cmds; i++) @@ -1878,7 +1878,7 @@ static struct lil_value *fnc_reflect(struct lil *lil, size_t argc, return r; } - if (!strcmp(type, "has-func")) { + if (!strcmp(type, "has-proc")) { const char *target; if (argc == 1) @@ -1948,7 +1948,7 @@ static struct lil_value *fnc_reflect(struct lil *lil, size_t argc, return NULL; } -static struct lil_value *fnc_func(struct lil *lil, size_t argc, +static struct lil_value *fnc_proc(struct lil *lil, size_t argc, struct lil_value **argv) { struct lil_value *name; @@ -2472,8 +2472,8 @@ static struct lil_value *real_inc(struct lil *lil, const char *varname, return pv; } -static struct lil_value *fnc_inc(struct lil *lil, size_t argc, - struct lil_value **argv) +static struct lil_value *fnc_incr(struct lil *lil, size_t argc, + struct lil_value **argv) { if (argc < 1) return NULL; @@ -2482,8 +2482,8 @@ static struct lil_value *fnc_inc(struct lil *lil, size_t argc, argc > 1 ? lil_to_integer(argv[1]) : 1); } -static struct lil_value *fnc_dec(struct lil *lil, size_t argc, - struct lil_value **argv) +static struct lil_value *fnc_decr(struct lil *lil, size_t argc, + struct lil_value **argv) { if (argc < 1) return NULL; @@ -2786,8 +2786,8 @@ static struct lil_value *fnc_rtrim(struct lil *lil, size_t argc, return real_trim(lil_to_string(argv[0]), chars, 0, 1); } -static struct lil_value *fnc_strcmp(struct lil *lil, size_t argc, - struct lil_value **argv) +static struct lil_value *fnc_compare(struct lil *lil, size_t argc, + struct lil_value **argv) { if (argc < 2) return NULL; @@ -2940,18 +2940,18 @@ static struct lil_value *fnc_lmap(struct lil *lil, size_t argc, static void register_stdcmds(struct lil *lil) { - lil_register(lil, "dec", fnc_dec); + lil_register(lil, "decr", fnc_decr); lil_register(lil, "eval", fnc_eval); lil_register(lil, "expr", fnc_expr); lil_register(lil, "for", fnc_for); lil_register(lil, "foreach", fnc_foreach); - lil_register(lil, "func", fnc_func); + lil_register(lil, "proc", fnc_proc); lil_register(lil, "if", fnc_if); - lil_register(lil, "inc", fnc_inc); + lil_register(lil, "incr", fnc_incr); lil_register(lil, "local", fnc_local); lil_register(lil, "return", fnc_return); lil_register(lil, "set", fnc_set); - lil_register(lil, "strcmp", fnc_strcmp); + lil_register(lil, "compare", fnc_compare); lil_register(lil, "try", fnc_try); lil_register(lil, "while", fnc_while); From patchwork Thu Jul 1 06:15:49 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sean Anderson X-Patchwork-Id: 1499278 X-Patchwork-Delegate: trini@ti.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=lists.denx.de (client-ip=85.214.62.61; helo=phobos.denx.de; envelope-from=u-boot-bounces@lists.denx.de; receiver=) Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20161025 header.b=ZNayrIIq; dkim-atps=neutral Received: from phobos.denx.de (phobos.denx.de [85.214.62.61]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 4GFp095tjfz9sWX for ; Thu, 1 Jul 2021 16:17:13 +1000 (AEST) Received: from h2850616.stratoserver.net (localhost [IPv6:::1]) by phobos.denx.de (Postfix) with ESMTP id B5D668321C; Thu, 1 Jul 2021 08:16:34 +0200 (CEST) Authentication-Results: phobos.denx.de; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=u-boot-bounces@lists.denx.de Authentication-Results: phobos.denx.de; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="ZNayrIIq"; dkim-atps=neutral Received: by phobos.denx.de (Postfix, from userid 109) id 8325383271; Thu, 1 Jul 2021 08:16:30 +0200 (CEST) X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on phobos.denx.de X-Spam-Level: X-Spam-Status: No, score=-2.0 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,FREEMAIL_FROM,SPF_HELO_NONE autolearn=ham autolearn_force=no version=3.4.2 Received: from mail-qt1-x833.google.com (mail-qt1-x833.google.com [IPv6:2607:f8b0:4864:20::833]) (using TLSv1.3 with cipher TLS_AES_128_GCM_SHA256 (128/128 bits)) (No client certificate requested) by phobos.denx.de (Postfix) with ESMTPS id 7CF9D8321E for ; Thu, 1 Jul 2021 08:16:20 +0200 (CEST) Authentication-Results: phobos.denx.de; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=seanga2@gmail.com Received: by mail-qt1-x833.google.com with SMTP id c13so3356280qtb.12 for ; Wed, 30 Jun 2021 23:16:20 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=yjms/r4TRyD7PnAT/kUWqC3GJwb1jt5iMV+Y6BStEps=; b=ZNayrIIqkYDl+CMNZXxziYKi3LpMDMS7GRrDban1xzqZqOIiIdjiTLC5m80II6zlm/ Ng9pv/UbrkaUgZ78aGI1f0NhHhNmeE7oReOx4FOSx32Uebr+0m4HxAQQWeMDbFH1ybgx lhtHWNpp1N3I+cSTKu/aGcydmSV2eqOUeyGxY2HdaSrjck/xQfMjfS1Bhs6lQComq5wr qkTuPyNYRL8Bi4D1Z5oekA6BmFiceB3mqNfx2OMWq5D5VhJEqEViPoTPvRIx0HGTfi95 lLBfMmiVrGELYBTHpHjcT8J3wKunVuedFGHd/v6wG8raSsCVPHaSOS6yQOFLNenMM4qe PHMg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=yjms/r4TRyD7PnAT/kUWqC3GJwb1jt5iMV+Y6BStEps=; b=N7d/kf1kiOnaCGLKwfR/RE+vP3QzR9cyTC8Fl1Nq4ATSYwVfN0LC4Kg0/mEh9EOGZd CDetsg3SYGlWvz0m2RsOigZ45UILBm85MU+BAbz4mtK8nGhqTpgw0iNMLZtdrnVOIZpK DLJmYavTwMJe6V3JfirZwiUtGJ23bVD3zQ7KjTG4Asn8Yad3SVHjG3NqcKB30O5ibVdB n7Z26CNXwnhd0vP3xI12qHnzNJxJPxoHfaoou1s6b0r+a7dXk88V3uAc4F9fUcSKTHAv 9GJXrvzUswU3VpxXnEJfmOFUySz0MGiuV2Bhz0Jux0j36X7QqnR0OBxZ84h4UaIFcA93 bWNA== X-Gm-Message-State: AOAM533DxwMQigeATM98FyrToL4sS8er23iUUZvHQZma8Kfk/U0a+827 ngJcOBaoRe0EsDvP9JuHLu6PQVApB7A= X-Google-Smtp-Source: ABdhPJxRF+JCwa8rFgqddudESiYJ23gTIvEoV3kuVnreWFsakjnAuJY5kr6qfWhyVI3eGPsYVBtLvQ== X-Received: by 2002:a05:622a:394:: with SMTP id j20mr5400372qtx.270.1625120179178; Wed, 30 Jun 2021 23:16:19 -0700 (PDT) Received: from godwin.fios-router.home (pool-74-96-87-9.washdc.fios.verizon.net. [74.96.87.9]) by smtp.gmail.com with ESMTPSA id g21sm1684673qts.90.2021.06.30.23.16.18 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 30 Jun 2021 23:16:18 -0700 (PDT) From: Sean Anderson To: u-boot@lists.denx.de, Tom Rini Cc: =?utf-8?q?Marek_Beh=C3=BAn?= , Wolfgang Denk , Simon Glass , Roland Gaudig , Heinrich Schuchardt , Kostas Michalopoulos , Sean Anderson Subject: [RFC PATCH 06/28] cli: lil: Convert some defines to enums Date: Thu, 1 Jul 2021 02:15:49 -0400 Message-Id: <20210701061611.957918-7-seanga2@gmail.com> X-Mailer: git-send-email 2.32.0 In-Reply-To: <20210701061611.957918-1-seanga2@gmail.com> References: <20210701061611.957918-1-seanga2@gmail.com> MIME-Version: 1.0 X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.34 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" X-Virus-Scanned: clamav-milter 0.103.2 at phobos.denx.de X-Virus-Status: Clean This converts some defines to enums. This allows them to be documented more easily, and helps the compiler give better errors for switch statements. Signed-off-by: Sean Anderson --- common/cli_lil.c | 44 +++++++++++++++++++++++--------------------- include/cli_lil.h | 12 +++++++----- 2 files changed, 30 insertions(+), 26 deletions(-) diff --git a/common/cli_lil.c b/common/cli_lil.c index a6c77ee19c..3d1e6181f9 100644 --- a/common/cli_lil.c +++ b/common/cli_lil.c @@ -24,10 +24,6 @@ * overflows and is also useful when running through an automated fuzzer like AFL */ /*#define LIL_ENABLE_RECLIMIT 10000*/ -#define ERROR_NOERROR 0 -#define ERROR_DEFAULT 1 -#define ERROR_FIXHEAD 2 - #define CALLBACKS 8 #define HASHMAP_CELLS 256 #define HASHMAP_CELLMASK 0xFF @@ -97,7 +93,11 @@ struct lil { struct lil_env *rootenv; struct lil_env *downenv; struct lil_value *empty; - int error; + enum { + ERROR_NOERROR = 0, + ERROR_DEFAULT, + ERROR_FIXHEAD, + } error; size_t err_head; char *err_msg; lil_callback_proc_t callback[CALLBACKS]; @@ -108,7 +108,12 @@ struct expreval { const char *code; size_t len, head; ssize_t ival; - int error; + enum { + EERR_NO_ERROR = 0, + EERR_SYNTAX_ERROR, + EERR_DIVISION_BY_ZERO, + EERR_INVALID_EXPRESSION, + } error; }; static struct lil_value *next_word(struct lil *lil); @@ -645,7 +650,7 @@ int lil_register(struct lil *lil, const char *name, lil_func_proc_t proc) } struct lil_var *lil_set_var(struct lil *lil, const char *name, - struct lil_value *val, int local) + struct lil_value *val, enum lil_setvar local) { struct lil_var **nvar; struct lil_env *env = @@ -1222,11 +1227,6 @@ int lil_error(struct lil *lil, const char **msg, size_t *pos) return 1; } -#define EERR_NO_ERROR 0 -#define EERR_SYNTAX_ERROR 1 -#define EERR_DIVISION_BY_ZERO 3 -#define EERR_INVALID_EXPRESSION 4 - static void ee_expr(struct expreval *ee); static int ee_invalidpunct(int ch) @@ -1673,16 +1673,18 @@ struct lil_value *lil_eval_expr(struct lil *lil, struct lil_value *code) ee_expr(&ee); lil_free_value(code); - if (ee.error) { - switch (ee.error) { - case EERR_DIVISION_BY_ZERO: - lil_set_error(lil, "division by zero in expression"); - break; - case EERR_SYNTAX_ERROR: - lil_set_error(lil, "expression syntax error"); - break; - } + switch (ee.error) { + case EERR_DIVISION_BY_ZERO: + lil_set_error(lil, "division by zero in expression"); return NULL; + case EERR_SYNTAX_ERROR: + lil_set_error(lil, "expression syntax error"); + return NULL; + case EERR_INVALID_EXPRESSION: + lil_set_error(lil, "invalid expression"); + return NULL; + case EERR_NO_ERROR: + break; } return lil_alloc_integer(ee.ival); } diff --git a/include/cli_lil.h b/include/cli_lil.h index c72977ea5c..48735e0605 100644 --- a/include/cli_lil.h +++ b/include/cli_lil.h @@ -13,10 +13,12 @@ #define LIL_VERSION_STRING "0.1" -#define LIL_SETVAR_GLOBAL 0 -#define LIL_SETVAR_LOCAL 1 -#define LIL_SETVAR_LOCAL_NEW 2 -#define LIL_SETVAR_LOCAL_ONLY 3 +enum lil_setvar { + LIL_SETVAR_GLOBAL = 0, + LIL_SETVAR_LOCAL, + LIL_SETVAR_LOCAL_NEW, + LIL_SETVAR_LOCAL_ONLY, +}; #define LIL_CALLBACK_EXIT 0 #define LIL_CALLBACK_WRITE 1 @@ -98,7 +100,7 @@ struct lil_env *lil_push_env(struct lil *lil); void lil_pop_env(struct lil *lil); struct lil_var *lil_set_var(struct lil *lil, const char *name, - struct lil_value *val, int local); + struct lil_value *val, enum lil_setvar local); struct lil_value *lil_get_var(struct lil *lil, const char *name); struct lil_value *lil_get_var_or(struct lil *lil, const char *name, struct lil_value *defvalue); From patchwork Thu Jul 1 06:15:50 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sean Anderson X-Patchwork-Id: 1499282 X-Patchwork-Delegate: trini@ti.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=lists.denx.de (client-ip=85.214.62.61; helo=phobos.denx.de; envelope-from=u-boot-bounces@lists.denx.de; receiver=) Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20161025 header.b=pl9ESLPv; dkim-atps=neutral Received: from phobos.denx.de (phobos.denx.de [85.214.62.61]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 4GFp0t01CCz9sWk for ; Thu, 1 Jul 2021 16:17:49 +1000 (AEST) Received: from h2850616.stratoserver.net (localhost [IPv6:::1]) by phobos.denx.de (Postfix) with ESMTP id 1B0528328C; Thu, 1 Jul 2021 08:16:43 +0200 (CEST) Authentication-Results: phobos.denx.de; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=u-boot-bounces@lists.denx.de Authentication-Results: phobos.denx.de; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="pl9ESLPv"; dkim-atps=neutral Received: by phobos.denx.de (Postfix, from userid 109) id 6F2AB83288; Thu, 1 Jul 2021 08:16:36 +0200 (CEST) X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on phobos.denx.de X-Spam-Level: X-Spam-Status: No, score=-2.0 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,FREEMAIL_FROM,SPF_HELO_NONE autolearn=ham autolearn_force=no version=3.4.2 Received: from mail-qv1-xf2a.google.com (mail-qv1-xf2a.google.com [IPv6:2607:f8b0:4864:20::f2a]) (using TLSv1.3 with cipher TLS_AES_128_GCM_SHA256 (128/128 bits)) (No client certificate requested) by phobos.denx.de (Postfix) with ESMTPS id 3A0A580C58 for ; Thu, 1 Jul 2021 08:16:21 +0200 (CEST) Authentication-Results: phobos.denx.de; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=seanga2@gmail.com Received: by mail-qv1-xf2a.google.com with SMTP id m15so2418826qvc.9 for ; Wed, 30 Jun 2021 23:16:21 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=FFjmfHBG0eN99hMAiWv4/os41SxMb64zojoinmB8qU8=; b=pl9ESLPv3wzgeXj3ghkKTbIOI0+4wzT3I9aLVTxaYqdVORYz+xgIWDsfREN5TK1SiE BrSHDdTEvNXPQoRxx4r8CvrM+JmBttiGAlVWHe2/wAA/qFF2nxN4odD+AMYwsSVghtyr Y9NomaRcvRFRhVnnNq1OLlowZvMFnnB/+Jc2BmhDdZIp4Gp2bsyRxz2DKtWMA18J7mOv qUsD2DS/uLzWNVQHmxfyh4YCcInzkIblNxFR1fwdYZClXA+EOsDM+XKJjjVWNQC43hyv mO7a2pSn0ms62c22X2OUlHRjhJM2hE/oYGNOgMOaRifhbKpq7IQz+Dxzc71Jb1oSWrWV xzrA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=FFjmfHBG0eN99hMAiWv4/os41SxMb64zojoinmB8qU8=; b=VUSnPduD9fVdc/ahHGULyFJ/hK3Jqj+KJ/cOYmIFpaGT0u7f2l8OzIDE8DY5FLlcPs 1oYT6KFd5GuShWY5Meq5+QB8MAkyG8BOvjczy80rtJejkJFGYmzHJ7aEopMeN/RYKK6v GF5zwEC8d8e5Nm2Lbu6fOc3SE6L4Sx1fxvh+/V3JZOlgfdam6ncdFOWVHIEbdNt7wLGI /+513xxwVREYSDF5HGX0mZprgYSYSkMiWg2gG04uKDaG/Q4pNYowglm9H6lcOURygNri 3+dDmGn+MhwsfpnTpEMUskf450TOFWCoqEylbWvojPOydqoP5ZPhrgW9YX2JrYVrpG+6 K3Ug== X-Gm-Message-State: AOAM533o9lLy34m6qL0due4RZjuVmNfAvJIWD8Rw1eqFdsd/a4M8ucIh 3lb4El/94sqFheWIP5lcQhha1AhpJ3k= X-Google-Smtp-Source: ABdhPJww4QwuDI+qwl53ZV1jmA6DEdkSYEyj1kiAhTcWmFXZPPH/EdV9u5AOk11ygNkau2ARbelilA== X-Received: by 2002:a05:6214:a66:: with SMTP id ef6mr18136518qvb.60.1625120179935; Wed, 30 Jun 2021 23:16:19 -0700 (PDT) Received: from godwin.fios-router.home (pool-74-96-87-9.washdc.fios.verizon.net. [74.96.87.9]) by smtp.gmail.com with ESMTPSA id g21sm1684673qts.90.2021.06.30.23.16.19 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 30 Jun 2021 23:16:19 -0700 (PDT) From: Sean Anderson To: u-boot@lists.denx.de, Tom Rini Cc: =?utf-8?q?Marek_Beh=C3=BAn?= , Wolfgang Denk , Simon Glass , Roland Gaudig , Heinrich Schuchardt , Kostas Michalopoulos , Sean Anderson Subject: [RFC PATCH 07/28] cli: lil: Simplify callbacks Date: Thu, 1 Jul 2021 02:15:50 -0400 Message-Id: <20210701061611.957918-8-seanga2@gmail.com> X-Mailer: git-send-email 2.32.0 In-Reply-To: <20210701061611.957918-1-seanga2@gmail.com> References: <20210701061611.957918-1-seanga2@gmail.com> MIME-Version: 1.0 X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.34 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" X-Virus-Scanned: clamav-milter 0.103.2 at phobos.denx.de X-Virus-Status: Clean We only need the getvar and setvar callbacks for now (to integrate with the environment). So remove all the other callbacks. Instead of allowing callbacks to be set at any time, just set them once in lil_new. Lastly, get rid of the typedefs and use a struct to pass in the callbacks instead. Signed-off-by: Sean Anderson --- common/cli_lil.c | 45 ++++++++++++--------------------------------- include/cli_lil.h | 34 ++++++++-------------------------- 2 files changed, 20 insertions(+), 59 deletions(-) diff --git a/common/cli_lil.c b/common/cli_lil.c index 3d1e6181f9..539a4a3238 100644 --- a/common/cli_lil.c +++ b/common/cli_lil.c @@ -24,7 +24,6 @@ * overflows and is also useful when running through an automated fuzzer like AFL */ /*#define LIL_ENABLE_RECLIMIT 10000*/ -#define CALLBACKS 8 #define HASHMAP_CELLS 256 #define HASHMAP_CELLMASK 0xFF @@ -100,7 +99,7 @@ struct lil { } error; size_t err_head; char *err_msg; - lil_callback_proc_t callback[CALLBACKS]; + struct lil_callbacks callbacks; size_t parse_depth; }; @@ -667,14 +666,10 @@ struct lil_var *lil_set_var(struct lil *lil, const char *name, var->env == lil->rootenv && var->env != env) var = NULL; - if (((!var && env == lil->rootenv) || - (var && var->env == lil->rootenv)) && - lil->callback[LIL_CALLBACK_SETVAR]) { - lil_setvar_callback_proc_t proc = - (lil_setvar_callback_proc_t) - lil->callback[LIL_CALLBACK_SETVAR]; + if (lil->callbacks.setvar && ((!var && env == lil->rootenv) || + (var && var->env == lil->rootenv))) { struct lil_value *newval = val; - int r = proc(lil, name, &newval); + int r = lil->callbacks.setvar(lil, name, &newval); if (r < 0) { return NULL; @@ -717,14 +712,10 @@ struct lil_value *lil_get_var_or(struct lil *lil, const char *name, struct lil_var *var = lil_find_var(lil, lil->env, name); struct lil_value *retval = var ? var->v : defvalue; - if (lil->callback[LIL_CALLBACK_GETVAR] && - (!var || var->env == lil->rootenv)) { - lil_getvar_callback_proc_t proc = - (lil_getvar_callback_proc_t) - lil->callback[LIL_CALLBACK_GETVAR]; + if (lil->callbacks.getvar && (!var || var->env == lil->rootenv)) { struct lil_value *newretval = retval; - if (proc(lil, name, &newretval)) + if (lil->callbacks.getvar(lil, name, &newretval)) retval = newretval; } return retval; @@ -748,10 +739,15 @@ void lil_pop_env(struct lil *lil) } } -struct lil *lil_new(void) +struct lil *lil_new(const struct lil_callbacks *callbacks) { struct lil *lil = calloc(1, sizeof(struct lil)); + if (!lil) + return NULL; + + if (callbacks) + memcpy(&lil->callbacks, callbacks, sizeof(lil->callbacks)); lil->rootenv = lil->env = lil_alloc_env(NULL); lil->empty = alloc_value(NULL); hm_init(&lil->cmdmap); @@ -1148,15 +1144,6 @@ struct lil_value *lil_parse(struct lil *lil, const char *code, size_t codelen, } cleanup: - if (lil->error && lil->callback[LIL_CALLBACK_ERROR] && - lil->parse_depth == 1) { - lil_error_callback_proc_t proc = - (lil_error_callback_proc_t) - lil->callback[LIL_CALLBACK_ERROR]; - - proc(lil, lil->err_head, lil->err_msg); - } - if (words) lil_free_list(words); lil->code = save_code; @@ -1185,14 +1172,6 @@ struct lil_value *lil_parse_value(struct lil *lil, struct lil_value *val, return lil_parse(lil, val->d, val->l, funclevel); } -void lil_callback(struct lil *lil, int cb, lil_callback_proc_t proc) -{ - if (cb < 0 || cb > CALLBACKS) - return; - - lil->callback[cb] = proc; -} - void lil_set_error(struct lil *lil, const char *msg) { if (lil->error) diff --git a/include/cli_lil.h b/include/cli_lil.h index 48735e0605..b8df94a766 100644 --- a/include/cli_lil.h +++ b/include/cli_lil.h @@ -20,15 +20,6 @@ enum lil_setvar { LIL_SETVAR_LOCAL_ONLY, }; -#define LIL_CALLBACK_EXIT 0 -#define LIL_CALLBACK_WRITE 1 -#define LIL_CALLBACK_READ 2 -#define LIL_CALLBACK_STORE 3 -#define LIL_CALLBACK_SOURCE 4 -#define LIL_CALLBACK_ERROR 5 -#define LIL_CALLBACK_SETVAR 6 -#define LIL_CALLBACK_GETVAR 7 - #include #include @@ -40,22 +31,15 @@ struct lil_list; struct lil; typedef struct lil_value *(*lil_func_proc_t)(struct lil *lil, size_t argc, struct lil_value **argv); -typedef void (*lil_exit_callback_proc_t)(struct lil *lil, - struct lil_value *arg); -typedef void (*lil_write_callback_proc_t)(struct lil *lil, const char *msg); -typedef char *(*lil_read_callback_proc_t)(struct lil *lil, const char *name); -typedef char *(*lil_source_callback_proc_t)(struct lil *lil, const char *name); -typedef void (*lil_store_callback_proc_t)(struct lil *lil, const char *name, - const char *data); -typedef void (*lil_error_callback_proc_t)(struct lil *lil, size_t pos, - const char *msg); -typedef int (*lil_setvar_callback_proc_t)(struct lil *lil, const char *name, - struct lil_value **value); -typedef int (*lil_getvar_callback_proc_t)(struct lil *lil, const char *name, - struct lil_value **value); -typedef void (*lil_callback_proc_t)(void); -struct lil *lil_new(void); +struct lil_callbacks { + int (*setvar)(struct lil *lil, const char *name, + struct lil_value **value); + int (*getvar)(struct lil *lil, const char *name, + struct lil_value **value); +}; + +struct lil *lil_new(const struct lil_callbacks *callbacks); void lil_free(struct lil *lil); int lil_register(struct lil *lil, const char *name, lil_func_proc_t proc); @@ -65,8 +49,6 @@ struct lil_value *lil_parse(struct lil *lil, const char *code, size_t codelen, struct lil_value *lil_parse_value(struct lil *lil, struct lil_value *val, int funclevel); -void lil_callback(struct lil *lil, int cb, lil_callback_proc_t proc); - void lil_set_error(struct lil *lil, const char *msg); void lil_set_error_at(struct lil *lil, size_t pos, const char *msg); int lil_error(struct lil *lil, const char **msg, size_t *pos); From patchwork Thu Jul 1 06:15:51 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sean Anderson X-Patchwork-Id: 1499283 X-Patchwork-Delegate: trini@ti.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=lists.denx.de (client-ip=2a01:238:438b:c500:173d:9f52:ddab:ee01; helo=phobos.denx.de; envelope-from=u-boot-bounces@lists.denx.de; receiver=) Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20161025 header.b=rWckWO28; dkim-atps=neutral Received: from phobos.denx.de (phobos.denx.de [IPv6:2a01:238:438b:c500:173d:9f52:ddab:ee01]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 4GFp141CKzz9sWX for ; Thu, 1 Jul 2021 16:18:00 +1000 (AEST) Received: from h2850616.stratoserver.net (localhost [IPv6:::1]) by phobos.denx.de (Postfix) with ESMTP id 2DA3683292; Thu, 1 Jul 2021 08:16:46 +0200 (CEST) Authentication-Results: phobos.denx.de; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=u-boot-bounces@lists.denx.de Authentication-Results: phobos.denx.de; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="rWckWO28"; dkim-atps=neutral Received: by phobos.denx.de (Postfix, from userid 109) id EDC0E83289; Thu, 1 Jul 2021 08:16:36 +0200 (CEST) X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on phobos.denx.de X-Spam-Level: X-Spam-Status: No, score=-2.0 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,FREEMAIL_FROM,SPF_HELO_NONE autolearn=ham autolearn_force=no version=3.4.2 Received: from mail-qt1-x82e.google.com (mail-qt1-x82e.google.com [IPv6:2607:f8b0:4864:20::82e]) (using TLSv1.3 with cipher TLS_AES_128_GCM_SHA256 (128/128 bits)) (No client certificate requested) by phobos.denx.de (Postfix) with ESMTPS id E6B7E83224 for ; Thu, 1 Jul 2021 08:16:21 +0200 (CEST) Authentication-Results: phobos.denx.de; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=seanga2@gmail.com Received: by mail-qt1-x82e.google.com with SMTP id r20so3381582qtp.3 for ; Wed, 30 Jun 2021 23:16:21 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=M4Rh55YANvuXtSpYG6Jp5ee+K65tP18NTj2LTjrYNTY=; b=rWckWO281aphHwTsaiqNikXtQYMf93sFxUDHM2vYgbEVXz/v4L6S1jBuA9kVIM5Hov PnF0wv7Iegjllkq+CNachevStzPmHSae3D/rr0kYgC5nr5aiIBpor0kI7XlCax8iWfxU vvzPyj5gRFX5v9IOFIoKxLP/lIFEbFYGLUvjFxOLO4onfw0AcPaWhU4/BbfV4OsorUqy zUmdUwvg7UeSvcgQKuw/j8KiEbGUeIM6+Y6vaufK0qAWb/STgBRdP1sSlPIdlYK5yCkw yfr0tVtUKewM+ZIseLBDWiCjmXgp6T7ldRqK0JGwUyY4lZJWs3pi3BZn0jibpX0eje1g RNww== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=M4Rh55YANvuXtSpYG6Jp5ee+K65tP18NTj2LTjrYNTY=; b=jQ71SdFQNu57e0uFEzSOYm3xaR0CVKUlUSGp8/2lrbPm9ck4TF7NNPxX8hGtj4dG5p vgABOTMtSWMkReNOhofAH7RDvmnw/810HrhJuFH4Qkpbob/nFbSoIPazO35eG1XOiZLu oN5vOIv8i7qk4SMI6IseuHAeIR93Qhu0oKtDsci84mHhyHsbECKHECJWCAT7qVbf7PZW rNw9Xt2ZTq0QTk91ZXFJKfZNP5VQDGOU9qM8vESdhu6RGMALISA9pgIOyNr3ehym4L30 BqmJE+ktxbuILaFsLoG5KQGyI0Jt05YnalNjr3ZW9oBx8l8amw6BxcOm1sjAtkLolF+o qZGg== X-Gm-Message-State: AOAM532Sksn3FX08bunDzpEDvLiYybIPmMBuDsYX2xJnYqaTlZCN3LLJ 9PauzNHPl54bEk7K0izr/l1+81Hu3OE= X-Google-Smtp-Source: ABdhPJy9EmWN2vQTEZogfBBE2eaJFLlwDJhWA4YOI34QvWE//lvsPP9ExqkP/nL4VQpzFCFiUQdghw== X-Received: by 2002:a05:622a:409:: with SMTP id n9mr35900430qtx.261.1625120180640; Wed, 30 Jun 2021 23:16:20 -0700 (PDT) Received: from godwin.fios-router.home (pool-74-96-87-9.washdc.fios.verizon.net. [74.96.87.9]) by smtp.gmail.com with ESMTPSA id g21sm1684673qts.90.2021.06.30.23.16.20 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 30 Jun 2021 23:16:20 -0700 (PDT) From: Sean Anderson To: u-boot@lists.denx.de, Tom Rini Cc: =?utf-8?q?Marek_Beh=C3=BAn?= , Wolfgang Denk , Simon Glass , Roland Gaudig , Heinrich Schuchardt , Kostas Michalopoulos , Sean Anderson Subject: [RFC PATCH 08/28] cli: lil: Handle commands with dots Date: Thu, 1 Jul 2021 02:15:51 -0400 Message-Id: <20210701061611.957918-9-seanga2@gmail.com> X-Mailer: git-send-email 2.32.0 In-Reply-To: <20210701061611.957918-1-seanga2@gmail.com> References: <20210701061611.957918-1-seanga2@gmail.com> MIME-Version: 1.0 X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.34 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" X-Virus-Scanned: clamav-milter 0.103.2 at phobos.denx.de X-Virus-Status: Clean Some commands (such as md, mw, etc) use dot suffixes as options to the command. Therefore, we must ignore anything after a dot when finding commands. Signed-off-by: Sean Anderson --- common/cli_lil.c | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/common/cli_lil.c b/common/cli_lil.c index 539a4a3238..dd020e8452 100644 --- a/common/cli_lil.c +++ b/common/cli_lil.c @@ -579,7 +579,17 @@ static struct lil_var *lil_find_var(struct lil *lil, struct lil_env *env, static struct lil_func *lil_find_cmd(struct lil *lil, const char *name) { - return hm_get(&lil->cmdmap, name); + struct lil_func *r; + char *dot = strchr(name, '.'); + + /* Some U-Boot commands have dots in their names */ + if (dot) + *dot = '\0'; + r = hm_get(&lil->cmdmap, name); + + if (dot) + *dot = '.'; + return r; } static struct lil_func *add_func(struct lil *lil, const char *name) From patchwork Thu Jul 1 06:15:52 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sean Anderson X-Patchwork-Id: 1499286 X-Patchwork-Delegate: trini@ti.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=lists.denx.de (client-ip=85.214.62.61; helo=phobos.denx.de; envelope-from=u-boot-bounces@lists.denx.de; receiver=) Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20161025 header.b=mi4QHX+2; dkim-atps=neutral Received: from phobos.denx.de (phobos.denx.de [85.214.62.61]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 4GFp1g5x53z9sWX for ; Thu, 1 Jul 2021 16:18:31 +1000 (AEST) Received: from h2850616.stratoserver.net (localhost [IPv6:::1]) by phobos.denx.de (Postfix) with ESMTP id 40B8983267; Thu, 1 Jul 2021 08:16:58 +0200 (CEST) Authentication-Results: phobos.denx.de; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=u-boot-bounces@lists.denx.de Authentication-Results: phobos.denx.de; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="mi4QHX+2"; dkim-atps=neutral Received: by phobos.denx.de (Postfix, from userid 109) id 723418328F; Thu, 1 Jul 2021 08:16:41 +0200 (CEST) X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on phobos.denx.de X-Spam-Level: X-Spam-Status: No, score=-2.0 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,FREEMAIL_FROM,SPF_HELO_NONE autolearn=unavailable autolearn_force=no version=3.4.2 Received: from mail-qt1-x831.google.com (mail-qt1-x831.google.com [IPv6:2607:f8b0:4864:20::831]) (using TLSv1.3 with cipher TLS_AES_128_GCM_SHA256 (128/128 bits)) (No client certificate requested) by phobos.denx.de (Postfix) with ESMTPS id AB5008322D for ; Thu, 1 Jul 2021 08:16:22 +0200 (CEST) Authentication-Results: phobos.denx.de; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=seanga2@gmail.com Received: by mail-qt1-x831.google.com with SMTP id r20so3381598qtp.3 for ; Wed, 30 Jun 2021 23:16:22 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=NgXiQHDQn3J5iCEKzm5RyGa3MhJZv1yAdHIWMbwYgtQ=; b=mi4QHX+25H8sKicjAgtaZfYydSEQxRd444h36d2O0XwIJVrOcCnLLOGMcEoQpICfFD GeJxyMxksH7LnwIL1hGW/Xd71XJOR+F2xXFmvySkwYQcmLrJxhiZ68xShGXIoiZah0RS Hri3yH5JKzrAeXBcg4eihIgUcaLz+ub/+Swd8VA8H92ifESM7xoEVz4U4P9LY3PUGjXd OL+MOYOABvekMs3w9xZ2Hj8aBrToT+okosr0i3bJ4tjsY9W9GhcRGSyAK3X2bpwrNa8w MpblJJ3B23O69yAdVK2J3IImF/IovXN1U+jiR5bklumITMYXScCJTuCeztT1RGRg16DL +x2A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=NgXiQHDQn3J5iCEKzm5RyGa3MhJZv1yAdHIWMbwYgtQ=; b=a8uXUhxGTVT/h6r+wKH3eDKQKgHd9ybMKyci6mxA9fKmZ7w16cqJ+QQkJbGkAhiVuS mWt/rNXCI8YVPBz+g5mwW0EMMU9efe2tafZtM8P9yk9YF0L9g7lsTINezMK/aMb1APLu iJf1qOqbnGjXPjT2awadXC0xfeiEIdBHqEuvzbwEHnypyin5ZIJTnOPa/fB4yEiFjshk ZfxwgFrfnyJ6WFDBdOc4bFlumcXZw3ia+kKRAdBjgbW/TZ+s4eorFumS8Ko5NAu3TsyM tFqnoXgOjcu5x0NFhdawcMNfa3Y1gXc3OlRy5bI2LAHmZFM3gtlloTdAVkDyZPirjNFb o+Ng== X-Gm-Message-State: AOAM530u1ZwOrhwVXW/mUxY6lEt7OOuJBR32/k9AxvivGEeJ+Oz92TC9 iLdtDFv6/iA2S0DzyMbAq0JKA+9mIOY= X-Google-Smtp-Source: ABdhPJwDkaA3w/yxGcvLd/i2PNAXnRmHe5u7llaiPJ+NPR0fWDqYGC/TfVnlR54lbi+U1mnUdRNR6Q== X-Received: by 2002:a05:622a:1814:: with SMTP id t20mr8411263qtc.70.1625120181369; Wed, 30 Jun 2021 23:16:21 -0700 (PDT) Received: from godwin.fios-router.home (pool-74-96-87-9.washdc.fios.verizon.net. [74.96.87.9]) by smtp.gmail.com with ESMTPSA id g21sm1684673qts.90.2021.06.30.23.16.20 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 30 Jun 2021 23:16:21 -0700 (PDT) From: Sean Anderson To: u-boot@lists.denx.de, Tom Rini Cc: =?utf-8?q?Marek_Beh=C3=BAn?= , Wolfgang Denk , Simon Glass , Roland Gaudig , Heinrich Schuchardt , Kostas Michalopoulos , Sean Anderson Subject: [RFC PATCH 09/28] cli: lil: Use error codes Date: Thu, 1 Jul 2021 02:15:52 -0400 Message-Id: <20210701061611.957918-10-seanga2@gmail.com> X-Mailer: git-send-email 2.32.0 In-Reply-To: <20210701061611.957918-1-seanga2@gmail.com> References: <20210701061611.957918-1-seanga2@gmail.com> MIME-Version: 1.0 X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.34 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" X-Virus-Scanned: clamav-milter 0.103.2 at phobos.denx.de X-Virus-Status: Clean This adds numeric error codes to be used for errors. This makes it easier for code to differentiate between different classes of errors. Some error codes are not used yet, and will be added in future commits. The position argument has been removed for a few reasons: - It doesn't make sense for some errors, such as running out of memory - It was often not very helpful, since it pointed at the end of the command - Determining the location of an error is more difficult when the parsing and evaluating steps are done separately. In addition, lil.error is changed to lil.err for consistency. Signed-off-by: Sean Anderson --- common/cli_lil.c | 92 ++++++++++++++++++----------------------------- include/cli_lil.h | 36 +++++++++++++++++-- 2 files changed, 68 insertions(+), 60 deletions(-) diff --git a/common/cli_lil.c b/common/cli_lil.c index dd020e8452..1bdf0e5265 100644 --- a/common/cli_lil.c +++ b/common/cli_lil.c @@ -92,12 +92,7 @@ struct lil { struct lil_env *rootenv; struct lil_env *downenv; struct lil_value *empty; - enum { - ERROR_NOERROR = 0, - ERROR_DEFAULT, - ERROR_FIXHEAD, - } error; - size_t err_head; + enum lil_error err; char *err_msg; struct lil_callbacks callbacks; size_t parse_depth; @@ -117,6 +112,7 @@ struct expreval { static struct lil_value *next_word(struct lil *lil); static void register_stdcmds(struct lil *lil); +static void lil_set_error(struct lil *lil, enum lil_error err, const char *msg); #ifdef LIL_ENABLE_POOLS static struct lil_value **pool; @@ -974,7 +970,7 @@ static struct lil_list *substitute(struct lil *lil) struct lil_list *words = lil_alloc_list(); lil_skip_spaces(lil); - while (lil->head < lil->clen && !ateol(lil) && !lil->error) { + while (lil->head < lil->clen && !ateol(lil) && !lil->err) { struct lil_value *w = alloc_value(NULL); do { @@ -993,7 +989,7 @@ static struct lil_list *substitute(struct lil *lil) lil_free_value(wp); } while (lil->head < lil->clen && !eolchar(lil->code[lil->head]) && - !isspace(lil->code[lil->head]) && !lil->error); + !isspace(lil->code[lil->head]) && !lil->err); lil_skip_spaces(lil); lil_list_append(words, w); @@ -1042,13 +1038,7 @@ static struct lil_value *run_cmd(struct lil *lil, struct lil_func *cmd, struct lil_value *r; if (cmd->proc) { - size_t shead = lil->head; - r = cmd->proc(lil, words->c - 1, words->v + 1); - if (lil->error == ERROR_FIXHEAD) { - lil->error = ERROR_DEFAULT; - lil->err_head = shead; - } } else { lil_push_env(lil); lil->env->func = cmd; @@ -1102,18 +1092,18 @@ struct lil_value *lil_parse(struct lil *lil, const char *code, size_t codelen, lil->parse_depth++; #ifdef LIL_ENABLE_RECLIMIT if (lil->parse_depth > LIL_ENABLE_RECLIMIT) { - lil_set_error(lil, "Too many recursive calls"); + lil_set_error(lil, LIL_ERR_DEPTH, "Too many recursive calls"); goto cleanup; } #endif if (lil->parse_depth == 1) - lil->error = 0; + lil->err = LIL_ERR_NONE; if (funclevel) lil->env->breakrun = 0; - while (lil->head < lil->clen && !lil->error) { + while (lil->head < lil->clen && !lil->err) { if (words) lil_free_list(words); @@ -1122,7 +1112,7 @@ struct lil_value *lil_parse(struct lil *lil, const char *code, size_t codelen, val = NULL; words = substitute(lil); - if (!words || lil->error) + if (!words || lil->err) goto cleanup; if (words->c) { @@ -1136,7 +1126,7 @@ struct lil_value *lil_parse(struct lil *lil, const char *code, size_t codelen, snprintf(msg, sizeof(msg), "unknown function %s", words->v[0]->d); - lil_set_error_at(lil, lil->head, msg); + lil_set_error(lil, LIL_ERR_NOCMD, msg); goto cleanup; } } else { @@ -1182,38 +1172,25 @@ struct lil_value *lil_parse_value(struct lil *lil, struct lil_value *val, return lil_parse(lil, val->d, val->l, funclevel); } -void lil_set_error(struct lil *lil, const char *msg) +static void lil_set_error(struct lil *lil, enum lil_error err, const char *msg) { - if (lil->error) - return; - + assert(!lil->err); free(lil->err_msg); - lil->error = ERROR_FIXHEAD; - lil->err_head = 0; - lil->err_msg = strdup(msg ? msg : ""); + lil->err = err; + lil->err_msg = strdup(msg); } -void lil_set_error_at(struct lil *lil, size_t pos, const char *msg) +enum lil_error lil_error(struct lil *lil, const char **msg) { - if (lil->error) - return; + enum lil_error err = lil->err; - free(lil->err_msg); - lil->error = ERROR_DEFAULT; - lil->err_head = pos; - lil->err_msg = strdup(msg ? msg : ""); -} - -int lil_error(struct lil *lil, const char **msg, size_t *pos) -{ - if (!lil->error) - return 0; + if (!err) + return LIL_ERR_NONE; *msg = lil->err_msg; - *pos = lil->err_head; - lil->error = ERROR_NOERROR; + lil->err = LIL_ERR_NONE; - return 1; + return err; } static void ee_expr(struct expreval *ee); @@ -1642,7 +1619,7 @@ struct lil_value *lil_eval_expr(struct lil *lil, struct lil_value *code) struct expreval ee; code = lil_subst_to_value(lil, code); - if (lil->error) + if (lil->err) return NULL; ee.code = lil_to_string(code); @@ -1664,13 +1641,13 @@ struct lil_value *lil_eval_expr(struct lil *lil, struct lil_value *code) lil_free_value(code); switch (ee.error) { case EERR_DIVISION_BY_ZERO: - lil_set_error(lil, "division by zero in expression"); + lil_set_error(lil, LIL_ERR_DOM, "division by zero"); return NULL; case EERR_SYNTAX_ERROR: - lil_set_error(lil, "expression syntax error"); + lil_set_error(lil, LIL_ERR_SYNTAX, "expression syntax"); return NULL; case EERR_INVALID_EXPRESSION: - lil_set_error(lil, "invalid expression"); + lil_set_error(lil, LIL_ERR_SYNTAX, "invalid expression"); return NULL; case EERR_NO_ERROR: break; @@ -1994,7 +1971,7 @@ static struct lil_value *fnc_rename(struct lil *lil, size_t argc, char *msg = malloc(24 + strlen(oldname)); sprintf(msg, "unknown function '%s'", oldname); - lil_set_error_at(lil, lil->head, msg); + lil_set_error(lil, LIL_ERR_NOCMD, msg); free(msg); return NULL; } @@ -2397,7 +2374,7 @@ static struct lil_value *fnc_foreach(struct lil *lil, size_t argc, else lil_free_value(rv); - if (lil->env->breakrun || lil->error) + if (lil->env->breakrun || lil->err) break; } @@ -2499,7 +2476,7 @@ static struct lil_value *fnc_if(struct lil *lil, size_t argc, return NULL; val = lil_eval_expr(lil, argv[base]); - if (!val || lil->error) + if (!val || lil->err) return NULL; v = lil_to_boolean(val); @@ -2530,9 +2507,9 @@ static struct lil_value *fnc_while(struct lil *lil, size_t argc, if (argc < (size_t)base + 2) return NULL; - while (!lil->error && !lil->env->breakrun) { + while (!lil->err && !lil->env->breakrun) { val = lil_eval_expr(lil, argv[base]); - if (!val || lil->error) + if (!val || lil->err) return NULL; v = lil_to_boolean(val); @@ -2562,9 +2539,9 @@ static struct lil_value *fnc_for(struct lil *lil, size_t argc, return NULL; lil_free_value(lil_parse_value(lil, argv[0], 0)); - while (!lil->error && !lil->env->breakrun) { + while (!lil->err && !lil->env->breakrun) { val = lil_eval_expr(lil, argv[1]); - if (!val || lil->error) + if (!val || lil->err) return NULL; if (!lil_to_boolean(val)) { @@ -2889,12 +2866,12 @@ static struct lil_value *fnc_try(struct lil *lil, size_t argc, if (argc < 1) return NULL; - if (lil->error) + if (lil->err) return NULL; r = lil_parse_value(lil, argv[0], 0); - if (lil->error) { - lil->error = ERROR_NOERROR; + if (lil->err) { + lil->err = LIL_ERR_NONE; lil_free_value(r); if (argc > 1) @@ -2908,7 +2885,8 @@ static struct lil_value *fnc_try(struct lil *lil, size_t argc, static struct lil_value *fnc_error(struct lil *lil, size_t argc, struct lil_value **argv) { - lil_set_error(lil, argc > 0 ? lil_to_string(argv[0]) : NULL); + lil_set_error(lil, LIL_ERR_USER, + argc > 0 ? lil_to_string(argv[0]) : NULL); return NULL; } diff --git a/include/cli_lil.h b/include/cli_lil.h index b8df94a766..cdaa79fd15 100644 --- a/include/cli_lil.h +++ b/include/cli_lil.h @@ -39,6 +39,38 @@ struct lil_callbacks { struct lil_value **value); }; +/** + * enum lil_error - Error codes returned by @lil_error + * LIL_ERR_NONE: No error + * LIL_ERR_OOM: Out of memory + * LIL_ERR_CASE: Missing case statement; generally a bug in the program or the + * result of memory corruption. + * LIL_ERR_DEPTH: Too much recursion while parsing or evaluating + * LIL_ERR_SYNTAX: Unrecoverable syntax error + * LIL_ERR_EOF: Unexpected end of input; may be recoverable by prompting the + * user for more input. + * LIL_ERR_INTR: Interrupted by Ctrl-C + * LIL_ERR_NOCMD: No such command + * LIL_ERR_NOVAR: No such variable + * LIL_ERR_ARGC: Incorrect number of arguments + * LIL_ERR_DOM: Numerical argument out of range (typically division by zero) + * LIL_ERR_USER: Error set by user + */ +enum lil_error { + LIL_ERR_NONE = 0, + LIL_ERR_OOM, + LIL_ERR_CASE, + LIL_ERR_DEPTH, + LIL_ERR_SYNTAX, + LIL_ERR_EOF, + LIL_ERR_INTR, + LIL_ERR_NOCMD, + LIL_ERR_NOVAR, + LIL_ERR_ARGC, + LIL_ERR_DOM, + LIL_ERR_USER, +}; + struct lil *lil_new(const struct lil_callbacks *callbacks); void lil_free(struct lil *lil); @@ -49,9 +81,7 @@ struct lil_value *lil_parse(struct lil *lil, const char *code, size_t codelen, struct lil_value *lil_parse_value(struct lil *lil, struct lil_value *val, int funclevel); -void lil_set_error(struct lil *lil, const char *msg); -void lil_set_error_at(struct lil *lil, size_t pos, const char *msg); -int lil_error(struct lil *lil, const char **msg, size_t *pos); +enum lil_error lil_error(struct lil *lil, const char **msg); const char *lil_to_string(struct lil_value *val); ssize_t lil_to_integer(struct lil_value *val); From patchwork Thu Jul 1 06:15:53 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sean Anderson X-Patchwork-Id: 1499284 X-Patchwork-Delegate: trini@ti.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=lists.denx.de (client-ip=2a01:238:438b:c500:173d:9f52:ddab:ee01; helo=phobos.denx.de; envelope-from=u-boot-bounces@lists.denx.de; receiver=) Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20161025 header.b=GF8C0CE1; dkim-atps=neutral Received: from phobos.denx.de (phobos.denx.de [IPv6:2a01:238:438b:c500:173d:9f52:ddab:ee01]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 4GFp1S0mWyz9sWk for ; Thu, 1 Jul 2021 16:18:20 +1000 (AEST) Received: from h2850616.stratoserver.net (localhost [IPv6:::1]) by phobos.denx.de (Postfix) with ESMTP id CB07F8324F; Thu, 1 Jul 2021 08:16:55 +0200 (CEST) Authentication-Results: phobos.denx.de; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=u-boot-bounces@lists.denx.de Authentication-Results: phobos.denx.de; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="GF8C0CE1"; dkim-atps=neutral Received: by phobos.denx.de (Postfix, from userid 109) id DCF3D8328C; Thu, 1 Jul 2021 08:16:40 +0200 (CEST) X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on phobos.denx.de X-Spam-Level: X-Spam-Status: No, score=-2.0 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,FREEMAIL_FROM,SPF_HELO_NONE autolearn=unavailable autolearn_force=no version=3.4.2 Received: from mail-qv1-xf2e.google.com (mail-qv1-xf2e.google.com [IPv6:2607:f8b0:4864:20::f2e]) (using TLSv1.3 with cipher TLS_AES_128_GCM_SHA256 (128/128 bits)) (No client certificate requested) by phobos.denx.de (Postfix) with ESMTPS id 70F718323B for ; Thu, 1 Jul 2021 08:16:23 +0200 (CEST) Authentication-Results: phobos.denx.de; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=seanga2@gmail.com Received: by mail-qv1-xf2e.google.com with SMTP id p7so2428204qvn.5 for ; Wed, 30 Jun 2021 23:16:23 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=Sw1eTFmTy3H5IZEH9k692+ZRdlrXYDZ3jaN41rUaqhM=; b=GF8C0CE1GP/Nrgrkb+Nhi1ycWU0n0Zx7mJXC1yem8XedaEtm7RziOFcvfNU5JEZ4Sy nBI+3A1poiHBOA9Js67DEM+NLK5kQ/ggXPksDDjcEPENyrrrFMsozQixNiflviFAoCri kbnxz7wu77uucAtu+D/5Izi3ivOVW+nikJnV9IrmTBS0J4WFinClq9fk5/tHE3e6LInX 0GQnLgAR7OjtPS/HMu+v2WXFnRFCYcw8PbJBQERjIh+ZRt5YhImkWwZh0B+Pe3q2gO5H zM8hd7Z2+8g+hKCIeDt8pdXdvWnMqn6SUqLgbPTG2lIVLUgYcmN8EmsOUtuRcgfwwA5b X8Iw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=Sw1eTFmTy3H5IZEH9k692+ZRdlrXYDZ3jaN41rUaqhM=; b=c9fGYmNJd+BL+wfrreI/j5hTlfjzO8/qe9O0uAFcMe1+tQ+TH+3umTAXBodgtFXq+2 qIR0BSlsFM3LNBSj8YgNV2vtyCm3stzyAg1Z1Vr5Sxitf6R4LxvK2b6ymzkTNviqi19M l9hQn1rLDoj1K6cODC2GOIgeZI0FJFOQESJeJF1rY6/LibNmOO9KZxMwx38f/gOf4Bme +EdmXx85myOpMxwusRKpnRFBDYAKaO+riY29SU/SwZwv4t6iI/visIA4dCMi7ETZ7TPC 5RPZlWBzN+GV5sfJZEBZKCBsScjciEThmjertcRZXwOued4+m2npWDTR8GddRdstnrzx 9OCg== X-Gm-Message-State: AOAM530F2ONW35db1AQ64XCXepp8MH1jqPh70SRadZg9QJRQ/d/Rc5q7 sYOE+nwzPJW+//ru29sE7oOO18MhZ6s= X-Google-Smtp-Source: ABdhPJzkLn0924+7DeFan4uV2p/vZQr5SUDw5RT18i6/cSXU2e+btR6aqc7YXpBTEdW+U4UWbKuieg== X-Received: by 2002:a05:6214:1a0f:: with SMTP id fh15mr9094931qvb.29.1625120182126; Wed, 30 Jun 2021 23:16:22 -0700 (PDT) Received: from godwin.fios-router.home (pool-74-96-87-9.washdc.fios.verizon.net. [74.96.87.9]) by smtp.gmail.com with ESMTPSA id g21sm1684673qts.90.2021.06.30.23.16.21 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 30 Jun 2021 23:16:21 -0700 (PDT) From: Sean Anderson To: u-boot@lists.denx.de, Tom Rini Cc: =?utf-8?q?Marek_Beh=C3=BAn?= , Wolfgang Denk , Simon Glass , Roland Gaudig , Heinrich Schuchardt , Kostas Michalopoulos , Sean Anderson Subject: [RFC PATCH 10/28] cli: lil: Add printf-style format helper for errors Date: Thu, 1 Jul 2021 02:15:53 -0400 Message-Id: <20210701061611.957918-11-seanga2@gmail.com> X-Mailer: git-send-email 2.32.0 In-Reply-To: <20210701061611.957918-1-seanga2@gmail.com> References: <20210701061611.957918-1-seanga2@gmail.com> MIME-Version: 1.0 X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.34 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" X-Virus-Scanned: clamav-milter 0.103.2 at phobos.denx.de X-Virus-Status: Clean This adds a helper for dynamically-created error messages. Signed-off-by: Sean Anderson --- common/cli_lil.c | 43 ++++++++++++++++++++++++++++++++----------- 1 file changed, 32 insertions(+), 11 deletions(-) diff --git a/common/cli_lil.c b/common/cli_lil.c index 1bdf0e5265..f29c8065d8 100644 --- a/common/cli_lil.c +++ b/common/cli_lil.c @@ -113,6 +113,9 @@ struct expreval { static struct lil_value *next_word(struct lil *lil); static void register_stdcmds(struct lil *lil); static void lil_set_error(struct lil *lil, enum lil_error err, const char *msg); +static void lil_set_errorf(struct lil *lil, enum lil_error err, + const char *fmt, ...) + __attribute((format(__printf__, 3, 4))); #ifdef LIL_ENABLE_POOLS static struct lil_value **pool; @@ -1121,12 +1124,9 @@ struct lil_value *lil_parse(struct lil *lil, const char *code, size_t codelen, if (!cmd) { if (words->v[0]->l) { - char msg[64]; - - snprintf(msg, sizeof(msg), - "unknown function %s", - words->v[0]->d); - lil_set_error(lil, LIL_ERR_NOCMD, msg); + lil_set_errorf(lil, LIL_ERR_NOCMD, + "unknown function %s", + words->v[0]->d); goto cleanup; } } else { @@ -1180,6 +1180,30 @@ static void lil_set_error(struct lil *lil, enum lil_error err, const char *msg) lil->err_msg = strdup(msg); } +static void lil_set_errorf(struct lil *lil, enum lil_error err, + const char *fmt, ...) +{ + va_list args, saveargs; + size_t n; + + assert(!lil->err); + free(lil->err_msg); + lil->err = err; + + va_start(args, fmt); + va_copy(saveargs, args); + n = vsnprintf(NULL, 0, fmt, args) + 1; + va_end(args); + + lil->err_msg = calloc(1, n); + if (!lil->err_msg) { + lil->err = LIL_ERR_OOM; + return; + } + vsnprintf(lil->err_msg, n, fmt, saveargs); + va_end(saveargs); +} + enum lil_error lil_error(struct lil *lil, const char **msg) { enum lil_error err = lil->err; @@ -1968,11 +1992,8 @@ static struct lil_value *fnc_rename(struct lil *lil, size_t argc, newname = lil_to_string(argv[1]); func = lil_find_cmd(lil, oldname); if (!func) { - char *msg = malloc(24 + strlen(oldname)); - - sprintf(msg, "unknown function '%s'", oldname); - lil_set_error(lil, LIL_ERR_NOCMD, msg); - free(msg); + lil_set_errorf(lil, LIL_ERR_NOCMD, "unknown function %s", + oldname); return NULL; } From patchwork Thu Jul 1 06:15:54 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sean Anderson X-Patchwork-Id: 1499292 X-Patchwork-Delegate: trini@ti.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=lists.denx.de (client-ip=2a01:238:438b:c500:173d:9f52:ddab:ee01; helo=phobos.denx.de; envelope-from=u-boot-bounces@lists.denx.de; receiver=) Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20161025 header.b=WfAHrZL5; dkim-atps=neutral Received: from phobos.denx.de (phobos.denx.de [IPv6:2a01:238:438b:c500:173d:9f52:ddab:ee01]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 4GFp2n1GxWz9sWX for ; Thu, 1 Jul 2021 16:19:29 +1000 (AEST) Received: from h2850616.stratoserver.net (localhost [IPv6:::1]) by phobos.denx.de (Postfix) with ESMTP id 009AA83285; Thu, 1 Jul 2021 08:17:14 +0200 (CEST) Authentication-Results: phobos.denx.de; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=u-boot-bounces@lists.denx.de Authentication-Results: phobos.denx.de; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="WfAHrZL5"; dkim-atps=neutral Received: by phobos.denx.de (Postfix, from userid 109) id 4575783297; Thu, 1 Jul 2021 08:16:53 +0200 (CEST) X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on phobos.denx.de X-Spam-Level: X-Spam-Status: No, score=-2.0 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,FREEMAIL_FROM,SPF_HELO_NONE autolearn=ham autolearn_force=no version=3.4.2 Received: from mail-qt1-x831.google.com (mail-qt1-x831.google.com [IPv6:2607:f8b0:4864:20::831]) (using TLSv1.3 with cipher TLS_AES_128_GCM_SHA256 (128/128 bits)) (No client certificate requested) by phobos.denx.de (Postfix) with ESMTPS id 3F39C83250 for ; Thu, 1 Jul 2021 08:16:24 +0200 (CEST) Authentication-Results: phobos.denx.de; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=seanga2@gmail.com Received: by mail-qt1-x831.google.com with SMTP id v10so3373645qto.1 for ; Wed, 30 Jun 2021 23:16:24 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=QdGwPqXb8bo4KMun/OlSVqGm//O5em/UfX5YVBSSr6M=; b=WfAHrZL5FCi2sDqDhpJQivinGS/3rO3yR2nfHQ/5kO0ray3Z5bRmFj8gZ2tXS9cPez FUdykQ2zN5wLIr5M+/krELZJNBOptjDBj8D0ovRnSLZ8m70ufSGNZWAfRbMLASHd77d8 VaSPY0br+05tSMoL99OXCPXHvUREYiM8K+1HPAAQOwwQsfPX3/WrjNjTj7bCdz8JzK4K XQ3qhzuI0FNGJpY/g3vRGMwINP5E4rya7HLDTpTCnQl7rAAGYPUHHUKA2KfnZcO2lkR8 8lm35niIvJaV9nLlT9vYGtsS8OyNerYL1pl+6PP7WU20OTPbilbbPV7K10URsbvzxPKh /MMQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=QdGwPqXb8bo4KMun/OlSVqGm//O5em/UfX5YVBSSr6M=; b=JRV9G7MzVHRCl5YfsRGcEz4EWMABS4YPhC9FOwuyosRYZ+fwLRr7jYnPceeabV4wrX soyic4+fhl31XsdZHMpK92aiA538q110xmHD4Hbkjxs9CFqZG0XpThDjmcBYPNf9Y6mq KN5zPzQ4XIWsaKiZ4/OG1bjuVQrk9XtUG5KLNws2K1gojj9e8nZh3YTb5D2RMsfAr33Y HnhOwHEZYVq+OF9mrdcsTnAh87MeJLuOwaeD1qju7oFf7sz/1wVew9nUrJNDkm4EZM9z oojKC6u54Y4nPQ/GXNlnj+Em9F5xcYSsK081HYeYUWB9eeQpayXSPEmoZx2CICL4qADa mFzg== X-Gm-Message-State: AOAM530wuRJ3HhTHvyjY6QvK0BbNsuk8prV4AXvY4t8sl8oR3ydSqpb3 zm8yG9fkauxlOA3jTzXrs/3vBOBAC8U= X-Google-Smtp-Source: ABdhPJza6GR5VxjMo0ZUMxpDOUYVaaq29ysgUSUq6ZhjHUJ8jP2kkUB3fzLgyi9PmG692DdJOuc1QA== X-Received: by 2002:ac8:5cd5:: with SMTP id s21mr18540314qta.192.1625120182898; Wed, 30 Jun 2021 23:16:22 -0700 (PDT) Received: from godwin.fios-router.home (pool-74-96-87-9.washdc.fios.verizon.net. [74.96.87.9]) by smtp.gmail.com with ESMTPSA id g21sm1684673qts.90.2021.06.30.23.16.22 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 30 Jun 2021 23:16:22 -0700 (PDT) From: Sean Anderson To: u-boot@lists.denx.de, Tom Rini Cc: =?utf-8?q?Marek_Beh=C3=BAn?= , Wolfgang Denk , Simon Glass , Roland Gaudig , Heinrich Schuchardt , Kostas Michalopoulos , Sean Anderson Subject: [RFC PATCH 11/28] cli: lil: Add several helper functions for errors Date: Thu, 1 Jul 2021 02:15:54 -0400 Message-Id: <20210701061611.957918-12-seanga2@gmail.com> X-Mailer: git-send-email 2.32.0 In-Reply-To: <20210701061611.957918-1-seanga2@gmail.com> References: <20210701061611.957918-1-seanga2@gmail.com> MIME-Version: 1.0 X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.34 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" X-Virus-Scanned: clamav-milter 0.103.2 at phobos.denx.de X-Virus-Status: Clean This adds several helper functions for common error types. This helps keep down code duplication, and also ensures that each of these errors uses the same message. Signed-off-by: Sean Anderson --- common/cli_lil.c | 38 +++++++++++++++++++++++++++++++++----- 1 file changed, 33 insertions(+), 5 deletions(-) diff --git a/common/cli_lil.c b/common/cli_lil.c index f29c8065d8..c19a25b2bf 100644 --- a/common/cli_lil.c +++ b/common/cli_lil.c @@ -116,6 +116,9 @@ static void lil_set_error(struct lil *lil, enum lil_error err, const char *msg); static void lil_set_errorf(struct lil *lil, enum lil_error err, const char *fmt, ...) __attribute((format(__printf__, 3, 4))); +static void lil_set_error_oom(struct lil *lil); +static void lil_set_error_nocmd(struct lil *lil, const char *cmdname); +static void lil_set_error_intr(struct lil *lil); #ifdef LIL_ENABLE_POOLS static struct lil_value **pool; @@ -1124,9 +1127,8 @@ struct lil_value *lil_parse(struct lil *lil, const char *code, size_t codelen, if (!cmd) { if (words->v[0]->l) { - lil_set_errorf(lil, LIL_ERR_NOCMD, - "unknown function %s", - words->v[0]->d); + lil_set_error_nocmd(lil, + words->v[0]->d); goto cleanup; } } else { @@ -1204,6 +1206,33 @@ static void lil_set_errorf(struct lil *lil, enum lil_error err, va_end(saveargs); } +/* + * The next several functions provide helpers for throwing some formulaic + * errors. Their purpose is to ensure that the same wording is used everywhere + * the error is used. This reduces data size. + */ + +static void lil_set_error_oom(struct lil *lil) +{ + lil_set_error(lil, LIL_ERR_OOM, NULL); +} + +static void lil_set_error_argc(struct lil *lil, size_t expected) +{ + lil_set_errorf(lil, LIL_ERR_ARGC, "%s: expected %zu arguments", + lil->env->proc ?: lil->env->func->name, expected); +} + +static void lil_set_error_nocmd(struct lil *lil, const char *cmdname) +{ + lil_set_errorf(lil, LIL_ERR_NOCMD, "no such command %s", cmdname); +} + +static void lil_set_error_intr(struct lil *lil) +{ + lil_set_error(lil, LIL_ERR_INTR, "interrupted"); +} + enum lil_error lil_error(struct lil *lil, const char **msg) { enum lil_error err = lil->err; @@ -1992,8 +2021,7 @@ static struct lil_value *fnc_rename(struct lil *lil, size_t argc, newname = lil_to_string(argv[1]); func = lil_find_cmd(lil, oldname); if (!func) { - lil_set_errorf(lil, LIL_ERR_NOCMD, "unknown function %s", - oldname); + lil_set_error_nocmd(lil, oldname); return NULL; } From patchwork Thu Jul 1 06:15:55 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sean Anderson X-Patchwork-Id: 1499291 X-Patchwork-Delegate: trini@ti.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=lists.denx.de (client-ip=2a01:238:438b:c500:173d:9f52:ddab:ee01; helo=phobos.denx.de; envelope-from=u-boot-bounces@lists.denx.de; receiver=) Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20161025 header.b=tV9XACid; dkim-atps=neutral Received: from phobos.denx.de (phobos.denx.de [IPv6:2a01:238:438b:c500:173d:9f52:ddab:ee01]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 4GFp2c1KJrz9sWX for ; Thu, 1 Jul 2021 16:19:20 +1000 (AEST) Received: from h2850616.stratoserver.net (localhost [IPv6:::1]) by phobos.denx.de (Postfix) with ESMTP id 968FD83203; Thu, 1 Jul 2021 08:17:12 +0200 (CEST) Authentication-Results: phobos.denx.de; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=u-boot-bounces@lists.denx.de Authentication-Results: phobos.denx.de; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="tV9XACid"; dkim-atps=neutral Received: by phobos.denx.de (Postfix, from userid 109) id 0AFAA83253; Thu, 1 Jul 2021 08:16:52 +0200 (CEST) X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on phobos.denx.de X-Spam-Level: X-Spam-Status: No, score=-2.0 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,FREEMAIL_FROM,SPF_HELO_NONE autolearn=ham autolearn_force=no version=3.4.2 Received: from mail-qv1-xf2b.google.com (mail-qv1-xf2b.google.com [IPv6:2607:f8b0:4864:20::f2b]) (using TLSv1.3 with cipher TLS_AES_128_GCM_SHA256 (128/128 bits)) (No client certificate requested) by phobos.denx.de (Postfix) with ESMTPS id EE29483246 for ; Thu, 1 Jul 2021 08:16:24 +0200 (CEST) Authentication-Results: phobos.denx.de; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=seanga2@gmail.com Received: by mail-qv1-xf2b.google.com with SMTP id v17so2409462qvw.12 for ; Wed, 30 Jun 2021 23:16:24 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=yl8F6yq7QDxvWNd6OBtUqPpk3X+bGE9fNd8SyQkV2yY=; b=tV9XACidYrZ+tu4CWUfpa52lbqTvDNkOzIop/brFtJ/8JuVRhBPJAb055zyRH/8cm3 pGzZ06Net1hju/asmWam8Slp9svHXnPE1J7O8WJtS5FrwM9TQWR8SjF96lFFs85lFvgT j+Rdtt4Lfeo5+BxvFP1IRx4at6nIx5ExjBH3s31wHYwaZzp9tr+mQDd6MWpk+D58k4aK BbKzZLd1zjdPYX5Nzs4OJ8GhHr/QvzYpzCJLX5VsSKKU9Iu3b/ai8Vij6wZQlqFqX2+L YY36xx9OcjMchvIwn/PGm3UTzNSf6uCod1od0gmbhh4uEJkmOObruwluXLivGvn+w+TU sDWg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=yl8F6yq7QDxvWNd6OBtUqPpk3X+bGE9fNd8SyQkV2yY=; b=C4Fv5RRC0iNbhROo6sw/pV3lErxoYaefJe9+Mhs0yX2snXKB6pFRHMTVzxeqt966E6 lVDdEL7UyY0ZeHB5sWuWGgaeaNWhUIdtonRJ5U9YArOL7mOOjs3XkCq77mLz87xaPVP7 h275QpxHLSyl5C62RhtQXoH2f4tCV48WXIQn0p+rdFG4rGTUgJJrMeZJ6JPZ4q1b5HPT ZawIHWFtWOfCl+ZIUm6+M7qdg0N0b3cz7E7WJ5TFChAgSt5Sx3Riiv7VBExp/fxDaC1u qLiCak2phf8bglvS9x1d63ykcqHWAjFeIsLukfZSKiPnN2pJr0ub3UOzLTwDU5oojFfC WzNA== X-Gm-Message-State: AOAM532/q8M57qw/izchlzLFzKH/dsH2shvILSCJiqyUj1+KEJn5zuFk QqGUm4cWGw4sm/Xb6rP+Rs7t9UAVSBg= X-Google-Smtp-Source: ABdhPJw9LLLw0Ku3IXZst8gTH4Db9108RFfp4k/prCCvo0k4pwZT0nbZE4VpzG+FEpjIp9wYEYlPrg== X-Received: by 2002:a0c:e494:: with SMTP id n20mr2040435qvl.58.1625120183631; Wed, 30 Jun 2021 23:16:23 -0700 (PDT) Received: from godwin.fios-router.home (pool-74-96-87-9.washdc.fios.verizon.net. [74.96.87.9]) by smtp.gmail.com with ESMTPSA id g21sm1684673qts.90.2021.06.30.23.16.23 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 30 Jun 2021 23:16:23 -0700 (PDT) From: Sean Anderson To: u-boot@lists.denx.de, Tom Rini Cc: =?utf-8?q?Marek_Beh=C3=BAn?= , Wolfgang Denk , Simon Glass , Roland Gaudig , Heinrich Schuchardt , Kostas Michalopoulos , Sean Anderson Subject: [RFC PATCH 12/28] cli: lil: Check for ctrl-c Date: Thu, 1 Jul 2021 02:15:55 -0400 Message-Id: <20210701061611.957918-13-seanga2@gmail.com> X-Mailer: git-send-email 2.32.0 In-Reply-To: <20210701061611.957918-1-seanga2@gmail.com> References: <20210701061611.957918-1-seanga2@gmail.com> MIME-Version: 1.0 X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.34 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" X-Virus-Scanned: clamav-milter 0.103.2 at phobos.denx.de X-Virus-Status: Clean Check for ctrl-c in lil_parse. This works out to around every time a function or command is called. We also check at the beginning of lil_eval_expr so that constructs like while {1} {} get interrupted. Since there are no non-trivial commands in that example, lil_parse never gets to its ctrlc check. However, we do need to evaluate the loop expression, so that's a good place to put a check. Signed-off-by: Sean Anderson Reviewed-by: Simon Glass --- common/cli_lil.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/common/cli_lil.c b/common/cli_lil.c index c19a25b2bf..50e314a643 100644 --- a/common/cli_lil.c +++ b/common/cli_lil.c @@ -10,6 +10,7 @@ #include #include +#include #include #include #include @@ -1117,6 +1118,11 @@ struct lil_value *lil_parse(struct lil *lil, const char *code, size_t codelen, lil_free_value(val); val = NULL; + if (ctrlc()) { + lil_set_error(lil, LIL_ERR_INTR, "interrupted"); + goto cleanup; + } + words = substitute(lil); if (!words || lil->err) goto cleanup; @@ -1671,6 +1677,11 @@ struct lil_value *lil_eval_expr(struct lil *lil, struct lil_value *code) { struct expreval ee; + if (ctrlc()) { + lil_set_error(lil, LIL_ERR_INTR, "interrupted"); + return NULL; + } + code = lil_subst_to_value(lil, code); if (lil->err) return NULL; From patchwork Thu Jul 1 06:15:56 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sean Anderson X-Patchwork-Id: 1499287 X-Patchwork-Delegate: trini@ti.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=lists.denx.de (client-ip=85.214.62.61; helo=phobos.denx.de; envelope-from=u-boot-bounces@lists.denx.de; receiver=) Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20161025 header.b=qjcKCvez; dkim-atps=neutral Received: from phobos.denx.de (phobos.denx.de [85.214.62.61]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 4GFp1t5Tgvz9sWX for ; Thu, 1 Jul 2021 16:18:42 +1000 (AEST) Received: from h2850616.stratoserver.net (localhost [IPv6:::1]) by phobos.denx.de (Postfix) with ESMTP id 72F098326E; Thu, 1 Jul 2021 08:17:01 +0200 (CEST) Authentication-Results: phobos.denx.de; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=u-boot-bounces@lists.denx.de Authentication-Results: phobos.denx.de; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="qjcKCvez"; dkim-atps=neutral Received: by phobos.denx.de (Postfix, from userid 109) id 7E2538322D; Thu, 1 Jul 2021 08:16:45 +0200 (CEST) X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on phobos.denx.de X-Spam-Level: X-Spam-Status: No, score=-2.0 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,FREEMAIL_FROM,SPF_HELO_NONE autolearn=ham autolearn_force=no version=3.4.2 Received: from mail-qk1-x72e.google.com (mail-qk1-x72e.google.com [IPv6:2607:f8b0:4864:20::72e]) (using TLSv1.3 with cipher TLS_AES_128_GCM_SHA256 (128/128 bits)) (No client certificate requested) by phobos.denx.de (Postfix) with ESMTPS id D74BE83248 for ; Thu, 1 Jul 2021 08:16:25 +0200 (CEST) Authentication-Results: phobos.denx.de; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=seanga2@gmail.com Received: by mail-qk1-x72e.google.com with SMTP id q16so4983054qke.10 for ; Wed, 30 Jun 2021 23:16:25 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=tY7JkSxvyEnw3VdjCcU8l09ax3Xy/FeDeT8k/f8HH4g=; b=qjcKCvezjtThdcWYaeLnN331cRnHyio1GDzpjKTyejGZ0SkpgnXaMH0laozw9+jHPn g215Zvvuw5Fip0K1vrlk/EOltLTYCssfobBTDl4eGdu4Sp1atRf96WEt3F+kPUzY0cgQ OcOJb5ld6eTX+3rrWQNMo7qUWN1GrKDcxsFmT5GG9h4CFoRF1AhtgwklMkTqQ0gOmzG6 ovAiAElQVtSkVakz2byZzZjilsOg5yg4C0jrZggaFQSn31iu+pzSDhPVDQlqJ6zn1Kwt ZmLgQMIm0Xz59F9BYLv1WTTYyLu2fGwHyCYCVnk7Z58BVjia/azQeq+FhHANz+aueuS2 Zr7Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=tY7JkSxvyEnw3VdjCcU8l09ax3Xy/FeDeT8k/f8HH4g=; b=MiRppp8jxwztnHQrnJ9kynC4RzL9xaYuPdtgxZNzgCO+m4emnTieedxQi1z2bfnS3N y//wCzKLf9JBZYuQZb0JC6RW6WDxIlnCkli/9WryTYV1YykU/IwnaMwyelQ1bjzKeEXd gk+zKHHTzPe+NPU+Jp4j6529TV88/zWPyoMPts/PfBBAdjGEvFcYpCwnElYQV5tblwzz 5Rp93Ltxc7jYPlSJ3AZhHGHTnnMtN2GlQgwX3h2/oYuTstM0w0MyXA5M7U3IA3ijscJV CMcKXAovUjGwjOpzZLHHVad29j888gtNj5kITP2dDRcrQxCuIgilDk9eD1b4SHKJYbwu 8Xww== X-Gm-Message-State: AOAM531IDZ6QvRRXrjIeqm0erAe7v/hUaReCT1bWGM9MedngsazV41Ca ge5GMoo4pD1FZjlHADhXzHtKVQo+3sA= X-Google-Smtp-Source: ABdhPJyP+sVXvQ8rFy/TrP4ph80ZYTUuekjAU1sTMZUoNV8Qpaad/bxBHwiSqpOcFM54GkaGwcFlVA== X-Received: by 2002:a37:2e81:: with SMTP id u123mr20026373qkh.24.1625120184389; Wed, 30 Jun 2021 23:16:24 -0700 (PDT) Received: from godwin.fios-router.home (pool-74-96-87-9.washdc.fios.verizon.net. [74.96.87.9]) by smtp.gmail.com with ESMTPSA id g21sm1684673qts.90.2021.06.30.23.16.23 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 30 Jun 2021 23:16:24 -0700 (PDT) From: Sean Anderson To: u-boot@lists.denx.de, Tom Rini Cc: =?utf-8?q?Marek_Beh=C3=BAn?= , Wolfgang Denk , Simon Glass , Roland Gaudig , Heinrich Schuchardt , Kostas Michalopoulos , Sean Anderson Subject: [RFC PATCH 13/28] cli: lil: Wire up LIL to the rest of U-Boot Date: Thu, 1 Jul 2021 02:15:56 -0400 Message-Id: <20210701061611.957918-14-seanga2@gmail.com> X-Mailer: git-send-email 2.32.0 In-Reply-To: <20210701061611.957918-1-seanga2@gmail.com> References: <20210701061611.957918-1-seanga2@gmail.com> MIME-Version: 1.0 X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.34 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" X-Virus-Scanned: clamav-milter 0.103.2 at phobos.denx.de X-Virus-Status: Clean This sets the shell to LIL when CONFIG_LIL is enabled. Repeated commands are not supporteed. Neither are partial commands a la Hush's secondary prompt. Setting and getting environmental variables is done through callbacks to assist with testing. Signed-off-by: Sean Anderson --- cmd/Kconfig | 12 +++++-- common/cli.c | 84 +++++++++++++++++++++++++++++++++++++++--------- common/cli_lil.c | 32 ++++++++++++++++++ 3 files changed, 111 insertions(+), 17 deletions(-) diff --git a/cmd/Kconfig b/cmd/Kconfig index 0a7b73cb6d..b61a7557a9 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -11,9 +11,14 @@ config CMDLINE Depending on the number of commands enabled, this can add substantially to the size of U-Boot. +if CMDLINE + +choice + prompt "Shell" + default HUSH_PARSER + config HUSH_PARSER bool "Use hush shell" - depends on CMDLINE help This option enables the "hush" shell (from Busybox) as command line interpreter, thus enabling powerful command line syntax like @@ -25,13 +30,14 @@ config HUSH_PARSER config LIL bool "Use LIL shell" - depends on CMDLINE help This options enables the "Little Interpreted Language" (LIL) shell as command line interpreter, thus enabling powerful command line syntax like `proc name {args} {body}' functions or `echo [some command]` command substitution ("tcl scripts"). +endchoice + if LIL config LIL_FULL @@ -42,6 +48,8 @@ config LIL_FULL endif +endif + config CMDLINE_EDITING bool "Enable command line editing" depends on CMDLINE diff --git a/common/cli.c b/common/cli.c index 048eacb9ef..ad5d76d563 100644 --- a/common/cli.c +++ b/common/cli.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -22,6 +23,53 @@ DECLARE_GLOBAL_DATA_PTR; +#ifdef CONFIG_LIL +static struct lil *lil; + +static int env_setvar(struct lil *lil, const char *name, + struct lil_value **value) +{ + if (env_set(name, lil_to_string(*value))) + return -1; + return 0; +} + +static int env_getvar(struct lil *lil, const char *name, + struct lil_value **value) +{ + *value = lil_alloc_string(env_get(name)); + return 1; +} + +static const struct lil_callbacks env_callbacks = { + .setvar = env_setvar, + .getvar = env_getvar, +}; + +static int lil_run(const char *cmd) +{ + int err; + struct lil_value *result = lil_parse(lil, cmd, 0, 0); + const char *err_msg, *strres = lil_to_string(result); + + /* The result may be very big, so use puts */ + if (strres && strres[0]) { + puts(strres); + putc('\n'); + } + lil_free_value(result); + + err = lil_error(lil, &err_msg); + if (err) { + if (err_msg) + printf("error: %s\n", err_msg); + else + printf("error: %d\n", err); + } + return !!err; +} +#endif + #ifdef CONFIG_CMDLINE /* * Run a command using the selected parser. @@ -32,7 +80,15 @@ DECLARE_GLOBAL_DATA_PTR; */ int run_command(const char *cmd, int flag) { -#if !CONFIG_IS_ENABLED(HUSH_PARSER) +#ifdef CONFIG_HUSH_PARSER + int hush_flags = FLAG_PARSE_SEMICOLON | FLAG_EXIT_FROM_LOOP; + + if (flag & CMD_FLAG_ENV) + hush_flags |= FLAG_CONT_ON_NEWLINE; + return parse_string_outer(cmd, hush_flags); +#elif defined(CONFIG_LIL) + return lil_run(cmd); +#else /* * cli_run_command can return 0 or 1 for success, so clean up * its result. @@ -41,12 +97,6 @@ int run_command(const char *cmd, int flag) return 1; return 0; -#else - int hush_flags = FLAG_PARSE_SEMICOLON | FLAG_EXIT_FROM_LOOP; - - if (flag & CMD_FLAG_ENV) - hush_flags |= FLAG_CONT_ON_NEWLINE; - return parse_string_outer(cmd, hush_flags); #endif } @@ -59,9 +109,7 @@ int run_command(const char *cmd, int flag) */ int run_command_repeatable(const char *cmd, int flag) { -#ifndef CONFIG_HUSH_PARSER - return cli_simple_run_command(cmd, flag); -#else +#ifdef CONFIG_HUSH_PARSER /* * parse_string_outer() returns 1 for failure, so clean up * its result. @@ -71,6 +119,10 @@ int run_command_repeatable(const char *cmd, int flag) return -1; return 0; +#elif defined(CONFIG_LIL) + return run_command(cmd, flag); +#else + return cli_simple_run_command(cmd, flag); #endif } #else @@ -90,7 +142,7 @@ int run_command_list(const char *cmd, int len, int flag) if (len == -1) { len = strlen(cmd); -#ifdef CONFIG_HUSH_PARSER +#if defined(CONFIG_HUSH_PARSER) || defined(CONFIG_LIL) /* hush will never change our string */ need_buff = 0; #else @@ -107,7 +159,9 @@ int run_command_list(const char *cmd, int len, int flag) } #ifdef CONFIG_HUSH_PARSER rcode = parse_string_outer(buff, FLAG_PARSE_SEMICOLON); -#else +#elif defined(CONFIG_LIL) + rcode = lil_run(buff); +#elif defined(CONFIG_CMDLINE) /* * This function will overwrite any \n it sees with a \0, which * is why it can't work with a const char *. Here we are making @@ -115,11 +169,9 @@ int run_command_list(const char *cmd, int len, int flag) * doing a malloc() which is actually required only in a case that * is pretty rare. */ -#ifdef CONFIG_CMDLINE rcode = cli_simple_run_command_list(buff, flag); #else rcode = board_run_command(buff); -#endif #endif if (need_buff) free(buff); @@ -241,9 +293,11 @@ void cli_init(void) { #ifdef CONFIG_HUSH_PARSER u_boot_hush_start(); +#elif defined(CONFIG_LIL) + lil = lil_new(&env_callbacks); #endif -#if defined(CONFIG_HUSH_INIT_VAR) +#ifdef CONFIG_HUSH_INIT_VAR hush_init_var(); #endif } diff --git a/common/cli_lil.c b/common/cli_lil.c index 50e314a643..66ee62bf33 100644 --- a/common/cli_lil.c +++ b/common/cli_lil.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -59,6 +60,7 @@ struct lil_var { struct lil_env { struct lil_env *parent; struct lil_func *func; + const char *proc; struct lil_var **var; size_t vars; struct hashmap varmap; @@ -1045,7 +1047,9 @@ static struct lil_value *run_cmd(struct lil *lil, struct lil_func *cmd, struct lil_value *r; if (cmd->proc) { + lil->env->proc = words->v[0]->d; r = cmd->proc(lil, words->c - 1, words->v + 1); + lil->env->proc = NULL; } else { lil_push_env(lil); lil->env->func = cmd; @@ -2967,8 +2971,33 @@ static struct lil_value *fnc_lmap(struct lil *lil, size_t argc, return NULL; } +static struct lil_value *fnc_builtin(struct lil *lil, size_t argc, + struct lil_value **lil_argv) +{ + int err, repeatable; + size_t i; + /* + * We need space for the function name, and the last argv must be NULL + */ + char **argv = calloc(sizeof(char *), argc + 2); + + argv[0] = (char *)lil->env->proc; + for (i = 0; i < argc; i++) + argv[i + 1] = (char *)lil_to_string(lil_argv[i]); + + err = cmd_process(0, argc + 1, argv, &repeatable, NULL); + if (err) + lil_set_errorf(lil, LIL_ERR_USER, "%s failed", argv[0]); + free(argv); + + return 0; +} + static void register_stdcmds(struct lil *lil) { + struct cmd_tbl *cmdtp, *start = ll_entry_start(struct cmd_tbl, cmd); + const int len = ll_entry_count(struct cmd_tbl, cmd); + lil_register(lil, "decr", fnc_decr); lil_register(lil, "eval", fnc_eval); lil_register(lil, "expr", fnc_expr); @@ -2984,6 +3013,9 @@ static void register_stdcmds(struct lil *lil) lil_register(lil, "try", fnc_try); lil_register(lil, "while", fnc_while); + for (cmdtp = start; cmdtp != start + len; cmdtp++) + lil_register(lil, cmdtp->name, fnc_builtin); + if (IS_ENABLED(CONFIG_LIL_FULL)) { lil_register(lil, "append", fnc_append); lil_register(lil, "char", fnc_char); From patchwork Thu Jul 1 06:15:57 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sean Anderson X-Patchwork-Id: 1499288 X-Patchwork-Delegate: trini@ti.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=lists.denx.de (client-ip=85.214.62.61; helo=phobos.denx.de; envelope-from=u-boot-bounces@lists.denx.de; receiver=) Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20161025 header.b=CH/X4sVj; dkim-atps=neutral Received: from phobos.denx.de (phobos.denx.de [85.214.62.61]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 4GFp241pyTz9sWX for ; Thu, 1 Jul 2021 16:18:52 +1000 (AEST) Received: from h2850616.stratoserver.net (localhost [IPv6:::1]) by phobos.denx.de (Postfix) with ESMTP id EBC7383246; Thu, 1 Jul 2021 08:17:05 +0200 (CEST) Authentication-Results: phobos.denx.de; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=u-boot-bounces@lists.denx.de Authentication-Results: phobos.denx.de; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="CH/X4sVj"; dkim-atps=neutral Received: by phobos.denx.de (Postfix, from userid 109) id 973D88322D; Thu, 1 Jul 2021 08:16:46 +0200 (CEST) X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on phobos.denx.de X-Spam-Level: X-Spam-Status: No, score=-2.0 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,FREEMAIL_FROM,SPF_HELO_NONE autolearn=ham autolearn_force=no version=3.4.2 Received: from mail-qk1-x72b.google.com (mail-qk1-x72b.google.com [IPv6:2607:f8b0:4864:20::72b]) (using TLSv1.3 with cipher TLS_AES_128_GCM_SHA256 (128/128 bits)) (No client certificate requested) by phobos.denx.de (Postfix) with ESMTPS id 792518324C for ; Thu, 1 Jul 2021 08:16:26 +0200 (CEST) Authentication-Results: phobos.denx.de; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=seanga2@gmail.com Received: by mail-qk1-x72b.google.com with SMTP id b2so4986606qka.7 for ; Wed, 30 Jun 2021 23:16:26 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=oQbmnjCPN0OG6omllTvCcV09BDM4xXSjTSyDDHFFB40=; b=CH/X4sVjV2qHQvVH14G+naAGQgbzqqEAwjVuGms26b6loUTc6WnM7SKn21pgVqVmKy pwlVzFnYJOLcOtk1YpBmjLS6WHOODBkUdI2F6WkwnxvIT/BqME6Oe/RnPRGA9c0QR9Au maGu/Q4Yog0TL0UGmrCSTCl2fKG3GT3dXBX/hR/QNk3wG9CxWbVOWKJBE4Yb4EER061J JlQawihFtol12Nqi4VwFsyZHi1NoSP5sYjeWutt7sTtceU7ebCE+c7tp79fQ57YBEZ+U DV71LdVd3yJd46fprSTTnfgoQkogSpOGoAOTuiiFJGPOU+CsD+8CNsQcMCU9EdsJepIU NlvA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=oQbmnjCPN0OG6omllTvCcV09BDM4xXSjTSyDDHFFB40=; b=jD6C6GGHM+vh6X+SAK6ffhlC3hBZYwl/+JVmeu7rGEzSzmhApUAY5m6MCxhf5vfHwb Lrbry3Jb4AvJAyyvlHAqQSiYShLQ6qhiBBobwE7WrQRa2U2aFRcTMi6a5E8DF8vkJn26 XKuUdAUaNI0vTvAnn5lBQM95Fh+t9I37xiz1xLlfEhIEFFG60IzINo+BOMwup0DDAg0o qLPADCaoiUj5ikhYhS6xp7LmDTctYMxmGW8/8DjP/QIzhGrdmQxzqq9aZsgluDXkz4fK GqK9tUTSbTuWm58pXKnDEvA+Qv5wu43GBwEzBeIrKeuKnAk/bftV70IOGbkbk6LPDXw3 U2mQ== X-Gm-Message-State: AOAM530PenYd2awtZf0yxZrohOJdDXNnU/9zjE5qhQxzeeLlMsiuZz9b IqV2mR+zTpiiTc9QGYSKM+98/hQ/f50= X-Google-Smtp-Source: ABdhPJzFPYIOD9NSAa2GNE7OFhx1g0HpNC/PT8X6uXDHANZNl1vyIwDsUek5lCoUQfi8P/65Q/4f/g== X-Received: by 2002:a05:620a:2229:: with SMTP id n9mr40058661qkh.41.1625120185146; Wed, 30 Jun 2021 23:16:25 -0700 (PDT) Received: from godwin.fios-router.home (pool-74-96-87-9.washdc.fios.verizon.net. [74.96.87.9]) by smtp.gmail.com with ESMTPSA id g21sm1684673qts.90.2021.06.30.23.16.24 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 30 Jun 2021 23:16:24 -0700 (PDT) From: Sean Anderson To: u-boot@lists.denx.de, Tom Rini Cc: =?utf-8?q?Marek_Beh=C3=BAn?= , Wolfgang Denk , Simon Glass , Roland Gaudig , Heinrich Schuchardt , Kostas Michalopoulos , Sean Anderson Subject: [RFC PATCH 14/28] cli: lil: Document structures Date: Thu, 1 Jul 2021 02:15:57 -0400 Message-Id: <20210701061611.957918-15-seanga2@gmail.com> X-Mailer: git-send-email 2.32.0 In-Reply-To: <20210701061611.957918-1-seanga2@gmail.com> References: <20210701061611.957918-1-seanga2@gmail.com> MIME-Version: 1.0 X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.34 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" X-Virus-Scanned: clamav-milter 0.103.2 at phobos.denx.de X-Virus-Status: Clean This documents the major structures of LIL. Signed-off-by: Sean Anderson --- common/cli_lil.c | 100 ++++++++++++++++++++++++++++++++++++++++++++++ include/cli_lil.h | 57 ++++++++++++++++++++++++++ 2 files changed, 157 insertions(+) diff --git a/common/cli_lil.c b/common/cli_lil.c index 66ee62bf33..7fbae1964a 100644 --- a/common/cli_lil.c +++ b/common/cli_lil.c @@ -29,20 +29,44 @@ #define HASHMAP_CELLS 256 #define HASHMAP_CELLMASK 0xFF +/** + * struct hashentry - An entry in a cell + * @k: The key + * @v: The value + */ struct hashentry { char *k; void *v; }; +/** + * struct hashcell - A list of entries with the same hash + * @e: An array of entries + * @c: The capacity of this cell + */ struct hashcell { struct hashentry *e; size_t c; }; +/** + * struct hashmap - An array-based hash map + * @cell: An array of cells + * + * This hash map uses separate chaining with an array for each cell. We cannot + * use the existing hash map functions (hsearch_r et al.) because they assume + * that the value is a string. + */ struct hashmap { struct hashcell cell[HASHMAP_CELLS]; }; +/** + * struct lil_value - A string and its length + * @l: The length of the value. + * @c: The capacity of this value (maximum of @l before requiring reallocation). + * @d: The contents of the value, as a nul-terminated string. + */ struct lil_value { size_t l; #ifdef LIL_ENABLE_POOLS @@ -51,12 +75,34 @@ struct lil_value { char *d; }; +/** + * struct lil_var - A variable + * @n: The name of this variable + * @env: The environment containing this variable + * @v: The value of this variable + */ struct lil_var { char *n; struct lil_env *env; struct lil_value *v; }; +/** + * struct lil_env - A function call's execution environment + * @parent: The parent of this environment. This is %NULL for the root + * environment. + * @func: The currently-executing function + * @proc: The name of the currently-executing built-in procedure + * @var: A list of the variables in this environment + * @vars: The number of variables in @var + * @varmap: A hash map mapping variable names to pointers to variables in @var + * @retval: The value set by "return" or "result" + * @retval_set: Whether @retval has been set + * @breakrun: Whether to immediately break out the current run of execution. + * This is set by "return" + * + * Variables are inherited from @parent. + */ struct lil_env { struct lil_env *parent; struct lil_func *func; @@ -69,12 +115,28 @@ struct lil_env { int breakrun; }; +/** + * struct list - A list of values + * @v: A list of pointers to &struct lil_value + * @c: The number of values in this list + * @cap: The space allocated for @v + */ struct lil_list { struct lil_value **v; size_t c; size_t cap; }; +/** + * struct lil_func - A function which may be evaluated with a list of arguments + * @name: The name of the function + * @code: A value containing LIL code to be evaluated + * @argnames: The names of variables to assign to the passed-in arguments + * @proc: A C function to use to evaluate this function + * + * @code and @argnames are only used to evaluate the function if @proc is %NULL. + * If @argnames is empty, then the arguments are assigned to a variable "args". + */ struct lil_func { char *name; struct lil_value *code; @@ -82,6 +144,31 @@ struct lil_func { lil_func_proc_t proc; }; +/** + * struct lil - The current state of the interpreter + * @code: The code which is being interpreted + * @rootcode: The top-level code (e.g. the code for the initial call to + * lil_parse()) + * @clen: The length of @code + * @head: The first uninterpreted part of this code, as an index of @code + * @ignoreeol: Whether to treat newlines as whitespace or command terminators + * @cmd: A list of the current commands + * @cmds: The number of commands in @cmd + * @cmdmap: A hash map mapping command names to pointers to commands in @cmd + * @env: The current environment to evaluate commands in + * @rootenv: The top-level "root" environment + * @downenv: The original environment after a call to "topeval" or "upeval" + * @empty: An empty value, allocated once + * @ERROR_NOERROR: There is no error. + * @ERROR_DEFAULT: There was an error. + * @ERROR_FIXHEAD: There was an error, but @err_head needs to be fixed. + * @ERROR_UNBALANCED: An opening quote or bracket lacks a balancing closing + * quote or bracket. + * @error: The current error status + * @err_head: The offset in @code which caused the @error + * @err_msg: An optional string describing the current @error + * @parse_depth: The depth of recursive function calls + */ struct lil { const char *code; /* need save on parse */ const char *rootcode; @@ -101,6 +188,19 @@ struct lil { size_t parse_depth; }; +/** + * struct expreval - A (mathematical) expression being evaluated + * @code: The complete code for this expression + * @len: The length of @code + * @head: The first unevaluated part of this expression, as an index of @code + * @ival: The integer value of this expression + * @EERR_NO_ERROR: There is no error + * @EERR_SYNTAX_ERROR: Syntax error. For now this is just mismatched + * parentheses. + * @EERR_DIVISION_BY_ZERO: Attempted division by zero + * @EERR_INVALID_EXPRESSION: A non-number was present + * @error: The error of this expression (if any) + */ struct expreval { const char *code; size_t len, head; diff --git a/include/cli_lil.h b/include/cli_lil.h index cdaa79fd15..91e79c12f4 100644 --- a/include/cli_lil.h +++ b/include/cli_lil.h @@ -13,10 +13,35 @@ #define LIL_VERSION_STRING "0.1" +/** + * enum lil_setvar - The strategy to use when creating new variables + */ enum lil_setvar { + /** + * @LIL_SETVAR_GLOBAL: Set in the root environment + */ LIL_SETVAR_GLOBAL = 0, + /** + * @LIL_SETVAR_LOCAL: Set, starting with the local environment + * + * Search for a variable. If one is found, overwrite it. Otherwise, + * create a new variable in the local environment. + */ LIL_SETVAR_LOCAL, + /** + * @LIL_SETVAR_LOCAL_NEW: Create in the local environment + * + * Create a new variable in the local environment. This never overrides + * existing variables (even if one exists in the local environment). + */ LIL_SETVAR_LOCAL_NEW, + /** + * @LIL_SETVAR_LOCAL_ONLY: Set in a local environment only + * + * Search for a variable in the local environment. If one is found, + * overwrite it. Otherwise, create a new variable in the local + * environment. + */ LIL_SETVAR_LOCAL_ONLY, }; @@ -32,9 +57,41 @@ struct lil; typedef struct lil_value *(*lil_func_proc_t)(struct lil *lil, size_t argc, struct lil_value **argv); +/** + * struct lil_callbacks - Functions called by LIL to allow overriding behavior + */ struct lil_callbacks { + /** + * @setvar: Called when a non-existent global variable is assigned + * + * @lil: The LIL interpreter + * + * @name: The name of the variable + * + * @value: A pointer to the value which would be assigned. This may be + * modified to assign a different value. + * + * This can be used to override or cancel the assignment of a variable + * + * @Return: A negative value to prevent the assignment, %0 to assign the + * original value, or a positive value to assign @value. + */ int (*setvar)(struct lil *lil, const char *name, struct lil_value **value); + /** + * @getvar: Called when a global variable is read + * + * @lil: The LIL interpreter + * + * @name The name of the variable + * + * @value: The pointer to the value which would be read. This may be + * modified to read a different value. + * + * @Return: A non-zero value to read the value pointed to by @value + * instead of the original value, or anything else to use the + * original value + */ int (*getvar)(struct lil *lil, const char *name, struct lil_value **value); }; From patchwork Thu Jul 1 06:15:58 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sean Anderson X-Patchwork-Id: 1499289 X-Patchwork-Delegate: trini@ti.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=lists.denx.de (client-ip=85.214.62.61; helo=phobos.denx.de; envelope-from=u-boot-bounces@lists.denx.de; receiver=) Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20161025 header.b=n8VAW6oC; dkim-atps=neutral Received: from phobos.denx.de (phobos.denx.de [85.214.62.61]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 4GFp2G00XVz9sWX for ; Thu, 1 Jul 2021 16:19:01 +1000 (AEST) Received: from h2850616.stratoserver.net (localhost [IPv6:::1]) by phobos.denx.de (Postfix) with ESMTP id 07CCD83276; Thu, 1 Jul 2021 08:17:08 +0200 (CEST) Authentication-Results: phobos.denx.de; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=u-boot-bounces@lists.denx.de Authentication-Results: phobos.denx.de; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="n8VAW6oC"; dkim-atps=neutral Received: by phobos.denx.de (Postfix, from userid 109) id 6EE048322D; Thu, 1 Jul 2021 08:16:50 +0200 (CEST) X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on phobos.denx.de X-Spam-Level: X-Spam-Status: No, score=-2.0 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,FREEMAIL_FROM,SPF_HELO_NONE autolearn=unavailable autolearn_force=no version=3.4.2 Received: from mail-qk1-x733.google.com (mail-qk1-x733.google.com [IPv6:2607:f8b0:4864:20::733]) (using TLSv1.3 with cipher TLS_AES_128_GCM_SHA256 (128/128 bits)) (No client certificate requested) by phobos.denx.de (Postfix) with ESMTPS id 304EC83251 for ; Thu, 1 Jul 2021 08:16:27 +0200 (CEST) Authentication-Results: phobos.denx.de; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=seanga2@gmail.com Received: by mail-qk1-x733.google.com with SMTP id z3so5015480qkl.4 for ; Wed, 30 Jun 2021 23:16:27 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=pQjm0zzCxQ27Aa+iWG9SvYy+a592NuSTf3geQ4pMPMA=; b=n8VAW6oCxxZ9lLXGBojYpVhs3Tz1SuUeU3smBiG5xolQvzMBGp+saAgrlLcEID8tJ2 RWuHOHwDpLFxtnfRPKDmvLA2d9QrCGI5n+Z0GCZS/UJu0SUEEv2c3G2namMqhlbGIkpb dYoBXkOM6AxSiHUdeJ2CTSbyG3C3p4F3GwO0GHZUb65BUNxam2mjtIqKKPjP7RO4ZpCe /RSiA7IGaZYvDgwIdoAaIoGG3NG0cIhkP5R7HN/FeMFlOSzdtJ4S+pNxdA6i29+2OZ8L gA5jt8AE+wVTVdEzRGipCVfRaK3ZSVIqopbAseKHXby7lJ2m7UkLi4A160A9HjU1Y/DG WIYg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=pQjm0zzCxQ27Aa+iWG9SvYy+a592NuSTf3geQ4pMPMA=; b=FfPPFXG2M19VCdW+Bud+RDyu88oEmfBn7H4IqFHA/P9fW+bdOS0Ikpd0vteX0EbxJO zMaO/nBFKye98dg9CJ8qmdU3bQc3rCg64QEbjJ4cysRHN7Tf0CGfxNPNfT8croOXnHl1 GhOl3xN1LuXwqVfBSPj7Bw2TqxEXO1z4PlaEECP1B6qK5tTvFvnefU/BF2ETbK1eoR+R V3zNdkGxvU1zbnCO8yd2A1aoAQXG/HLAuQDWsYrUKoz1hnx1eCbhwZqSZhs8UNYDrhvd /4ww7AC7tMkZfoHX4GzIOU0pboMneHkNWOunKC7WgAA11pU7LEV+JVNTWjmvqdtd+mIc +GEA== X-Gm-Message-State: AOAM533/8N8MVDh4o/YWChyS7gtiAoKT+7jPnLI4BSohKL8pEz90euwc HziQDFh1BdoajvED/5g/lww9jNxzTHg= X-Google-Smtp-Source: ABdhPJwt21PYmX2k8z4au0Wjru4VKFHPKU/Z/omKu5IJzHPYIKPy6QOwiqtavRFg6Wdp3WBT6rPUDg== X-Received: by 2002:a05:620a:5f3:: with SMTP id z19mr15438870qkg.454.1625120185894; Wed, 30 Jun 2021 23:16:25 -0700 (PDT) Received: from godwin.fios-router.home (pool-74-96-87-9.washdc.fios.verizon.net. [74.96.87.9]) by smtp.gmail.com with ESMTPSA id g21sm1684673qts.90.2021.06.30.23.16.25 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 30 Jun 2021 23:16:25 -0700 (PDT) From: Sean Anderson To: u-boot@lists.denx.de, Tom Rini Cc: =?utf-8?q?Marek_Beh=C3=BAn?= , Wolfgang Denk , Simon Glass , Roland Gaudig , Heinrich Schuchardt , Kostas Michalopoulos , Sean Anderson Subject: [RFC PATCH 15/28] cli: lil: Convert LIL_ENABLE_POOLS to Kconfig Date: Thu, 1 Jul 2021 02:15:58 -0400 Message-Id: <20210701061611.957918-16-seanga2@gmail.com> X-Mailer: git-send-email 2.32.0 In-Reply-To: <20210701061611.957918-1-seanga2@gmail.com> References: <20210701061611.957918-1-seanga2@gmail.com> MIME-Version: 1.0 X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.34 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" X-Virus-Scanned: clamav-milter 0.103.2 at phobos.denx.de X-Virus-Status: Clean This adds a Kconfig option to enable memory pools for some commonly-created and destroyed LIL structures. There is still some significant code duplication, so perhaps these functions can be refactored further to all use common pool functions. Signed-off-by: Sean Anderson --- cmd/Kconfig | 7 ++ common/cli_lil.c | 275 +++++++++++++++++++++++------------------------ 2 files changed, 143 insertions(+), 139 deletions(-) diff --git a/cmd/Kconfig b/cmd/Kconfig index b61a7557a9..28a387b380 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -46,6 +46,13 @@ config LIL_FULL This enables all LIL builtin functions, as well as expression support for arithmetic and bitwise operations. +config LIL_POOLS + bool "Use memory pools for LIL structures" + help + Enable pools for reusing values, lists, and environments. This will + use more memory but will cause considerably less memory fragmentation + and improve the script execution performance. + endif endif diff --git a/common/cli_lil.c b/common/cli_lil.c index 7fbae1964a..750a085f63 100644 --- a/common/cli_lil.c +++ b/common/cli_lil.c @@ -17,11 +17,6 @@ #include #include -/* Enable pools for reusing values, lists and environments. This will use more memory and - * will rely on the runtime/OS to free the pools once the program ends, but will cause - * considerably less memory fragmentation and improve the script execution performance. */ -/*#define LIL_ENABLE_POOLS*/ - /* Enable limiting recursive calls to lil_parse - this can be used to avoid call stack * overflows and is also useful when running through an automated fuzzer like AFL */ /*#define LIL_ENABLE_RECLIMIT 10000*/ @@ -69,7 +64,7 @@ struct hashmap { */ struct lil_value { size_t l; -#ifdef LIL_ENABLE_POOLS +#ifdef CONFIG_LIL_POOLS size_t c; #endif char *d; @@ -223,14 +218,12 @@ static void lil_set_error_oom(struct lil *lil); static void lil_set_error_nocmd(struct lil *lil, const char *cmdname); static void lil_set_error_intr(struct lil *lil); -#ifdef LIL_ENABLE_POOLS -static struct lil_value **pool; -static int poolsize, poolcap; -static struct lil_list **listpool; -static size_t listpoolsize, listpoolcap; -static struct lil_env **envpool; -static size_t envpoolsize, envpoolcap; -#endif +static __maybe_unused struct lil_value **pool; +static __maybe_unused int poolsize, poolcap; +static __maybe_unused struct lil_list **listpool; +static __maybe_unused size_t listpoolsize, listpoolcap; +static __maybe_unused struct lil_env **envpool; +static __maybe_unused size_t envpoolsize, envpoolcap; static unsigned long hm_hash(const char *key) { @@ -298,7 +291,7 @@ static int hm_has(struct hashmap *hm, const char *key) return 0; } -#ifdef LIL_ENABLE_POOLS +#ifdef CONFIG_LIL_POOLS static struct lil_value *alloc_from_pool(void) { if (poolsize > 0) { @@ -327,39 +320,48 @@ static void ensure_capacity(struct lil_value *val, size_t cap) val->d = realloc(val->d, val->c); } } +#else +static struct lil_value *alloc_from_pool(void) +{ + return NULL; +} + +static void release_to_pool(struct lil_value *val) { } +static void ensure_capacity(struct lil_value *val, size_t cap) { } #endif static struct lil_value *alloc_value_len(const char *str, size_t len) { -#ifdef LIL_ENABLE_POOLS - struct lil_value *val = alloc_from_pool(); -#else - struct lil_value *val = calloc(1, sizeof(struct lil_value)); -#endif + struct lil_value *val; + if (IS_ENABLED(CONFIG_LIL_POOLS)) + val = alloc_from_pool(); + else + val = calloc(1, sizeof(struct lil_value)); if (!val) return NULL; + if (str) { val->l = len; -#ifdef LIL_ENABLE_POOLS - ensure_capacity(val, len + 1); -#else - val->d = malloc(len + 1); - if (!val->d) { - free(val); - return NULL; + if (IS_ENABLED(CONFIG_LIL_POOLS)) { + ensure_capacity(val, len + 1); + } else { + val->d = malloc(len + 1); + if (!val->d) { + free(val); + return NULL; + } } -#endif memcpy(val->d, str, len); val->d[len] = 0; } else { val->l = 0; -#ifdef LIL_ENABLE_POOLS - ensure_capacity(val, 1); - val->d[0] = '\0'; -#else - val->d = NULL; -#endif + if (IS_ENABLED(CONFIG_LIL_POOLS)) { + ensure_capacity(val, 1); + val->d[0] = '\0'; + } else { + val->d = NULL; + } } return val; } @@ -375,76 +377,72 @@ struct lil_value *lil_clone_value(struct lil_value *src) if (!src) return NULL; -#ifdef LIL_ENABLE_POOLS - val = alloc_from_pool(); -#else - val = calloc(1, sizeof(struct lil_value)); -#endif + + if (IS_ENABLED(CONFIG_LIL_POOLS)) + val = alloc_from_pool(); + else + val = calloc(1, sizeof(struct lil_value)); if (!val) return NULL; val->l = src->l; if (src->l) { -#ifdef LIL_ENABLE_POOLS - ensure_capacity(val, val->l + 1); -#else - val->d = malloc(val->l + 1); - if (!val->d) { - free(val); - return NULL; + if (IS_ENABLED(CONFIG_LIL_POOLS)) { + ensure_capacity(val, val->l + 1); + } else { + val->d = malloc(val->l + 1); + if (!val->d) { + free(val); + return NULL; + } } -#endif memcpy(val->d, src->d, val->l + 1); } else { -#ifdef LIL_ENABLE_POOLS - ensure_capacity(val, 1); - val->d[0] = '\0'; -#else - val->d = NULL; -#endif + if (IS_ENABLED(CONFIG_LIL_POOLS)) { + ensure_capacity(val, 1); + val->d[0] = '\0'; + } else { + val->d = NULL; + } } return val; } int lil_append_char(struct lil_value *val, char ch) { -#ifdef LIL_ENABLE_POOLS - ensure_capacity(val, val->l + 2); - val->d[val->l++] = ch; - val->d[val->l] = '\0'; -#else - char *new = realloc(val->d, val->l + 2); + if (IS_ENABLED(CONFIG_LIL_POOLS)) { + ensure_capacity(val, val->l + 2); + val->d[val->l++] = ch; + val->d[val->l] = '\0'; + } else { + char *new = realloc(val->d, val->l + 2); - if (!new) - return 0; + if (!new) + return 0; - new[val->l++] = ch; - new[val->l] = 0; - val->d = new; -#endif + new[val->l++] = ch; + new[val->l] = 0; + val->d = new; + } return 1; } int lil_append_string_len(struct lil_value *val, const char *s, size_t len) { -#ifndef LIL_ENABLE_POOLS - char *new; -#endif - if (!s || !s[0]) return 1; -#ifdef LIL_ENABLE_POOLS - ensure_capacity(val, val->l + len + 1); - memcpy(val->d + val->l, s, len + 1); -#else - new = realloc(val->d, val->l + len + 1); - if (!new) - return 0; + if (IS_ENABLED(CONFIG_LIL_POOLS)) { + ensure_capacity(val, val->l + len + 1); + memcpy(val->d + val->l, s, len + 1); + } else { + char *new = realloc(val->d, val->l + len + 1); - memcpy(new + val->l, s, len + 1); - val->d = new; -#endif + if (!new) + return 0; + memcpy(new + val->l, s, len + 1); + val->d = new; + } val->l += len; return 1; } @@ -456,24 +454,20 @@ int lil_append_string(struct lil_value *val, const char *s) int lil_append_val(struct lil_value *val, struct lil_value *v) { -#ifndef LIL_ENABLE_POOLS - char *new; -#endif - if (!v || !v->l) return 1; -#ifdef LIL_ENABLE_POOLS - ensure_capacity(val, val->l + v->l + 1); - memcpy(val->d + val->l, v->d, v->l + 1); -#else - new = realloc(val->d, val->l + v->l + 1); - if (!new) - return 0; + if (IS_ENABLED(CONFIG_LIL_POOLS)) { + ensure_capacity(val, val->l + v->l + 1); + memcpy(val->d + val->l, v->d, v->l + 1); + } else { + char *new = realloc(val->d, val->l + v->l + 1); - memcpy(new + val->l, v->d, v->l + 1); - val->d = new; -#endif + if (!new) + return 0; + memcpy(new + val->l, v->d, v->l + 1); + val->d = new; + } val->l += v->l; return 1; } @@ -483,22 +477,21 @@ void lil_free_value(struct lil_value *val) if (!val) return; -#ifdef LIL_ENABLE_POOLS - release_to_pool(val); -#else - free(val->d); - free(val); -#endif + if (IS_ENABLED(CONFIG_LIL_POOLS)) { + release_to_pool(val); + } else { + free(val->d); + free(val); + } } struct lil_list *lil_alloc_list(void) { struct lil_list *list; -#ifdef LIL_ENABLE_POOLS - if (listpoolsize > 0) + if (IS_ENABLED(CONFIG_LIL_POOLS) && listpoolsize > 0) return listpool[--listpoolsize]; -#endif + list = calloc(1, sizeof(struct lil_list)); list->v = NULL; return list; @@ -514,19 +507,21 @@ void lil_free_list(struct lil_list *list) for (i = 0; i < list->c; i++) lil_free_value(list->v[i]); -#ifdef LIL_ENABLE_POOLS - list->c = 0; - if (listpoolsize == listpoolcap) { - listpoolcap = - listpoolcap ? (listpoolcap + listpoolcap / 2) : 32; - listpool = realloc(listpool, - sizeof(struct lil_list *) * listpoolcap); + if (IS_ENABLED(CONFIG_LIL_POOLS)) { + list->c = 0; + if (listpoolsize == listpoolcap) { + if (listpoolcap) + listpoolcap += listpoolcap / 2; + else + listpoolcap = 32; + listpool = realloc(listpool, + sizeof(*listpool) * listpoolcap); + } + listpool[listpoolsize++] = list; + } else { + free(list->v); + free(list); } - listpool[listpoolsize++] = list; -#else - free(list->v); - free(list); -#endif } void lil_list_append(struct lil_list *list, struct lil_value *val) @@ -603,8 +598,7 @@ struct lil_env *lil_alloc_env(struct lil_env *parent) { struct lil_env *env; -#ifdef LIL_ENABLE_POOLS - if (envpoolsize > 0) { + if (IS_ENABLED(CONFIG_LIL_POOLS) && envpoolsize > 0) { size_t i, j; env = envpool[--envpoolsize]; @@ -622,7 +616,7 @@ struct lil_env *lil_alloc_env(struct lil_env *parent) } return env; } -#endif + env = calloc(1, sizeof(struct lil_env)); env->parent = parent; return env; @@ -636,30 +630,33 @@ void lil_free_env(struct lil_env *env) return; lil_free_value(env->retval); -#ifdef LIL_ENABLE_POOLS - for (i = 0; i < env->vars; i++) { - free(env->var[i]->n); - lil_free_value(env->var[i]->v); - free(env->var[i]); - } - free(env->var); + if (IS_ENABLED(CONFIG_LIL_POOLS)) { + for (i = 0; i < env->vars; i++) { + free(env->var[i]->n); + lil_free_value(env->var[i]->v); + free(env->var[i]); + } + free(env->var); - if (envpoolsize == envpoolcap) { - envpoolcap = envpoolcap ? (envpoolcap + envpoolcap / 2) : 64; - envpool = - realloc(envpool, sizeof(struct lil_env *) * envpoolcap); + if (envpoolsize == envpoolcap) { + if (envpoolcap) + envpoolcap += envpoolcap / 2; + else + envpoolcap = 64; + envpool = realloc(envpool, + sizeof(*envpool) * envpoolcap); + } + envpool[envpoolsize++] = env; + } else { + hm_destroy(&env->varmap); + for (i = 0; i < env->vars; i++) { + free(env->var[i]->n); + lil_free_value(env->var[i]->v); + free(env->var[i]); + } + free(env->var); + free(env); } - envpool[envpoolsize++] = env; -#else - hm_destroy(&env->varmap); - for (i = 0; i < env->vars; i++) { - free(env->var[i]->n); - lil_free_value(env->var[i]->v); - free(env->var[i]); - } - free(env->var); - free(env); -#endif } static struct lil_var *lil_find_local_var(struct lil *lil, struct lil_env *env, From patchwork Thu Jul 1 06:15:59 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sean Anderson X-Patchwork-Id: 1499290 X-Patchwork-Delegate: trini@ti.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=lists.denx.de (client-ip=2a01:238:438b:c500:173d:9f52:ddab:ee01; helo=phobos.denx.de; envelope-from=u-boot-bounces@lists.denx.de; receiver=) Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20161025 header.b=Uz5Atiev; dkim-atps=neutral Received: from phobos.denx.de (phobos.denx.de [IPv6:2a01:238:438b:c500:173d:9f52:ddab:ee01]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 4GFp2R3VWRz9sWX for ; Thu, 1 Jul 2021 16:19:11 +1000 (AEST) Received: from h2850616.stratoserver.net (localhost [IPv6:::1]) by phobos.denx.de (Postfix) with ESMTP id 4C36683298; Thu, 1 Jul 2021 08:17:10 +0200 (CEST) Authentication-Results: phobos.denx.de; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=u-boot-bounces@lists.denx.de Authentication-Results: phobos.denx.de; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="Uz5Atiev"; dkim-atps=neutral Received: by phobos.denx.de (Postfix, from userid 109) id 0F0D383251; Thu, 1 Jul 2021 08:16:51 +0200 (CEST) X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on phobos.denx.de X-Spam-Level: X-Spam-Status: No, score=-2.0 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,FREEMAIL_FROM,SPF_HELO_NONE autolearn=unavailable autolearn_force=no version=3.4.2 Received: from mail-qv1-xf29.google.com (mail-qv1-xf29.google.com [IPv6:2607:f8b0:4864:20::f29]) (using TLSv1.3 with cipher TLS_AES_128_GCM_SHA256 (128/128 bits)) (No client certificate requested) by phobos.denx.de (Postfix) with ESMTPS id EC40F83258 for ; Thu, 1 Jul 2021 08:16:27 +0200 (CEST) Authentication-Results: phobos.denx.de; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=seanga2@gmail.com Received: by mail-qv1-xf29.google.com with SMTP id fi7so1887453qvb.0 for ; Wed, 30 Jun 2021 23:16:27 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=gaRO/GDk7N8Ri1FUGJ4kNQQOJ1Hu3IDUd4HjCJ0LS2g=; b=Uz5Atiev5jbRDSk1JsJof8iEHEaZxGUNy/KkkI1yofTOpUHYUAWR6gMJqwdX6Hz50V GMq0OEBEaEzi2ILdQURWAjw33z8ZoFw2vvUdfwIB0AUJzLB1+8SSR5HbBLOOg9pM34b+ 2RZQJEcUgE/OxoRBO8paIixFaLL8faqsrQKticA7dGGCQrQNV1QNuypZnHrniF0hI/S9 QSYI42pv8F0wWTg/ElpiVta80rpg9JQTEdEHbLkgpgBgMHidhFgTiMs36K5a0dvZiy6q iE1g3RSn6wW75vt7CHQZaLjb8hyVYqgqG4U9vINh3ONZnpwJMDAULUxw+ELFluzpTBW6 6lXg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=gaRO/GDk7N8Ri1FUGJ4kNQQOJ1Hu3IDUd4HjCJ0LS2g=; b=Jk38bTKUnz3lCJRKyr1FIS+rltJMjOghuEsWngCgKMLoGlo7n2DAazSUEFLbyknCOZ SRSiIkMBf6zyeRWOt8KiPvKyCXNbJF+y6buaZrvqVK8LD2bsWyBBMakHkpL3OdWUfmFs 747A/YfuSAVYQeavqO1GZW97jzMzSv2juODq+MqZl82+4qdjfdRfwqhZiXkUJaHTqt6R 7QKSY/oyFTTZFcDNgLhW8AUNyAlRiIKcn3k19JAjSV/GgAtVZLpvzlPDW0FI8SHnoSHG 1rWEvVuIpsvltlcfHkqGqQGESxs77wnNjsJhcNXHK0QkOQcfzCwPzm1UIiGsA1XtzhgU KWkg== X-Gm-Message-State: AOAM532AqL5MpYkSR2sKYaP+ik+5TA7vovii25Np+oxhfui3p4zEbBHX fgwgM5veLNO1Tr16EcLGtIfdJyisvdU= X-Google-Smtp-Source: ABdhPJz57/JZAD3pL5eLn2Xoz2HIsH2UWQMcdJmsvYzus1l4egB0aPFtBxkXtShWvCHApHZYVbiSzQ== X-Received: by 2002:a05:6214:17d2:: with SMTP id cu18mr41051914qvb.48.1625120186623; Wed, 30 Jun 2021 23:16:26 -0700 (PDT) Received: from godwin.fios-router.home (pool-74-96-87-9.washdc.fios.verizon.net. [74.96.87.9]) by smtp.gmail.com with ESMTPSA id g21sm1684673qts.90.2021.06.30.23.16.26 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 30 Jun 2021 23:16:26 -0700 (PDT) From: Sean Anderson To: u-boot@lists.denx.de, Tom Rini Cc: =?utf-8?q?Marek_Beh=C3=BAn?= , Wolfgang Denk , Simon Glass , Roland Gaudig , Heinrich Schuchardt , Kostas Michalopoulos , Sean Anderson Subject: [RFC PATCH 16/28] cli: lil: Convert LIL_ENABLE_RECLIMIT to KConfig Date: Thu, 1 Jul 2021 02:15:59 -0400 Message-Id: <20210701061611.957918-17-seanga2@gmail.com> X-Mailer: git-send-email 2.32.0 In-Reply-To: <20210701061611.957918-1-seanga2@gmail.com> References: <20210701061611.957918-1-seanga2@gmail.com> MIME-Version: 1.0 X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.34 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" X-Virus-Scanned: clamav-milter 0.103.2 at phobos.denx.de X-Virus-Status: Clean This adds a configuration option to set the recursion limit. I've set it to a (conservative) 1000 by default. In addition, there is an option to turn it off for a very minor space savings and performance increase. Signed-off-by: Sean Anderson --- Do we need this? Perhaps it should default to 0. cmd/Kconfig | 8 ++++++++ common/cli_lil.c | 10 ++-------- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/cmd/Kconfig b/cmd/Kconfig index 28a387b380..7c8962cfc2 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -53,6 +53,14 @@ config LIL_POOLS use more memory but will cause considerably less memory fragmentation and improve the script execution performance. +config LIL_RECLIMIT + int "LIL function recursion limit" + default 1000 + help + Enable limiting recursive calls to lil_parse - this can be used to + avoid call stack overflows and is also useful when running through an + automated fuzzer like AFL. Set to 0 to disable the recursion limit. + endif endif diff --git a/common/cli_lil.c b/common/cli_lil.c index 750a085f63..6c05531441 100644 --- a/common/cli_lil.c +++ b/common/cli_lil.c @@ -17,10 +17,6 @@ #include #include -/* Enable limiting recursive calls to lil_parse - this can be used to avoid call stack - * overflows and is also useful when running through an automated fuzzer like AFL */ -/*#define LIL_ENABLE_RECLIMIT 10000*/ - #define HASHMAP_CELLS 256 #define HASHMAP_CELLMASK 0xFF @@ -1198,12 +1194,10 @@ struct lil_value *lil_parse(struct lil *lil, const char *code, size_t codelen, lil_skip_spaces(lil); lil->parse_depth++; -#ifdef LIL_ENABLE_RECLIMIT - if (lil->parse_depth > LIL_ENABLE_RECLIMIT) { - lil_set_error(lil, LIL_ERR_DEPTH, "Too many recursive calls"); + if (CONFIG_LIL_RECLIMIT && lil->parse_depth > CONFIG_LIL_RECLIMIT) { + lil_set_error(lil, LIL_ERR_DEPTH, "recursion limit reached"); goto cleanup; } -#endif if (lil->parse_depth == 1) lil->err = LIL_ERR_NONE; From patchwork Thu Jul 1 06:16:00 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sean Anderson X-Patchwork-Id: 1499293 X-Patchwork-Delegate: trini@ti.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=lists.denx.de (client-ip=85.214.62.61; helo=phobos.denx.de; envelope-from=u-boot-bounces@lists.denx.de; receiver=) Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20161025 header.b=n4geB5L7; dkim-atps=neutral Received: from phobos.denx.de (phobos.denx.de [85.214.62.61]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 4GFp2y0dkjz9sWk for ; Thu, 1 Jul 2021 16:19:37 +1000 (AEST) Received: from h2850616.stratoserver.net (localhost [IPv6:::1]) by phobos.denx.de (Postfix) with ESMTP id 0A5E9832A2; Thu, 1 Jul 2021 08:17:17 +0200 (CEST) Authentication-Results: phobos.denx.de; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=u-boot-bounces@lists.denx.de Authentication-Results: phobos.denx.de; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="n4geB5L7"; dkim-atps=neutral Received: by phobos.denx.de (Postfix, from userid 109) id BE7378324F; Thu, 1 Jul 2021 08:16:53 +0200 (CEST) X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on phobos.denx.de X-Spam-Level: X-Spam-Status: No, score=-2.0 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,FREEMAIL_FROM,SPF_HELO_NONE autolearn=ham autolearn_force=no version=3.4.2 Received: from mail-qk1-x72c.google.com (mail-qk1-x72c.google.com [IPv6:2607:f8b0:4864:20::72c]) (using TLSv1.3 with cipher TLS_AES_128_GCM_SHA256 (128/128 bits)) (No client certificate requested) by phobos.denx.de (Postfix) with ESMTPS id AF4308326A for ; Thu, 1 Jul 2021 08:16:28 +0200 (CEST) Authentication-Results: phobos.denx.de; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=seanga2@gmail.com Received: by mail-qk1-x72c.google.com with SMTP id 65so5011287qko.5 for ; Wed, 30 Jun 2021 23:16:28 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=uYFW+eha2yjc2LZPtiy+Ds0cR6cXIiRrqbwimyt7+FU=; b=n4geB5L7p19jXag1WrjSqkGV7nEI2kusJcVINT4puxZMaiR9oS5SaesF/9FTqq2LVZ 9q8dCnI3eCP6DqoTjSgMqhX79w5zzODviGL29XFk66f4JVnz8JNC//3luy5MGxeRE/dv Y+0nsr1cCzQyry+RnWl2HEXTz9pCLeQxD4qUhlpZRbN5ZdJ8839xgAsQouJvdAjScWCJ uoeUg6JTqFJ8YNbg8FIHe44Je3YeZI1eHexyhuJ+rIC9IJ9kwBMHp7AxWoV2iVEPOE5D kJ6QxzEIkMS4+1csBkq9CUit29rJ7e8tabBP5UVnm/uohbMhkpynnPmcCwrZr+dUTJBw VomQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=uYFW+eha2yjc2LZPtiy+Ds0cR6cXIiRrqbwimyt7+FU=; b=Hu7jBCbsZNG5JGEaayNhgcXYlyyBLHJx38Eu0MO7IGShlok10reqaCsiKWYrRT0fK8 RuudjjBJWJuwzSwMCc64G5aUiDDNJnPTt1XGaJh8cRWwCE8vL+GbiSyrSRWBDZD3mVRF hyzTkY7WoXcT1FiyrLmE98nvrKg8zgRnBWvpR9e0afmG5xM95TbadWQp+YhmQC68/neo ps8e4r7p9HwoJ9D0bshkv8fIsRmNez2xKpQHqBV8SiKnesPwETXcNF/dPsrqKh9/Xp56 fECYs3lpVkqP8o97QR7py99gHmFddBwu7XDFgW1pMwuVz3IgjXfApL7B9hvvMqedw9J4 kEAw== X-Gm-Message-State: AOAM532OCmqT/y8B6y5iOhpxJ7q3ButjXXbZ+aSkA7C7Yfa+6EppI1fW Vl9WCk6ympfc8a1b1fTPKAIviJmiQ+o= X-Google-Smtp-Source: ABdhPJxlLACPvh7jIn1DMWXdzD1YBcNNArqC8OQgAryteJgyylY2X3szPAPoPC+ZH5mSNl/2rBaqdQ== X-Received: by 2002:a37:5886:: with SMTP id m128mr33895064qkb.414.1625120187340; Wed, 30 Jun 2021 23:16:27 -0700 (PDT) Received: from godwin.fios-router.home (pool-74-96-87-9.washdc.fios.verizon.net. [74.96.87.9]) by smtp.gmail.com with ESMTPSA id g21sm1684673qts.90.2021.06.30.23.16.26 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 30 Jun 2021 23:16:27 -0700 (PDT) From: Sean Anderson To: u-boot@lists.denx.de, Tom Rini Cc: =?utf-8?q?Marek_Beh=C3=BAn?= , Wolfgang Denk , Simon Glass , Roland Gaudig , Heinrich Schuchardt , Kostas Michalopoulos , Sean Anderson Subject: [RFC PATCH 17/28] test: Add tests for LIL Date: Thu, 1 Jul 2021 02:16:00 -0400 Message-Id: <20210701061611.957918-18-seanga2@gmail.com> X-Mailer: git-send-email 2.32.0 In-Reply-To: <20210701061611.957918-1-seanga2@gmail.com> References: <20210701061611.957918-1-seanga2@gmail.com> MIME-Version: 1.0 X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.34 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" X-Virus-Scanned: clamav-milter 0.103.2 at phobos.denx.de X-Virus-Status: Clean This tests several aspects of the parser. These tests are primarily adapted from the *.lil code examples included with upstream LIL. These tests should probably get their own category, especially if I add additional styles of tests. Signed-off-by: Sean Anderson Reviewed-by: Simon Glass --- Yes, I know checkpatch complains about the quoted string being split, but that warning is intended for user-visible strings. MAINTAINERS | 1 + test/cmd/Makefile | 1 + test/cmd/lil.c | 339 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 341 insertions(+) create mode 100644 test/cmd/lil.c diff --git a/MAINTAINERS b/MAINTAINERS index 0184de5f93..3bf460127b 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -771,6 +771,7 @@ M: Sean Anderson S: Maintained F: common/cli_lil.c F: include/cli_lil.h +F: test/cmd/lil.c LOGGING M: Simon Glass diff --git a/test/cmd/Makefile b/test/cmd/Makefile index 2cfe43a6bd..4f7440cb44 100644 --- a/test/cmd/Makefile +++ b/test/cmd/Makefile @@ -5,6 +5,7 @@ ifdef CONFIG_HUSH_PARSER obj-$(CONFIG_CONSOLE_RECORD) += test_echo.o endif +obj-$(CONFIG_LIL_FULL) += lil.o obj-y += mem.o obj-$(CONFIG_CMD_ADDRMAP) += addrmap.o obj-$(CONFIG_CMD_MEM_SEARCH) += mem_search.o diff --git a/test/cmd/lil.c b/test/cmd/lil.c new file mode 100644 index 0000000000..896b2fed15 --- /dev/null +++ b/test/cmd/lil.c @@ -0,0 +1,339 @@ +// SPDX-License-Identifier: GPL-2.0+ AND Zlib +/* + * Copyright (C) 2021 Sean Anderson + * Copyright (C) 2010-2021 Kostas Michalopoulos + * + * This file contains code which originated from the LIL project, licensed under + * Zlib. All modifications are licensed under GPL-2.0+ + */ + +#include +#include +#include +#include +#include +#include + +const char helpers[] = + "proc assert {cond} {" + "if not [upeval expr [set cond]] {" + "error [set cond]" + "}" + "};" + "proc assert_err {cmd} {" + "set ok 1;" + "try {upeval $cmd; set ok 0} {};" + "assert {$ok};" + "};" + "proc asserteq {expr1 expr2} {" + "set val1 [upeval 'expr \"$expr1\"'];" + "set val2 [upeval 'expr \"$expr2\"'];" + "if {$val1 != $val2} {" + "error '$expr1 == ${expr2}: " + "Expected ${val1}, got $val2'" + "}" + "};" + "proc asserteq_str {expr1 expr2} {" + "set val1 [upeval 'subst \"$expr1\"'];" + "set val2 [upeval 'subst \"$expr2\"'];" + "if not [streq $val1 $val2] {" + "error '$expr1 == ${expr2}: " + "Expected ${val1}, got $val2'" + "}" + "};" + "proc asserteq_list {xs ys} {" + "set xlen [count $xs];" + "set ylen [count $ys];" + "if not {$xlen == $ylen} {" + "error '\\[count ${xs}\\] == \\[count ${ys}\\]: " + "Expected ${xlen}, got $ylen'\n" + "};" + "for {set i 0} {$i < $xlen} {incr i} {" + "set x [index $xs $i];" + "set y [index $ys $i];" + "if not {[streq $x $y]} {" + "error '$xs == ${ys}: " + "Expected $x at ${i}, got $y'" + "}" + "}" + "}"; + +static const struct { + const char *name; + const char *cmd; +} lil_tests[] = { + {"and", + "proc and args {" + "foreach [slice $args 1] {" + "upeval 'downeval \\'set v \\'\\[${i}\\]';" + "if not $v { return 0 }" + "};" + "return 1" + "};" + "set a 0;" + "set final [and {set a 3} {return 0} {set a 32}];" + "asserteq 0 {$final};" + "assert 3 {$a};" + }, + {"assert", + "assert 1;" + "assert_err {assert 0};" + "asserteq 1 1;" + "assert_err {asserteq 1 0};" + "asserteq_str {string one} {string one};" + "assert_err {asserteq_str {string one} {string two}};" + "asserteq_list [list 1 2 3] [list 1 2 3];" + "assert_err {asserteq_list [list 1 2] [list 1 2 3]};" + "assert_err {asserteq_list [list 1 2 3] [list 1 2]};" + "assert_err {asserteq_list [list 1 2 3] [list 1 2 4]};" + }, + {"downeval", + "proc grab-some-list {} {" + "set items {};" + "upeval {" + "foreach $some-list {" + "downeval 'append items $i'" + "}" + "};" + "return $items" + "};" + "set some-list [list foo bar baz blah moo boo];" + "asserteq_list $some-list [grab-some-list]" + }, + {"expr", + "asserteq 7 {1 + ( 2 * 3 )};" + "asserteq 7 {1+(2*3)};" + "asserteq -6 {1+ ~(2*3)};" + "asserteq -6 {1 + ~( 2 * 3 )};" + "asserteq -6 {1 +~ (2*3 )};" + "asserteq -6 {~(2*3)+1};" + "asserteq 0 {1*!(2+2)};" + "asserteq -1 {~!(!{})};" + "asserteq 1 {1 +~*(2*3)};" + "asserteq 1 {'hello'};" + "asserteq 0 {0};" + "asserteq 0 {{}};" + "asserteq 1 {()};" + "asserteq 1 {( )};" + "asserteq_str '' {[expr]};" + }, + {"factorial", + "proc fact {n} {" + "if {$n} {" + "expr {$n * [fact [expr {$n - 1}]]}" + "} {" + "return 1" + "}" + "};" + "asserteq 1 {[fact 0]};" + "asserteq 1 {[fact 1]};" + "asserteq 6 {[fact 3]};" + "asserteq 3628800 {[fact 10]};" + "asserteq 2432902008176640000 {[fact 20]}" + }, + {"filter", + "set short_procs [filter [reflect procs] {[length $x] < 5}];" + "foreach $short_procs {assert {[length $i] < 5}}" + }, + {"funcs", + "proc lapply {list proc} {" + "set ret {};" + "foreach $list {" + "append ret [$proc $i];" + "};" + "return $ret" + "};" + "set list [list {bad's day} {good's day} eh??];" + "asserteq_list [lapply $list split] [list " + "[list {bad's} day] " + "[list {good's} day] " + "[list eh??]" + "];" + "asserteq_list [lapply $list length] [list 9 10 4];" + "asserteq_list [lapply $list [proc {a} {" + "return [index [split $a] 0]" + "}]] [list {bad's} {good's} eh??]" + }, + {"lists", + "set l [list foo bar baz bad];" + "asserteq_str baz {[index $l 2]};" + "append l 'Hello, world!';" + "asserteq_list $l [list foo bar baz bad 'Hello, world!'];" + "set l [subst $l];" + "asserteq_list $l [list foo bar baz bad Hello, world!];" + "lmap $l foox barx bamia;" + "asserteq_str foo {$foox};" + "asserteq_str bar {$barx};" + "asserteq_str baz {$bamia};" + "set l {one # linebreaks are ignored in list parsing mode\n" + "\n" + "two;three # a semicolon still counts as line break\n" + " # (which in list mode is treated as a\n" + " # separator for list entries)\n" + "# of course a semicolon inside quotes is treated like normal\n" + "three';'and';a;half'\n" + "# like in code mode, a semicolon will stop the comment; four\n" + "\n" + "# below we have a quote, square brackets for inline\n" + "# expansions are still taken into consideration\n" + "[quote {this line will be ignored completely\n" + " as will this line and instead be replaced\n" + " with the 'five' below since while in code\n" + " mode (that is, inside the brackets here)\n" + " linebreaks are still processed}\n" + " quote five]\n" + "\n" + "# The curly brackets are also processed so the next three\n" + "# lines will show up as three separate lines\n" + "{six\n" + "seven\n" + "eight}}\n" + "asserteq_list $l [list one two three 'three;and;a;half' four " + "five 'six\\nseven\\neight'];" + }, + {"local", + "proc bits-for {x} {" + "local y bits;" + "set y 0 bits 0;" + "while {$y <= $x} {" + "incr bits;" + "set y [expr 1 << $bits]" + "};" + "return $bits" + "};" + "set y 1001;" + "set bits [bits-for $y];" + "set x 45;" + "set bitsx [bits-for $x];" + "asserteq 1001 {$y};" + "asserteq 10 {$bits};" + "asserteq 45 {$x};" + "asserteq 6 {$bitsx}" + }, + {"multiline comment", + "# this line will not be executed, but the following will\n" + "set ok1 1\n" + "## This is a multiline comment\n" + " which, as the name implies,\n" + " spans multiple lines.\n" + "set ok2 1\n" + " the code above wouldn't execute,\n" + " but this will --> ##set ok3 1\n" + "### more than two #s will not count as multiline comments\n" + "set ok4 1\n" + "# Note that semicolons can be used as linebreaks so\n" + "# this code will be executed: ; set ok5 1\n" + "##\n" + " ...however inside multiline comments semicolons do not\n" + " stop the comment section (pretty much like linebreaks)\n" + " and this code will not be executed: ; set ok6 1\n" + "##\n" + "# Also note that unlike in regular code, semicolons cannot\n" + "# be escaped in single-line comments, e.g.: ; set ok7 1\n" + "asserteq_str 1 {$ok1};" + "assert {![reflect has-var ok2]}" + "asserteq_str 1 {$ok3};" + "asserteq_str 1 {$ok4};" + "asserteq_str 1 {$ok5};" + "assert {![reflect has-var ok6]}" + "asserteq_str 1 {$ok7};" + }, + {"multiline code", + "asserteq_list [list hello \\\n" + " world] [list hello world]" + }, + {"return", + "proc uses_return {} {" + "return 1;" + "return 0;" + "};" + "proc doesnt_use_return {} {" + "quote 1;" + "};" + "proc uses_result {} {" + "result 1;" + "quote 0;" + "};" + "assert {[uses_return]};" + "assert {[doesnt_use_return]};" + "assert {[uses_result]}" + }, + {"strings", + "set a 'This is a string';" + "set b 'This is another string';" + "asserteq 16 {[length $a]};" + "asserteq 22 {[length $b]};" + "asserteq_str a {[charat $a [expr [length $a] / 2]]};" + "asserteq_str t {[charat $b [expr [length $b] / 2]]};" + "asserteq 97 {[codeat $a [expr [length $a] / 2]]};" + "asserteq 116 {[codeat $b [expr [length $b] / 2]]};" + "asserteq 10 {[strpos $a string]};" + "asserteq 16 {[strpos $b string]};" + "asserteq -78 {[compare $a $b]};" + "assert {![streq $a $b]};" + "asserteq_str 'This is a foo' {[repstr $a string foo]};" + "asserteq_str 'This is another foo' {[repstr $b string foo]};" + "asserteq_list [split $a] [list This is a string];" + "asserteq_list [split $b] [list This is another string];" + }, + {"topeval", + "proc does-something {} {" + "topeval {" + "asserteq 10 {$x};" + "set x 42;" + "downeval {set y [expr $x * 10]}" + "};" + "asserteq 420 {$y}" + "};" + "proc calls-something {} {" + "local x;" + "set x 33;" + "does-something;" + "asserteq 33 {$x};" + "asserteq 420 {$y}" + "};" + "set x 10;" + "set y 20;" + "calls-something;" + "asserteq 42 {$x};" + "asserteq 420 {$y}" + }, + {"trim", + "set str ' Hello, world! ';" + "asserteq_str 'Hello, world!' {[trim $str]};" + "asserteq_str 'Hello, world! ' {[ltrim $str]};" + "asserteq_str ' Hello, world!' {[rtrim $str]};" + "asserteq_str 'Hello world' {[foreach [split $str] {" + "quote [trim $i {,!}]" + "}]};" + "asserteq_str 'Hello world' {[filter [split $str {,! }] {" + "[length $x] > 0" + "}]};" + }, +}; + +static int lib_test_lil(struct unit_test_state *uts) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(lil_tests); i++) { + const char *err_msg; + enum lil_error err; + struct lil *lil = lil_new(NULL); + + lil_free_value(lil_parse(lil, helpers, sizeof(helpers) - 1, 0)); + ut_asserteq(LIL_ERR_NONE, lil_error(lil, &err_msg)); + lil_free_value(lil_parse(lil, lil_tests[i].cmd, 0, 0)); + err = lil_error(lil, &err_msg); + if (err) { + ut_failf(uts, __FILE__, __LINE__, __func__, + lil_tests[i].name, "err=%d: %s", err, err_msg); + lil_free(lil); + return CMD_RET_FAILURE; + }; + lil_free(lil); + } + + return 0; +} +LIB_TEST(lib_test_lil, 0); From patchwork Thu Jul 1 06:16:01 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sean Anderson X-Patchwork-Id: 1499295 X-Patchwork-Delegate: trini@ti.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=lists.denx.de (client-ip=85.214.62.61; helo=phobos.denx.de; envelope-from=u-boot-bounces@lists.denx.de; receiver=) Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20161025 header.b=Vbl1mX1h; dkim-atps=neutral Received: from phobos.denx.de (phobos.denx.de [85.214.62.61]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 4GFp373VKgz9sWk for ; Thu, 1 Jul 2021 16:19:47 +1000 (AEST) Received: from h2850616.stratoserver.net (localhost [IPv6:::1]) by phobos.denx.de (Postfix) with ESMTP id F118D832A6; Thu, 1 Jul 2021 08:17:18 +0200 (CEST) Authentication-Results: phobos.denx.de; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=u-boot-bounces@lists.denx.de Authentication-Results: phobos.denx.de; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="Vbl1mX1h"; dkim-atps=neutral Received: by phobos.denx.de (Postfix, from userid 109) id 0605F83253; Thu, 1 Jul 2021 08:16:55 +0200 (CEST) X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on phobos.denx.de X-Spam-Level: X-Spam-Status: No, score=-2.0 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,FREEMAIL_FROM,SPF_HELO_NONE autolearn=unavailable autolearn_force=no version=3.4.2 Received: from mail-qk1-x736.google.com (mail-qk1-x736.google.com [IPv6:2607:f8b0:4864:20::736]) (using TLSv1.3 with cipher TLS_AES_128_GCM_SHA256 (128/128 bits)) (No client certificate requested) by phobos.denx.de (Postfix) with ESMTPS id 78D8D8326B for ; Thu, 1 Jul 2021 08:16:29 +0200 (CEST) Authentication-Results: phobos.denx.de; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=seanga2@gmail.com Received: by mail-qk1-x736.google.com with SMTP id f6so5069004qka.0 for ; Wed, 30 Jun 2021 23:16:29 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=LX6Bcoqpmn7XzMqEQXoZYWEJzkcheIM6tqexEWFToaw=; b=Vbl1mX1hsmTCln9hFj3ON80lsPb5JqitQpzCZoWfJ5H2anNtgFZeTROUFM8/p8J6NX wFaJLXUEb8XJbVbczyc/87W7bv/FOxiXJM3NhIs+seBsNC5jWE3eLTl+sIQYcBRQ+JP2 WgDPqKNySxalesNUS/Zw75gSl+FlNkFFBRzgGkPILE2XkjjW3GMYZfmuJnEWc/8pGGRI nFmVflXZo9fz3ohpbcctgM436qpPe+RbIKYW8VIpsEseyXP4EAE2KHJjfyxQ4VWR1BPA Qnslo/AsMRUt8f9ZtDQBYW1+DNbL2W/WF9yTbQVl2GZ5lB7soo91xd0GMFMF3P96O+0C 5IAw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=LX6Bcoqpmn7XzMqEQXoZYWEJzkcheIM6tqexEWFToaw=; b=tWmKC49/jdRqTP01bgCMrxTSferVr42CMDKyZKQoM5OFx3T5H7SX5OUAJfLcHZ9teO lxcjw/TkPq3XL0XY6f4RR9edbKJ23yD6lvSWWt2UAOtnIv6NbcHF3m9pnjR62Bb968Op +wnxkqRAsL/YuVAgbeECYOjJuKoVWxzdP4svWAcYyjeGKDXshCCO9+o7Bt1UW3j+JFOo 4eQkoZtS+BXBtCFM+ZX07okMnExrfWIVS2haJBXL5kKD2jWD6Q+SOMuozQCrebh5IamZ tP4+qUwIdIHq9gUiuys+/NLBSqquI7IU+wJA+XgaDguF5mT1uWFsIHeBzzycPRvSAxcw njNg== X-Gm-Message-State: AOAM530xuI68gOHR6F2HxGy3ZuAnN9XXf5ZEisnkKJMxbLSTCny3AMmG c/xPCfhNdQVhfSZ7HLRLoBKynDvXDCQ= X-Google-Smtp-Source: ABdhPJxQuNxfs9NsDrzwNRdS+/BOFSaynq19qG3TL6r3FJGJr1TYnaIfuS835DBom1Bd/a00s8jL9Q== X-Received: by 2002:a37:b143:: with SMTP id a64mr40651809qkf.492.1625120188162; Wed, 30 Jun 2021 23:16:28 -0700 (PDT) Received: from godwin.fios-router.home (pool-74-96-87-9.washdc.fios.verizon.net. [74.96.87.9]) by smtp.gmail.com with ESMTPSA id g21sm1684673qts.90.2021.06.30.23.16.27 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 30 Jun 2021 23:16:27 -0700 (PDT) From: Sean Anderson To: u-boot@lists.denx.de, Tom Rini Cc: =?utf-8?q?Marek_Beh=C3=BAn?= , Wolfgang Denk , Simon Glass , Roland Gaudig , Heinrich Schuchardt , Kostas Michalopoulos , Sean Anderson Subject: [RFC PATCH 18/28] cli: lil: Remove duplicate function bodies Date: Thu, 1 Jul 2021 02:16:01 -0400 Message-Id: <20210701061611.957918-19-seanga2@gmail.com> X-Mailer: git-send-email 2.32.0 In-Reply-To: <20210701061611.957918-1-seanga2@gmail.com> References: <20210701061611.957918-1-seanga2@gmail.com> MIME-Version: 1.0 X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.34 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" X-Virus-Scanned: clamav-milter 0.103.2 at phobos.denx.de X-Virus-Status: Clean lil_append_val is just lil_append_string with the string and length taken from a struct lil_value. Use lil_append_stringh_len to implement both. Do the same for lil_clone_value. Signed-off-by: Sean Anderson --- common/cli_lil.c | 51 ++---------------------------------------------- 1 file changed, 2 insertions(+), 49 deletions(-) diff --git a/common/cli_lil.c b/common/cli_lil.c index 6c05531441..5875fbd46b 100644 --- a/common/cli_lil.c +++ b/common/cli_lil.c @@ -369,39 +369,7 @@ static struct lil_value *alloc_value(const char *str) struct lil_value *lil_clone_value(struct lil_value *src) { - struct lil_value *val; - - if (!src) - return NULL; - - if (IS_ENABLED(CONFIG_LIL_POOLS)) - val = alloc_from_pool(); - else - val = calloc(1, sizeof(struct lil_value)); - if (!val) - return NULL; - - val->l = src->l; - if (src->l) { - if (IS_ENABLED(CONFIG_LIL_POOLS)) { - ensure_capacity(val, val->l + 1); - } else { - val->d = malloc(val->l + 1); - if (!val->d) { - free(val); - return NULL; - } - } - memcpy(val->d, src->d, val->l + 1); - } else { - if (IS_ENABLED(CONFIG_LIL_POOLS)) { - ensure_capacity(val, 1); - val->d[0] = '\0'; - } else { - val->d = NULL; - } - } - return val; + return alloc_value_len(src->d, src->l); } int lil_append_char(struct lil_value *val, char ch) @@ -450,22 +418,7 @@ int lil_append_string(struct lil_value *val, const char *s) int lil_append_val(struct lil_value *val, struct lil_value *v) { - if (!v || !v->l) - return 1; - - if (IS_ENABLED(CONFIG_LIL_POOLS)) { - ensure_capacity(val, val->l + v->l + 1); - memcpy(val->d + val->l, v->d, v->l + 1); - } else { - char *new = realloc(val->d, val->l + v->l + 1); - - if (!new) - return 0; - memcpy(new + val->l, v->d, v->l + 1); - val->d = new; - } - val->l += v->l; - return 1; + return lil_append_string_len(val, v->d, v->l); } void lil_free_value(struct lil_value *val) From patchwork Thu Jul 1 06:16:02 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sean Anderson X-Patchwork-Id: 1499297 X-Patchwork-Delegate: trini@ti.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=lists.denx.de (client-ip=2a01:238:438b:c500:173d:9f52:ddab:ee01; helo=phobos.denx.de; envelope-from=u-boot-bounces@lists.denx.de; receiver=) Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20161025 header.b=jP8Cblx1; dkim-atps=neutral Received: from phobos.denx.de (phobos.denx.de [IPv6:2a01:238:438b:c500:173d:9f52:ddab:ee01]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 4GFp3W1Ghpz9sWX for ; Thu, 1 Jul 2021 16:20:07 +1000 (AEST) Received: from h2850616.stratoserver.net (localhost [IPv6:::1]) by phobos.denx.de (Postfix) with ESMTP id B5940832B0; Thu, 1 Jul 2021 08:17:22 +0200 (CEST) Authentication-Results: phobos.denx.de; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=u-boot-bounces@lists.denx.de Authentication-Results: phobos.denx.de; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="jP8Cblx1"; dkim-atps=neutral Received: by phobos.denx.de (Postfix, from userid 109) id 5B5B58326A; Thu, 1 Jul 2021 08:16:56 +0200 (CEST) X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on phobos.denx.de X-Spam-Level: X-Spam-Status: No, score=-2.0 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,FREEMAIL_FROM,SPF_HELO_NONE autolearn=ham autolearn_force=no version=3.4.2 Received: from mail-qv1-xf31.google.com (mail-qv1-xf31.google.com [IPv6:2607:f8b0:4864:20::f31]) (using TLSv1.3 with cipher TLS_AES_128_GCM_SHA256 (128/128 bits)) (No client certificate requested) by phobos.denx.de (Postfix) with ESMTPS id 5A86E8326E for ; Thu, 1 Jul 2021 08:16:30 +0200 (CEST) Authentication-Results: phobos.denx.de; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=seanga2@gmail.com Received: by mail-qv1-xf31.google.com with SMTP id v17so2409558qvw.12 for ; Wed, 30 Jun 2021 23:16:30 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=kA9y/T2TnoAAhhAGh5zTnG4o199xck/MVpEfq5WBDyA=; b=jP8Cblx1LfA5a1ojBO6dvCp6mb6F+K2/+yN+6hakNDIWlVgmC3t3jIpAFj+zJKYXaz RWO7GjkWg33aiqQMyV1NzTIkIi22v105QtvdGA0I4/Gz7EtNn4klQopLY7D2Zh1tBFxb 73kJ2QVSxxDjjGkyNaIb8AbngOXgwPCFSdv8PAY5KsLwm4QCDwwepia5XKp2bWO01ewT VghxEBg7gNLbgUYwR3xQDwqH1kjpq0f0gwKZ+27Un+2ljxA9LdCkN3R0KWgqcJ6aH6Kb O2cORFEKKhYIIqG5oKKP6nD4NyMyahT55C3w/GIh+5BfgvbPqyYOw+d/nZorJDA+lcK2 RBpw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=kA9y/T2TnoAAhhAGh5zTnG4o199xck/MVpEfq5WBDyA=; b=IesGmR3Jz0OUKnT9zROE7ejgM279aASK4YGndARhh9CPV+TbKzuu2LFNVray+UYfsh Z7vFxYBJohgwyOuOA65dgfU/Yy1ctjBKSrP2vOq9AeI6ul9S0T9GMjY7TJKteD98T315 EOzMEEbLjICIWjaOsnLcoAeSTLHLEHbhZR7qVWvMIYwE3KiztDCTwAAwxL+MJHT8J9H1 oo8G8qGk/teieWjL2lMykq4fropV55zbwDNgYHHIKTbV8MM/WRI8B3EqVFUwqNhEiEYR uSLn9+3A/LyreWGn/temifbZ2sKGHuyC3Y3PP1awWOt+eeB+2bHa00NcMbbW9Y9CWXAS fNug== X-Gm-Message-State: AOAM532DzaSOV2mPssU53SHM5qGQ1GCPxHz5rmE0vzmH1BkMFz4SZWJT 6euzchKUcbEUvy8PmyFqulXWwvhRVIo= X-Google-Smtp-Source: ABdhPJzHmdugGTGlDb33I5ZNEtzlQCJHkNQS1ODvUQvyaXMwYco0p4Um3paLsMfQFGsPqiNNusIpvw== X-Received: by 2002:ad4:4b22:: with SMTP id s2mr41352855qvw.22.1625120188979; Wed, 30 Jun 2021 23:16:28 -0700 (PDT) Received: from godwin.fios-router.home (pool-74-96-87-9.washdc.fios.verizon.net. [74.96.87.9]) by smtp.gmail.com with ESMTPSA id g21sm1684673qts.90.2021.06.30.23.16.28 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 30 Jun 2021 23:16:28 -0700 (PDT) From: Sean Anderson To: u-boot@lists.denx.de, Tom Rini Cc: =?utf-8?q?Marek_Beh=C3=BAn?= , Wolfgang Denk , Simon Glass , Roland Gaudig , Heinrich Schuchardt , Kostas Michalopoulos , Sean Anderson Subject: [RFC PATCH 19/28] cli: lil: Add "symbol" structure Date: Thu, 1 Jul 2021 02:16:02 -0400 Message-Id: <20210701061611.957918-20-seanga2@gmail.com> X-Mailer: git-send-email 2.32.0 In-Reply-To: <20210701061611.957918-1-seanga2@gmail.com> References: <20210701061611.957918-1-seanga2@gmail.com> MIME-Version: 1.0 X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.34 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" X-Virus-Scanned: clamav-milter 0.103.2 at phobos.denx.de X-Virus-Status: Clean We need a generic structure to hold symbols parsed by the parser. We would also like to re-use existing code as much as possible without rewriting everything. To do this, we hijack the allocators for lil_list and lil_value and have them allocate enough space for a lil_symbol. While we're at it, we can make lil_list hold lil_symbols instead of lil_values. To keep all the old users sane, we just cast back to lil_value before retrieving the value (with an assert to make sure we're not sending back something else). Unfortunately, many functions were accessing the list vector directly, so convert them. This commit also fixes pools not behaving properly when running out of memory. This should likely be refactored in the future so everything uses one set of allocator/free routines to avoid code duplication. Signed-off-by: Sean Anderson --- common/cli_lil.c | 317 +++++++++++++++++++++++++++++----------------- include/cli_lil.h | 8 +- 2 files changed, 206 insertions(+), 119 deletions(-) diff --git a/common/cli_lil.c b/common/cli_lil.c index 5875fbd46b..06fd37c383 100644 --- a/common/cli_lil.c +++ b/common/cli_lil.c @@ -106,18 +106,45 @@ struct lil_env { int breakrun; }; +struct lil_symbol; + /** - * struct list - A list of values - * @v: A list of pointers to &struct lil_value - * @c: The number of values in this list + * struct list - A list of symbols + * @v: A list of pointers to symbols + * @c: The number of symbols in this list * @cap: The space allocated for @v */ struct lil_list { - struct lil_value **v; + struct lil_symbol **v; size_t c; size_t cap; }; +/** + * struct lil_symbol - A symbol parsed by the parser + * @LIL_SYMBOL_VALUE: A plain old string and length + * @LIL_SYMBOL_VARIABLE: A name of a variable to be substituted + * @LIL_SYMBOL_LIST: A list of symbols + * @value: A literal value or name of variable + * @list: A list of commands in the script + * @word: Another word to be evaluated + * @type: The type of word + */ +struct lil_symbol { + union { + struct lil_value value; + struct lil_symbol *symbol; + struct lil_list list; + }; + enum { + LIL_SYMBOL_VALUE = 0, + LIL_SYMBOL_LIST, + LIL_SYMBOL_VARIABLE, + LIL_SYMBOL_COMMAND, + LIL_SYMBOL_SCRIPT, + } type; +}; + /** * struct lil_func - A function which may be evaluated with a list of arguments * @name: The name of the function @@ -287,24 +314,50 @@ static int hm_has(struct hashmap *hm, const char *key) return 0; } -#ifdef CONFIG_LIL_POOLS +static void lil_free_symbol(struct lil_symbol *sym) +{ + switch (sym->type) { + case LIL_SYMBOL_VALUE: + lil_free_value(&sym->value); + return; + case LIL_SYMBOL_VARIABLE: + lil_free_symbol(sym->symbol); + free(sym); + return; + case LIL_SYMBOL_LIST: + case LIL_SYMBOL_COMMAND: + case LIL_SYMBOL_SCRIPT: + lil_free_list(&sym->list); + return; + } + log_debug("unknown type %d\n", sym->type); + assert(0); +} + +#if IS_ENABLED(CONFIG_LIL_POOLS) static struct lil_value *alloc_from_pool(void) { - if (poolsize > 0) { - poolsize--; - return pool[poolsize]; - } else { - struct lil_value *val = calloc(1, sizeof(struct lil_value)); - - return val; - } + if (poolsize > 0) + return pool[--poolsize]; + else + return calloc(1, sizeof(struct lil_symbol)); } static void release_to_pool(struct lil_value *val) { if (poolsize == poolcap) { - poolcap = poolcap ? (poolcap + poolcap / 2) : 64; - pool = realloc(pool, sizeof(struct lil_value *) * poolcap); + size_t npoolcap = poolcap ? (poolcap + poolcap / 2) : 64; + struct lil_value **npool = + realloc(pool, sizeof(struct lil_symbol *) * npoolcap); + + if (!npool) { + free(val->d); + free(val); + return; + } + + poolcap = npoolcap; + pool = npool; } pool[poolsize++] = val; } @@ -319,45 +372,48 @@ static void ensure_capacity(struct lil_value *val, size_t cap) #else static struct lil_value *alloc_from_pool(void) { - return NULL; + return calloc(1, sizeof(struct lil_symbol)); } -static void release_to_pool(struct lil_value *val) { } -static void ensure_capacity(struct lil_value *val, size_t cap) { } +static void release_to_pool(struct lil_value *val) +{ + free(val->d); + free(val); +} +static void ensure_capacity(struct lil_value *val, size_t cap) +{ + val->d = realloc(val->d, cap); +} #endif +static struct lil_symbol *value_to_symbol(struct lil_value *val) +{ + return container_of(val, struct lil_symbol, value); +} + static struct lil_value *alloc_value_len(const char *str, size_t len) { struct lil_value *val; - if (IS_ENABLED(CONFIG_LIL_POOLS)) - val = alloc_from_pool(); - else - val = calloc(1, sizeof(struct lil_value)); + val = alloc_from_pool(); if (!val) return NULL; + value_to_symbol(val)->type = LIL_SYMBOL_VALUE; if (str) { val->l = len; - if (IS_ENABLED(CONFIG_LIL_POOLS)) { - ensure_capacity(val, len + 1); - } else { - val->d = malloc(len + 1); - if (!val->d) { - free(val); - return NULL; - } + ensure_capacity(val, len + 1); + if (!val->d) { + release_to_pool(val); + return NULL; } memcpy(val->d, str, len); val->d[len] = 0; } else { val->l = 0; - if (IS_ENABLED(CONFIG_LIL_POOLS)) { - ensure_capacity(val, 1); + ensure_capacity(val, 1); + if (val->d) val->d[0] = '\0'; - } else { - val->d = NULL; - } } return val; } @@ -372,53 +428,41 @@ struct lil_value *lil_clone_value(struct lil_value *src) return alloc_value_len(src->d, src->l); } -int lil_append_char(struct lil_value *val, char ch) +enum lil_error lil_append_char(struct lil_value *val, char ch) { - if (IS_ENABLED(CONFIG_LIL_POOLS)) { - ensure_capacity(val, val->l + 2); - val->d[val->l++] = ch; - val->d[val->l] = '\0'; - } else { - char *new = realloc(val->d, val->l + 2); + ensure_capacity(val, val->l + 2); + if (!val->d) + return LIL_ERR_OOM; - if (!new) - return 0; - - new[val->l++] = ch; - new[val->l] = 0; - val->d = new; - } - return 1; + val->d[val->l++] = ch; + val->d[val->l] = '\0'; + return LIL_ERR_NONE; } -int lil_append_string_len(struct lil_value *val, const char *s, size_t len) +static enum lil_error lil_append_string_len(struct lil_value *val, + const char *s, size_t len) { if (!s || !s[0]) - return 1; + return LIL_ERR_NONE; - if (IS_ENABLED(CONFIG_LIL_POOLS)) { - ensure_capacity(val, val->l + len + 1); - memcpy(val->d + val->l, s, len + 1); - } else { - char *new = realloc(val->d, val->l + len + 1); - - if (!new) - return 0; - memcpy(new + val->l, s, len + 1); - val->d = new; - } + ensure_capacity(val, val->l + len + 1); + if (!val->d) + return LIL_ERR_OOM; + memcpy(val->d + val->l, s, len + 1); val->l += len; - return 1; + return LIL_ERR_NONE; } -int lil_append_string(struct lil_value *val, const char *s) +enum lil_error lil_append_string(struct lil_value *val, const char *s) { return lil_append_string_len(val, s, strlen(s)); } -int lil_append_val(struct lil_value *val, struct lil_value *v) +enum lil_error lil_append_val(struct lil_value *val, struct lil_value *v) { - return lil_append_string_len(val, v->d, v->l); + if (v) + return lil_append_string_len(val, v->d, v->l); + return LIL_ERR_NONE; } void lil_free_value(struct lil_value *val) @@ -426,23 +470,28 @@ void lil_free_value(struct lil_value *val) if (!val) return; - if (IS_ENABLED(CONFIG_LIL_POOLS)) { - release_to_pool(val); - } else { - free(val->d); - free(val); - } + release_to_pool(val); +} + +static struct lil_symbol *list_to_symbol(struct lil_list *list) +{ + return container_of(list, struct lil_symbol, list); } struct lil_list *lil_alloc_list(void) { struct lil_list *list; - if (IS_ENABLED(CONFIG_LIL_POOLS) && listpoolsize > 0) - return listpool[--listpoolsize]; + if (IS_ENABLED(CONFIG_LIL_POOLS) && listpoolsize > 0) { + list = listpool[--listpoolsize]; + } else { + list = calloc(1, sizeof(struct lil_symbol)); + if (!list) + return list; + list->v = NULL; + } - list = calloc(1, sizeof(struct lil_list)); - list->v = NULL; + list_to_symbol(list)->type = LIL_SYMBOL_LIST; return list; } @@ -454,49 +503,61 @@ void lil_free_list(struct lil_list *list) return; for (i = 0; i < list->c; i++) - lil_free_value(list->v[i]); + lil_free_symbol(list->v[i]); if (IS_ENABLED(CONFIG_LIL_POOLS)) { list->c = 0; if (listpoolsize == listpoolcap) { + int ncap; + struct lil_list **npool; + if (listpoolcap) - listpoolcap += listpoolcap / 2; + ncap += listpoolcap / 2; else - listpoolcap = 32; - listpool = realloc(listpool, - sizeof(*listpool) * listpoolcap); + ncap = 32; + npool = realloc(listpool, sizeof(*npool) * ncap); + if (!npool) + goto free; + + listpoolcap = ncap; + listpool = npool; } listpool[listpoolsize++] = list; } else { +free: free(list->v); free(list); } } -void lil_list_append(struct lil_list *list, struct lil_value *val) +int lil_list_append(struct lil_list *list, void *item) { if (list->c == list->cap) { size_t cap = list->cap ? (list->cap + list->cap / 2) : 32; - struct lil_value **nv = - realloc(list->v, sizeof(struct lil_value *) * cap); + struct lil_symbol **nv = realloc(list->v, sizeof(void *) * cap); if (!nv) - return; + return -ENOMEM; list->cap = cap; list->v = nv; } - list->v[list->c++] = val; + list->v[list->c++] = item; + return 0; } -size_t lil_list_size(struct lil_list *list) +static struct lil_symbol *lil_list_gets(struct lil_list *list, size_t index) { - return list->c; + assert(index < list->c); + return list->v[index]; } struct lil_value *lil_list_get(struct lil_list *list, size_t index) { - return index >= list->c ? NULL : list->v[index]; + struct lil_symbol *sym = lil_list_gets(list, index); + + assert(sym->type == LIL_SYMBOL_VALUE); + return &sym->value; } static int needs_escape(const char *str) @@ -519,25 +580,26 @@ struct lil_value *lil_list_to_value(struct lil_list *list, int do_escape) size_t i, j; for (i = 0; i < list->c; i++) { + struct lil_value *item = lil_list_get(list, i); int escape = - do_escape ? needs_escape(lil_to_string(list->v[i])) : 0; + do_escape ? needs_escape(lil_to_string(item)) : 0; if (i) lil_append_char(val, ' '); if (escape) { lil_append_char(val, '{'); - for (j = 0; j < list->v[i]->l; j++) { - if (list->v[i]->d[j] == '{') + for (j = 0; j < item->l; j++) { + if (item->d[j] == '{') lil_append_string(val, "}\"\\o\"{"); - else if (list->v[i]->d[j] == '}') + else if (item->d[j] == '}') lil_append_string(val, "}\"\\c\"{"); else - lil_append_char(val, list->v[i]->d[j]); + lil_append_char(val, item->d[j]); } lil_append_char(val, '}'); } else { - lil_append_val(val, list->v[i]); + lil_append_val(val, item); } } return val; @@ -588,15 +650,23 @@ void lil_free_env(struct lil_env *env) free(env->var); if (envpoolsize == envpoolcap) { + int ncap; + struct lil_env **npool; + if (envpoolcap) envpoolcap += envpoolcap / 2; else envpoolcap = 64; - envpool = realloc(envpool, - sizeof(*envpool) * envpoolcap); + npool = realloc(envpool, sizeof(*npool) * ncap); + if (!npool) + goto free; + + envpoolcap = ncap; + envpool = npool; } envpool[envpoolsize++] = env; } else { +free: hm_destroy(&env->varmap); for (i = 0; i < env->vars; i++) { free(env->var[i]->n); @@ -660,6 +730,8 @@ static struct lil_func *add_func(struct lil *lil, const char *name) } cmd = calloc(1, sizeof(struct lil_func)); + if (!cmd) + return NULL; cmd->name = strdup(name); ncmd = realloc(lil->cmd, sizeof(struct lil_func *) * (lil->cmds + 1)); @@ -1093,15 +1165,17 @@ static struct lil_value *run_cmd(struct lil *lil, struct lil_func *cmd, struct lil_value *r; if (cmd->proc) { - lil->env->proc = words->v[0]->d; - r = cmd->proc(lil, words->c - 1, words->v + 1); + lil->env->proc = lil_to_string(lil_list_get(words, 0)); + r = cmd->proc(lil, words->c - 1, + (struct lil_value **)words->v + 1); lil->env->proc = NULL; } else { lil_push_env(lil); lil->env->func = cmd; if (cmd->argnames->c == 1 && - !strcmp(lil_to_string(cmd->argnames->v[0]), "args")) { + !strcmp(lil_to_string(lil_list_get(cmd->argnames, 0)), + "args")) { struct lil_value *args = lil_list_to_value(words, 1); lil_set_var(lil, "args", args, LIL_SETVAR_LOCAL_NEW); @@ -1111,14 +1185,15 @@ static struct lil_value *run_cmd(struct lil *lil, struct lil_func *cmd, for (i = 0; i < cmd->argnames->c; i++) { struct lil_value *val; + struct lil_value *name = + lil_list_get(cmd->argnames, i); if (i < words->c - 1) - val = words->v[i + 1]; + val = lil_list_get(words, i + 1); else val = lil->empty; - lil_set_var(lil, - lil_to_string(cmd->argnames->v[i]), + lil_set_var(lil, lil_to_string(name), val, LIL_SETVAR_LOCAL_NEW); } } @@ -1176,13 +1251,13 @@ struct lil_value *lil_parse(struct lil *lil, const char *code, size_t codelen, goto cleanup; if (words->c) { - struct lil_func *cmd = - lil_find_cmd(lil, lil_to_string(words->v[0])); + const char *cmdname = + lil_to_string(lil_list_get(words, 0)); + struct lil_func *cmd = lil_find_cmd(lil, cmdname); if (!cmd) { - if (words->v[0]->l) { - lil_set_error_nocmd(lil, - words->v[0]->d); + if (cmdname[0]) { + lil_set_error_nocmd(lil, cmdname); goto cleanup; } } else { @@ -2042,6 +2117,9 @@ static struct lil_value *fnc_proc(struct lil *lil, size_t argc, name = lil_clone_value(argv[0]); fargs = lil_subst_to_list(lil, argv[1]); cmd = add_func(lil, lil_to_string(argv[0])); + if (!cmd) + return NULL; + cmd->argnames = fargs; cmd->code = lil_clone_value(argv[2]); } else { @@ -2052,11 +2130,17 @@ static struct lil_value *fnc_proc(struct lil *lil, size_t argc, fargs = lil_subst_to_list(lil, tmp); lil_free_value(tmp); cmd = add_func(lil, lil_to_string(name)); + if (!cmd) + return NULL; + cmd->argnames = fargs; cmd->code = lil_clone_value(argv[0]); } else { fargs = lil_subst_to_list(lil, argv[0]); cmd = add_func(lil, lil_to_string(name)); + if (!cmd) + return NULL; + cmd->argnames = fargs; cmd->code = lil_clone_value(argv[1]); } @@ -2278,7 +2362,7 @@ static struct lil_value *fnc_index(struct lil *lil, size_t argc, if (index >= list->c) r = NULL; else - r = lil_clone_value(list->v[index]); + r = lil_clone_value(lil_list_get(list, index)); lil_free_list(list); return r; } @@ -2295,7 +2379,7 @@ static struct lil_value *fnc_indexof(struct lil *lil, size_t argc, list = lil_subst_to_list(lil, argv[0]); for (index = 0; index < list->c; index++) { - if (!strcmp(lil_to_string(list->v[index]), + if (!strcmp(lil_to_string(lil_list_get(list, index)), lil_to_string(argv[1]))) { r = lil_alloc_integer(index); break; @@ -2364,7 +2448,7 @@ static struct lil_value *fnc_slice(struct lil *lil, size_t argc, slice = lil_alloc_list(); for (i = (size_t)from; i < (size_t)to; i++) - lil_list_append(slice, lil_clone_value(list->v[i])); + lil_list_append(slice, lil_clone_value(lil_list_get(list, i))); lil_free_list(list); r = lil_list_to_value(slice, 1); @@ -2395,10 +2479,12 @@ static struct lil_value *fnc_filter(struct lil *lil, size_t argc, list = lil_subst_to_list(lil, argv[base]); filtered = lil_alloc_list(); for (i = 0; i < list->c && !lil->env->breakrun; i++) { - lil_set_var(lil, varname, list->v[i], LIL_SETVAR_LOCAL_ONLY); + lil_set_var(lil, varname, lil_list_get(list, i), + LIL_SETVAR_LOCAL_ONLY); r = lil_eval_expr(lil, argv[base + 1]); if (lil_to_boolean(r)) - lil_list_append(filtered, lil_clone_value(list->v[i])); + lil_list_append(filtered, + lil_clone_value(lil_list_get(list, i))); lil_free_value(r); } lil_free_list(list); @@ -2475,7 +2561,8 @@ static struct lil_value *fnc_foreach(struct lil *lil, size_t argc, for (i = 0; i < list->c; i++) { struct lil_value *rv; - lil_set_var(lil, varname, list->v[i], LIL_SETVAR_LOCAL_ONLY); + lil_set_var(lil, varname, lil_list_get(list, i), + LIL_SETVAR_LOCAL_ONLY); rv = lil_parse_value(lil, argv[codeidx], 0); if (rv->l) lil_list_append(rlist, rv); diff --git a/include/cli_lil.h b/include/cli_lil.h index 91e79c12f4..40c822401e 100644 --- a/include/cli_lil.h +++ b/include/cli_lil.h @@ -149,13 +149,13 @@ struct lil_value *lil_alloc_integer(ssize_t num); void lil_free_value(struct lil_value *val); struct lil_value *lil_clone_value(struct lil_value *src); -int lil_append_char(struct lil_value *val, char ch); -int lil_append_string(struct lil_value *val, const char *s); -int lil_append_val(struct lil_value *val, struct lil_value *v); +enum lil_error lil_append_char(struct lil_value *val, char ch); +enum lil_error lil_append_string(struct lil_value *val, const char *s); +enum lil_error lil_append_val(struct lil_value *val, struct lil_value *v); struct lil_list *lil_alloc_list(void); void lil_free_list(struct lil_list *list); -void lil_list_append(struct lil_list *list, struct lil_value *val); +int lil_list_append(struct lil_list *list, void *item); size_t lil_list_size(struct lil_list *list); struct lil_value *lil_list_get(struct lil_list *list, size_t index); struct lil_value *lil_list_to_value(struct lil_list *list, int do_escape); From patchwork Thu Jul 1 06:16:03 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sean Anderson X-Patchwork-Id: 1499296 X-Patchwork-Delegate: trini@ti.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=lists.denx.de (client-ip=85.214.62.61; helo=phobos.denx.de; envelope-from=u-boot-bounces@lists.denx.de; receiver=) Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20161025 header.b=qeFnLMVH; dkim-atps=neutral Received: from phobos.denx.de (phobos.denx.de [85.214.62.61]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 4GFp3J6g2cz9sWX for ; Thu, 1 Jul 2021 16:19:56 +1000 (AEST) Received: from h2850616.stratoserver.net (localhost [IPv6:::1]) by phobos.denx.de (Postfix) with ESMTP id 45B96832AB; Thu, 1 Jul 2021 08:17:21 +0200 (CEST) Authentication-Results: phobos.denx.de; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=u-boot-bounces@lists.denx.de Authentication-Results: phobos.denx.de; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="qeFnLMVH"; dkim-atps=neutral Received: by phobos.denx.de (Postfix, from userid 109) id A413783260; Thu, 1 Jul 2021 08:16:55 +0200 (CEST) X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on phobos.denx.de X-Spam-Level: X-Spam-Status: No, score=-2.0 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,FREEMAIL_FROM,SPF_HELO_NONE autolearn=ham autolearn_force=no version=3.4.2 Received: from mail-qk1-x733.google.com (mail-qk1-x733.google.com [IPv6:2607:f8b0:4864:20::733]) (using TLSv1.3 with cipher TLS_AES_128_GCM_SHA256 (128/128 bits)) (No client certificate requested) by phobos.denx.de (Postfix) with ESMTPS id E441283270 for ; Thu, 1 Jul 2021 08:16:30 +0200 (CEST) Authentication-Results: phobos.denx.de; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=seanga2@gmail.com Received: by mail-qk1-x733.google.com with SMTP id bj15so4972927qkb.11 for ; Wed, 30 Jun 2021 23:16:30 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=qRNgH4drkBTQli8UEL56MsaXpzEJXsN+W+l2IxU9qtg=; b=qeFnLMVHLzr8E0wZMyhs8g+XAbqmvIrL+fJLWLcUtOJsBJV22YCVzHT601cRggJZDc ua3iRs+DE8GNrmU+6sn0ZDG/2cibiRRCqCpb9vZAAe+RSlok/4NdBg8a41ZA2cWcfAxP wiIbVhv5pcTKm7jfkEoCHh0AyPP8Ff3kj6TNyGKUnB9Zb6e3bryZAE5K44sdljdUVRbt dmChlmCwR6Qk0+MhhfLIejMYvR+lugMyQ7Z+YSnicSsI+W/nlXdyEOPqZ2/H7xz7BnKm MzMFUy4TQZyHpngvwpGJ72t6kdEcNuUyk5wCjbyshiuobBO5pMy1LXm0gDw+6mXwPjZV kENg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=qRNgH4drkBTQli8UEL56MsaXpzEJXsN+W+l2IxU9qtg=; b=ki/uQgrgyr2NRGYcIlnG87StqdoTpvjovki0VCGaFyp9MIZjnFre51HDbcyVLcLZTV M5dUr2f4HYzVOQj8stpozvTmxyTY5ZgfUuCwSnWJ1GLs8vX7lW7Tt9CkIRzqSWpUQ6jT igT5tf7NnBLuS1wjjNBQkoAp7VpPA2yqjm/CoFw3RQKbAvenzOC8Cz9RvrJI1yWdgkkJ R4WMdiDsvv+vchs2mRd++IC300zZnSjkofeFvkIg4DGC2+Xa4ok3XZQLLbmAE/0OOUKu 4Ke1VRleVVtwAQ+BhJRjvucQlRlotjMeM34xu7sCMt/CIJZDoYAryzIgdE5XBYsplIhK JUow== X-Gm-Message-State: AOAM531PmJ9JhDf+zVfeVcS67m5mFQHqWIVk7cufRvnpXU3xOKw5UWG4 UNhv6vE+7t7oOLIZg1Epn4AKXdLtDSI= X-Google-Smtp-Source: ABdhPJyncCXXfiOdt45qf0S5GtWINdg53qKI5KXXhI3RIybDNE4IsK1IRgHgAUqHYaBzUKKc5m8bdw== X-Received: by 2002:a37:4197:: with SMTP id o145mr41241288qka.75.1625120189708; Wed, 30 Jun 2021 23:16:29 -0700 (PDT) Received: from godwin.fios-router.home (pool-74-96-87-9.washdc.fios.verizon.net. [74.96.87.9]) by smtp.gmail.com with ESMTPSA id g21sm1684673qts.90.2021.06.30.23.16.29 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 30 Jun 2021 23:16:29 -0700 (PDT) From: Sean Anderson To: u-boot@lists.denx.de, Tom Rini Cc: =?utf-8?q?Marek_Beh=C3=BAn?= , Wolfgang Denk , Simon Glass , Roland Gaudig , Heinrich Schuchardt , Kostas Michalopoulos , Sean Anderson Subject: [RFC PATCH 20/28] cli: lil: Add config to enable debug output Date: Thu, 1 Jul 2021 02:16:03 -0400 Message-Id: <20210701061611.957918-21-seanga2@gmail.com> X-Mailer: git-send-email 2.32.0 In-Reply-To: <20210701061611.957918-1-seanga2@gmail.com> References: <20210701061611.957918-1-seanga2@gmail.com> MIME-Version: 1.0 X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.34 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" X-Virus-Scanned: clamav-milter 0.103.2 at phobos.denx.de X-Virus-Status: Clean This provides an easy way to enable assertions and debug messages. It will also be used to enable tracing features in future patches. Signed-off-by: Sean Anderson --- cmd/Kconfig | 6 ++++++ common/Makefile | 3 +++ 2 files changed, 9 insertions(+) diff --git a/cmd/Kconfig b/cmd/Kconfig index 7c8962cfc2..bba72bbdc2 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -61,6 +61,12 @@ config LIL_RECLIMIT avoid call stack overflows and is also useful when running through an automated fuzzer like AFL. Set to 0 to disable the recursion limit. +config LIL_DEBUG + bool "Enable LIL debugging" + help + This enables debug prints, assertions, and other tracing features in + LIL. If you are not working on LIL, say 'n' here. + endif endif diff --git a/common/Makefile b/common/Makefile index dce04b305e..558e1932fe 100644 --- a/common/Makefile +++ b/common/Makefile @@ -11,6 +11,9 @@ obj-y += exports.o obj-$(CONFIG_HASH) += hash.o obj-$(CONFIG_HUSH_PARSER) += cli_hush.o obj-$(CONFIG_LIL) += cli_lil.o +ifneq ($(CONFIG_LIL_DEBUG),) +CFLAGS_cli_lil.o += -DDEBUG +endif obj-$(CONFIG_AUTOBOOT) += autoboot.o # This option is not just y/n - it can have a numeric value From patchwork Thu Jul 1 06:16:04 2021 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Sean Anderson X-Patchwork-Id: 1499300 X-Patchwork-Delegate: trini@ti.com Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Authentication-Results: ozlabs.org; spf=pass (sender SPF authorized) smtp.mailfrom=lists.denx.de (client-ip=2a01:238:438b:c500:173d:9f52:ddab:ee01; helo=phobos.denx.de; envelope-from=u-boot-bounces@lists.denx.de; receiver=) Authentication-Results: ozlabs.org; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.a=rsa-sha256 header.s=20161025 header.b=Hyz98dhN; dkim-atps=neutral Received: from phobos.denx.de (phobos.denx.de [IPv6:2a01:238:438b:c500:173d:9f52:ddab:ee01]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 4GFp3t0fDCz9sWk for ; Thu, 1 Jul 2021 16:20:25 +1000 (AEST) Received: from h2850616.stratoserver.net (localhost [IPv6:::1]) by phobos.denx.de (Postfix) with ESMTP id 79EA9832BB; Thu, 1 Jul 2021 08:17:26 +0200 (CEST) Authentication-Results: phobos.denx.de; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=u-boot-bounces@lists.denx.de Authentication-Results: phobos.denx.de; dkim=pass (2048-bit key; unprotected) header.d=gmail.com header.i=@gmail.com header.b="Hyz98dhN"; dkim-atps=neutral Received: by phobos.denx.de (Postfix, from userid 109) id 3D81683298; Thu, 1 Jul 2021 08:17:07 +0200 (CEST) X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on phobos.denx.de X-Spam-Level: X-Spam-Status: No, score=-2.0 required=5.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,FREEMAIL_FROM,SPF_HELO_NONE autolearn=ham autolearn_force=no version=3.4.2 Received: from mail-qt1-x832.google.com (mail-qt1-x832.google.com [IPv6:2607:f8b0:4864:20::832]) (using TLSv1.3 with cipher TLS_AES_128_GCM_SHA256 (128/128 bits)) (No client certificate requested) by phobos.denx.de (Postfix) with ESMTPS id 7B7238326F for ; Thu, 1 Jul 2021 08:16:32 +0200 (CEST) Authentication-Results: phobos.denx.de; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: phobos.denx.de; spf=pass smtp.mailfrom=seanga2@gmail.com Received: by mail-qt1-x832.google.com with SMTP id g12so3376829qtb.2 for ; Wed, 30 Jun 2021 23:16:32 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=8TjMD76hnIhsUU0J7V/ohTYPev13NWZjXPDHRaTQl6k=; b=Hyz98dhNq3ojFzzE+0Kw8KbEth8ki4KZFS7LVM1JFM4K+wzqp1hpAFW8RWsnilnPrd qZ4LDO48JTyBJOVIK3shuNGqzkugJW2RB0l8L0a3jHL2lrl0cJUstJMumelkbBnDpf3M yYPPcFmhoYWH48C7qzyH9/D6apUWrrBLsJVuvJ+ZCHJ1o5CeAulURhQg6u2FHoA4vwAO H5wnJnumnLWqvt+Qz7DHMvx+852WfxRg6ONuVCywvtMY9sNZrGk8G2DmUJ+p7ak9cFDd nfcp9LJ2gngRa+LMfKffgi9r1TVhmf4iKgKQZ6HBTmkGgbgNfy/8EfFmmMTUsN/4GFtj 1TCw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=8TjMD76hnIhsUU0J7V/ohTYPev13NWZjXPDHRaTQl6k=; b=X7a2ln3DKociXgYuT2EjvOKEnNRLvuxvpjR/3bBNV4VIJQ+efBvHCfRpzYuQTifJw4 1GO8J7CzvP/dRa8LgUIOgOE23kN43B8WDEQOc9i25DNEUSOISXGz48WjI6BlLT3usHET 6zl4PMG8w9l1c80pEOAyOsD33BFXvNY38Qn0xYvfYpLfswGPK4dlqlEXZdPGr5ba2UDM eEyPdtM9vECc/aW3H3TuL9Jeqr0k6Zl0UWTGNJXhME30mUhKL7Qww86fp0CDMvCM43hC DXw1XwNOd78YsrGrU1bTtyktsu7eHdBlqXxA2/q1J8ytJizVsj7GFkY4xuMyynE00h6q ZEXQ== X-Gm-Message-State: AOAM530Qsg8AUbqwo+dU0oBOR4B3wxD22RQ1oTCOuxP86jJYXjmdZ/0f qHlyTqJtuk1fLfy5D9jv4/MD4eIUHqo= X-Google-Smtp-Source: ABdhPJxcUZSGm96RmvrdhS0JlLe3+kEY+970VnYuvoHhMnFdRtzVxvyaxPNceS+Gbh0SrmOMOFW9mQ== X-Received: by 2002:a05:622a:148f:: with SMTP id t15mr10416309qtx.25.1625120190643; Wed, 30 Jun 2021 23:16:30 -0700 (PDT) Received: from godwin.fios-router.home (pool-74-96-87-9.washdc.fios.verizon.net. [74.96.87.9]) by smtp.gmail.com with ESMTPSA id g21sm1684673qts.90.2021.06.30.23.16.29 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 30 Jun 2021 23:16:30 -0700 (PDT) From: Sean Anderson To: u-boot@lists.denx.de, Tom Rini Cc: =?utf-8?q?Marek_Beh=C3=BAn?= , Wolfgang Denk , Simon Glass , Roland Gaudig , Heinrich Schuchardt , Kostas Michalopoulos , Sean Anderson Subject: [RFC PATCH 21/28] cli: lil: Add a distinct parsing step Date: Thu, 1 Jul 2021 02:16:04 -0400 Message-Id: <20210701061611.957918-22-seanga2@gmail.com> X-Mailer: git-send-email 2.32.0 In-Reply-To: <20210701061611.957918-1-seanga2@gmail.com> References: <20210701061611.957918-1-seanga2@gmail.com> MIME-Version: 1.0 X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.34 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: u-boot-bounces@lists.denx.de Sender: "U-Boot" X-Virus-Scanned: clamav-milter 0.103.2 at phobos.denx.de X-Virus-Status: Clean This adds a parser to LIL (as separate from the interpreter). This is necessary to detect syntax errors before evaluating anything. Before this, running a script like echo some message; echo syntax error} would result in "some message" being printed before the error was discovered. This is not only rather surprising, but also makes things like Hush's secondary prompt impossible to implement. In addition, the original parser would accept almost any input, and silently return NULL if it encountered problems. This made it difficult to determine if a command had been mis-parsed, since an empty command would just evaluate to "". The grammar is not the same as LIL originally. Several ideas have been taken from TCL proper as well. In order to simplify the parser, it has been rewritten to be LL(1), except for line continuations which are LL(2). In particular, multi-line comments and command/variable subtitutions partially through unquoted words (e.g. a$b) have been removed. Some other characters such as unescaped, unmatched }s are now syntax errors. On the other hand, some things such as escaped characters in unquoted words have been added back (as seen in TCL). Unlike TCL, comments may be placed almost anywhere. The exact grammar is subject to change, but I have tried to make it as sane as I can get it. The grammar has been documented in (extended) EBNF. The names of the nonterminals are the same as are used in the dodekalogue [1]. Each nonterminal foo has a function parse_foo() which recognizes it. [1] https://www.tcl.tk/man/tcl8.6/TclCmd/Tcl.htm Signed-off-by: Sean Anderson --- cmd/Kconfig | 4 +- common/cli.c | 2 +- common/cli_lil.c | 1880 ++++++++++++++++++++++++++++++++++++--------- include/cli_lil.h | 11 +- test/cmd/lil.c | 73 +- 5 files changed, 1527 insertions(+), 443 deletions(-) diff --git a/cmd/Kconfig b/cmd/Kconfig index bba72bbdc2..7ff8e4a7e5 100644 --- a/cmd/Kconfig +++ b/cmd/Kconfig @@ -43,8 +43,8 @@ if LIL config LIL_FULL bool "Enable all LIL features" help - This enables all LIL builtin functions, as well as expression support - for arithmetic and bitwise operations. + This enables all LIL builtin functions, expression support for + arithmetic and bitwise operations, and expanded error messages. config LIL_POOLS bool "Use memory pools for LIL structures" diff --git a/common/cli.c b/common/cli.c index ad5d76d563..391fee0ec7 100644 --- a/common/cli.c +++ b/common/cli.c @@ -49,7 +49,7 @@ static const struct lil_callbacks env_callbacks = { static int lil_run(const char *cmd) { int err; - struct lil_value *result = lil_parse(lil, cmd, 0, 0); + struct lil_value *result = lil_parse_eval(lil, cmd, 0, true); const char *err_msg, *strres = lil_to_string(result); /* The result may be very big, so use puts */ diff --git a/common/cli_lil.c b/common/cli_lil.c index 06fd37c383..2ed96ebc2d 100644 --- a/common/cli_lil.c +++ b/common/cli_lil.c @@ -16,6 +16,7 @@ #include #include #include +#include #define HASHMAP_CELLS 256 #define HASHMAP_CELLMASK 0xFF @@ -121,14 +122,27 @@ struct lil_list { }; /** - * struct lil_symbol - A symbol parsed by the parser + * enum lil_symbol_type - The type of data in a symbol * @LIL_SYMBOL_VALUE: A plain old string and length * @LIL_SYMBOL_VARIABLE: A name of a variable to be substituted * @LIL_SYMBOL_LIST: A list of symbols - * @value: A literal value or name of variable - * @list: A list of commands in the script - * @word: Another word to be evaluated - * @type: The type of word + * @LIL_SYMBOL_COMMAND: A command to be ran + * @LIL_SYMBOL_SCRIPT: A script to be run + */ +enum lil_symbol_type { + LIL_SYMBOL_VALUE = 0, + LIL_SYMBOL_LIST, + LIL_SYMBOL_VARIABLE, + LIL_SYMBOL_COMMAND, + LIL_SYMBOL_SCRIPT, +}; + +/** + * struct lil_symbol - A symbol parsed by the parser + * @value: A literal value + * @list: A list of commands, words, or symbols + * @symbol: Another symbol to be evaluated + * @type: The type of symbol */ struct lil_symbol { union { @@ -136,13 +150,7 @@ struct lil_symbol { struct lil_symbol *symbol; struct lil_list list; }; - enum { - LIL_SYMBOL_VALUE = 0, - LIL_SYMBOL_LIST, - LIL_SYMBOL_VARIABLE, - LIL_SYMBOL_COMMAND, - LIL_SYMBOL_SCRIPT, - } type; + enum lil_symbol_type type; }; /** @@ -162,14 +170,64 @@ struct lil_func { lil_func_proc_t proc; }; +/** + * struct lil_position - A position within a script + * @head: The absolute offset + * @line: The current line (as delineated by newlines) + * @column: The column within the current line + */ +struct lil_position { + size_t head; + size_t line; + size_t column; +}; + +/** + * struct lil_parser_error - Errors encountered while parsing a script + * @PERR_NONE: There is no error. + * @PERR_OOM: We ran out of memory. + * @PERR_EXPECTED: The parser was expecting a specific character but got + * something else or ran out of input. + * @PERR_UNEXPECTED: The parser encountered an unexpected character or ran out + * of input. + * @type: The type of error + * @func: The name of the function which caused the error + * @expected: The character we expected to find + * @matching: The position of the character which made us expect @expected + * + * @expected is only valid when @type is %PERR_EXPECTED. @matching is valid when + * @type is %PERR_EXPECTED or when @type is %PERR_UNEXPECTED and the parser is + * at the end of the file. + */ +struct lil_parser_error { + struct lil_position matching; + const char *func; + enum { + PERR_NONE = 0, + PERR_OOM, + PERR_EXPECTED, + PERR_UNEXPECTED, + } type; + char expected; +}; + +/** + * struct lil_parser - State used when parsing a script + * @code: The script which is being parsed + * @len: The length of the script + * @pos: Our current position within @code + * @err: The current error (if any) + */ +struct lil_parser { + const char *code; + size_t len; + size_t depth; + struct lil_position pos; + struct lil_parser_error err; +}; + /** * struct lil - The current state of the interpreter - * @code: The code which is being interpreted - * @rootcode: The top-level code (e.g. the code for the initial call to - * lil_parse()) - * @clen: The length of @code - * @head: The first uninterpreted part of this code, as an index of @code - * @ignoreeol: Whether to treat newlines as whitespace or command terminators * @cmd: A list of the current commands * @cmds: The number of commands in @cmd * @cmdmap: A hash map mapping command names to pointers to commands in @cmd @@ -185,14 +243,9 @@ struct lil_func { * @error: The current error status * @err_head: The offset in @code which caused the @error * @err_msg: An optional string describing the current @error - * @parse_depth: The depth of recursive function calls + * @depth: The depth of recursive function calls */ struct lil { - const char *code; /* need save on parse */ - const char *rootcode; - size_t clen; /* need save on parse */ - size_t head; /* need save on parse */ - int ignoreeol; struct lil_func **cmd; size_t cmds; struct hashmap cmdmap; @@ -203,7 +256,7 @@ struct lil { enum lil_error err; char *err_msg; struct lil_callbacks callbacks; - size_t parse_depth; + size_t depth; }; /** @@ -231,7 +284,6 @@ struct expreval { } error; }; -static struct lil_value *next_word(struct lil *lil); static void register_stdcmds(struct lil *lil); static void lil_set_error(struct lil *lil, enum lil_error err, const char *msg); static void lil_set_errorf(struct lil *lil, enum lil_error err, @@ -560,51 +612,6 @@ struct lil_value *lil_list_get(struct lil_list *list, size_t index) return &sym->value; } -static int needs_escape(const char *str) -{ - size_t i; - - if (!str || !str[0]) - return 1; - - for (i = 0; str[i]; i++) - if (ispunct(str[i]) || isspace(str[i])) - return 1; - - return 0; -} - -struct lil_value *lil_list_to_value(struct lil_list *list, int do_escape) -{ - struct lil_value *val = alloc_value(NULL); - size_t i, j; - - for (i = 0; i < list->c; i++) { - struct lil_value *item = lil_list_get(list, i); - int escape = - do_escape ? needs_escape(lil_to_string(item)) : 0; - - if (i) - lil_append_char(val, ' '); - - if (escape) { - lil_append_char(val, '{'); - for (j = 0; j < item->l; j++) { - if (item->d[j] == '{') - lil_append_string(val, "}\"\\o\"{"); - else if (item->d[j] == '}') - lil_append_string(val, "}\"\\c\"{"); - else - lil_append_char(val, item->d[j]); - } - lil_append_char(val, '}'); - } else { - lil_append_val(val, item); - } - } - return val; -} - struct lil_env *lil_alloc_env(struct lil_env *parent) { struct lil_env *env; @@ -888,275 +895,1167 @@ struct lil *lil_new(const struct lil_callbacks *callbacks) return lil; } -static int islilspecial(char ch) +/** + * DOC: Syntax + * + * Syntax is EBNF, except that [], {}, ?, *, and + have been borrowed from + * regular expressions and , is optional. In addition, a - b matches strings + * which matches a but which do not match b (set difference). + * + * :: + * + * script = whitespace* command? ( terminator whitespace* command? )* ; + * + * command = word ( ( whitespace - continuation ) whitespace* word )* + * whitespace* ; + * + * word = single-quote | double-quote | brace | bracket | dollar + * | ( escape-sequence | continuation | + * ( character - word-special - space ) )+ ; + * + * single-quote = "'" ( subcommand | dollar | escape-sequence | continuation + * | ( character - "'" ) )* "'" ; + * double-quote = '"' ( subcommand | dollar | escape-sequence | continuation + * | ( character - '"' ) )* '"' ; + * escape-sequence = '\\' ( character - '\n' ) ; + * brace = '{' ( brace | '\\' character | comment + * | ( character - '}' - '#' ) )* '}' ; + * bracket = '[' script ']' ; + * dollar = '$' word ; + * + * whitespace = space | continuation ; + * continuation = '\\\n' ; + * terminator = '\n' | ';' | comment ; + * comment = '#' ( character - '\n' )* '\n' ; + * + * space = ' ' | '\f' | '\r' | '\t' | '\v' ; + * word-special = '$' | '{' | '}' | '[' | ']' | '"' | "'" | '\\' | + * | terminator ; + * character = [\x00-\xff] ; + * + * In addition to the full syntax above, many commands expect arguments + * formatted as lists. This syntax is similar to the above, with the following + * exceptions: + * + * - Lists are words separated by whitespace. + * - Neither ``$`` nor ``[`` substitutions are performed. + * - ``#`` and ``;`` have no special meaning. + * - ``\n`` is considered whitespace. + * + * :: + * + * list = space* word? ( space+ word )* space* ; + * + * word = single-quote | double-quote | brace + * | ( escape-sequence | continuation | ( character - space ) )+ ; + * + * single-quote = "'" ( escape-sequence | continuation + * | ( character - "'" ) )* "'" ; + * double-quote = '"' ( escape-sequence | continuation + * | ( character - '"' ) )* '"' ; + * escape-sequence = '\\' ( character - '\n' ) ; + * brace = '{' ( brace | '\\' character | ( character - '}' ) )* '}' ; + * + * continuation = '\\\n' ; + * space = ' ' | '\f' | '\n' | '\r' | '\t' | '\v' ; + * character = [\x00-\xff] ; + * + * Because of the similarity of these grammars, they may be parsed using the + * same functions. Where differences occur, they are selected by a boolean + * parameter. + * + * In general, each parse function must determine two things: what symbol to try + * and parse when there are multiple possible choices, and where the end of the + * symbol is. To choose from symbols, we consider the FIRST set of the possible + * symbols. The FIRST set is the set of terminals which may begin a symbol. For + * example, in the grammar + * + * :: + * + * number = sign? integer fraction? ; + * integer = digit+ ; + * fraction = '.' digit+ ; + * sign = '+' | '-' ; + * digit = [0-9] ; + * + * the FIRST sets are + * + * :: + * + * FIRST(sign) = '+' | '-' ; + * FIRST(digit) = [0-9] ; + * FIRST(fraction) = '.' ; + * FIRST(integer) = FIRST(digit) ; + * = [0-9] ; + * FIRST(number) = FIRST(sign) | FIRST(integer) ; + * = '+' | '-' | [0-9] ; + * + * A parser, when deciding whether to parse a sign or an integer, may observe + * whether the number begins with FIRST(sign) or FIRST(integer). To prevent + * backtracking, the FIRST sets of all symbols which must be picked from must + * be disjoint. When this is not the case (like for escape-sequence and + * continuation), the analogous SECOND set may be used. + * + * The FOLLOW set of a symbol is the union of all FIRST sets which may come + * after it. For the above grammar, the FOLLOW sets are + * + * :: + * + * FOLLOW(number) = ; + * FOLLOW(sign) = FIRST(integer) ; + * = [0-9] ; + * FOLLOW(integer) = FIRST(fraction) | ; + * = '.' | ; + * FOLLOW(fraction) = ; + * FOLLOW(digit) = FOLLOW(integer) | FOLLOW(fraction) ; + * = '.' | ; + * + * The parser, when deciding whether it is done parsing an integer, may consider + * whether the current character matches FOLLOW(integer). To prevent + * backtracking, the FOLLOW sets of each symbol must not contain characters + * which may be present at the end of the symbol. In general, the FOLLOW set of + * a symbol is interesting if the symbol has a trailing repeating portion which + * is ended only by the next symbol. + */ + +static struct lil_symbol *parse_word(struct lil_parser *p, bool islist); +static struct lil_list *parse_script(struct lil_parser *p); + +/** + * eof() - Whether we have reached the end of input + * @p: The parser + * + * Return: %true if there are no more characters left + */ +static bool eof(struct lil_parser *p) { - return ch == '$' || ch == '{' || ch == '}' || ch == '[' || ch == ']' || - ch == '"' || ch == '\'' || ch == ';'; + return p->pos.head >= p->len; } -static int eolchar(char ch) +/** + * peek() - Peek at the next character to parse + * @p: The parser + * + * eof() for @p must be %false. + * + * Return: The character which would be returned by pop(). + */ +static char peek(struct lil_parser *p) { - return ch == '\n' || ch == '\r' || ch == ';'; + return p->code[p->pos.head]; } -static int ateol(struct lil *lil) +/** + * peek2() - Peek two characters ahead + * @p: The parser + * + * NB: Unlike peek(), peek2() checks for eof(). + * + * Return: The character which would be returned by pop()ing twice, or %-1 if no + * such character is present (due to the end of input). + */ +static char peek2(struct lil_parser *p) { - return !(lil->ignoreeol) && eolchar(lil->code[lil->head]); + return p->pos.head + 1 < p->len ? p->code[p->pos.head + 1] : -1; } -static void lil_skip_spaces(struct lil *lil) +/** + * pop() - Advance the parser by one character + * @p: The parser + * + * Return: The character which is next in the input + */ +static char pop(struct lil_parser *p) { - while (lil->head < lil->clen) { - if (lil->code[lil->head] == '#') { - if (lil->code[lil->head + 1] == '#' && - lil->code[lil->head + 2] != '#') { - lil->head += 2; - while (lil->head < lil->clen) { - if ((lil->code[lil->head] == '#') && - (lil->code[lil->head + 1] == '#') && - (lil->code[lil->head + 2] != '#')) { - lil->head += 2; - break; - } - lil->head++; - } - } else { - while (lil->head < lil->clen && - !eolchar(lil->code[lil->head])) - lil->head++; + char ret = p->code[p->pos.head++]; + +#if IS_ENABLED(CONFIG_LIL_FULL) + p->pos.column++; + if (ret == '\n') { + p->pos.line++; + p->pos.column = 1; + } +#endif + return ret; +} + +#if IS_ENABLED(CONFIG_LIL_DEBUG) +#define set_err_func(p, _func) (p)->err.func = _func +#else +#define set_err_func(p, _func) +#endif + +#define err_oom(p) do { \ + (p)->err.type = PERR_OOM; \ + set_err_func(p, __func__); \ +} while (0) + +#define err_unexpected_1(p) do { \ + assert(!eof(p)); \ + (p)->err.type = PERR_UNEXPECTED; \ + set_err_func(p, __func__); \ +} while (0) + +#if IS_ENABLED(CONFIG_LIL_FULL) +#define err_expected(p, c, _matching) do { \ + (p)->err.type = PERR_EXPECTED; \ + set_err_func(p, __func__); \ + (p)->err.expected = (c); \ + (p)->err.matching = (_matching); \ +} while (0) + +#define err_unexpected_2(p, _matching) do { \ + assert(eof(p)); \ + (p)->err.type = PERR_UNEXPECTED; \ + set_err_func(p, __func__); \ + (p)->err.matching = (_matching); \ +} while (0) +#else /* CONFIG_LIL_FULL */ +#define UNUSED(x) (void)(x) + +#define err_expected(p, c, _matching) do { \ + (p)->err.type = PERR_EXPECTED; \ + set_err_func(p, __func__); \ + UNUSED(_matching); \ +} while (0) + +#define err_unexpected_2(p, _matching) do { \ + assert(eof(p)); \ + (p)->err.type = PERR_UNEXPECTED; \ + set_err_func(p, __func__); \ + UNUSED(_matching); \ +} while (0) +#endif + +#define err_unexpected(p, ...) \ + __concat(err_unexpected_, __count_args(p, ##__VA_ARGS__)) (p, ##__VA_ARGS__) + +/** + * expect() - Expect a specific character next + * @p: The parser + * @sym: The symbol to return on success; free()'d on error + * @expected: The character to expect + * @matching: The position of the thing we expected to match + * + * Return: @w, or %NULL on error + */ +static struct lil_symbol *_expect(struct lil_parser *p, struct lil_symbol *sym, + char expected, struct lil_position *matching, + const char *func) +{ + char got; + + if (!sym) + return sym; + + if (eof(p)) + goto err; + + got = pop(p); + if (got != expected) + goto err; + return sym; + +err: + lil_free_symbol(sym); + err_expected(p, expected, *matching); + set_err_func(p, func); + return NULL; +} + +#define expect(p, sym, expected, matching) \ + _expect(p, sym, expected, matching, __func__) + +#define CASE_SPACE \ + case '\f': \ + case '\r': \ + case '\t': \ + case '\v': \ + case ' ' \ + +#define CASE_WHITESPACE \ + CASE_SPACE: \ + case '\\' + +#define CASE_TERMINATOR \ + case '\n': \ + case ';': \ + case '#' + +/** + * parse_continuation() - Parse a line continuation + * @p: The parser + * + * FIRST(continuation) = '\\' ; + * SECOND(continuation) = '\n' ; + */ +static void parse_continuation(struct lil_parser *p) +{ + assert(pop(p) == '\\'); + assert(pop(p) == '\n'); +} + +/** + * parse_whitespace() - Parse a single unit of whitespace, if present + * @p: The parser + * + * FIRST(whitespace) = space | '\\' ; + */ +static void parse_whitespace(struct lil_parser *p) +{ + switch (peek(p)) { + CASE_SPACE: + pop(p); + return; + case '\\': + parse_continuation(p); + return; + default: + return; + } +} + +/** + * parse_dollar() - Parse a variable reference + * @p: The parser + * + * FIRST(variable) = '$' ; + * FOLLOW(variable) = FOLLOW(word) ; + * + * Return: A symbol containing the name of the variable, or %NULL on error + */ +static struct lil_symbol *parse_dollar(struct lil_parser *p) +{ + struct lil_symbol *sym = calloc(1, sizeof(struct lil_symbol)); + + if (!sym) { + err_oom(p); + return NULL; + } + + assert(pop(p) == '$'); + sym->type = LIL_SYMBOL_VARIABLE; + sym->symbol = parse_word(p, false); + if (!sym->symbol) { + lil_free_symbol(sym); + return NULL; + } + return sym; +} + +/** + * parse_bracket() - Parse a subscript enclosed in brackets + * @p: The parser + * + * FIRST(bracket) = '[' ; + * FOLLOW(bracket) = FOLLOW(word) ; + * + * Return: A symbol containing the script, or %NULL on error + */ +static struct lil_symbol *parse_bracket(struct lil_parser *p) +{ + struct lil_position savepos = p->pos; + + assert(pop(p) == '['); + return expect(p, list_to_symbol(parse_script(p)), ']', &savepos); +} + +/** + * parse_comment() - Parse a comment + * @p: The parser + * + * FIRST(comment) = '#' ; + */ +static void parse_comment(struct lil_parser *p) +{ + assert(pop(p) == '#'); + while (!eof(p)) { + switch (pop(p)) { + case '\n': + return; + } + } +} + +/** + * parse_brace() - Parse a value enclosed in braces + * @p: The parser + * @islist: If we are parsing a list + * + * This function is used to parse braces in scripts and lists. If we are parsing + * a list, then comments are not parsed. + * + * FIRST(brace) = '{' ; + * FOLLOW(brace) = FOLLOW(word) ; + * + * Return: A symbol containing a value, or %NULL on error + */ +static struct lil_symbol *parse_brace(struct lil_parser *p, bool islist) +{ + struct lil_value *val = alloc_value(NULL); + struct lil_position savepos = p->pos; + + if (!val) + goto oom; + + assert(pop(p) == '{'); + while (!eof(p)) { + switch (peek(p)) { + case '{': { + bool fail; + struct lil_symbol *brace = parse_brace(p, islist); + + if (!brace) + goto err; + + fail = lil_append_char(val, '{') || + lil_append_val(val, &brace->value) || + lil_append_char(val, '}'); + + lil_free_symbol(brace); + if (fail) + goto oom; + break; + } + case '#': + if (islist) + goto character; + parse_comment(p); + break; + case '\\': { + struct lil_position escapepos = p->pos; + + if (lil_append_char(val, pop(p))) + goto oom; + + if (eof(p)) { + err_unexpected(p, escapepos); + goto err; } - } else if (lil->code[lil->head] == '\\' && - eolchar(lil->code[lil->head + 1])) { - lil->head++; - while (lil->head < lil->clen && - eolchar(lil->code[lil->head])) - lil->head++; - } else if (eolchar(lil->code[lil->head])) { - if (lil->ignoreeol) - lil->head++; + } + fallthrough; + default: +character: + if (lil_append_char(val, pop(p))) + goto oom; + break; + case '}': + pop(p); + return value_to_symbol(val); + } + } + err_expected(p, '}', savepos); + goto err; + +oom: + err_oom(p); +err: + lil_free_value(val); + return NULL; +} + +/** + * parse_escape() - Parse an escape sequence + * @p: The parser + * + * FIRST(escape) = '\\' ; + * SECOND(escape) = character - '\n' ; + * + * Return: The character parsed. If there was an error, then @p->err.type will + * be set. + */ +static char parse_escape(struct lil_parser *p) +{ + char c; + struct lil_position savepos = p->pos; + + assert(pop(p) == '\\'); + if (eof(p)) { + err_unexpected(p, savepos); + return -1; + } + + c = pop(p); + switch (c) { + case 'a': + return '\a'; + case 'b': + return '\b'; + case 'f': + return '\f'; + case 'n': + return '\n'; + case 't': + return '\t'; + case 'r': + return '\r'; + case 'v': + return '\v'; + case '\n': + assert(0); + fallthrough; + default: + return c; + } +} + +/** + * parse_quote() - Parse a value in quotes + * @p: The parser + * @q: The quote character, either ``'`` or ``"``. The special value %-1 may + * also be used to specify that no enclosing quotes are expected. + * @islist: If we are parsing a list + * + * This function is used both for parsing scripts and lists. When used in + * scripts, we parse a list of symbols which must be later evaluated and + * concatenated. When parsing a list, we just parse a value because lists do not + * contain $ or [ substitutions. + * + * When @q is %-1, then no enclosing quotes are parsed. This may be used to + * parse a string into a form which may have substitutions performed on it. + * + * FIRST(quote) = "'" | '"' ; + * FOLLOW(quote) = FOLLOW(word) ; + * + * Return: A list of symbols, a value (if @islist), or %NULL on error. + */ +static struct lil_symbol *parse_quote(struct lil_parser *p, char q, bool islist) +{ + struct lil_position savepos = p->pos; + struct lil_list *list = islist ? NULL : lil_alloc_list(); + struct lil_value *val = alloc_value(NULL); + + if ((!islist && !list) || !val) + goto oom; + + if (q != -1) { + assert(q == '\'' || q == '"'); + assert(pop(p) == q); + } + + while (!eof(p)) { + char c = peek(p); + + switch (c) { + case '$': + case '[': { + struct lil_symbol *sym; + + if (islist) + goto character; + + if (val->l) { + if (lil_list_append(list, val)) + goto oom; + val = alloc_value(NULL); + if (!val) + goto oom; + } + + if (c == '[') + sym = parse_bracket(p); else - break; - } else if (isspace(lil->code[lil->head])) { - lil->head++; - } else { + sym = parse_dollar(p); + + if (!sym) + goto err; + + if (lil_list_append(list, sym)) + goto oom; + + break; + } + case '\'': + case '"': + if (c == q) { + pop(p); + goto out; + } + goto character; + case '\\': + if (peek2(p) == '\n') { + parse_continuation(p); + continue; + } + + c = parse_escape(p); + if (p->err.type) + goto err; + goto character_post_pop; + default: +character: + c = pop(p); +character_post_pop: + if (lil_append_char(val, c)) + goto oom; break; } } + + if (q == -1) + goto out; + err_expected(p, q, savepos); + goto err; + +out: + if (islist) + return value_to_symbol(val); + else if (lil_list_append(list, val)) + goto oom; + return list_to_symbol(list); + +oom: + err_oom(p); +err: + lil_free_list(list); + lil_free_value(val); + return NULL; } -static struct lil_value *get_bracketpart(struct lil *lil) +/** + * parse_word() - Parse a word + * @p: The parser + * + * This function used to parse words for both scripts and lists. For lists, $ + * and [ subtitution is not performed. In addition, there are less illegal + * characters (since we no longer need to worry about some cases of nesting). + * Because of this, the FIRST and FOLLOW sets for parsing scripts are: + * + * FIRST(word) = character - ']' - '\\' - '}' - terminator - FIRST(whitespace) ; + * FOLLOW(word) = ''' | '"' | ']' | FIRST(terminator) | space | ; + * + * and the sets when parsing lists are: + * + * FIRST(word) = character - space ; + * FOLLOW(word) = space | ; + * + * Return: A symbol for one word, or %NULL on error. If we are parsing a list, + * this symbol will always have type %LIL_SYMBOL_VALUE. + */ +static struct lil_symbol *parse_word(struct lil_parser *p, bool islist) { - size_t cnt = 1; - struct lil_value *val, *cmd = alloc_value(NULL); - int save_eol = lil->ignoreeol; + struct lil_value *word; - lil->ignoreeol = 0; - lil->head++; - while (lil->head < lil->clen) { - if (lil->code[lil->head] == '[') { - lil->head++; - cnt++; - lil_append_char(cmd, '['); - } else if (lil->code[lil->head] == ']') { - lil->head++; - if (--cnt == 0) - break; - else - lil_append_char(cmd, ']'); - } else { - lil_append_char(cmd, lil->code[lil->head++]); - } + switch (peek(p)) { + case '\'': + case '"': + return parse_quote(p, peek(p), islist); + case '{': + return parse_brace(p, islist); + case '[': + if (islist) + break; + return parse_bracket(p); + case '$': + if (islist) + break; + return parse_dollar(p); + case '\\': + if (peek2(p) == '\n') + goto terminator; + break; + case ']': + case '}': + case ';': + case '#': + if (islist) + break; + fallthrough; + case '\n': + CASE_SPACE: +terminator: + err_unexpected(p); + return NULL; } - val = lil_parse_value(lil, cmd, 0); - lil_free_value(cmd); - lil->ignoreeol = save_eol; - return val; -} + word = alloc_value(NULL); + if (!word) + goto oom; -static struct lil_value *get_dollarpart(struct lil *lil) -{ - struct lil_value *val, *name, *tmp; + do { + char c = peek(p); - lil->head++; - name = next_word(lil); - tmp = alloc_value("set "); - lil_append_val(tmp, name); - lil_free_value(name); - - val = lil_parse_value(lil, tmp, 0); - lil_free_value(tmp); - return val; -} - -static struct lil_value *next_word(struct lil *lil) -{ - struct lil_value *val; - size_t start; - - lil_skip_spaces(lil); - if (lil->code[lil->head] == '$') { - val = get_dollarpart(lil); - } else if (lil->code[lil->head] == '{') { - size_t cnt = 1; - - lil->head++; - val = alloc_value(NULL); - while (lil->head < lil->clen) { - if (lil->code[lil->head] == '{') { - lil->head++; - cnt++; - lil_append_char(val, '{'); - } else if (lil->code[lil->head] == '}') { - lil->head++; - if (--cnt == 0) - break; - else - lil_append_char(val, '}'); - } else { - lil_append_char(val, lil->code[lil->head++]); + switch (c) { + case ']': + case '\'': + case '"': + case ';': + case '#': + if (islist) + goto character; + fallthrough; + case '\n': + CASE_SPACE: + return value_to_symbol(word); + case '{': + case '}': + case '[': + case '$': + if (islist) + goto character; + err_unexpected(p); + return NULL; + case '\\': + if (peek2(p) == '\n') { + parse_continuation(p); + continue; } + + c = parse_escape(p); + if (p->err.type) + goto err; + goto character_post_pop; + default: +character: + c = pop(p); +character_post_pop: + if (lil_append_char(word, c)) + goto oom; } - } else if (lil->code[lil->head] == '[') { - val = get_bracketpart(lil); - } else if (lil->code[lil->head] == '"' || - lil->code[lil->head] == '\'') { - char sc = lil->code[lil->head++]; + } while (!eof(p)); - val = alloc_value(NULL); - while (lil->head < lil->clen) { - if (lil->code[lil->head] == '[' || - lil->code[lil->head] == '$') { - struct lil_value *tmp = - lil->code[lil->head] == '$' ? - get_dollarpart(lil) : - get_bracketpart(lil); + return value_to_symbol(word); - lil_append_val(val, tmp); - lil_free_value(tmp); - lil->head--; /* avoid skipping the char below */ - } else if (lil->code[lil->head] == '\\') { - lil->head++; - switch (lil->code[lil->head]) { - case 'b': - lil_append_char(val, '\b'); - break; - case 't': - lil_append_char(val, '\t'); - break; - case 'n': - lil_append_char(val, '\n'); - break; - case 'v': - lil_append_char(val, '\v'); - break; - case 'f': - lil_append_char(val, '\f'); - break; - case 'r': - lil_append_char(val, '\r'); - break; - case '0': - lil_append_char(val, 0); - break; - case 'a': - lil_append_char(val, '\a'); - break; - case 'c': - lil_append_char(val, '}'); - break; - case 'o': - lil_append_char(val, '{'); - break; - default: - lil_append_char(val, - lil->code[lil->head]); - break; +oom: + err_oom(p); +err: + lil_free_value(word); + return NULL; +} + +/** + * parse_command() - Parse a command + * @p: The parser + * + * FIRST(command) = FIRST(word) ; + * FOLLOW(command) = ']' | FIRST(terminator) | ; + * + * Return: A list of words which compose the command, or %NULL on error + */ +static struct lil_list *parse_command(struct lil_parser *p) +{ + struct lil_symbol *word; + struct lil_list *command = lil_alloc_list(); + + if (!command) + goto oom; + list_to_symbol(command)->type = LIL_SYMBOL_COMMAND; + + do { + word = parse_word(p, false); + if (!word) + goto err; + + if (lil_list_append(command, word)) + goto oom; + + if (eof(p)) + return command; + + switch (peek(p)) { + CASE_WHITESPACE: + do { + switch (peek(p)) { + case '\\': + if (peek2(p) != '\n') + break; + fallthrough; + CASE_SPACE: + parse_whitespace(p); + continue; + case ']': + CASE_TERMINATOR: + return command; } - } else if (lil->code[lil->head] == sc) { - lil->head++; + break; - } else { - lil_append_char(val, lil->code[lil->head]); - } - lil->head++; + } while (!eof(p)); + + continue; + case ']': + CASE_TERMINATOR: + return command; } - } else { - start = lil->head; - while (lil->head < lil->clen && - !isspace(lil->code[lil->head]) && - !islilspecial(lil->code[lil->head])) - lil->head++; - val = alloc_value_len(lil->code + start, lil->head - start); - } - return val ? val : alloc_value(NULL); + + err_unexpected(p); + goto err; + } while (!eof(p)); + + return command; + +oom: + err_oom(p); +err: + lil_free_list(command); + return NULL; } -static struct lil_list *substitute(struct lil *lil) +/** + * parse_terminator() - Parse the end of a command + * @p: The parser + * + * FIRST(terminator) = ';' | '\n' | '#' ; + */ +static void parse_terminator(struct lil_parser *p) { - struct lil_list *words = lil_alloc_list(); + switch (peek(p)) { + case '\n': + case ';': + pop(p); + return; + case '#': + parse_comment(p); + return; + } + assert(0); +} - lil_skip_spaces(lil); - while (lil->head < lil->clen && !ateol(lil) && !lil->err) { - struct lil_value *w = alloc_value(NULL); +/** + * parse_script - Parse a script + * + * FIRST(script) = FIRST(whitespace) | FIRST(command) | FIRST(terminator) ; + * FOLLOW(script) = ']' | ; + * + * Return: A symbol containing a list of commands which compose the script, or + * %NULL on error + */ +static struct lil_list *parse_script(struct lil_parser *p) +{ + struct lil_list *command; + struct lil_list *script = lil_alloc_list(); - do { - size_t head = lil->head; - struct lil_value *wp = next_word(lil); + if (!script) + goto oom; + list_to_symbol(script)->type = LIL_SYMBOL_SCRIPT; - if (head == - lil->head) { /* something wrong, the parser can't proceed */ - lil_free_value(w); - lil_free_value(wp); - lil_free_list(words); - return NULL; + do { + while (!eof(p)) { + switch (peek(p)) { + case '\\': + if (peek2(p) != '\n') + break; + fallthrough; + CASE_SPACE: + parse_whitespace(p); + continue; } + break; + } - lil_append_val(w, wp); - lil_free_value(wp); - } while (lil->head < lil->clen && - !eolchar(lil->code[lil->head]) && - !isspace(lil->code[lil->head]) && !lil->err); - lil_skip_spaces(lil); + switch (peek(p)) { + case '\\': + if (peek2(p) != '\n') + break; + fallthrough; + CASE_SPACE: + err_unexpected(p); + goto err; + CASE_TERMINATOR: + parse_terminator(p); + continue; + case ']': + return script; + } - lil_list_append(words, w); + command = parse_command(p); + if (!command) + goto err; + + if (lil_list_append(script, list_to_symbol(command))) + goto oom; + } while (!eof(p)); + return script; + +oom: + err_oom(p); +err: + lil_free_list(script); + return NULL; +} + +#if IS_ENABLED(CONFIG_LIL_DEBUG) +static void do_print_symbol(struct lil_symbol *sym, unsigned int level) +{ + unsigned int i; + + for (i = 0; i < level; i++) + putc('\t'); + + switch (sym->type) { + case LIL_SYMBOL_VALUE: + puts(lil_to_string(&sym->value)); + putc('\n'); + break; + case LIL_SYMBOL_SCRIPT: + puts("