| 1 | /*      $NetBSD: eval.c,v 1.84 2005/06/23 23:05:29 christos Exp $       */
 | 
|---|
| 2 | 
 | 
|---|
| 3 | /*-
 | 
|---|
| 4 |  * Copyright (c) 1993
 | 
|---|
| 5 |  *      The Regents of the University of California.  All rights reserved.
 | 
|---|
| 6 |  *
 | 
|---|
| 7 |  * This code is derived from software contributed to Berkeley by
 | 
|---|
| 8 |  * Kenneth Almquist.
 | 
|---|
| 9 |  *
 | 
|---|
| 10 |  * Redistribution and use in source and binary forms, with or without
 | 
|---|
| 11 |  * modification, are permitted provided that the following conditions
 | 
|---|
| 12 |  * are met:
 | 
|---|
| 13 |  * 1. Redistributions of source code must retain the above copyright
 | 
|---|
| 14 |  *    notice, this list of conditions and the following disclaimer.
 | 
|---|
| 15 |  * 2. Redistributions in binary form must reproduce the above copyright
 | 
|---|
| 16 |  *    notice, this list of conditions and the following disclaimer in the
 | 
|---|
| 17 |  *    documentation and/or other materials provided with the distribution.
 | 
|---|
| 18 |  * 3. Neither the name of the University nor the names of its contributors
 | 
|---|
| 19 |  *    may be used to endorse or promote products derived from this software
 | 
|---|
| 20 |  *    without specific prior written permission.
 | 
|---|
| 21 |  *
 | 
|---|
| 22 |  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 | 
|---|
| 23 |  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 | 
|---|
| 24 |  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 | 
|---|
| 25 |  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 | 
|---|
| 26 |  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 | 
|---|
| 27 |  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 | 
|---|
| 28 |  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 | 
|---|
| 29 |  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 | 
|---|
| 30 |  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 | 
|---|
| 31 |  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 | 
|---|
| 32 |  * SUCH DAMAGE.
 | 
|---|
| 33 |  */
 | 
|---|
| 34 | 
 | 
|---|
| 35 | #include <sys/cdefs.h>
 | 
|---|
| 36 | #ifndef lint
 | 
|---|
| 37 | #if 0
 | 
|---|
| 38 | static char sccsid[] = "@(#)eval.c      8.9 (Berkeley) 6/8/95";
 | 
|---|
| 39 | #else
 | 
|---|
| 40 | __RCSID("$NetBSD: eval.c,v 1.84 2005/06/23 23:05:29 christos Exp $");
 | 
|---|
| 41 | #endif
 | 
|---|
| 42 | #endif /* not lint */
 | 
|---|
| 43 | 
 | 
|---|
| 44 | #include <stdlib.h>
 | 
|---|
| 45 | #include <signal.h>
 | 
|---|
| 46 | #include <stdio.h>
 | 
|---|
| 47 | #include <unistd.h>
 | 
|---|
| 48 | #include <sys/fcntl.h>
 | 
|---|
| 49 | #include <sys/times.h>
 | 
|---|
| 50 | #include <sys/param.h>
 | 
|---|
| 51 | #include <sys/types.h>
 | 
|---|
| 52 | #include <sys/wait.h>
 | 
|---|
| 53 | #include <sys/sysctl.h>
 | 
|---|
| 54 | 
 | 
|---|
| 55 | /*
 | 
|---|
| 56 |  * Evaluate a command.
 | 
|---|
| 57 |  */
 | 
|---|
| 58 | 
 | 
|---|
| 59 | #include "shell.h"
 | 
|---|
| 60 | #include "nodes.h"
 | 
|---|
| 61 | #include "syntax.h"
 | 
|---|
| 62 | #include "expand.h"
 | 
|---|
| 63 | #include "parser.h"
 | 
|---|
| 64 | #include "jobs.h"
 | 
|---|
| 65 | #include "eval.h"
 | 
|---|
| 66 | #include "builtins.h"
 | 
|---|
| 67 | #include "options.h"
 | 
|---|
| 68 | #include "exec.h"
 | 
|---|
| 69 | #include "redir.h"
 | 
|---|
| 70 | #include "input.h"
 | 
|---|
| 71 | #include "output.h"
 | 
|---|
| 72 | #include "trap.h"
 | 
|---|
| 73 | #include "var.h"
 | 
|---|
| 74 | #include "memalloc.h"
 | 
|---|
| 75 | #include "error.h"
 | 
|---|
| 76 | #include "show.h"
 | 
|---|
| 77 | #include "mystring.h"
 | 
|---|
| 78 | #include "main.h"
 | 
|---|
| 79 | #ifndef SMALL
 | 
|---|
| 80 | #include "myhistedit.h"
 | 
|---|
| 81 | #endif
 | 
|---|
| 82 | 
 | 
|---|
| 83 | 
 | 
|---|
| 84 | /* flags in argument to evaltree */
 | 
|---|
| 85 | #define EV_EXIT 01              /* exit after evaluating tree */
 | 
|---|
| 86 | #define EV_TESTED 02            /* exit status is checked; ignore -e flag */
 | 
|---|
| 87 | #define EV_BACKCMD 04           /* command executing within back quotes */
 | 
|---|
| 88 | 
 | 
|---|
| 89 | int evalskip;                   /* set if we are skipping commands */
 | 
|---|
| 90 | STATIC int skipcount;           /* number of levels to skip */
 | 
|---|
| 91 | MKINIT int loopnest;            /* current loop nesting level */
 | 
|---|
| 92 | int funcnest;                   /* depth of function calls */
 | 
|---|
| 93 | 
 | 
|---|
| 94 | 
 | 
|---|
| 95 | char *commandname;
 | 
|---|
| 96 | struct strlist *cmdenviron;
 | 
|---|
| 97 | int exitstatus;                 /* exit status of last command */
 | 
|---|
| 98 | int back_exitstatus;            /* exit status of backquoted command */
 | 
|---|
| 99 | 
 | 
|---|
| 100 | 
 | 
|---|
| 101 | STATIC void evalloop(union node *, int);
 | 
|---|
| 102 | STATIC void evalfor(union node *, int);
 | 
|---|
| 103 | STATIC void evalcase(union node *, int);
 | 
|---|
| 104 | STATIC void evalsubshell(union node *, int);
 | 
|---|
| 105 | STATIC void expredir(union node *);
 | 
|---|
| 106 | STATIC void evalpipe(union node *);
 | 
|---|
| 107 | STATIC void evalcommand(union node *, int, struct backcmd *);
 | 
|---|
| 108 | STATIC void prehash(union node *);
 | 
|---|
| 109 | 
 | 
|---|
| 110 | 
 | 
|---|
| 111 | /*
 | 
|---|
| 112 |  * Called to reset things after an exception.
 | 
|---|
| 113 |  */
 | 
|---|
| 114 | 
 | 
|---|
| 115 | #ifdef mkinit
 | 
|---|
| 116 | INCLUDE "eval.h"
 | 
|---|
| 117 | 
 | 
|---|
| 118 | RESET {
 | 
|---|
| 119 |         evalskip = 0;
 | 
|---|
| 120 |         loopnest = 0;
 | 
|---|
| 121 |         funcnest = 0;
 | 
|---|
| 122 | }
 | 
|---|
| 123 | 
 | 
|---|
| 124 | SHELLPROC {
 | 
|---|
| 125 |         exitstatus = 0;
 | 
|---|
| 126 | }
 | 
|---|
| 127 | #endif
 | 
|---|
| 128 | 
 | 
|---|
| 129 | static int
 | 
|---|
| 130 | sh_pipe(int fds[2])
 | 
|---|
| 131 | {
 | 
|---|
| 132 |         int nfd;
 | 
|---|
| 133 | 
 | 
|---|
| 134 |         if (pipe(fds))
 | 
|---|
| 135 |                 return -1;
 | 
|---|
| 136 | 
 | 
|---|
| 137 |         if (fds[0] < 3) {
 | 
|---|
| 138 |                 nfd = fcntl(fds[0], F_DUPFD, 3);
 | 
|---|
| 139 |                 if (nfd != -1) {
 | 
|---|
| 140 |                         close(fds[0]);
 | 
|---|
| 141 |                         fds[0] = nfd;
 | 
|---|
| 142 |                 }
 | 
|---|
| 143 |         }
 | 
|---|
| 144 | 
 | 
|---|
| 145 |         if (fds[1] < 3) {
 | 
|---|
| 146 |                 nfd = fcntl(fds[1], F_DUPFD, 3);
 | 
|---|
| 147 |                 if (nfd != -1) {
 | 
|---|
| 148 |                         close(fds[1]);
 | 
|---|
| 149 |                         fds[1] = nfd;
 | 
|---|
| 150 |                 }
 | 
|---|
| 151 |         }
 | 
|---|
| 152 |         return 0;
 | 
|---|
| 153 | }
 | 
|---|
| 154 | 
 | 
|---|
| 155 | 
 | 
|---|
| 156 | /*
 | 
|---|
| 157 |  * The eval commmand.
 | 
|---|
| 158 |  */
 | 
|---|
| 159 | 
 | 
|---|
| 160 | int
 | 
|---|
| 161 | evalcmd(int argc, char **argv)
 | 
|---|
| 162 | {
 | 
|---|
| 163 |         char *p;
 | 
|---|
| 164 |         char *concat;
 | 
|---|
| 165 |         char **ap;
 | 
|---|
| 166 | 
 | 
|---|
| 167 |         if (argc > 1) {
 | 
|---|
| 168 |                 p = argv[1];
 | 
|---|
| 169 |                 if (argc > 2) {
 | 
|---|
| 170 |                         STARTSTACKSTR(concat);
 | 
|---|
| 171 |                         ap = argv + 2;
 | 
|---|
| 172 |                         for (;;) {
 | 
|---|
| 173 |                                 while (*p)
 | 
|---|
| 174 |                                         STPUTC(*p++, concat);
 | 
|---|
| 175 |                                 if ((p = *ap++) == NULL)
 | 
|---|
| 176 |                                         break;
 | 
|---|
| 177 |                                 STPUTC(' ', concat);
 | 
|---|
| 178 |                         }
 | 
|---|
| 179 |                         STPUTC('\0', concat);
 | 
|---|
| 180 |                         p = grabstackstr(concat);
 | 
|---|
| 181 |                 }
 | 
|---|
| 182 |                 evalstring(p, EV_TESTED);
 | 
|---|
| 183 |         }
 | 
|---|
| 184 |         return exitstatus;
 | 
|---|
| 185 | }
 | 
|---|
| 186 | 
 | 
|---|
| 187 | 
 | 
|---|
| 188 | /*
 | 
|---|
| 189 |  * Execute a command or commands contained in a string.
 | 
|---|
| 190 |  */
 | 
|---|
| 191 | 
 | 
|---|
| 192 | void
 | 
|---|
| 193 | evalstring(char *s, int flag)
 | 
|---|
| 194 | {
 | 
|---|
| 195 |         union node *n;
 | 
|---|
| 196 |         struct stackmark smark;
 | 
|---|
| 197 | 
 | 
|---|
| 198 |         setstackmark(&smark);
 | 
|---|
| 199 |         setinputstring(s, 1);
 | 
|---|
| 200 | 
 | 
|---|
| 201 |         while ((n = parsecmd(0)) != NEOF) {
 | 
|---|
| 202 |                 evaltree(n, flag);
 | 
|---|
| 203 |                 popstackmark(&smark);
 | 
|---|
| 204 |         }
 | 
|---|
| 205 |         popfile();
 | 
|---|
| 206 |         popstackmark(&smark);
 | 
|---|
| 207 | }
 | 
|---|
| 208 | 
 | 
|---|
| 209 | 
 | 
|---|
| 210 | 
 | 
|---|
| 211 | /*
 | 
|---|
| 212 |  * Evaluate a parse tree.  The value is left in the global variable
 | 
|---|
| 213 |  * exitstatus.
 | 
|---|
| 214 |  */
 | 
|---|
| 215 | 
 | 
|---|
| 216 | void
 | 
|---|
| 217 | evaltree(union node *n, int flags)
 | 
|---|
| 218 | {
 | 
|---|
| 219 |         if (n == NULL) {
 | 
|---|
| 220 |                 TRACE(("evaltree(NULL) called\n"));
 | 
|---|
| 221 |                 exitstatus = 0;
 | 
|---|
| 222 |                 goto out;
 | 
|---|
| 223 |         }
 | 
|---|
| 224 | #ifndef SMALL
 | 
|---|
| 225 |         displayhist = 1;        /* show history substitutions done with fc */
 | 
|---|
| 226 | #endif
 | 
|---|
| 227 |         TRACE(("pid %d, evaltree(%p: %d, %d) called\n",
 | 
|---|
| 228 |             getpid(), n, n->type, flags));
 | 
|---|
| 229 |         switch (n->type) {
 | 
|---|
| 230 |         case NSEMI:
 | 
|---|
| 231 |                 evaltree(n->nbinary.ch1, flags & EV_TESTED);
 | 
|---|
| 232 |                 if (evalskip)
 | 
|---|
| 233 |                         goto out;
 | 
|---|
| 234 |                 evaltree(n->nbinary.ch2, flags);
 | 
|---|
| 235 |                 break;
 | 
|---|
| 236 |         case NAND:
 | 
|---|
| 237 |                 evaltree(n->nbinary.ch1, EV_TESTED);
 | 
|---|
| 238 |                 if (evalskip || exitstatus != 0)
 | 
|---|
| 239 |                         goto out;
 | 
|---|
| 240 |                 evaltree(n->nbinary.ch2, flags);
 | 
|---|
| 241 |                 break;
 | 
|---|
| 242 |         case NOR:
 | 
|---|
| 243 |                 evaltree(n->nbinary.ch1, EV_TESTED);
 | 
|---|
| 244 |                 if (evalskip || exitstatus == 0)
 | 
|---|
| 245 |                         goto out;
 | 
|---|
| 246 |                 evaltree(n->nbinary.ch2, flags);
 | 
|---|
| 247 |                 break;
 | 
|---|
| 248 |         case NREDIR:
 | 
|---|
| 249 |                 expredir(n->nredir.redirect);
 | 
|---|
| 250 |                 redirect(n->nredir.redirect, REDIR_PUSH);
 | 
|---|
| 251 |                 evaltree(n->nredir.n, flags);
 | 
|---|
| 252 |                 popredir();
 | 
|---|
| 253 |                 break;
 | 
|---|
| 254 |         case NSUBSHELL:
 | 
|---|
| 255 |                 evalsubshell(n, flags);
 | 
|---|
| 256 |                 break;
 | 
|---|
| 257 |         case NBACKGND:
 | 
|---|
| 258 |                 evalsubshell(n, flags);
 | 
|---|
| 259 |                 break;
 | 
|---|
| 260 |         case NIF: {
 | 
|---|
| 261 |                 evaltree(n->nif.test, EV_TESTED);
 | 
|---|
| 262 |                 if (evalskip)
 | 
|---|
| 263 |                         goto out;
 | 
|---|
| 264 |                 if (exitstatus == 0)
 | 
|---|
| 265 |                         evaltree(n->nif.ifpart, flags);
 | 
|---|
| 266 |                 else if (n->nif.elsepart)
 | 
|---|
| 267 |                         evaltree(n->nif.elsepart, flags);
 | 
|---|
| 268 |                 else
 | 
|---|
| 269 |                         exitstatus = 0;
 | 
|---|
| 270 |                 break;
 | 
|---|
| 271 |         }
 | 
|---|
| 272 |         case NWHILE:
 | 
|---|
| 273 |         case NUNTIL:
 | 
|---|
| 274 |                 evalloop(n, flags);
 | 
|---|
| 275 |                 break;
 | 
|---|
| 276 |         case NFOR:
 | 
|---|
| 277 |                 evalfor(n, flags);
 | 
|---|
| 278 |                 break;
 | 
|---|
| 279 |         case NCASE:
 | 
|---|
| 280 |                 evalcase(n, flags);
 | 
|---|
| 281 |                 break;
 | 
|---|
| 282 |         case NDEFUN:
 | 
|---|
| 283 |                 defun(n->narg.text, n->narg.next);
 | 
|---|
| 284 |                 exitstatus = 0;
 | 
|---|
| 285 |                 break;
 | 
|---|
| 286 |         case NNOT:
 | 
|---|
| 287 |                 evaltree(n->nnot.com, EV_TESTED);
 | 
|---|
| 288 |                 exitstatus = !exitstatus;
 | 
|---|
| 289 |                 break;
 | 
|---|
| 290 |         case NPIPE:
 | 
|---|
| 291 |                 evalpipe(n);
 | 
|---|
| 292 |                 break;
 | 
|---|
| 293 |         case NCMD:
 | 
|---|
| 294 |                 evalcommand(n, flags, (struct backcmd *)NULL);
 | 
|---|
| 295 |                 break;
 | 
|---|
| 296 |         default:
 | 
|---|
| 297 |                 out1fmt("Node type = %d\n", n->type);
 | 
|---|
| 298 |                 flushout(&output);
 | 
|---|
| 299 |                 break;
 | 
|---|
| 300 |         }
 | 
|---|
| 301 | out:
 | 
|---|
| 302 |         if (pendingsigs)
 | 
|---|
| 303 |                 dotrap();
 | 
|---|
| 304 |         if ((flags & EV_EXIT) != 0)
 | 
|---|
| 305 |                 exitshell(exitstatus);
 | 
|---|
| 306 | }
 | 
|---|
| 307 | 
 | 
|---|
| 308 | 
 | 
|---|
| 309 | STATIC void
 | 
|---|
| 310 | evalloop(union node *n, int flags)
 | 
|---|
| 311 | {
 | 
|---|
| 312 |         int status;
 | 
|---|
| 313 | 
 | 
|---|
| 314 |         loopnest++;
 | 
|---|
| 315 |         status = 0;
 | 
|---|
| 316 |         for (;;) {
 | 
|---|
| 317 |                 evaltree(n->nbinary.ch1, EV_TESTED);
 | 
|---|
| 318 |                 if (evalskip) {
 | 
|---|
| 319 | skipping:         if (evalskip == SKIPCONT && --skipcount <= 0) {
 | 
|---|
| 320 |                                 evalskip = 0;
 | 
|---|
| 321 |                                 continue;
 | 
|---|
| 322 |                         }
 | 
|---|
| 323 |                         if (evalskip == SKIPBREAK && --skipcount <= 0)
 | 
|---|
| 324 |                                 evalskip = 0;
 | 
|---|
| 325 |                         break;
 | 
|---|
| 326 |                 }
 | 
|---|
| 327 |                 if (n->type == NWHILE) {
 | 
|---|
| 328 |                         if (exitstatus != 0)
 | 
|---|
| 329 |                                 break;
 | 
|---|
| 330 |                 } else {
 | 
|---|
| 331 |                         if (exitstatus == 0)
 | 
|---|
| 332 |                                 break;
 | 
|---|
| 333 |                 }
 | 
|---|
| 334 |                 evaltree(n->nbinary.ch2, flags & EV_TESTED);
 | 
|---|
| 335 |                 status = exitstatus;
 | 
|---|
| 336 |                 if (evalskip)
 | 
|---|
| 337 |                         goto skipping;
 | 
|---|
| 338 |         }
 | 
|---|
| 339 |         loopnest--;
 | 
|---|
| 340 |         exitstatus = status;
 | 
|---|
| 341 | }
 | 
|---|
| 342 | 
 | 
|---|
| 343 | 
 | 
|---|
| 344 | 
 | 
|---|
| 345 | STATIC void
 | 
|---|
| 346 | evalfor(union node *n, int flags)
 | 
|---|
| 347 | {
 | 
|---|
| 348 |         struct arglist arglist;
 | 
|---|
| 349 |         union node *argp;
 | 
|---|
| 350 |         struct strlist *sp;
 | 
|---|
| 351 |         struct stackmark smark;
 | 
|---|
| 352 |         int status = 0;
 | 
|---|
| 353 | 
 | 
|---|
| 354 |         setstackmark(&smark);
 | 
|---|
| 355 |         arglist.lastp = &arglist.list;
 | 
|---|
| 356 |         for (argp = n->nfor.args ; argp ; argp = argp->narg.next) {
 | 
|---|
| 357 |                 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE);
 | 
|---|
| 358 |                 if (evalskip)
 | 
|---|
| 359 |                         goto out;
 | 
|---|
| 360 |         }
 | 
|---|
| 361 |         *arglist.lastp = NULL;
 | 
|---|
| 362 | 
 | 
|---|
| 363 |         loopnest++;
 | 
|---|
| 364 |         for (sp = arglist.list ; sp ; sp = sp->next) {
 | 
|---|
| 365 |                 setvar(n->nfor.var, sp->text, 0);
 | 
|---|
| 366 |                 evaltree(n->nfor.body, flags & EV_TESTED);
 | 
|---|
| 367 |                 status = exitstatus;
 | 
|---|
| 368 |                 if (evalskip) {
 | 
|---|
| 369 |                         if (evalskip == SKIPCONT && --skipcount <= 0) {
 | 
|---|
| 370 |                                 evalskip = 0;
 | 
|---|
| 371 |                                 continue;
 | 
|---|
| 372 |                         }
 | 
|---|
| 373 |                         if (evalskip == SKIPBREAK && --skipcount <= 0)
 | 
|---|
| 374 |                                 evalskip = 0;
 | 
|---|
| 375 |                         break;
 | 
|---|
| 376 |                 }
 | 
|---|
| 377 |         }
 | 
|---|
| 378 |         loopnest--;
 | 
|---|
| 379 |         exitstatus = status;
 | 
|---|
| 380 | out:
 | 
|---|
| 381 |         popstackmark(&smark);
 | 
|---|
| 382 | }
 | 
|---|
| 383 | 
 | 
|---|
| 384 | 
 | 
|---|
| 385 | 
 | 
|---|
| 386 | STATIC void
 | 
|---|
| 387 | evalcase(union node *n, int flags)
 | 
|---|
| 388 | {
 | 
|---|
| 389 |         union node *cp;
 | 
|---|
| 390 |         union node *patp;
 | 
|---|
| 391 |         struct arglist arglist;
 | 
|---|
| 392 |         struct stackmark smark;
 | 
|---|
| 393 |         int status = 0;
 | 
|---|
| 394 | 
 | 
|---|
| 395 |         setstackmark(&smark);
 | 
|---|
| 396 |         arglist.lastp = &arglist.list;
 | 
|---|
| 397 |         expandarg(n->ncase.expr, &arglist, EXP_TILDE);
 | 
|---|
| 398 |         for (cp = n->ncase.cases ; cp && evalskip == 0 ; cp = cp->nclist.next) {
 | 
|---|
| 399 |                 for (patp = cp->nclist.pattern ; patp ; patp = patp->narg.next) {
 | 
|---|
| 400 |                         if (casematch(patp, arglist.list->text)) {
 | 
|---|
| 401 |                                 if (evalskip == 0) {
 | 
|---|
| 402 |                                         evaltree(cp->nclist.body, flags);
 | 
|---|
| 403 |                                         status = exitstatus;
 | 
|---|
| 404 |                                 }
 | 
|---|
| 405 |                                 goto out;
 | 
|---|
| 406 |                         }
 | 
|---|
| 407 |                 }
 | 
|---|
| 408 |         }
 | 
|---|
| 409 | out:
 | 
|---|
| 410 |         exitstatus = status;
 | 
|---|
| 411 |         popstackmark(&smark);
 | 
|---|
| 412 | }
 | 
|---|
| 413 | 
 | 
|---|
| 414 | 
 | 
|---|
| 415 | 
 | 
|---|
| 416 | /*
 | 
|---|
| 417 |  * Kick off a subshell to evaluate a tree.
 | 
|---|
| 418 |  */
 | 
|---|
| 419 | 
 | 
|---|
| 420 | STATIC void
 | 
|---|
| 421 | evalsubshell(union node *n, int flags)
 | 
|---|
| 422 | {
 | 
|---|
| 423 |         struct job *jp;
 | 
|---|
| 424 |         int backgnd = (n->type == NBACKGND);
 | 
|---|
| 425 | 
 | 
|---|
| 426 |         expredir(n->nredir.redirect);
 | 
|---|
| 427 |         INTOFF;
 | 
|---|
| 428 |         jp = makejob(n, 1);
 | 
|---|
| 429 |         if (forkshell(jp, n, backgnd ? FORK_BG : FORK_FG) == 0) {
 | 
|---|
| 430 |                 INTON;
 | 
|---|
| 431 |                 if (backgnd)
 | 
|---|
| 432 |                         flags &=~ EV_TESTED;
 | 
|---|
| 433 |                 redirect(n->nredir.redirect, 0);
 | 
|---|
| 434 |                 /* never returns */
 | 
|---|
| 435 |                 evaltree(n->nredir.n, flags | EV_EXIT);
 | 
|---|
| 436 |         }
 | 
|---|
| 437 |         if (! backgnd)
 | 
|---|
| 438 |                 exitstatus = waitforjob(jp);
 | 
|---|
| 439 |         INTON;
 | 
|---|
| 440 | }
 | 
|---|
| 441 | 
 | 
|---|
| 442 | 
 | 
|---|
| 443 | 
 | 
|---|
| 444 | /*
 | 
|---|
| 445 |  * Compute the names of the files in a redirection list.
 | 
|---|
| 446 |  */
 | 
|---|
| 447 | 
 | 
|---|
| 448 | STATIC void
 | 
|---|
| 449 | expredir(union node *n)
 | 
|---|
| 450 | {
 | 
|---|
| 451 |         union node *redir;
 | 
|---|
| 452 | 
 | 
|---|
| 453 |         for (redir = n ; redir ; redir = redir->nfile.next) {
 | 
|---|
| 454 |                 struct arglist fn;
 | 
|---|
| 455 |                 fn.lastp = &fn.list;
 | 
|---|
| 456 |                 switch (redir->type) {
 | 
|---|
| 457 |                 case NFROMTO:
 | 
|---|
| 458 |                 case NFROM:
 | 
|---|
| 459 |                 case NTO:
 | 
|---|
| 460 |                 case NCLOBBER:
 | 
|---|
| 461 |                 case NAPPEND:
 | 
|---|
| 462 |                         expandarg(redir->nfile.fname, &fn, EXP_TILDE | EXP_REDIR);
 | 
|---|
| 463 |                         redir->nfile.expfname = fn.list->text;
 | 
|---|
| 464 |                         break;
 | 
|---|
| 465 |                 case NFROMFD:
 | 
|---|
| 466 |                 case NTOFD:
 | 
|---|
| 467 |                         if (redir->ndup.vname) {
 | 
|---|
| 468 |                                 expandarg(redir->ndup.vname, &fn, EXP_FULL | EXP_TILDE);
 | 
|---|
| 469 |                                 fixredir(redir, fn.list->text, 1);
 | 
|---|
| 470 |                         }
 | 
|---|
| 471 |                         break;
 | 
|---|
| 472 |                 }
 | 
|---|
| 473 |         }
 | 
|---|
| 474 | }
 | 
|---|
| 475 | 
 | 
|---|
| 476 | 
 | 
|---|
| 477 | 
 | 
|---|
| 478 | /*
 | 
|---|
| 479 |  * Evaluate a pipeline.  All the processes in the pipeline are children
 | 
|---|
| 480 |  * of the process creating the pipeline.  (This differs from some versions
 | 
|---|
| 481 |  * of the shell, which make the last process in a pipeline the parent
 | 
|---|
| 482 |  * of all the rest.)
 | 
|---|
| 483 |  */
 | 
|---|
| 484 | 
 | 
|---|
| 485 | STATIC void
 | 
|---|
| 486 | evalpipe(union node *n)
 | 
|---|
| 487 | {
 | 
|---|
| 488 |         struct job *jp;
 | 
|---|
| 489 |         struct nodelist *lp;
 | 
|---|
| 490 |         int pipelen;
 | 
|---|
| 491 |         int prevfd;
 | 
|---|
| 492 |         int pip[2];
 | 
|---|
| 493 | 
 | 
|---|
| 494 |         TRACE(("evalpipe(0x%lx) called\n", (long)n));
 | 
|---|
| 495 |         pipelen = 0;
 | 
|---|
| 496 |         for (lp = n->npipe.cmdlist ; lp ; lp = lp->next)
 | 
|---|
| 497 |                 pipelen++;
 | 
|---|
| 498 |         INTOFF;
 | 
|---|
| 499 |         jp = makejob(n, pipelen);
 | 
|---|
| 500 |         prevfd = -1;
 | 
|---|
| 501 |         for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) {
 | 
|---|
| 502 |                 prehash(lp->n);
 | 
|---|
| 503 |                 pip[1] = -1;
 | 
|---|
| 504 |                 if (lp->next) {
 | 
|---|
| 505 |                         if (sh_pipe(pip) < 0) {
 | 
|---|
| 506 |                                 close(prevfd);
 | 
|---|
| 507 |                                 error("Pipe call failed");
 | 
|---|
| 508 |                         }
 | 
|---|
| 509 |                 }
 | 
|---|
| 510 |                 if (forkshell(jp, lp->n, n->npipe.backgnd ? FORK_BG : FORK_FG) == 0) {
 | 
|---|
| 511 |                         INTON;
 | 
|---|
| 512 |                         if (prevfd > 0) {
 | 
|---|
| 513 |                                 close(0);
 | 
|---|
| 514 |                                 copyfd(prevfd, 0);
 | 
|---|
| 515 |                                 close(prevfd);
 | 
|---|
| 516 |                         }
 | 
|---|
| 517 |                         if (pip[1] >= 0) {
 | 
|---|
| 518 |                                 close(pip[0]);
 | 
|---|
| 519 |                                 if (pip[1] != 1) {
 | 
|---|
| 520 |                                         close(1);
 | 
|---|
| 521 |                                         copyfd(pip[1], 1);
 | 
|---|
| 522 |                                         close(pip[1]);
 | 
|---|
| 523 |                                 }
 | 
|---|
| 524 |                         }
 | 
|---|
| 525 |                         evaltree(lp->n, EV_EXIT);
 | 
|---|
| 526 |                 }
 | 
|---|
| 527 |                 if (prevfd >= 0)
 | 
|---|
| 528 |                         close(prevfd);
 | 
|---|
| 529 |                 prevfd = pip[0];
 | 
|---|
| 530 |                 close(pip[1]);
 | 
|---|
| 531 |         }
 | 
|---|
| 532 |         if (n->npipe.backgnd == 0) {
 | 
|---|
| 533 |                 exitstatus = waitforjob(jp);
 | 
|---|
| 534 |                 TRACE(("evalpipe:  job done exit status %d\n", exitstatus));
 | 
|---|
| 535 |         }
 | 
|---|
| 536 |         INTON;
 | 
|---|
| 537 | }
 | 
|---|
| 538 | 
 | 
|---|
| 539 | 
 | 
|---|
| 540 | 
 | 
|---|
| 541 | /*
 | 
|---|
| 542 |  * Execute a command inside back quotes.  If it's a builtin command, we
 | 
|---|
| 543 |  * want to save its output in a block obtained from malloc.  Otherwise
 | 
|---|
| 544 |  * we fork off a subprocess and get the output of the command via a pipe.
 | 
|---|
| 545 |  * Should be called with interrupts off.
 | 
|---|
| 546 |  */
 | 
|---|
| 547 | 
 | 
|---|
| 548 | void
 | 
|---|
| 549 | evalbackcmd(union node *n, struct backcmd *result)
 | 
|---|
| 550 | {
 | 
|---|
| 551 |         int pip[2];
 | 
|---|
| 552 |         struct job *jp;
 | 
|---|
| 553 |         struct stackmark smark;         /* unnecessary */
 | 
|---|
| 554 | 
 | 
|---|
| 555 |         setstackmark(&smark);
 | 
|---|
| 556 |         result->fd = -1;
 | 
|---|
| 557 |         result->buf = NULL;
 | 
|---|
| 558 |         result->nleft = 0;
 | 
|---|
| 559 |         result->jp = NULL;
 | 
|---|
| 560 |         if (n == NULL) {
 | 
|---|
| 561 |                 goto out;
 | 
|---|
| 562 |         }
 | 
|---|
| 563 | #ifdef notyet
 | 
|---|
| 564 |         /*
 | 
|---|
| 565 |          * For now we disable executing builtins in the same
 | 
|---|
| 566 |          * context as the shell, because we are not keeping
 | 
|---|
| 567 |          * enough state to recover from changes that are
 | 
|---|
| 568 |          * supposed only to affect subshells. eg. echo "`cd /`"
 | 
|---|
| 569 |          */
 | 
|---|
| 570 |         if (n->type == NCMD) {
 | 
|---|
| 571 |                 exitstatus = oexitstatus;
 | 
|---|
| 572 |                 evalcommand(n, EV_BACKCMD, result);
 | 
|---|
| 573 |         } else
 | 
|---|
| 574 | #endif
 | 
|---|
| 575 |         {
 | 
|---|
| 576 |                 INTOFF;
 | 
|---|
| 577 |                 if (sh_pipe(pip) < 0)
 | 
|---|
| 578 |                         error("Pipe call failed");
 | 
|---|
| 579 |                 jp = makejob(n, 1);
 | 
|---|
| 580 |                 if (forkshell(jp, n, FORK_NOJOB) == 0) {
 | 
|---|
| 581 |                         FORCEINTON;
 | 
|---|
| 582 |                         close(pip[0]);
 | 
|---|
| 583 |                         if (pip[1] != 1) {
 | 
|---|
| 584 |                                 close(1);
 | 
|---|
| 585 |                                 copyfd(pip[1], 1);
 | 
|---|
| 586 |                                 close(pip[1]);
 | 
|---|
| 587 |                         }
 | 
|---|
| 588 |                         eflag = 0;
 | 
|---|
| 589 |                         evaltree(n, EV_EXIT);
 | 
|---|
| 590 |                         /* NOTREACHED */
 | 
|---|
| 591 |                 }
 | 
|---|
| 592 |                 close(pip[1]);
 | 
|---|
| 593 |                 result->fd = pip[0];
 | 
|---|
| 594 |                 result->jp = jp;
 | 
|---|
| 595 |                 INTON;
 | 
|---|
| 596 |         }
 | 
|---|
| 597 | out:
 | 
|---|
| 598 |         popstackmark(&smark);
 | 
|---|
| 599 |         TRACE(("evalbackcmd done: fd=%d buf=0x%x nleft=%d jp=0x%x\n",
 | 
|---|
| 600 |                 result->fd, result->buf, result->nleft, result->jp));
 | 
|---|
| 601 | }
 | 
|---|
| 602 | 
 | 
|---|
| 603 | static const char *
 | 
|---|
| 604 | syspath(void)
 | 
|---|
| 605 | {
 | 
|---|
| 606 |         static char *sys_path = NULL;
 | 
|---|
| 607 |         static int mib[] = {CTL_USER, USER_CS_PATH};
 | 
|---|
| 608 | #ifdef PC_PATH_SEP
 | 
|---|
| 609 |         static char def_path[] = "PATH=/usr/bin;/bin;/usr/sbin;/sbin";
 | 
|---|
| 610 | #else
 | 
|---|
| 611 |         static char def_path[] = "PATH=/usr/bin:/bin:/usr/sbin:/sbin";
 | 
|---|
| 612 | #endif
 | 
|---|
| 613 |         size_t len;
 | 
|---|
| 614 | 
 | 
|---|
| 615 |         if (sys_path == NULL) {
 | 
|---|
| 616 |                 if (sysctl(mib, 2, 0, &len, 0, 0) != -1 &&
 | 
|---|
| 617 |                     (sys_path = ckmalloc(len + 5)) != NULL &&
 | 
|---|
| 618 |                     sysctl(mib, 2, sys_path + 5, &len, 0, 0) != -1) {
 | 
|---|
| 619 |                         memcpy(sys_path, "PATH=", 5);
 | 
|---|
| 620 |                 } else {
 | 
|---|
| 621 |                         ckfree(sys_path);
 | 
|---|
| 622 |                         /* something to keep things happy */
 | 
|---|
| 623 |                         sys_path = def_path;
 | 
|---|
| 624 |                 }
 | 
|---|
| 625 |         }
 | 
|---|
| 626 |         return sys_path;
 | 
|---|
| 627 | }
 | 
|---|
| 628 | 
 | 
|---|
| 629 | static int
 | 
|---|
| 630 | parse_command_args(int argc, char **argv, int *use_syspath)
 | 
|---|
| 631 | {
 | 
|---|
| 632 |         int sv_argc = argc;
 | 
|---|
| 633 |         char *cp, c;
 | 
|---|
| 634 | 
 | 
|---|
| 635 |         *use_syspath = 0;
 | 
|---|
| 636 | 
 | 
|---|
| 637 |         for (;;) {
 | 
|---|
| 638 |                 argv++;
 | 
|---|
| 639 |                 if (--argc == 0)
 | 
|---|
| 640 |                         break;
 | 
|---|
| 641 |                 cp = *argv;
 | 
|---|
| 642 |                 if (*cp++ != '-')
 | 
|---|
| 643 |                         break;
 | 
|---|
| 644 |                 if (*cp == '-' && cp[1] == 0) {
 | 
|---|
| 645 |                         argv++;
 | 
|---|
| 646 |                         argc--;
 | 
|---|
| 647 |                         break;
 | 
|---|
| 648 |                 }
 | 
|---|
| 649 |                 while ((c = *cp++)) {
 | 
|---|
| 650 |                         switch (c) {
 | 
|---|
| 651 |                         case 'p':
 | 
|---|
| 652 |                                 *use_syspath = 1;
 | 
|---|
| 653 |                                 break;
 | 
|---|
| 654 |                         default:
 | 
|---|
| 655 |                                 /* run 'typecmd' for other options */
 | 
|---|
| 656 |                                 return 0;
 | 
|---|
| 657 |                         }
 | 
|---|
| 658 |                 }
 | 
|---|
| 659 |         }
 | 
|---|
| 660 |         return sv_argc - argc;
 | 
|---|
| 661 | }
 | 
|---|
| 662 | 
 | 
|---|
| 663 | int vforked = 0;
 | 
|---|
| 664 | 
 | 
|---|
| 665 | /*
 | 
|---|
| 666 |  * Execute a simple command.
 | 
|---|
| 667 |  */
 | 
|---|
| 668 | 
 | 
|---|
| 669 | STATIC void
 | 
|---|
| 670 | evalcommand(union node *cmd, int flags, struct backcmd *backcmd)
 | 
|---|
| 671 | {
 | 
|---|
| 672 |         struct stackmark smark;
 | 
|---|
| 673 |         union node *argp;
 | 
|---|
| 674 |         struct arglist arglist;
 | 
|---|
| 675 |         struct arglist varlist;
 | 
|---|
| 676 |         char **argv;
 | 
|---|
| 677 |         int argc;
 | 
|---|
| 678 |         char **envp;
 | 
|---|
| 679 |         int varflag;
 | 
|---|
| 680 |         struct strlist *sp;
 | 
|---|
| 681 |         int mode;
 | 
|---|
| 682 |         int pip[2];
 | 
|---|
| 683 |         struct cmdentry cmdentry;
 | 
|---|
| 684 |         struct job *jp;
 | 
|---|
| 685 |         struct jmploc jmploc;
 | 
|---|
| 686 |         struct jmploc *volatile savehandler;
 | 
|---|
| 687 |         char *volatile savecmdname;
 | 
|---|
| 688 |         volatile struct shparam saveparam;
 | 
|---|
| 689 |         struct localvar *volatile savelocalvars;
 | 
|---|
| 690 |         volatile int e;
 | 
|---|
| 691 |         char *lastarg;
 | 
|---|
| 692 |         const char *path = pathval();
 | 
|---|
| 693 |         volatile int temp_path;
 | 
|---|
| 694 | #if __GNUC__
 | 
|---|
| 695 |         /* Avoid longjmp clobbering */
 | 
|---|
| 696 |         (void) &argv;
 | 
|---|
| 697 |         (void) &argc;
 | 
|---|
| 698 |         (void) &lastarg;
 | 
|---|
| 699 |         (void) &flags;
 | 
|---|
| 700 | #endif
 | 
|---|
| 701 | 
 | 
|---|
| 702 |         vforked = 0;
 | 
|---|
| 703 |         /* First expand the arguments. */
 | 
|---|
| 704 |         TRACE(("evalcommand(0x%lx, %d) called\n", (long)cmd, flags));
 | 
|---|
| 705 |         setstackmark(&smark);
 | 
|---|
| 706 |         back_exitstatus = 0;
 | 
|---|
| 707 | 
 | 
|---|
| 708 |         arglist.lastp = &arglist.list;
 | 
|---|
| 709 |         varflag = 1;
 | 
|---|
| 710 |         /* Expand arguments, ignoring the initial 'name=value' ones */
 | 
|---|
| 711 |         for (argp = cmd->ncmd.args ; argp ; argp = argp->narg.next) {
 | 
|---|
| 712 |                 char *p = argp->narg.text;
 | 
|---|
| 713 |                 if (varflag && is_name(*p)) {
 | 
|---|
| 714 |                         do {
 | 
|---|
| 715 |                                 p++;
 | 
|---|
| 716 |                         } while (is_in_name(*p));
 | 
|---|
| 717 |                         if (*p == '=')
 | 
|---|
| 718 |                                 continue;
 | 
|---|
| 719 |                 }
 | 
|---|
| 720 |                 expandarg(argp, &arglist, EXP_FULL | EXP_TILDE);
 | 
|---|
| 721 |                 varflag = 0;
 | 
|---|
| 722 |         }
 | 
|---|
| 723 |         *arglist.lastp = NULL;
 | 
|---|
| 724 | 
 | 
|---|
| 725 |         expredir(cmd->ncmd.redirect);
 | 
|---|
| 726 | 
 | 
|---|
| 727 |         /* Now do the initial 'name=value' ones we skipped above */
 | 
|---|
| 728 |         varlist.lastp = &varlist.list;
 | 
|---|
| 729 |         for (argp = cmd->ncmd.args ; argp ; argp = argp->narg.next) {
 | 
|---|
| 730 |                 char *p = argp->narg.text;
 | 
|---|
| 731 |                 if (!is_name(*p))
 | 
|---|
| 732 |                         break;
 | 
|---|
| 733 |                 do
 | 
|---|
| 734 |                         p++;
 | 
|---|
| 735 |                 while (is_in_name(*p));
 | 
|---|
| 736 |                 if (*p != '=')
 | 
|---|
| 737 |                         break;
 | 
|---|
| 738 |                 expandarg(argp, &varlist, EXP_VARTILDE);
 | 
|---|
| 739 |         }
 | 
|---|
| 740 |         *varlist.lastp = NULL;
 | 
|---|
| 741 | 
 | 
|---|
| 742 |         argc = 0;
 | 
|---|
| 743 |         for (sp = arglist.list ; sp ; sp = sp->next)
 | 
|---|
| 744 |                 argc++;
 | 
|---|
| 745 |         argv = stalloc(sizeof (char *) * (argc + 1));
 | 
|---|
| 746 | 
 | 
|---|
| 747 |         for (sp = arglist.list ; sp ; sp = sp->next) {
 | 
|---|
| 748 |                 TRACE(("evalcommand arg: %s\n", sp->text));
 | 
|---|
| 749 |                 *argv++ = sp->text;
 | 
|---|
| 750 |         }
 | 
|---|
| 751 |         *argv = NULL;
 | 
|---|
| 752 |         lastarg = NULL;
 | 
|---|
| 753 |         if (iflag && funcnest == 0 && argc > 0)
 | 
|---|
| 754 |                 lastarg = argv[-1];
 | 
|---|
| 755 |         argv -= argc;
 | 
|---|
| 756 | 
 | 
|---|
| 757 |         /* Print the command if xflag is set. */
 | 
|---|
| 758 |         if (xflag) {
 | 
|---|
| 759 |                 char sep = 0;
 | 
|---|
| 760 |                 out2str(ps4val());
 | 
|---|
| 761 |                 for (sp = varlist.list ; sp ; sp = sp->next) {
 | 
|---|
| 762 |                         if (sep != 0)
 | 
|---|
| 763 |                                 outc(sep, &errout);
 | 
|---|
| 764 |                         out2str(sp->text);
 | 
|---|
| 765 |                         sep = ' ';
 | 
|---|
| 766 |                 }
 | 
|---|
| 767 |                 for (sp = arglist.list ; sp ; sp = sp->next) {
 | 
|---|
| 768 |                         if (sep != 0)
 | 
|---|
| 769 |                                 outc(sep, &errout);
 | 
|---|
| 770 |                         out2str(sp->text);
 | 
|---|
| 771 |                         sep = ' ';
 | 
|---|
| 772 |                 }
 | 
|---|
| 773 |                 outc('\n', &errout);
 | 
|---|
| 774 |                 flushout(&errout);
 | 
|---|
| 775 |         }
 | 
|---|
| 776 | 
 | 
|---|
| 777 |         /* Now locate the command. */
 | 
|---|
| 778 |         if (argc == 0) {
 | 
|---|
| 779 |                 cmdentry.cmdtype = CMDSPLBLTIN;
 | 
|---|
| 780 |                 cmdentry.u.bltin = bltincmd;
 | 
|---|
| 781 |         } else {
 | 
|---|
| 782 |                 static const char PATH[] = "PATH=";
 | 
|---|
| 783 |                 int cmd_flags = DO_ERR;
 | 
|---|
| 784 | 
 | 
|---|
| 785 |                 /*
 | 
|---|
| 786 |                  * Modify the command lookup path, if a PATH= assignment
 | 
|---|
| 787 |                  * is present
 | 
|---|
| 788 |                  */
 | 
|---|
| 789 |                 for (sp = varlist.list; sp; sp = sp->next)
 | 
|---|
| 790 |                         if (strncmp(sp->text, PATH, sizeof(PATH) - 1) == 0)
 | 
|---|
| 791 |                                 path = sp->text + sizeof(PATH) - 1;
 | 
|---|
| 792 | 
 | 
|---|
| 793 |                 do {
 | 
|---|
| 794 |                         int argsused, use_syspath;
 | 
|---|
| 795 |                         find_command(argv[0], &cmdentry, cmd_flags, path);
 | 
|---|
| 796 |                         if (cmdentry.cmdtype == CMDUNKNOWN) {
 | 
|---|
| 797 |                                 exitstatus = 127;
 | 
|---|
| 798 |                                 flushout(&errout);
 | 
|---|
| 799 |                                 goto out;
 | 
|---|
| 800 |                         }
 | 
|---|
| 801 | 
 | 
|---|
| 802 |                         /* implement the 'command' builtin here */
 | 
|---|
| 803 |                         if (cmdentry.cmdtype != CMDBUILTIN ||
 | 
|---|
| 804 |                             cmdentry.u.bltin != bltincmd)
 | 
|---|
| 805 |                                 break;
 | 
|---|
| 806 |                         cmd_flags |= DO_NOFUNC;
 | 
|---|
| 807 |                         argsused = parse_command_args(argc, argv, &use_syspath);
 | 
|---|
| 808 |                         if (argsused == 0) {
 | 
|---|
| 809 |                                 /* use 'type' builting to display info */
 | 
|---|
| 810 |                                 cmdentry.u.bltin = typecmd;
 | 
|---|
| 811 |                                 break;
 | 
|---|
| 812 |                         }
 | 
|---|
| 813 |                         argc -= argsused;
 | 
|---|
| 814 |                         argv += argsused;
 | 
|---|
| 815 |                         if (use_syspath)
 | 
|---|
| 816 |                                 path = syspath() + 5;
 | 
|---|
| 817 |                 } while (argc != 0);
 | 
|---|
| 818 |                 if (cmdentry.cmdtype == CMDSPLBLTIN && cmd_flags & DO_NOFUNC)
 | 
|---|
| 819 |                         /* posix mandates that 'command <splbltin>' act as if
 | 
|---|
| 820 |                            <splbltin> was a normal builtin */
 | 
|---|
| 821 |                         cmdentry.cmdtype = CMDBUILTIN;
 | 
|---|
| 822 |         }
 | 
|---|
| 823 | 
 | 
|---|
| 824 |         /* Fork off a child process if necessary. */
 | 
|---|
| 825 |         if (cmd->ncmd.backgnd
 | 
|---|
| 826 |          || (cmdentry.cmdtype == CMDNORMAL && (flags & EV_EXIT) == 0)
 | 
|---|
| 827 |          || ((flags & EV_BACKCMD) != 0
 | 
|---|
| 828 |             && ((cmdentry.cmdtype != CMDBUILTIN && cmdentry.cmdtype != CMDSPLBLTIN)
 | 
|---|
| 829 |                  || cmdentry.u.bltin == dotcmd
 | 
|---|
| 830 |                  || cmdentry.u.bltin == evalcmd))) {
 | 
|---|
| 831 |                 INTOFF;
 | 
|---|
| 832 |                 jp = makejob(cmd, 1);
 | 
|---|
| 833 |                 mode = cmd->ncmd.backgnd;
 | 
|---|
| 834 |                 if (flags & EV_BACKCMD) {
 | 
|---|
| 835 |                         mode = FORK_NOJOB;
 | 
|---|
| 836 |                         if (sh_pipe(pip) < 0)
 | 
|---|
| 837 |                                 error("Pipe call failed");
 | 
|---|
| 838 |                 }
 | 
|---|
| 839 | #ifdef DO_SHAREDVFORK
 | 
|---|
| 840 |                 /* It is essential that if DO_SHAREDVFORK is defined that the
 | 
|---|
| 841 |                  * child's address space is actually shared with the parent as
 | 
|---|
| 842 |                  * we rely on this.
 | 
|---|
| 843 |                  */
 | 
|---|
| 844 |                 if (cmdentry.cmdtype == CMDNORMAL) {
 | 
|---|
| 845 |                         pid_t   pid;
 | 
|---|
| 846 | 
 | 
|---|
| 847 |                         savelocalvars = localvars;
 | 
|---|
| 848 |                         localvars = NULL;
 | 
|---|
| 849 |                         vforked = 1;
 | 
|---|
| 850 |                         switch (pid = vfork()) {
 | 
|---|
| 851 |                         case -1:
 | 
|---|
| 852 |                                 TRACE(("Vfork failed, errno=%d\n", errno));
 | 
|---|
| 853 |                                 INTON;
 | 
|---|
| 854 |                                 error("Cannot vfork");
 | 
|---|
| 855 |                                 break;
 | 
|---|
| 856 |                         case 0:
 | 
|---|
| 857 |                                 /* Make sure that exceptions only unwind to
 | 
|---|
| 858 |                                  * after the vfork(2)
 | 
|---|
| 859 |                                  */
 | 
|---|
| 860 |                                 if (setjmp(jmploc.loc)) {
 | 
|---|
| 861 |                                         if (exception == EXSHELLPROC) {
 | 
|---|
| 862 |                                                 /* We can't progress with the vfork,
 | 
|---|
| 863 |                                                  * so, set vforked = 2 so the parent
 | 
|---|
| 864 |                                                  * knows, and _exit();
 | 
|---|
| 865 |                                                  */
 | 
|---|
| 866 |                                                 vforked = 2;
 | 
|---|
| 867 |                                                 _exit(0);
 | 
|---|
| 868 |                                         } else {
 | 
|---|
| 869 |                                                 _exit(exerrno);
 | 
|---|
| 870 |                                         }
 | 
|---|
| 871 |                                 }
 | 
|---|
| 872 |                                 savehandler = handler;
 | 
|---|
| 873 |                                 handler = &jmploc;
 | 
|---|
| 874 |                                 listmklocal(varlist.list, VEXPORT | VNOFUNC);
 | 
|---|
| 875 |                                 forkchild(jp, cmd, mode, vforked);
 | 
|---|
| 876 |                                 break;
 | 
|---|
| 877 |                         default:
 | 
|---|
| 878 |                                 handler = savehandler;  /* restore from vfork(2) */
 | 
|---|
| 879 |                                 poplocalvars();
 | 
|---|
| 880 |                                 localvars = savelocalvars;
 | 
|---|
| 881 |                                 if (vforked == 2) {
 | 
|---|
| 882 |                                         vforked = 0;
 | 
|---|
| 883 | 
 | 
|---|
| 884 |                                         (void)waitpid(pid, NULL, 0);
 | 
|---|
| 885 |                                         /* We need to progress in a normal fork fashion */
 | 
|---|
| 886 |                                         goto normal_fork;
 | 
|---|
| 887 |                                 }
 | 
|---|
| 888 |                                 vforked = 0;
 | 
|---|
| 889 |                                 forkparent(jp, cmd, mode, pid);
 | 
|---|
| 890 |                                 goto parent;
 | 
|---|
| 891 |                         }
 | 
|---|
| 892 |                 } else {
 | 
|---|
| 893 | normal_fork:
 | 
|---|
| 894 | #endif
 | 
|---|
| 895 |                         if (forkshell(jp, cmd, mode) != 0)
 | 
|---|
| 896 |                                 goto parent;    /* at end of routine */
 | 
|---|
| 897 |                         FORCEINTON;
 | 
|---|
| 898 | #ifdef DO_SHAREDVFORK
 | 
|---|
| 899 |                 }
 | 
|---|
| 900 | #endif
 | 
|---|
| 901 |                 if (flags & EV_BACKCMD) {
 | 
|---|
| 902 |                         if (!vforked) {
 | 
|---|
| 903 |                                 FORCEINTON;
 | 
|---|
| 904 |                         }
 | 
|---|
| 905 |                         close(pip[0]);
 | 
|---|
| 906 |                         if (pip[1] != 1) {
 | 
|---|
| 907 |                                 close(1);
 | 
|---|
| 908 |                                 copyfd(pip[1], 1);
 | 
|---|
| 909 |                                 close(pip[1]);
 | 
|---|
| 910 |                         }
 | 
|---|
| 911 |                 }
 | 
|---|
| 912 |                 flags |= EV_EXIT;
 | 
|---|
| 913 |         }
 | 
|---|
| 914 | 
 | 
|---|
| 915 |         /* This is the child process if a fork occurred. */
 | 
|---|
| 916 |         /* Execute the command. */
 | 
|---|
| 917 |         switch (cmdentry.cmdtype) {
 | 
|---|
| 918 |         case CMDFUNCTION:
 | 
|---|
| 919 | #ifdef DEBUG
 | 
|---|
| 920 |                 trputs("Shell function:  ");  trargs(argv);
 | 
|---|
| 921 | #endif
 | 
|---|
| 922 |                 redirect(cmd->ncmd.redirect, REDIR_PUSH);
 | 
|---|
| 923 |                 saveparam = shellparam;
 | 
|---|
| 924 |                 shellparam.malloc = 0;
 | 
|---|
| 925 |                 shellparam.reset = 1;
 | 
|---|
| 926 |                 shellparam.nparam = argc - 1;
 | 
|---|
| 927 |                 shellparam.p = argv + 1;
 | 
|---|
| 928 |                 shellparam.optnext = NULL;
 | 
|---|
| 929 |                 INTOFF;
 | 
|---|
| 930 |                 savelocalvars = localvars;
 | 
|---|
| 931 |                 localvars = NULL;
 | 
|---|
| 932 |                 INTON;
 | 
|---|
| 933 |                 if (setjmp(jmploc.loc)) {
 | 
|---|
| 934 |                         if (exception == EXSHELLPROC) {
 | 
|---|
| 935 |                                 freeparam((volatile struct shparam *)
 | 
|---|
| 936 |                                     &saveparam);
 | 
|---|
| 937 |                         } else {
 | 
|---|
| 938 |                                 freeparam(&shellparam);
 | 
|---|
| 939 |                                 shellparam = saveparam;
 | 
|---|
| 940 |                         }
 | 
|---|
| 941 |                         poplocalvars();
 | 
|---|
| 942 |                         localvars = savelocalvars;
 | 
|---|
| 943 |                         handler = savehandler;
 | 
|---|
| 944 |                         longjmp(handler->loc, 1);
 | 
|---|
| 945 |                 }
 | 
|---|
| 946 |                 savehandler = handler;
 | 
|---|
| 947 |                 handler = &jmploc;
 | 
|---|
| 948 |                 listmklocal(varlist.list, 0);
 | 
|---|
| 949 |                 /* stop shell blowing its stack */
 | 
|---|
| 950 |                 if (++funcnest > 1000)
 | 
|---|
| 951 |                         error("too many nested function calls");
 | 
|---|
| 952 |                 evaltree(cmdentry.u.func, flags & EV_TESTED);
 | 
|---|
| 953 |                 funcnest--;
 | 
|---|
| 954 |                 INTOFF;
 | 
|---|
| 955 |                 poplocalvars();
 | 
|---|
| 956 |                 localvars = savelocalvars;
 | 
|---|
| 957 |                 freeparam(&shellparam);
 | 
|---|
| 958 |                 shellparam = saveparam;
 | 
|---|
| 959 |                 handler = savehandler;
 | 
|---|
| 960 |                 popredir();
 | 
|---|
| 961 |                 INTON;
 | 
|---|
| 962 |                 if (evalskip == SKIPFUNC) {
 | 
|---|
| 963 |                         evalskip = 0;
 | 
|---|
| 964 |                         skipcount = 0;
 | 
|---|
| 965 |                 }
 | 
|---|
| 966 |                 if (flags & EV_EXIT)
 | 
|---|
| 967 |                         exitshell(exitstatus);
 | 
|---|
| 968 |                 break;
 | 
|---|
| 969 | 
 | 
|---|
| 970 |         case CMDBUILTIN:
 | 
|---|
| 971 |         case CMDSPLBLTIN:
 | 
|---|
| 972 | #ifdef DEBUG
 | 
|---|
| 973 |                 trputs("builtin command:  ");  trargs(argv);
 | 
|---|
| 974 | #endif
 | 
|---|
| 975 |                 mode = (cmdentry.u.bltin == execcmd) ? 0 : REDIR_PUSH;
 | 
|---|
| 976 |                 if (flags == EV_BACKCMD) {
 | 
|---|
| 977 |                         memout.nleft = 0;
 | 
|---|
| 978 |                         memout.nextc = memout.buf;
 | 
|---|
| 979 |                         memout.bufsize = 64;
 | 
|---|
| 980 |                         mode |= REDIR_BACKQ;
 | 
|---|
| 981 |                 }
 | 
|---|
| 982 |                 e = -1;
 | 
|---|
| 983 |                 savehandler = handler;
 | 
|---|
| 984 |                 savecmdname = commandname;
 | 
|---|
| 985 |                 handler = &jmploc;
 | 
|---|
| 986 |                 if (!setjmp(jmploc.loc)) {
 | 
|---|
| 987 |                         /* We need to ensure the command hash table isn't
 | 
|---|
| 988 |                          * corruped by temporary PATH assignments.
 | 
|---|
| 989 |                          * However we must ensure the 'local' command works!
 | 
|---|
| 990 |                          */
 | 
|---|
| 991 |                         if (path != pathval() && (cmdentry.u.bltin == hashcmd ||
 | 
|---|
| 992 |                             cmdentry.u.bltin == typecmd)) {
 | 
|---|
| 993 |                                 savelocalvars = localvars;
 | 
|---|
| 994 |                                 localvars = 0;
 | 
|---|
| 995 |                                 mklocal(path - 5 /* PATH= */, 0);
 | 
|---|
| 996 |                                 temp_path = 1;
 | 
|---|
| 997 |                         } else
 | 
|---|
| 998 |                                 temp_path = 0;
 | 
|---|
| 999 |                         redirect(cmd->ncmd.redirect, mode);
 | 
|---|
| 1000 | 
 | 
|---|
| 1001 |                         /* exec is a special builtin, but needs this list... */
 | 
|---|
| 1002 |                         cmdenviron = varlist.list;
 | 
|---|
| 1003 |                         /* we must check 'readonly' flag for all builtins */
 | 
|---|
| 1004 |                         listsetvar(varlist.list,
 | 
|---|
| 1005 |                                 cmdentry.cmdtype == CMDSPLBLTIN ? 0 : VNOSET);
 | 
|---|
| 1006 |                         commandname = argv[0];
 | 
|---|
| 1007 |                         /* initialize nextopt */
 | 
|---|
| 1008 |                         argptr = argv + 1;
 | 
|---|
| 1009 |                         optptr = NULL;
 | 
|---|
| 1010 |                         /* and getopt */
 | 
|---|
| 1011 |                         optreset = 1;
 | 
|---|
| 1012 |                         optind = 1;
 | 
|---|
| 1013 |                         exitstatus = cmdentry.u.bltin(argc, argv);
 | 
|---|
| 1014 |                 } else {
 | 
|---|
| 1015 |                         e = exception;
 | 
|---|
| 1016 |                         exitstatus = e == EXINT ? SIGINT + 128 :
 | 
|---|
| 1017 |                                         e == EXEXEC ? exerrno : 2;
 | 
|---|
| 1018 |                 }
 | 
|---|
| 1019 |                 handler = savehandler;
 | 
|---|
| 1020 |                 output_flushall();
 | 
|---|
| 1021 |                 out1 = &output;
 | 
|---|
| 1022 |                 out2 = &errout;
 | 
|---|
| 1023 |                 freestdout();
 | 
|---|
| 1024 |                 if (temp_path) {
 | 
|---|
| 1025 |                         poplocalvars();
 | 
|---|
| 1026 |                         localvars = savelocalvars;
 | 
|---|
| 1027 |                 }
 | 
|---|
| 1028 |                 cmdenviron = NULL;
 | 
|---|
| 1029 |                 if (e != EXSHELLPROC) {
 | 
|---|
| 1030 |                         commandname = savecmdname;
 | 
|---|
| 1031 |                         if (flags & EV_EXIT)
 | 
|---|
| 1032 |                                 exitshell(exitstatus);
 | 
|---|
| 1033 |                 }
 | 
|---|
| 1034 |                 if (e != -1) {
 | 
|---|
| 1035 |                         if ((e != EXERROR && e != EXEXEC)
 | 
|---|
| 1036 |                             || cmdentry.cmdtype == CMDSPLBLTIN)
 | 
|---|
| 1037 |                                 exraise(e);
 | 
|---|
| 1038 |                         FORCEINTON;
 | 
|---|
| 1039 |                 }
 | 
|---|
| 1040 |                 if (cmdentry.u.bltin != execcmd)
 | 
|---|
| 1041 |                         popredir();
 | 
|---|
| 1042 |                 if (flags == EV_BACKCMD) {
 | 
|---|
| 1043 |                         backcmd->buf = memout.buf;
 | 
|---|
| 1044 |                         backcmd->nleft = memout.nextc - memout.buf;
 | 
|---|
| 1045 |                         memout.buf = NULL;
 | 
|---|
| 1046 |                 }
 | 
|---|
| 1047 |                 break;
 | 
|---|
| 1048 | 
 | 
|---|
| 1049 |         default:
 | 
|---|
| 1050 | #ifdef DEBUG
 | 
|---|
| 1051 |                 trputs("normal command:  ");  trargs(argv);
 | 
|---|
| 1052 | #endif
 | 
|---|
| 1053 |                 clearredir(vforked);
 | 
|---|
| 1054 |                 redirect(cmd->ncmd.redirect, vforked ? REDIR_VFORK : 0);
 | 
|---|
| 1055 |                 if (!vforked)
 | 
|---|
| 1056 |                         for (sp = varlist.list ; sp ; sp = sp->next)
 | 
|---|
| 1057 |                                 setvareq(sp->text, VEXPORT|VSTACK);
 | 
|---|
| 1058 |                 envp = environment();
 | 
|---|
| 1059 |                 shellexec(argv, envp, path, cmdentry.u.index, vforked);
 | 
|---|
| 1060 |                 break;
 | 
|---|
| 1061 |         }
 | 
|---|
| 1062 |         goto out;
 | 
|---|
| 1063 | 
 | 
|---|
| 1064 | parent: /* parent process gets here (if we forked) */
 | 
|---|
| 1065 |         if (mode == FORK_FG) {  /* argument to fork */
 | 
|---|
| 1066 |                 exitstatus = waitforjob(jp);
 | 
|---|
| 1067 |         } else if (mode == FORK_NOJOB) {
 | 
|---|
| 1068 |                 backcmd->fd = pip[0];
 | 
|---|
| 1069 |                 close(pip[1]);
 | 
|---|
| 1070 |                 backcmd->jp = jp;
 | 
|---|
| 1071 |         }
 | 
|---|
| 1072 |         FORCEINTON;
 | 
|---|
| 1073 | 
 | 
|---|
| 1074 | out:
 | 
|---|
| 1075 |         if (lastarg)
 | 
|---|
| 1076 |                 /* dsl: I think this is intended to be used to support
 | 
|---|
| 1077 |                  * '_' in 'vi' command mode during line editing...
 | 
|---|
| 1078 |                  * However I implemented that within libedit itself.
 | 
|---|
| 1079 |                  */
 | 
|---|
| 1080 |                 setvar("_", lastarg, 0);
 | 
|---|
| 1081 |         popstackmark(&smark);
 | 
|---|
| 1082 | 
 | 
|---|
| 1083 |         if (eflag && exitstatus && !(flags & EV_TESTED))
 | 
|---|
| 1084 |                 exitshell(exitstatus);
 | 
|---|
| 1085 | }
 | 
|---|
| 1086 | 
 | 
|---|
| 1087 | 
 | 
|---|
| 1088 | /*
 | 
|---|
| 1089 |  * Search for a command.  This is called before we fork so that the
 | 
|---|
| 1090 |  * location of the command will be available in the parent as well as
 | 
|---|
| 1091 |  * the child.  The check for "goodname" is an overly conservative
 | 
|---|
| 1092 |  * check that the name will not be subject to expansion.
 | 
|---|
| 1093 |  */
 | 
|---|
| 1094 | 
 | 
|---|
| 1095 | STATIC void
 | 
|---|
| 1096 | prehash(union node *n)
 | 
|---|
| 1097 | {
 | 
|---|
| 1098 |         struct cmdentry entry;
 | 
|---|
| 1099 | 
 | 
|---|
| 1100 |         if (n->type == NCMD && n->ncmd.args)
 | 
|---|
| 1101 |                 if (goodname(n->ncmd.args->narg.text))
 | 
|---|
| 1102 |                         find_command(n->ncmd.args->narg.text, &entry, 0,
 | 
|---|
| 1103 |                                      pathval());
 | 
|---|
| 1104 | }
 | 
|---|
| 1105 | 
 | 
|---|
| 1106 | 
 | 
|---|
| 1107 | 
 | 
|---|
| 1108 | /*
 | 
|---|
| 1109 |  * Builtin commands.  Builtin commands whose functions are closely
 | 
|---|
| 1110 |  * tied to evaluation are implemented here.
 | 
|---|
| 1111 |  */
 | 
|---|
| 1112 | 
 | 
|---|
| 1113 | /*
 | 
|---|
| 1114 |  * No command given.
 | 
|---|
| 1115 |  */
 | 
|---|
| 1116 | 
 | 
|---|
| 1117 | int
 | 
|---|
| 1118 | bltincmd(int argc, char **argv)
 | 
|---|
| 1119 | {
 | 
|---|
| 1120 |         /*
 | 
|---|
| 1121 |          * Preserve exitstatus of a previous possible redirection
 | 
|---|
| 1122 |          * as POSIX mandates
 | 
|---|
| 1123 |          */
 | 
|---|
| 1124 |         return back_exitstatus;
 | 
|---|
| 1125 | }
 | 
|---|
| 1126 | 
 | 
|---|
| 1127 | 
 | 
|---|
| 1128 | /*
 | 
|---|
| 1129 |  * Handle break and continue commands.  Break, continue, and return are
 | 
|---|
| 1130 |  * all handled by setting the evalskip flag.  The evaluation routines
 | 
|---|
| 1131 |  * above all check this flag, and if it is set they start skipping
 | 
|---|
| 1132 |  * commands rather than executing them.  The variable skipcount is
 | 
|---|
| 1133 |  * the number of loops to break/continue, or the number of function
 | 
|---|
| 1134 |  * levels to return.  (The latter is always 1.)  It should probably
 | 
|---|
| 1135 |  * be an error to break out of more loops than exist, but it isn't
 | 
|---|
| 1136 |  * in the standard shell so we don't make it one here.
 | 
|---|
| 1137 |  */
 | 
|---|
| 1138 | 
 | 
|---|
| 1139 | int
 | 
|---|
| 1140 | breakcmd(int argc, char **argv)
 | 
|---|
| 1141 | {
 | 
|---|
| 1142 |         int n = argc > 1 ? number(argv[1]) : 1;
 | 
|---|
| 1143 | 
 | 
|---|
| 1144 |         if (n > loopnest)
 | 
|---|
| 1145 |                 n = loopnest;
 | 
|---|
| 1146 |         if (n > 0) {
 | 
|---|
| 1147 |                 evalskip = (**argv == 'c')? SKIPCONT : SKIPBREAK;
 | 
|---|
| 1148 |                 skipcount = n;
 | 
|---|
| 1149 |         }
 | 
|---|
| 1150 |         return 0;
 | 
|---|
| 1151 | }
 | 
|---|
| 1152 | 
 | 
|---|
| 1153 | 
 | 
|---|
| 1154 | /*
 | 
|---|
| 1155 |  * The return command.
 | 
|---|
| 1156 |  */
 | 
|---|
| 1157 | 
 | 
|---|
| 1158 | int
 | 
|---|
| 1159 | returncmd(int argc, char **argv)
 | 
|---|
| 1160 | {
 | 
|---|
| 1161 |         int ret = argc > 1 ? number(argv[1]) : exitstatus;
 | 
|---|
| 1162 | 
 | 
|---|
| 1163 |         if (funcnest) {
 | 
|---|
| 1164 |                 evalskip = SKIPFUNC;
 | 
|---|
| 1165 |                 skipcount = 1;
 | 
|---|
| 1166 |                 return ret;
 | 
|---|
| 1167 |         }
 | 
|---|
| 1168 |         else {
 | 
|---|
| 1169 |                 /* Do what ksh does; skip the rest of the file */
 | 
|---|
| 1170 |                 evalskip = SKIPFILE;
 | 
|---|
| 1171 |                 skipcount = 1;
 | 
|---|
| 1172 |                 return ret;
 | 
|---|
| 1173 |         }
 | 
|---|
| 1174 | }
 | 
|---|
| 1175 | 
 | 
|---|
| 1176 | 
 | 
|---|
| 1177 | int
 | 
|---|
| 1178 | falsecmd(int argc, char **argv)
 | 
|---|
| 1179 | {
 | 
|---|
| 1180 |         return 1;
 | 
|---|
| 1181 | }
 | 
|---|
| 1182 | 
 | 
|---|
| 1183 | 
 | 
|---|
| 1184 | int
 | 
|---|
| 1185 | truecmd(int argc, char **argv)
 | 
|---|
| 1186 | {
 | 
|---|
| 1187 |         return 0;
 | 
|---|
| 1188 | }
 | 
|---|
| 1189 | 
 | 
|---|
| 1190 | 
 | 
|---|
| 1191 | int
 | 
|---|
| 1192 | execcmd(int argc, char **argv)
 | 
|---|
| 1193 | {
 | 
|---|
| 1194 |         if (argc > 1) {
 | 
|---|
| 1195 |                 struct strlist *sp;
 | 
|---|
| 1196 | 
 | 
|---|
| 1197 |                 iflag = 0;              /* exit on error */
 | 
|---|
| 1198 |                 mflag = 0;
 | 
|---|
| 1199 |                 optschanged();
 | 
|---|
| 1200 |                 for (sp = cmdenviron; sp; sp = sp->next)
 | 
|---|
| 1201 |                         setvareq(sp->text, VEXPORT|VSTACK);
 | 
|---|
| 1202 |                 shellexec(argv + 1, environment(), pathval(), 0, 0);
 | 
|---|
| 1203 |         }
 | 
|---|
| 1204 |         return 0;
 | 
|---|
| 1205 | }
 | 
|---|
| 1206 | 
 | 
|---|
| 1207 | static int
 | 
|---|
| 1208 | conv_time(clock_t ticks, char *seconds, size_t l)
 | 
|---|
| 1209 | {
 | 
|---|
| 1210 |         static clock_t tpm = 0;
 | 
|---|
| 1211 |         clock_t mins;
 | 
|---|
| 1212 |         int i;
 | 
|---|
| 1213 | 
 | 
|---|
| 1214 |         if (!tpm)
 | 
|---|
| 1215 |                 tpm = sysconf(_SC_CLK_TCK) * 60;
 | 
|---|
| 1216 | 
 | 
|---|
| 1217 |         mins = ticks / tpm;
 | 
|---|
| 1218 |         snprintf(seconds, l, "%.4f", (ticks - mins * tpm) * 60.0 / tpm );
 | 
|---|
| 1219 | 
 | 
|---|
| 1220 |         if (seconds[0] == '6' && seconds[1] == '0') {
 | 
|---|
| 1221 |                 /* 59.99995 got rounded up... */
 | 
|---|
| 1222 |                 mins++;
 | 
|---|
| 1223 |                 strlcpy(seconds, "0.0", l);
 | 
|---|
| 1224 |                 return mins;
 | 
|---|
| 1225 |         }
 | 
|---|
| 1226 | 
 | 
|---|
| 1227 |         /* suppress trailing zeros */
 | 
|---|
| 1228 |         i = strlen(seconds) - 1;
 | 
|---|
| 1229 |         for (; seconds[i] == '0' && seconds[i - 1] != '.'; i--)
 | 
|---|
| 1230 |                 seconds[i] = 0;
 | 
|---|
| 1231 |         return mins;
 | 
|---|
| 1232 | }
 | 
|---|
| 1233 | 
 | 
|---|
| 1234 | int
 | 
|---|
| 1235 | timescmd(int argc, char **argv)
 | 
|---|
| 1236 | {
 | 
|---|
| 1237 |         struct tms tms;
 | 
|---|
| 1238 |         int u, s, cu, cs;
 | 
|---|
| 1239 |         char us[8], ss[8], cus[8], css[8];
 | 
|---|
| 1240 | 
 | 
|---|
| 1241 |         nextopt("");
 | 
|---|
| 1242 | 
 | 
|---|
| 1243 |         times(&tms);
 | 
|---|
| 1244 | 
 | 
|---|
| 1245 |         u = conv_time(tms.tms_utime, us, sizeof(us));
 | 
|---|
| 1246 |         s = conv_time(tms.tms_stime, ss, sizeof(ss));
 | 
|---|
| 1247 |         cu = conv_time(tms.tms_cutime, cus, sizeof(cus));
 | 
|---|
| 1248 |         cs = conv_time(tms.tms_cstime, css, sizeof(css));
 | 
|---|
| 1249 | 
 | 
|---|
| 1250 |         outfmt(out1, "%dm%ss %dm%ss\n%dm%ss %dm%ss\n",
 | 
|---|
| 1251 |                 u, us, s, ss, cu, cus, cs, css);
 | 
|---|
| 1252 | 
 | 
|---|
| 1253 |         return 0;
 | 
|---|
| 1254 | }
 | 
|---|