[BusyBox] Forbidden characters in shell function names?

Felipe Kellermann stdfk at terra.com.br
Sun Jan 2 15:24:04 UTC 2005


On Sun, 2 Jan 2005 1:45pm  +0100, Patrick Spousta wrote:

> Hi all,
> I tried to cross over from BB 0.6 (from WISP linux distribution!) to 1.0 and
> now I can see some shell scripts have wrong function names :-(

Hi Patrick,


> test_01
> [root at tmp]# busybox sh test_underline
> test with underline
> [root at tmp]#
> 
> What I'm doing wrong? Or is normal that function name can't contain these
> characters?

Although (almost) every shell implementation permits `.', `-', etc., these 
are just extensions to the standard. Quoting XCU, 2.9.5:
"The function is named fname; the application shall ensure that it is a 
 name (see the Base Definitions volume of IEEE Std 1003.1-2001, Section 
 3.230, Name). An implementation may allow other characters in a function 
 name as an extension. (...)"

Quoting XBD, Section 3.230:
"In the shell command language, a word consisting solely of underscores, 
 digits, and alphabetics from the portable character set. The first 
 character of a name is not a digit."

So the "portable" identifier would be ``[_[:alnum:]]''.

Note that busybox's ash is just applying the same rules for various types 
of identifiers (parameter (variable) names, function names, etc). So this 
could be seen as a simplification; other shells (eg, ksh, zsh, bash) uses 
different rules for different identifiers:

$ a.b=1
bash: a.b=1: command not found
$ a.b() { echo $FUNCNAME; }
$ a.b
a.b

I don't use non-portable identifier in my scripts, but I think it would be 
nice to have busybox's ash accepting the "common extensions".  Attached is 
very simple patch that adds an option to enable implementation-specific 
characters in function names: `.' and `-':

ash$ a.b=1
-sh: a.b=1: not found
ash$ a.b() { echo a.b; }
ash$ a.b
a.b
ash$ a-b() { echo $(($1 - $2)); }
ash$ a-b 3 1
2
ash$ a-b=2
-sh: a-b=2: not found

-- 
Felipe Kellermann
-------------- next part --------------
Index: shell/Config.in
===================================================================
RCS file: /var/cvs/busybox/shell/Config.in,v
retrieving revision 1.18
diff -u -3 -p -u -p -r1.18 Config.in
--- shell/Config.in	24 Sep 2004 09:09:44 -0000	1.18
+++ shell/Config.in	2 Jan 2005 15:18:45 -0000
@@ -117,6 +117,14 @@ config CONFIG_ASH_RANDOM_SUPPORT
 	  After "unset RANDOM" then generator will switch off and this
 	  variable will no longer have special treatment.
 
+config CONFIG_ASH_FN_EXT
+	bool "  Enable `.' and `_' characters in function names"
+	default y
+	depends on CONFIG_ASH
+	help
+	  Enable implementation-specific (as defined in XCU, 2.9.5)
+	  characters (`.' and `_') in function names.
+
 config CONFIG_HUSH
 	bool "hush"
 	default n
Index: shell/ash.c
===================================================================
RCS file: /var/cvs/busybox/shell/ash.c,v
retrieving revision 1.108
diff -u -3 -p -u -p -r1.108 ash.c
--- shell/ash.c	8 Oct 2004 09:43:34 -0000	1.108
+++ shell/ash.c	2 Jan 2005 15:19:21 -0000
@@ -600,6 +600,9 @@ static union node *parsecmd(int);
 static void fixredir(union node *, const char *, int);
 static const char *const *findkwd(const char *);
 static char *endofname(const char *);
+#ifdef CONFIG_ASH_FN_EXT
+static char *endoffuncname(const char *);
+#endif
 
 /*      $NetBSD: shell.h,v 1.16 2003/01/22 20:36:04 dsl Exp $   */
 
@@ -753,6 +756,9 @@ static const char *tokname(int tok)
 #define is_digit(c)     ((unsigned)((c) - '0') <= 9)
 #define is_name(c)      ((c) == '_' || isalpha((unsigned char)(c)))
 #define is_in_name(c)   ((c) == '_' || isalnum((unsigned char)(c)))
+#ifdef CONFIG_ASH_FN_EXT
+#define is_fn_ext(c)    ((c) == '.' || (c) == '-')
+#endif
 
 /*
  * is_special(c) evaluates to 1 for c in "!#$*-0123456789?@"; 0 otherwise
@@ -3512,6 +3518,15 @@ goodname(const char *p)
 	return !*endofname(p);
 }
 
+#ifdef CONFIG_ASH_FN_EXT
+static inline int
+goodfuncname(const char *p)
+{
+	return !*endoffuncname(p);
+}
+#endif
+
+
 /*
  * Search for a command.  This is called before we fork so that the
  * location of the command will be available in the parent as well as
@@ -9792,7 +9807,11 @@ simplecmd(void) {
 					synexpect(TRP);
 				name = n->narg.text;
 				if (
+#ifdef CONFIG_ASH_FN_EXT
+					!goodfuncname(name) || (
+#else
 					!goodname(name) || (
+#endif
 						(bcmd = find_builtin(name)) &&
 						IS_BUILTIN_SPECIAL(bcmd)
 					)
@@ -10839,6 +10858,28 @@ endofname(const char *name)
 	return p;
 }
 
+/*
+ * Return of a legal function name (a letter or underscore followed by zero or
+ * more letters, underscores, digits, and extension-specific characters).
+ */
+
+#ifdef CONFIG_ASH_FN_EXT
+static char *
+endoffuncname(const char *name)
+{
+	char *p;
+
+	p = (char *) name;
+	if (! (is_name(*p) || is_fn_ext(*p)))
+		return p;
+	while (*++p) {
+		if (! (is_in_name(*p) || is_fn_ext(*p)))
+			break;
+	}
+	return p;
+}
+#endif /* CONFIG_ASH_FN_EXT */
+
 
 /*
  * Called when an unexpected token is read during the parse.  The argument


More information about the busybox mailing list