1 |
/* $OpenBSD: main.c,v 1.43 2007/05/31 20:47:44 otto Exp $ */ |
2 |
/* $OpenBSD: tty.c,v 1.9 2006/03/14 22:08:01 deraadt Exp $ */ |
3 |
/* $OpenBSD: io.c,v 1.22 2006/03/17 16:30:13 millert Exp $ */ |
4 |
/* $OpenBSD: table.c,v 1.12 2005/12/11 20:31:21 otto Exp $ */ |
5 |
|
6 |
#define EXTERN |
7 |
#include "sh.h" |
8 |
|
9 |
#if HAVE_LANGINFO_CODESET |
10 |
#include <langinfo.h> |
11 |
#endif |
12 |
#if HAVE_SETLOCALE_CTYPE |
13 |
#include <locale.h> |
14 |
#endif |
15 |
|
16 |
__RCSID("$MirOS: src/bin/mksh/main.c,v 1.87 2007/08/19 22:06:26 tg Exp $"); |
17 |
|
18 |
extern char **environ; |
19 |
|
20 |
#if !HAVE_SETRESUGID |
21 |
extern uid_t kshuid; |
22 |
extern gid_t kshgid, kshegid; |
23 |
#endif |
24 |
|
25 |
static void reclaim(void); |
26 |
static void remove_temps(struct temp *); |
27 |
|
28 |
static const char initifs[] = "IFS= \t\n"; |
29 |
|
30 |
static const char initsubs[] = "${PS2=> } ${PS3=#? } ${PS4=+ }"; |
31 |
|
32 |
static const char *initcoms[] = { |
33 |
"typeset", "-r", initvsn, NULL, |
34 |
"typeset", "-x", "SHELL", "PATH", "HOME", NULL, |
35 |
"typeset", "-i", "PPID", "OPTIND=1", NULL, |
36 |
"eval", "typeset -i RANDOM SECONDS=\"${SECONDS-0}\" TMOUT=\"${TMOUT-0}\"", NULL, |
37 |
"alias", "integer=typeset -i", "local=typeset", NULL, |
38 |
NULL |
39 |
}; |
40 |
|
41 |
static const char *initcoms_korn[] = { |
42 |
"alias", |
43 |
"hash=alias -t", /* not "alias -t --": hash -r needs to work */ |
44 |
"type=whence -v", |
45 |
"stop=kill -STOP", |
46 |
"suspend=kill -STOP $$", |
47 |
"autoload=typeset -fu", |
48 |
"functions=typeset -f", |
49 |
"history=fc -l", |
50 |
"nohup=nohup ", |
51 |
"r=fc -e -", |
52 |
"source=PATH=$PATH:. command .", |
53 |
"login=exec login", |
54 |
NULL, |
55 |
/* this is what at&t ksh seems to track, with the addition of emacs */ |
56 |
"alias", "-tU", |
57 |
"cat", "cc", "chmod", "cp", "date", "ed", "emacs", "grep", "ls", |
58 |
"make", "mv", "pr", "rm", "sed", "sh", "vi", "who", NULL, |
59 |
NULL |
60 |
}; |
61 |
|
62 |
static int initio_done; |
63 |
|
64 |
int |
65 |
main(int argc, const char *argv[]) |
66 |
{ |
67 |
int argi, i; |
68 |
Source *s; |
69 |
struct block *l; |
70 |
int restricted, errexit; |
71 |
const char **wp, *cc; |
72 |
struct env env; |
73 |
pid_t ppid; |
74 |
struct tbl *vp; |
75 |
struct stat s_stdin; |
76 |
#if !defined(_PATH_DEFPATH) && defined(_CS_PATH) |
77 |
size_t k; |
78 |
char *cp; |
79 |
#endif |
80 |
|
81 |
/* make sure argv[] is sane */ |
82 |
if (!*argv) { |
83 |
static const char *empty_argv[] = { |
84 |
"mksh", NULL |
85 |
}; |
86 |
|
87 |
argv = empty_argv; |
88 |
argc = 1; |
89 |
} |
90 |
kshname = *argv; |
91 |
|
92 |
ainit(&aperm); /* initialise permanent Area */ |
93 |
|
94 |
/* set up base environment */ |
95 |
memset(&env, 0, sizeof(env)); |
96 |
env.type = E_NONE; |
97 |
ainit(&env.area); |
98 |
e = &env; |
99 |
newblock(); /* set up global l->vars and l->funs */ |
100 |
|
101 |
/* Do this first so output routines (eg, errorf, shellf) can work */ |
102 |
initio(); |
103 |
|
104 |
argi = parse_args(argv, OF_FIRSTTIME, NULL); |
105 |
if (argi < 0) |
106 |
exit(1); |
107 |
|
108 |
initvar(); |
109 |
|
110 |
initctypes(); |
111 |
|
112 |
inittraps(); |
113 |
|
114 |
coproc_init(); |
115 |
|
116 |
/* set up variable and command dictionaries */ |
117 |
ktinit(&taliases, APERM, 0); |
118 |
ktinit(&aliases, APERM, 0); |
119 |
#ifndef MKSH_SMALL |
120 |
ktinit(&homedirs, APERM, 0); |
121 |
#endif |
122 |
|
123 |
/* define shell keywords */ |
124 |
initkeywords(); |
125 |
|
126 |
/* define built-in commands */ |
127 |
ktinit(&builtins, APERM, 64); /* must be 2^n (currently 40 builtins) */ |
128 |
for (i = 0; mkshbuiltins[i].name != NULL; i++) |
129 |
builtin(mkshbuiltins[i].name, mkshbuiltins[i].func); |
130 |
|
131 |
init_histvec(); |
132 |
|
133 |
#ifdef _PATH_DEFPATH |
134 |
def_path = _PATH_DEFPATH; |
135 |
#else |
136 |
#ifdef _CS_PATH |
137 |
if ((k = confstr(_CS_PATH, NULL, 0)) != (size_t)-1 && k > 0 && |
138 |
confstr(_CS_PATH, cp = alloc(k + 1, APERM), k + 1) == k + 1) |
139 |
def_path = cp; |
140 |
else |
141 |
#endif |
142 |
/* |
143 |
* this is uniform across all OSes unless it |
144 |
* breaks somewhere; don't try to optimise, |
145 |
* e.g. add stuff for Interix or remove /usr |
146 |
* for HURD, because e.g. Debian GNU/HURD is |
147 |
* "keeping a regular /usr"; this is supposed |
148 |
* to be a sane 'basic' default PATH |
149 |
*/ |
150 |
def_path = "/bin:/usr/bin:/sbin:/usr/sbin"; |
151 |
#endif |
152 |
|
153 |
/* Set PATH to def_path (will set the path global variable). |
154 |
* (import of environment below will probably change this setting). |
155 |
*/ |
156 |
vp = global("PATH"); |
157 |
/* setstr can't fail here */ |
158 |
setstr(vp, def_path, KSH_RETURN_ERROR); |
159 |
|
160 |
/* Set FPOSIX if we're called as -sh or /bin/sh or so */ |
161 |
cc = kshname; |
162 |
i = 0; argi = 0; |
163 |
while (cc[i] != '\0') |
164 |
if ((cc[i++] | 2) == '/') |
165 |
argi = i; |
166 |
if (((cc[argi] | 0x20) == 's') && ((cc[argi + 1] | 0x20) == 'h')) |
167 |
Flag(FPOSIX) = 1; |
168 |
|
169 |
/* Turn on nohup by default for now - will change to off |
170 |
* by default once people are aware of its existence |
171 |
* (at&t ksh does not have a nohup option - it always sends |
172 |
* the hup). |
173 |
*/ |
174 |
Flag(FNOHUP) = 1; |
175 |
|
176 |
/* Turn on brace expansion by default. AT&T kshs that have |
177 |
* alternation always have it on. |
178 |
*/ |
179 |
Flag(FBRACEEXPAND) = 1; |
180 |
|
181 |
/* Set edit mode to emacs by default, may be overridden |
182 |
* by the environment or the user. Also, we want tab completion |
183 |
* on in vi by default. */ |
184 |
change_flag(FEMACS, OF_SPECIAL, 1); |
185 |
#ifndef MKSH_NOVI |
186 |
Flag(FVITABCOMPLETE) = 1; |
187 |
#endif |
188 |
|
189 |
/* import environment */ |
190 |
if (environ != NULL) |
191 |
for (wp = (const char **)environ; *wp != NULL; wp++) |
192 |
typeset(*wp, IMPORT | EXPORT, 0, 0, 0); |
193 |
|
194 |
kshpid = procpid = getpid(); |
195 |
typeset(initifs, 0, 0, 0, 0); /* for security */ |
196 |
|
197 |
/* assign default shell variable values */ |
198 |
substitute(initsubs, 0); |
199 |
|
200 |
/* Figure out the current working directory and set $PWD */ |
201 |
{ |
202 |
struct stat s_pwd, s_dot; |
203 |
struct tbl *pwd_v = global("PWD"); |
204 |
char *pwd = str_val(pwd_v); |
205 |
char *pwdx = pwd; |
206 |
|
207 |
/* Try to use existing $PWD if it is valid */ |
208 |
if (pwd[0] != '/' || |
209 |
stat(pwd, &s_pwd) < 0 || stat(".", &s_dot) < 0 || |
210 |
s_pwd.st_dev != s_dot.st_dev || |
211 |
s_pwd.st_ino != s_dot.st_ino) |
212 |
pwdx = NULL; |
213 |
set_current_wd(pwdx); |
214 |
if (current_wd[0]) |
215 |
simplify_path(current_wd); |
216 |
/* Only set pwd if we know where we are or if it had a |
217 |
* bogus value |
218 |
*/ |
219 |
if (current_wd[0] || pwd != null) |
220 |
/* setstr can't fail here */ |
221 |
setstr(pwd_v, current_wd, KSH_RETURN_ERROR); |
222 |
} |
223 |
ppid = getppid(); |
224 |
change_random(((u_long)kshname) ^ ((u_long)time(NULL) * kshpid * ppid)); |
225 |
#if HAVE_ARC4RANDOM |
226 |
Flag(FARC4RANDOM) = 2; /* use arc4random(3) until $RANDOM is written */ |
227 |
#endif |
228 |
setint(global("PPID"), (long)ppid); |
229 |
|
230 |
for (wp = initcoms; *wp != NULL; wp++) { |
231 |
shcomexec(wp); |
232 |
while (*wp != NULL) |
233 |
wp++; |
234 |
} |
235 |
|
236 |
safe_prompt = (ksheuid = geteuid()) ? "$ " : "# "; |
237 |
vp = global("PS1"); |
238 |
/* Set PS1 if unset or we are root and prompt doesn't contain a # */ |
239 |
if (!(vp->flag & ISSET) || |
240 |
(!ksheuid && !strchr(str_val(vp), '#'))) |
241 |
/* setstr can't fail here */ |
242 |
setstr(vp, safe_prompt, KSH_RETURN_ERROR); |
243 |
|
244 |
/* Set this before parsing arguments */ |
245 |
#if HAVE_SETRESUGID |
246 |
Flag(FPRIVILEGED) = getuid() != ksheuid || getgid() != getegid(); |
247 |
#else |
248 |
Flag(FPRIVILEGED) = (kshuid = getuid()) != ksheuid || |
249 |
(kshgid = getgid()) != (kshegid = getegid()); |
250 |
#endif |
251 |
|
252 |
/* this to note if monitor is set on command line (see below) */ |
253 |
Flag(FMONITOR) = 127; |
254 |
argi = parse_args(argv, OF_CMDLINE, NULL); |
255 |
if (argi < 0) |
256 |
exit(1); |
257 |
|
258 |
if (!Flag(FPOSIX)) |
259 |
for (wp = initcoms_korn; *wp != NULL; wp++) { |
260 |
shcomexec(wp); |
261 |
while (*wp != NULL) |
262 |
wp++; |
263 |
} |
264 |
|
265 |
if (Flag(FCOMMAND)) { |
266 |
s = pushs(SSTRING, ATEMP); |
267 |
if (!(s->start = s->str = argv[argi++])) |
268 |
errorf("-c requires an argument"); |
269 |
if (argv[argi]) |
270 |
kshname = argv[argi++]; |
271 |
} else if (argi < argc && !Flag(FSTDIN)) { |
272 |
s = pushs(SFILE, ATEMP); |
273 |
s->file = kshname = argv[argi++]; |
274 |
s->u.shf = shf_open(s->file, O_RDONLY, 0, |
275 |
SHF_MAPHI | SHF_CLEXEC); |
276 |
if (s->u.shf == NULL) { |
277 |
exstat = 127; /* POSIX */ |
278 |
errorf("%s: %s", s->file, strerror(errno)); |
279 |
} |
280 |
} else { |
281 |
Flag(FSTDIN) = 1; |
282 |
s = pushs(SSTDIN, ATEMP); |
283 |
s->file = "<stdin>"; |
284 |
s->u.shf = shf_fdopen(0, SHF_RD | can_seek(0), |
285 |
NULL); |
286 |
if (isatty(0) && isatty(2)) { |
287 |
Flag(FTALKING) = Flag(FTALKING_I) = 1; |
288 |
/* The following only if isatty(0) */ |
289 |
s->flags |= SF_TTY; |
290 |
s->u.shf->flags |= SHF_INTERRUPT; |
291 |
s->file = NULL; |
292 |
} |
293 |
} |
294 |
|
295 |
/* This bizarreness is mandated by POSIX */ |
296 |
if (fstat(0, &s_stdin) >= 0 && S_ISCHR(s_stdin.st_mode) && |
297 |
Flag(FTALKING)) |
298 |
reset_nonblock(0); |
299 |
|
300 |
/* initialise job control */ |
301 |
i = Flag(FMONITOR) != 127; |
302 |
Flag(FMONITOR) = 0; |
303 |
j_init(i); |
304 |
/* Do this after j_init(), as tty_fd is not initialised 'til then */ |
305 |
if (Flag(FTALKING)) { |
306 |
#ifndef MKSH_ASSUME_UTF8 |
307 |
#if HAVE_SETLOCALE_CTYPE |
308 |
#define isuc(x) (((x) != NULL) && \ |
309 |
(stristr((x), "UTF-8") || stristr((x), "utf8"))) |
310 |
/* Check if we're in a UTF-8 locale */ |
311 |
if (!Flag(FUTFHACK)) { |
312 |
cc = setlocale(LC_CTYPE, ""); |
313 |
#if HAVE_LANGINFO_CODESET |
314 |
if (!isuc(cc)) |
315 |
cc = nl_langinfo(CODESET); |
316 |
#endif |
317 |
Flag(FUTFHACK) = isuc(cc); |
318 |
} |
319 |
#undef isuc |
320 |
#endif |
321 |
#else |
322 |
Flag(FUTFHACK) = 1; |
323 |
#endif |
324 |
x_init(); |
325 |
} |
326 |
|
327 |
l = e->loc; |
328 |
l->argv = &argv[argi - 1]; |
329 |
l->argc = argc - argi; |
330 |
l->argv[0] = kshname; |
331 |
getopts_reset(1); |
332 |
|
333 |
/* Disable during .profile/ENV reading */ |
334 |
restricted = Flag(FRESTRICTED); |
335 |
Flag(FRESTRICTED) = 0; |
336 |
errexit = Flag(FERREXIT); |
337 |
Flag(FERREXIT) = 0; |
338 |
|
339 |
/* Do this before profile/$ENV so that if it causes problems in them, |
340 |
* user will know why things broke. |
341 |
*/ |
342 |
if (!current_wd[0] && Flag(FTALKING)) |
343 |
warningf(false, "Cannot determine current working directory"); |
344 |
|
345 |
if (Flag(FLOGIN)) { |
346 |
include(KSH_SYSTEM_PROFILE, 0, NULL, 1); |
347 |
if (!Flag(FPRIVILEGED)) |
348 |
include(substitute("$HOME/.profile", 0), 0, |
349 |
NULL, 1); |
350 |
} |
351 |
if (Flag(FPRIVILEGED)) |
352 |
include("/etc/suid_profile", 0, NULL, 1); |
353 |
else if (Flag(FTALKING)) { |
354 |
char *env_file; |
355 |
|
356 |
/* include $ENV */ |
357 |
env_file = substitute(substitute("${ENV:-~/.mkshrc}", 0), |
358 |
DOTILDE); |
359 |
if (*env_file != '\0') |
360 |
include(env_file, 0, NULL, 1); |
361 |
} |
362 |
|
363 |
if (restricted) { |
364 |
static const char *restr_com[] = { |
365 |
"typeset", "-r", "PATH", |
366 |
"ENV", "SHELL", |
367 |
NULL |
368 |
}; |
369 |
shcomexec(restr_com); |
370 |
/* After typeset command... */ |
371 |
Flag(FRESTRICTED) = 1; |
372 |
} |
373 |
if (errexit) |
374 |
Flag(FERREXIT) = 1; |
375 |
|
376 |
if (Flag(FTALKING)) { |
377 |
hist_init(s); |
378 |
alarm_init(); |
379 |
} else |
380 |
Flag(FTRACKALL) = 1; /* set after ENV */ |
381 |
|
382 |
shell(s, true); /* doesn't return */ |
383 |
return 0; |
384 |
} |
385 |
|
386 |
int |
387 |
include(const char *name, int argc, const char **argv, int intr_ok) |
388 |
{ |
389 |
Source *volatile s = NULL; |
390 |
struct shf *shf; |
391 |
const char **volatile old_argv; |
392 |
volatile int old_argc; |
393 |
int i; |
394 |
|
395 |
shf = shf_open(name, O_RDONLY, 0, SHF_MAPHI | SHF_CLEXEC); |
396 |
if (shf == NULL) |
397 |
return -1; |
398 |
|
399 |
if (argv) { |
400 |
old_argv = e->loc->argv; |
401 |
old_argc = e->loc->argc; |
402 |
} else { |
403 |
old_argv = NULL; |
404 |
old_argc = 0; |
405 |
} |
406 |
newenv(E_INCL); |
407 |
i = sigsetjmp(e->jbuf, 0); |
408 |
if (i) { |
409 |
quitenv(s ? s->u.shf : NULL); |
410 |
if (old_argv) { |
411 |
e->loc->argv = old_argv; |
412 |
e->loc->argc = old_argc; |
413 |
} |
414 |
switch (i) { |
415 |
case LRETURN: |
416 |
case LERROR: |
417 |
return exstat & 0xff; /* see below */ |
418 |
case LINTR: |
419 |
/* intr_ok is set if we are including .profile or $ENV. |
420 |
* If user ^Cs out, we don't want to kill the shell... |
421 |
*/ |
422 |
if (intr_ok && (exstat - 128) != SIGTERM) |
423 |
return 1; |
424 |
/* FALLTHRU */ |
425 |
case LEXIT: |
426 |
case LLEAVE: |
427 |
case LSHELL: |
428 |
unwind(i); |
429 |
/* NOTREACHED */ |
430 |
default: |
431 |
internal_errorf("include: %d", i); |
432 |
/* NOTREACHED */ |
433 |
} |
434 |
} |
435 |
if (argv) { |
436 |
e->loc->argv = argv; |
437 |
e->loc->argc = argc; |
438 |
} |
439 |
s = pushs(SFILE, ATEMP); |
440 |
s->u.shf = shf; |
441 |
s->file = str_save(name, ATEMP); |
442 |
i = shell(s, false); |
443 |
quitenv(s->u.shf); |
444 |
if (old_argv) { |
445 |
e->loc->argv = old_argv; |
446 |
e->loc->argc = old_argc; |
447 |
} |
448 |
return i & 0xff; /* & 0xff to ensure value not -1 */ |
449 |
} |
450 |
|
451 |
int |
452 |
command(const char *comm) |
453 |
{ |
454 |
Source *s; |
455 |
|
456 |
s = pushs(SSTRING, ATEMP); |
457 |
s->start = s->str = comm; |
458 |
return shell(s, false); |
459 |
} |
460 |
|
461 |
/* |
462 |
* run the commands from the input source, returning status. |
463 |
*/ |
464 |
int |
465 |
shell(Source * volatile s, volatile int toplevel) |
466 |
{ |
467 |
struct op *t; |
468 |
volatile int wastty = s->flags & SF_TTY; |
469 |
volatile int attempts = 13; |
470 |
volatile int interactive = Flag(FTALKING) && toplevel; |
471 |
Source *volatile old_source = source; |
472 |
int i; |
473 |
|
474 |
s->flags |= SF_FIRST; /* enable UTF-8 BOM check */ |
475 |
|
476 |
newenv(E_PARSE); |
477 |
if (interactive) |
478 |
really_exit = 0; |
479 |
i = sigsetjmp(e->jbuf, 0); |
480 |
if (i) { |
481 |
switch (i) { |
482 |
case LINTR: /* we get here if SIGINT not caught or ignored */ |
483 |
case LERROR: |
484 |
case LSHELL: |
485 |
if (interactive) { |
486 |
if (i == LINTR) |
487 |
shellf("\n"); |
488 |
/* Reset any eof that was read as part of a |
489 |
* multiline command. |
490 |
*/ |
491 |
if (Flag(FIGNOREEOF) && s->type == SEOF && |
492 |
wastty) |
493 |
s->type = SSTDIN; |
494 |
/* Used by exit command to get back to |
495 |
* top level shell. Kind of strange since |
496 |
* interactive is set if we are reading from |
497 |
* a tty, but to have stopped jobs, one only |
498 |
* needs FMONITOR set (not FTALKING/SF_TTY)... |
499 |
*/ |
500 |
/* toss any input we have so far */ |
501 |
s->start = s->str = null; |
502 |
break; |
503 |
} |
504 |
/* FALLTHRU */ |
505 |
case LEXIT: |
506 |
case LLEAVE: |
507 |
case LRETURN: |
508 |
source = old_source; |
509 |
quitenv(NULL); |
510 |
unwind(i); /* keep on going */ |
511 |
/* NOTREACHED */ |
512 |
default: |
513 |
source = old_source; |
514 |
quitenv(NULL); |
515 |
internal_errorf("shell: %d", i); |
516 |
/* NOTREACHED */ |
517 |
} |
518 |
} |
519 |
while (1) { |
520 |
if (trap) |
521 |
runtraps(0); |
522 |
|
523 |
if (s->next == NULL) { |
524 |
if (Flag(FVERBOSE)) |
525 |
s->flags |= SF_ECHO; |
526 |
else |
527 |
s->flags &= ~SF_ECHO; |
528 |
} |
529 |
if (interactive) { |
530 |
j_notify(); |
531 |
set_prompt(PS1, s); |
532 |
} |
533 |
t = compile(s); |
534 |
if (t != NULL && t->type == TEOF) { |
535 |
if (wastty && Flag(FIGNOREEOF) && --attempts > 0) { |
536 |
shellf("Use 'exit' to leave ksh\n"); |
537 |
s->type = SSTDIN; |
538 |
} else if (wastty && !really_exit && |
539 |
j_stopped_running()) { |
540 |
really_exit = 1; |
541 |
s->type = SSTDIN; |
542 |
} else { |
543 |
/* this for POSIX which says EXIT traps |
544 |
* shall be taken in the environment |
545 |
* immediately after the last command |
546 |
* executed. |
547 |
*/ |
548 |
if (toplevel) |
549 |
unwind(LEXIT); |
550 |
break; |
551 |
} |
552 |
} |
553 |
if (t && (!Flag(FNOEXEC) || (s->flags & SF_TTY))) |
554 |
exstat = execute(t, 0); |
555 |
|
556 |
if (t != NULL && t->type != TEOF && interactive && really_exit) |
557 |
really_exit = 0; |
558 |
|
559 |
reclaim(); |
560 |
} |
561 |
quitenv(NULL); |
562 |
source = old_source; |
563 |
return exstat; |
564 |
} |
565 |
|
566 |
/* return to closest error handler or shell(), exit if none found */ |
567 |
void |
568 |
unwind(int i) |
569 |
{ |
570 |
/* ordering for EXIT vs ERR is a bit odd (this is what at&t ksh does) */ |
571 |
if (i == LEXIT || (Flag(FERREXIT) && (i == LERROR || i == LINTR) && |
572 |
sigtraps[SIGEXIT_].trap)) { |
573 |
runtrap(&sigtraps[SIGEXIT_]); |
574 |
i = LLEAVE; |
575 |
} else if (Flag(FERREXIT) && (i == LERROR || i == LINTR)) { |
576 |
runtrap(&sigtraps[SIGERR_]); |
577 |
i = LLEAVE; |
578 |
} |
579 |
while (1) { |
580 |
switch (e->type) { |
581 |
case E_PARSE: |
582 |
case E_FUNC: |
583 |
case E_INCL: |
584 |
case E_LOOP: |
585 |
case E_ERRH: |
586 |
siglongjmp(e->jbuf, i); |
587 |
/* NOTREACHED */ |
588 |
case E_NONE: |
589 |
if (i == LINTR) |
590 |
e->flags |= EF_FAKE_SIGDIE; |
591 |
/* FALLTHRU */ |
592 |
default: |
593 |
quitenv(NULL); |
594 |
} |
595 |
} |
596 |
} |
597 |
|
598 |
void |
599 |
newenv(int type) |
600 |
{ |
601 |
struct env *ep; |
602 |
|
603 |
ep = (struct env *) alloc(sizeof(*ep), ATEMP); |
604 |
ep->type = type; |
605 |
ep->flags = 0; |
606 |
ainit(&ep->area); |
607 |
ep->loc = e->loc; |
608 |
ep->savefd = NULL; |
609 |
ep->oenv = e; |
610 |
ep->temps = NULL; |
611 |
e = ep; |
612 |
} |
613 |
|
614 |
void |
615 |
quitenv(struct shf *shf) |
616 |
{ |
617 |
struct env *ep = e; |
618 |
int fd; |
619 |
|
620 |
if (ep->oenv && ep->oenv->loc != ep->loc) |
621 |
popblock(); |
622 |
if (ep->savefd != NULL) { |
623 |
for (fd = 0; fd < NUFILE; fd++) |
624 |
/* if ep->savefd[fd] < 0, means fd was closed */ |
625 |
if (ep->savefd[fd]) |
626 |
restfd(fd, ep->savefd[fd]); |
627 |
if (ep->savefd[2]) /* Clear any write errors */ |
628 |
shf_reopen(2, SHF_WR, shl_out); |
629 |
} |
630 |
/* Bottom of the stack. |
631 |
* Either main shell is exiting or cleanup_parents_env() was called. |
632 |
*/ |
633 |
if (ep->oenv == NULL) { |
634 |
if (ep->type == E_NONE) { /* Main shell exiting? */ |
635 |
#if HAVE_PERSISTENT_HISTORY |
636 |
if (Flag(FTALKING)) |
637 |
hist_finish(); |
638 |
#endif |
639 |
j_exit(); |
640 |
if (ep->flags & EF_FAKE_SIGDIE) { |
641 |
int sig = exstat - 128; |
642 |
|
643 |
/* ham up our death a bit (at&t ksh |
644 |
* only seems to do this for SIGTERM) |
645 |
* Don't do it for SIGQUIT, since we'd |
646 |
* dump a core.. |
647 |
*/ |
648 |
if ((sig == SIGINT || sig == SIGTERM) && |
649 |
getpgrp() == kshpid) { |
650 |
setsig(&sigtraps[sig], SIG_DFL, |
651 |
SS_RESTORE_CURR | SS_FORCE); |
652 |
kill(0, sig); |
653 |
} |
654 |
} |
655 |
} |
656 |
if (shf) |
657 |
shf_close(shf); |
658 |
reclaim(); |
659 |
exit(exstat); |
660 |
} |
661 |
if (shf) |
662 |
shf_close(shf); |
663 |
reclaim(); |
664 |
|
665 |
e = e->oenv; |
666 |
afree(ep, ATEMP); |
667 |
} |
668 |
|
669 |
/* Called after a fork to cleanup stuff left over from parents environment */ |
670 |
void |
671 |
cleanup_parents_env(void) |
672 |
{ |
673 |
struct env *ep; |
674 |
int fd; |
675 |
|
676 |
/* Don't clean up temporary files - parent will probably need them. |
677 |
* Also, can't easily reclaim memory since variables, etc. could be |
678 |
* anywhere. |
679 |
*/ |
680 |
|
681 |
/* close all file descriptors hiding in savefd */ |
682 |
for (ep = e; ep; ep = ep->oenv) { |
683 |
if (ep->savefd) { |
684 |
for (fd = 0; fd < NUFILE; fd++) |
685 |
if (ep->savefd[fd] > 0) |
686 |
close(ep->savefd[fd]); |
687 |
afree(ep->savefd, &ep->area); |
688 |
ep->savefd = NULL; |
689 |
} |
690 |
} |
691 |
e->oenv = NULL; |
692 |
} |
693 |
|
694 |
/* Called just before an execve cleanup stuff temporary files */ |
695 |
void |
696 |
cleanup_proc_env(void) |
697 |
{ |
698 |
struct env *ep; |
699 |
|
700 |
for (ep = e; ep; ep = ep->oenv) |
701 |
remove_temps(ep->temps); |
702 |
} |
703 |
|
704 |
/* remove temp files and free ATEMP Area */ |
705 |
static void |
706 |
reclaim(void) |
707 |
{ |
708 |
remove_temps(e->temps); |
709 |
e->temps = NULL; |
710 |
afreeall(&e->area); |
711 |
} |
712 |
|
713 |
static void |
714 |
remove_temps(struct temp *tp) |
715 |
{ |
716 |
|
717 |
for (; tp != NULL; tp = tp->next) |
718 |
if (tp->pid == procpid) { |
719 |
unlink(tp->name); |
720 |
} |
721 |
} |
722 |
|
723 |
/* Initialise tty_fd. Used for saving/reseting tty modes upon |
724 |
* foreground job completion and for setting up tty process group. |
725 |
*/ |
726 |
void |
727 |
tty_init(int init_ttystate) |
728 |
{ |
729 |
int do_close = 1; |
730 |
int tfd; |
731 |
|
732 |
if (tty_fd >= 0) { |
733 |
close(tty_fd); |
734 |
tty_fd = -1; |
735 |
} |
736 |
tty_devtty = 1; |
737 |
|
738 |
#ifdef _UWIN |
739 |
if (isatty(3)) |
740 |
tfd = 3; |
741 |
else |
742 |
#endif |
743 |
if ((tfd = open("/dev/tty", O_RDWR, 0)) < 0) { |
744 |
tty_devtty = 0; |
745 |
warningf(false, "No controlling tty (open /dev/tty: %s)", |
746 |
strerror(errno)); |
747 |
} |
748 |
if (tfd < 0) { |
749 |
do_close = 0; |
750 |
if (isatty(0)) |
751 |
tfd = 0; |
752 |
else if (isatty(2)) |
753 |
tfd = 2; |
754 |
else { |
755 |
warningf(false, "Can't find tty file descriptor"); |
756 |
return; |
757 |
} |
758 |
} |
759 |
if ((tty_fd = fcntl(tfd, F_DUPFD, FDBASE)) < 0) { |
760 |
warningf(false, "j_ttyinit: dup of tty fd failed: %s", |
761 |
strerror(errno)); |
762 |
} else if (fcntl(tty_fd, F_SETFD, FD_CLOEXEC) < 0) { |
763 |
warningf(false, "j_ttyinit: can't set close-on-exec flag: %s", |
764 |
strerror(errno)); |
765 |
close(tty_fd); |
766 |
tty_fd = -1; |
767 |
} else if (init_ttystate) |
768 |
tcgetattr(tty_fd, &tty_state); |
769 |
if (do_close) |
770 |
close(tfd); |
771 |
} |
772 |
|
773 |
void |
774 |
tty_close(void) |
775 |
{ |
776 |
if (tty_fd >= 0) { |
777 |
close(tty_fd); |
778 |
tty_fd = -1; |
779 |
} |
780 |
} |
781 |
|
782 |
/* A shell error occurred (eg, syntax error, etc.) */ |
783 |
void |
784 |
errorf(const char *fmt, ...) |
785 |
{ |
786 |
va_list va; |
787 |
|
788 |
shl_stdout_ok = 0; /* debugging: note that stdout not valid */ |
789 |
exstat = 1; |
790 |
if (*fmt) { |
791 |
error_prefix(true); |
792 |
va_start(va, fmt); |
793 |
shf_vfprintf(shl_out, fmt, va); |
794 |
va_end(va); |
795 |
shf_putchar('\n', shl_out); |
796 |
} |
797 |
shf_flush(shl_out); |
798 |
unwind(LERROR); |
799 |
} |
800 |
|
801 |
/* like errorf(), but no unwind is done */ |
802 |
void |
803 |
warningf(bool fileline, const char *fmt, ...) |
804 |
{ |
805 |
va_list va; |
806 |
|
807 |
error_prefix(fileline); |
808 |
va_start(va, fmt); |
809 |
shf_vfprintf(shl_out, fmt, va); |
810 |
va_end(va); |
811 |
shf_putchar('\n', shl_out); |
812 |
shf_flush(shl_out); |
813 |
} |
814 |
|
815 |
/* Used by built-in utilities to prefix shell and utility name to message |
816 |
* (also unwinds environments for special builtins). |
817 |
*/ |
818 |
void |
819 |
bi_errorf(const char *fmt, ...) |
820 |
{ |
821 |
va_list va; |
822 |
|
823 |
shl_stdout_ok = 0; /* debugging: note that stdout not valid */ |
824 |
exstat = 1; |
825 |
if (*fmt) { |
826 |
error_prefix(true); |
827 |
/* not set when main() calls parse_args() */ |
828 |
if (builtin_argv0) |
829 |
shf_fprintf(shl_out, "%s: ", builtin_argv0); |
830 |
va_start(va, fmt); |
831 |
shf_vfprintf(shl_out, fmt, va); |
832 |
va_end(va); |
833 |
shf_putchar('\n', shl_out); |
834 |
} |
835 |
shf_flush(shl_out); |
836 |
/* POSIX special builtins and ksh special builtins cause |
837 |
* non-interactive shells to exit. |
838 |
* XXX odd use of KEEPASN; also may not want LERROR here |
839 |
*/ |
840 |
if (builtin_flag & SPEC_BI) { |
841 |
builtin_argv0 = NULL; |
842 |
unwind(LERROR); |
843 |
} |
844 |
} |
845 |
|
846 |
/* Called when something that shouldn't happen does */ |
847 |
static void internal_verrorf(const char *, va_list) |
848 |
__attribute__((format (printf, 1, 0))); |
849 |
static void |
850 |
internal_verrorf(const char *fmt, va_list ap) |
851 |
{ |
852 |
shf_fprintf(shl_out, "internal error: "); |
853 |
shf_vfprintf(shl_out, fmt, ap); |
854 |
shf_putchar('\n', shl_out); |
855 |
shf_flush(shl_out); |
856 |
} |
857 |
|
858 |
void |
859 |
internal_errorf(const char *fmt, ...) |
860 |
{ |
861 |
va_list va; |
862 |
|
863 |
va_start(va, fmt); |
864 |
internal_verrorf(fmt, va); |
865 |
va_end(va); |
866 |
unwind(LERROR); |
867 |
} |
868 |
|
869 |
void |
870 |
internal_warningf(const char *fmt, ...) |
871 |
{ |
872 |
va_list va; |
873 |
|
874 |
va_start(va, fmt); |
875 |
internal_verrorf(fmt, va); |
876 |
va_end(va); |
877 |
} |
878 |
|
879 |
/* used by error reporting functions to print "ksh: .kshrc[25]: " */ |
880 |
void |
881 |
error_prefix(bool fileline) |
882 |
{ |
883 |
/* Avoid foo: foo[2]: ... */ |
884 |
if (!fileline || !source || !source->file || |
885 |
strcmp(source->file, kshname) != 0) |
886 |
shf_fprintf(shl_out, "%s: ", kshname + (*kshname == '-')); |
887 |
if (fileline && source && source->file != NULL) { |
888 |
shf_fprintf(shl_out, "%s[%d]: ", source->file, |
889 |
source->errline > 0 ? source->errline : source->line); |
890 |
source->errline = 0; |
891 |
} |
892 |
} |
893 |
|
894 |
/* printf to shl_out (stderr) with flush */ |
895 |
void |
896 |
shellf(const char *fmt, ...) |
897 |
{ |
898 |
va_list va; |
899 |
|
900 |
if (!initio_done) /* shl_out may not be set up yet... */ |
901 |
return; |
902 |
va_start(va, fmt); |
903 |
shf_vfprintf(shl_out, fmt, va); |
904 |
va_end(va); |
905 |
shf_flush(shl_out); |
906 |
} |
907 |
|
908 |
/* printf to shl_stdout (stdout) */ |
909 |
void |
910 |
shprintf(const char *fmt, ...) |
911 |
{ |
912 |
va_list va; |
913 |
|
914 |
if (!shl_stdout_ok) |
915 |
internal_errorf("shl_stdout not valid"); |
916 |
va_start(va, fmt); |
917 |
shf_vfprintf(shl_stdout, fmt, va); |
918 |
va_end(va); |
919 |
} |
920 |
|
921 |
/* test if we can seek backwards fd (returns 0 or SHF_UNBUF) */ |
922 |
int |
923 |
can_seek(int fd) |
924 |
{ |
925 |
struct stat statb; |
926 |
|
927 |
return fstat(fd, &statb) == 0 && !S_ISREG(statb.st_mode) ? |
928 |
SHF_UNBUF : 0; |
929 |
} |
930 |
|
931 |
struct shf shf_iob[3]; |
932 |
|
933 |
void |
934 |
initio(void) |
935 |
{ |
936 |
shf_fdopen(1, SHF_WR, shl_stdout); /* force buffer allocation */ |
937 |
shf_fdopen(2, SHF_WR, shl_out); |
938 |
shf_fdopen(2, SHF_WR, shl_spare); /* force buffer allocation */ |
939 |
initio_done = 1; |
940 |
} |
941 |
|
942 |
/* A dup2() with error checking */ |
943 |
int |
944 |
ksh_dup2(int ofd, int nfd, int errok) |
945 |
{ |
946 |
int ret = dup2(ofd, nfd); |
947 |
|
948 |
if (ret < 0 && errno != EBADF && !errok) |
949 |
errorf("too many files open in shell"); |
950 |
|
951 |
return ret; |
952 |
} |
953 |
|
954 |
/* |
955 |
* move fd from user space (0<=fd<10) to shell space (fd>=10), |
956 |
* set close-on-exec flag. |
957 |
*/ |
958 |
short |
959 |
savefd(int fd) |
960 |
{ |
961 |
int nfd = fd; |
962 |
|
963 |
if (fd < FDBASE && (nfd = fcntl(fd, F_DUPFD, FDBASE)) < 0 && |
964 |
errno == EBADF) |
965 |
return -1; |
966 |
if (nfd < 0 || nfd > SHRT_MAX) |
967 |
errorf("too many files open in shell"); |
968 |
fcntl(nfd, F_SETFD, FD_CLOEXEC); |
969 |
return ((short)nfd); |
970 |
} |
971 |
|
972 |
void |
973 |
restfd(int fd, int ofd) |
974 |
{ |
975 |
if (fd == 2) |
976 |
shf_flush(&shf_iob[fd]); |
977 |
if (ofd < 0) /* original fd closed */ |
978 |
close(fd); |
979 |
else if (fd != ofd) { |
980 |
ksh_dup2(ofd, fd, true); /* XXX: what to do if this fails? */ |
981 |
close(ofd); |
982 |
} |
983 |
} |
984 |
|
985 |
void |
986 |
openpipe(int *pv) |
987 |
{ |
988 |
int lpv[2]; |
989 |
|
990 |
if (pipe(lpv) < 0) |
991 |
errorf("can't create pipe - try again"); |
992 |
pv[0] = savefd(lpv[0]); |
993 |
if (pv[0] != lpv[0]) |
994 |
close(lpv[0]); |
995 |
pv[1] = savefd(lpv[1]); |
996 |
if (pv[1] != lpv[1]) |
997 |
close(lpv[1]); |
998 |
} |
999 |
|
1000 |
void |
1001 |
closepipe(int *pv) |
1002 |
{ |
1003 |
close(pv[0]); |
1004 |
close(pv[1]); |
1005 |
} |
1006 |
|
1007 |
/* Called by iosetup() (deals with 2>&4, etc.), c_read, c_print to turn |
1008 |
* a string (the X in 2>&X, read -uX, print -uX) into a file descriptor. |
1009 |
*/ |
1010 |
int |
1011 |
check_fd(const char *name, int mode, const char **emsgp) |
1012 |
{ |
1013 |
int fd, fl; |
1014 |
|
1015 |
if (ksh_isdigit(name[0]) && !name[1]) { |
1016 |
if ((fl = fcntl(fd = name[0] - '0', F_GETFL, 0)) < 0) { |
1017 |
if (emsgp) |
1018 |
*emsgp = "bad file descriptor"; |
1019 |
return -1; |
1020 |
} |
1021 |
fl &= O_ACCMODE; |
1022 |
/* X_OK is a kludge to disable this check for dups (x<&1): |
1023 |
* historical shells never did this check (XXX don't know what |
1024 |
* posix has to say). |
1025 |
*/ |
1026 |
if (!(mode & X_OK) && fl != O_RDWR && |
1027 |
(((mode & R_OK) && fl != O_RDONLY) || |
1028 |
((mode & W_OK) && fl != O_WRONLY))) { |
1029 |
if (emsgp) |
1030 |
*emsgp = (fl == O_WRONLY) ? |
1031 |
"fd not open for reading" : |
1032 |
"fd not open for writing"; |
1033 |
return -1; |
1034 |
} |
1035 |
return fd; |
1036 |
} else if (name[0] == 'p' && !name[1]) |
1037 |
return coproc_getfd(mode, emsgp); |
1038 |
if (emsgp) |
1039 |
*emsgp = "illegal file descriptor name"; |
1040 |
return -1; |
1041 |
} |
1042 |
|
1043 |
/* Called once from main */ |
1044 |
void |
1045 |
coproc_init(void) |
1046 |
{ |
1047 |
coproc.read = coproc.readw = coproc.write = -1; |
1048 |
coproc.njobs = 0; |
1049 |
coproc.id = 0; |
1050 |
} |
1051 |
|
1052 |
/* Called by c_read() when eof is read - close fd if it is the co-process fd */ |
1053 |
void |
1054 |
coproc_read_close(int fd) |
1055 |
{ |
1056 |
if (coproc.read >= 0 && fd == coproc.read) { |
1057 |
coproc_readw_close(fd); |
1058 |
close(coproc.read); |
1059 |
coproc.read = -1; |
1060 |
} |
1061 |
} |
1062 |
|
1063 |
/* Called by c_read() and by iosetup() to close the other side of the |
1064 |
* read pipe, so reads will actually terminate. |
1065 |
*/ |
1066 |
void |
1067 |
coproc_readw_close(int fd) |
1068 |
{ |
1069 |
if (coproc.readw >= 0 && coproc.read >= 0 && fd == coproc.read) { |
1070 |
close(coproc.readw); |
1071 |
coproc.readw = -1; |
1072 |
} |
1073 |
} |
1074 |
|
1075 |
/* Called by c_print when a write to a fd fails with EPIPE and by iosetup |
1076 |
* when co-process input is dup'd |
1077 |
*/ |
1078 |
void |
1079 |
coproc_write_close(int fd) |
1080 |
{ |
1081 |
if (coproc.write >= 0 && fd == coproc.write) { |
1082 |
close(coproc.write); |
1083 |
coproc.write = -1; |
1084 |
} |
1085 |
} |
1086 |
|
1087 |
/* Called to check for existence of/value of the co-process file descriptor. |
1088 |
* (Used by check_fd() and by c_read/c_print to deal with -p option). |
1089 |
*/ |
1090 |
int |
1091 |
coproc_getfd(int mode, const char **emsgp) |
1092 |
{ |
1093 |
int fd = (mode & R_OK) ? coproc.read : coproc.write; |
1094 |
|
1095 |
if (fd >= 0) |
1096 |
return fd; |
1097 |
if (emsgp) |
1098 |
*emsgp = "no coprocess"; |
1099 |
return -1; |
1100 |
} |
1101 |
|
1102 |
/* called to close file descriptors related to the coprocess (if any) |
1103 |
* Should be called with SIGCHLD blocked. |
1104 |
*/ |
1105 |
void |
1106 |
coproc_cleanup(int reuse) |
1107 |
{ |
1108 |
/* This to allow co-processes to share output pipe */ |
1109 |
if (!reuse || coproc.readw < 0 || coproc.read < 0) { |
1110 |
if (coproc.read >= 0) { |
1111 |
close(coproc.read); |
1112 |
coproc.read = -1; |
1113 |
} |
1114 |
if (coproc.readw >= 0) { |
1115 |
close(coproc.readw); |
1116 |
coproc.readw = -1; |
1117 |
} |
1118 |
} |
1119 |
if (coproc.write >= 0) { |
1120 |
close(coproc.write); |
1121 |
coproc.write = -1; |
1122 |
} |
1123 |
} |
1124 |
|
1125 |
struct temp * |
1126 |
maketemp(Area *ap, Temp_type type, struct temp **tlist) |
1127 |
{ |
1128 |
struct temp *tp; |
1129 |
int len; |
1130 |
int fd; |
1131 |
char *pathname; |
1132 |
const char *dir; |
1133 |
|
1134 |
dir = tmpdir ? tmpdir : "/tmp"; |
1135 |
len = strlen(dir) + 6 + 10 + 1; |
1136 |
tp = (struct temp *) alloc(sizeof(struct temp) + len, ap); |
1137 |
tp->name = pathname = (char *)&tp[1]; |
1138 |
tp->shf = NULL; |
1139 |
tp->type = type; |
1140 |
shf_snprintf(pathname, len, "%s/mksh.XXXXXXXXXX", dir); |
1141 |
if ((fd = mkstemp(pathname)) >= 0) |
1142 |
tp->shf = shf_fdopen(fd, SHF_WR, NULL); |
1143 |
tp->pid = procpid; |
1144 |
|
1145 |
tp->next = *tlist; |
1146 |
*tlist = tp; |
1147 |
return tp; |
1148 |
} |
1149 |
|
1150 |
#define INIT_TBLS 8 /* initial table size (power of 2) */ |
1151 |
|
1152 |
static void texpand(struct table *, int); |
1153 |
static int tnamecmp(const void *, const void *); |
1154 |
|
1155 |
unsigned int |
1156 |
hash(const char *n) |
1157 |
{ |
1158 |
unsigned int h = 0; |
1159 |
|
1160 |
while (*n != '\0') |
1161 |
h = 2*h + *n++; |
1162 |
return h * 32821; /* scatter bits */ |
1163 |
} |
1164 |
|
1165 |
void |
1166 |
ktinit(struct table *tp, Area *ap, int tsize) |
1167 |
{ |
1168 |
tp->areap = ap; |
1169 |
tp->tbls = NULL; |
1170 |
tp->size = tp->nfree = 0; |
1171 |
if (tsize) |
1172 |
texpand(tp, tsize); |
1173 |
} |
1174 |
|
1175 |
static void |
1176 |
texpand(struct table *tp, int nsize) |
1177 |
{ |
1178 |
int i; |
1179 |
struct tbl *tblp, **p; |
1180 |
struct tbl **ntblp, **otblp = tp->tbls; |
1181 |
int osize = tp->size; |
1182 |
|
1183 |
ntblp = (struct tbl **)alloc(sizeofN(struct tbl *, nsize), tp->areap); |
1184 |
for (i = 0; i < nsize; i++) |
1185 |
ntblp[i] = NULL; |
1186 |
tp->size = nsize; |
1187 |
tp->nfree = 8 * nsize / 10; /* table can get 80% full */ |
1188 |
tp->tbls = ntblp; |
1189 |
if (otblp == NULL) |
1190 |
return; |
1191 |
for (i = 0; i < osize; i++) |
1192 |
if ((tblp = otblp[i]) != NULL) { |
1193 |
if ((tblp->flag & DEFINED)) { |
1194 |
for (p = &ntblp[hash(tblp->name) & |
1195 |
(tp->size - 1)]; *p != NULL; p--) |
1196 |
if (p == ntblp) /* wrap */ |
1197 |
p += tp->size; |
1198 |
*p = tblp; |
1199 |
tp->nfree--; |
1200 |
} else if (!(tblp->flag & FINUSE)) { |
1201 |
afree((void *)tblp, tp->areap); |
1202 |
} |
1203 |
} |
1204 |
afree((void *)otblp, tp->areap); |
1205 |
} |
1206 |
|
1207 |
/* table */ |
1208 |
/* name to enter */ |
1209 |
/* hash(n) */ |
1210 |
struct tbl * |
1211 |
ktsearch(struct table *tp, const char *n, unsigned int h) |
1212 |
{ |
1213 |
struct tbl **pp, *p; |
1214 |
|
1215 |
if (tp->size == 0) |
1216 |
return NULL; |
1217 |
|
1218 |
/* search for name in hashed table */ |
1219 |
for (pp = &tp->tbls[h & (tp->size - 1)]; (p = *pp) != NULL; pp--) { |
1220 |
if (*p->name == *n && strcmp(p->name, n) == 0 && |
1221 |
(p->flag & DEFINED)) |
1222 |
return p; |
1223 |
if (pp == tp->tbls) /* wrap */ |
1224 |
pp += tp->size; |
1225 |
} |
1226 |
|
1227 |
return NULL; |
1228 |
} |
1229 |
|
1230 |
/* table */ |
1231 |
/* name to enter */ |
1232 |
/* hash(n) */ |
1233 |
struct tbl * |
1234 |
ktenter(struct table *tp, const char *n, unsigned int h) |
1235 |
{ |
1236 |
struct tbl **pp, *p; |
1237 |
int len; |
1238 |
|
1239 |
if (tp->size == 0) |
1240 |
texpand(tp, INIT_TBLS); |
1241 |
Search: |
1242 |
/* search for name in hashed table */ |
1243 |
for (pp = &tp->tbls[h & (tp->size - 1)]; (p = *pp) != NULL; pp--) { |
1244 |
if (*p->name == *n && strcmp(p->name, n) == 0) |
1245 |
return p; /* found */ |
1246 |
if (pp == tp->tbls) /* wrap */ |
1247 |
pp += tp->size; |
1248 |
} |
1249 |
|
1250 |
if (tp->nfree <= 0) { /* too full */ |
1251 |
texpand(tp, 2 * tp->size); |
1252 |
goto Search; |
1253 |
} |
1254 |
/* create new tbl entry */ |
1255 |
len = strlen(n) + 1; |
1256 |
p = (struct tbl *)alloc(offsetof(struct tbl, name[0])+len, |
1257 |
tp->areap); |
1258 |
p->flag = 0; |
1259 |
p->type = 0; |
1260 |
p->areap = tp->areap; |
1261 |
p->u2.field = 0; |
1262 |
p->u.array = NULL; |
1263 |
memcpy(p->name, n, len); |
1264 |
|
1265 |
/* enter in tp->tbls */ |
1266 |
tp->nfree--; |
1267 |
*pp = p; |
1268 |
return p; |
1269 |
} |
1270 |
|
1271 |
void |
1272 |
ktdelete(struct tbl *p) |
1273 |
{ |
1274 |
p->flag = 0; |
1275 |
} |
1276 |
|
1277 |
void |
1278 |
ktwalk(struct tstate *ts, struct table *tp) |
1279 |
{ |
1280 |
ts->left = tp->size; |
1281 |
ts->next = tp->tbls; |
1282 |
} |
1283 |
|
1284 |
struct tbl * |
1285 |
ktnext(struct tstate *ts) |
1286 |
{ |
1287 |
while (--ts->left >= 0) { |
1288 |
struct tbl *p = *ts->next++; |
1289 |
if (p != NULL && (p->flag & DEFINED)) |
1290 |
return p; |
1291 |
} |
1292 |
return NULL; |
1293 |
} |
1294 |
|
1295 |
static int |
1296 |
tnamecmp(const void *p1, const void *p2) |
1297 |
{ |
1298 |
const struct tbl *a = *((struct tbl * const *)p1); |
1299 |
const struct tbl *b = *((struct tbl * const *)p2); |
1300 |
|
1301 |
return (strcmp(a->name, b->name)); |
1302 |
} |
1303 |
|
1304 |
struct tbl ** |
1305 |
ktsort(struct table *tp) |
1306 |
{ |
1307 |
size_t i; |
1308 |
struct tbl **p, **sp, **dp; |
1309 |
|
1310 |
p = (struct tbl **)alloc(sizeofN(struct tbl *, tp->size + 1), ATEMP); |
1311 |
sp = tp->tbls; /* source */ |
1312 |
dp = p; /* dest */ |
1313 |
for (i = 0; i < (size_t)tp->size; i++) |
1314 |
if ((*dp = *sp++) != NULL && (((*dp)->flag & DEFINED) || |
1315 |
((*dp)->flag & ARRAY))) |
1316 |
dp++; |
1317 |
qsort(p, (i = dp - p), sizeof (void *), tnamecmp); |
1318 |
p[i] = NULL; |
1319 |
return p; |
1320 |
} |