source: trunk/src/kash/eval.c@ 3475

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

kash: exec.c/h+eval.c: Fixed no-rehashing-needed optimizations so they work for windows and OS/2 as well (UNIX-specific abspath detection). Cache the suffix found by stat_pc_exec_exts so that we don't have to repeat it 2 microseconds after find_command called it. Use the specialized shfile_stat variants where possible. eval.c: Fixed double free of commandname in evalcommand_doit.

  • Property svn:eol-style set to LF
  • Property svn:keywords set to Id
File size: 35.5 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(redir->type == NHERE || redir->type == NXHERE);
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 else 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,
1044 args->cmdentry.u.n.index, args->cmdentry.u.n.suffix);
1045 break;
1046 }
1047 }
1048
1049 evalcommand_out(psh, args->flags, args->lastarg, args->expfnamedepth, &args->smark);
1050}
1051
1052/* child callback. */
1053static int evalcommand_child(shinstance *psh, union node *cmd, void *argp)
1054{
1055 struct evalcommanddoit *args = (struct evalcommanddoit *)argp;
1056
1057 if (args->flags & EV_BACKCMD) {
1058 FORCEINTON;
1059 shfile_close(&psh->fdtab, args->pip[0]);
1060 if (args->pip[1] != 1) {
1061 movefd(psh, args->pip[1], 1);
1062 }
1063 }
1064 args->flags |= EV_EXIT;
1065
1066 evalcommand_doit(psh, cmd, args);
1067 /* not reached */ /** @todo make it return here */
1068 return 0;
1069}
1070
1071/* Copies data in the argument structure from parent to child. */
1072static void evalcommand_setup_child(shinstance *pshchild, shinstance *pshparent, void *argp)
1073{
1074 struct evalcommanddoit *args = (struct evalcommanddoit *)argp;
1075 char **argv;
1076 char **srcargv;
1077 struct strlist *sp;
1078 int argc, i;
1079
1080 setstackmark(pshchild, &args->smark);
1081
1082 /* copy arguments. */
1083 srcargv = args->argv;
1084 argc = args->argc;
1085 args->argv = argv = stalloc(pshchild, sizeof(char *) * (argc + 1));
1086 for (i = 0; i < argc; i++)
1087 argv[i] = stsavestr(pshchild, srcargv[i]);
1088 argv[argc] = NULL;
1089 if (args->lastarg)
1090 args->lastarg = argv[argc - 1];
1091
1092 /* copy variable list, checking for the 'path'. */
1093 sp = args->varlist.list;
1094 args->varlist.list = NULL;
1095 args->varlist.lastp = &args->varlist.list;
1096 for (; sp; sp = sp->next) {
1097 struct strlist *snew = (struct strlist *)stalloc(pshchild, sizeof(*snew));
1098 char *text;
1099 snew->next = NULL;
1100 snew->text = text = stsavestr(pshchild, sp->text);
1101
1102 if (&text[5] == args->path)
1103 args->path = &text[sizeof("PATH=") - 1];
1104
1105 *args->varlist.lastp = snew;
1106 args->varlist.lastp = &snew->next;
1107 }
1108
1109 if (args->path == pathval(pshparent))
1110 args->path = pathval(pshchild);
1111
1112 /* back tick command should be ignored in this codepath
1113 (flags != EV_BACKCMD as EV_EXIT is ORed in). */
1114
1115 /* If cmdentry references an internal function, we must duplicates (reference) it's nodes. */
1116 if (args->cmdentry.cmdtype == CMDFUNCTION)
1117 args->cmdentry.u.func = copyparsetree(pshchild, args->cmdentry.u.func); /** @todo isn't this duplicated already? */
1118}
1119
1120/*
1121 * Execute a simple command.
1122 */
1123
1124STATIC void
1125evalcommand(shinstance *psh, union node *cmd, int flags, struct backcmd *backcmd)
1126{
1127 struct evalcommanddoit args;
1128 char **argv;
1129 int argc;
1130
1131 union node *argp;
1132 int numvars;
1133 struct arglist arglist;
1134 struct strlist *sp;
1135 const char *path = pathval(psh);
1136
1137 /* First expand the arguments. */
1138 TRACE((psh, "evalcommand(0x%lx, %d) called\n", (long)cmd, flags));
1139 setstackmark(psh, &args.smark);
1140 psh->back_exitstatus = 0;
1141
1142 arglist.lastp = &arglist.list;
1143 /* Expand arguments, ignoring the initial 'name=value' ones */
1144 for (argp = cmd->ncmd.args, numvars = 0 ; argp ; argp = argp->narg.next, numvars++) {
1145 char *p = argp->narg.text;
1146 char ch = *p;
1147 if (is_name(ch)) {
1148 do ch = *++p;
1149 while (is_in_name(ch));
1150 if (ch == '=')
1151 continue;
1152 }
1153 break;
1154 }
1155 for (/*continue on argp from above. */ ; argp ; argp = argp->narg.next)
1156 expandarg(psh, argp, &arglist, EXP_FULL | EXP_TILDE);
1157 *arglist.lastp = NULL;
1158
1159 args.expfnamedepth = expredir(psh, cmd->ncmd.redirect);
1160
1161 /* Now do the initial 'name=value' ones we skipped above */
1162 args.varlist.lastp = &args.varlist.list;
1163 for (argp = cmd->ncmd.args ; numvars > 0 && argp ; argp = argp->narg.next, numvars--)
1164 expandarg(psh, argp, &args.varlist, EXP_VARTILDE);
1165 *args.varlist.lastp = NULL;
1166
1167 argc = 0;
1168 for (sp = arglist.list ; sp ; sp = sp->next)
1169 argc++;
1170 args.argc = argc;
1171 args.argv = argv = stalloc(psh, sizeof (char *) * (argc + 1));
1172
1173 for (sp = arglist.list ; sp ; sp = sp->next) {
1174 TRACE((psh, "evalcommand arg: %s\n", sp->text));
1175 *argv++ = sp->text;
1176 }
1177 *argv = NULL;
1178 args.lastarg = NULL;
1179 if (iflag(psh) && psh->funcnest == 0 && argc > 0)
1180 args.lastarg = argv[-1];
1181 argv -= argc;
1182
1183 /* Print the command if xflag is set. */
1184 if (xflag(psh)) {
1185 char sep = 0;
1186 out2str(psh, ps4val(psh));
1187 for (sp = args.varlist.list ; sp ; sp = sp->next) {
1188 if (sep != 0)
1189 outc(sep, &psh->errout);
1190 out2str(psh, sp->text);
1191 sep = ' ';
1192 }
1193 for (sp = arglist.list ; sp ; sp = sp->next) {
1194 if (sep != 0)
1195 outc(sep, &psh->errout);
1196 out2str(psh, sp->text);
1197 sep = ' ';
1198 }
1199 outc('\n', &psh->errout);
1200 flushout(&psh->errout);
1201 }
1202
1203 /* Now locate the command. */
1204 if (argc == 0) {
1205 args.cmdentry.cmdtype = CMDSPLBLTIN;
1206 args.cmdentry.u.bltin = bltincmd;
1207 } else {
1208 static const char PATH[] = "PATH=";
1209 int cmd_flags = DO_ERR;
1210
1211 /*
1212 * Modify the command lookup path, if a PATH= assignment
1213 * is present
1214 */
1215 for (sp = args.varlist.list; sp; sp = sp->next)
1216 if (strncmp(sp->text, PATH, sizeof(PATH) - 1) == 0)
1217 path = sp->text + sizeof(PATH) - 1;
1218
1219 do {
1220 int argsused, use_syspath;
1221 find_command(psh, argv[0], &args.cmdentry, cmd_flags, path);
1222 if (args.cmdentry.cmdtype == CMDUNKNOWN) {
1223 psh->exitstatus = 127;
1224 flushout(&psh->errout);
1225 evalcommand_out(psh, flags, args.lastarg, args.expfnamedepth, &args.smark);
1226 return;
1227 }
1228
1229 /* implement the 'command' builtin here */
1230 if (args.cmdentry.cmdtype != CMDBUILTIN ||
1231 args.cmdentry.u.bltin != bltincmd)
1232 break;
1233 cmd_flags |= DO_NOFUNC;
1234 argsused = parse_command_args(psh, argc, argv, &use_syspath);
1235 if (argsused == 0) {
1236 /* use 'type' builting to display info */
1237 args.cmdentry.u.bltin = typecmd;
1238 break;
1239 }
1240 argc -= argsused;
1241 argv += argsused;
1242 if (use_syspath)
1243 path = syspath(psh) + 5;
1244 } while (argc != 0);
1245 if (args.cmdentry.cmdtype == CMDSPLBLTIN && cmd_flags & DO_NOFUNC)
1246 /* posix mandates that 'command <splbltin>' act as if
1247 <splbltin> was a normal builtin */
1248 args.cmdentry.cmdtype = CMDBUILTIN;
1249 }
1250
1251 /* Fork off a child process if necessary. */
1252 if (cmd->ncmd.backgnd
1253 || (args.cmdentry.cmdtype == CMDNORMAL && (flags & EV_EXIT) == 0)
1254 || ( (flags & EV_BACKCMD) != 0
1255 && ( (args.cmdentry.cmdtype != CMDBUILTIN && args.cmdentry.cmdtype != CMDSPLBLTIN)
1256 || args.cmdentry.u.bltin == dotcmd
1257 || args.cmdentry.u.bltin == evalcmd))) {
1258 struct job *jp;
1259 int mode;
1260
1261 INTOFF;
1262 jp = makejob(psh, cmd, 1);
1263 mode = cmd->ncmd.backgnd;
1264 args.pip[0] = -1;
1265 args.pip[1] = -1;
1266 if (flags & EV_BACKCMD) {
1267 mode = FORK_NOJOB;
1268 if (sh_pipe(psh, args.pip) < 0)
1269 error(psh, "Pipe call failed");
1270 }
1271
1272 args.backcmd = backcmd;
1273 args.flags = flags;
1274 args.path = path;
1275#ifdef KASH_USE_FORKSHELL2
1276 forkshell2(psh, jp, cmd, mode, evalcommand_child, cmd,
1277 &args, sizeof(args), evalcommand_setup_child);
1278 evalcommand_parent(psh, flags, args.lastarg, args.expfnamedepth,
1279 &args.smark, mode, jp, args.pip, backcmd);
1280#else
1281 if (forkshell(psh, jp, cmd, mode) != 0) {
1282 evalcommand_parent(psh, flags, args.lastarg, args.expfnamedepth,
1283 &args.smark, mode, jp, args.pip, backcmd);
1284 return; /* at end of routine */
1285 }
1286 evalcommand_child(psh, cmd, &args);
1287#endif
1288 } else {
1289 args.backcmd = backcmd;
1290 args.flags = flags;
1291 args.path = path;
1292 evalcommand_doit(psh, cmd, &args);
1293 }
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, -1);
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.