From mboxrd@z Thu Jan 1 00:00:00 1970 Delivery-date: Wed, 22 Dec 2021 14:07:08 +0100 Received: from metis.ext.pengutronix.de ([2001:67c:670:201:290:27ff:fe1d:cc33]) by lore.white.stw.pengutronix.de with esmtps (TLS1.3) tls TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 (Exim 4.94.2) (envelope-from ) id 1n01Km-00EgIw-2r for lore@lore.pengutronix.de; Wed, 22 Dec 2021 14:07:08 +0100 Received: from localhost ([127.0.0.1] helo=metis.ext.pengutronix.de) by metis.ext.pengutronix.de with esmtp (Exim 4.92) (envelope-from ) id 1n01Kk-0004YW-W5; Wed, 22 Dec 2021 14:07:07 +0100 Received: from mail-eopbgr140089.outbound.protection.outlook.com ([40.107.14.89] helo=EUR01-VE1-obe.outbound.protection.outlook.com) by metis.ext.pengutronix.de with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1n01HA-0000lC-QF for ptxdist@pengutronix.de; Wed, 22 Dec 2021 14:03:41 +0100 ARC-Seal: i=1; a=rsa-sha256; s=arcselector9901; d=microsoft.com; cv=none; b=SIcF02mvoNh6MsvNMUESh9XywJ2rA+Ohh4PRLPLIuFQccrYAMoTm6yNXyDTsdNe9TUYCvv6T6z1BhC1es3+owPoiYcOYvWo08R7ECBx5DvgFzcx4IshleUvDVjBdGcq6sJirOD9NZVLvFXzt5fDganRG3pcxihsKzlmCDl0uUK2rWsWzfLV14DAzsKUWrBjGc9N6aVAXgzcM7kOoCOy10r+YMBfaEn+6mpINpQxNdHw5lrHlNVHAJElGXCt+K2/SBF8Lupq2o7O8as35A5zTs6r3+yIC0Aaq+vvzK1XrC6FyRkr58Sd/snfeQZzods8ZpsfufZc/IE62XIPdTxCOEw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=microsoft.com; s=arcselector9901; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-AntiSpam-MessageData-ChunkCount:X-MS-Exchange-AntiSpam-MessageData-0:X-MS-Exchange-AntiSpam-MessageData-1; bh=M60tRsvd5T0UGOQo4VITHvLZd3OMJDdyb7dfzpnZeMg=; b=BJ6byLVTAc239CRkNBZNCIiUmKQHOP2GBrsirhJvODOgibl00qRWfHFGYxE016f5ZUYB4Pvj4BaxyXvUlurC6SaL3G9LpGaqPO5RZy5D7/DzUav0IqwCY9eXsN5dmtYhOJ9OXedc2EFSGBmCuE10J1oD68oSdZTJqxof54Q9nsh60hoZjGNb1Q4q5Eu/eDJuJFULdnjxEOwsaLJB6WcuIGz+DxNyY6PSLxDZS6xHpPKcppHN6NFcqN6Hu+KN9tQEasNuEJ6CnCPjNiQWIOZtufo290l9H2GF5HaC0LvxjTPZGL+t8RDRBsDi7cIbsA7NzOyDoF+8bAaJtu6bT7XCMQ== ARC-Authentication-Results: i=1; mx.microsoft.com 1; spf=pass smtp.mailfrom=t2data.com; dmarc=pass action=none header.from=t2data.com; dkim=pass header.d=t2data.com; arc=none DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=t2datacom.onmicrosoft.com; s=selector1-t2datacom-onmicrosoft-com; h=From:Date:Subject:Message-ID:Content-Type:MIME-Version:X-MS-Exchange-SenderADCheck; bh=M60tRsvd5T0UGOQo4VITHvLZd3OMJDdyb7dfzpnZeMg=; b=De4+NOK8FxAZz902fVV12FKU/CUS1xBYG3vC+Z3t9pkyJwffCJK9jDeGRWZ1odlbrGsncW9h0NKen2Z6Vp+q3P2YpOcXrkOeFrH3BwAKjj14PdDsZh7AKKAZoKB3hNa0s8Bm0WdeTuF87Wb7Y3TogIjLzfsph+zUT9sbgGNl9P8= Authentication-Results: dkim=none (message not signed) header.d=none;dmarc=none action=none header.from=t2data.com; Received: from DB9P251MB0618.EURP251.PROD.OUTLOOK.COM (2603:10a6:10:334::22) by DB9P251MB0077.EURP251.PROD.OUTLOOK.COM (2603:10a6:10:2c9::7) with Microsoft SMTP Server (version=TLS1_2, cipher=TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384) id 15.20.4823.18; Wed, 22 Dec 2021 13:03:18 +0000 Received: from DB9P251MB0618.EURP251.PROD.OUTLOOK.COM ([fe80::91c:5b0e:d5b2:5b18]) by DB9P251MB0618.EURP251.PROD.OUTLOOK.COM ([fe80::91c:5b0e:d5b2:5b18%6]) with mapi id 15.20.4823.019; Wed, 22 Dec 2021 13:03:18 +0000 From: Christian Melki To: ptxdist@pengutronix.de Date: Wed, 22 Dec 2021 14:02:42 +0100 Message-Id: <20211222130304.2549154-1-christian.melki@t2data.com> X-Mailer: git-send-email 2.30.2 X-ClientProxiedBy: GV3P280CA0036.SWEP280.PROD.OUTLOOK.COM (2603:10a6:150:9::23) To DB9P251MB0618.EURP251.PROD.OUTLOOK.COM (2603:10a6:10:334::22) MIME-Version: 1.0 X-MS-PublicTrafficType: Email X-MS-Office365-Filtering-Correlation-Id: a04503e1-cace-419d-59f6-08d9c54b6c77 X-MS-TrafficTypeDiagnostic: DB9P251MB0077:EE_ X-Microsoft-Antispam-PRVS: X-MS-Oob-TLC-OOBClassifiers: OLM:256; X-MS-Exchange-SenderADCheck: 1 X-MS-Exchange-AntiSpam-Relay: 0 X-Microsoft-Antispam: BCL:0; X-Microsoft-Antispam-Message-Info: 6486p8gSodz9Mr80i+K02ZcTS+HNnuIbCoJVKaDvfH+lOmx8PmYtNqlsd337HlzgOm/44cpKDo43eghXWuAk9y8pdkeHM8M6tuhZHDfHSyRGcAGg3q8f3RD1e4aIqg88KctD0elPsMtGj4GSAwIbZknya45T/g/OhAJDJXWEaMZOU6YDWaa0Mfsg0zh/CowO/2jFw6O9n5umWpFpFkyMw1oiI523yE/GBqLJ+wq8IBC78Wj1jwJZsAtteo3I5TCcpy5Id/ChV3k9jt0ZyZyXvbyIsaHXPN0vBpLJwtdQ0kY9yyANrlQeGFiRNIowgXgw1fPQEHiThDKUa8BLqhNyhepYoLjRVPwIjMr6Z0tmjH8r73Hw7lWnUoKm38PlH6GQzA1i/lWtDj0uUYrAj5MJNawT+ahca/aWPJxBbi5edkYXHEhAmfUQva8XYRqXg4NZbYj5LoMJ9ncajLVIYd4YItT6BnhR7deHdRvR+gaPCmiblY94QrjKiVOduwbL00vgpiDSBigyWIarBPGp0JDm9vh13bLNMks5x0e8UfzpvUa1JmuGMC/9dg/BqRyZbQXF28ETTSz2xr14Cx4W4kV6pypNuD0cfdfq1u1Sjo62XtpqPWvPYzHDOvvqEJRo++k/06OrKkI4LJ6RS430GredONZs5Vqb/upPEuMBHk4H6cooxTjbXGvAA07gAmjgr4LssTAx9KbDbRT+rko1PiUr0HoIVHNNbCGIhv7yP18EEzEtY/k4f1h5ABovf0fZwZQMiOYtyz8eY0p3wRDAWIfd4hBAybkE32QphqSdCmv3sRtVGt/vkGfAbi+nASGOhmvN0wwijBeopSarUX6wtDfUeA== X-Forefront-Antispam-Report: CIP:255.255.255.255; CTRY:; LANG:en; SCL:1; SRV:; IPV:NLI; SFV:NSPM; H:DB9P251MB0618.EURP251.PROD.OUTLOOK.COM; PTR:; CAT:NONE; SFS:(396003)(42606007)(39830400003)(376002)(346002)(366004)(136003)(6666004)(6506007)(6512007)(8936002)(6916009)(4001150100001)(52116002)(83380400001)(8676002)(186003)(84970400001)(26005)(38100700002)(30864003)(2906002)(38350700002)(508600001)(1076003)(316002)(66476007)(66556008)(86362001)(66946007)(44832011)(2616005)(36756003)(5660300002)(6486002)(21314003)(2004002)(559001)(579004); DIR:OUT; SFP:1101; X-MS-Exchange-AntiSpam-MessageData-ChunkCount: 1 X-MS-Exchange-AntiSpam-MessageData-0: =?us-ascii?Q?wqpdQYFuGtXthw5bVbYyK5b5xi7pnMjFw2mhQkRNE8yG8fOHmejlcH5Gkx+r?= =?us-ascii?Q?QIfmp+mrzTQab6fydj0KzKahBUVxbF4DNoGna78VM8gp8ZMFOHzTlM5E+cTy?= =?us-ascii?Q?jU9LI6SOFLHayMkETwjG+/vTM+WLLEp+o0H8rOmciNJhQKEW6kCCviogMcyb?= =?us-ascii?Q?5ofuRLbAuYgfPFQofzbpgaAMYswy/CWn9MCokvGjhCrfkzaNYEvQTR8pivgv?= =?us-ascii?Q?To3eUNy68Ienfnz3WhXVjp7fa0XVbvSivqT3r6GdOzm3epne3J0PXI8/CmmF?= =?us-ascii?Q?C/qZnM7hV0h3+ZGYXHJQjG7YOyX6+rk5cgr07/eSL1okSAVlcWqgoFJdK+qe?= =?us-ascii?Q?TddMwiRUAq3R4f9fJQ2w3wxlMGEBqZX7x1frr/2bY3k3Hk+0fjMd9e8ptlce?= =?us-ascii?Q?jpr1ql0SL5//7Q/Fqf0MATuDwz/Y2N1BF26l5CLkWbcIe13Dgn300HlEe2KR?= =?us-ascii?Q?4SgHojhFcyo8pu5ATdStSAeb8rDWh+Qt6iYtIOO6vYVU6Rd2fA6c85o7c1Tb?= =?us-ascii?Q?QJj/FmEykFOt8mPDTtqDDRKNoNcnYLnnw+7+rJVyPN/E68Ze4JoUSE6fTc46?= =?us-ascii?Q?vCKqp4/hC66igrz5V5EGWFyGhWh3zwr6TIfx6bt9lgyXISWN6UuseOSIWJro?= =?us-ascii?Q?524Kj07ILE43cFojOZGwG+Uv7L+9rXnOVxp+KC4Q04sSemkKqCauWqPG0zqY?= =?us-ascii?Q?nNQnc1cf4IE7jZjiLhZS5Y5h2dSfBNfjNWFZMDgUWKKVMlY8yF8IxTKbFpXF?= =?us-ascii?Q?d3LdT2K+yRJBx/sZa4p2Djb6S2Z9RTqc8rauj11a0M/N5jrYi1Ef//nj9fop?= =?us-ascii?Q?a8U1unL9vtCxrbwZlrOVnAOwnZrPZM1HRGoBW1fQrYbVFMHYMxi4YPpGSrGh?= =?us-ascii?Q?22rLpTJtffe4T/v53iB7ty+fFRa0LJUa6lgUf4ej/gHeqa2FytBjQJ78PdOZ?= =?us-ascii?Q?x658bQfpN+SGnKccXq0X/ND+5dFytZbe0VvvZsWnbn10lQ1XQ91PM4CXJndv?= =?us-ascii?Q?NegFdLmqZDn3Qb7SZjbg/yUpctD/VY5rTpx3VNIY3en7UAPxuQjSIgDqBeFu?= =?us-ascii?Q?MpC0QpWPE9awQj3o7xdkRV87xUIZsaSPbK08U5vJ2yEM4l56W5ZmODYG94iQ?= =?us-ascii?Q?K5LDO3TYPRrEhkesFZQYWGHIAvYYwI8aSYlJCgj9ibuHXDGgdp14KtqFrgHZ?= =?us-ascii?Q?4pHxsnuUFWgkKz3iBJshZJVmyL2lkSU+ye12DhMVLeiBsGSjyO/GQ/9X2C4Y?= =?us-ascii?Q?IkVok8fjU7369txkCf31tBAcpgEo/CqsBi6+8GZZs+bmL6vrP6tqCwE+S1/P?= =?us-ascii?Q?EHDA8asjOJWy8S1rT3Jqpa2kl8LyjcgWXb367cx0ePIKKypIJbm2yKfvTEQz?= =?us-ascii?Q?XY7skVdYM16xuY7O9JyA2cfdH49/LWPzWxAKNL3xEY1U3LvuKUm66Vj74iEq?= =?us-ascii?Q?E5XSpkoeRkGFzPYnpdnpq2yWixPSqtQZTKs1Ez52MEQ+AajKCPohUPQakAJH?= =?us-ascii?Q?cw4ylOaHp/JKJJd0MPCf7Y0K50s7uwpQt5gQhy325/BvoxvtXUEDrIqvsBDr?= =?us-ascii?Q?X9NXcggB92RlEAUc9CkUWNnysHqmqMXer2SGeYxnY3E4SN7buNiNj5hDWTm8?= =?us-ascii?Q?Iqre7bevYA8PaWsXVM6yW2qiDML67pIRK2m+xCS6+WR8+CnZ6tx5x0chyj9q?= =?us-ascii?Q?Nu5392lf6bNBPYvP4O61zxul2N8=3D?= X-OriginatorOrg: t2data.com X-MS-Exchange-CrossTenant-Network-Message-Id: a04503e1-cace-419d-59f6-08d9c54b6c77 X-MS-Exchange-CrossTenant-AuthSource: DB9P251MB0618.EURP251.PROD.OUTLOOK.COM X-MS-Exchange-CrossTenant-AuthAs: Internal X-MS-Exchange-CrossTenant-OriginalArrivalTime: 22 Dec 2021 13:03:18.6689 (UTC) X-MS-Exchange-CrossTenant-FromEntityHeader: Hosted X-MS-Exchange-CrossTenant-Id: 27928da5-aacd-4ba1-9566-c748a6863e6c X-MS-Exchange-CrossTenant-MailboxType: HOSTED X-MS-Exchange-CrossTenant-UserPrincipalName: jDBw/+0aK+X/skb8ab5bQpY//y9H6fwxOoCOIK2u46me/dzlvl/fIU59toGubAAAp3HAyNx2nm7Mknv/sOwQPeishX/+uoWkhMBbwCg9P2c= X-MS-Exchange-Transport-CrossTenantHeadersStamped: DB9P251MB0077 X-Spam-Checker-Version: SpamAssassin 3.4.2 (2018-09-13) on metis.ext.pengutronix.de X-Spam-Level: X-Spam-Status: No, score=-1.9 required=4.0 tests=AWL,BAYES_00,DKIM_SIGNED, DKIM_VALID,RCVD_IN_DNSWL_NONE,RCVD_IN_MSPIKE_H2,SPF_HELO_PASS,SPF_PASS autolearn=ham autolearn_force=no version=3.4.2 Subject: [ptxdist] [PATCH] bash: Version bump. 4.3.30 (+patches-33) -> 5.1.8 (patches-12). X-BeenThere: ptxdist@pengutronix.de X-Mailman-Version: 2.1.29 Precedence: list List-Id: PTXdist Development Mailing List List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: ptxdist@pengutronix.de Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Sender: "ptxdist" X-SA-Exim-Connect-IP: 127.0.0.1 X-SA-Exim-Mail-From: ptxdist-bounces@pengutronix.de X-SA-Exim-Scanned: No (on metis.ext.pengutronix.de); SAEximRunCond expanded to false Upgrade bash to 5.1.8 plus patches to patchlevel 12. Roll up patches as in previous series. Fixes CVE-2019-18276 and CVE-2019-9924 with a new baseline, without patches. Fixes loads of bugs in bash. Signed-off-by: Christian Melki --- .../bash-4.3.30/0001-Bash-4.3-patch-31.patch | 5467 ----------------- .../bash-4.3.30/0002-Bash-4.3-patch-32.patch | 5409 ---------------- .../bash-4.3.30/0003-Bash-4.3-patch-33.patch | 204 - patches/bash-4.3.30/series | 6 - .../bash-5.1.8/0001-Bash-5.1-patch-12.patch | 262 + patches/bash-5.1.8/series | 1 + rules/bash.make | 4 +- 7 files changed, 265 insertions(+), 11088 deletions(-) delete mode 100644 patches/bash-4.3.30/0001-Bash-4.3-patch-31.patch delete mode 100644 patches/bash-4.3.30/0002-Bash-4.3-patch-32.patch delete mode 100644 patches/bash-4.3.30/0003-Bash-4.3-patch-33.patch delete mode 100644 patches/bash-4.3.30/series create mode 100644 patches/bash-5.1.8/0001-Bash-5.1-patch-12.patch create mode 100644 patches/bash-5.1.8/series diff --git a/patches/bash-4.3.30/0001-Bash-4.3-patch-31.patch b/patches/bash-4.3.30/0001-Bash-4.3-patch-31.patch deleted file mode 100644 index d9a187dcb..000000000 --- a/patches/bash-4.3.30/0001-Bash-4.3-patch-31.patch +++ /dev/null @@ -1,5467 +0,0 @@ -From: Chet Ramey -Date: Thu, 15 Jan 2015 10:20:04 -0500 -Subject: [PATCH] Bash-4.3 patch 31 - ---- - patchlevel.h | 2 +- - subst.h | 1 + - variables.c | 32 +- - variables.c.orig | 5365 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ - 4 files changed, 5397 insertions(+), 3 deletions(-) - create mode 100644 variables.c.orig - -diff --git a/patchlevel.h b/patchlevel.h -index e5dde5245275..0ad46aafbdd9 100644 ---- a/patchlevel.h -+++ b/patchlevel.h -@@ -25,6 +25,6 @@ - regexp `^#define[ ]*PATCHLEVEL', since that's what support/mkversion.sh - looks for to find the patch level (for the sccs version string). */ - --#define PATCHLEVEL 30 -+#define PATCHLEVEL 31 - - #endif /* _PATCHLEVEL_H_ */ -diff --git a/subst.h b/subst.h -index cedaf8b6b444..1c300ab96b04 100644 ---- a/subst.h -+++ b/subst.h -@@ -47,6 +47,7 @@ - #define ASS_MKASSOC 0x0004 - #define ASS_MKGLOBAL 0x0008 /* force global assignment */ - #define ASS_NAMEREF 0x0010 /* assigning to nameref variable */ -+#define ASS_FROMREF 0x0020 /* assigning from value of nameref variable */ - - /* Flags for the string extraction functions. */ - #define SX_NOALLOC 0x0001 /* just skip; don't return substring */ -diff --git a/variables.c b/variables.c -index 7c82710e0f0b..81b7877e32e8 100644 ---- a/variables.c -+++ b/variables.c -@@ -2516,10 +2516,27 @@ bind_variable_internal (name, value, table, hflags, aflags) - HASH_TABLE *table; - int hflags, aflags; - { -- char *newval; -+ char *newname, *newval; - SHELL_VAR *entry; -+#if defined (ARRAY_VARS) -+ arrayind_t ind; -+ char *subp; -+ int sublen; -+#endif - -+ newname = 0; -+#if defined (ARRAY_VARS) -+ if ((aflags & ASS_FROMREF) && (hflags & HASH_NOSRCH) == 0 && valid_array_reference (name)) -+ { -+ newname = array_variable_name (name, &subp, &sublen); -+ if (newname == 0) -+ return (SHELL_VAR *)NULL; /* XXX */ -+ entry = hash_lookup (newname, table); -+ } -+ else -+#endif - entry = (hflags & HASH_NOSRCH) ? (SHELL_VAR *)NULL : hash_lookup (name, table); -+ - /* Follow the nameref chain here if this is the global variables table */ - if (entry && nameref_p (entry) && (invisible_p (entry) == 0) && table == global_variables->table) - { -@@ -2550,6 +2567,16 @@ bind_variable_internal (name, value, table, hflags, aflags) - var_setvalue (entry, make_variable_value (entry, value, 0)); - } - } -+#if defined (ARRAY_VARS) -+ else if (entry == 0 && newname) -+ { -+ entry = make_new_array_variable (newname); /* indexed array by default */ -+ if (entry == 0) -+ return entry; -+ ind = array_expand_index (name, subp, sublen); -+ bind_array_element (entry, ind, value, aflags); -+ } -+#endif - else if (entry == 0) - { - entry = make_new_variable (name, table); -@@ -2670,7 +2697,8 @@ bind_variable (name, value, flags) - normal. */ - if (nameref_cell (nv) == 0) - return (bind_variable_internal (nv->name, value, nvc->table, 0, flags)); -- return (bind_variable_internal (nameref_cell (nv), value, nvc->table, 0, flags)); -+ /* XXX - bug here with ref=array[index] */ -+ return (bind_variable_internal (nameref_cell (nv), value, nvc->table, 0, flags|ASS_FROMREF)); - } - else - v = nv; -diff --git a/variables.c.orig b/variables.c.orig -new file mode 100644 -index 000000000000..7c82710e0f0b ---- /dev/null -+++ b/variables.c.orig -@@ -0,0 +1,5365 @@ -+/* variables.c -- Functions for hacking shell variables. */ -+ -+/* Copyright (C) 1987-2013 Free Software Foundation, Inc. -+ -+ This file is part of GNU Bash, the Bourne Again SHell. -+ -+ Bash is free software: you can redistribute it and/or modify -+ it under the terms of the GNU General Public License as published by -+ the Free Software Foundation, either version 3 of the License, or -+ (at your option) any later version. -+ -+ Bash is distributed in the hope that it will be useful, -+ but WITHOUT ANY WARRANTY; without even the implied warranty of -+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -+ GNU General Public License for more details. -+ -+ You should have received a copy of the GNU General Public License -+ along with Bash. If not, see . -+*/ -+ -+#include "config.h" -+ -+#include "bashtypes.h" -+#include "posixstat.h" -+#include "posixtime.h" -+ -+#if defined (__QNX__) -+# if defined (__QNXNTO__) -+# include -+# else -+# include -+# endif /* !__QNXNTO__ */ -+#endif /* __QNX__ */ -+ -+#if defined (HAVE_UNISTD_H) -+# include -+#endif -+ -+#include -+#include "chartypes.h" -+#if defined (HAVE_PWD_H) -+# include -+#endif -+#include "bashansi.h" -+#include "bashintl.h" -+ -+#define NEED_XTRACE_SET_DECL -+ -+#include "shell.h" -+#include "flags.h" -+#include "execute_cmd.h" -+#include "findcmd.h" -+#include "mailcheck.h" -+#include "input.h" -+#include "hashcmd.h" -+#include "pathexp.h" -+#include "alias.h" -+#include "jobs.h" -+ -+#include "version.h" -+ -+#include "builtins/getopt.h" -+#include "builtins/common.h" -+#include "builtins/builtext.h" -+ -+#if defined (READLINE) -+# include "bashline.h" -+# include -+#else -+# include -+#endif -+ -+#if defined (HISTORY) -+# include "bashhist.h" -+# include -+#endif /* HISTORY */ -+ -+#if defined (PROGRAMMABLE_COMPLETION) -+# include "pcomplete.h" -+#endif -+ -+#define TEMPENV_HASH_BUCKETS 4 /* must be power of two */ -+ -+#define ifsname(s) ((s)[0] == 'I' && (s)[1] == 'F' && (s)[2] == 'S' && (s)[3] == '\0') -+ -+#define BASHFUNC_PREFIX "BASH_FUNC_" -+#define BASHFUNC_PREFLEN 10 /* == strlen(BASHFUNC_PREFIX */ -+#define BASHFUNC_SUFFIX "%%" -+#define BASHFUNC_SUFFLEN 2 /* == strlen(BASHFUNC_SUFFIX) */ -+ -+extern char **environ; -+ -+/* Variables used here and defined in other files. */ -+extern int posixly_correct; -+extern int line_number, line_number_base; -+extern int subshell_environment, indirection_level, subshell_level; -+extern int build_version, patch_level; -+extern int expanding_redir; -+extern int last_command_exit_value; -+extern char *dist_version, *release_status; -+extern char *shell_name; -+extern char *primary_prompt, *secondary_prompt; -+extern char *current_host_name; -+extern sh_builtin_func_t *this_shell_builtin; -+extern SHELL_VAR *this_shell_function; -+extern char *the_printed_command_except_trap; -+extern char *this_command_name; -+extern char *command_execution_string; -+extern time_t shell_start_time; -+extern int assigning_in_environment; -+extern int executing_builtin; -+extern int funcnest_max; -+ -+#if defined (READLINE) -+extern int no_line_editing; -+extern int perform_hostname_completion; -+#endif -+ -+/* The list of shell variables that the user has created at the global -+ scope, or that came from the environment. */ -+VAR_CONTEXT *global_variables = (VAR_CONTEXT *)NULL; -+ -+/* The current list of shell variables, including function scopes */ -+VAR_CONTEXT *shell_variables = (VAR_CONTEXT *)NULL; -+ -+/* The list of shell functions that the user has created, or that came from -+ the environment. */ -+HASH_TABLE *shell_functions = (HASH_TABLE *)NULL; -+ -+#if defined (DEBUGGER) -+/* The table of shell function definitions that the user defined or that -+ came from the environment. */ -+HASH_TABLE *shell_function_defs = (HASH_TABLE *)NULL; -+#endif -+ -+/* The current variable context. This is really a count of how deep into -+ executing functions we are. */ -+int variable_context = 0; -+ -+/* The set of shell assignments which are made only in the environment -+ for a single command. */ -+HASH_TABLE *temporary_env = (HASH_TABLE *)NULL; -+ -+/* Set to non-zero if an assignment error occurs while putting variables -+ into the temporary environment. */ -+int tempenv_assign_error; -+ -+/* Some funky variables which are known about specially. Here is where -+ "$*", "$1", and all the cruft is kept. */ -+char *dollar_vars[10]; -+WORD_LIST *rest_of_args = (WORD_LIST *)NULL; -+ -+/* The value of $$. */ -+pid_t dollar_dollar_pid; -+ -+/* Non-zero means that we have to remake EXPORT_ENV. */ -+int array_needs_making = 1; -+ -+/* The number of times BASH has been executed. This is set -+ by initialize_variables (). */ -+int shell_level = 0; -+ -+/* An array which is passed to commands as their environment. It is -+ manufactured from the union of the initial environment and the -+ shell variables that are marked for export. */ -+char **export_env = (char **)NULL; -+static int export_env_index; -+static int export_env_size; -+ -+#if defined (READLINE) -+static int winsize_assignment; /* currently assigning to LINES or COLUMNS */ -+#endif -+ -+static HASH_TABLE *last_table_searched; /* hash_lookup sets this */ -+ -+/* Some forward declarations. */ -+static void create_variable_tables __P((void)); -+ -+static void set_machine_vars __P((void)); -+static void set_home_var __P((void)); -+static void set_shell_var __P((void)); -+static char *get_bash_name __P((void)); -+static void initialize_shell_level __P((void)); -+static void uidset __P((void)); -+#if defined (ARRAY_VARS) -+static void make_vers_array __P((void)); -+#endif -+ -+static SHELL_VAR *null_assign __P((SHELL_VAR *, char *, arrayind_t, char *)); -+#if defined (ARRAY_VARS) -+static SHELL_VAR *null_array_assign __P((SHELL_VAR *, char *, arrayind_t, char *)); -+#endif -+static SHELL_VAR *get_self __P((SHELL_VAR *)); -+ -+#if defined (ARRAY_VARS) -+static SHELL_VAR *init_dynamic_array_var __P((char *, sh_var_value_func_t *, sh_var_assign_func_t *, int)); -+static SHELL_VAR *init_dynamic_assoc_var __P((char *, sh_var_value_func_t *, sh_var_assign_func_t *, int)); -+#endif -+ -+static SHELL_VAR *assign_seconds __P((SHELL_VAR *, char *, arrayind_t, char *)); -+static SHELL_VAR *get_seconds __P((SHELL_VAR *)); -+static SHELL_VAR *init_seconds_var __P((void)); -+ -+static int brand __P((void)); -+static void sbrand __P((unsigned long)); /* set bash random number generator. */ -+static void seedrand __P((void)); /* seed generator randomly */ -+static SHELL_VAR *assign_random __P((SHELL_VAR *, char *, arrayind_t, char *)); -+static SHELL_VAR *get_random __P((SHELL_VAR *)); -+ -+static SHELL_VAR *assign_lineno __P((SHELL_VAR *, char *, arrayind_t, char *)); -+static SHELL_VAR *get_lineno __P((SHELL_VAR *)); -+ -+static SHELL_VAR *assign_subshell __P((SHELL_VAR *, char *, arrayind_t, char *)); -+static SHELL_VAR *get_subshell __P((SHELL_VAR *)); -+ -+static SHELL_VAR *get_bashpid __P((SHELL_VAR *)); -+ -+#if defined (HISTORY) -+static SHELL_VAR *get_histcmd __P((SHELL_VAR *)); -+#endif -+ -+#if defined (READLINE) -+static SHELL_VAR *get_comp_wordbreaks __P((SHELL_VAR *)); -+static SHELL_VAR *assign_comp_wordbreaks __P((SHELL_VAR *, char *, arrayind_t, char *)); -+#endif -+ -+#if defined (PUSHD_AND_POPD) && defined (ARRAY_VARS) -+static SHELL_VAR *assign_dirstack __P((SHELL_VAR *, char *, arrayind_t, char *)); -+static SHELL_VAR *get_dirstack __P((SHELL_VAR *)); -+#endif -+ -+#if defined (ARRAY_VARS) -+static SHELL_VAR *get_groupset __P((SHELL_VAR *)); -+ -+static SHELL_VAR *build_hashcmd __P((SHELL_VAR *)); -+static SHELL_VAR *get_hashcmd __P((SHELL_VAR *)); -+static SHELL_VAR *assign_hashcmd __P((SHELL_VAR *, char *, arrayind_t, char *)); -+# if defined (ALIAS) -+static SHELL_VAR *build_aliasvar __P((SHELL_VAR *)); -+static SHELL_VAR *get_aliasvar __P((SHELL_VAR *)); -+static SHELL_VAR *assign_aliasvar __P((SHELL_VAR *, char *, arrayind_t, char *)); -+# endif -+#endif -+ -+static SHELL_VAR *get_funcname __P((SHELL_VAR *)); -+static SHELL_VAR *init_funcname_var __P((void)); -+ -+static void initialize_dynamic_variables __P((void)); -+ -+static SHELL_VAR *hash_lookup __P((const char *, HASH_TABLE *)); -+static SHELL_VAR *new_shell_variable __P((const char *)); -+static SHELL_VAR *make_new_variable __P((const char *, HASH_TABLE *)); -+static SHELL_VAR *bind_variable_internal __P((const char *, char *, HASH_TABLE *, int, int)); -+ -+static void dispose_variable_value __P((SHELL_VAR *)); -+static void free_variable_hash_data __P((PTR_T)); -+ -+static VARLIST *vlist_alloc __P((int)); -+static VARLIST *vlist_realloc __P((VARLIST *, int)); -+static void vlist_add __P((VARLIST *, SHELL_VAR *, int)); -+ -+static void flatten __P((HASH_TABLE *, sh_var_map_func_t *, VARLIST *, int)); -+ -+static int qsort_var_comp __P((SHELL_VAR **, SHELL_VAR **)); -+ -+static SHELL_VAR **vapply __P((sh_var_map_func_t *)); -+static SHELL_VAR **fapply __P((sh_var_map_func_t *)); -+ -+static int visible_var __P((SHELL_VAR *)); -+static int visible_and_exported __P((SHELL_VAR *)); -+static int export_environment_candidate __P((SHELL_VAR *)); -+static int local_and_exported __P((SHELL_VAR *)); -+static int variable_in_context __P((SHELL_VAR *)); -+#if defined (ARRAY_VARS) -+static int visible_array_vars __P((SHELL_VAR *)); -+#endif -+ -+static SHELL_VAR *find_nameref_at_context __P((SHELL_VAR *, VAR_CONTEXT *)); -+static SHELL_VAR *find_variable_nameref_context __P((SHELL_VAR *, VAR_CONTEXT *, VAR_CONTEXT **)); -+static SHELL_VAR *find_variable_last_nameref_context __P((SHELL_VAR *, VAR_CONTEXT *, VAR_CONTEXT **)); -+ -+static SHELL_VAR *bind_tempenv_variable __P((const char *, char *)); -+static void push_temp_var __P((PTR_T)); -+static void propagate_temp_var __P((PTR_T)); -+static void dispose_temporary_env __P((sh_free_func_t *)); -+ -+static inline char *mk_env_string __P((const char *, const char *, int)); -+static char **make_env_array_from_var_list __P((SHELL_VAR **)); -+static char **make_var_export_array __P((VAR_CONTEXT *)); -+static char **make_func_export_array __P((void)); -+static void add_temp_array_to_env __P((char **, int, int)); -+ -+static int n_shell_variables __P((void)); -+static int set_context __P((SHELL_VAR *)); -+ -+static void push_func_var __P((PTR_T)); -+static void push_exported_var __P((PTR_T)); -+ -+static inline int find_special_var __P((const char *)); -+ -+static void -+create_variable_tables () -+{ -+ if (shell_variables == 0) -+ { -+ shell_variables = global_variables = new_var_context ((char *)NULL, 0); -+ shell_variables->scope = 0; -+ shell_variables->table = hash_create (0); -+ } -+ -+ if (shell_functions == 0) -+ shell_functions = hash_create (0); -+ -+#if defined (DEBUGGER) -+ if (shell_function_defs == 0) -+ shell_function_defs = hash_create (0); -+#endif -+} -+ -+/* Initialize the shell variables from the current environment. -+ If PRIVMODE is nonzero, don't import functions from ENV or -+ parse $SHELLOPTS. */ -+void -+initialize_shell_variables (env, privmode) -+ char **env; -+ int privmode; -+{ -+ char *name, *string, *temp_string; -+ int c, char_index, string_index, string_length, ro; -+ SHELL_VAR *temp_var; -+ -+ create_variable_tables (); -+ -+ for (string_index = 0; string = env[string_index++]; ) -+ { -+ char_index = 0; -+ name = string; -+ while ((c = *string++) && c != '=') -+ ; -+ if (string[-1] == '=') -+ char_index = string - name - 1; -+ -+ /* If there are weird things in the environment, like `=xxx' or a -+ string without an `=', just skip them. */ -+ if (char_index == 0) -+ continue; -+ -+ /* ASSERT(name[char_index] == '=') */ -+ name[char_index] = '\0'; -+ /* Now, name = env variable name, string = env variable value, and -+ char_index == strlen (name) */ -+ -+ temp_var = (SHELL_VAR *)NULL; -+ -+ /* If exported function, define it now. Don't import functions from -+ the environment in privileged mode. */ -+ if (privmode == 0 && read_but_dont_execute == 0 && -+ STREQN (BASHFUNC_PREFIX, name, BASHFUNC_PREFLEN) && -+ STREQ (BASHFUNC_SUFFIX, name + char_index - BASHFUNC_SUFFLEN) && -+ STREQN ("() {", string, 4)) -+ { -+ size_t namelen; -+ char *tname; /* desired imported function name */ -+ -+ namelen = char_index - BASHFUNC_PREFLEN - BASHFUNC_SUFFLEN; -+ -+ tname = name + BASHFUNC_PREFLEN; /* start of func name */ -+ tname[namelen] = '\0'; /* now tname == func name */ -+ -+ string_length = strlen (string); -+ temp_string = (char *)xmalloc (namelen + string_length + 2); -+ -+ memcpy (temp_string, tname, namelen); -+ temp_string[namelen] = ' '; -+ memcpy (temp_string + namelen + 1, string, string_length + 1); -+ -+ /* Don't import function names that are invalid identifiers from the -+ environment, though we still allow them to be defined as shell -+ variables. */ -+ if (absolute_program (tname) == 0 && (posixly_correct == 0 || legal_identifier (tname))) -+ parse_and_execute (temp_string, tname, SEVAL_NONINT|SEVAL_NOHIST|SEVAL_FUNCDEF|SEVAL_ONECMD); -+ -+ if (temp_var = find_function (tname)) -+ { -+ VSETATTR (temp_var, (att_exported|att_imported)); -+ array_needs_making = 1; -+ } -+ else -+ { -+ if (temp_var = bind_variable (name, string, 0)) -+ { -+ VSETATTR (temp_var, (att_exported | att_imported | att_invisible)); -+ array_needs_making = 1; -+ } -+ last_command_exit_value = 1; -+ report_error (_("error importing function definition for `%s'"), tname); -+ } -+ -+ /* Restore original suffix */ -+ tname[namelen] = BASHFUNC_SUFFIX[0]; -+ } -+#if defined (ARRAY_VARS) -+# if ARRAY_EXPORT -+ /* Array variables may not yet be exported. */ -+ else if (*string == '(' && string[1] == '[' && string[strlen (string) - 1] == ')') -+ { -+ string_length = 1; -+ temp_string = extract_array_assignment_list (string, &string_length); -+ temp_var = assign_array_from_string (name, temp_string); -+ FREE (temp_string); -+ VSETATTR (temp_var, (att_exported | att_imported)); -+ array_needs_making = 1; -+ } -+# endif /* ARRAY_EXPORT */ -+#endif -+#if 0 -+ else if (legal_identifier (name)) -+#else -+ else -+#endif -+ { -+ ro = 0; -+ if (posixly_correct && STREQ (name, "SHELLOPTS")) -+ { -+ temp_var = find_variable ("SHELLOPTS"); -+ ro = temp_var && readonly_p (temp_var); -+ if (temp_var) -+ VUNSETATTR (temp_var, att_readonly); -+ } -+ temp_var = bind_variable (name, string, 0); -+ if (temp_var) -+ { -+ if (legal_identifier (name)) -+ VSETATTR (temp_var, (att_exported | att_imported)); -+ else -+ VSETATTR (temp_var, (att_exported | att_imported | att_invisible)); -+ if (ro) -+ VSETATTR (temp_var, att_readonly); -+ array_needs_making = 1; -+ } -+ } -+ -+ name[char_index] = '='; -+ /* temp_var can be NULL if it was an exported function with a syntax -+ error (a different bug, but it still shouldn't dump core). */ -+ if (temp_var && function_p (temp_var) == 0) /* XXX not yet */ -+ { -+ CACHE_IMPORTSTR (temp_var, name); -+ } -+ } -+ -+ set_pwd (); -+ -+ /* Set up initial value of $_ */ -+ temp_var = set_if_not ("_", dollar_vars[0]); -+ -+ /* Remember this pid. */ -+ dollar_dollar_pid = getpid (); -+ -+ /* Now make our own defaults in case the vars that we think are -+ important are missing. */ -+ temp_var = set_if_not ("PATH", DEFAULT_PATH_VALUE); -+#if 0 -+ set_auto_export (temp_var); /* XXX */ -+#endif -+ -+ temp_var = set_if_not ("TERM", "dumb"); -+#if 0 -+ set_auto_export (temp_var); /* XXX */ -+#endif -+ -+#if defined (__QNX__) -+ /* set node id -- don't import it from the environment */ -+ { -+ char node_name[22]; -+# if defined (__QNXNTO__) -+ netmgr_ndtostr(ND2S_LOCAL_STR, ND_LOCAL_NODE, node_name, sizeof(node_name)); -+# else -+ qnx_nidtostr (getnid (), node_name, sizeof (node_name)); -+# endif -+ temp_var = bind_variable ("NODE", node_name, 0); -+ set_auto_export (temp_var); -+ } -+#endif -+ -+ /* set up the prompts. */ -+ if (interactive_shell) -+ { -+#if defined (PROMPT_STRING_DECODE) -+ set_if_not ("PS1", primary_prompt); -+#else -+ if (current_user.uid == -1) -+ get_current_user_info (); -+ set_if_not ("PS1", current_user.euid == 0 ? "# " : primary_prompt); -+#endif -+ set_if_not ("PS2", secondary_prompt); -+ } -+ set_if_not ("PS4", "+ "); -+ -+ /* Don't allow IFS to be imported from the environment. */ -+ temp_var = bind_variable ("IFS", " \t\n", 0); -+ setifs (temp_var); -+ -+ /* Magic machine types. Pretty convenient. */ -+ set_machine_vars (); -+ -+ /* Default MAILCHECK for interactive shells. Defer the creation of a -+ default MAILPATH until the startup files are read, because MAIL -+ names a mail file if MAILPATH is not set, and we should provide a -+ default only if neither is set. */ -+ if (interactive_shell) -+ { -+ temp_var = set_if_not ("MAILCHECK", posixly_correct ? "600" : "60"); -+ VSETATTR (temp_var, att_integer); -+ } -+ -+ /* Do some things with shell level. */ -+ initialize_shell_level (); -+ -+ set_ppid (); -+ -+ /* Initialize the `getopts' stuff. */ -+ temp_var = bind_variable ("OPTIND", "1", 0); -+ VSETATTR (temp_var, att_integer); -+ getopts_reset (0); -+ bind_variable ("OPTERR", "1", 0); -+ sh_opterr = 1; -+ -+ if (login_shell == 1 && posixly_correct == 0) -+ set_home_var (); -+ -+ /* Get the full pathname to THIS shell, and set the BASH variable -+ to it. */ -+ name = get_bash_name (); -+ temp_var = bind_variable ("BASH", name, 0); -+ free (name); -+ -+ /* Make the exported environment variable SHELL be the user's login -+ shell. Note that the `tset' command looks at this variable -+ to determine what style of commands to output; if it ends in "csh", -+ then C-shell commands are output, else Bourne shell commands. */ -+ set_shell_var (); -+ -+ /* Make a variable called BASH_VERSION which contains the version info. */ -+ bind_variable ("BASH_VERSION", shell_version_string (), 0); -+#if defined (ARRAY_VARS) -+ make_vers_array (); -+#endif -+ -+ if (command_execution_string) -+ bind_variable ("BASH_EXECUTION_STRING", command_execution_string, 0); -+ -+ /* Find out if we're supposed to be in Posix.2 mode via an -+ environment variable. */ -+ temp_var = find_variable ("POSIXLY_CORRECT"); -+ if (!temp_var) -+ temp_var = find_variable ("POSIX_PEDANTIC"); -+ if (temp_var && imported_p (temp_var)) -+ sv_strict_posix (temp_var->name); -+ -+#if defined (HISTORY) -+ /* Set history variables to defaults, and then do whatever we would -+ do if the variable had just been set. Do this only in the case -+ that we are remembering commands on the history list. */ -+ if (remember_on_history) -+ { -+ name = bash_tilde_expand (posixly_correct ? "~/.sh_history" : "~/.bash_history", 0); -+ -+ set_if_not ("HISTFILE", name); -+ free (name); -+ } -+#endif /* HISTORY */ -+ -+ /* Seed the random number generator. */ -+ seedrand (); -+ -+ /* Handle some "special" variables that we may have inherited from a -+ parent shell. */ -+ if (interactive_shell) -+ { -+ temp_var = find_variable ("IGNOREEOF"); -+ if (!temp_var) -+ temp_var = find_variable ("ignoreeof"); -+ if (temp_var && imported_p (temp_var)) -+ sv_ignoreeof (temp_var->name); -+ } -+ -+#if defined (HISTORY) -+ if (interactive_shell && remember_on_history) -+ { -+ sv_history_control ("HISTCONTROL"); -+ sv_histignore ("HISTIGNORE"); -+ sv_histtimefmt ("HISTTIMEFORMAT"); -+ } -+#endif /* HISTORY */ -+ -+#if defined (READLINE) && defined (STRICT_POSIX) -+ /* POSIXLY_CORRECT will only be 1 here if the shell was compiled -+ -DSTRICT_POSIX */ -+ if (interactive_shell && posixly_correct && no_line_editing == 0) -+ rl_prefer_env_winsize = 1; -+#endif /* READLINE && STRICT_POSIX */ -+ -+ /* -+ * 24 October 2001 -+ * -+ * I'm tired of the arguing and bug reports. Bash now leaves SSH_CLIENT -+ * and SSH2_CLIENT alone. I'm going to rely on the shell_level check in -+ * isnetconn() to avoid running the startup files more often than wanted. -+ * That will, of course, only work if the user's login shell is bash, so -+ * I've made that behavior conditional on SSH_SOURCE_BASHRC being defined -+ * in config-top.h. -+ */ -+#if 0 -+ temp_var = find_variable ("SSH_CLIENT"); -+ if (temp_var && imported_p (temp_var)) -+ { -+ VUNSETATTR (temp_var, att_exported); -+ array_needs_making = 1; -+ } -+ temp_var = find_variable ("SSH2_CLIENT"); -+ if (temp_var && imported_p (temp_var)) -+ { -+ VUNSETATTR (temp_var, att_exported); -+ array_needs_making = 1; -+ } -+#endif -+ -+ /* Get the user's real and effective user ids. */ -+ uidset (); -+ -+ temp_var = find_variable ("BASH_XTRACEFD"); -+ if (temp_var && imported_p (temp_var)) -+ sv_xtracefd (temp_var->name); -+ -+ /* Initialize the dynamic variables, and seed their values. */ -+ initialize_dynamic_variables (); -+} -+ -+/* **************************************************************** */ -+/* */ -+/* Setting values for special shell variables */ -+/* */ -+/* **************************************************************** */ -+ -+static void -+set_machine_vars () -+{ -+ SHELL_VAR *temp_var; -+ -+ temp_var = set_if_not ("HOSTTYPE", HOSTTYPE); -+ temp_var = set_if_not ("OSTYPE", OSTYPE); -+ temp_var = set_if_not ("MACHTYPE", MACHTYPE); -+ -+ temp_var = set_if_not ("HOSTNAME", current_host_name); -+} -+ -+/* Set $HOME to the information in the password file if we didn't get -+ it from the environment. */ -+ -+/* This function is not static so the tilde and readline libraries can -+ use it. */ -+char * -+sh_get_home_dir () -+{ -+ if (current_user.home_dir == 0) -+ get_current_user_info (); -+ return current_user.home_dir; -+} -+ -+static void -+set_home_var () -+{ -+ SHELL_VAR *temp_var; -+ -+ temp_var = find_variable ("HOME"); -+ if (temp_var == 0) -+ temp_var = bind_variable ("HOME", sh_get_home_dir (), 0); -+#if 0 -+ VSETATTR (temp_var, att_exported); -+#endif -+} -+ -+/* Set $SHELL to the user's login shell if it is not already set. Call -+ get_current_user_info if we haven't already fetched the shell. */ -+static void -+set_shell_var () -+{ -+ SHELL_VAR *temp_var; -+ -+ temp_var = find_variable ("SHELL"); -+ if (temp_var == 0) -+ { -+ if (current_user.shell == 0) -+ get_current_user_info (); -+ temp_var = bind_variable ("SHELL", current_user.shell, 0); -+ } -+#if 0 -+ VSETATTR (temp_var, att_exported); -+#endif -+} -+ -+static char * -+get_bash_name () -+{ -+ char *name; -+ -+ if ((login_shell == 1) && RELPATH(shell_name)) -+ { -+ if (current_user.shell == 0) -+ get_current_user_info (); -+ name = savestring (current_user.shell); -+ } -+ else if (ABSPATH(shell_name)) -+ name = savestring (shell_name); -+ else if (shell_name[0] == '.' && shell_name[1] == '/') -+ { -+ /* Fast path for common case. */ -+ char *cdir; -+ int len; -+ -+ cdir = get_string_value ("PWD"); -+ if (cdir) -+ { -+ len = strlen (cdir); -+ name = (char *)xmalloc (len + strlen (shell_name) + 1); -+ strcpy (name, cdir); -+ strcpy (name + len, shell_name + 1); -+ } -+ else -+ name = savestring (shell_name); -+ } -+ else -+ { -+ char *tname; -+ int s; -+ -+ tname = find_user_command (shell_name); -+ -+ if (tname == 0) -+ { -+ /* Try the current directory. If there is not an executable -+ there, just punt and use the login shell. */ -+ s = file_status (shell_name); -+ if (s & FS_EXECABLE) -+ { -+ tname = make_absolute (shell_name, get_string_value ("PWD")); -+ if (*shell_name == '.') -+ { -+ name = sh_canonpath (tname, PATH_CHECKDOTDOT|PATH_CHECKEXISTS); -+ if (name == 0) -+ name = tname; -+ else -+ free (tname); -+ } -+ else -+ name = tname; -+ } -+ else -+ { -+ if (current_user.shell == 0) -+ get_current_user_info (); -+ name = savestring (current_user.shell); -+ } -+ } -+ else -+ { -+ name = full_pathname (tname); -+ free (tname); -+ } -+ } -+ -+ return (name); -+} -+ -+void -+adjust_shell_level (change) -+ int change; -+{ -+ char new_level[5], *old_SHLVL; -+ intmax_t old_level; -+ SHELL_VAR *temp_var; -+ -+ old_SHLVL = get_string_value ("SHLVL"); -+ if (old_SHLVL == 0 || *old_SHLVL == '\0' || legal_number (old_SHLVL, &old_level) == 0) -+ old_level = 0; -+ -+ shell_level = old_level + change; -+ if (shell_level < 0) -+ shell_level = 0; -+ else if (shell_level > 1000) -+ { -+ internal_warning (_("shell level (%d) too high, resetting to 1"), shell_level); -+ shell_level = 1; -+ } -+ -+ /* We don't need the full generality of itos here. */ -+ if (shell_level < 10) -+ { -+ new_level[0] = shell_level + '0'; -+ new_level[1] = '\0'; -+ } -+ else if (shell_level < 100) -+ { -+ new_level[0] = (shell_level / 10) + '0'; -+ new_level[1] = (shell_level % 10) + '0'; -+ new_level[2] = '\0'; -+ } -+ else if (shell_level < 1000) -+ { -+ new_level[0] = (shell_level / 100) + '0'; -+ old_level = shell_level % 100; -+ new_level[1] = (old_level / 10) + '0'; -+ new_level[2] = (old_level % 10) + '0'; -+ new_level[3] = '\0'; -+ } -+ -+ temp_var = bind_variable ("SHLVL", new_level, 0); -+ set_auto_export (temp_var); -+} -+ -+static void -+initialize_shell_level () -+{ -+ adjust_shell_level (1); -+} -+ -+/* If we got PWD from the environment, update our idea of the current -+ working directory. In any case, make sure that PWD exists before -+ checking it. It is possible for getcwd () to fail on shell startup, -+ and in that case, PWD would be undefined. If this is an interactive -+ login shell, see if $HOME is the current working directory, and if -+ that's not the same string as $PWD, set PWD=$HOME. */ -+ -+void -+set_pwd () -+{ -+ SHELL_VAR *temp_var, *home_var; -+ char *temp_string, *home_string; -+ -+ home_var = find_variable ("HOME"); -+ home_string = home_var ? value_cell (home_var) : (char *)NULL; -+ -+ temp_var = find_variable ("PWD"); -+ if (temp_var && imported_p (temp_var) && -+ (temp_string = value_cell (temp_var)) && -+ same_file (temp_string, ".", (struct stat *)NULL, (struct stat *)NULL)) -+ set_working_directory (temp_string); -+ else if (home_string && interactive_shell && login_shell && -+ same_file (home_string, ".", (struct stat *)NULL, (struct stat *)NULL)) -+ { -+ set_working_directory (home_string); -+ temp_var = bind_variable ("PWD", home_string, 0); -+ set_auto_export (temp_var); -+ } -+ else -+ { -+ temp_string = get_working_directory ("shell-init"); -+ if (temp_string) -+ { -+ temp_var = bind_variable ("PWD", temp_string, 0); -+ set_auto_export (temp_var); -+ free (temp_string); -+ } -+ } -+ -+ /* According to the Single Unix Specification, v2, $OLDPWD is an -+ `environment variable' and therefore should be auto-exported. -+ Make a dummy invisible variable for OLDPWD, and mark it as exported. */ -+ temp_var = bind_variable ("OLDPWD", (char *)NULL, 0); -+ VSETATTR (temp_var, (att_exported | att_invisible)); -+} -+ -+/* Make a variable $PPID, which holds the pid of the shell's parent. */ -+void -+set_ppid () -+{ -+ char namebuf[INT_STRLEN_BOUND(pid_t) + 1], *name; -+ SHELL_VAR *temp_var; -+ -+ name = inttostr (getppid (), namebuf, sizeof(namebuf)); -+ temp_var = find_variable ("PPID"); -+ if (temp_var) -+ VUNSETATTR (temp_var, (att_readonly | att_exported)); -+ temp_var = bind_variable ("PPID", name, 0); -+ VSETATTR (temp_var, (att_readonly | att_integer)); -+} -+ -+static void -+uidset () -+{ -+ char buff[INT_STRLEN_BOUND(uid_t) + 1], *b; -+ register SHELL_VAR *v; -+ -+ b = inttostr (current_user.uid, buff, sizeof (buff)); -+ v = find_variable ("UID"); -+ if (v == 0) -+ { -+ v = bind_variable ("UID", b, 0); -+ VSETATTR (v, (att_readonly | att_integer)); -+ } -+ -+ if (current_user.euid != current_user.uid) -+ b = inttostr (current_user.euid, buff, sizeof (buff)); -+ -+ v = find_variable ("EUID"); -+ if (v == 0) -+ { -+ v = bind_variable ("EUID", b, 0); -+ VSETATTR (v, (att_readonly | att_integer)); -+ } -+} -+ -+#if defined (ARRAY_VARS) -+static void -+make_vers_array () -+{ -+ SHELL_VAR *vv; -+ ARRAY *av; -+ char *s, d[32], b[INT_STRLEN_BOUND(int) + 1]; -+ -+ unbind_variable ("BASH_VERSINFO"); -+ -+ vv = make_new_array_variable ("BASH_VERSINFO"); -+ av = array_cell (vv); -+ strcpy (d, dist_version); -+ s = strchr (d, '.'); -+ if (s) -+ *s++ = '\0'; -+ array_insert (av, 0, d); -+ array_insert (av, 1, s); -+ s = inttostr (patch_level, b, sizeof (b)); -+ array_insert (av, 2, s); -+ s = inttostr (build_version, b, sizeof (b)); -+ array_insert (av, 3, s); -+ array_insert (av, 4, release_status); -+ array_insert (av, 5, MACHTYPE); -+ -+ VSETATTR (vv, att_readonly); -+} -+#endif /* ARRAY_VARS */ -+ -+/* Set the environment variables $LINES and $COLUMNS in response to -+ a window size change. */ -+void -+sh_set_lines_and_columns (lines, cols) -+ int lines, cols; -+{ -+ char val[INT_STRLEN_BOUND(int) + 1], *v; -+ -+#if defined (READLINE) -+ /* If we are currently assigning to LINES or COLUMNS, don't do anything. */ -+ if (winsize_assignment) -+ return; -+#endif -+ -+ v = inttostr (lines, val, sizeof (val)); -+ bind_variable ("LINES", v, 0); -+ -+ v = inttostr (cols, val, sizeof (val)); -+ bind_variable ("COLUMNS", v, 0); -+} -+ -+/* **************************************************************** */ -+/* */ -+/* Printing variables and values */ -+/* */ -+/* **************************************************************** */ -+ -+/* Print LIST (a list of shell variables) to stdout in such a way that -+ they can be read back in. */ -+void -+print_var_list (list) -+ register SHELL_VAR **list; -+{ -+ register int i; -+ register SHELL_VAR *var; -+ -+ for (i = 0; list && (var = list[i]); i++) -+ if (invisible_p (var) == 0) -+ print_assignment (var); -+} -+ -+/* Print LIST (a list of shell functions) to stdout in such a way that -+ they can be read back in. */ -+void -+print_func_list (list) -+ register SHELL_VAR **list; -+{ -+ register int i; -+ register SHELL_VAR *var; -+ -+ for (i = 0; list && (var = list[i]); i++) -+ { -+ printf ("%s ", var->name); -+ print_var_function (var); -+ printf ("\n"); -+ } -+} -+ -+/* Print the value of a single SHELL_VAR. No newline is -+ output, but the variable is printed in such a way that -+ it can be read back in. */ -+void -+print_assignment (var) -+ SHELL_VAR *var; -+{ -+ if (var_isset (var) == 0) -+ return; -+ -+ if (function_p (var)) -+ { -+ printf ("%s", var->name); -+ print_var_function (var); -+ printf ("\n"); -+ } -+#if defined (ARRAY_VARS) -+ else if (array_p (var)) -+ print_array_assignment (var, 0); -+ else if (assoc_p (var)) -+ print_assoc_assignment (var, 0); -+#endif /* ARRAY_VARS */ -+ else -+ { -+ printf ("%s=", var->name); -+ print_var_value (var, 1); -+ printf ("\n"); -+ } -+} -+ -+/* Print the value cell of VAR, a shell variable. Do not print -+ the name, nor leading/trailing newline. If QUOTE is non-zero, -+ and the value contains shell metacharacters, quote the value -+ in such a way that it can be read back in. */ -+void -+print_var_value (var, quote) -+ SHELL_VAR *var; -+ int quote; -+{ -+ char *t; -+ -+ if (var_isset (var) == 0) -+ return; -+ -+ if (quote && posixly_correct == 0 && ansic_shouldquote (value_cell (var))) -+ { -+ t = ansic_quote (value_cell (var), 0, (int *)0); -+ printf ("%s", t); -+ free (t); -+ } -+ else if (quote && sh_contains_shell_metas (value_cell (var))) -+ { -+ t = sh_single_quote (value_cell (var)); -+ printf ("%s", t); -+ free (t); -+ } -+ else -+ printf ("%s", value_cell (var)); -+} -+ -+/* Print the function cell of VAR, a shell variable. Do not -+ print the name, nor leading/trailing newline. */ -+void -+print_var_function (var) -+ SHELL_VAR *var; -+{ -+ char *x; -+ -+ if (function_p (var) && var_isset (var)) -+ { -+ x = named_function_string ((char *)NULL, function_cell(var), FUNC_MULTILINE|FUNC_EXTERNAL); -+ printf ("%s", x); -+ } -+} -+ -+/* **************************************************************** */ -+/* */ -+/* Dynamic Variables */ -+/* */ -+/* **************************************************************** */ -+ -+/* DYNAMIC VARIABLES -+ -+ These are variables whose values are generated anew each time they are -+ referenced. These are implemented using a pair of function pointers -+ in the struct variable: assign_func, which is called from bind_variable -+ and, if arrays are compiled into the shell, some of the functions in -+ arrayfunc.c, and dynamic_value, which is called from find_variable. -+ -+ assign_func is called from bind_variable_internal, if -+ bind_variable_internal discovers that the variable being assigned to -+ has such a function. The function is called as -+ SHELL_VAR *temp = (*(entry->assign_func)) (entry, value, ind) -+ and the (SHELL_VAR *)temp is returned as the value of bind_variable. It -+ is usually ENTRY (self). IND is an index for an array variable, and -+ unused otherwise. -+ -+ dynamic_value is called from find_variable_internal to return a `new' -+ value for the specified dynamic varible. If this function is NULL, -+ the variable is treated as a `normal' shell variable. If it is not, -+ however, then this function is called like this: -+ tempvar = (*(var->dynamic_value)) (var); -+ -+ Sometimes `tempvar' will replace the value of `var'. Other times, the -+ shell will simply use the string value. Pretty object-oriented, huh? -+ -+ Be warned, though: if you `unset' a special variable, it loses its -+ special meaning, even if you subsequently set it. -+ -+ The special assignment code would probably have been better put in -+ subst.c: do_assignment_internal, in the same style as -+ stupidly_hack_special_variables, but I wanted the changes as -+ localized as possible. */ -+ -+#define INIT_DYNAMIC_VAR(var, val, gfunc, afunc) \ -+ do \ -+ { \ -+ v = bind_variable (var, (val), 0); \ -+ v->dynamic_value = gfunc; \ -+ v->assign_func = afunc; \ -+ } \ -+ while (0) -+ -+#define INIT_DYNAMIC_ARRAY_VAR(var, gfunc, afunc) \ -+ do \ -+ { \ -+ v = make_new_array_variable (var); \ -+ v->dynamic_value = gfunc; \ -+ v->assign_func = afunc; \ -+ } \ -+ while (0) -+ -+#define INIT_DYNAMIC_ASSOC_VAR(var, gfunc, afunc) \ -+ do \ -+ { \ -+ v = make_new_assoc_variable (var); \ -+ v->dynamic_value = gfunc; \ -+ v->assign_func = afunc; \ -+ } \ -+ while (0) -+ -+static SHELL_VAR * -+null_assign (self, value, unused, key) -+ SHELL_VAR *self; -+ char *value; -+ arrayind_t unused; -+ char *key; -+{ -+ return (self); -+} -+ -+#if defined (ARRAY_VARS) -+static SHELL_VAR * -+null_array_assign (self, value, ind, key) -+ SHELL_VAR *self; -+ char *value; -+ arrayind_t ind; -+ char *key; -+{ -+ return (self); -+} -+#endif -+ -+/* Degenerate `dynamic_value' function; just returns what's passed without -+ manipulation. */ -+static SHELL_VAR * -+get_self (self) -+ SHELL_VAR *self; -+{ -+ return (self); -+} -+ -+#if defined (ARRAY_VARS) -+/* A generic dynamic array variable initializer. Initialize array variable -+ NAME with dynamic value function GETFUNC and assignment function SETFUNC. */ -+static SHELL_VAR * -+init_dynamic_array_var (name, getfunc, setfunc, attrs) -+ char *name; -+ sh_var_value_func_t *getfunc; -+ sh_var_assign_func_t *setfunc; -+ int attrs; -+{ -+ SHELL_VAR *v; -+ -+ v = find_variable (name); -+ if (v) -+ return (v); -+ INIT_DYNAMIC_ARRAY_VAR (name, getfunc, setfunc); -+ if (attrs) -+ VSETATTR (v, attrs); -+ return v; -+} -+ -+static SHELL_VAR * -+init_dynamic_assoc_var (name, getfunc, setfunc, attrs) -+ char *name; -+ sh_var_value_func_t *getfunc; -+ sh_var_assign_func_t *setfunc; -+ int attrs; -+{ -+ SHELL_VAR *v; -+ -+ v = find_variable (name); -+ if (v) -+ return (v); -+ INIT_DYNAMIC_ASSOC_VAR (name, getfunc, setfunc); -+ if (attrs) -+ VSETATTR (v, attrs); -+ return v; -+} -+#endif -+ -+/* The value of $SECONDS. This is the number of seconds since shell -+ invocation, or, the number of seconds since the last assignment + the -+ value of the last assignment. */ -+static intmax_t seconds_value_assigned; -+ -+static SHELL_VAR * -+assign_seconds (self, value, unused, key) -+ SHELL_VAR *self; -+ char *value; -+ arrayind_t unused; -+ char *key; -+{ -+ if (legal_number (value, &seconds_value_assigned) == 0) -+ seconds_value_assigned = 0; -+ shell_start_time = NOW; -+ return (self); -+} -+ -+static SHELL_VAR * -+get_seconds (var) -+ SHELL_VAR *var; -+{ -+ time_t time_since_start; -+ char *p; -+ -+ time_since_start = NOW - shell_start_time; -+ p = itos(seconds_value_assigned + time_since_start); -+ -+ FREE (value_cell (var)); -+ -+ VSETATTR (var, att_integer); -+ var_setvalue (var, p); -+ return (var); -+} -+ -+static SHELL_VAR * -+init_seconds_var () -+{ -+ SHELL_VAR *v; -+ -+ v = find_variable ("SECONDS"); -+ if (v) -+ { -+ if (legal_number (value_cell(v), &seconds_value_assigned) == 0) -+ seconds_value_assigned = 0; -+ } -+ INIT_DYNAMIC_VAR ("SECONDS", (v ? value_cell (v) : (char *)NULL), get_seconds, assign_seconds); -+ return v; -+} -+ -+/* The random number seed. You can change this by setting RANDOM. */ -+static unsigned long rseed = 1; -+static int last_random_value; -+static int seeded_subshell = 0; -+ -+/* A linear congruential random number generator based on the example -+ one in the ANSI C standard. This one isn't very good, but a more -+ complicated one is overkill. */ -+ -+/* Returns a pseudo-random number between 0 and 32767. */ -+static int -+brand () -+{ -+ /* From "Random number generators: good ones are hard to find", -+ Park and Miller, Communications of the ACM, vol. 31, no. 10, -+ October 1988, p. 1195. filtered through FreeBSD */ -+ long h, l; -+ -+ /* Can't seed with 0. */ -+ if (rseed == 0) -+ rseed = 123459876; -+ h = rseed / 127773; -+ l = rseed % 127773; -+ rseed = 16807 * l - 2836 * h; -+#if 0 -+ if (rseed < 0) -+ rseed += 0x7fffffff; -+#endif -+ return ((unsigned int)(rseed & 32767)); /* was % 32768 */ -+} -+ -+/* Set the random number generator seed to SEED. */ -+static void -+sbrand (seed) -+ unsigned long seed; -+{ -+ rseed = seed; -+ last_random_value = 0; -+} -+ -+static void -+seedrand () -+{ -+ struct timeval tv; -+ -+ gettimeofday (&tv, NULL); -+ sbrand (tv.tv_sec ^ tv.tv_usec ^ getpid ()); -+} -+ -+static SHELL_VAR * -+assign_random (self, value, unused, key) -+ SHELL_VAR *self; -+ char *value; -+ arrayind_t unused; -+ char *key; -+{ -+ sbrand (strtoul (value, (char **)NULL, 10)); -+ if (subshell_environment) -+ seeded_subshell = getpid (); -+ return (self); -+} -+ -+int -+get_random_number () -+{ -+ int rv, pid; -+ -+ /* Reset for command and process substitution. */ -+ pid = getpid (); -+ if (subshell_environment && seeded_subshell != pid) -+ { -+ seedrand (); -+ seeded_subshell = pid; -+ } -+ -+ do -+ rv = brand (); -+ while (rv == last_random_value); -+ return rv; -+} -+ -+static SHELL_VAR * -+get_random (var) -+ SHELL_VAR *var; -+{ -+ int rv; -+ char *p; -+ -+ rv = get_random_number (); -+ last_random_value = rv; -+ p = itos (rv); -+ -+ FREE (value_cell (var)); -+ -+ VSETATTR (var, att_integer); -+ var_setvalue (var, p); -+ return (var); -+} -+ -+static SHELL_VAR * -+assign_lineno (var, value, unused, key) -+ SHELL_VAR *var; -+ char *value; -+ arrayind_t unused; -+ char *key; -+{ -+ intmax_t new_value; -+ -+ if (value == 0 || *value == '\0' || legal_number (value, &new_value) == 0) -+ new_value = 0; -+ line_number = line_number_base = new_value; -+ return var; -+} -+ -+/* Function which returns the current line number. */ -+static SHELL_VAR * -+get_lineno (var) -+ SHELL_VAR *var; -+{ -+ char *p; -+ int ln; -+ -+ ln = executing_line_number (); -+ p = itos (ln); -+ FREE (value_cell (var)); -+ var_setvalue (var, p); -+ return (var); -+} -+ -+static SHELL_VAR * -+assign_subshell (var, value, unused, key) -+ SHELL_VAR *var; -+ char *value; -+ arrayind_t unused; -+ char *key; -+{ -+ intmax_t new_value; -+ -+ if (value == 0 || *value == '\0' || legal_number (value, &new_value) == 0) -+ new_value = 0; -+ subshell_level = new_value; -+ return var; -+} -+ -+static SHELL_VAR * -+get_subshell (var) -+ SHELL_VAR *var; -+{ -+ char *p; -+ -+ p = itos (subshell_level); -+ FREE (value_cell (var)); -+ var_setvalue (var, p); -+ return (var); -+} -+ -+static SHELL_VAR * -+get_bashpid (var) -+ SHELL_VAR *var; -+{ -+ int pid; -+ char *p; -+ -+ pid = getpid (); -+ p = itos (pid); -+ -+ FREE (value_cell (var)); -+ VSETATTR (var, att_integer|att_readonly); -+ var_setvalue (var, p); -+ return (var); -+} -+ -+static SHELL_VAR * -+get_bash_command (var) -+ SHELL_VAR *var; -+{ -+ char *p; -+ -+ if (the_printed_command_except_trap) -+ p = savestring (the_printed_command_except_trap); -+ else -+ { -+ p = (char *)xmalloc (1); -+ p[0] = '\0'; -+ } -+ FREE (value_cell (var)); -+ var_setvalue (var, p); -+ return (var); -+} -+ -+#if defined (HISTORY) -+static SHELL_VAR * -+get_histcmd (var) -+ SHELL_VAR *var; -+{ -+ char *p; -+ -+ p = itos (history_number ()); -+ FREE (value_cell (var)); -+ var_setvalue (var, p); -+ return (var); -+} -+#endif -+ -+#if defined (READLINE) -+/* When this function returns, VAR->value points to malloced memory. */ -+static SHELL_VAR * -+get_comp_wordbreaks (var) -+ SHELL_VAR *var; -+{ -+ /* If we don't have anything yet, assign a default value. */ -+ if (rl_completer_word_break_characters == 0 && bash_readline_initialized == 0) -+ enable_hostname_completion (perform_hostname_completion); -+ -+ FREE (value_cell (var)); -+ var_setvalue (var, savestring (rl_completer_word_break_characters)); -+ -+ return (var); -+} -+ -+/* When this function returns, rl_completer_word_break_characters points to -+ malloced memory. */ -+static SHELL_VAR * -+assign_comp_wordbreaks (self, value, unused, key) -+ SHELL_VAR *self; -+ char *value; -+ arrayind_t unused; -+ char *key; -+{ -+ if (rl_completer_word_break_characters && -+ rl_completer_word_break_characters != rl_basic_word_break_characters) -+ free (rl_completer_word_break_characters); -+ -+ rl_completer_word_break_characters = savestring (value); -+ return self; -+} -+#endif /* READLINE */ -+ -+#if defined (PUSHD_AND_POPD) && defined (ARRAY_VARS) -+static SHELL_VAR * -+assign_dirstack (self, value, ind, key) -+ SHELL_VAR *self; -+ char *value; -+ arrayind_t ind; -+ char *key; -+{ -+ set_dirstack_element (ind, 1, value); -+ return self; -+} -+ -+static SHELL_VAR * -+get_dirstack (self) -+ SHELL_VAR *self; -+{ -+ ARRAY *a; -+ WORD_LIST *l; -+ -+ l = get_directory_stack (0); -+ a = array_from_word_list (l); -+ array_dispose (array_cell (self)); -+ dispose_words (l); -+ var_setarray (self, a); -+ return self; -+} -+#endif /* PUSHD AND POPD && ARRAY_VARS */ -+ -+#if defined (ARRAY_VARS) -+/* We don't want to initialize the group set with a call to getgroups() -+ unless we're asked to, but we only want to do it once. */ -+static SHELL_VAR * -+get_groupset (self) -+ SHELL_VAR *self; -+{ -+ register int i; -+ int ng; -+ ARRAY *a; -+ static char **group_set = (char **)NULL; -+ -+ if (group_set == 0) -+ { -+ group_set = get_group_list (&ng); -+ a = array_cell (self); -+ for (i = 0; i < ng; i++) -+ array_insert (a, i, group_set[i]); -+ } -+ return (self); -+} -+ -+static SHELL_VAR * -+build_hashcmd (self) -+ SHELL_VAR *self; -+{ -+ HASH_TABLE *h; -+ int i; -+ char *k, *v; -+ BUCKET_CONTENTS *item; -+ -+ h = assoc_cell (self); -+ if (h) -+ assoc_dispose (h); -+ -+ if (hashed_filenames == 0 || HASH_ENTRIES (hashed_filenames) == 0) -+ { -+ var_setvalue (self, (char *)NULL); -+ return self; -+ } -+ -+ h = assoc_create (hashed_filenames->nbuckets); -+ for (i = 0; i < hashed_filenames->nbuckets; i++) -+ { -+ for (item = hash_items (i, hashed_filenames); item; item = item->next) -+ { -+ k = savestring (item->key); -+ v = pathdata(item)->path; -+ assoc_insert (h, k, v); -+ } -+ } -+ -+ var_setvalue (self, (char *)h); -+ return self; -+} -+ -+static SHELL_VAR * -+get_hashcmd (self) -+ SHELL_VAR *self; -+{ -+ build_hashcmd (self); -+ return (self); -+} -+ -+static SHELL_VAR * -+assign_hashcmd (self, value, ind, key) -+ SHELL_VAR *self; -+ char *value; -+ arrayind_t ind; -+ char *key; -+{ -+ phash_insert (key, value, 0, 0); -+ return (build_hashcmd (self)); -+} -+ -+#if defined (ALIAS) -+static SHELL_VAR * -+build_aliasvar (self) -+ SHELL_VAR *self; -+{ -+ HASH_TABLE *h; -+ int i; -+ char *k, *v; -+ BUCKET_CONTENTS *item; -+ -+ h = assoc_cell (self); -+ if (h) -+ assoc_dispose (h); -+ -+ if (aliases == 0 || HASH_ENTRIES (aliases) == 0) -+ { -+ var_setvalue (self, (char *)NULL); -+ return self; -+ } -+ -+ h = assoc_create (aliases->nbuckets); -+ for (i = 0; i < aliases->nbuckets; i++) -+ { -+ for (item = hash_items (i, aliases); item; item = item->next) -+ { -+ k = savestring (item->key); -+ v = ((alias_t *)(item->data))->value; -+ assoc_insert (h, k, v); -+ } -+ } -+ -+ var_setvalue (self, (char *)h); -+ return self; -+} -+ -+static SHELL_VAR * -+get_aliasvar (self) -+ SHELL_VAR *self; -+{ -+ build_aliasvar (self); -+ return (self); -+} -+ -+static SHELL_VAR * -+assign_aliasvar (self, value, ind, key) -+ SHELL_VAR *self; -+ char *value; -+ arrayind_t ind; -+ char *key; -+{ -+ add_alias (key, value); -+ return (build_aliasvar (self)); -+} -+#endif /* ALIAS */ -+ -+#endif /* ARRAY_VARS */ -+ -+/* If ARRAY_VARS is not defined, this just returns the name of any -+ currently-executing function. If we have arrays, it's a call stack. */ -+static SHELL_VAR * -+get_funcname (self) -+ SHELL_VAR *self; -+{ -+#if ! defined (ARRAY_VARS) -+ char *t; -+ if (variable_context && this_shell_function) -+ { -+ FREE (value_cell (self)); -+ t = savestring (this_shell_function->name); -+ var_setvalue (self, t); -+ } -+#endif -+ return (self); -+} -+ -+void -+make_funcname_visible (on_or_off) -+ int on_or_off; -+{ -+ SHELL_VAR *v; -+ -+ v = find_variable ("FUNCNAME"); -+ if (v == 0 || v->dynamic_value == 0) -+ return; -+ -+ if (on_or_off) -+ VUNSETATTR (v, att_invisible); -+ else -+ VSETATTR (v, att_invisible); -+} -+ -+static SHELL_VAR * -+init_funcname_var () -+{ -+ SHELL_VAR *v; -+ -+ v = find_variable ("FUNCNAME"); -+ if (v) -+ return v; -+#if defined (ARRAY_VARS) -+ INIT_DYNAMIC_ARRAY_VAR ("FUNCNAME", get_funcname, null_array_assign); -+#else -+ INIT_DYNAMIC_VAR ("FUNCNAME", (char *)NULL, get_funcname, null_assign); -+#endif -+ VSETATTR (v, att_invisible|att_noassign); -+ return v; -+} -+ -+static void -+initialize_dynamic_variables () -+{ -+ SHELL_VAR *v; -+ -+ v = init_seconds_var (); -+ -+ INIT_DYNAMIC_VAR ("BASH_COMMAND", (char *)NULL, get_bash_command, (sh_var_assign_func_t *)NULL); -+ INIT_DYNAMIC_VAR ("BASH_SUBSHELL", (char *)NULL, get_subshell, assign_subshell); -+ -+ INIT_DYNAMIC_VAR ("RANDOM", (char *)NULL, get_random, assign_random); -+ VSETATTR (v, att_integer); -+ INIT_DYNAMIC_VAR ("LINENO", (char *)NULL, get_lineno, assign_lineno); -+ VSETATTR (v, att_integer); -+ -+ INIT_DYNAMIC_VAR ("BASHPID", (char *)NULL, get_bashpid, null_assign); -+ VSETATTR (v, att_integer|att_readonly); -+ -+#if defined (HISTORY) -+ INIT_DYNAMIC_VAR ("HISTCMD", (char *)NULL, get_histcmd, (sh_var_assign_func_t *)NULL); -+ VSETATTR (v, att_integer); -+#endif -+ -+#if defined (READLINE) -+ INIT_DYNAMIC_VAR ("COMP_WORDBREAKS", (char *)NULL, get_comp_wordbreaks, assign_comp_wordbreaks); -+#endif -+ -+#if defined (PUSHD_AND_POPD) && defined (ARRAY_VARS) -+ v = init_dynamic_array_var ("DIRSTACK", get_dirstack, assign_dirstack, 0); -+#endif /* PUSHD_AND_POPD && ARRAY_VARS */ -+ -+#if defined (ARRAY_VARS) -+ v = init_dynamic_array_var ("GROUPS", get_groupset, null_array_assign, att_noassign); -+ -+# if defined (DEBUGGER) -+ v = init_dynamic_array_var ("BASH_ARGC", get_self, null_array_assign, att_noassign|att_nounset); -+ v = init_dynamic_array_var ("BASH_ARGV", get_self, null_array_assign, att_noassign|att_nounset); -+# endif /* DEBUGGER */ -+ v = init_dynamic_array_var ("BASH_SOURCE", get_self, null_array_assign, att_noassign|att_nounset); -+ v = init_dynamic_array_var ("BASH_LINENO", get_self, null_array_assign, att_noassign|att_nounset); -+ -+ v = init_dynamic_assoc_var ("BASH_CMDS", get_hashcmd, assign_hashcmd, att_nofree); -+# if defined (ALIAS) -+ v = init_dynamic_assoc_var ("BASH_ALIASES", get_aliasvar, assign_aliasvar, att_nofree); -+# endif -+#endif -+ -+ v = init_funcname_var (); -+} -+ -+/* **************************************************************** */ -+/* */ -+/* Retrieving variables and values */ -+/* */ -+/* **************************************************************** */ -+ -+/* How to get a pointer to the shell variable or function named NAME. -+ HASHED_VARS is a pointer to the hash table containing the list -+ of interest (either variables or functions). */ -+ -+static SHELL_VAR * -+hash_lookup (name, hashed_vars) -+ const char *name; -+ HASH_TABLE *hashed_vars; -+{ -+ BUCKET_CONTENTS *bucket; -+ -+ bucket = hash_search (name, hashed_vars, 0); -+ /* If we find the name in HASHED_VARS, set LAST_TABLE_SEARCHED to that -+ table. */ -+ if (bucket) -+ last_table_searched = hashed_vars; -+ return (bucket ? (SHELL_VAR *)bucket->data : (SHELL_VAR *)NULL); -+} -+ -+SHELL_VAR * -+var_lookup (name, vcontext) -+ const char *name; -+ VAR_CONTEXT *vcontext; -+{ -+ VAR_CONTEXT *vc; -+ SHELL_VAR *v; -+ -+ v = (SHELL_VAR *)NULL; -+ for (vc = vcontext; vc; vc = vc->down) -+ if (v = hash_lookup (name, vc->table)) -+ break; -+ -+ return v; -+} -+ -+/* Look up the variable entry named NAME. If SEARCH_TEMPENV is non-zero, -+ then also search the temporarily built list of exported variables. -+ The lookup order is: -+ temporary_env -+ shell_variables list -+*/ -+ -+SHELL_VAR * -+find_variable_internal (name, force_tempenv) -+ const char *name; -+ int force_tempenv; -+{ -+ SHELL_VAR *var; -+ int search_tempenv; -+ VAR_CONTEXT *vc; -+ -+ var = (SHELL_VAR *)NULL; -+ -+ /* If explicitly requested, first look in the temporary environment for -+ the variable. This allows constructs such as "foo=x eval 'echo $foo'" -+ to get the `exported' value of $foo. This happens if we are executing -+ a function or builtin, or if we are looking up a variable in a -+ "subshell environment". */ -+ search_tempenv = force_tempenv || (expanding_redir == 0 && subshell_environment); -+ -+ if (search_tempenv && temporary_env) -+ var = hash_lookup (name, temporary_env); -+ -+ vc = shell_variables; -+#if 0 -+if (search_tempenv == 0 && /* (subshell_environment & SUBSHELL_COMSUB) && */ -+ expanding_redir && -+ (this_shell_builtin == eval_builtin || this_shell_builtin == command_builtin)) -+ { -+ itrace("find_variable_internal: search_tempenv == 0: skipping VC_BLTNENV"); -+ while (vc && (vc->flags & VC_BLTNENV)) -+ vc = vc->down; -+ if (vc == 0) -+ vc = shell_variables; -+ } -+#endif -+ -+ if (var == 0) -+ var = var_lookup (name, vc); -+ -+ if (var == 0) -+ return ((SHELL_VAR *)NULL); -+ -+ return (var->dynamic_value ? (*(var->dynamic_value)) (var) : var); -+} -+ -+/* Look up and resolve the chain of nameref variables starting at V all the -+ way to NULL or non-nameref. */ -+SHELL_VAR * -+find_variable_nameref (v) -+ SHELL_VAR *v; -+{ -+ int level; -+ char *newname; -+ SHELL_VAR *orig, *oldv; -+ -+ level = 0; -+ orig = v; -+ while (v && nameref_p (v)) -+ { -+ level++; -+ if (level > NAMEREF_MAX) -+ return ((SHELL_VAR *)0); /* error message here? */ -+ newname = nameref_cell (v); -+ if (newname == 0 || *newname == '\0') -+ return ((SHELL_VAR *)0); -+ oldv = v; -+ v = find_variable_internal (newname, (expanding_redir == 0 && (assigning_in_environment || executing_builtin))); -+ if (v == orig || v == oldv) -+ { -+ internal_warning (_("%s: circular name reference"), orig->name); -+ return ((SHELL_VAR *)0); -+ } -+ } -+ return v; -+} -+ -+/* Resolve the chain of nameref variables for NAME. XXX - could change later */ -+SHELL_VAR * -+find_variable_last_nameref (name) -+ const char *name; -+{ -+ SHELL_VAR *v, *nv; -+ char *newname; -+ int level; -+ -+ nv = v = find_variable_noref (name); -+ level = 0; -+ while (v && nameref_p (v)) -+ { -+ level++; -+ if (level > NAMEREF_MAX) -+ return ((SHELL_VAR *)0); /* error message here? */ -+ newname = nameref_cell (v); -+ if (newname == 0 || *newname == '\0') -+ return ((SHELL_VAR *)0); -+ nv = v; -+ v = find_variable_internal (newname, (expanding_redir == 0 && (assigning_in_environment || executing_builtin))); -+ } -+ return nv; -+} -+ -+/* Resolve the chain of nameref variables for NAME. XXX - could change later */ -+SHELL_VAR * -+find_global_variable_last_nameref (name) -+ const char *name; -+{ -+ SHELL_VAR *v, *nv; -+ char *newname; -+ int level; -+ -+ nv = v = find_global_variable_noref (name); -+ level = 0; -+ while (v && nameref_p (v)) -+ { -+ level++; -+ if (level > NAMEREF_MAX) -+ return ((SHELL_VAR *)0); /* error message here? */ -+ newname = nameref_cell (v); -+ if (newname == 0 || *newname == '\0') -+ return ((SHELL_VAR *)0); -+ nv = v; -+ v = find_global_variable_noref (newname); -+ } -+ return nv; -+} -+ -+static SHELL_VAR * -+find_nameref_at_context (v, vc) -+ SHELL_VAR *v; -+ VAR_CONTEXT *vc; -+{ -+ SHELL_VAR *nv, *nv2; -+ VAR_CONTEXT *nvc; -+ char *newname; -+ int level; -+ -+ nv = v; -+ level = 1; -+ while (nv && nameref_p (nv)) -+ { -+ level++; -+ if (level > NAMEREF_MAX) -+ return ((SHELL_VAR *)NULL); -+ newname = nameref_cell (nv); -+ if (newname == 0 || *newname == '\0') -+ return ((SHELL_VAR *)NULL); -+ nv2 = hash_lookup (newname, vc->table); -+ if (nv2 == 0) -+ break; -+ nv = nv2; -+ } -+ return nv; -+} -+ -+/* Do nameref resolution from the VC, which is the local context for some -+ function or builtin, `up' the chain to the global variables context. If -+ NVCP is not NULL, return the variable context where we finally ended the -+ nameref resolution (so the bind_variable_internal can use the correct -+ variable context and hash table). */ -+static SHELL_VAR * -+find_variable_nameref_context (v, vc, nvcp) -+ SHELL_VAR *v; -+ VAR_CONTEXT *vc; -+ VAR_CONTEXT **nvcp; -+{ -+ SHELL_VAR *nv, *nv2; -+ VAR_CONTEXT *nvc; -+ -+ /* Look starting at the current context all the way `up' */ -+ for (nv = v, nvc = vc; nvc; nvc = nvc->down) -+ { -+ nv2 = find_nameref_at_context (nv, nvc); -+ if (nv2 == 0) -+ continue; -+ nv = nv2; -+ if (*nvcp) -+ *nvcp = nvc; -+ if (nameref_p (nv) == 0) -+ break; -+ } -+ return (nameref_p (nv) ? (SHELL_VAR *)NULL : nv); -+} -+ -+/* Do nameref resolution from the VC, which is the local context for some -+ function or builtin, `up' the chain to the global variables context. If -+ NVCP is not NULL, return the variable context where we finally ended the -+ nameref resolution (so the bind_variable_internal can use the correct -+ variable context and hash table). */ -+static SHELL_VAR * -+find_variable_last_nameref_context (v, vc, nvcp) -+ SHELL_VAR *v; -+ VAR_CONTEXT *vc; -+ VAR_CONTEXT **nvcp; -+{ -+ SHELL_VAR *nv, *nv2; -+ VAR_CONTEXT *nvc; -+ -+ /* Look starting at the current context all the way `up' */ -+ for (nv = v, nvc = vc; nvc; nvc = nvc->down) -+ { -+ nv2 = find_nameref_at_context (nv, nvc); -+ if (nv2 == 0) -+ continue; -+ nv = nv2; -+ if (*nvcp) -+ *nvcp = nvc; -+ } -+ return (nameref_p (nv) ? nv : (SHELL_VAR *)NULL); -+} -+ -+/* Find a variable, forcing a search of the temporary environment first */ -+SHELL_VAR * -+find_variable_tempenv (name) -+ const char *name; -+{ -+ SHELL_VAR *var; -+ -+ var = find_variable_internal (name, 1); -+ if (var && nameref_p (var)) -+ var = find_variable_nameref (var); -+ return (var); -+} -+ -+/* Find a variable, not forcing a search of the temporary environment first */ -+SHELL_VAR * -+find_variable_notempenv (name) -+ const char *name; -+{ -+ SHELL_VAR *var; -+ -+ var = find_variable_internal (name, 0); -+ if (var && nameref_p (var)) -+ var = find_variable_nameref (var); -+ return (var); -+} -+ -+SHELL_VAR * -+find_global_variable (name) -+ const char *name; -+{ -+ SHELL_VAR *var; -+ -+ var = var_lookup (name, global_variables); -+ if (var && nameref_p (var)) -+ var = find_variable_nameref (var); -+ -+ if (var == 0) -+ return ((SHELL_VAR *)NULL); -+ -+ return (var->dynamic_value ? (*(var->dynamic_value)) (var) : var); -+} -+ -+SHELL_VAR * -+find_global_variable_noref (name) -+ const char *name; -+{ -+ SHELL_VAR *var; -+ -+ var = var_lookup (name, global_variables); -+ -+ if (var == 0) -+ return ((SHELL_VAR *)NULL); -+ -+ return (var->dynamic_value ? (*(var->dynamic_value)) (var) : var); -+} -+ -+SHELL_VAR * -+find_shell_variable (name) -+ const char *name; -+{ -+ SHELL_VAR *var; -+ -+ var = var_lookup (name, shell_variables); -+ if (var && nameref_p (var)) -+ var = find_variable_nameref (var); -+ -+ if (var == 0) -+ return ((SHELL_VAR *)NULL); -+ -+ return (var->dynamic_value ? (*(var->dynamic_value)) (var) : var); -+} -+ -+/* Look up the variable entry named NAME. Returns the entry or NULL. */ -+SHELL_VAR * -+find_variable (name) -+ const char *name; -+{ -+ SHELL_VAR *v; -+ -+ last_table_searched = 0; -+ v = find_variable_internal (name, (expanding_redir == 0 && (assigning_in_environment || executing_builtin))); -+ if (v && nameref_p (v)) -+ v = find_variable_nameref (v); -+ return v; -+} -+ -+SHELL_VAR * -+find_variable_noref (name) -+ const char *name; -+{ -+ SHELL_VAR *v; -+ -+ v = find_variable_internal (name, (expanding_redir == 0 && (assigning_in_environment || executing_builtin))); -+ return v; -+} -+ -+/* Look up the function entry whose name matches STRING. -+ Returns the entry or NULL. */ -+SHELL_VAR * -+find_function (name) -+ const char *name; -+{ -+ return (hash_lookup (name, shell_functions)); -+} -+ -+/* Find the function definition for the shell function named NAME. Returns -+ the entry or NULL. */ -+FUNCTION_DEF * -+find_function_def (name) -+ const char *name; -+{ -+#if defined (DEBUGGER) -+ return ((FUNCTION_DEF *)hash_lookup (name, shell_function_defs)); -+#else -+ return ((FUNCTION_DEF *)0); -+#endif -+} -+ -+/* Return the value of VAR. VAR is assumed to have been the result of a -+ lookup without any subscript, if arrays are compiled into the shell. */ -+char * -+get_variable_value (var) -+ SHELL_VAR *var; -+{ -+ if (var == 0) -+ return ((char *)NULL); -+#if defined (ARRAY_VARS) -+ else if (array_p (var)) -+ return (array_reference (array_cell (var), 0)); -+ else if (assoc_p (var)) -+ return (assoc_reference (assoc_cell (var), "0")); -+#endif -+ else -+ return (value_cell (var)); -+} -+ -+/* Return the string value of a variable. Return NULL if the variable -+ doesn't exist. Don't cons a new string. This is a potential memory -+ leak if the variable is found in the temporary environment. Since -+ functions and variables have separate name spaces, returns NULL if -+ var_name is a shell function only. */ -+char * -+get_string_value (var_name) -+ const char *var_name; -+{ -+ SHELL_VAR *var; -+ -+ var = find_variable (var_name); -+ return ((var) ? get_variable_value (var) : (char *)NULL); -+} -+ -+/* This is present for use by the tilde and readline libraries. */ -+char * -+sh_get_env_value (v) -+ const char *v; -+{ -+ return get_string_value (v); -+} -+ -+/* **************************************************************** */ -+/* */ -+/* Creating and setting variables */ -+/* */ -+/* **************************************************************** */ -+ -+/* Set NAME to VALUE if NAME has no value. */ -+SHELL_VAR * -+set_if_not (name, value) -+ char *name, *value; -+{ -+ SHELL_VAR *v; -+ -+ if (shell_variables == 0) -+ create_variable_tables (); -+ -+ v = find_variable (name); -+ if (v == 0) -+ v = bind_variable_internal (name, value, global_variables->table, HASH_NOSRCH, 0); -+ return (v); -+} -+ -+/* Create a local variable referenced by NAME. */ -+SHELL_VAR * -+make_local_variable (name) -+ const char *name; -+{ -+ SHELL_VAR *new_var, *old_var; -+ VAR_CONTEXT *vc; -+ int was_tmpvar; -+ char *tmp_value; -+ -+ /* local foo; local foo; is a no-op. */ -+ old_var = find_variable (name); -+ if (old_var && local_p (old_var) && old_var->context == variable_context) -+ return (old_var); -+ -+ was_tmpvar = old_var && tempvar_p (old_var); -+ /* If we're making a local variable in a shell function, the temporary env -+ has already been merged into the function's variable context stack. We -+ can assume that a temporary var in the same context appears in the same -+ VAR_CONTEXT and can safely be returned without creating a new variable -+ (which results in duplicate names in the same VAR_CONTEXT->table */ -+ /* We can't just test tmpvar_p because variables in the temporary env given -+ to a shell function appear in the function's local variable VAR_CONTEXT -+ but retain their tempvar attribute. We want temporary variables that are -+ found in temporary_env, hence the test for last_table_searched, which is -+ set in hash_lookup and only (so far) checked here. */ -+ if (was_tmpvar && old_var->context == variable_context && last_table_searched != temporary_env) -+ { -+ VUNSETATTR (old_var, att_invisible); -+ return (old_var); -+ } -+ if (was_tmpvar) -+ tmp_value = value_cell (old_var); -+ -+ for (vc = shell_variables; vc; vc = vc->down) -+ if (vc_isfuncenv (vc) && vc->scope == variable_context) -+ break; -+ -+ if (vc == 0) -+ { -+ internal_error (_("make_local_variable: no function context at current scope")); -+ return ((SHELL_VAR *)NULL); -+ } -+ else if (vc->table == 0) -+ vc->table = hash_create (TEMPENV_HASH_BUCKETS); -+ -+ /* Since this is called only from the local/declare/typeset code, we can -+ call builtin_error here without worry (of course, it will also work -+ for anything that sets this_command_name). Variables with the `noassign' -+ attribute may not be made local. The test against old_var's context -+ level is to disallow local copies of readonly global variables (since I -+ believe that this could be a security hole). Readonly copies of calling -+ function local variables are OK. */ -+ if (old_var && (noassign_p (old_var) || -+ (readonly_p (old_var) && old_var->context == 0))) -+ { -+ if (readonly_p (old_var)) -+ sh_readonly (name); -+ else if (noassign_p (old_var)) -+ builtin_error (_("%s: variable may not be assigned value"), name); -+#if 0 -+ /* Let noassign variables through with a warning */ -+ if (readonly_p (old_var)) -+#endif -+ return ((SHELL_VAR *)NULL); -+ } -+ -+ if (old_var == 0) -+ new_var = make_new_variable (name, vc->table); -+ else -+ { -+ new_var = make_new_variable (name, vc->table); -+ -+ /* If we found this variable in one of the temporary environments, -+ inherit its value. Watch to see if this causes problems with -+ things like `x=4 local x'. XXX - see above for temporary env -+ variables with the same context level as variable_context */ -+ /* XXX - we should only do this if the variable is not an array. */ -+ if (was_tmpvar) -+ var_setvalue (new_var, savestring (tmp_value)); -+ -+ new_var->attributes = exported_p (old_var) ? att_exported : 0; -+ } -+ -+ vc->flags |= VC_HASLOCAL; -+ -+ new_var->context = variable_context; -+ VSETATTR (new_var, att_local); -+ -+ if (ifsname (name)) -+ setifs (new_var); -+ -+ if (was_tmpvar == 0) -+ VSETATTR (new_var, att_invisible); /* XXX */ -+ return (new_var); -+} -+ -+/* Create a new shell variable with name NAME. */ -+static SHELL_VAR * -+new_shell_variable (name) -+ const char *name; -+{ -+ SHELL_VAR *entry; -+ -+ entry = (SHELL_VAR *)xmalloc (sizeof (SHELL_VAR)); -+ -+ entry->name = savestring (name); -+ var_setvalue (entry, (char *)NULL); -+ CLEAR_EXPORTSTR (entry); -+ -+ entry->dynamic_value = (sh_var_value_func_t *)NULL; -+ entry->assign_func = (sh_var_assign_func_t *)NULL; -+ -+ entry->attributes = 0; -+ -+ /* Always assume variables are to be made at toplevel! -+ make_local_variable has the responsibility of changing the -+ variable context. */ -+ entry->context = 0; -+ -+ return (entry); -+} -+ -+/* Create a new shell variable with name NAME and add it to the hash table -+ TABLE. */ -+static SHELL_VAR * -+make_new_variable (name, table) -+ const char *name; -+ HASH_TABLE *table; -+{ -+ SHELL_VAR *entry; -+ BUCKET_CONTENTS *elt; -+ -+ entry = new_shell_variable (name); -+ -+ /* Make sure we have a shell_variables hash table to add to. */ -+ if (shell_variables == 0) -+ create_variable_tables (); -+ -+ elt = hash_insert (savestring (name), table, HASH_NOSRCH); -+ elt->data = (PTR_T)entry; -+ -+ return entry; -+} -+ -+#if defined (ARRAY_VARS) -+SHELL_VAR * -+make_new_array_variable (name) -+ char *name; -+{ -+ SHELL_VAR *entry; -+ ARRAY *array; -+ -+ entry = make_new_variable (name, global_variables->table); -+ array = array_create (); -+ -+ var_setarray (entry, array); -+ VSETATTR (entry, att_array); -+ return entry; -+} -+ -+SHELL_VAR * -+make_local_array_variable (name, assoc_ok) -+ char *name; -+ int assoc_ok; -+{ -+ SHELL_VAR *var; -+ ARRAY *array; -+ -+ var = make_local_variable (name); -+ if (var == 0 || array_p (var) || (assoc_ok && assoc_p (var))) -+ return var; -+ -+ array = array_create (); -+ -+ dispose_variable_value (var); -+ var_setarray (var, array); -+ VSETATTR (var, att_array); -+ return var; -+} -+ -+SHELL_VAR * -+make_new_assoc_variable (name) -+ char *name; -+{ -+ SHELL_VAR *entry; -+ HASH_TABLE *hash; -+ -+ entry = make_new_variable (name, global_variables->table); -+ hash = assoc_create (0); -+ -+ var_setassoc (entry, hash); -+ VSETATTR (entry, att_assoc); -+ return entry; -+} -+ -+SHELL_VAR * -+make_local_assoc_variable (name) -+ char *name; -+{ -+ SHELL_VAR *var; -+ HASH_TABLE *hash; -+ -+ var = make_local_variable (name); -+ if (var == 0 || assoc_p (var)) -+ return var; -+ -+ dispose_variable_value (var); -+ hash = assoc_create (0); -+ -+ var_setassoc (var, hash); -+ VSETATTR (var, att_assoc); -+ return var; -+} -+#endif -+ -+char * -+make_variable_value (var, value, flags) -+ SHELL_VAR *var; -+ char *value; -+ int flags; -+{ -+ char *retval, *oval; -+ intmax_t lval, rval; -+ int expok, olen, op; -+ -+ /* If this variable has had its type set to integer (via `declare -i'), -+ then do expression evaluation on it and store the result. The -+ functions in expr.c (evalexp()) and bind_int_variable() are responsible -+ for turning off the integer flag if they don't want further -+ evaluation done. */ -+ if (integer_p (var)) -+ { -+ if (flags & ASS_APPEND) -+ { -+ oval = value_cell (var); -+ lval = evalexp (oval, &expok); /* ksh93 seems to do this */ -+ if (expok == 0) -+ { -+ top_level_cleanup (); -+ jump_to_top_level (DISCARD); -+ } -+ } -+ rval = evalexp (value, &expok); -+ if (expok == 0) -+ { -+ top_level_cleanup (); -+ jump_to_top_level (DISCARD); -+ } -+ /* This can be fooled if the variable's value changes while evaluating -+ `rval'. We can change it if we move the evaluation of lval to here. */ -+ if (flags & ASS_APPEND) -+ rval += lval; -+ retval = itos (rval); -+ } -+#if defined (CASEMOD_ATTRS) -+ else if (capcase_p (var) || uppercase_p (var) || lowercase_p (var)) -+ { -+ if (flags & ASS_APPEND) -+ { -+ oval = get_variable_value (var); -+ if (oval == 0) /* paranoia */ -+ oval = ""; -+ olen = STRLEN (oval); -+ retval = (char *)xmalloc (olen + (value ? STRLEN (value) : 0) + 1); -+ strcpy (retval, oval); -+ if (value) -+ strcpy (retval+olen, value); -+ } -+ else if (*value) -+ retval = savestring (value); -+ else -+ { -+ retval = (char *)xmalloc (1); -+ retval[0] = '\0'; -+ } -+ op = capcase_p (var) ? CASE_CAPITALIZE -+ : (uppercase_p (var) ? CASE_UPPER : CASE_LOWER); -+ oval = sh_modcase (retval, (char *)0, op); -+ free (retval); -+ retval = oval; -+ } -+#endif /* CASEMOD_ATTRS */ -+ else if (value) -+ { -+ if (flags & ASS_APPEND) -+ { -+ oval = get_variable_value (var); -+ if (oval == 0) /* paranoia */ -+ oval = ""; -+ olen = STRLEN (oval); -+ retval = (char *)xmalloc (olen + (value ? STRLEN (value) : 0) + 1); -+ strcpy (retval, oval); -+ if (value) -+ strcpy (retval+olen, value); -+ } -+ else if (*value) -+ retval = savestring (value); -+ else -+ { -+ retval = (char *)xmalloc (1); -+ retval[0] = '\0'; -+ } -+ } -+ else -+ retval = (char *)NULL; -+ -+ return retval; -+} -+ -+/* Bind a variable NAME to VALUE in the HASH_TABLE TABLE, which may be the -+ temporary environment (but usually is not). */ -+static SHELL_VAR * -+bind_variable_internal (name, value, table, hflags, aflags) -+ const char *name; -+ char *value; -+ HASH_TABLE *table; -+ int hflags, aflags; -+{ -+ char *newval; -+ SHELL_VAR *entry; -+ -+ entry = (hflags & HASH_NOSRCH) ? (SHELL_VAR *)NULL : hash_lookup (name, table); -+ /* Follow the nameref chain here if this is the global variables table */ -+ if (entry && nameref_p (entry) && (invisible_p (entry) == 0) && table == global_variables->table) -+ { -+ entry = find_global_variable (entry->name); -+ /* Let's see if we have a nameref referencing a variable that hasn't yet -+ been created. */ -+ if (entry == 0) -+ entry = find_variable_last_nameref (name); /* XXX */ -+ if (entry == 0) /* just in case */ -+ return (entry); -+ } -+ -+ /* The first clause handles `declare -n ref; ref=x;' */ -+ if (entry && invisible_p (entry) && nameref_p (entry)) -+ goto assign_value; -+ else if (entry && nameref_p (entry)) -+ { -+ newval = nameref_cell (entry); -+#if defined (ARRAY_VARS) -+ /* declare -n foo=x[2] */ -+ if (valid_array_reference (newval)) -+ /* XXX - should it be aflags? */ -+ entry = assign_array_element (newval, make_variable_value (entry, value, 0), aflags); -+ else -+#endif -+ { -+ entry = make_new_variable (newval, table); -+ var_setvalue (entry, make_variable_value (entry, value, 0)); -+ } -+ } -+ else if (entry == 0) -+ { -+ entry = make_new_variable (name, table); -+ var_setvalue (entry, make_variable_value (entry, value, 0)); /* XXX */ -+ } -+ else if (entry->assign_func) /* array vars have assign functions now */ -+ { -+ INVALIDATE_EXPORTSTR (entry); -+ newval = (aflags & ASS_APPEND) ? make_variable_value (entry, value, aflags) : value; -+ if (assoc_p (entry)) -+ entry = (*(entry->assign_func)) (entry, newval, -1, savestring ("0")); -+ else if (array_p (entry)) -+ entry = (*(entry->assign_func)) (entry, newval, 0, 0); -+ else -+ entry = (*(entry->assign_func)) (entry, newval, -1, 0); -+ if (newval != value) -+ free (newval); -+ return (entry); -+ } -+ else -+ { -+assign_value: -+ if (readonly_p (entry) || noassign_p (entry)) -+ { -+ if (readonly_p (entry)) -+ err_readonly (name); -+ return (entry); -+ } -+ -+ /* Variables which are bound are visible. */ -+ VUNSETATTR (entry, att_invisible); -+ -+#if defined (ARRAY_VARS) -+ if (assoc_p (entry) || array_p (entry)) -+ newval = make_array_variable_value (entry, 0, "0", value, aflags); -+ else -+#endif -+ -+ newval = make_variable_value (entry, value, aflags); /* XXX */ -+ -+ /* Invalidate any cached export string */ -+ INVALIDATE_EXPORTSTR (entry); -+ -+#if defined (ARRAY_VARS) -+ /* XXX -- this bears looking at again -- XXX */ -+ /* If an existing array variable x is being assigned to with x=b or -+ `read x' or something of that nature, silently convert it to -+ x[0]=b or `read x[0]'. */ -+ if (assoc_p (entry)) -+ { -+ assoc_insert (assoc_cell (entry), savestring ("0"), newval); -+ free (newval); -+ } -+ else if (array_p (entry)) -+ { -+ array_insert (array_cell (entry), 0, newval); -+ free (newval); -+ } -+ else -+#endif -+ { -+ FREE (value_cell (entry)); -+ var_setvalue (entry, newval); -+ } -+ } -+ -+ if (mark_modified_vars) -+ VSETATTR (entry, att_exported); -+ -+ if (exported_p (entry)) -+ array_needs_making = 1; -+ -+ return (entry); -+} -+ -+/* Bind a variable NAME to VALUE. This conses up the name -+ and value strings. If we have a temporary environment, we bind there -+ first, then we bind into shell_variables. */ -+ -+SHELL_VAR * -+bind_variable (name, value, flags) -+ const char *name; -+ char *value; -+ int flags; -+{ -+ SHELL_VAR *v, *nv; -+ VAR_CONTEXT *vc, *nvc; -+ int level; -+ -+ if (shell_variables == 0) -+ create_variable_tables (); -+ -+ /* If we have a temporary environment, look there first for the variable, -+ and, if found, modify the value there before modifying it in the -+ shell_variables table. This allows sourced scripts to modify values -+ given to them in a temporary environment while modifying the variable -+ value that the caller sees. */ -+ if (temporary_env) -+ bind_tempenv_variable (name, value); -+ -+ /* XXX -- handle local variables here. */ -+ for (vc = shell_variables; vc; vc = vc->down) -+ { -+ if (vc_isfuncenv (vc) || vc_isbltnenv (vc)) -+ { -+ v = hash_lookup (name, vc->table); -+ nvc = vc; -+ if (v && nameref_p (v)) -+ { -+ nv = find_variable_nameref_context (v, vc, &nvc); -+ if (nv == 0) -+ { -+ nv = find_variable_last_nameref_context (v, vc, &nvc); -+ if (nv && nameref_p (nv)) -+ { -+ /* If this nameref variable doesn't have a value yet, -+ set the value. Otherwise, assign using the value as -+ normal. */ -+ if (nameref_cell (nv) == 0) -+ return (bind_variable_internal (nv->name, value, nvc->table, 0, flags)); -+ return (bind_variable_internal (nameref_cell (nv), value, nvc->table, 0, flags)); -+ } -+ else -+ v = nv; -+ } -+ else -+ v = nv; -+ } -+ if (v) -+ return (bind_variable_internal (v->name, value, nvc->table, 0, flags)); -+ } -+ } -+ /* bind_variable_internal will handle nameref resolution in this case */ -+ return (bind_variable_internal (name, value, global_variables->table, 0, flags)); -+} -+ -+SHELL_VAR * -+bind_global_variable (name, value, flags) -+ const char *name; -+ char *value; -+ int flags; -+{ -+ SHELL_VAR *v, *nv; -+ VAR_CONTEXT *vc, *nvc; -+ int level; -+ -+ if (shell_variables == 0) -+ create_variable_tables (); -+ -+ /* bind_variable_internal will handle nameref resolution in this case */ -+ return (bind_variable_internal (name, value, global_variables->table, 0, flags)); -+} -+ -+/* Make VAR, a simple shell variable, have value VALUE. Once assigned a -+ value, variables are no longer invisible. This is a duplicate of part -+ of the internals of bind_variable. If the variable is exported, or -+ all modified variables should be exported, mark the variable for export -+ and note that the export environment needs to be recreated. */ -+SHELL_VAR * -+bind_variable_value (var, value, aflags) -+ SHELL_VAR *var; -+ char *value; -+ int aflags; -+{ -+ char *t; -+ int invis; -+ -+ invis = invisible_p (var); -+ VUNSETATTR (var, att_invisible); -+ -+ if (var->assign_func) -+ { -+ /* If we're appending, we need the old value, so use -+ make_variable_value */ -+ t = (aflags & ASS_APPEND) ? make_variable_value (var, value, aflags) : value; -+ (*(var->assign_func)) (var, t, -1, 0); -+ if (t != value && t) -+ free (t); -+ } -+ else -+ { -+ t = make_variable_value (var, value, aflags); -+#if defined (ARRAY_VARS) -+ if ((aflags & ASS_NAMEREF) && (t == 0 || *t == 0 || (legal_identifier (t) == 0 && valid_array_reference (t) == 0))) -+#else -+ if ((aflags & ASS_NAMEREF) && (t == 0 || *t == 0 || legal_identifier (t) == 0)) -+#endif -+ { -+ free (t); -+ if (invis) -+ VSETATTR (var, att_invisible); /* XXX */ -+ return ((SHELL_VAR *)NULL); -+ } -+ FREE (value_cell (var)); -+ var_setvalue (var, t); -+ } -+ -+ INVALIDATE_EXPORTSTR (var); -+ -+ if (mark_modified_vars) -+ VSETATTR (var, att_exported); -+ -+ if (exported_p (var)) -+ array_needs_making = 1; -+ -+ return (var); -+} -+ -+/* Bind/create a shell variable with the name LHS to the RHS. -+ This creates or modifies a variable such that it is an integer. -+ -+ This used to be in expr.c, but it is here so that all of the -+ variable binding stuff is localized. Since we don't want any -+ recursive evaluation from bind_variable() (possible without this code, -+ since bind_variable() calls the evaluator for variables with the integer -+ attribute set), we temporarily turn off the integer attribute for each -+ variable we set here, then turn it back on after binding as necessary. */ -+ -+SHELL_VAR * -+bind_int_variable (lhs, rhs) -+ char *lhs, *rhs; -+{ -+ register SHELL_VAR *v; -+ int isint, isarr, implicitarray; -+ -+ isint = isarr = implicitarray = 0; -+#if defined (ARRAY_VARS) -+ if (valid_array_reference (lhs)) -+ { -+ isarr = 1; -+ v = array_variable_part (lhs, (char **)0, (int *)0); -+ } -+ else -+#endif -+ v = find_variable (lhs); -+ -+ if (v) -+ { -+ isint = integer_p (v); -+ VUNSETATTR (v, att_integer); -+#if defined (ARRAY_VARS) -+ if (array_p (v) && isarr == 0) -+ implicitarray = 1; -+#endif -+ } -+ -+#if defined (ARRAY_VARS) -+ if (isarr) -+ v = assign_array_element (lhs, rhs, 0); -+ else if (implicitarray) -+ v = bind_array_variable (lhs, 0, rhs, 0); -+ else -+#endif -+ v = bind_variable (lhs, rhs, 0); -+ -+ if (v && isint) -+ VSETATTR (v, att_integer); -+ -+ VUNSETATTR (v, att_invisible); -+ -+ return (v); -+} -+ -+SHELL_VAR * -+bind_var_to_int (var, val) -+ char *var; -+ intmax_t val; -+{ -+ char ibuf[INT_STRLEN_BOUND (intmax_t) + 1], *p; -+ -+ p = fmtulong (val, 10, ibuf, sizeof (ibuf), 0); -+ return (bind_int_variable (var, p)); -+} -+ -+/* Do a function binding to a variable. You pass the name and -+ the command to bind to. This conses the name and command. */ -+SHELL_VAR * -+bind_function (name, value) -+ const char *name; -+ COMMAND *value; -+{ -+ SHELL_VAR *entry; -+ -+ entry = find_function (name); -+ if (entry == 0) -+ { -+ BUCKET_CONTENTS *elt; -+ -+ elt = hash_insert (savestring (name), shell_functions, HASH_NOSRCH); -+ entry = new_shell_variable (name); -+ elt->data = (PTR_T)entry; -+ } -+ else -+ INVALIDATE_EXPORTSTR (entry); -+ -+ if (var_isset (entry)) -+ dispose_command (function_cell (entry)); -+ -+ if (value) -+ var_setfunc (entry, copy_command (value)); -+ else -+ var_setfunc (entry, 0); -+ -+ VSETATTR (entry, att_function); -+ -+ if (mark_modified_vars) -+ VSETATTR (entry, att_exported); -+ -+ VUNSETATTR (entry, att_invisible); /* Just to be sure */ -+ -+ if (exported_p (entry)) -+ array_needs_making = 1; -+ -+#if defined (PROGRAMMABLE_COMPLETION) -+ set_itemlist_dirty (&it_functions); -+#endif -+ -+ return (entry); -+} -+ -+#if defined (DEBUGGER) -+/* Bind a function definition, which includes source file and line number -+ information in addition to the command, into the FUNCTION_DEF hash table.*/ -+void -+bind_function_def (name, value) -+ const char *name; -+ FUNCTION_DEF *value; -+{ -+ FUNCTION_DEF *entry; -+ BUCKET_CONTENTS *elt; -+ COMMAND *cmd; -+ -+ entry = find_function_def (name); -+ if (entry) -+ { -+ dispose_function_def_contents (entry); -+ entry = copy_function_def_contents (value, entry); -+ } -+ else -+ { -+ cmd = value->command; -+ value->command = 0; -+ entry = copy_function_def (value); -+ value->command = cmd; -+ -+ elt = hash_insert (savestring (name), shell_function_defs, HASH_NOSRCH); -+ elt->data = (PTR_T *)entry; -+ } -+} -+#endif /* DEBUGGER */ -+ -+/* Add STRING, which is of the form foo=bar, to the temporary environment -+ HASH_TABLE (temporary_env). The functions in execute_cmd.c are -+ responsible for moving the main temporary env to one of the other -+ temporary environments. The expansion code in subst.c calls this. */ -+int -+assign_in_env (word, flags) -+ WORD_DESC *word; -+ int flags; -+{ -+ int offset, aflags; -+ char *name, *temp, *value; -+ SHELL_VAR *var; -+ const char *string; -+ -+ string = word->word; -+ -+ aflags = 0; -+ offset = assignment (string, 0); -+ name = savestring (string); -+ value = (char *)NULL; -+ -+ if (name[offset] == '=') -+ { -+ name[offset] = 0; -+ -+ /* don't ignore the `+' when assigning temporary environment */ -+ if (name[offset - 1] == '+') -+ { -+ name[offset - 1] = '\0'; -+ aflags |= ASS_APPEND; -+ } -+ -+ var = find_variable (name); -+ if (var && (readonly_p (var) || noassign_p (var))) -+ { -+ if (readonly_p (var)) -+ err_readonly (name); -+ free (name); -+ return (0); -+ } -+ -+ temp = name + offset + 1; -+ value = expand_assignment_string_to_string (temp, 0); -+ -+ if (var && (aflags & ASS_APPEND)) -+ { -+ temp = make_variable_value (var, value, aflags); -+ FREE (value); -+ value = temp; -+ } -+ } -+ -+ if (temporary_env == 0) -+ temporary_env = hash_create (TEMPENV_HASH_BUCKETS); -+ -+ var = hash_lookup (name, temporary_env); -+ if (var == 0) -+ var = make_new_variable (name, temporary_env); -+ else -+ FREE (value_cell (var)); -+ -+ if (value == 0) -+ { -+ value = (char *)xmalloc (1); /* like do_assignment_internal */ -+ value[0] = '\0'; -+ } -+ -+ var_setvalue (var, value); -+ var->attributes |= (att_exported|att_tempvar); -+ var->context = variable_context; /* XXX */ -+ -+ INVALIDATE_EXPORTSTR (var); -+ var->exportstr = mk_env_string (name, value, 0); -+ -+ array_needs_making = 1; -+ -+ if (flags) -+ stupidly_hack_special_variables (name); -+ -+ if (echo_command_at_execute) -+ /* The Korn shell prints the `+ ' in front of assignment statements, -+ so we do too. */ -+ xtrace_print_assignment (name, value, 0, 1); -+ -+ free (name); -+ return 1; -+} -+ -+/* **************************************************************** */ -+/* */ -+/* Copying variables */ -+/* */ -+/* **************************************************************** */ -+ -+#ifdef INCLUDE_UNUSED -+/* Copy VAR to a new data structure and return that structure. */ -+SHELL_VAR * -+copy_variable (var) -+ SHELL_VAR *var; -+{ -+ SHELL_VAR *copy = (SHELL_VAR *)NULL; -+ -+ if (var) -+ { -+ copy = (SHELL_VAR *)xmalloc (sizeof (SHELL_VAR)); -+ -+ copy->attributes = var->attributes; -+ copy->name = savestring (var->name); -+ -+ if (function_p (var)) -+ var_setfunc (copy, copy_command (function_cell (var))); -+#if defined (ARRAY_VARS) -+ else if (array_p (var)) -+ var_setarray (copy, array_copy (array_cell (var))); -+ else if (assoc_p (var)) -+ var_setassoc (copy, assoc_copy (assoc_cell (var))); -+#endif -+ else if (nameref_cell (var)) /* XXX - nameref */ -+ var_setref (copy, savestring (nameref_cell (var))); -+ else if (value_cell (var)) /* XXX - nameref */ -+ var_setvalue (copy, savestring (value_cell (var))); -+ else -+ var_setvalue (copy, (char *)NULL); -+ -+ copy->dynamic_value = var->dynamic_value; -+ copy->assign_func = var->assign_func; -+ -+ copy->exportstr = COPY_EXPORTSTR (var); -+ -+ copy->context = var->context; -+ } -+ return (copy); -+} -+#endif -+ -+/* **************************************************************** */ -+/* */ -+/* Deleting and unsetting variables */ -+/* */ -+/* **************************************************************** */ -+ -+/* Dispose of the information attached to VAR. */ -+static void -+dispose_variable_value (var) -+ SHELL_VAR *var; -+{ -+ if (function_p (var)) -+ dispose_command (function_cell (var)); -+#if defined (ARRAY_VARS) -+ else if (array_p (var)) -+ array_dispose (array_cell (var)); -+ else if (assoc_p (var)) -+ assoc_dispose (assoc_cell (var)); -+#endif -+ else if (nameref_p (var)) -+ FREE (nameref_cell (var)); -+ else -+ FREE (value_cell (var)); -+} -+ -+void -+dispose_variable (var) -+ SHELL_VAR *var; -+{ -+ if (var == 0) -+ return; -+ -+ if (nofree_p (var) == 0) -+ dispose_variable_value (var); -+ -+ FREE_EXPORTSTR (var); -+ -+ free (var->name); -+ -+ if (exported_p (var)) -+ array_needs_making = 1; -+ -+ free (var); -+} -+ -+/* Unset the shell variable referenced by NAME. Unsetting a nameref variable -+ unsets the variable it resolves to but leaves the nameref alone. */ -+int -+unbind_variable (name) -+ const char *name; -+{ -+ SHELL_VAR *v, *nv; -+ int r; -+ -+ v = var_lookup (name, shell_variables); -+ nv = (v && nameref_p (v)) ? find_variable_nameref (v) : (SHELL_VAR *)NULL; -+ -+ r = nv ? makunbound (nv->name, shell_variables) : makunbound (name, shell_variables); -+ return r; -+} -+ -+/* Unbind NAME, where NAME is assumed to be a nameref variable */ -+int -+unbind_nameref (name) -+ const char *name; -+{ -+ SHELL_VAR *v; -+ -+ v = var_lookup (name, shell_variables); -+ if (v && nameref_p (v)) -+ return makunbound (name, shell_variables); -+ return 0; -+} -+ -+/* Unset the shell function named NAME. */ -+int -+unbind_func (name) -+ const char *name; -+{ -+ BUCKET_CONTENTS *elt; -+ SHELL_VAR *func; -+ -+ elt = hash_remove (name, shell_functions, 0); -+ -+ if (elt == 0) -+ return -1; -+ -+#if defined (PROGRAMMABLE_COMPLETION) -+ set_itemlist_dirty (&it_functions); -+#endif -+ -+ func = (SHELL_VAR *)elt->data; -+ if (func) -+ { -+ if (exported_p (func)) -+ array_needs_making++; -+ dispose_variable (func); -+ } -+ -+ free (elt->key); -+ free (elt); -+ -+ return 0; -+} -+ -+#if defined (DEBUGGER) -+int -+unbind_function_def (name) -+ const char *name; -+{ -+ BUCKET_CONTENTS *elt; -+ FUNCTION_DEF *funcdef; -+ -+ elt = hash_remove (name, shell_function_defs, 0); -+ -+ if (elt == 0) -+ return -1; -+ -+ funcdef = (FUNCTION_DEF *)elt->data; -+ if (funcdef) -+ dispose_function_def (funcdef); -+ -+ free (elt->key); -+ free (elt); -+ -+ return 0; -+} -+#endif /* DEBUGGER */ -+ -+int -+delete_var (name, vc) -+ const char *name; -+ VAR_CONTEXT *vc; -+{ -+ BUCKET_CONTENTS *elt; -+ SHELL_VAR *old_var; -+ VAR_CONTEXT *v; -+ -+ for (elt = (BUCKET_CONTENTS *)NULL, v = vc; v; v = v->down) -+ if (elt = hash_remove (name, v->table, 0)) -+ break; -+ -+ if (elt == 0) -+ return (-1); -+ -+ old_var = (SHELL_VAR *)elt->data; -+ free (elt->key); -+ free (elt); -+ -+ dispose_variable (old_var); -+ return (0); -+} -+ -+/* Make the variable associated with NAME go away. HASH_LIST is the -+ hash table from which this variable should be deleted (either -+ shell_variables or shell_functions). -+ Returns non-zero if the variable couldn't be found. */ -+int -+makunbound (name, vc) -+ const char *name; -+ VAR_CONTEXT *vc; -+{ -+ BUCKET_CONTENTS *elt, *new_elt; -+ SHELL_VAR *old_var; -+ VAR_CONTEXT *v; -+ char *t; -+ -+ for (elt = (BUCKET_CONTENTS *)NULL, v = vc; v; v = v->down) -+ if (elt = hash_remove (name, v->table, 0)) -+ break; -+ -+ if (elt == 0) -+ return (-1); -+ -+ old_var = (SHELL_VAR *)elt->data; -+ -+ if (old_var && exported_p (old_var)) -+ array_needs_making++; -+ -+ /* If we're unsetting a local variable and we're still executing inside -+ the function, just mark the variable as invisible. The function -+ eventually called by pop_var_context() will clean it up later. This -+ must be done so that if the variable is subsequently assigned a new -+ value inside the function, the `local' attribute is still present. -+ We also need to add it back into the correct hash table. */ -+ if (old_var && local_p (old_var) && variable_context == old_var->context) -+ { -+ if (nofree_p (old_var)) -+ var_setvalue (old_var, (char *)NULL); -+#if defined (ARRAY_VARS) -+ else if (array_p (old_var)) -+ array_dispose (array_cell (old_var)); -+ else if (assoc_p (old_var)) -+ assoc_dispose (assoc_cell (old_var)); -+#endif -+ else if (nameref_p (old_var)) -+ FREE (nameref_cell (old_var)); -+ else -+ FREE (value_cell (old_var)); -+ /* Reset the attributes. Preserve the export attribute if the variable -+ came from a temporary environment. Make sure it stays local, and -+ make it invisible. */ -+ old_var->attributes = (exported_p (old_var) && tempvar_p (old_var)) ? att_exported : 0; -+ VSETATTR (old_var, att_local); -+ VSETATTR (old_var, att_invisible); -+ var_setvalue (old_var, (char *)NULL); -+ INVALIDATE_EXPORTSTR (old_var); -+ -+ new_elt = hash_insert (savestring (old_var->name), v->table, 0); -+ new_elt->data = (PTR_T)old_var; -+ stupidly_hack_special_variables (old_var->name); -+ -+ free (elt->key); -+ free (elt); -+ return (0); -+ } -+ -+ /* Have to save a copy of name here, because it might refer to -+ old_var->name. If so, stupidly_hack_special_variables will -+ reference freed memory. */ -+ t = savestring (name); -+ -+ free (elt->key); -+ free (elt); -+ -+ dispose_variable (old_var); -+ stupidly_hack_special_variables (t); -+ free (t); -+ -+ return (0); -+} -+ -+/* Get rid of all of the variables in the current context. */ -+void -+kill_all_local_variables () -+{ -+ VAR_CONTEXT *vc; -+ -+ for (vc = shell_variables; vc; vc = vc->down) -+ if (vc_isfuncenv (vc) && vc->scope == variable_context) -+ break; -+ if (vc == 0) -+ return; /* XXX */ -+ -+ if (vc->table && vc_haslocals (vc)) -+ { -+ delete_all_variables (vc->table); -+ hash_dispose (vc->table); -+ } -+ vc->table = (HASH_TABLE *)NULL; -+} -+ -+static void -+free_variable_hash_data (data) -+ PTR_T data; -+{ -+ SHELL_VAR *var; -+ -+ var = (SHELL_VAR *)data; -+ dispose_variable (var); -+} -+ -+/* Delete the entire contents of the hash table. */ -+void -+delete_all_variables (hashed_vars) -+ HASH_TABLE *hashed_vars; -+{ -+ hash_flush (hashed_vars, free_variable_hash_data); -+} -+ -+/* **************************************************************** */ -+/* */ -+/* Setting variable attributes */ -+/* */ -+/* **************************************************************** */ -+ -+#define FIND_OR_MAKE_VARIABLE(name, entry) \ -+ do \ -+ { \ -+ entry = find_variable (name); \ -+ if (!entry) \ -+ { \ -+ entry = bind_variable (name, "", 0); \ -+ if (!no_invisible_vars && entry) entry->attributes |= att_invisible; \ -+ } \ -+ } \ -+ while (0) -+ -+/* Make the variable associated with NAME be readonly. -+ If NAME does not exist yet, create it. */ -+void -+set_var_read_only (name) -+ char *name; -+{ -+ SHELL_VAR *entry; -+ -+ FIND_OR_MAKE_VARIABLE (name, entry); -+ VSETATTR (entry, att_readonly); -+} -+ -+#ifdef INCLUDE_UNUSED -+/* Make the function associated with NAME be readonly. -+ If NAME does not exist, we just punt, like auto_export code below. */ -+void -+set_func_read_only (name) -+ const char *name; -+{ -+ SHELL_VAR *entry; -+ -+ entry = find_function (name); -+ if (entry) -+ VSETATTR (entry, att_readonly); -+} -+ -+/* Make the variable associated with NAME be auto-exported. -+ If NAME does not exist yet, create it. */ -+void -+set_var_auto_export (name) -+ char *name; -+{ -+ SHELL_VAR *entry; -+ -+ FIND_OR_MAKE_VARIABLE (name, entry); -+ set_auto_export (entry); -+} -+ -+/* Make the function associated with NAME be auto-exported. */ -+void -+set_func_auto_export (name) -+ const char *name; -+{ -+ SHELL_VAR *entry; -+ -+ entry = find_function (name); -+ if (entry) -+ set_auto_export (entry); -+} -+#endif -+ -+/* **************************************************************** */ -+/* */ -+/* Creating lists of variables */ -+/* */ -+/* **************************************************************** */ -+ -+static VARLIST * -+vlist_alloc (nentries) -+ int nentries; -+{ -+ VARLIST *vlist; -+ -+ vlist = (VARLIST *)xmalloc (sizeof (VARLIST)); -+ vlist->list = (SHELL_VAR **)xmalloc ((nentries + 1) * sizeof (SHELL_VAR *)); -+ vlist->list_size = nentries; -+ vlist->list_len = 0; -+ vlist->list[0] = (SHELL_VAR *)NULL; -+ -+ return vlist; -+} -+ -+static VARLIST * -+vlist_realloc (vlist, n) -+ VARLIST *vlist; -+ int n; -+{ -+ if (vlist == 0) -+ return (vlist = vlist_alloc (n)); -+ if (n > vlist->list_size) -+ { -+ vlist->list_size = n; -+ vlist->list = (SHELL_VAR **)xrealloc (vlist->list, (vlist->list_size + 1) * sizeof (SHELL_VAR *)); -+ } -+ return vlist; -+} -+ -+static void -+vlist_add (vlist, var, flags) -+ VARLIST *vlist; -+ SHELL_VAR *var; -+ int flags; -+{ -+ register int i; -+ -+ for (i = 0; i < vlist->list_len; i++) -+ if (STREQ (var->name, vlist->list[i]->name)) -+ break; -+ if (i < vlist->list_len) -+ return; -+ -+ if (i >= vlist->list_size) -+ vlist = vlist_realloc (vlist, vlist->list_size + 16); -+ -+ vlist->list[vlist->list_len++] = var; -+ vlist->list[vlist->list_len] = (SHELL_VAR *)NULL; -+} -+ -+/* Map FUNCTION over the variables in VAR_HASH_TABLE. Return an array of the -+ variables for which FUNCTION returns a non-zero value. A NULL value -+ for FUNCTION means to use all variables. */ -+SHELL_VAR ** -+map_over (function, vc) -+ sh_var_map_func_t *function; -+ VAR_CONTEXT *vc; -+{ -+ VAR_CONTEXT *v; -+ VARLIST *vlist; -+ SHELL_VAR **ret; -+ int nentries; -+ -+ for (nentries = 0, v = vc; v; v = v->down) -+ nentries += HASH_ENTRIES (v->table); -+ -+ if (nentries == 0) -+ return (SHELL_VAR **)NULL; -+ -+ vlist = vlist_alloc (nentries); -+ -+ for (v = vc; v; v = v->down) -+ flatten (v->table, function, vlist, 0); -+ -+ ret = vlist->list; -+ free (vlist); -+ return ret; -+} -+ -+SHELL_VAR ** -+map_over_funcs (function) -+ sh_var_map_func_t *function; -+{ -+ VARLIST *vlist; -+ SHELL_VAR **ret; -+ -+ if (shell_functions == 0 || HASH_ENTRIES (shell_functions) == 0) -+ return ((SHELL_VAR **)NULL); -+ -+ vlist = vlist_alloc (HASH_ENTRIES (shell_functions)); -+ -+ flatten (shell_functions, function, vlist, 0); -+ -+ ret = vlist->list; -+ free (vlist); -+ return ret; -+} -+ -+/* Flatten VAR_HASH_TABLE, applying FUNC to each member and adding those -+ elements for which FUNC succeeds to VLIST->list. FLAGS is reserved -+ for future use. Only unique names are added to VLIST. If FUNC is -+ NULL, each variable in VAR_HASH_TABLE is added to VLIST. If VLIST is -+ NULL, FUNC is applied to each SHELL_VAR in VAR_HASH_TABLE. If VLIST -+ and FUNC are both NULL, nothing happens. */ -+static void -+flatten (var_hash_table, func, vlist, flags) -+ HASH_TABLE *var_hash_table; -+ sh_var_map_func_t *func; -+ VARLIST *vlist; -+ int flags; -+{ -+ register int i; -+ register BUCKET_CONTENTS *tlist; -+ int r; -+ SHELL_VAR *var; -+ -+ if (var_hash_table == 0 || (HASH_ENTRIES (var_hash_table) == 0) || (vlist == 0 && func == 0)) -+ return; -+ -+ for (i = 0; i < var_hash_table->nbuckets; i++) -+ { -+ for (tlist = hash_items (i, var_hash_table); tlist; tlist = tlist->next) -+ { -+ var = (SHELL_VAR *)tlist->data; -+ -+ r = func ? (*func) (var) : 1; -+ if (r && vlist) -+ vlist_add (vlist, var, flags); -+ } -+ } -+} -+ -+void -+sort_variables (array) -+ SHELL_VAR **array; -+{ -+ qsort (array, strvec_len ((char **)array), sizeof (SHELL_VAR *), (QSFUNC *)qsort_var_comp); -+} -+ -+static int -+qsort_var_comp (var1, var2) -+ SHELL_VAR **var1, **var2; -+{ -+ int result; -+ -+ if ((result = (*var1)->name[0] - (*var2)->name[0]) == 0) -+ result = strcmp ((*var1)->name, (*var2)->name); -+ -+ return (result); -+} -+ -+/* Apply FUNC to each variable in SHELL_VARIABLES, adding each one for -+ which FUNC succeeds to an array of SHELL_VAR *s. Returns the array. */ -+static SHELL_VAR ** -+vapply (func) -+ sh_var_map_func_t *func; -+{ -+ SHELL_VAR **list; -+ -+ list = map_over (func, shell_variables); -+ if (list /* && posixly_correct */) -+ sort_variables (list); -+ return (list); -+} -+ -+/* Apply FUNC to each variable in SHELL_FUNCTIONS, adding each one for -+ which FUNC succeeds to an array of SHELL_VAR *s. Returns the array. */ -+static SHELL_VAR ** -+fapply (func) -+ sh_var_map_func_t *func; -+{ -+ SHELL_VAR **list; -+ -+ list = map_over_funcs (func); -+ if (list /* && posixly_correct */) -+ sort_variables (list); -+ return (list); -+} -+ -+/* Create a NULL terminated array of all the shell variables. */ -+SHELL_VAR ** -+all_shell_variables () -+{ -+ return (vapply ((sh_var_map_func_t *)NULL)); -+} -+ -+/* Create a NULL terminated array of all the shell functions. */ -+SHELL_VAR ** -+all_shell_functions () -+{ -+ return (fapply ((sh_var_map_func_t *)NULL)); -+} -+ -+static int -+visible_var (var) -+ SHELL_VAR *var; -+{ -+ return (invisible_p (var) == 0); -+} -+ -+SHELL_VAR ** -+all_visible_functions () -+{ -+ return (fapply (visible_var)); -+} -+ -+SHELL_VAR ** -+all_visible_variables () -+{ -+ return (vapply (visible_var)); -+} -+ -+/* Return non-zero if the variable VAR is visible and exported. Array -+ variables cannot be exported. */ -+static int -+visible_and_exported (var) -+ SHELL_VAR *var; -+{ -+ return (invisible_p (var) == 0 && exported_p (var)); -+} -+ -+/* Candidate variables for the export environment are either valid variables -+ with the export attribute or invalid variables inherited from the initial -+ environment and simply passed through. */ -+static int -+export_environment_candidate (var) -+ SHELL_VAR *var; -+{ -+ return (exported_p (var) && (invisible_p (var) == 0 || imported_p (var))); -+} -+ -+/* Return non-zero if VAR is a local variable in the current context and -+ is exported. */ -+static int -+local_and_exported (var) -+ SHELL_VAR *var; -+{ -+ return (invisible_p (var) == 0 && local_p (var) && var->context == variable_context && exported_p (var)); -+} -+ -+SHELL_VAR ** -+all_exported_variables () -+{ -+ return (vapply (visible_and_exported)); -+} -+ -+SHELL_VAR ** -+local_exported_variables () -+{ -+ return (vapply (local_and_exported)); -+} -+ -+static int -+variable_in_context (var) -+ SHELL_VAR *var; -+{ -+ return (invisible_p (var) == 0 && local_p (var) && var->context == variable_context); -+} -+ -+SHELL_VAR ** -+all_local_variables () -+{ -+ VARLIST *vlist; -+ SHELL_VAR **ret; -+ VAR_CONTEXT *vc; -+ -+ vc = shell_variables; -+ for (vc = shell_variables; vc; vc = vc->down) -+ if (vc_isfuncenv (vc) && vc->scope == variable_context) -+ break; -+ -+ if (vc == 0) -+ { -+ internal_error (_("all_local_variables: no function context at current scope")); -+ return (SHELL_VAR **)NULL; -+ } -+ if (vc->table == 0 || HASH_ENTRIES (vc->table) == 0 || vc_haslocals (vc) == 0) -+ return (SHELL_VAR **)NULL; -+ -+ vlist = vlist_alloc (HASH_ENTRIES (vc->table)); -+ -+ flatten (vc->table, variable_in_context, vlist, 0); -+ -+ ret = vlist->list; -+ free (vlist); -+ if (ret) -+ sort_variables (ret); -+ return ret; -+} -+ -+#if defined (ARRAY_VARS) -+/* Return non-zero if the variable VAR is visible and an array. */ -+static int -+visible_array_vars (var) -+ SHELL_VAR *var; -+{ -+ return (invisible_p (var) == 0 && array_p (var)); -+} -+ -+SHELL_VAR ** -+all_array_variables () -+{ -+ return (vapply (visible_array_vars)); -+} -+#endif /* ARRAY_VARS */ -+ -+char ** -+all_variables_matching_prefix (prefix) -+ const char *prefix; -+{ -+ SHELL_VAR **varlist; -+ char **rlist; -+ int vind, rind, plen; -+ -+ plen = STRLEN (prefix); -+ varlist = all_visible_variables (); -+ for (vind = 0; varlist && varlist[vind]; vind++) -+ ; -+ if (varlist == 0 || vind == 0) -+ return ((char **)NULL); -+ rlist = strvec_create (vind + 1); -+ for (vind = rind = 0; varlist[vind]; vind++) -+ { -+ if (plen == 0 || STREQN (prefix, varlist[vind]->name, plen)) -+ rlist[rind++] = savestring (varlist[vind]->name); -+ } -+ rlist[rind] = (char *)0; -+ free (varlist); -+ -+ return rlist; -+} -+ -+/* **************************************************************** */ -+/* */ -+/* Managing temporary variable scopes */ -+/* */ -+/* **************************************************************** */ -+ -+/* Make variable NAME have VALUE in the temporary environment. */ -+static SHELL_VAR * -+bind_tempenv_variable (name, value) -+ const char *name; -+ char *value; -+{ -+ SHELL_VAR *var; -+ -+ var = temporary_env ? hash_lookup (name, temporary_env) : (SHELL_VAR *)NULL; -+ -+ if (var) -+ { -+ FREE (value_cell (var)); -+ var_setvalue (var, savestring (value)); -+ INVALIDATE_EXPORTSTR (var); -+ } -+ -+ return (var); -+} -+ -+/* Find a variable in the temporary environment that is named NAME. -+ Return the SHELL_VAR *, or NULL if not found. */ -+SHELL_VAR * -+find_tempenv_variable (name) -+ const char *name; -+{ -+ return (temporary_env ? hash_lookup (name, temporary_env) : (SHELL_VAR *)NULL); -+} -+ -+char **tempvar_list; -+int tvlist_ind; -+ -+/* Push the variable described by (SHELL_VAR *)DATA down to the next -+ variable context from the temporary environment. */ -+static void -+push_temp_var (data) -+ PTR_T data; -+{ -+ SHELL_VAR *var, *v; -+ HASH_TABLE *binding_table; -+ -+ var = (SHELL_VAR *)data; -+ -+ binding_table = shell_variables->table; -+ if (binding_table == 0) -+ { -+ if (shell_variables == global_variables) -+ /* shouldn't happen */ -+ binding_table = shell_variables->table = global_variables->table = hash_create (0); -+ else -+ binding_table = shell_variables->table = hash_create (TEMPENV_HASH_BUCKETS); -+ } -+ -+ v = bind_variable_internal (var->name, value_cell (var), binding_table, 0, 0); -+ -+ /* XXX - should we set the context here? It shouldn't matter because of how -+ assign_in_env works, but might want to check. */ -+ if (binding_table == global_variables->table) /* XXX */ -+ var->attributes &= ~(att_tempvar|att_propagate); -+ else -+ { -+ var->attributes |= att_propagate; -+ if (binding_table == shell_variables->table) -+ shell_variables->flags |= VC_HASTMPVAR; -+ } -+ v->attributes |= var->attributes; -+ -+ if (find_special_var (var->name) >= 0) -+ tempvar_list[tvlist_ind++] = savestring (var->name); -+ -+ dispose_variable (var); -+} -+ -+static void -+propagate_temp_var (data) -+ PTR_T data; -+{ -+ SHELL_VAR *var; -+ -+ var = (SHELL_VAR *)data; -+ if (tempvar_p (var) && (var->attributes & att_propagate)) -+ push_temp_var (data); -+ else -+ { -+ if (find_special_var (var->name) >= 0) -+ tempvar_list[tvlist_ind++] = savestring (var->name); -+ dispose_variable (var); -+ } -+} -+ -+/* Free the storage used in the hash table for temporary -+ environment variables. PUSHF is a function to be called -+ to free each hash table entry. It takes care of pushing variables -+ to previous scopes if appropriate. PUSHF stores names of variables -+ that require special handling (e.g., IFS) on tempvar_list, so this -+ function can call stupidly_hack_special_variables on all the -+ variables in the list when the temporary hash table is destroyed. */ -+static void -+dispose_temporary_env (pushf) -+ sh_free_func_t *pushf; -+{ -+ int i; -+ -+ tempvar_list = strvec_create (HASH_ENTRIES (temporary_env) + 1); -+ tempvar_list[tvlist_ind = 0] = 0; -+ -+ hash_flush (temporary_env, pushf); -+ hash_dispose (temporary_env); -+ temporary_env = (HASH_TABLE *)NULL; -+ -+ tempvar_list[tvlist_ind] = 0; -+ -+ array_needs_making = 1; -+ -+#if 0 -+ sv_ifs ("IFS"); /* XXX here for now -- check setifs in assign_in_env */ -+#endif -+ for (i = 0; i < tvlist_ind; i++) -+ stupidly_hack_special_variables (tempvar_list[i]); -+ -+ strvec_dispose (tempvar_list); -+ tempvar_list = 0; -+ tvlist_ind = 0; -+} -+ -+void -+dispose_used_env_vars () -+{ -+ if (temporary_env) -+ { -+ dispose_temporary_env (propagate_temp_var); -+ maybe_make_export_env (); -+ } -+} -+ -+/* Take all of the shell variables in the temporary environment HASH_TABLE -+ and make shell variables from them at the current variable context. */ -+void -+merge_temporary_env () -+{ -+ if (temporary_env) -+ dispose_temporary_env (push_temp_var); -+} -+ -+/* **************************************************************** */ -+/* */ -+/* Creating and manipulating the environment */ -+/* */ -+/* **************************************************************** */ -+ -+static inline char * -+mk_env_string (name, value, isfunc) -+ const char *name, *value; -+ int isfunc; -+{ -+ size_t name_len, value_len; -+ char *p, *q; -+ -+ name_len = strlen (name); -+ value_len = STRLEN (value); -+ -+ /* If we are exporting a shell function, construct the encoded function -+ name. */ -+ if (isfunc && value) -+ { -+ p = (char *)xmalloc (BASHFUNC_PREFLEN + name_len + BASHFUNC_SUFFLEN + value_len + 2); -+ q = p; -+ memcpy (q, BASHFUNC_PREFIX, BASHFUNC_PREFLEN); -+ q += BASHFUNC_PREFLEN; -+ memcpy (q, name, name_len); -+ q += name_len; -+ memcpy (q, BASHFUNC_SUFFIX, BASHFUNC_SUFFLEN); -+ q += BASHFUNC_SUFFLEN; -+ } -+ else -+ { -+ p = (char *)xmalloc (2 + name_len + value_len); -+ memcpy (p, name, name_len); -+ q = p + name_len; -+ } -+ -+ q[0] = '='; -+ if (value && *value) -+ memcpy (q + 1, value, value_len + 1); -+ else -+ q[1] = '\0'; -+ -+ return (p); -+} -+ -+#ifdef DEBUG -+/* Debugging */ -+static int -+valid_exportstr (v) -+ SHELL_VAR *v; -+{ -+ char *s; -+ -+ s = v->exportstr; -+ if (s == 0) -+ { -+ internal_error (_("%s has null exportstr"), v->name); -+ return (0); -+ } -+ if (legal_variable_starter ((unsigned char)*s) == 0) -+ { -+ internal_error (_("invalid character %d in exportstr for %s"), *s, v->name); -+ return (0); -+ } -+ for (s = v->exportstr + 1; s && *s; s++) -+ { -+ if (*s == '=') -+ break; -+ if (legal_variable_char ((unsigned char)*s) == 0) -+ { -+ internal_error (_("invalid character %d in exportstr for %s"), *s, v->name); -+ return (0); -+ } -+ } -+ if (*s != '=') -+ { -+ internal_error (_("no `=' in exportstr for %s"), v->name); -+ return (0); -+ } -+ return (1); -+} -+#endif -+ -+static char ** -+make_env_array_from_var_list (vars) -+ SHELL_VAR **vars; -+{ -+ register int i, list_index; -+ register SHELL_VAR *var; -+ char **list, *value; -+ -+ list = strvec_create ((1 + strvec_len ((char **)vars))); -+ -+#define USE_EXPORTSTR (value == var->exportstr) -+ -+ for (i = 0, list_index = 0; var = vars[i]; i++) -+ { -+#if defined (__CYGWIN__) -+ /* We don't use the exportstr stuff on Cygwin at all. */ -+ INVALIDATE_EXPORTSTR (var); -+#endif -+ if (var->exportstr) -+ value = var->exportstr; -+ else if (function_p (var)) -+ value = named_function_string ((char *)NULL, function_cell (var), 0); -+#if defined (ARRAY_VARS) -+ else if (array_p (var)) -+# if ARRAY_EXPORT -+ value = array_to_assignment_string (array_cell (var)); -+# else -+ continue; /* XXX array vars cannot yet be exported */ -+# endif /* ARRAY_EXPORT */ -+ else if (assoc_p (var)) -+# if 0 -+ value = assoc_to_assignment_string (assoc_cell (var)); -+# else -+ continue; /* XXX associative array vars cannot yet be exported */ -+# endif -+#endif -+ else -+ value = value_cell (var); -+ -+ if (value) -+ { -+ /* Gee, I'd like to get away with not using savestring() if we're -+ using the cached exportstr... */ -+ list[list_index] = USE_EXPORTSTR ? savestring (value) -+ : mk_env_string (var->name, value, function_p (var)); -+ -+ if (USE_EXPORTSTR == 0) -+ SAVE_EXPORTSTR (var, list[list_index]); -+ -+ list_index++; -+#undef USE_EXPORTSTR -+ -+#if 0 /* not yet */ -+#if defined (ARRAY_VARS) -+ if (array_p (var) || assoc_p (var)) -+ free (value); -+#endif -+#endif -+ } -+ } -+ -+ list[list_index] = (char *)NULL; -+ return (list); -+} -+ -+/* Make an array of assignment statements from the hash table -+ HASHED_VARS which contains SHELL_VARs. Only visible, exported -+ variables are eligible. */ -+static char ** -+make_var_export_array (vcxt) -+ VAR_CONTEXT *vcxt; -+{ -+ char **list; -+ SHELL_VAR **vars; -+ -+#if 0 -+ vars = map_over (visible_and_exported, vcxt); -+#else -+ vars = map_over (export_environment_candidate, vcxt); -+#endif -+ -+ if (vars == 0) -+ return (char **)NULL; -+ -+ list = make_env_array_from_var_list (vars); -+ -+ free (vars); -+ return (list); -+} -+ -+static char ** -+make_func_export_array () -+{ -+ char **list; -+ SHELL_VAR **vars; -+ -+ vars = map_over_funcs (visible_and_exported); -+ if (vars == 0) -+ return (char **)NULL; -+ -+ list = make_env_array_from_var_list (vars); -+ -+ free (vars); -+ return (list); -+} -+ -+/* Add ENVSTR to the end of the exported environment, EXPORT_ENV. */ -+#define add_to_export_env(envstr,do_alloc) \ -+do \ -+ { \ -+ if (export_env_index >= (export_env_size - 1)) \ -+ { \ -+ export_env_size += 16; \ -+ export_env = strvec_resize (export_env, export_env_size); \ -+ environ = export_env; \ -+ } \ -+ export_env[export_env_index++] = (do_alloc) ? savestring (envstr) : envstr; \ -+ export_env[export_env_index] = (char *)NULL; \ -+ } while (0) -+ -+/* Add ASSIGN to EXPORT_ENV, or supercede a previous assignment in the -+ array with the same left-hand side. Return the new EXPORT_ENV. */ -+char ** -+add_or_supercede_exported_var (assign, do_alloc) -+ char *assign; -+ int do_alloc; -+{ -+ register int i; -+ int equal_offset; -+ -+ equal_offset = assignment (assign, 0); -+ if (equal_offset == 0) -+ return (export_env); -+ -+ /* If this is a function, then only supersede the function definition. -+ We do this by including the `=() {' in the comparison, like -+ initialize_shell_variables does. */ -+ if (assign[equal_offset + 1] == '(' && -+ strncmp (assign + equal_offset + 2, ") {", 3) == 0) /* } */ -+ equal_offset += 4; -+ -+ for (i = 0; i < export_env_index; i++) -+ { -+ if (STREQN (assign, export_env[i], equal_offset + 1)) -+ { -+ free (export_env[i]); -+ export_env[i] = do_alloc ? savestring (assign) : assign; -+ return (export_env); -+ } -+ } -+ add_to_export_env (assign, do_alloc); -+ return (export_env); -+} -+ -+static void -+add_temp_array_to_env (temp_array, do_alloc, do_supercede) -+ char **temp_array; -+ int do_alloc, do_supercede; -+{ -+ register int i; -+ -+ if (temp_array == 0) -+ return; -+ -+ for (i = 0; temp_array[i]; i++) -+ { -+ if (do_supercede) -+ export_env = add_or_supercede_exported_var (temp_array[i], do_alloc); -+ else -+ add_to_export_env (temp_array[i], do_alloc); -+ } -+ -+ free (temp_array); -+} -+ -+/* Make the environment array for the command about to be executed, if the -+ array needs making. Otherwise, do nothing. If a shell action could -+ change the array that commands receive for their environment, then the -+ code should `array_needs_making++'. -+ -+ The order to add to the array is: -+ temporary_env -+ list of var contexts whose head is shell_variables -+ shell_functions -+ -+ This is the shell variable lookup order. We add only new variable -+ names at each step, which allows local variables and variables in -+ the temporary environments to shadow variables in the global (or -+ any previous) scope. -+*/ -+ -+static int -+n_shell_variables () -+{ -+ VAR_CONTEXT *vc; -+ int n; -+ -+ for (n = 0, vc = shell_variables; vc; vc = vc->down) -+ n += HASH_ENTRIES (vc->table); -+ return n; -+} -+ -+int -+chkexport (name) -+ char *name; -+{ -+ SHELL_VAR *v; -+ -+ v = find_variable (name); -+ if (v && exported_p (v)) -+ { -+ array_needs_making = 1; -+ maybe_make_export_env (); -+ return 1; -+ } -+ return 0; -+} -+ -+void -+maybe_make_export_env () -+{ -+ register char **temp_array; -+ int new_size; -+ VAR_CONTEXT *tcxt; -+ -+ if (array_needs_making) -+ { -+ if (export_env) -+ strvec_flush (export_env); -+ -+ /* Make a guess based on how many shell variables and functions we -+ have. Since there will always be array variables, and array -+ variables are not (yet) exported, this will always be big enough -+ for the exported variables and functions. */ -+ new_size = n_shell_variables () + HASH_ENTRIES (shell_functions) + 1 + -+ HASH_ENTRIES (temporary_env); -+ if (new_size > export_env_size) -+ { -+ export_env_size = new_size; -+ export_env = strvec_resize (export_env, export_env_size); -+ environ = export_env; -+ } -+ export_env[export_env_index = 0] = (char *)NULL; -+ -+ /* Make a dummy variable context from the temporary_env, stick it on -+ the front of shell_variables, call make_var_export_array on the -+ whole thing to flatten it, and convert the list of SHELL_VAR *s -+ to the form needed by the environment. */ -+ if (temporary_env) -+ { -+ tcxt = new_var_context ((char *)NULL, 0); -+ tcxt->table = temporary_env; -+ tcxt->down = shell_variables; -+ } -+ else -+ tcxt = shell_variables; -+ -+ temp_array = make_var_export_array (tcxt); -+ if (temp_array) -+ add_temp_array_to_env (temp_array, 0, 0); -+ -+ if (tcxt != shell_variables) -+ free (tcxt); -+ -+#if defined (RESTRICTED_SHELL) -+ /* Restricted shells may not export shell functions. */ -+ temp_array = restricted ? (char **)0 : make_func_export_array (); -+#else -+ temp_array = make_func_export_array (); -+#endif -+ if (temp_array) -+ add_temp_array_to_env (temp_array, 0, 0); -+ -+ array_needs_making = 0; -+ } -+} -+ -+/* This is an efficiency hack. PWD and OLDPWD are auto-exported, so -+ we will need to remake the exported environment every time we -+ change directories. `_' is always put into the environment for -+ every external command, so without special treatment it will always -+ cause the environment to be remade. -+ -+ If there is no other reason to make the exported environment, we can -+ just update the variables in place and mark the exported environment -+ as no longer needing a remake. */ -+void -+update_export_env_inplace (env_prefix, preflen, value) -+ char *env_prefix; -+ int preflen; -+ char *value; -+{ -+ char *evar; -+ -+ evar = (char *)xmalloc (STRLEN (value) + preflen + 1); -+ strcpy (evar, env_prefix); -+ if (value) -+ strcpy (evar + preflen, value); -+ export_env = add_or_supercede_exported_var (evar, 0); -+} -+ -+/* We always put _ in the environment as the name of this command. */ -+void -+put_command_name_into_env (command_name) -+ char *command_name; -+{ -+ update_export_env_inplace ("_=", 2, command_name); -+} -+ -+/* **************************************************************** */ -+/* */ -+/* Managing variable contexts */ -+/* */ -+/* **************************************************************** */ -+ -+/* Allocate and return a new variable context with NAME and FLAGS. -+ NAME can be NULL. */ -+ -+VAR_CONTEXT * -+new_var_context (name, flags) -+ char *name; -+ int flags; -+{ -+ VAR_CONTEXT *vc; -+ -+ vc = (VAR_CONTEXT *)xmalloc (sizeof (VAR_CONTEXT)); -+ vc->name = name ? savestring (name) : (char *)NULL; -+ vc->scope = variable_context; -+ vc->flags = flags; -+ -+ vc->up = vc->down = (VAR_CONTEXT *)NULL; -+ vc->table = (HASH_TABLE *)NULL; -+ -+ return vc; -+} -+ -+/* Free a variable context and its data, including the hash table. Dispose -+ all of the variables. */ -+void -+dispose_var_context (vc) -+ VAR_CONTEXT *vc; -+{ -+ FREE (vc->name); -+ -+ if (vc->table) -+ { -+ delete_all_variables (vc->table); -+ hash_dispose (vc->table); -+ } -+ -+ free (vc); -+} -+ -+/* Set VAR's scope level to the current variable context. */ -+static int -+set_context (var) -+ SHELL_VAR *var; -+{ -+ return (var->context = variable_context); -+} -+ -+/* Make a new variable context with NAME and FLAGS and a HASH_TABLE of -+ temporary variables, and push it onto shell_variables. This is -+ for shell functions. */ -+VAR_CONTEXT * -+push_var_context (name, flags, tempvars) -+ char *name; -+ int flags; -+ HASH_TABLE *tempvars; -+{ -+ VAR_CONTEXT *vc; -+ -+ vc = new_var_context (name, flags); -+ vc->table = tempvars; -+ if (tempvars) -+ { -+ /* Have to do this because the temp environment was created before -+ variable_context was incremented. */ -+ flatten (tempvars, set_context, (VARLIST *)NULL, 0); -+ vc->flags |= VC_HASTMPVAR; -+ } -+ vc->down = shell_variables; -+ shell_variables->up = vc; -+ -+ return (shell_variables = vc); -+} -+ -+static void -+push_func_var (data) -+ PTR_T data; -+{ -+ SHELL_VAR *var, *v; -+ -+ var = (SHELL_VAR *)data; -+ -+ if (tempvar_p (var) && (posixly_correct || (var->attributes & att_propagate))) -+ { -+ /* Make sure we have a hash table to store the variable in while it is -+ being propagated down to the global variables table. Create one if -+ we have to */ -+ if ((vc_isfuncenv (shell_variables) || vc_istempenv (shell_variables)) && shell_variables->table == 0) -+ shell_variables->table = hash_create (0); -+ /* XXX - should we set v->context here? */ -+ v = bind_variable_internal (var->name, value_cell (var), shell_variables->table, 0, 0); -+ if (shell_variables == global_variables) -+ var->attributes &= ~(att_tempvar|att_propagate); -+ else -+ shell_variables->flags |= VC_HASTMPVAR; -+ v->attributes |= var->attributes; -+ } -+ else -+ stupidly_hack_special_variables (var->name); /* XXX */ -+ -+ dispose_variable (var); -+} -+ -+/* Pop the top context off of VCXT and dispose of it, returning the rest of -+ the stack. */ -+void -+pop_var_context () -+{ -+ VAR_CONTEXT *ret, *vcxt; -+ -+ vcxt = shell_variables; -+ if (vc_isfuncenv (vcxt) == 0) -+ { -+ internal_error (_("pop_var_context: head of shell_variables not a function context")); -+ return; -+ } -+ -+ if (ret = vcxt->down) -+ { -+ ret->up = (VAR_CONTEXT *)NULL; -+ shell_variables = ret; -+ if (vcxt->table) -+ hash_flush (vcxt->table, push_func_var); -+ dispose_var_context (vcxt); -+ } -+ else -+ internal_error (_("pop_var_context: no global_variables context")); -+} -+ -+/* Delete the HASH_TABLEs for all variable contexts beginning at VCXT, and -+ all of the VAR_CONTEXTs except GLOBAL_VARIABLES. */ -+void -+delete_all_contexts (vcxt) -+ VAR_CONTEXT *vcxt; -+{ -+ VAR_CONTEXT *v, *t; -+ -+ for (v = vcxt; v != global_variables; v = t) -+ { -+ t = v->down; -+ dispose_var_context (v); -+ } -+ -+ delete_all_variables (global_variables->table); -+ shell_variables = global_variables; -+} -+ -+/* **************************************************************** */ -+/* */ -+/* Pushing and Popping temporary variable scopes */ -+/* */ -+/* **************************************************************** */ -+ -+VAR_CONTEXT * -+push_scope (flags, tmpvars) -+ int flags; -+ HASH_TABLE *tmpvars; -+{ -+ return (push_var_context ((char *)NULL, flags, tmpvars)); -+} -+ -+static void -+push_exported_var (data) -+ PTR_T data; -+{ -+ SHELL_VAR *var, *v; -+ -+ var = (SHELL_VAR *)data; -+ -+ /* If a temp var had its export attribute set, or it's marked to be -+ propagated, bind it in the previous scope before disposing it. */ -+ /* XXX - This isn't exactly right, because all tempenv variables have the -+ export attribute set. */ -+#if 0 -+ if (exported_p (var) || (var->attributes & att_propagate)) -+#else -+ if (tempvar_p (var) && exported_p (var) && (var->attributes & att_propagate)) -+#endif -+ { -+ var->attributes &= ~att_tempvar; /* XXX */ -+ v = bind_variable_internal (var->name, value_cell (var), shell_variables->table, 0, 0); -+ if (shell_variables == global_variables) -+ var->attributes &= ~att_propagate; -+ v->attributes |= var->attributes; -+ } -+ else -+ stupidly_hack_special_variables (var->name); /* XXX */ -+ -+ dispose_variable (var); -+} -+ -+void -+pop_scope (is_special) -+ int is_special; -+{ -+ VAR_CONTEXT *vcxt, *ret; -+ -+ vcxt = shell_variables; -+ if (vc_istempscope (vcxt) == 0) -+ { -+ internal_error (_("pop_scope: head of shell_variables not a temporary environment scope")); -+ return; -+ } -+ -+ ret = vcxt->down; -+ if (ret) -+ ret->up = (VAR_CONTEXT *)NULL; -+ -+ shell_variables = ret; -+ -+ /* Now we can take care of merging variables in VCXT into set of scopes -+ whose head is RET (shell_variables). */ -+ FREE (vcxt->name); -+ if (vcxt->table) -+ { -+ if (is_special) -+ hash_flush (vcxt->table, push_func_var); -+ else -+ hash_flush (vcxt->table, push_exported_var); -+ hash_dispose (vcxt->table); -+ } -+ free (vcxt); -+ -+ sv_ifs ("IFS"); /* XXX here for now */ -+} -+ -+/* **************************************************************** */ -+/* */ -+/* Pushing and Popping function contexts */ -+/* */ -+/* **************************************************************** */ -+ -+static WORD_LIST **dollar_arg_stack = (WORD_LIST **)NULL; -+static int dollar_arg_stack_slots; -+static int dollar_arg_stack_index; -+ -+/* XXX - we might want to consider pushing and popping the `getopts' state -+ when we modify the positional parameters. */ -+void -+push_context (name, is_subshell, tempvars) -+ char *name; /* function name */ -+ int is_subshell; -+ HASH_TABLE *tempvars; -+{ -+ if (is_subshell == 0) -+ push_dollar_vars (); -+ variable_context++; -+ push_var_context (name, VC_FUNCENV, tempvars); -+} -+ -+/* Only called when subshell == 0, so we don't need to check, and can -+ unconditionally pop the dollar vars off the stack. */ -+void -+pop_context () -+{ -+ pop_dollar_vars (); -+ variable_context--; -+ pop_var_context (); -+ -+ sv_ifs ("IFS"); /* XXX here for now */ -+} -+ -+/* Save the existing positional parameters on a stack. */ -+void -+push_dollar_vars () -+{ -+ if (dollar_arg_stack_index + 2 > dollar_arg_stack_slots) -+ { -+ dollar_arg_stack = (WORD_LIST **) -+ xrealloc (dollar_arg_stack, (dollar_arg_stack_slots += 10) -+ * sizeof (WORD_LIST *)); -+ } -+ dollar_arg_stack[dollar_arg_stack_index++] = list_rest_of_args (); -+ dollar_arg_stack[dollar_arg_stack_index] = (WORD_LIST *)NULL; -+} -+ -+/* Restore the positional parameters from our stack. */ -+void -+pop_dollar_vars () -+{ -+ if (!dollar_arg_stack || dollar_arg_stack_index == 0) -+ return; -+ -+ remember_args (dollar_arg_stack[--dollar_arg_stack_index], 1); -+ dispose_words (dollar_arg_stack[dollar_arg_stack_index]); -+ dollar_arg_stack[dollar_arg_stack_index] = (WORD_LIST *)NULL; -+ set_dollar_vars_unchanged (); -+} -+ -+void -+dispose_saved_dollar_vars () -+{ -+ if (!dollar_arg_stack || dollar_arg_stack_index == 0) -+ return; -+ -+ dispose_words (dollar_arg_stack[dollar_arg_stack_index]); -+ dollar_arg_stack[dollar_arg_stack_index] = (WORD_LIST *)NULL; -+} -+ -+/* Manipulate the special BASH_ARGV and BASH_ARGC variables. */ -+ -+void -+push_args (list) -+ WORD_LIST *list; -+{ -+#if defined (ARRAY_VARS) && defined (DEBUGGER) -+ SHELL_VAR *bash_argv_v, *bash_argc_v; -+ ARRAY *bash_argv_a, *bash_argc_a; -+ WORD_LIST *l; -+ arrayind_t i; -+ char *t; -+ -+ GET_ARRAY_FROM_VAR ("BASH_ARGV", bash_argv_v, bash_argv_a); -+ GET_ARRAY_FROM_VAR ("BASH_ARGC", bash_argc_v, bash_argc_a); -+ -+ for (l = list, i = 0; l; l = l->next, i++) -+ array_push (bash_argv_a, l->word->word); -+ -+ t = itos (i); -+ array_push (bash_argc_a, t); -+ free (t); -+#endif /* ARRAY_VARS && DEBUGGER */ -+} -+ -+/* Remove arguments from BASH_ARGV array. Pop top element off BASH_ARGC -+ array and use that value as the count of elements to remove from -+ BASH_ARGV. */ -+void -+pop_args () -+{ -+#if defined (ARRAY_VARS) && defined (DEBUGGER) -+ SHELL_VAR *bash_argv_v, *bash_argc_v; -+ ARRAY *bash_argv_a, *bash_argc_a; -+ ARRAY_ELEMENT *ce; -+ intmax_t i; -+ -+ GET_ARRAY_FROM_VAR ("BASH_ARGV", bash_argv_v, bash_argv_a); -+ GET_ARRAY_FROM_VAR ("BASH_ARGC", bash_argc_v, bash_argc_a); -+ -+ ce = array_shift (bash_argc_a, 1, 0); -+ if (ce == 0 || legal_number (element_value (ce), &i) == 0) -+ i = 0; -+ -+ for ( ; i > 0; i--) -+ array_pop (bash_argv_a); -+ array_dispose_element (ce); -+#endif /* ARRAY_VARS && DEBUGGER */ -+} -+ -+/************************************************* -+ * * -+ * Functions to manage special variables * -+ * * -+ *************************************************/ -+ -+/* Extern declarations for variables this code has to manage. */ -+extern int eof_encountered, eof_encountered_limit, ignoreeof; -+ -+#if defined (READLINE) -+extern int hostname_list_initialized; -+#endif -+ -+/* An alist of name.function for each special variable. Most of the -+ functions don't do much, and in fact, this would be faster with a -+ switch statement, but by the end of this file, I am sick of switch -+ statements. */ -+ -+#define SET_INT_VAR(name, intvar) intvar = find_variable (name) != 0 -+ -+/* This table will be sorted with qsort() the first time it's accessed. */ -+struct name_and_function { -+ char *name; -+ sh_sv_func_t *function; -+}; -+ -+static struct name_and_function special_vars[] = { -+ { "BASH_COMPAT", sv_shcompat }, -+ { "BASH_XTRACEFD", sv_xtracefd }, -+ -+#if defined (JOB_CONTROL) -+ { "CHILD_MAX", sv_childmax }, -+#endif -+ -+#if defined (READLINE) -+# if defined (STRICT_POSIX) -+ { "COLUMNS", sv_winsize }, -+# endif -+ { "COMP_WORDBREAKS", sv_comp_wordbreaks }, -+#endif -+ -+ { "FUNCNEST", sv_funcnest }, -+ -+ { "GLOBIGNORE", sv_globignore }, -+ -+#if defined (HISTORY) -+ { "HISTCONTROL", sv_history_control }, -+ { "HISTFILESIZE", sv_histsize }, -+ { "HISTIGNORE", sv_histignore }, -+ { "HISTSIZE", sv_histsize }, -+ { "HISTTIMEFORMAT", sv_histtimefmt }, -+#endif -+ -+#if defined (__CYGWIN__) -+ { "HOME", sv_home }, -+#endif -+ -+#if defined (READLINE) -+ { "HOSTFILE", sv_hostfile }, -+#endif -+ -+ { "IFS", sv_ifs }, -+ { "IGNOREEOF", sv_ignoreeof }, -+ -+ { "LANG", sv_locale }, -+ { "LC_ALL", sv_locale }, -+ { "LC_COLLATE", sv_locale }, -+ { "LC_CTYPE", sv_locale }, -+ { "LC_MESSAGES", sv_locale }, -+ { "LC_NUMERIC", sv_locale }, -+ { "LC_TIME", sv_locale }, -+ -+#if defined (READLINE) && defined (STRICT_POSIX) -+ { "LINES", sv_winsize }, -+#endif -+ -+ { "MAIL", sv_mail }, -+ { "MAILCHECK", sv_mail }, -+ { "MAILPATH", sv_mail }, -+ -+ { "OPTERR", sv_opterr }, -+ { "OPTIND", sv_optind }, -+ -+ { "PATH", sv_path }, -+ { "POSIXLY_CORRECT", sv_strict_posix }, -+ -+#if defined (READLINE) -+ { "TERM", sv_terminal }, -+ { "TERMCAP", sv_terminal }, -+ { "TERMINFO", sv_terminal }, -+#endif /* READLINE */ -+ -+ { "TEXTDOMAIN", sv_locale }, -+ { "TEXTDOMAINDIR", sv_locale }, -+ -+#if defined (HAVE_TZSET) -+ { "TZ", sv_tz }, -+#endif -+ -+#if defined (HISTORY) && defined (BANG_HISTORY) -+ { "histchars", sv_histchars }, -+#endif /* HISTORY && BANG_HISTORY */ -+ -+ { "ignoreeof", sv_ignoreeof }, -+ -+ { (char *)0, (sh_sv_func_t *)0 } -+}; -+ -+#define N_SPECIAL_VARS (sizeof (special_vars) / sizeof (special_vars[0]) - 1) -+ -+static int -+sv_compare (sv1, sv2) -+ struct name_and_function *sv1, *sv2; -+{ -+ int r; -+ -+ if ((r = sv1->name[0] - sv2->name[0]) == 0) -+ r = strcmp (sv1->name, sv2->name); -+ return r; -+} -+ -+static inline int -+find_special_var (name) -+ const char *name; -+{ -+ register int i, r; -+ -+ for (i = 0; special_vars[i].name; i++) -+ { -+ r = special_vars[i].name[0] - name[0]; -+ if (r == 0) -+ r = strcmp (special_vars[i].name, name); -+ if (r == 0) -+ return i; -+ else if (r > 0) -+ /* Can't match any of rest of elements in sorted list. Take this out -+ if it causes problems in certain environments. */ -+ break; -+ } -+ return -1; -+} -+ -+/* The variable in NAME has just had its state changed. Check to see if it -+ is one of the special ones where something special happens. */ -+void -+stupidly_hack_special_variables (name) -+ char *name; -+{ -+ static int sv_sorted = 0; -+ int i; -+ -+ if (sv_sorted == 0) /* shouldn't need, but it's fairly cheap. */ -+ { -+ qsort (special_vars, N_SPECIAL_VARS, sizeof (special_vars[0]), -+ (QSFUNC *)sv_compare); -+ sv_sorted = 1; -+ } -+ -+ i = find_special_var (name); -+ if (i != -1) -+ (*(special_vars[i].function)) (name); -+} -+ -+/* Special variables that need hooks to be run when they are unset as part -+ of shell reinitialization should have their sv_ functions run here. */ -+void -+reinit_special_variables () -+{ -+#if defined (READLINE) -+ sv_comp_wordbreaks ("COMP_WORDBREAKS"); -+#endif -+ sv_globignore ("GLOBIGNORE"); -+ sv_opterr ("OPTERR"); -+} -+ -+void -+sv_ifs (name) -+ char *name; -+{ -+ SHELL_VAR *v; -+ -+ v = find_variable ("IFS"); -+ setifs (v); -+} -+ -+/* What to do just after the PATH variable has changed. */ -+void -+sv_path (name) -+ char *name; -+{ -+ /* hash -r */ -+ phash_flush (); -+} -+ -+/* What to do just after one of the MAILxxxx variables has changed. NAME -+ is the name of the variable. This is called with NAME set to one of -+ MAIL, MAILCHECK, or MAILPATH. */ -+void -+sv_mail (name) -+ char *name; -+{ -+ /* If the time interval for checking the files has changed, then -+ reset the mail timer. Otherwise, one of the pathname vars -+ to the users mailbox has changed, so rebuild the array of -+ filenames. */ -+ if (name[4] == 'C') /* if (strcmp (name, "MAILCHECK") == 0) */ -+ reset_mail_timer (); -+ else -+ { -+ free_mail_files (); -+ remember_mail_dates (); -+ } -+} -+ -+void -+sv_funcnest (name) -+ char *name; -+{ -+ SHELL_VAR *v; -+ intmax_t num; -+ -+ v = find_variable (name); -+ if (v == 0) -+ funcnest_max = 0; -+ else if (legal_number (value_cell (v), &num) == 0) -+ funcnest_max = 0; -+ else -+ funcnest_max = num; -+} -+ -+/* What to do when GLOBIGNORE changes. */ -+void -+sv_globignore (name) -+ char *name; -+{ -+ if (privileged_mode == 0) -+ setup_glob_ignore (name); -+} -+ -+#if defined (READLINE) -+void -+sv_comp_wordbreaks (name) -+ char *name; -+{ -+ SHELL_VAR *sv; -+ -+ sv = find_variable (name); -+ if (sv == 0) -+ reset_completer_word_break_chars (); -+} -+ -+/* What to do just after one of the TERMxxx variables has changed. -+ If we are an interactive shell, then try to reset the terminal -+ information in readline. */ -+void -+sv_terminal (name) -+ char *name; -+{ -+ if (interactive_shell && no_line_editing == 0) -+ rl_reset_terminal (get_string_value ("TERM")); -+} -+ -+void -+sv_hostfile (name) -+ char *name; -+{ -+ SHELL_VAR *v; -+ -+ v = find_variable (name); -+ if (v == 0) -+ clear_hostname_list (); -+ else -+ hostname_list_initialized = 0; -+} -+ -+#if defined (STRICT_POSIX) -+/* In strict posix mode, we allow assignments to LINES and COLUMNS (and values -+ found in the initial environment) to override the terminal size reported by -+ the kernel. */ -+void -+sv_winsize (name) -+ char *name; -+{ -+ SHELL_VAR *v; -+ intmax_t xd; -+ int d; -+ -+ if (posixly_correct == 0 || interactive_shell == 0 || no_line_editing) -+ return; -+ -+ v = find_variable (name); -+ if (v == 0 || var_isnull (v)) -+ rl_reset_screen_size (); -+ else -+ { -+ if (legal_number (value_cell (v), &xd) == 0) -+ return; -+ winsize_assignment = 1; -+ d = xd; /* truncate */ -+ if (name[0] == 'L') /* LINES */ -+ rl_set_screen_size (d, -1); -+ else /* COLUMNS */ -+ rl_set_screen_size (-1, d); -+ winsize_assignment = 0; -+ } -+} -+#endif /* STRICT_POSIX */ -+#endif /* READLINE */ -+ -+/* Update the value of HOME in the export environment so tilde expansion will -+ work on cygwin. */ -+#if defined (__CYGWIN__) -+sv_home (name) -+ char *name; -+{ -+ array_needs_making = 1; -+ maybe_make_export_env (); -+} -+#endif -+ -+#if defined (HISTORY) -+/* What to do after the HISTSIZE or HISTFILESIZE variables change. -+ If there is a value for this HISTSIZE (and it is numeric), then stifle -+ the history. Otherwise, if there is NO value for this variable, -+ unstifle the history. If name is HISTFILESIZE, and its value is -+ numeric, truncate the history file to hold no more than that many -+ lines. */ -+void -+sv_histsize (name) -+ char *name; -+{ -+ char *temp; -+ intmax_t num; -+ int hmax; -+ -+ temp = get_string_value (name); -+ -+ if (temp && *temp) -+ { -+ if (legal_number (temp, &num)) -+ { -+ hmax = num; -+ if (hmax < 0 && name[4] == 'S') -+ unstifle_history (); /* unstifle history if HISTSIZE < 0 */ -+ else if (name[4] == 'S') -+ { -+ stifle_history (hmax); -+ hmax = where_history (); -+ if (history_lines_this_session > hmax) -+ history_lines_this_session = hmax; -+ } -+ else if (hmax >= 0) /* truncate HISTFILE if HISTFILESIZE >= 0 */ -+ { -+ history_truncate_file (get_string_value ("HISTFILE"), hmax); -+ if (hmax <= history_lines_in_file) -+ history_lines_in_file = hmax; -+ } -+ } -+ } -+ else if (name[4] == 'S') -+ unstifle_history (); -+} -+ -+/* What to do after the HISTIGNORE variable changes. */ -+void -+sv_histignore (name) -+ char *name; -+{ -+ setup_history_ignore (name); -+} -+ -+/* What to do after the HISTCONTROL variable changes. */ -+void -+sv_history_control (name) -+ char *name; -+{ -+ char *temp; -+ char *val; -+ int tptr; -+ -+ history_control = 0; -+ temp = get_string_value (name); -+ -+ if (temp == 0 || *temp == 0) -+ return; -+ -+ tptr = 0; -+ while (val = extract_colon_unit (temp, &tptr)) -+ { -+ if (STREQ (val, "ignorespace")) -+ history_control |= HC_IGNSPACE; -+ else if (STREQ (val, "ignoredups")) -+ history_control |= HC_IGNDUPS; -+ else if (STREQ (val, "ignoreboth")) -+ history_control |= HC_IGNBOTH; -+ else if (STREQ (val, "erasedups")) -+ history_control |= HC_ERASEDUPS; -+ -+ free (val); -+ } -+} -+ -+#if defined (BANG_HISTORY) -+/* Setting/unsetting of the history expansion character. */ -+void -+sv_histchars (name) -+ char *name; -+{ -+ char *temp; -+ -+ temp = get_string_value (name); -+ if (temp) -+ { -+ history_expansion_char = *temp; -+ if (temp[0] && temp[1]) -+ { -+ history_subst_char = temp[1]; -+ if (temp[2]) -+ history_comment_char = temp[2]; -+ } -+ } -+ else -+ { -+ history_expansion_char = '!'; -+ history_subst_char = '^'; -+ history_comment_char = '#'; -+ } -+} -+#endif /* BANG_HISTORY */ -+ -+void -+sv_histtimefmt (name) -+ char *name; -+{ -+ SHELL_VAR *v; -+ -+ if (v = find_variable (name)) -+ { -+ if (history_comment_char == 0) -+ history_comment_char = '#'; -+ } -+ history_write_timestamps = (v != 0); -+} -+#endif /* HISTORY */ -+ -+#if defined (HAVE_TZSET) -+void -+sv_tz (name) -+ char *name; -+{ -+ if (chkexport (name)) -+ tzset (); -+} -+#endif -+ -+/* If the variable exists, then the value of it can be the number -+ of times we actually ignore the EOF. The default is small, -+ (smaller than csh, anyway). */ -+void -+sv_ignoreeof (name) -+ char *name; -+{ -+ SHELL_VAR *tmp_var; -+ char *temp; -+ -+ eof_encountered = 0; -+ -+ tmp_var = find_variable (name); -+ ignoreeof = tmp_var != 0; -+ temp = tmp_var ? value_cell (tmp_var) : (char *)NULL; -+ if (temp) -+ eof_encountered_limit = (*temp && all_digits (temp)) ? atoi (temp) : 10; -+ set_shellopts (); /* make sure `ignoreeof' is/is not in $SHELLOPTS */ -+} -+ -+void -+sv_optind (name) -+ char *name; -+{ -+ char *tt; -+ int s; -+ -+ tt = get_string_value ("OPTIND"); -+ if (tt && *tt) -+ { -+ s = atoi (tt); -+ -+ /* According to POSIX, setting OPTIND=1 resets the internal state -+ of getopt (). */ -+ if (s < 0 || s == 1) -+ s = 0; -+ } -+ else -+ s = 0; -+ getopts_reset (s); -+} -+ -+void -+sv_opterr (name) -+ char *name; -+{ -+ char *tt; -+ -+ tt = get_string_value ("OPTERR"); -+ sh_opterr = (tt && *tt) ? atoi (tt) : 1; -+} -+ -+void -+sv_strict_posix (name) -+ char *name; -+{ -+ SET_INT_VAR (name, posixly_correct); -+ posix_initialize (posixly_correct); -+#if defined (READLINE) -+ if (interactive_shell) -+ posix_readline_initialize (posixly_correct); -+#endif /* READLINE */ -+ set_shellopts (); /* make sure `posix' is/is not in $SHELLOPTS */ -+} -+ -+void -+sv_locale (name) -+ char *name; -+{ -+ char *v; -+ int r; -+ -+ v = get_string_value (name); -+ if (name[0] == 'L' && name[1] == 'A') /* LANG */ -+ r = set_lang (name, v); -+ else -+ r = set_locale_var (name, v); /* LC_*, TEXTDOMAIN* */ -+ -+#if 1 -+ if (r == 0 && posixly_correct) -+ last_command_exit_value = 1; -+#endif -+} -+ -+#if defined (ARRAY_VARS) -+void -+set_pipestatus_array (ps, nproc) -+ int *ps; -+ int nproc; -+{ -+ SHELL_VAR *v; -+ ARRAY *a; -+ ARRAY_ELEMENT *ae; -+ register int i; -+ char *t, tbuf[INT_STRLEN_BOUND(int) + 1]; -+ -+ v = find_variable ("PIPESTATUS"); -+ if (v == 0) -+ v = make_new_array_variable ("PIPESTATUS"); -+ if (array_p (v) == 0) -+ return; /* Do nothing if not an array variable. */ -+ a = array_cell (v); -+ -+ if (a == 0 || array_num_elements (a) == 0) -+ { -+ for (i = 0; i < nproc; i++) /* was ps[i] != -1, not i < nproc */ -+ { -+ t = inttostr (ps[i], tbuf, sizeof (tbuf)); -+ array_insert (a, i, t); -+ } -+ return; -+ } -+ -+ /* Fast case */ -+ if (array_num_elements (a) == nproc && nproc == 1) -+ { -+ ae = element_forw (a->head); -+ free (element_value (ae)); -+ ae->value = itos (ps[0]); -+ } -+ else if (array_num_elements (a) <= nproc) -+ { -+ /* modify in array_num_elements members in place, then add */ -+ ae = a->head; -+ for (i = 0; i < array_num_elements (a); i++) -+ { -+ ae = element_forw (ae); -+ free (element_value (ae)); -+ ae->value = itos (ps[i]); -+ } -+ /* add any more */ -+ for ( ; i < nproc; i++) -+ { -+ t = inttostr (ps[i], tbuf, sizeof (tbuf)); -+ array_insert (a, i, t); -+ } -+ } -+ else -+ { -+ /* deleting elements. it's faster to rebuild the array. */ -+ array_flush (a); -+ for (i = 0; ps[i] != -1; i++) -+ { -+ t = inttostr (ps[i], tbuf, sizeof (tbuf)); -+ array_insert (a, i, t); -+ } -+ } -+} -+ -+ARRAY * -+save_pipestatus_array () -+{ -+ SHELL_VAR *v; -+ ARRAY *a, *a2; -+ -+ v = find_variable ("PIPESTATUS"); -+ if (v == 0 || array_p (v) == 0 || array_cell (v) == 0) -+ return ((ARRAY *)NULL); -+ -+ a = array_cell (v); -+ a2 = array_copy (array_cell (v)); -+ -+ return a2; -+} -+ -+void -+restore_pipestatus_array (a) -+ ARRAY *a; -+{ -+ SHELL_VAR *v; -+ ARRAY *a2; -+ -+ v = find_variable ("PIPESTATUS"); -+ /* XXX - should we still assign even if existing value is NULL? */ -+ if (v == 0 || array_p (v) == 0 || array_cell (v) == 0) -+ return; -+ -+ a2 = array_cell (v); -+ var_setarray (v, a); -+ -+ array_dispose (a2); -+} -+#endif -+ -+void -+set_pipestatus_from_exit (s) -+ int s; -+{ -+#if defined (ARRAY_VARS) -+ static int v[2] = { 0, -1 }; -+ -+ v[0] = s; -+ set_pipestatus_array (v, 1); -+#endif -+} -+ -+void -+sv_xtracefd (name) -+ char *name; -+{ -+ SHELL_VAR *v; -+ char *t, *e; -+ int fd; -+ FILE *fp; -+ -+ v = find_variable (name); -+ if (v == 0) -+ { -+ xtrace_reset (); -+ return; -+ } -+ -+ t = value_cell (v); -+ if (t == 0 || *t == 0) -+ xtrace_reset (); -+ else -+ { -+ fd = (int)strtol (t, &e, 10); -+ if (e != t && *e == '\0' && sh_validfd (fd)) -+ { -+ fp = fdopen (fd, "w"); -+ if (fp == 0) -+ internal_error (_("%s: %s: cannot open as FILE"), name, value_cell (v)); -+ else -+ xtrace_set (fd, fp); -+ } -+ else -+ internal_error (_("%s: %s: invalid value for trace file descriptor"), name, value_cell (v)); -+ } -+} -+ -+#define MIN_COMPAT_LEVEL 31 -+ -+void -+sv_shcompat (name) -+ char *name; -+{ -+ SHELL_VAR *v; -+ char *val; -+ int tens, ones, compatval; -+ -+ v = find_variable (name); -+ if (v == 0) -+ { -+ shell_compatibility_level = DEFAULT_COMPAT_LEVEL; -+ set_compatibility_opts (); -+ return; -+ } -+ val = value_cell (v); -+ if (val == 0 || *val == '\0') -+ { -+ shell_compatibility_level = DEFAULT_COMPAT_LEVEL; -+ set_compatibility_opts (); -+ return; -+ } -+ /* Handle decimal-like compatibility version specifications: 4.2 */ -+ if (isdigit (val[0]) && val[1] == '.' && isdigit (val[2]) && val[3] == 0) -+ { -+ tens = val[0] - '0'; -+ ones = val[2] - '0'; -+ compatval = tens*10 + ones; -+ } -+ /* Handle integer-like compatibility version specifications: 42 */ -+ else if (isdigit (val[0]) && isdigit (val[1]) && val[2] == 0) -+ { -+ tens = val[0] - '0'; -+ ones = val[1] - '0'; -+ compatval = tens*10 + ones; -+ } -+ else -+ { -+compat_error: -+ internal_error (_("%s: %s: compatibility value out of range"), name, val); -+ shell_compatibility_level = DEFAULT_COMPAT_LEVEL; -+ set_compatibility_opts (); -+ return; -+ } -+ -+ if (compatval < MIN_COMPAT_LEVEL || compatval > DEFAULT_COMPAT_LEVEL) -+ goto compat_error; -+ -+ shell_compatibility_level = compatval; -+ set_compatibility_opts (); -+} -+ -+#if defined (JOB_CONTROL) -+void -+sv_childmax (name) -+ char *name; -+{ -+ char *tt; -+ int s; -+ -+ tt = get_string_value (name); -+ s = (tt && *tt) ? atoi (tt) : 0; -+ set_maxchild (s); -+} -+#endif diff --git a/patches/bash-4.3.30/0002-Bash-4.3-patch-32.patch b/patches/bash-4.3.30/0002-Bash-4.3-patch-32.patch deleted file mode 100644 index 801b4a609..000000000 --- a/patches/bash-4.3.30/0002-Bash-4.3-patch-32.patch +++ /dev/null @@ -1,5409 +0,0 @@ -From: Chet Ramey -Date: Thu, 15 Jan 2015 10:20:45 -0500 -Subject: [PATCH] Bash-4.3 patch 32 - ---- - jobs.c | 4 +- - patchlevel.h | 2 +- - variables.c.orig | 5365 ------------------------------------------------------ - 3 files changed, 4 insertions(+), 5367 deletions(-) - delete mode 100644 variables.c.orig - -diff --git a/jobs.c b/jobs.c -index f38b0c3f4446..b6e59eba0de8 100644 ---- a/jobs.c -+++ b/jobs.c -@@ -3339,7 +3339,9 @@ itrace("waitchld: waitpid returns %d block = %d", pid, block); - if (posixly_correct && this_shell_builtin && this_shell_builtin == wait_builtin) - { - interrupt_immediately = 0; -- trap_handler (SIGCHLD); /* set pending_traps[SIGCHLD] */ -+ /* This was trap_handler (SIGCHLD) but that can lose traps if -+ children_exited > 1 */ -+ queue_sigchld_trap (children_exited); - wait_signal_received = SIGCHLD; - /* If we're in a signal handler, let CHECK_WAIT_INTR pick it up; - run_pending_traps will call run_sigchld_trap later */ -diff --git a/patchlevel.h b/patchlevel.h -index 0ad46aafbdd9..b8bf38704ed2 100644 ---- a/patchlevel.h -+++ b/patchlevel.h -@@ -25,6 +25,6 @@ - regexp `^#define[ ]*PATCHLEVEL', since that's what support/mkversion.sh - looks for to find the patch level (for the sccs version string). */ - --#define PATCHLEVEL 31 -+#define PATCHLEVEL 32 - - #endif /* _PATCHLEVEL_H_ */ -diff --git a/variables.c.orig b/variables.c.orig -deleted file mode 100644 -index 7c82710e0f0b..000000000000 ---- a/variables.c.orig -+++ /dev/null -@@ -1,5365 +0,0 @@ --/* variables.c -- Functions for hacking shell variables. */ -- --/* Copyright (C) 1987-2013 Free Software Foundation, Inc. -- -- This file is part of GNU Bash, the Bourne Again SHell. -- -- Bash is free software: you can redistribute it and/or modify -- it under the terms of the GNU General Public License as published by -- the Free Software Foundation, either version 3 of the License, or -- (at your option) any later version. -- -- Bash is distributed in the hope that it will be useful, -- but WITHOUT ANY WARRANTY; without even the implied warranty of -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -- GNU General Public License for more details. -- -- You should have received a copy of the GNU General Public License -- along with Bash. If not, see . --*/ -- --#include "config.h" -- --#include "bashtypes.h" --#include "posixstat.h" --#include "posixtime.h" -- --#if defined (__QNX__) --# if defined (__QNXNTO__) --# include --# else --# include --# endif /* !__QNXNTO__ */ --#endif /* __QNX__ */ -- --#if defined (HAVE_UNISTD_H) --# include --#endif -- --#include --#include "chartypes.h" --#if defined (HAVE_PWD_H) --# include --#endif --#include "bashansi.h" --#include "bashintl.h" -- --#define NEED_XTRACE_SET_DECL -- --#include "shell.h" --#include "flags.h" --#include "execute_cmd.h" --#include "findcmd.h" --#include "mailcheck.h" --#include "input.h" --#include "hashcmd.h" --#include "pathexp.h" --#include "alias.h" --#include "jobs.h" -- --#include "version.h" -- --#include "builtins/getopt.h" --#include "builtins/common.h" --#include "builtins/builtext.h" -- --#if defined (READLINE) --# include "bashline.h" --# include --#else --# include --#endif -- --#if defined (HISTORY) --# include "bashhist.h" --# include --#endif /* HISTORY */ -- --#if defined (PROGRAMMABLE_COMPLETION) --# include "pcomplete.h" --#endif -- --#define TEMPENV_HASH_BUCKETS 4 /* must be power of two */ -- --#define ifsname(s) ((s)[0] == 'I' && (s)[1] == 'F' && (s)[2] == 'S' && (s)[3] == '\0') -- --#define BASHFUNC_PREFIX "BASH_FUNC_" --#define BASHFUNC_PREFLEN 10 /* == strlen(BASHFUNC_PREFIX */ --#define BASHFUNC_SUFFIX "%%" --#define BASHFUNC_SUFFLEN 2 /* == strlen(BASHFUNC_SUFFIX) */ -- --extern char **environ; -- --/* Variables used here and defined in other files. */ --extern int posixly_correct; --extern int line_number, line_number_base; --extern int subshell_environment, indirection_level, subshell_level; --extern int build_version, patch_level; --extern int expanding_redir; --extern int last_command_exit_value; --extern char *dist_version, *release_status; --extern char *shell_name; --extern char *primary_prompt, *secondary_prompt; --extern char *current_host_name; --extern sh_builtin_func_t *this_shell_builtin; --extern SHELL_VAR *this_shell_function; --extern char *the_printed_command_except_trap; --extern char *this_command_name; --extern char *command_execution_string; --extern time_t shell_start_time; --extern int assigning_in_environment; --extern int executing_builtin; --extern int funcnest_max; -- --#if defined (READLINE) --extern int no_line_editing; --extern int perform_hostname_completion; --#endif -- --/* The list of shell variables that the user has created at the global -- scope, or that came from the environment. */ --VAR_CONTEXT *global_variables = (VAR_CONTEXT *)NULL; -- --/* The current list of shell variables, including function scopes */ --VAR_CONTEXT *shell_variables = (VAR_CONTEXT *)NULL; -- --/* The list of shell functions that the user has created, or that came from -- the environment. */ --HASH_TABLE *shell_functions = (HASH_TABLE *)NULL; -- --#if defined (DEBUGGER) --/* The table of shell function definitions that the user defined or that -- came from the environment. */ --HASH_TABLE *shell_function_defs = (HASH_TABLE *)NULL; --#endif -- --/* The current variable context. This is really a count of how deep into -- executing functions we are. */ --int variable_context = 0; -- --/* The set of shell assignments which are made only in the environment -- for a single command. */ --HASH_TABLE *temporary_env = (HASH_TABLE *)NULL; -- --/* Set to non-zero if an assignment error occurs while putting variables -- into the temporary environment. */ --int tempenv_assign_error; -- --/* Some funky variables which are known about specially. Here is where -- "$*", "$1", and all the cruft is kept. */ --char *dollar_vars[10]; --WORD_LIST *rest_of_args = (WORD_LIST *)NULL; -- --/* The value of $$. */ --pid_t dollar_dollar_pid; -- --/* Non-zero means that we have to remake EXPORT_ENV. */ --int array_needs_making = 1; -- --/* The number of times BASH has been executed. This is set -- by initialize_variables (). */ --int shell_level = 0; -- --/* An array which is passed to commands as their environment. It is -- manufactured from the union of the initial environment and the -- shell variables that are marked for export. */ --char **export_env = (char **)NULL; --static int export_env_index; --static int export_env_size; -- --#if defined (READLINE) --static int winsize_assignment; /* currently assigning to LINES or COLUMNS */ --#endif -- --static HASH_TABLE *last_table_searched; /* hash_lookup sets this */ -- --/* Some forward declarations. */ --static void create_variable_tables __P((void)); -- --static void set_machine_vars __P((void)); --static void set_home_var __P((void)); --static void set_shell_var __P((void)); --static char *get_bash_name __P((void)); --static void initialize_shell_level __P((void)); --static void uidset __P((void)); --#if defined (ARRAY_VARS) --static void make_vers_array __P((void)); --#endif -- --static SHELL_VAR *null_assign __P((SHELL_VAR *, char *, arrayind_t, char *)); --#if defined (ARRAY_VARS) --static SHELL_VAR *null_array_assign __P((SHELL_VAR *, char *, arrayind_t, char *)); --#endif --static SHELL_VAR *get_self __P((SHELL_VAR *)); -- --#if defined (ARRAY_VARS) --static SHELL_VAR *init_dynamic_array_var __P((char *, sh_var_value_func_t *, sh_var_assign_func_t *, int)); --static SHELL_VAR *init_dynamic_assoc_var __P((char *, sh_var_value_func_t *, sh_var_assign_func_t *, int)); --#endif -- --static SHELL_VAR *assign_seconds __P((SHELL_VAR *, char *, arrayind_t, char *)); --static SHELL_VAR *get_seconds __P((SHELL_VAR *)); --static SHELL_VAR *init_seconds_var __P((void)); -- --static int brand __P((void)); --static void sbrand __P((unsigned long)); /* set bash random number generator. */ --static void seedrand __P((void)); /* seed generator randomly */ --static SHELL_VAR *assign_random __P((SHELL_VAR *, char *, arrayind_t, char *)); --static SHELL_VAR *get_random __P((SHELL_VAR *)); -- --static SHELL_VAR *assign_lineno __P((SHELL_VAR *, char *, arrayind_t, char *)); --static SHELL_VAR *get_lineno __P((SHELL_VAR *)); -- --static SHELL_VAR *assign_subshell __P((SHELL_VAR *, char *, arrayind_t, char *)); --static SHELL_VAR *get_subshell __P((SHELL_VAR *)); -- --static SHELL_VAR *get_bashpid __P((SHELL_VAR *)); -- --#if defined (HISTORY) --static SHELL_VAR *get_histcmd __P((SHELL_VAR *)); --#endif -- --#if defined (READLINE) --static SHELL_VAR *get_comp_wordbreaks __P((SHELL_VAR *)); --static SHELL_VAR *assign_comp_wordbreaks __P((SHELL_VAR *, char *, arrayind_t, char *)); --#endif -- --#if defined (PUSHD_AND_POPD) && defined (ARRAY_VARS) --static SHELL_VAR *assign_dirstack __P((SHELL_VAR *, char *, arrayind_t, char *)); --static SHELL_VAR *get_dirstack __P((SHELL_VAR *)); --#endif -- --#if defined (ARRAY_VARS) --static SHELL_VAR *get_groupset __P((SHELL_VAR *)); -- --static SHELL_VAR *build_hashcmd __P((SHELL_VAR *)); --static SHELL_VAR *get_hashcmd __P((SHELL_VAR *)); --static SHELL_VAR *assign_hashcmd __P((SHELL_VAR *, char *, arrayind_t, char *)); --# if defined (ALIAS) --static SHELL_VAR *build_aliasvar __P((SHELL_VAR *)); --static SHELL_VAR *get_aliasvar __P((SHELL_VAR *)); --static SHELL_VAR *assign_aliasvar __P((SHELL_VAR *, char *, arrayind_t, char *)); --# endif --#endif -- --static SHELL_VAR *get_funcname __P((SHELL_VAR *)); --static SHELL_VAR *init_funcname_var __P((void)); -- --static void initialize_dynamic_variables __P((void)); -- --static SHELL_VAR *hash_lookup __P((const char *, HASH_TABLE *)); --static SHELL_VAR *new_shell_variable __P((const char *)); --static SHELL_VAR *make_new_variable __P((const char *, HASH_TABLE *)); --static SHELL_VAR *bind_variable_internal __P((const char *, char *, HASH_TABLE *, int, int)); -- --static void dispose_variable_value __P((SHELL_VAR *)); --static void free_variable_hash_data __P((PTR_T)); -- --static VARLIST *vlist_alloc __P((int)); --static VARLIST *vlist_realloc __P((VARLIST *, int)); --static void vlist_add __P((VARLIST *, SHELL_VAR *, int)); -- --static void flatten __P((HASH_TABLE *, sh_var_map_func_t *, VARLIST *, int)); -- --static int qsort_var_comp __P((SHELL_VAR **, SHELL_VAR **)); -- --static SHELL_VAR **vapply __P((sh_var_map_func_t *)); --static SHELL_VAR **fapply __P((sh_var_map_func_t *)); -- --static int visible_var __P((SHELL_VAR *)); --static int visible_and_exported __P((SHELL_VAR *)); --static int export_environment_candidate __P((SHELL_VAR *)); --static int local_and_exported __P((SHELL_VAR *)); --static int variable_in_context __P((SHELL_VAR *)); --#if defined (ARRAY_VARS) --static int visible_array_vars __P((SHELL_VAR *)); --#endif -- --static SHELL_VAR *find_nameref_at_context __P((SHELL_VAR *, VAR_CONTEXT *)); --static SHELL_VAR *find_variable_nameref_context __P((SHELL_VAR *, VAR_CONTEXT *, VAR_CONTEXT **)); --static SHELL_VAR *find_variable_last_nameref_context __P((SHELL_VAR *, VAR_CONTEXT *, VAR_CONTEXT **)); -- --static SHELL_VAR *bind_tempenv_variable __P((const char *, char *)); --static void push_temp_var __P((PTR_T)); --static void propagate_temp_var __P((PTR_T)); --static void dispose_temporary_env __P((sh_free_func_t *)); -- --static inline char *mk_env_string __P((const char *, const char *, int)); --static char **make_env_array_from_var_list __P((SHELL_VAR **)); --static char **make_var_export_array __P((VAR_CONTEXT *)); --static char **make_func_export_array __P((void)); --static void add_temp_array_to_env __P((char **, int, int)); -- --static int n_shell_variables __P((void)); --static int set_context __P((SHELL_VAR *)); -- --static void push_func_var __P((PTR_T)); --static void push_exported_var __P((PTR_T)); -- --static inline int find_special_var __P((const char *)); -- --static void --create_variable_tables () --{ -- if (shell_variables == 0) -- { -- shell_variables = global_variables = new_var_context ((char *)NULL, 0); -- shell_variables->scope = 0; -- shell_variables->table = hash_create (0); -- } -- -- if (shell_functions == 0) -- shell_functions = hash_create (0); -- --#if defined (DEBUGGER) -- if (shell_function_defs == 0) -- shell_function_defs = hash_create (0); --#endif --} -- --/* Initialize the shell variables from the current environment. -- If PRIVMODE is nonzero, don't import functions from ENV or -- parse $SHELLOPTS. */ --void --initialize_shell_variables (env, privmode) -- char **env; -- int privmode; --{ -- char *name, *string, *temp_string; -- int c, char_index, string_index, string_length, ro; -- SHELL_VAR *temp_var; -- -- create_variable_tables (); -- -- for (string_index = 0; string = env[string_index++]; ) -- { -- char_index = 0; -- name = string; -- while ((c = *string++) && c != '=') -- ; -- if (string[-1] == '=') -- char_index = string - name - 1; -- -- /* If there are weird things in the environment, like `=xxx' or a -- string without an `=', just skip them. */ -- if (char_index == 0) -- continue; -- -- /* ASSERT(name[char_index] == '=') */ -- name[char_index] = '\0'; -- /* Now, name = env variable name, string = env variable value, and -- char_index == strlen (name) */ -- -- temp_var = (SHELL_VAR *)NULL; -- -- /* If exported function, define it now. Don't import functions from -- the environment in privileged mode. */ -- if (privmode == 0 && read_but_dont_execute == 0 && -- STREQN (BASHFUNC_PREFIX, name, BASHFUNC_PREFLEN) && -- STREQ (BASHFUNC_SUFFIX, name + char_index - BASHFUNC_SUFFLEN) && -- STREQN ("() {", string, 4)) -- { -- size_t namelen; -- char *tname; /* desired imported function name */ -- -- namelen = char_index - BASHFUNC_PREFLEN - BASHFUNC_SUFFLEN; -- -- tname = name + BASHFUNC_PREFLEN; /* start of func name */ -- tname[namelen] = '\0'; /* now tname == func name */ -- -- string_length = strlen (string); -- temp_string = (char *)xmalloc (namelen + string_length + 2); -- -- memcpy (temp_string, tname, namelen); -- temp_string[namelen] = ' '; -- memcpy (temp_string + namelen + 1, string, string_length + 1); -- -- /* Don't import function names that are invalid identifiers from the -- environment, though we still allow them to be defined as shell -- variables. */ -- if (absolute_program (tname) == 0 && (posixly_correct == 0 || legal_identifier (tname))) -- parse_and_execute (temp_string, tname, SEVAL_NONINT|SEVAL_NOHIST|SEVAL_FUNCDEF|SEVAL_ONECMD); -- -- if (temp_var = find_function (tname)) -- { -- VSETATTR (temp_var, (att_exported|att_imported)); -- array_needs_making = 1; -- } -- else -- { -- if (temp_var = bind_variable (name, string, 0)) -- { -- VSETATTR (temp_var, (att_exported | att_imported | att_invisible)); -- array_needs_making = 1; -- } -- last_command_exit_value = 1; -- report_error (_("error importing function definition for `%s'"), tname); -- } -- -- /* Restore original suffix */ -- tname[namelen] = BASHFUNC_SUFFIX[0]; -- } --#if defined (ARRAY_VARS) --# if ARRAY_EXPORT -- /* Array variables may not yet be exported. */ -- else if (*string == '(' && string[1] == '[' && string[strlen (string) - 1] == ')') -- { -- string_length = 1; -- temp_string = extract_array_assignment_list (string, &string_length); -- temp_var = assign_array_from_string (name, temp_string); -- FREE (temp_string); -- VSETATTR (temp_var, (att_exported | att_imported)); -- array_needs_making = 1; -- } --# endif /* ARRAY_EXPORT */ --#endif --#if 0 -- else if (legal_identifier (name)) --#else -- else --#endif -- { -- ro = 0; -- if (posixly_correct && STREQ (name, "SHELLOPTS")) -- { -- temp_var = find_variable ("SHELLOPTS"); -- ro = temp_var && readonly_p (temp_var); -- if (temp_var) -- VUNSETATTR (temp_var, att_readonly); -- } -- temp_var = bind_variable (name, string, 0); -- if (temp_var) -- { -- if (legal_identifier (name)) -- VSETATTR (temp_var, (att_exported | att_imported)); -- else -- VSETATTR (temp_var, (att_exported | att_imported | att_invisible)); -- if (ro) -- VSETATTR (temp_var, att_readonly); -- array_needs_making = 1; -- } -- } -- -- name[char_index] = '='; -- /* temp_var can be NULL if it was an exported function with a syntax -- error (a different bug, but it still shouldn't dump core). */ -- if (temp_var && function_p (temp_var) == 0) /* XXX not yet */ -- { -- CACHE_IMPORTSTR (temp_var, name); -- } -- } -- -- set_pwd (); -- -- /* Set up initial value of $_ */ -- temp_var = set_if_not ("_", dollar_vars[0]); -- -- /* Remember this pid. */ -- dollar_dollar_pid = getpid (); -- -- /* Now make our own defaults in case the vars that we think are -- important are missing. */ -- temp_var = set_if_not ("PATH", DEFAULT_PATH_VALUE); --#if 0 -- set_auto_export (temp_var); /* XXX */ --#endif -- -- temp_var = set_if_not ("TERM", "dumb"); --#if 0 -- set_auto_export (temp_var); /* XXX */ --#endif -- --#if defined (__QNX__) -- /* set node id -- don't import it from the environment */ -- { -- char node_name[22]; --# if defined (__QNXNTO__) -- netmgr_ndtostr(ND2S_LOCAL_STR, ND_LOCAL_NODE, node_name, sizeof(node_name)); --# else -- qnx_nidtostr (getnid (), node_name, sizeof (node_name)); --# endif -- temp_var = bind_variable ("NODE", node_name, 0); -- set_auto_export (temp_var); -- } --#endif -- -- /* set up the prompts. */ -- if (interactive_shell) -- { --#if defined (PROMPT_STRING_DECODE) -- set_if_not ("PS1", primary_prompt); --#else -- if (current_user.uid == -1) -- get_current_user_info (); -- set_if_not ("PS1", current_user.euid == 0 ? "# " : primary_prompt); --#endif -- set_if_not ("PS2", secondary_prompt); -- } -- set_if_not ("PS4", "+ "); -- -- /* Don't allow IFS to be imported from the environment. */ -- temp_var = bind_variable ("IFS", " \t\n", 0); -- setifs (temp_var); -- -- /* Magic machine types. Pretty convenient. */ -- set_machine_vars (); -- -- /* Default MAILCHECK for interactive shells. Defer the creation of a -- default MAILPATH until the startup files are read, because MAIL -- names a mail file if MAILPATH is not set, and we should provide a -- default only if neither is set. */ -- if (interactive_shell) -- { -- temp_var = set_if_not ("MAILCHECK", posixly_correct ? "600" : "60"); -- VSETATTR (temp_var, att_integer); -- } -- -- /* Do some things with shell level. */ -- initialize_shell_level (); -- -- set_ppid (); -- -- /* Initialize the `getopts' stuff. */ -- temp_var = bind_variable ("OPTIND", "1", 0); -- VSETATTR (temp_var, att_integer); -- getopts_reset (0); -- bind_variable ("OPTERR", "1", 0); -- sh_opterr = 1; -- -- if (login_shell == 1 && posixly_correct == 0) -- set_home_var (); -- -- /* Get the full pathname to THIS shell, and set the BASH variable -- to it. */ -- name = get_bash_name (); -- temp_var = bind_variable ("BASH", name, 0); -- free (name); -- -- /* Make the exported environment variable SHELL be the user's login -- shell. Note that the `tset' command looks at this variable -- to determine what style of commands to output; if it ends in "csh", -- then C-shell commands are output, else Bourne shell commands. */ -- set_shell_var (); -- -- /* Make a variable called BASH_VERSION which contains the version info. */ -- bind_variable ("BASH_VERSION", shell_version_string (), 0); --#if defined (ARRAY_VARS) -- make_vers_array (); --#endif -- -- if (command_execution_string) -- bind_variable ("BASH_EXECUTION_STRING", command_execution_string, 0); -- -- /* Find out if we're supposed to be in Posix.2 mode via an -- environment variable. */ -- temp_var = find_variable ("POSIXLY_CORRECT"); -- if (!temp_var) -- temp_var = find_variable ("POSIX_PEDANTIC"); -- if (temp_var && imported_p (temp_var)) -- sv_strict_posix (temp_var->name); -- --#if defined (HISTORY) -- /* Set history variables to defaults, and then do whatever we would -- do if the variable had just been set. Do this only in the case -- that we are remembering commands on the history list. */ -- if (remember_on_history) -- { -- name = bash_tilde_expand (posixly_correct ? "~/.sh_history" : "~/.bash_history", 0); -- -- set_if_not ("HISTFILE", name); -- free (name); -- } --#endif /* HISTORY */ -- -- /* Seed the random number generator. */ -- seedrand (); -- -- /* Handle some "special" variables that we may have inherited from a -- parent shell. */ -- if (interactive_shell) -- { -- temp_var = find_variable ("IGNOREEOF"); -- if (!temp_var) -- temp_var = find_variable ("ignoreeof"); -- if (temp_var && imported_p (temp_var)) -- sv_ignoreeof (temp_var->name); -- } -- --#if defined (HISTORY) -- if (interactive_shell && remember_on_history) -- { -- sv_history_control ("HISTCONTROL"); -- sv_histignore ("HISTIGNORE"); -- sv_histtimefmt ("HISTTIMEFORMAT"); -- } --#endif /* HISTORY */ -- --#if defined (READLINE) && defined (STRICT_POSIX) -- /* POSIXLY_CORRECT will only be 1 here if the shell was compiled -- -DSTRICT_POSIX */ -- if (interactive_shell && posixly_correct && no_line_editing == 0) -- rl_prefer_env_winsize = 1; --#endif /* READLINE && STRICT_POSIX */ -- -- /* -- * 24 October 2001 -- * -- * I'm tired of the arguing and bug reports. Bash now leaves SSH_CLIENT -- * and SSH2_CLIENT alone. I'm going to rely on the shell_level check in -- * isnetconn() to avoid running the startup files more often than wanted. -- * That will, of course, only work if the user's login shell is bash, so -- * I've made that behavior conditional on SSH_SOURCE_BASHRC being defined -- * in config-top.h. -- */ --#if 0 -- temp_var = find_variable ("SSH_CLIENT"); -- if (temp_var && imported_p (temp_var)) -- { -- VUNSETATTR (temp_var, att_exported); -- array_needs_making = 1; -- } -- temp_var = find_variable ("SSH2_CLIENT"); -- if (temp_var && imported_p (temp_var)) -- { -- VUNSETATTR (temp_var, att_exported); -- array_needs_making = 1; -- } --#endif -- -- /* Get the user's real and effective user ids. */ -- uidset (); -- -- temp_var = find_variable ("BASH_XTRACEFD"); -- if (temp_var && imported_p (temp_var)) -- sv_xtracefd (temp_var->name); -- -- /* Initialize the dynamic variables, and seed their values. */ -- initialize_dynamic_variables (); --} -- --/* **************************************************************** */ --/* */ --/* Setting values for special shell variables */ --/* */ --/* **************************************************************** */ -- --static void --set_machine_vars () --{ -- SHELL_VAR *temp_var; -- -- temp_var = set_if_not ("HOSTTYPE", HOSTTYPE); -- temp_var = set_if_not ("OSTYPE", OSTYPE); -- temp_var = set_if_not ("MACHTYPE", MACHTYPE); -- -- temp_var = set_if_not ("HOSTNAME", current_host_name); --} -- --/* Set $HOME to the information in the password file if we didn't get -- it from the environment. */ -- --/* This function is not static so the tilde and readline libraries can -- use it. */ --char * --sh_get_home_dir () --{ -- if (current_user.home_dir == 0) -- get_current_user_info (); -- return current_user.home_dir; --} -- --static void --set_home_var () --{ -- SHELL_VAR *temp_var; -- -- temp_var = find_variable ("HOME"); -- if (temp_var == 0) -- temp_var = bind_variable ("HOME", sh_get_home_dir (), 0); --#if 0 -- VSETATTR (temp_var, att_exported); --#endif --} -- --/* Set $SHELL to the user's login shell if it is not already set. Call -- get_current_user_info if we haven't already fetched the shell. */ --static void --set_shell_var () --{ -- SHELL_VAR *temp_var; -- -- temp_var = find_variable ("SHELL"); -- if (temp_var == 0) -- { -- if (current_user.shell == 0) -- get_current_user_info (); -- temp_var = bind_variable ("SHELL", current_user.shell, 0); -- } --#if 0 -- VSETATTR (temp_var, att_exported); --#endif --} -- --static char * --get_bash_name () --{ -- char *name; -- -- if ((login_shell == 1) && RELPATH(shell_name)) -- { -- if (current_user.shell == 0) -- get_current_user_info (); -- name = savestring (current_user.shell); -- } -- else if (ABSPATH(shell_name)) -- name = savestring (shell_name); -- else if (shell_name[0] == '.' && shell_name[1] == '/') -- { -- /* Fast path for common case. */ -- char *cdir; -- int len; -- -- cdir = get_string_value ("PWD"); -- if (cdir) -- { -- len = strlen (cdir); -- name = (char *)xmalloc (len + strlen (shell_name) + 1); -- strcpy (name, cdir); -- strcpy (name + len, shell_name + 1); -- } -- else -- name = savestring (shell_name); -- } -- else -- { -- char *tname; -- int s; -- -- tname = find_user_command (shell_name); -- -- if (tname == 0) -- { -- /* Try the current directory. If there is not an executable -- there, just punt and use the login shell. */ -- s = file_status (shell_name); -- if (s & FS_EXECABLE) -- { -- tname = make_absolute (shell_name, get_string_value ("PWD")); -- if (*shell_name == '.') -- { -- name = sh_canonpath (tname, PATH_CHECKDOTDOT|PATH_CHECKEXISTS); -- if (name == 0) -- name = tname; -- else -- free (tname); -- } -- else -- name = tname; -- } -- else -- { -- if (current_user.shell == 0) -- get_current_user_info (); -- name = savestring (current_user.shell); -- } -- } -- else -- { -- name = full_pathname (tname); -- free (tname); -- } -- } -- -- return (name); --} -- --void --adjust_shell_level (change) -- int change; --{ -- char new_level[5], *old_SHLVL; -- intmax_t old_level; -- SHELL_VAR *temp_var; -- -- old_SHLVL = get_string_value ("SHLVL"); -- if (old_SHLVL == 0 || *old_SHLVL == '\0' || legal_number (old_SHLVL, &old_level) == 0) -- old_level = 0; -- -- shell_level = old_level + change; -- if (shell_level < 0) -- shell_level = 0; -- else if (shell_level > 1000) -- { -- internal_warning (_("shell level (%d) too high, resetting to 1"), shell_level); -- shell_level = 1; -- } -- -- /* We don't need the full generality of itos here. */ -- if (shell_level < 10) -- { -- new_level[0] = shell_level + '0'; -- new_level[1] = '\0'; -- } -- else if (shell_level < 100) -- { -- new_level[0] = (shell_level / 10) + '0'; -- new_level[1] = (shell_level % 10) + '0'; -- new_level[2] = '\0'; -- } -- else if (shell_level < 1000) -- { -- new_level[0] = (shell_level / 100) + '0'; -- old_level = shell_level % 100; -- new_level[1] = (old_level / 10) + '0'; -- new_level[2] = (old_level % 10) + '0'; -- new_level[3] = '\0'; -- } -- -- temp_var = bind_variable ("SHLVL", new_level, 0); -- set_auto_export (temp_var); --} -- --static void --initialize_shell_level () --{ -- adjust_shell_level (1); --} -- --/* If we got PWD from the environment, update our idea of the current -- working directory. In any case, make sure that PWD exists before -- checking it. It is possible for getcwd () to fail on shell startup, -- and in that case, PWD would be undefined. If this is an interactive -- login shell, see if $HOME is the current working directory, and if -- that's not the same string as $PWD, set PWD=$HOME. */ -- --void --set_pwd () --{ -- SHELL_VAR *temp_var, *home_var; -- char *temp_string, *home_string; -- -- home_var = find_variable ("HOME"); -- home_string = home_var ? value_cell (home_var) : (char *)NULL; -- -- temp_var = find_variable ("PWD"); -- if (temp_var && imported_p (temp_var) && -- (temp_string = value_cell (temp_var)) && -- same_file (temp_string, ".", (struct stat *)NULL, (struct stat *)NULL)) -- set_working_directory (temp_string); -- else if (home_string && interactive_shell && login_shell && -- same_file (home_string, ".", (struct stat *)NULL, (struct stat *)NULL)) -- { -- set_working_directory (home_string); -- temp_var = bind_variable ("PWD", home_string, 0); -- set_auto_export (temp_var); -- } -- else -- { -- temp_string = get_working_directory ("shell-init"); -- if (temp_string) -- { -- temp_var = bind_variable ("PWD", temp_string, 0); -- set_auto_export (temp_var); -- free (temp_string); -- } -- } -- -- /* According to the Single Unix Specification, v2, $OLDPWD is an -- `environment variable' and therefore should be auto-exported. -- Make a dummy invisible variable for OLDPWD, and mark it as exported. */ -- temp_var = bind_variable ("OLDPWD", (char *)NULL, 0); -- VSETATTR (temp_var, (att_exported | att_invisible)); --} -- --/* Make a variable $PPID, which holds the pid of the shell's parent. */ --void --set_ppid () --{ -- char namebuf[INT_STRLEN_BOUND(pid_t) + 1], *name; -- SHELL_VAR *temp_var; -- -- name = inttostr (getppid (), namebuf, sizeof(namebuf)); -- temp_var = find_variable ("PPID"); -- if (temp_var) -- VUNSETATTR (temp_var, (att_readonly | att_exported)); -- temp_var = bind_variable ("PPID", name, 0); -- VSETATTR (temp_var, (att_readonly | att_integer)); --} -- --static void --uidset () --{ -- char buff[INT_STRLEN_BOUND(uid_t) + 1], *b; -- register SHELL_VAR *v; -- -- b = inttostr (current_user.uid, buff, sizeof (buff)); -- v = find_variable ("UID"); -- if (v == 0) -- { -- v = bind_variable ("UID", b, 0); -- VSETATTR (v, (att_readonly | att_integer)); -- } -- -- if (current_user.euid != current_user.uid) -- b = inttostr (current_user.euid, buff, sizeof (buff)); -- -- v = find_variable ("EUID"); -- if (v == 0) -- { -- v = bind_variable ("EUID", b, 0); -- VSETATTR (v, (att_readonly | att_integer)); -- } --} -- --#if defined (ARRAY_VARS) --static void --make_vers_array () --{ -- SHELL_VAR *vv; -- ARRAY *av; -- char *s, d[32], b[INT_STRLEN_BOUND(int) + 1]; -- -- unbind_variable ("BASH_VERSINFO"); -- -- vv = make_new_array_variable ("BASH_VERSINFO"); -- av = array_cell (vv); -- strcpy (d, dist_version); -- s = strchr (d, '.'); -- if (s) -- *s++ = '\0'; -- array_insert (av, 0, d); -- array_insert (av, 1, s); -- s = inttostr (patch_level, b, sizeof (b)); -- array_insert (av, 2, s); -- s = inttostr (build_version, b, sizeof (b)); -- array_insert (av, 3, s); -- array_insert (av, 4, release_status); -- array_insert (av, 5, MACHTYPE); -- -- VSETATTR (vv, att_readonly); --} --#endif /* ARRAY_VARS */ -- --/* Set the environment variables $LINES and $COLUMNS in response to -- a window size change. */ --void --sh_set_lines_and_columns (lines, cols) -- int lines, cols; --{ -- char val[INT_STRLEN_BOUND(int) + 1], *v; -- --#if defined (READLINE) -- /* If we are currently assigning to LINES or COLUMNS, don't do anything. */ -- if (winsize_assignment) -- return; --#endif -- -- v = inttostr (lines, val, sizeof (val)); -- bind_variable ("LINES", v, 0); -- -- v = inttostr (cols, val, sizeof (val)); -- bind_variable ("COLUMNS", v, 0); --} -- --/* **************************************************************** */ --/* */ --/* Printing variables and values */ --/* */ --/* **************************************************************** */ -- --/* Print LIST (a list of shell variables) to stdout in such a way that -- they can be read back in. */ --void --print_var_list (list) -- register SHELL_VAR **list; --{ -- register int i; -- register SHELL_VAR *var; -- -- for (i = 0; list && (var = list[i]); i++) -- if (invisible_p (var) == 0) -- print_assignment (var); --} -- --/* Print LIST (a list of shell functions) to stdout in such a way that -- they can be read back in. */ --void --print_func_list (list) -- register SHELL_VAR **list; --{ -- register int i; -- register SHELL_VAR *var; -- -- for (i = 0; list && (var = list[i]); i++) -- { -- printf ("%s ", var->name); -- print_var_function (var); -- printf ("\n"); -- } --} -- --/* Print the value of a single SHELL_VAR. No newline is -- output, but the variable is printed in such a way that -- it can be read back in. */ --void --print_assignment (var) -- SHELL_VAR *var; --{ -- if (var_isset (var) == 0) -- return; -- -- if (function_p (var)) -- { -- printf ("%s", var->name); -- print_var_function (var); -- printf ("\n"); -- } --#if defined (ARRAY_VARS) -- else if (array_p (var)) -- print_array_assignment (var, 0); -- else if (assoc_p (var)) -- print_assoc_assignment (var, 0); --#endif /* ARRAY_VARS */ -- else -- { -- printf ("%s=", var->name); -- print_var_value (var, 1); -- printf ("\n"); -- } --} -- --/* Print the value cell of VAR, a shell variable. Do not print -- the name, nor leading/trailing newline. If QUOTE is non-zero, -- and the value contains shell metacharacters, quote the value -- in such a way that it can be read back in. */ --void --print_var_value (var, quote) -- SHELL_VAR *var; -- int quote; --{ -- char *t; -- -- if (var_isset (var) == 0) -- return; -- -- if (quote && posixly_correct == 0 && ansic_shouldquote (value_cell (var))) -- { -- t = ansic_quote (value_cell (var), 0, (int *)0); -- printf ("%s", t); -- free (t); -- } -- else if (quote && sh_contains_shell_metas (value_cell (var))) -- { -- t = sh_single_quote (value_cell (var)); -- printf ("%s", t); -- free (t); -- } -- else -- printf ("%s", value_cell (var)); --} -- --/* Print the function cell of VAR, a shell variable. Do not -- print the name, nor leading/trailing newline. */ --void --print_var_function (var) -- SHELL_VAR *var; --{ -- char *x; -- -- if (function_p (var) && var_isset (var)) -- { -- x = named_function_string ((char *)NULL, function_cell(var), FUNC_MULTILINE|FUNC_EXTERNAL); -- printf ("%s", x); -- } --} -- --/* **************************************************************** */ --/* */ --/* Dynamic Variables */ --/* */ --/* **************************************************************** */ -- --/* DYNAMIC VARIABLES -- -- These are variables whose values are generated anew each time they are -- referenced. These are implemented using a pair of function pointers -- in the struct variable: assign_func, which is called from bind_variable -- and, if arrays are compiled into the shell, some of the functions in -- arrayfunc.c, and dynamic_value, which is called from find_variable. -- -- assign_func is called from bind_variable_internal, if -- bind_variable_internal discovers that the variable being assigned to -- has such a function. The function is called as -- SHELL_VAR *temp = (*(entry->assign_func)) (entry, value, ind) -- and the (SHELL_VAR *)temp is returned as the value of bind_variable. It -- is usually ENTRY (self). IND is an index for an array variable, and -- unused otherwise. -- -- dynamic_value is called from find_variable_internal to return a `new' -- value for the specified dynamic varible. If this function is NULL, -- the variable is treated as a `normal' shell variable. If it is not, -- however, then this function is called like this: -- tempvar = (*(var->dynamic_value)) (var); -- -- Sometimes `tempvar' will replace the value of `var'. Other times, the -- shell will simply use the string value. Pretty object-oriented, huh? -- -- Be warned, though: if you `unset' a special variable, it loses its -- special meaning, even if you subsequently set it. -- -- The special assignment code would probably have been better put in -- subst.c: do_assignment_internal, in the same style as -- stupidly_hack_special_variables, but I wanted the changes as -- localized as possible. */ -- --#define INIT_DYNAMIC_VAR(var, val, gfunc, afunc) \ -- do \ -- { \ -- v = bind_variable (var, (val), 0); \ -- v->dynamic_value = gfunc; \ -- v->assign_func = afunc; \ -- } \ -- while (0) -- --#define INIT_DYNAMIC_ARRAY_VAR(var, gfunc, afunc) \ -- do \ -- { \ -- v = make_new_array_variable (var); \ -- v->dynamic_value = gfunc; \ -- v->assign_func = afunc; \ -- } \ -- while (0) -- --#define INIT_DYNAMIC_ASSOC_VAR(var, gfunc, afunc) \ -- do \ -- { \ -- v = make_new_assoc_variable (var); \ -- v->dynamic_value = gfunc; \ -- v->assign_func = afunc; \ -- } \ -- while (0) -- --static SHELL_VAR * --null_assign (self, value, unused, key) -- SHELL_VAR *self; -- char *value; -- arrayind_t unused; -- char *key; --{ -- return (self); --} -- --#if defined (ARRAY_VARS) --static SHELL_VAR * --null_array_assign (self, value, ind, key) -- SHELL_VAR *self; -- char *value; -- arrayind_t ind; -- char *key; --{ -- return (self); --} --#endif -- --/* Degenerate `dynamic_value' function; just returns what's passed without -- manipulation. */ --static SHELL_VAR * --get_self (self) -- SHELL_VAR *self; --{ -- return (self); --} -- --#if defined (ARRAY_VARS) --/* A generic dynamic array variable initializer. Initialize array variable -- NAME with dynamic value function GETFUNC and assignment function SETFUNC. */ --static SHELL_VAR * --init_dynamic_array_var (name, getfunc, setfunc, attrs) -- char *name; -- sh_var_value_func_t *getfunc; -- sh_var_assign_func_t *setfunc; -- int attrs; --{ -- SHELL_VAR *v; -- -- v = find_variable (name); -- if (v) -- return (v); -- INIT_DYNAMIC_ARRAY_VAR (name, getfunc, setfunc); -- if (attrs) -- VSETATTR (v, attrs); -- return v; --} -- --static SHELL_VAR * --init_dynamic_assoc_var (name, getfunc, setfunc, attrs) -- char *name; -- sh_var_value_func_t *getfunc; -- sh_var_assign_func_t *setfunc; -- int attrs; --{ -- SHELL_VAR *v; -- -- v = find_variable (name); -- if (v) -- return (v); -- INIT_DYNAMIC_ASSOC_VAR (name, getfunc, setfunc); -- if (attrs) -- VSETATTR (v, attrs); -- return v; --} --#endif -- --/* The value of $SECONDS. This is the number of seconds since shell -- invocation, or, the number of seconds since the last assignment + the -- value of the last assignment. */ --static intmax_t seconds_value_assigned; -- --static SHELL_VAR * --assign_seconds (self, value, unused, key) -- SHELL_VAR *self; -- char *value; -- arrayind_t unused; -- char *key; --{ -- if (legal_number (value, &seconds_value_assigned) == 0) -- seconds_value_assigned = 0; -- shell_start_time = NOW; -- return (self); --} -- --static SHELL_VAR * --get_seconds (var) -- SHELL_VAR *var; --{ -- time_t time_since_start; -- char *p; -- -- time_since_start = NOW - shell_start_time; -- p = itos(seconds_value_assigned + time_since_start); -- -- FREE (value_cell (var)); -- -- VSETATTR (var, att_integer); -- var_setvalue (var, p); -- return (var); --} -- --static SHELL_VAR * --init_seconds_var () --{ -- SHELL_VAR *v; -- -- v = find_variable ("SECONDS"); -- if (v) -- { -- if (legal_number (value_cell(v), &seconds_value_assigned) == 0) -- seconds_value_assigned = 0; -- } -- INIT_DYNAMIC_VAR ("SECONDS", (v ? value_cell (v) : (char *)NULL), get_seconds, assign_seconds); -- return v; --} -- --/* The random number seed. You can change this by setting RANDOM. */ --static unsigned long rseed = 1; --static int last_random_value; --static int seeded_subshell = 0; -- --/* A linear congruential random number generator based on the example -- one in the ANSI C standard. This one isn't very good, but a more -- complicated one is overkill. */ -- --/* Returns a pseudo-random number between 0 and 32767. */ --static int --brand () --{ -- /* From "Random number generators: good ones are hard to find", -- Park and Miller, Communications of the ACM, vol. 31, no. 10, -- October 1988, p. 1195. filtered through FreeBSD */ -- long h, l; -- -- /* Can't seed with 0. */ -- if (rseed == 0) -- rseed = 123459876; -- h = rseed / 127773; -- l = rseed % 127773; -- rseed = 16807 * l - 2836 * h; --#if 0 -- if (rseed < 0) -- rseed += 0x7fffffff; --#endif -- return ((unsigned int)(rseed & 32767)); /* was % 32768 */ --} -- --/* Set the random number generator seed to SEED. */ --static void --sbrand (seed) -- unsigned long seed; --{ -- rseed = seed; -- last_random_value = 0; --} -- --static void --seedrand () --{ -- struct timeval tv; -- -- gettimeofday (&tv, NULL); -- sbrand (tv.tv_sec ^ tv.tv_usec ^ getpid ()); --} -- --static SHELL_VAR * --assign_random (self, value, unused, key) -- SHELL_VAR *self; -- char *value; -- arrayind_t unused; -- char *key; --{ -- sbrand (strtoul (value, (char **)NULL, 10)); -- if (subshell_environment) -- seeded_subshell = getpid (); -- return (self); --} -- --int --get_random_number () --{ -- int rv, pid; -- -- /* Reset for command and process substitution. */ -- pid = getpid (); -- if (subshell_environment && seeded_subshell != pid) -- { -- seedrand (); -- seeded_subshell = pid; -- } -- -- do -- rv = brand (); -- while (rv == last_random_value); -- return rv; --} -- --static SHELL_VAR * --get_random (var) -- SHELL_VAR *var; --{ -- int rv; -- char *p; -- -- rv = get_random_number (); -- last_random_value = rv; -- p = itos (rv); -- -- FREE (value_cell (var)); -- -- VSETATTR (var, att_integer); -- var_setvalue (var, p); -- return (var); --} -- --static SHELL_VAR * --assign_lineno (var, value, unused, key) -- SHELL_VAR *var; -- char *value; -- arrayind_t unused; -- char *key; --{ -- intmax_t new_value; -- -- if (value == 0 || *value == '\0' || legal_number (value, &new_value) == 0) -- new_value = 0; -- line_number = line_number_base = new_value; -- return var; --} -- --/* Function which returns the current line number. */ --static SHELL_VAR * --get_lineno (var) -- SHELL_VAR *var; --{ -- char *p; -- int ln; -- -- ln = executing_line_number (); -- p = itos (ln); -- FREE (value_cell (var)); -- var_setvalue (var, p); -- return (var); --} -- --static SHELL_VAR * --assign_subshell (var, value, unused, key) -- SHELL_VAR *var; -- char *value; -- arrayind_t unused; -- char *key; --{ -- intmax_t new_value; -- -- if (value == 0 || *value == '\0' || legal_number (value, &new_value) == 0) -- new_value = 0; -- subshell_level = new_value; -- return var; --} -- --static SHELL_VAR * --get_subshell (var) -- SHELL_VAR *var; --{ -- char *p; -- -- p = itos (subshell_level); -- FREE (value_cell (var)); -- var_setvalue (var, p); -- return (var); --} -- --static SHELL_VAR * --get_bashpid (var) -- SHELL_VAR *var; --{ -- int pid; -- char *p; -- -- pid = getpid (); -- p = itos (pid); -- -- FREE (value_cell (var)); -- VSETATTR (var, att_integer|att_readonly); -- var_setvalue (var, p); -- return (var); --} -- --static SHELL_VAR * --get_bash_command (var) -- SHELL_VAR *var; --{ -- char *p; -- -- if (the_printed_command_except_trap) -- p = savestring (the_printed_command_except_trap); -- else -- { -- p = (char *)xmalloc (1); -- p[0] = '\0'; -- } -- FREE (value_cell (var)); -- var_setvalue (var, p); -- return (var); --} -- --#if defined (HISTORY) --static SHELL_VAR * --get_histcmd (var) -- SHELL_VAR *var; --{ -- char *p; -- -- p = itos (history_number ()); -- FREE (value_cell (var)); -- var_setvalue (var, p); -- return (var); --} --#endif -- --#if defined (READLINE) --/* When this function returns, VAR->value points to malloced memory. */ --static SHELL_VAR * --get_comp_wordbreaks (var) -- SHELL_VAR *var; --{ -- /* If we don't have anything yet, assign a default value. */ -- if (rl_completer_word_break_characters == 0 && bash_readline_initialized == 0) -- enable_hostname_completion (perform_hostname_completion); -- -- FREE (value_cell (var)); -- var_setvalue (var, savestring (rl_completer_word_break_characters)); -- -- return (var); --} -- --/* When this function returns, rl_completer_word_break_characters points to -- malloced memory. */ --static SHELL_VAR * --assign_comp_wordbreaks (self, value, unused, key) -- SHELL_VAR *self; -- char *value; -- arrayind_t unused; -- char *key; --{ -- if (rl_completer_word_break_characters && -- rl_completer_word_break_characters != rl_basic_word_break_characters) -- free (rl_completer_word_break_characters); -- -- rl_completer_word_break_characters = savestring (value); -- return self; --} --#endif /* READLINE */ -- --#if defined (PUSHD_AND_POPD) && defined (ARRAY_VARS) --static SHELL_VAR * --assign_dirstack (self, value, ind, key) -- SHELL_VAR *self; -- char *value; -- arrayind_t ind; -- char *key; --{ -- set_dirstack_element (ind, 1, value); -- return self; --} -- --static SHELL_VAR * --get_dirstack (self) -- SHELL_VAR *self; --{ -- ARRAY *a; -- WORD_LIST *l; -- -- l = get_directory_stack (0); -- a = array_from_word_list (l); -- array_dispose (array_cell (self)); -- dispose_words (l); -- var_setarray (self, a); -- return self; --} --#endif /* PUSHD AND POPD && ARRAY_VARS */ -- --#if defined (ARRAY_VARS) --/* We don't want to initialize the group set with a call to getgroups() -- unless we're asked to, but we only want to do it once. */ --static SHELL_VAR * --get_groupset (self) -- SHELL_VAR *self; --{ -- register int i; -- int ng; -- ARRAY *a; -- static char **group_set = (char **)NULL; -- -- if (group_set == 0) -- { -- group_set = get_group_list (&ng); -- a = array_cell (self); -- for (i = 0; i < ng; i++) -- array_insert (a, i, group_set[i]); -- } -- return (self); --} -- --static SHELL_VAR * --build_hashcmd (self) -- SHELL_VAR *self; --{ -- HASH_TABLE *h; -- int i; -- char *k, *v; -- BUCKET_CONTENTS *item; -- -- h = assoc_cell (self); -- if (h) -- assoc_dispose (h); -- -- if (hashed_filenames == 0 || HASH_ENTRIES (hashed_filenames) == 0) -- { -- var_setvalue (self, (char *)NULL); -- return self; -- } -- -- h = assoc_create (hashed_filenames->nbuckets); -- for (i = 0; i < hashed_filenames->nbuckets; i++) -- { -- for (item = hash_items (i, hashed_filenames); item; item = item->next) -- { -- k = savestring (item->key); -- v = pathdata(item)->path; -- assoc_insert (h, k, v); -- } -- } -- -- var_setvalue (self, (char *)h); -- return self; --} -- --static SHELL_VAR * --get_hashcmd (self) -- SHELL_VAR *self; --{ -- build_hashcmd (self); -- return (self); --} -- --static SHELL_VAR * --assign_hashcmd (self, value, ind, key) -- SHELL_VAR *self; -- char *value; -- arrayind_t ind; -- char *key; --{ -- phash_insert (key, value, 0, 0); -- return (build_hashcmd (self)); --} -- --#if defined (ALIAS) --static SHELL_VAR * --build_aliasvar (self) -- SHELL_VAR *self; --{ -- HASH_TABLE *h; -- int i; -- char *k, *v; -- BUCKET_CONTENTS *item; -- -- h = assoc_cell (self); -- if (h) -- assoc_dispose (h); -- -- if (aliases == 0 || HASH_ENTRIES (aliases) == 0) -- { -- var_setvalue (self, (char *)NULL); -- return self; -- } -- -- h = assoc_create (aliases->nbuckets); -- for (i = 0; i < aliases->nbuckets; i++) -- { -- for (item = hash_items (i, aliases); item; item = item->next) -- { -- k = savestring (item->key); -- v = ((alias_t *)(item->data))->value; -- assoc_insert (h, k, v); -- } -- } -- -- var_setvalue (self, (char *)h); -- return self; --} -- --static SHELL_VAR * --get_aliasvar (self) -- SHELL_VAR *self; --{ -- build_aliasvar (self); -- return (self); --} -- --static SHELL_VAR * --assign_aliasvar (self, value, ind, key) -- SHELL_VAR *self; -- char *value; -- arrayind_t ind; -- char *key; --{ -- add_alias (key, value); -- return (build_aliasvar (self)); --} --#endif /* ALIAS */ -- --#endif /* ARRAY_VARS */ -- --/* If ARRAY_VARS is not defined, this just returns the name of any -- currently-executing function. If we have arrays, it's a call stack. */ --static SHELL_VAR * --get_funcname (self) -- SHELL_VAR *self; --{ --#if ! defined (ARRAY_VARS) -- char *t; -- if (variable_context && this_shell_function) -- { -- FREE (value_cell (self)); -- t = savestring (this_shell_function->name); -- var_setvalue (self, t); -- } --#endif -- return (self); --} -- --void --make_funcname_visible (on_or_off) -- int on_or_off; --{ -- SHELL_VAR *v; -- -- v = find_variable ("FUNCNAME"); -- if (v == 0 || v->dynamic_value == 0) -- return; -- -- if (on_or_off) -- VUNSETATTR (v, att_invisible); -- else -- VSETATTR (v, att_invisible); --} -- --static SHELL_VAR * --init_funcname_var () --{ -- SHELL_VAR *v; -- -- v = find_variable ("FUNCNAME"); -- if (v) -- return v; --#if defined (ARRAY_VARS) -- INIT_DYNAMIC_ARRAY_VAR ("FUNCNAME", get_funcname, null_array_assign); --#else -- INIT_DYNAMIC_VAR ("FUNCNAME", (char *)NULL, get_funcname, null_assign); --#endif -- VSETATTR (v, att_invisible|att_noassign); -- return v; --} -- --static void --initialize_dynamic_variables () --{ -- SHELL_VAR *v; -- -- v = init_seconds_var (); -- -- INIT_DYNAMIC_VAR ("BASH_COMMAND", (char *)NULL, get_bash_command, (sh_var_assign_func_t *)NULL); -- INIT_DYNAMIC_VAR ("BASH_SUBSHELL", (char *)NULL, get_subshell, assign_subshell); -- -- INIT_DYNAMIC_VAR ("RANDOM", (char *)NULL, get_random, assign_random); -- VSETATTR (v, att_integer); -- INIT_DYNAMIC_VAR ("LINENO", (char *)NULL, get_lineno, assign_lineno); -- VSETATTR (v, att_integer); -- -- INIT_DYNAMIC_VAR ("BASHPID", (char *)NULL, get_bashpid, null_assign); -- VSETATTR (v, att_integer|att_readonly); -- --#if defined (HISTORY) -- INIT_DYNAMIC_VAR ("HISTCMD", (char *)NULL, get_histcmd, (sh_var_assign_func_t *)NULL); -- VSETATTR (v, att_integer); --#endif -- --#if defined (READLINE) -- INIT_DYNAMIC_VAR ("COMP_WORDBREAKS", (char *)NULL, get_comp_wordbreaks, assign_comp_wordbreaks); --#endif -- --#if defined (PUSHD_AND_POPD) && defined (ARRAY_VARS) -- v = init_dynamic_array_var ("DIRSTACK", get_dirstack, assign_dirstack, 0); --#endif /* PUSHD_AND_POPD && ARRAY_VARS */ -- --#if defined (ARRAY_VARS) -- v = init_dynamic_array_var ("GROUPS", get_groupset, null_array_assign, att_noassign); -- --# if defined (DEBUGGER) -- v = init_dynamic_array_var ("BASH_ARGC", get_self, null_array_assign, att_noassign|att_nounset); -- v = init_dynamic_array_var ("BASH_ARGV", get_self, null_array_assign, att_noassign|att_nounset); --# endif /* DEBUGGER */ -- v = init_dynamic_array_var ("BASH_SOURCE", get_self, null_array_assign, att_noassign|att_nounset); -- v = init_dynamic_array_var ("BASH_LINENO", get_self, null_array_assign, att_noassign|att_nounset); -- -- v = init_dynamic_assoc_var ("BASH_CMDS", get_hashcmd, assign_hashcmd, att_nofree); --# if defined (ALIAS) -- v = init_dynamic_assoc_var ("BASH_ALIASES", get_aliasvar, assign_aliasvar, att_nofree); --# endif --#endif -- -- v = init_funcname_var (); --} -- --/* **************************************************************** */ --/* */ --/* Retrieving variables and values */ --/* */ --/* **************************************************************** */ -- --/* How to get a pointer to the shell variable or function named NAME. -- HASHED_VARS is a pointer to the hash table containing the list -- of interest (either variables or functions). */ -- --static SHELL_VAR * --hash_lookup (name, hashed_vars) -- const char *name; -- HASH_TABLE *hashed_vars; --{ -- BUCKET_CONTENTS *bucket; -- -- bucket = hash_search (name, hashed_vars, 0); -- /* If we find the name in HASHED_VARS, set LAST_TABLE_SEARCHED to that -- table. */ -- if (bucket) -- last_table_searched = hashed_vars; -- return (bucket ? (SHELL_VAR *)bucket->data : (SHELL_VAR *)NULL); --} -- --SHELL_VAR * --var_lookup (name, vcontext) -- const char *name; -- VAR_CONTEXT *vcontext; --{ -- VAR_CONTEXT *vc; -- SHELL_VAR *v; -- -- v = (SHELL_VAR *)NULL; -- for (vc = vcontext; vc; vc = vc->down) -- if (v = hash_lookup (name, vc->table)) -- break; -- -- return v; --} -- --/* Look up the variable entry named NAME. If SEARCH_TEMPENV is non-zero, -- then also search the temporarily built list of exported variables. -- The lookup order is: -- temporary_env -- shell_variables list --*/ -- --SHELL_VAR * --find_variable_internal (name, force_tempenv) -- const char *name; -- int force_tempenv; --{ -- SHELL_VAR *var; -- int search_tempenv; -- VAR_CONTEXT *vc; -- -- var = (SHELL_VAR *)NULL; -- -- /* If explicitly requested, first look in the temporary environment for -- the variable. This allows constructs such as "foo=x eval 'echo $foo'" -- to get the `exported' value of $foo. This happens if we are executing -- a function or builtin, or if we are looking up a variable in a -- "subshell environment". */ -- search_tempenv = force_tempenv || (expanding_redir == 0 && subshell_environment); -- -- if (search_tempenv && temporary_env) -- var = hash_lookup (name, temporary_env); -- -- vc = shell_variables; --#if 0 --if (search_tempenv == 0 && /* (subshell_environment & SUBSHELL_COMSUB) && */ -- expanding_redir && -- (this_shell_builtin == eval_builtin || this_shell_builtin == command_builtin)) -- { -- itrace("find_variable_internal: search_tempenv == 0: skipping VC_BLTNENV"); -- while (vc && (vc->flags & VC_BLTNENV)) -- vc = vc->down; -- if (vc == 0) -- vc = shell_variables; -- } --#endif -- -- if (var == 0) -- var = var_lookup (name, vc); -- -- if (var == 0) -- return ((SHELL_VAR *)NULL); -- -- return (var->dynamic_value ? (*(var->dynamic_value)) (var) : var); --} -- --/* Look up and resolve the chain of nameref variables starting at V all the -- way to NULL or non-nameref. */ --SHELL_VAR * --find_variable_nameref (v) -- SHELL_VAR *v; --{ -- int level; -- char *newname; -- SHELL_VAR *orig, *oldv; -- -- level = 0; -- orig = v; -- while (v && nameref_p (v)) -- { -- level++; -- if (level > NAMEREF_MAX) -- return ((SHELL_VAR *)0); /* error message here? */ -- newname = nameref_cell (v); -- if (newname == 0 || *newname == '\0') -- return ((SHELL_VAR *)0); -- oldv = v; -- v = find_variable_internal (newname, (expanding_redir == 0 && (assigning_in_environment || executing_builtin))); -- if (v == orig || v == oldv) -- { -- internal_warning (_("%s: circular name reference"), orig->name); -- return ((SHELL_VAR *)0); -- } -- } -- return v; --} -- --/* Resolve the chain of nameref variables for NAME. XXX - could change later */ --SHELL_VAR * --find_variable_last_nameref (name) -- const char *name; --{ -- SHELL_VAR *v, *nv; -- char *newname; -- int level; -- -- nv = v = find_variable_noref (name); -- level = 0; -- while (v && nameref_p (v)) -- { -- level++; -- if (level > NAMEREF_MAX) -- return ((SHELL_VAR *)0); /* error message here? */ -- newname = nameref_cell (v); -- if (newname == 0 || *newname == '\0') -- return ((SHELL_VAR *)0); -- nv = v; -- v = find_variable_internal (newname, (expanding_redir == 0 && (assigning_in_environment || executing_builtin))); -- } -- return nv; --} -- --/* Resolve the chain of nameref variables for NAME. XXX - could change later */ --SHELL_VAR * --find_global_variable_last_nameref (name) -- const char *name; --{ -- SHELL_VAR *v, *nv; -- char *newname; -- int level; -- -- nv = v = find_global_variable_noref (name); -- level = 0; -- while (v && nameref_p (v)) -- { -- level++; -- if (level > NAMEREF_MAX) -- return ((SHELL_VAR *)0); /* error message here? */ -- newname = nameref_cell (v); -- if (newname == 0 || *newname == '\0') -- return ((SHELL_VAR *)0); -- nv = v; -- v = find_global_variable_noref (newname); -- } -- return nv; --} -- --static SHELL_VAR * --find_nameref_at_context (v, vc) -- SHELL_VAR *v; -- VAR_CONTEXT *vc; --{ -- SHELL_VAR *nv, *nv2; -- VAR_CONTEXT *nvc; -- char *newname; -- int level; -- -- nv = v; -- level = 1; -- while (nv && nameref_p (nv)) -- { -- level++; -- if (level > NAMEREF_MAX) -- return ((SHELL_VAR *)NULL); -- newname = nameref_cell (nv); -- if (newname == 0 || *newname == '\0') -- return ((SHELL_VAR *)NULL); -- nv2 = hash_lookup (newname, vc->table); -- if (nv2 == 0) -- break; -- nv = nv2; -- } -- return nv; --} -- --/* Do nameref resolution from the VC, which is the local context for some -- function or builtin, `up' the chain to the global variables context. If -- NVCP is not NULL, return the variable context where we finally ended the -- nameref resolution (so the bind_variable_internal can use the correct -- variable context and hash table). */ --static SHELL_VAR * --find_variable_nameref_context (v, vc, nvcp) -- SHELL_VAR *v; -- VAR_CONTEXT *vc; -- VAR_CONTEXT **nvcp; --{ -- SHELL_VAR *nv, *nv2; -- VAR_CONTEXT *nvc; -- -- /* Look starting at the current context all the way `up' */ -- for (nv = v, nvc = vc; nvc; nvc = nvc->down) -- { -- nv2 = find_nameref_at_context (nv, nvc); -- if (nv2 == 0) -- continue; -- nv = nv2; -- if (*nvcp) -- *nvcp = nvc; -- if (nameref_p (nv) == 0) -- break; -- } -- return (nameref_p (nv) ? (SHELL_VAR *)NULL : nv); --} -- --/* Do nameref resolution from the VC, which is the local context for some -- function or builtin, `up' the chain to the global variables context. If -- NVCP is not NULL, return the variable context where we finally ended the -- nameref resolution (so the bind_variable_internal can use the correct -- variable context and hash table). */ --static SHELL_VAR * --find_variable_last_nameref_context (v, vc, nvcp) -- SHELL_VAR *v; -- VAR_CONTEXT *vc; -- VAR_CONTEXT **nvcp; --{ -- SHELL_VAR *nv, *nv2; -- VAR_CONTEXT *nvc; -- -- /* Look starting at the current context all the way `up' */ -- for (nv = v, nvc = vc; nvc; nvc = nvc->down) -- { -- nv2 = find_nameref_at_context (nv, nvc); -- if (nv2 == 0) -- continue; -- nv = nv2; -- if (*nvcp) -- *nvcp = nvc; -- } -- return (nameref_p (nv) ? nv : (SHELL_VAR *)NULL); --} -- --/* Find a variable, forcing a search of the temporary environment first */ --SHELL_VAR * --find_variable_tempenv (name) -- const char *name; --{ -- SHELL_VAR *var; -- -- var = find_variable_internal (name, 1); -- if (var && nameref_p (var)) -- var = find_variable_nameref (var); -- return (var); --} -- --/* Find a variable, not forcing a search of the temporary environment first */ --SHELL_VAR * --find_variable_notempenv (name) -- const char *name; --{ -- SHELL_VAR *var; -- -- var = find_variable_internal (name, 0); -- if (var && nameref_p (var)) -- var = find_variable_nameref (var); -- return (var); --} -- --SHELL_VAR * --find_global_variable (name) -- const char *name; --{ -- SHELL_VAR *var; -- -- var = var_lookup (name, global_variables); -- if (var && nameref_p (var)) -- var = find_variable_nameref (var); -- -- if (var == 0) -- return ((SHELL_VAR *)NULL); -- -- return (var->dynamic_value ? (*(var->dynamic_value)) (var) : var); --} -- --SHELL_VAR * --find_global_variable_noref (name) -- const char *name; --{ -- SHELL_VAR *var; -- -- var = var_lookup (name, global_variables); -- -- if (var == 0) -- return ((SHELL_VAR *)NULL); -- -- return (var->dynamic_value ? (*(var->dynamic_value)) (var) : var); --} -- --SHELL_VAR * --find_shell_variable (name) -- const char *name; --{ -- SHELL_VAR *var; -- -- var = var_lookup (name, shell_variables); -- if (var && nameref_p (var)) -- var = find_variable_nameref (var); -- -- if (var == 0) -- return ((SHELL_VAR *)NULL); -- -- return (var->dynamic_value ? (*(var->dynamic_value)) (var) : var); --} -- --/* Look up the variable entry named NAME. Returns the entry or NULL. */ --SHELL_VAR * --find_variable (name) -- const char *name; --{ -- SHELL_VAR *v; -- -- last_table_searched = 0; -- v = find_variable_internal (name, (expanding_redir == 0 && (assigning_in_environment || executing_builtin))); -- if (v && nameref_p (v)) -- v = find_variable_nameref (v); -- return v; --} -- --SHELL_VAR * --find_variable_noref (name) -- const char *name; --{ -- SHELL_VAR *v; -- -- v = find_variable_internal (name, (expanding_redir == 0 && (assigning_in_environment || executing_builtin))); -- return v; --} -- --/* Look up the function entry whose name matches STRING. -- Returns the entry or NULL. */ --SHELL_VAR * --find_function (name) -- const char *name; --{ -- return (hash_lookup (name, shell_functions)); --} -- --/* Find the function definition for the shell function named NAME. Returns -- the entry or NULL. */ --FUNCTION_DEF * --find_function_def (name) -- const char *name; --{ --#if defined (DEBUGGER) -- return ((FUNCTION_DEF *)hash_lookup (name, shell_function_defs)); --#else -- return ((FUNCTION_DEF *)0); --#endif --} -- --/* Return the value of VAR. VAR is assumed to have been the result of a -- lookup without any subscript, if arrays are compiled into the shell. */ --char * --get_variable_value (var) -- SHELL_VAR *var; --{ -- if (var == 0) -- return ((char *)NULL); --#if defined (ARRAY_VARS) -- else if (array_p (var)) -- return (array_reference (array_cell (var), 0)); -- else if (assoc_p (var)) -- return (assoc_reference (assoc_cell (var), "0")); --#endif -- else -- return (value_cell (var)); --} -- --/* Return the string value of a variable. Return NULL if the variable -- doesn't exist. Don't cons a new string. This is a potential memory -- leak if the variable is found in the temporary environment. Since -- functions and variables have separate name spaces, returns NULL if -- var_name is a shell function only. */ --char * --get_string_value (var_name) -- const char *var_name; --{ -- SHELL_VAR *var; -- -- var = find_variable (var_name); -- return ((var) ? get_variable_value (var) : (char *)NULL); --} -- --/* This is present for use by the tilde and readline libraries. */ --char * --sh_get_env_value (v) -- const char *v; --{ -- return get_string_value (v); --} -- --/* **************************************************************** */ --/* */ --/* Creating and setting variables */ --/* */ --/* **************************************************************** */ -- --/* Set NAME to VALUE if NAME has no value. */ --SHELL_VAR * --set_if_not (name, value) -- char *name, *value; --{ -- SHELL_VAR *v; -- -- if (shell_variables == 0) -- create_variable_tables (); -- -- v = find_variable (name); -- if (v == 0) -- v = bind_variable_internal (name, value, global_variables->table, HASH_NOSRCH, 0); -- return (v); --} -- --/* Create a local variable referenced by NAME. */ --SHELL_VAR * --make_local_variable (name) -- const char *name; --{ -- SHELL_VAR *new_var, *old_var; -- VAR_CONTEXT *vc; -- int was_tmpvar; -- char *tmp_value; -- -- /* local foo; local foo; is a no-op. */ -- old_var = find_variable (name); -- if (old_var && local_p (old_var) && old_var->context == variable_context) -- return (old_var); -- -- was_tmpvar = old_var && tempvar_p (old_var); -- /* If we're making a local variable in a shell function, the temporary env -- has already been merged into the function's variable context stack. We -- can assume that a temporary var in the same context appears in the same -- VAR_CONTEXT and can safely be returned without creating a new variable -- (which results in duplicate names in the same VAR_CONTEXT->table */ -- /* We can't just test tmpvar_p because variables in the temporary env given -- to a shell function appear in the function's local variable VAR_CONTEXT -- but retain their tempvar attribute. We want temporary variables that are -- found in temporary_env, hence the test for last_table_searched, which is -- set in hash_lookup and only (so far) checked here. */ -- if (was_tmpvar && old_var->context == variable_context && last_table_searched != temporary_env) -- { -- VUNSETATTR (old_var, att_invisible); -- return (old_var); -- } -- if (was_tmpvar) -- tmp_value = value_cell (old_var); -- -- for (vc = shell_variables; vc; vc = vc->down) -- if (vc_isfuncenv (vc) && vc->scope == variable_context) -- break; -- -- if (vc == 0) -- { -- internal_error (_("make_local_variable: no function context at current scope")); -- return ((SHELL_VAR *)NULL); -- } -- else if (vc->table == 0) -- vc->table = hash_create (TEMPENV_HASH_BUCKETS); -- -- /* Since this is called only from the local/declare/typeset code, we can -- call builtin_error here without worry (of course, it will also work -- for anything that sets this_command_name). Variables with the `noassign' -- attribute may not be made local. The test against old_var's context -- level is to disallow local copies of readonly global variables (since I -- believe that this could be a security hole). Readonly copies of calling -- function local variables are OK. */ -- if (old_var && (noassign_p (old_var) || -- (readonly_p (old_var) && old_var->context == 0))) -- { -- if (readonly_p (old_var)) -- sh_readonly (name); -- else if (noassign_p (old_var)) -- builtin_error (_("%s: variable may not be assigned value"), name); --#if 0 -- /* Let noassign variables through with a warning */ -- if (readonly_p (old_var)) --#endif -- return ((SHELL_VAR *)NULL); -- } -- -- if (old_var == 0) -- new_var = make_new_variable (name, vc->table); -- else -- { -- new_var = make_new_variable (name, vc->table); -- -- /* If we found this variable in one of the temporary environments, -- inherit its value. Watch to see if this causes problems with -- things like `x=4 local x'. XXX - see above for temporary env -- variables with the same context level as variable_context */ -- /* XXX - we should only do this if the variable is not an array. */ -- if (was_tmpvar) -- var_setvalue (new_var, savestring (tmp_value)); -- -- new_var->attributes = exported_p (old_var) ? att_exported : 0; -- } -- -- vc->flags |= VC_HASLOCAL; -- -- new_var->context = variable_context; -- VSETATTR (new_var, att_local); -- -- if (ifsname (name)) -- setifs (new_var); -- -- if (was_tmpvar == 0) -- VSETATTR (new_var, att_invisible); /* XXX */ -- return (new_var); --} -- --/* Create a new shell variable with name NAME. */ --static SHELL_VAR * --new_shell_variable (name) -- const char *name; --{ -- SHELL_VAR *entry; -- -- entry = (SHELL_VAR *)xmalloc (sizeof (SHELL_VAR)); -- -- entry->name = savestring (name); -- var_setvalue (entry, (char *)NULL); -- CLEAR_EXPORTSTR (entry); -- -- entry->dynamic_value = (sh_var_value_func_t *)NULL; -- entry->assign_func = (sh_var_assign_func_t *)NULL; -- -- entry->attributes = 0; -- -- /* Always assume variables are to be made at toplevel! -- make_local_variable has the responsibility of changing the -- variable context. */ -- entry->context = 0; -- -- return (entry); --} -- --/* Create a new shell variable with name NAME and add it to the hash table -- TABLE. */ --static SHELL_VAR * --make_new_variable (name, table) -- const char *name; -- HASH_TABLE *table; --{ -- SHELL_VAR *entry; -- BUCKET_CONTENTS *elt; -- -- entry = new_shell_variable (name); -- -- /* Make sure we have a shell_variables hash table to add to. */ -- if (shell_variables == 0) -- create_variable_tables (); -- -- elt = hash_insert (savestring (name), table, HASH_NOSRCH); -- elt->data = (PTR_T)entry; -- -- return entry; --} -- --#if defined (ARRAY_VARS) --SHELL_VAR * --make_new_array_variable (name) -- char *name; --{ -- SHELL_VAR *entry; -- ARRAY *array; -- -- entry = make_new_variable (name, global_variables->table); -- array = array_create (); -- -- var_setarray (entry, array); -- VSETATTR (entry, att_array); -- return entry; --} -- --SHELL_VAR * --make_local_array_variable (name, assoc_ok) -- char *name; -- int assoc_ok; --{ -- SHELL_VAR *var; -- ARRAY *array; -- -- var = make_local_variable (name); -- if (var == 0 || array_p (var) || (assoc_ok && assoc_p (var))) -- return var; -- -- array = array_create (); -- -- dispose_variable_value (var); -- var_setarray (var, array); -- VSETATTR (var, att_array); -- return var; --} -- --SHELL_VAR * --make_new_assoc_variable (name) -- char *name; --{ -- SHELL_VAR *entry; -- HASH_TABLE *hash; -- -- entry = make_new_variable (name, global_variables->table); -- hash = assoc_create (0); -- -- var_setassoc (entry, hash); -- VSETATTR (entry, att_assoc); -- return entry; --} -- --SHELL_VAR * --make_local_assoc_variable (name) -- char *name; --{ -- SHELL_VAR *var; -- HASH_TABLE *hash; -- -- var = make_local_variable (name); -- if (var == 0 || assoc_p (var)) -- return var; -- -- dispose_variable_value (var); -- hash = assoc_create (0); -- -- var_setassoc (var, hash); -- VSETATTR (var, att_assoc); -- return var; --} --#endif -- --char * --make_variable_value (var, value, flags) -- SHELL_VAR *var; -- char *value; -- int flags; --{ -- char *retval, *oval; -- intmax_t lval, rval; -- int expok, olen, op; -- -- /* If this variable has had its type set to integer (via `declare -i'), -- then do expression evaluation on it and store the result. The -- functions in expr.c (evalexp()) and bind_int_variable() are responsible -- for turning off the integer flag if they don't want further -- evaluation done. */ -- if (integer_p (var)) -- { -- if (flags & ASS_APPEND) -- { -- oval = value_cell (var); -- lval = evalexp (oval, &expok); /* ksh93 seems to do this */ -- if (expok == 0) -- { -- top_level_cleanup (); -- jump_to_top_level (DISCARD); -- } -- } -- rval = evalexp (value, &expok); -- if (expok == 0) -- { -- top_level_cleanup (); -- jump_to_top_level (DISCARD); -- } -- /* This can be fooled if the variable's value changes while evaluating -- `rval'. We can change it if we move the evaluation of lval to here. */ -- if (flags & ASS_APPEND) -- rval += lval; -- retval = itos (rval); -- } --#if defined (CASEMOD_ATTRS) -- else if (capcase_p (var) || uppercase_p (var) || lowercase_p (var)) -- { -- if (flags & ASS_APPEND) -- { -- oval = get_variable_value (var); -- if (oval == 0) /* paranoia */ -- oval = ""; -- olen = STRLEN (oval); -- retval = (char *)xmalloc (olen + (value ? STRLEN (value) : 0) + 1); -- strcpy (retval, oval); -- if (value) -- strcpy (retval+olen, value); -- } -- else if (*value) -- retval = savestring (value); -- else -- { -- retval = (char *)xmalloc (1); -- retval[0] = '\0'; -- } -- op = capcase_p (var) ? CASE_CAPITALIZE -- : (uppercase_p (var) ? CASE_UPPER : CASE_LOWER); -- oval = sh_modcase (retval, (char *)0, op); -- free (retval); -- retval = oval; -- } --#endif /* CASEMOD_ATTRS */ -- else if (value) -- { -- if (flags & ASS_APPEND) -- { -- oval = get_variable_value (var); -- if (oval == 0) /* paranoia */ -- oval = ""; -- olen = STRLEN (oval); -- retval = (char *)xmalloc (olen + (value ? STRLEN (value) : 0) + 1); -- strcpy (retval, oval); -- if (value) -- strcpy (retval+olen, value); -- } -- else if (*value) -- retval = savestring (value); -- else -- { -- retval = (char *)xmalloc (1); -- retval[0] = '\0'; -- } -- } -- else -- retval = (char *)NULL; -- -- return retval; --} -- --/* Bind a variable NAME to VALUE in the HASH_TABLE TABLE, which may be the -- temporary environment (but usually is not). */ --static SHELL_VAR * --bind_variable_internal (name, value, table, hflags, aflags) -- const char *name; -- char *value; -- HASH_TABLE *table; -- int hflags, aflags; --{ -- char *newval; -- SHELL_VAR *entry; -- -- entry = (hflags & HASH_NOSRCH) ? (SHELL_VAR *)NULL : hash_lookup (name, table); -- /* Follow the nameref chain here if this is the global variables table */ -- if (entry && nameref_p (entry) && (invisible_p (entry) == 0) && table == global_variables->table) -- { -- entry = find_global_variable (entry->name); -- /* Let's see if we have a nameref referencing a variable that hasn't yet -- been created. */ -- if (entry == 0) -- entry = find_variable_last_nameref (name); /* XXX */ -- if (entry == 0) /* just in case */ -- return (entry); -- } -- -- /* The first clause handles `declare -n ref; ref=x;' */ -- if (entry && invisible_p (entry) && nameref_p (entry)) -- goto assign_value; -- else if (entry && nameref_p (entry)) -- { -- newval = nameref_cell (entry); --#if defined (ARRAY_VARS) -- /* declare -n foo=x[2] */ -- if (valid_array_reference (newval)) -- /* XXX - should it be aflags? */ -- entry = assign_array_element (newval, make_variable_value (entry, value, 0), aflags); -- else --#endif -- { -- entry = make_new_variable (newval, table); -- var_setvalue (entry, make_variable_value (entry, value, 0)); -- } -- } -- else if (entry == 0) -- { -- entry = make_new_variable (name, table); -- var_setvalue (entry, make_variable_value (entry, value, 0)); /* XXX */ -- } -- else if (entry->assign_func) /* array vars have assign functions now */ -- { -- INVALIDATE_EXPORTSTR (entry); -- newval = (aflags & ASS_APPEND) ? make_variable_value (entry, value, aflags) : value; -- if (assoc_p (entry)) -- entry = (*(entry->assign_func)) (entry, newval, -1, savestring ("0")); -- else if (array_p (entry)) -- entry = (*(entry->assign_func)) (entry, newval, 0, 0); -- else -- entry = (*(entry->assign_func)) (entry, newval, -1, 0); -- if (newval != value) -- free (newval); -- return (entry); -- } -- else -- { --assign_value: -- if (readonly_p (entry) || noassign_p (entry)) -- { -- if (readonly_p (entry)) -- err_readonly (name); -- return (entry); -- } -- -- /* Variables which are bound are visible. */ -- VUNSETATTR (entry, att_invisible); -- --#if defined (ARRAY_VARS) -- if (assoc_p (entry) || array_p (entry)) -- newval = make_array_variable_value (entry, 0, "0", value, aflags); -- else --#endif -- -- newval = make_variable_value (entry, value, aflags); /* XXX */ -- -- /* Invalidate any cached export string */ -- INVALIDATE_EXPORTSTR (entry); -- --#if defined (ARRAY_VARS) -- /* XXX -- this bears looking at again -- XXX */ -- /* If an existing array variable x is being assigned to with x=b or -- `read x' or something of that nature, silently convert it to -- x[0]=b or `read x[0]'. */ -- if (assoc_p (entry)) -- { -- assoc_insert (assoc_cell (entry), savestring ("0"), newval); -- free (newval); -- } -- else if (array_p (entry)) -- { -- array_insert (array_cell (entry), 0, newval); -- free (newval); -- } -- else --#endif -- { -- FREE (value_cell (entry)); -- var_setvalue (entry, newval); -- } -- } -- -- if (mark_modified_vars) -- VSETATTR (entry, att_exported); -- -- if (exported_p (entry)) -- array_needs_making = 1; -- -- return (entry); --} -- --/* Bind a variable NAME to VALUE. This conses up the name -- and value strings. If we have a temporary environment, we bind there -- first, then we bind into shell_variables. */ -- --SHELL_VAR * --bind_variable (name, value, flags) -- const char *name; -- char *value; -- int flags; --{ -- SHELL_VAR *v, *nv; -- VAR_CONTEXT *vc, *nvc; -- int level; -- -- if (shell_variables == 0) -- create_variable_tables (); -- -- /* If we have a temporary environment, look there first for the variable, -- and, if found, modify the value there before modifying it in the -- shell_variables table. This allows sourced scripts to modify values -- given to them in a temporary environment while modifying the variable -- value that the caller sees. */ -- if (temporary_env) -- bind_tempenv_variable (name, value); -- -- /* XXX -- handle local variables here. */ -- for (vc = shell_variables; vc; vc = vc->down) -- { -- if (vc_isfuncenv (vc) || vc_isbltnenv (vc)) -- { -- v = hash_lookup (name, vc->table); -- nvc = vc; -- if (v && nameref_p (v)) -- { -- nv = find_variable_nameref_context (v, vc, &nvc); -- if (nv == 0) -- { -- nv = find_variable_last_nameref_context (v, vc, &nvc); -- if (nv && nameref_p (nv)) -- { -- /* If this nameref variable doesn't have a value yet, -- set the value. Otherwise, assign using the value as -- normal. */ -- if (nameref_cell (nv) == 0) -- return (bind_variable_internal (nv->name, value, nvc->table, 0, flags)); -- return (bind_variable_internal (nameref_cell (nv), value, nvc->table, 0, flags)); -- } -- else -- v = nv; -- } -- else -- v = nv; -- } -- if (v) -- return (bind_variable_internal (v->name, value, nvc->table, 0, flags)); -- } -- } -- /* bind_variable_internal will handle nameref resolution in this case */ -- return (bind_variable_internal (name, value, global_variables->table, 0, flags)); --} -- --SHELL_VAR * --bind_global_variable (name, value, flags) -- const char *name; -- char *value; -- int flags; --{ -- SHELL_VAR *v, *nv; -- VAR_CONTEXT *vc, *nvc; -- int level; -- -- if (shell_variables == 0) -- create_variable_tables (); -- -- /* bind_variable_internal will handle nameref resolution in this case */ -- return (bind_variable_internal (name, value, global_variables->table, 0, flags)); --} -- --/* Make VAR, a simple shell variable, have value VALUE. Once assigned a -- value, variables are no longer invisible. This is a duplicate of part -- of the internals of bind_variable. If the variable is exported, or -- all modified variables should be exported, mark the variable for export -- and note that the export environment needs to be recreated. */ --SHELL_VAR * --bind_variable_value (var, value, aflags) -- SHELL_VAR *var; -- char *value; -- int aflags; --{ -- char *t; -- int invis; -- -- invis = invisible_p (var); -- VUNSETATTR (var, att_invisible); -- -- if (var->assign_func) -- { -- /* If we're appending, we need the old value, so use -- make_variable_value */ -- t = (aflags & ASS_APPEND) ? make_variable_value (var, value, aflags) : value; -- (*(var->assign_func)) (var, t, -1, 0); -- if (t != value && t) -- free (t); -- } -- else -- { -- t = make_variable_value (var, value, aflags); --#if defined (ARRAY_VARS) -- if ((aflags & ASS_NAMEREF) && (t == 0 || *t == 0 || (legal_identifier (t) == 0 && valid_array_reference (t) == 0))) --#else -- if ((aflags & ASS_NAMEREF) && (t == 0 || *t == 0 || legal_identifier (t) == 0)) --#endif -- { -- free (t); -- if (invis) -- VSETATTR (var, att_invisible); /* XXX */ -- return ((SHELL_VAR *)NULL); -- } -- FREE (value_cell (var)); -- var_setvalue (var, t); -- } -- -- INVALIDATE_EXPORTSTR (var); -- -- if (mark_modified_vars) -- VSETATTR (var, att_exported); -- -- if (exported_p (var)) -- array_needs_making = 1; -- -- return (var); --} -- --/* Bind/create a shell variable with the name LHS to the RHS. -- This creates or modifies a variable such that it is an integer. -- -- This used to be in expr.c, but it is here so that all of the -- variable binding stuff is localized. Since we don't want any -- recursive evaluation from bind_variable() (possible without this code, -- since bind_variable() calls the evaluator for variables with the integer -- attribute set), we temporarily turn off the integer attribute for each -- variable we set here, then turn it back on after binding as necessary. */ -- --SHELL_VAR * --bind_int_variable (lhs, rhs) -- char *lhs, *rhs; --{ -- register SHELL_VAR *v; -- int isint, isarr, implicitarray; -- -- isint = isarr = implicitarray = 0; --#if defined (ARRAY_VARS) -- if (valid_array_reference (lhs)) -- { -- isarr = 1; -- v = array_variable_part (lhs, (char **)0, (int *)0); -- } -- else --#endif -- v = find_variable (lhs); -- -- if (v) -- { -- isint = integer_p (v); -- VUNSETATTR (v, att_integer); --#if defined (ARRAY_VARS) -- if (array_p (v) && isarr == 0) -- implicitarray = 1; --#endif -- } -- --#if defined (ARRAY_VARS) -- if (isarr) -- v = assign_array_element (lhs, rhs, 0); -- else if (implicitarray) -- v = bind_array_variable (lhs, 0, rhs, 0); -- else --#endif -- v = bind_variable (lhs, rhs, 0); -- -- if (v && isint) -- VSETATTR (v, att_integer); -- -- VUNSETATTR (v, att_invisible); -- -- return (v); --} -- --SHELL_VAR * --bind_var_to_int (var, val) -- char *var; -- intmax_t val; --{ -- char ibuf[INT_STRLEN_BOUND (intmax_t) + 1], *p; -- -- p = fmtulong (val, 10, ibuf, sizeof (ibuf), 0); -- return (bind_int_variable (var, p)); --} -- --/* Do a function binding to a variable. You pass the name and -- the command to bind to. This conses the name and command. */ --SHELL_VAR * --bind_function (name, value) -- const char *name; -- COMMAND *value; --{ -- SHELL_VAR *entry; -- -- entry = find_function (name); -- if (entry == 0) -- { -- BUCKET_CONTENTS *elt; -- -- elt = hash_insert (savestring (name), shell_functions, HASH_NOSRCH); -- entry = new_shell_variable (name); -- elt->data = (PTR_T)entry; -- } -- else -- INVALIDATE_EXPORTSTR (entry); -- -- if (var_isset (entry)) -- dispose_command (function_cell (entry)); -- -- if (value) -- var_setfunc (entry, copy_command (value)); -- else -- var_setfunc (entry, 0); -- -- VSETATTR (entry, att_function); -- -- if (mark_modified_vars) -- VSETATTR (entry, att_exported); -- -- VUNSETATTR (entry, att_invisible); /* Just to be sure */ -- -- if (exported_p (entry)) -- array_needs_making = 1; -- --#if defined (PROGRAMMABLE_COMPLETION) -- set_itemlist_dirty (&it_functions); --#endif -- -- return (entry); --} -- --#if defined (DEBUGGER) --/* Bind a function definition, which includes source file and line number -- information in addition to the command, into the FUNCTION_DEF hash table.*/ --void --bind_function_def (name, value) -- const char *name; -- FUNCTION_DEF *value; --{ -- FUNCTION_DEF *entry; -- BUCKET_CONTENTS *elt; -- COMMAND *cmd; -- -- entry = find_function_def (name); -- if (entry) -- { -- dispose_function_def_contents (entry); -- entry = copy_function_def_contents (value, entry); -- } -- else -- { -- cmd = value->command; -- value->command = 0; -- entry = copy_function_def (value); -- value->command = cmd; -- -- elt = hash_insert (savestring (name), shell_function_defs, HASH_NOSRCH); -- elt->data = (PTR_T *)entry; -- } --} --#endif /* DEBUGGER */ -- --/* Add STRING, which is of the form foo=bar, to the temporary environment -- HASH_TABLE (temporary_env). The functions in execute_cmd.c are -- responsible for moving the main temporary env to one of the other -- temporary environments. The expansion code in subst.c calls this. */ --int --assign_in_env (word, flags) -- WORD_DESC *word; -- int flags; --{ -- int offset, aflags; -- char *name, *temp, *value; -- SHELL_VAR *var; -- const char *string; -- -- string = word->word; -- -- aflags = 0; -- offset = assignment (string, 0); -- name = savestring (string); -- value = (char *)NULL; -- -- if (name[offset] == '=') -- { -- name[offset] = 0; -- -- /* don't ignore the `+' when assigning temporary environment */ -- if (name[offset - 1] == '+') -- { -- name[offset - 1] = '\0'; -- aflags |= ASS_APPEND; -- } -- -- var = find_variable (name); -- if (var && (readonly_p (var) || noassign_p (var))) -- { -- if (readonly_p (var)) -- err_readonly (name); -- free (name); -- return (0); -- } -- -- temp = name + offset + 1; -- value = expand_assignment_string_to_string (temp, 0); -- -- if (var && (aflags & ASS_APPEND)) -- { -- temp = make_variable_value (var, value, aflags); -- FREE (value); -- value = temp; -- } -- } -- -- if (temporary_env == 0) -- temporary_env = hash_create (TEMPENV_HASH_BUCKETS); -- -- var = hash_lookup (name, temporary_env); -- if (var == 0) -- var = make_new_variable (name, temporary_env); -- else -- FREE (value_cell (var)); -- -- if (value == 0) -- { -- value = (char *)xmalloc (1); /* like do_assignment_internal */ -- value[0] = '\0'; -- } -- -- var_setvalue (var, value); -- var->attributes |= (att_exported|att_tempvar); -- var->context = variable_context; /* XXX */ -- -- INVALIDATE_EXPORTSTR (var); -- var->exportstr = mk_env_string (name, value, 0); -- -- array_needs_making = 1; -- -- if (flags) -- stupidly_hack_special_variables (name); -- -- if (echo_command_at_execute) -- /* The Korn shell prints the `+ ' in front of assignment statements, -- so we do too. */ -- xtrace_print_assignment (name, value, 0, 1); -- -- free (name); -- return 1; --} -- --/* **************************************************************** */ --/* */ --/* Copying variables */ --/* */ --/* **************************************************************** */ -- --#ifdef INCLUDE_UNUSED --/* Copy VAR to a new data structure and return that structure. */ --SHELL_VAR * --copy_variable (var) -- SHELL_VAR *var; --{ -- SHELL_VAR *copy = (SHELL_VAR *)NULL; -- -- if (var) -- { -- copy = (SHELL_VAR *)xmalloc (sizeof (SHELL_VAR)); -- -- copy->attributes = var->attributes; -- copy->name = savestring (var->name); -- -- if (function_p (var)) -- var_setfunc (copy, copy_command (function_cell (var))); --#if defined (ARRAY_VARS) -- else if (array_p (var)) -- var_setarray (copy, array_copy (array_cell (var))); -- else if (assoc_p (var)) -- var_setassoc (copy, assoc_copy (assoc_cell (var))); --#endif -- else if (nameref_cell (var)) /* XXX - nameref */ -- var_setref (copy, savestring (nameref_cell (var))); -- else if (value_cell (var)) /* XXX - nameref */ -- var_setvalue (copy, savestring (value_cell (var))); -- else -- var_setvalue (copy, (char *)NULL); -- -- copy->dynamic_value = var->dynamic_value; -- copy->assign_func = var->assign_func; -- -- copy->exportstr = COPY_EXPORTSTR (var); -- -- copy->context = var->context; -- } -- return (copy); --} --#endif -- --/* **************************************************************** */ --/* */ --/* Deleting and unsetting variables */ --/* */ --/* **************************************************************** */ -- --/* Dispose of the information attached to VAR. */ --static void --dispose_variable_value (var) -- SHELL_VAR *var; --{ -- if (function_p (var)) -- dispose_command (function_cell (var)); --#if defined (ARRAY_VARS) -- else if (array_p (var)) -- array_dispose (array_cell (var)); -- else if (assoc_p (var)) -- assoc_dispose (assoc_cell (var)); --#endif -- else if (nameref_p (var)) -- FREE (nameref_cell (var)); -- else -- FREE (value_cell (var)); --} -- --void --dispose_variable (var) -- SHELL_VAR *var; --{ -- if (var == 0) -- return; -- -- if (nofree_p (var) == 0) -- dispose_variable_value (var); -- -- FREE_EXPORTSTR (var); -- -- free (var->name); -- -- if (exported_p (var)) -- array_needs_making = 1; -- -- free (var); --} -- --/* Unset the shell variable referenced by NAME. Unsetting a nameref variable -- unsets the variable it resolves to but leaves the nameref alone. */ --int --unbind_variable (name) -- const char *name; --{ -- SHELL_VAR *v, *nv; -- int r; -- -- v = var_lookup (name, shell_variables); -- nv = (v && nameref_p (v)) ? find_variable_nameref (v) : (SHELL_VAR *)NULL; -- -- r = nv ? makunbound (nv->name, shell_variables) : makunbound (name, shell_variables); -- return r; --} -- --/* Unbind NAME, where NAME is assumed to be a nameref variable */ --int --unbind_nameref (name) -- const char *name; --{ -- SHELL_VAR *v; -- -- v = var_lookup (name, shell_variables); -- if (v && nameref_p (v)) -- return makunbound (name, shell_variables); -- return 0; --} -- --/* Unset the shell function named NAME. */ --int --unbind_func (name) -- const char *name; --{ -- BUCKET_CONTENTS *elt; -- SHELL_VAR *func; -- -- elt = hash_remove (name, shell_functions, 0); -- -- if (elt == 0) -- return -1; -- --#if defined (PROGRAMMABLE_COMPLETION) -- set_itemlist_dirty (&it_functions); --#endif -- -- func = (SHELL_VAR *)elt->data; -- if (func) -- { -- if (exported_p (func)) -- array_needs_making++; -- dispose_variable (func); -- } -- -- free (elt->key); -- free (elt); -- -- return 0; --} -- --#if defined (DEBUGGER) --int --unbind_function_def (name) -- const char *name; --{ -- BUCKET_CONTENTS *elt; -- FUNCTION_DEF *funcdef; -- -- elt = hash_remove (name, shell_function_defs, 0); -- -- if (elt == 0) -- return -1; -- -- funcdef = (FUNCTION_DEF *)elt->data; -- if (funcdef) -- dispose_function_def (funcdef); -- -- free (elt->key); -- free (elt); -- -- return 0; --} --#endif /* DEBUGGER */ -- --int --delete_var (name, vc) -- const char *name; -- VAR_CONTEXT *vc; --{ -- BUCKET_CONTENTS *elt; -- SHELL_VAR *old_var; -- VAR_CONTEXT *v; -- -- for (elt = (BUCKET_CONTENTS *)NULL, v = vc; v; v = v->down) -- if (elt = hash_remove (name, v->table, 0)) -- break; -- -- if (elt == 0) -- return (-1); -- -- old_var = (SHELL_VAR *)elt->data; -- free (elt->key); -- free (elt); -- -- dispose_variable (old_var); -- return (0); --} -- --/* Make the variable associated with NAME go away. HASH_LIST is the -- hash table from which this variable should be deleted (either -- shell_variables or shell_functions). -- Returns non-zero if the variable couldn't be found. */ --int --makunbound (name, vc) -- const char *name; -- VAR_CONTEXT *vc; --{ -- BUCKET_CONTENTS *elt, *new_elt; -- SHELL_VAR *old_var; -- VAR_CONTEXT *v; -- char *t; -- -- for (elt = (BUCKET_CONTENTS *)NULL, v = vc; v; v = v->down) -- if (elt = hash_remove (name, v->table, 0)) -- break; -- -- if (elt == 0) -- return (-1); -- -- old_var = (SHELL_VAR *)elt->data; -- -- if (old_var && exported_p (old_var)) -- array_needs_making++; -- -- /* If we're unsetting a local variable and we're still executing inside -- the function, just mark the variable as invisible. The function -- eventually called by pop_var_context() will clean it up later. This -- must be done so that if the variable is subsequently assigned a new -- value inside the function, the `local' attribute is still present. -- We also need to add it back into the correct hash table. */ -- if (old_var && local_p (old_var) && variable_context == old_var->context) -- { -- if (nofree_p (old_var)) -- var_setvalue (old_var, (char *)NULL); --#if defined (ARRAY_VARS) -- else if (array_p (old_var)) -- array_dispose (array_cell (old_var)); -- else if (assoc_p (old_var)) -- assoc_dispose (assoc_cell (old_var)); --#endif -- else if (nameref_p (old_var)) -- FREE (nameref_cell (old_var)); -- else -- FREE (value_cell (old_var)); -- /* Reset the attributes. Preserve the export attribute if the variable -- came from a temporary environment. Make sure it stays local, and -- make it invisible. */ -- old_var->attributes = (exported_p (old_var) && tempvar_p (old_var)) ? att_exported : 0; -- VSETATTR (old_var, att_local); -- VSETATTR (old_var, att_invisible); -- var_setvalue (old_var, (char *)NULL); -- INVALIDATE_EXPORTSTR (old_var); -- -- new_elt = hash_insert (savestring (old_var->name), v->table, 0); -- new_elt->data = (PTR_T)old_var; -- stupidly_hack_special_variables (old_var->name); -- -- free (elt->key); -- free (elt); -- return (0); -- } -- -- /* Have to save a copy of name here, because it might refer to -- old_var->name. If so, stupidly_hack_special_variables will -- reference freed memory. */ -- t = savestring (name); -- -- free (elt->key); -- free (elt); -- -- dispose_variable (old_var); -- stupidly_hack_special_variables (t); -- free (t); -- -- return (0); --} -- --/* Get rid of all of the variables in the current context. */ --void --kill_all_local_variables () --{ -- VAR_CONTEXT *vc; -- -- for (vc = shell_variables; vc; vc = vc->down) -- if (vc_isfuncenv (vc) && vc->scope == variable_context) -- break; -- if (vc == 0) -- return; /* XXX */ -- -- if (vc->table && vc_haslocals (vc)) -- { -- delete_all_variables (vc->table); -- hash_dispose (vc->table); -- } -- vc->table = (HASH_TABLE *)NULL; --} -- --static void --free_variable_hash_data (data) -- PTR_T data; --{ -- SHELL_VAR *var; -- -- var = (SHELL_VAR *)data; -- dispose_variable (var); --} -- --/* Delete the entire contents of the hash table. */ --void --delete_all_variables (hashed_vars) -- HASH_TABLE *hashed_vars; --{ -- hash_flush (hashed_vars, free_variable_hash_data); --} -- --/* **************************************************************** */ --/* */ --/* Setting variable attributes */ --/* */ --/* **************************************************************** */ -- --#define FIND_OR_MAKE_VARIABLE(name, entry) \ -- do \ -- { \ -- entry = find_variable (name); \ -- if (!entry) \ -- { \ -- entry = bind_variable (name, "", 0); \ -- if (!no_invisible_vars && entry) entry->attributes |= att_invisible; \ -- } \ -- } \ -- while (0) -- --/* Make the variable associated with NAME be readonly. -- If NAME does not exist yet, create it. */ --void --set_var_read_only (name) -- char *name; --{ -- SHELL_VAR *entry; -- -- FIND_OR_MAKE_VARIABLE (name, entry); -- VSETATTR (entry, att_readonly); --} -- --#ifdef INCLUDE_UNUSED --/* Make the function associated with NAME be readonly. -- If NAME does not exist, we just punt, like auto_export code below. */ --void --set_func_read_only (name) -- const char *name; --{ -- SHELL_VAR *entry; -- -- entry = find_function (name); -- if (entry) -- VSETATTR (entry, att_readonly); --} -- --/* Make the variable associated with NAME be auto-exported. -- If NAME does not exist yet, create it. */ --void --set_var_auto_export (name) -- char *name; --{ -- SHELL_VAR *entry; -- -- FIND_OR_MAKE_VARIABLE (name, entry); -- set_auto_export (entry); --} -- --/* Make the function associated with NAME be auto-exported. */ --void --set_func_auto_export (name) -- const char *name; --{ -- SHELL_VAR *entry; -- -- entry = find_function (name); -- if (entry) -- set_auto_export (entry); --} --#endif -- --/* **************************************************************** */ --/* */ --/* Creating lists of variables */ --/* */ --/* **************************************************************** */ -- --static VARLIST * --vlist_alloc (nentries) -- int nentries; --{ -- VARLIST *vlist; -- -- vlist = (VARLIST *)xmalloc (sizeof (VARLIST)); -- vlist->list = (SHELL_VAR **)xmalloc ((nentries + 1) * sizeof (SHELL_VAR *)); -- vlist->list_size = nentries; -- vlist->list_len = 0; -- vlist->list[0] = (SHELL_VAR *)NULL; -- -- return vlist; --} -- --static VARLIST * --vlist_realloc (vlist, n) -- VARLIST *vlist; -- int n; --{ -- if (vlist == 0) -- return (vlist = vlist_alloc (n)); -- if (n > vlist->list_size) -- { -- vlist->list_size = n; -- vlist->list = (SHELL_VAR **)xrealloc (vlist->list, (vlist->list_size + 1) * sizeof (SHELL_VAR *)); -- } -- return vlist; --} -- --static void --vlist_add (vlist, var, flags) -- VARLIST *vlist; -- SHELL_VAR *var; -- int flags; --{ -- register int i; -- -- for (i = 0; i < vlist->list_len; i++) -- if (STREQ (var->name, vlist->list[i]->name)) -- break; -- if (i < vlist->list_len) -- return; -- -- if (i >= vlist->list_size) -- vlist = vlist_realloc (vlist, vlist->list_size + 16); -- -- vlist->list[vlist->list_len++] = var; -- vlist->list[vlist->list_len] = (SHELL_VAR *)NULL; --} -- --/* Map FUNCTION over the variables in VAR_HASH_TABLE. Return an array of the -- variables for which FUNCTION returns a non-zero value. A NULL value -- for FUNCTION means to use all variables. */ --SHELL_VAR ** --map_over (function, vc) -- sh_var_map_func_t *function; -- VAR_CONTEXT *vc; --{ -- VAR_CONTEXT *v; -- VARLIST *vlist; -- SHELL_VAR **ret; -- int nentries; -- -- for (nentries = 0, v = vc; v; v = v->down) -- nentries += HASH_ENTRIES (v->table); -- -- if (nentries == 0) -- return (SHELL_VAR **)NULL; -- -- vlist = vlist_alloc (nentries); -- -- for (v = vc; v; v = v->down) -- flatten (v->table, function, vlist, 0); -- -- ret = vlist->list; -- free (vlist); -- return ret; --} -- --SHELL_VAR ** --map_over_funcs (function) -- sh_var_map_func_t *function; --{ -- VARLIST *vlist; -- SHELL_VAR **ret; -- -- if (shell_functions == 0 || HASH_ENTRIES (shell_functions) == 0) -- return ((SHELL_VAR **)NULL); -- -- vlist = vlist_alloc (HASH_ENTRIES (shell_functions)); -- -- flatten (shell_functions, function, vlist, 0); -- -- ret = vlist->list; -- free (vlist); -- return ret; --} -- --/* Flatten VAR_HASH_TABLE, applying FUNC to each member and adding those -- elements for which FUNC succeeds to VLIST->list. FLAGS is reserved -- for future use. Only unique names are added to VLIST. If FUNC is -- NULL, each variable in VAR_HASH_TABLE is added to VLIST. If VLIST is -- NULL, FUNC is applied to each SHELL_VAR in VAR_HASH_TABLE. If VLIST -- and FUNC are both NULL, nothing happens. */ --static void --flatten (var_hash_table, func, vlist, flags) -- HASH_TABLE *var_hash_table; -- sh_var_map_func_t *func; -- VARLIST *vlist; -- int flags; --{ -- register int i; -- register BUCKET_CONTENTS *tlist; -- int r; -- SHELL_VAR *var; -- -- if (var_hash_table == 0 || (HASH_ENTRIES (var_hash_table) == 0) || (vlist == 0 && func == 0)) -- return; -- -- for (i = 0; i < var_hash_table->nbuckets; i++) -- { -- for (tlist = hash_items (i, var_hash_table); tlist; tlist = tlist->next) -- { -- var = (SHELL_VAR *)tlist->data; -- -- r = func ? (*func) (var) : 1; -- if (r && vlist) -- vlist_add (vlist, var, flags); -- } -- } --} -- --void --sort_variables (array) -- SHELL_VAR **array; --{ -- qsort (array, strvec_len ((char **)array), sizeof (SHELL_VAR *), (QSFUNC *)qsort_var_comp); --} -- --static int --qsort_var_comp (var1, var2) -- SHELL_VAR **var1, **var2; --{ -- int result; -- -- if ((result = (*var1)->name[0] - (*var2)->name[0]) == 0) -- result = strcmp ((*var1)->name, (*var2)->name); -- -- return (result); --} -- --/* Apply FUNC to each variable in SHELL_VARIABLES, adding each one for -- which FUNC succeeds to an array of SHELL_VAR *s. Returns the array. */ --static SHELL_VAR ** --vapply (func) -- sh_var_map_func_t *func; --{ -- SHELL_VAR **list; -- -- list = map_over (func, shell_variables); -- if (list /* && posixly_correct */) -- sort_variables (list); -- return (list); --} -- --/* Apply FUNC to each variable in SHELL_FUNCTIONS, adding each one for -- which FUNC succeeds to an array of SHELL_VAR *s. Returns the array. */ --static SHELL_VAR ** --fapply (func) -- sh_var_map_func_t *func; --{ -- SHELL_VAR **list; -- -- list = map_over_funcs (func); -- if (list /* && posixly_correct */) -- sort_variables (list); -- return (list); --} -- --/* Create a NULL terminated array of all the shell variables. */ --SHELL_VAR ** --all_shell_variables () --{ -- return (vapply ((sh_var_map_func_t *)NULL)); --} -- --/* Create a NULL terminated array of all the shell functions. */ --SHELL_VAR ** --all_shell_functions () --{ -- return (fapply ((sh_var_map_func_t *)NULL)); --} -- --static int --visible_var (var) -- SHELL_VAR *var; --{ -- return (invisible_p (var) == 0); --} -- --SHELL_VAR ** --all_visible_functions () --{ -- return (fapply (visible_var)); --} -- --SHELL_VAR ** --all_visible_variables () --{ -- return (vapply (visible_var)); --} -- --/* Return non-zero if the variable VAR is visible and exported. Array -- variables cannot be exported. */ --static int --visible_and_exported (var) -- SHELL_VAR *var; --{ -- return (invisible_p (var) == 0 && exported_p (var)); --} -- --/* Candidate variables for the export environment are either valid variables -- with the export attribute or invalid variables inherited from the initial -- environment and simply passed through. */ --static int --export_environment_candidate (var) -- SHELL_VAR *var; --{ -- return (exported_p (var) && (invisible_p (var) == 0 || imported_p (var))); --} -- --/* Return non-zero if VAR is a local variable in the current context and -- is exported. */ --static int --local_and_exported (var) -- SHELL_VAR *var; --{ -- return (invisible_p (var) == 0 && local_p (var) && var->context == variable_context && exported_p (var)); --} -- --SHELL_VAR ** --all_exported_variables () --{ -- return (vapply (visible_and_exported)); --} -- --SHELL_VAR ** --local_exported_variables () --{ -- return (vapply (local_and_exported)); --} -- --static int --variable_in_context (var) -- SHELL_VAR *var; --{ -- return (invisible_p (var) == 0 && local_p (var) && var->context == variable_context); --} -- --SHELL_VAR ** --all_local_variables () --{ -- VARLIST *vlist; -- SHELL_VAR **ret; -- VAR_CONTEXT *vc; -- -- vc = shell_variables; -- for (vc = shell_variables; vc; vc = vc->down) -- if (vc_isfuncenv (vc) && vc->scope == variable_context) -- break; -- -- if (vc == 0) -- { -- internal_error (_("all_local_variables: no function context at current scope")); -- return (SHELL_VAR **)NULL; -- } -- if (vc->table == 0 || HASH_ENTRIES (vc->table) == 0 || vc_haslocals (vc) == 0) -- return (SHELL_VAR **)NULL; -- -- vlist = vlist_alloc (HASH_ENTRIES (vc->table)); -- -- flatten (vc->table, variable_in_context, vlist, 0); -- -- ret = vlist->list; -- free (vlist); -- if (ret) -- sort_variables (ret); -- return ret; --} -- --#if defined (ARRAY_VARS) --/* Return non-zero if the variable VAR is visible and an array. */ --static int --visible_array_vars (var) -- SHELL_VAR *var; --{ -- return (invisible_p (var) == 0 && array_p (var)); --} -- --SHELL_VAR ** --all_array_variables () --{ -- return (vapply (visible_array_vars)); --} --#endif /* ARRAY_VARS */ -- --char ** --all_variables_matching_prefix (prefix) -- const char *prefix; --{ -- SHELL_VAR **varlist; -- char **rlist; -- int vind, rind, plen; -- -- plen = STRLEN (prefix); -- varlist = all_visible_variables (); -- for (vind = 0; varlist && varlist[vind]; vind++) -- ; -- if (varlist == 0 || vind == 0) -- return ((char **)NULL); -- rlist = strvec_create (vind + 1); -- for (vind = rind = 0; varlist[vind]; vind++) -- { -- if (plen == 0 || STREQN (prefix, varlist[vind]->name, plen)) -- rlist[rind++] = savestring (varlist[vind]->name); -- } -- rlist[rind] = (char *)0; -- free (varlist); -- -- return rlist; --} -- --/* **************************************************************** */ --/* */ --/* Managing temporary variable scopes */ --/* */ --/* **************************************************************** */ -- --/* Make variable NAME have VALUE in the temporary environment. */ --static SHELL_VAR * --bind_tempenv_variable (name, value) -- const char *name; -- char *value; --{ -- SHELL_VAR *var; -- -- var = temporary_env ? hash_lookup (name, temporary_env) : (SHELL_VAR *)NULL; -- -- if (var) -- { -- FREE (value_cell (var)); -- var_setvalue (var, savestring (value)); -- INVALIDATE_EXPORTSTR (var); -- } -- -- return (var); --} -- --/* Find a variable in the temporary environment that is named NAME. -- Return the SHELL_VAR *, or NULL if not found. */ --SHELL_VAR * --find_tempenv_variable (name) -- const char *name; --{ -- return (temporary_env ? hash_lookup (name, temporary_env) : (SHELL_VAR *)NULL); --} -- --char **tempvar_list; --int tvlist_ind; -- --/* Push the variable described by (SHELL_VAR *)DATA down to the next -- variable context from the temporary environment. */ --static void --push_temp_var (data) -- PTR_T data; --{ -- SHELL_VAR *var, *v; -- HASH_TABLE *binding_table; -- -- var = (SHELL_VAR *)data; -- -- binding_table = shell_variables->table; -- if (binding_table == 0) -- { -- if (shell_variables == global_variables) -- /* shouldn't happen */ -- binding_table = shell_variables->table = global_variables->table = hash_create (0); -- else -- binding_table = shell_variables->table = hash_create (TEMPENV_HASH_BUCKETS); -- } -- -- v = bind_variable_internal (var->name, value_cell (var), binding_table, 0, 0); -- -- /* XXX - should we set the context here? It shouldn't matter because of how -- assign_in_env works, but might want to check. */ -- if (binding_table == global_variables->table) /* XXX */ -- var->attributes &= ~(att_tempvar|att_propagate); -- else -- { -- var->attributes |= att_propagate; -- if (binding_table == shell_variables->table) -- shell_variables->flags |= VC_HASTMPVAR; -- } -- v->attributes |= var->attributes; -- -- if (find_special_var (var->name) >= 0) -- tempvar_list[tvlist_ind++] = savestring (var->name); -- -- dispose_variable (var); --} -- --static void --propagate_temp_var (data) -- PTR_T data; --{ -- SHELL_VAR *var; -- -- var = (SHELL_VAR *)data; -- if (tempvar_p (var) && (var->attributes & att_propagate)) -- push_temp_var (data); -- else -- { -- if (find_special_var (var->name) >= 0) -- tempvar_list[tvlist_ind++] = savestring (var->name); -- dispose_variable (var); -- } --} -- --/* Free the storage used in the hash table for temporary -- environment variables. PUSHF is a function to be called -- to free each hash table entry. It takes care of pushing variables -- to previous scopes if appropriate. PUSHF stores names of variables -- that require special handling (e.g., IFS) on tempvar_list, so this -- function can call stupidly_hack_special_variables on all the -- variables in the list when the temporary hash table is destroyed. */ --static void --dispose_temporary_env (pushf) -- sh_free_func_t *pushf; --{ -- int i; -- -- tempvar_list = strvec_create (HASH_ENTRIES (temporary_env) + 1); -- tempvar_list[tvlist_ind = 0] = 0; -- -- hash_flush (temporary_env, pushf); -- hash_dispose (temporary_env); -- temporary_env = (HASH_TABLE *)NULL; -- -- tempvar_list[tvlist_ind] = 0; -- -- array_needs_making = 1; -- --#if 0 -- sv_ifs ("IFS"); /* XXX here for now -- check setifs in assign_in_env */ --#endif -- for (i = 0; i < tvlist_ind; i++) -- stupidly_hack_special_variables (tempvar_list[i]); -- -- strvec_dispose (tempvar_list); -- tempvar_list = 0; -- tvlist_ind = 0; --} -- --void --dispose_used_env_vars () --{ -- if (temporary_env) -- { -- dispose_temporary_env (propagate_temp_var); -- maybe_make_export_env (); -- } --} -- --/* Take all of the shell variables in the temporary environment HASH_TABLE -- and make shell variables from them at the current variable context. */ --void --merge_temporary_env () --{ -- if (temporary_env) -- dispose_temporary_env (push_temp_var); --} -- --/* **************************************************************** */ --/* */ --/* Creating and manipulating the environment */ --/* */ --/* **************************************************************** */ -- --static inline char * --mk_env_string (name, value, isfunc) -- const char *name, *value; -- int isfunc; --{ -- size_t name_len, value_len; -- char *p, *q; -- -- name_len = strlen (name); -- value_len = STRLEN (value); -- -- /* If we are exporting a shell function, construct the encoded function -- name. */ -- if (isfunc && value) -- { -- p = (char *)xmalloc (BASHFUNC_PREFLEN + name_len + BASHFUNC_SUFFLEN + value_len + 2); -- q = p; -- memcpy (q, BASHFUNC_PREFIX, BASHFUNC_PREFLEN); -- q += BASHFUNC_PREFLEN; -- memcpy (q, name, name_len); -- q += name_len; -- memcpy (q, BASHFUNC_SUFFIX, BASHFUNC_SUFFLEN); -- q += BASHFUNC_SUFFLEN; -- } -- else -- { -- p = (char *)xmalloc (2 + name_len + value_len); -- memcpy (p, name, name_len); -- q = p + name_len; -- } -- -- q[0] = '='; -- if (value && *value) -- memcpy (q + 1, value, value_len + 1); -- else -- q[1] = '\0'; -- -- return (p); --} -- --#ifdef DEBUG --/* Debugging */ --static int --valid_exportstr (v) -- SHELL_VAR *v; --{ -- char *s; -- -- s = v->exportstr; -- if (s == 0) -- { -- internal_error (_("%s has null exportstr"), v->name); -- return (0); -- } -- if (legal_variable_starter ((unsigned char)*s) == 0) -- { -- internal_error (_("invalid character %d in exportstr for %s"), *s, v->name); -- return (0); -- } -- for (s = v->exportstr + 1; s && *s; s++) -- { -- if (*s == '=') -- break; -- if (legal_variable_char ((unsigned char)*s) == 0) -- { -- internal_error (_("invalid character %d in exportstr for %s"), *s, v->name); -- return (0); -- } -- } -- if (*s != '=') -- { -- internal_error (_("no `=' in exportstr for %s"), v->name); -- return (0); -- } -- return (1); --} --#endif -- --static char ** --make_env_array_from_var_list (vars) -- SHELL_VAR **vars; --{ -- register int i, list_index; -- register SHELL_VAR *var; -- char **list, *value; -- -- list = strvec_create ((1 + strvec_len ((char **)vars))); -- --#define USE_EXPORTSTR (value == var->exportstr) -- -- for (i = 0, list_index = 0; var = vars[i]; i++) -- { --#if defined (__CYGWIN__) -- /* We don't use the exportstr stuff on Cygwin at all. */ -- INVALIDATE_EXPORTSTR (var); --#endif -- if (var->exportstr) -- value = var->exportstr; -- else if (function_p (var)) -- value = named_function_string ((char *)NULL, function_cell (var), 0); --#if defined (ARRAY_VARS) -- else if (array_p (var)) --# if ARRAY_EXPORT -- value = array_to_assignment_string (array_cell (var)); --# else -- continue; /* XXX array vars cannot yet be exported */ --# endif /* ARRAY_EXPORT */ -- else if (assoc_p (var)) --# if 0 -- value = assoc_to_assignment_string (assoc_cell (var)); --# else -- continue; /* XXX associative array vars cannot yet be exported */ --# endif --#endif -- else -- value = value_cell (var); -- -- if (value) -- { -- /* Gee, I'd like to get away with not using savestring() if we're -- using the cached exportstr... */ -- list[list_index] = USE_EXPORTSTR ? savestring (value) -- : mk_env_string (var->name, value, function_p (var)); -- -- if (USE_EXPORTSTR == 0) -- SAVE_EXPORTSTR (var, list[list_index]); -- -- list_index++; --#undef USE_EXPORTSTR -- --#if 0 /* not yet */ --#if defined (ARRAY_VARS) -- if (array_p (var) || assoc_p (var)) -- free (value); --#endif --#endif -- } -- } -- -- list[list_index] = (char *)NULL; -- return (list); --} -- --/* Make an array of assignment statements from the hash table -- HASHED_VARS which contains SHELL_VARs. Only visible, exported -- variables are eligible. */ --static char ** --make_var_export_array (vcxt) -- VAR_CONTEXT *vcxt; --{ -- char **list; -- SHELL_VAR **vars; -- --#if 0 -- vars = map_over (visible_and_exported, vcxt); --#else -- vars = map_over (export_environment_candidate, vcxt); --#endif -- -- if (vars == 0) -- return (char **)NULL; -- -- list = make_env_array_from_var_list (vars); -- -- free (vars); -- return (list); --} -- --static char ** --make_func_export_array () --{ -- char **list; -- SHELL_VAR **vars; -- -- vars = map_over_funcs (visible_and_exported); -- if (vars == 0) -- return (char **)NULL; -- -- list = make_env_array_from_var_list (vars); -- -- free (vars); -- return (list); --} -- --/* Add ENVSTR to the end of the exported environment, EXPORT_ENV. */ --#define add_to_export_env(envstr,do_alloc) \ --do \ -- { \ -- if (export_env_index >= (export_env_size - 1)) \ -- { \ -- export_env_size += 16; \ -- export_env = strvec_resize (export_env, export_env_size); \ -- environ = export_env; \ -- } \ -- export_env[export_env_index++] = (do_alloc) ? savestring (envstr) : envstr; \ -- export_env[export_env_index] = (char *)NULL; \ -- } while (0) -- --/* Add ASSIGN to EXPORT_ENV, or supercede a previous assignment in the -- array with the same left-hand side. Return the new EXPORT_ENV. */ --char ** --add_or_supercede_exported_var (assign, do_alloc) -- char *assign; -- int do_alloc; --{ -- register int i; -- int equal_offset; -- -- equal_offset = assignment (assign, 0); -- if (equal_offset == 0) -- return (export_env); -- -- /* If this is a function, then only supersede the function definition. -- We do this by including the `=() {' in the comparison, like -- initialize_shell_variables does. */ -- if (assign[equal_offset + 1] == '(' && -- strncmp (assign + equal_offset + 2, ") {", 3) == 0) /* } */ -- equal_offset += 4; -- -- for (i = 0; i < export_env_index; i++) -- { -- if (STREQN (assign, export_env[i], equal_offset + 1)) -- { -- free (export_env[i]); -- export_env[i] = do_alloc ? savestring (assign) : assign; -- return (export_env); -- } -- } -- add_to_export_env (assign, do_alloc); -- return (export_env); --} -- --static void --add_temp_array_to_env (temp_array, do_alloc, do_supercede) -- char **temp_array; -- int do_alloc, do_supercede; --{ -- register int i; -- -- if (temp_array == 0) -- return; -- -- for (i = 0; temp_array[i]; i++) -- { -- if (do_supercede) -- export_env = add_or_supercede_exported_var (temp_array[i], do_alloc); -- else -- add_to_export_env (temp_array[i], do_alloc); -- } -- -- free (temp_array); --} -- --/* Make the environment array for the command about to be executed, if the -- array needs making. Otherwise, do nothing. If a shell action could -- change the array that commands receive for their environment, then the -- code should `array_needs_making++'. -- -- The order to add to the array is: -- temporary_env -- list of var contexts whose head is shell_variables -- shell_functions -- -- This is the shell variable lookup order. We add only new variable -- names at each step, which allows local variables and variables in -- the temporary environments to shadow variables in the global (or -- any previous) scope. --*/ -- --static int --n_shell_variables () --{ -- VAR_CONTEXT *vc; -- int n; -- -- for (n = 0, vc = shell_variables; vc; vc = vc->down) -- n += HASH_ENTRIES (vc->table); -- return n; --} -- --int --chkexport (name) -- char *name; --{ -- SHELL_VAR *v; -- -- v = find_variable (name); -- if (v && exported_p (v)) -- { -- array_needs_making = 1; -- maybe_make_export_env (); -- return 1; -- } -- return 0; --} -- --void --maybe_make_export_env () --{ -- register char **temp_array; -- int new_size; -- VAR_CONTEXT *tcxt; -- -- if (array_needs_making) -- { -- if (export_env) -- strvec_flush (export_env); -- -- /* Make a guess based on how many shell variables and functions we -- have. Since there will always be array variables, and array -- variables are not (yet) exported, this will always be big enough -- for the exported variables and functions. */ -- new_size = n_shell_variables () + HASH_ENTRIES (shell_functions) + 1 + -- HASH_ENTRIES (temporary_env); -- if (new_size > export_env_size) -- { -- export_env_size = new_size; -- export_env = strvec_resize (export_env, export_env_size); -- environ = export_env; -- } -- export_env[export_env_index = 0] = (char *)NULL; -- -- /* Make a dummy variable context from the temporary_env, stick it on -- the front of shell_variables, call make_var_export_array on the -- whole thing to flatten it, and convert the list of SHELL_VAR *s -- to the form needed by the environment. */ -- if (temporary_env) -- { -- tcxt = new_var_context ((char *)NULL, 0); -- tcxt->table = temporary_env; -- tcxt->down = shell_variables; -- } -- else -- tcxt = shell_variables; -- -- temp_array = make_var_export_array (tcxt); -- if (temp_array) -- add_temp_array_to_env (temp_array, 0, 0); -- -- if (tcxt != shell_variables) -- free (tcxt); -- --#if defined (RESTRICTED_SHELL) -- /* Restricted shells may not export shell functions. */ -- temp_array = restricted ? (char **)0 : make_func_export_array (); --#else -- temp_array = make_func_export_array (); --#endif -- if (temp_array) -- add_temp_array_to_env (temp_array, 0, 0); -- -- array_needs_making = 0; -- } --} -- --/* This is an efficiency hack. PWD and OLDPWD are auto-exported, so -- we will need to remake the exported environment every time we -- change directories. `_' is always put into the environment for -- every external command, so without special treatment it will always -- cause the environment to be remade. -- -- If there is no other reason to make the exported environment, we can -- just update the variables in place and mark the exported environment -- as no longer needing a remake. */ --void --update_export_env_inplace (env_prefix, preflen, value) -- char *env_prefix; -- int preflen; -- char *value; --{ -- char *evar; -- -- evar = (char *)xmalloc (STRLEN (value) + preflen + 1); -- strcpy (evar, env_prefix); -- if (value) -- strcpy (evar + preflen, value); -- export_env = add_or_supercede_exported_var (evar, 0); --} -- --/* We always put _ in the environment as the name of this command. */ --void --put_command_name_into_env (command_name) -- char *command_name; --{ -- update_export_env_inplace ("_=", 2, command_name); --} -- --/* **************************************************************** */ --/* */ --/* Managing variable contexts */ --/* */ --/* **************************************************************** */ -- --/* Allocate and return a new variable context with NAME and FLAGS. -- NAME can be NULL. */ -- --VAR_CONTEXT * --new_var_context (name, flags) -- char *name; -- int flags; --{ -- VAR_CONTEXT *vc; -- -- vc = (VAR_CONTEXT *)xmalloc (sizeof (VAR_CONTEXT)); -- vc->name = name ? savestring (name) : (char *)NULL; -- vc->scope = variable_context; -- vc->flags = flags; -- -- vc->up = vc->down = (VAR_CONTEXT *)NULL; -- vc->table = (HASH_TABLE *)NULL; -- -- return vc; --} -- --/* Free a variable context and its data, including the hash table. Dispose -- all of the variables. */ --void --dispose_var_context (vc) -- VAR_CONTEXT *vc; --{ -- FREE (vc->name); -- -- if (vc->table) -- { -- delete_all_variables (vc->table); -- hash_dispose (vc->table); -- } -- -- free (vc); --} -- --/* Set VAR's scope level to the current variable context. */ --static int --set_context (var) -- SHELL_VAR *var; --{ -- return (var->context = variable_context); --} -- --/* Make a new variable context with NAME and FLAGS and a HASH_TABLE of -- temporary variables, and push it onto shell_variables. This is -- for shell functions. */ --VAR_CONTEXT * --push_var_context (name, flags, tempvars) -- char *name; -- int flags; -- HASH_TABLE *tempvars; --{ -- VAR_CONTEXT *vc; -- -- vc = new_var_context (name, flags); -- vc->table = tempvars; -- if (tempvars) -- { -- /* Have to do this because the temp environment was created before -- variable_context was incremented. */ -- flatten (tempvars, set_context, (VARLIST *)NULL, 0); -- vc->flags |= VC_HASTMPVAR; -- } -- vc->down = shell_variables; -- shell_variables->up = vc; -- -- return (shell_variables = vc); --} -- --static void --push_func_var (data) -- PTR_T data; --{ -- SHELL_VAR *var, *v; -- -- var = (SHELL_VAR *)data; -- -- if (tempvar_p (var) && (posixly_correct || (var->attributes & att_propagate))) -- { -- /* Make sure we have a hash table to store the variable in while it is -- being propagated down to the global variables table. Create one if -- we have to */ -- if ((vc_isfuncenv (shell_variables) || vc_istempenv (shell_variables)) && shell_variables->table == 0) -- shell_variables->table = hash_create (0); -- /* XXX - should we set v->context here? */ -- v = bind_variable_internal (var->name, value_cell (var), shell_variables->table, 0, 0); -- if (shell_variables == global_variables) -- var->attributes &= ~(att_tempvar|att_propagate); -- else -- shell_variables->flags |= VC_HASTMPVAR; -- v->attributes |= var->attributes; -- } -- else -- stupidly_hack_special_variables (var->name); /* XXX */ -- -- dispose_variable (var); --} -- --/* Pop the top context off of VCXT and dispose of it, returning the rest of -- the stack. */ --void --pop_var_context () --{ -- VAR_CONTEXT *ret, *vcxt; -- -- vcxt = shell_variables; -- if (vc_isfuncenv (vcxt) == 0) -- { -- internal_error (_("pop_var_context: head of shell_variables not a function context")); -- return; -- } -- -- if (ret = vcxt->down) -- { -- ret->up = (VAR_CONTEXT *)NULL; -- shell_variables = ret; -- if (vcxt->table) -- hash_flush (vcxt->table, push_func_var); -- dispose_var_context (vcxt); -- } -- else -- internal_error (_("pop_var_context: no global_variables context")); --} -- --/* Delete the HASH_TABLEs for all variable contexts beginning at VCXT, and -- all of the VAR_CONTEXTs except GLOBAL_VARIABLES. */ --void --delete_all_contexts (vcxt) -- VAR_CONTEXT *vcxt; --{ -- VAR_CONTEXT *v, *t; -- -- for (v = vcxt; v != global_variables; v = t) -- { -- t = v->down; -- dispose_var_context (v); -- } -- -- delete_all_variables (global_variables->table); -- shell_variables = global_variables; --} -- --/* **************************************************************** */ --/* */ --/* Pushing and Popping temporary variable scopes */ --/* */ --/* **************************************************************** */ -- --VAR_CONTEXT * --push_scope (flags, tmpvars) -- int flags; -- HASH_TABLE *tmpvars; --{ -- return (push_var_context ((char *)NULL, flags, tmpvars)); --} -- --static void --push_exported_var (data) -- PTR_T data; --{ -- SHELL_VAR *var, *v; -- -- var = (SHELL_VAR *)data; -- -- /* If a temp var had its export attribute set, or it's marked to be -- propagated, bind it in the previous scope before disposing it. */ -- /* XXX - This isn't exactly right, because all tempenv variables have the -- export attribute set. */ --#if 0 -- if (exported_p (var) || (var->attributes & att_propagate)) --#else -- if (tempvar_p (var) && exported_p (var) && (var->attributes & att_propagate)) --#endif -- { -- var->attributes &= ~att_tempvar; /* XXX */ -- v = bind_variable_internal (var->name, value_cell (var), shell_variables->table, 0, 0); -- if (shell_variables == global_variables) -- var->attributes &= ~att_propagate; -- v->attributes |= var->attributes; -- } -- else -- stupidly_hack_special_variables (var->name); /* XXX */ -- -- dispose_variable (var); --} -- --void --pop_scope (is_special) -- int is_special; --{ -- VAR_CONTEXT *vcxt, *ret; -- -- vcxt = shell_variables; -- if (vc_istempscope (vcxt) == 0) -- { -- internal_error (_("pop_scope: head of shell_variables not a temporary environment scope")); -- return; -- } -- -- ret = vcxt->down; -- if (ret) -- ret->up = (VAR_CONTEXT *)NULL; -- -- shell_variables = ret; -- -- /* Now we can take care of merging variables in VCXT into set of scopes -- whose head is RET (shell_variables). */ -- FREE (vcxt->name); -- if (vcxt->table) -- { -- if (is_special) -- hash_flush (vcxt->table, push_func_var); -- else -- hash_flush (vcxt->table, push_exported_var); -- hash_dispose (vcxt->table); -- } -- free (vcxt); -- -- sv_ifs ("IFS"); /* XXX here for now */ --} -- --/* **************************************************************** */ --/* */ --/* Pushing and Popping function contexts */ --/* */ --/* **************************************************************** */ -- --static WORD_LIST **dollar_arg_stack = (WORD_LIST **)NULL; --static int dollar_arg_stack_slots; --static int dollar_arg_stack_index; -- --/* XXX - we might want to consider pushing and popping the `getopts' state -- when we modify the positional parameters. */ --void --push_context (name, is_subshell, tempvars) -- char *name; /* function name */ -- int is_subshell; -- HASH_TABLE *tempvars; --{ -- if (is_subshell == 0) -- push_dollar_vars (); -- variable_context++; -- push_var_context (name, VC_FUNCENV, tempvars); --} -- --/* Only called when subshell == 0, so we don't need to check, and can -- unconditionally pop the dollar vars off the stack. */ --void --pop_context () --{ -- pop_dollar_vars (); -- variable_context--; -- pop_var_context (); -- -- sv_ifs ("IFS"); /* XXX here for now */ --} -- --/* Save the existing positional parameters on a stack. */ --void --push_dollar_vars () --{ -- if (dollar_arg_stack_index + 2 > dollar_arg_stack_slots) -- { -- dollar_arg_stack = (WORD_LIST **) -- xrealloc (dollar_arg_stack, (dollar_arg_stack_slots += 10) -- * sizeof (WORD_LIST *)); -- } -- dollar_arg_stack[dollar_arg_stack_index++] = list_rest_of_args (); -- dollar_arg_stack[dollar_arg_stack_index] = (WORD_LIST *)NULL; --} -- --/* Restore the positional parameters from our stack. */ --void --pop_dollar_vars () --{ -- if (!dollar_arg_stack || dollar_arg_stack_index == 0) -- return; -- -- remember_args (dollar_arg_stack[--dollar_arg_stack_index], 1); -- dispose_words (dollar_arg_stack[dollar_arg_stack_index]); -- dollar_arg_stack[dollar_arg_stack_index] = (WORD_LIST *)NULL; -- set_dollar_vars_unchanged (); --} -- --void --dispose_saved_dollar_vars () --{ -- if (!dollar_arg_stack || dollar_arg_stack_index == 0) -- return; -- -- dispose_words (dollar_arg_stack[dollar_arg_stack_index]); -- dollar_arg_stack[dollar_arg_stack_index] = (WORD_LIST *)NULL; --} -- --/* Manipulate the special BASH_ARGV and BASH_ARGC variables. */ -- --void --push_args (list) -- WORD_LIST *list; --{ --#if defined (ARRAY_VARS) && defined (DEBUGGER) -- SHELL_VAR *bash_argv_v, *bash_argc_v; -- ARRAY *bash_argv_a, *bash_argc_a; -- WORD_LIST *l; -- arrayind_t i; -- char *t; -- -- GET_ARRAY_FROM_VAR ("BASH_ARGV", bash_argv_v, bash_argv_a); -- GET_ARRAY_FROM_VAR ("BASH_ARGC", bash_argc_v, bash_argc_a); -- -- for (l = list, i = 0; l; l = l->next, i++) -- array_push (bash_argv_a, l->word->word); -- -- t = itos (i); -- array_push (bash_argc_a, t); -- free (t); --#endif /* ARRAY_VARS && DEBUGGER */ --} -- --/* Remove arguments from BASH_ARGV array. Pop top element off BASH_ARGC -- array and use that value as the count of elements to remove from -- BASH_ARGV. */ --void --pop_args () --{ --#if defined (ARRAY_VARS) && defined (DEBUGGER) -- SHELL_VAR *bash_argv_v, *bash_argc_v; -- ARRAY *bash_argv_a, *bash_argc_a; -- ARRAY_ELEMENT *ce; -- intmax_t i; -- -- GET_ARRAY_FROM_VAR ("BASH_ARGV", bash_argv_v, bash_argv_a); -- GET_ARRAY_FROM_VAR ("BASH_ARGC", bash_argc_v, bash_argc_a); -- -- ce = array_shift (bash_argc_a, 1, 0); -- if (ce == 0 || legal_number (element_value (ce), &i) == 0) -- i = 0; -- -- for ( ; i > 0; i--) -- array_pop (bash_argv_a); -- array_dispose_element (ce); --#endif /* ARRAY_VARS && DEBUGGER */ --} -- --/************************************************* -- * * -- * Functions to manage special variables * -- * * -- *************************************************/ -- --/* Extern declarations for variables this code has to manage. */ --extern int eof_encountered, eof_encountered_limit, ignoreeof; -- --#if defined (READLINE) --extern int hostname_list_initialized; --#endif -- --/* An alist of name.function for each special variable. Most of the -- functions don't do much, and in fact, this would be faster with a -- switch statement, but by the end of this file, I am sick of switch -- statements. */ -- --#define SET_INT_VAR(name, intvar) intvar = find_variable (name) != 0 -- --/* This table will be sorted with qsort() the first time it's accessed. */ --struct name_and_function { -- char *name; -- sh_sv_func_t *function; --}; -- --static struct name_and_function special_vars[] = { -- { "BASH_COMPAT", sv_shcompat }, -- { "BASH_XTRACEFD", sv_xtracefd }, -- --#if defined (JOB_CONTROL) -- { "CHILD_MAX", sv_childmax }, --#endif -- --#if defined (READLINE) --# if defined (STRICT_POSIX) -- { "COLUMNS", sv_winsize }, --# endif -- { "COMP_WORDBREAKS", sv_comp_wordbreaks }, --#endif -- -- { "FUNCNEST", sv_funcnest }, -- -- { "GLOBIGNORE", sv_globignore }, -- --#if defined (HISTORY) -- { "HISTCONTROL", sv_history_control }, -- { "HISTFILESIZE", sv_histsize }, -- { "HISTIGNORE", sv_histignore }, -- { "HISTSIZE", sv_histsize }, -- { "HISTTIMEFORMAT", sv_histtimefmt }, --#endif -- --#if defined (__CYGWIN__) -- { "HOME", sv_home }, --#endif -- --#if defined (READLINE) -- { "HOSTFILE", sv_hostfile }, --#endif -- -- { "IFS", sv_ifs }, -- { "IGNOREEOF", sv_ignoreeof }, -- -- { "LANG", sv_locale }, -- { "LC_ALL", sv_locale }, -- { "LC_COLLATE", sv_locale }, -- { "LC_CTYPE", sv_locale }, -- { "LC_MESSAGES", sv_locale }, -- { "LC_NUMERIC", sv_locale }, -- { "LC_TIME", sv_locale }, -- --#if defined (READLINE) && defined (STRICT_POSIX) -- { "LINES", sv_winsize }, --#endif -- -- { "MAIL", sv_mail }, -- { "MAILCHECK", sv_mail }, -- { "MAILPATH", sv_mail }, -- -- { "OPTERR", sv_opterr }, -- { "OPTIND", sv_optind }, -- -- { "PATH", sv_path }, -- { "POSIXLY_CORRECT", sv_strict_posix }, -- --#if defined (READLINE) -- { "TERM", sv_terminal }, -- { "TERMCAP", sv_terminal }, -- { "TERMINFO", sv_terminal }, --#endif /* READLINE */ -- -- { "TEXTDOMAIN", sv_locale }, -- { "TEXTDOMAINDIR", sv_locale }, -- --#if defined (HAVE_TZSET) -- { "TZ", sv_tz }, --#endif -- --#if defined (HISTORY) && defined (BANG_HISTORY) -- { "histchars", sv_histchars }, --#endif /* HISTORY && BANG_HISTORY */ -- -- { "ignoreeof", sv_ignoreeof }, -- -- { (char *)0, (sh_sv_func_t *)0 } --}; -- --#define N_SPECIAL_VARS (sizeof (special_vars) / sizeof (special_vars[0]) - 1) -- --static int --sv_compare (sv1, sv2) -- struct name_and_function *sv1, *sv2; --{ -- int r; -- -- if ((r = sv1->name[0] - sv2->name[0]) == 0) -- r = strcmp (sv1->name, sv2->name); -- return r; --} -- --static inline int --find_special_var (name) -- const char *name; --{ -- register int i, r; -- -- for (i = 0; special_vars[i].name; i++) -- { -- r = special_vars[i].name[0] - name[0]; -- if (r == 0) -- r = strcmp (special_vars[i].name, name); -- if (r == 0) -- return i; -- else if (r > 0) -- /* Can't match any of rest of elements in sorted list. Take this out -- if it causes problems in certain environments. */ -- break; -- } -- return -1; --} -- --/* The variable in NAME has just had its state changed. Check to see if it -- is one of the special ones where something special happens. */ --void --stupidly_hack_special_variables (name) -- char *name; --{ -- static int sv_sorted = 0; -- int i; -- -- if (sv_sorted == 0) /* shouldn't need, but it's fairly cheap. */ -- { -- qsort (special_vars, N_SPECIAL_VARS, sizeof (special_vars[0]), -- (QSFUNC *)sv_compare); -- sv_sorted = 1; -- } -- -- i = find_special_var (name); -- if (i != -1) -- (*(special_vars[i].function)) (name); --} -- --/* Special variables that need hooks to be run when they are unset as part -- of shell reinitialization should have their sv_ functions run here. */ --void --reinit_special_variables () --{ --#if defined (READLINE) -- sv_comp_wordbreaks ("COMP_WORDBREAKS"); --#endif -- sv_globignore ("GLOBIGNORE"); -- sv_opterr ("OPTERR"); --} -- --void --sv_ifs (name) -- char *name; --{ -- SHELL_VAR *v; -- -- v = find_variable ("IFS"); -- setifs (v); --} -- --/* What to do just after the PATH variable has changed. */ --void --sv_path (name) -- char *name; --{ -- /* hash -r */ -- phash_flush (); --} -- --/* What to do just after one of the MAILxxxx variables has changed. NAME -- is the name of the variable. This is called with NAME set to one of -- MAIL, MAILCHECK, or MAILPATH. */ --void --sv_mail (name) -- char *name; --{ -- /* If the time interval for checking the files has changed, then -- reset the mail timer. Otherwise, one of the pathname vars -- to the users mailbox has changed, so rebuild the array of -- filenames. */ -- if (name[4] == 'C') /* if (strcmp (name, "MAILCHECK") == 0) */ -- reset_mail_timer (); -- else -- { -- free_mail_files (); -- remember_mail_dates (); -- } --} -- --void --sv_funcnest (name) -- char *name; --{ -- SHELL_VAR *v; -- intmax_t num; -- -- v = find_variable (name); -- if (v == 0) -- funcnest_max = 0; -- else if (legal_number (value_cell (v), &num) == 0) -- funcnest_max = 0; -- else -- funcnest_max = num; --} -- --/* What to do when GLOBIGNORE changes. */ --void --sv_globignore (name) -- char *name; --{ -- if (privileged_mode == 0) -- setup_glob_ignore (name); --} -- --#if defined (READLINE) --void --sv_comp_wordbreaks (name) -- char *name; --{ -- SHELL_VAR *sv; -- -- sv = find_variable (name); -- if (sv == 0) -- reset_completer_word_break_chars (); --} -- --/* What to do just after one of the TERMxxx variables has changed. -- If we are an interactive shell, then try to reset the terminal -- information in readline. */ --void --sv_terminal (name) -- char *name; --{ -- if (interactive_shell && no_line_editing == 0) -- rl_reset_terminal (get_string_value ("TERM")); --} -- --void --sv_hostfile (name) -- char *name; --{ -- SHELL_VAR *v; -- -- v = find_variable (name); -- if (v == 0) -- clear_hostname_list (); -- else -- hostname_list_initialized = 0; --} -- --#if defined (STRICT_POSIX) --/* In strict posix mode, we allow assignments to LINES and COLUMNS (and values -- found in the initial environment) to override the terminal size reported by -- the kernel. */ --void --sv_winsize (name) -- char *name; --{ -- SHELL_VAR *v; -- intmax_t xd; -- int d; -- -- if (posixly_correct == 0 || interactive_shell == 0 || no_line_editing) -- return; -- -- v = find_variable (name); -- if (v == 0 || var_isnull (v)) -- rl_reset_screen_size (); -- else -- { -- if (legal_number (value_cell (v), &xd) == 0) -- return; -- winsize_assignment = 1; -- d = xd; /* truncate */ -- if (name[0] == 'L') /* LINES */ -- rl_set_screen_size (d, -1); -- else /* COLUMNS */ -- rl_set_screen_size (-1, d); -- winsize_assignment = 0; -- } --} --#endif /* STRICT_POSIX */ --#endif /* READLINE */ -- --/* Update the value of HOME in the export environment so tilde expansion will -- work on cygwin. */ --#if defined (__CYGWIN__) --sv_home (name) -- char *name; --{ -- array_needs_making = 1; -- maybe_make_export_env (); --} --#endif -- --#if defined (HISTORY) --/* What to do after the HISTSIZE or HISTFILESIZE variables change. -- If there is a value for this HISTSIZE (and it is numeric), then stifle -- the history. Otherwise, if there is NO value for this variable, -- unstifle the history. If name is HISTFILESIZE, and its value is -- numeric, truncate the history file to hold no more than that many -- lines. */ --void --sv_histsize (name) -- char *name; --{ -- char *temp; -- intmax_t num; -- int hmax; -- -- temp = get_string_value (name); -- -- if (temp && *temp) -- { -- if (legal_number (temp, &num)) -- { -- hmax = num; -- if (hmax < 0 && name[4] == 'S') -- unstifle_history (); /* unstifle history if HISTSIZE < 0 */ -- else if (name[4] == 'S') -- { -- stifle_history (hmax); -- hmax = where_history (); -- if (history_lines_this_session > hmax) -- history_lines_this_session = hmax; -- } -- else if (hmax >= 0) /* truncate HISTFILE if HISTFILESIZE >= 0 */ -- { -- history_truncate_file (get_string_value ("HISTFILE"), hmax); -- if (hmax <= history_lines_in_file) -- history_lines_in_file = hmax; -- } -- } -- } -- else if (name[4] == 'S') -- unstifle_history (); --} -- --/* What to do after the HISTIGNORE variable changes. */ --void --sv_histignore (name) -- char *name; --{ -- setup_history_ignore (name); --} -- --/* What to do after the HISTCONTROL variable changes. */ --void --sv_history_control (name) -- char *name; --{ -- char *temp; -- char *val; -- int tptr; -- -- history_control = 0; -- temp = get_string_value (name); -- -- if (temp == 0 || *temp == 0) -- return; -- -- tptr = 0; -- while (val = extract_colon_unit (temp, &tptr)) -- { -- if (STREQ (val, "ignorespace")) -- history_control |= HC_IGNSPACE; -- else if (STREQ (val, "ignoredups")) -- history_control |= HC_IGNDUPS; -- else if (STREQ (val, "ignoreboth")) -- history_control |= HC_IGNBOTH; -- else if (STREQ (val, "erasedups")) -- history_control |= HC_ERASEDUPS; -- -- free (val); -- } --} -- --#if defined (BANG_HISTORY) --/* Setting/unsetting of the history expansion character. */ --void --sv_histchars (name) -- char *name; --{ -- char *temp; -- -- temp = get_string_value (name); -- if (temp) -- { -- history_expansion_char = *temp; -- if (temp[0] && temp[1]) -- { -- history_subst_char = temp[1]; -- if (temp[2]) -- history_comment_char = temp[2]; -- } -- } -- else -- { -- history_expansion_char = '!'; -- history_subst_char = '^'; -- history_comment_char = '#'; -- } --} --#endif /* BANG_HISTORY */ -- --void --sv_histtimefmt (name) -- char *name; --{ -- SHELL_VAR *v; -- -- if (v = find_variable (name)) -- { -- if (history_comment_char == 0) -- history_comment_char = '#'; -- } -- history_write_timestamps = (v != 0); --} --#endif /* HISTORY */ -- --#if defined (HAVE_TZSET) --void --sv_tz (name) -- char *name; --{ -- if (chkexport (name)) -- tzset (); --} --#endif -- --/* If the variable exists, then the value of it can be the number -- of times we actually ignore the EOF. The default is small, -- (smaller than csh, anyway). */ --void --sv_ignoreeof (name) -- char *name; --{ -- SHELL_VAR *tmp_var; -- char *temp; -- -- eof_encountered = 0; -- -- tmp_var = find_variable (name); -- ignoreeof = tmp_var != 0; -- temp = tmp_var ? value_cell (tmp_var) : (char *)NULL; -- if (temp) -- eof_encountered_limit = (*temp && all_digits (temp)) ? atoi (temp) : 10; -- set_shellopts (); /* make sure `ignoreeof' is/is not in $SHELLOPTS */ --} -- --void --sv_optind (name) -- char *name; --{ -- char *tt; -- int s; -- -- tt = get_string_value ("OPTIND"); -- if (tt && *tt) -- { -- s = atoi (tt); -- -- /* According to POSIX, setting OPTIND=1 resets the internal state -- of getopt (). */ -- if (s < 0 || s == 1) -- s = 0; -- } -- else -- s = 0; -- getopts_reset (s); --} -- --void --sv_opterr (name) -- char *name; --{ -- char *tt; -- -- tt = get_string_value ("OPTERR"); -- sh_opterr = (tt && *tt) ? atoi (tt) : 1; --} -- --void --sv_strict_posix (name) -- char *name; --{ -- SET_INT_VAR (name, posixly_correct); -- posix_initialize (posixly_correct); --#if defined (READLINE) -- if (interactive_shell) -- posix_readline_initialize (posixly_correct); --#endif /* READLINE */ -- set_shellopts (); /* make sure `posix' is/is not in $SHELLOPTS */ --} -- --void --sv_locale (name) -- char *name; --{ -- char *v; -- int r; -- -- v = get_string_value (name); -- if (name[0] == 'L' && name[1] == 'A') /* LANG */ -- r = set_lang (name, v); -- else -- r = set_locale_var (name, v); /* LC_*, TEXTDOMAIN* */ -- --#if 1 -- if (r == 0 && posixly_correct) -- last_command_exit_value = 1; --#endif --} -- --#if defined (ARRAY_VARS) --void --set_pipestatus_array (ps, nproc) -- int *ps; -- int nproc; --{ -- SHELL_VAR *v; -- ARRAY *a; -- ARRAY_ELEMENT *ae; -- register int i; -- char *t, tbuf[INT_STRLEN_BOUND(int) + 1]; -- -- v = find_variable ("PIPESTATUS"); -- if (v == 0) -- v = make_new_array_variable ("PIPESTATUS"); -- if (array_p (v) == 0) -- return; /* Do nothing if not an array variable. */ -- a = array_cell (v); -- -- if (a == 0 || array_num_elements (a) == 0) -- { -- for (i = 0; i < nproc; i++) /* was ps[i] != -1, not i < nproc */ -- { -- t = inttostr (ps[i], tbuf, sizeof (tbuf)); -- array_insert (a, i, t); -- } -- return; -- } -- -- /* Fast case */ -- if (array_num_elements (a) == nproc && nproc == 1) -- { -- ae = element_forw (a->head); -- free (element_value (ae)); -- ae->value = itos (ps[0]); -- } -- else if (array_num_elements (a) <= nproc) -- { -- /* modify in array_num_elements members in place, then add */ -- ae = a->head; -- for (i = 0; i < array_num_elements (a); i++) -- { -- ae = element_forw (ae); -- free (element_value (ae)); -- ae->value = itos (ps[i]); -- } -- /* add any more */ -- for ( ; i < nproc; i++) -- { -- t = inttostr (ps[i], tbuf, sizeof (tbuf)); -- array_insert (a, i, t); -- } -- } -- else -- { -- /* deleting elements. it's faster to rebuild the array. */ -- array_flush (a); -- for (i = 0; ps[i] != -1; i++) -- { -- t = inttostr (ps[i], tbuf, sizeof (tbuf)); -- array_insert (a, i, t); -- } -- } --} -- --ARRAY * --save_pipestatus_array () --{ -- SHELL_VAR *v; -- ARRAY *a, *a2; -- -- v = find_variable ("PIPESTATUS"); -- if (v == 0 || array_p (v) == 0 || array_cell (v) == 0) -- return ((ARRAY *)NULL); -- -- a = array_cell (v); -- a2 = array_copy (array_cell (v)); -- -- return a2; --} -- --void --restore_pipestatus_array (a) -- ARRAY *a; --{ -- SHELL_VAR *v; -- ARRAY *a2; -- -- v = find_variable ("PIPESTATUS"); -- /* XXX - should we still assign even if existing value is NULL? */ -- if (v == 0 || array_p (v) == 0 || array_cell (v) == 0) -- return; -- -- a2 = array_cell (v); -- var_setarray (v, a); -- -- array_dispose (a2); --} --#endif -- --void --set_pipestatus_from_exit (s) -- int s; --{ --#if defined (ARRAY_VARS) -- static int v[2] = { 0, -1 }; -- -- v[0] = s; -- set_pipestatus_array (v, 1); --#endif --} -- --void --sv_xtracefd (name) -- char *name; --{ -- SHELL_VAR *v; -- char *t, *e; -- int fd; -- FILE *fp; -- -- v = find_variable (name); -- if (v == 0) -- { -- xtrace_reset (); -- return; -- } -- -- t = value_cell (v); -- if (t == 0 || *t == 0) -- xtrace_reset (); -- else -- { -- fd = (int)strtol (t, &e, 10); -- if (e != t && *e == '\0' && sh_validfd (fd)) -- { -- fp = fdopen (fd, "w"); -- if (fp == 0) -- internal_error (_("%s: %s: cannot open as FILE"), name, value_cell (v)); -- else -- xtrace_set (fd, fp); -- } -- else -- internal_error (_("%s: %s: invalid value for trace file descriptor"), name, value_cell (v)); -- } --} -- --#define MIN_COMPAT_LEVEL 31 -- --void --sv_shcompat (name) -- char *name; --{ -- SHELL_VAR *v; -- char *val; -- int tens, ones, compatval; -- -- v = find_variable (name); -- if (v == 0) -- { -- shell_compatibility_level = DEFAULT_COMPAT_LEVEL; -- set_compatibility_opts (); -- return; -- } -- val = value_cell (v); -- if (val == 0 || *val == '\0') -- { -- shell_compatibility_level = DEFAULT_COMPAT_LEVEL; -- set_compatibility_opts (); -- return; -- } -- /* Handle decimal-like compatibility version specifications: 4.2 */ -- if (isdigit (val[0]) && val[1] == '.' && isdigit (val[2]) && val[3] == 0) -- { -- tens = val[0] - '0'; -- ones = val[2] - '0'; -- compatval = tens*10 + ones; -- } -- /* Handle integer-like compatibility version specifications: 42 */ -- else if (isdigit (val[0]) && isdigit (val[1]) && val[2] == 0) -- { -- tens = val[0] - '0'; -- ones = val[1] - '0'; -- compatval = tens*10 + ones; -- } -- else -- { --compat_error: -- internal_error (_("%s: %s: compatibility value out of range"), name, val); -- shell_compatibility_level = DEFAULT_COMPAT_LEVEL; -- set_compatibility_opts (); -- return; -- } -- -- if (compatval < MIN_COMPAT_LEVEL || compatval > DEFAULT_COMPAT_LEVEL) -- goto compat_error; -- -- shell_compatibility_level = compatval; -- set_compatibility_opts (); --} -- --#if defined (JOB_CONTROL) --void --sv_childmax (name) -- char *name; --{ -- char *tt; -- int s; -- -- tt = get_string_value (name); -- s = (tt && *tt) ? atoi (tt) : 0; -- set_maxchild (s); --} --#endif diff --git a/patches/bash-4.3.30/0003-Bash-4.3-patch-33.patch b/patches/bash-4.3.30/0003-Bash-4.3-patch-33.patch deleted file mode 100644 index cda179735..000000000 --- a/patches/bash-4.3.30/0003-Bash-4.3-patch-33.patch +++ /dev/null @@ -1,204 +0,0 @@ -From: Chet Ramey -Date: Thu, 15 Jan 2015 10:21:08 -0500 -Subject: [PATCH] Bash-4.3 patch 33 - ---- - bashline.c | 6 ++++-- - builtins/common.h | 4 ++++ - builtins/read.def | 31 ++++++++++++++++++++++++++++--- - patchlevel.h | 2 +- - shell.c | 9 +++++++++ - sig.c | 6 ++++-- - 6 files changed, 50 insertions(+), 8 deletions(-) - -diff --git a/bashline.c b/bashline.c -index 77ca033f2cc8..c87415171a4a 100644 ---- a/bashline.c -+++ b/bashline.c -@@ -202,6 +202,7 @@ extern int current_command_line_count, saved_command_line_count; - extern int last_command_exit_value; - extern int array_needs_making; - extern int posixly_correct, no_symbolic_links; -+extern int sigalrm_seen; - extern char *current_prompt_string, *ps1_prompt; - extern STRING_INT_ALIST word_token_alist[]; - extern sh_builtin_func_t *last_shell_builtin, *this_shell_builtin; -@@ -4208,8 +4209,9 @@ bash_event_hook () - { - /* If we're going to longjmp to top_level, make sure we clean up readline. - check_signals will call QUIT, which will eventually longjmp to top_level, -- calling run_interrupt_trap along the way. */ -- if (interrupt_state) -+ calling run_interrupt_trap along the way. The check for sigalrm_seen is -+ to clean up the read builtin's state. */ -+ if (terminating_signal || interrupt_state || sigalrm_seen) - rl_cleanup_after_signal (); - bashline_reset_event_hook (); - check_signals_and_traps (); /* XXX */ -diff --git a/builtins/common.h b/builtins/common.h -index cae16b10fb65..a1298cb9c84a 100644 ---- a/builtins/common.h -+++ b/builtins/common.h -@@ -122,6 +122,10 @@ extern void bash_logout __P((void)); - /* Functions from getopts.def */ - extern void getopts_reset __P((int)); - -+/* Functions from read.def */ -+extern void read_tty_cleanup __P((void)); -+extern int read_tty_modified __P((void)); -+ - /* Functions from set.def */ - extern int minus_o_option_value __P((char *)); - extern void list_minus_o_opts __P((int, int)); -diff --git a/builtins/read.def b/builtins/read.def -index 43971544d081..56c23010bbe8 100644 ---- a/builtins/read.def -+++ b/builtins/read.def -@@ -140,10 +140,12 @@ static void reset_alarm __P((void)); - procenv_t alrmbuf; - int sigalrm_seen; - --static int reading; -+static int reading, tty_modified; - static SigHandler *old_alrm; - static unsigned char delim; - -+static struct ttsave termsave; -+ - /* In all cases, SIGALRM just sets a flag that we check periodically. This - avoids problems with the semi-tricky stuff we do with the xfree of - input_string at the top of the unwind-protect list (see below). */ -@@ -188,7 +190,6 @@ read_builtin (list) - struct stat tsb; - SHELL_VAR *var; - TTYSTRUCT ttattrs, ttset; -- struct ttsave termsave; - #if defined (ARRAY_VARS) - WORD_LIST *alist; - #endif -@@ -221,7 +222,7 @@ read_builtin (list) - USE_VAR(ps2); - USE_VAR(lastsig); - -- sigalrm_seen = reading = 0; -+ sigalrm_seen = reading = tty_modified = 0; - - i = 0; /* Index into the string that we are reading. */ - raw = edit = 0; /* Not reading raw input by default. */ -@@ -438,6 +439,8 @@ read_builtin (list) - retval = 128+SIGALRM; - goto assign_vars; - } -+ if (interactive_shell == 0) -+ initialize_terminating_signals (); - old_alrm = set_signal_handler (SIGALRM, sigalrm); - add_unwind_protect (reset_alarm, (char *)NULL); - #if defined (READLINE) -@@ -482,7 +485,10 @@ read_builtin (list) - i = silent ? ttfd_cbreak (fd, &ttset) : ttfd_onechar (fd, &ttset); - if (i < 0) - sh_ttyerror (1); -+ tty_modified = 1; - add_unwind_protect ((Function *)ttyrestore, (char *)&termsave); -+ if (interactive_shell == 0) -+ initialize_terminating_signals (); - } - } - else if (silent) /* turn off echo but leave term in canonical mode */ -@@ -497,7 +503,10 @@ read_builtin (list) - if (i < 0) - sh_ttyerror (1); - -+ tty_modified = 1; - add_unwind_protect ((Function *)ttyrestore, (char *)&termsave); -+ if (interactive_shell == 0) -+ initialize_terminating_signals (); - } - - /* This *must* be the top unwind-protect on the stack, so the manipulation -@@ -588,6 +597,8 @@ read_builtin (list) - } - else - lastsig = 0; -+ if (terminating_signal && tty_modified) -+ ttyrestore (&termsave); /* fix terminal before exiting */ - CHECK_TERMSIG; - eof = 1; - break; -@@ -978,6 +989,20 @@ ttyrestore (ttp) - struct ttsave *ttp; - { - ttsetattr (ttp->fd, ttp->attrs); -+ tty_modified = 0; -+} -+ -+void -+read_tty_cleanup () -+{ -+ if (tty_modified) -+ ttyrestore (&termsave); -+} -+ -+int -+read_tty_modified () -+{ -+ return (tty_modified); - } - - #if defined (READLINE) -diff --git a/patchlevel.h b/patchlevel.h -index b8bf38704ed2..cefe6bdd3a13 100644 ---- a/patchlevel.h -+++ b/patchlevel.h -@@ -25,6 +25,6 @@ - regexp `^#define[ ]*PATCHLEVEL', since that's what support/mkversion.sh - looks for to find the patch level (for the sccs version string). */ - --#define PATCHLEVEL 32 -+#define PATCHLEVEL 33 - - #endif /* _PATCHLEVEL_H_ */ -diff --git a/shell.c b/shell.c -index bbc8a66cc2eb..2fd8179ba10d 100644 ---- a/shell.c -+++ b/shell.c -@@ -73,6 +73,7 @@ - #endif - - #if defined (READLINE) -+# include - # include "bashline.h" - #endif - -@@ -909,6 +910,14 @@ exit_shell (s) - fflush (stdout); /* XXX */ - fflush (stderr); - -+ /* Clean up the terminal if we are in a state where it's been modified. */ -+#if defined (READLINE) -+ if (RL_ISSTATE (RL_STATE_TERMPREPPED) && rl_deprep_term_function) -+ (*rl_deprep_term_function) (); -+#endif -+ if (read_tty_modified ()) -+ read_tty_cleanup (); -+ - /* Do trap[0] if defined. Allow it to override the exit status - passed to us. */ - if (signal_is_trapped (0)) -diff --git a/sig.c b/sig.c -index 3b62ea5d7c5d..8bc45c17f478 100644 ---- a/sig.c -+++ b/sig.c -@@ -532,8 +532,10 @@ termsig_sighandler (sig) - #if defined (READLINE) - /* Set the event hook so readline will call it after the signal handlers - finish executing, so if this interrupted character input we can get -- quick response. */ -- if (interactive_shell && interactive && no_line_editing == 0) -+ quick response. If readline is active or has modified the terminal we -+ need to set this no matter what the signal is, though the check for -+ RL_STATE_TERMPREPPED is possibly redundant. */ -+ if (RL_ISSTATE (RL_STATE_SIGHANDLER) || RL_ISSTATE (RL_STATE_TERMPREPPED)) - bashline_set_event_hook (); - #endif - diff --git a/patches/bash-4.3.30/series b/patches/bash-4.3.30/series deleted file mode 100644 index 2e1fdf17f..000000000 --- a/patches/bash-4.3.30/series +++ /dev/null @@ -1,6 +0,0 @@ -# generated by git-ptx-patches -#tag:base --start-number 1 -0001-Bash-4.3-patch-31.patch -0002-Bash-4.3-patch-32.patch -0003-Bash-4.3-patch-33.patch -# 602897f584d96d29536a2fa60f8d5e23 - git-ptx-patches magic diff --git a/patches/bash-5.1.8/0001-Bash-5.1-patch-12.patch b/patches/bash-5.1.8/0001-Bash-5.1-patch-12.patch new file mode 100644 index 000000000..ef8b6ae7f --- /dev/null +++ b/patches/bash-5.1.8/0001-Bash-5.1-patch-12.patch @@ -0,0 +1,262 @@ +diff -urN bash-5.1.8.orig/builtins/wait.def bash-5.1.8/builtins/wait.def +--- bash-5.1.8.orig/builtins/wait.def 2021-12-21 12:57:08.083139502 +0100 ++++ bash-5.1.8/builtins/wait.def 2021-12-21 12:58:24.304849937 +0100 +@@ -111,7 +111,8 @@ + wait_builtin (list) + WORD_LIST *list; + { +- int status, code, opt, nflag, wflags; ++ int status, code, opt, nflag; ++ volatile int wflags; + char *vname; + SHELL_VAR *pidvar; + struct procstat pstat; +@@ -180,6 +181,8 @@ + last_command_exit_signal = wait_signal_received; + status = 128 + wait_signal_received; + wait_sigint_cleanup (); ++ if (wflags & JWAIT_WAITING) ++ unset_waitlist (); + WAIT_RETURN (status); + } + +diff -urN bash-5.1.8.orig/command.h bash-5.1.8/command.h +--- bash-5.1.8.orig/command.h 2021-12-21 12:57:08.099139862 +0100 ++++ bash-5.1.8/command.h 2021-12-21 12:58:27.760927380 +0100 +@@ -124,6 +124,7 @@ + #define SUBSHELL_PROCSUB 0x20 /* subshell caused by <(command) or >(command) */ + #define SUBSHELL_COPROC 0x40 /* subshell from a coproc pipeline */ + #define SUBSHELL_RESETTRAP 0x80 /* subshell needs to reset trap strings on first call to trap */ ++#define SUBSHELL_IGNTRAP 0x100 /* subshell should reset trapped signals from trap_handler */ + + /* A structure which represents a word. */ + typedef struct word_desc { +diff -urN bash-5.1.8.orig/execute_cmd.c bash-5.1.8/execute_cmd.c +--- bash-5.1.8.orig/execute_cmd.c 2021-12-21 12:57:08.091139683 +0100 ++++ bash-5.1.8/execute_cmd.c 2021-12-21 12:58:27.764927470 +0100 +@@ -1547,6 +1547,9 @@ + clear_pending_traps (); + reset_signal_handlers (); + subshell_environment |= SUBSHELL_RESETTRAP; ++ /* Note that signal handlers have been reset, so we should no longer ++ reset the handler and resend trapped signals to ourselves. */ ++ subshell_environment &= ~SUBSHELL_IGNTRAP; + + /* We are in a subshell, so forget that we are running a trap handler or + that the signal handler has changed (we haven't changed it!) */ +@@ -4320,7 +4323,8 @@ + already_forked = 1; + cmdflags |= CMD_NO_FORK; + +- subshell_environment = SUBSHELL_FORK; /* XXX */ ++ /* We redo some of what make_child() does with SUBSHELL_IGNTRAP */ ++ subshell_environment = SUBSHELL_FORK|SUBSHELL_IGNTRAP; /* XXX */ + if (pipe_in != NO_PIPE || pipe_out != NO_PIPE) + subshell_environment |= SUBSHELL_PIPE; + if (async) +@@ -4574,6 +4578,7 @@ + trap strings if we run trap to change a signal disposition. */ + reset_signal_handlers (); + subshell_environment |= SUBSHELL_RESETTRAP; ++ subshell_environment &= ~SUBSHELL_IGNTRAP; + + if (async) + { +@@ -5514,6 +5519,7 @@ + reset_terminating_signals (); /* XXX */ + /* Cancel traps, in trap.c. */ + restore_original_signals (); ++ subshell_environment &= ~SUBSHELL_IGNTRAP; + + #if defined (JOB_CONTROL) + FREE (p); +diff -urN bash-5.1.8.orig/jobs.c bash-5.1.8/jobs.c +--- bash-5.1.8.orig/jobs.c 2021-12-21 12:57:08.091139683 +0100 ++++ bash-5.1.8/jobs.c 2021-12-21 12:58:27.764927470 +0100 +@@ -2217,6 +2217,8 @@ + signals to the default state for a new process. */ + pid_t mypid; + ++ subshell_environment |= SUBSHELL_IGNTRAP; ++ + /* If this ends up being changed to modify or use `command' in the + child process, go back and change callers who free `command' in + the child process when this returns. */ +diff -urN bash-5.1.8.orig/lib/malloc/malloc.c bash-5.1.8/lib/malloc/malloc.c +--- bash-5.1.8.orig/lib/malloc/malloc.c 2021-12-21 12:57:08.095139773 +0100 ++++ bash-5.1.8/lib/malloc/malloc.c 2021-12-21 12:58:22.200802784 +0100 +@@ -1286,13 +1286,12 @@ + p = (union mhead *) ap - 1; + } + +- /* XXX - should we return 0 if ISFREE? */ +- maxbytes = binsize(p->mh_index); +- +- /* So the usable size is the maximum number of bytes in the bin less the +- malloc overhead */ +- maxbytes -= MOVERHEAD + MSLOP; +- return (maxbytes); ++ /* return 0 if ISFREE */ ++ if (p->mh_alloc == ISFREE) ++ return 0; ++ ++ /* Since we use bounds checking, the usable size is the last requested size. */ ++ return (p->mh_nbytes); + } + + #if !defined (NO_VALLOC) +diff -urN bash-5.1.8.orig/nojobs.c bash-5.1.8/nojobs.c +--- bash-5.1.8.orig/nojobs.c 2021-12-21 12:57:08.091139683 +0100 ++++ bash-5.1.8/nojobs.c 2021-12-21 12:58:27.764927470 +0100 +@@ -575,6 +575,8 @@ + last_asynchronous_pid = getpid (); + #endif + ++ subshell_environment |= SUBSHELL_IGNTRAP; ++ + default_tty_job_signals (); + } + else +diff -urN bash-5.1.8.orig/parse.y bash-5.1.8/parse.y +--- bash-5.1.8.orig/parse.y 2021-12-21 12:57:08.099139862 +0100 ++++ bash-5.1.8/parse.y 2021-12-21 12:58:26.112890455 +0100 +@@ -6493,10 +6493,8 @@ + old_expand_aliases = expand_aliases; + + push_stream (1); +-#if 0 /* TAG: bash-5.2 Alex fxmbsw7 Ratchev 11/17/2020 */ + if (ea = expanding_alias ()) + parser_save_alias (); +-#endif + last_read_token = WORD; /* WORD to allow reserved words here */ + current_command_line_count = 0; + echo_input_at_read = expand_aliases = 0; +@@ -6531,10 +6529,8 @@ + last_read_token = '\n'; + pop_stream (); + +-#if 0 /* TAG: bash-5.2 */ + if (ea) + parser_restore_alias (); +-#endif + + #if defined (HISTORY) + remember_on_history = old_remember_on_history; +diff -urN bash-5.1.8.orig/patchlevel.h bash-5.1.8/patchlevel.h +--- bash-5.1.8.orig/patchlevel.h 2021-12-21 12:57:08.075139321 +0100 ++++ bash-5.1.8/patchlevel.h 2021-12-21 12:58:27.764927470 +0100 +@@ -25,6 +25,6 @@ + regexp `^#define[ ]*PATCHLEVEL', since that's what support/mkversion.sh + looks for to find the patch level (for the sccs version string). */ + +-#define PATCHLEVEL 8 ++#define PATCHLEVEL 12 + + #endif /* _PATCHLEVEL_H_ */ +diff -urN bash-5.1.8.orig/sig.c bash-5.1.8/sig.c +--- bash-5.1.8.orig/sig.c 2021-12-21 12:57:08.071139231 +0100 ++++ bash-5.1.8/sig.c 2021-12-21 12:58:27.764927470 +0100 +@@ -55,7 +55,8 @@ + # include "bashhist.h" + #endif + +-extern void initialize_siglist (); ++extern void initialize_siglist PARAMS((void)); ++extern void set_original_signal PARAMS((int, SigHandler *)); + + #if !defined (JOB_CONTROL) + extern void initialize_job_signals PARAMS((void)); +@@ -255,6 +256,13 @@ + sigaction (XSIG (i), &act, &oact); + XHANDLER(i) = oact.sa_handler; + XSAFLAGS(i) = oact.sa_flags; ++ ++#if 0 ++ set_original_signal (XSIG(i), XHANDLER(i)); /* optimization */ ++#else ++ set_original_signal (XSIG(i), act.sa_handler); /* optimization */ ++#endif ++ + /* Don't do anything with signals that are ignored at shell entry + if the shell is not interactive. */ + /* XXX - should we do this for interactive shells, too? */ +diff -urN bash-5.1.8.orig/subst.c bash-5.1.8/subst.c +--- bash-5.1.8.orig/subst.c 2021-12-21 12:57:08.099139862 +0100 ++++ bash-5.1.8/subst.c 2021-12-21 12:58:27.764927470 +0100 +@@ -5951,6 +5951,7 @@ + free_pushed_string_input (); + /* Cancel traps, in trap.c. */ + restore_original_signals (); /* XXX - what about special builtins? bash-4.2 */ ++ subshell_environment &= ~SUBSHELL_IGNTRAP; + QUIT; /* catch any interrupts we got post-fork */ + setup_async_signals (); + #if 0 +@@ -6382,6 +6383,7 @@ + } + QUIT; /* catch any interrupts we got post-fork */ + subshell_environment |= SUBSHELL_RESETTRAP; ++ subshell_environment &= ~SUBSHELL_IGNTRAP; + } + + #if defined (JOB_CONTROL) +diff -urN bash-5.1.8.orig/trap.c bash-5.1.8/trap.c +--- bash-5.1.8.orig/trap.c 2021-12-21 12:57:08.083139502 +0100 ++++ bash-5.1.8/trap.c 2021-12-21 12:58:27.764927470 +0100 +@@ -481,6 +481,32 @@ + SIGRETURN (0); + } + ++ /* This means we're in a subshell, but have not yet reset the handler for ++ trapped signals. We're not supposed to execute the trap in this situation; ++ we should restore the original signal and resend the signal to ourselves ++ to preserve the Posix "signal traps that are not being ignored shall be ++ set to the default action" semantics. */ ++ if ((subshell_environment & SUBSHELL_IGNTRAP) && trap_list[sig] != (char *)IGNORE_SIG) ++ { ++ sigset_t mask; ++ ++ /* Paranoia */ ++ if (original_signals[sig] == IMPOSSIBLE_TRAP_HANDLER) ++ original_signals[sig] = SIG_DFL; ++ ++ restore_signal (sig); ++ ++ /* Make sure we let the signal we just caught through */ ++ sigemptyset (&mask); ++ sigprocmask (SIG_SETMASK, (sigset_t *)NULL, &mask); ++ sigdelset (&mask, sig); ++ sigprocmask (SIG_SETMASK, &mask, (sigset_t *)NULL); ++ ++ kill (getpid (), sig); ++ ++ SIGRETURN (0); ++ } ++ + if ((sig >= NSIG) || + (trap_list[sig] == (char *)DEFAULT_SIG) || + (trap_list[sig] == (char *)IGNORE_SIG)) +diff -urN bash-5.1.8.orig/y.tab.c bash-5.1.8/y.tab.c +--- bash-5.1.8.orig/y.tab.c 2021-12-21 12:57:08.075139321 +0100 ++++ bash-5.1.8/y.tab.c 2021-12-21 12:58:26.116890545 +0100 +@@ -8787,10 +8787,8 @@ + old_expand_aliases = expand_aliases; + + push_stream (1); +-#if 0 /* TAG: bash-5.2 Alex fxmbsw7 Ratchev 11/17/2020 */ + if (ea = expanding_alias ()) + parser_save_alias (); +-#endif + last_read_token = WORD; /* WORD to allow reserved words here */ + current_command_line_count = 0; + echo_input_at_read = expand_aliases = 0; +@@ -8825,10 +8823,8 @@ + last_read_token = '\n'; + pop_stream (); + +-#if 0 /* TAG: bash-5.2 */ + if (ea) + parser_restore_alias (); +-#endif + + #if defined (HISTORY) + remember_on_history = old_remember_on_history; diff --git a/patches/bash-5.1.8/series b/patches/bash-5.1.8/series new file mode 100644 index 000000000..0e53e492a --- /dev/null +++ b/patches/bash-5.1.8/series @@ -0,0 +1 @@ +0001-Bash-5.1-patch-12.patch diff --git a/rules/bash.make b/rules/bash.make index bed121586..c46196f7f 100644 --- a/rules/bash.make +++ b/rules/bash.make @@ -13,8 +13,8 @@ PACKAGES-$(PTXCONF_BASH) += bash # # Paths and names # -BASH_VERSION := 4.3.30 -BASH_MD5 := a27b3ee9be83bd3ba448c0ff52b28447 +BASH_VERSION := 5.1.8 +BASH_MD5 := 23eee6195b47318b9fd878e590ccb38c BASH := bash-$(BASH_VERSION) BASH_SUFFIX := tar.gz BASH_URL := $(call ptx/mirror, GNU, bash/$(BASH).$(BASH_SUFFIX)) -- 2.30.2 _______________________________________________ ptxdist mailing list ptxdist@pengutronix.de To unsubscribe, send a mail with subject "unsubscribe" to ptxdist-request@pengutronix.de