2 * startup, main loop, environments and error handling
5 #define EXTERN /* define EXTERNs in sh.h */
8 #include <aros/debug.h>
15 extern char **environ
;
21 static void reclaim(void);
22 static void remove_temps(struct temp
*tp
);
25 * shell initialization
28 static const char initifs
[] = "IFS= \t\n";
30 static const char initsubs
[] = "${PS2=> } ${PS3=#? } ${PS4=+ }";
32 static const char *const initcoms
[] = {
33 "typeset", "-r", "KSH_VERSION", NULL
,
34 "typeset", "-x", "SHELL", "PATH", "HOME", "TMPDIR", "tmp", "LOGNAME",
35 "USER", "HISTFILE", "MAKE", "PREFIX", "PATH_SEPARATOR", "DIR_SEPARATOR", "LD", NULL
,
36 "typeset", "-i", "PPID", NULL
,
37 "typeset", "-i", "OPTIND=1", NULL
,
39 /* Standard ksh aliases */
40 "hash=alias -t", /* not "alias -t --": hash -r needs to work */
42 "autoload=typeset -fu",
43 "functions=typeset -f",
50 /* Aliases that are builtin commands in at&t */
52 /* Aliases that are AmigaOS4 specific - coreutils renames */
54 "date=gdate", "dir=gdir", "install=ginstall", "env=genv",
55 "make=gmake", "info=ginfo",
59 /* this is what at&t ksh seems to track, with the addition of emacs */
61 "cat", "cc", "chmod", "cp", "date", "ed", "grep", "ls",
62 "make", "mv", "pr", "rm", "sed", "sh", "who",
67 #define version_param (initcoms[2])
69 const char *amiversion
__attribute__((used
)) = "$VER: abc-shell " ABC_VERSION
" (" RELEASE_DATE
") " RELEASE_COMMENT
"\0";
70 const char * stack_cookie
__attribute__((used
)) = "$STACK: " STACK_SIZE
"\0";
73 main(int argc
, char *argv
[])
84 /* make sure argv[] is sane */
86 static const char *empty_argv
[] = {"sh", (char *) 0};
88 argv
= (char **) empty_argv
;
95 ainit(aperm
); /* initialize permanent Area */
97 /* set up base environment */
98 memset(&env
, 0, sizeof(env
));
102 newblock(); /* set up global l->vars and l->funs */
104 /* Do this first so output routines (eg, errorf, shellf) can work */
115 /* allocate tables */
117 taliases
= malloc(sizeof(struct table
));
118 aliases
= malloc(sizeof(struct table
));
119 homedirs
= malloc(sizeof(struct table
));
121 /* set up variable and command dictionaries */
122 ktinit(taliases
, APERM
, 0);
123 ktinit(aliases
, APERM
, 0);
124 ktinit(homedirs
, APERM
, 0);
126 /* define shell keywords */
129 /* define built-in commands */
130 ktinit(&builtins
, APERM
, 64); /* must be 2^n (currently 40 builtins) */
131 for (i
= 0; shbuiltins
[i
].name
!= NULL
; i
++)
132 builtin(shbuiltins
[i
].name
, shbuiltins
[i
].func
);
133 for (i
= 0; kshbuiltins
[i
].name
!= NULL
; i
++)
134 builtin(kshbuiltins
[i
].name
, kshbuiltins
[i
].func
);
138 #if defined(__AROS__)
139 def_path
= "/Developer/bin:/Developer/usr/bin:/C:.";
141 def_path
= "/gcc/bin:/SDK/C:/SDK/Local/C:/SDK/Local/newlib/bin:/SDK/Local/clib2/bin:/C:.";
144 /* Set PATH to def_path (will set the path global variable).
145 * (import of environment below will probably change this setting).
148 struct tbl
*vp
= global("PATH");
149 /* setstr can't fail here */
150 setstr(vp
, def_path
, KSH_RETURN_ERROR
);
153 /* Set PATH_SEPARATOR and DIR_SEPARATOR. */
155 struct tbl
*vp
= global("PATH_SEPARATOR");
156 /* setstr can't fail here */
157 setstr(vp
, ":", KSH_RETURN_ERROR
);
161 struct tbl
*vp
= global("DIR_SEPARATOR");
162 /* setstr can't fail here */
163 setstr(vp
, "/", KSH_RETURN_ERROR
);
166 /* Set DISABLE_COMMANDLINE_WILDCARD_EXPANSION - to disable
167 * wildcard expansion in applications using clib2 >= 1.201
170 struct tbl
*vp
= global("DISABLE_COMMANDLINE_WILDCARD_EXPANSION");
171 /* setstr can't fail here */
172 setstr(vp
, "true", KSH_RETURN_ERROR
);
177 struct tbl
*vp
= global("SHELL");
178 /* setstr can't fail here */
179 #if defined(__AROS__)
180 setstr(vp
, "/Developer/bin/sh", KSH_RETURN_ERROR
);
182 setstr(vp
, "/SDK/C/sh", KSH_RETURN_ERROR
);
188 struct tbl
*vp
= global("HOME");
189 /* setstr can't fail here */
190 setstr(vp
, "/home", KSH_RETURN_ERROR
);
195 struct tbl
*vp
= global("TMPDIR");
196 /* setstr can't fail here */
197 setstr(vp
, "/T", KSH_RETURN_ERROR
);
202 struct tbl
*vp
= global("tmp");
203 /* setstr can't fail here */
204 setstr(vp
, "/T", KSH_RETURN_ERROR
);
209 struct tbl
*vp
= global("LOGNAME");
210 /* setstr can't fail here */
211 setstr(vp
, "root", KSH_RETURN_ERROR
);
214 /* Set USER - deprecated synonym of LOGNAME. */
216 struct tbl
*vp
= global("USER");
217 /* setstr can't fail here */
218 setstr(vp
, "root", KSH_RETURN_ERROR
);
221 /* Set HISTFILE - history file. */
223 struct tbl
*vp
= global("HISTFILE");
224 /* setstr can't fail here */
225 #if defined(__AROS__)
226 setstr(vp
, "/Developer/Data/abc-shell/history", KSH_RETURN_ERROR
);
228 setstr(vp
, "/SDK/Data/abc-shell/history", KSH_RETURN_ERROR
);
235 struct tbl
*vp
= global("MAKE");
236 /* setstr can't fail here */
237 setstr(vp
, "/SDK/C/gmake", KSH_RETURN_ERROR
);
242 struct tbl
*vp
= global("LD");
243 /* setstr can't fail here */
244 setstr(vp
, "ld", KSH_RETURN_ERROR
);
248 /* Turn on brace expansion by default. At&t ksh's that have
249 * alternation always have it on. BUT, posix doesn't have
250 * brace expansion, so set this before setting up FPOSIX
251 * (change_flag() clears FBRACEEXPAND when FPOSIX is set).
254 Flag(FBRACEEXPAND
) = 1;
256 /* set posix flag just before environment so that it will have
257 * exactly the same effect as the POSIXLY_CORRECT environment
258 * variable. If this needs to be done sooner to ensure correct posix
259 * operation, an initial scan of the environment will also have
262 change_flag(FPOSIX
, OF_SPECIAL
, 1);
264 /* import environment */
267 for (wp
= environ
; *wp
!= NULL
; wp
++)
269 if(strncmp("_=",*wp
,2))
271 typeset(*wp
, IMPORTV
|EXPORTV
, 0, 0, 0);
275 typeset(*wp
,IMPORTV
, 0, 0, 0);
279 kshpid
= procpid
= getpid();
280 typeset(initifs
, 0, 0, 0, 0); /* for security */
282 /* assign default shell variable values */
283 substitute(initsubs
, 0);
285 /* Figure out the current working directory and set $PWD */
287 struct stat s_pwd
, s_dot
;
288 struct tbl
*pwd_v
= global("PWD");
289 char *pwd
= str_val(pwd_v
);
292 /* Try to use existing $PWD if it is valid */
294 stat(pwd
, &s_pwd
) < 0 || stat(".", &s_dot
) < 0 ||
295 s_pwd
.st_dev
!= s_dot
.st_dev
||
296 s_pwd
.st_ino
!= s_dot
.st_ino
)
298 set_current_wd(pwdx
);
300 simplify_path(current_wd
);
301 /* Only set pwd if we know where we are or if it had a
304 if (current_wd
[0] || pwd
!= null
)
305 /* setstr can't fail here */
306 setstr(pwd_v
, current_wd
, KSH_RETURN_ERROR
);
309 setint(global("PPID"), (long) ppid
);
310 setint(global("RANDOM"), (long) (time((time_t *)0) * kshpid
* ppid
));
311 /* setstr can't fail here */
312 setstr(global(version_param
), ksh_version
, KSH_RETURN_ERROR
);
314 /* execute initialization statements */
315 for (wp
= (char**) initcoms
; *wp
!= NULL
; wp
++) {
317 for (; *wp
!= NULL
; wp
++)
325 safe_prompt
= "$PWD> ";
327 struct tbl
*vp
= global("PS1");
329 /* Set PS1 if it isn't set, or we are root and prompt doesn't
332 if (!(vp
->flag
& ISSET
))
333 /* setstr can't fail here */
334 setstr(vp
, safe_prompt
, KSH_RETURN_ERROR
);
337 argi
= parse_args(argv
, OF_CMDLINE
, (int *) 0);
341 if (Flag(FCOMMAND
)) {
342 s
= pushs(SSTRING
, ATEMP
);
343 if (!(s
->start
= s
->str
= argv
[argi
++]))
344 errorf("-c requires an argument");
346 kshname
= argv
[argi
++];
347 } else if (argi
< argc
&& !Flag(FSTDIN
)) {
348 s
= pushs(SFILE
, ATEMP
);
349 s
->file
= argv
[argi
++];
350 s
->u
.shf
= shf_open(s
->file
, O_RDONLY
, 0, SHF_MAPHI
|SHF_CLEXEC
);
351 if (s
->u
.shf
== NULL
) {
352 exstat
= 127; /* POSIX */
353 errorf("%s: %s", s
->file
, strerror(errno
));
358 s
= pushs(SSTDIN
, ATEMP
);
360 s
->u
.shf
= shf_fdopen(0, SHF_RD
| can_seek(0),
362 if (isatty(0) && isatty(2)) {
363 Flag(FTALKING
) = Flag(FTALKING_I
) = 1;
364 /* The following only if isatty(0) */
366 s
->u
.shf
->flags
|= SHF_INTERRUPT
;
367 s
->file
= (char *) 0;
371 /* This bizarreness is mandated by POSIX */
375 if (fstat(0, &s_stdin
) >= 0 && S_ISCHR(s_stdin
.st_mode
) &&
383 l
->argv
= &argv
[argi
- 1];
384 l
->argc
= argc
- argi
;
385 l
->argv
[0] = (char *) kshname
;
388 errexit
= Flag(FERREXIT
);
391 if (!current_wd
[0] && Flag(FTALKING
))
392 warningf(false, "Cannot determine current working directory");
397 env_file
= str_val(global("ENV"));
399 /* If env isn't set, include default environment */
400 if (env_file
== null
)
401 #if defined(__AROS__)
402 env_file
= strdup("/Developer/Data/abc-shell/variables");
404 env_file
= strdup("/SDK/Data/abc-shell/variables");
407 env_file
= substitute(env_file
, DOTILDE
);
408 if (*env_file
!= '\0')
409 include(env_file
, 0, (char **) 0, 1);
414 if (Flag(FTALKING
)) {
418 Flag(FTRACKALL
) = 1; /* set after ENV */
420 shell(s
, true); /* doesn't return */
425 include(const char *name
, int argc
, char **argv
, int intr_ok
)
427 Source
*volatile s
= NULL
;
429 char **volatile old_argv
;
430 volatile int old_argc
;
433 shf
= shf_open(name
, O_RDONLY
, 0, SHF_MAPHI
|SHF_CLEXEC
);
438 old_argv
= e
->loc
->argv
;
439 old_argc
= e
->loc
->argc
;
441 old_argv
= (char **) 0;
445 i
= ksh_sigsetjmp(e
->jbuf
, 0);
447 quitenv(s
? s
->u
.shf
: NULL
);
449 e
->loc
->argv
= old_argv
;
450 e
->loc
->argc
= old_argc
;
455 return exstat
& 0xff; /* see below */
457 if (intr_ok
&& (exstat
- 128) != SIGTERM
)
466 internal_errorf(1, "include: %d", i
);
474 s
= pushs(SFILE
, ATEMP
);
476 s
->file
= str_save(name
, ATEMP
);
480 e
->loc
->argv
= old_argv
;
481 e
->loc
->argc
= old_argc
;
483 return i
& 0xff; /* & 0xff to ensure value not -1 */
487 command(const char *comm
)
491 s
= pushs(SSTRING
, ATEMP
);
492 s
->start
= s
->str
= comm
;
493 return shell(s
, false);
497 * run the commands from the input source, returning status.
500 shell(Source
*volatile s
, volatile int toplevel
)
503 volatile int wastty
= s
->flags
& SF_TTY
;
504 volatile int attempts
= 13;
505 volatile int interactive
= Flag(FTALKING
) && toplevel
;
506 Source
*volatile old_source
= source
;
512 i
= ksh_sigsetjmp(e
->jbuf
, 0);
515 case LINTR
: /* we get here if SIGINT not caught or ignored */
521 /* Reset any eof that was read as part of a
524 if (Flag(FIGNOREEOF
) && s
->type
== SEOF
&&
527 /* Used by exit command to get back to
528 * top level shell. Kind of strange since
529 * interactive is set if we are reading from
532 /* toss any input we have so far */
533 s
->start
= s
->str
= null
;
542 unwind(i
); /* keep on going */
547 internal_errorf(1, "shell: %d", i
);
556 if (s
->next
== NULL
) {
560 s
->flags
&= ~SF_ECHO
;
569 if (t
!= NULL
&& t
->type
== TEOF
) {
570 if (wastty
&& Flag(FIGNOREEOF
) && --attempts
> 0) {
571 shellf("Use `exit' to leave ksh\n");
573 } else if (wastty
&& !really_exit
&&
579 /* this for POSIX, which says EXIT traps
580 * shall be taken in the environment
581 * immediately after the last command
590 if (t
&& (!Flag(FNOEXEC
) || (s
->flags
& SF_TTY
)))
591 exstat
= execute(t
, 0);
593 if (t
!= NULL
&& t
->type
!= TEOF
&& interactive
&& really_exit
)
603 /* return to closest error handler or shell(), exit if none found */
607 /* ordering for EXIT vs ERR is a bit odd (this is what at&t ksh does) */
608 if (i
== LEXIT
|| (Flag(FERREXIT
) && (i
== LERROR
|| i
== LINTR
) &&
609 sigtraps
[SIGEXIT_
].trap
))
611 runtrap(&sigtraps
[SIGEXIT_
]);
613 } else if (Flag(FERREXIT
) && (i
== LERROR
|| i
== LINTR
)) {
614 runtrap(&sigtraps
[SIGERR_
]);
625 ksh_siglongjmp(e
->jbuf
, i
);
630 e
->flags
|= EF_FAKE_SIGDIE
;
643 ep
= (struct env
*) alloc(sizeof(*ep
), ATEMP
);
655 quitenv(struct shf
*shf
)
660 if (ep
->oenv
&& ep
->oenv
->loc
!= ep
->loc
)
662 if (ep
->savefd
!= NULL
) {
663 for (fd
= 0; fd
< NUFILE
; fd
++)
664 /* if ep->savefd[fd] < 0, means fd was closed */
666 restfd(fd
, ep
->savefd
[fd
]);
667 if (ep
->savefd
[2]) /* Clear any write errors */
668 shf_reopen(2, SHF_WR
, shl_out
);
671 /* Bottom of the stack.
672 * Either main shell is exiting or cleanup_parents_env() was called.
674 if (ep
->oenv
== NULL
) {
675 if (ep
->type
== E_NONE
) { /* Main shell exiting? */
679 if (ep
->flags
& EF_FAKE_SIGDIE
) {
680 int sig
= exstat
- 128;
682 /* ham up our death a bit (at&t ksh
683 * only seems to do this for SIGTERM)
684 * Don't do it for SIGQUIT, since we'd
687 if (sig
== SIGINT
|| sig
== SIGTERM
) {
688 setsig(&sigtraps
[sig
], SIG_DFL
,
689 SS_RESTORE_CURR
|SS_FORCE
);
695 #endif /* MEM_DEBUG */
710 /* Called after a fork to cleanup stuff left over from parents environment */
712 cleanup_parents_env(void)
717 /* Don't clean up temporary files - parent will probably need them.
718 * Also, can't easily reclaim memory since variables, etc. could be
722 /* close all file descriptors hiding in savefd */
723 for (ep
= e
; ep
; ep
= ep
->oenv
) {
725 for (fd
= 0; fd
< NUFILE
; fd
++)
726 if (ep
->savefd
[fd
] > 0)
727 close(ep
->savefd
[fd
]);
728 afree(ep
->savefd
, &ep
->area
);
729 ep
->savefd
= (short *) 0;
732 e
->oenv
= (struct env
*) 0;
735 /* Called just before an execve cleanup stuff temporary files */
737 cleanup_proc_env(void)
741 for (ep
= e
; ep
; ep
= ep
->oenv
)
742 remove_temps(ep
->temps
);
745 /* remove temp files and free ATEMP Area */
749 remove_temps(e
->temps
);
755 remove_temps(struct temp
*tp
)
757 for (; tp
!= NULL
; tp
= tp
->next
)
758 if (tp
->pid
== procpid
) {