VirtualBox

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

Last change on this file since 3451 was 3449, checked in by bird, 5 years ago

kash: Eliminate the 'temp' node field in nfile so we can share node trees later.

  • Property svn:eol-style set to LF
  • Property svn:keywords set to Id
File size: 35.3 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, 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 popstackmark(psh, smarkp);
829
830 if (eflag(psh) && psh->exitstatus && !(flags & EV_TESTED))
831 exitshell(psh, psh->exitstatus);
832}
833
834
835/* Called if we forkshell(). */
836STATIC void
837evalcommand_parent(shinstance *psh, int flags, char *lastarg, struct stackmark *smarkp,
838 int mode, struct job *jp, int pip[2], struct backcmd *backcmd)
839{
840 if (mode == FORK_FG) { /* argument to fork */
841 psh->exitstatus = waitforjob(psh, jp);
842 } else if (mode == FORK_NOJOB) {
843 backcmd->fd = pip[0];
844 shfile_close(&psh->fdtab, pip[1]);
845 backcmd->jp = jp;
846 }
847 FORCEINTON;
848
849 evalcommand_out(psh, flags, lastarg, smarkp);
850}
851
852struct evalcommanddoit
853{
854 struct stackmark smark;
855
856 struct backcmd *backcmd;
857 int flags;
858 int argc;
859 char **argv;
860 char *lastarg;
861 struct arglist varlist;
862 const char *path;
863 struct cmdentry cmdentry;
864
865 /* for child stuff only: */
866 int pip[2];
867};
868
869STATIC void
870evalcommand_doit(shinstance *psh, union node *cmd, struct evalcommanddoit *args)
871{
872 struct jmploc jmploc;
873 struct jmploc *volatile savehandler;
874 struct localvar *volatile savelocalvars;
875
876 /* This is the child process if a fork occurred. */
877 /* Execute the command. */
878 switch (args->cmdentry.cmdtype) {
879 case CMDFUNCTION: {
880 volatile struct shparam saveparam;
881#ifdef DEBUG
882 trputs(psh, "Shell function: "); trargs(psh, args->argv);
883#endif
884 redirect(psh, cmd->ncmd.redirect, REDIR_PUSH);
885 saveparam = psh->shellparam;
886 psh->shellparam.malloc = 0;
887 psh->shellparam.reset = 1;
888 psh->shellparam.nparam = args->argc - 1;
889 psh->shellparam.p = args->argv + 1;
890 psh->shellparam.optnext = NULL;
891 INTOFF;
892 savelocalvars = psh->localvars;
893 psh->localvars = NULL;
894 INTON;
895 if (setjmp(jmploc.loc)) {
896 if (psh->exception == EXSHELLPROC) {
897 freeparam(psh, (volatile struct shparam *)
898 &saveparam);
899 } else {
900 freeparam(psh, &psh->shellparam);
901 psh->shellparam = saveparam;
902 }
903 poplocalvars(psh);
904 psh->localvars = savelocalvars;
905 psh->handler = savehandler;
906 longjmp(psh->handler->loc, 1);
907 }
908 savehandler = psh->handler;
909 psh->handler = &jmploc;
910 listmklocal(psh, args->varlist.list, 0);
911 /* stop shell blowing its stack */
912 if (++psh->funcnest > 1000)
913 error(psh, "too many nested function calls");
914 evaltree(psh, args->cmdentry.u.func, args->flags & EV_TESTED);
915 psh->funcnest--;
916 INTOFF;
917 poplocalvars(psh);
918 psh->localvars = savelocalvars;
919 freeparam(psh, &psh->shellparam);
920 psh->shellparam = saveparam;
921 psh->handler = savehandler;
922 popredir(psh);
923 INTON;
924 if (psh->evalskip == SKIPFUNC) {
925 psh->evalskip = 0;
926 psh->skipcount = 0;
927 }
928 if (args->flags & EV_EXIT)
929 exitshell(psh, psh->exitstatus);
930 break;
931 }
932
933 case CMDBUILTIN:
934 case CMDSPLBLTIN: {
935 volatile int temp_path = 0;
936 char *volatile savecmdname;
937 int volatile savecmdnamemalloc;
938 volatile int e;
939 int mode;
940#ifdef DEBUG
941 trputs(psh, "builtin command: "); trargs(psh, args->argv);
942#endif
943 mode = (args->cmdentry.u.bltin == execcmd) ? 0 : REDIR_PUSH;
944 if (args->flags == EV_BACKCMD) {
945 psh->memout.nleft = 0;
946 psh->memout.nextc = psh->memout.buf;
947 psh->memout.bufsize = 64;
948 mode |= REDIR_BACKQ;
949 }
950 e = -1;
951 savehandler = psh->handler;
952 savecmdname = psh->commandname;
953 savecmdnamemalloc = psh->commandnamemalloc;
954 psh->handler = &jmploc;
955 if (!setjmp(jmploc.loc)) {
956 /* We need to ensure the command hash table isn't
957 * corruped by temporary PATH assignments.
958 * However we must ensure the 'local' command works!
959 */
960 if (args->path != pathval(psh) && (args->cmdentry.u.bltin == hashcmd ||
961 args->cmdentry.u.bltin == typecmd)) {
962 savelocalvars = psh->localvars;
963 psh->localvars = 0;
964 mklocal(psh, args->path - 5 /* PATH= */, 0);
965 temp_path = 1;
966 } else
967 temp_path = 0;
968 redirect(psh, cmd->ncmd.redirect, mode);
969
970 /* exec is a special builtin, but needs this list... */
971 psh->cmdenviron = args->varlist.list;
972 /* we must check 'readonly' flag for all builtins */
973 listsetvar(psh, args->varlist.list,
974 args->cmdentry.cmdtype == CMDSPLBLTIN ? 0 : VNOSET);
975 psh->commandnamemalloc = 0;
976 psh->commandname = args->argv[0];
977 /* initialize nextopt */
978 psh->argptr = args->argv + 1;
979 psh->optptr = NULL;
980 /* and getopt */
981#if 0 /** @todo fix getop usage! */
982#if defined(__FreeBSD__) || defined(__EMX__) || defined(__APPLE__)
983 optreset = 1;
984 optind = 1;
985#else
986 optind = 0; /* init */
987#endif
988#endif
989
990 psh->exitstatus = args->cmdentry.u.bltin(psh, args->argc, args->argv);
991 } else {
992 e = psh->exception;
993 psh->exitstatus = e == EXINT ? SIGINT + 128 :
994 e == EXEXEC ? psh->exerrno : 2;
995 }
996 psh->handler = savehandler;
997 output_flushall(psh);
998 psh->out1 = &psh->output;
999 psh->out2 = &psh->errout;
1000 freestdout(psh);
1001 if (temp_path) {
1002 poplocalvars(psh);
1003 psh->localvars = savelocalvars;
1004 }
1005 psh->cmdenviron = NULL;
1006 if (e != EXSHELLPROC) {
1007 psh->commandname = savecmdname;
1008 psh->commandnamemalloc = savecmdnamemalloc;
1009 if (args->flags & EV_EXIT)
1010 exitshell(psh, psh->exitstatus);
1011 }
1012 if (savecmdnamemalloc)
1013 sh_free(psh, savecmdname);
1014 if (e != -1) {
1015 if ((e != EXERROR && e != EXEXEC)
1016 || args->cmdentry.cmdtype == CMDSPLBLTIN)
1017 exraise(psh, e);
1018 FORCEINTON;
1019 }
1020 if (args->cmdentry.u.bltin != execcmd)
1021 popredir(psh);
1022 if (args->flags == EV_BACKCMD) {
1023 args->backcmd->buf = psh->memout.buf;
1024 args->backcmd->nleft = (int)(psh->memout.nextc - psh->memout.buf);
1025 psh->memout.buf = NULL;
1026 }
1027 break;
1028 }
1029
1030 default: {
1031 struct strlist *sp;
1032 char **envp;
1033#ifdef DEBUG
1034 trputs(psh, "normal command: "); trargs(psh, args->argv);
1035#endif
1036 clearredir(psh);
1037 redirect(psh, cmd->ncmd.redirect, 0);
1038 for (sp = args->varlist.list ; sp ; sp = sp->next)
1039 setvareq(psh, sp->text, VEXPORT|VSTACK);
1040 envp = environment(psh);
1041 shellexec(psh, args->argv, envp, args->path, args->cmdentry.u.index);
1042 break;
1043 }
1044 }
1045
1046 evalcommand_out(psh, args->flags, args->lastarg, &args->smark);
1047}
1048
1049/* child callback. */
1050static int evalcommand_child(shinstance *psh, union node *cmd, void *argp)
1051{
1052 struct evalcommanddoit *args = (struct evalcommanddoit *)argp;
1053
1054 if (args->flags & EV_BACKCMD) {
1055 FORCEINTON;
1056 shfile_close(&psh->fdtab, args->pip[0]);
1057 if (args->pip[1] != 1) {
1058 movefd(psh, args->pip[1], 1);
1059 }
1060 }
1061 args->flags |= EV_EXIT;
1062
1063 evalcommand_doit(psh, cmd, args);
1064 /* not reached */ /** @todo make it return here */
1065 return 0;
1066}
1067
1068/* Copies data in the argument structure from parent to child. */
1069static void evalcommand_setup_child(shinstance *pshchild, shinstance *pshparent, void *argp)
1070{
1071 struct evalcommanddoit *args = (struct evalcommanddoit *)argp;
1072 char **argv;
1073 char **srcargv;
1074 struct strlist *sp;
1075 int argc, i;
1076
1077 setstackmark(pshchild, &args->smark);
1078
1079 /* copy arguments. */
1080 srcargv = args->argv;
1081 argc = args->argc;
1082 args->argv = argv = stalloc(pshchild, sizeof(char *) * (argc + 1));
1083 for (i = 0; i < argc; i++)
1084 argv[i] = stsavestr(pshchild, srcargv[i]);
1085 argv[argc] = NULL;
1086 if (args->lastarg)
1087 args->lastarg = argv[argc - 1];
1088
1089 /* copy variable list, checking for the 'path'. */
1090 sp = args->varlist.list;
1091 args->varlist.list = NULL;
1092 args->varlist.lastp = &args->varlist.list;
1093 for (; sp; sp = sp->next) {
1094 struct strlist *snew = (struct strlist *)stalloc(pshchild, sizeof(*snew));
1095 char *text;
1096 snew->next = NULL;
1097 snew->text = text = stsavestr(pshchild, sp->text);
1098
1099 if (&text[5] == args->path)
1100 args->path = &text[sizeof("PATH=") - 1];
1101
1102 *args->varlist.lastp = snew;
1103 args->varlist.lastp = &snew->next;
1104 }
1105
1106 if (args->path == pathval(pshparent))
1107 args->path = pathval(pshchild);
1108
1109 /* back tick command should be ignored in this codepath
1110 (flags != EV_BACKCMD as EV_EXIT is ORed in). */
1111
1112 /* If cmdentry references an internal function, we must duplicates it's nodes. */
1113 if (args->cmdentry.cmdtype == CMDFUNCTION)
1114 args->cmdentry.u.func = copyparsetree(pshchild, args->cmdentry.u.func); /** @todo isn't this duplicated already? */
1115}
1116
1117/*
1118 * Execute a simple command.
1119 */
1120
1121STATIC void
1122evalcommand(shinstance *psh, union node *cmd, int flags, struct backcmd *backcmd)
1123{
1124 struct evalcommanddoit args;
1125 char **argv;
1126 int argc;
1127
1128 union node *argp;
1129 int numvars;
1130 struct arglist arglist;
1131 struct strlist *sp;
1132 const char *path = pathval(psh);
1133 unsigned expfnamedepth;
1134
1135 /* First expand the arguments. */
1136 TRACE((psh, "evalcommand(0x%lx, %d) called\n", (long)cmd, flags));
1137 setstackmark(psh, &args.smark);
1138 psh->back_exitstatus = 0;
1139
1140 arglist.lastp = &arglist.list;
1141 /* Expand arguments, ignoring the initial 'name=value' ones */
1142 for (argp = cmd->ncmd.args, numvars = 0 ; argp ; argp = argp->narg.next, numvars++) {
1143 char *p = argp->narg.text;
1144 char ch = *p;
1145 if (is_name(ch)) {
1146 do ch = *++p;
1147 while (is_in_name(ch));
1148 if (ch == '=')
1149 continue;
1150 }
1151 break;
1152 }
1153 for (/*continue on argp from above. */ ; argp ; argp = argp->narg.next)
1154 expandarg(psh, argp, &arglist, EXP_FULL | EXP_TILDE);
1155 *arglist.lastp = NULL;
1156
1157 expfnamedepth = expredir(psh, cmd->ncmd.redirect);
1158
1159 /* Now do the initial 'name=value' ones we skipped above */
1160 args.varlist.lastp = &args.varlist.list;
1161 for (argp = cmd->ncmd.args ; numvars > 0 && argp ; argp = argp->narg.next, numvars--)
1162 expandarg(psh, argp, &args.varlist, EXP_VARTILDE);
1163 *args.varlist.lastp = NULL;
1164
1165 argc = 0;
1166 for (sp = arglist.list ; sp ; sp = sp->next)
1167 argc++;
1168 args.argc = argc;
1169 args.argv = argv = stalloc(psh, sizeof (char *) * (argc + 1));
1170
1171 for (sp = arglist.list ; sp ; sp = sp->next) {
1172 TRACE((psh, "evalcommand arg: %s\n", sp->text));
1173 *argv++ = sp->text;
1174 }
1175 *argv = NULL;
1176 args.lastarg = NULL;
1177 if (iflag(psh) && psh->funcnest == 0 && argc > 0)
1178 args.lastarg = argv[-1];
1179 argv -= argc;
1180
1181 /* Print the command if xflag is set. */
1182 if (xflag(psh)) {
1183 char sep = 0;
1184 out2str(psh, ps4val(psh));
1185 for (sp = args.varlist.list ; sp ; sp = sp->next) {
1186 if (sep != 0)
1187 outc(sep, &psh->errout);
1188 out2str(psh, sp->text);
1189 sep = ' ';
1190 }
1191 for (sp = arglist.list ; sp ; sp = sp->next) {
1192 if (sep != 0)
1193 outc(sep, &psh->errout);
1194 out2str(psh, sp->text);
1195 sep = ' ';
1196 }
1197 outc('\n', &psh->errout);
1198 flushout(&psh->errout);
1199 }
1200
1201 /* Now locate the command. */
1202 if (argc == 0) {
1203 args.cmdentry.cmdtype = CMDSPLBLTIN;
1204 args.cmdentry.u.bltin = bltincmd;
1205 } else {
1206 static const char PATH[] = "PATH=";
1207 int cmd_flags = DO_ERR;
1208
1209 /*
1210 * Modify the command lookup path, if a PATH= assignment
1211 * is present
1212 */
1213 for (sp = args.varlist.list; sp; sp = sp->next)
1214 if (strncmp(sp->text, PATH, sizeof(PATH) - 1) == 0)
1215 path = sp->text + sizeof(PATH) - 1;
1216
1217 do {
1218 int argsused, use_syspath;
1219 find_command(psh, argv[0], &args.cmdentry, cmd_flags, path);
1220 if (args.cmdentry.cmdtype == CMDUNKNOWN) {
1221 psh->exitstatus = 127;
1222 flushout(&psh->errout);
1223 evalcommand_out(psh, flags, args.lastarg, &args.smark);
1224 return;
1225 }
1226
1227 /* implement the 'command' builtin here */
1228 if (args.cmdentry.cmdtype != CMDBUILTIN ||
1229 args.cmdentry.u.bltin != bltincmd)
1230 break;
1231 cmd_flags |= DO_NOFUNC;
1232 argsused = parse_command_args(psh, argc, argv, &use_syspath);
1233 if (argsused == 0) {
1234 /* use 'type' builting to display info */
1235 args.cmdentry.u.bltin = typecmd;
1236 break;
1237 }
1238 argc -= argsused;
1239 argv += argsused;
1240 if (use_syspath)
1241 path = syspath(psh) + 5;
1242 } while (argc != 0);
1243 if (args.cmdentry.cmdtype == CMDSPLBLTIN && cmd_flags & DO_NOFUNC)
1244 /* posix mandates that 'command <splbltin>' act as if
1245 <splbltin> was a normal builtin */
1246 args.cmdentry.cmdtype = CMDBUILTIN;
1247 }
1248
1249 /* Fork off a child process if necessary. */
1250 if (cmd->ncmd.backgnd
1251 || (args.cmdentry.cmdtype == CMDNORMAL && (flags & EV_EXIT) == 0)
1252 || ( (flags & EV_BACKCMD) != 0
1253 && ( (args.cmdentry.cmdtype != CMDBUILTIN && args.cmdentry.cmdtype != CMDSPLBLTIN)
1254 || args.cmdentry.u.bltin == dotcmd
1255 || args.cmdentry.u.bltin == evalcmd))) {
1256 struct job *jp;
1257 int mode;
1258
1259 INTOFF;
1260 jp = makejob(psh, cmd, 1);
1261 mode = cmd->ncmd.backgnd;
1262 args.pip[0] = -1;
1263 args.pip[1] = -1;
1264 if (flags & EV_BACKCMD) {
1265 mode = FORK_NOJOB;
1266 if (sh_pipe(psh, args.pip) < 0)
1267 error(psh, "Pipe call failed");
1268 }
1269
1270 args.backcmd = backcmd;
1271 args.flags = flags;
1272 args.path = path;
1273#ifdef KASH_USE_FORKSHELL2
1274 forkshell2(psh, jp, cmd, mode, evalcommand_child, cmd,
1275 &args, sizeof(args), evalcommand_setup_child);
1276 evalcommand_parent(psh, flags, args.lastarg, &args.smark, mode, jp,
1277 args.pip, backcmd);
1278#else
1279 if (forkshell(psh, jp, cmd, mode) != 0) {
1280 evalcommand_parent(psh, flags, args.lastarg, &args.smark, mode, jp,
1281 args.pip, backcmd);
1282 expredircleanup(psh, expfnamedepth);
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 expredircleanup(psh, expfnamedepth);
1294}
1295
1296
1297/*
1298 * Search for a command. This is called before we fork so that the
1299 * location of the command will be available in the parent as well as
1300 * the child. The check for "goodname" is an overly conservative
1301 * check that the name will not be subject to expansion.
1302 */
1303
1304STATIC void
1305prehash(shinstance *psh, union node *n)
1306{
1307 struct cmdentry entry;
1308
1309 if (n->type == NCMD && n->ncmd.args)
1310 if (goodname(n->ncmd.args->narg.text))
1311 find_command(psh, n->ncmd.args->narg.text, &entry, 0,
1312 pathval(psh));
1313}
1314
1315
1316
1317/*
1318 * Builtin commands. Builtin commands whose functions are closely
1319 * tied to evaluation are implemented here.
1320 */
1321
1322/*
1323 * No command given.
1324 */
1325
1326int
1327bltincmd(shinstance *psh, int argc, char **argv)
1328{
1329 /*
1330 * Preserve psh->exitstatus of a previous possible redirection
1331 * as POSIX mandates
1332 */
1333 return psh->back_exitstatus;
1334}
1335
1336
1337/*
1338 * Handle break and continue commands. Break, continue, and return are
1339 * all handled by setting the psh->evalskip flag. The evaluation routines
1340 * above all check this flag, and if it is set they start skipping
1341 * commands rather than executing them. The variable skipcount is
1342 * the number of loops to break/continue, or the number of function
1343 * levels to return. (The latter is always 1.) It should probably
1344 * be an error to break out of more loops than exist, but it isn't
1345 * in the standard shell so we don't make it one here.
1346 */
1347
1348int
1349breakcmd(shinstance *psh, int argc, char **argv)
1350{
1351 int n = argc > 1 ? number(psh, argv[1]) : 1;
1352
1353 if (n > psh->loopnest)
1354 n = psh->loopnest;
1355 if (n > 0) {
1356 psh->evalskip = (**argv == 'c')? SKIPCONT : SKIPBREAK;
1357 psh->skipcount = n;
1358 }
1359 return 0;
1360}
1361
1362
1363/*
1364 * The return command.
1365 */
1366
1367int
1368returncmd(shinstance *psh, int argc, char **argv)
1369{
1370#if 0
1371 int ret = argc > 1 ? number(psh, argv[1]) : psh->exitstatus;
1372#else
1373 int ret;
1374 if (argc > 1) {
1375 /* make return -1 and VSC lite work ... */
1376 if (argv[1][0] != '-' || !is_number(&argv[1][1]))
1377 ret = number(psh, argv[1]);
1378 else
1379 ret = -number(psh, &argv[1][1]) & 255; /* take the bash approach */
1380 } else {
1381 ret = psh->exitstatus;
1382 }
1383#endif
1384
1385 if (psh->funcnest) {
1386 psh->evalskip = SKIPFUNC;
1387 psh->skipcount = 1;
1388 return ret;
1389 }
1390 else {
1391 /* Do what ksh does; skip the rest of the file */
1392 psh->evalskip = SKIPFILE;
1393 psh->skipcount = 1;
1394 return ret;
1395 }
1396}
1397
1398
1399int
1400falsecmd(shinstance *psh, int argc, char **argv)
1401{
1402 return 1;
1403}
1404
1405
1406int
1407truecmd(shinstance *psh, int argc, char **argv)
1408{
1409 return 0;
1410}
1411
1412
1413int
1414execcmd(shinstance *psh, int argc, char **argv)
1415{
1416 if (argc > 1) {
1417 struct strlist *sp;
1418
1419 iflag(psh) = 0; /* exit on error */
1420 mflag(psh) = 0;
1421 optschanged(psh);
1422 for (sp = psh->cmdenviron; sp; sp = sp->next)
1423 setvareq(psh, sp->text, VEXPORT|VSTACK);
1424 shellexec(psh, argv + 1, environment(psh), pathval(psh), 0);
1425 }
1426 return 0;
1427}
1428
1429static int
1430conv_time(clock_t ticks, char *seconds, size_t l)
1431{
1432 static clock_t tpm = 0;
1433 clock_t mins;
1434 size_t i;
1435
1436 if (!tpm)
1437 tpm = /*sysconf(_SC_CLK_TCK)*/sh_sysconf_clk_tck() * 60;
1438
1439 mins = ticks / tpm;
1440#ifdef _MSC_VER
1441 {
1442 char tmp[64];
1443 sprintf(tmp, "%.4f", (ticks - mins * tpm) * 60.0 / tpm);
1444 strlcpy(seconds, tmp, l);
1445 }
1446#else
1447 snprintf(seconds, l, "%.4f", (ticks - mins * tpm) * 60.0 / tpm );
1448#endif
1449
1450 if (seconds[0] == '6' && seconds[1] == '0') {
1451 /* 59.99995 got rounded up... */
1452 mins++;
1453 strlcpy(seconds, "0.0", l);
1454 return mins;
1455 }
1456
1457 /* suppress trailing zeros */
1458 i = strlen(seconds) - 1;
1459 for (; seconds[i] == '0' && seconds[i - 1] != '.'; i--)
1460 seconds[i] = 0;
1461 return mins;
1462}
1463
1464int
1465timescmd(shinstance *psh, int argc, char **argv)
1466{
1467 shtms tms;
1468 int u, s, cu, cs;
1469 char us[8], ss[8], cus[8], css[8];
1470
1471 nextopt(psh, "");
1472
1473 sh_times(psh, &tms);
1474
1475 u = conv_time(tms.tms_utime, us, sizeof(us));
1476 s = conv_time(tms.tms_stime, ss, sizeof(ss));
1477 cu = conv_time(tms.tms_cutime, cus, sizeof(cus));
1478 cs = conv_time(tms.tms_cstime, css, sizeof(css));
1479
1480 outfmt(psh->out1, "%dm%ss %dm%ss\n%dm%ss %dm%ss\n",
1481 u, us, s, ss, cu, cus, cs, css);
1482
1483 return 0;
1484}
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