/* ============ 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 */
}
#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
free(vp);
INT_ON;
} else {
- setvar(s, 0, 0);
+ setvar2(s, 0);
vp->flags &= ~VEXPORT;
}
ok:
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
switch (subtype) {
case VSASSIGN:
- setvar(varname, startp, 0);
+ setvar2(varname, startp);
amount = startp - expdest;
STADJUST(amount, expdest);
return startp;
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) {
#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;
case NFROMFD:
#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
#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 },
* '_' in 'vi' command mode during line editing...
* However I implemented that within libedit itself.
*/
- setvar("_", lastarg, 0);
+ setvar2("_", lastarg);
}
popstackmark(&smark);
}
* _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 */
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;
/* "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 */
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);
}
#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.
*/
}
}
- 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)
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;
#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