1 1.59 kre /* $NetBSD: exec.c,v 1.59 2024/07/12 07:30:30 kre Exp $ */ 2 1.15 cgd 3 1.1 cgd /*- 4 1.8 jtc * Copyright (c) 1991, 1993 5 1.8 jtc * The Regents of the University of California. All rights reserved. 6 1.1 cgd * 7 1.1 cgd * This code is derived from software contributed to Berkeley by 8 1.1 cgd * Kenneth Almquist. 9 1.1 cgd * 10 1.1 cgd * Redistribution and use in source and binary forms, with or without 11 1.1 cgd * modification, are permitted provided that the following conditions 12 1.1 cgd * are met: 13 1.1 cgd * 1. Redistributions of source code must retain the above copyright 14 1.1 cgd * notice, this list of conditions and the following disclaimer. 15 1.1 cgd * 2. Redistributions in binary form must reproduce the above copyright 16 1.1 cgd * notice, this list of conditions and the following disclaimer in the 17 1.1 cgd * documentation and/or other materials provided with the distribution. 18 1.37 agc * 3. Neither the name of the University nor the names of its contributors 19 1.1 cgd * may be used to endorse or promote products derived from this software 20 1.1 cgd * without specific prior written permission. 21 1.1 cgd * 22 1.1 cgd * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 23 1.1 cgd * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 1.1 cgd * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 1.1 cgd * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 26 1.1 cgd * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27 1.1 cgd * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 28 1.1 cgd * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29 1.1 cgd * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 1.1 cgd * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31 1.1 cgd * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 1.1 cgd * SUCH DAMAGE. 33 1.1 cgd */ 34 1.1 cgd 35 1.23 christos #include <sys/cdefs.h> 36 1.1 cgd #ifndef lint 37 1.15 cgd #if 0 38 1.17 christos static char sccsid[] = "@(#)exec.c 8.4 (Berkeley) 6/8/95"; 39 1.15 cgd #else 40 1.59 kre __RCSID("$NetBSD: exec.c,v 1.59 2024/07/12 07:30:30 kre Exp $"); 41 1.15 cgd #endif 42 1.1 cgd #endif /* not lint */ 43 1.1 cgd 44 1.16 christos #include <sys/types.h> 45 1.16 christos #include <sys/stat.h> 46 1.33 christos #include <sys/wait.h> 47 1.16 christos #include <unistd.h> 48 1.16 christos #include <fcntl.h> 49 1.16 christos #include <errno.h> 50 1.33 christos #include <stdio.h> 51 1.16 christos #include <stdlib.h> 52 1.16 christos 53 1.1 cgd /* 54 1.1 cgd * When commands are first encountered, they are entered in a hash table. 55 1.1 cgd * This ensures that a full path search will not have to be done for them 56 1.1 cgd * on each invocation. 57 1.1 cgd * 58 1.1 cgd * We should investigate converting to a linear search, even though that 59 1.1 cgd * would make the command name "hash" a misnomer. 60 1.1 cgd */ 61 1.1 cgd 62 1.1 cgd #include "shell.h" 63 1.1 cgd #include "main.h" 64 1.1 cgd #include "nodes.h" 65 1.1 cgd #include "parser.h" 66 1.1 cgd #include "redir.h" 67 1.1 cgd #include "eval.h" 68 1.1 cgd #include "exec.h" 69 1.1 cgd #include "builtins.h" 70 1.1 cgd #include "var.h" 71 1.1 cgd #include "options.h" 72 1.1 cgd #include "input.h" 73 1.1 cgd #include "output.h" 74 1.1 cgd #include "syntax.h" 75 1.1 cgd #include "memalloc.h" 76 1.1 cgd #include "error.h" 77 1.1 cgd #include "init.h" 78 1.1 cgd #include "mystring.h" 79 1.16 christos #include "show.h" 80 1.8 jtc #include "jobs.h" 81 1.22 christos #include "alias.h" 82 1.1 cgd 83 1.1 cgd 84 1.1 cgd #define CMDTABLESIZE 31 /* should be prime */ 85 1.1 cgd #define ARB 1 /* actual size determined at run time */ 86 1.1 cgd 87 1.1 cgd 88 1.1 cgd 89 1.1 cgd struct tblentry { 90 1.1 cgd struct tblentry *next; /* next entry in hash chain */ 91 1.1 cgd union param param; /* definition of builtin function */ 92 1.1 cgd short cmdtype; /* index identifying command */ 93 1.1 cgd char rehash; /* if set, cd done since entry created */ 94 1.49 kre char fn_ln1; /* for functions, LINENO from 1 */ 95 1.49 kre int lineno; /* for functions abs LINENO of definition */ 96 1.1 cgd char cmdname[ARB]; /* name of command */ 97 1.1 cgd }; 98 1.1 cgd 99 1.1 cgd 100 1.1 cgd STATIC struct tblentry *cmdtable[CMDTABLESIZE]; 101 1.1 cgd STATIC int builtinloc = -1; /* index in path of %builtin, or -1 */ 102 1.19 christos int exerrno = 0; /* Last exec error */ 103 1.1 cgd 104 1.1 cgd 105 1.34 christos STATIC void tryexec(char *, char **, char **, int); 106 1.34 christos STATIC void printentry(struct tblentry *, int); 107 1.46 christos STATIC void addcmdentry(char *, struct cmdentry *); 108 1.34 christos STATIC void clearcmdentry(int); 109 1.34 christos STATIC struct tblentry *cmdlookup(const char *, int); 110 1.34 christos STATIC void delete_cmd_entry(void); 111 1.1 cgd 112 1.42 dholland #ifndef BSD 113 1.42 dholland STATIC void execinterp(char **, char **); 114 1.42 dholland #endif 115 1.42 dholland 116 1.1 cgd 117 1.41 matt extern const char *const parsekwd[]; 118 1.1 cgd 119 1.1 cgd /* 120 1.1 cgd * Exec a program. Never returns. If you change this routine, you may 121 1.1 cgd * have to change the find_command routine as well. 122 1.1 cgd */ 123 1.1 cgd 124 1.1 cgd void 125 1.34 christos shellexec(char **argv, char **envp, const char *path, int idx, int vforked) 126 1.12 cgd { 127 1.1 cgd char *cmdname; 128 1.58 kre int e, action; 129 1.58 kre struct stat statb; 130 1.58 kre 131 1.58 kre action = E_EXEC; 132 1.1 cgd 133 1.1 cgd if (strchr(argv[0], '/') != NULL) { 134 1.33 christos tryexec(argv[0], argv, envp, vforked); 135 1.1 cgd e = errno; 136 1.58 kre if (e == EACCES && stat(argv[0], &statb) == -1) 137 1.58 kre action = E_OPEN; 138 1.1 cgd } else { 139 1.1 cgd e = ENOENT; 140 1.48 kre while ((cmdname = padvance(&path, argv[0], 1)) != NULL) { 141 1.27 christos if (--idx < 0 && pathopt == NULL) { 142 1.58 kre /* 143 1.58 kre * tryexec() does not return if it works. 144 1.58 kre */ 145 1.33 christos tryexec(cmdname, argv, envp, vforked); 146 1.58 kre /* 147 1.58 kre * If do not already have a meaningful error 148 1.58 kre * from earlier in the PATH, examine this one 149 1.58 kre * if it is a simple "not found", just keep 150 1.58 kre * searching. 151 1.58 kre */ 152 1.58 kre if (e == ENOENT && 153 1.58 kre errno != ENOENT && errno != ENOTDIR) { 154 1.58 kre /* 155 1.58 kre * If the error is from permission 156 1.58 kre * denied on the path search (a call 157 1.58 kre * to stat() also fails) ignore it 158 1.58 kre * (just continue with the search) 159 1.58 kre * If it is EACCESS and the file exists 160 1.58 kre * (the stat succeeds) that means no 161 1.58 kre * 'x' perm on the file itself, which 162 1.58 kre * is a meaningful error, this will be 163 1.58 kre * the one reported if no later PATH 164 1.58 kre * element actually succeeds. 165 1.58 kre */ 166 1.58 kre if (errno == EACCES) { 167 1.58 kre if (stat(cmdname, &statb) != -1) 168 1.58 kre e = EACCES; 169 1.58 kre } else { 170 1.58 kre /* 171 1.58 kre * any other error we will 172 1.58 kre * remember as the significant 173 1.58 kre * error 174 1.58 kre */ 175 1.58 kre e = errno; 176 1.58 kre } 177 1.58 kre } 178 1.1 cgd } 179 1.1 cgd stunalloc(cmdname); 180 1.1 cgd } 181 1.1 cgd } 182 1.19 christos 183 1.19 christos /* Map to POSIX errors */ 184 1.19 christos switch (e) { 185 1.51 kre case EACCES: /* particularly this (unless no search perm) */ 186 1.58 kre if (action == E_OPEN) { 187 1.58 kre /* 188 1.58 kre * this is an EACCES from namei 189 1.58 kre * ie: no permission to search the path given 190 1.58 kre * rather than an EACCESS from exec 191 1.58 kre * ie: no 'x' bit on the file to be executed 192 1.58 kre */ 193 1.58 kre exerrno = 127; 194 1.58 kre break; 195 1.58 kre } 196 1.58 kre /* FALLTHROUGH */ 197 1.51 kre case EINVAL: /* also explicitly these */ 198 1.51 kre case ENOEXEC: 199 1.51 kre default: /* and anything else */ 200 1.19 christos exerrno = 126; 201 1.19 christos break; 202 1.51 kre 203 1.51 kre case ENOENT: /* these are the "pathname lookup failed" errors */ 204 1.51 kre case ELOOP: 205 1.51 kre case ENOTDIR: 206 1.51 kre case ENAMETOOLONG: 207 1.19 christos exerrno = 127; 208 1.19 christos break; 209 1.19 christos } 210 1.51 kre CTRACE(DBG_ERRS|DBG_CMDS|DBG_EVAL, 211 1.51 kre ("shellexec failed for %s, errno %d, vforked %d, suppressint %d\n", 212 1.51 kre argv[0], e, vforked, suppressint)); 213 1.58 kre exerror(EXEXEC, "%s: %s", argv[0], errmsg(e, action)); 214 1.26 mycroft /* NOTREACHED */ 215 1.1 cgd } 216 1.1 cgd 217 1.1 cgd 218 1.1 cgd STATIC void 219 1.34 christos tryexec(char *cmd, char **argv, char **envp, int vforked) 220 1.34 christos { 221 1.1 cgd int e; 222 1.16 christos #ifndef BSD 223 1.1 cgd char *p; 224 1.16 christos #endif 225 1.1 cgd 226 1.1 cgd #ifdef SYSV 227 1.1 cgd do { 228 1.1 cgd execve(cmd, argv, envp); 229 1.1 cgd } while (errno == EINTR); 230 1.1 cgd #else 231 1.1 cgd execve(cmd, argv, envp); 232 1.1 cgd #endif 233 1.1 cgd e = errno; 234 1.1 cgd if (e == ENOEXEC) { 235 1.33 christos if (vforked) { 236 1.33 christos /* We are currently vfork(2)ed, so raise an 237 1.33 christos * exception, and evalcommand will try again 238 1.33 christos * with a normal fork(2). 239 1.33 christos */ 240 1.33 christos exraise(EXSHELLPROC); 241 1.33 christos } 242 1.40 christos #ifdef DEBUG 243 1.51 kre VTRACE(DBG_CMDS, ("execve(cmd=%s) returned ENOEXEC\n", cmd)); 244 1.40 christos #endif 245 1.1 cgd initshellproc(); 246 1.1 cgd setinputfile(cmd, 0); 247 1.1 cgd commandname = arg0 = savestr(argv[0]); 248 1.1 cgd #ifndef BSD 249 1.1 cgd pgetc(); pungetc(); /* fill up input buffer */ 250 1.1 cgd p = parsenextc; 251 1.1 cgd if (parsenleft > 2 && p[0] == '#' && p[1] == '!') { 252 1.1 cgd argv[0] = cmd; 253 1.1 cgd execinterp(argv, envp); 254 1.1 cgd } 255 1.1 cgd #endif 256 1.1 cgd setparam(argv + 1); 257 1.1 cgd exraise(EXSHELLPROC); 258 1.1 cgd } 259 1.1 cgd errno = e; 260 1.1 cgd } 261 1.1 cgd 262 1.1 cgd 263 1.1 cgd #ifndef BSD 264 1.1 cgd /* 265 1.1 cgd * Execute an interpreter introduced by "#!", for systems where this 266 1.1 cgd * feature has not been built into the kernel. If the interpreter is 267 1.1 cgd * the shell, return (effectively ignoring the "#!"). If the execution 268 1.1 cgd * of the interpreter fails, exit. 269 1.1 cgd * 270 1.1 cgd * This code peeks inside the input buffer in order to avoid actually 271 1.1 cgd * reading any input. It would benefit from a rewrite. 272 1.1 cgd */ 273 1.1 cgd 274 1.1 cgd #define NEWARGS 5 275 1.1 cgd 276 1.1 cgd STATIC void 277 1.34 christos execinterp(char **argv, char **envp) 278 1.34 christos { 279 1.1 cgd int n; 280 1.1 cgd char *inp; 281 1.1 cgd char *outp; 282 1.1 cgd char c; 283 1.1 cgd char *p; 284 1.1 cgd char **ap; 285 1.1 cgd char *newargs[NEWARGS]; 286 1.1 cgd int i; 287 1.1 cgd char **ap2; 288 1.1 cgd char **new; 289 1.1 cgd 290 1.1 cgd n = parsenleft - 2; 291 1.1 cgd inp = parsenextc + 2; 292 1.1 cgd ap = newargs; 293 1.1 cgd for (;;) { 294 1.1 cgd while (--n >= 0 && (*inp == ' ' || *inp == '\t')) 295 1.1 cgd inp++; 296 1.1 cgd if (n < 0) 297 1.1 cgd goto bad; 298 1.1 cgd if ((c = *inp++) == '\n') 299 1.1 cgd break; 300 1.1 cgd if (ap == &newargs[NEWARGS]) 301 1.1 cgd bad: error("Bad #! line"); 302 1.1 cgd STARTSTACKSTR(outp); 303 1.1 cgd do { 304 1.1 cgd STPUTC(c, outp); 305 1.1 cgd } while (--n >= 0 && (c = *inp++) != ' ' && c != '\t' && c != '\n'); 306 1.1 cgd STPUTC('\0', outp); 307 1.1 cgd n++, inp--; 308 1.1 cgd *ap++ = grabstackstr(outp); 309 1.1 cgd } 310 1.1 cgd if (ap == newargs + 1) { /* if no args, maybe no exec is needed */ 311 1.1 cgd p = newargs[0]; 312 1.1 cgd for (;;) { 313 1.1 cgd if (equal(p, "sh") || equal(p, "ash")) { 314 1.1 cgd return; 315 1.1 cgd } 316 1.1 cgd while (*p != '/') { 317 1.1 cgd if (*p == '\0') 318 1.1 cgd goto break2; 319 1.1 cgd p++; 320 1.1 cgd } 321 1.1 cgd p++; 322 1.1 cgd } 323 1.1 cgd break2:; 324 1.1 cgd } 325 1.1 cgd i = (char *)ap - (char *)newargs; /* size in bytes */ 326 1.1 cgd if (i == 0) 327 1.1 cgd error("Bad #! line"); 328 1.1 cgd for (ap2 = argv ; *ap2++ != NULL ; ); 329 1.1 cgd new = ckmalloc(i + ((char *)ap2 - (char *)argv)); 330 1.1 cgd ap = newargs, ap2 = new; 331 1.1 cgd while ((i -= sizeof (char **)) >= 0) 332 1.1 cgd *ap2++ = *ap++; 333 1.1 cgd ap = argv; 334 1.1 cgd while (*ap2++ = *ap++); 335 1.1 cgd shellexec(new, envp, pathval(), 0); 336 1.26 mycroft /* NOTREACHED */ 337 1.1 cgd } 338 1.1 cgd #endif 339 1.1 cgd 340 1.1 cgd 341 1.1 cgd 342 1.1 cgd /* 343 1.1 cgd * Do a path search. The variable path (passed by reference) should be 344 1.1 cgd * set to the start of the path before the first call; padvance will update 345 1.1 cgd * this value as it proceeds. Successive calls to padvance will return 346 1.1 cgd * the possible path expansions in sequence. If an option (indicated by 347 1.1 cgd * a percent sign) appears in the path entry then the global variable 348 1.1 cgd * pathopt will be set to point to it; otherwise pathopt will be set to 349 1.1 cgd * NULL. 350 1.1 cgd */ 351 1.1 cgd 352 1.27 christos const char *pathopt; 353 1.1 cgd 354 1.1 cgd char * 355 1.48 kre padvance(const char **path, const char *name, int magic_percent) 356 1.34 christos { 357 1.27 christos const char *p; 358 1.27 christos char *q; 359 1.27 christos const char *start; 360 1.1 cgd int len; 361 1.1 cgd 362 1.1 cgd if (*path == NULL) 363 1.1 cgd return NULL; 364 1.48 kre if (magic_percent) 365 1.48 kre magic_percent = '%'; 366 1.48 kre 367 1.1 cgd start = *path; 368 1.48 kre for (p = start ; *p && *p != ':' && *p != magic_percent ; p++) 369 1.48 kre ; 370 1.1 cgd len = p - start + strlen(name) + 2; /* "2" is for '/' and '\0' */ 371 1.1 cgd while (stackblocksize() < len) 372 1.1 cgd growstackblock(); 373 1.1 cgd q = stackblock(); 374 1.1 cgd if (p != start) { 375 1.11 mycroft memcpy(q, start, p - start); 376 1.1 cgd q += p - start; 377 1.48 kre if (q[-1] != '/') 378 1.48 kre *q++ = '/'; 379 1.1 cgd } 380 1.1 cgd strcpy(q, name); 381 1.1 cgd pathopt = NULL; 382 1.48 kre if (*p == magic_percent) { 383 1.1 cgd pathopt = ++p; 384 1.48 kre while (*p && *p != ':') 385 1.48 kre p++; 386 1.1 cgd } 387 1.1 cgd if (*p == ':') 388 1.1 cgd *path = p + 1; 389 1.1 cgd else 390 1.1 cgd *path = NULL; 391 1.50 kre return grabstackstr(q + strlen(name) + 1); 392 1.1 cgd } 393 1.1 cgd 394 1.1 cgd 395 1.1 cgd /*** Command hashing code ***/ 396 1.1 cgd 397 1.1 cgd 398 1.12 cgd int 399 1.34 christos hashcmd(int argc, char **argv) 400 1.12 cgd { 401 1.1 cgd struct tblentry **pp; 402 1.1 cgd struct tblentry *cmdp; 403 1.1 cgd int c; 404 1.1 cgd struct cmdentry entry; 405 1.1 cgd char *name; 406 1.47 kre int allopt=0, bopt=0, fopt=0, ropt=0, sopt=0, uopt=0, verbose=0; 407 1.57 kre int errs=1, emsg=DO_ERR; 408 1.57 kre int status = 0; 409 1.1 cgd 410 1.57 kre while ((c = nextopt("bcefqrsuv")) != '\0') 411 1.47 kre switch (c) { 412 1.47 kre case 'b': bopt = 1; break; 413 1.47 kre case 'c': uopt = 1; break; /* c == u */ 414 1.57 kre case 'e': errs = 0; break; 415 1.47 kre case 'f': fopt = 1; break; 416 1.57 kre case 'q': emsg = 0; break; 417 1.47 kre case 'r': ropt = 1; break; 418 1.47 kre case 's': sopt = 1; break; 419 1.47 kre case 'u': uopt = 1; break; 420 1.47 kre case 'v': verbose = 1; break; 421 1.1 cgd } 422 1.47 kre 423 1.57 kre if (!errs) 424 1.57 kre emsg ^= DO_ERR; 425 1.57 kre 426 1.47 kre if (ropt) 427 1.47 kre clearcmdentry(0); 428 1.47 kre 429 1.47 kre if (bopt == 0 && fopt == 0 && sopt == 0 && uopt == 0) 430 1.47 kre allopt = bopt = fopt = sopt = uopt = 1; 431 1.47 kre 432 1.8 jtc if (*argptr == NULL) { 433 1.8 jtc for (pp = cmdtable ; pp < &cmdtable[CMDTABLESIZE] ; pp++) { 434 1.8 jtc for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) { 435 1.47 kre switch (cmdp->cmdtype) { 436 1.47 kre case CMDNORMAL: 437 1.47 kre if (!uopt) 438 1.47 kre continue; 439 1.47 kre break; 440 1.47 kre case CMDBUILTIN: 441 1.47 kre if (!bopt) 442 1.47 kre continue; 443 1.47 kre break; 444 1.47 kre case CMDSPLBLTIN: 445 1.47 kre if (!sopt) 446 1.47 kre continue; 447 1.47 kre break; 448 1.47 kre case CMDFUNCTION: 449 1.47 kre if (!fopt) 450 1.47 kre continue; 451 1.47 kre break; 452 1.47 kre default: /* never happens */ 453 1.47 kre continue; 454 1.47 kre } 455 1.47 kre if (!allopt || verbose || 456 1.47 kre cmdp->cmdtype == CMDNORMAL) 457 1.36 dsl printentry(cmdp, verbose); 458 1.8 jtc } 459 1.8 jtc } 460 1.57 kre flushout(out1); 461 1.57 kre if (io_err(out1)) { 462 1.57 kre out2str("hash: I/O error writing to standard output\n"); 463 1.57 kre return 1; 464 1.57 kre } 465 1.8 jtc return 0; 466 1.8 jtc } 467 1.47 kre 468 1.47 kre while ((name = *argptr++) != NULL) { 469 1.47 kre if ((cmdp = cmdlookup(name, 0)) != NULL) { 470 1.47 kre switch (cmdp->cmdtype) { 471 1.47 kre case CMDNORMAL: 472 1.47 kre if (!uopt) 473 1.47 kre continue; 474 1.47 kre delete_cmd_entry(); 475 1.47 kre break; 476 1.47 kre case CMDBUILTIN: 477 1.47 kre if (!bopt) 478 1.47 kre continue; 479 1.47 kre if (builtinloc >= 0) 480 1.47 kre delete_cmd_entry(); 481 1.47 kre break; 482 1.47 kre case CMDSPLBLTIN: 483 1.47 kre if (!sopt) 484 1.47 kre continue; 485 1.47 kre break; 486 1.47 kre case CMDFUNCTION: 487 1.47 kre if (!fopt) 488 1.47 kre continue; 489 1.47 kre break; 490 1.47 kre } 491 1.47 kre } 492 1.57 kre find_command(name, &entry, emsg, pathval()); 493 1.57 kre if (errs && entry.cmdtype == CMDUNKNOWN) 494 1.57 kre status = 1; 495 1.1 cgd if (verbose) { 496 1.1 cgd if (entry.cmdtype != CMDUNKNOWN) { /* if no error msg */ 497 1.1 cgd cmdp = cmdlookup(name, 0); 498 1.38 christos if (cmdp != NULL) 499 1.38 christos printentry(cmdp, verbose); 500 1.1 cgd } 501 1.1 cgd flushall(); 502 1.1 cgd } 503 1.1 cgd } 504 1.57 kre return status; 505 1.1 cgd } 506 1.1 cgd 507 1.1 cgd STATIC void 508 1.34 christos printentry(struct tblentry *cmdp, int verbose) 509 1.34 christos { 510 1.27 christos int idx; 511 1.27 christos const char *path; 512 1.1 cgd char *name; 513 1.1 cgd 514 1.34 christos switch (cmdp->cmdtype) { 515 1.34 christos case CMDNORMAL: 516 1.27 christos idx = cmdp->param.index; 517 1.1 cgd path = pathval(); 518 1.1 cgd do { 519 1.48 kre name = padvance(&path, cmdp->cmdname, 1); 520 1.1 cgd stunalloc(name); 521 1.27 christos } while (--idx >= 0); 522 1.47 kre if (verbose) 523 1.47 kre out1fmt("Command from PATH[%d]: ", 524 1.47 kre cmdp->param.index); 525 1.1 cgd out1str(name); 526 1.34 christos break; 527 1.34 christos case CMDSPLBLTIN: 528 1.47 kre if (verbose) 529 1.47 kre out1str("special "); 530 1.47 kre /* FALLTHROUGH */ 531 1.34 christos case CMDBUILTIN: 532 1.47 kre if (verbose) 533 1.47 kre out1str("builtin "); 534 1.47 kre out1fmt("%s", cmdp->cmdname); 535 1.34 christos break; 536 1.34 christos case CMDFUNCTION: 537 1.47 kre if (verbose) 538 1.47 kre out1str("function "); 539 1.47 kre out1fmt("%s", cmdp->cmdname); 540 1.8 jtc if (verbose) { 541 1.34 christos struct procstat ps; 542 1.52 kre 543 1.8 jtc INTOFF; 544 1.52 kre commandtext(&ps, getfuncnode(cmdp->param.func)); 545 1.36 dsl INTON; 546 1.36 dsl out1str("() { "); 547 1.34 christos out1str(ps.cmd); 548 1.36 dsl out1str("; }"); 549 1.8 jtc } 550 1.34 christos break; 551 1.34 christos default: 552 1.47 kre error("internal error: %s cmdtype %d", 553 1.47 kre cmdp->cmdname, cmdp->cmdtype); 554 1.1 cgd } 555 1.1 cgd if (cmdp->rehash) 556 1.1 cgd out1c('*'); 557 1.1 cgd out1c('\n'); 558 1.1 cgd } 559 1.1 cgd 560 1.1 cgd 561 1.1 cgd 562 1.1 cgd /* 563 1.1 cgd * Resolve a command name. If you change this routine, you may have to 564 1.1 cgd * change the shellexec routine as well. 565 1.1 cgd */ 566 1.1 cgd 567 1.1 cgd void 568 1.34 christos find_command(char *name, struct cmdentry *entry, int act, const char *path) 569 1.12 cgd { 570 1.35 dsl struct tblentry *cmdp, loc_cmd; 571 1.27 christos int idx; 572 1.1 cgd int prev; 573 1.1 cgd char *fullname; 574 1.1 cgd struct stat statb; 575 1.1 cgd int e; 576 1.34 christos int (*bltin)(int,char **); 577 1.1 cgd 578 1.35 dsl /* If name contains a slash, don't use PATH or hash table */ 579 1.1 cgd if (strchr(name, '/') != NULL) { 580 1.24 christos if (act & DO_ABS) { 581 1.24 christos while (stat(name, &statb) < 0) { 582 1.34 christos #ifdef SYSV 583 1.24 christos if (errno == EINTR) 584 1.24 christos continue; 585 1.34 christos #endif 586 1.24 christos if (errno != ENOENT && errno != ENOTDIR) 587 1.24 christos e = errno; 588 1.24 christos entry->cmdtype = CMDUNKNOWN; 589 1.24 christos entry->u.index = -1; 590 1.24 christos return; 591 1.24 christos } 592 1.24 christos entry->cmdtype = CMDNORMAL; 593 1.24 christos entry->u.index = -1; 594 1.24 christos return; 595 1.24 christos } 596 1.1 cgd entry->cmdtype = CMDNORMAL; 597 1.1 cgd entry->u.index = 0; 598 1.1 cgd return; 599 1.1 cgd } 600 1.1 cgd 601 1.35 dsl if (path != pathval()) 602 1.35 dsl act |= DO_ALTPATH; 603 1.35 dsl 604 1.35 dsl if (act & DO_ALTPATH && strstr(path, "%builtin") != NULL) 605 1.35 dsl act |= DO_ALTBLTIN; 606 1.35 dsl 607 1.35 dsl /* If name is in the table, check answer will be ok */ 608 1.35 dsl if ((cmdp = cmdlookup(name, 0)) != NULL) { 609 1.56 rillig switch (cmdp->cmdtype) { 610 1.56 rillig case CMDNORMAL: 611 1.56 rillig if (act & DO_ALTPATH) 612 1.56 rillig cmdp = NULL; 613 1.56 rillig break; 614 1.56 rillig case CMDFUNCTION: 615 1.56 rillig if (act & DO_NOFUNC) 616 1.56 rillig cmdp = NULL; 617 1.56 rillig break; 618 1.56 rillig case CMDBUILTIN: 619 1.56 rillig if ((act & DO_ALTBLTIN) || builtinloc >= 0) 620 1.56 rillig cmdp = NULL; 621 1.56 rillig break; 622 1.56 rillig } 623 1.56 rillig /* if not invalidated by cd, we're done */ 624 1.56 rillig if (cmdp != NULL && cmdp->rehash == 0) 625 1.56 rillig goto success; 626 1.35 dsl } 627 1.1 cgd 628 1.1 cgd /* If %builtin not in path, check for builtin next */ 629 1.35 dsl if ((act & DO_ALTPATH ? !(act & DO_ALTBLTIN) : builtinloc < 0) && 630 1.35 dsl (bltin = find_builtin(name)) != 0) 631 1.35 dsl goto builtin_success; 632 1.1 cgd 633 1.1 cgd /* We have to search path. */ 634 1.1 cgd prev = -1; /* where to start */ 635 1.1 cgd if (cmdp) { /* doing a rehash */ 636 1.1 cgd if (cmdp->cmdtype == CMDBUILTIN) 637 1.1 cgd prev = builtinloc; 638 1.1 cgd else 639 1.1 cgd prev = cmdp->param.index; 640 1.1 cgd } 641 1.1 cgd 642 1.1 cgd e = ENOENT; 643 1.27 christos idx = -1; 644 1.1 cgd loop: 645 1.48 kre while ((fullname = padvance(&path, name, 1)) != NULL) { 646 1.1 cgd stunalloc(fullname); 647 1.27 christos idx++; 648 1.1 cgd if (pathopt) { 649 1.1 cgd if (prefix("builtin", pathopt)) { 650 1.34 christos if ((bltin = find_builtin(name)) == 0) 651 1.1 cgd goto loop; 652 1.35 dsl goto builtin_success; 653 1.1 cgd } else if (prefix("func", pathopt)) { 654 1.1 cgd /* handled below */ 655 1.1 cgd } else { 656 1.35 dsl /* ignore unimplemented options */ 657 1.35 dsl goto loop; 658 1.1 cgd } 659 1.1 cgd } 660 1.1 cgd /* if rehash, don't redo absolute path names */ 661 1.27 christos if (fullname[0] == '/' && idx <= prev) { 662 1.27 christos if (idx < prev) 663 1.1 cgd goto loop; 664 1.51 kre VTRACE(DBG_CMDS, ("searchexec \"%s\": no change\n", 665 1.51 kre name)); 666 1.1 cgd goto success; 667 1.1 cgd } 668 1.1 cgd while (stat(fullname, &statb) < 0) { 669 1.1 cgd #ifdef SYSV 670 1.1 cgd if (errno == EINTR) 671 1.1 cgd continue; 672 1.1 cgd #endif 673 1.1 cgd if (errno != ENOENT && errno != ENOTDIR) 674 1.1 cgd e = errno; 675 1.1 cgd goto loop; 676 1.1 cgd } 677 1.1 cgd e = EACCES; /* if we fail, this will be the error */ 678 1.14 mycroft if (!S_ISREG(statb.st_mode)) 679 1.1 cgd goto loop; 680 1.1 cgd if (pathopt) { /* this is a %func directory */ 681 1.50 kre char *endname; 682 1.50 kre 683 1.35 dsl if (act & DO_NOFUNC) 684 1.35 dsl goto loop; 685 1.50 kre endname = fullname + strlen(fullname) + 1; 686 1.50 kre grabstackstr(endname); 687 1.1 cgd readcmdfile(fullname); 688 1.35 dsl if ((cmdp = cmdlookup(name, 0)) == NULL || 689 1.35 dsl cmdp->cmdtype != CMDFUNCTION) 690 1.1 cgd error("%s not defined in %s", name, fullname); 691 1.50 kre ungrabstackstr(fullname, endname); 692 1.1 cgd goto success; 693 1.1 cgd } 694 1.8 jtc #ifdef notdef 695 1.35 dsl /* XXX this code stops root executing stuff, and is buggy 696 1.35 dsl if you need a group from the group list. */ 697 1.8 jtc if (statb.st_uid == geteuid()) { 698 1.1 cgd if ((statb.st_mode & 0100) == 0) 699 1.1 cgd goto loop; 700 1.1 cgd } else if (statb.st_gid == getegid()) { 701 1.1 cgd if ((statb.st_mode & 010) == 0) 702 1.1 cgd goto loop; 703 1.1 cgd } else { 704 1.8 jtc if ((statb.st_mode & 01) == 0) 705 1.1 cgd goto loop; 706 1.1 cgd } 707 1.4 cgd #endif 708 1.51 kre VTRACE(DBG_CMDS, ("searchexec \"%s\" returns \"%s\"\n", name, 709 1.51 kre fullname)); 710 1.1 cgd INTOFF; 711 1.35 dsl if (act & DO_ALTPATH) { 712 1.50 kre /* 713 1.50 kre * this should be a grabstackstr() but is not needed: 714 1.50 kre * fullname is no longer needed for anything 715 1.35 dsl stalloc(strlen(fullname) + 1); 716 1.50 kre */ 717 1.35 dsl cmdp = &loc_cmd; 718 1.35 dsl } else 719 1.35 dsl cmdp = cmdlookup(name, 1); 720 1.54 kre 721 1.54 kre if (cmdp->cmdtype == CMDFUNCTION) 722 1.54 kre cmdp = &loc_cmd; 723 1.54 kre 724 1.1 cgd cmdp->cmdtype = CMDNORMAL; 725 1.27 christos cmdp->param.index = idx; 726 1.1 cgd INTON; 727 1.1 cgd goto success; 728 1.1 cgd } 729 1.1 cgd 730 1.1 cgd /* We failed. If there was an entry for this command, delete it */ 731 1.1 cgd if (cmdp) 732 1.1 cgd delete_cmd_entry(); 733 1.24 christos if (act & DO_ERR) 734 1.20 abrown outfmt(out2, "%s: %s\n", name, errmsg(e, E_EXEC)); 735 1.1 cgd entry->cmdtype = CMDUNKNOWN; 736 1.55 kre entry->u.index = idx + 1; 737 1.1 cgd return; 738 1.1 cgd 739 1.35 dsl builtin_success: 740 1.35 dsl INTOFF; 741 1.35 dsl if (act & DO_ALTPATH) 742 1.35 dsl cmdp = &loc_cmd; 743 1.35 dsl else 744 1.35 dsl cmdp = cmdlookup(name, 1); 745 1.36 dsl if (cmdp->cmdtype == CMDFUNCTION) 746 1.36 dsl /* DO_NOFUNC must have been set */ 747 1.36 dsl cmdp = &loc_cmd; 748 1.35 dsl cmdp->cmdtype = CMDBUILTIN; 749 1.35 dsl cmdp->param.bltin = bltin; 750 1.35 dsl INTON; 751 1.1 cgd success: 752 1.39 christos if (cmdp) { 753 1.39 christos cmdp->rehash = 0; 754 1.39 christos entry->cmdtype = cmdp->cmdtype; 755 1.49 kre entry->lineno = cmdp->lineno; 756 1.49 kre entry->lno_frel = cmdp->fn_ln1; 757 1.39 christos entry->u = cmdp->param; 758 1.55 kre } else { 759 1.39 christos entry->cmdtype = CMDUNKNOWN; 760 1.55 kre entry->u.index = -1; 761 1.55 kre } 762 1.1 cgd } 763 1.1 cgd 764 1.1 cgd 765 1.1 cgd 766 1.1 cgd /* 767 1.1 cgd * Search the table of builtin commands. 768 1.1 cgd */ 769 1.1 cgd 770 1.1 cgd int 771 1.43 matt (*find_builtin(char *name))(int, char **) 772 1.12 cgd { 773 1.21 tls const struct builtincmd *bp; 774 1.1 cgd 775 1.1 cgd for (bp = builtincmd ; bp->name ; bp++) { 776 1.44 dsl if (*bp->name == *name 777 1.44 dsl && (*name == '%' || equal(bp->name, name))) 778 1.34 christos return bp->builtin; 779 1.34 christos } 780 1.34 christos return 0; 781 1.34 christos } 782 1.34 christos 783 1.34 christos int 784 1.43 matt (*find_splbltin(char *name))(int, char **) 785 1.34 christos { 786 1.34 christos const struct builtincmd *bp; 787 1.34 christos 788 1.34 christos for (bp = splbltincmd ; bp->name ; bp++) { 789 1.34 christos if (*bp->name == *name && equal(bp->name, name)) 790 1.34 christos return bp->builtin; 791 1.34 christos } 792 1.34 christos return 0; 793 1.34 christos } 794 1.34 christos 795 1.34 christos /* 796 1.34 christos * At shell startup put special builtins into hash table. 797 1.34 christos * ensures they are executed first (see posix). 798 1.34 christos * We stop functions being added with the same name 799 1.34 christos * (as they are impossible to call) 800 1.34 christos */ 801 1.34 christos 802 1.34 christos void 803 1.34 christos hash_special_builtins(void) 804 1.34 christos { 805 1.34 christos const struct builtincmd *bp; 806 1.34 christos struct tblentry *cmdp; 807 1.34 christos 808 1.34 christos for (bp = splbltincmd ; bp->name ; bp++) { 809 1.34 christos cmdp = cmdlookup(bp->name, 1); 810 1.34 christos cmdp->cmdtype = CMDSPLBLTIN; 811 1.34 christos cmdp->param.bltin = bp->builtin; 812 1.1 cgd } 813 1.1 cgd } 814 1.1 cgd 815 1.1 cgd 816 1.1 cgd 817 1.1 cgd /* 818 1.1 cgd * Called when a cd is done. Marks all commands so the next time they 819 1.1 cgd * are executed they will be rehashed. 820 1.1 cgd */ 821 1.1 cgd 822 1.1 cgd void 823 1.34 christos hashcd(void) 824 1.34 christos { 825 1.1 cgd struct tblentry **pp; 826 1.1 cgd struct tblentry *cmdp; 827 1.1 cgd 828 1.1 cgd for (pp = cmdtable ; pp < &cmdtable[CMDTABLESIZE] ; pp++) { 829 1.1 cgd for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) { 830 1.1 cgd if (cmdp->cmdtype == CMDNORMAL 831 1.16 christos || (cmdp->cmdtype == CMDBUILTIN && builtinloc >= 0)) 832 1.1 cgd cmdp->rehash = 1; 833 1.1 cgd } 834 1.1 cgd } 835 1.1 cgd } 836 1.1 cgd 837 1.1 cgd 838 1.1 cgd 839 1.1 cgd /* 840 1.35 dsl * Fix command hash table when PATH changed. 841 1.1 cgd * Called before PATH is changed. The argument is the new value of PATH; 842 1.35 dsl * pathval() still returns the old value at this point. 843 1.35 dsl * Called with interrupts off. 844 1.1 cgd */ 845 1.1 cgd 846 1.1 cgd void 847 1.59 kre changepath(char *newval, int flags __unused) 848 1.13 mycroft { 849 1.18 christos const char *old, *new; 850 1.27 christos int idx; 851 1.1 cgd int firstchange; 852 1.1 cgd int bltin; 853 1.1 cgd 854 1.1 cgd old = pathval(); 855 1.1 cgd new = newval; 856 1.1 cgd firstchange = 9999; /* assume no change */ 857 1.27 christos idx = 0; 858 1.1 cgd bltin = -1; 859 1.1 cgd for (;;) { 860 1.1 cgd if (*old != *new) { 861 1.27 christos firstchange = idx; 862 1.16 christos if ((*old == '\0' && *new == ':') 863 1.16 christos || (*old == ':' && *new == '\0')) 864 1.1 cgd firstchange++; 865 1.1 cgd old = new; /* ignore subsequent differences */ 866 1.1 cgd } 867 1.1 cgd if (*new == '\0') 868 1.1 cgd break; 869 1.1 cgd if (*new == '%' && bltin < 0 && prefix("builtin", new + 1)) 870 1.27 christos bltin = idx; 871 1.1 cgd if (*new == ':') { 872 1.27 christos idx++; 873 1.1 cgd } 874 1.1 cgd new++, old++; 875 1.1 cgd } 876 1.1 cgd if (builtinloc < 0 && bltin >= 0) 877 1.1 cgd builtinloc = bltin; /* zap builtins */ 878 1.1 cgd if (builtinloc >= 0 && bltin < 0) 879 1.1 cgd firstchange = 0; 880 1.1 cgd clearcmdentry(firstchange); 881 1.1 cgd builtinloc = bltin; 882 1.1 cgd } 883 1.1 cgd 884 1.1 cgd 885 1.1 cgd /* 886 1.1 cgd * Clear out command entries. The argument specifies the first entry in 887 1.1 cgd * PATH which has changed. 888 1.1 cgd */ 889 1.1 cgd 890 1.1 cgd STATIC void 891 1.34 christos clearcmdentry(int firstchange) 892 1.12 cgd { 893 1.1 cgd struct tblentry **tblp; 894 1.1 cgd struct tblentry **pp; 895 1.1 cgd struct tblentry *cmdp; 896 1.1 cgd 897 1.1 cgd INTOFF; 898 1.1 cgd for (tblp = cmdtable ; tblp < &cmdtable[CMDTABLESIZE] ; tblp++) { 899 1.1 cgd pp = tblp; 900 1.1 cgd while ((cmdp = *pp) != NULL) { 901 1.16 christos if ((cmdp->cmdtype == CMDNORMAL && 902 1.16 christos cmdp->param.index >= firstchange) 903 1.16 christos || (cmdp->cmdtype == CMDBUILTIN && 904 1.16 christos builtinloc >= firstchange)) { 905 1.1 cgd *pp = cmdp->next; 906 1.1 cgd ckfree(cmdp); 907 1.1 cgd } else { 908 1.1 cgd pp = &cmdp->next; 909 1.1 cgd } 910 1.1 cgd } 911 1.1 cgd } 912 1.1 cgd INTON; 913 1.1 cgd } 914 1.1 cgd 915 1.1 cgd 916 1.1 cgd /* 917 1.1 cgd * Delete all functions. 918 1.1 cgd */ 919 1.1 cgd 920 1.1 cgd #ifdef mkinit 921 1.34 christos MKINIT void deletefuncs(void); 922 1.34 christos MKINIT void hash_special_builtins(void); 923 1.34 christos 924 1.34 christos INIT { 925 1.34 christos hash_special_builtins(); 926 1.34 christos } 927 1.1 cgd 928 1.1 cgd SHELLPROC { 929 1.1 cgd deletefuncs(); 930 1.1 cgd } 931 1.1 cgd #endif 932 1.1 cgd 933 1.1 cgd void 934 1.34 christos deletefuncs(void) 935 1.34 christos { 936 1.1 cgd struct tblentry **tblp; 937 1.1 cgd struct tblentry **pp; 938 1.1 cgd struct tblentry *cmdp; 939 1.1 cgd 940 1.1 cgd INTOFF; 941 1.1 cgd for (tblp = cmdtable ; tblp < &cmdtable[CMDTABLESIZE] ; tblp++) { 942 1.1 cgd pp = tblp; 943 1.1 cgd while ((cmdp = *pp) != NULL) { 944 1.1 cgd if (cmdp->cmdtype == CMDFUNCTION) { 945 1.1 cgd *pp = cmdp->next; 946 1.1 cgd freefunc(cmdp->param.func); 947 1.1 cgd ckfree(cmdp); 948 1.1 cgd } else { 949 1.1 cgd pp = &cmdp->next; 950 1.1 cgd } 951 1.1 cgd } 952 1.1 cgd } 953 1.1 cgd INTON; 954 1.1 cgd } 955 1.1 cgd 956 1.1 cgd 957 1.1 cgd 958 1.1 cgd /* 959 1.1 cgd * Locate a command in the command hash table. If "add" is nonzero, 960 1.1 cgd * add the command to the table if it is not already present. The 961 1.1 cgd * variable "lastcmdentry" is set to point to the address of the link 962 1.1 cgd * pointing to the entry, so that delete_cmd_entry can delete the 963 1.1 cgd * entry. 964 1.1 cgd */ 965 1.1 cgd 966 1.1 cgd struct tblentry **lastcmdentry; 967 1.1 cgd 968 1.1 cgd 969 1.1 cgd STATIC struct tblentry * 970 1.34 christos cmdlookup(const char *name, int add) 971 1.12 cgd { 972 1.1 cgd int hashval; 973 1.34 christos const char *p; 974 1.1 cgd struct tblentry *cmdp; 975 1.1 cgd struct tblentry **pp; 976 1.1 cgd 977 1.1 cgd p = name; 978 1.1 cgd hashval = *p << 4; 979 1.1 cgd while (*p) 980 1.1 cgd hashval += *p++; 981 1.1 cgd hashval &= 0x7FFF; 982 1.1 cgd pp = &cmdtable[hashval % CMDTABLESIZE]; 983 1.1 cgd for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) { 984 1.1 cgd if (equal(cmdp->cmdname, name)) 985 1.1 cgd break; 986 1.1 cgd pp = &cmdp->next; 987 1.1 cgd } 988 1.1 cgd if (add && cmdp == NULL) { 989 1.1 cgd INTOFF; 990 1.1 cgd cmdp = *pp = ckmalloc(sizeof (struct tblentry) - ARB 991 1.1 cgd + strlen(name) + 1); 992 1.1 cgd cmdp->next = NULL; 993 1.1 cgd cmdp->cmdtype = CMDUNKNOWN; 994 1.1 cgd cmdp->rehash = 0; 995 1.1 cgd strcpy(cmdp->cmdname, name); 996 1.1 cgd INTON; 997 1.1 cgd } 998 1.1 cgd lastcmdentry = pp; 999 1.1 cgd return cmdp; 1000 1.1 cgd } 1001 1.1 cgd 1002 1.1 cgd /* 1003 1.1 cgd * Delete the command entry returned on the last lookup. 1004 1.1 cgd */ 1005 1.1 cgd 1006 1.1 cgd STATIC void 1007 1.34 christos delete_cmd_entry(void) 1008 1.34 christos { 1009 1.1 cgd struct tblentry *cmdp; 1010 1.1 cgd 1011 1.1 cgd INTOFF; 1012 1.1 cgd cmdp = *lastcmdentry; 1013 1.1 cgd *lastcmdentry = cmdp->next; 1014 1.1 cgd ckfree(cmdp); 1015 1.1 cgd INTON; 1016 1.1 cgd } 1017 1.1 cgd 1018 1.1 cgd 1019 1.1 cgd 1020 1.1 cgd #ifdef notdef 1021 1.1 cgd void 1022 1.34 christos getcmdentry(char *name, struct cmdentry *entry) 1023 1.34 christos { 1024 1.1 cgd struct tblentry *cmdp = cmdlookup(name, 0); 1025 1.1 cgd 1026 1.1 cgd if (cmdp) { 1027 1.1 cgd entry->u = cmdp->param; 1028 1.1 cgd entry->cmdtype = cmdp->cmdtype; 1029 1.1 cgd } else { 1030 1.1 cgd entry->cmdtype = CMDUNKNOWN; 1031 1.1 cgd entry->u.index = 0; 1032 1.1 cgd } 1033 1.1 cgd } 1034 1.1 cgd #endif 1035 1.1 cgd 1036 1.1 cgd 1037 1.1 cgd /* 1038 1.1 cgd * Add a new command entry, replacing any existing command entry for 1039 1.34 christos * the same name - except special builtins. 1040 1.1 cgd */ 1041 1.1 cgd 1042 1.35 dsl STATIC void 1043 1.34 christos addcmdentry(char *name, struct cmdentry *entry) 1044 1.34 christos { 1045 1.1 cgd struct tblentry *cmdp; 1046 1.1 cgd 1047 1.1 cgd INTOFF; 1048 1.1 cgd cmdp = cmdlookup(name, 1); 1049 1.34 christos if (cmdp->cmdtype != CMDSPLBLTIN) { 1050 1.52 kre if (cmdp->cmdtype == CMDFUNCTION) 1051 1.52 kre unreffunc(cmdp->param.func); 1052 1.34 christos cmdp->cmdtype = entry->cmdtype; 1053 1.49 kre cmdp->lineno = entry->lineno; 1054 1.49 kre cmdp->fn_ln1 = entry->lno_frel; 1055 1.34 christos cmdp->param = entry->u; 1056 1.1 cgd } 1057 1.1 cgd INTON; 1058 1.1 cgd } 1059 1.1 cgd 1060 1.1 cgd 1061 1.1 cgd /* 1062 1.1 cgd * Define a shell function. 1063 1.1 cgd */ 1064 1.1 cgd 1065 1.1 cgd void 1066 1.49 kre defun(char *name, union node *func, int lineno) 1067 1.34 christos { 1068 1.1 cgd struct cmdentry entry; 1069 1.1 cgd 1070 1.1 cgd INTOFF; 1071 1.1 cgd entry.cmdtype = CMDFUNCTION; 1072 1.49 kre entry.lineno = lineno; 1073 1.49 kre entry.lno_frel = fnline1; 1074 1.1 cgd entry.u.func = copyfunc(func); 1075 1.1 cgd addcmdentry(name, &entry); 1076 1.1 cgd INTON; 1077 1.1 cgd } 1078 1.1 cgd 1079 1.1 cgd 1080 1.1 cgd /* 1081 1.1 cgd * Delete a function if it exists. 1082 1.1 cgd */ 1083 1.1 cgd 1084 1.8 jtc int 1085 1.34 christos unsetfunc(char *name) 1086 1.34 christos { 1087 1.1 cgd struct tblentry *cmdp; 1088 1.1 cgd 1089 1.35 dsl if ((cmdp = cmdlookup(name, 0)) != NULL && 1090 1.35 dsl cmdp->cmdtype == CMDFUNCTION) { 1091 1.52 kre unreffunc(cmdp->param.func); 1092 1.1 cgd delete_cmd_entry(); 1093 1.1 cgd } 1094 1.45 christos return 0; 1095 1.22 christos } 1096 1.22 christos 1097 1.22 christos /* 1098 1.22 christos * Locate and print what a word is... 1099 1.35 dsl * also used for 'command -[v|V]' 1100 1.22 christos */ 1101 1.22 christos 1102 1.22 christos int 1103 1.34 christos typecmd(int argc, char **argv) 1104 1.22 christos { 1105 1.22 christos struct cmdentry entry; 1106 1.22 christos struct tblentry *cmdp; 1107 1.41 matt const char * const *pp; 1108 1.22 christos struct alias *ap; 1109 1.27 christos int err = 0; 1110 1.35 dsl char *arg; 1111 1.35 dsl int c; 1112 1.35 dsl int V_flag = 0; 1113 1.35 dsl int v_flag = 0; 1114 1.35 dsl int p_flag = 0; 1115 1.22 christos 1116 1.35 dsl while ((c = nextopt("vVp")) != 0) { 1117 1.35 dsl switch (c) { 1118 1.35 dsl case 'v': v_flag = 1; break; 1119 1.35 dsl case 'V': V_flag = 1; break; 1120 1.35 dsl case 'p': p_flag = 1; break; 1121 1.35 dsl } 1122 1.35 dsl } 1123 1.35 dsl 1124 1.53 kre if (argv[0][0] != 'c' && v_flag | V_flag | p_flag) 1125 1.53 kre error("usage: %s name...", argv[0]); 1126 1.53 kre 1127 1.53 kre if (v_flag && V_flag) 1128 1.53 kre error("-v and -V cannot both be specified"); 1129 1.53 kre 1130 1.53 kre if (*argptr == NULL) 1131 1.53 kre error("usage: %s%s name ...", argv[0], 1132 1.53 kre argv[0][0] == 'c' ? " [-p] [-v|-V]" : ""); 1133 1.35 dsl 1134 1.35 dsl while ((arg = *argptr++)) { 1135 1.35 dsl if (!v_flag) 1136 1.35 dsl out1str(arg); 1137 1.22 christos /* First look at the keywords */ 1138 1.30 matt for (pp = parsekwd; *pp; pp++) 1139 1.35 dsl if (**pp == *arg && equal(*pp, arg)) 1140 1.22 christos break; 1141 1.22 christos 1142 1.22 christos if (*pp) { 1143 1.35 dsl if (v_flag) 1144 1.53 kre out1fmt("%s\n", arg); 1145 1.35 dsl else 1146 1.35 dsl out1str(" is a shell keyword\n"); 1147 1.22 christos continue; 1148 1.22 christos } 1149 1.22 christos 1150 1.22 christos /* Then look at the aliases */ 1151 1.35 dsl if ((ap = lookupalias(arg, 1)) != NULL) { 1152 1.53 kre int ml = 0; 1153 1.53 kre 1154 1.53 kre if (!v_flag) { 1155 1.53 kre out1str(" is an alias "); 1156 1.53 kre if (strchr(ap->val, '\n')) { 1157 1.53 kre out1str("(multiline)...\n"); 1158 1.53 kre ml = 1; 1159 1.53 kre } else 1160 1.53 kre out1str("for: "); 1161 1.53 kre } 1162 1.35 dsl out1fmt("%s\n", ap->val); 1163 1.53 kre if (ml && *argptr != NULL) 1164 1.53 kre out1c('\n'); 1165 1.22 christos continue; 1166 1.22 christos } 1167 1.22 christos 1168 1.22 christos /* Then check if it is a tracked alias */ 1169 1.53 kre if (!p_flag && (cmdp = cmdlookup(arg, 0)) != NULL) { 1170 1.22 christos entry.cmdtype = cmdp->cmdtype; 1171 1.22 christos entry.u = cmdp->param; 1172 1.35 dsl } else { 1173 1.53 kre cmdp = NULL; 1174 1.22 christos /* Finally use brute force */ 1175 1.53 kre find_command(arg, &entry, DO_ABS, 1176 1.53 kre p_flag ? syspath() + 5 : pathval()); 1177 1.22 christos } 1178 1.22 christos 1179 1.22 christos switch (entry.cmdtype) { 1180 1.22 christos case CMDNORMAL: { 1181 1.35 dsl if (strchr(arg, '/') == NULL) { 1182 1.53 kre const char *path; 1183 1.31 christos char *name; 1184 1.31 christos int j = entry.u.index; 1185 1.53 kre 1186 1.53 kre path = p_flag ? syspath() + 5 : pathval(); 1187 1.53 kre 1188 1.31 christos do { 1189 1.48 kre name = padvance(&path, arg, 1); 1190 1.24 christos stunalloc(name); 1191 1.24 christos } while (--j >= 0); 1192 1.35 dsl if (!v_flag) 1193 1.35 dsl out1fmt(" is%s ", 1194 1.35 dsl cmdp ? " a tracked alias for" : ""); 1195 1.35 dsl out1fmt("%s\n", name); 1196 1.31 christos } else { 1197 1.35 dsl if (access(arg, X_OK) == 0) { 1198 1.35 dsl if (!v_flag) 1199 1.35 dsl out1fmt(" is "); 1200 1.35 dsl out1fmt("%s\n", arg); 1201 1.35 dsl } else { 1202 1.35 dsl if (!v_flag) 1203 1.35 dsl out1fmt(": %s\n", 1204 1.35 dsl strerror(errno)); 1205 1.35 dsl else 1206 1.35 dsl err = 126; 1207 1.35 dsl } 1208 1.24 christos } 1209 1.31 christos break; 1210 1.22 christos } 1211 1.22 christos case CMDFUNCTION: 1212 1.35 dsl if (!v_flag) 1213 1.35 dsl out1str(" is a shell function\n"); 1214 1.35 dsl else 1215 1.35 dsl out1fmt("%s\n", arg); 1216 1.22 christos break; 1217 1.22 christos 1218 1.22 christos case CMDBUILTIN: 1219 1.35 dsl if (!v_flag) 1220 1.35 dsl out1str(" is a shell builtin\n"); 1221 1.35 dsl else 1222 1.35 dsl out1fmt("%s\n", arg); 1223 1.34 christos break; 1224 1.34 christos 1225 1.34 christos case CMDSPLBLTIN: 1226 1.35 dsl if (!v_flag) 1227 1.35 dsl out1str(" is a special shell builtin\n"); 1228 1.35 dsl else 1229 1.35 dsl out1fmt("%s\n", arg); 1230 1.22 christos break; 1231 1.22 christos 1232 1.22 christos default: 1233 1.35 dsl if (!v_flag) 1234 1.35 dsl out1str(": not found\n"); 1235 1.35 dsl err = 127; 1236 1.22 christos break; 1237 1.22 christos } 1238 1.22 christos } 1239 1.27 christos return err; 1240 1.1 cgd } 1241