VirtualBox

source: kBuild/trunk/src/kash/eval.c@ 3454

Last change on this file since 3454 was 3453, checked in by bird, 5 years ago

kash: eval: Call expredircleanup before we pop the allocator stack.

  • Property svn:eol-style set to LF
  • Property svn:keywords set to Id
File size: 35.4 KB
Line 
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#if 0
36#ifndef lint
37static char sccsid[] = "@(#)eval.c 8.9 (Berkeley) 6/8/95";
38#else
39__RCSID("$NetBSD: eval.c,v 1.84 2005/06/23 23:05:29 christos Exp $");
40#endif /* not lint */
41#endif
42
43#include <assert.h>
44#include <stddef.h>
45#include <stdlib.h>
46#include <stdio.h>
47#include <sys/types.h>
48#ifdef HAVE_SYSCTL_H
49# ifdef __OpenBSD__ /* joyful crap */
50# include <sys/param.h>
51# undef psh
52# endif
53# include <sys/sysctl.h>
54#endif
55
56/*
57 * Evaluate a command.
58 */
59
60#include "shell.h"
61#include "nodes.h"
62#include "syntax.h"
63#include "expand.h"
64#include "parser.h"
65#include "jobs.h"
66#include "eval.h"
67#include "builtins.h"
68#include "options.h"
69#include "exec.h"
70#include "redir.h"
71#include "input.h"
72#include "output.h"
73#include "trap.h"
74#include "var.h"
75#include "memalloc.h"
76#include "error.h"
77#include "show.h"
78#include "mystring.h"
79#include "main.h"
80#ifndef SMALL
81# include "myhistedit.h"
82#endif
83#include "shinstance.h"
84
85
86/* flags in argument to evaltree */
87#define EV_EXIT 01 /* exit after evaluating tree */
88#define EV_TESTED 02 /* exit status is checked; ignore -e flag */
89#define EV_BACKCMD 04 /* command executing within back quotes */
90
91/*int evalskip;*/ /* set if we are skipping commands */
92/*STATIC int skipcount;*/ /* number of levels to skip */
93/*MKINIT int loopnest;*/ /* current loop nesting level */
94/*int funcnest;*/ /* depth of function calls */
95
96
97/*char *commandname;*/
98/*struct strlist *cmdenviron;*/
99/*int exitstatus;*/ /* exit status of last command */
100/*int back_exitstatus;*/ /* exit status of backquoted command */
101
102
103STATIC void evalloop(shinstance *, union node *, int);
104STATIC void evalfor(shinstance *, union node *, int);
105STATIC void evalcase(shinstance *, union node *, int);
106STATIC void evalsubshell(shinstance *, union node *, int);
107STATIC unsigned expredir(shinstance *, union node *);
108STATIC void evalpipe(shinstance *, union node *);
109STATIC void evalcommand(shinstance *, union node *, int, struct backcmd *);
110STATIC void prehash(shinstance *, union node *);
111
112
113/*
114 * Called to reset things after an exception.
115 */
116
117#ifdef mkinit
118INCLUDE "eval.h"
119
120RESET {
121 psh->evalskip = 0;
122 psh->loopnest = 0;
123 psh->funcnest = 0;
124}
125
126SHELLPROC {
127 psh->exitstatus = 0;
128}
129#endif
130
131static int
132sh_pipe(shinstance *psh, int fds[2])
133{
134 int nfd;
135
136 if (shfile_pipe(&psh->fdtab, fds))
137 return -1;
138
139 if (fds[0] < 3) {
140 nfd = shfile_fcntl(&psh->fdtab, fds[0], F_DUPFD, 3);
141 if (nfd != -1) {
142 shfile_close(&psh->fdtab, fds[0]);
143 fds[0] = nfd;
144 }
145 }
146
147 if (fds[1] < 3) {
148 nfd = shfile_fcntl(&psh->fdtab, fds[1], F_DUPFD, 3);
149 if (nfd != -1) {
150 shfile_close(&psh->fdtab, fds[1]);
151 fds[1] = nfd;
152 }
153 }
154 return 0;
155}
156
157
158/*
159 * The eval commmand.
160 */
161
162int
163evalcmd(shinstance *psh, int argc, char **argv)
164{
165 char *p;
166 char *concat;
167 char **ap;
168
169 if (argc > 1) {
170 p = argv[1];
171 if (argc > 2) {
172 STARTSTACKSTR(psh, concat);
173 ap = argv + 2;
174 for (;;) {
175 while (*p)
176 STPUTC(psh, *p++, concat);
177 if ((p = *ap++) == NULL)
178 break;
179 STPUTC(psh, ' ', concat);
180 }
181 STPUTC(psh, '\0', concat);
182 p = grabstackstr(psh, concat);
183 }
184 evalstring(psh, p, EV_TESTED);
185 }
186 return psh->exitstatus;
187}
188
189
190/*
191 * Execute a command or commands contained in a string.
192 */
193
194void
195evalstring(shinstance *psh, char *s, int flag)
196{
197 union node *n;
198 struct stackmark smark;
199
200 setstackmark(psh, &smark);
201 setinputstring(psh, s, 1);
202
203 while ((n = parsecmd(psh, 0)) != NEOF) {
204 evaltree(psh, n, flag);
205 popstackmark(psh, &smark);
206 }
207 popfile(psh);
208 popstackmark(psh, &smark);
209}
210
211
212
213/*
214 * Evaluate a parse tree. The value is left in the global variable
215 * exitstatus.
216 */
217
218void
219evaltree(shinstance *psh, union node *n, int flags)
220{
221 if (n == NULL) {
222 TRACE((psh, "evaltree(NULL) called\n"));
223 psh->exitstatus = 0;
224 goto out;
225 }
226#ifndef SMALL
227 psh->displayhist = 1; /* show history substitutions done with fc */
228#endif
229 TRACE((psh, "pid %" SHPID_PRI ", evaltree(%p: %d, %d) called\n",
230 sh_getpid(psh), n, n->type, flags));
231 switch (n->type) {
232 case NSEMI:
233 evaltree(psh, n->nbinary.ch1, flags & EV_TESTED);
234 if (psh->evalskip)
235 goto out;
236 evaltree(psh, n->nbinary.ch2, flags);
237 break;
238 case NAND:
239 evaltree(psh, n->nbinary.ch1, EV_TESTED);
240 if (psh->evalskip || psh->exitstatus != 0)
241 goto out;
242 evaltree(psh, n->nbinary.ch2, flags);
243 break;
244 case NOR:
245 evaltree(psh, n->nbinary.ch1, EV_TESTED);
246 if (psh->evalskip || psh->exitstatus == 0)
247 goto out;
248 evaltree(psh, n->nbinary.ch2, flags);
249 break;
250 case NREDIR: {
251 unsigned const oldfnames = expredir(psh, n->nredir.redirect);
252 redirect(psh, n->nredir.redirect, REDIR_PUSH);
253 evaltree(psh, n->nredir.n, flags);
254 popredir(psh);
255 expredircleanup(psh, oldfnames);
256 break;
257 }
258 case NSUBSHELL:
259 evalsubshell(psh, n, flags);
260 break;
261 case NBACKGND:
262 evalsubshell(psh, n, flags);
263 break;
264 case NIF: {
265 evaltree(psh, n->nif.test, EV_TESTED);
266 if (psh->evalskip)
267 goto out;
268 if (psh->exitstatus == 0)
269 evaltree(psh, n->nif.ifpart, flags);
270 else if (n->nif.elsepart)
271 evaltree(psh, n->nif.elsepart, flags);
272 else
273 psh->exitstatus = 0;
274 break;
275 }
276 case NWHILE:
277 case NUNTIL:
278 evalloop(psh, n, flags);
279 break;
280 case NFOR:
281 evalfor(psh, n, flags);
282 break;
283 case NCASE:
284 evalcase(psh, n, flags);
285 break;
286 case NDEFUN:
287 defun(psh, n->narg.text, n->narg.next);
288 psh->exitstatus = 0;
289 break;
290 case NNOT:
291 evaltree(psh, n->nnot.com, EV_TESTED);
292 psh->exitstatus = !psh->exitstatus;
293 break;
294 case NPIPE:
295 evalpipe(psh, n);
296 break;
297 case NCMD:
298 evalcommand(psh, n, flags, (struct backcmd *)NULL);
299 break;
300 default:
301 out1fmt(psh, "Node type = %d\n", n->type);
302 flushout(&psh->output);
303 break;
304 }
305out:
306 if (psh->pendingsigs)
307 dotrap(psh);
308 if ((flags & EV_EXIT) != 0)
309 exitshell(psh, psh->exitstatus);
310}
311
312
313STATIC void
314evalloop(shinstance *psh, union node *n, int flags)
315{
316 int status;
317
318 psh->loopnest++;
319 status = 0;
320 for (;;) {
321 evaltree(psh, n->nbinary.ch1, EV_TESTED);
322 if (psh->evalskip) {
323skipping: if (psh->evalskip == SKIPCONT && --psh->skipcount <= 0) {
324 psh->evalskip = 0;
325 continue;
326 }
327 if (psh->evalskip == SKIPBREAK && --psh->skipcount <= 0)
328 psh->evalskip = 0;
329 break;
330 }
331 if (n->type == NWHILE) {
332 if (psh->exitstatus != 0)
333 break;
334 } else {
335 if (psh->exitstatus == 0)
336 break;
337 }
338 evaltree(psh, n->nbinary.ch2, flags & EV_TESTED);
339 status = psh->exitstatus;
340 if (psh->evalskip)
341 goto skipping;
342 }
343 psh->loopnest--;
344 psh->exitstatus = status;
345}
346
347
348
349STATIC void
350evalfor(shinstance *psh, union node *n, int flags)
351{
352 struct arglist arglist;
353 union node *argp;
354 struct strlist *sp;
355 struct stackmark smark;
356 int status = 0;
357
358 setstackmark(psh, &smark);
359 arglist.lastp = &arglist.list;
360 for (argp = n->nfor.args ; argp ; argp = argp->narg.next) {
361 expandarg(psh, argp, &arglist, EXP_FULL | EXP_TILDE);
362 if (psh->evalskip)
363 goto out;
364 }
365 *arglist.lastp = NULL;
366
367 psh->loopnest++;
368 for (sp = arglist.list ; sp ; sp = sp->next) {
369 setvar(psh, n->nfor.var, sp->text, 0);
370 evaltree(psh, n->nfor.body, flags & EV_TESTED);
371 status = psh->exitstatus;
372 if (psh->evalskip) {
373 if (psh->evalskip == SKIPCONT && --psh->skipcount <= 0) {
374 psh->evalskip = 0;
375 continue;
376 }
377 if (psh->evalskip == SKIPBREAK && --psh->skipcount <= 0)
378 psh->evalskip = 0;
379 break;
380 }
381 }
382 psh->loopnest--;
383 psh->exitstatus = status;
384out:
385 popstackmark(psh, &smark);
386}
387
388
389
390STATIC void
391evalcase(shinstance *psh, union node *n, int flags)
392{
393 union node *cp;
394 union node *patp;
395 struct arglist arglist;
396 struct stackmark smark;
397 int status = 0;
398
399 setstackmark(psh, &smark);
400 arglist.lastp = &arglist.list;
401 expandarg(psh, n->ncase.expr, &arglist, EXP_TILDE);
402 for (cp = n->ncase.cases ; cp && psh->evalskip == 0 ; cp = cp->nclist.next) {
403 for (patp = cp->nclist.pattern ; patp ; patp = patp->narg.next) {
404 if (casematch(psh, patp, arglist.list->text)) {
405 if (psh->evalskip == 0) {
406 evaltree(psh, cp->nclist.body, flags);
407 status = psh->exitstatus;
408 }
409 goto out;
410 }
411 }
412 }
413out:
414 psh->exitstatus = status;
415 popstackmark(psh, &smark);
416}
417
418
419#ifdef KASH_USE_FORKSHELL2
420/*
421 * Child of evalsubshell.
422 */
423struct evalsubshellchild
424{
425 int flags;
426 int backgnd;
427};
428
429static int evalsubshell_child(shinstance *psh, union node *n, void *argp)
430{
431 struct evalsubshellchild args = *(struct evalsubshellchild *)argp;
432
433 INTON;
434 if (args.backgnd)
435 args.flags &=~ EV_TESTED;
436 redirect(psh, n->nredir.redirect, 0);
437 /* never returns */
438 evaltree(psh, n->nredir.n, args.flags | EV_EXIT);
439 /** @todo make us return here. */
440 return 0;
441}
442#endif /* KASH_USE_FORKSHELL2 */
443
444
445/*
446 * Kick off a subshell to evaluate a tree.
447 */
448
449STATIC void
450evalsubshell(shinstance *psh, union node *n, int flags)
451{
452 struct job *jp;
453 int backgnd = (n->type == NBACKGND);
454 unsigned expfnamedepth;
455
456 expfnamedepth = expredir(psh, n->nredir.redirect);
457 INTOFF;
458 jp = makejob(psh, n, 1);
459#ifdef KASH_USE_FORKSHELL2
460 {
461 struct evalsubshellchild args;
462 args.flags = flags;
463 args.backgnd = backgnd;
464 forkshell2(psh, jp, n, backgnd ? FORK_BG : FORK_FG,
465 evalsubshell_child, n, &args, sizeof(args), NULL);
466 }
467#else
468 if (forkshell(psh, jp, n, backgnd ? FORK_BG : FORK_FG) == 0) {
469 INTON;
470 if (backgnd)
471 flags &=~ EV_TESTED;
472 redirect(psh, n->nredir.redirect, 0);
473 /* never returns */
474 evaltree(psh, n->nredir.n, flags | EV_EXIT);
475 }
476#endif
477 if (! backgnd)
478 psh->exitstatus = waitforjob(psh, jp);
479 expredircleanup(psh, expfnamedepth);
480 INTON;
481}
482
483
484
485/*
486 * Compute the names of the files in a redirection list.
487 */
488
489STATIC unsigned
490expredir(shinstance *psh, union node *n)
491{
492 union node *redir;
493 redirexpfnames *expfnames;
494 unsigned i;
495
496 /* We typically end up here w/o redirections. */
497 if (!n)
498 return !(expfnames = psh->expfnames) ? 0 : expfnames->depth + 1;
499
500 /* Prepare a table for the expanded names. */
501 i = 0;
502 for (redir = n; redir ; redir = redir->nfile.next)
503 i++;
504 expfnames = stalloc(psh, offsetof(redirexpfnames, names) + sizeof(expfnames->names[0]) * i);
505 expfnames->count = i;
506 TRACE2((psh, "expredir: %p: count=%u\n", expfnames, i));
507
508 /* Do the expansion. */
509 for (redir = n, i = 0 ; redir ; redir = redir->nfile.next, i++) {
510 struct arglist fn;
511 fn.lastp = &fn.list;
512 switch (redir->type) {
513 case NFROMTO:
514 case NFROM:
515 case NTO:
516 case NCLOBBER:
517 case NAPPEND:
518 expandarg(psh, redir->nfile.fname, &fn, EXP_TILDE | EXP_REDIR);
519 expfnames->names[i] = fn.list->text;
520 break;
521 case NFROMFD:
522 case NTOFD:
523 if (redir->ndup.vname) {
524 expandarg(psh, redir->ndup.vname, &fn, EXP_FULL | EXP_TILDE);
525 fixredir(psh, redir, fn.list->text, 1);
526 }
527 expfnames->names[i] = NULL;
528 break;
529 default:
530 assert(0);
531 expfnames->names[i] = NULL;
532 break;
533 }
534 }
535 assert(i == expfnames->count);
536
537 /* Do the linking at the end, as nesting happens when we expand backtick arguments. */
538 expfnames->prev = psh->expfnames;
539 psh->expfnames = expfnames;
540 return expfnames->depth = psh->expfnames ? psh->expfnames->depth + 1 : 1;
541}
542
543STATIC void
544expredircleanup(shinstance *psh, unsigned depth)
545{
546 redirexpfnames *expfnames = psh->expfnames;
547 assert(expfnames == NULL ? depth == 0 : expfnames->depth == depth || expfnames->depth + 1 == depth);
548 while (expfnames && expfnames->depth >= depth)
549 expfnames = psh->expfnames = expfnames->prev;
550}
551
552
553#ifdef KASH_USE_FORKSHELL2
554/*
555 * Child of evalpipe.
556 */
557struct evalpipechild
558{
559 int prevfd;
560 int pip[2];
561};
562
563static int evalpipe_child(shinstance *psh, union node *n, void *argp)
564{
565 struct evalpipechild args = *(struct evalpipechild *)argp;
566
567 if (args.prevfd > 0) {
568 movefd(psh, args.prevfd, 0);
569 }
570 if (args.pip[1] >= 0) {
571 shfile_close(&psh->fdtab, args.pip[0]);
572 if (args.pip[1] != 1) {
573 movefd(psh, args.pip[1], 1);
574 }
575 }
576 evaltree(psh, n, EV_EXIT);
577 /** @todo make it return thru here. */
578 return 0;
579}
580#endif /* KASH_USE_FORKSHELL2 */
581
582/*
583 * Evaluate a pipeline. All the processes in the pipeline are children
584 * of the process creating the pipeline. (This differs from some versions
585 * of the shell, which make the last process in a pipeline the parent
586 * of all the rest.)
587 */
588
589STATIC void
590evalpipe(shinstance *psh, union node *n)
591{
592 struct job *jp;
593 struct nodelist *lp;
594 int pipelen;
595 int prevfd;
596 int pip[2];
597
598 TRACE((psh, "evalpipe(0x%lx) called\n", (long)n));
599 pipelen = 0;
600 for (lp = n->npipe.cmdlist ; lp ; lp = lp->next)
601 pipelen++;
602 INTOFF;
603 jp = makejob(psh, n, pipelen);
604 prevfd = -1;
605 for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) {
606 prehash(psh, lp->n);
607 pip[1] = -1;
608 if (lp->next) {
609 if (sh_pipe(psh, pip) < 0) {
610 shfile_close(&psh->fdtab, prevfd);
611 error(psh, "Pipe call failed");
612 }
613 }
614#ifdef KASH_USE_FORKSHELL2
615 {
616 struct evalpipechild args;
617 args.prevfd = prevfd;
618 args.pip[0] = pip[0];
619 args.pip[1] = pip[1];
620 forkshell2(psh, jp, lp->n, n->npipe.backgnd ? FORK_BG : FORK_FG,
621 evalpipe_child, lp->n, &args, sizeof(args), NULL);
622 }
623#else
624 if (forkshell(psh, jp, lp->n, n->npipe.backgnd ? FORK_BG : FORK_FG) == 0) {
625 INTON;
626 if (prevfd > 0) {
627 movefd(psh, prevfd, 0);
628 }
629 if (pip[1] >= 0) {
630 shfile_close(&psh->fdtab, pip[0]);
631 if (pip[1] != 1) {
632 movefd(psh, pip[1], 1);
633 }
634 }
635 evaltree(psh, lp->n, EV_EXIT);
636 }
637#endif
638 if (prevfd >= 0)
639 shfile_close(&psh->fdtab, prevfd);
640 prevfd = pip[0];
641 shfile_close(&psh->fdtab, pip[1]);
642 }
643 if (n->npipe.backgnd == 0) {
644 psh->exitstatus = waitforjob(psh, jp);
645 TRACE((psh, "evalpipe: job done exit status %d\n", psh->exitstatus));
646 }
647 INTON;
648}
649
650#ifdef KASH_USE_FORKSHELL2
651/*
652 * evalbackcmd child.
653 */
654struct evalbackcmdchild
655{
656 int pip[2];
657};
658
659static int evalbackcmd_child(shinstance *psh, union node *n, void *argp)
660{
661 struct evalbackcmdchild args = *(struct evalbackcmdchild *)argp;
662
663 FORCEINTON;
664 shfile_close(&psh->fdtab, args.pip[0]);
665 if (args.pip[1] != 1) {
666 movefd(psh, args.pip[1], 1);
667 }
668 eflag(psh) = 0;
669 evaltree(psh, n, EV_EXIT);
670 /* NOTREACHED */ /** @todo make it return here to simplify thread handling (no need for setjmp). */
671 return 0;
672}
673#endif /* KASH_USE_FORKSHELL2 */
674
675/*
676 * Execute a command inside back quotes. If it's a builtin command, we
677 * want to save its output in a block obtained from malloc. Otherwise
678 * we fork off a subprocess and get the output of the command via a pipe.
679 * Should be called with interrupts off.
680 */
681
682void
683evalbackcmd(shinstance *psh, union node *n, struct backcmd *result)
684{
685 int pip[2];
686 struct job *jp;
687 struct stackmark smark; /* unnecessary */
688
689 setstackmark(psh, &smark);
690 result->fd = -1;
691 result->buf = NULL;
692 result->nleft = 0;
693 result->jp = NULL;
694 if (n == NULL) {
695 goto out;
696 }
697#ifdef notyet
698 /*
699 * For now we disable executing builtins in the same
700 * context as the shell, because we are not keeping
701 * enough state to recover from changes that are
702 * supposed only to affect subshells. eg. echo "`cd /`"
703 */
704 if (n->type == NCMD) {
705 psh->exitstatus = opsh->exitstatus;
706 evalcommand(psh, n, EV_BACKCMD, result);
707 } else
708#endif
709 {
710 INTOFF;
711 if (sh_pipe(psh, pip) < 0)
712 error(psh, "Pipe call failed");
713 jp = makejob(psh, n, 1);
714#ifdef KASH_USE_FORKSHELL2
715 {
716 struct evalbackcmdchild args;
717 args.pip[0] = pip[0];
718 args.pip[1] = pip[1];
719 forkshell2(psh, jp, n, FORK_NOJOB,
720 evalbackcmd_child, n, &args, sizeof(args), NULL);
721 }
722#else
723 if (forkshell(psh, jp, n, FORK_NOJOB) == 0) {
724 FORCEINTON;
725 shfile_close(&psh->fdtab, pip[0]);
726 if (pip[1] != 1) {
727 movefd(psh, pip[1], 1);
728 }
729 eflag(psh) = 0;
730 evaltree(psh, n, EV_EXIT);
731 /* NOTREACHED */
732 }
733#endif
734 shfile_close(&psh->fdtab, pip[1]);
735 result->fd = pip[0];
736 result->jp = jp;
737 INTON;
738 }
739out:
740 popstackmark(psh, &smark);
741 TRACE((psh, "evalbackcmd done: fd=%d buf=0x%x nleft=%d jp=0x%x\n",
742 result->fd, result->buf, result->nleft, result->jp));
743}
744
745static const char *
746syspath(shinstance *psh)
747{
748#ifdef CTL_USER
749 static char *sys_path = NULL;
750 static int mib[] = {CTL_USER, USER_CS_PATH};
751#endif
752#ifdef PC_PATH_SEP
753 static char def_path[] = "PATH=/usr/bin;/bin;/usr/sbin;/sbin";
754#else
755 static char def_path[] = "PATH=/usr/bin:/bin:/usr/sbin:/sbin";
756#endif
757#ifdef CTL_USER
758 size_t len;
759
760 if (sys_path == NULL) {
761 if (sysctl(mib, 2, 0, &len, 0, 0) != -1 &&
762 (sys_path = ckmalloc(psh, len + 5)) != NULL &&
763 sysctl(mib, 2, sys_path + 5, &len, 0, 0) != -1) {
764 memcpy(sys_path, "PATH=", 5);
765 } else {
766 ckfree(psh, sys_path);
767 /* something to keep things happy */
768 sys_path = def_path;
769 }
770 }
771 return sys_path;
772#else
773 return def_path;
774#endif
775}
776
777static int
778parse_command_args(shinstance *psh, int argc, char **argv, int *use_syspath)
779{
780 int sv_argc = argc;
781 char *cp, c;
782
783 *use_syspath = 0;
784
785 for (;;) {
786 argv++;
787 if (--argc == 0)
788 break;
789 cp = *argv;
790 if (*cp++ != '-')
791 break;
792 if (*cp == '-' && cp[1] == 0) {
793 argv++;
794 argc--;
795 break;
796 }
797 while ((c = *cp++)) {
798 switch (c) {
799 case 'p':
800 *use_syspath = 1;
801 break;
802 default:
803 /* run 'typecmd' for other options */
804 return 0;
805 }
806 }
807 }
808 return sv_argc - argc;
809}
810
811
812/*
813 * The split up evalcommand code:
814 * evalcommand_out, evalcommand_parent, evalcommand_doit, evalcommand_child
815 */
816/*int vforked = 0; - obsolete */
817
818/* Both child and parent exits thru here. */
819STATIC void
820evalcommand_out(shinstance *psh, int flags, char *lastarg, unsigned expfnamedepth, struct stackmark *smarkp)
821{
822 if (lastarg)
823 /* dsl: I think this is intended to be used to support
824 * '_' in 'vi' command mode during line editing...
825 * However I implemented that within libedit itself.
826 */
827 setvar(psh, "_", lastarg, 0);
828 expredircleanup(psh, expfnamedepth);
829 popstackmark(psh, smarkp);
830
831 if (eflag(psh) && psh->exitstatus && !(flags & EV_TESTED))
832 exitshell(psh, psh->exitstatus);
833}
834
835
836/* Called if we forkshell(). */
837STATIC void
838evalcommand_parent(shinstance *psh, int flags, char *lastarg, unsigned expfnamedepth, struct stackmark *smarkp,
839 int mode, struct job *jp, int pip[2], struct backcmd *backcmd)
840{
841 if (mode == FORK_FG) { /* argument to fork */
842 psh->exitstatus = waitforjob(psh, jp);
843 } else if (mode == FORK_NOJOB) {
844 backcmd->fd = pip[0];
845 shfile_close(&psh->fdtab, pip[1]);
846 backcmd->jp = jp;
847 }
848 FORCEINTON;
849
850 evalcommand_out(psh, flags, lastarg, expfnamedepth, smarkp);
851}
852
853struct evalcommanddoit
854{
855 struct stackmark smark;
856 unsigned expfnamedepth;
857
858 struct backcmd *backcmd;
859 int flags;
860 int argc;
861 char **argv;
862 char *lastarg;
863 struct arglist varlist;
864 const char *path;
865 struct cmdentry cmdentry;
866
867 /* for child stuff only: */
868 int pip[2];
869};
870
871STATIC void
872evalcommand_doit(shinstance *psh, union node *cmd, struct evalcommanddoit *args)
873{
874 struct jmploc jmploc;
875 struct jmploc *volatile savehandler;
876 struct localvar *volatile savelocalvars;
877
878 /* This is the child process if a fork occurred. */
879 /* Execute the command. */
880 switch (args->cmdentry.cmdtype) {
881 case CMDFUNCTION: {
882 volatile struct shparam saveparam;
883#ifdef DEBUG
884 trputs(psh, "Shell function: "); trargs(psh, args->argv);
885#endif
886 redirect(psh, cmd->ncmd.redirect, REDIR_PUSH);
887 saveparam = psh->shellparam;
888 psh->shellparam.malloc = 0;
889 psh->shellparam.reset = 1;
890 psh->shellparam.nparam = args->argc - 1;
891 psh->shellparam.p = args->argv + 1;
892 psh->shellparam.optnext = NULL;
893 INTOFF;
894 savelocalvars = psh->localvars;
895 psh->localvars = NULL;
896 INTON;
897 if (setjmp(jmploc.loc)) {
898 if (psh->exception == EXSHELLPROC) {
899 freeparam(psh, (volatile struct shparam *)
900 &saveparam);
901 } else {
902 freeparam(psh, &psh->shellparam);
903 psh->shellparam = saveparam;
904 }
905 poplocalvars(psh);
906 psh->localvars = savelocalvars;
907 psh->handler = savehandler;
908 longjmp(psh->handler->loc, 1);
909 }
910 savehandler = psh->handler;
911 psh->handler = &jmploc;
912 listmklocal(psh, args->varlist.list, 0);
913 /* stop shell blowing its stack */
914 if (++psh->funcnest > 1000)
915 error(psh, "too many nested function calls");
916 evaltree(psh, args->cmdentry.u.func, args->flags & EV_TESTED);
917 psh->funcnest--;
918 INTOFF;
919 poplocalvars(psh);
920 psh->localvars = savelocalvars;
921 freeparam(psh, &psh->shellparam);
922 psh->shellparam = saveparam;
923 psh->handler = savehandler;
924 popredir(psh);
925 INTON;
926 if (psh->evalskip == SKIPFUNC) {
927 psh->evalskip = 0;
928 psh->skipcount = 0;
929 }
930 if (args->flags & EV_EXIT)
931 exitshell(psh, psh->exitstatus);
932 break;
933 }
934
935 case CMDBUILTIN:
936 case CMDSPLBLTIN: {
937 volatile int temp_path = 0;
938 char *volatile savecmdname;
939 int volatile savecmdnamemalloc;
940 volatile int e;
941 int mode;
942#ifdef DEBUG
943 trputs(psh, "builtin command: "); trargs(psh, args->argv);
944#endif
945 mode = (args->cmdentry.u.bltin == execcmd) ? 0 : REDIR_PUSH;
946 if (args->flags == EV_BACKCMD) {
947 psh->memout.nleft = 0;
948 psh->memout.nextc = psh->memout.buf;
949 psh->memout.bufsize = 64;
950 mode |= REDIR_BACKQ;
951 }
952 e = -1;
953 savehandler = psh->handler;
954 savecmdname = psh->commandname;
955 savecmdnamemalloc = psh->commandnamemalloc;
956 psh->handler = &jmploc;
957 if (!setjmp(jmploc.loc)) {
958 /* We need to ensure the command hash table isn't
959 * corruped by temporary PATH assignments.
960 * However we must ensure the 'local' command works!
961 */
962 if (args->path != pathval(psh) && (args->cmdentry.u.bltin == hashcmd ||
963 args->cmdentry.u.bltin == typecmd)) {
964 savelocalvars = psh->localvars;
965 psh->localvars = 0;
966 mklocal(psh, args->path - 5 /* PATH= */, 0);
967 temp_path = 1;
968 } else
969 temp_path = 0;
970 redirect(psh, cmd->ncmd.redirect, mode);
971
972 /* exec is a special builtin, but needs this list... */
973 psh->cmdenviron = args->varlist.list;
974 /* we must check 'readonly' flag for all builtins */
975 listsetvar(psh, args->varlist.list,
976 args->cmdentry.cmdtype == CMDSPLBLTIN ? 0 : VNOSET);
977 psh->commandnamemalloc = 0;
978 psh->commandname = args->argv[0];
979 /* initialize nextopt */
980 psh->argptr = args->argv + 1;
981 psh->optptr = NULL;
982 /* and getopt */
983#if 0 /** @todo fix getop usage! */
984#if defined(__FreeBSD__) || defined(__EMX__) || defined(__APPLE__)
985 optreset = 1;
986 optind = 1;
987#else
988 optind = 0; /* init */
989#endif
990#endif
991
992 psh->exitstatus = args->cmdentry.u.bltin(psh, args->argc, args->argv);
993 } else {
994 e = psh->exception;
995 psh->exitstatus = e == EXINT ? SIGINT + 128 :
996 e == EXEXEC ? psh->exerrno : 2;
997 }
998 psh->handler = savehandler;
999 output_flushall(psh);
1000 psh->out1 = &psh->output;
1001 psh->out2 = &psh->errout;
1002 freestdout(psh);
1003 if (temp_path) {
1004 poplocalvars(psh);
1005 psh->localvars = savelocalvars;
1006 }
1007 psh->cmdenviron = NULL;
1008 if (e != EXSHELLPROC) {
1009 psh->commandname = savecmdname;
1010 psh->commandnamemalloc = savecmdnamemalloc;
1011 if (args->flags & EV_EXIT)
1012 exitshell(psh, psh->exitstatus);
1013 }
1014 if (savecmdnamemalloc)
1015 sh_free(psh, savecmdname);
1016 if (e != -1) {
1017 if ((e != EXERROR && e != EXEXEC)
1018 || args->cmdentry.cmdtype == CMDSPLBLTIN)
1019 exraise(psh, e);
1020 FORCEINTON;
1021 }
1022 if (args->cmdentry.u.bltin != execcmd)
1023 popredir(psh);
1024 if (args->flags == EV_BACKCMD) {
1025 args->backcmd->buf = psh->memout.buf;
1026 args->backcmd->nleft = (int)(psh->memout.nextc - psh->memout.buf);
1027 psh->memout.buf = NULL;
1028 }
1029 break;
1030 }
1031
1032 default: {
1033 struct strlist *sp;
1034 char **envp;
1035#ifdef DEBUG
1036 trputs(psh, "normal command: "); trargs(psh, args->argv);
1037#endif
1038 clearredir(psh);
1039 redirect(psh, cmd->ncmd.redirect, 0);
1040 for (sp = args->varlist.list ; sp ; sp = sp->next)
1041 setvareq(psh, sp->text, VEXPORT|VSTACK);
1042 envp = environment(psh);
1043 shellexec(psh, args->argv, envp, args->path, args->cmdentry.u.index);
1044 break;
1045 }
1046 }
1047
1048 evalcommand_out(psh, args->flags, args->lastarg, args->expfnamedepth, &args->smark);
1049}
1050
1051/* child callback. */
1052static int evalcommand_child(shinstance *psh, union node *cmd, void *argp)
1053{
1054 struct evalcommanddoit *args = (struct evalcommanddoit *)argp;
1055
1056 if (args->flags & EV_BACKCMD) {
1057 FORCEINTON;
1058 shfile_close(&psh->fdtab, args->pip[0]);
1059 if (args->pip[1] != 1) {
1060 movefd(psh, args->pip[1], 1);
1061 }
1062 }
1063 args->flags |= EV_EXIT;
1064
1065 evalcommand_doit(psh, cmd, args);
1066 /* not reached */ /** @todo make it return here */
1067 return 0;
1068}
1069
1070/* Copies data in the argument structure from parent to child. */
1071static void evalcommand_setup_child(shinstance *pshchild, shinstance *pshparent, void *argp)
1072{
1073 struct evalcommanddoit *args = (struct evalcommanddoit *)argp;
1074 char **argv;
1075 char **srcargv;
1076 struct strlist *sp;
1077 int argc, i;
1078
1079 setstackmark(pshchild, &args->smark);
1080
1081 /* copy arguments. */
1082 srcargv = args->argv;
1083 argc = args->argc;
1084 args->argv = argv = stalloc(pshchild, sizeof(char *) * (argc + 1));
1085 for (i = 0; i < argc; i++)
1086 argv[i] = stsavestr(pshchild, srcargv[i]);
1087 argv[argc] = NULL;
1088 if (args->lastarg)
1089 args->lastarg = argv[argc - 1];
1090
1091 /* copy variable list, checking for the 'path'. */
1092 sp = args->varlist.list;
1093 args->varlist.list = NULL;
1094 args->varlist.lastp = &args->varlist.list;
1095 for (; sp; sp = sp->next) {
1096 struct strlist *snew = (struct strlist *)stalloc(pshchild, sizeof(*snew));
1097 char *text;
1098 snew->next = NULL;
1099 snew->text = text = stsavestr(pshchild, sp->text);
1100
1101 if (&text[5] == args->path)
1102 args->path = &text[sizeof("PATH=") - 1];
1103
1104 *args->varlist.lastp = snew;
1105 args->varlist.lastp = &snew->next;
1106 }
1107
1108 if (args->path == pathval(pshparent))
1109 args->path = pathval(pshchild);
1110
1111 /* back tick command should be ignored in this codepath
1112 (flags != EV_BACKCMD as EV_EXIT is ORed in). */
1113
1114 /* If cmdentry references an internal function, we must duplicates it's nodes. */
1115 if (args->cmdentry.cmdtype == CMDFUNCTION)
1116 args->cmdentry.u.func = copyparsetree(pshchild, args->cmdentry.u.func); /** @todo isn't this duplicated already? */
1117}
1118
1119/*
1120 * Execute a simple command.
1121 */
1122
1123STATIC void
1124evalcommand(shinstance *psh, union node *cmd, int flags, struct backcmd *backcmd)
1125{
1126 struct evalcommanddoit args;
1127 char **argv;
1128 int argc;
1129
1130 union node *argp;
1131 int numvars;
1132 struct arglist arglist;
1133 struct strlist *sp;
1134 const char *path = pathval(psh);
1135
1136 /* First expand the arguments. */
1137 TRACE((psh, "evalcommand(0x%lx, %d) called\n", (long)cmd, flags));
1138 setstackmark(psh, &args.smark);
1139 psh->back_exitstatus = 0;
1140
1141 arglist.lastp = &arglist.list;
1142 /* Expand arguments, ignoring the initial 'name=value' ones */
1143 for (argp = cmd->ncmd.args, numvars = 0 ; argp ; argp = argp->narg.next, numvars++) {
1144 char *p = argp->narg.text;
1145 char ch = *p;
1146 if (is_name(ch)) {
1147 do ch = *++p;
1148 while (is_in_name(ch));
1149 if (ch == '=')
1150 continue;
1151 }
1152 break;
1153 }
1154 for (/*continue on argp from above. */ ; argp ; argp = argp->narg.next)
1155 expandarg(psh, argp, &arglist, EXP_FULL | EXP_TILDE);
1156 *arglist.lastp = NULL;
1157
1158 args.expfnamedepth = expredir(psh, cmd->ncmd.redirect);
1159
1160 /* Now do the initial 'name=value' ones we skipped above */
1161 args.varlist.lastp = &args.varlist.list;
1162 for (argp = cmd->ncmd.args ; numvars > 0 && argp ; argp = argp->narg.next, numvars--)
1163 expandarg(psh, argp, &args.varlist, EXP_VARTILDE);
1164 *args.varlist.lastp = NULL;
1165
1166 argc = 0;
1167 for (sp = arglist.list ; sp ; sp = sp->next)
1168 argc++;
1169 args.argc = argc;
1170 args.argv = argv = stalloc(psh, sizeof (char *) * (argc + 1));
1171
1172 for (sp = arglist.list ; sp ; sp = sp->next) {
1173 TRACE((psh, "evalcommand arg: %s\n", sp->text));
1174 *argv++ = sp->text;
1175 }
1176 *argv = NULL;
1177 args.lastarg = NULL;
1178 if (iflag(psh) && psh->funcnest == 0 && argc > 0)
1179 args.lastarg = argv[-1];
1180 argv -= argc;
1181
1182 /* Print the command if xflag is set. */
1183 if (xflag(psh)) {
1184 char sep = 0;
1185 out2str(psh, ps4val(psh));
1186 for (sp = args.varlist.list ; sp ; sp = sp->next) {
1187 if (sep != 0)
1188 outc(sep, &psh->errout);
1189 out2str(psh, sp->text);
1190 sep = ' ';
1191 }
1192 for (sp = arglist.list ; sp ; sp = sp->next) {
1193 if (sep != 0)
1194 outc(sep, &psh->errout);
1195 out2str(psh, sp->text);
1196 sep = ' ';
1197 }
1198 outc('\n', &psh->errout);
1199 flushout(&psh->errout);
1200 }
1201
1202 /* Now locate the command. */
1203 if (argc == 0) {
1204 args.cmdentry.cmdtype = CMDSPLBLTIN;
1205 args.cmdentry.u.bltin = bltincmd;
1206 } else {
1207 static const char PATH[] = "PATH=";
1208 int cmd_flags = DO_ERR;
1209
1210 /*
1211 * Modify the command lookup path, if a PATH= assignment
1212 * is present
1213 */
1214 for (sp = args.varlist.list; sp; sp = sp->next)
1215 if (strncmp(sp->text, PATH, sizeof(PATH) - 1) == 0)
1216 path = sp->text + sizeof(PATH) - 1;
1217
1218 do {
1219 int argsused, use_syspath;
1220 find_command(psh, argv[0], &args.cmdentry, cmd_flags, path);
1221 if (args.cmdentry.cmdtype == CMDUNKNOWN) {
1222 psh->exitstatus = 127;
1223 flushout(&psh->errout);
1224 evalcommand_out(psh, flags, args.lastarg, args.expfnamedepth, &args.smark);
1225 return;
1226 }
1227
1228 /* implement the 'command' builtin here */
1229 if (args.cmdentry.cmdtype != CMDBUILTIN ||
1230 args.cmdentry.u.bltin != bltincmd)
1231 break;
1232 cmd_flags |= DO_NOFUNC;
1233 argsused = parse_command_args(psh, argc, argv, &use_syspath);
1234 if (argsused == 0) {
1235 /* use 'type' builting to display info */
1236 args.cmdentry.u.bltin = typecmd;
1237 break;
1238 }
1239 argc -= argsused;
1240 argv += argsused;
1241 if (use_syspath)
1242 path = syspath(psh) + 5;
1243 } while (argc != 0);
1244 if (args.cmdentry.cmdtype == CMDSPLBLTIN && cmd_flags & DO_NOFUNC)
1245 /* posix mandates that 'command <splbltin>' act as if
1246 <splbltin> was a normal builtin */
1247 args.cmdentry.cmdtype = CMDBUILTIN;
1248 }
1249
1250 /* Fork off a child process if necessary. */
1251 if (cmd->ncmd.backgnd
1252 || (args.cmdentry.cmdtype == CMDNORMAL && (flags & EV_EXIT) == 0)
1253 || ( (flags & EV_BACKCMD) != 0
1254 && ( (args.cmdentry.cmdtype != CMDBUILTIN && args.cmdentry.cmdtype != CMDSPLBLTIN)
1255 || args.cmdentry.u.bltin == dotcmd
1256 || args.cmdentry.u.bltin == evalcmd))) {
1257 struct job *jp;
1258 int mode;
1259
1260 INTOFF;
1261 jp = makejob(psh, cmd, 1);
1262 mode = cmd->ncmd.backgnd;
1263 args.pip[0] = -1;
1264 args.pip[1] = -1;
1265 if (flags & EV_BACKCMD) {
1266 mode = FORK_NOJOB;
1267 if (sh_pipe(psh, args.pip) < 0)
1268 error(psh, "Pipe call failed");
1269 }
1270
1271 args.backcmd = backcmd;
1272 args.flags = flags;
1273 args.path = path;
1274#ifdef KASH_USE_FORKSHELL2
1275 forkshell2(psh, jp, cmd, mode, evalcommand_child, cmd,
1276 &args, sizeof(args), evalcommand_setup_child);
1277 evalcommand_parent(psh, flags, args.lastarg, args.expfnamedepth,
1278 &args.smark, mode, jp, args.pip, backcmd);
1279#else
1280 if (forkshell(psh, jp, cmd, mode) != 0) {
1281 evalcommand_parent(psh, flags, args.lastarg, args.expfnamedepth,
1282 &args.smark, mode, jp, args.pip, backcmd);
1283 return; /* at end of routine */
1284 }
1285 evalcommand_child(psh, cmd, &args);
1286#endif
1287 } else {
1288 args.backcmd = backcmd;
1289 args.flags = flags;
1290 args.path = path;
1291 evalcommand_doit(psh, cmd, &args);
1292 }
1293}
1294
1295
1296/*
1297 * Search for a command. This is called before we fork so that the
1298 * location of the command will be available in the parent as well as
1299 * the child. The check for "goodname" is an overly conservative
1300 * check that the name will not be subject to expansion.
1301 */
1302
1303STATIC void
1304prehash(shinstance *psh, union node *n)
1305{
1306 struct cmdentry entry;
1307
1308 if (n->type == NCMD && n->ncmd.args)
1309 if (goodname(n->ncmd.args->narg.text))
1310 find_command(psh, n->ncmd.args->narg.text, &entry, 0,
1311 pathval(psh));
1312}
1313
1314
1315
1316/*
1317 * Builtin commands. Builtin commands whose functions are closely
1318 * tied to evaluation are implemented here.
1319 */
1320
1321/*
1322 * No command given.
1323 */
1324
1325int
1326bltincmd(shinstance *psh, int argc, char **argv)
1327{
1328 /*
1329 * Preserve psh->exitstatus of a previous possible redirection
1330 * as POSIX mandates
1331 */
1332 return psh->back_exitstatus;
1333}
1334
1335
1336/*
1337 * Handle break and continue commands. Break, continue, and return are
1338 * all handled by setting the psh->evalskip flag. The evaluation routines
1339 * above all check this flag, and if it is set they start skipping
1340 * commands rather than executing them. The variable skipcount is
1341 * the number of loops to break/continue, or the number of function
1342 * levels to return. (The latter is always 1.) It should probably
1343 * be an error to break out of more loops than exist, but it isn't
1344 * in the standard shell so we don't make it one here.
1345 */
1346
1347int
1348breakcmd(shinstance *psh, int argc, char **argv)
1349{
1350 int n = argc > 1 ? number(psh, argv[1]) : 1;
1351
1352 if (n > psh->loopnest)
1353 n = psh->loopnest;
1354 if (n > 0) {
1355 psh->evalskip = (**argv == 'c')? SKIPCONT : SKIPBREAK;
1356 psh->skipcount = n;
1357 }
1358 return 0;
1359}
1360
1361
1362/*
1363 * The return command.
1364 */
1365
1366int
1367returncmd(shinstance *psh, int argc, char **argv)
1368{
1369#if 0
1370 int ret = argc > 1 ? number(psh, argv[1]) : psh->exitstatus;
1371#else
1372 int ret;
1373 if (argc > 1) {
1374 /* make return -1 and VSC lite work ... */
1375 if (argv[1][0] != '-' || !is_number(&argv[1][1]))
1376 ret = number(psh, argv[1]);
1377 else
1378 ret = -number(psh, &argv[1][1]) & 255; /* take the bash approach */
1379 } else {
1380 ret = psh->exitstatus;
1381 }
1382#endif
1383
1384 if (psh->funcnest) {
1385 psh->evalskip = SKIPFUNC;
1386 psh->skipcount = 1;
1387 return ret;
1388 }
1389 else {
1390 /* Do what ksh does; skip the rest of the file */
1391 psh->evalskip = SKIPFILE;
1392 psh->skipcount = 1;
1393 return ret;
1394 }
1395}
1396
1397
1398int
1399falsecmd(shinstance *psh, int argc, char **argv)
1400{
1401 return 1;
1402}
1403
1404
1405int
1406truecmd(shinstance *psh, int argc, char **argv)
1407{
1408 return 0;
1409}
1410
1411
1412int
1413execcmd(shinstance *psh, int argc, char **argv)
1414{
1415 if (argc > 1) {
1416 struct strlist *sp;
1417
1418 iflag(psh) = 0; /* exit on error */
1419 mflag(psh) = 0;
1420 optschanged(psh);
1421 for (sp = psh->cmdenviron; sp; sp = sp->next)
1422 setvareq(psh, sp->text, VEXPORT|VSTACK);
1423 shellexec(psh, argv + 1, environment(psh), pathval(psh), 0);
1424 }
1425 return 0;
1426}
1427
1428static int
1429conv_time(clock_t ticks, char *seconds, size_t l)
1430{
1431 static clock_t tpm = 0;
1432 clock_t mins;
1433 size_t i;
1434
1435 if (!tpm)
1436 tpm = /*sysconf(_SC_CLK_TCK)*/sh_sysconf_clk_tck() * 60;
1437
1438 mins = ticks / tpm;
1439#ifdef _MSC_VER
1440 {
1441 char tmp[64];
1442 sprintf(tmp, "%.4f", (ticks - mins * tpm) * 60.0 / tpm);
1443 strlcpy(seconds, tmp, l);
1444 }
1445#else
1446 snprintf(seconds, l, "%.4f", (ticks - mins * tpm) * 60.0 / tpm );
1447#endif
1448
1449 if (seconds[0] == '6' && seconds[1] == '0') {
1450 /* 59.99995 got rounded up... */
1451 mins++;
1452 strlcpy(seconds, "0.0", l);
1453 return mins;
1454 }
1455
1456 /* suppress trailing zeros */
1457 i = strlen(seconds) - 1;
1458 for (; seconds[i] == '0' && seconds[i - 1] != '.'; i--)
1459 seconds[i] = 0;
1460 return mins;
1461}
1462
1463int
1464timescmd(shinstance *psh, int argc, char **argv)
1465{
1466 shtms tms;
1467 int u, s, cu, cs;
1468 char us[8], ss[8], cus[8], css[8];
1469
1470 nextopt(psh, "");
1471
1472 sh_times(psh, &tms);
1473
1474 u = conv_time(tms.tms_utime, us, sizeof(us));
1475 s = conv_time(tms.tms_stime, ss, sizeof(ss));
1476 cu = conv_time(tms.tms_cutime, cus, sizeof(cus));
1477 cs = conv_time(tms.tms_cstime, css, sizeof(css));
1478
1479 outfmt(psh->out1, "%dm%ss %dm%ss\n%dm%ss %dm%ss\n",
1480 u, us, s, ss, cu, cus, cs, css);
1481
1482 return 0;
1483}
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette