do not fail build if MAXSYMLINKS isn't defined
[oweals/busybox.git] / shell / ash.c
index 010924db4a0db30cd80f9352fc28a80e0f233b9f..71ef9a690f5941f567e35b7c911b7a248fde45d4 100644 (file)
@@ -385,6 +385,9 @@ static void trace_vprintf(const char *fmt, va_list va);
 /* ============ Utility functions */
 #define xbarrier() do { __asm__ __volatile__ ("": : :"memory"); } while (0)
 
+#define is_name(c)      ((c) == '_' || isalpha((unsigned char)(c)))
+#define is_in_name(c)   ((c) == '_' || isalnum((unsigned char)(c)))
+
 static int isdigit_str9(const char *str)
 {
        int maxlen = 9 + 1; /* max 9 digits: 999999999 */
@@ -2008,27 +2011,6 @@ getoptsreset(const char *value)
 }
 #endif
 
-/* math.h has these, otherwise define our private copies */
-#if !ENABLE_SH_MATH_SUPPORT
-#define is_name(c)      ((c) == '_' || isalpha((unsigned char)(c)))
-#define is_in_name(c)   ((c) == '_' || isalnum((unsigned char)(c)))
-/*
- * Return the pointer to the first char which is not part of a legal variable name
- * (a letter or underscore followed by letters, underscores, and digits).
- */
-static const char*
-endofname(const char *name)
-{
-       if (!is_name(*name))
-               return name;
-       while (*++name) {
-               if (!is_in_name(*name))
-                       break;
-       }
-       return name;
-}
-#endif
-
 /*
  * Compares two strings up to the first = or '\0'.  The first
  * string must be terminated by '='; the second may be terminated by
@@ -2304,7 +2286,7 @@ unsetvar(const char *s)
                        free(vp);
                        INT_ON;
                } else {
-                       setvar(s, 0, 0);
+                       setvar2(s, 0);
                        vp->flags &= ~VEXPORT;
                }
  ok:
@@ -3564,7 +3546,8 @@ set_curjob(struct job *jp, unsigned mode)
                break;
        case CUR_RUNNING:
                /* newly created job or backgrounded job,
-                  put after all stopped jobs. */
+                * put after all stopped jobs.
+                */
                while (1) {
                        jp1 = *jpp;
 #if JOBS
@@ -6356,7 +6339,7 @@ subevalvar(char *p, char *varname, int strloc, int subtype,
 
        switch (subtype) {
        case VSASSIGN:
-               setvar(varname, startp, 0);
+               setvar2(varname, startp);
                amount = startp - expdest;
                STADJUST(amount, expdest);
                return startp;
@@ -8571,7 +8554,7 @@ evalfor(union node *n, int flags)
        loopnest++;
        flags &= EV_TESTED;
        for (sp = arglist.list; sp; sp = sp->next) {
-               setvar(n->nfor.var, sp->text, 0);
+               setvar2(n->nfor.var, sp->text);
                evaltree(n->nfor.body, flags);
                if (evalskip) {
                        if (evalskip == SKIPCONT && --skipcount <= 0) {
@@ -8675,6 +8658,17 @@ expredir(union node *n)
                        TRACE(("expredir expanded to '%s'\n", fn.list->text));
 #if ENABLE_ASH_BASH_COMPAT
  store_expfname:
+#endif
+#if 0
+// By the design of stack allocator, the loop of this kind:
+//     while true; do while true; do break; done </dev/null; done
+// will look like a memory leak: ash plans to free expfname's
+// of "/dev/null" as soon as it finishes running the loop
+// (in this case, never).
+// This "fix" is wrong:
+                       if (redir->nfile.expfname)
+                               stunalloc(redir->nfile.expfname);
+// It results in corrupted state of stacked allocations.
 #endif
                        redir->nfile.expfname = fn.list->text;
                        break;
@@ -9039,6 +9033,9 @@ static int getoptscmd(int, char **) FAST_FUNC;
 #if !ENABLE_FEATURE_SH_EXTRA_QUIET
 static int helpcmd(int, char **) FAST_FUNC;
 #endif
+#if MAX_HISTORY
+static int historycmd(int, char **) FAST_FUNC;
+#endif
 #if ENABLE_SH_MATH_SUPPORT
 static int letcmd(int, char **) FAST_FUNC;
 #endif
@@ -9112,6 +9109,9 @@ static const struct builtincmd builtintab[] = {
 #if !ENABLE_FEATURE_SH_EXTRA_QUIET
        { BUILTIN_NOSPEC        "help"    , helpcmd    },
 #endif
+#if MAX_HISTORY
+       { BUILTIN_NOSPEC        "history" , historycmd },
+#endif
 #if JOBS
        { BUILTIN_REGULAR       "jobs"    , jobscmd    },
        { BUILTIN_REGULAR       "kill"    , killcmd    },
@@ -9451,7 +9451,7 @@ evalcommand(union node *cmd, int flags)
                 * '_' in 'vi' command mode during line editing...
                 * However I implemented that within libedit itself.
                 */
-               setvar("_", lastarg, 0);
+               setvar2("_", lastarg);
        }
        popstackmark(&smark);
 }
@@ -9657,7 +9657,12 @@ preadfd(void)
                 * _during_ shell execution, not only if it was set when
                 * shell was started. Therefore, re-check LANG every time:
                 */
-               reinit_unicode(lookupvar("LANG"));
+               {
+                       const char *s = lookupvar("LC_ALL");
+                       if (!s) s = lookupvar("LC_CTYPE");
+                       if (!s) s = lookupvar("LANG");
+                       reinit_unicode(s);
+               }
                nr = read_line_input(line_input_state, cmdedit_prompt, buf, IBUFSIZ, timeout);
                if (nr == 0) {
                        /* Ctrl+C pressed */
@@ -11638,8 +11643,9 @@ parsebackq: {
        INT_ON;
        if (oldstyle) {
                /* We must read until the closing backquote, giving special
-                  treatment to some slashes, and then push the string and
-                  reread it as input, interpreting it normally.  */
+                * treatment to some slashes, and then push the string and
+                * reread it as input, interpreting it normally.
+                */
                char *pout;
                size_t psavelen;
                char *pstr;
@@ -12263,8 +12269,10 @@ dotcmd(int argc, char **argv)
        /* "false; . empty_file; echo $?" should print 0, not 1: */
        exitstatus = 0;
 
+       /* This aborts if file isn't found, which is POSIXly correct.
+        * bash returns exitcode 1 instead.
+        */
        fullname = find_dot_file(argv[1]);
-
        argv += 2;
        argc -= 2;
        if (argc) { /* argc > 0, argv[0] != NULL */
@@ -12274,6 +12282,9 @@ dotcmd(int argc, char **argv)
                shellparam.p = argv;
        };
 
+       /* This aborts if file can't be opened, which is POSIXly correct.
+        * bash returns exitcode 1 instead.
+        */
        setinputfile(fullname, INPUT_PUSH_FILE);
        commandname = fullname;
        cmdloop(0);
@@ -12620,6 +12631,15 @@ helpcmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
 }
 #endif /* FEATURE_SH_EXTRA_QUIET */
 
+#if MAX_HISTORY
+static int FAST_FUNC
+historycmd(int argc UNUSED_PARAM, char **argv UNUSED_PARAM)
+{
+       show_history(line_input_state);
+       return EXIT_SUCCESS;
+}
+#endif
+
 /*
  * The export and readonly commands.
  */
@@ -12991,8 +13011,11 @@ init(void)
                        }
                }
 
-               setvar("PPID", utoa(getppid()), 0);
-
+               setvar2("PPID", utoa(getppid()));
+#if ENABLE_ASH_BASH_COMPAT
+               p = lookupvar("SHLVL");
+               setvar2("SHLVL", utoa(p ? atoi(p) + 1 : 1));
+#endif
                p = lookupvar("PWD");
                if (p) {
                        if (*p != '/' || stat(p, &st1) || stat(".", &st2)
@@ -13196,27 +13219,21 @@ int ash_main(int argc UNUSED_PARAM, char **argv)
        setstackmark(&smark);
        procargs(argv);
 
-#if ENABLE_FEATURE_EDITING_SAVEHISTORY
-       if (iflag) {
-               const char *hp = lookupvar("HISTFILE");
-               if (!hp) {
-                       hp = lookupvar("HOME");
-                       if (hp) {
-                               char *defhp = concat_path_file(hp, ".ash_history");
-                               setvar("HISTFILE", defhp, 0);
-                               free(defhp);
-                       }
-               }
-       }
-#endif
        if (argv[0] && argv[0][0] == '-')
                isloginsh = 1;
        if (isloginsh) {
+               const char *hp;
+
                state = 1;
                read_profile("/etc/profile");
  state1:
                state = 2;
-               read_profile(".profile");
+               hp = lookupvar("HOME");
+               if (hp) {
+                       hp = concat_path_file(hp, ".profile");
+                       read_profile(hp);
+                       free((char*)hp);
+               }
        }
  state2:
        state = 3;
@@ -13248,6 +13265,15 @@ int ash_main(int argc UNUSED_PARAM, char **argv)
 #if MAX_HISTORY > 0 && ENABLE_FEATURE_EDITING_SAVEHISTORY
                if (iflag) {
                        const char *hp = lookupvar("HISTFILE");
+                       if (!hp) {
+                               hp = lookupvar("HOME");
+                               if (hp) {
+                                       hp = concat_path_file(hp, ".ash_history");
+                                       setvar2("HISTFILE", hp);
+                                       free((char*)hp);
+                                       hp = lookupvar("HISTFILE");
+                               }
+                       }
                        if (hp)
                                line_input_state->hist_file = hp;
 # if ENABLE_FEATURE_SH_HISTFILESIZE