eval.c revision 185231
1227825Stheraven/*- 2227825Stheraven * Copyright (c) 1993 3227825Stheraven * The Regents of the University of California. All rights reserved. 4227825Stheraven * 5227825Stheraven * This code is derived from software contributed to Berkeley by 6227825Stheraven * Kenneth Almquist. 7227825Stheraven * 8227825Stheraven * Redistribution and use in source and binary forms, with or without 9227825Stheraven * modification, are permitted provided that the following conditions 10227825Stheraven * are met: 11227825Stheraven * 1. Redistributions of source code must retain the above copyright 12227825Stheraven * notice, this list of conditions and the following disclaimer. 13227825Stheraven * 2. Redistributions in binary form must reproduce the above copyright 14227825Stheraven * notice, this list of conditions and the following disclaimer in the 15227825Stheraven * documentation and/or other materials provided with the distribution. 16227825Stheraven * 4. Neither the name of the University nor the names of its contributors 17227825Stheraven * may be used to endorse or promote products derived from this software 18227825Stheraven * without specific prior written permission. 19227825Stheraven * 20227825Stheraven * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 21227825Stheraven * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22227825Stheraven * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 23227825Stheraven * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 24227825Stheraven * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25227825Stheraven * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 26227825Stheraven * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 27227825Stheraven * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 28227825Stheraven * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 29227825Stheraven * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 30227825Stheraven * SUCH DAMAGE. 31227825Stheraven */ 32227825Stheraven 33227825Stheraven#ifndef lint 34227825Stheraven#if 0 35227825Stheravenstatic char sccsid[] = "@(#)eval.c 8.9 (Berkeley) 6/8/95"; 36227825Stheraven#endif 37227825Stheraven#endif /* not lint */ 38227825Stheraven#include <sys/cdefs.h> 39227825Stheraven__FBSDID("$FreeBSD: head/bin/sh/eval.c 185231 2008-11-23 20:23:57Z stefanf $"); 40227825Stheraven 41227825Stheraven#include <paths.h> 42227825Stheraven#include <signal.h> 43227825Stheraven#include <stdlib.h> 44227825Stheraven#include <unistd.h> 45227825Stheraven#include <sys/resource.h> 46227825Stheraven#include <sys/wait.h> /* For WIFSIGNALED(status) */ 47227825Stheraven#include <errno.h> 48227825Stheraven 49227825Stheraven/* 50227825Stheraven * Evaluate a command. 51227825Stheraven */ 52227825Stheraven 53227825Stheraven#include "shell.h" 54227825Stheraven#include "nodes.h" 55227825Stheraven#include "syntax.h" 56227825Stheraven#include "expand.h" 57227825Stheraven#include "parser.h" 58227825Stheraven#include "jobs.h" 59227825Stheraven#include "eval.h" 60227825Stheraven#include "builtins.h" 61227825Stheraven#include "options.h" 62227825Stheraven#include "exec.h" 63227825Stheraven#include "redir.h" 64227825Stheraven#include "input.h" 65227825Stheraven#include "output.h" 66227825Stheraven#include "trap.h" 67227825Stheraven#include "var.h" 68227825Stheraven#include "memalloc.h" 69227825Stheraven#include "error.h" 70227825Stheraven#include "show.h" 71227825Stheraven#include "mystring.h" 72227825Stheraven#ifndef NO_HISTORY 73227825Stheraven#include "myhistedit.h" 74227825Stheraven#endif 75227825Stheraven 76227825Stheraven 77227825Stheraven/* flags in argument to evaltree */ 78227825Stheraven#define EV_EXIT 01 /* exit after evaluating tree */ 79227825Stheraven#define EV_TESTED 02 /* exit status is checked; ignore -e flag */ 80227825Stheraven#define EV_BACKCMD 04 /* command executing within back quotes */ 81227825Stheraven 82227825StheravenMKINIT int evalskip; /* set if we are skipping commands */ 83227825StheravenSTATIC int skipcount; /* number of levels to skip */ 84227825StheravenMKINIT int loopnest; /* current loop nesting level */ 85227825Stheravenint funcnest; /* depth of function calls */ 86227825Stheraven 87227825Stheraven 88227825Stheravenchar *commandname; 89227825Stheravenstruct strlist *cmdenviron; 90227825Stheravenint exitstatus; /* exit status of last command */ 91227825Stheravenint oexitstatus; /* saved exit status */ 92227825Stheraven 93227825Stheraven 94227825StheravenSTATIC void evalloop(union node *, int); 95227825StheravenSTATIC void evalfor(union node *, int); 96227825StheravenSTATIC void evalcase(union node *, int); 97227825StheravenSTATIC void evalsubshell(union node *, int); 98227825StheravenSTATIC void expredir(union node *); 99227825StheravenSTATIC void evalpipe(union node *); 100227825StheravenSTATIC void evalcommand(union node *, int, struct backcmd *); 101227825StheravenSTATIC void prehash(union node *); 102227825Stheraven 103227825Stheraven 104227825Stheraven/* 105227825Stheraven * Called to reset things after an exception. 106227825Stheraven */ 107227825Stheraven 108227825Stheraven#ifdef mkinit 109227825StheravenINCLUDE "eval.h" 110227825Stheraven 111227825StheravenRESET { 112227825Stheraven evalskip = 0; 113227825Stheraven loopnest = 0; 114227825Stheraven funcnest = 0; 115227825Stheraven} 116227825Stheraven 117227825StheravenSHELLPROC { 118227825Stheraven exitstatus = 0; 119227825Stheraven} 120227825Stheraven#endif 121227825Stheraven 122227825Stheraven 123227825Stheraven 124227825Stheraven/* 125227825Stheraven * The eval command. 126227825Stheraven */ 127227825Stheraven 128227825Stheravenint 129227825Stheravenevalcmd(int argc, char **argv) 130227825Stheraven{ 131227825Stheraven char *p; 132227825Stheraven char *concat; 133227825Stheraven char **ap; 134227825Stheraven 135227825Stheraven if (argc > 1) { 136227825Stheraven p = argv[1]; 137227825Stheraven if (argc > 2) { 138227825Stheraven STARTSTACKSTR(concat); 139227825Stheraven ap = argv + 2; 140227825Stheraven for (;;) { 141227825Stheraven while (*p) 142227825Stheraven STPUTC(*p++, concat); 143227825Stheraven if ((p = *ap++) == NULL) 144227825Stheraven break; 145227825Stheraven STPUTC(' ', concat); 146227825Stheraven } 147227825Stheraven STPUTC('\0', concat); 148227825Stheraven p = grabstackstr(concat); 149227825Stheraven } 150227825Stheraven evalstring(p); 151227825Stheraven } 152227825Stheraven return exitstatus; 153227825Stheraven} 154227825Stheraven 155227825Stheraven 156227825Stheraven/* 157227825Stheraven * Execute a command or commands contained in a string. 158227825Stheraven */ 159227825Stheraven 160227825Stheravenvoid 161227825Stheravenevalstring(char *s) 162227825Stheraven{ 163227825Stheraven union node *n; 164227825Stheraven struct stackmark smark; 165227825Stheraven 166227825Stheraven setstackmark(&smark); 167227825Stheraven setinputstring(s, 1); 168227825Stheraven while ((n = parsecmd(0)) != NEOF) { 169227825Stheraven evaltree(n, 0); 170227825Stheraven popstackmark(&smark); 171227825Stheraven } 172227825Stheraven popfile(); 173227825Stheraven popstackmark(&smark); 174227825Stheraven} 175227825Stheraven 176227825Stheraven 177227825Stheraven 178227825Stheraven/* 179227825Stheraven * Evaluate a parse tree. The value is left in the global variable 180227825Stheraven * exitstatus. 181227825Stheraven */ 182227825Stheraven 183227825Stheravenvoid 184227825Stheravenevaltree(union node *n, int flags) 185227825Stheraven{ 186227825Stheraven int do_etest; 187227825Stheraven 188227825Stheraven do_etest = 0; 189227825Stheraven if (n == NULL) { 190227825Stheraven TRACE(("evaltree(NULL) called\n")); 191227825Stheraven exitstatus = 0; 192227825Stheraven goto out; 193227825Stheraven } 194227825Stheraven#ifndef NO_HISTORY 195227825Stheraven displayhist = 1; /* show history substitutions done with fc */ 196227825Stheraven#endif 197227825Stheraven TRACE(("evaltree(%p: %d) called\n", (void *)n, n->type)); 198227825Stheraven switch (n->type) { 199227825Stheraven case NSEMI: 200227825Stheraven evaltree(n->nbinary.ch1, flags & ~EV_EXIT); 201227825Stheraven if (evalskip) 202227825Stheraven goto out; 203227825Stheraven evaltree(n->nbinary.ch2, flags); 204227825Stheraven break; 205227825Stheraven case NAND: 206227825Stheraven evaltree(n->nbinary.ch1, EV_TESTED); 207227825Stheraven if (evalskip || exitstatus != 0) { 208227825Stheraven goto out; 209227825Stheraven } 210227825Stheraven evaltree(n->nbinary.ch2, flags); 211227825Stheraven break; 212227825Stheraven case NOR: 213227825Stheraven evaltree(n->nbinary.ch1, EV_TESTED); 214227825Stheraven if (evalskip || exitstatus == 0) 215227825Stheraven goto out; 216227825Stheraven evaltree(n->nbinary.ch2, flags); 217227825Stheraven break; 218227825Stheraven case NREDIR: 219227825Stheraven expredir(n->nredir.redirect); 220227825Stheraven redirect(n->nredir.redirect, REDIR_PUSH); 221227825Stheraven evaltree(n->nredir.n, flags); 222227825Stheraven popredir(); 223227825Stheraven break; 224227825Stheraven case NSUBSHELL: 225227825Stheraven evalsubshell(n, flags); 226227825Stheraven do_etest = !(flags & EV_TESTED); 227227825Stheraven break; 228227825Stheraven case NBACKGND: 229227825Stheraven evalsubshell(n, flags); 230227825Stheraven break; 231227825Stheraven case NIF: { 232227825Stheraven evaltree(n->nif.test, EV_TESTED); 233227825Stheraven if (evalskip) 234227825Stheraven goto out; 235227825Stheraven if (exitstatus == 0) 236227825Stheraven evaltree(n->nif.ifpart, flags); 237227825Stheraven else if (n->nif.elsepart) 238227825Stheraven evaltree(n->nif.elsepart, flags); 239227825Stheraven else 240227825Stheraven exitstatus = 0; 241227825Stheraven break; 242227825Stheraven } 243227825Stheraven case NWHILE: 244227825Stheraven case NUNTIL: 245227825Stheraven evalloop(n, flags & ~EV_EXIT); 246227825Stheraven break; 247227825Stheraven case NFOR: 248227825Stheraven evalfor(n, flags & ~EV_EXIT); 249227825Stheraven break; 250227825Stheraven case NCASE: 251227825Stheraven evalcase(n, flags); 252227825Stheraven break; 253227825Stheraven case NDEFUN: 254227825Stheraven defun(n->narg.text, n->narg.next); 255227825Stheraven exitstatus = 0; 256227825Stheraven break; 257227825Stheraven case NNOT: 258227825Stheraven evaltree(n->nnot.com, EV_TESTED); 259227825Stheraven exitstatus = !exitstatus; 260227825Stheraven break; 261227825Stheraven 262227825Stheraven case NPIPE: 263227825Stheraven evalpipe(n); 264227825Stheraven do_etest = !(flags & EV_TESTED); 265227825Stheraven break; 266241903Sdim case NCMD: 267241903Sdim evalcommand(n, flags, (struct backcmd *)NULL); 268241903Sdim do_etest = !(flags & EV_TESTED); 269241903Sdim break; 270227825Stheraven default: 271227825Stheraven out1fmt("Node type = %d\n", n->type); 272227825Stheraven flushout(&output); 273227825Stheraven break; 274227825Stheraven } 275227825Stheravenout: 276227825Stheraven if (pendingsigs) 277227825Stheraven dotrap(); 278227825Stheraven if ((flags & EV_EXIT) || (eflag && exitstatus != 0 && do_etest)) 279227825Stheraven exitshell(exitstatus); 280227825Stheraven} 281227825Stheraven 282227825Stheraven 283227825StheravenSTATIC void 284227825Stheravenevalloop(union node *n, int flags) 285227825Stheraven{ 286227825Stheraven int status; 287227825Stheraven 288227825Stheraven loopnest++; 289227825Stheraven status = 0; 290227825Stheraven for (;;) { 291227825Stheraven evaltree(n->nbinary.ch1, EV_TESTED); 292227825Stheraven if (evalskip) { 293227825Stheravenskipping: if (evalskip == SKIPCONT && --skipcount <= 0) { 294227825Stheraven evalskip = 0; 295227825Stheraven continue; 296241903Sdim } 297241903Sdim if (evalskip == SKIPBREAK && --skipcount <= 0) 298227825Stheraven evalskip = 0; 299227825Stheraven break; 300227825Stheraven } 301227825Stheraven if (n->type == NWHILE) { 302241903Sdim if (exitstatus != 0) 303227825Stheraven break; 304227825Stheraven } else { 305227825Stheraven if (exitstatus == 0) 306227825Stheraven break; 307227825Stheraven } 308227825Stheraven evaltree(n->nbinary.ch2, flags); 309227825Stheraven status = exitstatus; 310227825Stheraven if (evalskip) 311227825Stheraven goto skipping; 312227825Stheraven } 313227825Stheraven loopnest--; 314227825Stheraven exitstatus = status; 315227825Stheraven} 316227825Stheraven 317227825Stheraven 318227825Stheraven 319227825StheravenSTATIC void 320249998Sdimevalfor(union node *n, int flags) 321243673Stheraven{ 322243673Stheraven struct arglist arglist; 323243673Stheraven union node *argp; 324227825Stheraven struct strlist *sp; 325227825Stheraven struct stackmark smark; 326227825Stheraven 327227825Stheraven setstackmark(&smark); 328227825Stheraven arglist.lastp = &arglist.list; 329227825Stheraven for (argp = n->nfor.args ; argp ; argp = argp->narg.next) { 330227825Stheraven oexitstatus = exitstatus; 331227825Stheraven expandarg(argp, &arglist, EXP_FULL | EXP_TILDE); 332227825Stheraven if (evalskip) 333227825Stheraven goto out; 334249998Sdim } 335249998Sdim *arglist.lastp = NULL; 336249998Sdim 337249998Sdim exitstatus = 0; 338249998Sdim loopnest++; 339227825Stheraven for (sp = arglist.list ; sp ; sp = sp->next) { 340227825Stheraven setvar(n->nfor.var, sp->text, 0); 341227825Stheraven evaltree(n->nfor.body, flags); 342227825Stheraven if (evalskip) { 343227825Stheraven if (evalskip == SKIPCONT && --skipcount <= 0) { 344242945Stheraven evalskip = 0; 345227825Stheraven continue; 346227825Stheraven } 347227825Stheraven if (evalskip == SKIPBREAK && --skipcount <= 0) 348227825Stheraven evalskip = 0; 349227825Stheraven break; 350227825Stheraven } 351227825Stheraven } 352227825Stheraven loopnest--; 353227825Stheravenout: 354227825Stheraven popstackmark(&smark); 355227825Stheraven} 356227825Stheraven 357227825Stheraven 358227825Stheraven 359227825StheravenSTATIC void 360227825Stheravenevalcase(union node *n, int flags) 361227825Stheraven{ 362227825Stheraven union node *cp; 363227825Stheraven union node *patp; 364227825Stheraven struct arglist arglist; 365227825Stheraven struct stackmark smark; 366227825Stheraven 367227825Stheraven setstackmark(&smark); 368227825Stheraven arglist.lastp = &arglist.list; 369227825Stheraven oexitstatus = exitstatus; 370227825Stheraven exitstatus = 0; 371227825Stheraven expandarg(n->ncase.expr, &arglist, EXP_TILDE); 372227825Stheraven for (cp = n->ncase.cases ; cp && evalskip == 0 ; cp = cp->nclist.next) { 373227825Stheraven for (patp = cp->nclist.pattern ; patp ; patp = patp->narg.next) { 374227825Stheraven if (casematch(patp, arglist.list->text)) { 375227825Stheraven if (evalskip == 0) { 376227825Stheraven evaltree(cp->nclist.body, flags); 377227825Stheraven } 378227825Stheraven goto out; 379227825Stheraven } 380227825Stheraven } 381249998Sdim } 382227825Stheravenout: 383227825Stheraven popstackmark(&smark); 384227825Stheraven} 385249998Sdim 386227825Stheraven 387227825Stheraven 388227825Stheraven/* 389227825Stheraven * Kick off a subshell to evaluate a tree. 390227825Stheraven */ 391227825Stheraven 392227825StheravenSTATIC void 393227825Stheravenevalsubshell(union node *n, int flags) 394227825Stheraven{ 395227825Stheraven struct job *jp; 396227825Stheraven int backgnd = (n->type == NBACKGND); 397227825Stheraven 398227825Stheraven expredir(n->nredir.redirect); 399227825Stheraven jp = makejob(n, 1); 400227825Stheraven if (forkshell(jp, n, backgnd) == 0) { 401227825Stheraven if (backgnd) 402227825Stheraven flags &=~ EV_TESTED; 403227825Stheraven redirect(n->nredir.redirect, 0); 404227825Stheraven evaltree(n->nredir.n, flags | EV_EXIT); /* never returns */ 405227825Stheraven } 406227825Stheraven if (! backgnd) { 407227825Stheraven INTOFF; 408227825Stheraven exitstatus = waitforjob(jp, (int *)NULL); 409227825Stheraven INTON; 410227825Stheraven } 411227825Stheraven} 412227825Stheraven 413227825Stheraven 414227825Stheraven 415227825Stheraven/* 416249998Sdim * Compute the names of the files in a redirection list. 417227825Stheraven */ 418227825Stheraven 419227825StheravenSTATIC void 420227825Stheravenexpredir(union node *n) 421227825Stheraven{ 422227825Stheraven union node *redir; 423227825Stheraven 424227825Stheraven for (redir = n ; redir ; redir = redir->nfile.next) { 425227825Stheraven struct arglist fn; 426227825Stheraven fn.lastp = &fn.list; 427227825Stheraven oexitstatus = exitstatus; 428227825Stheraven switch (redir->type) { 429227825Stheraven case NFROM: 430227825Stheraven case NTO: 431227825Stheraven case NFROMTO: 432227825Stheraven case NAPPEND: 433227825Stheraven case NCLOBBER: 434227825Stheraven expandarg(redir->nfile.fname, &fn, EXP_TILDE | EXP_REDIR); 435227825Stheraven redir->nfile.expfname = fn.list->text; 436227825Stheraven break; 437227825Stheraven case NFROMFD: 438227825Stheraven case NTOFD: 439227825Stheraven if (redir->ndup.vname) { 440227825Stheraven expandarg(redir->ndup.vname, &fn, EXP_TILDE | EXP_REDIR); 441227825Stheraven fixredir(redir, fn.list->text, 1); 442227825Stheraven } 443227825Stheraven break; 444227825Stheraven } 445227825Stheraven } 446227825Stheraven} 447227825Stheraven 448227825Stheraven 449227825Stheraven 450227825Stheraven/* 451227825Stheraven * Evaluate a pipeline. All the processes in the pipeline are children 452227825Stheraven * of the process creating the pipeline. (This differs from some versions 453227825Stheraven * of the shell, which make the last process in a pipeline the parent 454227825Stheraven * of all the rest.) 455227825Stheraven */ 456227825Stheraven 457227825StheravenSTATIC void 458227825Stheravenevalpipe(union node *n) 459227825Stheraven{ 460227825Stheraven struct job *jp; 461227825Stheraven struct nodelist *lp; 462227825Stheraven int pipelen; 463227825Stheraven int prevfd; 464227825Stheraven int pip[2]; 465227825Stheraven 466227825Stheraven TRACE(("evalpipe(%p) called\n", (void *)n)); 467227825Stheraven pipelen = 0; 468227825Stheraven for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) 469227825Stheraven pipelen++; 470227825Stheraven INTOFF; 471227825Stheraven jp = makejob(n, pipelen); 472227825Stheraven prevfd = -1; 473227825Stheraven for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) { 474227825Stheraven prehash(lp->n); 475227825Stheraven pip[1] = -1; 476227825Stheraven if (lp->next) { 477227825Stheraven if (pipe(pip) < 0) { 478227825Stheraven close(prevfd); 479227825Stheraven error("Pipe call failed: %s", strerror(errno)); 480227825Stheraven } 481227825Stheraven } 482227825Stheraven if (forkshell(jp, lp->n, n->npipe.backgnd) == 0) { 483227825Stheraven INTON; 484227825Stheraven if (prevfd > 0) { 485227825Stheraven dup2(prevfd, 0); 486227825Stheraven close(prevfd); 487227825Stheraven } 488227825Stheraven if (pip[1] >= 0) { 489227825Stheraven if (!(prevfd >= 0 && pip[0] == 0)) 490227825Stheraven close(pip[0]); 491227825Stheraven if (pip[1] != 1) { 492227825Stheraven dup2(pip[1], 1); 493227825Stheraven close(pip[1]); 494227825Stheraven } 495227825Stheraven } 496227825Stheraven evaltree(lp->n, EV_EXIT); 497227825Stheraven } 498227825Stheraven if (prevfd >= 0) 499227825Stheraven close(prevfd); 500227825Stheraven prevfd = pip[0]; 501227825Stheraven close(pip[1]); 502227825Stheraven } 503227825Stheraven INTON; 504227825Stheraven if (n->npipe.backgnd == 0) { 505227825Stheraven INTOFF; 506227825Stheraven exitstatus = waitforjob(jp, (int *)NULL); 507227825Stheraven TRACE(("evalpipe: job done exit status %d\n", exitstatus)); 508227825Stheraven INTON; 509227825Stheraven } 510227825Stheraven} 511227825Stheraven 512227825Stheraven 513249998Sdim 514227825Stheraven/* 515227825Stheraven * Execute a command inside back quotes. If it's a builtin command, we 516227825Stheraven * want to save its output in a block obtained from malloc. Otherwise 517227825Stheraven * we fork off a subprocess and get the output of the command via a pipe. 518227825Stheraven * Should be called with interrupts off. 519227825Stheraven */ 520227825Stheraven 521227825Stheravenvoid 522227825Stheravenevalbackcmd(union node *n, struct backcmd *result) 523227825Stheraven{ 524227825Stheraven int pip[2]; 525227825Stheraven struct job *jp; 526227825Stheraven struct stackmark smark; /* unnecessary */ 527227825Stheraven 528227825Stheraven setstackmark(&smark); 529227825Stheraven result->fd = -1; 530227825Stheraven result->buf = NULL; 531227825Stheraven result->nleft = 0; 532227825Stheraven result->jp = NULL; 533227825Stheraven if (n == NULL) { 534227825Stheraven exitstatus = 0; 535227825Stheraven goto out; 536227825Stheraven } 537227825Stheraven if (n->type == NCMD) { 538227825Stheraven exitstatus = oexitstatus; 539227825Stheraven evalcommand(n, EV_BACKCMD, result); 540227825Stheraven } else { 541227825Stheraven exitstatus = 0; 542227825Stheraven if (pipe(pip) < 0) 543227825Stheraven error("Pipe call failed: %s", strerror(errno)); 544227825Stheraven jp = makejob(n, 1); 545227825Stheraven if (forkshell(jp, n, FORK_NOJOB) == 0) { 546227825Stheraven FORCEINTON; 547227825Stheraven close(pip[0]); 548227825Stheraven if (pip[1] != 1) { 549227825Stheraven dup2(pip[1], 1); 550227825Stheraven close(pip[1]); 551227825Stheraven } 552227825Stheraven evaltree(n, EV_EXIT); 553227825Stheraven } 554227825Stheraven close(pip[1]); 555227825Stheraven result->fd = pip[0]; 556227825Stheraven result->jp = jp; 557227825Stheraven } 558227825Stheravenout: 559227825Stheraven popstackmark(&smark); 560227825Stheraven TRACE(("evalbackcmd done: fd=%d buf=%p nleft=%d jp=%p\n", 561227825Stheraven result->fd, result->buf, result->nleft, result->jp)); 562227825Stheraven} 563227825Stheraven 564227825Stheraven 565227825Stheraven 566227825Stheraven/* 567227825Stheraven * Execute a simple command. 568227825Stheraven */ 569227825Stheraven 570227825StheravenSTATIC void 571227825Stheravenevalcommand(union node *cmd, int flags, struct backcmd *backcmd) 572227825Stheraven{ 573227825Stheraven struct stackmark smark; 574227825Stheraven union node *argp; 575227825Stheraven struct arglist arglist; 576227825Stheraven struct arglist varlist; 577227825Stheraven char **argv; 578227825Stheraven int argc; 579227825Stheraven char **envp; 580227825Stheraven int varflag; 581227825Stheraven struct strlist *sp; 582227825Stheraven int mode; 583227825Stheraven int pip[2]; 584227825Stheraven struct cmdentry cmdentry; 585227825Stheraven struct job *jp; 586227825Stheraven struct jmploc jmploc; 587227825Stheraven struct jmploc *volatile savehandler; 588227825Stheraven char *volatile savecmdname; 589227825Stheraven volatile struct shparam saveparam; 590227825Stheraven struct localvar *volatile savelocalvars; 591227825Stheraven volatile int e; 592227825Stheraven char *lastarg; 593227825Stheraven int realstatus; 594227825Stheraven int do_clearcmdentry; 595227825Stheraven#if __GNUC__ 596227825Stheraven /* Avoid longjmp clobbering */ 597227825Stheraven (void) &argv; 598227825Stheraven (void) &argc; 599227825Stheraven (void) &lastarg; 600227825Stheraven (void) &flags; 601227825Stheraven (void) &do_clearcmdentry; 602227825Stheraven#endif 603227825Stheraven 604227825Stheraven /* First expand the arguments. */ 605227825Stheraven TRACE(("evalcommand(%p, %d) called\n", (void *)cmd, flags)); 606227825Stheraven setstackmark(&smark); 607227825Stheraven arglist.lastp = &arglist.list; 608227825Stheraven varlist.lastp = &varlist.list; 609227825Stheraven varflag = 1; 610227825Stheraven do_clearcmdentry = 0; 611227825Stheraven oexitstatus = exitstatus; 612227825Stheraven exitstatus = 0; 613227825Stheraven for (argp = cmd->ncmd.args ; argp ; argp = argp->narg.next) { 614227825Stheraven char *p = argp->narg.text; 615227825Stheraven if (varflag && is_name(*p)) { 616227825Stheraven do { 617227825Stheraven p++; 618227825Stheraven } while (is_in_name(*p)); 619227825Stheraven if (*p == '=') { 620249998Sdim expandarg(argp, &varlist, EXP_VARTILDE); 621227825Stheraven continue; 622227825Stheraven } 623227825Stheraven } 624227825Stheraven expandarg(argp, &arglist, EXP_FULL | EXP_TILDE); 625227825Stheraven varflag = 0; 626227825Stheraven } 627227825Stheraven *arglist.lastp = NULL; 628227825Stheraven *varlist.lastp = NULL; 629227825Stheraven expredir(cmd->ncmd.redirect); 630227825Stheraven argc = 0; 631227825Stheraven for (sp = arglist.list ; sp ; sp = sp->next) 632227825Stheraven argc++; 633227825Stheraven argv = stalloc(sizeof (char *) * (argc + 1)); 634227825Stheraven 635227825Stheraven for (sp = arglist.list ; sp ; sp = sp->next) { 636227825Stheraven TRACE(("evalcommand arg: %s\n", sp->text)); 637227825Stheraven *argv++ = sp->text; 638227825Stheraven } 639227825Stheraven *argv = NULL; 640227825Stheraven lastarg = NULL; 641227825Stheraven if (iflag && funcnest == 0 && argc > 0) 642227825Stheraven lastarg = argv[-1]; 643227825Stheraven argv -= argc; 644227825Stheraven 645227825Stheraven /* Print the command if xflag is set. */ 646227825Stheraven if (xflag) { 647227825Stheraven char sep = 0; 648227825Stheraven out2str(ps4val()); 649227825Stheraven for (sp = varlist.list ; sp ; sp = sp->next) { 650227825Stheraven if (sep != 0) 651227825Stheraven outc(' ', &errout); 652227825Stheraven out2str(sp->text); 653249998Sdim sep = ' '; 654227825Stheraven } 655227825Stheraven for (sp = arglist.list ; sp ; sp = sp->next) { 656227825Stheraven if (sep != 0) 657227825Stheraven outc(' ', &errout); 658227825Stheraven out2str(sp->text); 659227825Stheraven sep = ' '; 660227825Stheraven } 661227825Stheraven outc('\n', &errout); 662227825Stheraven flushout(&errout); 663227825Stheraven } 664227825Stheraven 665227825Stheraven /* Now locate the command. */ 666227825Stheraven if (argc == 0) { 667227825Stheraven /* Variable assignment(s) without command */ 668227825Stheraven cmdentry.cmdtype = CMDBUILTIN; 669227825Stheraven cmdentry.u.index = BLTINCMD; 670227825Stheraven cmdentry.special = 1; 671227825Stheraven } else { 672227825Stheraven static const char PATH[] = "PATH="; 673227825Stheraven char *path = pathval(); 674227825Stheraven 675227825Stheraven /* 676227825Stheraven * Modify the command lookup path, if a PATH= assignment 677227825Stheraven * is present 678227825Stheraven */ 679227825Stheraven for (sp = varlist.list ; sp ; sp = sp->next) 680227825Stheraven if (strncmp(sp->text, PATH, sizeof(PATH) - 1) == 0) { 681227825Stheraven path = sp->text + sizeof(PATH) - 1; 682227825Stheraven /* 683227825Stheraven * On `PATH=... command`, we need to make 684227825Stheraven * sure that the command isn't using the 685227825Stheraven * non-updated hash table of the outer PATH 686249998Sdim * setting and we need to make sure that 687227825Stheraven * the hash table isn't filled with items 688227825Stheraven * from the temporary setting. 689227825Stheraven * 690227825Stheraven * It would be better to forbit using and 691227825Stheraven * updating the table while this command 692227825Stheraven * runs, by the command finding mechanism 693227825Stheraven * is heavily integrated with hash handling, 694227825Stheraven * so we just delete the hash before and after 695227825Stheraven * the command runs. Partly deleting like 696227825Stheraven * changepatch() does doesn't seem worth the 697227825Stheraven * bookinging effort, since most such runs add 698227825Stheraven * directories in front of the new PATH. 699227825Stheraven */ 700227825Stheraven clearcmdentry(0); 701227825Stheraven do_clearcmdentry = 1; 702227825Stheraven } 703227825Stheraven 704227825Stheraven find_command(argv[0], &cmdentry, 1, path); 705227825Stheraven if (cmdentry.cmdtype == CMDUNKNOWN) { /* command not found */ 706227825Stheraven exitstatus = 127; 707227825Stheraven flushout(&errout); 708227825Stheraven return; 709227825Stheraven } 710227825Stheraven /* implement the bltin builtin here */ 711227825Stheraven if (cmdentry.cmdtype == CMDBUILTIN && cmdentry.u.index == BLTINCMD) { 712227825Stheraven for (;;) { 713227825Stheraven argv++; 714227825Stheraven if (--argc == 0) 715227825Stheraven break; 716227825Stheraven if ((cmdentry.u.index = find_builtin(*argv, 717227825Stheraven &cmdentry.special)) < 0) { 718227825Stheraven outfmt(&errout, "%s: not found\n", *argv); 719227825Stheraven exitstatus = 127; 720227825Stheraven flushout(&errout); 721227825Stheraven return; 722249998Sdim } 723227825Stheraven if (cmdentry.u.index != BLTINCMD) 724227825Stheraven break; 725227825Stheraven } 726227825Stheraven } 727227825Stheraven } 728227825Stheraven 729227825Stheraven /* Fork off a child process if necessary. */ 730227825Stheraven if (cmd->ncmd.backgnd 731227825Stheraven || (cmdentry.cmdtype == CMDNORMAL 732227825Stheraven && ((flags & EV_EXIT) == 0 || Tflag)) 733227825Stheraven || ((flags & EV_BACKCMD) != 0 734227825Stheraven && (cmdentry.cmdtype != CMDBUILTIN 735227825Stheraven || cmdentry.u.index == CDCMD 736227825Stheraven || cmdentry.u.index == DOTCMD 737227825Stheraven || cmdentry.u.index == EVALCMD)) 738227825Stheraven || (cmdentry.cmdtype == CMDBUILTIN && 739227825Stheraven cmdentry.u.index == COMMANDCMD)) { 740227825Stheraven jp = makejob(cmd, 1); 741227825Stheraven mode = cmd->ncmd.backgnd; 742227825Stheraven if (flags & EV_BACKCMD) { 743227825Stheraven mode = FORK_NOJOB; 744227825Stheraven if (pipe(pip) < 0) 745227825Stheraven error("Pipe call failed: %s", strerror(errno)); 746227825Stheraven } 747227825Stheraven if (forkshell(jp, cmd, mode) != 0) 748227825Stheraven goto parent; /* at end of routine */ 749227825Stheraven if (flags & EV_BACKCMD) { 750227825Stheraven FORCEINTON; 751227825Stheraven close(pip[0]); 752227825Stheraven if (pip[1] != 1) { 753227825Stheraven dup2(pip[1], 1); 754227825Stheraven close(pip[1]); 755227825Stheraven } 756227825Stheraven } 757227825Stheraven flags |= EV_EXIT; 758227825Stheraven } 759227825Stheraven 760227825Stheraven /* This is the child process if a fork occurred. */ 761249998Sdim /* Execute the command. */ 762227825Stheraven if (cmdentry.cmdtype == CMDFUNCTION) { 763227825Stheraven#ifdef DEBUG 764227825Stheraven trputs("Shell function: "); trargs(argv); 765227825Stheraven#endif 766227825Stheraven redirect(cmd->ncmd.redirect, REDIR_PUSH); 767227825Stheraven saveparam = shellparam; 768227825Stheraven shellparam.malloc = 0; 769227825Stheraven shellparam.reset = 1; 770227825Stheraven shellparam.nparam = argc - 1; 771227825Stheraven shellparam.p = argv + 1; 772227825Stheraven shellparam.optnext = NULL; 773227825Stheraven INTOFF; 774227825Stheraven savelocalvars = localvars; 775227825Stheraven localvars = NULL; 776227825Stheraven INTON; 777227825Stheraven if (setjmp(jmploc.loc)) { 778227825Stheraven if (exception == EXSHELLPROC) 779227825Stheraven freeparam((struct shparam *)&saveparam); 780227825Stheraven else { 781227825Stheraven freeparam(&shellparam); 782227825Stheraven shellparam = saveparam; 783227825Stheraven } 784227825Stheraven poplocalvars(); 785227825Stheraven localvars = savelocalvars; 786227825Stheraven handler = savehandler; 787227825Stheraven longjmp(handler->loc, 1); 788227825Stheraven } 789227825Stheraven savehandler = handler; 790249998Sdim handler = &jmploc; 791227825Stheraven for (sp = varlist.list ; sp ; sp = sp->next) 792227825Stheraven mklocal(sp->text); 793227825Stheraven funcnest++; 794227825Stheraven exitstatus = oexitstatus; 795227825Stheraven if (flags & EV_TESTED) 796227825Stheraven evaltree(cmdentry.u.func, EV_TESTED); 797227825Stheraven else 798227825Stheraven evaltree(cmdentry.u.func, 0); 799227825Stheraven funcnest--; 800227825Stheraven INTOFF; 801227825Stheraven poplocalvars(); 802243673Stheraven localvars = savelocalvars; 803227825Stheraven freeparam(&shellparam); 804227825Stheraven shellparam = saveparam; 805227825Stheraven handler = savehandler; 806227825Stheraven popredir(); 807227825Stheraven INTON; 808227825Stheraven if (evalskip == SKIPFUNC) { 809227825Stheraven evalskip = 0; 810227825Stheraven skipcount = 0; 811227825Stheraven } 812227825Stheraven if (flags & EV_EXIT) 813227825Stheraven exitshell(exitstatus); 814227825Stheraven } else if (cmdentry.cmdtype == CMDBUILTIN) { 815227825Stheraven#ifdef DEBUG 816243673Stheraven trputs("builtin command: "); trargs(argv); 817227825Stheraven#endif 818227825Stheraven mode = (cmdentry.u.index == EXECCMD)? 0 : REDIR_PUSH; 819227825Stheraven if (flags == EV_BACKCMD) { 820243673Stheraven memout.nleft = 0; 821227825Stheraven memout.nextc = memout.buf; 822227825Stheraven memout.bufsize = 64; 823243673Stheraven mode |= REDIR_BACKQ; 824241903Sdim } 825246487Stheraven savecmdname = commandname; 826241903Sdim cmdenviron = varlist.list; 827246487Stheraven e = -1; 828241903Sdim if (setjmp(jmploc.loc)) { 829227825Stheraven e = exception; 830227825Stheraven exitstatus = (e == EXINT)? SIGINT+128 : 2; 831232950Stheraven goto cmddone; 832232950Stheraven } 833227825Stheraven savehandler = handler; 834227825Stheraven handler = &jmploc; 835227825Stheraven redirect(cmd->ncmd.redirect, mode); 836243673Stheraven if (cmdentry.special) 837227825Stheraven listsetvar(cmdenviron); 838227825Stheraven commandname = argv[0]; 839227825Stheraven argptr = argv + 1; 840227825Stheraven optptr = NULL; /* initialize nextopt */ 841243673Stheraven exitstatus = (*builtinfunc[cmdentry.u.index])(argc, argv); 842227825Stheraven flushall(); 843227825Stheravencmddone: 844227825Stheraven cmdenviron = NULL; 845243673Stheraven out1 = &output; 846227825Stheraven out2 = &errout; 847227825Stheraven freestdout(); 848227825Stheraven if (e != EXSHELLPROC) { 849227825Stheraven commandname = savecmdname; 850227825Stheraven if (flags & EV_EXIT) { 851227825Stheraven exitshell(exitstatus); 852227825Stheraven } 853227825Stheraven } 854227825Stheraven handler = savehandler; 855227825Stheraven if (e != -1) { 856227825Stheraven if ((e != EXERROR && e != EXEXEC) 857227825Stheraven || cmdentry.special) 858227825Stheraven exraise(e); 859227825Stheraven FORCEINTON; 860227825Stheraven } 861249998Sdim if (cmdentry.u.index != EXECCMD) 862227825Stheraven popredir(); 863227825Stheraven if (flags == EV_BACKCMD) { 864227825Stheraven backcmd->buf = memout.buf; 865227825Stheraven backcmd->nleft = memout.nextc - memout.buf; 866227825Stheraven memout.buf = NULL; 867227825Stheraven } 868227825Stheraven } else { 869227825Stheraven#ifdef DEBUG 870227825Stheraven trputs("normal command: "); trargs(argv); 871227825Stheraven#endif 872241903Sdim clearredir(); 873227825Stheraven redirect(cmd->ncmd.redirect, 0); 874241903Sdim for (sp = varlist.list ; sp ; sp = sp->next) 875227825Stheraven setvareq(sp->text, VEXPORT|VSTACK); 876227825Stheraven envp = environment(); 877227825Stheraven shellexec(argv, envp, pathval(), cmdentry.u.index); 878227825Stheraven /*NOTREACHED*/ 879227825Stheraven } 880227825Stheraven goto out; 881227825Stheraven 882227825Stheravenparent: /* parent process gets here (if we forked) */ 883227825Stheraven if (mode == 0) { /* argument to fork */ 884227825Stheraven INTOFF; 885241903Sdim exitstatus = waitforjob(jp, &realstatus); 886241903Sdim INTON; 887243673Stheraven if (iflag && loopnest > 0 && WIFSIGNALED(realstatus)) { 888243673Stheraven evalskip = SKIPBREAK; 889243673Stheraven skipcount = loopnest; 890243673Stheraven } 891241903Sdim } else if (mode == 2) { 892241903Sdim backcmd->fd = pip[0]; 893241903Sdim close(pip[1]); 894241903Sdim backcmd->jp = jp; 895241903Sdim } 896241903Sdim 897241903Sdimout: 898243673Stheraven if (lastarg) 899227825Stheraven setvar("_", lastarg, 0); 900227825Stheraven if (do_clearcmdentry) 901227825Stheraven clearcmdentry(0); 902249998Sdim popstackmark(&smark); 903227825Stheraven} 904227825Stheraven 905227825Stheraven 906227825Stheraven 907227825Stheraven/* 908227825Stheraven * Search for a command. This is called before we fork so that the 909227825Stheraven * location of the command will be available in the parent as well as 910227825Stheraven * the child. The check for "goodname" is an overly conservative 911227825Stheraven * check that the name will not be subject to expansion. 912227825Stheraven */ 913227825Stheraven 914227825StheravenSTATIC void 915227825Stheravenprehash(union node *n) 916227825Stheraven{ 917227825Stheraven struct cmdentry entry; 918227825Stheraven 919227825Stheraven if (n && n->type == NCMD && n->ncmd.args) 920227825Stheraven if (goodname(n->ncmd.args->narg.text)) 921227825Stheraven find_command(n->ncmd.args->narg.text, &entry, 0, 922227825Stheraven pathval()); 923227825Stheraven} 924227825Stheraven 925227825Stheraven 926227825Stheraven 927227825Stheraven/* 928227825Stheraven * Builtin commands. Builtin commands whose functions are closely 929227825Stheraven * tied to evaluation are implemented here. 930227825Stheraven */ 931227825Stheraven 932227825Stheraven/* 933227825Stheraven * No command given, or a bltin command with no arguments. 934227825Stheraven */ 935227825Stheraven 936227825Stheravenint 937227825Stheravenbltincmd(int argc __unused, char **argv __unused) 938227825Stheraven{ 939227825Stheraven /* 940227825Stheraven * Preserve exitstatus of a previous possible redirection 941227825Stheraven * as POSIX mandates 942227825Stheraven */ 943227825Stheraven return exitstatus; 944227825Stheraven} 945227825Stheraven 946227825Stheraven 947227825Stheraven/* 948227825Stheraven * Handle break and continue commands. Break, continue, and return are 949227825Stheraven * all handled by setting the evalskip flag. The evaluation routines 950227825Stheraven * above all check this flag, and if it is set they start skipping 951227825Stheraven * commands rather than executing them. The variable skipcount is 952227825Stheraven * the number of loops to break/continue, or the number of function 953227825Stheraven * levels to return. (The latter is always 1.) It should probably 954227825Stheraven * be an error to break out of more loops than exist, but it isn't 955227825Stheraven * in the standard shell so we don't make it one here. 956227825Stheraven */ 957227825Stheraven 958227825Stheravenint 959227825Stheravenbreakcmd(int argc, char **argv) 960227825Stheraven{ 961227825Stheraven int n = argc > 1 ? number(argv[1]) : 1; 962227825Stheraven 963227825Stheraven if (n > loopnest) 964227825Stheraven n = loopnest; 965227825Stheraven if (n > 0) { 966227825Stheraven evalskip = (**argv == 'c')? SKIPCONT : SKIPBREAK; 967227825Stheraven skipcount = n; 968227825Stheraven } 969227825Stheraven return 0; 970227825Stheraven} 971227825Stheraven 972227825Stheraven/* 973227825Stheraven * The `command' command. 974227825Stheraven */ 975227825Stheravenint 976227825Stheravencommandcmd(int argc, char **argv) 977227825Stheraven{ 978227825Stheraven static char stdpath[] = _PATH_STDPATH; 979227825Stheraven struct jmploc loc, *old; 980227825Stheraven struct strlist *sp; 981227825Stheraven char *path; 982227825Stheraven int ch; 983227825Stheraven int cmd = -1; 984227825Stheraven 985227825Stheraven for (sp = cmdenviron; sp ; sp = sp->next) 986227825Stheraven setvareq(sp->text, VEXPORT|VSTACK); 987227825Stheraven path = pathval(); 988227825Stheraven 989227825Stheraven optind = optreset = 1; 990227825Stheraven opterr = 0; 991227825Stheraven while ((ch = getopt(argc, argv, "pvV")) != -1) { 992227825Stheraven switch (ch) { 993227825Stheraven case 'p': 994227825Stheraven path = stdpath; 995227825Stheraven break; 996227825Stheraven case 'v': 997227825Stheraven cmd = TYPECMD_SMALLV; 998227825Stheraven break; 999227825Stheraven case 'V': 1000227825Stheraven cmd = TYPECMD_BIGV; 1001227825Stheraven break; 1002227825Stheraven case '?': 1003227825Stheraven default: 1004227825Stheraven error("unknown option: -%c", optopt); 1005227825Stheraven } 1006227825Stheraven } 1007227825Stheraven argc -= optind; 1008227825Stheraven argv += optind; 1009227825Stheraven 1010227825Stheraven if (cmd != -1) { 1011227825Stheraven if (argc != 1) 1012227825Stheraven error("wrong number of arguments"); 1013227825Stheraven return typecmd_impl(2, argv - 1, cmd); 1014227825Stheraven } 1015227825Stheraven if (argc != 0) { 1016227825Stheraven old = handler; 1017227825Stheraven handler = &loc; 1018227825Stheraven if (setjmp(handler->loc) == 0) 1019227825Stheraven shellexec(argv, environment(), path, 0); 1020227825Stheraven handler = old; 1021227825Stheraven if (exception == EXEXEC) 1022227825Stheraven exit(exerrno); 1023227825Stheraven exraise(exception); 1024227825Stheraven } 1025227825Stheraven 1026227825Stheraven /* 1027241903Sdim * Do nothing successfully if no command was specified; 1028227825Stheraven * ksh also does this. 1029227825Stheraven */ 1030227825Stheraven exit(0); 1031227825Stheraven} 1032241903Sdim 1033227825Stheraven 1034227825Stheraven/* 1035227825Stheraven * The return command. 1036227825Stheraven */ 1037241903Sdim 1038227825Stheravenint 1039227825Stheravenreturncmd(int argc, char **argv) 1040227825Stheraven{ 1041227825Stheraven int ret = argc > 1 ? number(argv[1]) : oexitstatus; 1042241903Sdim 1043227825Stheraven if (funcnest) { 1044227825Stheraven evalskip = SKIPFUNC; 1045227825Stheraven skipcount = 1; 1046227825Stheraven } else { 1047241903Sdim /* skip the rest of the file */ 1048227825Stheraven evalskip = SKIPFILE; 1049227825Stheraven skipcount = 1; 1050227825Stheraven } 1051227825Stheraven return ret; 1052241903Sdim} 1053227825Stheraven 1054227825Stheraven 1055227825Stheravenint 1056227825Stheravenfalsecmd(int argc __unused, char **argv __unused) 1057241903Sdim{ 1058227825Stheraven return 1; 1059227825Stheraven} 1060227825Stheraven 1061227825Stheraven 1062241903Sdimint 1063227825Stheraventruecmd(int argc __unused, char **argv __unused) 1064227825Stheraven{ 1065227825Stheraven return 0; 1066241903Sdim} 1067241903Sdim 1068241903Sdim 1069241903Sdimint 1070227825Stheravenexeccmd(int argc, char **argv) 1071227825Stheraven{ 1072241903Sdim if (argc > 1) { 1073227825Stheraven struct strlist *sp; 1074227825Stheraven 1075227825Stheraven iflag = 0; /* exit on error */ 1076227825Stheraven mflag = 0; 1077227825Stheraven optschanged(); 1078227825Stheraven for (sp = cmdenviron; sp ; sp = sp->next) 1079227825Stheraven setvareq(sp->text, VEXPORT|VSTACK); 1080227825Stheraven shellexec(argv + 1, environment(), pathval(), 0); 1081227825Stheraven 1082227825Stheraven } 1083227825Stheraven return 0; 1084227825Stheraven} 1085227825Stheraven 1086227825Stheraven 1087227825Stheravenint 1088227825Stheraventimescmd(int argc __unused, char **argv __unused) 1089227825Stheraven{ 1090227825Stheraven struct rusage ru; 1091227825Stheraven long shumins, shsmins, chumins, chsmins; 1092227825Stheraven double shusecs, shssecs, chusecs, chssecs; 1093227825Stheraven 1094227825Stheraven if (getrusage(RUSAGE_SELF, &ru) < 0) 1095227825Stheraven return 1; 1096227825Stheraven shumins = ru.ru_utime.tv_sec / 60; 1097227825Stheraven shusecs = ru.ru_utime.tv_sec % 60 + ru.ru_utime.tv_usec / 1000000.; 1098227825Stheraven shsmins = ru.ru_stime.tv_sec / 60; 1099227825Stheraven shssecs = ru.ru_stime.tv_sec % 60 + ru.ru_stime.tv_usec / 1000000.; 1100227825Stheraven if (getrusage(RUSAGE_CHILDREN, &ru) < 0) 1101227825Stheraven return 1; 1102227825Stheraven chumins = ru.ru_utime.tv_sec / 60; 1103227825Stheraven chusecs = ru.ru_utime.tv_sec % 60 + ru.ru_utime.tv_usec / 1000000.; 1104227825Stheraven chsmins = ru.ru_stime.tv_sec / 60; 1105227825Stheraven chssecs = ru.ru_stime.tv_sec % 60 + ru.ru_stime.tv_usec / 1000000.; 1106227825Stheraven out1fmt("%ldm%.3fs %ldm%.3fs\n%ldm%.3fs %ldm%.3fs\n", shumins, 1107227825Stheraven shusecs, shsmins, shssecs, chumins, chusecs, chsmins, chssecs); 1108227825Stheraven return 0; 1109227825Stheraven} 1110227825Stheraven