diff options
Diffstat (limited to 'shell_cmds')
774 files changed, 68459 insertions, 0 deletions
diff --git a/shell_cmds/.upstream_base_commits b/shell_cmds/.upstream_base_commits new file mode 100644 index 0000000..23f2adb --- /dev/null +++ b/shell_cmds/.upstream_base_commits @@ -0,0 +1,4 @@ +xargs/pathname.h freebsd usr.bin/xargs/pathname.h 872b698bd4a1bfc0bf008c09228e6fd238809c75 +xargs/strnsubst.c freebsd usr.bin/xargs/strnsubst.c 56640efcbe39386a0152f14fb18a314b85e7e014 +xargs/xargs.1 freebsd usr.bin/xargs/xargs.1 7e6cabd06e6caa6a02eeb86308dc0cb3f27e10da +xargs/xargs.c freebsd usr.bin/xargs/xargs.c 872b698bd4a1bfc0bf008c09228e6fd238809c75 diff --git a/shell_cmds/alias/alias.1 b/shell_cmds/alias/alias.1 new file mode 100644 index 0000000..5a811f4 --- /dev/null +++ b/shell_cmds/alias/alias.1 @@ -0,0 +1 @@ +.so man1/builtin.1 diff --git a/shell_cmds/alias/builtin.1 b/shell_cmds/alias/builtin.1 new file mode 100644 index 0000000..0fae99a --- /dev/null +++ b/shell_cmds/alias/builtin.1 @@ -0,0 +1,333 @@ +.\" +.\" Copyright (c) 1999 Sheldon Hearn +.\" +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" $FreeBSD: /repoman/r/ncvs/src/share/man/man1/builtin.1,v 1.25 2005/09/09 17:02:08 garys Exp $ +.\" +.Dd February 23, 2005 +.Dt BUILTIN 1 +.Os +.Sh NAME +.Nm builtin , +.Nm \&! , +.Nm \&% , +.Nm \&. , +.Nm \&: , +.Nm \&@ , +.Nm \&{ , +.Nm \&} , +.Nm alias , +.Nm alloc , +.Nm bg , +.Nm bind , +.Nm bindkey , +.Nm break , +.Nm breaksw , +.Nm builtins , +.Nm case , +.Nm cd , +.Nm chdir , +.Nm command , +.Nm complete , +.Nm continue , +.Nm default , +.Nm dirs , +.Nm do , +.Nm done , +.Nm echo , +.Nm echotc , +.Nm elif , +.Nm else , +.Nm end , +.Nm endif , +.Nm endsw , +.Nm esac , +.Nm eval , +.Nm exec , +.Nm exit , +.Nm export , +.Nm false , +.Nm fc , +.Nm fg , +.Nm filetest , +.Nm fi , +.Nm for , +.Nm foreach , +.Nm getopts , +.Nm glob , +.Nm goto , +.Nm hash , +.Nm hashstat , +.Nm history , +.Nm hup , +.Nm if , +.Nm jobid , +.Nm jobs , +.Nm kill , +.Nm limit , +.Nm local , +.Nm log , +.Nm login , +.Nm logout , +.Nm ls-F , +.Nm nice , +.Nm nohup , +.Nm notify , +.Nm onintr , +.Nm popd , +.Nm printenv , +.Nm pushd , +.Nm pwd , +.Nm read , +.Nm readonly , +.Nm rehash , +.Nm repeat , +.Nm return , +.Nm sched , +.Nm set , +.Nm setenv , +.Nm settc , +.Nm setty , +.Nm setvar , +.Nm shift , +.Nm source , +.Nm stop , +.Nm suspend , +.Nm switch , +.Nm telltc , +.Nm test , +.Nm then , +.Nm time , +.Nm times , +.Nm trap , +.Nm true , +.Nm type , +.Nm ulimit , +.Nm umask , +.Nm unalias , +.Nm uncomplete , +.Nm unhash , +.Nm unlimit , +.Nm unset , +.Nm unsetenv , +.Nm until , +.Nm wait , +.Nm where , +.Nm which , +.Nm while +.Nd shell built-in commands +.Sh SYNOPSIS +.Nm +.Op Fl options +.Op Ar args ... +.Sh DESCRIPTION +Shell builtin commands are commands that can be executed within the +running shell's process. +Note that, in the case of +.Xr csh 1 +builtin commands, the command is executed in a subshell if it occurs as +any component of a pipeline except the last. +.Pp +If a command specified to the shell contains a slash +.Dq \&/ , +the shell will not execute a builtin command, even if the last component +of the specified command matches the name of a builtin command. +Thus, while specifying +.Dq Ic echo +causes a builtin command to be executed under shells that support the +.Nm echo +builtin command, +specifying +.Dq Ic /bin/echo +or +.Dq Ic ./echo +does not. +.Pp +While some builtin commands may exist in more than one shell, their +operation may be different under each shell which supports them. +Below is a table which lists shell builtin commands, the standard shells +that support them and whether they exist as standalone utilities. +.Pp +Only builtin commands for the +.Xr csh 1 +and +.Xr sh 1 +shells are listed here. +Consult a shell's manual page for +details on the operation of its builtin commands. +Beware that the +.Xr sh 1 +manual page, at least, calls some of these commands +.Dq built-in commands +and some of them +.Dq reserved words . +Users of other shells may need to consult an +.Xr info 1 +page or other sources of documentation. +.Pp +Commands marked +.Dq Li No** +under +.Em External +do exist externally, +but are implemented as scripts using a builtin command of the same name. +.Bl -column ".Ic uncomplete" ".Em External" ".Xr csh 1" ".Xr sh 1" -offset indent +.It Xo +.Em "Command External" Ta Xr csh 1 Ta Xr sh 1 +.Xc +.It Ic ! Ta \&No Ta \&No Ta \&Yes +.It Ic % Ta \&No Ta Yes Ta \&No +.It Ic . Ta \&No Ta \&No Ta Yes +.It Ic : Ta \&No Ta Yes Ta Yes +.It Ic @ Ta \&No Ta Yes Ta Yes +.It Ic { Ta \&No Ta \&No Ta \&Yes +.It Ic } Ta \&No Ta \&No Ta \&Yes +.It Ic alias Ta \&No** Ta Yes Ta Yes +.It Ic alloc Ta \&No Ta Yes Ta \&No +.It Ic bg Ta No** Ta Yes Ta Yes +.It Ic bind Ta \&No Ta \&No Ta Yes +.It Ic bindkey Ta \&No Ta Yes Ta \&No +.It Ic break Ta \&No Ta Yes Ta \&Yes +.It Ic breaksw Ta \&No Ta Yes Ta \&No +.It Ic builtin Ta \&No Ta \&No Ta Yes +.It Ic builtins Ta \&No Ta Yes Ta \&No +.It Ic case Ta \&No Ta Yes Ta Yes +.It Ic cd Ta \&No** Ta Yes Ta Yes +.It Ic chdir Ta \&No Ta Yes Ta \&Yes +.It Ic command Ta \&No** Ta \&No Ta Yes +.It Ic complete Ta \&No Ta Yes Ta \&No +.It Ic continue Ta \&No Ta Yes Ta \&Yes +.It Ic default Ta \&No Ta Yes Ta \&No +.It Ic dirs Ta \&No Ta Yes Ta \&No +.It Ic do Ta \&No Ta \&No Ta Yes +.It Ic done Ta \&No Ta \&No Ta Yes +.It Ic echo Ta Yes Ta Yes Ta Yes +.It Ic echotc Ta \&No Ta Yes Ta \&No +.It Ic elif Ta \&No Ta \&No Ta Yes +.It Ic else Ta \&No Ta Yes Ta \&Yes +.It Ic end Ta \&No Ta Yes Ta \&No +.It Ic endif Ta \&No Ta Yes Ta \&No +.It Ic endsw Ta \&No Ta Yes Ta \&No +.It Ic esac Ta \&No Ta \&No Ta Yes +.It Ic eval Ta \&No Ta Yes Ta Yes +.It Ic exec Ta \&No Ta Yes Ta Yes +.It Ic exit Ta \&No Ta Yes Ta Yes +.It Ic export Ta \&No Ta \&No Ta Yes +.It Ic false Ta Yes Ta \&No Ta Yes +.It Ic fc Ta \&No** Ta \&No Ta Yes +.It Ic fg Ta \&No** Ta Yes Ta Yes +.It Ic filetest Ta \&No Ta Yes Ta \&No +.It Ic fi Ta \&No Ta \&No Ta Yes +.It Ic for Ta \&No Ta \&No Ta Yes +.It Ic foreach Ta \&No Ta Yes Ta \&No +.It Ic getopts Ta \&No** Ta \&No Ta Yes +.It Ic glob Ta \&No Ta Yes Ta \&No +.It Ic goto Ta \&No Ta Yes Ta \&No +.It Ic hash Ta \&No Ta \&No Ta Yes +.It Ic hashstat Ta \&No Ta Yes Ta \&No +.It Ic history Ta \&No Ta Yes Ta \&No +.It Ic hup Ta \&No Ta Yes Ta \&No +.It Ic if Ta \&No Ta Yes Ta \&Yes +.It Ic jobid Ta \&No Ta \&No Ta Yes +.It Ic jobs Ta \&No** Ta Yes Ta Yes +.It Ic kill Ta Yes Ta Yes Ta \&No +.It Ic limit Ta \&No Ta Yes Ta \&No +.It Ic local Ta \&No Ta \&No Ta Yes +.It Ic log Ta \&No Ta Yes Ta \&No +.It Ic login Ta Yes Ta Yes Ta \&No +.It Ic logout Ta \&No Ta Yes Ta \&No +.It Ic ls-F Ta \&No Ta Yes Ta \&No +.It Ic nice Ta Yes Ta Yes Ta \&No +.It Ic nohup Ta Yes Ta Yes Ta \&No +.It Ic notify Ta \&No Ta Yes Ta \&No +.It Ic onintr Ta \&No Ta Yes Ta \&No +.It Ic popd Ta \&No Ta Yes Ta \&No +.It Ic printenv Ta Yes Ta Yes Ta \&No +.It Ic pushd Ta \&No Ta Yes Ta \&No +.It Ic pwd Ta Yes Ta \&No Ta Yes +.It Ic read Ta \&No** Ta \&No Ta Yes +.It Ic readonly Ta \&No Ta \&No Ta Yes +.It Ic rehash Ta \&No Ta Yes Ta \&No +.It Ic repeat Ta \&No Ta Yes Ta \&No +.It Ic return Ta \&No Ta \&No Ta Yes +.It Ic sched Ta \&No Ta Yes Ta \&No +.It Ic set Ta \&No Ta Yes Ta \&Yes +.It Ic setenv Ta \&No Ta Yes Ta \&No +.It Ic settc Ta \&No Ta Yes Ta \&No +.It Ic setty Ta \&No Ta Yes Ta \&No +.It Ic setvar Ta \&No Ta \&No Ta Yes +.It Ic shift Ta \&No Ta Yes Ta Yes +.It Ic source Ta \&No Ta Yes Ta \&No +.It Ic stop Ta \&No Ta Yes Ta \&No +.It Ic suspend Ta \&No Ta Yes Ta \&No +.It Ic switch Ta \&No Ta Yes Ta \&No +.It Ic telltc Ta \&No Ta Yes Ta \&No +.It Ic test Ta Yes Ta \&No Ta Yes +.It Ic then Ta \&No Ta \&No Ta Yes +.It Ic time Ta Yes Ta Yes Ta \&No +.It Ic times Ta \&No Ta \&No Ta Yes +.It Ic trap Ta \&No Ta \&No Ta Yes +.It Ic true Ta Yes Ta \&No Ta Yes +.It Ic type Ta \&No Ta \&No Ta Yes +.It Ic ulimit Ta \&No Ta \&No Ta Yes +.It Ic umask Ta \&No** Ta Yes Ta Yes +.It Ic unalias Ta \&No** Ta Yes Ta Yes +.It Ic uncomplete Ta \&No Ta Yes Ta \&No +.It Ic unhash Ta \&No Ta Yes Ta \&No +.It Ic unlimit Ta \&No Ta Yes Ta \&No +.It Ic unset Ta \&No Ta Yes Ta Yes +.It Ic unsetenv Ta \&No Ta Yes Ta \&No +.It Ic until Ta \&No Ta \&No Ta Yes +.It Ic wait Ta \&No** Ta Yes Ta Yes +.It Ic where Ta \&No Ta Yes Ta \&No +.It Ic which Ta Yes Ta Yes Ta \&No +.It Ic while Ta \&No Ta Yes Ta \&Yes +.El +.Sh SEE ALSO +.Xr csh 1 , +.Xr dash 1 , +.Xr echo 1 , +.Xr false 1 , +.Xr info 1 , +.Xr kill 1 , +.Xr login 1 , +.Xr nice 1 , +.Xr nohup 1 , +.Xr printenv 1 , +.Xr pwd 1 , +.Xr sh 1 , +.Xr test 1 , +.Xr time 1 , +.Xr true 1 , +.Xr which 1 , +.Xr zsh 1 +.Sh HISTORY +The +.Nm +manual page first appeared in +.Fx 3.4 . +.Sh AUTHORS +This manual page was written by +.An Sheldon Hearn Aq sheldonh@FreeBSD.org . diff --git a/shell_cmds/alias/generic.sh b/shell_cmds/alias/generic.sh new file mode 100644 index 0000000..5948cc9 --- /dev/null +++ b/shell_cmds/alias/generic.sh @@ -0,0 +1,4 @@ +#!/bin/sh +# $FreeBSD: src/usr.bin/alias/generic.sh,v 1.2 2005/10/24 22:32:19 cperciva Exp $ +# This file is in the public domain. +builtin `echo ${0##*/} | tr \[:upper:] \[:lower:]` ${1+"$@"} diff --git a/shell_cmds/apply/apply.1 b/shell_cmds/apply/apply.1 new file mode 100644 index 0000000..2eb1516 --- /dev/null +++ b/shell_cmds/apply/apply.1 @@ -0,0 +1,140 @@ +.\" Copyright (c) 1983, 1990, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)apply.1 8.2 (Berkeley) 4/4/94 +.\" $FreeBSD: src/usr.bin/apply/apply.1,v 1.14 2002/04/19 23:16:06 charnier Exp $ +.\" +.Dd April 4, 1994 +.Dt APPLY 1 +.Os +.Sh NAME +.Nm apply +.Nd apply a command to a set of arguments +.Sh SYNOPSIS +.Nm +.Op Fl a Ar c +.Op Fl d +.Op Fl # +.Ar command argument ... +.Sh DESCRIPTION +The +.Nm +utility runs the named +.Ar command +on each +argument +.Ar argument +in turn. +.Pp +Character sequences of the form +.Dq Li \&%d +in +.Ar command , +where +.Sq Li d +is a digit from 1 to 9, are replaced by the +.Li d Ns \'th +following unused +.Ar argument . +In this case, the largest digit number of arguments are discarded for +each execution of +.Ar command . +.Pp +The options are as follows: +.Bl -tag -width indent +.It Fl # +Normally arguments are taken singly; the optional number +.Fl # +specifies the number of arguments to be passed to +.Ar command . +If the number is zero, +.Ar command +is run, without arguments, once for each +.Ar argument . +.Pp +If any sequences of +.Dq Li \&%d +occur in +.Ar command , +the +.Fl # +option is ignored. +.It Fl a Ar c +The use of the character +.Sq Li % +as a magic character may be changed with the +.Fl a +option. +.It Fl d +Display the commands that would have been executed, but do not actually +execute them. +.El +.Sh ENVIRONMENT +The following environment variable affects the execution of +.Nm : +.Bl -tag -width SHELL +.It Ev SHELL +Pathname of shell to use. +If this variable is not defined, the Bourne shell is used. +.El +.Sh EXAMPLES +.Bl -tag -width apply -compact +.It Li "apply echo a*" +is similar to +.Xr ls 1 ; +.It Li "apply \-2 cmp a1 b1 a2 b2 a3 b3" +compares the `a' files to the `b' files; +.It Li "apply \-0 who 1 2 3 4 5" +runs +.Xr who 1 +5 times; and +.It Li "apply \'ln %1 /usr/joe\'" * +links all files in the current directory to the directory +.Pa /usr/joe . +.El +.Sh FILES +.Bl -tag -width /bin/sh -compact +.It Pa /bin/sh +default shell +.El +.Sh AUTHORS +.An Rob Pike +.Sh BUGS +Shell metacharacters in +.Ar command +may have bizarre effects; it is best to enclose complicated +commands in single quotes +.Pq '' . +.Sh HISTORY +The +.Nm +command appeared in +.Bx 4.2 . diff --git a/shell_cmds/apply/apply.c b/shell_cmds/apply/apply.c new file mode 100644 index 0000000..cf4e9c1 --- /dev/null +++ b/shell_cmds/apply/apply.c @@ -0,0 +1,267 @@ +/*- + * Copyright (c) 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Jan-Simon Pendry. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if 0 +#ifndef lint +static char sccsid[] = "@(#)apply.c 8.4 (Berkeley) 4/4/94"; +#endif +#endif + +#include <sys/cdefs.h> +__RCSID("$FreeBSD: src/usr.bin/apply/apply.c,v 1.22 2002/07/14 18:22:12 alfred Exp $"); + +#include <sys/types.h> +#include <sys/wait.h> + +#include <ctype.h> +#include <err.h> +#include <paths.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#define EXEC "exec " + +static int exec_shell(const char *, char *, char *); +static void usage(void); + +int +main(int argc, char *argv[]) { + int ch, debug, i, magic, n, nargs, offset, rval; + size_t clen, cmdsize, l; + char *c, *cmd, *name, *p, *q, *shell, *slashp, *tmpshell; + + debug = 0; + magic = '%'; /* Default magic char is `%'. */ + nargs = -1; + while ((ch = getopt(argc, argv, "a:d0123456789")) != -1) + switch (ch) { + case 'a': + if (optarg[1] != '\0') + errx(1, + "illegal magic character specification"); + magic = optarg[0]; + break; + case 'd': + debug = 1; + break; + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': + if (nargs != -1) + errx(1, + "only one -# argument may be specified"); + nargs = optopt - '0'; + break; + default: + usage(); + } + argc -= optind; + argv += optind; + + if (argc < 2) + usage(); + + /* + * The command to run is argv[0], and the args are argv[1..]. + * Look for %digit references in the command, remembering the + * largest one. + */ + for (n = 0, p = argv[0]; *p != '\0'; ++p) + if (p[0] == magic && isdigit(p[1]) && p[1] != '0') { + ++p; + if (p[0] - '0' > n) + n = p[0] - '0'; + } + + /* + * Figure out the shell and name arguments to pass to execl() + * in exec_shell(). Always malloc() shell and just set name + * to point at the last part of shell if there are any backslashes, + * otherwise just set it to point at the space malloc()'d. If + * SHELL environment variable exists, replace contents of + * shell with it. + */ + shell = name = NULL; + tmpshell = getenv("SHELL"); + shell = (tmpshell != NULL) ? strdup(tmpshell) : strdup(_PATH_BSHELL); + if (shell == NULL) + err(1, "strdup() failed"); + slashp = strrchr(shell, '/'); + name = (slashp != NULL) ? slashp + 1 : shell; + + /* + * If there were any %digit references, then use those, otherwise + * build a new command string with sufficient %digit references at + * the end to consume (nargs) arguments each time round the loop. + * Allocate enough space to hold the maximum command. Save the + * size to pass to snprintf(). + */ + cmdsize = sizeof(EXEC) - 1 + strlen(argv[0]) + + 9 * (sizeof(" %1") - 1) + 1; + if ((cmd = malloc(cmdsize)) == NULL) + err(1, NULL); + + if (n == 0) { + /* If nargs not set, default to a single argument. */ + if (nargs == -1) + nargs = 1; + + p = cmd; + offset = snprintf(cmd, cmdsize, EXEC "%s", argv[0]); + if ((size_t)offset >= cmdsize) + err(1, "snprintf() failed"); + p += offset; + cmdsize -= offset; + for (i = 1; i <= nargs; i++) { + offset = snprintf(p, cmdsize, " %c%d", magic, i); + if ((size_t)offset >= cmdsize) + err(1, "snprintf() failed"); + p += offset; + cmdsize -= offset; + } + + /* + * If nargs set to the special value 0, eat a single + * argument for each command execution. + */ + if (nargs == 0) + nargs = 1; + } else { + offset = snprintf(cmd, cmdsize, EXEC "%s", argv[0]); + if ((size_t)offset >= cmdsize) + err(1, "snprintf() failed"); + nargs = n; + } + + /* + * Grab some space in which to build the command. Allocate + * as necessary later, but no reason to build it up slowly + * for the normal case. + */ + if ((c = malloc(clen = 1024)) == NULL) + err(1, NULL); + + /* + * (argc) and (argv) are still offset by one to make it simpler to + * expand %digit references. At the end of the loop check for (argc) + * equals 1 means that all the (argv) has been consumed. + */ + for (rval = 0; argc > nargs; argc -= nargs, argv += nargs) { + /* + * Find a max value for the command length, and ensure + * there's enough space to build it. + */ + for (l = strlen(cmd), i = 0; i < nargs; i++) + l += strlen(argv[i+1]); + if (l > clen && (c = realloc(c, clen = l)) == NULL) + err(1, NULL); + + /* Expand command argv references. */ + for (p = cmd, q = c; *p != '\0'; ++p) + if (p[0] == magic && isdigit(p[1]) && p[1] != '0') { + offset = snprintf(q, l, "%s", + argv[(++p)[0] - '0']); + if ((size_t)offset >= l) + err(1, "snprintf() failed"); + q += offset; + l -= offset; + } else + *q++ = *p; + + /* Terminate the command string. */ + *q = '\0'; + + /* Run the command. */ + if (debug) + (void)printf("%s\n", c); + else + if (exec_shell(c, shell, name)) + rval = 1; + } + + if (argc != 1) + errx(1, "expecting additional argument%s after \"%s\"", + (nargs - argc) ? "s" : "", argv[argc - 1]); + free(cmd); + free(c); + free(shell); + exit(rval); +} + +/* + * exec_shell -- + * Execute a shell command using passed use_shell and use_name + * arguments. + */ +static int +exec_shell(const char *command, char *use_shell, char *use_name) +{ + pid_t pid; + int omask, pstat; + sig_t intsave, quitsave; + + if (!command) /* just checking... */ + return(1); + + omask = sigblock(sigmask(SIGCHLD)); + switch(pid = vfork()) { + case -1: /* error */ + err(1, "vfork"); + case 0: /* child */ + (void)sigsetmask(omask); + execl(use_shell, use_name, "-c", command, (char *)NULL); + warn("%s", use_shell); + _exit(1); + } + intsave = signal(SIGINT, SIG_IGN); + quitsave = signal(SIGQUIT, SIG_IGN); + pid = waitpid(pid, &pstat, 0); + (void)sigsetmask(omask); + (void)signal(SIGINT, intsave); + (void)signal(SIGQUIT, quitsave); + return(pid == -1 ? -1 : pstat); +} + +void +usage(void) +{ + + (void)fprintf(stderr, + "usage: apply [-a magic] [-d] [-0123456789] command arguments ...\n"); + exit(1); +} diff --git a/shell_cmds/basename/basename.1 b/shell_cmds/basename/basename.1 new file mode 100644 index 0000000..835d21a --- /dev/null +++ b/shell_cmds/basename/basename.1 @@ -0,0 +1,114 @@ +.\" Copyright (c) 1990, 1993, 1994 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" the Institute of Electrical and Electronics Engineers, Inc. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)basename.1 8.2 (Berkeley) 4/18/94 +.\" $FreeBSD: src/usr.bin/basename/basename.1,v 1.13 2002/06/30 13:40:35 jmallett Exp $ +.\" +.Dd April 18, 1994 +.Dt BASENAME 1 +.Os +.Sh NAME +.Nm basename , dirname +.Nd return filename or directory portion of pathname +.Sh SYNOPSIS +.Nm +.Ar string +.Op Ar suffix +.Nm +.Op Fl a +.Op Fl s Ar suffix +.Ar string +.Op Ar ... +.Nm dirname +.Ar string +.Sh DESCRIPTION +The +.Nm +utility deletes any prefix ending with the last slash +.Ql \&/ +character present in +.Ar string +(after first stripping trailing slashes), +and a +.Ar suffix , +if given. +The +.Ar suffix +is not stripped if it is identical to the remaining characters in +.Ar string . +The resulting filename is written to the standard output. +A non-existent suffix is ignored. +If +.Fl a +is specified, then every argument is treated as a +.Ar string +as if +.Nm +were invoked with just one argument. +If +.Fl s +is specified, then the +.Ar suffix +is taken as its argument, and all other arguments are treated as a +.Ar string . +.Pp +The +.Nm dirname +utility deletes the filename portion, beginning +with the last slash +.Ql \&/ +character to the end of +.Ar string +(after first stripping trailing slashes), +and writes the result to the standard output. +.Sh EXAMPLES +The following line sets the shell variable +.Ev FOO +to +.Pa /usr/bin . +.Pp +.Dl FOO=`dirname /usr/bin/trail` +.Sh DIAGNOSTICS +.Ex -std basename dirname +.Sh SEE ALSO +.Xr csh 1 , +.Xr sh 1 +.Sh STANDARDS +The +.Nm +and +.Nm dirname +utilities are expected to be +.St -p1003.2 +compatible. diff --git a/shell_cmds/basename/basename.c b/shell_cmds/basename/basename.c new file mode 100644 index 0000000..6bde858 --- /dev/null +++ b/shell_cmds/basename/basename.c @@ -0,0 +1,119 @@ +/*- + * Copyright (c) 1991, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static const char copyright[] = +"@(#) Copyright (c) 1991, 1993, 1994\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif + +#if 0 +#ifndef lint +static char sccsid[] = "@(#)basename.c 8.4 (Berkeley) 5/4/95"; +#endif /* not lint */ +#endif + +#include <sys/cdefs.h> +__RCSID("$FreeBSD: src/usr.bin/basename/basename.c,v 1.14 2002/09/04 23:28:52 dwmalone Exp $"); + +#include <err.h> +#include <libgen.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +void usage(void); + +int +main(int argc, char **argv) +{ + char *p, *q, *suffix; + size_t suffixlen; + int aflag, ch; + + aflag = 0; + suffix = NULL; + suffixlen = 0; + + while ((ch = getopt(argc, argv, "as:")) != -1) + switch(ch) { + case 'a': + aflag = 1; + break; + case 's': + suffix = optarg; + break; + case '?': + default: + usage(); + } + argc -= optind; + argv += optind; + + if (argc < 1) + usage(); + + if (!*argv[0]) { + printf("\n"); + exit(0); + } + if ((p = basename(argv[0])) == NULL) + err(1, "%s", argv[0]); + if ((suffix == NULL && !aflag) && argc == 2) { + suffix = argv[1]; + argc--; + } + if (suffix != NULL) + suffixlen = strlen(suffix); + while (argc--) { + if ((p = basename(*argv)) == NULL) + err(1, "%s", argv[0]); + if (suffixlen && (q = strchr(p, '\0') - suffixlen) > p && + strcmp(suffix, q) == 0) + *q = '\0'; + argv++; + (void)printf("%s\n", p); + } + exit(0); +} + +void +usage(void) +{ + + (void)fprintf(stderr, +"usage: basename string [suffix]\n" +" basename [-a] [-s suffix] string [...]\n"); + exit(1); +} diff --git a/shell_cmds/basename/dirname.1 b/shell_cmds/basename/dirname.1 new file mode 100644 index 0000000..ee3bc6f --- /dev/null +++ b/shell_cmds/basename/dirname.1 @@ -0,0 +1 @@ +.so man1/basename.1 diff --git a/shell_cmds/chroot/chroot.8 b/shell_cmds/chroot/chroot.8 new file mode 100644 index 0000000..fce026b --- /dev/null +++ b/shell_cmds/chroot/chroot.8 @@ -0,0 +1,102 @@ +.\" Copyright (c) 1988, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" from: @(#)chroot.8 8.1 (Berkeley) 6/9/93 +.\" $NetBSD: chroot.8,v 1.7 1998/10/06 03:47:50 mrg Exp $ +.\" +.Dd October 6, 1998 +.Dt CHROOT 8 +.Os BSD 4.3 +.Sh NAME +.Nm chroot +.Nd change root directory +.Sh SYNOPSIS +.Nm +.Op Fl u user +.Op Fl g group +.Op Fl G group,group,... +.Ar newroot +.Op Ar command +.Sh DESCRIPTION +The +.Nm +command changes its root directory to the supplied directory +.Ar newroot +and exec's +.Ar command , +if supplied, or an interactive copy of your shell. +.Pp +If the +.Fl u , +.Fl g +or +.Fl G +options are given, the user, group and group list of the process are +set to these values after the chroot has taken place. See +.Xr setgid 2 , +.Xr setgroups 2 , +.Xr setuid 2 , +.Xr getgrnam 3 +and +.Xr getpwnam 3 . +.Pp +Note, +.Ar command +or the shell are run as your real-user-id. +.Sh ENVIRONMENT +The following environment variable is referenced by +.Nm : +.Bl -tag -width SHELL +.It Ev SHELL +If set, +the string specified by +.Ev SHELL +is interpreted as the name of +the shell to exec. +If the variable +.Ev SHELL +is not set, +.Pa /bin/sh +is used. +.El +.Sh SEE ALSO +.Xr chdir 2 , +.Xr chroot 2 , +.Xr environ 7 +.Sh HISTORY +The +.Nm +utility first appeared in +.Bx 4.4 . +.Sh SECURITY CONSIDERATIONS +.Nm +should never be installed setuid root, as it would then be possible +to exploit the program to gain root privileges. diff --git a/shell_cmds/chroot/chroot.c b/shell_cmds/chroot/chroot.c new file mode 100644 index 0000000..c9c38f5 --- /dev/null +++ b/shell_cmds/chroot/chroot.c @@ -0,0 +1,178 @@ +/* + * Copyright (c) 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +#ifndef lint +__COPYRIGHT("@(#) Copyright (c) 1988, 1993\n\ + The Regents of the University of California. All rights reserved.\n"); +#endif /* not lint */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)chroot.c 8.1 (Berkeley) 6/9/93"; +#else +__RCSID("$NetBSD: chroot.c,v 1.7 1998/10/06 03:47:51 mrg Exp $"); +#endif +#endif /* not lint */ + +#include <sys/param.h> + +#include <ctype.h> +#include <err.h> +#include <errno.h> +#include <grp.h> +#include <paths.h> +#include <pwd.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +int main __P((int, char **)); +void usage __P((void)) __attribute__((__noreturn__)); + +char *user; /* user to switch to before running program */ +char *group; /* group to switch to ... */ +char *grouplist; /* group list to switch to ... */ + +int +main(argc, argv) + int argc; + char *argv[]; +{ + struct group *gp; + struct passwd *pw; + char *shell, *endp, *comma; + gid_t gid = 0, gidlist[NGROUPS_MAX]; + uid_t uid = 0; + int ch, gids; + + while ((ch = getopt(argc, argv, "G:g:u:")) != -1) + switch(ch) { + case 'u': + user = optarg; + break; + case 'g': + group = optarg; + break; + case 'G': + grouplist = optarg; + break; + case '?': + default: + usage(); + } + argc -= optind; + argv += optind; + + if (argc < 1) + usage(); + + if (group) { + if (isdigit(*group)) { + gid = (gid_t)strtol(group, &endp, 0); + if (endp == group) + goto getgroup; + } else { +getgroup: + if ((gp = getgrnam(group))) + gid = gp->gr_gid; + else + errx(1, "no such group %s", group); + } + } + + for (gids = 0; grouplist; ) { + comma = strchr(grouplist, ','); + + if (comma) + *comma++ = '\0'; + + if (isdigit(*grouplist)) { + gidlist[gids] = (gid_t)strtol(grouplist, &endp, 0); + if (endp == grouplist) + goto getglist; + } else { +getglist: + if ((gp = getgrnam(grouplist))) + gidlist[gids] = gp->gr_gid; + else + errx(1, "no such group %s", group); + } + gids++; + grouplist = comma; + } + + if (user) { + if (isdigit(*user)) { + uid = (uid_t)strtol(user, &endp, 0); + if (endp == user) + goto getuser; + } else { +getuser: + if ((pw = getpwnam(user))) + uid = pw->pw_uid; + else + errx(1, "no such user %s", user); + } + } + + if (chdir(argv[0]) || chroot(".")) + err(1, "%s", argv[0]); + + if (gids && setgroups(gids, gidlist) < 0) + err(1, "setgroups"); + if (group && setgid(gid) < 0) + err(1, "setgid"); + if (user && setuid(uid) < 0) + err(1, "setuid"); + + if (argv[1]) { + execvp(argv[1], &argv[1]); + err(1, "%s", argv[1]); + } + + if (!(shell = getenv("SHELL"))) + shell = _PATH_BSHELL; + execlp(shell, shell, "-i", NULL); + err(1, "%s", shell); + /* NOTREACHED */ +} + +void +usage() +{ + (void)fprintf(stderr, "usage: chroot [-g group] [-G group,group,...] " + "[-u user] newroot [command]\n"); + exit(1); +} diff --git a/shell_cmds/date/date.1 b/shell_cmds/date/date.1 new file mode 100644 index 0000000..0cabb01 --- /dev/null +++ b/shell_cmds/date/date.1 @@ -0,0 +1,472 @@ +.\"- +.\" Copyright (c) 1980, 1990, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" the Institute of Electrical and Electronics Engineers, Inc. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)date.1 8.3 (Berkeley) 4/28/95 +.\" $FreeBSD$ +.\" +.Dd May 7, 2015 +.Dt DATE 1 +.Os +.Sh NAME +.Nm date +.Nd display or set date and time +.Sh SYNOPSIS +.Nm +.Op Fl jRu +.Op Fl r Ar seconds | Ar filename +.Oo +.Fl v +.Sm off +.Op Cm + | - +.Ar val Op Ar ymwdHMS +.Sm on +.Oc +.Ar ... +.Op Cm + Ns Ar output_fmt +.Nm +.Op Fl jnu +.Sm off +.Op Oo Oo Ar mm Oc Ar dd Oc Ar HH +.Ar MM Oo Oo Ar cc Oc Ar yy Oc Op Ar .ss +.Sm on +.Nm +.Op Fl jnRu +.Fl f Ar input_fmt new_date +.Op Cm + Ns Ar output_fmt +.Nm +.Op Fl d Ar dst +.Op Fl t Ar minutes_west +.Sh DESCRIPTION +When invoked without arguments, the +.Nm +utility displays the current date and time. +Otherwise, depending on the options specified, +.Nm +will set the date and time or print it in a user-defined way. +.Pp +The +.Nm +utility displays the date and time read from the kernel clock. +When used to set the date and time, +both the kernel clock and the hardware clock are updated. +.Pp +Only the superuser may set the date, +and if the system securelevel (see +.Xr securelevel 7 ) +is greater than 1, +the time may not be changed by more than 1 second. +.Pp +The options are as follows: +.Bl -tag -width Ds +.It Fl d Ar dst +Set the kernel's value for daylight saving time. +If +.Ar dst +is non-zero, future calls +to +.Xr gettimeofday 2 +will return a non-zero for +.Fa tz_dsttime . +.It Fl f +Use +.Ar input_fmt +as the format string to parse the +.Ar new_date +provided rather than using the default +.Sm off +.Oo Oo Oo +.Ar mm Oc +.Ar dd Oc +.Ar HH Oc +.Ar MM +.Oo Oo +.Ar cc Oc +.Ar yy Oc Oo +.Ar .ss Oc +.Sm on +format. +Parsing is done using +.Xr strptime 3 . +.It Fl j +Do not try to set the date. +This allows you to use the +.Fl f +flag in addition to the +.Cm + +option to convert one date format to another. +.It Fl n +By default, if the +.Xr timed 8 +daemon is running, +.Nm +sets the time on all of the machines in the local group. +The +.Fl n +option suppresses this behavior and causes the time to be set only on the +current machine. +.It Fl R +Use RFC 2822 date and time output format. This is equivalent to use +.Dq Li %a, %d %b %Y \&%T %z +as +.Ar output_fmt +while +.Ev LC_TIME +is set to the +.Dq C +locale . +.It Fl r Ar seconds +Print the date and time represented by +.Ar seconds , +where +.Ar seconds +is the number of seconds since the Epoch +(00:00:00 UTC, January 1, 1970; +see +.Xr time 3 ) , +and can be specified in decimal, octal, or hex. +.It Fl r Ar filename +Print the date and time of the last modification of +.Ar filename . +.It Fl t Ar minutes_west +Set the system's value for minutes west of +.Tn GMT . +.Ar minutes_west +specifies the number of minutes returned in +.Fa tz_minuteswest +by future calls to +.Xr gettimeofday 2 . +.It Fl u +Display or set the date in +.Tn UTC +(Coordinated Universal) time. +.It Fl v +Adjust (i.e., take the current date and display the result of the +adjustment; not actually set the date) the second, minute, hour, month +day, week day, month or year according to +.Ar val . +If +.Ar val +is preceded with a plus or minus sign, +the date is adjusted forwards or backwards according to the remaining string, +otherwise the relevant part of the date is set. +The date can be adjusted as many times as required using these flags. +Flags are processed in the order given. +.Pp +When setting values +(rather than adjusting them), +seconds are in the range 0-59, minutes are in the range 0-59, hours are +in the range 0-23, month days are in the range 1-31, week days are in the +range 0-6 (Sun-Sat), +months are in the range 1-12 (Jan-Dec) +and years are in the range 80-38 or 1980-2038. +.Pp +If +.Ar val +is numeric, one of either +.Ar y , +.Ar m , +.Ar w , +.Ar d , +.Ar H , +.Ar M +or +.Ar S +must be used to specify which part of the date is to be adjusted. +.Pp +The week day or month may be specified using a name rather than a +number. +If a name is used with the plus +(or minus) +sign, the date will be put forwards +(or backwards) +to the next +(previous) +date that matches the given week day or month. +This will not adjust the date, +if the given week day or month is the same as the current one. +.Pp +When a date is adjusted to a specific value or in units greater than hours, +daylight savings time considerations are ignored. +Adjustments in units of hours or less honor daylight saving time. +So, assuming the current date is March 26, 0:30 and that the DST adjustment +means that the clock goes forward at 01:00 to 02:00, using +.Fl v No +1H +will adjust the date to March 26, 2:30. +Likewise, if the date is October 29, 0:30 and the DST adjustment means that +the clock goes back at 02:00 to 01:00, using +.Fl v No +3H +will be necessary to reach October 29, 2:30. +.Pp +When the date is adjusted to a specific value that does not actually exist +(for example March 26, 1:30 BST 2000 in the Europe/London timezone), +the date will be silently adjusted forwards in units of one hour until it +reaches a valid time. +When the date is adjusted to a specific value that occurs twice +(for example October 29, 1:30 2000), +the resulting timezone will be set so that the date matches the earlier of +the two times. +.Pp +It is not possible to adjust a date to an invalid absolute day, so using +the switches +.Fl v No 31d Fl v No 12m +will simply fail five months of the year. +It is therefore usual to set the month before setting the day; using +.Fl v No 12m Fl v No 31d +always works. +.Pp +Adjusting the date by months is inherently ambiguous because +a month is a unit of variable length depending on the current date. +This kind of date adjustment is applied in the most intuitive way. +First of all, +.Nm +tries to preserve the day of the month. +If it is impossible because the target month is shorter than the present one, +the last day of the target month will be the result. +For example, using +.Fl v No +1m +on May 31 will adjust the date to June 30, while using the same option +on January 30 will result in the date adjusted to the last day of February. +This approach is also believed to make the most sense for shell scripting. +Nevertheless, be aware that going forth and back by the same number of +months may take you to a different date. +.Pp +Refer to the examples below for further details. +.El +.Pp +An operand with a leading plus +.Pq Sq + +sign signals a user-defined format string +which specifies the format in which to display the date and time. +The format string may contain any of the conversion specifications +described in the +.Xr strftime 3 +manual page, as well as any arbitrary text. +A newline +.Pq Ql \en +character is always output after the characters specified by +the format string. +The format string for the default display is +.Dq +%+ . +.Pp +If an operand does not have a leading plus sign, it is interpreted as +a value for setting the system's notion of the current date and time. +The canonical representation for setting the date and time is: +.Pp +.Bl -tag -width Ds -compact -offset indent +.It Ar cc +Century +(either 19 or 20) +prepended to the abbreviated year. +.It Ar yy +Year in abbreviated form +(e.g., 89 for 1989, 06 for 2006). +.It Ar mm +Numeric month, a number from 1 to 12. +.It Ar dd +Day, a number from 1 to 31. +.It Ar HH +Hour, a number from 0 to 23. +.It Ar MM +Minutes, a number from 0 to 59. +.It Ar ss +Seconds, a number from 0 to 61 +(59 plus a maximum of two leap seconds). +.El +.Pp +Everything but the minutes is optional. +.Pp +Time changes for Daylight Saving Time, standard time, leap seconds, +and leap years are handled automatically. +.Sh ENVIRONMENT +The following environment variables affect the execution of +.Nm : +.Bl -tag -width Ds +.It Ev TZ +The timezone to use when displaying dates. +The normal format is a pathname relative to +.Pa /usr/share/zoneinfo . +For example, the command +.Dq TZ=America/Los_Angeles date +displays the current time in California. +See +.Xr environ 7 +for more information. +.El +.Sh FILES +.Bl -tag -width /var/log/messages -compact +.It Pa /var/log/messages +record of the user setting the time +.El +.Sh EXIT STATUS +The +.Nm +utility exits 0 on success, 1 if unable to set the date, and 2 +if able to set the local date, but unable to set it globally. +.Sh EXAMPLES +The command: +.Pp +.Dl "date ""+DATE: %Y-%m-%d%nTIME: %H:%M:%S""" +.Pp +will display: +.Bd -literal -offset indent +DATE: 1987-11-21 +TIME: 13:36:16 +.Ed +.Pp +In the Europe/London timezone, the command: +.Pp +.Dl "date -v1m -v+1y" +.Pp +will display: +.Pp +.Dl "Sun Jan 4 04:15:24 GMT 1998" +.Pp +where it is currently +.Li "Mon Aug 4 04:15:24 BST 1997" . +.Pp +The command: +.Pp +.Dl "date -v1d -v3m -v0y -v-1d" +.Pp +will display the last day of February in the year 2000: +.Pp +.Dl "Tue Feb 29 03:18:00 GMT 2000" +.Pp +So will the command: +.Pp +.Dl "date -v3m -v30d -v0y -v-1m" +.Pp +because there is no such date as the 30th of February. +.Pp +The command: +.Pp +.Dl "date -v1d -v+1m -v-1d -v-fri" +.Pp +will display the last Friday of the month: +.Pp +.Dl "Fri Aug 29 04:31:11 BST 1997" +.Pp +where it is currently +.Li "Mon Aug 4 04:31:11 BST 1997" . +.Pp +The command: +.Pp +.Dl "date 0613162785" +.Pp +sets the date to +.Dq Li "June 13, 1985, 4:27 PM" . +.Pp +.Dl "date ""+%m%d%H%M%Y.%S""" +.Pp +may be used on one machine to print out the date +suitable for setting on another. +.Pp +The command: +.Pp +.Dl "date 1432" +.Pp +sets the time to +.Li "2:32 PM" , +without modifying the date. +.Pp +Finally the command: +.Pp +.Dl "date -j -f ""%a %b %d %T %Z %Y"" ""`date`"" ""+%s""" +.Pp +can be used to parse the output from +.Nm +and express it in Epoch time. +.Sh DIAGNOSTICS +Occasionally, when +.Xr timed 8 +synchronizes the time on many hosts, the setting of a new time value may +require more than a few seconds. +On these occasions, +.Nm +prints: +.Ql Network time being set . +The message +.Ql Communication error with timed +occurs when the communication +between +.Nm +and +.Xr timed 8 +fails. +.Sh LEGACY SYNOPSIS +As above, except for the second line, which is: +.Pp +.Nm +.Op Fl jnu +.Sm off +.Op Oo Oo Oo Oo Ar cc Oc Ar yy Oc Ar mm Oc Ar dd Oc Ar HH +.Ar MM Op Ar .ss +.Sm on +.Sh LEGACY DIAGNOSTICS +When invoked in legacy mode, the following exit values are returned: +.Bl -tag -width X -compact +.It 0 +The date was written successfully +.It 1 +Unable to set the date +.It 2 +Able to set the local date, but unable to set it globally +.El +.Pp +For more information about legacy mode, see +.Xr compat 5 . +.Sh SEE ALSO +.Xr locale 1 , +.Xr gettimeofday 2 , +.Xr getutxent 3 , +.Xr strftime 3 , +.Xr strptime 3 , +.Xr timed 8 +.Rs +.%T "TSP: The Time Synchronization Protocol for UNIX 4.3BSD" +.%A R. Gusella +.%A S. Zatti +.Re +.Sh STANDARDS +The +.Nm +utility is expected to be compatible with +.St -p1003.2 . +The +.Fl d , f , j , n , r , t , +and +.Fl v +options are all extensions to the standard. +.Sh HISTORY +A +.Nm +command appeared in +.At v1 . diff --git a/shell_cmds/date/date.c b/shell_cmds/date/date.c new file mode 100644 index 0000000..a45e110 --- /dev/null +++ b/shell_cmds/date/date.c @@ -0,0 +1,365 @@ +/*- + * Copyright (c) 1985, 1987, 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char const copyright[] = +"@(#) Copyright (c) 1985, 1987, 1988, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#if 0 +#ifndef lint +static char sccsid[] = "@(#)date.c 8.2 (Berkeley) 4/28/95"; +#endif /* not lint */ +#endif + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/time.h> +#include <sys/stat.h> + +#include <ctype.h> +#include <err.h> +#include <locale.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> +#include <unistd.h> +#include <utmpx.h> + +#ifdef __APPLE__ +#include <get_compat.h> +#else +#define COMPAT_MODE(a,b) (1) +#endif /* __APPLE__ */ + +#include "extern.h" +#include "vary.h" + +#ifndef TM_YEAR_BASE +#define TM_YEAR_BASE 1900 +#endif + +#ifdef __APPLE__ +#define st_mtim st_mtimespec +#endif + +static time_t tval; +int retval; +static int unix2003_std; /* to determine legacy vs std mode */ + +static void setthetime(const char *, const char *, int, int); +static void badformat(void); +static void usage(void); + +static const char *rfc2822_format = "%a, %d %b %Y %T %z"; + +int +main(int argc, char *argv[]) +{ + struct timezone tz; + int ch, rflag; + int jflag, nflag, Rflag; + const char *format; + char buf[1024]; + char *endptr, *fmt; + char *tmp; + int set_timezone; + struct vary *v; + const struct vary *badv; + struct tm lt; + struct stat sb; + + unix2003_std = COMPAT_MODE("bin/date", "unix2003"); /* Determine the STD */ + + v = NULL; + fmt = NULL; + (void) setlocale(LC_TIME, ""); + tz.tz_dsttime = tz.tz_minuteswest = 0; + rflag = 0; + jflag = nflag = Rflag = 0; + set_timezone = 0; + while ((ch = getopt(argc, argv, "d:f:jnRr:t:uv:")) != -1) + switch((char)ch) { + case 'd': /* daylight savings time */ + tz.tz_dsttime = strtol(optarg, &endptr, 10) ? 1 : 0; + if (endptr == optarg || *endptr != '\0') + usage(); + set_timezone = 1; + break; + case 'f': + fmt = optarg; + break; + case 'j': + jflag = 1; /* don't set time */ + break; + case 'n': /* don't set network */ + nflag = 1; + break; + case 'R': /* RFC 2822 datetime format */ + Rflag = 1; + break; + case 'r': /* user specified seconds */ + rflag = 1; + tval = strtoq(optarg, &tmp, 0); + if (*tmp != 0) { + if (stat(optarg, &sb) == 0) + tval = sb.st_mtim.tv_sec; + else + usage(); + } + break; + case 't': /* minutes west of UTC */ + /* error check; don't allow "PST" */ + tz.tz_minuteswest = strtol(optarg, &endptr, 10); + if (endptr == optarg || *endptr != '\0') + usage(); + set_timezone = 1; + break; + case 'u': /* do everything in UTC */ + (void)setenv("TZ", "UTC0", 1); + break; + case 'v': + v = vary_append(v, optarg); + break; + default: + usage(); + } + argc -= optind; + argv += optind; + + /* + * If -d or -t, set the timezone or daylight savings time; this + * doesn't belong here; the kernel should not know about either. + */ + if (set_timezone && settimeofday(NULL, &tz) != 0) + err(1, "settimeofday (timezone)"); + + if (!rflag && time(&tval) == -1) + err(1, "time"); + + format = "%+"; + + if (Rflag) + format = rfc2822_format; + + /* allow the operands in any order */ + if (*argv && **argv == '+') { + format = *argv + 1; + ++argv; + } + + if (*argv) { + setthetime(fmt, *argv, jflag, nflag); + ++argv; + } else if (fmt != NULL) + usage(); + + if (*argv && **argv == '+') + format = *argv + 1; + +#ifdef __APPLE__ + /* 7999711 */ + struct tm *ltp = localtime(&tval); + if (ltp == NULL) { + err(1, "localtime"); + } + lt = *ltp; +#else + lt = *localtime(&tval); +#endif + badv = vary_apply(v, <); + if (badv) { + fprintf(stderr, "%s: Cannot apply date adjustment\n", + badv->arg); + vary_destroy(v); + usage(); + } + vary_destroy(v); + + if (format == rfc2822_format) + /* + * When using RFC 2822 datetime format, don't honor the + * locale. + */ + setlocale(LC_TIME, "C"); + + (void)strftime(buf, sizeof(buf), format, <); + (void)printf("%s\n", buf); + if (fflush(stdout)) + err(1, "stdout"); + /* + * If date/time could not be set/notified in the other hosts as + * determined by netsetval(), a return value 2 is set, which is + * only propagated back to shell in legacy mode. + */ + if (unix2003_std) + exit(0); + exit(retval); +} + +#define ATOI2(s) ((s) += 2, ((s)[-2] - '0') * 10 + ((s)[-1] - '0')) +#define ATOI2_OFFSET(s, o) (((s)[o] - '0') * 10 + ((s)[o + 1] - '0')) + +static void +setthetime(const char *fmt, const char *p, int jflag, int nflag) +{ + struct utmpx utx; + struct tm *lt; + struct timeval tv; + const char *dot, *t; + int century; + size_t length; + + lt = localtime(&tval); + lt->tm_isdst = -1; /* divine correct DST */ + + if (fmt != NULL) { + t = strptime(p, fmt, lt); + if (t == NULL) { + fprintf(stderr, "Failed conversion of ``%s''" + " using format ``%s''\n", p, fmt); + badformat(); + } else if (*t != '\0') + fprintf(stderr, "Warning: Ignoring %ld extraneous" + " characters in date string (%s)\n", + (long) strlen(t), t); + } else { + for (t = p, dot = NULL; *t; ++t) { + if (isdigit(*t)) + continue; + if (*t == '.' && dot == NULL) { + dot = t; + continue; + } + badformat(); + } + + if (dot != NULL) { /* .ss */ + dot++; /* *dot++ = '\0'; */ + if (strlen(dot) != 2) + badformat(); + lt->tm_sec = ATOI2(dot); + if (lt->tm_sec > 61) + badformat(); + } else + lt->tm_sec = 0; + + century = 0; + /* if p has a ".ss" field then let's pretend it's not there */ + switch (length = strlen(p) - ((dot != NULL) ? 3 : 0)) { + case 12: /* cc */ + lt->tm_year = (unix2003_std ? ATOI2_OFFSET(p, length - 4) : ATOI2(p)) * 100 - TM_YEAR_BASE; + century = 1; + /* FALLTHROUGH */ + case 10: /* yy */ + if (century) + lt->tm_year += (unix2003_std ? ATOI2_OFFSET(p, length - 2) : ATOI2(p)); + else { + lt->tm_year = (unix2003_std ? ATOI2_OFFSET(p, length - 2) : ATOI2(p)); + if (lt->tm_year < 69) /* hack for 2000 ;-} */ + lt->tm_year += 2000 - TM_YEAR_BASE; + else + lt->tm_year += 1900 - TM_YEAR_BASE; + } + /* FALLTHROUGH */ + case 8: /* mm */ + lt->tm_mon = ATOI2(p); + if (lt->tm_mon > 12) + badformat(); + --lt->tm_mon; /* time struct is 0 - 11 */ + /* FALLTHROUGH */ + case 6: /* dd */ + lt->tm_mday = ATOI2(p); + if (lt->tm_mday > 31) + badformat(); + /* FALLTHROUGH */ + case 4: /* HH */ + lt->tm_hour = ATOI2(p); + if (lt->tm_hour > 23) + badformat(); + /* FALLTHROUGH */ + case 2: /* MM */ + lt->tm_min = ATOI2(p); + if (lt->tm_min > 59) + badformat(); + break; + default: + badformat(); + } + } + + /* convert broken-down time to GMT clock time */ + if ((tval = mktime(lt)) == -1) + errx(1, "nonexistent time"); + + if (!jflag) { + /* set the time */ + if (nflag || netsettime(tval)) { + utx.ut_type = OLD_TIME; + (void)gettimeofday(&utx.ut_tv, NULL); + pututxline(&utx); + tv.tv_sec = tval; + tv.tv_usec = 0; + if (settimeofday(&tv, NULL) != 0) + err(1, "settimeofday (timeval)"); + utx.ut_type = NEW_TIME; + (void)gettimeofday(&utx.ut_tv, NULL); + pututxline(&utx); + } + + if ((p = getlogin()) == NULL) + p = "???"; + syslog(LOG_AUTH | LOG_NOTICE, "date set by %s", p); + } +} + +static void +badformat(void) +{ + warnx("illegal time format"); + usage(); +} + +static void +usage(void) +{ + (void)fprintf(stderr, "%s\n%s\n", + "usage: date [-jnRu] [-d dst] [-r seconds] [-t west] " + "[-v[+|-]val[ymwdHMS]] ... ", + unix2003_std ? + " " + "[-f fmt date | [[[mm]dd]HH]MM[[cc]yy][.ss]] [+format]" : + " " + "[-f fmt date | [[[[[cc]yy]mm]dd]HH]MM[.ss]] [+format]"); + exit(1); +} diff --git a/shell_cmds/date/date.plist.part b/shell_cmds/date/date.plist.part new file mode 100644 index 0000000..da97d84 --- /dev/null +++ b/shell_cmds/date/date.plist.part @@ -0,0 +1,22 @@ + <dict> + <key>OpenSourceProject</key> + <string>date</string> + <key>OpenSourceVersion</key> + <string>2015-05-07</string> + <key>OpenSourceWebsiteURL</key> + <string>http://svnweb.freebsd.org/base/head/bin/date/</string> + <key>OpenSourceSCM</key> + <string>svn co http://svn.freebsd.org/base/head/bin/date/</string> + <key>OpenSourceImportDate</key> + <string>2015-11-06</string> + <key>OpenSourceModifications</key> + <array> + <string>date.1: discuss conformance changes</string> + <string>date.1: remove bogus utmpx path</string> + <string>date.c: conformance changes for date format and exit value</string> + <string>date.c: fix crash on invalid dates (7999711)</string> + <string>date.c: st_mtim compatibility</string> + </array> + <key>OpenSourceLicense</key> + <string>bsd</string> + </dict> diff --git a/shell_cmds/date/extern.h b/shell_cmds/date/extern.h new file mode 100644 index 0000000..91aeab2 --- /dev/null +++ b/shell_cmds/date/extern.h @@ -0,0 +1,35 @@ +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)extern.h 8.1 (Berkeley) 5/31/93 + * $FreeBSD$ + */ + +extern int retval; + +int netsettime(time_t); diff --git a/shell_cmds/date/netdate.c b/shell_cmds/date/netdate.c new file mode 100644 index 0000000..e506e6d --- /dev/null +++ b/shell_cmds/date/netdate.c @@ -0,0 +1,181 @@ +/*- + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if 0 +#ifndef lint +static char sccsid[] = "@(#)netdate.c 8.1 (Berkeley) 5/31/93"; +#endif /* not lint */ +#endif + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/param.h> +#include <sys/time.h> +#include <sys/socket.h> + +#include <netinet/in.h> +#include <netdb.h> +#define TSPTYPES +#include <protocols/timed.h> + +#include <err.h> +#include <errno.h> +#include <string.h> +#include <unistd.h> + +#include "extern.h" + +#define WAITACK 2 /* seconds */ +#define WAITDATEACK 5 /* seconds */ + +/* + * Set the date in the machines controlled by timedaemons by communicating the + * new date to the local timedaemon. If the timedaemon is in the master state, + * it performs the correction on all slaves. If it is in the slave state, it + * notifies the master that a correction is needed. + * Returns 0 on success. Returns > 0 on failure, setting retval to 2; + */ +int +netsettime(time_t tval) +{ + struct timeval tout; + struct servent *sp; + struct tsp msg; + struct sockaddr_in lsin, dest, from; + fd_set ready; + long waittime; + int s, port, timed_ack, found, lerr; + socklen_t length; + char hostname[MAXHOSTNAMELEN]; + + if ((sp = getservbyname("timed", "udp")) == NULL) { + warnx("timed/udp: unknown service"); + return (retval = 2); + } + + dest.sin_port = sp->s_port; + dest.sin_family = AF_INET; + dest.sin_addr.s_addr = htonl((u_long)INADDR_ANY); + s = socket(AF_INET, SOCK_DGRAM, 0); + if (s < 0) { + if (errno != EAFNOSUPPORT) + warn("timed"); + return (retval = 2); + } + + memset(&lsin, 0, sizeof(lsin)); + lsin.sin_family = AF_INET; + for (port = IPPORT_RESERVED - 1; port > IPPORT_RESERVED / 2; port--) { + lsin.sin_port = htons((u_short)port); + if (bind(s, (struct sockaddr *)&lsin, sizeof(lsin)) >= 0) + break; + if (errno == EADDRINUSE) + continue; + if (errno != EADDRNOTAVAIL) + warn("bind"); + goto bad; + } + if (port == IPPORT_RESERVED / 2) { + warnx("all ports in use"); + goto bad; + } + memset(&msg, 0, sizeof(msg)); + msg.tsp_type = TSP_SETDATE; + msg.tsp_vers = TSPVERSION; + if (gethostname(hostname, sizeof(hostname))) { + warn("gethostname"); + goto bad; + } + (void)strlcpy(msg.tsp_name, hostname, sizeof(msg.tsp_name)); + msg.tsp_seq = htons((u_short)0); + msg.tsp_time.tv_sec = htonl((u_long)tval); + msg.tsp_time.tv_usec = htonl((u_long)0); + length = sizeof(struct sockaddr_in); + if (connect(s, (struct sockaddr *)&dest, length) < 0) { + warn("connect"); + goto bad; + } + if (send(s, (char *)&msg, sizeof(struct tsp), 0) < 0) { + if (errno != ECONNREFUSED) + warn("send"); + goto bad; + } + + timed_ack = -1; + waittime = WAITACK; +loop: + tout.tv_sec = waittime; + tout.tv_usec = 0; + + FD_ZERO(&ready); + FD_SET(s, &ready); + found = select(FD_SETSIZE, &ready, (fd_set *)0, (fd_set *)0, &tout); + + length = sizeof(lerr); + if (!getsockopt(s, + SOL_SOCKET, SO_ERROR, (char *)&lerr, &length) && lerr) { + if (lerr != ECONNREFUSED) + warnc(lerr, "send (delayed error)"); + goto bad; + } + + if (found > 0 && FD_ISSET(s, &ready)) { + length = sizeof(struct sockaddr_in); + if (recvfrom(s, &msg, sizeof(struct tsp), 0, + (struct sockaddr *)&from, &length) < 0) { + if (errno != ECONNREFUSED) + warn("recvfrom"); + goto bad; + } + msg.tsp_seq = ntohs(msg.tsp_seq); + msg.tsp_time.tv_sec = ntohl(msg.tsp_time.tv_sec); + msg.tsp_time.tv_usec = ntohl(msg.tsp_time.tv_usec); + switch (msg.tsp_type) { + case TSP_ACK: + timed_ack = TSP_ACK; + waittime = WAITDATEACK; + goto loop; + case TSP_DATEACK: + (void)close(s); + return (0); + default: + warnx("wrong ack received from timed: %s", + tsptype[msg.tsp_type]); + timed_ack = -1; + break; + } + } + if (timed_ack == -1) + warnx("can't reach time daemon, time set locally"); + +bad: + (void)close(s); + return (retval = 2); +} diff --git a/shell_cmds/date/vary.c b/shell_cmds/date/vary.c new file mode 100644 index 0000000..5f01231 --- /dev/null +++ b/shell_cmds/date/vary.c @@ -0,0 +1,506 @@ +/*- + * Copyright (c) 1997 Brian Somers <brian@Awfulhak.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <err.h> +#include <time.h> +#include <string.h> +#include <stdlib.h> +#include "vary.h" + +struct trans { + int val; + const char *str; +}; + +static struct trans trans_mon[] = { + { 1, "january" }, { 2, "february" }, { 3, "march" }, { 4, "april" }, + { 5, "may"}, { 6, "june" }, { 7, "july" }, { 8, "august" }, + { 9, "september" }, { 10, "october" }, { 11, "november" }, { 12, "december" }, + { -1, NULL } +}; + +static struct trans trans_wday[] = { + { 0, "sunday" }, { 1, "monday" }, { 2, "tuesday" }, { 3, "wednesday" }, + { 4, "thursday" }, { 5, "friday" }, { 6, "saturday" }, + { -1, NULL } +}; + +static char digits[] = "0123456789"; +static int adjhour(struct tm *, char, int, int); + +static int +domktime(struct tm *t, char type) +{ + time_t ret; + + while ((ret = mktime(t)) == -1 && t->tm_year > 68 && t->tm_year < 138) + /* While mktime() fails, adjust by an hour */ + adjhour(t, type == '-' ? type : '+', 1, 0); + + return ret; +} + +static int +trans(const struct trans t[], const char *arg) +{ + int f; + + for (f = 0; t[f].val != -1; f++) + if (!strncasecmp(t[f].str, arg, 3) || + !strncasecmp(t[f].str, arg, strlen(t[f].str))) + return t[f].val; + + return -1; +} + +struct vary * +vary_append(struct vary *v, char *arg) +{ + struct vary *result, **nextp; + + if (v) { + result = v; + while (v->next) + v = v->next; + nextp = &v->next; + } else + nextp = &result; + + if ((*nextp = (struct vary *)malloc(sizeof(struct vary))) == NULL) + err(1, "malloc"); + (*nextp)->arg = arg; + (*nextp)->next = NULL; + return result; +} + +static int mdays[12] = { 31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }; + +static int +daysinmonth(const struct tm *t) +{ + int year; + + year = t->tm_year + 1900; + + if (t->tm_mon == 1) + if (!(year % 400)) + return 29; + else if (!(year % 100)) + return 28; + else if (!(year % 4)) + return 29; + else + return 28; + else if (t->tm_mon >= 0 && t->tm_mon < 12) + return mdays[t->tm_mon]; + + return 0; +} + + +static int +adjyear(struct tm *t, char type, int val, int mk) +{ + switch (type) { + case '+': + t->tm_year += val; + break; + case '-': + t->tm_year -= val; + break; + default: + t->tm_year = val; + if (t->tm_year < 69) + t->tm_year += 100; /* as per date.c */ + else if (t->tm_year > 1900) + t->tm_year -= 1900; /* struct tm holds years since 1900 */ + break; + } + return !mk || domktime(t, type) != -1; +} + +static int +adjmon(struct tm *t, char type, int val, int istext, int mk) +{ + int lmdays; + + if (val < 0) + return 0; + + switch (type) { + case '+': + if (istext) { + if (val <= t->tm_mon) + val += 11 - t->tm_mon; /* early next year */ + else + val -= t->tm_mon + 1; /* later this year */ + } + if (val) { + if (!adjyear(t, '+', (t->tm_mon + val) / 12, 0)) + return 0; + val %= 12; + t->tm_mon += val; + if (t->tm_mon > 11) + t->tm_mon -= 12; + } + break; + + case '-': + if (istext) { + if (val-1 > t->tm_mon) + val = 13 - val + t->tm_mon; /* later last year */ + else + val = t->tm_mon - val + 1; /* early this year */ + } + if (val) { + if (!adjyear(t, '-', val / 12, 0)) + return 0; + val %= 12; + if (val > t->tm_mon) { + if (!adjyear(t, '-', 1, 0)) + return 0; + val -= 12; + } + t->tm_mon -= val; + } + break; + + default: + if (val > 12 || val < 1) + return 0; + t->tm_mon = --val; + } + + /* e.g., -v-1m on March, 31 is the last day of February in common sense */ + lmdays = daysinmonth(t); + if (t->tm_mday > lmdays) + t->tm_mday = lmdays; + + return !mk || domktime(t, type) != -1; +} + +static int +adjday(struct tm *t, char type, int val, int mk) +{ + int lmdays; + + switch (type) { + case '+': + while (val) { + lmdays = daysinmonth(t); + if (val > lmdays - t->tm_mday) { + val -= lmdays - t->tm_mday + 1; + t->tm_mday = 1; + if (!adjmon(t, '+', 1, 0, 0)) + return 0; + } else { + t->tm_mday += val; + val = 0; + } + } + break; + case '-': + while (val) + if (val >= t->tm_mday) { + val -= t->tm_mday; + t->tm_mday = 1; + if (!adjmon(t, '-', 1, 0, 0)) + return 0; + t->tm_mday = daysinmonth(t); + } else { + t->tm_mday -= val; + val = 0; + } + break; + default: + if (val > 0 && val <= daysinmonth(t)) + t->tm_mday = val; + else + return 0; + break; + } + + return !mk || domktime(t, type) != -1; +} + +static int +adjwday(struct tm *t, char type, int val, int istext, int mk) +{ + if (val < 0) + return 0; + + switch (type) { + case '+': + if (istext) + if (val < t->tm_wday) + val = 7 - t->tm_wday + val; /* early next week */ + else + val -= t->tm_wday; /* later this week */ + else + val *= 7; /* "-v+5w" == "5 weeks in the future" */ + return !val || adjday(t, '+', val, mk); + case '-': + if (istext) { + if (val > t->tm_wday) + val = 7 - val + t->tm_wday; /* later last week */ + else + val = t->tm_wday - val; /* early this week */ + } else + val *= 7; /* "-v-5w" == "5 weeks ago" */ + return !val || adjday(t, '-', val, mk); + default: + if (val < t->tm_wday) + return adjday(t, '-', t->tm_wday - val, mk); + else if (val > 6) + return 0; + else if (val > t->tm_wday) + return adjday(t, '+', val - t->tm_wday, mk); + } + return 1; +} + +static int +adjhour(struct tm *t, char type, int val, int mk) +{ + if (val < 0) + return 0; + + switch (type) { + case '+': + if (val) { + int days; + + days = (t->tm_hour + val) / 24; + val %= 24; + t->tm_hour += val; + t->tm_hour %= 24; + if (!adjday(t, '+', days, 0)) + return 0; + } + break; + + case '-': + if (val) { + int days; + + days = val / 24; + val %= 24; + if (val > t->tm_hour) { + days++; + val -= 24; + } + t->tm_hour -= val; + if (!adjday(t, '-', days, 0)) + return 0; + } + break; + + default: + if (val > 23) + return 0; + t->tm_hour = val; + } + + return !mk || domktime(t, type) != -1; +} + +static int +adjmin(struct tm *t, char type, int val, int mk) +{ + if (val < 0) + return 0; + + switch (type) { + case '+': + if (val) { + if (!adjhour(t, '+', (t->tm_min + val) / 60, 0)) + return 0; + val %= 60; + t->tm_min += val; + if (t->tm_min > 59) + t->tm_min -= 60; + } + break; + + case '-': + if (val) { + if (!adjhour(t, '-', val / 60, 0)) + return 0; + val %= 60; + if (val > t->tm_min) { + if (!adjhour(t, '-', 1, 0)) + return 0; + val -= 60; + } + t->tm_min -= val; + } + break; + + default: + if (val > 59) + return 0; + t->tm_min = val; + } + + return !mk || domktime(t, type) != -1; +} + +static int +adjsec(struct tm *t, char type, int val, int mk) +{ + if (val < 0) + return 0; + + switch (type) { + case '+': + if (val) { + if (!adjmin(t, '+', (t->tm_sec + val) / 60, 0)) + return 0; + val %= 60; + t->tm_sec += val; + if (t->tm_sec > 59) + t->tm_sec -= 60; + } + break; + + case '-': + if (val) { + if (!adjmin(t, '-', val / 60, 0)) + return 0; + val %= 60; + if (val > t->tm_sec) { + if (!adjmin(t, '-', 1, 0)) + return 0; + val -= 60; + } + t->tm_sec -= val; + } + break; + + default: + if (val > 59) + return 0; + t->tm_sec = val; + } + + return !mk || domktime(t, type) != -1; +} + +const struct vary * +vary_apply(const struct vary *v, struct tm *t) +{ + char type; + char which; + char *arg; + size_t len; + int val; + + for (; v; v = v->next) { + type = *v->arg; + arg = v->arg; + if (type == '+' || type == '-') + arg++; + else + type = '\0'; + len = strlen(arg); + if (len < 2) + return v; + + if (type == '\0') + t->tm_isdst = -1; + + if (strspn(arg, digits) != len-1) { + val = trans(trans_wday, arg); + if (val != -1) { + if (!adjwday(t, type, val, 1, 1)) + return v; + } else { + val = trans(trans_mon, arg); + if (val != -1) { + if (!adjmon(t, type, val, 1, 1)) + return v; + } else + return v; + } + } else { + val = atoi(arg); + which = arg[len-1]; + + switch (which) { + case 'S': + if (!adjsec(t, type, val, 1)) + return v; + break; + case 'M': + if (!adjmin(t, type, val, 1)) + return v; + break; + case 'H': + if (!adjhour(t, type, val, 1)) + return v; + break; + case 'd': + t->tm_isdst = -1; + if (!adjday(t, type, val, 1)) + return v; + break; + case 'w': + t->tm_isdst = -1; + if (!adjwday(t, type, val, 0, 1)) + return v; + break; + case 'm': + t->tm_isdst = -1; + if (!adjmon(t, type, val, 0, 1)) + return v; + break; + case 'y': + t->tm_isdst = -1; + if (!adjyear(t, type, val, 1)) + return v; + break; + default: + return v; + } + } + } + return 0; +} + +void +vary_destroy(struct vary *v) +{ + struct vary *n; + + while (v) { + n = v->next; + free(v); + v = n; + } +} diff --git a/shell_cmds/date/vary.h b/shell_cmds/date/vary.h new file mode 100644 index 0000000..b39306a --- /dev/null +++ b/shell_cmds/date/vary.h @@ -0,0 +1,36 @@ +/*- + * Copyright (c) 1997 Brian Somers <brian@Awfulhak.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ + +struct vary { + char *arg; + struct vary *next; +}; + +extern struct vary *vary_append(struct vary *v, char *arg); +extern const struct vary *vary_apply(const struct vary *v, struct tm *t); +extern void vary_destroy(struct vary *v); diff --git a/shell_cmds/dirname/dirname.c b/shell_cmds/dirname/dirname.c new file mode 100644 index 0000000..11b19b2 --- /dev/null +++ b/shell_cmds/dirname/dirname.c @@ -0,0 +1,84 @@ +/*- + * Copyright (c) 1991, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static const char copyright[] = +"@(#) Copyright (c) 1991, 1993, 1994\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static const char sccsid[] = "@(#)dirname.c 8.4 (Berkeley) 5/4/95"; +#endif /* not lint */ +#include <sys/cdefs.h> +__RCSID("$FreeBSD: src/usr.bin/dirname/dirname.c,v 1.11 2002/07/28 15:43:56 dwmalone Exp $"); + +#include <err.h> +#include <libgen.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +void usage(void); + +int +main(int argc, char **argv) +{ + char *p; + int ch; + + while ((ch = getopt(argc, argv, "")) != -1) + switch(ch) { + case '?': + default: + usage(); + } + argc -= optind; + argv += optind; + + if (argc != 1) + usage(); + + if ((p = dirname(*argv)) == NULL) + err(1, "%s", *argv); + (void)printf("%s\n", p); + exit(0); +} + +void +usage(void) +{ + + (void)fprintf(stderr, "usage: dirname path\n"); + exit(1); +} diff --git a/shell_cmds/echo/echo.1 b/shell_cmds/echo/echo.1 new file mode 100644 index 0000000..87bcc34 --- /dev/null +++ b/shell_cmds/echo/echo.1 @@ -0,0 +1,101 @@ +.\"- +.\" Copyright (c) 1990, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" the Institute of Electrical and Electronics Engineers, Inc. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)echo.1 8.1 (Berkeley) 7/22/93 +.\" $FreeBSD: src/bin/echo/echo.1,v 1.19 2005/01/16 16:41:56 ru Exp $ +.\" +.Dd April 12, 2003 +.Dt ECHO 1 +.Os +.Sh NAME +.Nm echo +.Nd write arguments to the standard output +.Sh SYNOPSIS +.Nm +.Op Fl n +.Op Ar string ... +.Sh DESCRIPTION +The +.Nm +utility writes any specified operands, separated by single blank +.Pq Ql "\ " +characters and followed by a newline +.Pq Ql \en +character, to the standard +output. +.Pp +The following option is available: +.Bl -tag -width flag +.It Fl n +Do not print the trailing newline character. +This may also be achieved by appending +.Ql \ec +to the end of the string, as is done +by iBCS2 compatible systems. +Note that this option as well as the effect of +.Ql \ec +are implementation-defined in +.St -p1003.1-2001 +as amended by Cor.\& 1-2002. +Applications aiming for maximum +portability are strongly encouraged to use +.Xr printf 1 +to suppress the newline character. +.El +.Pp +Some shells may provide a builtin +.Nm +command which is similar or identical to this utility. +.\" 4874742 +Most notably, the builtin +.Nm +in +.Xr sh 1 +does not accept the +.Fl n +option. +.\" 4874742 +Consult the +.Xr builtin 1 +manual page. +.Sh EXIT STATUS +.Ex -std +.Sh SEE ALSO +.Xr builtin 1 , +.Xr csh 1 , +.Xr printf 1 , +.Xr sh 1 +.Sh STANDARDS +The +.Nm +utility conforms to +.St -p1003.1-2001 +as amended by Cor.\& 1-2002. diff --git a/shell_cmds/echo/echo.c b/shell_cmds/echo/echo.c new file mode 100644 index 0000000..a994e6d --- /dev/null +++ b/shell_cmds/echo/echo.c @@ -0,0 +1,182 @@ +/*- + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if 0 +#ifndef lint +static char const copyright[] = +"@(#) Copyright (c) 1989, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)echo.c 8.1 (Berkeley) 5/31/93"; +#endif /* not lint */ +#endif +#include <sys/cdefs.h> +__FBSDID("$FreeBSD: src/bin/echo/echo.c,v 1.18 2005/01/10 08:39:22 imp Exp $"); + +#include <err.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <wchar.h> + +static void +flush_and_exit(void) +{ + if (fflush(stdout) != 0) + err(1, "fflush"); + exit(0); +} + +static char * +print_one_char(char *cur, int posix, int *bytes_len_out) +{ + char *next; + wchar_t wc; + int bytes_len = mbtowc(&wc, cur, MB_CUR_MAX); + if (bytes_len <= 0) { + putchar(*cur); + bytes_len = 1; + goto out; + } + + /* If this is not an escape sequence, just print the character */ + if (wc != '\\') { + putwchar(wc); + goto out; + } + + next = cur + bytes_len; + + if (!posix) { + /* In non-POSIX mode, the only valid escape sequence is \c */ + if (*next == 'c') { + flush_and_exit(); + } else { + putchar(wc); + goto out; + } + } else { + cur = next; + bytes_len = 1; + } + + switch (*cur) { + case 'a': + putchar('\a'); + goto out; + + case 'b': + putchar('\b'); + goto out; + + case 'c': + flush_and_exit(); + + case 'f': + putchar('\f'); + goto out; + + case 'n': + putchar('\n'); + goto out; + + case 'r': + putchar('\r'); + goto out; + + case 't': + putchar('\t'); + goto out; + + case 'v': + putchar('\v'); + goto out; + + case '\\': + putchar('\\'); + goto out; + + case '0': { + int j = 0, num = 0; + while ((*++cur >= '0' && *cur <= '7') && + j++ < 3) { + num <<= 3; + num |= (*cur - '0'); + } + putchar(num); + --cur; + goto out; + } + default: + --cur; + putchar(*cur); + goto out; + } + + out: + if (bytes_len_out) + *bytes_len_out = bytes_len; + return cur; +} + +int +main(int argc, char *argv[]) +{ + int nflag = 0; + int posix = (getenv("POSIXLY_CORRECT") != NULL || getenv("POSIX_PEDANTIC") != NULL); + + if (!posix && argv[1] && strcmp(argv[1], "-n") == 0) + nflag = 1; + + for (int i = 0; i < argc; i++) { + /* argv[0] == progname */ + int ignore_arg = (i == 0 || (i == 1 && nflag == 1)); + int last_arg = (i == (argc - 1)); + if (!ignore_arg) { + char *cur = argv[i]; + size_t arg_len = strlen(cur); + int bytes_len = 0; + + for (const char *end = cur + arg_len; cur < end; cur += bytes_len) { + cur = print_one_char(cur, posix, &bytes_len); + } + } + if (last_arg && !nflag) + putchar('\n'); + else if (!last_arg && !ignore_arg) + putchar(' '); + + if (fflush(stdout) != 0) + err(1, "fflush"); + } + + return 0; +} diff --git a/shell_cmds/env/env.1 b/shell_cmds/env/env.1 new file mode 100644 index 0000000..101f4eb --- /dev/null +++ b/shell_cmds/env/env.1 @@ -0,0 +1,482 @@ +.\" Copyright (c) 1980, 1990, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" the Institute of Electrical and Electronics Engineers, Inc. +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" From @(#)printenv.1 8.1 (Berkeley) 6/6/93 +.\" From FreeBSD: src/usr.bin/printenv/printenv.1,v 1.17 2002/11/26 17:33:35 ru Exp +.\" $FreeBSD$ +.\" +.Dd April 17, 2008 +.Dt ENV 1 +.Os +.Sh NAME +.Nm env +.Nd set environment and execute command, or print environment +.Sh SYNOPSIS +.Nm +.Op Fl iv +.Op Fl P Ar altpath +.Op Fl S Ar string +.Op Fl u Ar name +.Op Ar name Ns = Ns Ar value ... +.Op Ar utility Op Ar argument ... +.Sh DESCRIPTION +The +.Nm +utility executes another +.Ar utility +after modifying the environment as +specified on the command line. +Each +.Ar name Ns = Ns Ar value +option specifies the setting of an environment variable, +.Ar name , +with a value of +.Ar value . +All such environment variables are set before the +.Ar utility +is executed. +.Pp +The options are as follows: +.Bl -tag -width indent +.It Fl i +Execute the +.Ar utility +with only those environment variables specified by +.Ar name Ns = Ns Ar value +options. +The environment inherited +by +.Nm +is ignored completely. +.\" -P +.It Fl P Ar altpath +Search the set of directories as specified by +.Ar altpath +to locate the specified +.Ar utility +program, instead of using the value of the +.Ev PATH +environment variable. +.\" -S +.It Fl S Ar string +Split apart the given +.Ar string +into multiple strings, and process each of the resulting strings +as separate arguments to the +.Nm +utility. +The +.Fl S +option recognizes some special character escape sequences and +also supports environment-variable substitution, as described +below. +.\" -u +.It Fl u Ar name +If the environment variable +.Ar name +is in the environment, then remove it before processing the +remaining options. +This is similar to the +.Ic unset +command in +.Xr sh 1 . +The value for +.Ar name +must not include the +.Ql = +character. +.\" -v +.It Fl v +Print verbose information for each step of processing done by the +.Nm +utility. +Additional information will be printed if +.Fl v +is specified multiple times. +.El +.Pp +The above options are only recognized when they are specified +before any +.Ar name Ns = Ns Ar value +options. +.Pp +If no +.Ar utility +is specified, +.Nm +prints out the names and values +of the variables in the environment, with one name/value pair per line. +.\" +.Ss Details of Fl S Ss (split-string) processing +The processing of the +.Fl S +option will split the given +.Ar string +into separate arguments based on any space or <tab> characters found in the +.Ar string . +Each of those new arguments will then be treated as if it had been +specified as a separate argument on the original +.Nm +command. +.Pp +Spaces and tabs may be embedded in one of those new arguments by using +single +.Pq Dq Li ' +or double +.Pq Ql \&" +quotes, or backslashes +.Pq Ql \e . +Single quotes will escape all non-single quote characters, up to +the matching single quote. +Double quotes will escape all non-double quote characters, up to +the matching double quote. +It is an error if the end of the +.Ar string +is reached before the matching quote character. +.Pp +If +.Fl S +would create a new argument that starts with the +.Ql # +character, then that argument and the remainder of the +.Ar string +will be ignored. +The +.Ql \e# +sequence can be used when you want a new argument to start +with a +.Ql # +character, without causing the remainder of the +.Ar string +to be skipped. +.Pp +While processing the +.Ar string +value, +.Fl S +processing will treat certain character combinations as escape +sequences which represent some action to take. +The character escape sequences are in backslash notation. +The characters and their meanings are as follows: +.Pp +.Bl -tag -width indent -offset indent -compact +.It Cm \ec +Ignore the remaining characters in the +.Ar string . +This must not appear inside a double-quoted string. +.It Cm \ef +Replace with a <form-feed> character. +.It Cm \en +Replace with a <new-line> character. +.It Cm \er +Replace with a <carriage return> character. +.It Cm \et +Replace with a <tab> character. +.It Cm \ev +Replace with a <vertical tab> character. +.It Cm \e# +Replace with a +.Ql # +character. +This would be useful when you need a +.Ql # +as the first character in one of the arguments created +by splitting apart the given +.Ar string . +.It Cm \e$ +Replace with a +.Ql $ +character. +.It Cm \e_ +If this is found inside of a double-quoted string, then replace it +with a single blank. +If this is found outside of a quoted string, then treat this as the +separator character between new arguments in the original +.Ar string . +.It Cm \e" +Replace with a <double quote> character. +.It Cm \e\' +Replace with a <single quote> character. +.It Cm \e\e +Replace with a backslash character. +.El +.Pp +The sequences for <single-quote> and backslash are the only sequences +which are recognized inside of a single-quoted string. +The other sequences have no special meaning inside a single-quoted +string. +All escape sequences are recognized inside of a double-quoted string. +It is an error if a single +.Ql \e +character is followed by a character other than the ones listed above. +.Pp +The processing of +.Fl S +also supports substitution of values from environment variables. +To do this, the name of the environment variable must be inside of +.Ql ${} , +such as: +.Li ${SOMEVAR} . +The common shell syntax of +.Li $SOMEVAR +is not supported. +All values substituted will be the values of the environment variables +as they were when the +.Nm +utility was originally invoked. +Those values will not be checked for any of the escape sequences as +described above. +And any settings of +.Ar name Ns = Ns Ar value +will not effect the values used for substitution in +.Fl S +processing. +.Pp +Also, +.Fl S +processing can not reference the value of the special parameters +which are defined by most shells. +For instance, +.Fl S +can not recognize special parameters such as: +.Ql $* , +.Ql $@ , +.Ql $# , +.Ql $? +or +.Ql $$ +if they appear inside the given +.Ar string . +.\" +.Ss Use in shell-scripts +The +.Nm +utility is often used as the +.Ar interpreter +on the first line of interpreted scripts, as +described in +.Xr execve 2 . +.Pp +Note that the way the kernel parses the +.Ql #! +(first line) of an interpreted script has changed as of +.Fx 6.0 . +Prior to that, the +.Fx +kernel would split that first line into separate arguments based +on any whitespace (space or <tab> characters) found in the line. +So, if a script named +.Pa /usr/local/bin/someport +had a first line of: +.Pp +.Dl "#!/usr/local/bin/php -n -q -dsafe_mode=0" +.Pp +then the +.Pa /usr/local/bin/php +program would have been started with the arguments of: +.Bd -literal -offset indent +arg[0] = '/usr/local/bin/php' +arg[1] = '-n' +arg[2] = '-q' +arg[3] = '-dsafe_mode=0' +arg[4] = '/usr/local/bin/someport' +.Ed +.Pp +plus any arguments the user specified when executing +.Pa someport . +However, this processing of multiple options on the +.Ql #! +line is not the way any other operating system parses the +first line of an interpreted script. +So after a change which was made for +.Fx 6.0 +release, that script will result in +.Pa /usr/local/bin/php +being started with the arguments of: +.Bd -literal -offset indent +arg[0] = '/usr/local/bin/php' +arg[1] = '-n -q -dsafe_mode=0' +arg[2] = '/usr/local/bin/someport' +.Ed +.Pp +plus any arguments the user specified. +This caused a significant change in the behavior of a few scripts. +In the case of above script, to have it behave the same way under +.Fx 6.0 +as it did under earlier releases, the first line should be +changed to: +.Pp +.Dl "#!/usr/bin/env -S /usr/local/bin/php -n -q -dsafe_mode=0" +.Pp +The +.Nm +utility will be started with the entire line as a single +argument: +.Pp +.Dl "arg[1] = '-S /usr/local/bin/php -n -q -dsafe_mode=0'" +.Pp +and then +.Fl S +processing will split that line into separate arguments before +executing +.Pa /usr/local/bin/php . +.\" +.Sh ENVIRONMENT +The +.Nm +utility uses the +.Ev PATH +environment variable to locate the requested +.Ar utility +if the name contains no +.Ql / +characters, unless the +.Fl P +option has been specified. +.Sh EXIT STATUS +.Ex -std +An exit status of 126 indicates that +.Ar utility +was found, but could not be executed. +An exit status of 127 indicates that +.Ar utility +could not be found. +.Sh EXAMPLES +Since the +.Nm +utility is often used as part of the first line of an interpreted script, +the following examples show a number of ways that the +.Nm +utility can be useful in scripts. +.Pp +The kernel processing of an interpreted script does not allow a script +to directly reference some other script as its own interpreter. +As a way around this, the main difference between +.Pp +.Dl #!/usr/local/bin/foo +and +.Dl "#!/usr/bin/env /usr/local/bin/foo" +.Pp +is that the latter works even if +.Pa /usr/local/bin/foo +is itself an interpreted script. +.Pp +Probably the most common use of +.Nm +is to find the correct interpreter for a script, when the interpreter +may be in different directories on different systems. +The following example will find the +.Ql perl +interpreter by searching through the directories specified by +.Ev PATH . +.Pp +.Dl "#!/usr/bin/env perl" +.Pp +One limitation of that example is that it assumes the user's value +for +.Ev PATH +is set to a value which will find the interpreter you want +to execute. +The +.Fl P +option can be used to make sure a specific list of directories is +used in the search for +.Ar utility . +Note that the +.Fl S +option is also required for this example to work correctly. +.Pp +.Dl "#!/usr/bin/env -S -P/usr/local/bin:/usr/bin perl" +.Pp +The above finds +.Ql perl +only if it is in +.Pa /usr/local/bin +or +.Pa /usr/bin . +That could be combined with the present value of +.Ev PATH , +to provide more flexibility. +Note that spaces are not required between the +.Fl S +and +.Fl P +options: +.Pp +.Dl "#!/usr/bin/env -S-P/usr/local/bin:/usr/bin:${PATH} perl" +.Sh COMPATIBILITY +The +.Nm +utility accepts the +.Fl +option as a synonym for +.Fl i . +.Sh SEE ALSO +.Xr printenv 1 , +.Xr sh 1 , +.Xr execvp 3 , +.Xr environ 7 +.Sh STANDARDS +The +.Nm +utility conforms to +.St -p1003.1-2001 . +The +.Fl P , S , u +and +.Fl v +options are non-standard extensions supported by +.Fx , +but which may not be available on other operating systems. +.Sh HISTORY +The +.Nm +command appeared in +.Bx 4.4 . +The +.Fl P , S +and +.Fl v +options were added in +.Fx 6.0 . +.Sh BUGS +The +.Nm +utility does not handle values of +.Ar utility +which have an equals sign +.Pq Ql = +in their name, for obvious reasons. +.Pp +The +.Nm +utility does not take multibyte characters into account when +processing the +.Fl S +option, which may lead to incorrect results in some locales. diff --git a/shell_cmds/env/env.c b/shell_cmds/env/env.c new file mode 100644 index 0000000..3dc152a --- /dev/null +++ b/shell_cmds/env/env.c @@ -0,0 +1,144 @@ +/* + * Copyright (c) 1988, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static const char copyright[] = +"@(#) Copyright (c) 1988, 1993, 1994\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#if 0 +#ifndef lint +static char sccsid[] = "@(#)env.c 8.3 (Berkeley) 4/2/94"; +#endif /* not lint */ +#endif + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <err.h> +#include <errno.h> +#include <stdio.h> +#include <string.h> +#include <stdlib.h> +#include <unistd.h> + +#include "envopts.h" + +extern char **environ; + +int env_verbosity; + +static void usage(void); + +int +main(int argc, char **argv) +{ + char *altpath, **ep, *p, **parg; + char *cleanenv[1]; + int ch, want_clear; + int rtrn; + + altpath = NULL; + want_clear = 0; + while ((ch = getopt(argc, argv, "-iP:S:u:v")) != -1) + switch(ch) { + case '-': + case 'i': + want_clear = 1; + break; + case 'P': + altpath = strdup(optarg); + break; + case 'S': + /* + * The -S option, for "split string on spaces, with + * support for some simple substitutions"... + */ + split_spaces(optarg, &optind, &argc, &argv); + break; + case 'u': + if (env_verbosity) + fprintf(stderr, "#env unset:\t%s\n", optarg); + rtrn = unsetenv(optarg); + if (rtrn == -1) + err(EXIT_FAILURE, "unsetenv %s", optarg); + break; + case 'v': + env_verbosity++; + if (env_verbosity > 1) + fprintf(stderr, "#env verbosity now at %d\n", + env_verbosity); + break; + case '?': + default: + usage(); + } + if (want_clear) { + environ = cleanenv; + cleanenv[0] = NULL; + if (env_verbosity) + fprintf(stderr, "#env clearing environ\n"); + } + for (argv += optind; *argv && (p = strchr(*argv, '=')); ++argv) { + if (env_verbosity) + fprintf(stderr, "#env setenv:\t%s\n", *argv); + *p = '\0'; + rtrn = setenv(*argv, p + 1, 1); + *p = '='; + if (rtrn == -1) + err(EXIT_FAILURE, "setenv %s", *argv); + } + if (*argv) { + if (altpath) + search_paths(altpath, argv); + if (env_verbosity) { + fprintf(stderr, "#env executing:\t%s\n", *argv); + for (parg = argv, argc = 0; *parg; parg++, argc++) + fprintf(stderr, "#env arg[%d]=\t'%s'\n", + argc, *parg); + if (env_verbosity > 1) + sleep(1); + } + execvp(*argv, argv); + err(errno == ENOENT ? 127 : 126, "%s", *argv); + } + for (ep = environ; *ep; ep++) + (void)printf("%s\n", *ep); + exit(0); +} + +static void +usage(void) +{ + (void)fprintf(stderr, + "usage: env [-iv] [-P utilpath] [-S string] [-u name]\n" + " [name=value ...] [utility [argument ...]]\n"); + exit(1); +} diff --git a/shell_cmds/env/envopts.c b/shell_cmds/env/envopts.c new file mode 100644 index 0000000..f821430 --- /dev/null +++ b/shell_cmds/env/envopts.c @@ -0,0 +1,468 @@ +/*- + * Copyright (c) 2005 - Garance Alistair Drosehn <gad@FreeBSD.org>. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation + * are those of the authors and should not be interpreted as representing + * official policies, either expressed or implied, of the FreeBSD Project. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/stat.h> +#include <sys/param.h> +#include <err.h> +#include <errno.h> +#include <ctype.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "envopts.h" + +static const char * + expand_vars(int in_thisarg, char **thisarg_p, char **dest_p, + const char **src_p); +static int is_there(char *candidate); + +/* + * The is*() routines take a parameter of 'int', but expect values in the range + * of unsigned char. Define some wrappers which take a value of type 'char', + * whether signed or unsigned, and ensure the value ends up in the right range. + */ +#define isalnumch(Anychar) isalnum((u_char)(Anychar)) +#define isalphach(Anychar) isalpha((u_char)(Anychar)) +#define isspacech(Anychar) isspace((u_char)(Anychar)) + +/* + * Routine to determine if a given fully-qualified filename is executable. + * This is copied almost verbatim from FreeBSD's usr.bin/which/which.c. + */ +static int +is_there(char *candidate) +{ + struct stat fin; + + /* XXX work around access(2) false positives for superuser */ + if (access(candidate, X_OK) == 0 && + stat(candidate, &fin) == 0 && + S_ISREG(fin.st_mode) && + (getuid() != 0 || + (fin.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) != 0)) { + if (env_verbosity > 1) + fprintf(stderr, "#env matched:\t'%s'\n", candidate); + return (1); + } + return (0); +} + +/** + * Routine to search through an alternate path-list, looking for a given + * filename to execute. If the file is found, replace the original + * unqualified name with a fully-qualified path. This allows `env' to + * execute programs from a specific strict list of possible paths, without + * changing the value of PATH seen by the program which will be executed. + * E.G.: + * #!/usr/bin/env -S-P/usr/local/bin:/usr/bin perl + * will execute /usr/local/bin/perl or /usr/bin/perl (whichever is found + * first), no matter what the current value of PATH is, and without + * changing the value of PATH that the script will see when it runs. + * + * This is similar to the print_matches() routine in usr.bin/which/which.c. + */ +void +search_paths(char *path, char **argv) +{ + char candidate[PATH_MAX]; + const char *d; + char *filename, *fqname; + + /* If the file has a `/' in it, then no search is done */ + filename = *argv; + if (strchr(filename, '/') != NULL) + return; + + if (env_verbosity > 1) { + fprintf(stderr, "#env Searching:\t'%s'\n", path); + fprintf(stderr, "#env for file:\t'%s'\n", filename); + } + + fqname = NULL; + while ((d = strsep(&path, ":")) != NULL) { + if (*d == '\0') + d = "."; + if (snprintf(candidate, sizeof(candidate), "%s/%s", d, + filename) >= (int)sizeof(candidate)) + continue; + if (is_there(candidate)) { + fqname = candidate; + break; + } + } + + if (fqname == NULL) { + errno = ENOENT; + err(127, "%s", filename); + } + *argv = strdup(candidate); +} + +/** + * Routine to split a string into multiple parameters, while recognizing a + * few special characters. It recognizes both single and double-quoted + * strings. This processing is designed entirely for the benefit of the + * parsing of "#!"-lines (aka "shebang" lines == the first line of an + * executable script). Different operating systems parse that line in very + * different ways, and this split-on-spaces processing is meant to provide + * ways to specify arbitrary arguments on that line, no matter how the OS + * parses it. + * + * Within a single-quoted string, the two characters "\'" are treated as + * a literal "'" character to add to the string, and "\\" are treated as + * a literal "\" character to add. Other than that, all characters are + * copied until the processing gets to a terminating "'". + * + * Within a double-quoted string, many more "\"-style escape sequences + * are recognized, mostly copied from what is recognized in the `printf' + * command. Some OS's will not allow a literal blank character to be + * included in the one argument that they recognize on a shebang-line, + * so a few additional escape-sequences are defined to provide ways to + * specify blanks. + * + * Within a double-quoted string "\_" is turned into a literal blank. + * (Inside of a single-quoted string, the two characters are just copied) + * Outside of a quoted string, "\_" is treated as both a blank, and the + * end of the current argument. So with a shelbang-line of: + * #!/usr/bin/env -SA=avalue\_perl + * the -S value would be broken up into arguments "A=avalue" and "perl". + */ +void +split_spaces(const char *str, int *origind, int *origc, char ***origv) +{ + static const char *nullarg = ""; + const char *bq_src, *copystr, *src; + char *dest, **newargv, *newstr, **nextarg, **oldarg; + int addcount, bq_destlen, copychar, found_sep, in_arg, in_dq, in_sq; + + /* + * Ignore leading space on the string, and then malloc enough room + * to build a copy of it. The copy might end up shorter than the + * original, due to quoted strings and '\'-processing. + */ + while (isspacech(*str)) + str++; + if (*str == '\0') + return; + newstr = malloc(strlen(str) + 1); + + /* + * Allocate plenty of space for the new array of arg-pointers, + * and start that array off with the first element of the old + * array. + */ + newargv = malloc((*origc + (strlen(str) / 2) + 2) * sizeof(char *)); + nextarg = newargv; + *nextarg++ = **origv; + + /* Come up with the new args by splitting up the given string. */ + addcount = 0; + bq_destlen = in_arg = in_dq = in_sq = 0; + bq_src = NULL; + for (src = str, dest = newstr; *src != '\0'; src++) { + /* + * This switch will look at a character in *src, and decide + * what should be copied to *dest. It only decides what + * character(s) to copy, it should not modify *dest. In some + * cases, it will look at multiple characters from *src. + */ + copychar = found_sep = 0; + copystr = NULL; + switch (*src) { + case '"': + if (in_sq) + copychar = *src; + else if (in_dq) + in_dq = 0; + else { + /* + * Referencing nullarg ensures that a new + * argument is created, even if this quoted + * string ends up with zero characters. + */ + copystr = nullarg; + in_dq = 1; + bq_destlen = dest - *(nextarg - 1); + bq_src = src; + } + break; + case '$': + if (in_sq) + copychar = *src; + else { + copystr = expand_vars(in_arg, (nextarg - 1), + &dest, &src); + } + break; + case '\'': + if (in_dq) + copychar = *src; + else if (in_sq) + in_sq = 0; + else { + /* + * Referencing nullarg ensures that a new + * argument is created, even if this quoted + * string ends up with zero characters. + */ + copystr = nullarg; + in_sq = 1; + bq_destlen = dest - *(nextarg - 1); + bq_src = src; + } + break; + case '\\': + if (in_sq) { + /* + * Inside single-quoted strings, only the + * "\'" and "\\" are recognized as special + * strings. + */ + copychar = *(src + 1); + if (copychar == '\'' || copychar == '\\') + src++; + else + copychar = *src; + break; + } + src++; + switch (*src) { + case '"': + case '#': + case '$': + case '\'': + case '\\': + copychar = *src; + break; + case '_': + /* + * Alternate way to get a blank, which allows + * that blank be used to separate arguments + * when it is not inside a quoted string. + */ + if (in_dq) + copychar = ' '; + else { + found_sep = 1; + src++; + } + break; + case 'c': + /* + * Ignore remaining characters in the -S string. + * This would not make sense if found in the + * middle of a quoted string. + */ + if (in_dq) + errx(1, "Sequence '\\%c' is not allowed" + " in quoted strings", *src); + goto str_done; + case 'f': + copychar = '\f'; + break; + case 'n': + copychar = '\n'; + break; + case 'r': + copychar = '\r'; + break; + case 't': + copychar = '\t'; + break; + case 'v': + copychar = '\v'; + break; + default: + if (isspacech(*src)) + copychar = *src; + else + errx(1, "Invalid sequence '\\%c' in -S", + *src); + } + break; + default: + if ((in_dq || in_sq) && in_arg) + copychar = *src; + else if (isspacech(*src)) + found_sep = 1; + else { + /* + * If the first character of a new argument + * is `#', then ignore the remaining chars. + */ + if (!in_arg && *src == '#') + goto str_done; + copychar = *src; + } + } + /* + * Now that the switch has determined what (if anything) + * needs to be copied, copy whatever that is to *dest. + */ + if (copychar || copystr != NULL) { + if (!in_arg) { + /* This is the first byte of a new argument */ + *nextarg++ = dest; + addcount++; + in_arg = 1; + } + if (copychar) + *dest++ = (char)copychar; + else if (copystr != NULL) + while (*copystr != '\0') + *dest++ = *copystr++; + } else if (found_sep) { + *dest++ = '\0'; + while (isspacech(*src)) + src++; + --src; + in_arg = 0; + } + } +str_done: + *dest = '\0'; + *nextarg = NULL; + if (in_dq || in_sq) { + errx(1, "No terminating quote for string: %.*s%s", + bq_destlen, *(nextarg - 1), bq_src); + } + if (env_verbosity > 1) { + fprintf(stderr, "#env split -S:\t'%s'\n", str); + oldarg = newargv + 1; + fprintf(stderr, "#env into:\t'%s'\n", *oldarg); + for (oldarg++; *oldarg; oldarg++) + fprintf(stderr, "#env &\t'%s'\n", *oldarg); + } + + /* Copy the unprocessed arg-pointers from the original array */ + for (oldarg = *origv + *origind; *oldarg; oldarg++) + *nextarg++ = *oldarg; + *nextarg = NULL; + + /* Update optind/argc/argv in the calling routine */ + *origind = 1; + *origc += addcount; + *origv = newargv; +} + +/** + * Routine to split expand any environment variables referenced in the string + * that -S is processing. For now it only supports the form ${VARNAME}. It + * explicitly does not support $VARNAME, and obviously can not handle special + * shell-variables such as $?, $*, $1, etc. It is called with *src_p pointing + * at the initial '$', and if successful it will update *src_p, *dest_p, and + * possibly *thisarg_p in the calling routine. + */ +static const char * +expand_vars(int in_thisarg, char **thisarg_p, char **dest_p, const char **src_p) +{ + const char *vbegin, *vend, *vvalue; + char *newstr, *vname; + int bad_reference; + size_t namelen, newlen; + + bad_reference = 1; + vbegin = vend = (*src_p) + 1; + if (*vbegin++ == '{') + if (*vbegin == '_' || isalphach(*vbegin)) { + vend = vbegin + 1; + while (*vend == '_' || isalnumch(*vend)) + vend++; + if (*vend == '}') + bad_reference = 0; + } + if (bad_reference) + errx(1, "Only ${VARNAME} expansion is supported, error at: %s", + *src_p); + + /* + * We now know we have a valid environment variable name, so update + * the caller's source-pointer to the last character in that reference, + * and then pick up the matching value. If the variable is not found, + * or if it has a null value, then our work here is done. + */ + *src_p = vend; + namelen = vend - vbegin + 1; + vname = malloc(namelen); + strlcpy(vname, vbegin, namelen); + vvalue = getenv(vname); + if (vvalue == NULL || *vvalue == '\0') { + if (env_verbosity > 2) + fprintf(stderr, + "#env replacing ${%s} with null string\n", + vname); + free(vname); + return (NULL); + } + + if (env_verbosity > 2) + fprintf(stderr, "#env expanding ${%s} into '%s'\n", vname, + vvalue); + + /* + * There is some value to copy to the destination. If the value is + * shorter than the ${VARNAME} reference that it replaces, then our + * caller can just copy the value to the existing destination. + */ + if (strlen(vname) + 3 >= strlen(vvalue)) { + free(vname); + return (vvalue); + } + + /* + * The value is longer than the string it replaces, which means the + * present destination area is too small to hold it. Create a new + * destination area, and update the caller's 'dest' variable to match. + * If the caller has already started copying some info for 'thisarg' + * into the present destination, then the new destination area must + * include a copy of that data, and the pointer to 'thisarg' must also + * be updated. Note that it is still the caller which copies this + * vvalue to the new *dest. + */ + newlen = strlen(vvalue) + strlen(*src_p) + 1; + if (in_thisarg) { + **dest_p = '\0'; /* Provide terminator for 'thisarg' */ + newlen += strlen(*thisarg_p); + newstr = malloc(newlen); + strcpy(newstr, *thisarg_p); + *thisarg_p = newstr; + } else { + newstr = malloc(newlen); + *newstr = '\0'; + } + *dest_p = strchr(newstr, '\0'); + free(vname); + return (vvalue); +} diff --git a/shell_cmds/env/envopts.h b/shell_cmds/env/envopts.h new file mode 100644 index 0000000..1f15c69 --- /dev/null +++ b/shell_cmds/env/envopts.h @@ -0,0 +1,37 @@ +/*- + * Copyright (c) 2005 - Garance Alistair Drosehn <gad@FreeBSD.org>. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation + * are those of the authors and should not be interpreted as representing + * official policies, either expressed or implied, of the FreeBSD Project. + * + * $FreeBSD$ + */ + +void search_paths(char *path, char **argv); +void split_spaces(const char *str, int *origind, int *origc, + char ***origv); + +extern int env_verbosity; diff --git a/shell_cmds/expr/expr.1 b/shell_cmds/expr/expr.1 new file mode 100644 index 0000000..9c8090c --- /dev/null +++ b/shell_cmds/expr/expr.1 @@ -0,0 +1,235 @@ +.\" -*- nroff -*- +.\"- +.\" Copyright (c) 1993 Winning Strategies, Inc. +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by Winning Strategies, Inc. +.\" 4. The name of the author may not be used to endorse or promote products +.\" derived from this software without specific prior written permission +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +.\" IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +.\" THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +.\" +.\" $FreeBSD: src/bin/expr/expr.1,v 1.31 2011/07/09 12:05:53 se Exp $ +.\" +.Dd September 9, 2010 +.Dt EXPR 1 +.Os +.Sh NAME +.Nm expr +.Nd evaluate expression +.Sh SYNOPSIS +.Nm +.Ar expression +.Sh DESCRIPTION +The +.Nm +utility evaluates +.Ar expression +and writes the result on standard output. +.Pp +All operators and operands must be passed as separate arguments. +Several of the operators have special meaning to command interpreters +and must therefore be quoted appropriately. +All integer operands are interpreted in base 10 and must consist of only +an optional leading minus sign followed by one or more digits. +.Pp +Arithmetic operations are performed using signed integer math with a +range according to the C +.Vt intmax_t +data type (the largest signed integral type available). +All conversions and operations are checked for overflow. +Overflow results in program termination with an error message on stdout +and with an error status. +.Pp +Operators are listed below in order of increasing precedence; all +are left-associative. +Operators with equal precedence are grouped within symbols +.Ql { +and +.Ql } . +.Bl -tag -width indent +.It Ar expr1 Li | Ar expr2 +Return the evaluation of +.Ar expr1 +if it is neither an empty string nor zero; +otherwise, returns the evaluation of +.Ar expr2 +if it is not an empty string; +otherwise, returns zero. +.It Ar expr1 Li & Ar expr2 +Return the evaluation of +.Ar expr1 +if neither expression evaluates to an empty string or zero; +otherwise, returns zero. +.It Ar expr1 Li "{=, >, >=, <, <=, !=}" Ar expr2 +Return the results of integer comparison if both arguments are integers; +otherwise, returns the results of string comparison using the locale-specific +collation sequence. +The result of each comparison is 1 if the specified relation is true, +or 0 if the relation is false. +.It Ar expr1 Li "{+, -}" Ar expr2 +Return the results of addition or subtraction of integer-valued arguments. +.It Ar expr1 Li "{*, /, %}" Ar expr2 +Return the results of multiplication, integer division, or remainder of integer-valued arguments. +.It Ar expr1 Li : Ar expr2 +The +.Dq Li \&: +operator matches +.Ar expr1 +against +.Ar expr2 , +which must be a basic regular expression. +The regular expression is anchored +to the beginning of the string with an implicit +.Dq Li ^ . +.Pp +If the match succeeds and the pattern contains at least one regular +expression subexpression +.Dq Li "\e(...\e)" , +the string corresponding to +.Dq Li \e1 +is returned; +otherwise the matching operator returns the number of characters matched. +If the match fails and the pattern contains a regular expression subexpression +the null string is returned; +otherwise 0. +.El +.Pp +Parentheses are used for grouping in the usual manner. +.Pp +The +.Nm +utility makes no lexical distinction between arguments which may be +operators and arguments which may be operands. +An operand which is lexically identical to an operator will be considered a +syntax error. +See the examples below for a work-around. +.Pp +The syntax of the +.Nm +command in general is historic and inconvenient. +New applications are advised to use shell arithmetic rather than +.Nm . +.Sh EXIT STATUS +The +.Nm +utility exits with one of the following values: +.Bl -tag -width indent -compact +.It 0 +the expression is neither an empty string nor 0. +.It 1 +the expression is an empty string or 0. +.It 2 +the expression is invalid. +.El +.Sh EXAMPLES +.Bl -bullet +.It +The following example (in +.Xr sh 1 +syntax) adds one to the variable +.Va a : +.Dl "a=$(expr $a + 1)" +.It +This will fail if the value of +.Va a +is a negative number. +To protect negative values of +.Va a +from being interpreted as options to the +.Nm +command, one might rearrange the expression: +.Dl "a=$(expr 1 + $a)" +.It +More generally, parenthesize possibly-negative values: +.Dl "a=$(expr \e( $a \e) + 1)" +.It +With shell arithmetic, no escaping is required: +.Dl "a=$((a + 1))" +.It +This example prints the filename portion of a pathname stored +in variable +.Va a . +Since +.Va a +might represent the path +.Pa / , +it is necessary to prevent it from being interpreted as the division operator. +The +.Li // +characters resolve this ambiguity. +.Dl "expr \*q//$a\*q \&: '.*/\e(.*\e)'" +.It +With modern +.Xr sh 1 +syntax, +.Dl "\*q${a##*/}\*q" +expands to the same value. +.El +.Pp +The following examples output the number of characters in variable +.Va a . +Again, if +.Va a +might begin with a hyphen, it is necessary to prevent it from being +interpreted as an option to +.Nm , +and +.Va a +might be interpreted as an operator. +.Bl -bullet +.It +To deal with all of this, a complicated command +is required: +.Dl "expr \e( \*qX$a\*q \&: \*q.*\*q \e) - 1" +.It +With modern +.Xr sh 1 +syntax, this can be done much more easily: +.Dl "${#a}" +expands to the required number. +.El +.Sh SEE ALSO +.Xr sh 1 , +.Xr test 1 +.Sh STANDARDS +The +.Nm +utility conforms to +.St -p1003.1-2008 . +.Pp +The extended arithmetic range and overflow checks do not conflict with +POSIX's requirement that arithmetic be done using signed longs, since +they only make a difference to the result in cases where using signed +longs would give undefined behavior. +.Pp +According to the +.Tn POSIX +standard, the use of string arguments +.Va length , +.Va substr , +.Va index , +or +.Va match +produces undefined results. In this version of +.Nm , +these arguments are treated just as their respective string values. diff --git a/shell_cmds/expr/expr.y b/shell_cmds/expr/expr.y new file mode 100644 index 0000000..1aa9474 --- /dev/null +++ b/shell_cmds/expr/expr.y @@ -0,0 +1,571 @@ +%{ +/*- + * Written by Pace Willisson (pace@blitz.com) + * and placed in the public domain. + * + * Largely rewritten by J.T. Conklin (jtc@wimsey.com) + * + * $FreeBSD: src/bin/expr/expr.y,v 1.28 2011/07/09 12:20:15 se Exp $ + */ + +#include <sys/types.h> + +#include <ctype.h> +#include <err.h> +#include <errno.h> +#include <inttypes.h> +#include <limits.h> +#include <locale.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <regex.h> +#include <unistd.h> + +/* + * POSIX specifies a specific error code for syntax errors. We exit + * with this code for all errors. + */ +#define ERR_EXIT 2 + +enum valtype { + integer, numeric_string, string +} ; + +struct val { + enum valtype type; + union { + char *s; + intmax_t i; + } u; +} ; + +char **av; +int nonposix; +struct val *result; + +void assert_to_integer(struct val *); +void assert_div(intmax_t, intmax_t); +void assert_minus(intmax_t, intmax_t, intmax_t); +void assert_plus(intmax_t, intmax_t, intmax_t); +void assert_times(intmax_t, intmax_t, intmax_t); +int compare_vals(struct val *, struct val *); +void free_value(struct val *); +int is_integer(const char *); +int is_string(struct val *); +int is_zero_or_null(struct val *); +struct val *make_integer(intmax_t); +struct val *make_str(const char *); +struct val *op_and(struct val *, struct val *); +struct val *op_colon(struct val *, struct val *); +struct val *op_div(struct val *, struct val *); +struct val *op_eq(struct val *, struct val *); +struct val *op_ge(struct val *, struct val *); +struct val *op_gt(struct val *, struct val *); +struct val *op_le(struct val *, struct val *); +struct val *op_lt(struct val *, struct val *); +struct val *op_minus(struct val *, struct val *); +struct val *op_ne(struct val *, struct val *); +struct val *op_or(struct val *, struct val *); +struct val *op_plus(struct val *, struct val *); +struct val *op_rem(struct val *, struct val *); +struct val *op_times(struct val *, struct val *); +int to_integer(struct val *); +void to_string(struct val *); +int yyerror(const char *); +int yylex(void); +int yyparse(void); + +%} + +%union +{ + struct val *val; +} + +%left <val> '|' +%left <val> '&' +%left <val> '=' '>' '<' GE LE NE +%left <val> '+' '-' +%left <val> '*' '/' '%' +%left <val> ':' + +%token <val> TOKEN +%type <val> start expr + +%% + +start: expr { result = $$; } + +expr: TOKEN + | '(' expr ')' { $$ = $2; } + | expr '|' expr { $$ = op_or($1, $3); } + | expr '&' expr { $$ = op_and($1, $3); } + | expr '=' expr { $$ = op_eq($1, $3); } + | expr '>' expr { $$ = op_gt($1, $3); } + | expr '<' expr { $$ = op_lt($1, $3); } + | expr GE expr { $$ = op_ge($1, $3); } + | expr LE expr { $$ = op_le($1, $3); } + | expr NE expr { $$ = op_ne($1, $3); } + | expr '+' expr { $$ = op_plus($1, $3); } + | expr '-' expr { $$ = op_minus($1, $3); } + | expr '*' expr { $$ = op_times($1, $3); } + | expr '/' expr { $$ = op_div($1, $3); } + | expr '%' expr { $$ = op_rem($1, $3); } + | expr ':' expr { $$ = op_colon($1, $3); } + ; + +%% + +struct val * +make_integer(intmax_t i) +{ + struct val *vp; + + vp = (struct val *)malloc(sizeof(*vp)); + if (vp == NULL) + errx(ERR_EXIT, "malloc() failed"); + + vp->type = integer; + vp->u.i = i; + return (vp); +} + +struct val * +make_str(const char *s) +{ + struct val *vp; + + vp = (struct val *)malloc(sizeof(*vp)); + if (vp == NULL || ((vp->u.s = strdup(s)) == NULL)) + errx(ERR_EXIT, "malloc() failed"); + + if (is_integer(s)) + vp->type = numeric_string; + else + vp->type = string; + + return (vp); +} + +void +free_value(struct val *vp) +{ + if (vp->type == string || vp->type == numeric_string) + free(vp->u.s); +} + +int +to_integer(struct val *vp) +{ + intmax_t i; + + /* we can only convert numeric_string to integer, here */ + if (vp->type == numeric_string) { + errno = 0; + i = strtoimax(vp->u.s, (char **)NULL, 10); + /* just keep as numeric_string, if the conversion fails */ + if (errno != ERANGE) { + free(vp->u.s); + vp->u.i = i; + vp->type = integer; + } + } + return (vp->type == integer); +} + +void +assert_to_integer(struct val *vp) +{ + if (vp->type == string) + errx(ERR_EXIT, "not a decimal number: '%s'", vp->u.s); + if (!to_integer(vp)) + errx(ERR_EXIT, "operand too large: '%s'", vp->u.s); +} + +void +to_string(struct val *vp) +{ + char *tmp; + + if (vp->type == string || vp->type == numeric_string) + return; + + /* + * log_10(x) ~= 0.3 * log_2(x). Rounding up gives the number + * of digits; add one each for the sign and terminating null + * character, respectively. + */ +#define NDIGITS(x) (3 * (sizeof(x) * CHAR_BIT) / 10 + 1 + 1 + 1) + tmp = malloc(NDIGITS(vp->u.i)); + if (tmp == NULL) + errx(ERR_EXIT, "malloc() failed"); + + sprintf(tmp, "%jd", vp->u.i); + vp->type = string; + vp->u.s = tmp; +} + +int +is_integer(const char *s) +{ + if (nonposix) { + if (*s == '\0') + return (1); + while (isspace((unsigned char)*s)) + s++; + } + if (*s == '-' || (nonposix && *s == '+')) + s++; + if (*s == '\0') + return (0); + while (isdigit((unsigned char)*s)) + s++; + return (*s == '\0'); +} + +int +is_string(struct val *vp) +{ + /* only TRUE if this string is not a valid integer */ + return (vp->type == string); +} + +int +yylex(void) +{ + char *p; + + if (*av == NULL) + return (0); + + p = *av++; + + if (strlen(p) == 1) { + if (strchr("|&=<>+-*/%:()", *p)) + return (*p); + } else if (strlen(p) == 2 && p[1] == '=') { + switch (*p) { + case '>': return (GE); + case '<': return (LE); + case '!': return (NE); + } + } + + yylval.val = make_str(p); + return (TOKEN); +} + +int +is_zero_or_null(struct val *vp) +{ + if (vp->type == integer) + return (vp->u.i == 0); + + return (*vp->u.s == 0 || (to_integer(vp) && vp->u.i == 0)); +} + +int +main(int argc, char *argv[]) +{ +#ifndef __APPLE__ + int c; +#endif + + setlocale(LC_ALL, ""); +#ifdef __APPLE__ + av = argv + 1; + if (*av && !strcmp(*av, "--")) + av++; +#else + if (getenv("EXPR_COMPAT") != NULL + || check_utility_compat("expr")) { + av = argv + 1; + nonposix = 1; + } else { + while ((c = getopt(argc, argv, "e")) != -1) { + switch (c) { + case 'e': + nonposix = 1; + break; + default: + errx(ERR_EXIT, + "usage: expr [-e] expression\n"); + } + } + av = argv + optind; + } +#endif + + yyparse(); + +#ifdef __APPLE__ + if (to_integer(result)) +#else + if (result->type == integer) +#endif + printf("%jd\n", result->u.i); + else + printf("%s\n", result->u.s); + + return (is_zero_or_null(result)); +} + +int +yyerror(const char *s __unused) +{ + errx(ERR_EXIT, "syntax error"); +} + +struct val * +op_or(struct val *a, struct val *b) +{ + if (!is_zero_or_null(a)) { + free_value(b); + return (a); + } + free_value(a); + if (!is_zero_or_null(b)) + return (b); + free_value(b); + return (make_integer((intmax_t)0)); +} + +struct val * +op_and(struct val *a, struct val *b) +{ + if (is_zero_or_null(a) || is_zero_or_null(b)) { + free_value(a); + free_value(b); + return (make_integer((intmax_t)0)); + } else { + free_value(b); + return (a); + } +} + +int +compare_vals(struct val *a, struct val *b) +{ + int r; + + if (is_string(a) || is_string(b)) { + to_string(a); + to_string(b); + r = strcoll(a->u.s, b->u.s); + } else { + assert_to_integer(a); + assert_to_integer(b); + if (a->u.i > b->u.i) + r = 1; + else if (a->u.i < b->u.i) + r = -1; + else + r = 0; + } + + free_value(a); + free_value(b); + return (r); +} + +struct val * +op_eq(struct val *a, struct val *b) +{ + return (make_integer((intmax_t)(compare_vals(a, b) == 0))); +} + +struct val * +op_gt(struct val *a, struct val *b) +{ + return (make_integer((intmax_t)(compare_vals(a, b) > 0))); +} + +struct val * +op_lt(struct val *a, struct val *b) +{ + return (make_integer((intmax_t)(compare_vals(a, b) < 0))); +} + +struct val * +op_ge(struct val *a, struct val *b) +{ + return (make_integer((intmax_t)(compare_vals(a, b) >= 0))); +} + +struct val * +op_le(struct val *a, struct val *b) +{ + return (make_integer((intmax_t)(compare_vals(a, b) <= 0))); +} + +struct val * +op_ne(struct val *a, struct val *b) +{ + return (make_integer((intmax_t)(compare_vals(a, b) != 0))); +} + +void +assert_plus(intmax_t a, intmax_t b, intmax_t r) +{ + /* + * sum of two positive numbers must be positive, + * sum of two negative numbers must be negative + */ + if ((a > 0 && b > 0 && r <= 0) || + (a < 0 && b < 0 && r >= 0)) + errx(ERR_EXIT, "overflow"); +} + +struct val * +op_plus(struct val *a, struct val *b) +{ + struct val *r; + + assert_to_integer(a); + assert_to_integer(b); + r = make_integer(a->u.i + b->u.i); + assert_plus(a->u.i, b->u.i, r->u.i); + + free_value(a); + free_value(b); + return (r); +} + +void +assert_minus(intmax_t a, intmax_t b, intmax_t r) +{ + /* special case subtraction of INTMAX_MIN */ + if (b == INTMAX_MIN && a < 0) + errx(ERR_EXIT, "overflow"); + /* check addition of negative subtrahend */ + assert_plus(a, -b, r); +} + +struct val * +op_minus(struct val *a, struct val *b) +{ + struct val *r; + + assert_to_integer(a); + assert_to_integer(b); + r = make_integer(a->u.i - b->u.i); + assert_minus(a->u.i, b->u.i, r->u.i); + + free_value(a); + free_value(b); + return (r); +} + +void +assert_times(intmax_t a, intmax_t b, intmax_t r) +{ + /* + * if first operand is 0, no overflow is possible, + * else result of division test must match second operand + */ + if (a != 0 && r / a != b) + errx(ERR_EXIT, "overflow"); +} + +struct val * +op_times(struct val *a, struct val *b) +{ + struct val *r; + + assert_to_integer(a); + assert_to_integer(b); + r = make_integer(a->u.i * b->u.i); + assert_times(a->u.i, b->u.i, r->u.i); + + free_value(a); + free_value(b); + return (r); +} + +void +assert_div(intmax_t a, intmax_t b) +{ + if (b == 0) + errx(ERR_EXIT, "division by zero"); + /* only INTMAX_MIN / -1 causes overflow */ + if (a == INTMAX_MIN && b == -1) + errx(ERR_EXIT, "overflow"); +} + +struct val * +op_div(struct val *a, struct val *b) +{ + struct val *r; + + assert_to_integer(a); + assert_to_integer(b); + /* assert based on operands only, not on result */ + assert_div(a->u.i, b->u.i); + r = make_integer(a->u.i / b->u.i); + + free_value(a); + free_value(b); + return (r); +} + +struct val * +op_rem(struct val *a, struct val *b) +{ + struct val *r; + + assert_to_integer(a); + assert_to_integer(b); + /* pass a=1 to only check for div by zero */ + assert_div(1, b->u.i); + r = make_integer(a->u.i % b->u.i); + + free_value(a); + free_value(b); + return (r); +} + +struct val * +op_colon(struct val *a, struct val *b) +{ + regex_t rp; + regmatch_t rm[2]; + char errbuf[256]; + int eval; + struct val *v; + + /* coerce both arguments to strings */ + to_string(a); + to_string(b); + + /* compile regular expression */ + if ((eval = regcomp(&rp, b->u.s, 0)) != 0) { + regerror(eval, &rp, errbuf, sizeof(errbuf)); + errx(ERR_EXIT, "%s", errbuf); + } + + /* compare string against pattern */ + /* remember that patterns are anchored to the beginning of the line */ + if (regexec(&rp, a->u.s, (size_t)2, rm, 0) == 0 && rm[0].rm_so == 0) + if (rm[1].rm_so >= 0) { + *(a->u.s + rm[1].rm_eo) = '\0'; + v = make_str(a->u.s + rm[1].rm_so); +#ifdef __APPLE__ + v->type = string; /* 8319378 */ +#endif + + } else + v = make_integer((intmax_t)(rm[0].rm_eo - rm[0].rm_so)); + else + if (rp.re_nsub == 0) + v = make_integer((intmax_t)0); + else + v = make_str(""); + + /* free arguments and pattern buffer */ + free_value(a); + free_value(b); + regfree(&rp); + + return (v); +} diff --git a/shell_cmds/false/false.1 b/shell_cmds/false/false.1 new file mode 100644 index 0000000..f7b07eb --- /dev/null +++ b/shell_cmds/false/false.1 @@ -0,0 +1,60 @@ +.\" $NetBSD: false.1,v 1.5 1997/10/18 14:46:54 lukem Exp $ +.\" +.\" Copyright (c) 1983, 1990 The Regents of the University of California. +.\" All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" the Institute of Electrical and Electronics Engineers, Inc. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" from: @(#)false.1 6.6 (Berkeley) 7/24/91 +.\" $NetBSD: false.1,v 1.5 1997/10/18 14:46:54 lukem Exp $ +.\" +.Dd July 24, 1991 +.Dt FALSE 1 +.Os BSD 4.2 +.Sh NAME +.Nm false +.Nd Return false value. +.Sh SYNOPSIS +.Nm +.Sh DESCRIPTION +The +.Nm +utility always exits with a nonzero exit code. +.Sh SEE ALSO +.Xr csh 1 , +.Xr sh 1 , +.Xr true 1 +.Sh STANDARDS +The +.Nm +utility conforms to +.St -p1003.2-92 . diff --git a/shell_cmds/false/false.c b/shell_cmds/false/false.c new file mode 100644 index 0000000..3883d89 --- /dev/null +++ b/shell_cmds/false/false.c @@ -0,0 +1,2 @@ +#include <stdlib.h> +int main () { exit(1); } diff --git a/shell_cmds/false/false.sh b/shell_cmds/false/false.sh new file mode 100644 index 0000000..2526efb --- /dev/null +++ b/shell_cmds/false/false.sh @@ -0,0 +1,2 @@ +#! /bin/sh +exit 1 diff --git a/shell_cmds/find/extern.h b/shell_cmds/find/extern.h new file mode 100644 index 0000000..f4282f7 --- /dev/null +++ b/shell_cmds/find/extern.h @@ -0,0 +1,124 @@ +/*- + * Copyright (c) 1991, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)extern.h 8.3 (Berkeley) 4/16/94 + * $FreeBSD: src/usr.bin/find/extern.h,v 1.26 2010/12/11 08:32:16 joel Exp $ + */ + +#include <sys/cdefs.h> + +void brace_subst(char *, char **, char *, int); +PLAN *find_create(char ***); +int find_execute(PLAN *, char **); +PLAN *find_formplan(char **); +PLAN *not_squish(PLAN *); +PLAN *or_squish(PLAN *); +PLAN *paren_squish(PLAN *); +time_t get_date(char *); +struct stat; +void printlong(char *, char *, struct stat *); +int queryuser(char **); +OPTION *lookup_option(const char *); +void finish_execplus(void); + +creat_f c_Xmin; +creat_f c_Xtime; +creat_f c_acl; +creat_f c_and; +creat_f c_delete; +creat_f c_depth; +creat_f c_empty; +creat_f c_exec; +creat_f c_flags; +creat_f c_follow; +creat_f c_fstype; +creat_f c_group; +creat_f c_inum; +creat_f c_links; +creat_f c_ls; +creat_f c_mXXdepth; +creat_f c_name; +creat_f c_newer; +creat_f c_nogroup; +creat_f c_nouser; +creat_f c_perm; +creat_f c_print; +creat_f c_regex; +creat_f c_samefile; +creat_f c_simple; +creat_f c_size; +creat_f c_type; +creat_f c_user; +creat_f c_xdev; + +exec_f f_Xmin; +exec_f f_Xtime; +exec_f f_acl; +exec_f f_always_true; +exec_f f_closeparen; +exec_f f_delete; +exec_f f_depth; +exec_f f_empty; +exec_f f_exec; +exec_f f_expr; +exec_f f_false; +exec_f f_flags; +exec_f f_fstype; +exec_f f_group; +exec_f f_inum; +exec_f f_links; +exec_f f_ls; +exec_f f_name; +exec_f f_newer; +exec_f f_nogroup; +exec_f f_not; +exec_f f_nouser; +exec_f f_openparen; +exec_f f_or; +exec_f f_path; +exec_f f_perm; +exec_f f_print; +exec_f f_print0; +exec_f f_prune; +exec_f f_quit; +exec_f f_regex; +exec_f f_size; +exec_f f_type; +exec_f f_user; +#ifdef __APPLE__ +exec_f f_xattr; +exec_f f_xattrname; +#endif /* __APPLE__ */ + +extern int ftsoptions, isdeprecated, isdepth, isoutput, issort, isxargs; +extern int mindepth, maxdepth; +extern int regexp_flags; +extern time_t now; +extern int dotfd; +extern FTS *tree; +extern int execplus_error; diff --git a/shell_cmds/find/find.1 b/shell_cmds/find/find.1 new file mode 100644 index 0000000..c6bb28f --- /dev/null +++ b/shell_cmds/find/find.1 @@ -0,0 +1,1078 @@ +.\" Copyright (c) 1990, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" the Institute of Electrical and Electronics Engineers, Inc. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)find.1 8.7 (Berkeley) 5/9/95 +.\" $FreeBSD: src/usr.bin/find/find.1,v 1.91 2011/09/28 18:53:36 ed Exp $ +.\" +.Dd September 28, 2011 +.Dt FIND 1 +.Os +.Sh NAME +.Nm find +.Nd walk a file hierarchy +.Sh SYNOPSIS +.Nm +.Op Fl H | Fl L | Fl P +.Op Fl EXdsx +.Op Fl f Ar path +.Ar path ... +.Op Ar expression +.Nm +.Op Fl H | Fl L | Fl P +.Op Fl EXdsx +.Fl f Ar path +.Op Ar path ... +.Op Ar expression +.Sh DESCRIPTION +The +.Nm +utility recursively descends the directory tree for each +.Ar path +listed, evaluating an +.Ar expression +(composed of the +.Dq primaries +and +.Dq operands +listed below) in terms +of each file in the tree. +.Pp +The options are as follows: +.Bl -tag -width indent +.It Fl E +Interpret regular expressions followed by +.Ic -regex +and +.Ic -iregex +primaries as extended (modern) regular expressions rather than basic +regular expressions (BRE's). +The +.Xr re_format 7 +manual page fully describes both formats. +.It Fl H +Cause the file information and file type (see +.Xr stat 2 ) +returned for each symbolic link specified on the command line to be +those of the file referenced by the link, not the link itself. +If the referenced file does not exist, the file information and type will +be for the link itself. +File information of all symbolic links not on +the command line is that of the link itself. +.It Fl L +Cause the file information and file type (see +.Xr stat 2 ) +returned for each symbolic link to be those of the file referenced by the +link, not the link itself. +If the referenced file does not exist, the file information and type will +be for the link itself. +.Pp +This option is equivalent to the deprecated +.Ic -follow +primary. +.It Fl P +Cause the file information and file type (see +.Xr stat 2 ) +returned for each symbolic link to be those of the link itself. +This is the default. +.It Fl X +Permit +.Nm +to be safely used in conjunction with +.Xr xargs 1 . +If a file name contains any of the delimiting characters used by +.Xr xargs 1 , +a diagnostic message is displayed on standard error, and the file +is skipped. +The delimiting characters include single +.Pq Dq Li " ' " +and double +.Pq Dq Li " \*q " +quotes, backslash +.Pq Dq Li \e , +space, tab and newline characters. +.Pp +However, you may wish to consider the +.Fl print0 +primary in conjunction with +.Dq Nm xargs Fl 0 +as an effective alternative. +.It Fl d +Cause +.Nm +to perform a depth-first traversal, i.e., directories +are visited in post-order and all entries in a directory will be acted +on before the directory itself. +By default, +.Nm +visits directories in pre-order, i.e., before their contents. +Note, the default is +.Em not +a breadth-first traversal. +.Pp +This option is equivalent to the +.Ic -depth +primary of +.St -p1003.1-2001 . +The +.Fl d +option +can be useful when +.Nm +is used with +.Xr cpio 1 +to process files that are contained in directories with unusual permissions. +It ensures that you have write permission while you are placing files in a +directory, then sets the directory's permissions as the last thing. +.It Fl f +Specify a file hierarchy for +.Nm +to traverse. +File hierarchies may also be specified as the operands immediately +following the options. +.It Fl s +Cause +.Nm +to traverse the file hierarchies in lexicographical order, +i.e., alphabetical order within each directory. +Note: +.Ql find -s +and +.Ql "find | sort" +may give different results. +.It Fl x +Prevent +.Nm +from descending into directories that have a device number different +than that of the file from which the descent began. +.Pp +This option is equivalent to the deprecated +.Ic -xdev +primary. +.El +.Sh PRIMARIES +.Pp +All primaries which take a numeric argument allow the number to be +preceded by a plus sign +.Pq Dq Li + +or a minus sign +.Pq Dq Li - . +A preceding plus sign means +.Dq more than n , +a preceding minus sign means +.Dq less than n +and neither means +.Dq exactly n . +.Bl -tag -width indent +.It Ic -Bmin Ar n +True if the difference between the time of a file's inode creation +and the time +.Nm +was started, rounded up to the next full minute, is +.Ar n +minutes. +.It Ic -Bnewer Ar file +Same as +.Ic -newerBm . +.It Ic -Btime Ar n Ns Op Cm smhdw +If no units are specified, this primary evaluates to +true if the difference between the time of a file's inode creation +and the time +.Nm +was started, rounded up to the next full 24-hour period, is +.Ar n +24-hour periods. +.Pp +If units are specified, this primary evaluates to +true if the difference between the time of a file's inode creation +and the time +.Nm +was started is exactly +.Ar n +units. +Please refer to the +.Ic -atime +primary description for information on supported time units. +.It Ic -acl +May be used in conjunction with other primaries to locate +files with extended ACLs. +See +.Xr acl 3 +for more information. +.It Ic -amin Ar n +True if the difference between the file last access time and the time +.Nm +was started, rounded up to the next full minute, is +.Ar n +minutes. +.It Ic -anewer Ar file +Same as +.Ic -neweram . +.It Ic -atime Ar n Ns Op Cm smhdw +If no units are specified, this primary evaluates to +true if the difference between the file last access time and the time +.Nm +was started, rounded up to the next full 24-hour period, is +.Ar n +24-hour periods. +.Pp +If units are specified, this primary evaluates to +true if the difference between the file last access time and the time +.Nm +was started is exactly +.Ar n +units. +Possible time units are as follows: +.Pp +.Bl -tag -width indent -compact +.It Cm s +second +.It Cm m +minute (60 seconds) +.It Cm h +hour (60 minutes) +.It Cm d +day (24 hours) +.It Cm w +week (7 days) +.El +.Pp +Any number of units may be combined in one +.Ic -atime +argument, for example, +.Dq Li "-atime -1h30m" . +Units are probably only useful when used in conjunction with the +.Cm + +or +.Cm - +modifier. +.It Ic -cmin Ar n +True if the difference between the time of last change of file status +information and the time +.Nm +was started, rounded up to the next full minute, is +.Ar n +minutes. +.It Ic -cnewer Ar file +Same as +.Ic -newercm . +.It Ic -ctime Ar n Ns Op Cm smhdw +If no units are specified, this primary evaluates to +true if the difference between the time of last change of file status +information and the time +.Nm +was started, rounded up to the next full 24-hour period, is +.Ar n +24-hour periods. +.Pp +If units are specified, this primary evaluates to +true if the difference between the time of last change of file status +information and the time +.Nm +was started is exactly +.Ar n +units. +Please refer to the +.Ic -atime +primary description for information on supported time units. +.It Ic -d +Same as +.Ic depth . +GNU find implements this as a primary in mistaken emulation of +.Fx +.Xr find 1 . +.It Ic -delete +Delete found files and/or directories. +Always returns true. +This executes +from the current working directory as +.Nm +recurses down the tree. +It will not attempt to delete a filename with a +.Dq Pa / +character in its pathname relative to +.Dq Pa \&. +for security reasons. +Depth-first traversal processing is implied by this option. +Following symlinks is incompatible with this option. +.It Ic -depth +Always true; +same as the +.Fl d +option. +.It Ic -depth Ar n +True if the depth of the file relative to the starting point of the traversal +is +.Ar n . +.It Ic -empty +True if the current file or directory is empty. +.It Ic -exec Ar utility Oo Ar argument ... Oc Li \&; +True if the program named +.Ar utility +returns a zero value as its exit status. +Optional +.Ar arguments +may be passed to the utility. +The expression must be terminated by a semicolon +.Pq Dq Li \&; . +If you invoke +.Nm +from a shell you may need to quote the semicolon if the shell would +otherwise treat it as a control operator. +If the string +.Dq Li {} +appears anywhere in the utility name or the +arguments it is replaced by the pathname of the current file. +.Ar Utility +will be executed from the directory from which +.Nm +was executed. +.Ar Utility +and +.Ar arguments +are not subject to the further expansion of shell patterns +and constructs. +.It Ic -exec Ar utility Oo Ar argument ... Oc Li {} + +Same as +.Ic -exec , +except that +.Dq Li {} +is replaced with as many pathnames as possible for each invocation of +.Ar utility . +This behaviour is similar to that of +.Xr xargs 1 . +.It Ic -execdir Ar utility Oo Ar argument ... Oc Li \&; +The +.Ic -execdir +primary is identical to the +.Ic -exec +primary with the exception that +.Ar utility +will be executed from the directory that holds +the current file. +The filename substituted for +the string +.Dq Li {} +is not qualified. +.It Ic -execdir Ar utility Oo Ar argument ... Oc Li {} + +Same as +.Ic -execdir , +except that +.Dq Li {} +is replaced with as many pathnames as possible for each invocation of +.Ar utility . +This behaviour is similar to that of +.Xr xargs 1 . +.It Ic -flags Oo Cm - Ns | Ns Cm + Oc Ns Ar flags , Ns Ar notflags +The flags are specified using symbolic names (see +.Xr chflags 1 ) . +Those with the +.Qq Li no +prefix (except +.Qq Li nodump ) +are said to be +.Ar notflags . +Flags in +.Ar flags +are checked to be set, and flags in +.Ar notflags +are checked to be not set. +Note that this is different from +.Ic -perm , +which only allows the user to specify mode bits that are set. +.Pp +If flags are preceded by a dash +.Pq Dq Li - , +this primary evaluates to true +if at least all of the bits in +.Ar flags +and none of the bits in +.Ar notflags +are set in the file's flags bits. +If flags are preceded by a plus +.Pq Dq Li + , +this primary evaluates to true +if any of the bits in +.Ar flags +is set in the file's flags bits, +or any of the bits in +.Ar notflags +is not set in the file's flags bits. +Otherwise, +this primary evaluates to true +if the bits in +.Ar flags +exactly match the file's flags bits, +and none of the +.Ar flags +bits match those of +.Ar notflags . +.It Ic -fstype Ar type +True if the file is contained in a file system of type +.Ar type . +The +.Xr lsvfs 1 +command can be used to find out the types of file systems +that are available on the system. +In addition, there are two pseudo-types, +.Dq Li local +and +.Dq Li rdonly . +The former matches any file system physically mounted on the system where +the +.Nm +is being executed and the latter matches any file system which is +mounted read-only. +.It Ic -gid Ar gname +The same thing as +.Ar -group Ar gname +for compatibility with GNU find. +GNU find imposes a restriction that +.Ar gname +is numeric, while +.Xr find 1 +does not. +.It Ic -group Ar gname +True if the file belongs to the group +.Ar gname . +If +.Ar gname +is numeric and there is no such group name, then +.Ar gname +is treated as a group ID. +.It Ic -ignore_readdir_race +This option is for GNU find compatibility and is ignored. +.It Ic -ilname Ar pattern +Like +.Ic -lname , +but the match is case insensitive. +This is a GNU find extension. +.It Ic -iname Ar pattern +Like +.Ic -name , +but the match is case insensitive. +.It Ic -inum Ar n +True if the file has inode number +.Ar n . +.It Ic -ipath Ar pattern +Like +.Ic -path , +but the match is case insensitive. +.It Ic -iregex Ar pattern +Like +.Ic -regex , +but the match is case insensitive. +.It Ic -iwholename Ar pattern +The same thing as +.Ic -ipath , +for GNU find compatibility. +.It Ic -links Ar n +True if the file has +.Ar n +links. +.It Ic -lname Ar pattern +Like +.Ic -name , +but the contents of the symbolic link are matched instead of the file +name. +Note that this only matches broken symbolic links +if symbolic links are being followed. +This is a GNU find extension. +.It Ic -ls +This primary always evaluates to true. +The following information for the current file is written to standard output: +its inode number, size in 512-byte blocks, file permissions, number of hard +links, owner, group, size in bytes, last modification time, and pathname. +If the file is a block or character special file, the device number +will be displayed instead of the size in bytes. +If the file is a symbolic link, the pathname of the linked-to file will be +displayed preceded by +.Dq Li -> . +The format is identical to that produced by +.Bk -words +.Dq Nm ls Fl dgils . +.Ek +.It Ic -maxdepth Ar n +Always true; descend at most +.Ar n +directory levels below the command line arguments. +If any +.Ic -maxdepth +primary is specified, it applies to the entire expression even if it would +not normally be evaluated. +.Dq Ic -maxdepth Li 0 +limits the whole search to the command line arguments. +.It Ic -mindepth Ar n +Always true; do not apply any tests or actions at levels less than +.Ar n . +If any +.Ic -mindepth +primary is specified, it applies to the entire expression even if it would +not normally be evaluated. +.Dq Ic -mindepth Li 1 +processes all but the command line arguments. +.It Ic -mmin Ar n +True if the difference between the file last modification time and the time +.Nm +was started, rounded up to the next full minute, is +.Ar n +minutes. +.It Ic -mnewer Ar file +Same as +.Ic -newer . +.It Ic -mount +The same thing as +.Ic -xdev , +for GNU find compatibility. +.It Ic -mtime Ar n Ns Op Cm smhdw +If no units are specified, this primary evaluates to +true if the difference between the file last modification time and the time +.Nm +was started, rounded up to the next full 24-hour period, is +.Ar n +24-hour periods. +.Pp +If units are specified, this primary evaluates to +true if the difference between the file last modification time and the time +.Nm +was started is exactly +.Ar n +units. +Please refer to the +.Ic -atime +primary description for information on supported time units. +.It Ic -name Ar pattern +True if the last component of the pathname being examined matches +.Ar pattern . +Special shell pattern matching characters +.Dq ( Li \&[ , +.Dq Li \&] , +.Dq Li * , +and +.Dq Li \&? ) +may be used as part of +.Ar pattern . +These characters may be matched explicitly by escaping them with a +backslash +.Pq Dq Li \e . +.It Ic -newer Ar file +True if the current file has a more recent last modification time than +.Ar file . +.It Ic -newer Ns Ar X Ns Ar Y Ar file +True if the current file has a more recent last access time +.Pq Ar X Ns = Ns Cm a , +inode creation time +.Pq Ar X Ns = Ns Cm B , +change time +.Pq Ar X Ns = Ns Cm c , +or modification time +.Pq Ar X Ns = Ns Cm m +than the last access time +.Pq Ar Y Ns = Ns Cm a , +inode creation time +.Pq Ar Y Ns = Ns Cm B , +change time +.Pq Ar Y Ns = Ns Cm c , +or modification time +.Pq Ar Y Ns = Ns Cm m +of +.Ar file . +In addition, if +.Ar Y Ns = Ns Cm t , +then +.Ar file +is instead interpreted as a direct date specification of the form +understood by +.Xr cvs 1 . +Note that +.Ic -newermm +is equivalent to +.Ic -newer . +.It Ic -nogroup +True if the file belongs to an unknown group. +.It Ic -noignore_readdir_race +This option is for GNU find compatibility and is ignored. +.It Ic -noleaf +This option is for GNU find compatibility. +In GNU find it disables an optimization not relevant to +.Xr find 1 , +so it is ignored. +.It Ic -nouser +True if the file belongs to an unknown user. +.It Ic -ok Ar utility Oo Ar argument ... Oc Li \&; +The +.Ic -ok +primary is identical to the +.Ic -exec +primary with the exception that +.Nm +requests user affirmation for the execution of the +.Ar utility +by printing +a message to the terminal and reading a response. +If the response is not affirmative +.Ql ( y +in the +.Dq Li POSIX +locale), +the command is not executed and the +value of the +.Ic -ok +expression is false. +.It Ic -okdir Ar utility Oo Ar argument ... Oc Li \&; +The +.Ic -okdir +primary is identical to the +.Ic -execdir +primary with the same exception as described for the +.Ic -ok +primary. +.It Ic -path Ar pattern +True if the pathname being examined matches +.Ar pattern . +Special shell pattern matching characters +.Dq ( Li \&[ , +.Dq Li \&] , +.Dq Li * , +and +.Dq Li \&? ) +may be used as part of +.Ar pattern . +These characters may be matched explicitly by escaping them with a +backslash +.Pq Dq Li \e . +Slashes +.Pq Dq Li / +are treated as normal characters and do not have to be +matched explicitly. +.It Ic -perm Oo Cm - Ns | Ns Cm + Oc Ns Ar mode +The +.Ar mode +may be either symbolic (see +.Xr chmod 1 ) +or an octal number. +If the +.Ar mode +is symbolic, a starting value of zero is assumed and the +.Ar mode +sets or clears permissions without regard to the process' file mode +creation mask. +If the +.Ar mode +is octal, only bits 07777 +.Pq Dv S_ISUID | S_ISGID | S_ISTXT | S_IRWXU | S_IRWXG | S_IRWXO +of the file's mode bits participate +in the comparison. +If the +.Ar mode +is preceded by a dash +.Pq Dq Li - , +this primary evaluates to true +if at least all of the bits in the +.Ar mode +are set in the file's mode bits. +If the +.Ar mode +is preceded by a plus +.Pq Dq Li + , +this primary evaluates to true +if any of the bits in the +.Ar mode +are set in the file's mode bits. +Otherwise, this primary evaluates to true if +the bits in the +.Ar mode +exactly match the file's mode bits. +Note, the first character of a symbolic mode may not be a dash +.Pq Dq Li - . +.It Ic -print +This primary always evaluates to true. +It prints the pathname of the current file to standard output. +If none of +.\" 4772561 +.Ic -exec , -ls , -print , -print0 , +or +.Ic -ok +is specified, the given expression shall be effectively replaced by +.Cm \&( Ar "given expression" Cm \&) Ic -print . +.It Ic -print0 +This primary always evaluates to true. +It prints the pathname of the current file to standard output, followed by an +.Tn ASCII +.Dv NUL +character (character code 0). +.It Ic -prune +This primary always evaluates to true. +It causes +.Nm +to not descend into the current file. +Note, the +.Ic -prune +primary has no effect if the +.Fl d +option was specified. +.It Ic -regex Ar pattern +True if the whole path of the file matches +.Ar pattern +using regular expression. +To match a file named +.Dq Pa ./foo/xyzzy , +you can use the regular expression +.Dq Li ".*/[xyz]*" +or +.Dq Li ".*/foo/.*" , +but not +.Dq Li xyzzy +or +.Dq Li /foo/ . +.It Ic -samefile Ar name +True if the file is a hard link to +.Ar name . +If the command option +.Ic -L +is specified, it is also true if the file is a symbolic link and +points to +.Ar name . +.It Ic -size Ar n Ns Op Cm ckMGTP +True if the file's size, rounded up, in 512-byte blocks is +.Ar n . +If +.Ar n +is followed by a +.Cm c , +then the primary is true if the +file's size is +.Ar n +bytes (characters). +Similarly if +.Ar n +is followed by a scale indicator then the file's size is compared to +.Ar n +scaled as: +.Pp +.Bl -tag -width indent -compact +.It Cm k +kilobytes (1024 bytes) +.It Cm M +megabytes (1024 kilobytes) +.It Cm G +gigabytes (1024 megabytes) +.It Cm T +terabytes (1024 gigabytes) +.It Cm P +petabytes (1024 terabytes) +.El +.It Ic -type Ar t +True if the file is of the specified type. +Possible file types are as follows: +.Pp +.Bl -tag -width indent -compact +.It Cm b +block special +.It Cm c +character special +.It Cm d +directory +.It Cm f +regular file +.It Cm l +symbolic link +.It Cm p +FIFO +.It Cm s +socket +.El +.It Ic -uid Ar uname +The same thing as +.Ar -user Ar uname +for compatibility with GNU find. +GNU find imposes a restriction that +.Ar uname +is numeric, while +.Xr find 1 +does not. +.It Ic -user Ar uname +True if the file belongs to the user +.Ar uname . +If +.Ar uname +is numeric and there is no such user name, then +.Ar uname +is treated as a user ID. +.It Ic -wholename Ar pattern +The same thing as +.Ic -path , +for GNU find compatibility. +.It Ic -xattr +True if the file has any extended attributes. +.It Ic -xattrname Ar name +True if the file has an extended attribute with the specified +.Ar name . +.El +.Sh OPERATORS +The primaries may be combined using the following operators. +The operators are listed in order of decreasing precedence. +.Pp +.Bl -tag -width indent -compact +.It Cm \&( Ar expression Cm \&) +This evaluates to true if the parenthesized expression evaluates to +true. +.Pp +.It Cm \&! Ar expression +.It Cm -not Ar expression +This is the unary +.Tn NOT +operator. +It evaluates to true if the expression is false. +.Pp +.It Cm -false +Always false. +.It Cm -true +Always true. +.Pp +.It Ar expression Cm -and Ar expression +.It Ar expression expression +The +.Cm -and +operator is the logical +.Tn AND +operator. +As it is implied by the juxtaposition of two expressions it does not +have to be specified. +The expression evaluates to true if both expressions are true. +The second expression is not evaluated if the first expression is false. +.Pp +.It Ar expression Cm -or Ar expression +The +.Cm -or +operator is the logical +.Tn OR +operator. +The expression evaluates to true if either the first or the second expression +is true. +The second expression is not evaluated if the first expression is true. +.El +.Pp +All operands and primaries must be separate arguments to +.Nm . +Primaries which themselves take arguments expect each argument +to be a separate argument to +.Nm . +.Sh ENVIRONMENT +The +.Ev LANG , LC_ALL , LC_COLLATE , LC_CTYPE , LC_MESSAGES +and +.Ev LC_TIME +environment variables affect the execution of the +.Nm +utility as described in +.Xr environ 7 . +.Sh EXAMPLES +The following examples are shown as given to the shell: +.Bl -tag -width indent +.It Li "find / \e! -name \*q*.c\*q -print" +Print out a list of all the files whose names do not end in +.Pa .c . +.It Li "find / -newer ttt -user wnj -print" +Print out a list of all the files owned by user +.Dq wnj +that are newer +than the file +.Pa ttt . +.It Li "find / \e! \e( -newer ttt -user wnj \e) -print" +Print out a list of all the files which are not both newer than +.Pa ttt +and owned by +.Dq wnj . +.It Li "find / \e( -newer ttt -or -user wnj \e) -print" +Print out a list of all the files that are either owned by +.Dq wnj +or that are newer than +.Pa ttt . +.It Li "find / -newerct '1 minute ago' -print" +Print out a list of all the files whose inode change time is more +recent than the current time minus one minute. +.It Li "find / -type f -exec echo {} \e;" +Use the +.Xr echo 1 +command to print out a list of all the files. +.It Li "find -L /usr/ports/packages -type l -exec rm -- {} +" +Delete all broken symbolic links in +.Pa /usr/ports/packages . +.It Li "find /usr/src -name CVS -prune -o -depth +6 -print" +Find files and directories that are at least seven levels deep +in the working directory +.Pa /usr/src . +.It Li "find /usr/src -name CVS -prune -o -mindepth 7 -print" +Is not equivalent to the previous example, since +.Ic -prune +is not evaluated below level seven. +.El +.Sh COMPATIBILITY +The +.Ic -follow +primary is deprecated; the +.Fl L +option should be used instead. +See the +.Sx STANDARDS +section below for details. +.Sh SEE ALSO +.Xr chflags 1 , +.Xr chmod 1 , +.Xr cvs 1 , +.Xr locate 1 , +.Xr lsvfs 1 , +.Xr whereis 1 , +.Xr which 1 , +.Xr xargs 1 , +.Xr stat 2 , +.Xr acl 3 , +.Xr fts 3 , +.Xr getgrent 3 , +.Xr getpwent 3 , +.Xr strmode 3 , +.Xr re_format 7 , +.Xr symlink 7 +.Sh STANDARDS +The +.Nm +utility syntax is a superset of the syntax specified by the +.St -p1003.1-2001 +standard. +.Pp +All the single character options except +.Fl H +and +.Fl L +as well as +.Ic -amin , -anewer , -cmin , -cnewer , -delete , -empty , -fstype , +.Ic -iname , -inum , -iregex , -ls , -maxdepth , -mindepth , -mmin , +.Ic -path , -print0 , -regex +and all of the +.Ic -B* +birthtime related primaries are extensions to +.St -p1003.1-2001 . +.Pp +Historically, the +.Fl d , L +and +.Fl x +options were implemented using the primaries +.Ic -depth , -follow , +and +.Ic -xdev . +These primaries always evaluated to true. +As they were really global variables that took effect before the traversal +began, some legal expressions could have unexpected results. +An example is the expression +.Ic -print Cm -o Ic -depth . +As +.Ic -print +always evaluates to true, the standard order of evaluation +implies that +.Ic -depth +would never be evaluated. +This is not the case. +.Pp +The operator +.Cm -or +was implemented as +.Cm -o , +and the operator +.Cm -and +was implemented as +.Cm -a . +.Pp +Historic implementations of the +.Ic -exec +and +.Ic -ok +primaries did not replace the string +.Dq Li {} +in the utility name or the +utility arguments if it had preceding or following non-whitespace characters. +This version replaces it no matter where in the utility name or arguments +it appears. +.Pp +The +.Fl E +option was inspired by the equivalent +.Xr grep 1 +and +.Xr sed 1 +options. +.Sh HISTORY +A +.Nm +command appeared in +.At v1 . +.Sh BUGS +The special characters used by +.Nm +are also special characters to many shell programs. +In particular, the characters +.Dq Li * , +.Dq Li \&[ , +.Dq Li \&] , +.Dq Li \&? , +.Dq Li \&( , +.Dq Li \&) , +.Dq Li \&! , +.Dq Li \e +and +.Dq Li \&; +may have to be escaped from the shell. +.Pp +As there is no delimiter separating options and file names or file +names and the +.Ar expression , +it is difficult to specify files named +.Pa -xdev +or +.Pa \&! . +These problems are handled by the +.Fl f +option and the +.Xr getopt 3 +.Dq Fl Fl +construct. +.Pp +The +.Ic -delete +primary does not interact well with other options that cause the file system +tree traversal options to be changed. +.Pp +The +.Ic -mindepth +and +.Ic -maxdepth +primaries are actually global options (as documented above). +They should +probably be replaced by options which look like options. diff --git a/shell_cmds/find/find.c b/shell_cmds/find/find.c new file mode 100644 index 0000000..c59a75a --- /dev/null +++ b/shell_cmds/find/find.c @@ -0,0 +1,323 @@ +/*- + * Copyright (c) 1991, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Cimarron D. Taylor of the University of California, Berkeley. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)find.c 8.5 (Berkeley) 8/5/94"; +#else +#endif +#endif /* not lint */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD: src/usr.bin/find/find.c,v 1.23 2010/12/11 08:32:16 joel Exp $"); + +#include <sys/types.h> +#include <sys/stat.h> + +#include <err.h> +#include <errno.h> +#include <fts.h> +#include <regex.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#ifdef __APPLE__ +#include <get_compat.h> +#include <unistd.h> +#else +#define COMPAT_MODE(func, mode) 1 +#endif + +#include "find.h" + +#ifdef __APPLE__ +static int find_compare(const FTSENT **s1, const FTSENT **s2); +#else /* !__APPLE__ */ +static int find_compare(const FTSENT * const *s1, const FTSENT * const *s2); +#endif /* __APPLE__ */ + +/* + * find_compare -- + * tell fts_open() how to order the traversal of the hierarchy. + * This variant gives lexicographical order, i.e., alphabetical + * order within each directory. + */ +static int +#ifdef __APPLE__ +find_compare(const FTSENT **s1, const FTSENT **s2) +#else /* !__APPLE__ */ +find_compare(const FTSENT * const *s1, const FTSENT * const *s2) +#endif /* __APPLE__ */ +{ + + return (strcoll((*s1)->fts_name, (*s2)->fts_name)); +} + +/* + * find_formplan -- + * process the command line and create a "plan" corresponding to the + * command arguments. + */ +PLAN * +find_formplan(char *argv[]) +{ + PLAN *plan, *tail, *new; + + /* + * for each argument in the command line, determine what kind of node + * it is, create the appropriate node type and add the new plan node + * to the end of the existing plan. The resulting plan is a linked + * list of plan nodes. For example, the string: + * + * % find . -name foo -newer bar -print + * + * results in the plan: + * + * [-name foo]--> [-newer bar]--> [-print] + * + * in this diagram, `[-name foo]' represents the plan node generated + * by c_name() with an argument of foo and `-->' represents the + * plan->next pointer. + */ + for (plan = tail = NULL; *argv;) { + if (!(new = find_create(&argv))) + continue; + if (plan == NULL) + tail = plan = new; + else { + tail->next = new; + tail = new; + } + } + + /* + * if the user didn't specify one of -print, -ok or -exec, then -print + * is assumed so we bracket the current expression with parens, if + * necessary, and add a -print node on the end. + */ + if (!isoutput) { + OPTION *p; + char **argv1 = 0; + + if (plan == NULL) { + p = lookup_option("-print"); + new = (p->create)(p, &argv1); + tail = plan = new; + } else { + p = lookup_option("("); + new = (p->create)(p, &argv1); + new->next = plan; + plan = new; + p = lookup_option(")"); + new = (p->create)(p, &argv1); + tail->next = new; + tail = new; + p = lookup_option("-print"); + new = (p->create)(p, &argv1); + tail->next = new; + tail = new; + } + } + + /* + * the command line has been completely processed into a search plan + * except for the (, ), !, and -o operators. Rearrange the plan so + * that the portions of the plan which are affected by the operators + * are moved into operator nodes themselves. For example: + * + * [!]--> [-name foo]--> [-print] + * + * becomes + * + * [! [-name foo] ]--> [-print] + * + * and + * + * [(]--> [-depth]--> [-name foo]--> [)]--> [-print] + * + * becomes + * + * [expr [-depth]-->[-name foo] ]--> [-print] + * + * operators are handled in order of precedence. + */ + + plan = paren_squish(plan); /* ()'s */ + plan = not_squish(plan); /* !'s */ + plan = or_squish(plan); /* -o's */ + return (plan); +} + +/* addPath - helper function used to build a list of paths that were + * specified on the command line that we are allowed to search. + */ +static char **addPath(char **array, char *newPath) +{ + static int pathCounter = 0; + + if (newPath == NULL) { /* initialize array */ + if ((array = malloc(sizeof(char *))) == NULL) + err(2, "out of memory"); + array[0] = NULL; + } + else { + array = realloc(array, (++pathCounter + 1) * sizeof(char *)); + if (array == NULL) + err(2, "out of memory"); + else { + array[pathCounter - 1] = newPath; + array[pathCounter] = NULL; /* ensure array is null terminated */ + } + } + return (array); +} + +FTS *tree; /* pointer to top of FTS hierarchy */ + +/* + * find_execute -- + * take a search plan and an array of search paths and executes the plan + * over all FTSENT's returned for the given search paths. + */ +int +find_execute(PLAN *plan, char *paths[]) +{ + FTSENT *entry; + PLAN *p; + int rval; + char **myPaths; + int nonSearchableDirFound = 0; + int pathIndex; + struct stat statInfo; + + /* special-case directories specified on command line - explicitly examine + * mode bits, to ensure failure if the directory cannot be searched + * (whether or not it's empty). UNIX conformance... <sigh> + */ + + int strict_symlinks = (ftsoptions & (FTS_COMFOLLOW|FTS_LOGICAL)) + && COMPAT_MODE("bin/find", "unix2003"); + + myPaths = addPath(NULL, NULL); + for (pathIndex = 0; paths[pathIndex] != NULL; ++pathIndex) { + int stat_ret = stat(paths[pathIndex], &statInfo); + int stat_errno = errno; + if (strict_symlinks && stat_ret < 0) { + if (stat_errno == ELOOP) { + errx(1, "Symlink loop resolving %s", paths[pathIndex]); + } + } + + /* retrieve mode bits, and examine "searchable" bit of + directories, exempt root from POSIX conformance */ + if (COMPAT_MODE("bin/find", "unix2003") && getuid() + && stat_ret == 0 + && ((statInfo.st_mode & S_IFMT) == S_IFDIR)) { + if (access(paths[pathIndex], X_OK) == 0) { + myPaths = addPath(myPaths, paths[pathIndex]); + } else { + if (stat_errno != ENAMETOOLONG) { /* if name is too long, just let existing logic handle it */ + warnx("%s: Permission denied", paths[pathIndex]); + nonSearchableDirFound = 1; + } + } + } else { + /* not a directory, so add path to array */ + myPaths = addPath(myPaths, paths[pathIndex]); + } + } + if (myPaths[0] == NULL) { /* were any directories searchable? */ + free(myPaths); + return(nonSearchableDirFound); /* no... */ + } + + tree = fts_open(myPaths, ftsoptions, (issort ? find_compare : NULL)); + if (tree == NULL) + err(1, "ftsopen"); + + for (rval = nonSearchableDirFound; (entry = fts_read(tree)) != NULL;) { + if (maxdepth != -1 && entry->fts_level >= maxdepth) { + if (fts_set(tree, entry, FTS_SKIP)) + err(1, "%s", entry->fts_path); + } + + switch (entry->fts_info) { + case FTS_D: + if (isdepth) + continue; + break; + case FTS_DP: + if (!isdepth) + continue; + break; + case FTS_DNR: + case FTS_ERR: + case FTS_NS: + (void)fflush(stdout); + warnx("%s: %s", + entry->fts_path, strerror(entry->fts_errno)); + rval = 1; + continue; +#ifdef FTS_W + case FTS_W: + continue; +#endif /* FTS_W */ + } +#define BADCH " \t\n\\'\"" + if (isxargs && strpbrk(entry->fts_path, BADCH)) { + (void)fflush(stdout); + warnx("%s: illegal path", entry->fts_path); + rval = 1; + continue; + } + + if (mindepth != -1 && entry->fts_level < mindepth) + continue; + + /* + * Call all the functions in the execution plan until one is + * false or all have been executed. This is where we do all + * the work specified by the user on the command line. + */ + for (p = plan; p && (p->execute)(p, entry); p = p->next); + } + free (myPaths); + finish_execplus(); + if (execplus_error) { + exit(execplus_error); + } + if (errno) + err(1, "fts_read"); + fts_close(tree); + return (rval); +} diff --git a/shell_cmds/find/find.h b/shell_cmds/find/find.h new file mode 100644 index 0000000..8960a0b --- /dev/null +++ b/shell_cmds/find/find.h @@ -0,0 +1,145 @@ +/*- + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Cimarron D. Taylor of the University of California, Berkeley. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)find.h 8.1 (Berkeley) 6/6/93 + * $FreeBSD: src/usr.bin/find/find.h,v 1.21 2010/12/11 08:32:16 joel Exp $ + */ + +#include <regex.h> + +/* forward declarations */ +struct _plandata; +struct _option; + +/* execute function */ +typedef int exec_f(struct _plandata *, FTSENT *); +/* create function */ +typedef struct _plandata *creat_f(struct _option *, char ***); + +/* function modifiers */ +#define F_NEEDOK 0x00000001 /* -ok vs. -exec */ +#define F_EXECDIR 0x00000002 /* -execdir vs. -exec */ +#define F_TIME_A 0x00000004 /* one of -atime, -anewer, -newera* */ +#define F_TIME_C 0x00000008 /* one of -ctime, -cnewer, -newerc* */ +#define F_TIME2_A 0x00000010 /* one of -newer?a */ +#define F_TIME2_C 0x00000020 /* one of -newer?c */ +#define F_TIME2_T 0x00000040 /* one of -newer?t */ +#define F_MAXDEPTH F_TIME_A /* maxdepth vs. mindepth */ +#define F_DEPTH F_TIME_A /* -depth n vs. -d */ +/* command line function modifiers */ +#define F_EQUAL 0x00000000 /* [acm]min [acm]time inum links size */ +#define F_LESSTHAN 0x00000100 +#define F_GREATER 0x00000200 +#define F_ELG_MASK 0x00000300 +#define F_ATLEAST 0x00000400 /* flags perm */ +#define F_ANY 0x00000800 /* perm */ +#define F_MTMASK 0x00003000 +#define F_MTFLAG 0x00000000 /* fstype */ +#define F_MTTYPE 0x00001000 +#define F_MTUNKNOWN 0x00002000 +#define F_IGNCASE 0x00010000 /* iname ipath iregex */ +#define F_EXACTTIME F_IGNCASE /* -[acm]time units syntax */ +#define F_EXECPLUS 0x00020000 /* -exec ... {} + */ +#define F_TIME_B 0x00040000 /* one of -Btime, -Bnewer, -newerB* */ +#define F_TIME2_B 0x00080000 /* one of -newer?B */ +#define F_LINK 0x00100000 /* lname or ilname */ + +/* node definition */ +typedef struct _plandata { + struct _plandata *next; /* next node */ + exec_f *execute; /* node evaluation function */ + int flags; /* private flags */ + union { + gid_t _g_data; /* gid */ + ino_t _i_data; /* inode */ + mode_t _m_data; /* mode mask */ + struct { + u_long _f_flags; + u_long _f_notflags; + } fl; + nlink_t _l_data; /* link count */ + short _d_data; /* level depth (-1 to N) */ + off_t _o_data; /* file size */ + time_t _t_data; /* time value */ + uid_t _u_data; /* uid */ + short _mt_data; /* mount flags */ + struct _plandata *_p_data[2]; /* PLAN trees */ + struct _ex { + char **_e_argv; /* argv array */ + char **_e_orig; /* original strings */ + int *_e_len; /* allocated length */ + int _e_pbnum; /* base num. of args. used */ + int _e_ppos; /* number of arguments used */ + int _e_pnummax; /* max. number of arguments */ + int _e_psize; /* number of bytes of args. */ + int _e_pbsize; /* base num. of bytes of args */ + int _e_psizemax; /* max num. of bytes of args */ + struct _plandata *_e_next;/* next F_EXECPLUS in tree */ + } ex; + char *_a_data[2]; /* array of char pointers */ + char *_c_data; /* char pointer */ + regex_t *_re_data; /* regex */ + } p_un; +} PLAN; +#define a_data p_un._a_data +#define c_data p_un._c_data +#define d_data p_un._d_data +#define fl_flags p_un.fl._f_flags +#define fl_notflags p_un.fl._f_notflags +#define g_data p_un._g_data +#define i_data p_un._i_data +#define l_data p_un._l_data +#define m_data p_un._m_data +#define mt_data p_un._mt_data +#define o_data p_un._o_data +#define p_data p_un._p_data +#define t_data p_un._t_data +#define u_data p_un._u_data +#define re_data p_un._re_data +#define e_argv p_un.ex._e_argv +#define e_orig p_un.ex._e_orig +#define e_len p_un.ex._e_len +#define e_pbnum p_un.ex._e_pbnum +#define e_ppos p_un.ex._e_ppos +#define e_pnummax p_un.ex._e_pnummax +#define e_psize p_un.ex._e_psize +#define e_pbsize p_un.ex._e_pbsize +#define e_psizemax p_un.ex._e_psizemax +#define e_next p_un.ex._e_next + +typedef struct _option { + const char *name; /* option name */ + creat_f *create; /* create function */ + exec_f *execute; /* execute function */ + int flags; +} OPTION; + +#include "extern.h" diff --git a/shell_cmds/find/find.plist.part b/shell_cmds/find/find.plist.part new file mode 100644 index 0000000..0148f95 --- /dev/null +++ b/shell_cmds/find/find.plist.part @@ -0,0 +1,30 @@ + <dict> + <key>OpenSourceProject</key> + <string>find</string> + <key>OpenSourceVersion</key> + <string>2011-12-10</string> + <key>OpenSourceWebsiteURL</key> + <string>http://svnweb.freebsd.org/base/head/usr.bin/find/</string> + <key>OpenSourceSCM</key> + <string>svn co http://svn.freebsd.org/base/head/usr.bin/find/</string> + <key>OpenSourceImportDate</key> + <string>2012-01-06</string> + <key>OpenSourceModifications</key> + <array> + <string>Add -xattr and -xattrname options.</string> + <string>execplus conformance fixes.</string> + <string>Man page fix (4772561).</string> + <string>Adjust FTS usage due to API differences.</string> + <string>Avoid searching unsearchable directories (3849245).</string> + <string>Add missing call to fts_close (4608460).</string> + <string>Conformance fixes related to F_EXACTTIME.</string> + <string>Apple-specific implementation of -acl primary.</string> + <string>Conformance fix for f_name (6591280).</string> + <string>setmode conformance fix (3936046).</string> + <string>Use a saner MAXLOGNAME definition.</string> + <string>Workaround for lack of rpmatch function.</string> + <string>FreeBSD revisions 260336, 260355.</string> + </array> + <key>OpenSourceLicense</key> + <string>bsd</string> + </dict> diff --git a/shell_cmds/find/function.c b/shell_cmds/find/function.c new file mode 100644 index 0000000..a673a02 --- /dev/null +++ b/shell_cmds/find/function.c @@ -0,0 +1,1772 @@ +/*- + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Cimarron D. Taylor of the University of California, Berkeley. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +#if 0 +static const char sccsid[] = "@(#)function.c 8.10 (Berkeley) 5/4/95"; +#endif +#endif /* not lint */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD: src/usr.bin/find/function.c,v 1.71 2011/06/13 05:22:07 avatar Exp $"); + +#include <sys/param.h> +#include <sys/ucred.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <sys/acl.h> +#include <sys/wait.h> +#include <sys/mount.h> + +#include <dirent.h> +#include <err.h> +#include <errno.h> +#include <fnmatch.h> +#include <fts.h> +#include <grp.h> +#include <limits.h> +#include <pwd.h> +#include <regex.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <ctype.h> + +#ifdef __APPLE__ +#include <sys/sysctl.h> +#include <sys/xattr.h> +#include <libgen.h> +#include <get_compat.h> +#else +#define COMPAT_MODE(func, mode) 1 +#endif + +#include "find.h" + +static PLAN *palloc(OPTION *); +static long long find_parsenum(PLAN *, const char *, char *, char *); +static long long find_parsetime(PLAN *, const char *, char *); +static char *nextarg(OPTION *, char ***); + +extern char **environ; + +static PLAN *lastexecplus = NULL; +int execplus_error; + +#define COMPARE(a, b) do { \ + switch (plan->flags & F_ELG_MASK) { \ + case F_EQUAL: \ + return (a == b); \ + case F_LESSTHAN: \ + return (a < b); \ + case F_GREATER: \ + return (a > b); \ + default: \ + abort(); \ + } \ +} while(0) + +static PLAN * +palloc(OPTION *option) +{ + PLAN *new; + + if ((new = malloc(sizeof(PLAN))) == NULL) + err(1, NULL); + new->execute = option->execute; + new->flags = option->flags; + new->next = NULL; + return new; +} + +/* + * find_parsenum -- + * Parse a string of the form [+-]# and return the value. + */ +static long long +find_parsenum(PLAN *plan, const char *option, char *vp, char *endch) +{ + long long value; + char *endchar, *str; /* Pointer to character ending conversion. */ + + /* Determine comparison from leading + or -. */ + str = vp; + switch (*str) { + case '+': + ++str; + plan->flags |= F_GREATER; + break; + case '-': + ++str; + plan->flags |= F_LESSTHAN; + break; + default: + plan->flags |= F_EQUAL; + break; + } + + /* + * Convert the string with strtoq(). Note, if strtoq() returns zero + * and endchar points to the beginning of the string we know we have + * a syntax error. + */ + value = strtoq(str, &endchar, 10); + if (value == 0 && endchar == str) + errx(1, "%s: %s: illegal numeric value", option, vp); + if (endchar[0] && endch == NULL) + errx(1, "%s: %s: illegal trailing character", option, vp); + if (endch) + *endch = endchar[0]; + return value; +} + +/* + * find_parsetime -- + * Parse a string of the form [+-]([0-9]+[smhdw]?)+ and return the value. + */ +static long long +find_parsetime(PLAN *plan, const char *option, char *vp) +{ + long long secs, value; + char *str, *unit; /* Pointer to character ending conversion. */ + + /* Determine comparison from leading + or -. */ + str = vp; + switch (*str) { + case '+': + ++str; + plan->flags |= F_GREATER; + break; + case '-': + ++str; + plan->flags |= F_LESSTHAN; + break; + default: + plan->flags |= F_EQUAL; + break; + } + + value = strtoq(str, &unit, 10); + if (value == 0 && unit == str) { + errx(1, "%s: %s: illegal time value", option, vp); + /* NOTREACHED */ + } + if (*unit == '\0') + return value; + + /* Units syntax. */ + secs = 0; + for (;;) { + switch(*unit) { + case 's': /* seconds */ + secs += value; + break; + case 'm': /* minutes */ + secs += value * 60; + break; + case 'h': /* hours */ + secs += value * 3600; + break; + case 'd': /* days */ + secs += value * 86400; + break; + case 'w': /* weeks */ + secs += value * 604800; + break; + default: + errx(1, "%s: %s: bad unit '%c'", option, vp, *unit); + /* NOTREACHED */ + } + str = unit + 1; + if (*str == '\0') /* EOS */ + break; + value = strtoq(str, &unit, 10); + if (value == 0 && unit == str) { + errx(1, "%s: %s: illegal time value", option, vp); + /* NOTREACHED */ + } + if (*unit == '\0') { + errx(1, "%s: %s: missing trailing unit", option, vp); + /* NOTREACHED */ + } + } + plan->flags |= F_EXACTTIME; + return secs; +} + +/* + * nextarg -- + * Check that another argument still exists, return a pointer to it, + * and increment the argument vector pointer. + */ +static char * +nextarg(OPTION *option, char ***argvp) +{ + char *arg; + + if ((arg = **argvp) == 0) + errx(1, "%s: requires additional arguments", option->name); + (*argvp)++; + return arg; +} /* nextarg() */ + +/* + * The value of n for the inode times (atime, birthtime, ctime, mtime) is a + * range, i.e. n matches from (n - 1) to n 24 hour periods. This interacts + * with -n, such that "-mtime -1" would be less than 0 days, which isn't what + * the user wanted. Correct so that -1 is "less than 1". + */ +#define TIME_CORRECT(p) \ + if (((p)->flags & F_ELG_MASK) == F_LESSTHAN) \ + ++((p)->t_data); + +/* + * -[acm]min n functions -- + * + * True if the difference between the + * file access time (-amin) + * file birth time (-Bmin) + * last change of file status information (-cmin) + * file modification time (-mmin) + * and the current time is n min periods. + */ +int +f_Xmin(PLAN *plan, FTSENT *entry) +{ + if (plan->flags & F_TIME_C) { + COMPARE((now - entry->fts_statp->st_ctime + + 60 - 1) / 60, plan->t_data); + } else if (plan->flags & F_TIME_A) { + COMPARE((now - entry->fts_statp->st_atime + + 60 - 1) / 60, plan->t_data); + } else if (plan->flags & F_TIME_B) { + COMPARE((now - entry->fts_statp->st_birthtime + + 60 - 1) / 60, plan->t_data); + } else { + COMPARE((now - entry->fts_statp->st_mtime + + 60 - 1) / 60, plan->t_data); + } +} + +PLAN * +c_Xmin(OPTION *option, char ***argvp) +{ + char *nmins; + PLAN *new; + + nmins = nextarg(option, argvp); + ftsoptions &= ~FTS_NOSTAT; + + new = palloc(option); + new->t_data = find_parsenum(new, option->name, nmins, NULL); + TIME_CORRECT(new); + return new; +} + +/* + * -[acm]time n functions -- + * + * True if the difference between the + * file access time (-atime) + * file birth time (-Btime) + * last change of file status information (-ctime) + * file modification time (-mtime) + * and the current time is n 24 hour periods. + */ + +int +f_Xtime(PLAN *plan, FTSENT *entry) +{ + time_t xtime; + + if (plan->flags & F_TIME_A) + xtime = entry->fts_statp->st_atime; + else if (plan->flags & F_TIME_B) + xtime = entry->fts_statp->st_birthtime; + else if (plan->flags & F_TIME_C) + xtime = entry->fts_statp->st_ctime; + else + xtime = entry->fts_statp->st_mtime; + + if (plan->flags & F_EXACTTIME) + COMPARE(now - xtime, plan->t_data); + else + COMPARE((now - xtime + (COMPAT_MODE("bin/find", "unix2003") ? 0 : 86400 - 1)) / 86400, plan->t_data); +} + +PLAN * +c_Xtime(OPTION *option, char ***argvp) +{ + char *value; + PLAN *new; + + value = nextarg(option, argvp); + ftsoptions &= ~FTS_NOSTAT; + + new = palloc(option); + new->t_data = find_parsetime(new, option->name, value); + if (!(new->flags & F_EXACTTIME) && !COMPAT_MODE("bin/find", "unix2003")) + TIME_CORRECT(new); + return new; +} + +/* + * -maxdepth/-mindepth n functions -- + * + * Does the same as -prune if the level of the current file is + * greater/less than the specified maximum/minimum depth. + * + * Note that -maxdepth and -mindepth are handled specially in + * find_execute() so their f_* functions are set to f_always_true(). + */ +PLAN * +c_mXXdepth(OPTION *option, char ***argvp) +{ + char *dstr; + PLAN *new; + + dstr = nextarg(option, argvp); + if (dstr[0] == '-') + /* all other errors handled by find_parsenum() */ + errx(1, "%s: %s: value must be positive", option->name, dstr); + + new = palloc(option); + if (option->flags & F_MAXDEPTH) + maxdepth = find_parsenum(new, option->name, dstr, NULL); + else + mindepth = find_parsenum(new, option->name, dstr, NULL); + return new; +} + +/* + * -acl function -- + * + * Show files with EXTENDED ACL attributes. + */ +#ifdef __APPLE__ +int +f_acl(PLAN *plan __unused, FTSENT *entry) +{ + acl_t facl; + int match; + acl_entry_t ae; + + match = 0; + if ((facl = acl_get_link_np(entry->fts_accpath, ACL_TYPE_EXTENDED)) != NULL) { + if (acl_get_entry(facl, ACL_FIRST_ENTRY, &ae) == 0) { + match = 1; + } + acl_free(facl); + } + return match; +} +#else /* !__APPLE__ */ +int +f_acl(PLAN *plan __unused, FTSENT *entry) +{ + acl_t facl; + acl_type_t acl_type; + int acl_supported = 0, ret, trivial; + + if (S_ISLNK(entry->fts_statp->st_mode)) + return 0; + ret = pathconf(entry->fts_accpath, _PC_ACL_NFS4); + if (ret > 0) { + acl_supported = 1; + acl_type = ACL_TYPE_NFS4; + } else if (ret < 0 && errno != EINVAL) { + warn("%s", entry->fts_accpath); + return (0); + } + if (acl_supported == 0) { + ret = pathconf(entry->fts_accpath, _PC_ACL_EXTENDED); + if (ret > 0) { + acl_supported = 1; + acl_type = ACL_TYPE_ACCESS; + } else if (ret < 0 && errno != EINVAL) { + warn("%s", entry->fts_accpath); + return (0); + } + } + if (acl_supported == 0) + return (0); + + facl = acl_get_file(entry->fts_accpath, acl_type); + if (facl == NULL) { + warn("%s", entry->fts_accpath); + return (0); + } + ret = acl_is_trivial_np(facl, &trivial); + acl_free(facl); + if (ret) { + warn("%s", entry->fts_accpath); + acl_free(facl); + return (0); + } + if (trivial) + return (0); + return (1); +} +#endif /* __APPLE__ */ + +PLAN * +c_acl(OPTION *option, char ***argvp __unused) +{ +#ifndef __APPLE__ + ftsoptions &= ~FTS_NOSTAT; +#endif /* !__APPLE__ */ + return (palloc(option)); +} + +#ifdef __APPLE__ +int +f_xattr(PLAN *plan __unused, FTSENT *entry) +{ + ssize_t xattr; + int match; + + match = 0; + xattr = listxattr(entry->fts_accpath, NULL, 0, XATTR_NOFOLLOW); + if (xattr > 0) { + match = 1; + } + return match; +} + +int +f_xattrname(PLAN *plan, FTSENT *entry) +{ + ssize_t xattr; + int match; + + match = 0; + xattr = getxattr(entry->fts_accpath, plan->c_data, NULL, 0, 0, XATTR_NOFOLLOW); + if (xattr > 0) { + match = 1; + } + return match; +} +#endif /* __APPLE__ */ + +/* + * -delete functions -- + * + * True always. Makes its best shot and continues on regardless. + */ +int +f_delete(PLAN *plan __unused, FTSENT *entry) +{ + /* ignore these from fts */ + if (strcmp(entry->fts_accpath, ".") == 0 || + strcmp(entry->fts_accpath, "..") == 0) + return 1; + + /* sanity check */ + if (isdepth == 0 || /* depth off */ + (ftsoptions & FTS_NOSTAT)) /* not stat()ing */ + errx(1, "-delete: insecure options got turned on"); + + if (!(ftsoptions & FTS_PHYSICAL) || /* physical off */ + (ftsoptions & FTS_LOGICAL)) /* or finally, logical on */ + errx(1, "-delete: forbidden when symlinks are followed"); + + /* Potentially unsafe - do not accept relative paths whatsoever */ + if (strchr(entry->fts_accpath, '/') != NULL) + errx(1, "-delete: %s: relative path potentially not safe", + entry->fts_accpath); + + /* Turn off user immutable bits if running as root */ + if ((entry->fts_statp->st_flags & (UF_APPEND|UF_IMMUTABLE)) && + !(entry->fts_statp->st_flags & (SF_APPEND|SF_IMMUTABLE)) && + geteuid() == 0) + lchflags(entry->fts_accpath, + entry->fts_statp->st_flags &= ~(UF_APPEND|UF_IMMUTABLE)); + + /* rmdir directories, unlink everything else */ + if (S_ISDIR(entry->fts_statp->st_mode)) { + if (rmdir(entry->fts_accpath) < 0 && errno != ENOTEMPTY) + warn("-delete: rmdir(%s)", entry->fts_path); + } else { + if (unlink(entry->fts_accpath) < 0) + warn("-delete: unlink(%s)", entry->fts_path); + } + + /* "succeed" */ + return 1; +} + +PLAN * +c_delete(OPTION *option, char ***argvp __unused) +{ + + ftsoptions &= ~FTS_NOSTAT; /* no optimise */ + isoutput = 1; /* possible output */ + isdepth = 1; /* -depth implied */ + + return palloc(option); +} + + +/* + * always_true -- + * + * Always true, used for -maxdepth, -mindepth, -xdev, -follow, and -true + */ +int +f_always_true(PLAN *plan __unused, FTSENT *entry __unused) +{ + return 1; +} + +/* + * -depth functions -- + * + * With argument: True if the file is at level n. + * Without argument: Always true, causes descent of the directory hierarchy + * to be done so that all entries in a directory are acted on before the + * directory itself. + */ +int +f_depth(PLAN *plan, FTSENT *entry) +{ + if (plan->flags & F_DEPTH) + COMPARE(entry->fts_level, plan->d_data); + else + return 1; +} + +PLAN * +c_depth(OPTION *option, char ***argvp) +{ + PLAN *new; + char *str; + + new = palloc(option); + + str = **argvp; + if (str && !(new->flags & F_DEPTH)) { + /* skip leading + or - */ + if (*str == '+' || *str == '-') + str++; + /* skip sign */ + if (*str == '+' || *str == '-') + str++; + if (isdigit(*str)) + new->flags |= F_DEPTH; + } + + if (new->flags & F_DEPTH) { /* -depth n */ + char *ndepth; + + ndepth = nextarg(option, argvp); + new->d_data = find_parsenum(new, option->name, ndepth, NULL); + } else { /* -d */ + isdepth = 1; + } + + return new; +} + +/* + * -empty functions -- + * + * True if the file or directory is empty + */ +int +f_empty(PLAN *plan __unused, FTSENT *entry) +{ + if (S_ISREG(entry->fts_statp->st_mode) && + entry->fts_statp->st_size == 0) + return 1; + if (S_ISDIR(entry->fts_statp->st_mode)) { + struct dirent *dp; + int empty; + DIR *dir; + + empty = 1; + dir = opendir(entry->fts_accpath); + if (dir == NULL) + return 0; + for (dp = readdir(dir); dp; dp = readdir(dir)) + if (dp->d_name[0] != '.' || + (dp->d_name[1] != '\0' && + (dp->d_name[1] != '.' || dp->d_name[2] != '\0'))) { + empty = 0; + break; + } + closedir(dir); + return empty; + } + return 0; +} + +PLAN * +c_empty(OPTION *option, char ***argvp __unused) +{ + ftsoptions &= ~FTS_NOSTAT; + + return palloc(option); +} + +/* + * [-exec | -execdir | -ok] utility [arg ... ] ; functions -- + * + * True if the executed utility returns a zero value as exit status. + * The end of the primary expression is delimited by a semicolon. If + * "{}" occurs anywhere, it gets replaced by the current pathname, + * or, in the case of -execdir, the current basename (filename + * without leading directory prefix). For -exec and -ok, + * the current directory for the execution of utility is the same as + * the current directory when the find utility was started, whereas + * for -execdir, it is the directory the file resides in. + * + * The primary -ok differs from -exec in that it requests affirmation + * of the user before executing the utility. + */ +int +f_exec(PLAN *plan, FTSENT *entry) +{ + int cnt; + pid_t pid; + int status; + char *file; + + if (entry == NULL && plan->flags & F_EXECPLUS) { + if (plan->e_ppos == plan->e_pbnum) + return (1); + plan->e_argv[plan->e_ppos] = NULL; + goto doexec; + } + + /* XXX - if file/dir ends in '/' this will not work -- can it? */ + if ((plan->flags & F_EXECDIR) && \ + (file = strrchr(entry->fts_path, '/'))) + file++; + else + file = entry->fts_path; + + if (plan->flags & F_EXECPLUS) { + if ((plan->e_argv[plan->e_ppos] = strdup(file)) == NULL) + err(1, NULL); + plan->e_len[plan->e_ppos] = strlen(file); + plan->e_psize += plan->e_len[plan->e_ppos]; + if (++plan->e_ppos < plan->e_pnummax && + plan->e_psize < plan->e_psizemax) + return (1); + plan->e_argv[plan->e_ppos] = NULL; + } else { + for (cnt = 0; plan->e_argv[cnt]; ++cnt) + if (plan->e_len[cnt]) + brace_subst(plan->e_orig[cnt], + &plan->e_argv[cnt], file, + plan->e_len[cnt]); + } + +doexec: if ((plan->flags & F_NEEDOK) && !queryuser(plan->e_argv)) + return 0; + + /* make sure find output is interspersed correctly with subprocesses */ + fflush(stdout); + fflush(stderr); + + switch (pid = fork()) { + case -1: + err(1, "fork"); + /* NOTREACHED */ + case 0: + /* change dir back from where we started */ + if (!(plan->flags & F_EXECDIR) && fchdir(dotfd)) { + warn("chdir"); + _exit(1); + } + execvp(plan->e_argv[0], plan->e_argv); + warn("%s", plan->e_argv[0]); + _exit(1); + } + if (plan->flags & F_EXECPLUS) { + while (--plan->e_ppos >= plan->e_pbnum) + free(plan->e_argv[plan->e_ppos]); + plan->e_ppos = plan->e_pbnum; + plan->e_psize = plan->e_pbsize; + } + pid = waitpid(pid, &status, 0); + if (plan->flags & F_EXECPLUS && WIFEXITED(status) && WEXITSTATUS(status) && !execplus_error) { + /* Test 140 (8907531, 10656525) */ + execplus_error = WEXITSTATUS(status); + } + return (pid != -1 && WIFEXITED(status) && !WEXITSTATUS(status)); +} + +/* + * c_exec, c_execdir, c_ok -- + * build three parallel arrays, one with pointers to the strings passed + * on the command line, one with (possibly duplicated) pointers to the + * argv array, and one with integer values that are lengths of the + * strings, but also flags meaning that the string has to be massaged. + */ +PLAN * +c_exec(OPTION *option, char ***argvp) +{ + PLAN *new; /* node returned */ + long argmax; + int cnt, i; + char **argv, **ap, **ep, *p; + + /* XXX - was in c_execdir, but seems unnecessary!? + ftsoptions &= ~FTS_NOSTAT; + */ + isoutput = 1; + + /* XXX - this is a change from the previous coding */ + new = palloc(option); + + for (ap = argv = *argvp;; ++ap) { + if (!*ap) + errx(1, + "%s: no terminating \";\" or \"+\"", option->name); + if (**ap == ';') + break; + if (**ap == '+' && ap != argv && strcmp(*(ap - 1), "{}") == 0) { + new->flags |= F_EXECPLUS; + break; + } + } + + if (ap == argv) + errx(1, "%s: no command specified", option->name); + + cnt = ap - *argvp + 1; + if (new->flags & F_EXECPLUS) { + new->e_ppos = new->e_pbnum = cnt - 2; + if ((argmax = sysconf(_SC_ARG_MAX)) == -1) { + warn("sysconf(_SC_ARG_MAX)"); + argmax = _POSIX_ARG_MAX; + } + argmax -= 1024; + for (ep = environ; *ep != NULL; ep++) + argmax -= strlen(*ep) + 1 + sizeof(*ep); + argmax -= 1 + sizeof(*ep); + new->e_pnummax = argmax / 16; + argmax -= sizeof(char *) * new->e_pnummax; + if (argmax <= 0) + errx(1, "no space for arguments"); + new->e_psizemax = argmax; + new->e_pbsize = 0; + cnt += new->e_pnummax + 1; + new->e_next = lastexecplus; + lastexecplus = new; + } + if ((new->e_argv = malloc(cnt * sizeof(char *))) == NULL) + err(1, NULL); + if ((new->e_orig = malloc(cnt * sizeof(char *))) == NULL) + err(1, NULL); + if ((new->e_len = malloc(cnt * sizeof(int))) == NULL) + err(1, NULL); + + for (argv = *argvp, cnt = 0; argv < ap; ++argv, ++cnt) { + new->e_orig[cnt] = *argv; + if (new->flags & F_EXECPLUS) + new->e_pbsize += strlen(*argv) + 1; + for (p = *argv; *p; ++p) + if (!(new->flags & F_EXECPLUS) && p[0] == '{' && + p[1] == '}') { + if ((new->e_argv[cnt] = + malloc(MAXPATHLEN)) == NULL) + err(1, NULL); + new->e_len[cnt] = MAXPATHLEN; + break; + } + if (!*p) { + new->e_argv[cnt] = *argv; + new->e_len[cnt] = 0; + } + } + if (new->flags & F_EXECPLUS) { + new->e_psize = new->e_pbsize; + cnt--; + for (i = 0; i < new->e_pnummax; i++) { + new->e_argv[cnt] = NULL; + new->e_len[cnt] = 0; + cnt++; + } + argv = ap; + goto done; + } + new->e_argv[cnt] = new->e_orig[cnt] = NULL; + +done: *argvp = argv + 1; + return new; +} + +/* Finish any pending -exec ... {} + functions. */ +void +finish_execplus(void) +{ + PLAN *p; + + p = lastexecplus; + while (p != NULL) { + (p->execute)(p, NULL); + p = p->e_next; + } +} + +int +f_flags(PLAN *plan, FTSENT *entry) +{ + u_long flags; + + flags = entry->fts_statp->st_flags; + if (plan->flags & F_ATLEAST) + return (flags | plan->fl_flags) == flags && + !(flags & plan->fl_notflags); + else if (plan->flags & F_ANY) + return (flags & plan->fl_flags) || + (flags | plan->fl_notflags) != flags; + else + return flags == plan->fl_flags && + !(plan->fl_flags & plan->fl_notflags); +} + +PLAN * +c_flags(OPTION *option, char ***argvp) +{ + char *flags_str; + PLAN *new; + u_long flags, notflags; + + flags_str = nextarg(option, argvp); + ftsoptions &= ~FTS_NOSTAT; + + new = palloc(option); + + if (*flags_str == '-') { + new->flags |= F_ATLEAST; + flags_str++; + } else if (*flags_str == '+') { + new->flags |= F_ANY; + flags_str++; + } + if (strtofflags(&flags_str, &flags, ¬flags) == 1) + errx(1, "%s: %s: illegal flags string", option->name, flags_str); + + new->fl_flags = flags; + new->fl_notflags = notflags; + return new; +} + +/* + * -follow functions -- + * + * Always true, causes symbolic links to be followed on a global + * basis. + */ +PLAN * +c_follow(OPTION *option, char ***argvp __unused) +{ + ftsoptions &= ~FTS_PHYSICAL; + ftsoptions |= FTS_LOGICAL; + + return palloc(option); +} + +/* + * -fstype functions -- + * + * True if the file is of a certain type. + */ +int +f_fstype(PLAN *plan, FTSENT *entry) +{ + static dev_t curdev; /* need a guaranteed illegal dev value */ + static int first = 1; + struct statfs sb; + static int val_flags; + static char fstype[sizeof(sb.f_fstypename)]; + char *p, save[2] = {0,0}; + + if ((plan->flags & F_MTMASK) == F_MTUNKNOWN) + return 0; + + /* Only check when we cross mount point. */ + if (first || curdev != entry->fts_statp->st_dev) { + curdev = entry->fts_statp->st_dev; + + /* + * Statfs follows symlinks; find wants the link's filesystem, + * not where it points. + */ + if (entry->fts_info == FTS_SL || + entry->fts_info == FTS_SLNONE) { + if ((p = strrchr(entry->fts_accpath, '/')) != NULL) + ++p; + else + p = entry->fts_accpath; + save[0] = p[0]; + p[0] = '.'; + save[1] = p[1]; + p[1] = '\0'; + } else + p = NULL; + + if (statfs(entry->fts_accpath, &sb)) + err(1, "%s", entry->fts_accpath); + + if (p) { + p[0] = save[0]; + p[1] = save[1]; + } + + first = 0; + + /* + * Further tests may need both of these values, so + * always copy both of them. + */ + val_flags = sb.f_flags; + strlcpy(fstype, sb.f_fstypename, sizeof(fstype)); + } + switch (plan->flags & F_MTMASK) { + case F_MTFLAG: + return val_flags & plan->mt_data; + case F_MTTYPE: + return (strncmp(fstype, plan->c_data, sizeof(fstype)) == 0); + default: + abort(); + } +} + +PLAN * +c_fstype(OPTION *option, char ***argvp) +{ + char *fsname; + PLAN *new; + + fsname = nextarg(option, argvp); + ftsoptions &= ~FTS_NOSTAT; + + new = palloc(option); + switch (*fsname) { + case 'l': + if (!strcmp(fsname, "local")) { + new->flags |= F_MTFLAG; + new->mt_data = MNT_LOCAL; + return new; + } + break; + case 'r': + if (!strcmp(fsname, "rdonly")) { + new->flags |= F_MTFLAG; + new->mt_data = MNT_RDONLY; + return new; + } + break; + } + + new->flags |= F_MTTYPE; + new->c_data = fsname; + return new; +} + +/* + * -group gname functions -- + * + * True if the file belongs to the group gname. If gname is numeric and + * an equivalent of the getgrnam() function does not return a valid group + * name, gname is taken as a group ID. + */ +int +f_group(PLAN *plan, FTSENT *entry) +{ + COMPARE(entry->fts_statp->st_gid, plan->g_data); +} + +PLAN * +c_group(OPTION *option, char ***argvp) +{ + char *gname; + PLAN *new; + struct group *g; + gid_t gid; + + gname = nextarg(option, argvp); + ftsoptions &= ~FTS_NOSTAT; + + new = palloc(option); + g = getgrnam(gname); + if (g == NULL) { + char* cp = gname; + if (gname[0] == '-' || gname[0] == '+') + gname++; + gid = atoi(gname); + if (gid == 0 && gname[0] != '0') + errx(1, "%s: %s: no such group", option->name, gname); + gid = find_parsenum(new, option->name, cp, NULL); + } else + gid = g->gr_gid; + + new->g_data = gid; + return new; +} + +/* + * -inum n functions -- + * + * True if the file has inode # n. + */ +int +f_inum(PLAN *plan, FTSENT *entry) +{ + COMPARE(entry->fts_statp->st_ino, plan->i_data); +} + +PLAN * +c_inum(OPTION *option, char ***argvp) +{ + char *inum_str; + PLAN *new; + + inum_str = nextarg(option, argvp); + ftsoptions &= ~FTS_NOSTAT; + + new = palloc(option); + new->i_data = find_parsenum(new, option->name, inum_str, NULL); + return new; +} + +/* + * -samefile FN + * + * True if the file has the same inode (eg hard link) FN + */ + +/* f_samefile is just f_inum */ +PLAN * +c_samefile(OPTION *option, char ***argvp) +{ + char *fn; + PLAN *new; + struct stat sb; + + fn = nextarg(option, argvp); + ftsoptions &= ~FTS_NOSTAT; + + new = palloc(option); + if (stat(fn, &sb)) + err(1, "%s", fn); + new->i_data = sb.st_ino; + return new; +} + +/* + * -links n functions -- + * + * True if the file has n links. + */ +int +f_links(PLAN *plan, FTSENT *entry) +{ + COMPARE(entry->fts_statp->st_nlink, plan->l_data); +} + +PLAN * +c_links(OPTION *option, char ***argvp) +{ + char *nlinks; + PLAN *new; + + nlinks = nextarg(option, argvp); + ftsoptions &= ~FTS_NOSTAT; + + new = palloc(option); + new->l_data = (nlink_t)find_parsenum(new, option->name, nlinks, NULL); + return new; +} + +/* + * -ls functions -- + * + * Always true - prints the current entry to stdout in "ls" format. + */ +int +f_ls(PLAN *plan __unused, FTSENT *entry) +{ + printlong(entry->fts_path, entry->fts_accpath, entry->fts_statp); + return 1; +} + +PLAN * +c_ls(OPTION *option, char ***argvp __unused) +{ + ftsoptions &= ~FTS_NOSTAT; + isoutput = 1; + + return palloc(option); +} + +/* + * -name functions -- + * + * True if the basename of the filename being examined + * matches pattern using Pattern Matching Notation S3.14 + */ +int +f_name(PLAN *plan, FTSENT *entry) +{ + char fn[PATH_MAX]; + const char *name; + ssize_t len; + + if (plan->flags & F_LINK) { + /* + * The below test both avoids obviously useless readlink() + * calls and ensures that symlinks with existent target do + * not match if symlinks are being followed. + * Assumption: fts will stat all symlinks that are to be + * followed and will return the stat information. + */ + if (entry->fts_info != FTS_NSOK && entry->fts_info != FTS_SL && + entry->fts_info != FTS_SLNONE) + return 0; + len = readlink(entry->fts_accpath, fn, sizeof(fn) - 1); + if (len == -1) + return 0; + fn[len] = '\0'; + name = fn; + } else if (entry->fts_namelen == 0) { + name = basename(entry->fts_path); + } else + name = entry->fts_name; + return !fnmatch(plan->c_data, name, + plan->flags & F_IGNCASE ? FNM_CASEFOLD : 0); +} + +PLAN * +c_name(OPTION *option, char ***argvp) +{ + char *pattern; + PLAN *new; + + pattern = nextarg(option, argvp); + new = palloc(option); + new->c_data = pattern; + return new; +} + +/* + * -newer file functions -- + * + * True if the current file has been modified more recently + * then the modification time of the file named by the pathname + * file. + */ +int +f_newer(PLAN *plan, FTSENT *entry) +{ + if (plan->flags & F_TIME_C) + return entry->fts_statp->st_ctime > plan->t_data; + else if (plan->flags & F_TIME_A) + return entry->fts_statp->st_atime > plan->t_data; + else if (plan->flags & F_TIME_B) + return entry->fts_statp->st_birthtime > plan->t_data; + else + return entry->fts_statp->st_mtime > plan->t_data; +} + +PLAN * +c_newer(OPTION *option, char ***argvp) +{ + char *fn_or_tspec; + PLAN *new; + struct stat sb; + + fn_or_tspec = nextarg(option, argvp); + ftsoptions &= ~FTS_NOSTAT; + + new = palloc(option); + /* compare against what */ + if (option->flags & F_TIME2_T) { + new->t_data = get_date(fn_or_tspec); + if (new->t_data == (time_t) -1) + errx(1, "Can't parse date/time: %s", fn_or_tspec); + } else { + if (stat(fn_or_tspec, &sb)) + err(1, "%s", fn_or_tspec); + if (option->flags & F_TIME2_C) + new->t_data = sb.st_ctime; + else if (option->flags & F_TIME2_A) + new->t_data = sb.st_atime; + else if (option->flags & F_TIME2_B) + new->t_data = sb.st_birthtime; + else + new->t_data = sb.st_mtime; + } + return new; +} + +/* + * -nogroup functions -- + * + * True if file belongs to a user ID for which the equivalent + * of the getgrnam() 9.2.1 [POSIX.1] function returns NULL. + */ +int +f_nogroup(PLAN *plan __unused, FTSENT *entry) +{ + return group_from_gid(entry->fts_statp->st_gid, 1) == NULL; +} + +PLAN * +c_nogroup(OPTION *option, char ***argvp __unused) +{ + ftsoptions &= ~FTS_NOSTAT; + + return palloc(option); +} + +/* + * -nouser functions -- + * + * True if file belongs to a user ID for which the equivalent + * of the getpwuid() 9.2.2 [POSIX.1] function returns NULL. + */ +int +f_nouser(PLAN *plan __unused, FTSENT *entry) +{ + return user_from_uid(entry->fts_statp->st_uid, 1) == NULL; +} + +PLAN * +c_nouser(OPTION *option, char ***argvp __unused) +{ + ftsoptions &= ~FTS_NOSTAT; + + return palloc(option); +} + +/* + * -path functions -- + * + * True if the path of the filename being examined + * matches pattern using Pattern Matching Notation S3.14 + */ +int +f_path(PLAN *plan, FTSENT *entry) +{ + return !fnmatch(plan->c_data, entry->fts_path, + plan->flags & F_IGNCASE ? FNM_CASEFOLD : 0); +} + +/* c_path is the same as c_name */ + +/* + * -perm functions -- + * + * The mode argument is used to represent file mode bits. If it starts + * with a leading digit, it's treated as an octal mode, otherwise as a + * symbolic mode. + */ +int +f_perm(PLAN *plan, FTSENT *entry) +{ + mode_t mode; + + mode = entry->fts_statp->st_mode & + (S_ISUID|S_ISGID|S_ISTXT|S_IRWXU|S_IRWXG|S_IRWXO); + if (plan->flags & F_ATLEAST) + return (plan->m_data | mode) == mode; + else if (plan->flags & F_ANY) + return (mode & plan->m_data); + else + return mode == plan->m_data; + /* NOTREACHED */ +} + +PLAN * +c_perm(OPTION *option, char ***argvp) +{ + char *perm; + PLAN *new; + mode_t *set; + + perm = nextarg(option, argvp); + ftsoptions &= ~FTS_NOSTAT; + + new = palloc(option); + + if (*perm == '-') { + new->flags |= F_ATLEAST; + ++perm; + } else if (*perm == '+') { + if ((set = setmode(perm + 1)) != NULL) { + new->flags |= F_ANY; + ++perm; + free(set); + } + } + + if ((set = setmode(perm)) == NULL) + errx(1, "%s: %s: illegal mode string", option->name, perm); + + new->m_data = getmode(set, 0); + free(set); + return new; +} + +/* + * -print functions -- + * + * Always true, causes the current pathname to be written to + * standard output. + */ +int +f_print(PLAN *plan __unused, FTSENT *entry) +{ + (void)puts(entry->fts_path); + return 1; +} + +PLAN * +c_print(OPTION *option, char ***argvp __unused) +{ + isoutput = 1; + + return palloc(option); +} + +/* + * -print0 functions -- + * + * Always true, causes the current pathname to be written to + * standard output followed by a NUL character + */ +int +f_print0(PLAN *plan __unused, FTSENT *entry) +{ + fputs(entry->fts_path, stdout); + fputc('\0', stdout); + return 1; +} + +/* c_print0 is the same as c_print */ + +/* + * -prune functions -- + * + * Prune a portion of the hierarchy. + */ +int +f_prune(PLAN *plan __unused, FTSENT *entry) +{ + if (fts_set(tree, entry, FTS_SKIP)) + err(1, "%s", entry->fts_path); + return 1; +} + +/* c_prune == c_simple */ + +/* + * -regex functions -- + * + * True if the whole path of the file matches pattern using + * regular expression. + */ +int +f_regex(PLAN *plan, FTSENT *entry) +{ + char *str; + int len; + regex_t *pre; + regmatch_t pmatch; + int errcode; + char errbuf[LINE_MAX]; + int matched; + + pre = plan->re_data; + str = entry->fts_path; + len = strlen(str); + matched = 0; + + pmatch.rm_so = 0; + pmatch.rm_eo = len; + + errcode = regexec(pre, str, 1, &pmatch, REG_STARTEND); + + if (errcode != 0 && errcode != REG_NOMATCH) { + regerror(errcode, pre, errbuf, sizeof errbuf); + errx(1, "%s: %s", + plan->flags & F_IGNCASE ? "-iregex" : "-regex", errbuf); + } + + if (errcode == 0 && pmatch.rm_so == 0 && pmatch.rm_eo == len) + matched = 1; + + return matched; +} + +PLAN * +c_regex(OPTION *option, char ***argvp) +{ + PLAN *new; + char *pattern; + regex_t *pre; + int errcode; + char errbuf[LINE_MAX]; + + if ((pre = malloc(sizeof(regex_t))) == NULL) + err(1, NULL); + + pattern = nextarg(option, argvp); + + if ((errcode = regcomp(pre, pattern, + regexp_flags | (option->flags & F_IGNCASE ? REG_ICASE : 0))) != 0) { + regerror(errcode, pre, errbuf, sizeof errbuf); + errx(1, "%s: %s: %s", + option->flags & F_IGNCASE ? "-iregex" : "-regex", + pattern, errbuf); + } + + new = palloc(option); + new->re_data = pre; + + return new; +} + +/* c_simple covers c_prune, c_openparen, c_closeparen, c_not, c_or, c_true, c_false */ + +PLAN * +c_simple(OPTION *option, char ***argvp __unused) +{ + return palloc(option); +} + +/* + * -size n[c] functions -- + * + * True if the file size in bytes, divided by an implementation defined + * value and rounded up to the next integer, is n. If n is followed by + * one of c k M G T P, the size is in bytes, kilobytes, + * megabytes, gigabytes, terabytes or petabytes respectively. + */ +#define FIND_SIZE 512 +static int divsize = 1; + +int +f_size(PLAN *plan, FTSENT *entry) +{ + off_t size; + + size = divsize ? (entry->fts_statp->st_size + FIND_SIZE - 1) / + FIND_SIZE : entry->fts_statp->st_size; + COMPARE(size, plan->o_data); +} + +PLAN * +c_size(OPTION *option, char ***argvp) +{ + char *size_str; + PLAN *new; + char endch; + off_t scale; + + size_str = nextarg(option, argvp); + ftsoptions &= ~FTS_NOSTAT; + + new = palloc(option); + endch = 'c'; + new->o_data = find_parsenum(new, option->name, size_str, &endch); + if (endch != '\0') { + divsize = 0; + + switch (endch) { + case 'c': /* characters */ + scale = 0x1LL; + break; + case 'k': /* kilobytes 1<<10 */ + scale = 0x400LL; + break; + case 'M': /* megabytes 1<<20 */ + scale = 0x100000LL; + break; + case 'G': /* gigabytes 1<<30 */ + scale = 0x40000000LL; + break; + case 'T': /* terabytes 1<<40 */ + scale = 0x1000000000LL; + break; + case 'P': /* petabytes 1<<50 */ + scale = 0x4000000000000LL; + break; + default: + errx(1, "%s: %s: illegal trailing character", + option->name, size_str); + break; + } + if (new->o_data > QUAD_MAX / scale) + errx(1, "%s: %s: value too large", + option->name, size_str); + new->o_data *= scale; + } + return new; +} + +/* + * -type c functions -- + * + * True if the type of the file is c, where c is b, c, d, p, f or w + * for block special file, character special file, directory, FIFO, + * regular file or whiteout respectively. + */ +int +f_type(PLAN *plan, FTSENT *entry) +{ + return (entry->fts_statp->st_mode & S_IFMT) == plan->m_data; +} + +PLAN * +c_type(OPTION *option, char ***argvp) +{ + char *typestring; + PLAN *new; + mode_t mask; + + typestring = nextarg(option, argvp); + ftsoptions &= ~FTS_NOSTAT; + + switch (typestring[0]) { + case 'b': + mask = S_IFBLK; + break; + case 'c': + mask = S_IFCHR; + break; + case 'd': + mask = S_IFDIR; + break; + case 'f': + mask = S_IFREG; + break; + case 'l': + mask = S_IFLNK; + break; + case 'p': + mask = S_IFIFO; + break; + case 's': + mask = S_IFSOCK; + break; +#ifdef FTS_WHITEOUT + case 'w': + mask = S_IFWHT; + ftsoptions |= FTS_WHITEOUT; + break; +#endif /* FTS_WHITEOUT */ + default: + errx(1, "%s: %s: unknown type", option->name, typestring); + } + + new = palloc(option); + new->m_data = mask; + return new; +} + +/* + * -user uname functions -- + * + * True if the file belongs to the user uname. If uname is numeric and + * an equivalent of the getpwnam() S9.2.2 [POSIX.1] function does not + * return a valid user name, uname is taken as a user ID. + */ +int +f_user(PLAN *plan, FTSENT *entry) +{ + COMPARE(entry->fts_statp->st_uid, plan->u_data); +} + +PLAN * +c_user(OPTION *option, char ***argvp) +{ + char *username; + PLAN *new; + struct passwd *p; + uid_t uid; + + username = nextarg(option, argvp); + ftsoptions &= ~FTS_NOSTAT; + + new = palloc(option); + p = getpwnam(username); + if (p == NULL) { + char* cp = username; + if( username[0] == '-' || username[0] == '+' ) + username++; + uid = atoi(username); + if (uid == 0 && username[0] != '0') + errx(1, "%s: %s: no such user", option->name, username); + uid = find_parsenum(new, option->name, cp, NULL); + } else + uid = p->pw_uid; + + new->u_data = uid; + return new; +} + +/* + * -xdev functions -- + * + * Always true, causes find not to descend past directories that have a + * different device ID (st_dev, see stat() S5.6.2 [POSIX.1]) + */ +PLAN * +c_xdev(OPTION *option, char ***argvp __unused) +{ + ftsoptions |= FTS_XDEV; + + return palloc(option); +} + +/* + * ( expression ) functions -- + * + * True if expression is true. + */ +int +f_expr(PLAN *plan, FTSENT *entry) +{ + PLAN *p; + int state = 0; + + for (p = plan->p_data[0]; + p && (state = (p->execute)(p, entry)); p = p->next); + return state; +} + +/* + * f_openparen and f_closeparen nodes are temporary place markers. They are + * eliminated during phase 2 of find_formplan() --- the '(' node is converted + * to a f_expr node containing the expression and the ')' node is discarded. + * The functions themselves are only used as constants. + */ + +int +f_openparen(PLAN *plan __unused, FTSENT *entry __unused) +{ + abort(); +} + +int +f_closeparen(PLAN *plan __unused, FTSENT *entry __unused) +{ + abort(); +} + +/* c_openparen == c_simple */ +/* c_closeparen == c_simple */ + +/* + * AND operator. Since AND is implicit, no node is allocated. + */ +PLAN * +c_and(OPTION *option __unused, char ***argvp __unused) +{ + return NULL; +} + +/* + * ! expression functions -- + * + * Negation of a primary; the unary NOT operator. + */ +int +f_not(PLAN *plan, FTSENT *entry) +{ + PLAN *p; + int state = 0; + + for (p = plan->p_data[0]; + p && (state = (p->execute)(p, entry)); p = p->next); + return !state; +} + +/* c_not == c_simple */ + +/* + * expression -o expression functions -- + * + * Alternation of primaries; the OR operator. The second expression is + * not evaluated if the first expression is true. + */ +int +f_or(PLAN *plan, FTSENT *entry) +{ + PLAN *p; + int state = 0; + + for (p = plan->p_data[0]; + p && (state = (p->execute)(p, entry)); p = p->next); + + if (state) + return 1; + + for (p = plan->p_data[1]; + p && (state = (p->execute)(p, entry)); p = p->next); + return state; +} + +/* c_or == c_simple */ + +/* + * -false + * + * Always false. + */ +int +f_false(PLAN *plan __unused, FTSENT *entry __unused) +{ + return 0; +} + +/* c_false == c_simple */ + +/* + * -quit + * + * Exits the program + */ +int +f_quit(PLAN *plan __unused, FTSENT *entry __unused) +{ + exit(0); +} + +/* c_quit == c_simple */ diff --git a/shell_cmds/find/getdate.y b/shell_cmds/find/getdate.y new file mode 100644 index 0000000..5a97c2c --- /dev/null +++ b/shell_cmds/find/getdate.y @@ -0,0 +1,961 @@ +%{ +/* +** Originally written by Steven M. Bellovin <smb@research.att.com> while +** at the University of North Carolina at Chapel Hill. Later tweaked by +** a couple of people on Usenet. Completely overhauled by Rich $alz +** <rsalz@bbn.com> and Jim Berets <jberets@bbn.com> in August, 1990; +** +** This grammar has 10 shift/reduce conflicts. +** +** This code is in the public domain and has no copyright. +*/ +/* SUPPRESS 287 on yaccpar_sccsid *//* Unused static variable */ +/* SUPPRESS 288 on yyerrlab *//* Label unused */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD: src/usr.bin/find/getdate.y,v 1.7 2010/02/09 21:24:41 ed Exp $"); + +#include <stdio.h> +#include <ctype.h> + +/* The code at the top of get_date which figures out the offset of the + current time zone checks various CPP symbols to see if special + tricks are need, but defaults to using the gettimeofday system call. + Include <sys/time.h> if that will be used. */ + +#if defined(vms) +# include <types.h> +#else /* defined(vms) */ +# include <sys/types.h> +# include <sys/time.h> +#endif /* !defined(vms) */ + +#if defined (__STDC__) || defined (USG) +#include <string.h> +#endif + +/* Some old versions of bison generate parsers that use bcopy. + That loses on systems that don't provide the function, so we have + to redefine it here. */ +#if !defined (HAVE_BCOPY) && defined (HAVE_MEMCPY) && !defined (bcopy) +#define bcopy(from, to, len) memcpy ((to), (from), (len)) +#endif + +#if defined (__STDC__) +#include <stdlib.h> +#endif + +/* NOTES on rebuilding getdate.c (particularly for inclusion in CVS + releases): + + We don't want to mess with all the portability hassles of alloca. + In particular, most (all?) versions of bison will use alloca in + their parser. If bison works on your system (e.g. it should work + with gcc), then go ahead and use it, but the more general solution + is to use byacc instead of bison, which should generate a portable + parser. I played with adding "#define alloca dont_use_alloca", to + give an error if the parser generator uses alloca (and thus detect + unportable getdate.c's), but that seems to cause as many problems + as it solves. */ + +#include <time.h> + +#define yyparse getdate_yyparse +#define yylex getdate_yylex +#define yyerror getdate_yyerror + +static int yyparse(void); +static int yylex(void); +static int yyerror(const char *); + +time_t get_date(char *); + +#define EPOCH 1970 +#define HOUR(x) ((time_t)(x) * 60) +#define SECSPERDAY (24L * 60L * 60L) + + +/* +** An entry in the lexical lookup table. +*/ +typedef struct _TABLE { + const char *name; + int type; + time_t value; +} TABLE; + + +/* +** Daylight-savings mode: on, off, or not yet known. +*/ +typedef enum _DSTMODE { + DSTon, DSToff, DSTmaybe +} DSTMODE; + +/* +** Meridian: am, pm, or 24-hour style. +*/ +typedef enum _MERIDIAN { + MERam, MERpm, MER24 +} MERIDIAN; + + +/* +** Global variables. We could get rid of most of these by using a good +** union as the yacc stack. (This routine was originally written before +** yacc had the %union construct.) Maybe someday; right now we only use +** the %union very rarely. +*/ +static char *yyInput; +static DSTMODE yyDSTmode; +static time_t yyDayOrdinal; +static time_t yyDayNumber; +static int yyHaveDate; +static int yyHaveDay; +static int yyHaveRel; +static int yyHaveTime; +static int yyHaveZone; +static time_t yyTimezone; +static time_t yyDay; +static time_t yyHour; +static time_t yyMinutes; +static time_t yyMonth; +static time_t yySeconds; +static time_t yyYear; +static MERIDIAN yyMeridian; +static time_t yyRelMonth; +static time_t yyRelSeconds; + +%} + +%union { + time_t Number; + enum _MERIDIAN Meridian; +} + +%token tAGO tDAY tDAYZONE tID tMERIDIAN tMINUTE_UNIT tMONTH tMONTH_UNIT +%token tSEC_UNIT tSNUMBER tUNUMBER tZONE tDST + +%type <Number> tDAY tDAYZONE tMINUTE_UNIT tMONTH tMONTH_UNIT +%type <Number> tSEC_UNIT tSNUMBER tUNUMBER tZONE +%type <Meridian> tMERIDIAN o_merid + +%% + +spec : /* NULL */ + | spec item + ; + +item : time { + yyHaveTime++; + } + | zone { + yyHaveZone++; + } + | date { + yyHaveDate++; + } + | day { + yyHaveDay++; + } + | rel { + yyHaveRel++; + } + | number + ; + +time : tUNUMBER tMERIDIAN { + yyHour = $1; + yyMinutes = 0; + yySeconds = 0; + yyMeridian = $2; + } + | tUNUMBER ':' tUNUMBER o_merid { + yyHour = $1; + yyMinutes = $3; + yySeconds = 0; + yyMeridian = $4; + } + | tUNUMBER ':' tUNUMBER tSNUMBER { + yyHour = $1; + yyMinutes = $3; + yyMeridian = MER24; + yyDSTmode = DSToff; + yyTimezone = - ($4 % 100 + ($4 / 100) * 60); + } + | tUNUMBER ':' tUNUMBER ':' tUNUMBER o_merid { + yyHour = $1; + yyMinutes = $3; + yySeconds = $5; + yyMeridian = $6; + } + | tUNUMBER ':' tUNUMBER ':' tUNUMBER tSNUMBER { + yyHour = $1; + yyMinutes = $3; + yySeconds = $5; + yyMeridian = MER24; + yyDSTmode = DSToff; + yyTimezone = - ($6 % 100 + ($6 / 100) * 60); + } + ; + +zone : tZONE { + yyTimezone = $1; + yyDSTmode = DSToff; + } + | tDAYZONE { + yyTimezone = $1; + yyDSTmode = DSTon; + } + | + tZONE tDST { + yyTimezone = $1; + yyDSTmode = DSTon; + } + ; + +day : tDAY { + yyDayOrdinal = 1; + yyDayNumber = $1; + } + | tDAY ',' { + yyDayOrdinal = 1; + yyDayNumber = $1; + } + | tUNUMBER tDAY { + yyDayOrdinal = $1; + yyDayNumber = $2; + } + ; + +date : tUNUMBER '/' tUNUMBER { + yyMonth = $1; + yyDay = $3; + } + | tUNUMBER '/' tUNUMBER '/' tUNUMBER { + if ($1 >= 100) { + yyYear = $1; + yyMonth = $3; + yyDay = $5; + } else { + yyMonth = $1; + yyDay = $3; + yyYear = $5; + } + } + | tUNUMBER tSNUMBER tSNUMBER { + /* ISO 8601 format. yyyy-mm-dd. */ + yyYear = $1; + yyMonth = -$2; + yyDay = -$3; + } + | tUNUMBER tMONTH tSNUMBER { + /* e.g. 17-JUN-1992. */ + yyDay = $1; + yyMonth = $2; + yyYear = -$3; + } + | tMONTH tUNUMBER { + yyMonth = $1; + yyDay = $2; + } + | tMONTH tUNUMBER ',' tUNUMBER { + yyMonth = $1; + yyDay = $2; + yyYear = $4; + } + | tUNUMBER tMONTH { + yyMonth = $2; + yyDay = $1; + } + | tUNUMBER tMONTH tUNUMBER { + yyMonth = $2; + yyDay = $1; + yyYear = $3; + } + ; + +rel : relunit tAGO { + yyRelSeconds = -yyRelSeconds; + yyRelMonth = -yyRelMonth; + } + | relunit + ; + +relunit : tUNUMBER tMINUTE_UNIT { + yyRelSeconds += $1 * $2 * 60L; + } + | tSNUMBER tMINUTE_UNIT { + yyRelSeconds += $1 * $2 * 60L; + } + | tMINUTE_UNIT { + yyRelSeconds += $1 * 60L; + } + | tSNUMBER tSEC_UNIT { + yyRelSeconds += $1; + } + | tUNUMBER tSEC_UNIT { + yyRelSeconds += $1; + } + | tSEC_UNIT { + yyRelSeconds++; + } + | tSNUMBER tMONTH_UNIT { + yyRelMonth += $1 * $2; + } + | tUNUMBER tMONTH_UNIT { + yyRelMonth += $1 * $2; + } + | tMONTH_UNIT { + yyRelMonth += $1; + } + ; + +number : tUNUMBER { + if (yyHaveTime && yyHaveDate && !yyHaveRel) + yyYear = $1; + else { + if($1>10000) { + yyHaveDate++; + yyDay= ($1)%100; + yyMonth= ($1/100)%100; + yyYear = $1/10000; + } + else { + yyHaveTime++; + if ($1 < 100) { + yyHour = $1; + yyMinutes = 0; + } + else { + yyHour = $1 / 100; + yyMinutes = $1 % 100; + } + yySeconds = 0; + yyMeridian = MER24; + } + } + } + ; + +o_merid : /* NULL */ { + $$ = MER24; + } + | tMERIDIAN { + $$ = $1; + } + ; + +%% + +/* Month and day table. */ +static TABLE const MonthDayTable[] = { + { "january", tMONTH, 1 }, + { "february", tMONTH, 2 }, + { "march", tMONTH, 3 }, + { "april", tMONTH, 4 }, + { "may", tMONTH, 5 }, + { "june", tMONTH, 6 }, + { "july", tMONTH, 7 }, + { "august", tMONTH, 8 }, + { "september", tMONTH, 9 }, + { "sept", tMONTH, 9 }, + { "october", tMONTH, 10 }, + { "november", tMONTH, 11 }, + { "december", tMONTH, 12 }, + { "sunday", tDAY, 0 }, + { "monday", tDAY, 1 }, + { "tuesday", tDAY, 2 }, + { "tues", tDAY, 2 }, + { "wednesday", tDAY, 3 }, + { "wednes", tDAY, 3 }, + { "thursday", tDAY, 4 }, + { "thur", tDAY, 4 }, + { "thurs", tDAY, 4 }, + { "friday", tDAY, 5 }, + { "saturday", tDAY, 6 }, + { NULL, 0, 0 } +}; + +/* Time units table. */ +static TABLE const UnitsTable[] = { + { "year", tMONTH_UNIT, 12 }, + { "month", tMONTH_UNIT, 1 }, + { "fortnight", tMINUTE_UNIT, 14 * 24 * 60 }, + { "week", tMINUTE_UNIT, 7 * 24 * 60 }, + { "day", tMINUTE_UNIT, 1 * 24 * 60 }, + { "hour", tMINUTE_UNIT, 60 }, + { "minute", tMINUTE_UNIT, 1 }, + { "min", tMINUTE_UNIT, 1 }, + { "second", tSEC_UNIT, 1 }, + { "sec", tSEC_UNIT, 1 }, + { NULL, 0, 0 } +}; + +/* Assorted relative-time words. */ +static TABLE const OtherTable[] = { + { "tomorrow", tMINUTE_UNIT, 1 * 24 * 60 }, + { "yesterday", tMINUTE_UNIT, -1 * 24 * 60 }, + { "today", tMINUTE_UNIT, 0 }, + { "now", tMINUTE_UNIT, 0 }, + { "last", tUNUMBER, -1 }, + { "this", tMINUTE_UNIT, 0 }, + { "next", tUNUMBER, 2 }, + { "first", tUNUMBER, 1 }, +/* { "second", tUNUMBER, 2 }, */ + { "third", tUNUMBER, 3 }, + { "fourth", tUNUMBER, 4 }, + { "fifth", tUNUMBER, 5 }, + { "sixth", tUNUMBER, 6 }, + { "seventh", tUNUMBER, 7 }, + { "eighth", tUNUMBER, 8 }, + { "ninth", tUNUMBER, 9 }, + { "tenth", tUNUMBER, 10 }, + { "eleventh", tUNUMBER, 11 }, + { "twelfth", tUNUMBER, 12 }, + { "ago", tAGO, 1 }, + { NULL, 0, 0 } +}; + +/* The timezone table. */ +/* Some of these are commented out because a time_t can't store a float. */ +static TABLE const TimezoneTable[] = { + { "gmt", tZONE, HOUR( 0) }, /* Greenwich Mean */ + { "ut", tZONE, HOUR( 0) }, /* Universal (Coordinated) */ + { "utc", tZONE, HOUR( 0) }, + { "wet", tZONE, HOUR( 0) }, /* Western European */ + { "bst", tDAYZONE, HOUR( 0) }, /* British Summer */ + { "wat", tZONE, HOUR( 1) }, /* West Africa */ + { "at", tZONE, HOUR( 2) }, /* Azores */ +#if 0 + /* For completeness. BST is also British Summer, and GST is + * also Guam Standard. */ + { "bst", tZONE, HOUR( 3) }, /* Brazil Standard */ + { "gst", tZONE, HOUR( 3) }, /* Greenland Standard */ +#endif +#if 0 + { "nft", tZONE, HOUR(3.5) }, /* Newfoundland */ + { "nst", tZONE, HOUR(3.5) }, /* Newfoundland Standard */ + { "ndt", tDAYZONE, HOUR(3.5) }, /* Newfoundland Daylight */ +#endif + { "ast", tZONE, HOUR( 4) }, /* Atlantic Standard */ + { "adt", tDAYZONE, HOUR( 4) }, /* Atlantic Daylight */ + { "est", tZONE, HOUR( 5) }, /* Eastern Standard */ + { "edt", tDAYZONE, HOUR( 5) }, /* Eastern Daylight */ + { "cst", tZONE, HOUR( 6) }, /* Central Standard */ + { "cdt", tDAYZONE, HOUR( 6) }, /* Central Daylight */ + { "mst", tZONE, HOUR( 7) }, /* Mountain Standard */ + { "mdt", tDAYZONE, HOUR( 7) }, /* Mountain Daylight */ + { "pst", tZONE, HOUR( 8) }, /* Pacific Standard */ + { "pdt", tDAYZONE, HOUR( 8) }, /* Pacific Daylight */ + { "yst", tZONE, HOUR( 9) }, /* Yukon Standard */ + { "ydt", tDAYZONE, HOUR( 9) }, /* Yukon Daylight */ + { "hst", tZONE, HOUR(10) }, /* Hawaii Standard */ + { "hdt", tDAYZONE, HOUR(10) }, /* Hawaii Daylight */ + { "cat", tZONE, HOUR(10) }, /* Central Alaska */ + { "ahst", tZONE, HOUR(10) }, /* Alaska-Hawaii Standard */ + { "nt", tZONE, HOUR(11) }, /* Nome */ + { "idlw", tZONE, HOUR(12) }, /* International Date Line West */ + { "cet", tZONE, -HOUR(1) }, /* Central European */ + { "met", tZONE, -HOUR(1) }, /* Middle European */ + { "mewt", tZONE, -HOUR(1) }, /* Middle European Winter */ + { "mest", tDAYZONE, -HOUR(1) }, /* Middle European Summer */ + { "swt", tZONE, -HOUR(1) }, /* Swedish Winter */ + { "sst", tDAYZONE, -HOUR(1) }, /* Swedish Summer */ + { "fwt", tZONE, -HOUR(1) }, /* French Winter */ + { "fst", tDAYZONE, -HOUR(1) }, /* French Summer */ + { "eet", tZONE, -HOUR(2) }, /* Eastern Europe, USSR Zone 1 */ + { "bt", tZONE, -HOUR(3) }, /* Baghdad, USSR Zone 2 */ +#if 0 + { "it", tZONE, -HOUR(3.5) },/* Iran */ +#endif + { "zp4", tZONE, -HOUR(4) }, /* USSR Zone 3 */ + { "zp5", tZONE, -HOUR(5) }, /* USSR Zone 4 */ +#if 0 + { "ist", tZONE, -HOUR(5.5) },/* Indian Standard */ +#endif + { "zp6", tZONE, -HOUR(6) }, /* USSR Zone 5 */ +#if 0 + /* For completeness. NST is also Newfoundland Stanard, and SST is + * also Swedish Summer. */ + { "nst", tZONE, -HOUR(6.5) },/* North Sumatra */ + { "sst", tZONE, -HOUR(7) }, /* South Sumatra, USSR Zone 6 */ +#endif /* 0 */ + { "wast", tZONE, -HOUR(7) }, /* West Australian Standard */ + { "wadt", tDAYZONE, -HOUR(7) }, /* West Australian Daylight */ +#if 0 + { "jt", tZONE, -HOUR(7.5) },/* Java (3pm in Cronusland!) */ +#endif + { "cct", tZONE, -HOUR(8) }, /* China Coast, USSR Zone 7 */ + { "jst", tZONE, -HOUR(9) }, /* Japan Standard, USSR Zone 8 */ +#if 0 + { "cast", tZONE, -HOUR(9.5) },/* Central Australian Standard */ + { "cadt", tDAYZONE, -HOUR(9.5) },/* Central Australian Daylight */ +#endif + { "east", tZONE, -HOUR(10) }, /* Eastern Australian Standard */ + { "eadt", tDAYZONE, -HOUR(10) }, /* Eastern Australian Daylight */ + { "gst", tZONE, -HOUR(10) }, /* Guam Standard, USSR Zone 9 */ + { "nzt", tZONE, -HOUR(12) }, /* New Zealand */ + { "nzst", tZONE, -HOUR(12) }, /* New Zealand Standard */ + { "nzdt", tDAYZONE, -HOUR(12) }, /* New Zealand Daylight */ + { "idle", tZONE, -HOUR(12) }, /* International Date Line East */ + { NULL, 0, 0 } +}; + +/* Military timezone table. */ +static TABLE const MilitaryTable[] = { + { "a", tZONE, HOUR( 1) }, + { "b", tZONE, HOUR( 2) }, + { "c", tZONE, HOUR( 3) }, + { "d", tZONE, HOUR( 4) }, + { "e", tZONE, HOUR( 5) }, + { "f", tZONE, HOUR( 6) }, + { "g", tZONE, HOUR( 7) }, + { "h", tZONE, HOUR( 8) }, + { "i", tZONE, HOUR( 9) }, + { "k", tZONE, HOUR( 10) }, + { "l", tZONE, HOUR( 11) }, + { "m", tZONE, HOUR( 12) }, + { "n", tZONE, HOUR(- 1) }, + { "o", tZONE, HOUR(- 2) }, + { "p", tZONE, HOUR(- 3) }, + { "q", tZONE, HOUR(- 4) }, + { "r", tZONE, HOUR(- 5) }, + { "s", tZONE, HOUR(- 6) }, + { "t", tZONE, HOUR(- 7) }, + { "u", tZONE, HOUR(- 8) }, + { "v", tZONE, HOUR(- 9) }, + { "w", tZONE, HOUR(-10) }, + { "x", tZONE, HOUR(-11) }, + { "y", tZONE, HOUR(-12) }, + { "z", tZONE, HOUR( 0) }, + { NULL, 0, 0 } +}; + + + + +/* ARGSUSED */ +static int +yyerror(const char *s __unused) +{ + return 0; +} + + +static time_t +ToSeconds(time_t Hours, time_t Minutes, time_t Seconds, MERIDIAN Meridian) +{ + if (Minutes < 0 || Minutes > 59 || Seconds < 0 || Seconds > 59) + return -1; + switch (Meridian) { + case MER24: + if (Hours < 0 || Hours > 23) + return -1; + return (Hours * 60L + Minutes) * 60L + Seconds; + case MERam: + if (Hours < 1 || Hours > 12) + return -1; + if (Hours == 12) + Hours = 0; + return (Hours * 60L + Minutes) * 60L + Seconds; + case MERpm: + if (Hours < 1 || Hours > 12) + return -1; + if (Hours == 12) + Hours = 0; + return ((Hours + 12) * 60L + Minutes) * 60L + Seconds; + default: + abort (); + } + /* NOTREACHED */ +} + + +/* Year is either + * A negative number, which means to use its absolute value (why?) + * A number from 0 to 99, which means a year from 1900 to 1999, or + * The actual year (>=100). */ +static time_t +Convert(time_t Month, time_t Day, time_t Year, + time_t Hours, time_t Minutes, time_t Seconds, + MERIDIAN Meridian, DSTMODE DSTmode) +{ + static int DaysInMonth[12] = { + 31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 + }; + time_t tod; + time_t Julian; + int i; + + if (Year < 0) + Year = -Year; + if (Year < 69) + Year += 2000; + else if (Year < 100) + Year += 1900; + DaysInMonth[1] = Year % 4 == 0 && (Year % 100 != 0 || Year % 400 == 0) + ? 29 : 28; + /* Checking for 2038 bogusly assumes that time_t is 32 bits. But + I'm too lazy to try to check for time_t overflow in another way. */ + if (Year < EPOCH || Year > 2038 + || Month < 1 || Month > 12 + /* Lint fluff: "conversion from long may lose accuracy" */ + || Day < 1 || Day > DaysInMonth[(int)--Month]) + return -1; + + for (Julian = Day - 1, i = 0; i < Month; i++) + Julian += DaysInMonth[i]; + for (i = EPOCH; i < Year; i++) + Julian += 365 + (i % 4 == 0); + Julian *= SECSPERDAY; + Julian += yyTimezone * 60L; + if ((tod = ToSeconds(Hours, Minutes, Seconds, Meridian)) < 0) + return -1; + Julian += tod; + if (DSTmode == DSTon + || (DSTmode == DSTmaybe && localtime(&Julian)->tm_isdst)) + Julian -= 60 * 60; + return Julian; +} + + +static time_t +DSTcorrect(time_t Start, time_t Future) +{ + time_t StartDay; + time_t FutureDay; + + StartDay = (localtime(&Start)->tm_hour + 1) % 24; + FutureDay = (localtime(&Future)->tm_hour + 1) % 24; + return (Future - Start) + (StartDay - FutureDay) * 60L * 60L; +} + + +static time_t +RelativeDate(time_t Start, time_t DayOrdinal, time_t DayNumber) +{ + struct tm *tm; + time_t now; + + now = Start; + tm = localtime(&now); + now += SECSPERDAY * ((DayNumber - tm->tm_wday + 7) % 7); + now += 7 * SECSPERDAY * (DayOrdinal <= 0 ? DayOrdinal : DayOrdinal - 1); + return DSTcorrect(Start, now); +} + + +static time_t +RelativeMonth(time_t Start, time_t RelMonth) +{ + struct tm *tm; + time_t Month; + time_t Year; + + if (RelMonth == 0) + return 0; + tm = localtime(&Start); + Month = 12 * (tm->tm_year + 1900) + tm->tm_mon + RelMonth; + Year = Month / 12; + Month = Month % 12 + 1; + return DSTcorrect(Start, + Convert(Month, (time_t)tm->tm_mday, Year, + (time_t)tm->tm_hour, (time_t)tm->tm_min, (time_t)tm->tm_sec, + MER24, DSTmaybe)); +} + + +static int +LookupWord(char *buff) +{ + char *p; + char *q; + const TABLE *tp; + int i; + int abbrev; + + /* Make it lowercase. */ + for (p = buff; *p; p++) + if (isupper(*p)) + *p = tolower(*p); + + if (strcmp(buff, "am") == 0 || strcmp(buff, "a.m.") == 0) { + yylval.Meridian = MERam; + return tMERIDIAN; + } + if (strcmp(buff, "pm") == 0 || strcmp(buff, "p.m.") == 0) { + yylval.Meridian = MERpm; + return tMERIDIAN; + } + + /* See if we have an abbreviation for a month. */ + if (strlen(buff) == 3) + abbrev = 1; + else if (strlen(buff) == 4 && buff[3] == '.') { + abbrev = 1; + buff[3] = '\0'; + } + else + abbrev = 0; + + for (tp = MonthDayTable; tp->name; tp++) { + if (abbrev) { + if (strncmp(buff, tp->name, 3) == 0) { + yylval.Number = tp->value; + return tp->type; + } + } + else if (strcmp(buff, tp->name) == 0) { + yylval.Number = tp->value; + return tp->type; + } + } + + for (tp = TimezoneTable; tp->name; tp++) + if (strcmp(buff, tp->name) == 0) { + yylval.Number = tp->value; + return tp->type; + } + + if (strcmp(buff, "dst") == 0) + return tDST; + + for (tp = UnitsTable; tp->name; tp++) + if (strcmp(buff, tp->name) == 0) { + yylval.Number = tp->value; + return tp->type; + } + + /* Strip off any plural and try the units table again. */ + i = strlen(buff) - 1; + if (buff[i] == 's') { + buff[i] = '\0'; + for (tp = UnitsTable; tp->name; tp++) + if (strcmp(buff, tp->name) == 0) { + yylval.Number = tp->value; + return tp->type; + } + buff[i] = 's'; /* Put back for "this" in OtherTable. */ + } + + for (tp = OtherTable; tp->name; tp++) + if (strcmp(buff, tp->name) == 0) { + yylval.Number = tp->value; + return tp->type; + } + + /* Military timezones. */ + if (buff[1] == '\0' && isalpha(*buff)) { + for (tp = MilitaryTable; tp->name; tp++) + if (strcmp(buff, tp->name) == 0) { + yylval.Number = tp->value; + return tp->type; + } + } + + /* Drop out any periods and try the timezone table again. */ + for (i = 0, p = q = buff; *q; q++) + if (*q != '.') + *p++ = *q; + else + i++; + *p = '\0'; + if (i) + for (tp = TimezoneTable; tp->name; tp++) + if (strcmp(buff, tp->name) == 0) { + yylval.Number = tp->value; + return tp->type; + } + + return tID; +} + + +static int +yylex(void) +{ + char c; + char *p; + char buff[20]; + int Count; + int sign; + + for ( ; ; ) { + while (isspace(*yyInput)) + yyInput++; + + if (isdigit(c = *yyInput) || c == '-' || c == '+') { + if (c == '-' || c == '+') { + sign = c == '-' ? -1 : 1; + if (!isdigit(*++yyInput)) + /* skip the '-' sign */ + continue; + } + else + sign = 0; + for (yylval.Number = 0; isdigit(c = *yyInput++); ) + yylval.Number = 10 * yylval.Number + c - '0'; + yyInput--; + if (sign < 0) + yylval.Number = -yylval.Number; + return sign ? tSNUMBER : tUNUMBER; + } + if (isalpha(c)) { + for (p = buff; isalpha(c = *yyInput++) || c == '.'; ) + if (p < &buff[sizeof buff - 1]) + *p++ = c; + *p = '\0'; + yyInput--; + return LookupWord(buff); + } + if (c != '(') + return *yyInput++; + Count = 0; + do { + c = *yyInput++; + if (c == '\0') + return c; + if (c == '(') + Count++; + else if (c == ')') + Count--; + } while (Count > 0); + } +} + +#define TM_YEAR_ORIGIN 1900 + +/* Yield A - B, measured in seconds. */ +static long +difftm (struct tm *a, struct tm *b) +{ + int ay = a->tm_year + (TM_YEAR_ORIGIN - 1); + int by = b->tm_year + (TM_YEAR_ORIGIN - 1); + int days = ( + /* difference in day of year */ + a->tm_yday - b->tm_yday + /* + intervening leap days */ + + ((ay >> 2) - (by >> 2)) + - (ay/100 - by/100) + + ((ay/100 >> 2) - (by/100 >> 2)) + /* + difference in years * 365 */ + + (long)(ay-by) * 365 + ); + return (60*(60*(24*days + (a->tm_hour - b->tm_hour)) + + (a->tm_min - b->tm_min)) + + (a->tm_sec - b->tm_sec)); +} + +time_t +get_date(char *p) +{ + struct tm *tm, *gmt_ptr, gmt; + int tzoff; + time_t Start; + time_t tod; + time_t nowtime; + + bzero (&gmt, sizeof(struct tm)); + yyInput = p; + + (void)time (&nowtime); + + gmt_ptr = gmtime (&nowtime); + if (gmt_ptr != NULL) + { + /* Make a copy, in case localtime modifies *tm (I think + that comment now applies to *gmt_ptr, but I am too + lazy to dig into how gmtime and locatime allocate the + structures they return pointers to). */ + gmt = *gmt_ptr; + } + + if (! (tm = localtime (&nowtime))) + return -1; + + if (gmt_ptr != NULL) + tzoff = difftm (&gmt, tm) / 60; + else + /* We are on a system like VMS, where the system clock is + in local time and the system has no concept of timezones. + Hopefully we can fake this out (for the case in which the + user specifies no timezone) by just saying the timezone + is zero. */ + tzoff = 0; + + if(tm->tm_isdst) + tzoff += 60; + + tm = localtime(&nowtime); + yyYear = tm->tm_year + 1900; + yyMonth = tm->tm_mon + 1; + yyDay = tm->tm_mday; + yyTimezone = tzoff; + yyDSTmode = DSTmaybe; + yyHour = 0; + yyMinutes = 0; + yySeconds = 0; + yyMeridian = MER24; + yyRelSeconds = 0; + yyRelMonth = 0; + yyHaveDate = 0; + yyHaveDay = 0; + yyHaveRel = 0; + yyHaveTime = 0; + yyHaveZone = 0; + + if (yyparse() + || yyHaveTime > 1 || yyHaveZone > 1 || yyHaveDate > 1 || yyHaveDay > 1) + return -1; + + if (yyHaveDate || yyHaveTime || yyHaveDay) { + Start = Convert(yyMonth, yyDay, yyYear, yyHour, yyMinutes, yySeconds, + yyMeridian, yyDSTmode); + if (Start < 0) + return -1; + } + else { + Start = nowtime; + if (!yyHaveRel) + Start -= ((tm->tm_hour * 60L + tm->tm_min) * 60L) + tm->tm_sec; + } + + Start += yyRelSeconds; + Start += RelativeMonth(Start, yyRelMonth); + + if (yyHaveDay && !yyHaveDate) { + tod = RelativeDate(Start, yyDayOrdinal, yyDayNumber); + Start += tod; + } + + /* Have to do *something* with a legitimate -1 so it's distinguishable + * from the error return value. (Alternately could set errno on error.) */ + return Start == -1 ? 0 : Start; +} + + +#if defined(TEST) + +/* ARGSUSED */ +int +main(int ac, char *av[]) +{ + char buff[128]; + time_t d; + + (void)printf("Enter date, or blank line to exit.\n\t> "); + (void)fflush(stdout); + while (gets(buff) && buff[0]) { + d = get_date(buff); + if (d == -1) + (void)printf("Bad format - couldn't convert.\n"); + else + (void)printf("%s", ctime(&d)); + (void)printf("\t> "); + (void)fflush(stdout); + } + exit(0); + /* NOTREACHED */ +} +#endif /* defined(TEST) */ diff --git a/shell_cmds/find/ls.c b/shell_cmds/find/ls.c new file mode 100644 index 0000000..f58d969 --- /dev/null +++ b/shell_cmds/find/ls.c @@ -0,0 +1,124 @@ +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)ls.c 8.1 (Berkeley) 6/6/93"; +#endif +#endif /* not lint */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD: src/usr.bin/find/ls.c,v 1.23 2011/09/28 18:53:36 ed Exp $"); + +#include <sys/param.h> +#include <sys/stat.h> + +#include <err.h> +#include <errno.h> +#include <fts.h> +#include <grp.h> +#include <inttypes.h> +#include <langinfo.h> +#include <pwd.h> +#include <stdio.h> +#include <string.h> +#include <time.h> +#include <unistd.h> + +#ifdef __APPLE__ +#undef MAXLOGNAME +#define MAXLOGNAME 17 +#endif /* __APPLE__ */ + +#include "find.h" + +/* Derived from the print routines in the ls(1) source code. */ + +static void printlink(char *); +static void printtime(time_t); + +void +printlong(char *name, char *accpath, struct stat *sb) +{ + char modep[15]; + + (void)printf("%6lu %8"PRId64" ", (u_long) sb->st_ino, sb->st_blocks); + (void)strmode(sb->st_mode, modep); + (void)printf("%s %3u %-*s %-*s ", modep, sb->st_nlink, MAXLOGNAME - 1, + user_from_uid(sb->st_uid, 0), MAXLOGNAME - 1, + group_from_gid(sb->st_gid, 0)); + + if (S_ISCHR(sb->st_mode) || S_ISBLK(sb->st_mode)) + (void)printf("%#8jx ", (uintmax_t)sb->st_rdev); + else + (void)printf("%8"PRId64" ", sb->st_size); + printtime(sb->st_mtime); + (void)printf("%s", name); + if (S_ISLNK(sb->st_mode)) + printlink(accpath); + (void)putchar('\n'); +} + +static void +printtime(time_t ftime) +{ + char longstring[80]; + static time_t lnow; + const char *format; + static int d_first = -1; + + if (d_first < 0) + d_first = (*nl_langinfo(D_MD_ORDER) == 'd'); + if (lnow == 0) + lnow = time(NULL); + +#define SIXMONTHS ((365 / 2) * 86400) + if (ftime + SIXMONTHS > lnow && ftime < lnow + SIXMONTHS) + /* mmm dd hh:mm || dd mmm hh:mm */ + format = d_first ? "%e %b %R " : "%b %e %R "; + else + /* mmm dd yyyy || dd mmm yyyy */ + format = d_first ? "%e %b %Y " : "%b %e %Y "; + strftime(longstring, sizeof(longstring), format, localtime(&ftime)); + fputs(longstring, stdout); +} + +static void +printlink(char *name) +{ + int lnklen; + char path[MAXPATHLEN]; + + if ((lnklen = readlink(name, path, MAXPATHLEN - 1)) == -1) { + warn("%s", name); + return; + } + path[lnklen] = '\0'; + (void)printf(" -> %s", path); +} diff --git a/shell_cmds/find/main.c b/shell_cmds/find/main.c new file mode 100644 index 0000000..e95e648 --- /dev/null +++ b/shell_cmds/find/main.c @@ -0,0 +1,166 @@ +/*- + * Copyright (c) 1990, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Cimarron D. Taylor of the University of California, Berkeley. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static const char copyright[] = +"@(#) Copyright (c) 1990, 1993, 1994\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)main.c 8.4 (Berkeley) 5/4/95"; +#endif +#endif /* not lint */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD: src/usr.bin/find/main.c,v 1.23 2011/12/10 18:11:06 ed Exp $"); + +#include <sys/types.h> +#include <sys/stat.h> + +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <fts.h> +#include <locale.h> +#include <regex.h> +#include <stdio.h> +#include <stdlib.h> +#include <time.h> +#include <unistd.h> + +#include "find.h" + +time_t now; /* time find was run */ +int dotfd; /* starting directory */ +int ftsoptions; /* options for the ftsopen(3) call */ +int isdeprecated; /* using deprecated syntax */ +int isdepth; /* do directories on post-order visit */ +int isoutput; /* user specified output operator */ +int issort; /* do hierarchies in lexicographical order */ +int isxargs; /* don't permit xargs delimiting chars */ +int mindepth = -1, maxdepth = -1; /* minimum and maximum depth */ +int regexp_flags = REG_BASIC; /* use the "basic" regexp by default*/ + +static void usage(void); + +int +main(int argc, char *argv[]) +{ + char **p, **start; + int Hflag, Lflag, ch; + + (void)setlocale(LC_ALL, ""); + + (void)time(&now); /* initialize the time-of-day */ + + p = start = argv; + Hflag = Lflag = 0; + ftsoptions = FTS_NOSTAT | FTS_PHYSICAL; + while ((ch = getopt(argc, argv, "EHLPXdf:sx")) != -1) + switch (ch) { + case 'E': + regexp_flags |= REG_EXTENDED; + break; + case 'H': + Hflag = 1; + Lflag = 0; + break; + case 'L': + Lflag = 1; + Hflag = 0; + break; + case 'P': + Hflag = Lflag = 0; + break; + case 'X': + isxargs = 1; + break; + case 'd': + isdepth = 1; + break; + case 'f': + *p++ = optarg; + break; + case 's': + issort = 1; + break; + case 'x': + ftsoptions |= FTS_XDEV; + break; + case '?': + default: + usage(); + } + + argc -= optind; + argv += optind; + + if (Hflag) + ftsoptions |= FTS_COMFOLLOW; + if (Lflag) { + ftsoptions &= ~FTS_PHYSICAL; + ftsoptions |= FTS_LOGICAL; + } + + /* + * Find first option to delimit the file list. The first argument + * that starts with a -, or is a ! or a ( must be interpreted as a + * part of the find expression, according to POSIX .2. + */ + for (; *argv != NULL; *p++ = *argv++) { + if (argv[0][0] == '-') + break; + if ((argv[0][0] == '!' || argv[0][0] == '(') && + argv[0][1] == '\0') + break; + } + + if (p == start) + usage(); + *p = NULL; + + if ((dotfd = open(".", O_RDONLY, 0)) < 0) + err(1, "."); + + exit(find_execute(find_formplan(argv), start)); +} + +static void +usage(void) +{ + (void)fprintf(stderr, "%s\n%s\n", +"usage: find [-H | -L | -P] [-EXdsx] [-f path] path ... [expression]", +" find [-H | -L | -P] [-EXdsx] -f path [path ...] [expression]"); + exit(1); +} diff --git a/shell_cmds/find/misc.c b/shell_cmds/find/misc.c new file mode 100644 index 0000000..a165c5e --- /dev/null +++ b/shell_cmds/find/misc.c @@ -0,0 +1,106 @@ +/*- + * Copyright (c) 1990, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Cimarron D. Taylor of the University of California, Berkeley. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)misc.c 8.2 (Berkeley) 4/1/94"; +#else +#endif +#endif /* not lint */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD: src/usr.bin/find/misc.c,v 1.13 2010/12/11 08:32:16 joel Exp $"); + +#include <sys/types.h> +#include <sys/stat.h> + +#include <err.h> +#include <errno.h> +#include <fts.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "find.h" + +/* + * brace_subst -- + * Replace occurrences of {} in s1 with s2 and return the result string. + */ +void +brace_subst(char *orig, char **store, char *path, int len) +{ + int plen; + char ch, *p; + + plen = strlen(path); + for (p = *store; (ch = *orig) != '\0'; ++orig) + if (ch == '{' && orig[1] == '}') { + while ((p - *store) + plen > len) + if (!(*store = realloc(*store, len *= 2))) + err(1, NULL); + memmove(p, path, plen); + p += plen; + ++orig; + } else + *p++ = ch; + *p = '\0'; +} + +/* + * queryuser -- + * print a message to standard error and then read input from standard + * input. If the input is an affirmative response (according to the + * current locale) then 1 is returned. + */ +int +queryuser(char *argv[]) +{ + char *p, resp[256]; + + (void)fprintf(stderr, "\"%s", *argv); + while (*++argv) + (void)fprintf(stderr, " %s", *argv); + (void)fprintf(stderr, "\"? "); + (void)fflush(stderr); + + if (fgets(resp, sizeof(resp), stdin) == NULL) + *resp = '\0'; + if ((p = strchr(resp, '\n')) != NULL) + *p = '\0'; + else { + (void)fprintf(stderr, "\n"); + (void)fflush(stderr); + } + + return (rpmatch(resp) == 1); +} diff --git a/shell_cmds/find/operator.c b/shell_cmds/find/operator.c new file mode 100644 index 0000000..1aa851e --- /dev/null +++ b/shell_cmds/find/operator.c @@ -0,0 +1,273 @@ +/*- + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Cimarron D. Taylor of the University of California, Berkeley. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)operator.c 8.1 (Berkeley) 6/6/93"; +#endif +#endif /* not lint */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD: src/usr.bin/find/operator.c,v 1.17 2010/12/11 08:32:16 joel Exp $"); + +#include <sys/types.h> + +#include <err.h> +#include <fts.h> +#include <stdio.h> + +#include "find.h" + +static PLAN *yanknode(PLAN **); +static PLAN *yankexpr(PLAN **); + +/* + * yanknode -- + * destructively removes the top from the plan + */ +static PLAN * +yanknode(PLAN **planp) +{ + PLAN *node; /* top node removed from the plan */ + + if ((node = (*planp)) == NULL) + return (NULL); + (*planp) = (*planp)->next; + node->next = NULL; + return (node); +} + +/* + * yankexpr -- + * Removes one expression from the plan. This is used mainly by + * paren_squish. In comments below, an expression is either a + * simple node or a f_expr node containing a list of simple nodes. + */ +static PLAN * +yankexpr(PLAN **planp) +{ + PLAN *next; /* temp node holding subexpression results */ + PLAN *node; /* pointer to returned node or expression */ + PLAN *tail; /* pointer to tail of subplan */ + PLAN *subplan; /* pointer to head of ( ) expression */ + + /* first pull the top node from the plan */ + if ((node = yanknode(planp)) == NULL) + return (NULL); + + /* + * If the node is an '(' then we recursively slurp up expressions + * until we find its associated ')'. If it's a closing paren we + * just return it and unwind our recursion; all other nodes are + * complete expressions, so just return them. + */ + if (node->execute == f_openparen) + for (tail = subplan = NULL;;) { + if ((next = yankexpr(planp)) == NULL) + errx(1, "(: missing closing ')'"); + /* + * If we find a closing ')' we store the collected + * subplan in our '(' node and convert the node to + * a f_expr. The ')' we found is ignored. Otherwise, + * we just continue to add whatever we get to our + * subplan. + */ + if (next->execute == f_closeparen) { + if (subplan == NULL) + errx(1, "(): empty inner expression"); + node->p_data[0] = subplan; + node->execute = f_expr; + break; + } else { + if (subplan == NULL) + tail = subplan = next; + else { + tail->next = next; + tail = next; + } + tail->next = NULL; + } + } + return (node); +} + +/* + * paren_squish -- + * replaces "parenthesized" plans in our search plan with "expr" nodes. + */ +PLAN * +paren_squish(PLAN *plan) +{ + PLAN *expr; /* pointer to next expression */ + PLAN *tail; /* pointer to tail of result plan */ + PLAN *result; /* pointer to head of result plan */ + + result = tail = NULL; + + /* + * the basic idea is to have yankexpr do all our work and just + * collect its results together. + */ + while ((expr = yankexpr(&plan)) != NULL) { + /* + * if we find an unclaimed ')' it means there is a missing + * '(' someplace. + */ + if (expr->execute == f_closeparen) + errx(1, "): no beginning '('"); + + /* add the expression to our result plan */ + if (result == NULL) + tail = result = expr; + else { + tail->next = expr; + tail = expr; + } + tail->next = NULL; + } + return (result); +} + +/* + * not_squish -- + * compresses "!" expressions in our search plan. + */ +PLAN * +not_squish(PLAN *plan) +{ + PLAN *next; /* next node being processed */ + PLAN *node; /* temporary node used in f_not processing */ + PLAN *tail; /* pointer to tail of result plan */ + PLAN *result; /* pointer to head of result plan */ + + tail = result = NULL; + + while ((next = yanknode(&plan))) { + /* + * if we encounter a ( expression ) then look for nots in + * the expr subplan. + */ + if (next->execute == f_expr) + next->p_data[0] = not_squish(next->p_data[0]); + + /* + * if we encounter a not, then snag the next node and place + * it in the not's subplan. As an optimization we compress + * several not's to zero or one not. + */ + if (next->execute == f_not) { + int notlevel = 1; + + node = yanknode(&plan); + while (node != NULL && node->execute == f_not) { + ++notlevel; + node = yanknode(&plan); + } + if (node == NULL) + errx(1, "!: no following expression"); + if (node->execute == f_or) + errx(1, "!: nothing between ! and -o"); + /* + * If we encounter ! ( expr ) then look for nots in + * the expr subplan. + */ + if (node->execute == f_expr) + node->p_data[0] = not_squish(node->p_data[0]); + if (notlevel % 2 != 1) + next = node; + else + next->p_data[0] = node; + } + + /* add the node to our result plan */ + if (result == NULL) + tail = result = next; + else { + tail->next = next; + tail = next; + } + tail->next = NULL; + } + return (result); +} + +/* + * or_squish -- + * compresses -o expressions in our search plan. + */ +PLAN * +or_squish(PLAN *plan) +{ + PLAN *next; /* next node being processed */ + PLAN *tail; /* pointer to tail of result plan */ + PLAN *result; /* pointer to head of result plan */ + + tail = result = next = NULL; + + while ((next = yanknode(&plan)) != NULL) { + /* + * if we encounter a ( expression ) then look for or's in + * the expr subplan. + */ + if (next->execute == f_expr) + next->p_data[0] = or_squish(next->p_data[0]); + + /* if we encounter a not then look for or's in the subplan */ + if (next->execute == f_not) + next->p_data[0] = or_squish(next->p_data[0]); + + /* + * if we encounter an or, then place our collected plan in the + * or's first subplan and then recursively collect the + * remaining stuff into the second subplan and return the or. + */ + if (next->execute == f_or) { + if (result == NULL) + errx(1, "-o: no expression before -o"); + next->p_data[0] = result; + next->p_data[1] = or_squish(plan); + if (next->p_data[1] == NULL) + errx(1, "-o: no expression after -o"); + return (next); + } + + /* add the node to our result plan */ + if (result == NULL) + tail = result = next; + else { + tail->next = next; + tail = next; + } + tail->next = NULL; + } + return (result); +} diff --git a/shell_cmds/find/option.c b/shell_cmds/find/option.c new file mode 100644 index 0000000..5771da4 --- /dev/null +++ b/shell_cmds/find/option.c @@ -0,0 +1,201 @@ +/*- + * Copyright (c) 1990, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Cimarron D. Taylor of the University of California, Berkeley. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +/* +static char sccsid[] = "@(#)option.c 8.2 (Berkeley) 4/16/94"; +*/ +#endif /* not lint */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD: src/usr.bin/find/option.c,v 1.32 2011/05/27 22:14:49 jilles Exp $"); + +#include <sys/types.h> +#include <sys/stat.h> + +#include <err.h> +#include <fts.h> +#include <regex.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "find.h" + +static int typecompare(const void *, const void *); + +/* NB: the following table must be sorted lexically. */ +/* Options listed with C++ comments are in gnu find, but not our find */ +static OPTION const options[] = { + { "!", c_simple, f_not, 0 }, + { "(", c_simple, f_openparen, 0 }, + { ")", c_simple, f_closeparen, 0 }, + { "-Bmin", c_Xmin, f_Xmin, F_TIME_B }, + { "-Bnewer", c_newer, f_newer, F_TIME_B }, + { "-Btime", c_Xtime, f_Xtime, F_TIME_B }, + { "-a", c_and, NULL, 0 }, + { "-acl", c_acl, f_acl, 0 }, + { "-amin", c_Xmin, f_Xmin, F_TIME_A }, + { "-and", c_and, NULL, 0 }, + { "-anewer", c_newer, f_newer, F_TIME_A }, + { "-atime", c_Xtime, f_Xtime, F_TIME_A }, + { "-cmin", c_Xmin, f_Xmin, F_TIME_C }, + { "-cnewer", c_newer, f_newer, F_TIME_C }, + { "-ctime", c_Xtime, f_Xtime, F_TIME_C }, + { "-d", c_depth, f_depth, 0 }, +// -daystart + { "-delete", c_delete, f_delete, 0 }, + { "-depth", c_depth, f_depth, 0 }, + { "-empty", c_empty, f_empty, 0 }, + { "-exec", c_exec, f_exec, 0 }, + { "-execdir", c_exec, f_exec, F_EXECDIR }, + { "-false", c_simple, f_false, 0 }, + { "-flags", c_flags, f_flags, 0 }, +// -fls + { "-follow", c_follow, f_always_true, 0 }, +// -fprint +// -fprint0 +// -fprintf + { "-fstype", c_fstype, f_fstype, 0 }, + { "-gid", c_group, f_group, 0 }, + { "-group", c_group, f_group, 0 }, + { "-ignore_readdir_race",c_simple, f_always_true,0 }, + { "-ilname", c_name, f_name, F_LINK | F_IGNCASE }, + { "-iname", c_name, f_name, F_IGNCASE }, + { "-inum", c_inum, f_inum, 0 }, + { "-ipath", c_name, f_path, F_IGNCASE }, + { "-iregex", c_regex, f_regex, F_IGNCASE }, + { "-iwholename",c_name, f_path, F_IGNCASE }, + { "-links", c_links, f_links, 0 }, + { "-lname", c_name, f_name, F_LINK }, + { "-ls", c_ls, f_ls, 0 }, + { "-maxdepth", c_mXXdepth, f_always_true, F_MAXDEPTH }, + { "-mindepth", c_mXXdepth, f_always_true, 0 }, + { "-mmin", c_Xmin, f_Xmin, 0 }, + { "-mnewer", c_newer, f_newer, 0 }, + { "-mount", c_xdev, f_always_true, 0 }, + { "-mtime", c_Xtime, f_Xtime, 0 }, + { "-name", c_name, f_name, 0 }, + { "-newer", c_newer, f_newer, 0 }, + { "-newerBB", c_newer, f_newer, F_TIME_B | F_TIME2_B }, + { "-newerBa", c_newer, f_newer, F_TIME_B | F_TIME2_A }, + { "-newerBc", c_newer, f_newer, F_TIME_B | F_TIME2_C }, + { "-newerBm", c_newer, f_newer, F_TIME_B }, + { "-newerBt", c_newer, f_newer, F_TIME_B | F_TIME2_T }, + { "-neweraB", c_newer, f_newer, F_TIME_A | F_TIME2_B }, + { "-neweraa", c_newer, f_newer, F_TIME_A | F_TIME2_A }, + { "-newerac", c_newer, f_newer, F_TIME_A | F_TIME2_C }, + { "-neweram", c_newer, f_newer, F_TIME_A }, + { "-newerat", c_newer, f_newer, F_TIME_A | F_TIME2_T }, + { "-newercB", c_newer, f_newer, F_TIME_C | F_TIME2_B }, + { "-newerca", c_newer, f_newer, F_TIME_C | F_TIME2_A }, + { "-newercc", c_newer, f_newer, F_TIME_C | F_TIME2_C }, + { "-newercm", c_newer, f_newer, F_TIME_C }, + { "-newerct", c_newer, f_newer, F_TIME_C | F_TIME2_T }, + { "-newermB", c_newer, f_newer, F_TIME2_B }, + { "-newerma", c_newer, f_newer, F_TIME2_A }, + { "-newermc", c_newer, f_newer, F_TIME2_C }, + { "-newermm", c_newer, f_newer, 0 }, + { "-newermt", c_newer, f_newer, F_TIME2_T }, + { "-nogroup", c_nogroup, f_nogroup, 0 }, + { "-noignore_readdir_race",c_simple, f_always_true,0 }, + { "-noleaf", c_simple, f_always_true, 0 }, + { "-not", c_simple, f_not, 0 }, + { "-nouser", c_nouser, f_nouser, 0 }, + { "-o", c_simple, f_or, 0 }, + { "-ok", c_exec, f_exec, F_NEEDOK }, + { "-okdir", c_exec, f_exec, F_NEEDOK | F_EXECDIR }, + { "-or", c_simple, f_or, 0 }, + { "-path", c_name, f_path, 0 }, + { "-perm", c_perm, f_perm, 0 }, + { "-print", c_print, f_print, 0 }, + { "-print0", c_print, f_print0, 0 }, +// -printf + { "-prune", c_simple, f_prune, 0 }, + { "-quit", c_simple, f_quit, 0 }, + { "-regex", c_regex, f_regex, 0 }, + { "-samefile", c_samefile, f_inum, 0 }, + { "-size", c_size, f_size, 0 }, + { "-true", c_simple, f_always_true, 0 }, + { "-type", c_type, f_type, 0 }, + { "-uid", c_user, f_user, 0 }, + { "-user", c_user, f_user, 0 }, + { "-wholename", c_name, f_path, 0 }, +#ifdef __APPLE__ + { "-xattr", c_simple, f_xattr, 0 }, + { "-xattrname", c_name, f_xattrname, 0 }, +#endif /* __APPLE__ */ + { "-xdev", c_xdev, f_always_true, 0 }, +// -xtype +}; + +/* + * find_create -- + * create a node corresponding to a command line argument. + * + * TODO: + * add create/process function pointers to node, so we can skip + * this switch stuff. + */ +PLAN * +find_create(char ***argvp) +{ + OPTION *p; + PLAN *new; + char **argv; + + argv = *argvp; + + if ((p = lookup_option(*argv)) == NULL) + errx(1, "%s: unknown primary or operator", *argv); + ++argv; + + new = (p->create)(p, &argv); + *argvp = argv; + return (new); +} + +OPTION * +lookup_option(const char *name) +{ + OPTION tmp; + + tmp.name = name; + return ((OPTION *)bsearch(&tmp, options, + sizeof(options)/sizeof(OPTION), sizeof(OPTION), typecompare)); +} + +static int +typecompare(const void *a, const void *b) +{ + return (strcmp(((const OPTION *)a)->name, ((const OPTION *)b)->name)); +} diff --git a/shell_cmds/getopt/getopt.1 b/shell_cmds/getopt/getopt.1 new file mode 100644 index 0000000..793c87a --- /dev/null +++ b/shell_cmds/getopt/getopt.1 @@ -0,0 +1,134 @@ +.\" $FreeBSD: src/usr.bin/getopt/getopt.1,v 1.15 2002/04/19 23:43:02 charnier Exp $ +.\" +.Dd April 3, 1999 +.Dt GETOPT 1 +.Os +.Sh NAME +.Nm getopt +.Nd parse command options +.Sh SYNOPSIS +.Nm args=\`getopt Ar optstring $*\` +; errcode=$?; set \-\- $args +.Sh DESCRIPTION +The +.Nm +utility is used to break up options in command lines for easy parsing by +shell procedures, and to check for legal options. +.Ar Optstring +is a string of recognized option letters (see +.Xr getopt 3 ) ; +if a letter is followed by a colon, the option +is expected to have an argument which may or may not be +separated from it by white space. +The special option +.Ql \-\- +is used to delimit the end of the options. +The +.Nm +utility will place +.Ql \-\- +in the arguments at the end of the options, +or recognize it if used explicitly. +The shell arguments +(\fB$1 $2\fR ...) are reset so that each option is +preceded by a +.Ql \- +and in its own shell argument; +each option argument is also in its own shell argument. +.Sh EXAMPLES +The following code fragment shows how one might process the arguments +for a command that can take the options +.Fl a +and +.Fl b , +and the option +.Fl o , +which requires an argument. +.Pp +.Bd -literal -offset indent +args=\`getopt abo: $*\` +# you should not use \`getopt abo: "$@"\` since that would parse +# the arguments differently from what the set command below does. +if [ $? != 0 ] +then + echo 'Usage: ...' + exit 2 +fi +set \-\- $args +# You cannot use the set command with a backquoted getopt directly, +# since the exit code from getopt would be shadowed by those of set, +# which is zero by definition. +for i +do + case "$i" + in + \-a|\-b) + echo flag $i set; sflags="${i#-}$sflags"; + shift;; + \-o) + echo oarg is "'"$2"'"; oarg="$2"; shift; + shift;; + \-\-) + shift; break;; + esac +done +echo single-char flags: "'"$sflags"'" +echo oarg is "'"$oarg"'" +.Ed +.Pp +This code will accept any of the following as equivalent: +.Pp +.Bd -literal -offset indent +cmd \-aoarg file file +cmd \-a \-o arg file file +cmd \-oarg -a file file +cmd \-a \-oarg \-\- file file +.Pp +.Ed +.Sh SEE ALSO +.Xr sh 1 , +.Xr getopt 3 +.Sh DIAGNOSTICS +The +.Nm +utility prints an error message on the standard error output and exits with +status > 0 when it encounters an option letter not included in +.Ar optstring . +.Sh HISTORY +Written by +.An Henry Spencer , +working from a Bell Labs manual page. +Behavior believed identical to the Bell version. +Example changed in +.Fx +version 3.2 and 4.0. +.Sh BUGS +Whatever +.Xr getopt 3 +has. +.Pp +Arguments containing white space or embedded shell metacharacters +generally will not survive intact; this looks easy to fix but +isn't. People trying to fix +.Nm +or the example in this manpage should check the history of this file +in +.Fx . +.Pp +The error message for an invalid option is identified as coming +from +.Nm +rather than from the shell procedure containing the invocation +of +.Nm ; +this again is hard to fix. +.Pp +The precise best way to use the +.Nm set +command to set the arguments without disrupting the value(s) of +shell options varies from one shell version to another. +.Pp +Each shellscript has to carry complex code to parse arguments halfway +correcty (like the example presented here). A better getopt-like tool +would move much of the complexity into the tool and keep the client +shell scripts simpler. diff --git a/shell_cmds/getopt/getopt.c b/shell_cmds/getopt/getopt.c new file mode 100644 index 0000000..5e8a743 --- /dev/null +++ b/shell_cmds/getopt/getopt.c @@ -0,0 +1,32 @@ +#include <sys/cdefs.h> +__RCSID("$FreeBSD: src/usr.bin/getopt/getopt.c,v 1.10 2002/09/04 23:29:01 dwmalone Exp $"); + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +int +main(int argc, char *argv[]) +{ + int c; + int status = 0; + + optind = 2; /* Past the program name and the option letters. */ + while ((c = getopt(argc, argv, argv[1])) != -1) + switch (c) { + case '?': + status = 1; /* getopt routine gave message */ + break; + default: + if (optarg != NULL) + printf(" -%c %s", c, optarg); + else + printf(" -%c", c); + break; + } + printf(" --"); + for (; optind < argc; optind++) + printf(" %s", argv[optind]); + printf("\n"); + return status; +} diff --git a/shell_cmds/hexdump/conv.c b/shell_cmds/hexdump/conv.c new file mode 100644 index 0000000..9eebc51 --- /dev/null +++ b/shell_cmds/hexdump/conv.c @@ -0,0 +1,178 @@ +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static const char sccsid[] = "@(#)conv.c 8.1 (Berkeley) 6/6/93"; +#endif /* not lint */ +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/types.h> + +#include <assert.h> +#include <ctype.h> +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <wchar.h> +#include <wctype.h> +#include "hexdump.h" + +void +conv_c(PR *pr, u_char *p, size_t bufsize) +{ + char buf[10]; + char const *str; + wchar_t wc; + size_t clen, oclen; + int converr, pad, width; + u_char peekbuf[MB_LEN_MAX]; + + if (pr->mbleft > 0) { + str = "**"; + pr->mbleft--; + goto strpr; + } + + switch(*p) { + case '\0': + str = "\\0"; + goto strpr; + /* case '\a': */ + case '\007': + str = "\\a"; + goto strpr; + case '\b': + str = "\\b"; + goto strpr; + case '\f': + str = "\\f"; + goto strpr; + case '\n': + str = "\\n"; + goto strpr; + case '\r': + str = "\\r"; + goto strpr; + case '\t': + str = "\\t"; + goto strpr; + case '\v': + str = "\\v"; + goto strpr; + default: + break; + } + /* + * Multibyte characters are disabled for hexdump(1) for backwards + * compatibility and consistency (none of its other output formats + * recognize them correctly). + */ + converr = 0; + if (odmode && MB_CUR_MAX > 1) { + oclen = 0; +retry: + clen = mbrtowc(&wc, (const char *)p, bufsize, &pr->mbstate); + if (clen == 0) + clen = 1; + else if (clen == (size_t)-1 || (clen == (size_t)-2 && + p == peekbuf)) { + memset(&pr->mbstate, 0, sizeof(pr->mbstate)); + wc = *p; + clen = 1; + converr = 1; + } else if (clen == (size_t)-2) { + /* + * Incomplete character; peek ahead and see if we + * can complete it. + */ + oclen = bufsize; + bufsize = peek(p = peekbuf, MB_CUR_MAX); + goto retry; + } + clen += oclen; + } else { + wc = *p; + clen = 1; + } + if (!converr && iswprint(wc)) { + if (!odmode) { + *pr->cchar = 'c'; + (void)printf(pr->fmt, (int)wc); + } else { + *pr->cchar = 'C'; + assert(strcmp(pr->fmt, "%3C") == 0); + width = wcwidth(wc); + assert(width >= 0); + pad = 3 - width; + if (pad < 0) + pad = 0; + (void)printf("%*s%C", pad, "", wc); + pr->mbleft = clen - 1; + } + } else { + (void)sprintf(buf, "%03o", (int)*p); + str = buf; +strpr: *pr->cchar = 's'; + (void)printf(pr->fmt, str); + } +} + +void +conv_u(PR *pr, u_char *p) +{ + static char const * list[] = { + "nul", "soh", "stx", "etx", "eot", "enq", "ack", "bel", + "bs", "ht", "lf", "vt", "ff", "cr", "so", "si", + "dle", "dc1", "dc2", "dc3", "dc4", "nak", "syn", "etb", + "can", "em", "sub", "esc", "fs", "gs", "rs", "us", + }; + + /* od used nl, not lf */ + if (*p <= 0x1f) { + *pr->cchar = 's'; + if (odmode && *p == 0x0a) + (void)printf(pr->fmt, "nl"); + else + (void)printf(pr->fmt, list[*p]); + } else if (*p == 0x7f) { + *pr->cchar = 's'; + (void)printf(pr->fmt, "del"); + } else if (odmode && *p == 0x20) { /* od replaced space with sp */ + *pr->cchar = 's'; + (void)printf(pr->fmt, " sp"); + } else if (isprint(*p)) { + *pr->cchar = 'c'; + (void)printf(pr->fmt, *p); + } else { + *pr->cchar = 'x'; + (void)printf(pr->fmt, (int)*p); + } +} diff --git a/shell_cmds/hexdump/display.c b/shell_cmds/hexdump/display.c new file mode 100644 index 0000000..978a50c --- /dev/null +++ b/shell_cmds/hexdump/display.c @@ -0,0 +1,417 @@ +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)display.c 8.1 (Berkeley) 6/6/93"; +#endif +#endif /* not lint */ +#include <sys/cdefs.h> +__FBSDID("$FreeBSD: src/usr.bin/hexdump/display.c,v 1.19 2004/07/11 01:11:12 tjr Exp $"); + +#include <sys/param.h> +#include <sys/stat.h> + +#include <ctype.h> +#include <err.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include "hexdump.h" + +enum _vflag vflag = FIRST; + +static off_t address; /* address/offset in stream */ +static off_t eaddress; /* end address */ + +static __inline void print(PR *, u_char *); + +void +display(void) +{ + FS *fs; + FU *fu; + PR *pr; + int cnt; + u_char *bp; + off_t saveaddress; + u_char savech=0, *savebp; + + while ((bp = get())) + for (fs = fshead, savebp = bp, saveaddress = address; fs; + fs = fs->nextfs, bp = savebp, address = saveaddress) + for (fu = fs->nextfu; fu; fu = fu->nextfu) { + if (fu->flags&F_IGNORE) + break; + for (cnt = fu->reps; cnt; --cnt) + for (pr = fu->nextpr; pr; address += pr->bcnt, + bp += pr->bcnt, pr = pr->nextpr) { + if (eaddress && address >= eaddress && + !(pr->flags & (F_TEXT|F_BPAD))) + bpad(pr); + if (cnt == 1 && pr->nospace) { + savech = *pr->nospace; + *pr->nospace = '\0'; + } + print(pr, bp); + if (cnt == 1 && pr->nospace) + *pr->nospace = savech; + } + } + if (endfu) { + /* + * If eaddress not set, error or file size was multiple of + * blocksize, and no partial block ever found. + */ + if (!eaddress) { + if (!address) { + return; + } + eaddress = address; + } + for (pr = endfu->nextpr; pr; pr = pr->nextpr) + switch(pr->flags) { + case F_ADDRESS: + (void)printf(pr->fmt, (quad_t)eaddress); + break; + case F_TEXT: + (void)printf("%s", pr->fmt); + break; + default: + break; + } + } +} + +static __inline void +print(PR *pr, u_char *bp) +{ + long double ldbl; + double f8; + float f4; + int16_t s2; + int8_t s8; + int32_t s4; + u_int16_t u2; + u_int32_t u4; + u_int64_t u8; + + switch(pr->flags) { + case F_ADDRESS: + (void)printf(pr->fmt, (quad_t)address); + break; + case F_BPAD: + (void)printf(pr->fmt, ""); + break; + case F_C: + conv_c(pr, bp, eaddress ? eaddress - address : + blocksize - address % blocksize); + break; + case F_CHAR: + (void)printf(pr->fmt, *bp); + break; + case F_DBL: + switch(pr->bcnt) { + case 4: + bcopy(bp, &f4, sizeof(f4)); + (void)printf(pr->fmt, f4); + break; + case 8: + bcopy(bp, &f8, sizeof(f8)); + (void)printf(pr->fmt, f8); + break; + default: + if (pr->bcnt == sizeof(long double)) { + bcopy(bp, &ldbl, sizeof(ldbl)); + (void)printf(pr->fmt, ldbl); + } + break; + } + break; + case F_INT: + switch(pr->bcnt) { + case 1: + (void)printf(pr->fmt, (quad_t)(signed char)*bp); + break; + case 2: + bcopy(bp, &s2, sizeof(s2)); + (void)printf(pr->fmt, (quad_t)s2); + break; + case 4: + bcopy(bp, &s4, sizeof(s4)); + (void)printf(pr->fmt, (quad_t)s4); + break; + case 8: + bcopy(bp, &s8, sizeof(s8)); + (void)printf(pr->fmt, s8); + break; + } + break; + case F_P: + (void)printf(pr->fmt, isprint(*bp) && isascii(*bp) ? *bp : '.'); + break; + case F_STR: + (void)printf(pr->fmt, (char *)bp); + break; + case F_TEXT: + (void)printf("%s", pr->fmt); + break; + case F_U: + conv_u(pr, bp); + break; + case F_UINT: + switch(pr->bcnt) { + case 1: + (void)printf(pr->fmt, (u_quad_t)*bp); + break; + case 2: + bcopy(bp, &u2, sizeof(u2)); + (void)printf(pr->fmt, (u_quad_t)u2); + break; + case 4: + bcopy(bp, &u4, sizeof(u4)); + (void)printf(pr->fmt, (u_quad_t)u4); + break; + case 8: + bcopy(bp, &u8, sizeof(u8)); + (void)printf(pr->fmt, u8); + break; + } + break; + } +} + +void +bpad(PR *pr) +{ + static char const *spec = " -0+#"; + char *p1, *p2; + + /* + * Remove all conversion flags; '-' is the only one valid + * with %s, and it's not useful here. + */ + pr->flags = F_BPAD; + pr->cchar[0] = 's'; + pr->cchar[1] = '\0'; + for (p1 = pr->fmt; *p1 != '%'; ++p1); + for (p2 = ++p1; *p1 && index(spec, *p1); ++p1); + while ((*p2++ = *p1++)); +} + +static char **_argv; + +u_char * +get(void) +{ + static int ateof = 1; + static u_char *curp, *savp; + int n; + int need, nread; + int valid_save = 0; + u_char *tmpp; + + if (!curp) { + if ((curp = calloc(1, blocksize)) == NULL) + err(1, NULL); + if ((savp = calloc(1, blocksize)) == NULL) + err(1, NULL); + } else { + tmpp = curp; + curp = savp; + savp = tmpp; + address += blocksize; + valid_save = 1; + } + for (need = blocksize, nread = 0;;) { + /* + * if read the right number of bytes, or at EOF for one file, + * and no other files are available, zero-pad the rest of the + * block and set the end flag. + */ + if (!length || (ateof && !next((char **)NULL))) { + if (odmode && address < skip) + errx(1, "cannot skip past end of input"); + if (need == blocksize) + return((u_char *)NULL); + /* + * XXX bcmp() is not quite right in the presence + * of multibyte characters. + */ +#ifdef __APPLE__ + /* 5650060 */ + if (!need && vflag != ALL && +#else + if (vflag != ALL && +#endif + valid_save && + bcmp(curp, savp, nread) == 0) { + if (vflag != DUP) + (void)printf("*\n"); + return((u_char *)NULL); + } + bzero((char *)curp + nread, need); + eaddress = address + nread; + if (length == 0) + lseek(STDIN_FILENO, ftell(stdin), SEEK_SET); /* rewind stdin for next process */ + return(curp); + } + n = fread((char *)curp + nread, sizeof(u_char), + length == -1 ? need : MIN(length, need), stdin); + if (!n) { + if (ferror(stdin)) + warn("%s", _argv[-1]); + ateof = 1; + continue; + } + ateof = 0; + if (length != -1) + length -= n; + if (!(need -= n)) { + /* + * XXX bcmp() is not quite right in the presence + * of multibyte characters. + */ + if (vflag == ALL || vflag == FIRST || + valid_save == 0 || + bcmp(curp, savp, blocksize) != 0) { + if (vflag == DUP || vflag == FIRST) + vflag = WAIT; + return(curp); + } + if (vflag == WAIT) + (void)printf("*\n"); + vflag = DUP; + address += blocksize; + need = blocksize; + nread = 0; + } + else + nread += n; + } +} + +size_t +peek(u_char *buf, size_t nbytes) +{ + size_t n, nread; + int c; + + if (length != -1 && nbytes > length) + nbytes = length; + nread = 0; + while (nread < nbytes && (c = getchar()) != EOF) { + *buf++ = c; + nread++; + } + n = nread; + while (n-- > 0) { + c = *--buf; + ungetc(c, stdin); + } + return (nread); +} + +int +next(char **argv) +{ + static int done; + int statok; + + if (argv) { + _argv = argv; + return(1); + } + for (;;) { + if (*_argv) { + if (!(freopen(*_argv, "r", stdin))) { + warn("%s", *_argv); + exitval = 1; + ++_argv; + continue; + } + statok = done = 1; + } else { + if (done++) + return(0); + statok = 0; + } + if (skip) + doskip(statok ? *_argv : "stdin", statok); + if (*_argv) + ++_argv; + if (!skip) + return(1); + } + /* NOTREACHED */ +} + +void +doskip(const char *fname, int statok) +{ + int cnt; + struct stat sb; + + if (statok) { + if (fstat(fileno(stdin), &sb)) + err(1, "%s", fname); + if (S_ISREG(sb.st_mode) && skip >= sb.st_size) { + address += sb.st_size; + skip -= sb.st_size; + return; + } + } +#ifdef __APPLE__ + /* try to seek first; fall back on ESPIPE */ + if (fseeko(stdin, skip, SEEK_SET) == 0) { +#else /* !__APPLE__ */ + if (S_ISREG(sb.st_mode)) { + if (fseeko(stdin, skip, SEEK_SET)) + err(1, "%s", fname); +#endif /* __APPLE__ */ + address += skip; + skip = 0; + } else { +#ifdef __APPLE__ + if (errno != ESPIPE) + err(1, "%s", fname); +#endif /* __APPLE__ */ + for (cnt = 0; cnt < skip; ++cnt) + if (getchar() == EOF) + break; + address += cnt; + skip -= cnt; + } +} diff --git a/shell_cmds/hexdump/hexdump.1 b/shell_cmds/hexdump/hexdump.1 new file mode 100644 index 0000000..5e49a64 --- /dev/null +++ b/shell_cmds/hexdump/hexdump.1 @@ -0,0 +1,342 @@ +.\" Copyright (c) 1989, 1990, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)hexdump.1 8.2 (Berkeley) 4/18/94 +.\" $FreeBSD: src/usr.bin/hexdump/hexdump.1,v 1.21 2004/07/10 13:11:00 tjr Exp $ +.\" +.Dd July 10, 2004 +.Dt HEXDUMP 1 +.Os +.Sh NAME +.Nm hexdump +.Nd ASCII, decimal, hexadecimal, octal dump +.Sh SYNOPSIS +.Nm +.Op Fl bcCdovx +.Op Fl e Ar format_string +.Op Fl f Ar format_file +.Op Fl n Ar length +.Bk -words +.Op Fl s Ar skip +.Ek +.Ar +.Sh DESCRIPTION +The +.Nm +utility is a filter which displays the specified files, or +the standard input, if no files are specified, in a user specified +format. +.Pp +The options are as follows: +.Bl -tag -width indent +.It Fl b +.Em One-byte octal display . +Display the input offset in hexadecimal, followed by sixteen +space-separated, three column, zero-filled, bytes of input data, +in octal, per line. +.It Fl C +.Em Canonical hex+ASCII display . +Display the input offset in hexadecimal, followed by sixteen +space-separated, two column, hexadecimal bytes, followed by the +same sixteen bytes in %_p format enclosed in ``|'' characters. +.It Fl c +.Em One-byte character display . +Display the input offset in hexadecimal, followed by sixteen +space-separated, three column, space-filled, characters of input +data per line. +.It Fl d +.Em Two-byte decimal display . +Display the input offset in hexadecimal, followed by eight +space-separated, five column, zero-filled, two-byte units +of input data, in unsigned decimal, per line. +.It Fl e Ar format_string +Specify a format string to be used for displaying data. +.It Fl f Ar format_file +Specify a file that contains one or more newline separated format strings. +Empty lines and lines whose first non-blank character is a hash mark +.Pf ( Cm \&# ) +are ignored. +.It Fl n Ar length +Interpret only +.Ar length +bytes of input. +.It Fl o +.Em Two-byte octal display . +Display the input offset in hexadecimal, followed by eight +space-separated, six column, zero-filled, two byte quantities of +input data, in octal, per line. +.It Fl s Ar offset +Skip +.Ar offset +bytes from the beginning of the input. +By default, +.Ar offset +is interpreted as a decimal number. +With a leading +.Cm 0x +or +.Cm 0X , +.Ar offset +is interpreted as a hexadecimal number, +otherwise, with a leading +.Cm 0 , +.Ar offset +is interpreted as an octal number. +Appending the character +.Cm b , +.Cm k , +.Cm m , +or +.Cm g +to +.Ar offset +causes it to be interpreted as a multiple of +.Li 512 , +.Li 1024 , +.Li 1048576 , +or +.Li 1073741824 , +respectively. +.It Fl v +Cause +.Nm +to display all input data. +Without the +.Fl v +option, any number of groups of output lines, which would be +identical to the immediately preceding group of output lines (except +for the input offsets), are replaced with a line comprised of a +single asterisk. +.It Fl x +.Em Two-byte hexadecimal display . +Display the input offset in hexadecimal, followed by eight, space +separated, four column, zero-filled, two-byte quantities of input +data, in hexadecimal, per line. +.El +.Pp +For each input file, +.Nm +sequentially copies the input to standard output, transforming the +data according to the format strings specified by the +.Fl e +and +.Fl f +options, in the order that they were specified. +.Ss Formats +A format string contains any number of format units, separated by +whitespace. +A format unit contains up to three items: an iteration count, a byte +count, and a format. +.Pp +The iteration count is an optional positive integer, which defaults to +one. +Each format is applied iteration count times. +.Pp +The byte count is an optional positive integer. +If specified it defines the number of bytes to be interpreted by +each iteration of the format. +.Pp +If an iteration count and/or a byte count is specified, a single slash +must be placed after the iteration count and/or before the byte count +to disambiguate them. +Any whitespace before or after the slash is ignored. +.Pp +The format is required and must be surrounded by double quote +(" ") marks. +It is interpreted as a fprintf-style format string (see +.Xr fprintf 3 ) , +with the +following exceptions: +.Bl -bullet -offset indent +.It +An asterisk (*) may not be used as a field width or precision. +.It +A byte count or field precision +.Em is +required for each ``s'' conversion +character (unlike the +.Xr fprintf 3 +default which prints the entire string if the precision is unspecified). +.It +The conversion characters ``h'', ``l'', ``n'', ``p'' and ``q'' are +not supported. +.It +The single character escape sequences +described in the C standard are supported: +.Bd -ragged -offset indent -compact +.Bl -column <alert_character> +.It "NUL \e0 +.It "<alert character> \ea +.It "<backspace> \eb +.It "<form-feed> \ef +.It "<newline> \en +.It "<carriage return> \er +.It "<tab> \et +.It "<vertical tab> \ev +.El +.Ed +.El +.Pp +The +.Nm +utility also supports the following additional conversion strings: +.Bl -tag -width Fl +.It Cm \&_a Ns Op Cm dox +Display the input offset, cumulative across input files, of the +next byte to be displayed. +The appended characters +.Cm d , +.Cm o , +and +.Cm x +specify the display base +as decimal, octal or hexadecimal respectively. +.It Cm \&_A Ns Op Cm dox +Identical to the +.Cm \&_a +conversion string except that it is only performed +once, when all of the input data has been processed. +.It Cm \&_c +Output characters in the default character set. +Nonprinting characters are displayed in three character, zero-padded +octal, except for those representable by standard escape notation +(see above), +which are displayed as two character strings. +.It Cm _p +Output characters in the ASCII character set. +Non-ASCII characters are displayed as a single +.Dq Cm \&. . +.It Cm _u +Output US +.Tn ASCII +characters, with the exception that control characters are +displayed using the following, lower-case, names. +Characters greater than 0xff, hexadecimal, are displayed as hexadecimal +strings. +.Bl -column \&000_nu \&001_so \&002_st \&003_et \&004_eo +.It "\&000\ NUL\t001\ SOH\t002\ STX\t003\ ETX\t004\ EOT\t005\ ENQ +.It "\&006\ ACK\t007\ BEL\t008\ BS\t009\ HT\t00A\ LF\t00B\ VT +.It "\&00C\ FF\t00D\ CR\t00E\ SO\t00F\ SI\t010\ DLE\t011\ DC1 +.It "\&012\ DC2\t013\ DC3\t014\ DC4\t015\ NAK\t016\ SYN\t017\ ETB +.It "\&018\ CAN\t019\ EM\t01A\ SUB\t01B\ ESC\t01C\ FS\t01D\ GS +.It "\&01E\ RS\t01F\ US\t0FF\ DEL +.El +.El +.Pp +The default and supported byte counts for the conversion characters +are as follows: +.Bl -tag -width "Xc,_Xc,_Xc,_Xc,_Xc,_Xc" -offset indent +.It Li \&%_c , \&%_p , \&%_u , \&%c +One byte counts only. +.It Xo +.Li \&%d , \&%i , \&%o , +.Li \&%u , \&%X , \&%x +.Xc +Four byte default, one, two and four byte counts supported. +.It Xo +.Li \&%E , \&%e , \&%f , +.Li \&%G , \&%g +.Xc +Eight byte default, four and twelve byte counts supported. +.El +.Pp +The amount of data interpreted by each format string is the sum of the +data required by each format unit, which is the iteration count times the +byte count, or the iteration count times the number of bytes required by +the format if the byte count is not specified. +.Pp +The input is manipulated in ``blocks'', where a block is defined as the +largest amount of data specified by any format string. +Format strings interpreting less than an input block's worth of data, +whose last format unit both interprets some number of bytes and does +not have a specified iteration count, have the iteration count +incremented until the entire input block has been processed or there +is not enough data remaining in the block to satisfy the format string. +.Pp +If, either as a result of user specification or +.Nm +modifying +the iteration count as described above, an iteration count is +greater than one, no trailing whitespace characters are output +during the last iteration. +.Pp +It is an error to specify a byte count as well as multiple conversion +characters or strings unless all but one of the conversion characters +or strings is +.Cm \&_a +or +.Cm \&_A . +.Pp +If, as a result of the specification of the +.Fl n +option or end-of-file being reached, input data only partially +satisfies a format string, the input block is zero-padded sufficiently +to display all available data (i.e., any format units overlapping the +end of data will display some number of the zero bytes). +.Pp +Further output by such format strings is replaced by an equivalent +number of spaces. +An equivalent number of spaces is defined as the number of spaces +output by an +.Cm s +conversion character with the same field width +and precision as the original conversion character or conversion +string but with any +.Dq Li \&+ , +.Dq \&\ \& , +.Dq Li \&# +conversion flag characters +removed, and referencing a NULL string. +.Pp +If no format strings are specified, the default display is a +one-byte hexadecimal display. +.Sh DIAGNOSTICS +.Ex -std +.Sh EXAMPLES +Note that the following format strings, used with +.Fl e , +must be enclosed in single quotes. +.Pp +Display the input in perusal format: +.Bd -literal -offset indent +"%06.6_ao " 12/1 "%3_u " +"\et\et" "%_p " +"\en" +.Ed +.Pp +Implement the \-x option: +.Bd -literal -offset indent +"%07.7_Ax\en" +"%07.7_ax " 8/2 "%04x " "\en" +.Ed +.Sh SEE ALSO +.Xr gdb 1 , +.Xr od 1 diff --git a/shell_cmds/hexdump/hexdump.c b/shell_cmds/hexdump/hexdump.c new file mode 100644 index 0000000..78df723 --- /dev/null +++ b/shell_cmds/hexdump/hexdump.c @@ -0,0 +1,86 @@ +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static const char copyright[] = +"@(#) Copyright (c) 1989, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)hexdump.c 8.1 (Berkeley) 6/6/93"; +#endif +#endif /* not lint */ +#include <sys/cdefs.h> +__FBSDID("$FreeBSD: src/usr.bin/hexdump/hexdump.c,v 1.7 2002/09/04 23:29:01 dwmalone Exp $"); + +#include <sys/types.h> +#include <locale.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include "hexdump.h" + +FS *fshead; /* head of format strings */ +int blocksize; /* data block size */ +int exitval; /* final exit value */ +int length = -1; /* max bytes to read */ + +int +main(int argc, char *argv[]) +{ + FS *tfs; + char *p; + + (void)setlocale(LC_ALL, ""); + + if (!(p = rindex(argv[0], 'o')) || strcmp(p, "od")) + newsyntax(argc, &argv); + else + oldsyntax(argc, &argv); + + /* figure out the data block size */ + for (blocksize = 0, tfs = fshead; tfs; tfs = tfs->nextfs) { + tfs->bcnt = size(tfs); + if (blocksize < tfs->bcnt) + blocksize = tfs->bcnt; + } + /* rewrite the rules, do syntax checking */ + for (tfs = fshead; tfs; tfs = tfs->nextfs) + rewrite(tfs); + + (void)next(argv); + display(); + exit(exitval); +} diff --git a/shell_cmds/hexdump/hexdump.h b/shell_cmds/hexdump/hexdump.h new file mode 100644 index 0000000..a984287 --- /dev/null +++ b/shell_cmds/hexdump/hexdump.h @@ -0,0 +1,108 @@ +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)hexdump.h 8.1 (Berkeley) 6/6/93 + * $FreeBSD: src/usr.bin/hexdump/hexdump.h,v 1.9 2004/07/11 01:11:12 tjr Exp $ + */ + +#include <wchar.h> + +typedef struct _pr { + struct _pr *nextpr; /* next print unit */ +#define F_ADDRESS 0x001 /* print offset */ +#define F_BPAD 0x002 /* blank pad */ +#define F_C 0x004 /* %_c */ +#define F_CHAR 0x008 /* %c */ +#define F_DBL 0x010 /* %[EefGf] */ +#define F_INT 0x020 /* %[di] */ +#define F_P 0x040 /* %_p */ +#define F_STR 0x080 /* %s */ +#define F_U 0x100 /* %_u */ +#define F_UINT 0x200 /* %[ouXx] */ +#define F_TEXT 0x400 /* no conversions */ + u_int flags; /* flag values */ + int bcnt; /* byte count */ + char *cchar; /* conversion character */ + char *fmt; /* printf format */ + char *nospace; /* no whitespace version */ + int mbleft; /* bytes left of multibyte char. */ + mbstate_t mbstate; /* conversion state */ +} PR; + +typedef struct _fu { + struct _fu *nextfu; /* next format unit */ + struct _pr *nextpr; /* next print unit */ +#define F_IGNORE 0x01 /* %_A */ +#define F_SETREP 0x02 /* rep count set, not default */ + u_int flags; /* flag values */ + int reps; /* repetition count */ + int bcnt; /* byte count */ + char *fmt; /* format string */ +} FU; + +typedef struct _fs { /* format strings */ + struct _fs *nextfs; /* linked list of format strings */ + struct _fu *nextfu; /* linked list of format units */ + int bcnt; +} FS; + +extern FS *fshead; /* head of format strings list */ +extern FU *endfu; /* format at end-of-data */ +extern int blocksize; /* data block size */ +extern int exitval; /* final exit value */ +extern int odmode; /* are we acting as od(1)? */ +extern int length; /* amount of data to read */ +extern off_t skip; /* amount of data to skip at start */ +enum _vflag { ALL, DUP, FIRST, WAIT }; /* -v values */ +extern enum _vflag vflag; + +void add(const char *); +void addfile(char *); +void badcnt(char *); +void badconv(char *); +void badfmt(const char *); +void badsfmt(void); +void bpad(PR *); +void conv_c(PR *, u_char *, size_t); +void conv_u(PR *, u_char *); +void display(void); +void doskip(const char *, int); +void escape(char *); +u_char *get(void); +void newsyntax(int, char ***); +int next(char **); +void nomem(void); +void oldsyntax(int, char ***); +size_t peek(u_char *, size_t); +void rewrite(FS *); +int size(FS *); +void usage(void); diff --git a/shell_cmds/hexdump/hexsyntax.c b/shell_cmds/hexdump/hexsyntax.c new file mode 100644 index 0000000..9961f2f --- /dev/null +++ b/shell_cmds/hexdump/hexsyntax.c @@ -0,0 +1,147 @@ +/*- + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)hexsyntax.c 8.2 (Berkeley) 5/4/95"; +#endif +#endif /* not lint */ +#include <sys/cdefs.h> +__FBSDID("$FreeBSD: src/usr.bin/hexdump/hexsyntax.c,v 1.12 2002/09/04 23:29:01 dwmalone Exp $"); + +#include <sys/types.h> + +#include <err.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "hexdump.h" + +off_t skip; /* bytes to skip */ + +void +newsyntax(int argc, char ***argvp) +{ + int ch; + char *p, **argv; + + argv = *argvp; + if ((p = rindex(argv[0], 'h')) != NULL && + strcmp(p, "hd") == 0) { + /* "Canonical" format, implies -C. */ + add("\"%08.8_Ax\n\""); + add("\"%08.8_ax \" 8/1 \"%02x \" \" \" 8/1 \"%02x \" "); + add("\" |\" 16/1 \"%_p\" \"|\\n\""); + } + while ((ch = getopt(argc, argv, "bcCde:f:n:os:vx")) != -1) + switch (ch) { + case 'b': + add("\"%07.7_Ax\n\""); + add("\"%07.7_ax \" 16/1 \"%03o \" \"\\n\""); + break; + case 'c': + add("\"%07.7_Ax\n\""); + add("\"%07.7_ax \" 16/1 \"%3_c \" \"\\n\""); + break; + case 'C': + add("\"%08.8_Ax\n\""); + add("\"%08.8_ax \" 8/1 \"%02x \" \" \" 8/1 \"%02x \" "); + add("\" |\" 16/1 \"%_p\" \"|\\n\""); + break; + case 'd': + add("\"%07.7_Ax\n\""); + add("\"%07.7_ax \" 8/2 \" %05u \" \"\\n\""); + break; + case 'e': + add(optarg); + break; + case 'f': + addfile(optarg); + break; + case 'n': + if ((length = atoi(optarg)) < 0) + errx(1, "%s: bad length value", optarg); + break; + case 'o': + add("\"%07.7_Ax\n\""); + add("\"%07.7_ax \" 8/2 \" %06o \" \"\\n\""); + break; + case 's': + if ((skip = strtoll(optarg, &p, 0)) < 0) + errx(1, "%s: bad skip value", optarg); + switch(*p) { + case 'b': + skip *= 512; + break; + case 'k': + skip *= 1024; + break; + case 'm': + skip *= 1048576; + break; + case 'g': + skip *= 1073741824; + break; + } + break; + case 'v': + vflag = ALL; + break; + case 'x': + add("\"%07.7_Ax\n\""); + add("\"%07.7_ax \" 8/2 \" %04x \" \"\\n\""); + break; + case '?': + usage(); + } + + if (!fshead) { + add("\"%07.7_Ax\n\""); + add("\"%07.7_ax \" 16/1 \"%02x \" \"\\n\""); + } + + *argvp += optind; +} + +void +usage(void) +{ + (void)fprintf(stderr, "%s\n%s\n%s\n%s\n", +"usage: hexdump [-bcCdovx] [-e fmt] [-f fmt_file] [-n length]", +" [-s skip] [file ...]", +" hd [-bcdovx] [-e fmt] [-f fmt_file] [-n length]", +" [-s skip] [file ...]"); + exit(1); +} diff --git a/shell_cmds/hexdump/od.1 b/shell_cmds/hexdump/od.1 new file mode 100644 index 0000000..f827f6c --- /dev/null +++ b/shell_cmds/hexdump/od.1 @@ -0,0 +1,267 @@ +.\" Copyright (c) 1990, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)od.1 8.1 (Berkeley) 6/6/93 +.\" $FreeBSD: src/usr.bin/hexdump/od.1,v 1.17 2004/07/11 01:11:12 tjr Exp $ +.\" +.Dd July 11, 2004 +.Os +.Dt OD 1 +.Sh NAME +.Nm od +.Nd octal, decimal, hex, ASCII dump +.Sh SYNOPSIS +.Nm +.Op Fl aBbcDdeFfHhIiLlOosvXx +.Op Fl A Ar base +.Op Fl j Ar skip +.Op Fl N Ar length +.Op Fl t Ar type +.Sm off +.Oo +.Op Cm \&+ +.Li offset +.Op Cm \&. +.Op Cm Bb +.Oc +.Sm on +.Op Ar +.Sh DESCRIPTION +The +.Nm +utility is a filter which displays the specified files, or standard +input if no files are specified, in a user specified format. +.Pp +The options are as follows: +.Bl -tag -width ".Fl I , L , l" +.It Fl A Ar base +Specify the input address base. +.Ar base +may be one of +.Cm d , +.Cm o , +.Cm x +or +.Cm n , +which specify decimal, octal, hexadecimal +addresses or no address, respectively. +.It Fl a +Output named characters. +Equivalent to +.Fl t Ar a . +.It Fl B , o +Output octal shorts. +Equivalent to +.Fl t Ar o2 . +.It Fl b +Output octal bytes. +Equivalent to +.Fl t Ar o1 . +.It Fl c +Output C-style escaped characters. +Equivalent to +.Fl t Ar c . +.It Fl D +Output unsigned decimal ints. +Equivalent to +.Fl t Ar u4 . +.It Fl e , F +Output double-precision floating point numbers. +Equivalent to +.Fl t Ar fD . +.It Fl f +Output single-precision floating point numbers. +Equivalent to +.Fl t Ar fF . +.It Fl H , X +Output hexadecimal ints. +Equivalent to +.Fl t Ar x4 . +.It Fl h , x +Output hexadecimal shorts. +Equivalent to +.Fl t Ar x2 . +.It Fl I , L , l +Output signed decimal longs. +Equivalent to +.Fl t Ar dL . +.It Fl i +Output signed decimal ints. +Equivalent to +.Fl t Ar dI . +.It Fl j Ar skip +Skip +.Ar skip +bytes of the combined input before dumping. +The number may be followed by one +of +.Cm b , k +or +.Cm m +which specify the units of the number as blocks (512 bytes), kilobytes and +megabytes, respectively. +.It Fl N Ar length +Dump at most +.Ar length +bytes of input. +.It Fl O +Output octal ints. +Equivalent to +.Fl t Ar o4 . +.It Fl s +Output signed decimal shorts. +Equivalent to +.Fl t Ar d2 . +.It Fl t Ar type +Specify the output format. +.Ar type +is a string containing one or more of the following kinds of type specifiers: +.Bl -tag -width indent +.It Cm a +Named characters +.Pq Tn ASCII . +Control characters are displayed using the following names: +.Bl -column "000 NUL" "001 SOH" "002 STX" "003 ETX" "004 EOT" "005 ENQ" +.It "000 NUL 001 SOH 002 STX 003 ETX 004 EOT 005 ENQ" +.It "006 ACK 007 BEL 008 BS 009 HT 00a NL 00b VT" +.It "00c FF 00d CR 00e SO 00f SI 010 DLE 011 DC1" +.It "012 DC2 013 DC3 014 DC4 015 NAK 016 SYN 017 ETB" +.It "018 CAN 019 EM 01a SUB 01b ESC 01c FS 01d GS" +.It "01e RS 01f US 020 SP 0ff DEL" +.El +.It Cm c +Characters in the default character set. +Non-printing characters are +represented as 3-digit octal character codes, except the following +characters, which are represented as C escapes: +.Pp +.Bl -tag -width carriage-return -compact +.It NUL +\e0 +.It alert +\ea +.It backspace +\eb +.It newline +\en +.It carriage-return +\er +.It tab +\et +.It vertical tab +\ev +.El +.Pp +Multi-byte characters are displayed in the area corresponding to the first +byte of the character. The remaining bytes are shown as +.Ql ** . +.It Xo +.Sm off +.Op Cm d | o | u | x +.Op Cm C | S | I | L | Ar n +.Sm on +.Xc +Signed decimal +.Pq Cm d , +octal +.Pq Cm o , +unsigned decimal +.Pq Cm u +or +hexadecimal +.Pq Cm x . +Followed by an optional size specifier, which may be either +.Cm C +.Pq Vt char , +.Cm S +.Pq Vt short , +.Cm I +.Pq Vt int , +.Cm L +.Pq Vt long , +or a byte count as a decimal integer. +.It Xo +.Sm off +.Cm f +.Op Cm F | D | L | Ar n +.Sm on +.Xc +Floating-point number. +Followed by an optional size specifier, which may be either +.Cm F +.Pq Vt float , +.Cm D +.Pq Vt double +or +.Cm L +.Pq Vt "long double" . +.El +.It Fl v +Write all input data, instead of replacing lines of duplicate values with a +.Ql * . +.El +.Pp +Multiple options that specify output format may be used; the output will +contain one line for each format. +.Pp +If no output format is specified, +.Fl t Ar oS +is assumed. +.Sh ENVIRONMENT +The +.Ev LANG , LC_ALL +and +.Ev LC_CTYPE +environment variables affect the execution of +.Nm +as described in +.Xr environ 7 . +.Sh DIAGNOSTICS +.Ex -std +.Sh COMPATIBILITY +The traditional +.Fl s +option to extract string constants is not supported; consider using +.Xr strings 1 +instead. +.Sh SEE ALSO +.Xr hexdump 1 , +.Xr strings 1 +.Sh STANDARDS +The +.Nm +utility conforms to +.St -p1003.1-2001 . +.Sh HISTORY +An +.Nm +command appeared in +.At v1 . diff --git a/shell_cmds/hexdump/odsyntax.c b/shell_cmds/hexdump/odsyntax.c new file mode 100644 index 0000000..6b9202e --- /dev/null +++ b/shell_cmds/hexdump/odsyntax.c @@ -0,0 +1,447 @@ +/*- + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)odsyntax.c 8.2 (Berkeley) 5/4/95"; +#endif +#endif /* not lint */ +#include <sys/cdefs.h> +__FBSDID("$FreeBSD: src/usr.bin/hexdump/odsyntax.c,v 1.16 2002/09/04 23:29:01 dwmalone Exp $"); + +#include <sys/types.h> + +#include <ctype.h> +#include <err.h> +#include <errno.h> +#include <float.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "hexdump.h" + +#define PADDING " " + +int odmode; + +static void odadd(const char *); +static void odformat(const char *); +static const char *odformatfp(char, const char *); +static const char *odformatint(char, const char *); +static void odoffset(int, char ***); +static void odusage(void); + +void +oldsyntax(int argc, char ***argvp) +{ + static char _n[] = "%_n", padding[] = PADDING; + int ch; + char **argv, *end; + + /* Add initial (default) address format. -A may change it later. */ +#define TYPE_OFFSET 7 + add("\"%07.7_Ao\n\""); + add("\"%07.7_ao \""); + + odmode = 1; + argv = *argvp; + while ((ch = getopt(argc, argv, "A:aBbcDdeFfHhIij:LlN:Oost:vXx")) != -1) + switch (ch) { + case 'A': + switch (*optarg) { + case 'd': case 'o': case 'x': + fshead->nextfu->fmt[TYPE_OFFSET] = *optarg; + fshead->nextfs->nextfu->fmt[TYPE_OFFSET] = + *optarg; + break; + case 'n': + fshead->nextfu->fmt = _n; /* mimic regular output */ + fshead->nextfs->nextfu->fmt = padding; + break; + default: + errx(1, "%s: invalid address base", optarg); + } + break; + case 'a': + odformat("a"); + break; + case 'B': + case 'o': + odformat("o2"); + break; + case 'b': + odformat("o1"); + break; + case 'c': + odformat("c"); + break; + case 'd': + odformat("u2"); + break; + case 'D': + odformat("u4"); + break; + case 'e': /* undocumented in od */ + case 'F': + odformat("fD"); + break; + case 'f': + odformat("fF"); + break; + case 'H': + case 'X': + odformat("x4"); + break; + case 'h': + case 'x': + odformat("x2"); + break; + case 'I': + case 'L': + case 'l': + odformat("dL"); + break; + case 'i': + odformat("dI"); + break; + case 'j': + errno = 0; + skip = strtoll(optarg, &end, 0); + if (*end == 'b') + skip *= 512; + else if (*end == 'k') + skip *= 1024; + else if (*end == 'm') + skip *= 1048576L; + else if (*end == 'g') + skip *= 1073741824; + else if (*end != '\0') + skip = -1; + if (errno != 0 || skip < 0 || strlen(end) > 1) + errx(1, "%s: invalid skip amount", optarg); + break; + case 'N': + if ((length = atoi(optarg)) <= 0) + errx(1, "%s: invalid length", optarg); + break; + case 'O': + odformat("o4"); + break; + case 's': + odformat("d2"); + break; + case 't': + odformat(optarg); + break; + case 'v': + vflag = ALL; + break; + case '?': + default: + odusage(); + } + + if (fshead->nextfs->nextfs == NULL) + odformat("oS"); + + argc -= optind; + *argvp += optind; + + if (argc) + odoffset(argc, argvp); +} + +static void +odusage(void) +{ + + fprintf(stderr, +"usage: od [-aBbcDdeFfHhIiLlOosvXx] [-A base] [-j skip] [-N length] [-t type]\n"); + fprintf(stderr, +" [[+]offset[.][Bb]] [file ...]\n"); + exit(1); +} + +static void +odoffset(int argc, char ***argvp) +{ + unsigned char *p, *q, *num, *end; + int base; + + /* + * The offset syntax of od(1) was genuinely bizarre. First, if + * it started with a plus it had to be an offset. Otherwise, if + * there were at least two arguments, a number or lower-case 'x' + * followed by a number makes it an offset. By default it was + * octal; if it started with 'x' or '0x' it was hex. If it ended + * in a '.', it was decimal. If a 'b' or 'B' was appended, it + * multiplied the number by 512 or 1024 byte units. There was + * no way to assign a block count to a hex offset. + * + * We assume it's a file if the offset is bad. + */ + p = (unsigned char *)(argc == 1 ? (*argvp)[0] : (*argvp)[1]); + + if (*p != '+' && (argc < 2 || + (!isdigit(p[0]) && (p[0] != 'x' || !isxdigit(p[1]))))) + return; + + base = 0; + /* + * skip over leading '+', 'x[0-9a-fA-f]' or '0x', and + * set base. + */ + if (p[0] == '+') + ++p; + if (p[0] == 'x' && isxdigit(p[1])) { + ++p; + base = 16; + } else if (p[0] == '0' && p[1] == 'x') { + p += 2; + base = 16; + } + + /* skip over the number */ + if (base == 16) + for (num = p; isxdigit(*p); ++p); + else + for (num = p; isdigit(*p); ++p); + + /* check for no number */ + if (num == p) + return; + q = p; + /* if terminates with a '.', base is decimal */ + if (*q == '.') { + if (base) + return; + base = 10; + q++; + } + + skip = strtoll((const char *)num, (char **)&end, base ? base : 8); + + /* if end isn't the same as p, we got a non-octal digit */ + if (end != p) { + skip = 0; + return; + } + + if (*q) { + if (*q == 'B') { + skip *= 1024; + ++p; + } else if (*q == 'b') { + skip *= 512; + ++q; + } + } + + if (*q) { + skip = 0; + return; + } + + /* + * If the offset uses a non-octal base, the base of the offset + * is changed as well. This isn't pretty, but it's easy. + */ + if (base == 16) { + fshead->nextfu->fmt[TYPE_OFFSET] = 'x'; + fshead->nextfs->nextfu->fmt[TYPE_OFFSET] = 'x'; + } else if (base == 10) { + fshead->nextfu->fmt[TYPE_OFFSET] = 'd'; + fshead->nextfs->nextfu->fmt[TYPE_OFFSET] = 'd'; + } + + /* Terminate file list. */ + (*argvp)[1] = NULL; +} + +static void +odformat(const char *fmt) +{ + char fchar; + + while (*fmt != '\0') { + switch ((fchar = *fmt++)) { + case 'a': + odadd("16/1 \"%3_u \" \"\\n\""); + break; + case 'c': + odadd("16/1 \"%3_c \" \"\\n\""); + break; + case 'o': case 'u': case 'd': case 'x': + fmt = odformatint(fchar, fmt); + break; + case 'f': + fmt = odformatfp(fchar, fmt); + break; + default: + errx(1, "%c: unrecognised format character", fchar); + } + } +} + +static const char * +odformatfp(char fchar __unused, const char *fmt) +{ + size_t isize; + int digits; + char *end, *hdfmt; + + isize = sizeof(double); + switch (*fmt) { + case 'F': + isize = sizeof(float); + fmt++; + break; + case 'D': + isize = sizeof(double); + fmt++; + break; + case 'L': + isize = sizeof(long double); + fmt++; + break; + default: + if (isdigit((unsigned char)*fmt)) { + errno = 0; + isize = (size_t)strtoul(fmt, &end, 10); + if (errno != 0 || isize == 0) + errx(1, "%s: invalid size", fmt); + fmt = (const char *)end; + } + } + switch (isize) { + case sizeof(float): + digits = FLT_DIG; + break; + case sizeof(double): + digits = DBL_DIG; + break; + default: + if (isize == sizeof(long double)) + digits = LDBL_DIG; + else + errx(1, "unsupported floating point size %lu", + (u_long)isize); + } + + asprintf(&hdfmt, "%lu/%lu \" %%%d.%de \" \"\\n\"", + 16UL / (u_long)isize, (u_long)isize, digits + 8, digits); + if (hdfmt == NULL) + err(1, NULL); + odadd(hdfmt); + free(hdfmt); + + return (fmt); +} + +static const char * +odformatint(char fchar, const char *fmt) +{ + unsigned long long n; + size_t isize; + int digits; + char *end, *hdfmt; + + isize = sizeof(int); + switch (*fmt) { + case 'C': + isize = sizeof(char); + fmt++; + break; + case 'I': + isize = sizeof(int); + fmt++; + break; + case 'L': + isize = sizeof(long); + fmt++; + break; + case 'S': + isize = sizeof(short); + fmt++; + break; + default: + if (isdigit((unsigned char)*fmt)) { + errno = 0; + isize = (size_t)strtoul(fmt, &end, 10); + if (errno != 0 || isize == 0) + errx(1, "%s: invalid size", fmt); + if (isize != sizeof(char) && isize != sizeof(short) && + isize != sizeof(int) && isize != sizeof(long)) + errx(1, "unsupported int size %lu", + (u_long)isize); + fmt = (const char *)end; + } + } + + /* + * Calculate the maximum number of digits we need to + * fit the number. Overestimate for decimal with log + * base 8. We need one extra space for signed numbers + * to store the sign. + */ + n = (1ULL << (8 * isize)) - 1; + digits = 0; + while (n != 0) { + digits++; + n >>= (fchar == 'x') ? 4 : 3; + } + if (fchar == 'd') + digits++; + asprintf(&hdfmt, "%lu/%lu \"%*s%%%s%d%c\" \"\\n\"", + 16UL / (u_long)isize, (u_long)isize, (int)(4 * isize - digits), + "", (fchar == 'd' || fchar == 'u') ? "" : "0", digits, fchar); + if (hdfmt == NULL) + err(1, NULL); + odadd(hdfmt); + free(hdfmt); + + return (fmt); +} + +static void +odadd(const char *fmt) +{ + static int needpad; + + if (needpad) + add("\""PADDING"\""); + add(fmt); + needpad = 1; +} diff --git a/shell_cmds/hexdump/parse.c b/shell_cmds/hexdump/parse.c new file mode 100644 index 0000000..5445f18 --- /dev/null +++ b/shell_cmds/hexdump/parse.c @@ -0,0 +1,523 @@ +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)parse.c 8.1 (Berkeley) 6/6/93"; +#endif +#endif /* not lint */ +#include <sys/cdefs.h> +__FBSDID("$FreeBSD: src/usr.bin/hexdump/parse.c,v 1.12 2002/09/04 23:29:01 dwmalone Exp $"); + +#include <sys/types.h> + +#include <err.h> +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <ctype.h> +#include <string.h> +#include "hexdump.h" + +FU *endfu; /* format at end-of-data */ + +void +addfile(char *name) +{ + unsigned char *p; + FILE *fp; + int ch; + char buf[2048 + 1]; + + if ((fp = fopen(name, "r")) == NULL) + err(1, "%s", name); + while (fgets(buf, sizeof(buf), fp)) { + if (!(p = (unsigned char *)index(buf, '\n'))) { + warnx("line too long"); + while ((ch = getchar()) != '\n' && ch != EOF); + continue; + } + *p = '\0'; + for (p = (unsigned char *)buf; *p && isspace(*p); ++p); + if (!*p || *p == '#') + continue; + add((const char *)p); + } + (void)fclose(fp); +} + +void +add(const char *fmt) +{ + unsigned const char *p, *savep; + static FS **nextfs; + FS *tfs; + FU *tfu, **nextfu; + + /* start new linked list of format units */ + if ((tfs = calloc(1, sizeof(FS))) == NULL) + err(1, NULL); + if (!fshead) + fshead = tfs; + else + *nextfs = tfs; + nextfs = &tfs->nextfs; + nextfu = &tfs->nextfu; + + /* take the format string and break it up into format units */ + for (p = (unsigned const char *)fmt;;) { + /* skip leading white space */ + for (; isspace(*p); ++p); + if (!*p) + break; + + /* allocate a new format unit and link it in */ + if ((tfu = calloc(1, sizeof(FU))) == NULL) + err(1, NULL); + *nextfu = tfu; + nextfu = &tfu->nextfu; + tfu->reps = 1; + + /* if leading digit, repetition count */ + if (isdigit(*p)) { + for (savep = p; isdigit(*p); ++p); + if (!isspace(*p) && *p != '/') + badfmt(fmt); + /* may overwrite either white space or slash */ + tfu->reps = atoi((const char *)savep); + tfu->flags = F_SETREP; + /* skip trailing white space */ + for (++p; isspace(*p); ++p); + } + + /* skip slash and trailing white space */ + if (*p == '/') + while (isspace(*++p)); + + /* byte count */ + if (isdigit(*p)) { + for (savep = p; isdigit(*p); ++p); + if (!isspace(*p)) + badfmt(fmt); + tfu->bcnt = atoi((const char *)savep); + /* skip trailing white space */ + for (++p; isspace(*p); ++p); + } + + /* format */ + if (*p != '"') + badfmt(fmt); + for (savep = ++p; *p != '"';) + if (*p++ == 0) + badfmt(fmt); + if (!(tfu->fmt = malloc(p - savep + 1))) + err(1, NULL); + (void) strncpy(tfu->fmt, (const char *)savep, p - savep); + tfu->fmt[p - savep] = '\0'; + escape(tfu->fmt); + p++; + } +} + +static const char *spec = ".#-+ 0123456789"; + +int +size(FS *fs) +{ + FU *fu; + int bcnt, cursize; + unsigned char *fmt; + int prec; + + /* figure out the data block size needed for each format unit */ + for (cursize = 0, fu = fs->nextfu; fu; fu = fu->nextfu) { + if (fu->bcnt) { + cursize += fu->bcnt * fu->reps; + continue; + } + for (bcnt = prec = 0, fmt = (unsigned char *)fu->fmt; *fmt; ++fmt) { + if (*fmt != '%') + continue; + /* + * skip any special chars -- save precision in + * case it's a %s format. + */ + while (index(spec + 1, *++fmt)); + if (*fmt == '.' && isdigit(*++fmt)) { + prec = atoi((const char *)fmt); + while (isdigit(*++fmt)); + } + switch(*fmt) { + case 'c': + bcnt += 1; + break; + case 'd': case 'i': case 'o': case 'u': + case 'x': case 'X': + bcnt += 4; + break; + case 'e': case 'E': case 'f': case 'g': case 'G': + bcnt += 8; + break; + case 's': + bcnt += prec; + break; + case '_': + switch(*++fmt) { + case 'c': case 'p': case 'u': + bcnt += 1; + break; + } + } + } + cursize += bcnt * fu->reps; + } + return (cursize); +} + +void +rewrite(FS *fs) +{ + enum { NOTOKAY, USEBCNT, USEPREC } sokay; + PR *pr, **nextpr = NULL; + FU *fu; + unsigned char *p1, *p2, *fmtp; + char savech, cs[3]; + int nconv, prec = 0; + + for (fu = fs->nextfu; fu; fu = fu->nextfu) { + /* + * Break each format unit into print units; each conversion + * character gets its own. + */ + for (nconv = 0, fmtp = (unsigned char *)fu->fmt; *fmtp; nextpr = &pr->nextpr) { + if ((pr = calloc(1, sizeof(PR))) == NULL) + err(1, NULL); + if (!fu->nextpr) + fu->nextpr = pr; + else + *nextpr = pr; + + /* Skip preceding text and up to the next % sign. */ + for (p1 = fmtp; *p1 && *p1 != '%'; ++p1); + + /* Only text in the string. */ + if (!*p1) { + pr->fmt = (char *)fmtp; + pr->flags = F_TEXT; + break; + } + + /* + * Get precision for %s -- if have a byte count, don't + * need it. + */ + if (fu->bcnt) { + sokay = USEBCNT; + /* Skip to conversion character. */ + for (++p1; index(spec, *p1); ++p1); + } else { + /* Skip any special chars, field width. */ + while (index(spec + 1, *++p1)); + if (*p1 == '.' && isdigit(*++p1)) { + sokay = USEPREC; + prec = atoi((const char *)p1); + while (isdigit(*++p1)); + } else + sokay = NOTOKAY; + } + + p2 = p1 + 1; /* Set end pointer. */ + cs[0] = *p1; /* Set conversion string. */ + cs[1] = '\0'; + + /* + * Figure out the byte count for each conversion; + * rewrite the format as necessary, set up blank- + * padding for end of data. + */ + switch(cs[0]) { + case 'c': + pr->flags = F_CHAR; + switch(fu->bcnt) { + case 0: case 1: + pr->bcnt = 1; + break; + default: + p1[1] = '\0'; + badcnt((char *)p1); + } + break; + case 'd': case 'i': + pr->flags = F_INT; + goto isint; + case 'o': case 'u': case 'x': case 'X': + pr->flags = F_UINT; +isint: cs[2] = '\0'; + cs[1] = cs[0]; + cs[0] = 'q'; + switch(fu->bcnt) { + case 0: case 4: + pr->bcnt = 4; + break; + case 1: + pr->bcnt = 1; + break; + case 2: + pr->bcnt = 2; + break; +#ifdef __APPLE__ + case 8: + pr->bcnt = 8; + break; +#endif /* __APPLE__ */ + default: + p1[1] = '\0'; + badcnt((char *)p1); + } + break; + case 'e': case 'E': case 'f': case 'g': case 'G': + pr->flags = F_DBL; + switch(fu->bcnt) { + case 0: case 8: + pr->bcnt = 8; + break; + case 4: + pr->bcnt = 4; + break; + default: + if (fu->bcnt == sizeof(long double)) { + cs[2] = '\0'; + cs[1] = cs[0]; + cs[0] = 'L'; + pr->bcnt = sizeof(long double); + } else { + p1[1] = '\0'; + badcnt((char *)p1); + } + } + break; + case 's': + pr->flags = F_STR; + switch(sokay) { + case NOTOKAY: + badsfmt(); + case USEBCNT: + pr->bcnt = fu->bcnt; + break; + case USEPREC: + pr->bcnt = prec; + break; + } + break; + case '_': + ++p2; + switch(p1[1]) { + case 'A': + endfu = fu; + fu->flags |= F_IGNORE; + /* FALLTHROUGH */ + case 'a': + pr->flags = F_ADDRESS; + ++p2; + switch(p1[2]) { + case 'd': case 'o': case'x': + cs[0] = 'q'; + cs[1] = p1[2]; + cs[2] = '\0'; + break; + default: + p1[3] = '\0'; + badconv((char *)p1); + } + break; + case 'c': + pr->flags = F_C; + /* cs[0] = 'c'; set in conv_c */ + goto isint2; + case 'p': + pr->flags = F_P; + cs[0] = 'c'; + goto isint2; + case 'u': + pr->flags = F_U; + /* cs[0] = 'c'; set in conv_u */ +isint2: switch(fu->bcnt) { + case 0: case 1: + pr->bcnt = 1; + break; + default: + p1[2] = '\0'; + badcnt((char *)p1); + } + break; + case 'n': /* Force -A n to dump extra blank line like default od behavior */ + endfu = fu; + fu->flags = F_IGNORE; + pr->flags = F_TEXT; + fmtp = (unsigned char *)"\n"; + cs[0] = '\0'; + break; + default: + p1[2] = '\0'; + badconv((char *)p1); + } + break; + default: + p1[1] = '\0'; + badconv((char *)p1); + } + + /* + * Copy to PR format string, set conversion character + * pointer, update original. + */ + savech = *p2; + p1[0] = '\0'; + if ((pr->fmt = calloc(1, strlen((const char *)fmtp) + 2)) == NULL) + err(1, NULL); + (void)strcpy(pr->fmt, (const char *)fmtp); + (void)strcat(pr->fmt, cs); + *p2 = savech; + pr->cchar = pr->fmt + (p1 - fmtp); + fmtp = p2; + + /* Only one conversion character if byte count. */ + if (!(pr->flags&F_ADDRESS) && fu->bcnt && nconv++) + errx(1, "byte count with multiple conversion characters"); + } + /* + * If format unit byte count not specified, figure it out + * so can adjust rep count later. + */ + if (!fu->bcnt) + for (pr = fu->nextpr; pr; pr = pr->nextpr) + fu->bcnt += pr->bcnt; + } + /* + * If the format string interprets any data at all, and it's + * not the same as the blocksize, and its last format unit + * interprets any data at all, and has no iteration count, + * repeat it as necessary. + * + * If, rep count is greater than 1, no trailing whitespace + * gets output from the last iteration of the format unit. + */ + for (fu = fs->nextfu; fu; fu = fu->nextfu) { + if (!fu->nextfu && fs->bcnt < blocksize && + !(fu->flags&F_SETREP) && fu->bcnt) + fu->reps += (blocksize - fs->bcnt) / fu->bcnt; + if (fu->reps > 1) { + for (pr = fu->nextpr;; pr = pr->nextpr) + if (!pr->nextpr) + break; + for (p1 = (unsigned char *)pr->fmt, p2 = NULL; *p1; ++p1) + p2 = isspace(*p1) ? p1 : NULL; + if (p2) + pr->nospace = (char *)p2; + } + } +#ifdef DEBUG + for (fu = fs->nextfu; fu; fu = fu->nextfu) { + (void)printf("fmt:"); + for (pr = fu->nextpr; pr; pr = pr->nextpr) + (void)printf(" {%s}", pr->fmt); + (void)printf("\n"); + } +#endif +} + +void +escape(char *p1) +{ + char *p2; + + /* alphabetic escape sequences have to be done in place */ + for (p2 = p1;; ++p1, ++p2) { + if (!*p1) { + *p2 = *p1; + break; + } + if (*p1 == '\\') + switch(*++p1) { + case 'a': + /* *p2 = '\a'; */ + *p2 = '\007'; + break; + case 'b': + *p2 = '\b'; + break; + case 'f': + *p2 = '\f'; + break; + case 'n': + *p2 = '\n'; + break; + case 'r': + *p2 = '\r'; + break; + case 't': + *p2 = '\t'; + break; + case 'v': + *p2 = '\v'; + break; + default: + *p2 = *p1; + break; + } + } +} + +void +badcnt(char *s) +{ + errx(1, "%s: bad byte count", s); +} + +void +badsfmt(void) +{ + errx(1, "%%s: requires a precision or a byte count"); +} + +void +badfmt(const char *fmt) +{ + errx(1, "\"%s\": bad format", fmt); +} + +void +badconv(char *ch) +{ + errx(1, "%%%s: bad conversion character", ch); +} diff --git a/shell_cmds/hostname/hostname.1 b/shell_cmds/hostname/hostname.1 new file mode 100644 index 0000000..291b319 --- /dev/null +++ b/shell_cmds/hostname/hostname.1 @@ -0,0 +1,67 @@ +.\"- +.\" Copyright (c) 1983, 1988, 1990, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)hostname.1 8.2 (Berkeley) 4/28/95 +.\" $FreeBSD: src/bin/hostname/hostname.1,v 1.21 2006/12/08 07:47:08 kientzle Exp $ +.\" +.Dd December 7, 2006 +.Dt HOSTNAME 1 +.Os +.Sh NAME +.Nm hostname +.Nd set or print name of current host system +.Sh SYNOPSIS +.Nm +.Op Fl fs +.Op Ar name-of-host +.Sh DESCRIPTION +The +.Nm +utility prints the name of the current host. +The super-user can +set the hostname by supplying an argument. +To keep the hostname between reboots, run +.Sq scutil --set HostName Ar name-of-host . +.Pp +Options: +.Bl -tag -width flag +.It Fl f +Include domain information in the printed name. +This is the default behavior. +.It Fl s +Trim off any domain information from the printed +name. +.El +.Sh SEE ALSO +.Xr gethostname 3 , +.Xr scutil 8 +.Sh HISTORY +The +.Nm +command appeared in +.Bx 4.2 . diff --git a/shell_cmds/hostname/hostname.c b/shell_cmds/hostname/hostname.c new file mode 100644 index 0000000..3df6542 --- /dev/null +++ b/shell_cmds/hostname/hostname.c @@ -0,0 +1,105 @@ +/*- + * Copyright (c) 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if 0 +#ifndef lint +static char const copyright[] = +"@(#) Copyright (c) 1988, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)hostname.c 8.1 (Berkeley) 5/31/93"; +#endif /* not lint */ +#endif +#include <sys/cdefs.h> +__FBSDID("$FreeBSD: src/bin/hostname/hostname.c,v 1.19 2006/12/08 07:47:08 kientzle Exp $"); + +#include <sys/param.h> + +#include <err.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +void usage(void); + +int +main(int argc, char *argv[]) +{ + int ch, sflag; + char *p, hostname[MAXHOSTNAMELEN]; + + sflag = 0; + while ((ch = getopt(argc, argv, "fs")) != -1) + switch (ch) { + case 'f': + /* + * On Linux, "hostname -f" prints FQDN. + * BSD "hostname" always prints FQDN by + * default, so we accept but ignore -f. + */ + break; + case 's': + sflag = 1; + break; + case '?': + default: + usage(); + } + argc -= optind; + argv += optind; + + if (argc > 1) + usage(); + + if (*argv) { + if (sethostname(*argv, (int)strlen(*argv))) + err(1, "sethostname"); + } else { + if (gethostname(hostname, (int)sizeof(hostname))) + err(1, "gethostname"); + if (sflag) { + p = strchr(hostname, '.'); + if (p != NULL) + *p = '\0'; + } + (void)printf("%s\n", hostname); + } + exit(0); +} + +void +usage(void) +{ + + (void)fprintf(stderr, "usage: hostname [-fs] [name-of-host]\n"); + exit(1); +} diff --git a/shell_cmds/id/groups.1 b/shell_cmds/id/groups.1 new file mode 100644 index 0000000..00c030b --- /dev/null +++ b/shell_cmds/id/groups.1 @@ -0,0 +1,63 @@ +.\" Copyright (c) 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)groups.1 8.1 (Berkeley) 6/6/93 +.\" $FreeBSD: src/usr.bin/id/groups.1,v 1.10 2006/12/23 17:14:01 ru Exp $ +.\" +.Dd June 6, 1993 +.Dt GROUPS 1 +.Os +.Sh NAME +.Nm groups +.Nd show group memberships +.Sh SYNOPSIS +.Nm +.Op Ar user +.Sh DESCRIPTION +The +.Nm +utility has been obsoleted by the +.Xr id 1 +utility, and is equivalent to +.Dq Nm id Fl Gn Op Ar user . +The command +.Dq Nm id Fl p +is suggested for normal interactive use. +.Pp +The +.Nm +utility displays the groups to which you (or the optionally specified +.Ar user ) +belong. +.Sh EXIT STATUS +.Ex -std +.Sh SEE ALSO +.Xr id 1 diff --git a/shell_cmds/id/id.1 b/shell_cmds/id/id.1 new file mode 100644 index 0000000..1090c9a --- /dev/null +++ b/shell_cmds/id/id.1 @@ -0,0 +1,167 @@ +.\" Copyright (c) 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" the Institute of Electrical and Electronics Engineers, Inc. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)id.1 8.1 (Berkeley) 6/6/93 +.\" $FreeBSD: src/usr.bin/id/id.1,v 1.18 2006/09/26 22:28:12 ceri Exp $ +.\" +.Dd September 26, 2006 +.Dt ID 1 +.Os +.Sh NAME +.Nm id +.Nd return user identity +.Sh SYNOPSIS +.Nm +.Op Ar user +.Nm +.Fl A +.Nm +.Fl F +.Op Ar user +.Nm +.Fl G Op Fl n +.Op Ar user +.Nm +.Fl M +.Nm +.Fl P +.Op Ar user +.Nm +.Fl g Op Fl nr +.Op Ar user +.Nm +.Fl p +.Op Ar user +.Nm +.Fl u Op Fl nr +.Op Ar user +.Sh DESCRIPTION +The +.Nm +utility displays the user and group names and numeric IDs, of the +calling process, to the standard output. +If the real and effective IDs are different, both are displayed, +otherwise only the real ID is displayed. +.Pp +If a +.Ar user +(login name or user ID) +is specified, the user and group IDs of that user are displayed. +In this case, the real and effective IDs are assumed to be the same. +.Pp +The options are as follows: +.Bl -tag -width indent +.It Fl A +Display the process audit user ID and other process audit properties, which +requires privilege. +.It Fl F +Display the full name of the user. +.It Fl G +Display the different group IDs (effective, real and supplementary) +as white-space separated numbers, in no particular order. +.It Fl M +Display the MAC label of the current process. +.It Fl P +Display the id as a password file entry. +.It Fl a +Ignored for compatibility with other +.Nm +implementations. +.It Fl g +Display the effective group ID as a number. +.It Fl n +Display the name of the user or group ID for the +.Fl G , +.Fl g +and +.Fl u +options instead of the number. +If any of the ID numbers cannot be mapped into names, the number will be +displayed as usual. +.It Fl p +Make the output human-readable. +If the user name returned by +.Xr getlogin 2 +is different from the login name referenced by the user ID, the name +returned by +.Xr getlogin 2 +is displayed, preceded by the keyword +.Dq login . +The user ID as a name is displayed, preceded by the keyword +.Dq uid . +If the effective user ID is different from the real user ID, the real user +ID is displayed as a name, preceded by the keyword +.Dq euid . +If the effective group ID is different from the real group ID, the real group +ID is displayed as a name, preceded by the keyword +.Dq rgid . +The list of groups to which the user belongs is then displayed as names, +preceded by the keyword +.Dq groups . +Each display is on a separate line. +.It Fl r +Display the real ID for the +.Fl g +and +.Fl u +options instead of the effective ID. +.It Fl u +Display the effective user ID as a number. +.El +.Sh EXIT STATUS +.Ex -std +.Sh SEE ALSO +.Xr who 1 +.Sh STANDARDS +The +.Nm +function is expected to conform to +.St -p1003.2 . +.Sh HISTORY +The +historic +.Xr groups 1 +command is equivalent to +.Dq Nm id Fl Gn Op Ar user . +.Pp +The +historic +.Xr whoami 1 +command is equivalent to +.Dq Nm id Fl un . +.Pp +The +.Nm +command appeared in +.Bx 4.4 . diff --git a/shell_cmds/id/id.c b/shell_cmds/id/id.c new file mode 100644 index 0000000..2567770 --- /dev/null +++ b/shell_cmds/id/id.c @@ -0,0 +1,529 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static const char copyright[] = +"@(#) Copyright (c) 1991, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)id.c 8.2 (Berkeley) 2/16/94"; +#endif +#endif /* not lint */ +#include <sys/cdefs.h> +__FBSDID("$FreeBSD: src/usr.bin/id/id.c,v 1.33 2006/12/29 12:28:34 stefanf Exp $"); + +#include <sys/param.h> +#ifndef __APPLE__ +#include <sys/mac.h> +#endif /* !__APPLE__ */ + +#ifdef USE_BSM_AUDIT +#include <bsm/audit.h> +#endif + +#include <err.h> +#include <errno.h> +#include <grp.h> +#include <pwd.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +void id_print(struct passwd *, int, int, int); +void pline(struct passwd *); +void pretty(struct passwd *); +void auditid(void); +void fullname(struct passwd *); +void group(struct passwd *, int); +void maclabel(void); +void usage(void); +struct passwd *who(char *); + +int isgroups, iswhoami; + +#ifdef __APPLE__ +// SPI for 5235093 +int32_t getgrouplist_2(const char *, gid_t, gid_t **); +#endif + +int +main(int argc, char *argv[]) +{ + struct group *gr; + struct passwd *pw; + int Gflag, Mflag, Pflag, ch, gflag, id, nflag, pflag, rflag, uflag; + int Aflag; + int Fflag; + const char *myname; + + Gflag = Mflag = Pflag = gflag = nflag = pflag = rflag = uflag = 0; + Aflag = 0; + Fflag = 0; + + myname = strrchr(argv[0], '/'); + myname = (myname != NULL) ? myname + 1 : argv[0]; + if (strcmp(myname, "groups") == 0) { + isgroups = 1; + Gflag = nflag = 1; + } + else if (strcmp(myname, "whoami") == 0) { + iswhoami = 1; + uflag = nflag = 1; + } + + while ((ch = getopt(argc, argv, + (isgroups || iswhoami) ? "" : "AFPGMagnpru")) != -1) + switch(ch) { +#ifdef USE_BSM_AUDIT + case 'A': + Aflag = 1; + break; +#endif + case 'F': + Fflag = 1; + break; + case 'G': + Gflag = 1; + break; + case 'M': + Mflag = 1; + break; + case 'P': + Pflag = 1; + break; + case 'a': + break; + case 'g': + gflag = 1; + break; + case 'n': + nflag = 1; + break; + case 'p': + pflag = 1; + break; + case 'r': + rflag = 1; + break; + case 'u': + uflag = 1; + break; + case '?': + default: + usage(); + } + argc -= optind; + argv += optind; + + if (iswhoami && argc > 0) + usage(); + + switch(Aflag + Fflag + Gflag + Mflag + Pflag + gflag + pflag + uflag) { + case 1: + break; + case 0: + if (!nflag && !rflag) + break; + /* FALLTHROUGH */ + default: + usage(); + } + + pw = *argv ? who(*argv) : NULL; + + if (Mflag && pw != NULL) + usage(); + +#ifdef USE_BSM_AUDIT + if (Aflag) { + auditid(); + exit(0); + } +#endif + + if (Fflag) { + fullname(pw); + exit(0); + } + + if (gflag) { + id = pw ? pw->pw_gid : rflag ? getgid() : getegid(); + if (nflag && (gr = getgrgid(id))) + (void)printf("%s\n", gr->gr_name); + else + (void)printf("%u\n", id); + exit(0); + } + + if (uflag) { + id = pw ? pw->pw_uid : rflag ? getuid() : geteuid(); + if (nflag && (pw = getpwuid(id))) + (void)printf("%s\n", pw->pw_name); + else + (void)printf("%u\n", id); + exit(0); + } + + if (Gflag) { + group(pw, nflag); + exit(0); + } + + if (Mflag) { + maclabel(); + exit(0); + } + + if (Pflag) { + pline(pw); + exit(0); + } + + if (pflag) { + pretty(pw); + exit(0); + } + + if (pw) { + id_print(pw, 1, 0, 0); + } + else { + id = getuid(); + pw = getpwuid(id); + id_print(pw, 0, 1, 1); + } + exit(0); +} + +void +pretty(struct passwd *pw) +{ + struct group *gr; + u_int eid, rid; + char *login; + + if (pw) { + (void)printf("uid\t%s\n", pw->pw_name); + (void)printf("groups\t"); + group(pw, 1); + } else { + if ((login = getlogin()) == NULL) + err(1, "getlogin"); + + pw = getpwuid(rid = getuid()); + if (pw == NULL || strcmp(login, pw->pw_name)) + (void)printf("login\t%s\n", login); + if (pw) + (void)printf("uid\t%s\n", pw->pw_name); + else + (void)printf("uid\t%u\n", rid); + + if ((eid = geteuid()) != rid) { + if ((pw = getpwuid(eid))) + (void)printf("euid\t%s\n", pw->pw_name); + else + (void)printf("euid\t%u\n", eid); + } + if ((rid = getgid()) != (eid = getegid())) { + if ((gr = getgrgid(rid))) + (void)printf("rgid\t%s\n", gr->gr_name); + else + (void)printf("rgid\t%u\n", rid); + } + (void)printf("groups\t"); + group(NULL, 1); + } +} + +void +id_print(struct passwd *pw, int use_ggl, int p_euid, int p_egid) +{ + struct group *gr; + gid_t gid, egid, lastgid; + uid_t uid, euid; + int cnt, ngroups; +#ifdef __APPLE__ + gid_t *groups = NULL; +#else + gid_t groups[NGROUPS + 1]; +#endif + const char *fmt; + +#ifdef __APPLE__ + if (pw == NULL) { + pw = getpwuid(getuid()); + } + + use_ggl = 1; +#endif + + if (pw != NULL) { + uid = pw->pw_uid; + gid = pw->pw_gid; + } + else { + uid = getuid(); + gid = getgid(); + } + + if (use_ggl && pw != NULL) { +#ifdef __APPLE__ + // 5235093 + ngroups = getgrouplist_2(pw->pw_name, gid, &groups); +#else + ngroups = NGROUPS + 1; + getgrouplist(pw->pw_name, gid, groups, &ngroups); +#endif + } + else { +#ifdef __APPLE__ + groups = malloc((NGROUPS + 1) * sizeof(gid_t)); +#endif + ngroups = getgroups(NGROUPS + 1, groups); + } + +#ifdef __APPLE__ + if (ngroups < 0) + warn("failed to retrieve group list"); +#endif + + if (pw != NULL) + printf("uid=%u(%s)", uid, pw->pw_name); + else + printf("uid=%u", getuid()); + printf(" gid=%u", gid); + if ((gr = getgrgid(gid))) + (void)printf("(%s)", gr->gr_name); + if (p_euid && (euid = geteuid()) != uid) { + (void)printf(" euid=%u", euid); + if ((pw = getpwuid(euid))) + (void)printf("(%s)", pw->pw_name); + } + if (p_egid && (egid = getegid()) != gid) { + (void)printf(" egid=%u", egid); + if ((gr = getgrgid(egid))) + (void)printf("(%s)", gr->gr_name); + } + fmt = " groups=%u"; + for (lastgid = -1, cnt = 0; cnt < ngroups; ++cnt) { + if (lastgid == (gid = groups[cnt])) + continue; + printf(fmt, gid); + fmt = ",%u"; + if ((gr = getgrgid(gid))) + printf("(%s)", gr->gr_name); + lastgid = gid; + } + printf("\n"); +#ifdef __APPLE__ + free(groups); +#endif +} + +#ifdef USE_BSM_AUDIT +void +auditid(void) +{ + auditinfo_addr_t auditinfo; + + if (getaudit_addr(&auditinfo, sizeof(auditinfo)) < 0) + err(1, "getaudit"); + printf("auid=%d\n", auditinfo.ai_auid); + printf("mask.success=0x%08x\n", auditinfo.ai_mask.am_success); + printf("mask.failure=0x%08x\n", auditinfo.ai_mask.am_failure); + printf("termid.port=0x%08x\n", auditinfo.ai_termid.at_port); + printf("asid=%d\n", auditinfo.ai_asid); +} +#endif + +void +fullname(struct passwd *pw) +{ + + if (!pw) { + if ((pw = getpwuid(getuid())) == NULL) + err(1, "getpwuid"); + } + + (void)printf("%s\n", pw->pw_gecos); +} + +void +group(struct passwd *pw, int nflag) +{ + struct group *gr; + int cnt, id, lastid, ngroups; +#ifdef __APPLE__ + gid_t *groups = NULL; +#else + gid_t groups[NGROUPS + 1]; +#endif + const char *fmt; + +#ifdef __APPLE__ + if (pw == NULL) { + pw = getpwuid(getuid()); + } +#endif + + if (pw) { +#ifdef __APPLE__ + // 5235093 + ngroups = getgrouplist_2(pw->pw_name, pw->pw_gid, &groups); +#else + ngroups = NGROUPS + 1; + (void) getgrouplist(pw->pw_name, pw->pw_gid, groups, &ngroups); +#endif + } else { +#ifdef __APPLE__ + groups = malloc((NGROUPS + 1) * sizeof(gid_t)); +#endif + groups[0] = getgid(); + ngroups = getgroups(NGROUPS, groups + 1) + 1; + } + fmt = nflag ? "%s" : "%u"; + for (lastid = -1, cnt = 0; cnt < ngroups; ++cnt) { + if (lastid == (id = groups[cnt])) + continue; + if (nflag) { + if ((gr = getgrgid(id))) + (void)printf(fmt, gr->gr_name); + else + (void)printf(*fmt == ' ' ? " %u" : "%u", + id); + fmt = " %s"; + } else { + (void)printf(fmt, id); + fmt = " %u"; + } + lastid = id; + } + (void)printf("\n"); +#ifdef __APPLE__ + free(groups); +#endif +} + +void +maclabel(void) +{ +#ifdef __APPLE__ + errx(1, "-M unsupported"); +#else /* !__APPLE__ */ + char *string; + mac_t label; + int error; + + error = mac_prepare_process_label(&label); + if (error == -1) + errx(1, "mac_prepare_type: %s", strerror(errno)); + + error = mac_get_proc(label); + if (error == -1) + errx(1, "mac_get_proc: %s", strerror(errno)); + + error = mac_to_text(label, &string); + if (error == -1) + errx(1, "mac_to_text: %s", strerror(errno)); + + (void)printf("%s\n", string); + mac_free(label); + free(string); +#endif /* __APPLE__ */ +} + +struct passwd * +who(char *u) +{ + struct passwd *pw; + long id; + char *ep; + + /* + * Translate user argument into a pw pointer. First, try to + * get it as specified. If that fails, try it as a number. + */ + if ((pw = getpwnam(u))) + return(pw); + id = strtol(u, &ep, 10); + if (*u && !*ep && (pw = getpwuid(id))) + return(pw); + errx(1, "%s: no such user", u); + /* NOTREACHED */ +} + +void +pline(struct passwd *pw) +{ + + if (!pw) { + if ((pw = getpwuid(getuid())) == NULL) + err(1, "getpwuid"); + } + + (void)printf("%s:%s:%d:%d:%s:%ld:%ld:%s:%s:%s\n", pw->pw_name, + pw->pw_passwd, pw->pw_uid, pw->pw_gid, pw->pw_class, + (long)pw->pw_change, (long)pw->pw_expire, pw->pw_gecos, + pw->pw_dir, pw->pw_shell); +} + + +void +usage(void) +{ + + if (isgroups) + (void)fprintf(stderr, "usage: groups [user]\n"); + else if (iswhoami) + (void)fprintf(stderr, "usage: whoami\n"); + else + (void)fprintf(stderr, "%s\n%s%s\n%s\n%s\n%s\n%s\n%s\n%s\n", + "usage: id [user]", +#ifdef USE_BSM_AUDIT + " id -A\n", +#else + "", +#endif + " id -F [user]", + " id -G [-n] [user]", + " id -M", + " id -P [user]", + " id -g [-nr] [user]", + " id -p [user]", + " id -u [-nr] [user]"); + exit(1); +} diff --git a/shell_cmds/id/open_directory.c b/shell_cmds/id/open_directory.c new file mode 100644 index 0000000..fb34de9 --- /dev/null +++ b/shell_cmds/id/open_directory.c @@ -0,0 +1,229 @@ +#include <membership.h> + +#include "open_directory.h" + +ODNodeRef +CreateNode(void) +{ + CFErrorRef error = NULL; + ODNodeRef node = NULL; + + node = ODNodeCreateWithNodeType(NULL, kODSessionDefault, kODTypeAuthenticationSearchNode, &error); + + if (node == NULL) { + CFShow(error); + exit(1); + } + + return node; +} + +static ODRecordRef +CopyRecordWithUUID(ODNodeRef node, CFStringRef type, uuid_t uuid) +{ + CFErrorRef error = NULL; + char uuidstr[37]; + CFStringRef uuidref; + ODQueryRef query = NULL; + CFTypeRef vals[] = { CFSTR(kDSAttributesStandardAll) }; + CFArrayRef attributes = CFArrayCreate(NULL, vals, 1, &kCFTypeArrayCallBacks); + CFArrayRef results = NULL; + ODRecordRef record = NULL; + + uuid_unparse(uuid, uuidstr); + uuidref = CFStringCreateWithCString(NULL, uuidstr, kCFStringEncodingUTF8); + + if (uuidref) { + query = ODQueryCreateWithNode(NULL, node, type, CFSTR(kDS1AttrGeneratedUID), kODMatchEqualTo, uuidref, attributes, 100, &error); + + if (query) { + results = ODQueryCopyResults(query, false, &error); + + if (results) { + if (CFArrayGetCount(results) == 1) { + record = (ODRecordRef)CFArrayGetValueAtIndex(results, 0); + CFRetain(record); + } + + CFRelease(results); + } + + CFRelease(query); + } + + CFRelease(uuidref); + } + + return record; +} + +ODRecordRef +CopyGroupRecordWithGID(ODNodeRef node, gid_t gid) +{ + uuid_t uuid; + + mbr_gid_to_uuid(gid, uuid); + + return CopyRecordWithUUID(node, CFSTR(kDSStdRecordTypeGroups), uuid); +} + +ODRecordRef +CopyUserRecordWithUID(ODNodeRef node, uid_t uid) +{ + uuid_t uuid; + + mbr_uid_to_uuid(uid, uuid); + + return CopyRecordWithUUID(node, CFSTR(kDSStdRecordTypeUsers), uuid); +} + +ODRecordRef +CopyUserRecordWithUsername(ODNodeRef node, char *name) +{ + CFStringRef nameref; + CFTypeRef vals[] = { CFSTR(kDSAttributesStandardAll) }; + CFArrayRef attributes = CFArrayCreate(NULL, vals, 1, &kCFTypeArrayCallBacks); + CFErrorRef error; + + nameref = CFStringCreateWithCString(NULL, name, kCFStringEncodingUTF8); + + if (nameref == NULL) + return NULL; + + return ODNodeCopyRecord(node, CFSTR(kDSStdRecordTypeUsers), nameref, attributes, &error); +} + +CFStringRef +CopyAttrFromRecord(ODRecordRef record, CFStringRef attribute) +{ + CFErrorRef error = NULL; + CFArrayRef values = ODRecordCopyValues(record, attribute, &error); + CFStringRef result = NULL; + + if (values) { + if (CFArrayGetCount(values) == 1) { + result = CFArrayGetValueAtIndex(values, 0); + CFRetain(result); + } + CFRelease(values); + } + + return result; +} + +int +GetIntAttrFromRecord(ODRecordRef record, CFStringRef attribute, int *output) +{ + int status = 1; + CFStringRef str = CopyAttrFromRecord(record, attribute); + + if (str) { + *output = CFStringGetIntValue(str); + status = 0; + CFRelease(str); + } + + return status; +} + +uid_t +GetUIDFromRecord(ODRecordRef record) +{ + int uid = -1; + + GetIntAttrFromRecord(record, CFSTR(kDS1AttrUniqueID), &uid); + + return uid; +} + +gid_t +GetGIDFromRecord(ODRecordRef record) +{ + int gid = -1; + + GetIntAttrFromRecord(record, CFSTR(kDS1AttrPrimaryGroupID), &gid); + + return gid; +} + +CFArrayRef +CopyGroupRecordsForUser(ODNodeRef node, ODRecordRef user, CFIndex limit) +{ + CFMutableArrayRef groups; + gid_t primary_gid; + ODRecordRef primary_group; + CFErrorRef error = NULL; + ODQueryRef query; + CFArrayRef results; + int i; + ODRecordRef gr; + + query = ODQueryCreateWithNode(NULL, node, CFSTR(kDSStdRecordTypeGroups), + CFSTR(kDSNAttrMember), kODMatchContains, ODRecordGetRecordName(user), NULL, limit, &error); + results = ODQueryCopyResults(query, false, &error); + CFRelease(query); + + groups = CFArrayCreateMutable(NULL, 0, &kCFTypeArrayCallBacks); + + primary_gid = GetGIDFromRecord(user); + primary_group = CopyGroupRecordWithGID(node, primary_gid); + CFArrayAppendValue(groups, primary_group); + CFRelease(primary_group); + + for (i = 0; i < CFArrayGetCount(results); i++) { + gr = (ODRecordRef)CFArrayGetValueAtIndex(results, i); + if (GetGIDFromRecord(gr) != primary_gid) { + CFArrayAppendValue(groups, gr); + } + } + + CFRelease(results); + + return groups; +} + +static int +cvfprintf(FILE *file, const char *format, va_list args) +{ + char* cstr; + int result = 0; + CFStringRef formatStr = CFStringCreateWithCStringNoCopy(NULL, format, kCFStringEncodingUTF8, kCFAllocatorNull); + if (formatStr) { + CFStringRef str = CFStringCreateWithFormatAndArguments(NULL, NULL, formatStr, args); + if (str) { + size_t size = CFStringGetMaximumSizeForEncoding(CFStringGetLength(str), kCFStringEncodingUTF8) + 1; + cstr = malloc(size); + if (cstr && CFStringGetCString(str, cstr, size, kCFStringEncodingUTF8)) { + result = fprintf(file, "%s", cstr); + free(cstr); + } + CFRelease(str); + } + CFRelease(formatStr); + } + return result; +} + +int +cfprintf(FILE *file, const char *format, ...) +{ + int result; + va_list args; + + va_start(args, format); + result = cvfprintf(file, format, args); + va_end(args); + return result; +} + +int +cprintf(const char *format, ...) +{ + int result; + va_list args; + + va_start(args, format); + result = cvfprintf(stdout, format, args); + va_end(args); + return result; +} diff --git a/shell_cmds/id/open_directory.h b/shell_cmds/id/open_directory.h new file mode 100644 index 0000000..2c79775 --- /dev/null +++ b/shell_cmds/id/open_directory.h @@ -0,0 +1,18 @@ +#include <OpenDirectory/OpenDirectory.h> + +ODNodeRef CreateNode(void); + +ODRecordRef CopyGroupRecordWithGID(ODNodeRef, gid_t); + +ODRecordRef CopyUserRecordWithUID(ODNodeRef, uid_t); +ODRecordRef CopyUserRecordWithUsername(ODNodeRef, char *); + +CFArrayRef CopyGroupRecordsForUser(ODNodeRef, ODRecordRef, CFIndex); + +CFStringRef CopyAttrFromRecord(ODRecordRef record, CFStringRef attribute); +int GetIntAttrFromRecord(ODRecordRef record, CFStringRef attribute, int *output); +uid_t GetUIDFromRecord(ODRecordRef); +gid_t GetGIDFromRecord(ODRecordRef); + +int cfprintf(FILE *file, const char *format, ...); +int cprintf(const char *format, ...); diff --git a/shell_cmds/id/whoami.1 b/shell_cmds/id/whoami.1 new file mode 100644 index 0000000..568fbaf --- /dev/null +++ b/shell_cmds/id/whoami.1 @@ -0,0 +1,60 @@ +.\" Copyright (c) 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)whoami.1 8.1 (Berkeley) 6/6/93 +.\" $FreeBSD: src/usr.bin/id/whoami.1,v 1.9 2005/01/17 07:44:19 ru Exp $ +.\" +.Dd June 6, 1993 +.Dt WHOAMI 1 +.Os +.Sh NAME +.Nm whoami +.Nd display effective user id +.Sh SYNOPSIS +.Nm +.Sh DESCRIPTION +The +.Nm +utility has been obsoleted by the +.Xr id 1 +utility, and is equivalent to +.Dq Nm id Fl un . +The command +.Dq Nm id Fl p +is suggested for normal interactive use. +.Pp +The +.Nm +utility displays your effective user ID as a name. +.Sh EXIT STATUS +.Ex -std +.Sh SEE ALSO +.Xr id 1 diff --git a/shell_cmds/jot/jot.1 b/shell_cmds/jot/jot.1 new file mode 100644 index 0000000..b94fab4 --- /dev/null +++ b/shell_cmds/jot/jot.1 @@ -0,0 +1,251 @@ +.\" Copyright (c) 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)jot.1 8.1 (Berkeley) 6/6/93 +.\" $FreeBSD: src/usr.bin/jot/jot.1,v 1.24 2010/02/20 00:16:44 delphij Exp $ +.\" +.Dd February 19, 2010 +.Dt JOT 1 +.Os +.Sh NAME +.Nm jot +.Nd print sequential or random data +.Sh SYNOPSIS +.Nm +.Op Fl cnr +.Op Fl b Ar word +.Op Fl w Ar word +.Op Fl s Ar string +.Op Fl p Ar precision +.Op Ar reps Op Ar begin Op Ar end Op Ar s +.Sh DESCRIPTION +The +.Nm +utility is used to print out increasing, decreasing, random, +or redundant data, usually numbers, one per line. +.Pp +The following options are available: +.Bl -tag -width indent +.It Fl r +Generate random data instead of the default sequential data. +.It Fl b Ar word +Just print +.Ar word +repetitively. +.It Fl w Ar word +Print +.Ar word +with the generated data appended to it. +Octal, hexadecimal, exponential, +.Tn ASCII , +zero padded, +and right-adjusted representations +are possible by using the appropriate +.Xr printf 3 +conversion specification inside +.Ar word , +in which case the data are inserted rather than appended. +.It Fl c +This is an abbreviation for +.Fl w Ar %c . +.It Fl s Ar string +Print data separated by +.Ar string . +Normally, newlines separate data. +.It Fl n +Do not print the final newline normally appended to the output. +.It Fl p Ar precision +Print only as many digits or characters of the data +as indicated by the integer +.Ar precision . +In the absence of +.Fl p , +the precision is the greater of the precisions of +.Ar begin +and +.Ar end . +The +.Fl p +option is overridden by whatever appears in a +.Xr printf 3 +conversion following +.Fl w . +.El +.Pp +The last four arguments indicate, respectively, +the number of data, the lower bound, the upper bound, +and the step size or, for random data, the seed. +While at least one of them must appear, +any of the other three may be omitted, and +will be considered as such if given as +.Fl "" +or as an empty string. +Any three of these arguments determines the fourth. +If four are specified and the given and computed values of +.Ar reps +conflict, the lower value is used. +If fewer than three are specified, defaults are assigned +left to right, except for +.Ar s , +which assumes a default of 1 or -1 if both +.Ar begin +and +.Ar end +are given. +.Pp +Defaults for the four arguments are, respectively, +100, 1, 100, and 1, except that when random data are requested, +the seed, +.Ar s , +is picked randomly. +The +.Ar reps +argument is expected to be an unsigned integer, +and if given as zero is taken to be infinite. +The +.Ar begin +and +.Ar end +arguments may be given as real numbers or as characters +representing the corresponding value in +.Tn ASCII . +The last argument must be a real number. +.Pp +Random numbers are obtained through +.Xr arc4random 3 +when no seed is specified, +and through +.Xr random 3 +when a seed is given. +When +.Nm +is asked to generate random integers or characters with begin +and end values in the range of the random number generator function +and no format is specified with one of the +.Fl w , +.Fl b , +or +.Fl p +options, +.Nm +will arrange for all the values in the range to appear in the output +with an equal probability. +In all other cases be careful to ensure that the output format's +rounding or truncation will not skew the distribution of output +values in an unintended way. +.Pp +The name +.Nm +derives in part from +.Nm iota , +a function in APL. +.Sh EXIT STATUS +.Ex -std +.Sh EXAMPLES +The command +.Dl jot - 1 10 +.Pp +prints the integers from 1 to 10, +while the command +.Dl jot 21 -1 1.00 +.Pp +prints 21 evenly spaced numbers increasing from -1 to 1. +The +.Tn ASCII +character set is generated with +.Dl jot -c 128 0 +.Pp +and the strings xaa through xaz with +.Dl jot -w xa%c 26 a +.Pp +while 20 random 8-letter strings are produced with +.Dl "jot -r -c 160 a z | rs -g 0 8" +.Pp +Infinitely many +.Em yes Ns 's +may be obtained through +.Dl jot -b yes 0 +.Pp +and thirty +.Xr ed 1 +substitution commands applying to lines 2, 7, 12, etc.\& is +the result of +.Dl jot -w %ds/old/new/ 30 2 - 5 +.Pp +The stuttering sequence 9, 9, 8, 8, 7, etc.\& can be +produced by suitable choice of step size, +as in +.Dl jot - 9 0 -.5 +.Pp +and a file containing exactly 1024 bytes is created with +.Dl jot -b x 512 > block +.Pp +Finally, to set tabs four spaces apart starting +from column 10 and ending in column 132, use +.Dl expand -`jot -s, - 10 132 4` +.Pp +and to print all lines 80 characters or longer, +.Dl grep `jot -s \&"\&" -b \&. 80` +.Sh DIAGNOSTICS +The following diagnostic messages deserve special explanation: +.Bl -diag +.It "illegal or unsupported format '%s'" +The requested conversion format specifier for +.Xr printf 3 +was not of the form +.Dl %[#][ ][{+,-}][0-9]*[.[0-9]*]? +where +.Dq ?\& +must be one of +.Dl [l]{d,i,o,u,x} +or +.Dl {c,e,f,g,D,E,G,O,U,X} +.It "range error in conversion" +A value to be printed fell outside the range of the data type +associated with the requested output format. +.It "too many conversions" +More than one conversion format specifier has been supplied, +but only one is allowed. +.El +.Sh SEE ALSO +.Xr ed 1 , +.Xr expand 1 , +.Xr rs 1 , +.Xr seq 1 , +.Xr yes 1 , +.Xr arc4random 3 , +.Xr printf 3 , +.Xr random 3 +.Sh HISTORY +The +.Nm +utility first appeared in +.Bx 4.2 . diff --git a/shell_cmds/jot/jot.c b/shell_cmds/jot/jot.c new file mode 100644 index 0000000..1438755 --- /dev/null +++ b/shell_cmds/jot/jot.c @@ -0,0 +1,490 @@ +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static const char copyright[] = +"@(#) Copyright (c) 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)jot.c 8.1 (Berkeley) 6/6/93"; +#endif +#endif +#include <sys/cdefs.h> +__FBSDID("$FreeBSD: src/usr.bin/jot/jot.c,v 1.40 2009/12/13 03:14:06 delphij Exp $"); + +/* + * jot - print sequential or random data + * + * Author: John Kunze, Office of Comp. Affairs, UCB + */ + +#include <ctype.h> +#include <err.h> +#include <limits.h> +#include <stdio.h> +#include <stdint.h> +#include <stdlib.h> +#include <stdbool.h> +#include <string.h> +#include <time.h> +#include <unistd.h> + +/* Defaults */ +#define REPS_DEF 100 +#define BEGIN_DEF 1 +#define ENDER_DEF 100 +#define STEP_DEF 1 + +/* Flags of options that have been set */ +#define HAVE_STEP 1 +#define HAVE_ENDER 2 +#define HAVE_BEGIN 4 +#define HAVE_REPS 8 + +#define is_default(s) (*(s) == 0 || strcmp((s), "-") == 0) + +static bool boring; +static int prec; +static bool longdata; +static bool intdata; +static bool chardata; +static bool nosign; +static const char *sepstring = "\n"; +static char format[BUFSIZ]; + +static void getformat(void); +static int getprec(const char *); +static int putdata(double, bool); +static void usage(void); + +int +main(int argc, char **argv) +{ + bool have_format = false; + bool infinity = false; + bool nofinalnl = false; + bool randomize = false; + bool use_random = false; + int ch; + int mask = 0; + int n = 0; + double begin = BEGIN_DEF; + double divisor; + double ender = ENDER_DEF; + double s = STEP_DEF; + double x, y; + long i; + long reps = REPS_DEF; + + while ((ch = getopt(argc, argv, "b:cnp:rs:w:")) != -1) + switch (ch) { + case 'b': + boring = true; + /* FALLTHROUGH */ + case 'w': + if (strlcpy(format, optarg, sizeof(format)) >= + sizeof(format)) + errx(1, "-%c word too long", ch); + have_format = true; + break; + case 'c': + chardata = true; + break; + case 'n': + nofinalnl = true; + break; + case 'p': + prec = atoi(optarg); + if (prec <= 0) + errx(1, "bad precision value"); + have_format = true; + break; + case 'r': + randomize = true; + break; + case 's': + sepstring = optarg; + break; + default: + usage(); + } + argc -= optind; + argv += optind; + + switch (argc) { /* examine args right to left, falling thru cases */ + case 4: + if (!is_default(argv[3])) { + if (!sscanf(argv[3], "%lf", &s)) + errx(1, "bad s value: %s", argv[3]); + mask |= HAVE_STEP; + if (randomize) + use_random = true; + } + /* FALLTHROUGH */ + case 3: + if (!is_default(argv[2])) { + if (!sscanf(argv[2], "%lf", &ender)) + ender = argv[2][strlen(argv[2])-1]; + mask |= HAVE_ENDER; + if (!prec) + n = getprec(argv[2]); + } + /* FALLTHROUGH */ + case 2: + if (!is_default(argv[1])) { + if (!sscanf(argv[1], "%lf", &begin)) + begin = argv[1][strlen(argv[1])-1]; + mask |= HAVE_BEGIN; + if (!prec) + prec = getprec(argv[1]); + if (n > prec) /* maximum precision */ + prec = n; + } + /* FALLTHROUGH */ + case 1: + if (!is_default(argv[0])) { + if (!sscanf(argv[0], "%ld", &reps)) + errx(1, "bad reps value: %s", argv[0]); + mask |= HAVE_REPS; + } + break; + case 0: + usage(); + default: + errx(1, "too many arguments. What do you mean by %s?", + argv[4]); + } + getformat(); + while (mask) /* 4 bit mask has 1's where last 4 args were given */ + switch (mask) { /* fill in the 0's by default or computation */ + case HAVE_STEP: + case HAVE_ENDER: + case HAVE_ENDER | HAVE_STEP: + case HAVE_BEGIN: + case HAVE_BEGIN | HAVE_STEP: + reps = REPS_DEF; + mask |= HAVE_REPS; + break; + case HAVE_BEGIN | HAVE_ENDER: + s = ender > begin ? 1 : -1; + mask |= HAVE_STEP; + break; + case HAVE_BEGIN | HAVE_ENDER | HAVE_STEP: + if (randomize) + reps = REPS_DEF; + else if (s == 0.0) + reps = 0; + else + reps = (ender - begin + s) / s; + if (reps <= 0) + errx(1, "impossible stepsize"); + mask = 0; + break; + case HAVE_REPS: + case HAVE_REPS | HAVE_STEP: + begin = BEGIN_DEF; + mask |= HAVE_BEGIN; + break; + case HAVE_REPS | HAVE_ENDER: + s = STEP_DEF; + mask = HAVE_REPS | HAVE_ENDER | HAVE_STEP; + break; + case HAVE_REPS | HAVE_ENDER | HAVE_STEP: + if (randomize) + begin = BEGIN_DEF; + else if (reps == 0) + errx(1, "must specify begin if reps == 0"); + begin = ender - reps * s + s; + mask = 0; + break; + case HAVE_REPS | HAVE_BEGIN: + s = STEP_DEF; + mask = HAVE_REPS | HAVE_BEGIN | HAVE_STEP; + break; + case HAVE_REPS | HAVE_BEGIN | HAVE_STEP: + if (randomize) + ender = ENDER_DEF; + else + ender = begin + reps * s - s; + mask = 0; + break; + case HAVE_REPS | HAVE_BEGIN | HAVE_ENDER: + if (reps == 0) + errx(1, "infinite sequences cannot be bounded"); + else if (reps == 1) + s = 0.0; + else + s = (ender - begin) / (reps - 1); + mask = 0; + break; + case HAVE_REPS | HAVE_BEGIN | HAVE_ENDER | HAVE_STEP: + /* if reps given and implied, */ + if (!randomize && s != 0.0) { + long t = (ender - begin + s) / s; + if (t <= 0) + errx(1, "impossible stepsize"); + if (t < reps) /* take lesser */ + reps = t; + } + mask = 0; + break; + default: + errx(1, "bad mask"); + } + if (reps == 0) + infinity = true; + if (randomize) { + if (use_random) { + srandom((unsigned long)s); + divisor = (double)INT32_MAX + 1; + } else + divisor = (double)UINT32_MAX + 1; + + /* + * Attempt to DWIM when the user has specified an + * integer range within that of the random number + * generator: distribute the numbers equally in + * the range [begin .. ender]. Jot's default %.0f + * format would make the appearance of the first and + * last specified value half as likely as the rest. + */ + if (!have_format && prec == 0 && + begin >= 0 && begin < divisor && + ender >= 0 && ender < divisor) { + ender += 1; + nosign = true; + intdata = true; + (void)strlcpy(format, + chardata ? "%c" : "%u", sizeof(format)); + } + x = (ender - begin) * (ender > begin ? 1 : -1); + for (i = 1; i <= reps || infinity; i++) { + if (use_random) + y = random() / divisor; + else + y = arc4random() / divisor; + if (putdata(y * x + begin, !(reps - i))) + errx(1, "range error in conversion"); + } + } else + for (i = 1, x = begin; i <= reps || infinity; i++, x += s) + if (putdata(x, !(reps - i))) + errx(1, "range error in conversion"); + if (!nofinalnl) + putchar('\n'); + exit(0); +} + +/* + * Send x to stdout using the specified format. + * Last is true if this is the set's last value. + * Return 0 if OK, or a positive number if the number passed was + * outside the range specified by the various flags. + */ +static int +putdata(double x, bool last) +{ + + if (boring) + printf("%s", format); + else if (longdata && nosign) { + if (x <= (double)ULONG_MAX && x >= (double)0) + printf(format, (unsigned long)x); + else + return (1); + } else if (longdata) { + if (x <= (double)LONG_MAX && x >= (double)LONG_MIN) + printf(format, (long)x); + else + return (1); + } else if (chardata || (intdata && !nosign)) { + if (x <= (double)INT_MAX && x >= (double)INT_MIN) + printf(format, (int)x); + else + return (1); + } else if (intdata) { + if (x <= (double)UINT_MAX && x >= (double)0) + printf(format, (unsigned int)x); + else + return (1); + + } else + printf(format, x); + if (!last) + fputs(sepstring, stdout); + + return (0); +} + +static void +usage(void) +{ + fprintf(stderr, "%s\n%s\n", + "usage: jot [-cnr] [-b word] [-w word] [-s string] [-p precision]", + " [reps [begin [end [s]]]]"); + exit(1); +} + +/* + * Return the number of digits following the number's decimal point. + * Return 0 if no decimal point is found. + */ +static int +getprec(const char *str) +{ + const char *p; + const char *q; + + for (p = str; *p; p++) + if (*p == '.') + break; + if (!*p) + return (0); + for (q = ++p; *p; p++) + if (!isdigit((unsigned char)*p)) + break; + return (p - q); +} + +/* + * Set format, intdata, chardata, longdata, and nosign + * based on the command line arguments. + */ +static void +getformat(void) +{ + char *p, *p2; + int dot, hash, space, sign, numbers = 0; + size_t sz; + + if (boring) /* no need to bother */ + return; + for (p = format; *p; p++) /* look for '%' */ + if (*p == '%') { + if (p[1] == '%') + p++; /* leave %% alone */ + else + break; + } + sz = sizeof(format) - strlen(format) - 1; + if (!*p && !chardata) { + if (snprintf(p, sz, "%%.%df", prec) >= (int)sz) + errx(1, "-w word too long"); + } else if (!*p && chardata) { + if (strlcpy(p, "%c", sz) >= sz) + errx(1, "-w word too long"); + intdata = true; + } else if (!*(p+1)) { + if (sz <= 0) + errx(1, "-w word too long"); + strcat(format, "%"); /* cannot end in single '%' */ + } else { + /* + * Allow conversion format specifiers of the form + * %[#][ ][{+,-}][0-9]*[.[0-9]*]? where ? must be one of + * [l]{d,i,o,u,x} or {f,e,g,E,G,d,o,x,D,O,U,X,c,u} + */ + p2 = p++; + dot = hash = space = sign = numbers = 0; + while (!isalpha((unsigned char)*p)) { + if (isdigit((unsigned char)*p)) { + numbers++; + p++; + } else if ((*p == '#' && !(numbers|dot|sign|space| + hash++)) || + (*p == ' ' && !(numbers|dot|space++)) || + ((*p == '+' || *p == '-') && !(numbers|dot|sign++)) + || (*p == '.' && !(dot++))) + p++; + else + goto fmt_broken; + } + if (*p == 'l') { + longdata = true; + if (*++p == 'l') { + if (p[1] != '\0') + p++; + goto fmt_broken; + } + } + switch (*p) { + case 'o': case 'u': case 'x': case 'X': + intdata = nosign = true; + break; + case 'd': case 'i': + intdata = true; + break; + case 'D': + if (!longdata) { + intdata = true; + break; + } + case 'O': case 'U': + if (!longdata) { + intdata = nosign = true; + break; + } + case 'c': + if (!(intdata | longdata)) { + chardata = true; + break; + } + case 'h': case 'n': case 'p': case 'q': case 's': case 'L': + case '$': case '*': + goto fmt_broken; + case 'f': case 'e': case 'g': case 'E': case 'G': + if (!longdata) + break; + /* FALLTHROUGH */ + default: +fmt_broken: + *++p = '\0'; + errx(1, "illegal or unsupported format '%s'", p2); + /* NOTREACHED */ + } + while (*++p) + if (*p == '%' && *(p+1) && *(p+1) != '%') + errx(1, "too many conversions"); + else if (*p == '%' && *(p+1) == '%') + p++; + else if (*p == '%' && !*(p+1)) { + if (strlcat(format, "%", sizeof(format)) >= + sizeof(format)) + errx(1, "-w word too long"); + break; + } + } +} diff --git a/shell_cmds/kill/kill.1 b/shell_cmds/kill/kill.1 new file mode 100644 index 0000000..07699e6 --- /dev/null +++ b/shell_cmds/kill/kill.1 @@ -0,0 +1,152 @@ +.\"- +.\" Copyright (c) 1980, 1990, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" the Institute of Electrical and Electronics Engineers, Inc. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)kill.1 8.2 (Berkeley) 4/28/95 +.\" $FreeBSD$ +.\" +.Dd April 28, 1995 +.Dt KILL 1 +.Os +.Sh NAME +.Nm kill +.Nd terminate or signal a process +.Sh SYNOPSIS +.Nm +.Op Fl s Ar signal_name +.Ar pid ... +.Nm +.Fl l +.Op Ar exit_status +.Nm +.Fl Ar signal_name +.Ar pid ... +.Nm +.Fl Ar signal_number +.Ar pid ... +.Sh DESCRIPTION +The +.Nm +utility sends a signal to the processes specified by the +.Ar pid +operands. +.Pp +Only the super-user may send signals to other users' processes. +.Pp +The options are as follows: +.Bl -tag -width indent +.It Fl s Ar signal_name +A symbolic signal name specifying the signal to be sent instead of the +default +.Dv TERM . +.It Fl l Op Ar exit_status +If no operand is given, list the signal names; otherwise, write +the signal name corresponding to +.Ar exit_status . +.It Fl Ar signal_name +A symbolic signal name specifying the signal to be sent instead of the +default +.Dv TERM . +.It Fl Ar signal_number +A non-negative decimal integer, specifying the signal to be sent instead +of the default +.Dv TERM . +.El +.Pp +The following PIDs have special meanings: +.Bl -tag -width indent +.It -1 +If superuser, broadcast the signal to all processes; otherwise broadcast +to all processes belonging to the user. +.El +.Pp +Some of the more commonly used signals: +.Pp +.Bl -tag -width indent -compact +.It 1 +HUP (hang up) +.It 2 +INT (interrupt) +.It 3 +QUIT (quit) +.It 6 +ABRT (abort) +.It 9 +KILL (non-catchable, non-ignorable kill) +.It 14 +ALRM (alarm clock) +.It 15 +TERM (software termination signal) +.El +.Pp +Some shells may provide a builtin +.Nm +command which is similar or identical to this utility. +Consult the +.Xr builtin 1 +manual page. +.Sh EXIT STATUS +.Ex -std +.Sh EXAMPLES +Terminate +the processes with PIDs 142 and 157: +.Pp +.Dl "kill 142 157" +.Pp +Send the hangup signal +.Pq Dv SIGHUP +to the process with PID 507: +.Pp +.Dl "kill -s HUP 507" +.Sh SEE ALSO +.Xr builtin 1 , +.Xr csh 1 , +.Xr killall 1 , +.Xr ps 1 , +.Xr sh 1 , +.Xr kill 2 , +.Xr sigaction 2 +.Sh STANDARDS +The +.Nm +utility is expected to be +.St -p1003.2 +compatible. +.Sh HISTORY +A +.Nm +command appeared in +.At v3 . +.Sh BUGS +A replacement for the command +.Dq Li kill 0 +for +.Xr csh 1 +users should be provided. diff --git a/shell_cmds/kill/kill.c b/shell_cmds/kill/kill.c new file mode 100644 index 0000000..bd4d908 --- /dev/null +++ b/shell_cmds/kill/kill.c @@ -0,0 +1,230 @@ +/*- + * Copyright (c) 1988, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +/* + * Important: This file is used both as a standalone program /bin/kill and + * as a builtin for /bin/sh (#define SHELL). + */ + +#if 0 +#ifndef lint +static char const copyright[] = +"@(#) Copyright (c) 1988, 1993, 1994\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)kill.c 8.4 (Berkeley) 4/28/95"; +#endif /* not lint */ +#endif +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <ctype.h> +#include <err.h> +#include <errno.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#ifdef SHELL +#define main killcmd +#include "bltin/bltin.h" +#endif + +static void nosig(const char *); +static void printsignals(FILE *); +static int signame_to_signum(const char *); +static void usage(void); +static void print_signum(int, FILE *, char); + +#ifdef __APPLE__ +#define sys_nsig NSIG +#endif /* __APPLE__ */ + +int +main(int argc, char *argv[]) +{ + int errors, numsig, pid, ret; + char *ep; + + if (argc < 2) + usage(); + + numsig = SIGTERM; + + argc--, argv++; + if (!strcmp(*argv, "-l")) { + argc--, argv++; + if (argc > 1) + usage(); + if (argc == 1) { + if (!isdigit(**argv)) + usage(); + numsig = strtol(*argv, &ep, 10); + if (!**argv || *ep) + errx(2, "illegal signal number: %s", *argv); + if (numsig >= 128) + numsig -= 128; + if (numsig <= 0 || numsig >= sys_nsig) + nosig(*argv); + print_signum(numsig, stdout, '\n'); + return (0); + } + printsignals(stdout); + return (0); + } + + if (!strcmp(*argv, "-s")) { + argc--, argv++; + if (argc < 1) { + warnx("option requires an argument -- s"); + usage(); + } + if (strcmp(*argv, "0")) { + if ((numsig = signame_to_signum(*argv)) < 0) + nosig(*argv); + } else + numsig = 0; + argc--, argv++; + } else if (**argv == '-' && *(*argv + 1) != '-') { + ++*argv; + if (isalpha(**argv)) { + if ((numsig = signame_to_signum(*argv)) < 0) + nosig(*argv); + } else if (isdigit(**argv)) { + numsig = strtol(*argv, &ep, 10); + if (!**argv || *ep) + errx(2, "illegal signal number: %s", *argv); + if (numsig < 0) + nosig(*argv); + } else + nosig(*argv); + argc--, argv++; + } + + if (argc > 0 && strncmp(*argv, "--", 2) == 0) + argc--, argv++; + + if (argc == 0) + usage(); + + for (errors = 0; argc; argc--, argv++) { +#ifdef SHELL + if (**argv == '%') + ret = killjob(*argv, numsig); + else +#endif + { + pid = strtol(*argv, &ep, 10); + if (!**argv || *ep) + errx(2, "illegal process id: %s", *argv); + ret = kill(pid, numsig); + } + if (ret == -1) { + warn("%s", *argv); + errors = 1; + } + } + + return (errors); +} + +static void +print_signum(int numsig, FILE *fp, char separator) +{ + char *signame; + int i; + + signame = strdup(sys_signame[numsig]); + + for(i = 0; signame[i] != '\0'; i++) { + char upper = toupper(signame[i]); + signame[i] = upper; + } + fprintf(fp, "%s%c", signame, separator); + + free(signame); +} + +static int +signame_to_signum(const char *sig) +{ + int n; + + if (strncasecmp(sig, "SIG", 3) == 0) + sig += 3; + for (n = 1; n < sys_nsig; n++) { + if (!strcasecmp(sys_signame[n], sig)) + return (n); + } + return (-1); +} + +static void +nosig(const char *name) +{ + + warnx("unknown signal %s; valid signals:", name); + printsignals(stderr); +#ifdef SHELL + error(NULL); +#else + exit(2); +#endif +} + +static void +printsignals(FILE *fp) +{ + int n; + + for (n = 1; n < sys_nsig; n++) { + char sep = ' '; + if (n == (sys_nsig / 2) || n == (sys_nsig - 1)) + sep = '\n'; + print_signum(n, fp, sep); + } +} + +static void +usage(void) +{ + + (void)fprintf(stderr, "%s\n%s\n%s\n%s\n", + "usage: kill [-s signal_name] pid ...", + " kill -l [exit_status]", + " kill -signal_name pid ...", + " kill -signal_number pid ..."); +#ifdef SHELL + error(NULL); +#else + exit(2); +#endif +} diff --git a/shell_cmds/kill/kill.plist.part b/shell_cmds/kill/kill.plist.part new file mode 100644 index 0000000..f7fcb9e --- /dev/null +++ b/shell_cmds/kill/kill.plist.part @@ -0,0 +1,18 @@ + <dict> + <key>OpenSourceProject</key> + <string>kill</string> + <key>OpenSourceVersion</key> + <string>2015-03-01</string> + <key>OpenSourceWebsiteURL</key> + <string>http://svnweb.freebsd.org/base/head/bin/kill/</string> + <key>OpenSourceSCM</key> + <string>svn co http://svn.freebsd.org/base/head/bin/kill/</string> + <key>OpenSourceImportDate</key> + <string>2015-07-24</string> + <key>OpenSourceModifications</key> + <array> + <string>kill.c: sys_nsig</string> + </array> + <key>OpenSourceLicense</key> + <string>bsd</string> + </dict> diff --git a/shell_cmds/killall/killall.1 b/shell_cmds/killall/killall.1 new file mode 100644 index 0000000..7c7b59d --- /dev/null +++ b/shell_cmds/killall/killall.1 @@ -0,0 +1,159 @@ +.\" Copyright (C) 1995 by Joerg Wunsch, Dresden +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR(S) ``AS IS'' AND ANY EXPRESS +.\" OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +.\" WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +.\" DISCLAIMED. IN NO EVENT SHALL THE AUTHOR(S) BE LIABLE FOR ANY DIRECT, +.\" INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +.\" (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +.\" SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +.\" STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING +.\" IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +.\" POSSIBILITY OF SUCH DAMAGE. +.\" +.\" $FreeBSD: src/usr.bin/killall/killall.1,v 1.34 2005/01/17 07:44:20 ru Exp $ +.\" +.Dd January 26, 2004 +.Os +.Dt KILLALL 1 +.Sh NAME +.Nm killall +.Nd kill processes by name +.Sh SYNOPSIS +.Nm +.Op Fl delmsvz +.Op Fl help +.Op Fl u Ar user +.Op Fl t Ar tty +.Op Fl c Ar procname +.Op Fl SIGNAL +.Op Ar procname ... +.Sh DESCRIPTION +The +.Nm +utility kills processes selected by name, as opposed to the selection by pid +as done by +.Xr kill 1 . +By default, it will send a +.Dv TERM +signal to all processes with a real UID identical to the +caller of +.Nm +that match the name +.Ar procname . +The super-user is allowed to kill any process. +.Pp +The options are as follows: +.Bl -tag -width 10n -offset indent +.It Fl v +Be more verbose about what will be done. +.It Fl e +Use the effective user ID instead of the (default) real user ID for matching +processes specified with the +.Fl u +option. +.It Fl help +Give a help on the command usage and exit. +.It Fl l +List the names of the available signals and exit, like in +.Xr kill 1 . +.It Fl m +Match the argument +.Ar procname +as a (case sensitive) regular expression against the names +of processes found. +CAUTION! +This is dangerous, a single dot will match any process +running under the real UID of the caller. +.It Fl s +Show only what would be done, but do not send any signal. +.It Fl d +Print detailed information about the processes matched, +but do not send any signal. +.It Fl SIGNAL +Send a different signal instead of the default +.Dv TERM . +The signal may be specified either as a name +(with or without a leading +.Dv SIG ) , +or numerically. +.It Fl u Ar user +Limit potentially matching processes to those belonging to +the specified +.Ar user . +.It Fl t Ar tty +Limit potentially matching processes to those running on +the specified +.Ar tty . +.It Fl c Ar procname +When used with the +.Fl u +or +.Fl t +flags, limit potentially matching processes to those matching +the specified +.Ar procname . +.It Fl z +Do not skip zombies. +This should not have any effect except to print a few error messages +if there are zombie processes that match the specified pattern. +.El +.Sh ALL PROCESSES +Sending a signal to all processes with uid +.Em XYZ +is already supported by +.Xr kill 1 . +So use +.Xr kill 1 +for this job (e.g.\& $ kill -TERM -1 or +as root $ echo kill -TERM -1 | su -m <user>) +.Sh EXIT STATUS +The +.Nm +command will respond with a short usage message and exit with a status +of 2 in case of a command error. +A status of 1 will be returned if +either no matching process has been found or not all processes have +been signalled successfully. +Otherwise, a status of 0 will be +returned. +.Sh DIAGNOSTICS +Diagnostic messages will only be printed if requested by +.Fl d +options. +.Sh SEE ALSO +.Xr kill 1 , +.Xr sysctl 3 +.Sh HISTORY +The +.Nm +command appeared in +.Fx 2.1 . +It has been modeled after the +.Nm +command as available on other platforms. +.Sh AUTHORS +.An -nosplit +The +.Nm +program was originally written in Perl and was contributed by +.An Wolfram Schneider , +this manual page has been written by +.An J\(:org Wunsch . +The current version of +.Nm +was rewritten in C by +.An Peter Wemm +using +.Xr sysctl 3 . diff --git a/shell_cmds/killall/killall.c b/shell_cmds/killall/killall.c new file mode 100644 index 0000000..4ef96a9 --- /dev/null +++ b/shell_cmds/killall/killall.c @@ -0,0 +1,549 @@ +/*- + * Copyright (c) 2000 Peter Wemm <peter@FreeBSD.org> + * Copyright (c) 2000 Paul Saab <ps@FreeBSD.org> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD: src/usr.bin/killall/killall.c,v 1.31 2004/07/29 18:36:35 maxim Exp $"); + +#include <sys/param.h> +#ifndef __APPLE__ +#include <sys/jail.h> +#endif /* !__APPLE__ */ +#include <sys/stat.h> +#include <sys/user.h> +#include <sys/sysctl.h> +#include <fcntl.h> +#include <dirent.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <pwd.h> +#include <signal.h> +#include <regex.h> +#include <ctype.h> +#include <err.h> +#include <errno.h> +#include <unistd.h> +#include <locale.h> + +#include <getopt.h> +#define OPTIONS ("c:dej:lmst:u:vz") + +#ifdef __APPLE__ +#include <TargetConditionals.h> +#endif + +static void __dead2 +usage(void) +{ + +#ifdef __APPLE__ + fprintf(stderr, "usage: killall [-delmsvz] [-help]\n"); +#else /* !__APPLE__ */ + fprintf(stderr, "usage: killall [-delmsvz] [-help] [-j jid]\n"); +#endif /* __APPLE__ */ + fprintf(stderr, + " [-u user] [-t tty] [-c cmd] [-SIGNAL] [cmd]...\n"); + fprintf(stderr, "At least one option or argument to specify processes must be given.\n"); + exit(1); +} + +static char * +upper(const char *str) +{ + static char buf[80]; + char *s; + + strncpy(buf, str, sizeof(buf)); + buf[sizeof(buf) - 1] = '\0'; + for (s = buf; *s; s++) + *s = toupper((unsigned char)*s); + return buf; +} + + +static void +printsig(FILE *fp) +{ + const char *const * p; + int cnt; + int offset = 0; + + for (cnt = NSIG, p = sys_signame + 1; --cnt; ++p) { + offset += fprintf(fp, "%s ", upper(*p)); + if (offset >= 75 && cnt > 1) { + offset = 0; + fprintf(fp, "\n"); + } + } + fprintf(fp, "\n"); +} + +static void +nosig(char *name) +{ + + warnx("unknown signal %s; valid signals:", name); + printsig(stderr); + exit(1); +} + +/* + * kludge_signal_args - remove any signal option (-SIGXXX, -##) from the argv array. + */ +void +kludge_signal_args(int *argc, char **argv, int *sig) +{ + int i; + int shift = 0; + int kludge = 1; + char *ptr; + const char *const *p; + char *ep; + + /* i = 1, skip program name */ + for (i = 1; i < *argc; i++) { + /* Stop kludging if we encounter -- */ + if (strcmp(argv[i], "--") == 0) + kludge = 0; + ptr = argv[i] + 1; + /* Only process arguments that start with - and do not look like an existing option. */ + if (kludge && *argv[i] == '-' && *ptr && strchr(OPTIONS, *ptr) == NULL) { + if (isalpha(*ptr)) { + if (strcmp(ptr, "help") == 0) + usage(); + if (strncasecmp(ptr, "sig", 3) == 0) + ptr += 3; + for (*sig = NSIG, p = sys_signame + 1; --*sig; ++p) + if (strcasecmp(*p, ptr) == 0) { + *sig = p - sys_signame; + break; + } + if (!*sig) + nosig(ptr); + } else if (isdigit(*ptr)) { + *sig = strtol(ptr, &ep, 10); + if (*ep) + errx(1, "illegal signal number: %s", ptr); + if (*sig < 0 || *sig >= NSIG) + nosig(ptr); + } else + nosig(ptr); + + shift++; + continue; + } + + argv[i - shift] = argv[i]; + } + + for (i = *argc - shift; i < *argc; i++) { + argv[i] = NULL; + } + + *argc -= shift; +} + +int +main(int ac, char **av) +{ + struct kinfo_proc *procs = NULL, *newprocs; + struct stat sb; + struct passwd *pw; + regex_t rgx; + regmatch_t pmatch; + int i, j; + char buf[256]; + char *user = NULL; + char *tty = NULL; + char *cmd = NULL; + int vflag = 0; + int sflag = 0; + int dflag = 0; + int eflag = 0; +#ifndef __APPLE__ + int jflag = 0; +#endif /* !__APPLE__*/ + int mflag = 0; + int zflag = 0; + uid_t uid = 0; + dev_t tdev = 0; + pid_t mypid; +#ifdef __APPLE__ + char *thiscmd; +#else /* !__APPLE__ */ + char thiscmd[MAXCOMLEN + 1]; +#endif /* __APPLE__ */ + pid_t thispid; +#ifndef __APPLE__ + uid_t thisuid; +#endif /* !__APPLE__ */ + dev_t thistdev; + int sig = SIGTERM; + char *ep; + int errors = 0; +#ifndef __APPLE__ + int jid; +#endif /* !__APPLE__ */ + int mib[4]; + size_t miblen; + int st, nprocs; + size_t size; + int matched; + int killed = 0; + int ch; + + setlocale(LC_ALL, ""); + + kludge_signal_args(&ac, av, &sig); + + while ((ch = getopt(ac, av, OPTIONS)) != -1) { + switch (ch) { + case 'c': + cmd = optarg; + break; + case 'd': + dflag++; + break; + case 'e': + eflag++; + break; +#ifndef __APPLE__ + case 'j': + jflag++; + jid = strtol(optarg, &ep, 10); + if (*ep) + errx(1, "illegal jid: %s", optarg); + if (jail_attach(jid) == -1) + err(1, "jail_attach(): %d", jid); + break; +#endif /* __APPLE__ */ + case 'l': + printsig(stdout); + exit(0); + case 'm': + mflag++; + break; + case 's': + sflag++; + break; + case 't': + tty = optarg; + break; + case 'u': + user = optarg; + break; + case 'v': + vflag++; + break; + case 'z': + zflag++; + break; + default: + usage(); + } + } + + ac -= optind; + av += optind; + +#ifdef __APPLE__ + if (user == NULL && tty == NULL && cmd == NULL && ac == 0) +#else /* !__APPLE__*/ + if (user == NULL && tty == NULL && cmd == NULL && !jflag && ac == 0) +#endif /* __APPLE__ */ + usage(); + + if (tty) { + if (strncmp(tty, "/dev/", 5) == 0) + snprintf(buf, sizeof(buf), "%s", tty); + else if (strncmp(tty, "tty", 3) == 0) + snprintf(buf, sizeof(buf), "/dev/%s", tty); + else + snprintf(buf, sizeof(buf), "/dev/tty%s", tty); + if (stat(buf, &sb) < 0) + err(1, "stat(%s)", buf); + if (!S_ISCHR(sb.st_mode)) + errx(1, "%s: not a character device", buf); + tdev = sb.st_rdev; + if (dflag) + printf("ttydev:0x%x\n", tdev); + } + if (user) { + uid = strtol(user, &ep, 10); + if (*user == '\0' || *ep != '\0') { /* was it a number? */ + pw = getpwnam(user); + if (pw == NULL) + errx(1, "user %s does not exist", user); + uid = pw->pw_uid; + if (dflag) + printf("uid:%d\n", uid); + } + } else { + uid = getuid(); + if (uid != 0) { + pw = getpwuid(uid); + if (pw) + user = pw->pw_name; + if (dflag) + printf("uid:%d\n", uid); + } + } + size = 0; + mib[0] = CTL_KERN; + mib[1] = KERN_PROC; +#ifdef __APPLE__ + mib[2] = KERN_PROC_ALL; +#else /* !__APPLE__ */ + mib[2] = KERN_PROC_PROC; +#endif /* __APPLE__ */ + mib[3] = 0; + miblen = 3; + + if (user) { + mib[2] = eflag ? KERN_PROC_UID : KERN_PROC_RUID; + mib[3] = uid; + miblen = 4; + } else if (tty) { + mib[2] = KERN_PROC_TTY; + mib[3] = tdev; + miblen = 4; + } + + do { + st = sysctl(mib, miblen, NULL, &size, NULL, 0); + if (st == -1) + err(1, "could not sysctl(KERN_PROC)"); + if (!size) + errx(1, "could not get size from sysctl(KERN_PROC)"); + size += size / 10; + newprocs = realloc(procs, size); + if (newprocs == 0) { + if (procs) + free(procs); + errx(1, "could not reallocate memory"); + } + procs = newprocs; + st = sysctl(mib, miblen, procs, &size, NULL, 0); + } while (st == -1 && errno == ENOMEM); + if (st == -1) + err(1, "could not sysctl(KERN_PROC)"); + if (size % sizeof(struct kinfo_proc) != 0) { + fprintf(stderr, "proc size mismatch (%zu total, %zu chunks)\n", + size, sizeof(struct kinfo_proc)); + fprintf(stderr, "userland out of sync with kernel, recompile libkvm etc\n"); + exit(1); + } + nprocs = size / sizeof(struct kinfo_proc); + if (dflag) + printf("nprocs %d\n", nprocs); + mypid = getpid(); + + for (i = 0; i < nprocs; i++) { +#ifdef __APPLE__ + if (procs[i].kp_proc.p_stat == SZOMB && !zflag) + continue; + thispid = procs[i].kp_proc.p_pid; + + int mib[3], argmax; + size_t syssize; + char *procargs, *cp; + + mib[0] = CTL_KERN; + mib[1] = KERN_ARGMAX; + + syssize = sizeof(argmax); + if (sysctl(mib, 2, &argmax, &syssize, NULL, 0) == -1) + continue; + + procargs = malloc(argmax); + if (procargs == NULL) + continue; + + mib[0] = CTL_KERN; + mib[1] = KERN_PROCARGS2; + mib[2] = thispid; + + syssize = (size_t)argmax; + if (sysctl(mib, 3, procargs, &syssize, NULL, 0) == -1) { + free(procargs); + continue; + } + + for (cp = procargs; cp < &procargs[syssize]; cp++) { + if (*cp == '\0') { + break; + } + } + + if (cp == &procargs[syssize]) { + free(procargs); + continue; + } + + for (; cp < &procargs[syssize]; cp++) { + if (*cp != '\0') { + break; + } + } + + if (cp == &procargs[syssize]) { + free(procargs); + continue; + } + + /* Strip off any path that was specified */ + for (thiscmd = cp; (cp < &procargs[syssize]) && (*cp != '\0'); cp++) { + if (*cp == '/') { + thiscmd = cp + 1; + } + } + + thistdev = procs[i].kp_eproc.e_tdev; +#else /* !__APPLE__ */ + if (procs[i].ki_stat == SZOMB && !zflag) + continue; + thispid = procs[i].ki_pid; + strncpy(thiscmd, procs[i].ki_comm, MAXCOMLEN); + thiscmd[MAXCOMLEN] = '\0'; + thistdev = procs[i].ki_tdev; +#endif /* __APPLE__ */ +#ifndef __APPLE__ + if (eflag) + thisuid = procs[i].ki_uid; /* effective uid */ + else + thisuid = procs[i].ki_ruid; /* real uid */ +#endif /* !__APPLE__ */ + + if (thispid == mypid) { +#ifdef __APPLE__ + free(procargs); +#endif /* __APPLE__ */ + continue; + } + matched = 1; +#ifndef __APPLE__ + if (user) { + if (thisuid != uid) + matched = 0; + } +#endif /* !__APPLE__ */ + if (tty) { + if (thistdev != tdev) + matched = 0; + } + if (cmd) { + if (mflag) { + if (regcomp(&rgx, cmd, + REG_EXTENDED|REG_NOSUB) != 0) { + mflag = 0; + warnx("%s: illegal regexp", cmd); + } + } + if (mflag) { + pmatch.rm_so = 0; + pmatch.rm_eo = strlen(thiscmd); + if (regexec(&rgx, thiscmd, 0, &pmatch, + REG_STARTEND) != 0) + matched = 0; + regfree(&rgx); + } else { + if (strncmp(thiscmd, cmd, MAXCOMLEN) != 0) + matched = 0; + } + } +#ifndef __APPLE__ + if (jflag && thispid == getpid()) + matched = 0; +#endif /* !__APPLE__ */ + if (matched == 0) { +#ifdef __APPLE__ + free(procargs); +#endif /* !__APPLE__ */ + continue; + } + if (ac > 0) + matched = 0; + for (j = 0; j < ac; j++) { + if (mflag) { + if (regcomp(&rgx, av[j], + REG_EXTENDED|REG_NOSUB) != 0) { + mflag = 0; + warnx("%s: illegal regexp", av[j]); + } + } + if (mflag) { + pmatch.rm_so = 0; + pmatch.rm_eo = strlen(thiscmd); + if (regexec(&rgx, thiscmd, 0, &pmatch, + REG_STARTEND) == 0) + matched = 1; + regfree(&rgx); + } else { + if (strcmp(thiscmd, av[j]) == 0) + matched = 1; + } + if (matched) + break; + } + if (matched == 0) { +#ifdef __APPLE__ + free(procargs); +#endif /* __APPLE__ */ + continue; + } + if (dflag) +#ifdef __APPLE__ + printf("sig:%d, cmd:%s, pid:%d, dev:0x%x\n", sig, + thiscmd, thispid, thistdev); +#else /* !__APPLE__ */ + printf("sig:%d, cmd:%s, pid:%d, dev:0x%x uid:%d\n", sig, + thiscmd, thispid, thistdev, thisuid); +#endif /* __APPLE__ */ + + if (vflag || sflag) + printf("kill -%s %d\n", upper(sys_signame[sig]), + thispid); + + killed++; + if (!dflag && !sflag) { + if (kill(thispid, sig) < 0 /* && errno != ESRCH */ ) { + warn("warning: kill -%s %d", + upper(sys_signame[sig]), thispid); + errors = 1; + } + } +#ifdef __APPLE__ + free(procargs); +#endif /* __APPLE__ */ + } + if (killed == 0) { + fprintf(stderr, "No matching processes %swere found\n", + getuid() != 0 ? "belonging to you " : ""); + errors = 1; + } + exit(errors); +} diff --git a/shell_cmds/lastcomm/lastcomm.1 b/shell_cmds/lastcomm/lastcomm.1 new file mode 100644 index 0000000..96f7638 --- /dev/null +++ b/shell_cmds/lastcomm/lastcomm.1 @@ -0,0 +1,132 @@ +.\" $NetBSD: lastcomm.1,v 1.11 2006/12/22 08:00:20 ad Exp $ +.\" +.\" Copyright (c) 1980, 1990, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)lastcomm.1 8.1 (Berkeley) 6/6/93 +.\" +.Dd December 22, 2006 +.Dt LASTCOMM 1 +.Os +.Sh NAME +.Nm lastcomm +.Nd show last commands executed in reverse order +.Sh SYNOPSIS +.Nm +.Op Fl f Ar file +.Op Ar command ... +.Op Ar user ... +.Op Ar terminal ... +.Sh DESCRIPTION +.Nm +gives information on previously executed commands. +With no arguments, +.Nm +prints information about all the commands recorded +during the current accounting file's lifetime. +.Pp +Option: +.Pp +.Bl -tag -width Fl +.It Fl f Ar file +Read from +.Ar file +rather than the default +accounting file. +.El +.Pp +If called with arguments, only accounting entries with a +matching +.Ar command +name, +.Ar user +name, +or +.Ar terminal +name +are printed. +So, for example: +.Pp +.Dl lastcomm a.out root ttyd0 +.Pp +would produce a listing of all the +executions of commands named +.Pa a.out +by user +.Ar root +on the terminal +.Ar ttyd0 . +.Pp +For each process entry, the following are printed. +.Pp +.Bl -bullet -offset indent -compact +.It +The name of the user who ran the process. +.It +Flags, as accumulated by the accounting facilities in the system. +.It +The command name under which the process was called. +.It +The amount of cpu time used by the process (in seconds). +.It +The time the process started. +.It +The elapsed time of the process. +.El +.Pp +The flags are encoded as follows: +.Dq S +indicates the command was +executed by the super-user, +.Dq F +indicates the command ran after +a fork, but without a following +.Xr exec 3 , +.Dq C +indicates the command was run in PDP-11 compatibility mode +(VAX only), +.Dq D +indicates the command terminated with the generation of a +.Pa core +file, and +.Dq X +indicates the command was terminated with a signal. +.Sh FILES +.Bl -tag -width /var/account/acct -compact +.It Pa /var/account/acct +Default accounting file. +.El +.Sh SEE ALSO +.Xr last 1 , +.Xr sigaction 2 , +.Xr acct 5 , +.Xr core 5 +.Sh HISTORY +The +.Nm +command appeared in +.Bx 3.0 . diff --git a/shell_cmds/lastcomm/lastcomm.c b/shell_cmds/lastcomm/lastcomm.c new file mode 100644 index 0000000..e1d9b66 --- /dev/null +++ b/shell_cmds/lastcomm/lastcomm.c @@ -0,0 +1,235 @@ +/* $NetBSD: lastcomm.c,v 1.21 2009/04/12 13:08:31 lukem Exp $ */ + +/* + * Copyright (c) 1980, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +#ifndef lint +__COPYRIGHT("@(#) Copyright (c) 1980, 1993\ + The Regents of the University of California. All rights reserved."); +#endif /* not lint */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)lastcomm.c 8.2 (Berkeley) 4/29/95"; +#endif +__RCSID("$NetBSD: lastcomm.c,v 1.21 2009/04/12 13:08:31 lukem Exp $"); +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/stat.h> +#include <sys/acct.h> + +#include <ctype.h> +#include <err.h> +#include <fcntl.h> +#include <math.h> +#include <pwd.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <struct.h> +#include <time.h> +#include <tzfile.h> +#include <unistd.h> +/* definitions from utmp.h */ +#define UT_NAMESIZE 8 +#define UT_LINESIZE 8 +#include "pathnames.h" + +static time_t expand(u_int); +static char *flagbits(int); +static const char *getdev(dev_t); +static int requested(char *[], struct acct *); +static void usage(void) __dead; + +int main(int, char **); + +int +main(int argc, char *argv[]) +{ + char *p; + struct acct ab; + struct stat sb; + FILE *fp; + off_t size; + time_t t; + double delta; + int ch; + const char *acctfile = _PATH_ACCT; + + setprogname(argv[0]); + + while ((ch = getopt(argc, argv, "f:")) != -1) + switch((char)ch) { + case 'f': + acctfile = optarg; + break; + case '?': + default: + usage(); + } + argc -= optind; + argv += optind; + + /* Open the file. */ + if ((fp = fopen(acctfile, "r")) == NULL || fstat(fileno(fp), &sb)) + err(1, "%s", acctfile); + + /* + * Round off to integral number of accounting records, probably + * not necessary, but it doesn't hurt. + */ + size = sb.st_size - sb.st_size % sizeof(struct acct); + + /* Check if any records to display. */ + if (size < (off_t)sizeof(struct acct)) + exit(0); + + /* + * Seek to before the last entry in the file; use lseek(2) in case + * the file is bigger than a "long". + */ + size -= sizeof(struct acct); + if (lseek(fileno(fp), size, SEEK_SET) == -1) + err(1, "%s", acctfile); + + for (;;) { + if (fread(&ab, sizeof(struct acct), 1, fp) != 1) + err(1, "%s", acctfile); + + if (ab.ac_comm[0] == '\0') { + ab.ac_comm[0] = '?'; + ab.ac_comm[1] = '\0'; + } else + for (p = &ab.ac_comm[0]; + p < &ab.ac_comm[fldsiz(acct, ac_comm)] && *p; ++p) + if (!isprint((unsigned char)*p)) + *p = '?'; + if (!*argv || requested(argv, &ab)) { + + time_t timelong = ab.ac_btime; + t = expand(ab.ac_utime) + expand(ab.ac_stime); + (void)printf( + "%-*.*s %-7s %-*.*s %-*.*s %6.2f secs %.16s", + (int)fldsiz(acct, ac_comm), + (int)fldsiz(acct, ac_comm), + ab.ac_comm, flagbits(ab.ac_flag), + UT_NAMESIZE, UT_NAMESIZE, + user_from_uid(ab.ac_uid, 0), UT_LINESIZE, + UT_LINESIZE, getdev(ab.ac_tty), + t / (double)AHZ, ctime(&timelong)); + delta = expand(ab.ac_etime) / (double)AHZ; + printf(" (%1.0f:%02.0f:%05.2f)\n", + floor(delta / SECSPERHOUR), + floor(fmod(delta, SECSPERHOUR) / SECSPERMIN), + fmod(delta, SECSPERMIN)); + } + /* are we at the beginning of the file yet? */ + if (size == 0) + break; + /* seek backward over the one we read and the next to read */ + if (fseek(fp, 2 * -(long)sizeof(struct acct), SEEK_CUR) == -1) + err(1, "%s", acctfile); + /* and account for its size */ + size -= sizeof(struct acct); + } + exit(0); +} + +static time_t +expand(u_int t) +{ + time_t nt; + + nt = t & 017777; + t >>= 13; + while (t) { + t--; + nt <<= 3; + } + return (nt); +} + +static char * +flagbits(int f) +{ + static char flags[20] = "-"; + char *p; + +#define BIT(flag, ch) if (f & flag) *p++ = ch + + p = flags + 1; + BIT(ASU, 'S'); + BIT(AFORK, 'F'); + BIT(ACOMPAT, 'C'); + BIT(ACORE, 'D'); + BIT(AXSIG, 'X'); + *p = '\0'; + return (flags); +} + +static int +requested(char *argv[], struct acct *acp) +{ + do { + if (!strcmp(user_from_uid(acp->ac_uid, 0), *argv)) + return (1); + if (!strcmp(getdev(acp->ac_tty), *argv)) + return (1); + if (!strncmp(acp->ac_comm, *argv, fldsiz(acct, ac_comm))) + return (1); + } while (*++argv); + return (0); +} + +static const char * +getdev(dev_t dev) +{ + static dev_t lastdev = (dev_t)-1; + static const char *lastname; + + if (dev == NODEV) /* Special case. */ + return ("__"); + if (dev == lastdev) /* One-element cache. */ + return (lastname); + lastdev = dev; + if ((lastname = devname(dev, S_IFCHR)) == NULL) + lastname = "??"; + return (lastname); +} + +static void +usage(void) +{ + (void)fprintf(stderr, + "Usage: %s [ -f file ] [command ...] [user ...] [tty ...]\n", + getprogname()); + exit(1); +} diff --git a/shell_cmds/lastcomm/pathnames.h b/shell_cmds/lastcomm/pathnames.h new file mode 100644 index 0000000..6ca75d8 --- /dev/null +++ b/shell_cmds/lastcomm/pathnames.h @@ -0,0 +1,36 @@ +/* $NetBSD: pathnames.h,v 1.4 2003/08/07 11:14:18 agc Exp $ */ + +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)pathnames.h 8.1 (Berkeley) 6/6/93 + */ + +#include <paths.h> + +#define _PATH_ACCT "/var/account/acct" diff --git a/shell_cmds/locate/bigram/locate.bigram.8 b/shell_cmds/locate/bigram/locate.bigram.8 new file mode 100644 index 0000000..129334c --- /dev/null +++ b/shell_cmds/locate/bigram/locate.bigram.8 @@ -0,0 +1 @@ +.so man8/locate.code.8 diff --git a/shell_cmds/locate/bigram/locate.bigram.c b/shell_cmds/locate/bigram/locate.bigram.c new file mode 100644 index 0000000..a4ad2b1 --- /dev/null +++ b/shell_cmds/locate/bigram/locate.bigram.c @@ -0,0 +1,110 @@ +/* + * Copyright (c) 1995 Wolfram Schneider <wosch@FreeBSD.org>. Berlin. + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * James A. Woods. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD: src/usr.bin/locate/bigram/locate.bigram.c,v 1.12 2005/02/09 09:13:36 stefanf Exp $ + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1989, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)locate.bigram.c 8.1 (Berkeley) 6/6/93"; +#endif /* not lint */ + +/* + * bigram < sorted_file_names | sort -nr | + * awk 'NR <= 128 { printf $2 }' > bigrams + * + * List bigrams for 'updatedb' script. + * Use 'code' to encode a file using this output. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <sys/param.h> /* for MAXPATHLEN */ +#include "locate.h" + +u_char buf1[MAXPATHLEN] = " "; +u_char buf2[MAXPATHLEN]; +u_int bigram[UCHAR_MAX + 1][UCHAR_MAX + 1]; + +int +main(void) +{ + register u_char *cp; + register u_char *oldpath = buf1, *path = buf2; + register u_int i, j; + + while (fgets(path, sizeof(buf2), stdin) != NULL) { + + /* + * We don't need remove newline character '\n'. + * '\n' is less than ASCII_MIN and will be later + * ignored at output. + */ + + + /* skip longest common prefix */ + for (cp = path; *cp == *oldpath; cp++, oldpath++) + if (*cp == '\0') + break; + + while (*cp != '\0' && *(cp + 1) != '\0') { + bigram[(u_char)*cp][(u_char)*(cp + 1)]++; + cp += 2; + } + + /* swap pointers */ + if (path == buf1) { + path = buf2; + oldpath = buf1; + } else { + path = buf1; + oldpath = buf2; + } + } + + /* output, boundary check */ + for (i = ASCII_MIN; i <= ASCII_MAX; i++) + for (j = ASCII_MIN; j <= ASCII_MAX; j++) + if (bigram[i][j] != 0) + (void)printf("%4u %c%c\n", bigram[i][j], i, j); + + exit(0); +} diff --git a/shell_cmds/locate/code/locate.code.8 b/shell_cmds/locate/code/locate.code.8 new file mode 100644 index 0000000..64dcfea --- /dev/null +++ b/shell_cmds/locate/code/locate.code.8 @@ -0,0 +1,23 @@ +.Dd October 15, 2004 +.Dt LOCATE.CODE 8 +.Os Darwin +.Sh NAME +.Nm locate.code, locate.bigram +.Nd sorted list compressor +.Sh SYNOPSIS +.Nm locate.code +.Nm locate.bigram +.Sh DESCRIPTION +.Nm locate.code +and +.Nm locate.bigram +are programs used by +.Xr locate 1 +during the indexing process. +.Nm locate.code +and +.Nm locate.bigram +should not be run manually. +.Sh SEE ALSO +.Xr locate 1 , +.Xr locate.updatedb 8 diff --git a/shell_cmds/locate/code/locate.code.c b/shell_cmds/locate/code/locate.code.c new file mode 100644 index 0000000..9f7850e --- /dev/null +++ b/shell_cmds/locate/code/locate.code.c @@ -0,0 +1,279 @@ +/* + * Copyright (c) 1995 Wolfram Schneider <wosch@FreeBSD.org>. Berlin. + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * James A. Woods. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD: src/usr.bin/locate/code/locate.code.c,v 1.13 2002/03/22 01:22:47 imp Exp $ + */ + +#ifndef lint +static char copyright[] = +"@(#) Copyright (c) 1989, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)locate.code.c 8.1 (Berkeley) 6/6/93"; +#endif /* not lint */ + +/* + * PURPOSE: sorted list compressor (works with a modified 'find' + * to encode/decode a filename database) + * + * USAGE: bigram < list > bigrams + * process bigrams (see updatedb) > common_bigrams + * code common_bigrams < list > squozen_list + * + * METHOD: Uses 'front compression' (see ";login:", Volume 8, Number 1 + * February/March 1983, p. 8). Output format is, per line, an + * offset differential count byte followed by a partially bigram- + * encoded ascii residue. A bigram is a two-character sequence, + * the first 128 most common of which are encoded in one byte. + * + * EXAMPLE: For simple front compression with no bigram encoding, + * if the input is... then the output is... + * + * /usr/src 0 /usr/src + * /usr/src/cmd/aardvark.c 8 /cmd/aardvark.c + * /usr/src/cmd/armadillo.c 14 armadillo.c + * /usr/tmp/zoo 5 tmp/zoo + * + * The codes are: + * + * 0-28 likeliest differential counts + offset to make nonnegative + * 30 switch code for out-of-range count to follow in next word + * 31 an 8 bit char followed + * 128-255 bigram codes (128 most common, as determined by 'updatedb') + * 32-127 single character (printable) ascii residue (ie, literal) + * + * The locate database store any character except newline ('\n') + * and NUL ('\0'). The 8-bit character support don't wast extra + * space until you have characters in file names less than 32 + * or greather than 127. + * + * + * SEE ALSO: updatedb.sh, ../bigram/locate.bigram.c + * + * AUTHOR: James A. Woods, Informatics General Corp., + * NASA Ames Research Center, 10/82 + * 8-bit file names characters: + * Wolfram Schneider, Berlin September 1996 + */ + +#include <sys/param.h> +#include <err.h> +#include <errno.h> +#include <stdlib.h> +#include <string.h> +#include <stdio.h> +#include <unistd.h> +#include "locate.h" + +#define BGBUFSIZE (NBG * 2) /* size of bigram buffer */ + +u_char buf1[MAXPATHLEN] = " "; +u_char buf2[MAXPATHLEN]; +u_char bigrams[BGBUFSIZE + 1] = { 0 }; + +#define LOOKUP 1 /* use a lookup array instead a function, 3x faster */ + +#ifdef LOOKUP +#define BGINDEX(x) (big[(u_char)*x][(u_char)*(x + 1)]) +typedef short bg_t; +bg_t big[UCHAR_MAX + 1][UCHAR_MAX + 1]; +#else +#define BGINDEX(x) bgindex(x) +typedef int bg_t; +int bgindex(char *); +#endif /* LOOKUP */ + + +void usage(void); + +int +main(argc, argv) + int argc; + char *argv[]; +{ + register u_char *cp, *oldpath, *path; + int ch, code, count, diffcount, oldcount; + FILE *fp; + register int i, j; + + while ((ch = getopt(argc, argv, "")) != -1) + switch(ch) { + default: + usage(); + } + argc -= optind; + argv += optind; + + if (argc != 1) + usage(); + + if ((fp = fopen(argv[0], "r")) == NULL) + err(1, "%s", argv[0]); + + /* First copy bigram array to stdout. */ + (void)fgets(bigrams, BGBUFSIZE + 1, fp); + + if (fwrite(bigrams, 1, BGBUFSIZE, stdout) != BGBUFSIZE) + err(1, "stdout"); + (void)fclose(fp); + +#ifdef LOOKUP + /* init lookup table */ + for (i = 0; i < UCHAR_MAX + 1; i++) + for (j = 0; j < UCHAR_MAX + 1; j++) + big[i][j] = (bg_t)-1; + + for (cp = bigrams, i = 0; *cp != '\0'; i += 2, cp += 2) + big[(u_char)*cp][(u_char)*(cp + 1)] = (bg_t)i; + +#endif /* LOOKUP */ + + oldpath = buf1; + path = buf2; + oldcount = 0; + + while (fgets(path, sizeof(buf2), stdin) != NULL) { + + /* skip empty lines */ + if (*path == '\n') + continue; + + /* remove newline */ + for (cp = path; *cp != '\0'; cp++) { +#ifndef LOCATE_CHAR30 + /* old locate implementations core'd for char 30 */ + if (*cp == SWITCH) + *cp = '?'; + else +#endif /* !LOCATE_CHAR30 */ + + /* chop newline */ + if (*cp == '\n') + *cp = '\0'; + } + + /* Skip longest common prefix. */ + for (cp = path; *cp == *oldpath; cp++, oldpath++) + if (*cp == '\0') + break; + + count = cp - path; + diffcount = count - oldcount + OFFSET; + oldcount = count; + if (diffcount < 0 || diffcount > 2 * OFFSET) { + if (putchar(SWITCH) == EOF || + putw(diffcount, stdout) == EOF) + err(1, "stdout"); + } else + if (putchar(diffcount) == EOF) + err(1, "stdout"); + + while (*cp != '\0') { + /* print *two* characters */ + + if ((code = BGINDEX(cp)) != (bg_t)-1) { + /* + * print *one* as bigram + * Found, so mark byte with + * parity bit. + */ + if (putchar((code / 2) | PARITY) == EOF) + err(1, "stdout"); + cp += 2; + } + + else { + for (i = 0; i < 2; i++) { + if (*cp == '\0') + break; + + /* print umlauts in file names */ + if (*cp < ASCII_MIN || + *cp > ASCII_MAX) { + if (putchar(UMLAUT) == EOF || + putchar(*cp++) == EOF) + err(1, "stdout"); + } + + else { + /* normal character */ + if(putchar(*cp++) == EOF) + err(1, "stdout"); + } + } + + } + } + + if (path == buf1) { /* swap pointers */ + path = buf2; + oldpath = buf1; + } else { + path = buf1; + oldpath = buf2; + } + } + /* Non-zero status if there were errors */ + if (fflush(stdout) != 0 || ferror(stdout)) + exit(1); + exit(0); +} + +#ifndef LOOKUP +int +bgindex(bg) /* Return location of bg in bigrams or -1. */ + char *bg; +{ + register char bg0, bg1, *p; + + bg0 = bg[0]; + bg1 = bg[1]; + for (p = bigrams; *p != NULL; p++) + if (*p++ == bg0 && *p == bg1) + break; + return (*p == NULL ? -1 : (--p - bigrams)); +} +#endif /* !LOOKUP */ + +void +usage() +{ + (void)fprintf(stderr, + "usage: locate.code common_bigrams < list > squozen_list\n"); + exit(1); +} diff --git a/shell_cmds/locate/locate/com.apple.locate.plist b/shell_cmds/locate/locate/com.apple.locate.plist new file mode 100644 index 0000000..ceb9078 --- /dev/null +++ b/shell_cmds/locate/locate/com.apple.locate.plist @@ -0,0 +1,35 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>Label</key> + <string>com.apple.locate</string> + <key>Disabled</key> + <true/> + <key>ProgramArguments</key> + <array> + <string>/usr/libexec/locate.updatedb</string> + </array> + <key>ProcessType</key> + <string>Background</string> + <key>KeepAlive</key> + <dict> + <key>PathState</key> + <dict> + <key>/var/db/locate.database</key> + <false/> + </dict> + </dict> + <key>StartCalendarInterval</key> + <dict> + <key>Hour</key> + <integer>3</integer> + <key>Minute</key> + <integer>15</integer> + <key>Weekday</key> + <integer>6</integer> + </dict> + <key>AbandonProcessGroup</key> + <true/> +</dict> +</plist> diff --git a/shell_cmds/locate/locate/concatdb.sh b/shell_cmds/locate/locate/concatdb.sh new file mode 100644 index 0000000..a236332 --- /dev/null +++ b/shell_cmds/locate/locate/concatdb.sh @@ -0,0 +1,70 @@ +#!/bin/sh +# +# Copyright (c) September 1995 Wolfram Schneider <wosch@FreeBSD.org>. Berlin. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# concatdb - concatenate locate databases +# +# usage: concatdb database1 ... databaseN > newdb +# +# Sequence of databases is important. +# +# $FreeBSD: src/usr.bin/locate/locate/concatdb.sh,v 1.10 2000/01/12 08:01:00 kris Exp $ + +# The directory containing locate subprograms +: ${LIBEXECDIR:=/usr/libexec}; export LIBEXECDIR + +PATH=$LIBEXECDIR:/bin:/usr/bin:$PATH; export PATH + +umask 077 # protect temp files + +: ${TMPDIR:=/var/tmp}; export TMPDIR; +test -d "$TMPDIR" || TMPDIR=/var/tmp + +# utilities to built locate database +: ${bigram:=locate.bigram} +: ${code:=locate.code} +: ${sort:=sort} +: ${locate:=locate} + + +case $# in + [01]) echo 'usage: concatdb databases1 ... databaseN > newdb' + exit 1 + ;; +esac + + +bigrams=`mktemp ${TMPDIR=/tmp}/_bigrams.XXXXXXXXXX` || exit 1 +trap 'rm -f $bigrams' 0 1 2 3 5 10 15 + +for db +do + $locate -d $db / +done | $bigram | $sort -nr | awk 'NR <= 128 { printf $2 }' > $bigrams + +for db +do + $locate -d $db / +done | $code $bigrams diff --git a/shell_cmds/locate/locate/fastfind.c b/shell_cmds/locate/locate/fastfind.c new file mode 100644 index 0000000..e683de7 --- /dev/null +++ b/shell_cmds/locate/locate/fastfind.c @@ -0,0 +1,338 @@ +/* + * Copyright (c) 1995 Wolfram Schneider <wosch@FreeBSD.org>. Berlin. + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * James A. Woods. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD: src/usr.bin/locate/locate/fastfind.c,v 1.14 2005/12/07 12:22:46 des Exp $ + */ + + +#ifndef _LOCATE_STATISTIC_ +#define _LOCATE_STATISTIC_ + +void +statistic (fp, path_fcodes) + FILE *fp; /* open database */ + char *path_fcodes; /* for error message */ +{ + register int lines, chars, size, big, zwerg; + register u_char *p, *s; + register int c; + int count, umlaut; + u_char bigram1[NBG], bigram2[NBG], path[MAXPATHLEN]; + + for (c = 0, p = bigram1, s = bigram2; c < NBG; c++) { + p[c] = check_bigram_char(getc(fp)); + s[c] = check_bigram_char(getc(fp)); + } + + lines = chars = big = zwerg = umlaut = 0; + size = NBG + NBG; + + for (c = getc(fp), count = 0; c != EOF; size++) { + if (c == SWITCH) { + count += getwf(fp) - OFFSET; + size += sizeof(int); + zwerg++; + } else + count += c - OFFSET; + + for (p = path + count; (c = getc(fp)) > SWITCH; size++) + if (c < PARITY) { + if (c == UMLAUT) { + c = getc(fp); + size++; + umlaut++; + } + p++; + } else { + /* bigram char */ + big++; + p += 2; + } + + p++; + lines++; + chars += (p - path); + } + + (void)printf("\nDatabase: %s\n", path_fcodes); + (void)printf("Compression: Front: %2.2f%%, ", + (size + big - (2 * NBG)) / (chars / (float)100)); + (void)printf("Bigram: %2.2f%%, ", (size - big) / (size / (float)100)); + (void)printf("Total: %2.2f%%\n", + (size - (2 * NBG)) / (chars / (float)100)); + (void)printf("Filenames: %d, ", lines); + (void)printf("Characters: %d, ", chars); + (void)printf("Database size: %d\n", size); + (void)printf("Bigram characters: %d, ", big); + (void)printf("Integers: %d, ", zwerg); + (void)printf("8-Bit characters: %d\n", umlaut); + +} +#endif /* _LOCATE_STATISTIC_ */ + +extern char separator; + +void +#ifdef FF_MMAP + + +#ifdef FF_ICASE +fastfind_mmap_icase +#else +fastfind_mmap +#endif /* FF_ICASE */ +(pathpart, paddr, len, database) + char *pathpart; /* search string */ + caddr_t paddr; /* mmap pointer */ + int len; /* length of database */ + char *database; /* for error message */ + + +#else /* MMAP */ + + +#ifdef FF_ICASE +fastfind_icase +#else +fastfind +#endif /* FF_ICASE */ + +(fp, pathpart, database) + FILE *fp; /* open database */ + char *pathpart; /* search string */ + char *database; /* for error message */ + + +#endif /* MMAP */ + +{ + register u_char *p, *s, *patend, *q, *foundchar; + register int c, cc; + int count, found, globflag; + u_char *cutoff; + u_char bigram1[NBG], bigram2[NBG], path[MAXPATHLEN]; + +#ifdef FF_ICASE + /* use a lookup table for case insensitive search */ + u_char table[UCHAR_MAX + 1]; + + tolower_word(pathpart); +#endif /* FF_ICASE*/ + + /* init bigram table */ +#ifdef FF_MMAP + if (len < (2*NBG)) + errx(1, "database too small: %s", database); + + for (c = 0, p = bigram1, s = bigram2; c < NBG; c++, len-= 2) { + p[c] = check_bigram_char(*paddr++); + s[c] = check_bigram_char(*paddr++); + } +#else + for (c = 0, p = bigram1, s = bigram2; c < NBG; c++) { + p[c] = check_bigram_char(getc(fp)); + s[c] = check_bigram_char(getc(fp)); + } +#endif /* FF_MMAP */ + + /* find optimal (last) char for searching */ + for (p = pathpart; *p != '\0'; p++) + if (index(LOCATE_REG, *p) != NULL) + break; + + if (*p == '\0') + globflag = 0; + else + globflag = 1; + + p = pathpart; + patend = patprep(p); + cc = *patend; + +#ifdef FF_ICASE + /* set patend char to true */ + for (c = 0; c < UCHAR_MAX + 1; c++) + table[c] = 0; + + table[TOLOWER(*patend)] = 1; + table[toupper(*patend)] = 1; +#endif /* FF_ICASE */ + + + /* main loop */ + found = count = 0; + foundchar = 0; + +#ifdef FF_MMAP + c = (u_char)*paddr++; len--; + for (; len > 0; ) { +#else + c = getc(fp); + for (; c != EOF; ) { +#endif /* FF_MMAP */ + + /* go forward or backward */ + if (c == SWITCH) { /* big step, an integer */ +#ifdef FF_MMAP + count += getwm(paddr) - OFFSET; + len -= INTSIZE; paddr += INTSIZE; +#else + count += getwf(fp) - OFFSET; +#endif /* FF_MMAP */ + } else { /* slow step, =< 14 chars */ + count += c - OFFSET; + } + +#ifdef __APPLE__ + if (count < 0) { + errx(1, "Your locate database appears to be corrupt. " + "Run 'sudo /usr/libexec/locate.updatedb' to " + "regenerate the database."); + } +#endif /* __APPLE__ */ + + /* overlay old path */ + p = path + count; + foundchar = p - 1; + +#ifdef FF_MMAP + for (; len > 0;) { + c = (u_char)*paddr++; + len--; +#else + for (;;) { + c = getc(fp); +#endif /* FF_MMAP */ + /* + * == UMLAUT: 8 bit char followed + * <= SWITCH: offset + * >= PARITY: bigram + * rest: single ascii char + * + * offset < SWITCH < UMLAUT < ascii < PARITY < bigram + */ + if (c < PARITY) { + if (c <= UMLAUT) { + if (c == UMLAUT) { +#ifdef FF_MMAP + c = (u_char)*paddr++; + len--; +#else + c = getc(fp); +#endif /* FF_MMAP */ + + } else + break; /* SWITCH */ + } +#ifdef FF_ICASE + if (table[c]) +#else + if (c == cc) +#endif /* FF_ICASE */ + foundchar = p; + *p++ = c; + } + else { + /* bigrams are parity-marked */ + TO7BIT(c); + +#ifndef FF_ICASE + if (bigram1[c] == cc || + bigram2[c] == cc) +#else + + if (table[bigram1[c]] || + table[bigram2[c]]) +#endif /* FF_ICASE */ + foundchar = p + 1; + + *p++ = bigram1[c]; + *p++ = bigram2[c]; + } + } + + if (found) { /* previous line matched */ + cutoff = path; + *p-- = '\0'; + foundchar = p; + } else if (foundchar >= path + count) { /* a char matched */ + *p-- = '\0'; + cutoff = path + count; + } else /* nothing to do */ + continue; + + found = 0; + for (s = foundchar; s >= cutoff; s--) { + if (*s == cc +#ifdef FF_ICASE + || TOLOWER(*s) == cc +#endif /* FF_ICASE */ + ) { /* fast first char check */ + for (p = patend - 1, q = s - 1; *p != '\0'; + p--, q--) + if (*q != *p +#ifdef FF_ICASE + && TOLOWER(*q) != *p +#endif /* FF_ICASE */ + ) + break; + if (*p == '\0') { /* fast match success */ + found = 1; + if (!globflag || +#ifndef FF_ICASE + !fnmatch(pathpart, path, 0)) +#else + !fnmatch(pathpart, path, + FNM_CASEFOLD)) +#endif /* !FF_ICASE */ + { + if (f_silent) + counter++; + else if (f_limit) { + counter++; + if (f_limit >= counter) + (void)printf("%s%c",path,separator); + else + errx(0, "[show only %d lines]", counter - 1); + } else + (void)printf("%s%c",path,separator); + } + break; + } + } + } + } +} diff --git a/shell_cmds/locate/locate/locate.1 b/shell_cmds/locate/locate/locate.1 new file mode 100644 index 0000000..cdde718 --- /dev/null +++ b/shell_cmds/locate/locate/locate.1 @@ -0,0 +1,276 @@ +.\" Copyright (c) 1995 Wolfram Schneider <wosch@FreeBSD.org>. Berlin. +.\" Copyright (c) 1990, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)locate.1 8.1 (Berkeley) 6/6/93 +.\" $FreeBSD: src/usr.bin/locate/locate/locate.1,v 1.34 2006/09/29 15:20:45 ru Exp $ +.\" +.Dd August 17, 2006 +.Dt LOCATE 1 +.Os +.Sh NAME +.Nm locate +.Nd find filenames quickly +.Sh SYNOPSIS +.Nm +.Op Fl 0Scims +.Op Fl l Ar limit +.Op Fl d Ar database +.Ar pattern ... +.Sh DESCRIPTION +The +.Nm +program searches a database for all pathnames which match the specified +.Ar pattern . +The database is recomputed periodically (usually weekly or daily), +and contains the pathnames +of all files which are publicly accessible. +.Pp +Shell globbing and quoting characters +.Dq ( * , +.Dq \&? , +.Dq \e , +.Dq \&[ +and +.Dq \&] ) +may be used in +.Ar pattern , +although they will have to be escaped from the shell. +Preceding any character with a backslash +.Pq Dq \e +eliminates any special +meaning which it may have. +The matching differs in that no characters must be matched explicitly, +including slashes +.Pq Dq / . +.Pp +As a special case, a pattern containing no globbing characters +.Pq Dq foo +is matched as though it were +.Dq *foo* . +.Pp +Historically, locate only stored characters between 32 and 127. +The +current implementation store any character except newline +.Pq Sq \en +and +.Dv NUL +.Pq Sq \e0 . +The 8-bit character support does not waste extra space for +plain ASCII file names. +Characters less than 32 or greater than 127 +are stored in 2 bytes. +.Pp +The following options are available: +.Bl -tag -width 10n +.It Fl 0 +Print pathnames separated by an +.Tn ASCII +.Dv NUL +character (character code 0) instead of default NL +(newline, character code 10). +.It Fl S +Print some statistics about the database and exit. +.It Fl c +Suppress normal output; instead print a count of matching file names. +.It Fl d Ar database +Search in +.Ar database +instead of the default file name database. +Multiple +.Fl d +options are allowed. +Each additional +.Fl d +option adds the specified database to the list +of databases to be searched. +.Pp +The option +.Ar database +may be a colon-separated list of databases. +A single colon is a reference +to the default database. +.Bd -literal +$ locate -d $HOME/lib/mydb: foo +.Ed +.Pp +will first search string +.Dq foo +in +.Pa $HOME/lib/mydb +and then in +.Pa /var/db/locate.database . +.Bd -literal +$ locate -d $HOME/lib/mydb::/cdrom/locate.database foo +.Ed +.Pp +will first search string +.Dq foo +in +.Pa $HOME/lib/mydb +and then in +.Pa /var/db/locate.database +and then in +.Pa /cdrom/locate.database . +.Pp +.Dl "$ locate -d db1 -d db2 -d db3 pattern" +.Pp +is the same as +.Pp +.Dl "$ locate -d db1:db2:db3 pattern" +.Pp +or +.Pp +.Dl "$ locate -d db1:db2 -d db3 pattern" +.Pp +If +.Fl +is given as the database name, standard input will be read instead. +For example, you can compress your database +and use: +.Bd -literal +$ zcat database.gz | locate -d - pattern +.Ed +.Pp +This might be useful on machines with a fast CPU and little RAM and slow +I/O. +Note: you can only use +.Em one +pattern for stdin. +.It Fl i +Ignore case distinctions in both the pattern and the database. +.It Fl l Ar number +Limit output to +.Ar number +of file names and exit. +.It Fl m +Use +.Xr mmap 2 +instead of the +.Xr stdio 3 +library. +This is the default behavior +and is faster in most cases. +.It Fl s +Use the +.Xr stdio 3 +library instead of +.Xr mmap 2 . +.El +.Sh ENVIRONMENT +.Bl -tag -width LOCATE_PATH -compact +.It Pa LOCATE_PATH +path to the locate database if set and not empty, ignored if the +.Fl d +option was specified. +.El +.Sh FILES +.Bl -tag -width /System/Library/LaunchDaemons/com.apple.locate.plist -compact +.It Pa /var/db/locate.database +locate database +.It Pa /usr/libexec/locate.updatedb +Script to update the locate database +.It Pa /System/Library/LaunchDaemons/com.apple.locate.plist +Job that starts the database rebuild +.El +.Sh SEE ALSO +.Xr find 1 , +.Xr whereis 1 , +.Xr which 1 , +.Xr fnmatch 3 , +.Xr locate.updatedb 8 +.Rs +.%A Woods, James A. +.%D 1983 +.%T "Finding Files Fast" +.%J ";login" +.%V 8:1 +.%P pp. 8-10 +.Re +.Sh HISTORY +The +.Nm +command first appeared in +.Bx 4.4 . +Many new features were +added in +.Fx 2.2 . +.Sh BUGS +The +.Nm +program may fail to list some files that are present, or may +list files that have been removed from the system. +This is because +locate only reports files that are present in the database, which is +typically only regenerated once a week by the +.Pa /System/Library/LaunchDaemons/com.apple.locate.plist +job. +Use +.Xr find 1 +to locate files that are of a more transitory nature. +.Pp +The +.Nm +database is typically built by user +.Dq nobody +and the +.Xr locate.updatedb 8 +utility skips directories +which are not readable for user +.Dq nobody , +group +.Dq nobody , +or +world. +For example, if your HOME directory is not world-readable, +.Em none +of your files are +in the database. +.Pp +The +.Nm +database is not byte order independent. +It is not possible +to share the databases between machines with different byte order. +The current +.Nm +implementation understands databases in host byte order or +network byte order if both architectures use the same integer size. +So on a +.Fx Ns /i386 +machine +(little endian), you can read +a locate database which was built on SunOS/sparc machine +(big endian, net). +.Pp +The +.Nm +utility does not recognize multibyte characters. diff --git a/shell_cmds/locate/locate/locate.c b/shell_cmds/locate/locate/locate.c new file mode 100644 index 0000000..315bfd3 --- /dev/null +++ b/shell_cmds/locate/locate/locate.c @@ -0,0 +1,389 @@ +/* + * Copyright (c) 1995 Wolfram Schneider <wosch@FreeBSD.org>. Berlin. + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * James A. Woods. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static const char copyright[] = +"@(#) Copyright (c) 1995-1996 Wolfram Schneider, Berlin.\n\ +@(#) Copyright (c) 1989, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)locate.c 8.1 (Berkeley) 6/6/93"; +#endif +static const char rcsid[] = + "$FreeBSD: src/usr.bin/locate/locate/locate.c,v 1.17 2006/06/11 17:40:25 maxim Exp $"; +#endif /* not lint */ + +/* + * Ref: Usenix ;login:, Vol 8, No 1, February/March, 1983, p. 8. + * + * Locate scans a file list for the full pathname of a file given only part + * of the name. The list has been processed with with "front-compression" + * and bigram coding. Front compression reduces space by a factor of 4-5, + * bigram coding by a further 20-25%. + * + * The codes are: + * + * 0-28 likeliest differential counts + offset to make nonnegative + * 30 switch code for out-of-range count to follow in next word + * 31 an 8 bit char followed + * 128-255 bigram codes (128 most common, as determined by 'updatedb') + * 32-127 single character (printable) ascii residue (ie, literal) + * + * A novel two-tiered string search technique is employed: + * + * First, a metacharacter-free subpattern and partial pathname is matched + * BACKWARDS to avoid full expansion of the pathname list. The time savings + * is 40-50% over forward matching, which cannot efficiently handle + * overlapped search patterns and compressed path residue. + * + * Then, the actual shell glob-style regular expression (if in this form) is + * matched against the candidate pathnames using the slower routines provided + * in the standard 'find'. + */ + +#include <sys/param.h> +#include <ctype.h> +#include <err.h> +#ifdef __APPLE__ +#include <errno.h> +#endif /* __APPLE__ */ +#include <fnmatch.h> +#include <locale.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#ifdef MMAP +# include <sys/types.h> +# include <sys/stat.h> +# include <sys/mman.h> +# include <fcntl.h> +#endif + + +#include "locate.h" +#include "pathnames.h" + +#ifdef DEBUG +# include <sys/time.h> +# include <sys/types.h> +# include <sys/resource.h> +#endif + +int f_mmap; /* use mmap */ +int f_icase; /* ignore case */ +int f_stdin; /* read database from stdin */ +int f_statistic; /* print statistic */ +int f_silent; /* suppress output, show only count of matches */ +int f_limit; /* limit number of output lines, 0 == infinite */ +u_int counter; /* counter for matches [-c] */ +char separator='\n'; /* line separator */ +#ifdef __APPLE__ +u_char myctype[UCHAR_MAX + 1]; +#endif /* __APPLE__ */ + + +void usage(void); +void statistic(FILE *, char *); +void fastfind(FILE *, char *, char *); +void fastfind_icase(FILE *, char *, char *); +void fastfind_mmap(char *, caddr_t, int, char *); +void fastfind_mmap_icase(char *, caddr_t, int, char *); +void search_mmap(char *, char **); +void search_fopen(char *, char **); +unsigned long cputime(void); + +extern char **colon(char **, char*, char*); +extern void print_matches(u_int); +extern int getwm(caddr_t); +extern int getwf(FILE *); +extern u_char *tolower_word(u_char *); +extern int check_bigram_char(int); +extern char *patprep(char *); + +int +main(argc, argv) + int argc; + char **argv; +{ + register int ch; + char **dbv = NULL; + char *path_fcodes; /* locate database */ +#ifdef MMAP + f_mmap = 1; /* mmap is default */ +#endif + (void) setlocale(LC_ALL, ""); + + while ((ch = getopt(argc, argv, "0Scd:il:ms")) != -1) + switch(ch) { + case '0': /* 'find -print0' style */ + separator = '\0'; + break; + case 'S': /* statistic lines */ + f_statistic = 1; + break; + case 'l': /* limit number of output lines, 0 == infinite */ + f_limit = atoi(optarg); + break; + case 'd': /* database */ + dbv = colon(dbv, optarg, _PATH_FCODES); + break; + case 'i': /* ignore case */ + f_icase = 1; + break; + case 'm': /* mmap */ +#ifdef MMAP + f_mmap = 1; +#else + warnx("mmap(2) not implemented"); +#endif + break; + case 's': /* stdio lib */ + f_mmap = 0; + break; + case 'c': /* suppress output, show only count of matches */ + f_silent = 1; + break; + default: + usage(); + } + argv += optind; + argc -= optind; + + /* to few arguments */ + if (argc < 1 && !(f_statistic)) + usage(); + + /* no (valid) database as argument */ + if (dbv == NULL || *dbv == NULL) { + /* try to read database from enviroment */ + if ((path_fcodes = getenv("LOCATE_PATH")) == NULL || + *path_fcodes == '\0') + /* use default database */ + dbv = colon(dbv, _PATH_FCODES, _PATH_FCODES); + else /* $LOCATE_PATH */ + dbv = colon(dbv, path_fcodes, _PATH_FCODES); + } + + if (f_icase && UCHAR_MAX < 4096) /* init tolower lookup table */ + for (ch = 0; ch < UCHAR_MAX + 1; ch++) + myctype[ch] = tolower(ch); + + /* foreach database ... */ + while((path_fcodes = *dbv) != NULL) { + dbv++; + + if (!strcmp(path_fcodes, "-")) + f_stdin = 1; + else + f_stdin = 0; + +#ifndef MMAP + f_mmap = 0; /* be paranoid */ +#endif + if (!f_mmap || f_stdin || f_statistic) + search_fopen(path_fcodes, argv); + else + search_mmap(path_fcodes, argv); + } + + if (f_silent) + print_matches(counter); + exit(0); +} + + +void +search_fopen(db, s) + char *db; /* database */ + char **s; /* search strings */ +{ + FILE *fp; +#ifdef DEBUG + long t0; +#endif + + /* can only read stdin once */ + if (f_stdin) { + fp = stdin; + if (*(s+1) != NULL) { + warnx("read database from stdin, use only `%s' as pattern", *s); + *(s+1) = NULL; + } + } +#ifdef __APPLE__ + else if ((fp = fopen(db, "r")) == NULL) { + if (errno == ENOENT && !strcmp(db, _PATH_FCODES)) { + fprintf(stderr, "\n" + "WARNING: The locate database (%s) does not exist.\n" + "To create the database, run the following command:\n" + "\n" + " sudo launchctl load -w /System/Library/LaunchDaemons/com.apple.locate.plist\n" + "\n" + "Please be aware that the database can take some time to generate; once\n" + "the database has been created, this message will no longer appear.\n" + "\n", + db); + exit(1); + } + err(1, "`%s'", db); + } +#else /* !__APPLE__ */ + else if ((fp = fopen(db, "r")) == NULL) + err(1, "`%s'", db); +#endif /* __APPLE__ */ + + /* count only chars or lines */ + if (f_statistic) { + statistic(fp, db); + (void)fclose(fp); + return; + } + + /* foreach search string ... */ + while(*s != NULL) { +#ifdef DEBUG + t0 = cputime(); +#endif + if (!f_stdin && + fseek(fp, (long)0, SEEK_SET) == -1) + err(1, "fseek to begin of ``%s''\n", db); + + if (f_icase) + fastfind_icase(fp, *s, db); + else + fastfind(fp, *s, db); +#ifdef DEBUG + warnx("fastfind %ld ms", cputime () - t0); +#endif + s++; + } + (void)fclose(fp); +} + +#ifdef MMAP +void +search_mmap(db, s) + char *db; /* database */ + char **s; /* search strings */ +{ + struct stat sb; + int fd; + caddr_t p; + off_t len; +#ifdef DEBUG + long t0; +#endif + if ((fd = open(db, O_RDONLY)) == -1 || + fstat(fd, &sb) == -1) + err(1, "`%s'", db); + len = sb.st_size; + + if ((p = mmap((caddr_t)0, (size_t)len, + PROT_READ, MAP_SHARED, + fd, (off_t)0)) == MAP_FAILED) + err(1, "mmap ``%s''", db); + + /* foreach search string ... */ + while (*s != NULL) { +#ifdef DEBUG + t0 = cputime(); +#endif + if (f_icase) + fastfind_mmap_icase(*s, p, (int)len, db); + else + fastfind_mmap(*s, p, (int)len, db); +#ifdef DEBUG + warnx("fastfind %ld ms", cputime () - t0); +#endif + s++; + } + + if (munmap(p, (size_t)len) == -1) + warn("munmap %s\n", db); + + (void)close(fd); +} +#endif /* MMAP */ + +#ifdef DEBUG +unsigned long +cputime () +{ + struct rusage rus; + + getrusage(0, &rus); + return(rus.ru_utime.tv_sec * 1000 + rus.ru_utime.tv_usec / 1000); +} +#endif /* DEBUG */ + +void +usage () +{ + (void)fprintf(stderr, + "usage: locate [-0Scims] [-l limit] [-d database] pattern ...\n\n"); + (void)fprintf(stderr, + "default database: `%s' or $LOCATE_PATH\n", _PATH_FCODES); + exit(1); +} + + +/* load fastfind functions */ + +/* statistic */ +/* fastfind_mmap, fastfind_mmap_icase */ +#ifdef MMAP +#undef FF_MMAP +#undef FF_ICASE + +#define FF_MMAP +#include "fastfind.c" +#define FF_ICASE +#include "fastfind.c" +#endif /* MMAP */ + +/* fopen */ +/* fastfind, fastfind_icase */ +#undef FF_MMAP +#undef FF_ICASE +#include "fastfind.c" +#define FF_ICASE +#include "fastfind.c" diff --git a/shell_cmds/locate/locate/locate.h b/shell_cmds/locate/locate/locate.h new file mode 100644 index 0000000..d4aef46 --- /dev/null +++ b/shell_cmds/locate/locate/locate.h @@ -0,0 +1,76 @@ +/* + * Copyright (c) 1995 Wolfram Schneider <wosch@FreeBSD.org>. Berlin. + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)locate.h 8.1 (Berkeley) 6/6/93 + * $FreeBSD: src/usr.bin/locate/locate/locate.h,v 1.7 1999/08/28 01:02:59 peter Exp $ + */ + +/* Symbolic constants shared by locate.c and code.c */ + +#define NBG 128 /* number of bigrams considered */ +#define OFFSET 14 /* abs value of max likely diff */ +#define PARITY 0200 /* parity bit */ +#define SWITCH 30 /* switch code */ +#define UMLAUT 31 /* an 8 bit char followed */ + +/* 0-28 likeliest differential counts + offset to make nonnegative */ +#define LDC_MIN 0 +#define LDC_MAX 28 + +/* 128-255 bigram codes (128 most common, as determined by 'updatedb') */ +#define BIGRAM_MIN (UCHAR_MAX - CHAR_MAX) +#define BIGRAM_MAX UCHAR_MAX + +/* 32-127 single character (printable) ascii residue (ie, literal) */ +#define ASCII_MIN 32 +#define ASCII_MAX CHAR_MAX + +/* #define TO7BIT(x) (x = ( ((u_char)x) & CHAR_MAX )) */ +#define TO7BIT(x) (x = x & CHAR_MAX ) + + +#if UCHAR_MAX >= 4096 + define TOLOWER(ch) tolower(ch) +#else + +#ifdef __APPLE__ +extern u_char myctype[UCHAR_MAX + 1]; +#else +u_char myctype[UCHAR_MAX + 1]; +#endif +#define TOLOWER(ch) (myctype[ch]) +#endif + +#define INTSIZE (sizeof(int)) + +#define LOCATE_REG "*?[]\\" /* fnmatch(3) meta characters */ diff --git a/shell_cmds/locate/locate/locate.rc b/shell_cmds/locate/locate/locate.rc new file mode 100644 index 0000000..09fbf0e --- /dev/null +++ b/shell_cmds/locate/locate/locate.rc @@ -0,0 +1,26 @@ +# +# /etc/locate.rc - command script for updatedb(8) +# +# $FreeBSD: src/usr.bin/locate/locate/locate.rc,v 1.9 2005/08/22 08:22:48 cperciva Exp $ + +# +# All commented values are the defaults +# +# temp directory +#TMPDIR="/tmp" + +# the actual database +#FCODES="/var/db/locate.database" + +# directories to be put in the database +#SEARCHPATHS="/" + +# directories unwanted in output +#PRUNEPATHS="/tmp /var/tmp" + +# filesystems allowed. Beware: a non-listed filesystem will be pruned +# and if the SEARCHPATHS starts in such a filesystem locate will build +# an empty database. +# +# be careful if you add 'nfs' +#FILESYSTEMS="hfs ufs apfs" diff --git a/shell_cmds/locate/locate/locate.updatedb.8 b/shell_cmds/locate/locate/locate.updatedb.8 new file mode 100755 index 0000000..880668a --- /dev/null +++ b/shell_cmds/locate/locate/locate.updatedb.8 @@ -0,0 +1,75 @@ +.\" Copyright (c) 1996 +.\" Mike Pritchard <mpp@FreeBSD.org>. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by Mike Pritchard. +.\" 4. Neither the name of the author nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" $FreeBSD: src/usr.bin/locate/locate/locate.updatedb.8,v 1.12 2005/01/18 13:43:50 ru Exp $ +.\" +.Dd February 11, 1996 +.Dt LOCATE.UPDATEDB 8 +.Os +.Sh NAME +.Nm locate.updatedb +.Nd update locate database +.Sh SYNOPSIS +.Nm /usr/libexec/locate.updatedb +.Sh DESCRIPTION +The +.Nm +utility updates the database used by +.Xr locate 1 . +It is typically run once a week by the +.Pa /System/Library/LaunchDaemons/com.apple.locate.plist +job. +.Pp +The contents of the newly built database can be controlled by the +.Pa /etc/locate.rc +file. +.Sh ENVIRONMENT +.Bl -tag -width /var/db/locate.database -compact +.It Pa LOCATE_CONFIG +path to the configuration file +.El +.Sh FILES +.Bl -tag -width /var/db/locate.database -compact +.It Pa /var/db/locate.database +the default database +.It Pa /etc/locate.rc +the configuration file +.El +.Sh SEE ALSO +.Xr locate 1 , +.Xr launchd 8 +.Rs +.%A Woods, James A. +.%D 1983 +.%T "Finding Files Fast" +.%J ";login" +.%V 8:1 +.%P pp. 8-10 +.Re diff --git a/shell_cmds/locate/locate/mklocatedb.sh b/shell_cmds/locate/locate/mklocatedb.sh new file mode 100644 index 0000000..7231eba --- /dev/null +++ b/shell_cmds/locate/locate/mklocatedb.sh @@ -0,0 +1,92 @@ +#!/bin/sh +# +# Copyright (c) September 1995 Wolfram Schneider <wosch@FreeBSD.org>. Berlin. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# mklocatedb - build locate database +# +# usage: mklocatedb [-presort] < filelist > database +# +# $FreeBSD: src/usr.bin/locate/locate/mklocatedb.sh,v 1.13 2002/07/22 05:35:59 tjr Exp $ + +# The directory containing locate subprograms +: ${LIBEXECDIR:=/usr/libexec}; export LIBEXECDIR + +PATH=$LIBEXECDIR:/bin:/usr/bin:$PATH; export PATH + +umask 077 # protect temp files + +: ${TMPDIR:=/tmp}; export TMPDIR +test -d "$TMPDIR" || TMPDIR=/tmp +if ! TMPDIR=`mktemp -d $TMPDIR/mklocateXXXXXXXXXX`; then + exit 1 +fi + + +# utilities to built locate database +: ${bigram:=locate.bigram} +: ${code:=locate.code} +: ${sort:=sort} + + +sortopt="-u -T $TMPDIR" +sortcmd=$sort + + +bigrams=$TMPDIR/_mklocatedb$$.bigrams +filelist=$TMPDIR/_mklocatedb$$.list + +trap 'rm -f $bigrams $filelist; rmdir $TMPDIR' 0 1 2 3 5 10 15 + + +# Input already sorted +if [ X"$1" = "X-presort" ]; then + shift; + + # create an empty file + true > $bigrams + + # Locate database bootstrapping + # 1. first build a temp database without bigram compression + # 2. create the bigram from the temp database + # 3. create the real locate database with bigram compression. + # + # This scheme avoid large temporary files in /tmp + + $code $bigrams > $filelist || exit 1 + locate -d $filelist / | $bigram | $sort -nr | head -128 | + awk '{if (/^[ ]*[0-9]+[ ]+..$/) {printf("%s",$2)} else {exit 1}}' > $bigrams || exit 1 + locate -d $filelist / | $code $bigrams || exit 1 + exit + +else + if $sortcmd $sortopt > $filelist; then + $bigram < $filelist | $sort -nr | + awk '{if (/^[ ]*[0-9]+[ ]+..$/) {printf("%s",$2)} else {exit 1}}' > $bigrams || exit 1 + $code $bigrams < $filelist || exit 1 + else + echo "`basename $0`: cannot build locate database" >&2 + exit 1 + fi +fi diff --git a/shell_cmds/locate/locate/pathnames.h b/shell_cmds/locate/locate/pathnames.h new file mode 100644 index 0000000..8fb0e8c --- /dev/null +++ b/shell_cmds/locate/locate/pathnames.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)pathnames.h 8.1 (Berkeley) 6/6/93 + */ + +#define _PATH_FCODES "/var/db/locate.database" diff --git a/shell_cmds/locate/locate/updatedb.sh b/shell_cmds/locate/locate/updatedb.sh new file mode 100644 index 0000000..2506f22 --- /dev/null +++ b/shell_cmds/locate/locate/updatedb.sh @@ -0,0 +1,106 @@ +#!/bin/sh +# +# Copyright (c) September 1995 Wolfram Schneider <wosch@FreeBSD.org>. Berlin. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# updatedb - update locate database for local mounted filesystems +# +# $FreeBSD: src/usr.bin/locate/locate/updatedb.sh,v 1.20 2005/11/12 12:45:08 grog Exp $ + +if [ "$(id -u)" = "0" ]; then + rc=0 + export FCODES=`sudo -u nobody mktemp -t updatedb` + chown nobody $FCODES + tmpdb=`su -fm nobody -c "$0"` || rc=1 + if [ $rc = 0 ]; then + install -m 0444 -o nobody -g wheel $FCODES /var/db/locate.database + fi + rm $FCODES + exit $rc +fi +: ${LOCATE_CONFIG="/etc/locate.rc"} +if [ -f "$LOCATE_CONFIG" -a -r "$LOCATE_CONFIG" ]; then + . $LOCATE_CONFIG +fi + +# The directory containing locate subprograms +: ${LIBEXECDIR:=/usr/libexec}; export LIBEXECDIR +: ${TMPDIR:=/tmp}; export TMPDIR +if ! TMPDIR=`mktemp -d $TMPDIR/locateXXXXXXXXXX`; then + exit 1 +fi + +PATH=$LIBEXECDIR:/bin:/usr/bin:$PATH; export PATH + +# 6497475 +set -o noglob + +: ${mklocatedb:=locate.mklocatedb} # make locate database program +: ${FCODES:=/var/db/locate.database} # the database +: ${SEARCHPATHS:="/"} # directories to be put in the database +: ${PRUNEPATHS:="/private/tmp /private/var/folders /private/var/tmp */Backups.backupdb"} # unwanted directories +: ${FILESYSTEMS:="hfs ufs apfs"} # allowed filesystems +: ${find:=find} + +case X"$SEARCHPATHS" in + X) echo "$0: empty variable SEARCHPATHS"; exit 1;; esac +case X"$FILESYSTEMS" in + X) echo "$0: empty variable FILESYSTEMS"; exit 1;; esac + +# Make a list a paths to exclude in the locate run +excludes="! (" or="" +for fstype in $FILESYSTEMS +do + excludes="$excludes $or -fstype $fstype" + or="-or" +done +excludes="$excludes ) -prune" + +case X"$PRUNEPATHS" in + X) ;; + *) for path in $PRUNEPATHS + do + excludes="$excludes -or -path $path -prune" + done;; +esac + +# Ignore the target of firmlinks +while read firmlink; do + excludes="$excludes -or -path $firmlink -prune" +done <<< "$(awk -F'\t' '{print "/System/Volumes/Data/" $2}' /usr/share/firmlinks)" + +tmp=$TMPDIR/_updatedb$$ +trap 'rm -f $tmp; rmdir $TMPDIR; exit' 0 1 2 3 5 10 15 + +# search locally +# echo $find $SEARCHPATHS $excludes -or -print && exit +if $find -s $SEARCHPATHS $excludes -or -print 2>/dev/null | + $mklocatedb -presort > $tmp +then + case X"`$find $tmp -size -257c -print`" in + X) cat $tmp > $FCODES;; + *) echo "updatedb: locate database $tmp is empty" + exit 1 + esac +fi diff --git a/shell_cmds/locate/locate/util.c b/shell_cmds/locate/locate/util.c new file mode 100644 index 0000000..0a94c9c --- /dev/null +++ b/shell_cmds/locate/locate/util.c @@ -0,0 +1,278 @@ +/* + * Copyright (c) 1995 Wolfram Schneider <wosch@FreeBSD.org>. Berlin. + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * James A. Woods. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD: src/usr.bin/locate/locate/util.c,v 1.10 2002/06/24 12:40:11 naddy Exp $ + */ + + +#include <stdlib.h> +#include <string.h> +#include <err.h> +#include <sys/param.h> +#include <arpa/inet.h> +#include <stdio.h> + +#include "locate.h" + +char **colon(char **, char*, char*); +char *patprep(char *); +void print_matches(u_int); +u_char *tolower_word(u_char *); +int getwm(caddr_t); +int getwf(FILE *); +int check_bigram_char(int); + +/* + * Validate bigram chars. If the test failed the database is corrupt + * or the database is obviously not a locate database. + */ +int +check_bigram_char(ch) + int ch; +{ + /* legal bigram: 0, ASCII_MIN ... ASCII_MAX */ + if (ch == 0 || + (ch >= ASCII_MIN && ch <= ASCII_MAX)) + return(ch); + + errx(1, + "locate database header corrupt, bigram char outside 0, %d-%d: %d", + ASCII_MIN, ASCII_MAX, ch); + exit(1); +} + +/* split a colon separated string into a char vector + * + * "bla:foo" -> {"foo", "bla"} + * "bla:" -> {"foo", dot} + * "bla" -> {"bla"} + * "" -> do nothing + * + */ +char ** +colon(dbv, path, dot) + char **dbv; + char *path; + char *dot; /* default for single ':' */ +{ + int vlen, slen; + char *c, *ch, *p; + char **pv; + + if (dbv == NULL) { + if ((dbv = malloc(sizeof(char **))) == NULL) + err(1, "malloc"); + *dbv = NULL; + } + + /* empty string */ + if (*path == '\0') { + warnx("empty database name, ignored"); + return(dbv); + } + + /* length of string vector */ + for(vlen = 0, pv = dbv; *pv != NULL; pv++, vlen++); + + for (ch = c = path; ; ch++) { + if (*ch == ':' || + (!*ch && !(*(ch - 1) == ':' && ch == 1+ path))) { + /* single colon -> dot */ + if (ch == c) + p = dot; + else { + /* a string */ + slen = ch - c; + if ((p = malloc(sizeof(char) * (slen + 1))) + == NULL) + err(1, "malloc"); + bcopy(c, p, slen); + *(p + slen) = '\0'; + } + /* increase dbv with element p */ + if ((dbv = realloc(dbv, sizeof(char **) * (vlen + 2))) + == NULL) + err(1, "realloc"); + *(dbv + vlen) = p; + *(dbv + ++vlen) = NULL; + c = ch + 1; + } + if (*ch == '\0') + break; + } + return (dbv); +} + +void +print_matches(counter) + u_int counter; +{ + (void)printf("%d\n", counter); +} + + +/* + * extract last glob-free subpattern in name for fast pre-match; prepend + * '\0' for backwards match; return end of new pattern + */ +static char globfree[100]; + +char * +patprep(name) + char *name; +{ + register char *endmark, *p, *subp; + + subp = globfree; + *subp++ = '\0'; /* set first element to '\0' */ + p = name + strlen(name) - 1; + + /* skip trailing metacharacters */ + for (; p >= name; p--) + if (index(LOCATE_REG, *p) == NULL) + break; + + /* + * check if maybe we are in a character class + * + * 'foo.[ch]' + * |----< p + */ + if (p >= name && + (index(p, '[') != NULL || index(p, ']') != NULL)) { + for (p = name; *p != '\0'; p++) + if (*p == ']' || *p == '[') + break; + p--; + + /* + * cannot find a non-meta character, give up + * '*\*[a-z]' + * |-------< p + */ + if (p >= name && index(LOCATE_REG, *p) != NULL) + p = name - 1; + } + + if (p < name) + /* only meta chars: "???", force '/' search */ + *subp++ = '/'; + + else { + for (endmark = p; p >= name; p--) + if (index(LOCATE_REG, *p) != NULL) + break; + for (++p; + (p <= endmark) && subp < (globfree + sizeof(globfree));) + *subp++ = *p++; + } + *subp = '\0'; + return(--subp); +} + +/* tolower word */ +u_char * +tolower_word(word) + u_char *word; +{ + register u_char *p; + + for(p = word; *p != '\0'; p++) + *p = TOLOWER(*p); + + return(word); +} + + +/* + * Read integer from mmap pointer. + * Essential a simple ``return *(int *)p'' but avoid sigbus + * for integer alignment (SunOS 4.x, 5.x). + * + * Convert network byte order to host byte order if neccessary. + * So we can read on FreeBSD/i386 (little endian) a locate database + * which was built on SunOS/sparc (big endian). + */ + +int +getwm(p) + caddr_t p; +{ + union { + char buf[INTSIZE]; + int i; + } u; + register int i; + + for (i = 0; i < INTSIZE; i++) + u.buf[i] = *p++; + + i = u.i; + + if (i > MAXPATHLEN || i < -(MAXPATHLEN)) { + i = ntohl(i); + if (i > MAXPATHLEN || i < -(MAXPATHLEN)) + errx(1, "integer out of +-MAXPATHLEN (%d): %d", + MAXPATHLEN, abs(i) < abs(htonl(i)) ? i : htonl(i)); + } + return(i); +} + +/* + * Read integer from stream. + * + * Convert network byte order to host byte order if neccessary. + * So we can read on FreeBSD/i386 (little endian) a locate database + * which was built on SunOS/sparc (big endian). + */ + +int +getwf(fp) + FILE *fp; +{ + register int word; + + word = getw(fp); + + if (word > MAXPATHLEN || word < -(MAXPATHLEN)) { + word = ntohl(word); + if (word > MAXPATHLEN || word < -(MAXPATHLEN)) + errx(1, "integer out of +-MAXPATHLEN (%d): %d", + MAXPATHLEN, abs(word) < abs(htonl(word)) ? word : + htonl(word)); + } + return(word); +} diff --git a/shell_cmds/logname/logname.1 b/shell_cmds/logname/logname.1 new file mode 100644 index 0000000..4d2a508 --- /dev/null +++ b/shell_cmds/logname/logname.1 @@ -0,0 +1,78 @@ +.\" $NetBSD: logname.1,v 1.8 1997/10/19 04:20:06 lukem Exp $ +.\" +.\" Copyright (c) 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" the Institute of Electrical and Electronics Engineers, Inc. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)logname.1 8.1 (Berkeley) 6/9/93 +.\" +.Dd June 9, 1993 +.Dt LOGNAME 1 +.Os BSD 4.4 +.Sh NAME +.Nm logname +.Nd display user's login name +.Sh SYNOPSIS +.Nm +.Sh DESCRIPTION +The +.Nm +utility writes the user's login name to standard output followed by +a newline. +.Pp +The +.Nm +utility explicitly ignores the +.Ev LOGNAME +and +.Ev USER +environment variables +because the environment cannot be trusted. +.Pp +The +.Nm +utility exits 0 on success, and >0 if an error occurs. +.Sh SEE ALSO +.Xr who 1 , +.Xr whoami 1 , +.Xr getlogin 2 +.Sh STANDARDS +The +.Nm +utility conforms to +.St -p1003.2-92 . +.Sh HISTORY +The +.Nm +command appeared in +.Bx 4.4 . diff --git a/shell_cmds/logname/logname.c b/shell_cmds/logname/logname.c new file mode 100644 index 0000000..ed63ffe --- /dev/null +++ b/shell_cmds/logname/logname.c @@ -0,0 +1,92 @@ +/* $NetBSD: logname.c,v 1.7 1997/10/19 04:20:06 lukem Exp $ */ + +/*- + * Copyright (c) 1991, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +#ifndef lint +__COPYRIGHT("@(#) Copyright (c) 1991, 1993, 1994\n\ + The Regents of the University of California. All rights reserved.\n"); +#endif /* not lint */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)logname.c 8.2 (Berkeley) 4/3/94"; +#endif +__RCSID("$NetBSD: logname.c,v 1.7 1997/10/19 04:20:06 lukem Exp $"); +#endif /* not lint */ + +#include <stdio.h> +#include <stdlib.h> +#include <locale.h> +#include <unistd.h> +#include <err.h> + +int main __P((int, char **)); +void usage __P((void)); + +int +main(argc, argv) + int argc; + char *argv[]; +{ + int ch; + char *p; + + setlocale(LC_ALL, ""); + + while ((ch = getopt(argc, argv, "")) != -1) + switch (ch) { + case '?': + default: + usage(); + /* NOTREACHED */ + } + + if (argc != optind) { + usage(); + /* NOTREACHED */ + } + + if ((p = getlogin()) == NULL) + err(1, "getlogin"); + (void)printf("%s\n", p); + exit(0); +} + +void +usage() +{ + (void)fprintf(stderr, "usage: logname\n"); + exit(1); +} diff --git a/shell_cmds/mktemp/mktemp.1 b/shell_cmds/mktemp/mktemp.1 new file mode 100644 index 0000000..921ffa3 --- /dev/null +++ b/shell_cmds/mktemp/mktemp.1 @@ -0,0 +1,215 @@ +.\" Copyright (c) 1989, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" From: $OpenBSD: mktemp.1,v 1.8 1998/03/19 06:13:37 millert Exp $ +.\" $FreeBSD$ +.\" +.Dd December 30, 2005 +.Dt MKTEMP 1 +.Os +.Sh NAME +.Nm mktemp +.Nd make temporary file name (unique) +.Sh SYNOPSIS +.Nm +.Op Fl d +.Op Fl q +.Op Fl t Ar prefix +.Op Fl u +.Ar template ... +.Nm +.Op Fl d +.Op Fl q +.Op Fl u +.Fl t Ar prefix +.Sh DESCRIPTION +The +.Nm +utility takes each of the given file name templates and overwrites a +portion of it to create a file name. +This file name is unique +and suitable for use by the application. +The template may be +any file name with some number of +.Ql X Ns s +appended +to it, for example +.Pa /tmp/temp.XXXX . +The trailing +.Ql X Ns s +are replaced with the current process number and/or a +unique letter combination. +The number of unique file names +.Nm +can return depends on the number of +.Ql X Ns s +provided; six +.Ql X Ns s +will +result in +.Nm +selecting 1 of 56800235584 (62 ** 6) possible file names. +On case-insensitive file systems, the effective number of unique +names is significantly less; given six +.Ql X Ns s , +.Nm +will instead select 1 of 2176782336 (36 ** 6) possible unique file names. +.Pp +If +.Nm +can successfully generate a unique file name, the file +is created with mode 0600 (unless the +.Fl u +flag is given) and the filename is printed +to standard output. +.Pp +If the +.Fl t Ar prefix +option is given, +.Nm +will generate a template string based on the +.Ar prefix +and the +.Dv _CS_DARWIN_USER_TEMP_DIR +configuration variable if available. +Fallback locations if +.Dv _CS_DARWIN_USER_TEMP_DIR +is not available are +.Ev TMPDIR +and +.Pa /tmp . +Care should +be taken to ensure that it is appropriate to use an environment variable +potentially supplied by the user. +.Pp +If no arguments are passed or if only the +.Fl d +flag is passed +.Nm +behaves as if +.Fl t Li tmp +was supplied. +.Pp +Any number of temporary files may be created in a single invocation, +including one based on the internal template resulting from the +.Fl t +flag. +.Pp +The +.Nm +utility is provided to allow shell scripts to safely use temporary files. +Traditionally, many shell scripts take the name of the program with +the pid as a suffix and use that as a temporary file name. +This +kind of naming scheme is predictable and the race condition it creates +is easy for an attacker to win. +A safer, though still inferior, approach +is to make a temporary directory using the same naming scheme. +While +this does allow one to guarantee that a temporary file will not be +subverted, it still allows a simple denial of service attack. +For these +reasons it is suggested that +.Nm +be used instead. +.Sh OPTIONS +The available options are as follows: +.Bl -tag -width indent +.It Fl d +Make a directory instead of a file. +.It Fl q +Fail silently if an error occurs. +This is useful if +a script does not want error output to go to standard error. +.It Fl t Ar prefix +Generate a template (using the supplied +.Ar prefix +and +.Ev TMPDIR +if set) to create a filename template. +.It Fl u +Operate in +.Dq unsafe +mode. +The temp file will be unlinked before +.Nm +exits. +This is slightly better than +.Xr mktemp 3 +but still introduces a race condition. +Use of this +option is not encouraged. +.El +.Sh EXIT STATUS +.Ex -std +.Sh EXAMPLES +The following +.Xr sh 1 +fragment illustrates a simple use of +.Nm +where the script should quit if it cannot get a safe +temporary file. +.Bd -literal -offset indent +tempfoo=`basename $0` +TMPFILE=`mktemp /tmp/${tempfoo}.XXXXXX` || exit 1 +echo "program output" >> $TMPFILE +.Ed +.Pp +To allow the use of $TMPDIR: +.Bd -literal -offset indent +tempfoo=`basename $0` +TMPFILE=`mktemp -t ${tempfoo}` || exit 1 +echo "program output" >> $TMPFILE +.Ed +.Pp +In this case, we want the script to catch the error itself. +.Bd -literal -offset indent +tempfoo=`basename $0` +TMPFILE=`mktemp -q /tmp/${tempfoo}.XXXXXX` +if [ $? -ne 0 ]; then + echo "$0: Can't create temp file, exiting..." + exit 1 +fi +.Ed +.Sh SEE ALSO +.Xr mkdtemp 3 , +.Xr mkstemp 3 , +.Xr mktemp 3 , +.Xr confstr 3 , +.Xr environ 7 +.Sh HISTORY +A +.Nm +utility appeared in +.Ox 2.1 . +This implementation was written independently based on the +.Ox +man page, and +first appeared in +.Fx 2.2.7 . +This man page is taken from +.Ox . diff --git a/shell_cmds/mktemp/mktemp.c b/shell_cmds/mktemp/mktemp.c new file mode 100644 index 0000000..11db17e --- /dev/null +++ b/shell_cmds/mktemp/mktemp.c @@ -0,0 +1,182 @@ +/*- + * Copyright (c) 1994, 1995, 1996, 1998 Peter Wemm <peter@netplex.com.au> + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + */ + +/* + * This program was originally written long ago, originally for a non + * BSD-like OS without mkstemp(). It's been modified over the years + * to use mkstemp() rather than the original O_CREAT|O_EXCL/fstat/lstat + * etc style hacks. + * A cleanup, misc options and mkdtemp() calls were added to try and work + * more like the OpenBSD version - which was first to publish the interface. + */ + +#include <err.h> +#ifdef __APPLE__ +#include <limits.h> +#endif +#include <paths.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#ifndef lint +static const char rcsid[] = + "$FreeBSD$"; +#endif /* not lint */ + +static void usage(void); + +int +main(int argc, char **argv) +{ + int c, fd, ret; + char *tmpdir; + const char *prefix; + char *name; + int dflag, qflag, tflag, uflag; +#ifdef __APPLE__ + char tmpbuf[PATH_MAX]; + size_t len; +#endif + + ret = dflag = qflag = tflag = uflag = 0; + prefix = "mktemp"; + name = NULL; + + while ((c = getopt(argc, argv, "dqt:u")) != -1) + switch (c) { + case 'd': + dflag++; + break; + + case 'q': + qflag++; + break; + + case 't': + prefix = optarg; + tflag++; + break; + + case 'u': + uflag++; + break; + + default: + usage(); + } + + argc -= optind; + argv += optind; + + if (!tflag && argc < 1) { + tflag = 1; + prefix = "tmp"; + } + + if (tflag) { +#ifdef __APPLE__ + if (confstr(_CS_DARWIN_USER_TEMP_DIR, tmpbuf, sizeof(tmpbuf)) > 0) { + tmpdir = tmpbuf; + } else { + tmpdir = getenv("TMPDIR"); + } + + if (tmpdir == NULL) { + tmpdir = _PATH_TMP; + } + + len = strlen(tmpdir); + if (len > 0 && tmpdir[len - 1] == '/') { + asprintf(&name, "%s%s.XXXXXXXX", tmpdir, prefix); + } else { + asprintf(&name, "%s/%s.XXXXXXXX", tmpdir, prefix); + } +#else + tmpdir = getenv("TMPDIR"); + if (tmpdir == NULL) + asprintf(&name, "%s%s.XXXXXXXX", _PATH_TMP, prefix); + else + asprintf(&name, "%s/%s.XXXXXXXX", tmpdir, prefix); +#endif + /* if this fails, the program is in big trouble already */ + if (name == NULL) { + if (qflag) + return (1); + else + errx(1, "cannot generate template"); + } + } + + /* generate all requested files */ + while (name != NULL || argc > 0) { + if (name == NULL) { + name = strdup(argv[0]); + argv++; + argc--; + } + + if (dflag) { + if (mkdtemp(name) == NULL) { + ret = 1; + if (!qflag) + warn("mkdtemp failed on %s", name); + } else { + printf("%s\n", name); + if (uflag) + rmdir(name); + } + } else { + fd = mkstemp(name); + if (fd < 0) { + ret = 1; + if (!qflag) + warn("mkstemp failed on %s", name); + } else { + close(fd); + if (uflag) + unlink(name); + printf("%s\n", name); + } + } + if (name) + free(name); + name = NULL; + } + return (ret); +} + +static void +usage(void) +{ + fprintf(stderr, + "usage: mktemp [-d] [-q] [-t prefix] [-u] template ...\n"); + fprintf(stderr, + " mktemp [-d] [-q] [-u] -t prefix \n"); + exit (1); +} diff --git a/shell_cmds/mktemp/mktemp.plist.part b/shell_cmds/mktemp/mktemp.plist.part new file mode 100644 index 0000000..db10919 --- /dev/null +++ b/shell_cmds/mktemp/mktemp.plist.part @@ -0,0 +1,21 @@ + <dict> + <key>OpenSourceProject</key> + <string>mktemp</string> + <key>OpenSourceVersion</key> + <string>2012-11-18</string> + <key>OpenSourceWebsiteURL</key> + <string>http://svnweb.freebsd.org/base/head/usr.bin/mktemp/</string> + <key>OpenSourceSCM</key> + <string>svn co http://svn.freebsd.org/base/head/usr.bin/mktemp/</string> + <key>OpenSourceImportDate</key> + <string>2014-08-11</string> + <key>OpenSourceModifications</key> + <array> + <string>Avoid double-slash in output. (5334050)</string> + <string>Document case-insensitive file system behavior. (14280248)</string> + <string>Prefer _CS_DARWIN_USER_TEMP_DIR over TMPDIR and _PATH_TMP. (14338140)</string> + <string>mktemp.1: Clarification of _CS_DARWIN_USER_TEMP_DIR/TMPDIR/_PATH_TMP behavior. (22954705)</string> + </array> + <key>OpenSourceLicense</key> + <string>bsd</string> + </dict> diff --git a/shell_cmds/nice/nice.1 b/shell_cmds/nice/nice.1 new file mode 100644 index 0000000..4dba5c9 --- /dev/null +++ b/shell_cmds/nice/nice.1 @@ -0,0 +1,120 @@ +.\" $NetBSD: nice.1,v 1.8 1997/10/19 06:28:02 lukem Exp $ +.\" +.\" Copyright (c) 1980, 1990, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)nice.1 8.1 (Berkeley) 6/6/93 +.\" +.Dd June 6, 1993 +.Dt NICE 1 +.Os +.Sh NAME +.Nm nice +.Nd execute a utility with an altered scheduling priority +.Sh SYNOPSIS +.Nm +.Op Fl n Ar increment +.Ar utility +.Op Ar argument ... +.Sh DESCRIPTION +.Nm +runs +.Ar utility +at an altered scheduling priority. +If an +.Ar increment +is given, it is used; otherwise +an increment of 10 is assumed. +The super-user can run utilities with priorities higher than normal by using +a negative +.Ar increment . +The priority can be adjusted over a +range of -20 (the highest) to 20 (the lowest). +.Pp +Available options: +.Bl -tag -width indent +.It Fl n Ar increment +A positive or negative decimal integer used to modify the system scheduling +priority of +.Ar utility. +.El +.Sh DIAGNOSTICS +The +.Nm +utility shall exit with one of the following values: +.Bl -tag -width indent +.It 1-125 +An error occurred in the +.Nm +utility. +.It 126 +The +.Ar utility +was found but could not be invoked. +.It 127 +The +.Ar utility +could not be found. +.El +.Pp +Otherwise, the exit status of +.Nm +shall be that of +.Ar utility . +.Sh COMPATIBILITY +The historic +.Fl Ns Ar increment +option has been deprecated but is still supported in this implementation. +.Sh SEE ALSO +.Xr csh 1 , +.Xr getpriority 2 , +.Xr setpriority 2 , +.Xr renice 8 +.Sh STANDARDS +The +.Nm +utility conforms to +.St -p1003.2-92 . +.Sh HISTORY +A +.Nm +utility appeared in +.At v6 . +.Sh BUGS +.Nm +is built into +.Xr csh 1 +with a slightly different syntax than described here. The form +.Ql nice +10 +nices to positive nice, and +.Ql nice \-10 +can be used +by the super-user to give a process more of the processor. diff --git a/shell_cmds/nice/nice.c b/shell_cmds/nice/nice.c new file mode 100644 index 0000000..62a197e --- /dev/null +++ b/shell_cmds/nice/nice.c @@ -0,0 +1,122 @@ +/* $NetBSD: nice.c,v 1.10 1997/10/19 06:28:04 lukem Exp $ */ + +/* + * Copyright (c) 1989 The Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +#ifndef lint +__COPYRIGHT( + "@(#) Copyright (c) 1989 The Regents of the University of California.\n\ + All rights reserved.\n"); +#endif /* not lint */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)nice.c 5.4 (Berkeley) 6/1/90"; +#endif +__RCSID("$NetBSD: nice.c,v 1.10 1997/10/19 06:28:04 lukem Exp $"); +#endif /* not lint */ + +#include <sys/types.h> +#include <sys/time.h> +#include <sys/resource.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <locale.h> +#include <ctype.h> +#include <errno.h> +#include <err.h> +#include <unistd.h> + +#define DEFNICE 10 + +int main __P((int, char **)); +static void usage __P((void)); + +int +main(argc, argv) + int argc; + char **argv; +{ + int niceness = DEFNICE; + int c; + + setlocale(LC_ALL, ""); + + /* handle obsolete -number syntax */ + if (argc > 1 && argv[1][0] == '-' && isdigit(argv[1][1])) { + niceness = atoi (argv[1] + 1); + argc--; argv++; + } + + while ((c = getopt (argc, argv, "n:")) != -1) { + switch (c) { + case 'n': + niceness = atoi (optarg); + break; + + case '?': + default: + usage(); + break; + } + } + argc -= optind; argv += optind; + + if (argc == 0) + usage(); + + errno = 0; + niceness += getpriority(PRIO_PROCESS, 0); + if (errno) { + err (1, "getpriority"); + /* NOTREACHED */ + } + if (setpriority(PRIO_PROCESS, 0, niceness)) { + warn ("setpriority"); + } + + execvp(argv[0], &argv[0]); + err ((errno == ENOENT) ? 127 : 126, "%s", argv[0]); + /* NOTREACHED */ +} + +static void +usage() +{ + (void)fprintf(stderr, + "usage: nice [ -n increment ] utility [ argument ...]\n"); + + exit(1); +} diff --git a/shell_cmds/nohup/nohup.1 b/shell_cmds/nohup/nohup.1 new file mode 100644 index 0000000..34f19e6 --- /dev/null +++ b/shell_cmds/nohup/nohup.1 @@ -0,0 +1,124 @@ +.\" Copyright (c) 1989, 1990, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" the Institute of Electrical and Electronics Engineers, Inc. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)nohup.1 8.1 (Berkeley) 6/6/93 +.\" $FreeBSD: src/usr.bin/nohup/nohup.1,v 1.13 2005/01/17 07:44:27 ru Exp $ +.\" +.Dd July 19, 2001 +.Dt NOHUP 1 +.Os +.Sh NAME +.Nm nohup +.Nd invoke a utility immune to hangups +.Sh SYNOPSIS +.Nm +.Op Fl Fl +.Ar utility +.Op Ar arguments +.Sh DESCRIPTION +The +.Nm +utility invokes +.Ar utility +with its +.Ar arguments +and at this time sets the signal +.Dv SIGHUP +to be ignored. +If the standard output is a terminal, the standard output is +appended to the file +.Pa nohup.out +in the current directory. +If standard error is a terminal, it is directed to the same place +as the standard output. +.Pp +Some shells may provide a builtin +.Nm +command which is similar or identical to this utility. +Consult the +.Xr builtin 1 +manual page. +.Sh ENVIRONMENT +The following variables are utilized by +.Nm : +.Bl -tag -width flag +.It Ev HOME +If the output file +.Pa nohup.out +cannot be created in the current directory, the +.Nm +utility uses the directory named by +.Ev HOME +to create the file. +.It Ev PATH +Used to locate the requested +.Ar utility +if the name contains no +.Ql / +characters. +.El +.Sh EXIT STATUS +The +.Nm +utility exits with one of the following values: +.Bl -tag -width Ds +.It 126 +The +.Ar utility +was found, but could not be invoked. +.It 127 +The +.Ar utility +could not be found or an error occurred in +.Nm . +.El +.Pp +Otherwise, the exit status of +.Nm +will be that of +.Ar utility . +.Sh SEE ALSO +.Xr builtin 1 , +.Xr csh 1 , +.Xr signal 3 +.Sh STANDARDS +The +.Nm +utility is expected to be +.St -p1003.2 +compatible. +.Sh BUGS +Two or more instances of +.Nm +can append to the same file, which makes for a confusing output. diff --git a/shell_cmds/nohup/nohup.c b/shell_cmds/nohup/nohup.c new file mode 100644 index 0000000..021361c --- /dev/null +++ b/shell_cmds/nohup/nohup.c @@ -0,0 +1,149 @@ +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * Portions copyright (c) 2007 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if 0 +#ifndef lint +static const char copyright[] = +"@(#) Copyright (c) 1989, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)nohup.c 8.1 (Berkeley) 6/6/93"; +#endif /* not lint */ +#endif +#include <sys/cdefs.h> +#ifndef __APPLE__ +__FBSDID("$FreeBSD: src/usr.bin/nohup/nohup.c,v 1.10 2003/05/03 19:44:46 obrien Exp $"); +#endif + +#include <sys/param.h> +#include <sys/stat.h> + +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#ifdef __APPLE__ +#include <TargetConditionals.h> +#include <vproc.h> +#include <vproc_priv.h> +#endif + +static void dofile(void); +static void usage(void); + +#define FILENAME "nohup.out" +/* + * POSIX mandates that we exit with: + * 126 - If the utility was found, but failed to execute. + * 127 - If any other error occurred. + */ +#define EXIT_NOEXEC 126 +#define EXIT_NOTFOUND 127 +#define EXIT_MISC 127 + +int +main(int argc, char *argv[]) +{ + int exit_status; + + while (getopt(argc, argv, "") != -1) + usage(); + argc -= optind; + argv += optind; + if (argc < 1) + usage(); + + if (isatty(STDOUT_FILENO)) + dofile(); + if (isatty(STDERR_FILENO) && dup2(STDOUT_FILENO, STDERR_FILENO) == -1) + /* may have just closed stderr */ + err(EXIT_MISC, "%s", argv[0]); + + (void)signal(SIGHUP, SIG_IGN); + +#if defined(__APPLE__) && !(TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR) + if (_vprocmgr_detach_from_console(0) != NULL) + err(EXIT_MISC, "can't detach from console"); +#endif + execvp(*argv, argv); + exit_status = (errno == ENOENT) ? EXIT_NOTFOUND : EXIT_NOEXEC; + err(exit_status, "%s", argv[0]); +} + +static void +dofile(void) +{ + int fd; + char path[MAXPATHLEN]; + const char *p; + + /* + * POSIX mandates if the standard output is a terminal, the standard + * output is appended to nohup.out in the working directory. Failing + * that, it will be appended to nohup.out in the directory obtained + * from the HOME environment variable. If file creation is required, + * the mode_t is set to S_IRUSR | S_IWUSR. + */ + p = FILENAME; + fd = open(p, O_RDWR | O_CREAT | O_APPEND, S_IRUSR | S_IWUSR); + if (fd != -1) + goto dupit; + if ((p = getenv("HOME")) != NULL && *p != '\0' && + (size_t)snprintf(path, sizeof(path), "%s/%s", p, FILENAME) < + sizeof(path)) { + fd = open(p = path, O_RDWR | O_CREAT | O_APPEND, + S_IRUSR | S_IWUSR); + if (fd != -1) + goto dupit; + } + errx(EXIT_MISC, "can't open a nohup.out file"); + +dupit: +#ifdef __APPLE__ + (void)lseek(fd, 0L, SEEK_END); +#endif + if (dup2(fd, STDOUT_FILENO) == -1) + err(EXIT_MISC, NULL); + (void)fprintf(stderr, "appending output to %s\n", p); +} + +static void +usage(void) +{ + (void)fprintf(stderr, "usage: nohup [--] utility [arguments]\n"); + exit(EXIT_MISC); +} diff --git a/shell_cmds/path_helper/path_helper.8 b/shell_cmds/path_helper/path_helper.8 new file mode 100644 index 0000000..c86f2d7 --- /dev/null +++ b/shell_cmds/path_helper/path_helper.8 @@ -0,0 +1,76 @@ +.\" +.\" Copyright (c) 2007 Apple Inc. All rights reserved. +.\" +.\" @APPLE_LICENSE_HEADER_START@ +.\" +.\" This file contains Original Code and/or Modifications of Original Code +.\" as defined in and that are subject to the Apple Public Source License +.\" Version 2.0 (the 'License'). You may not use this file except in +.\" compliance with the License. Please obtain a copy of the License at +.\" http://www.opensource.apple.com/apsl/ and read it before using this +.\" file. +.\" +.\" The Original Code and all software distributed under the License are +.\" distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER +.\" EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, +.\" INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, +.\" FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. +.\" Please see the License for the specific language governing rights and +.\" limitations under the License. +.\" +.\" @APPLE_LICENSE_HEADER_END@ +.\" +.Dd March 15, 2007 +.Dt path_helper 8 +.Os "Mac OS X" +.Sh NAME +.Nm path_helper +.Nd helper for constructing PATH environment variable +.Sh SYNOPSIS +.Nm +.Op Fl c | Fl s +.Sh DESCRIPTION +The +.Nm +utility reads the contents of the files in the directories +.Pa /etc/paths.d +and +.Pa /etc/manpaths.d +and appends their contents to the +.Ev PATH +and +.Ev MANPATH +environment variables respectively. +(The +.Ev MANPATH +environment variable will not be modified unless it is already set +in the environment.) +.Pp +Files in these directories should contain one path element per line. +.Pp +Prior to reading these directories, default +.Ev PATH +and +.Ev MANPATH +values are obtained from the files +.Pa /etc/paths +and +.Pa /etc/manpaths +respectively. +.Pp +Options: +.Bl -tag -width Ds +.It Fl c +Generate C-shell commands on stdout. This is the default if +.Ev SHELL +ends with "csh". +.It Fl s +Generate Bourne shell commands on stdout. This is the default if +.Ev SHELL +does not end with "csh". +.El +.Sh NOTE +The +.Nm +utility should not be invoked directly. +It is intended only for use by the shell profile. diff --git a/shell_cmds/path_helper/path_helper.c b/shell_cmds/path_helper/path_helper.c new file mode 100644 index 0000000..2313c99 --- /dev/null +++ b/shell_cmds/path_helper/path_helper.c @@ -0,0 +1,201 @@ +/* + * Copyright (c) 2008 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#include <fts.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <limits.h> + +static void usage() { + fprintf(stderr, "usage: path_helper [-c | -s]"); + exit(1); +} + +// Append path segment if it does not exist. Reallocate +// the path buffer as necessary. + +int append_path_segment(char** path, const char* segment) { + if (*path == NULL || segment == NULL) return -1; + + size_t pathlen = strlen(*path); + size_t seglen = strlen(segment); + + if (seglen == 0) return 0; + + // Does the segment already exist in the path? + // (^|:)segment(:|$) + char* match = strstr(*path, segment); + while (match) { + if ((match == *path || match[-1] == ':') && + (match[seglen] == ':' || match[seglen] == 0)) { + return 0; + } + match = strstr(match+1, segment); + } + + // size = pathlen + ':' + segment + '\0' + size_t size = pathlen + seglen + 2; + *path = reallocf(*path, size); + if (*path == NULL) return -1; + + if (pathlen > 0) strlcat(*path, ":", size); + strlcat(*path, segment, size); + return 0; +} + +// Convert fgetln output into a sanitized segment +// escape quotes, dollars, etc. +char* read_segment(const char* line, size_t len) { + int escapes = 0; + size_t i, j; + + for (i = 0; i < len; ++i) { + char c = line[i]; + if (c == '\"' || c == '\'' || c == '$') { + ++escapes; + } + } + + size_t size = len + escapes + 1; + + char* segment = calloc(1, size); + if (segment == NULL) return NULL; + + for (i = 0, j = 0; i < len; ++i, ++j) { + char c = line[i]; + if (c == '\"' || c == '\'' || c == '$') { + segment[j++] = '\\'; + segment[j] = c; + } else if (c == '\n') { + segment[j] = 0; + break; + } else { + segment[j] = line[i]; + } + } + + return segment; +} + +// Construct a path variable, starting with the contents +// of the given environment variable, adding the contents +// of the default file and files in the path directory. + +char* construct_path(char* env_var, char* defaults_path, char* dir_path) { + FTS* fts; + FTSENT* ent; + + char* result = calloc(sizeof(char), 1); + + char* dirpathv[] = { defaults_path, dir_path, NULL }; + fts = fts_open(dirpathv, FTS_PHYSICAL | FTS_XDEV, NULL); + if (!fts) { + perror(dir_path); + return NULL; + } + + while ((ent = fts_read(fts)) != NULL) { + // only interested in regular files, one level deep + if (ent->fts_info != FTS_F) { + if (ent->fts_level >= 1) fts_set(fts, ent, FTS_SKIP); + continue; + } + + FILE* f = fopen(ent->fts_accpath, "r"); + if (f == NULL) { + perror(ent->fts_accpath); + continue; + } + + for (;;) { + size_t len; + char* line = fgetln(f, &len); + if (line == NULL) break; + char* segment = read_segment(line, len); + + append_path_segment(&result, segment); + } + + fclose(f); + } + fts_close(fts); + + // merge in any existing custom PATH elemenets + char* str = getenv(env_var); + if (str) str = strdup(str); + while (str) { + char* sep = strchr(str, ':'); + if (sep) *sep = 0; + + append_path_segment(&result, str); + if (sep) { + str = sep + 1; + } else { + str = NULL; + } + } + + return result; +} + +enum { + STYLE_CSH, + STYLE_SH +}; + +int main(int argc, char* argv[]) { + int style = STYLE_SH; + + if (argc > 2) usage(); + + // default to csh style, if $SHELL ends with "csh". + char* shell = getenv("SHELL"); + if (shell) { + char* str = strstr(shell, "csh"); + if (str) style = STYLE_CSH; + } + + if (argc == 2 && strcmp(argv[1], "-c") == 0) style = STYLE_CSH; + if (argc == 2 && strcmp(argv[1], "-s") == 0) style = STYLE_SH; + + char* path = construct_path("PATH", "/etc/paths", "/etc/paths.d"); + char* manpath = NULL; + + // only adjust manpath if already set + int do_manpath = (getenv("MANPATH") != NULL); + if (do_manpath) { + manpath = construct_path("MANPATH", "/etc/manpaths", "/etc/manpaths.d"); + } + + if (style == STYLE_CSH) { + printf("setenv PATH \"%s\";\n", path); + if (do_manpath) printf("setenv MANPATH \"%s\";\n", manpath); + } else { + printf("PATH=\"%s\"; export PATH;\n", path); + if (do_manpath) printf("MANPATH=\"%s\"; export MANPATH;\n", manpath); + } + + return 0; +} diff --git a/shell_cmds/printenv/printenv.1 b/shell_cmds/printenv/printenv.1 new file mode 100644 index 0000000..4444375 --- /dev/null +++ b/shell_cmds/printenv/printenv.1 @@ -0,0 +1,84 @@ +.\" Copyright (c) 1980, 1990, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" the Institute of Electrical and Electronics Engineers, Inc. +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)printenv.1 8.1 (Berkeley) 6/6/93 +.\" $FreeBSD$ +.\" +.Dd May 12, 2003 +.Dt PRINTENV 1 +.Os +.Sh NAME +.Nm printenv +.Nd print out the environment +.Sh SYNOPSIS +.Nm +.Op Ar name +.Sh DESCRIPTION +The +.Nm +utility prints out the names and values of the variables in the environment, +with one name/value pair per line. +If +.Ar name +is specified, only +its value is printed. +.Pp +Some shells may provide a builtin +.Nm +command which is similar or identical to this utility. +Consult the +.Xr builtin 1 +manual page. +.Sh EXIT STATUS +.Ex -std +.Sh SEE ALSO +.Xr csh 1 , +.Xr env 1 , +.Xr sh 1 , +.Xr environ 7 +.Sh STANDARDS +The +.Nm +utility is provided for compatibility with earlier +.Bx +and +.Fx +releases and is not specified by any standards. +The functionality of +.Nm +can be duplicated with the +.Xr echo 1 +and +.Xr env 1 +utilities. +.Sh HISTORY +The +.Nm +command appeared in +.Bx 3.0 . diff --git a/shell_cmds/printenv/printenv.c b/shell_cmds/printenv/printenv.c new file mode 100644 index 0000000..315833e --- /dev/null +++ b/shell_cmds/printenv/printenv.c @@ -0,0 +1,99 @@ +/*- + * Copyright (c) 1987, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static const char copyright[] = +"@(#) Copyright (c) 1987, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#if 0 +#ifndef lint +static char sccsid[] = "@(#)printenv.c 8.2 (Berkeley) 5/4/95"; +#endif /* not lint */ +#endif + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/types.h> + +#include <stdlib.h> +#include <stdio.h> +#include <string.h> +#include <unistd.h> + +void usage(void); +extern char **environ; + +/* + * printenv + * + * Bill Joy, UCB + * February, 1979 + */ +int +main(int argc, char *argv[]) +{ + char *cp, **ep; + size_t len; + int ch; + + while ((ch = getopt(argc, argv, "")) != -1) + switch(ch) { + case '?': + default: + usage(); + } + argc -= optind; + argv += optind; + + if (argc == 0) { + for (ep = environ; *ep; ep++) + (void)printf("%s\n", *ep); + exit(0); + } + len = strlen(*argv); + for (ep = environ; *ep; ep++) + if (!memcmp(*ep, *argv, len)) { + cp = *ep + len; + if (!*cp || *cp == '=') { + (void)printf("%s\n", *cp ? cp + 1 : cp); + exit(0); + } + } + exit(1); +} + +void +usage(void) +{ + (void)fprintf(stderr, "usage: printenv [name]\n"); + exit(1); +} diff --git a/shell_cmds/printf/printf.1 b/shell_cmds/printf/printf.1 new file mode 100644 index 0000000..226b87b --- /dev/null +++ b/shell_cmds/printf/printf.1 @@ -0,0 +1,418 @@ +.\" Copyright (c) 1989, 1990, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" the Institute of Electrical and Electronics Engineers, Inc. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)printf.1 8.1 (Berkeley) 6/6/93 +.\" $FreeBSD$ +.\" +.Dd July 1, 2020 +.Dt PRINTF 1 +.Os +.Sh NAME +.Nm printf +.Nd formatted output +.Sh SYNOPSIS +.Nm +.Ar format Op Ar arguments ... +.Sh DESCRIPTION +The +.Nm +utility formats and prints its arguments, after the first, under control +of the +.Ar format . +The +.Ar format +is a character string which contains three types of objects: plain characters, +which are simply copied to standard output, character escape sequences which +are converted and copied to the standard output, and format specifications, +each of which causes printing of the next successive +.Ar argument . +.Pp +The +.Ar arguments +after the first are treated as strings if the corresponding format is +either +.Cm c , b +or +.Cm s ; +otherwise it is evaluated as a C constant, with the following extensions: +.Pp +.Bl -bullet -offset indent -compact +.It +A leading plus or minus sign is allowed. +.It +If the leading character is a single or double quote, the value is the +character code of the next character. +.El +.Pp +The format string is reused as often as necessary to satisfy the +.Ar arguments . +Any extra format specifications are evaluated with zero or the null +string. +.Pp +Character escape sequences are in backslash notation as defined in the +.St -ansiC , +with extensions. +The characters and their meanings +are as follows: +.Pp +.Bl -tag -width Ds -offset indent -compact +.It Cm \ea +Write a <bell> character. +.It Cm \eb +Write a <backspace> character. +.It Cm \ef +Write a <form-feed> character. +.It Cm \en +Write a <new-line> character. +.It Cm \er +Write a <carriage return> character. +.It Cm \et +Write a <tab> character. +.It Cm \ev +Write a <vertical tab> character. +.It Cm \e\' +Write a <single quote> character. +.It Cm \e\e +Write a backslash character. +.It Cm \e Ns Ar num +Write a byte whose +value is the 1-, 2-, or 3-digit +octal number +.Ar num . +Multibyte characters can be constructed using multiple +.Cm \e Ns Ar num +sequences. +.El +.Pp +Each format specification is introduced by the percent character +(``%''). +The remainder of the format specification includes, +in the following order: +.Bl -tag -width Ds +.It "Zero or more of the following flags:" +.Bl -tag -width Ds +.It Cm # +A `#' character +specifying that the value should be printed in an ``alternate form''. +For +.Cm b , c , d , s +and +.Cm u +formats, this option has no effect. +For the +.Cm o +formats the precision of the number is increased to force the first +character of the output string to a zero. +For the +.Cm x +.Pq Cm X +format, a non-zero result has the string +.Li 0x +.Pq Li 0X +prepended to it. +For +.Cm a , A , e , E , f , F , g +and +.Cm G +formats, the result will always contain a decimal point, even if no +digits follow the point (normally, a decimal point only appears in the +results of those formats if a digit follows the decimal point). +For +.Cm g +and +.Cm G +formats, trailing zeros are not removed from the result as they +would otherwise be; +.It Cm \&\- +A minus sign `\-' which specifies +.Em left adjustment +of the output in the indicated field; +.It Cm \&+ +A `+' character specifying that there should always be +a sign placed before the number when using signed formats. +.It Sq \&\ \& +A space specifying that a blank should be left before a positive number +for a signed format. +A `+' overrides a space if both are used; +.It Cm \&0 +A zero `0' character indicating that zero-padding should be used +rather than blank-padding. +A `\-' overrides a `0' if both are used; +.El +.It "Field Width:" +An optional digit string specifying a +.Em field width ; +if the output string has fewer bytes than the field width it will +be blank-padded on the left (or right, if the left-adjustment indicator +has been given) to make up the field width (note that a leading zero +is a flag, but an embedded zero is part of a field width); +.It Precision: +An optional period, +.Sq Cm \&.\& , +followed by an optional digit string giving a +.Em precision +which specifies the number of digits to appear after the decimal point, +for +.Cm e +and +.Cm f +formats, or the maximum number of bytes to be printed +from a string; if the digit string is missing, the precision is treated +as zero; +.It Format: +A character which indicates the type of format to use (one of +.Cm diouxXfFeEgGaAcsb ) . +The uppercase formats differ from their lowercase counterparts only in +that the output of the former is entirely in uppercase. +The floating-point format specifiers +.Pq Cm fFeEgGaA +may be prefixed by an +.Cm L +to request that additional precision be used, if available. +.El +.Pp +A field width or precision may be +.Sq Cm \&* +instead of a digit string. +In this case an +.Ar argument +supplies the field width or precision. +.Pp +The format characters and their meanings are: +.Bl -tag -width Fl +.It Cm diouXx +The +.Ar argument +is printed as a signed decimal (d or i), unsigned octal, unsigned decimal, +or unsigned hexadecimal (X or x), respectively. +.It Cm fF +The +.Ar argument +is printed in the style `[\-]ddd.ddd' where the number of d's +after the decimal point is equal to the precision specification for +the argument. +If the precision is missing, 6 digits are given; if the precision +is explicitly 0, no digits and no decimal point are printed. +The values \*[If] and \*[Na] are printed as +.Ql inf +and +.Ql nan , +respectively. +.It Cm eE +The +.Ar argument +is printed in the style +.Cm e +.Sm off +.Sq Op - Ar d.ddd No \(+- Ar dd +.Sm on +where there +is one digit before the decimal point and the number after is equal to +the precision specification for the argument; when the precision is +missing, 6 digits are produced. +The values \*[If] and \*[Na] are printed as +.Ql inf +and +.Ql nan , +respectively. +.It Cm gG +The +.Ar argument +is printed in style +.Cm f +.Pq Cm F +or in style +.Cm e +.Pq Cm E +whichever gives full precision in minimum space. +.It Cm aA +The +.Ar argument +is printed in style +.Sm off +.Sq Op - Ar h.hhh No \(+- Li p Ar d +.Sm on +where there is one digit before the hexadecimal point and the number +after is equal to the precision specification for the argument; +when the precision is missing, enough digits are produced to convey +the argument's exact double-precision floating-point representation. +The values \*[If] and \*[Na] are printed as +.Ql inf +and +.Ql nan , +respectively. +.It Cm c +The first byte of +.Ar argument +is printed. +.It Cm s +Bytes from the string +.Ar argument +are printed until the end is reached or until the number of bytes +indicated by the precision specification is reached; however if the +precision is 0 or missing, the string is printed entirely. +.It Cm b +As for +.Cm s , +but interpret character escapes in backslash notation in the string +.Ar argument . +The permitted escape sequences are slightly different in that +octal escapes are +.Cm \e0 Ns Ar num +instead of +.Cm \e Ns Ar num +and that an additional escape sequence +.Cm \ec +stops further output from this +.Nm +invocation. +.It Cm n$ +Allows reordering of the output according to +.Ar argument . +.It Cm \&% +Print a `%'; no argument is used. +.El +.Pp +The decimal point +character is defined in the program's locale (category +.Dv LC_NUMERIC ) . +.Pp +In no case does a non-existent or small field width cause truncation of +a field; padding takes place only if the specified field width exceeds +the actual width. +.Pp +Some shells may provide a builtin +.Nm +command which is similar or identical to this utility. +Consult the +.Xr builtin 1 +manual page. +.Sh EXIT STATUS +.Ex -std +.Sh EXAMPLES +Print the string +.Qq hello : +.Bd -literal -offset indent +$ printf "%s\en" hello +hello +.Ed +.Pp +Same as above, but notice that the format string is not quoted and hence we +do not get the expected behavior: +.Bd -literal -offset indent +$ printf %s\en hello +hellon$ +.Ed +.Pp +Print arguments forcing sign only for the first argument: +.Bd -literal -offset indent +$ printf "%+d\en%d\en%d\en" 1 -2 13 ++1 +-2 +13 +.Ed +.Pp +Same as above, but the single format string will be applied to the three +arguments: +.Bd -literal -offset indent +$ printf "%+d\en" 1 -2 13 ++1 +-2 ++13 +.Ed +.Pp +Print number using only two digits after the decimal point: +.Bd -literal -offset indent +$ printf "%.2f\en" 31.7456 +31.75 +.Ed +.Sh COMPATIBILITY +The traditional +.Bx +behavior of converting arguments of numeric formats not beginning +with a digit to the ASCII +code of the first character is not supported. +.Sh SEE ALSO +.Xr builtin 1 , +.Xr echo 1 , +.Xr sh 1 , +.Xr printf 3 +.Sh STANDARDS +The +.Nm +command is expected to be compatible with the +.St -p1003.2 +specification. +.Sh HISTORY +The +.Nm +command appeared in +.Bx 4.3 Reno . +It is modeled +after the standard library function, +.Xr printf 3 . +.Sh CAVEATS +ANSI hexadecimal character constants were deliberately not provided. +.Pp +Trying to print a dash ("-") as the first character causes +.Nm +to interpret the dash as a program argument. +.Nm -- +must be used before +.Ar format . +.Pp +If the locale contains multibyte characters +(such as UTF-8), +the +.Cm c +format and +.Cm b +and +.Cm s +formats with a precision +may not operate as expected. +.Sh BUGS +Since the floating point numbers are translated from ASCII +to floating-point and then back again, floating-point precision may be lost. +(By default, the number is translated to an IEEE-754 double-precision +value before being printed. +The +.Cm L +modifier may produce additional precision, depending on the hardware platform.) +.Pp +The escape sequence \e000 is the string terminator. +When present in the argument for the +.Cm b +format, the argument will be truncated at the \e000 character. +.Pp +Multibyte characters are not recognized in format strings (this is only +a problem if +.Ql % +can appear inside a multibyte character). diff --git a/shell_cmds/printf/printf.c b/shell_cmds/printf/printf.c new file mode 100644 index 0000000..6cc09ae --- /dev/null +++ b/shell_cmds/printf/printf.c @@ -0,0 +1,697 @@ +/*- + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright 2018 Staysail Systems, Inc. <info@staysail.tech> + * Copyright 2014 Garrett D'Amore <garrett@damore.org> + * Copyright 2010 Nexenta Systems, Inc. All rights reserved. + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +/* + * Important: This file is used both as a standalone program /usr/bin/printf + * and as a builtin for /bin/sh (#define SHELL). + */ + +#ifndef SHELL +#ifndef lint +static char const copyright[] = +"@(#) Copyright (c) 1989, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ +#endif + +#ifndef lint +#if 0 +static char const sccsid[] = "@(#)printf.c 8.1 (Berkeley) 7/20/93"; +#endif +static const char rcsid[] = + "$FreeBSD$"; +#endif /* not lint */ + +#include <sys/types.h> + +#include <ctype.h> +#include <err.h> +#include <errno.h> +#include <inttypes.h> +#include <limits.h> +#include <locale.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <wchar.h> + +#ifdef SHELL +#define main printfcmd +#include "bltin/bltin.h" +#include "options.h" +#endif + +#define PF(f, func) do { \ + if (havewidth) \ + if (haveprec) \ + (void)printf(f, fieldwidth, precision, func); \ + else \ + (void)printf(f, fieldwidth, func); \ + else if (haveprec) \ + (void)printf(f, precision, func); \ + else \ + (void)printf(f, func); \ +} while (0) + +static int asciicode(void); +static char *printf_doformat(char *, int *); +static int escape(char *, int, size_t *); +static int getchr(void); +static int getfloating(long double *, int); +static int getint(int *); +static int getnum(intmax_t *, uintmax_t *, int); +static const char + *getstr(void); +static char *mknum(char *, char); +static void usage(void); + +static const char digits[] = "0123456789"; + +static char end_fmt[1]; + +static int myargc; +static char **myargv; +static char **gargv; +static char **maxargv; + +int +main(int argc, char *argv[]) +{ + size_t len; + int end, rval; + char *format, *fmt, *start; +#ifndef SHELL + int ch; + + (void) setlocale(LC_ALL, ""); +#endif + +#ifdef SHELL + nextopt(""); + argc -= argptr - argv; + argv = argptr; +#else + while ((ch = getopt(argc, argv, "")) != -1) + switch (ch) { + case '?': + default: + usage(); + return (1); + } + argc -= optind; + argv += optind; +#endif + + if (argc < 1) { + usage(); + return (1); + } + +#ifdef SHELL + INTOFF; +#endif + /* + * Basic algorithm is to scan the format string for conversion + * specifications -- once one is found, find out if the field + * width or precision is a '*'; if it is, gather up value. Note, + * format strings are reused as necessary to use up the provided + * arguments, arguments of zero/null string are provided to use + * up the format string. + */ + fmt = format = *argv; + escape(fmt, 1, &len); /* backslash interpretation */ + rval = end = 0; + gargv = ++argv; + + for (;;) { + maxargv = gargv; + + myargv = gargv; + for (myargc = 0; gargv[myargc]; myargc++) + /* nop */; + start = fmt; + while (fmt < format + len) { + if (fmt[0] == '%') { + fwrite(start, 1, fmt - start, stdout); + if (fmt[1] == '%') { + /* %% prints a % */ + putchar('%'); + fmt += 2; + } else { + fmt = printf_doformat(fmt, &rval); + if (fmt == NULL || fmt == end_fmt) { +#ifdef SHELL + INTON; +#endif + return (fmt == NULL ? 1 : rval); + } + end = 0; + } + start = fmt; + } else + fmt++; + if (gargv > maxargv) + maxargv = gargv; + } + gargv = maxargv; + + if (end == 1) { + warnx("missing format character"); +#ifdef SHELL + INTON; +#endif + return (1); + } + fwrite(start, 1, fmt - start, stdout); + if (!*gargv) { +#ifdef SHELL + INTON; +#endif + return (rval); + } + /* Restart at the beginning of the format string. */ + fmt = format; + end = 1; + } + /* NOTREACHED */ +} + + +static char * +printf_doformat(char *fmt, int *rval) +{ + static const char skip1[] = "#'-+ 0"; + int fieldwidth, haveprec, havewidth, mod_ldbl, precision; + char convch, nextch; + char start[strlen(fmt) + 1]; + char **fargv; + char *dptr; + int l; + + dptr = start; + *dptr++ = '%'; + *dptr = 0; + + fmt++; + + /* look for "n$" field index specifier */ + l = strspn(fmt, digits); + if ((l > 0) && (fmt[l] == '$')) { + int idx = atoi(fmt); + if (idx <= myargc) { + gargv = &myargv[idx - 1]; + } else { + gargv = &myargv[myargc]; + } + if (gargv > maxargv) + maxargv = gargv; + fmt += l + 1; + + /* save format argument */ + fargv = gargv; + } else { + fargv = NULL; + } + + /* skip to field width */ + while (*fmt && strchr(skip1, *fmt) != NULL) { + *dptr++ = *fmt++; + *dptr = 0; + } + + if (*fmt == '*') { + + fmt++; + l = strspn(fmt, digits); + if ((l > 0) && (fmt[l] == '$')) { + int idx = atoi(fmt); + if (fargv == NULL) { + warnx("incomplete use of n$"); + return (NULL); + } + if (idx <= myargc) { + gargv = &myargv[idx - 1]; + } else { + gargv = &myargv[myargc]; + } + fmt += l + 1; + } else if (fargv != NULL) { + warnx("incomplete use of n$"); + return (NULL); + } + + if (getint(&fieldwidth)) + return (NULL); + if (gargv > maxargv) + maxargv = gargv; + havewidth = 1; + + *dptr++ = '*'; + *dptr = 0; + } else { + havewidth = 0; + + /* skip to possible '.', get following precision */ + while (isdigit(*fmt)) { + *dptr++ = *fmt++; + *dptr = 0; + } + } + + if (*fmt == '.') { + /* precision present? */ + fmt++; + *dptr++ = '.'; + + if (*fmt == '*') { + + fmt++; + l = strspn(fmt, digits); + if ((l > 0) && (fmt[l] == '$')) { + int idx = atoi(fmt); + if (fargv == NULL) { + warnx("incomplete use of n$"); + return (NULL); + } + if (idx <= myargc) { + gargv = &myargv[idx - 1]; + } else { + gargv = &myargv[myargc]; + } + fmt += l + 1; + } else if (fargv != NULL) { + warnx("incomplete use of n$"); + return (NULL); + } + + if (getint(&precision)) + return (NULL); + if (gargv > maxargv) + maxargv = gargv; + haveprec = 1; + *dptr++ = '*'; + *dptr = 0; + } else { + haveprec = 0; + + /* skip to conversion char */ + while (isdigit(*fmt)) { + *dptr++ = *fmt++; + *dptr = 0; + } + } + } else + haveprec = 0; + if (!*fmt) { + warnx("missing format character"); + return (NULL); + } + *dptr++ = *fmt; + *dptr = 0; + + /* + * Look for a length modifier. POSIX doesn't have these, so + * we only support them for floating-point conversions, which + * are extensions. This is useful because the L modifier can + * be used to gain extra range and precision, while omitting + * it is more likely to produce consistent results on different + * architectures. This is not so important for integers + * because overflow is the only bad thing that can happen to + * them, but consider the command printf %a 1.1 + */ + if (*fmt == 'L') { + mod_ldbl = 1; + fmt++; + if (!strchr("aAeEfFgG", *fmt)) { + warnx("bad modifier L for %%%c", *fmt); + return (NULL); + } + } else { + mod_ldbl = 0; + } + + /* save the current arg offset, and set to the format arg */ + if (fargv != NULL) { + gargv = fargv; + } + + convch = *fmt; + nextch = *++fmt; + + *fmt = '\0'; + switch (convch) { + case 'b': { + size_t len; + char *p; + int getout; + + /* Convert "b" to "s" for output. */ + start[strlen(start) - 1] = 's'; + if ((p = strdup(getstr())) == NULL) { + warnx("%s", strerror(ENOMEM)); + return (NULL); + } + getout = escape(p, 0, &len); + /* + * Special-case conversion of zero; print it instead of + * feeding an empty string to printf("%s") which would not + * print anything. + */ + if (len == 1 && p[0] == '\0') { + putchar(p[0]); + } else { + PF(start, p); + } + /* Restore format for next loop. */ + + free(p); + if (getout) + return (end_fmt); + break; + } + case 'c': { + char p; + + p = getchr(); + if (p != '\0') + PF(start, p); + break; + } + case 's': { + const char *p; + + p = getstr(); + PF(start, p); + break; + } + case 'd': case 'i': case 'o': case 'u': case 'x': case 'X': { + char *f; + intmax_t val; + uintmax_t uval; + int signedconv; + + signedconv = (convch == 'd' || convch == 'i'); + if ((f = mknum(start, convch)) == NULL) + return (NULL); + if (getnum(&val, &uval, signedconv)) + *rval = 1; + if (signedconv) + PF(f, val); + else + PF(f, uval); + break; + } + case 'e': case 'E': + case 'f': case 'F': + case 'g': case 'G': + case 'a': case 'A': { + long double p; + + if (getfloating(&p, mod_ldbl)) + *rval = 1; + if (mod_ldbl) + PF(start, p); + else + PF(start, (double)p); + break; + } + default: + warnx("illegal format character %c", convch); + return (NULL); + } + *fmt = nextch; + /* return the gargv to the next element */ + return (fmt); +} + +static char * +mknum(char *str, char ch) +{ + static char *copy; + static size_t copy_size; + char *newcopy; + size_t len, newlen; + + len = strlen(str) + 2; + if (len > copy_size) { + newlen = ((len + 1023) >> 10) << 10; + if ((newcopy = realloc(copy, newlen)) == NULL) { + warnx("%s", strerror(ENOMEM)); + return (NULL); + } + copy = newcopy; + copy_size = newlen; + } + + memmove(copy, str, len - 3); + copy[len - 3] = 'j'; + copy[len - 2] = ch; + copy[len - 1] = '\0'; + return (copy); +} + +static int +escape(char *fmt, int percent, size_t *len) +{ + char *save, *store, c; + int value; + + for (save = store = fmt; ((c = *fmt) != 0); ++fmt, ++store) { + if (c != '\\') { + *store = c; + continue; + } + switch (*++fmt) { + case '\0': /* EOS, user error */ + *store = '\\'; + *++store = '\0'; + *len = store - save; + return (0); + case '\\': /* backslash */ + case '\'': /* single quote */ + *store = *fmt; + break; + case 'a': /* bell/alert */ + *store = '\a'; + break; + case 'b': /* backspace */ + *store = '\b'; + break; + case 'c': + if (!percent) { + *store = '\0'; + *len = store - save; + return (1); + } + *store = 'c'; + break; + case 'f': /* form-feed */ + *store = '\f'; + break; + case 'n': /* newline */ + *store = '\n'; + break; + case 'r': /* carriage-return */ + *store = '\r'; + break; + case 't': /* horizontal tab */ + *store = '\t'; + break; + case 'v': /* vertical tab */ + *store = '\v'; + break; + /* octal constant */ + case '0': case '1': case '2': case '3': + case '4': case '5': case '6': case '7': + c = (!percent && *fmt == '0') ? 4 : 3; + for (value = 0; + c-- && *fmt >= '0' && *fmt <= '7'; ++fmt) { + value <<= 3; + value += *fmt - '0'; + } + --fmt; + if (percent && value == '%') { + *store++ = '%'; + *store = '%'; + } else + *store = (char)value; + break; + default: + *store = *fmt; + break; + } + } + *store = '\0'; + *len = store - save; + return (0); +} + +static int +getchr(void) +{ + if (!*gargv) + return ('\0'); + return ((int)**gargv++); +} + +static const char * +getstr(void) +{ + if (!*gargv) + return (""); + return (*gargv++); +} + +static int +getint(int *ip) +{ + intmax_t val; + uintmax_t uval; + int rval; + + if (getnum(&val, &uval, 1)) + return (1); + rval = 0; + if (val < INT_MIN || val > INT_MAX) { + warnx("%s: %s", *gargv, strerror(ERANGE)); + rval = 1; + } + *ip = (int)val; + return (rval); +} + +static int +getnum(intmax_t *ip, uintmax_t *uip, int signedconv) +{ + char *ep; + int rval; + + if (!*gargv) { + *ip = *uip = 0; + return (0); + } + if (**gargv == '"' || **gargv == '\'') { + if (signedconv) + *ip = asciicode(); + else + *uip = asciicode(); + return (0); + } + rval = 0; + errno = 0; + if (signedconv) + *ip = strtoimax(*gargv, &ep, 0); + else + *uip = strtoumax(*gargv, &ep, 0); + if (ep == *gargv) { + warnx("%s: expected numeric value", *gargv); + rval = 1; + } + else if (*ep != '\0') { + warnx("%s: not completely converted", *gargv); + rval = 1; + } + if (errno == ERANGE) { + warnx("%s: %s", *gargv, strerror(ERANGE)); + rval = 1; + } + ++gargv; + return (rval); +} + +static int +getfloating(long double *dp, int mod_ldbl) +{ + char *ep; + int rval; + + if (!*gargv) { + *dp = 0.0; + return (0); + } + if (**gargv == '"' || **gargv == '\'') { + *dp = asciicode(); + return (0); + } + rval = 0; + errno = 0; + if (mod_ldbl) + *dp = strtold(*gargv, &ep); + else + *dp = strtod(*gargv, &ep); + if (ep == *gargv) { + warnx("%s: expected numeric value", *gargv); + rval = 1; + } else if (*ep != '\0') { + warnx("%s: not completely converted", *gargv); + rval = 1; + } + if (errno == ERANGE) { + warnx("%s: %s", *gargv, strerror(ERANGE)); + rval = 1; + } + ++gargv; + return (rval); +} + +static int +asciicode(void) +{ + int ch; + wchar_t wch; + mbstate_t mbs; + + ch = (unsigned char)**gargv; + if (ch == '\'' || ch == '"') { + memset(&mbs, 0, sizeof(mbs)); + switch (mbrtowc(&wch, *gargv + 1, MB_LEN_MAX, &mbs)) { + case (size_t)-2: + case (size_t)-1: + wch = (unsigned char)gargv[0][1]; + break; + case 0: + wch = 0; + break; + } + ch = wch; + } + ++gargv; + return (ch); +} + +static void +usage(void) +{ + (void)fprintf(stderr, "usage: printf format [arguments ...]\n"); +} diff --git a/shell_cmds/printf/printf.plist.part b/shell_cmds/printf/printf.plist.part new file mode 100644 index 0000000..a040cf7 --- /dev/null +++ b/shell_cmds/printf/printf.plist.part @@ -0,0 +1,18 @@ + <dict> + <key>OpenSourceProject</key> + <string>printf</string> + <key>OpenSourceVersion</key> + <string>2020-07-01</string> + <key>OpenSourceWebsiteURL</key> + <string>https://github.com/freebsd/freebsd/tree/master/usr.bin/printf</string> + <key>OpenSourceSCM</key> + <string>https://github.com/freebsd/freebsd/tree/master/usr.bin/printf</string> + <key>OpenSourceImportDate</key> + <string>2020-07-23</string> + <key>OpenSourceModifications</key> + <array> + <string>65959587: UNIX Conformance | /usr/bin/printf, multiple format assertion failures in VSC</string> + </array> + <key>OpenSourceLicense</key> + <string>bsd</string> + </dict> diff --git a/shell_cmds/printf/tests/Makefile b/shell_cmds/printf/tests/Makefile new file mode 100644 index 0000000..b4ea79b --- /dev/null +++ b/shell_cmds/printf/tests/Makefile @@ -0,0 +1,23 @@ +# $FreeBSD$ + +PACKAGE= tests + +TAP_TESTS_SH= legacy_test + +${PACKAGE}FILES+= regress.b.out +${PACKAGE}FILES+= regress.bwidth.out +${PACKAGE}FILES+= regress.d.out +${PACKAGE}FILES+= regress.f.out +${PACKAGE}FILES+= regress.l1.out +${PACKAGE}FILES+= regress.l2.out +${PACKAGE}FILES+= regress.m1.out +${PACKAGE}FILES+= regress.m2.out +${PACKAGE}FILES+= regress.m3.out +${PACKAGE}FILES+= regress.m4.out +${PACKAGE}FILES+= regress.m5.out +${PACKAGE}FILES+= regress.missingpos1.out +${PACKAGE}FILES+= regress.s.out +${PACKAGE}FILES+= regress.sh +${PACKAGE}FILES+= regress.zero.out + +.include <bsd.test.mk> diff --git a/shell_cmds/printf/tests/Makefile.depend b/shell_cmds/printf/tests/Makefile.depend new file mode 100644 index 0000000..f80275d --- /dev/null +++ b/shell_cmds/printf/tests/Makefile.depend @@ -0,0 +1,11 @@ +# $FreeBSD$ +# Autogenerated - do NOT edit! + +DIRDEPS = \ + + +.include <dirdeps.mk> + +.if ${DEP_RELDIR} == ${_DEP_RELDIR} +# local dependencies - needed for -jN in clean tree +.endif diff --git a/shell_cmds/printf/tests/legacy_test.sh b/shell_cmds/printf/tests/legacy_test.sh new file mode 100644 index 0000000..a919deb --- /dev/null +++ b/shell_cmds/printf/tests/legacy_test.sh @@ -0,0 +1,6 @@ +#!/bin/sh +# $FreeBSD$ + +SRCDIR="$(dirname "${0}")"; export SRCDIR + +m4 /AppleInternal/Tests/shell_cmds/regress.m4 "${SRCDIR}/regress.sh" | sh diff --git a/shell_cmds/printf/tests/regress.b.out b/shell_cmds/printf/tests/regress.b.out new file mode 100644 index 0000000..0373d93 --- /dev/null +++ b/shell_cmds/printf/tests/regress.b.out @@ -0,0 +1 @@ +abcdef diff --git a/shell_cmds/printf/tests/regress.bwidth.out b/shell_cmds/printf/tests/regress.bwidth.out new file mode 100644 index 0000000..9e2ff61 --- /dev/null +++ b/shell_cmds/printf/tests/regress.bwidth.out @@ -0,0 +1 @@ + a diff --git a/shell_cmds/printf/tests/regress.d.out b/shell_cmds/printf/tests/regress.d.out new file mode 100644 index 0000000..da83c08 --- /dev/null +++ b/shell_cmds/printf/tests/regress.d.out @@ -0,0 +1 @@ +123, 123,00123,00123,00123 diff --git a/shell_cmds/printf/tests/regress.f.out b/shell_cmds/printf/tests/regress.f.out new file mode 100644 index 0000000..127cb8a --- /dev/null +++ b/shell_cmds/printf/tests/regress.f.out @@ -0,0 +1 @@ +42.250000,-42.250 ,inf,nan diff --git a/shell_cmds/printf/tests/regress.l1.out b/shell_cmds/printf/tests/regress.l1.out new file mode 100644 index 0000000..9be0dc9 --- /dev/null +++ b/shell_cmds/printf/tests/regress.l1.out @@ -0,0 +1 @@ +228 diff --git a/shell_cmds/printf/tests/regress.l2.out b/shell_cmds/printf/tests/regress.l2.out new file mode 100644 index 0000000..9be0dc9 --- /dev/null +++ b/shell_cmds/printf/tests/regress.l2.out @@ -0,0 +1 @@ +228 diff --git a/shell_cmds/printf/tests/regress.m1.out b/shell_cmds/printf/tests/regress.m1.out Binary files differnew file mode 100644 index 0000000..a9e063e --- /dev/null +++ b/shell_cmds/printf/tests/regress.m1.out diff --git a/shell_cmds/printf/tests/regress.m2.out b/shell_cmds/printf/tests/regress.m2.out new file mode 100644 index 0000000..cf14915 --- /dev/null +++ b/shell_cmds/printf/tests/regress.m2.out @@ -0,0 +1,2 @@ +abc +cdef
\ No newline at end of file diff --git a/shell_cmds/printf/tests/regress.m3.out b/shell_cmds/printf/tests/regress.m3.out new file mode 100644 index 0000000..bbe4e70 --- /dev/null +++ b/shell_cmds/printf/tests/regress.m3.out @@ -0,0 +1,4 @@ +%abc +%def +%ghi +%jkl diff --git a/shell_cmds/printf/tests/regress.m4.out b/shell_cmds/printf/tests/regress.m4.out new file mode 100644 index 0000000..5e0b5d4 --- /dev/null +++ b/shell_cmds/printf/tests/regress.m4.out @@ -0,0 +1 @@ +0,0.000000,, diff --git a/shell_cmds/printf/tests/regress.m5.out b/shell_cmds/printf/tests/regress.m5.out new file mode 100644 index 0000000..2838468 --- /dev/null +++ b/shell_cmds/printf/tests/regress.m5.out @@ -0,0 +1 @@ +-d diff --git a/shell_cmds/printf/tests/regress.missingpos1.out b/shell_cmds/printf/tests/regress.missingpos1.out new file mode 100644 index 0000000..3b04f03 --- /dev/null +++ b/shell_cmds/printf/tests/regress.missingpos1.out @@ -0,0 +1 @@ +printf: incomplete use of n$ diff --git a/shell_cmds/printf/tests/regress.s.out b/shell_cmds/printf/tests/regress.s.out new file mode 100644 index 0000000..3d572b9 --- /dev/null +++ b/shell_cmds/printf/tests/regress.s.out @@ -0,0 +1 @@ +abc,abc diff --git a/shell_cmds/printf/tests/regress.sh b/shell_cmds/printf/tests/regress.sh new file mode 100644 index 0000000..a2cf5fb --- /dev/null +++ b/shell_cmds/printf/tests/regress.sh @@ -0,0 +1,32 @@ +# $FreeBSD$ + +REGRESSION_START($1) + +echo '1..24' + +REGRESSION_TEST(`b', `printf "abc%b%b" "def\n" "\cghi"') +REGRESSION_TEST(`d', `printf "%d,%5d,%.5d,%0*d,%.*d\n" 123 123 123 5 123 5 123') +REGRESSION_TEST(`f', `printf "%f,%-8.3f,%f,%f\n" +42.25 -42.25 inf nan') +REGRESSION_TEST(`l1', `LC_ALL=en_US.ISO8859-1 printf "%d\n" $(printf \"\\344)') +REGRESSION_TEST(`l2', `LC_ALL=en_US.UTF-8 printf "%d\n" $(printf \"\\303\\244)') +REGRESSION_TEST(`m1', `printf "%c%%%d\0\045\n" abc \"abc') +REGRESSION_TEST(`m2', `printf "abc\n\cdef"') +REGRESSION_TEST(`m3', `printf "%%%s\n" abc def ghi jkl') +REGRESSION_TEST(`m4', `printf "%d,%f,%c,%s\n"') +REGRESSION_TEST(`m5', `printf -- "-d\n"') +REGRESSION_TEST(`s', `printf "%.3s,%-5s\n" abcd abc') +REGRESSION_TEST('zero', `printf "%u%u\n" 15') +REGRESSION_TEST('zero', `printf "%d%d\n" 15') +REGRESSION_TEST('zero', `printf "%d%u\n" 15') +REGRESSION_TEST('zero', `printf "%u%d\n" 15') +REGRESSION_TEST(`missingpos1', `printf "%1\$*s" 1 1 2>&1') +REGRESSION_TEST(`missingpos1', `printf "%*1\$s" 1 1 2>&1') +REGRESSION_TEST(`missingpos1', `printf "%1\$*.*s" 1 1 1 2>&1') +REGRESSION_TEST(`missingpos1', `printf "%*1\$.*s" 1 1 1 2>&1') +REGRESSION_TEST(`missingpos1', `printf "%*.*1\$s" 1 1 1 2>&1') +REGRESSION_TEST(`missingpos1', `printf "%1\$*2\$.*s" 1 1 1 2>&1') +REGRESSION_TEST(`missingpos1', `printf "%*1\$.*2\$s" 1 1 1 2>&1') +REGRESSION_TEST(`missingpos1', `printf "%1\$*.*2\$s" 1 1 1 2>&1') +REGRESSION_TEST(`bwidth', `printf "%8.2b" "a\nb\n"') + +REGRESSION_END() diff --git a/shell_cmds/printf/tests/regress.zero.out b/shell_cmds/printf/tests/regress.zero.out new file mode 100644 index 0000000..fa8f08c --- /dev/null +++ b/shell_cmds/printf/tests/regress.zero.out @@ -0,0 +1 @@ +150 diff --git a/shell_cmds/pwd/pwd.1 b/shell_cmds/pwd/pwd.1 new file mode 100644 index 0000000..929c4b3 --- /dev/null +++ b/shell_cmds/pwd/pwd.1 @@ -0,0 +1,103 @@ +.\"- +.\" Copyright (c) 1990, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" the Institute of Electrical and Electronics Engineers, Inc. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)pwd.1 8.2 (Berkeley) 4/28/95 +.\" $FreeBSD: src/bin/pwd/pwd.1,v 1.26 2005/01/16 16:41:58 ru Exp $ +.\" +.Dd April 12, 2003 +.Dt PWD 1 +.Os +.Sh NAME +.Nm pwd +.Nd return working directory name +.Sh SYNOPSIS +.Nm +.Op Fl L | P +.Sh DESCRIPTION +The +.Nm +utility writes the absolute pathname of the current working directory to +the standard output. +.Pp +Some shells may provide a builtin +.Nm +command which is similar or identical to this utility. +Consult the +.Xr builtin 1 +manual page. +.Pp +The options are as follows: +.Bl -tag -width indent +.It Fl L +Display the logical current working directory. +.It Fl P +Display the physical current working directory (all symbolic links resolved). +.El +.Pp +If no options are specified, the +.\" 4207130 +.Fl L +option is assumed. +.Sh ENVIRONMENT +Environment variables used by +.Nm : +.Bl -tag -width ".Ev PWD" +.It Ev PWD +Logical current working directory. +.El +.Sh EXIT STATUS +.Ex -std +.Sh SEE ALSO +.Xr builtin 1 , +.Xr cd 1 , +.Xr csh 1 , +.Xr sh 1 , +.Xr getcwd 3 +.Sh STANDARDS +The +.Nm +utility conforms to +.St -p1003.1-2001 . +.Sh BUGS +In +.Xr csh 1 +the command +.Ic dirs +is always faster because it is built into that shell. +However, it can give a different answer in the rare case +that the current directory or a containing directory was moved after +the shell descended into it. +.Pp +The +.Fl L +option does not work unless the +.Ev PWD +environment variable is exported by the shell. diff --git a/shell_cmds/pwd/pwd.c b/shell_cmds/pwd/pwd.c new file mode 100644 index 0000000..c5788b1 --- /dev/null +++ b/shell_cmds/pwd/pwd.c @@ -0,0 +1,127 @@ +/*- + * Copyright (c) 1991, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if 0 +#ifndef lint +static char const copyright[] = +"@(#) Copyright (c) 1991, 1993, 1994\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)pwd.c 8.3 (Berkeley) 4/1/94"; +#endif /* not lint */ +#endif +#include <sys/cdefs.h> +__FBSDID("$FreeBSD: src/bin/pwd/pwd.c,v 1.25 2005/02/09 17:37:38 ru Exp $"); + +#include <sys/param.h> +#include <sys/stat.h> +#include <sys/types.h> + +#include <err.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> + +static char *getcwd_logical(void); +void usage(void); + +int +main(int argc, char *argv[]) +{ + int physical; + int ch; + char *p; + + // 4207130 + physical = 0; + while ((ch = getopt(argc, argv, "LP")) != -1) + switch (ch) { + case 'L': + physical = 0; + break; + case 'P': + physical = 1; + break; + case '?': + default: + usage(); + } + argc -= optind; + argv += optind; + + if (argc != 0) + usage(); + + /* + * If we're trying to find the logical current directory and that + * fails, behave as if -P was specified. + * + * 4701537: Only fall back if $PWD was actually wrong. We want to + * fail if the path is too long (errno == ENAMETOOLONG). + */ + if ((!physical && (p = getcwd_logical()) != NULL) || + ((physical || errno == ENOENT) && (p = getcwd(NULL, 0)) != NULL)) + printf("%s\n", p); + else + err(1, "."); + + exit(0); +} + +void +usage(void) +{ + + (void)fprintf(stderr, "usage: pwd [-L | -P]\n"); + exit(1); +} + +static char * +getcwd_logical(void) +{ + struct stat lg, phy; + char *pwd; + + /* + * Check that $PWD is an absolute logical pathname referring to + * the current working directory. + */ + if ((pwd = getenv("PWD")) != NULL && *pwd == '/') { + if (stat(pwd, &lg) == -1 || stat(".", &phy) == -1) + return (NULL); + if (lg.st_dev == phy.st_dev && lg.st_ino == phy.st_ino) + return (pwd); + } + + errno = ENOENT; + return (NULL); +} diff --git a/shell_cmds/renice/renice.8 b/shell_cmds/renice/renice.8 new file mode 100644 index 0000000..5a03755 --- /dev/null +++ b/shell_cmds/renice/renice.8 @@ -0,0 +1,138 @@ +.\" Copyright (c) 1983, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)renice.8 8.1 (Berkeley) 6/9/93 +.\" $FreeBSD: src/usr.bin/renice/renice.8,v 1.16 2003/02/26 20:27:24 charnier Exp $ +.\" +.Dd June 9, 1993 +.Dt RENICE 8 +.Os +.Sh NAME +.Nm renice +.Nd alter priority of running processes +.Sh SYNOPSIS +.Nm +.Ar priority +.Op Oo Fl p Oc Ar pid ... +.Op Oo Fl g Oc Ar pgrp ... +.Op Oo Fl u Oc Ar user ... +.Nm +.Fl n Ar increment +.Op Oo Fl p Oc Ar pid ... +.Op Oo Fl g Oc Ar pgrp ... +.Op Oo Fl u Oc Ar user ... +.Sh DESCRIPTION +The +.Nm +utility alters the +scheduling priority of one or more running processes. +The following +.Ar who +parameters are interpreted as process ID's, process group +ID's, user ID's or user names. +The +.Nm Ns 'ing +of a process group causes all processes in the process group +to have their scheduling priority altered. +The +.Nm Ns 'ing +of a user causes all processes owned by the user to have +their scheduling priority altered. +By default, the processes to be affected are specified by +their process ID's. +.Pp +The following options are available: +.Bl -tag -width indent +.It Fl g +Force +.Ar who +parameters to be interpreted as process group ID's. +.It Fl n +Instead of changing the specified processes to the given priority, +interpret the following argument as an increment to be applied to +the current priority of each process. +.It Fl u +Force the +.Ar who +parameters to be interpreted as user names or user ID's. +.It Fl p +Reset the +.Ar who +interpretation to be (the default) process ID's. +.El +.Pp +For example, +.Pp +.Dl "renice +1 987 -u daemon root -p 32" +.Pp +would change the priority of process ID's 987 and 32, and +all processes owned by users daemon and root. +.Pp +Users other than the super-user may only alter the priority of +processes they own, +and can only monotonically increase their ``nice value'' +within the range 0 to +.Dv PRIO_MAX +(20). +(This prevents overriding administrative fiats.) +The super-user +may alter the priority of any process +and set the priority to any value in the range +.Dv PRIO_MIN +(\-20) +to +.Dv PRIO_MAX . +Useful priorities are: +20 (the affected processes will run at the lowest priority), +0 (the ``base'' scheduling priority), +anything negative (lower values cause more favorable scheduling). +.Sh FILES +.Bl -tag -width /etc/passwd -compact +.It Pa /etc/passwd +to map user names to user ID's +.El +.Sh SEE ALSO +.Xr nice 1 , +.Xr getpriority 2 , +.Xr setpriority 2 +.Sh STANDARDS +The +.Nm +utility conforms to +.St -p1003.1-2001 . +.Sh HISTORY +The +.Nm +utility appeared in +.Bx 4.0 . +.Sh BUGS +Non super-users cannot increase scheduling priorities of their own processes, +even if they were the ones that decreased the priorities in the first place. diff --git a/shell_cmds/renice/renice.c b/shell_cmds/renice/renice.c new file mode 100644 index 0000000..d7253e0 --- /dev/null +++ b/shell_cmds/renice/renice.c @@ -0,0 +1,203 @@ +/* + * Copyright (c) 1983, 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static const char copyright[] = +"@(#) Copyright (c) 1983, 1989, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#if 0 +#ifndef lint +static char sccsid[] = "@(#)renice.c 8.1 (Berkeley) 6/9/93"; +#endif /* not lint */ +#endif + +#include <sys/types.h> +#include <sys/time.h> +#include <sys/resource.h> + +#include <err.h> +#include <errno.h> +#include <limits.h> +#include <pwd.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +static int donice(int, int, int, int); +static int getnum(const char *, const char *, int *); +static void usage(void); + +/* + * Change the priority (nice) of processes + * or groups of processes which are already + * running. + */ +int +main(int argc, char *argv[]) +{ + struct passwd *pwd; + int errs, incr, prio, which, who; + int delim; + int priflag; + + errs = 0; + incr = 0; + which = PRIO_PROCESS; + who = 0; + argc--, argv++; + if (argc < 2) + usage(); + delim = 0; + priflag = 0; + + /* incrementing priflag here ensures we only process + the single priority arg if it is the very first arg */ + for (; argc > 0; argc--, argv++, priflag++) { + /* once we've seen -- , don't process anymore switches */ + if (0 == delim) { + /* -n must immediately be followed by the incremental + priority */ + if (strcmp(*argv, "-n") == 0) { + incr = 1; + argc--, argv++; + if (getnum("priority", *argv, &prio)) + return (1); + continue; + } + if (strcmp(*argv, "-g") == 0) { + which = PRIO_PGRP; + continue; + } + if (strcmp(*argv, "-u") == 0) { + which = PRIO_USER; + continue; + } + if (strcmp(*argv, "-p") == 0) { + which = PRIO_PROCESS; + continue; + } + if (strcmp(*argv, "--") == 0) { + delim = 1; + continue; + } + if (0 == priflag) { + /* if very first switch/arg and we've made it to + here, this must be the priority */ + if (getnum("priority", *argv, &prio)) { + return(1); + } + continue; + } + } + if (which == PRIO_USER) { + if ((pwd = getpwnam(*argv)) != NULL) + who = pwd->pw_uid; + else if (getnum("uid", *argv, &who)) { + errs++; + continue; + } else if (who < 0) { + warnx("%s: bad value", *argv); + errs++; + continue; + } + } else { + if (getnum("pid", *argv, &who)) { + errs++; + continue; + } + if (who < 0) { + warnx("%s: bad value", *argv); + errs++; + continue; + } + } + errs += donice(which, who, prio, incr); + } + exit(errs != 0); +} + +static int +donice(int which, int who, int prio, int incr) +{ + int oldprio; + + errno = 0; + oldprio = getpriority(which, who); + if (oldprio == -1 && errno) { + warn("%d: getpriority", who); + return (1); + } + if (incr) + prio = oldprio + prio; + if (prio > PRIO_MAX) + prio = PRIO_MAX; + if (prio < PRIO_MIN) + prio = PRIO_MIN; + if (setpriority(which, who, prio) < 0) { + warn("%d: setpriority", who); + return (1); + } + return (0); +} + +static int +getnum(const char *com, const char *str, int *val) +{ + long v; + char *ep; + + errno = 0; + v = strtol(str, &ep, 10); + if (v < INT_MIN || v > INT_MAX || errno == ERANGE) { + warnx("%s argument %s is out of range.", com, str); + return (1); + } + if (ep == str || *ep != '\0' || errno != 0) { + warnx("Bad %s argument: %s.", com, str); + return (1); + } + + *val = (int)v; + return (0); +} + +static void +usage() +{ + fprintf(stderr, "%s\n%s\n", +"usage: renice priority [[-p] pid ...] [[-g] pgrp ...] [[-u] user ...]", +" renice -n increment [[-p] pid ...] [[-g] pgrp ...] [[-u] user ...]"); + exit(1); +} diff --git a/shell_cmds/script/script.1 b/shell_cmds/script/script.1 new file mode 100644 index 0000000..ad12c53 --- /dev/null +++ b/shell_cmds/script/script.1 @@ -0,0 +1,217 @@ +.\" Copyright (c) 1980, 1990, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)script.1 8.1 (Berkeley) 6/6/93 +.\" $FreeBSD$ +.\" +.Dd December 4, 2013 +.Dt SCRIPT 1 +.Os +.Sh NAME +.Nm script +.Nd make typescript of terminal session +.Sh SYNOPSIS +.Nm +.Op Fl adkpqr +.Op Fl F Ar pipe +.Op Fl t Ar time +.Op Ar file Op Ar command ... +.Sh DESCRIPTION +The +.Nm +utility makes a typescript of everything printed on your terminal. +It is useful for students who need a hardcopy record of an interactive +session as proof of an assignment, as the typescript file +can be printed out later with +.Xr lpr 1 . +.Pp +If the argument +.Ar file +is given, +.Nm +saves all dialogue in +.Ar file . +If no file name is given, the typescript is saved in the file +.Pa typescript . +.Pp +If the argument +.Ar command +is given, +.Nm +will run the specified command with an optional argument vector +instead of an interactive shell. +.Pp +The following options are available: +.Bl -tag -width indent +.It Fl a +Append the output to +.Ar file +or +.Pa typescript , +retaining the prior contents. +.It Fl d +When playing back a session with the +.Fl p +flag, do not sleep between records when playing back a timestamped session. +.It Fl F Ar pipe +Immediately flush output after each write. +This will allow a user to create a named pipe using +.Xr mkfifo 1 +and another user may watch the live session using a utility like +.Xr cat 1 . +.\".It Fl f +.\"Create +.\".Ar file.filemon +.\"or +.\".Pa typescript.filemon +.\"using +.\".Xr filemon 4 . +.It Fl k +Log keys sent to the program as well as output. +.It Fl p +Play back a session recorded with the +.Fl r +flag in real time. +.It Fl q +Run in quiet mode, omit the start, stop and command status messages. +.It Fl r +Record a session with input, output, and timestamping. +.It Fl t Ar time +Specify the interval at which the script output file will be flushed +to disk, in seconds. +A value of 0 +causes +.Nm +to flush after every character I/O event. +The default interval is +30 seconds. +.El +.Pp +The script ends when the forked shell (or command) exits (a +.Em control-D +to exit +the Bourne shell +.Pf ( Xr sh 1 ) , +and +.Em exit , +.Em logout +or +.Em control-D +(if +.Em ignoreeof +is not set) for the +C-shell, +.Xr csh 1 ) . +.Pp +Certain interactive commands, such as +.Xr vi 1 , +create garbage in the typescript file. +The +.Nm +utility works best with commands that do not manipulate the screen. +The results are meant to emulate a hardcopy terminal, not an addressable one. +.Sh ENVIRONMENT +The following environment variables are utilized by +.Nm : +.Bl -tag -width SHELL +.It Ev SCRIPT +The +.Ev SCRIPT +environment variable is added to the sub-shell. +If +.Ev SCRIPT +already existed in the users environment, +its value is overwritten within the sub-shell. +The value of +.Ev SCRIPT +is the name of the +.Ar typescript +file. +.It Ev SHELL +If the variable +.Ev SHELL +exists, the shell forked by +.Nm +will be that shell. +If +.Ev SHELL +is not set, the Bourne shell +is assumed. +.Pq Most shells set this variable automatically . +.El +.Sh SEE ALSO +.Xr csh 1 +.\".Xr filemon 4 +.\".Po +.\"for the +.\".Em history +.\"mechanism +.\".Pc . +.Sh HISTORY +The +.Nm +command appeared in +.Bx 3.0 . +.Pp +The +.Fl d , +.Fl p +and +.Fl r +options first appeared in +.Nx 2.0 +and were ported to +.Fx 9.2 . +.Sh BUGS +The +.Nm +utility places +.Sy everything +in the log file, including linefeeds and backspaces. +This is not what the naive user expects. +.Pp +It is not possible to specify a command without also naming the script file +because of argument parsing compatibility issues. +.Pp +When running in +.Fl k +mode, echo cancelling is far from ideal. +The slave terminal mode is checked +for ECHO mode to check when to avoid manual echo logging. +This does not +work when the terminal is in a raw mode where +the program being run is doing manual echo. +.Pp +If +.Nm +reads zero bytes from the terminal, it switches to a mode when it +only attempts to read +once a second until there is data to read. +This prevents +.Nm +from spinning on zero-byte reads, but might cause a 1-second delay in +processing of user input. diff --git a/shell_cmds/script/script.c b/shell_cmds/script/script.c new file mode 100644 index 0000000..90e895e --- /dev/null +++ b/shell_cmds/script/script.c @@ -0,0 +1,546 @@ +/* + * Copyright (c) 2010, 2012 David E. O'Brien + * Copyright (c) 1980, 1992, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/param.h> +__FBSDID("$FreeBSD$"); +#ifndef lint +static const char copyright[] = +"@(#) Copyright (c) 1980, 1992, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif +#ifndef lint +static const char sccsid[] = "@(#)script.c 8.1 (Berkeley) 6/6/93"; +#endif + +#include <sys/wait.h> +#include <sys/stat.h> +#include <sys/ioctl.h> +#include <sys/time.h> +#include <sys/uio.h> +#ifdef __APPLE__ +#include <libkern/OSByteOrder.h> +#else +#include <sys/endian.h> +#endif +#ifdef ENABLE_FILEMON +#include <dev/filemon/filemon.h> +#endif /* ENABLE_FILEMON */ + +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#ifdef __APPLE__ +#include <util.h> +#else +#include <libutil.h> +#endif +#include <paths.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <termios.h> +#include <unistd.h> + +#define DEF_BUF 65536 + +struct stamp { + uint64_t scr_len; /* amount of data */ + uint64_t scr_sec; /* time it arrived in seconds... */ + uint32_t scr_usec; /* ...and microseconds */ + uint32_t scr_direction; /* 'i', 'o', etc (also indicates endianness) */ +}; + +static FILE *fscript; +static int master, slave; +static int child; +static const char *fname; +static char *fmfname; +#ifdef ENABLE_FILEMON +static int fflg, qflg, ttyflg; +#else /* !ENABLE_FILEMON */ +static int qflg, ttyflg; +#endif /* ENABLE_FILEMON */ +static int usesleep, rawout; + +static struct termios tt; + +static void done(int) __dead2; +static void doshell(char **); +static void fail(void); +static void finish(void); +static void record(FILE *, char *, size_t, int); +static void consume(FILE *, off_t, char *, int); +static void playback(FILE *) __dead2; +static void usage(void); + +int +main(int argc, char *argv[]) +{ + int cc; + struct termios rtt, stt; + struct winsize win; + struct timeval tv, *tvp; + time_t tvec, start; + char obuf[BUFSIZ]; + char ibuf[BUFSIZ]; + fd_set rfd; + int aflg, Fflg, kflg, pflg, ch, k, n; + int flushtime, readstdin; +#ifdef ENABLE_FILEMON + int fm_fd, fm_log; +#endif /* ENABLE_FILEMON */ + + aflg = Fflg = kflg = pflg = 0; + usesleep = 1; + rawout = 0; + flushtime = 30; +#ifdef ENABLE_FILEMON + fm_fd = -1; /* Shut up stupid "may be used uninitialized" GCC + warning. (not needed w/clang) */ +#endif /* ENABLE_FILEMON */ + +#ifdef ENABLE_FILEMON + while ((ch = getopt(argc, argv, "adFfkpqrt:")) != -1) +#else /* !ENABLE_FILEMON */ + while ((ch = getopt(argc, argv, "adFkpqrt:")) != -1) +#endif /* ENABLE_FILEMON */ + switch(ch) { + case 'a': + aflg = 1; + break; + case 'd': + usesleep = 0; + break; + case 'F': + Fflg = 1; + break; +#ifdef ENABLE_FILEMON + case 'f': + fflg = 1; + break; +#endif /* ENABLE_FILEMON */ + case 'k': + kflg = 1; + break; + case 'p': + pflg = 1; + break; + case 'q': + qflg = 1; + break; + case 'r': + rawout = 1; + break; + case 't': + flushtime = atoi(optarg); + if (flushtime < 0) + err(1, "invalid flush time %d", flushtime); + break; + case '?': + default: + usage(); + } + argc -= optind; + argv += optind; + + if (argc > 0) { + fname = argv[0]; + argv++; + argc--; + } else + fname = "typescript"; + + if ((fscript = fopen(fname, pflg ? "r" : aflg ? "a" : "w")) == NULL) + err(1, "%s", fname); + +#ifdef ENABLE_FILEMON + if (fflg) { + asprintf(&fmfname, "%s.filemon", fname); + if (!fmfname) + err(1, "%s.filemon", fname); + if ((fm_fd = open("/dev/filemon", O_RDWR)) == -1) + err(1, "open(\"/dev/filemon\", O_RDWR)"); + if ((fm_log = open(fmfname, O_WRONLY | O_CREAT | O_TRUNC, + S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) == -1) + err(1, "open(%s)", fmfname); + if (ioctl(fm_fd, FILEMON_SET_FD, &fm_log) < 0) + err(1, "Cannot set filemon log file descriptor"); + + /* Set up these two fd's to close on exec. */ + (void)fcntl(fm_fd, F_SETFD, FD_CLOEXEC); + (void)fcntl(fm_log, F_SETFD, FD_CLOEXEC); + } +#endif /* ENABLE_FILEMON */ + + if (pflg) + playback(fscript); + + if ((ttyflg = isatty(STDIN_FILENO)) != 0) { + if (tcgetattr(STDIN_FILENO, &tt) == -1) + err(1, "tcgetattr"); + if (ioctl(STDIN_FILENO, TIOCGWINSZ, &win) == -1) + err(1, "ioctl"); + if (openpty(&master, &slave, NULL, &tt, &win) == -1) + err(1, "openpty"); + } else { + if (openpty(&master, &slave, NULL, NULL, NULL) == -1) + err(1, "openpty"); + } + + if (rawout) + record(fscript, NULL, 0, 's'); + + if (!qflg) { + tvec = time(NULL); + (void)printf("Script started, output file is %s\n", fname); + if (!rawout) { + (void)fprintf(fscript, "Script started on %s", + ctime(&tvec)); + if (argv[0]) { + fprintf(fscript, "command: "); + for (k = 0 ; argv[k] ; ++k) + fprintf(fscript, "%s%s", k ? " " : "", + argv[k]); + fprintf(fscript, "\n"); + } + } + fflush(fscript); +#ifdef ENABLE_FILEMON + if (fflg) { + (void)printf("Filemon started, output file is %s\n", + fmfname); + } +#endif /* ENABLE_FILEMON */ + } + if (ttyflg) { + rtt = tt; + cfmakeraw(&rtt); + rtt.c_lflag &= ~ECHO; + (void)tcsetattr(STDIN_FILENO, TCSAFLUSH, &rtt); + } + + child = fork(); + if (child < 0) { + warn("fork"); + done(1); + } + if (child == 0) + doshell(argv); + close(slave); + +#ifdef ENABLE_FILEMON + if (fflg && ioctl(fm_fd, FILEMON_SET_PID, &child) < 0) + err(1, "Cannot set filemon PID"); +#endif /* ENABLE_FILEMON */ + + start = tvec = time(0); + readstdin = 1; + for (;;) { + FD_ZERO(&rfd); + FD_SET(master, &rfd); + if (readstdin) + FD_SET(STDIN_FILENO, &rfd); + if (!readstdin && ttyflg) { + tv.tv_sec = 1; + tv.tv_usec = 0; + tvp = &tv; + readstdin = 1; + } else if (flushtime > 0) { + tv.tv_sec = flushtime - (tvec - start); + tv.tv_usec = 0; + tvp = &tv; + } else { + tvp = NULL; + } + n = select(master + 1, &rfd, 0, 0, tvp); + if (n < 0 && errno != EINTR) + break; + if (n > 0 && FD_ISSET(STDIN_FILENO, &rfd)) { + cc = read(STDIN_FILENO, ibuf, BUFSIZ); + if (cc < 0) + break; + if (cc == 0) { + if (tcgetattr(master, &stt) == 0 && + (stt.c_lflag & ICANON) != 0) { + (void)write(master, &stt.c_cc[VEOF], 1); + } + readstdin = 0; + } + if (cc > 0) { + if (rawout) + record(fscript, ibuf, cc, 'i'); + (void)write(master, ibuf, cc); + if (kflg && tcgetattr(master, &stt) >= 0 && + ((stt.c_lflag & ECHO) == 0)) { + (void)fwrite(ibuf, 1, cc, fscript); + } + } + } + if (n > 0 && FD_ISSET(master, &rfd)) { + cc = read(master, obuf, sizeof (obuf)); + if (cc <= 0) + break; + (void)write(STDOUT_FILENO, obuf, cc); + if (rawout) + record(fscript, obuf, cc, 'o'); + else + (void)fwrite(obuf, 1, cc, fscript); + } + tvec = time(0); + if (tvec - start >= flushtime) { + fflush(fscript); + start = tvec; + } + if (Fflg) + fflush(fscript); + } + finish(); + done(0); +} + +static void +usage(void) +{ + (void)fprintf(stderr, +#ifdef ENABLE_FILEMON + "usage: script [-adfkpqr] [-t time] [file [command ...]]\n"); +#else /* !ENABLE_FILEMON */ + "usage: script [-adkpqr] [-t time] [file [command ...]]\n"); +#endif /* ENABLE_FILEMON */ + exit(1); +} + +static void +finish(void) +{ + int e, status; + + if (waitpid(child, &status, 0) == child) { + if (WIFEXITED(status)) + e = WEXITSTATUS(status); + else if (WIFSIGNALED(status)) + e = WTERMSIG(status); + else /* can't happen */ + e = 1; + done(e); + } +} + +static void +doshell(char **av) +{ + const char *shell; + + shell = getenv("SHELL"); + if (shell == NULL) + shell = _PATH_BSHELL; + + (void)close(master); + (void)fclose(fscript); + free(fmfname); + login_tty(slave); + setenv("SCRIPT", fname, 1); + if (av[0]) { + execvp(av[0], av); + warn("%s", av[0]); + } else { + execl(shell, shell, "-i", (char *)NULL); + warn("%s", shell); + } + fail(); +} + +static void +fail(void) +{ + (void)kill(0, SIGTERM); + done(1); +} + +static void +done(int eno) +{ + time_t tvec; + + if (ttyflg) + (void)tcsetattr(STDIN_FILENO, TCSAFLUSH, &tt); + tvec = time(NULL); + if (rawout) + record(fscript, NULL, 0, 'e'); + if (!qflg) { + if (!rawout) + (void)fprintf(fscript,"\nScript done on %s", + ctime(&tvec)); + (void)printf("\nScript done, output file is %s\n", fname); +#ifdef ENABLE_FILEMON + if (fflg) { + (void)printf("Filemon done, output file is %s\n", + fmfname); + } +#endif /* ENABLE_FILEMON */ + } + (void)fclose(fscript); + (void)close(master); + exit(eno); +} + +static void +record(FILE *fp, char *buf, size_t cc, int direction) +{ + struct iovec iov[2]; + struct stamp stamp; + struct timeval tv; + + (void)gettimeofday(&tv, NULL); + stamp.scr_len = cc; + stamp.scr_sec = tv.tv_sec; + stamp.scr_usec = tv.tv_usec; + stamp.scr_direction = direction; + iov[0].iov_len = sizeof(stamp); + iov[0].iov_base = &stamp; + iov[1].iov_len = cc; + iov[1].iov_base = buf; + if (writev(fileno(fp), &iov[0], 2) == -1) + err(1, "writev"); +} + +static void +consume(FILE *fp, off_t len, char *buf, int reg) +{ + size_t l; + + if (reg) { + if (fseeko(fp, len, SEEK_CUR) == -1) + err(1, NULL); + } + else { + while (len > 0) { + l = MIN(DEF_BUF, len); + if (fread(buf, sizeof(char), l, fp) != l) + err(1, "cannot read buffer"); + len -= l; + } + } +} + +#ifdef __APPLE__ +#define bswap32 OSSwapInt32 +#define bswap64 OSSwapInt64 +#endif /* __APPLE__ */ +#define swapstamp(stamp) do { \ + if (stamp.scr_direction > 0xff) { \ + stamp.scr_len = bswap64(stamp.scr_len); \ + stamp.scr_sec = bswap64(stamp.scr_sec); \ + stamp.scr_usec = bswap32(stamp.scr_usec); \ + stamp.scr_direction = bswap32(stamp.scr_direction); \ + } \ +} while (0/*CONSTCOND*/) + +static void +playback(FILE *fp) +{ + struct timespec tsi, tso; + struct stamp stamp; + struct stat pst; + char buf[DEF_BUF]; + off_t nread, save_len; + size_t l; + time_t tclock; + int reg; + + if (fstat(fileno(fp), &pst) == -1) + err(1, "fstat failed"); + + reg = S_ISREG(pst.st_mode); + + for (nread = 0; !reg || nread < pst.st_size; nread += save_len) { + if (fread(&stamp, sizeof(stamp), 1, fp) != 1) { + if (reg) + err(1, "reading playback header"); + else + break; + } + swapstamp(stamp); + save_len = sizeof(stamp); + + if (reg && stamp.scr_len > + (uint64_t)(pst.st_size - save_len) - nread) + errx(1, "invalid stamp"); + + save_len += stamp.scr_len; + tclock = stamp.scr_sec; + tso.tv_sec = stamp.scr_sec; + tso.tv_nsec = stamp.scr_usec * 1000; + + switch (stamp.scr_direction) { + case 's': + if (!qflg) + (void)printf("Script started on %s", + ctime(&tclock)); + tsi = tso; + (void)consume(fp, stamp.scr_len, buf, reg); + break; + case 'e': + if (!qflg) + (void)printf("\nScript done on %s", + ctime(&tclock)); + (void)consume(fp, stamp.scr_len, buf, reg); + break; + case 'i': + /* throw input away */ + (void)consume(fp, stamp.scr_len, buf, reg); + break; + case 'o': + tsi.tv_sec = tso.tv_sec - tsi.tv_sec; + tsi.tv_nsec = tso.tv_nsec - tsi.tv_nsec; + if (tsi.tv_nsec < 0) { + tsi.tv_sec -= 1; + tsi.tv_nsec += 1000000000; + } + if (usesleep) + (void)nanosleep(&tsi, NULL); + tsi = tso; + while (stamp.scr_len > 0) { + l = MIN(DEF_BUF, stamp.scr_len); + if (fread(buf, sizeof(char), l, fp) != l) + err(1, "cannot read buffer"); + + (void)write(STDOUT_FILENO, buf, l); + stamp.scr_len -= l; + } + break; + default: + errx(1, "invalid direction"); + } + } + (void)fclose(fp); + exit(0); +} diff --git a/shell_cmds/script/script.plist.part b/shell_cmds/script/script.plist.part new file mode 100644 index 0000000..b112d31 --- /dev/null +++ b/shell_cmds/script/script.plist.part @@ -0,0 +1,20 @@ + <dict> + <key>OpenSourceProject</key> + <string>script</string> + <key>OpenSourceVersion</key> + <string>2014-01-17</string> + <key>OpenSourceWebsiteURL</key> + <string>http://svnweb.freebsd.org/base/head/usr.bin/script/</string> + <key>OpenSourceSCM</key> + <string>svn co http://svn.freebsd.org/base/head/usr.bin/script/</string> + <key>OpenSourceImportDate</key> + <string>2014-09-22</string> + <key>OpenSourceModifications</key> + <array> + <string>Use OSByteOrder swap functions.</string> + <string>Include util.h instead of libutil.h.</string> + <string>Disable -f (filemon) feature, guarded with ENABLE_FILEMON.</string> + </array> + <key>OpenSourceLicense</key> + <string>bsd</string> + </dict> diff --git a/shell_cmds/seq/seq.1 b/shell_cmds/seq/seq.1 new file mode 100644 index 0000000..4af7b99 --- /dev/null +++ b/shell_cmds/seq/seq.1 @@ -0,0 +1,187 @@ +.\" $NetBSD: seq.1,v 1.6 2008/11/26 15:03:47 ginsbach Exp $ +.\" +.\" Copyright (c) 2005 The NetBSD Foundation, Inc. +.\" All rights reserved. +.\" +.\" This code is derived from software contributed to The NetBSD Foundation +.\" by Brian Ginsbach. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS +.\" ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +.\" TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +.\" PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS +.\" BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +.\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +.\" POSSIBILITY OF SUCH DAMAGE. +.\" +.\" $FreeBSD: src/usr.bin/seq/seq.1,v 1.1 2010/02/19 23:54:12 delphij Exp $ +.\" +.Dd February 19, 2010 +.Dt SEQ 1 +.Os +.Sh NAME +.Nm seq +.Nd print sequences of numbers +.Sh SYNOPSIS +.Nm +.Op Fl w +.Op Fl f Ar format +.Op Fl s Ar string +.Op Fl t Ar string +.Op Ar first Op Ar incr +.Ar last +.Sh DESCRIPTION +The +.Nm +utility prints a sequence of numbers, one per line +.Pq default , +from +.Ar first +.Pq default 1 , +to near +.Ar last +as possible, in increments of +.Ar incr +.Pq default 1 . +When +.Ar first +is larger than +.Ar last +the default +.Ar incr +is -1. +.Pp +All numbers are interpreted as floating point. +.Pp +Normally integer values are printed as decimal integers. +.Pp +The +.Nm +utility accepts the following options: +.Bl -tag -width Ar +.It Fl f Ar format +Use a +.Xr printf 3 +style +.Ar format +to print each number. +Only the +.Cm E , +.Cm e , +.Cm f , +.Cm G , +.Cm g , +and +.Cm % +conversion characters are valid, along with any optional +flags and an optional numeric minimum field width or precision. +The +.Ar format +can contain character escape sequences in backslash notation as +defined in +.St -ansiC . +The default is +.Cm %g . +.It Fl s Ar string +Use +.Ar string +to separate numbers. +The +.Ar string +can contain character escape sequences in backslash notation as +defined in +.St -ansiC . +The default is +.Cm \en . +.It Fl t Ar string +Use +.Ar string +to terminate sequence of numbers. +The +.Ar string +can contain character escape sequences in backslash notation as +defined in +.St -ansiC . +This option is useful when the default separator +does not contain a +.Cm \en . +.It Fl w +Equalize the widths of all numbers by padding with zeros as necessary. +This option has no effect with the +.Fl f +option. +If any sequence numbers will be printed in exponential notation, +the default conversion is changed to +.Cm %e . +.El +.Pp +The +.Nm +utility exits 0 on success and non-zero if an error occurs. +.Sh EXAMPLES +.Bd -literal -offset indent +# seq 1 3 +1 +2 +3 + +# seq 3 1 +3 +2 +1 + +# seq -w 0 .05 .1 +0.00 +0.05 +0.10 +.Ed +.Sh SEE ALSO +.Xr jot 1 , +.Xr printf 1 , +.Xr printf 3 +.Sh HISTORY +The +.Nm +command first appeared in +.Tn "Plan 9 from Bell Labs" . +A +.Nm +command appeared in +.Nx 3.0 , +and ported to +.Fx 9.0 . +This command was based on the command of the same name in +.Tn "Plan 9 from Bell Labs" +and the +.Tn GNU +core utilities. +The +.Tn GNU +.Nm +command first appeared in the 1.13 shell utilities release. +.Sh BUGS +The +.Fl w +option does not handle the transition from pure floating point +to exponent representation very well. +The +.Nm +command is not bug for bug compatible with the +.Tn "Plan 9 from Bell Labs" +or +.Tn GNU +versions of +.Nm . diff --git a/shell_cmds/seq/seq.c b/shell_cmds/seq/seq.c new file mode 100644 index 0000000..9c3623d --- /dev/null +++ b/shell_cmds/seq/seq.c @@ -0,0 +1,453 @@ +/* $NetBSD: seq.c,v 1.5 2008/07/21 14:19:26 lukem Exp $ */ +/* + * Copyright (c) 2005 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Brian Ginsbach. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD: src/usr.bin/seq/seq.c,v 1.2 2010/02/20 01:23:15 delphij Exp $"); + +#include <ctype.h> +#include <err.h> +#include <errno.h> +#include <math.h> +#include <locale.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#define ZERO '0' +#define SPACE ' ' + +#define MAX(a, b) (((a) < (b))? (b) : (a)) +#define ISSIGN(c) ((int)(c) == '-' || (int)(c) == '+') +#define ISEXP(c) ((int)(c) == 'e' || (int)(c) == 'E') +#define ISODIGIT(c) ((int)(c) >= '0' && (int)(c) <= '7') + +/* Globals */ + +const char *decimal_point = "."; /* default */ +char default_format[] = { "%g" }; /* default */ + +/* Prototypes */ + +double e_atof(const char *); + +int decimal_places(const char *); +int main(int, char *[]); +int numeric(const char *); +int valid_format(const char *); + +char *generate_format(double, double, double, int, char); +char *unescape(char *); + +/* + * The seq command will print out a numeric sequence from 1, the default, + * to a user specified upper limit by 1. The lower bound and increment + * maybe indicated by the user on the command line. The sequence can + * be either whole, the default, or decimal numbers. + */ +int +main(int argc, char *argv[]) +{ + int c = 0, errflg = 0; + int equalize = 0; + double first = 1.0; + double last = 0.0; + double incr = 0.0; + struct lconv *locale; + char *fmt = NULL; + const char *sep = "\n"; + const char *term = NULL; + char pad = ZERO; + + /* Determine the locale's decimal point. */ + locale = localeconv(); + if (locale && locale->decimal_point && locale->decimal_point[0] != '\0') + decimal_point = locale->decimal_point; + + /* + * Process options, but handle negative numbers separately + * least they trip up getopt(3). + */ + while ((optind < argc) && !numeric(argv[optind]) && + (c = getopt(argc, argv, "f:hs:t:w")) != -1) { + + switch (c) { + case 'f': /* format (plan9) */ + fmt = optarg; + equalize = 0; + break; + case 's': /* separator (GNU) */ + sep = unescape(optarg); + break; + case 't': /* terminator (new) */ + term = unescape(optarg); + break; + case 'w': /* equal width (plan9) */ + if (!fmt) + if (equalize++) + pad = SPACE; + break; + case 'h': /* help (GNU) */ + default: + errflg++; + break; + } + } + + argc -= optind; + argv += optind; + if (argc < 1 || argc > 3) + errflg++; + + if (errflg) { + fprintf(stderr, + "usage: %s [-w] [-f format] [-s string] [-t string] [first [incr]] last\n", + getprogname()); + exit(1); + } + + last = e_atof(argv[argc - 1]); + + if (argc > 1) + first = e_atof(argv[0]); + + if (argc > 2) { + incr = e_atof(argv[1]); + /* Plan 9/GNU don't do zero */ + if (incr == 0.0) + errx(1, "zero %screment", (first < last)? "in" : "de"); + } + + /* default is one for Plan 9/GNU work alike */ + if (incr == 0.0) + incr = (first < last) ? 1.0 : -1.0; + + if (incr <= 0.0 && first < last) + errx(1, "needs positive increment"); + + if (incr >= 0.0 && first > last) + errx(1, "needs negative decrement"); + + if (fmt != NULL) { + if (!valid_format(fmt)) + errx(1, "invalid format string: `%s'", fmt); + fmt = unescape(fmt); + /* + * XXX to be bug for bug compatible with Plan 9 add a + * newline if none found at the end of the format string. + */ + } else + fmt = generate_format(first, incr, last, equalize, pad); + + if (incr > 0) { + for (; first <= last; first += incr) { + printf(fmt, first); + fputs(sep, stdout); + } + } else { + for (; first >= last; first += incr) { + printf(fmt, first); + fputs(sep, stdout); + } + } + if (term != NULL) + fputs(term, stdout); + + return (0); +} + +/* + * numeric - verify that string is numeric + */ +int +numeric(const char *s) +{ + int seen_decimal_pt, decimal_pt_len; + + /* skip any sign */ + if (ISSIGN((unsigned char)*s)) + s++; + + seen_decimal_pt = 0; + decimal_pt_len = strlen(decimal_point); + while (*s) { + if (!isdigit((unsigned char)*s)) { + if (!seen_decimal_pt && + strncmp(s, decimal_point, decimal_pt_len) == 0) { + s += decimal_pt_len; + seen_decimal_pt = 1; + continue; + } + if (ISEXP((unsigned char)*s)) { + s++; + if (ISSIGN((unsigned char)*s) || + isdigit((unsigned char)*s)) { + s++; + continue; + } + } + break; + } + s++; + } + return (*s == '\0'); +} + +/* + * valid_format - validate user specified format string + */ +int +valid_format(const char *fmt) +{ + int conversions = 0; + + while (*fmt != '\0') { + /* scan for conversions */ + if (*fmt != '\0' && *fmt != '%') { + do { + fmt++; + } while (*fmt != '\0' && *fmt != '%'); + } + /* scan a conversion */ + if (*fmt != '\0') { + do { + fmt++; + + /* ok %% */ + if (*fmt == '%') { + fmt++; + break; + } + /* valid conversions */ + if (strchr("eEfgG", *fmt) && + conversions++ < 1) { + fmt++; + break; + } + /* flags, width and precsision */ + if (isdigit((unsigned char)*fmt) || + strchr("+- 0#.", *fmt)) + continue; + + /* oops! bad conversion format! */ + return (0); + } while (*fmt != '\0'); + } + } + + return (conversions <= 1); +} + +/* + * unescape - handle C escapes in a string + */ +char * +unescape(char *orig) +{ + char c, *cp, *new = orig; + int i; + + for (cp = orig; (*orig = *cp); cp++, orig++) { + if (*cp != '\\') + continue; + + switch (*++cp) { + case 'a': /* alert (bell) */ + *orig = '\a'; + continue; + case 'b': /* backspace */ + *orig = '\b'; + continue; + case 'e': /* escape */ + *orig = '\e'; + continue; + case 'f': /* formfeed */ + *orig = '\f'; + continue; + case 'n': /* newline */ + *orig = '\n'; + continue; + case 'r': /* carriage return */ + *orig = '\r'; + continue; + case 't': /* horizontal tab */ + *orig = '\t'; + continue; + case 'v': /* vertical tab */ + *orig = '\v'; + continue; + case '\\': /* backslash */ + *orig = '\\'; + continue; + case '\'': /* single quote */ + *orig = '\''; + continue; + case '\"': /* double quote */ + *orig = '"'; + continue; + case '0': + case '1': + case '2': + case '3': /* octal */ + case '4': + case '5': + case '6': + case '7': /* number */ + for (i = 0, c = 0; + ISODIGIT((unsigned char)*cp) && i < 3; + i++, cp++) { + c <<= 3; + c |= (*cp - '0'); + } + *orig = c; + --cp; + continue; + case 'x': /* hexidecimal number */ + cp++; /* skip 'x' */ + for (i = 0, c = 0; + isxdigit((unsigned char)*cp) && i < 2; + i++, cp++) { + c <<= 4; + if (isdigit((unsigned char)*cp)) + c |= (*cp - '0'); + else + c |= ((toupper((unsigned char)*cp) - + 'A') + 10); + } + *orig = c; + --cp; + continue; + default: + --cp; + break; + } + } + + return (new); +} + +/* + * e_atof - convert an ASCII string to a double + * exit if string is not a valid double, or if converted value would + * cause overflow or underflow + */ +double +e_atof(const char *num) +{ + char *endp; + double dbl; + + errno = 0; + dbl = strtod(num, &endp); + + if (errno == ERANGE) + /* under or overflow */ + err(2, "%s", num); + else if (*endp != '\0') + /* "junk" left in number */ + errx(2, "invalid floating point argument: %s", num); + + /* zero shall have no sign */ + if (dbl == -0.0) + dbl = 0; + return (dbl); +} + +/* + * decimal_places - count decimal places in a number (string) + */ +int +decimal_places(const char *number) +{ + int places = 0; + char *dp; + + /* look for a decimal point */ + if ((dp = strstr(number, decimal_point))) { + dp += strlen(decimal_point); + + while (isdigit((unsigned char)*dp++)) + places++; + } + return (places); +} + +/* + * generate_format - create a format string + * + * XXX to be bug for bug compatable with Plan9 and GNU return "%g" + * when "%g" prints as "%e" (this way no width adjustments are made) + */ +char * +generate_format(double first, double incr, double last, int equalize, char pad) +{ + static char buf[256]; + char cc = '\0'; + int precision, width1, width2, places; + + if (equalize == 0) + return (default_format); + + /* figure out "last" value printed */ + if (first > last) + last = first - incr * floor((first - last) / incr); + else + last = first + incr * floor((last - first) / incr); + + sprintf(buf, "%g", incr); + if (strchr(buf, 'e')) + cc = 'e'; + precision = decimal_places(buf); + + width1 = sprintf(buf, "%g", first); + if (strchr(buf, 'e')) + cc = 'e'; + if ((places = decimal_places(buf))) + width1 -= (places + strlen(decimal_point)); + + precision = MAX(places, precision); + + width2 = sprintf(buf, "%g", last); + if (strchr(buf, 'e')) + cc = 'e'; + if ((places = decimal_places(buf))) + width2 -= (places + strlen(decimal_point)); + + if (precision) { + sprintf(buf, "%%%c%d.%d%c", pad, + MAX(width1, width2) + (int) strlen(decimal_point) + + precision, precision, (cc) ? cc : 'f'); + } else { + sprintf(buf, "%%%c%d%c", pad, MAX(width1, width2), + (cc) ? cc : 'g'); + } + + return (buf); +} diff --git a/shell_cmds/sh/Makefile b/shell_cmds/sh/Makefile new file mode 100644 index 0000000..8d08662 --- /dev/null +++ b/shell_cmds/sh/Makefile @@ -0,0 +1,70 @@ +# @(#)Makefile 8.4 (Berkeley) 5/5/95 +# $FreeBSD: head/bin/sh/Makefile 322515 2017-08-14 19:21:37Z ngie $ + +.include <src.opts.mk> + +PACKAGE=runtime +PROG= sh +INSTALLFLAGS= -S +SHSRCS= alias.c arith_yacc.c arith_yylex.c cd.c echo.c error.c eval.c \ + exec.c expand.c \ + histedit.c input.c jobs.c kill.c mail.c main.c memalloc.c miscbltin.c \ + mystring.c options.c output.c parser.c printf.c redir.c show.c \ + test.c trap.c var.c +GENSRCS= builtins.c nodes.c syntax.c +GENHDRS= builtins.h nodes.h syntax.h token.h +SRCS= ${SHSRCS} ${GENSRCS} ${GENHDRS} + +# MLINKS for Shell built in commands for which there are no userland +# utilities of the same name are handled with the associated manpage, +# builtin.1 in share/man/man1/. + +LIBADD= edit + +CFLAGS+=-DSHELL -I. -I${.CURDIR} +# for debug: +# DEBUG_FLAGS+= -g -DDEBUG=2 -fno-inline +WARNS?= 2 +WFORMAT=0 + +.PATH: ${.CURDIR}/bltin \ + ${.CURDIR:H}/kill \ + ${.CURDIR:H}/test \ + ${SRCTOP}/usr.bin/printf + +CLEANFILES+= mknodes mknodes.o \ + mksyntax mksyntax.o +CLEANFILES+= ${GENSRCS} ${GENHDRS} + +build-tools: mknodes mksyntax + +.ORDER: builtins.c builtins.h +builtins.h: .NOMETA +builtins.c builtins.h: mkbuiltins builtins.def + sh ${.CURDIR}/mkbuiltins ${.CURDIR} + +# XXX this is just to stop the default .c rule being used, so that the +# intermediate object has a fixed name. +# XXX we have a default .c rule, but no default .o rule. +mknodes.o mksyntax.o: ${BUILD_TOOLS_META} + ${CC} ${CFLAGS} ${LDFLAGS} ${.IMPSRC} ${LDLIBS} -o ${.TARGET} +mknodes: mknodes.o ${BUILD_TOOLS_META} +mksyntax: mksyntax.o ${BUILD_TOOLS_META} + +.ORDER: nodes.c nodes.h +nodes.h: .NOMETA +nodes.c nodes.h: mknodes nodetypes nodes.c.pat + ${BTOOLSPATH:U.}/mknodes ${.CURDIR}/nodetypes ${.CURDIR}/nodes.c.pat + +.ORDER: syntax.c syntax.h +syntax.h: .NOMETA +syntax.c syntax.h: mksyntax + ${BTOOLSPATH:U.}/mksyntax + +token.h: mktokens + sh ${.CURDIR}/mktokens + +HAS_TESTS= +SUBDIR.${MK_TESTS}+= tests + +.include <bsd.prog.mk> diff --git a/shell_cmds/sh/Makefile.depend b/shell_cmds/sh/Makefile.depend new file mode 100644 index 0000000..189c031 --- /dev/null +++ b/shell_cmds/sh/Makefile.depend @@ -0,0 +1,19 @@ +# $FreeBSD: head/bin/sh/Makefile.depend 325188 2017-10-31 00:07:04Z bdrewery $ +# Autogenerated - do NOT edit! + +DIRDEPS = \ + gnu/lib/csu \ + include \ + include/xlocale \ + lib/${CSU_DIR} \ + lib/libc \ + lib/libcompiler_rt \ + lib/libedit \ + lib/ncurses/ncursesw \ + + +.include <dirdeps.mk> + +.if ${DEP_RELDIR} == ${_DEP_RELDIR} +# local dependencies - needed for -jN in clean tree +.endif diff --git a/shell_cmds/sh/TOUR b/shell_cmds/sh/TOUR new file mode 100644 index 0000000..82215f3 --- /dev/null +++ b/shell_cmds/sh/TOUR @@ -0,0 +1,301 @@ +# @(#)TOUR 8.1 (Berkeley) 5/31/93 +# $FreeBSD: head/bin/sh/TOUR 317882 2017-05-06 13:28:42Z jilles $ + +NOTE -- This is the original TOUR paper distributed with ash and +does not represent the current state of the shell. It is provided anyway +since it provides helpful information for how the shell is structured, +but be warned that things have changed -- the current shell is +still under development. + +================================================================ + + A Tour through Ash + + Copyright 1989 by Kenneth Almquist. + + +DIRECTORIES: The subdirectory bltin contains commands which can +be compiled stand-alone. The rest of the source is in the main +ash directory. + +SOURCE CODE GENERATORS: Files whose names begin with "mk" are +programs that generate source code. A complete list of these +programs is: + + program input files generates + ------- ----------- --------- + mkbuiltins builtins.def builtins.h builtins.c + mknodes nodetypes nodes.h nodes.c + mksyntax - syntax.h syntax.c + mktokens - token.h + +There are undoubtedly too many of these. + +EXCEPTIONS: Code for dealing with exceptions appears in +exceptions.c. The C language doesn't include exception handling, +so I implement it using setjmp and longjmp. The global variable +exception contains the type of exception. EXERROR is raised by +calling error. EXINT is an interrupt. + +INTERRUPTS: In an interactive shell, an interrupt will cause an +EXINT exception to return to the main command loop. (Exception: +EXINT is not raised if the user traps interrupts using the trap +command.) The INTOFF and INTON macros (defined in exception.h) +provide uninterruptible critical sections. Between the execution +of INTOFF and the execution of INTON, interrupt signals will be +held for later delivery. INTOFF and INTON can be nested. + +MEMALLOC.C: Memalloc.c defines versions of malloc and realloc +which call error when there is no memory left. It also defines a +stack oriented memory allocation scheme. Allocating off a stack +is probably more efficient than allocation using malloc, but the +big advantage is that when an exception occurs all we have to do +to free up the memory in use at the time of the exception is to +restore the stack pointer. The stack is implemented using a +linked list of blocks. + +STPUTC: If the stack were contiguous, it would be easy to store +strings on the stack without knowing in advance how long the +string was going to be: + p = stackptr; + *p++ = c; /* repeated as many times as needed */ + stackptr = p; +The following three macros (defined in memalloc.h) perform these +operations, but grow the stack if you run off the end: + STARTSTACKSTR(p); + STPUTC(c, p); /* repeated as many times as needed */ + grabstackstr(p); + +We now start a top-down look at the code: + +MAIN.C: The main routine performs some initialization, executes +the user's profile if necessary, and calls cmdloop. Cmdloop +repeatedly parses and executes commands. + +OPTIONS.C: This file contains the option processing code. It is +called from main to parse the shell arguments when the shell is +invoked, and it also contains the set builtin. The -i and -m op- +tions (the latter turns on job control) require changes in signal +handling. The routines setjobctl (in jobs.c) and setinteractive +(in trap.c) are called to handle changes to these options. + +PARSING: The parser code is all in parser.c. A recursive des- +cent parser is used. Syntax tables (generated by mksyntax) are +used to classify characters during lexical analysis. There are +four tables: one for normal use, one for use when inside single +quotes and dollar single quotes, one for use when inside double +quotes and one for use in arithmetic. The tables are machine +dependent because they are indexed by character variables and +the range of a char varies from machine to machine. + +PARSE OUTPUT: The output of the parser consists of a tree of +nodes. The various types of nodes are defined in the file node- +types. + +Nodes of type NARG are used to represent both words and the con- +tents of here documents. An early version of ash kept the con- +tents of here documents in temporary files, but keeping here do- +cuments in memory typically results in significantly better per- +formance. It would have been nice to make it an option to use +temporary files for here documents, for the benefit of small +machines, but the code to keep track of when to delete the tem- +porary files was complex and I never fixed all the bugs in it. +(AT&T has been maintaining the Bourne shell for more than ten +years, and to the best of my knowledge they still haven't gotten +it to handle temporary files correctly in obscure cases.) + +The text field of a NARG structure points to the text of the +word. The text consists of ordinary characters and a number of +special codes defined in parser.h. The special codes are: + + CTLVAR Parameter expansion + CTLENDVAR End of parameter expansion + CTLBACKQ Command substitution + CTLBACKQ|CTLQUOTE Command substitution inside double quotes + CTLARI Arithmetic expansion + CTLENDARI End of arithmetic expansion + CTLESC Escape next character + +A variable substitution contains the following elements: + + CTLVAR type name '=' [ alternative-text CTLENDVAR ] + +The type field is a single character specifying the type of sub- +stitution. The possible types are: + + VSNORMAL $var + VSMINUS ${var-text} + VSMINUS|VSNUL ${var:-text} + VSPLUS ${var+text} + VSPLUS|VSNUL ${var:+text} + VSQUESTION ${var?text} + VSQUESTION|VSNUL ${var:?text} + VSASSIGN ${var=text} + VSASSIGN|VSNUL ${var:=text} + VSTRIMLEFT ${var#text} + VSTRIMLEFTMAX ${var##text} + VSTRIMRIGHT ${var%text} + VSTRIMRIGHTMAX ${var%%text} + VSLENGTH ${#var} + VSERROR delayed error + +In addition, the type field will have the VSQUOTE flag set if the +variable is enclosed in double quotes and the VSLINENO flag if +LINENO is being expanded (the parameter name is the decimal line +number). The parameter's name comes next, terminated by an equals +sign. If the type is not VSNORMAL (including when it is VSLENGTH), +then the text field in the substitution follows, terminated by a +CTLENDVAR byte. + +The type VSERROR is used to allow parsing bad substitutions like +${var[7]} and generate an error when they are expanded. + +Commands in back quotes are parsed and stored in a linked list. +The locations of these commands in the string are indicated by +CTLBACKQ and CTLBACKQ+CTLQUOTE characters, depending upon whether +the back quotes were enclosed in double quotes. + +Arithmetic expansion starts with CTLARI and ends with CTLENDARI. + +The character CTLESC escapes the next character, so that in case +any of the CTL characters mentioned above appear in the input, +they can be passed through transparently. CTLESC is also used to +escape '*', '?', '[', and '!' characters which were quoted by the +user and thus should not be used for file name generation. + +CTLESC characters have proved to be particularly tricky to get +right. In the case of here documents which are not subject to +variable and command substitution, the parser doesn't insert any +CTLESC characters to begin with (so the contents of the text +field can be written without any processing). Other here docu- +ments, and words which are not subject to file name generation, +have the CTLESC characters removed during the variable and command +substitution phase. Words which are subject to file name +generation have the CTLESC characters removed as part of the file +name phase. + +EXECUTION: Command execution is handled by the following files: + eval.c The top level routines. + redir.c Code to handle redirection of input and output. + jobs.c Code to handle forking, waiting, and job control. + exec.c Code to do path searches and the actual exec sys call. + expand.c Code to evaluate arguments. + var.c Maintains the variable symbol table. Called from expand.c. + +EVAL.C: Evaltree recursively executes a parse tree. The exit +status is returned in the global variable exitstatus. The alter- +native entry evalbackcmd is called to evaluate commands in back +quotes. It saves the result in memory if the command is a buil- +tin; otherwise it forks off a child to execute the command and +connects the standard output of the child to a pipe. + +JOBS.C: To create a process, you call makejob to return a job +structure, and then call forkshell (passing the job structure as +an argument) to create the process. Waitforjob waits for a job +to complete. These routines take care of process groups if job +control is defined. + +REDIR.C: Ash allows file descriptors to be redirected and then +restored without forking off a child process. This is accom- +plished by duplicating the original file descriptors. The redir- +tab structure records where the file descriptors have been dupli- +cated to. + +EXEC.C: The routine find_command locates a command, and enters +the command in the hash table if it is not already there. The +third argument specifies whether it is to print an error message +if the command is not found. (When a pipeline is set up, +find_command is called for all the commands in the pipeline be- +fore any forking is done, so to get the commands into the hash +table of the parent process. But to make command hashing as +transparent as possible, we silently ignore errors at that point +and only print error messages if the command cannot be found +later.) + +The routine shellexec is the interface to the exec system call. + +EXPAND.C: As the routine argstr generates words by parameter +expansion, command substitution and arithmetic expansion, it +performs word splitting on the result. As each word is output, +the routine expandmeta performs file name generation (if enabled). + +VAR.C: Variables are stored in a hash table. Probably we should +switch to extensible hashing. The variable name is stored in the +same string as the value (using the format "name=value") so that +no string copying is needed to create the environment of a com- +mand. Variables which the shell references internally are preal- +located so that the shell can reference the values of these vari- +ables without doing a lookup. + +When a program is run, the code in eval.c sticks any environment +variables which precede the command (as in "PATH=xxx command") in +the variable table as the simplest way to strip duplicates, and +then calls "environment" to get the value of the environment. + +BUILTIN COMMANDS: The procedures for handling these are scat- +tered throughout the code, depending on which location appears +most appropriate. They can be recognized because their names al- +ways end in "cmd". The mapping from names to procedures is +specified in the file builtins.def, which is processed by the +mkbuiltins command. + +A builtin command is invoked with argc and argv set up like a +normal program. A builtin command is allowed to overwrite its +arguments. Builtin routines can call nextopt to do option pars- +ing. This is kind of like getopt, but you don't pass argc and +argv to it. Builtin routines can also call error. This routine +normally terminates the shell (or returns to the main command +loop if the shell is interactive), but when called from a non- +special builtin command it causes the builtin command to +terminate with an exit status of 2. + +The directory bltins contains commands which can be compiled in- +dependently but can also be built into the shell for efficiency +reasons. The header file bltin.h takes care of most of the +differences between the ash and the stand-alone environment. +The user should call the main routine "main", and #define main to +be the name of the routine to use when the program is linked into +ash. This #define should appear before bltin.h is included; +bltin.h will #undef main if the program is to be compiled +stand-alone. A similar approach is used for a few utilities from +bin and usr.bin. + +CD.C: This file defines the cd and pwd builtins. + +SIGNALS: Trap.c implements the trap command. The routine set- +signal figures out what action should be taken when a signal is +received and invokes the signal system call to set the signal ac- +tion appropriately. When a signal that a user has set a trap for +is caught, the routine "onsig" sets a flag. The routine dotrap +is called at appropriate points to actually handle the signal. +When an interrupt is caught and no trap has been set for that +signal, the routine "onint" in error.c is called. + +OUTPUT: Ash uses its own output routines. There are three out- +put structures allocated. "Output" represents the standard out- +put, "errout" the standard error, and "memout" contains output +which is to be stored in memory. This last is used when a buil- +tin command appears in backquotes, to allow its output to be col- +lected without doing any I/O through the UNIX operating system. +The variables out1 and out2 normally point to output and errout, +respectively, but they are set to point to memout when appropri- +ate inside backquotes. + +INPUT: The basic input routine is pgetc, which reads from the +current input file. There is a stack of input files; the current +input file is the top file on this stack. The code allows the +input to come from a string rather than a file. (This is for the +-c option and the "." and eval builtin commands.) The global +variable plinno is saved and restored when files are pushed and +popped from the stack. The parser routines store the number of +the current line in this variable. + +DEBUGGING: If DEBUG is defined in shell.h, then the shell will +write debugging information to the file $HOME/trace. Most of +this is done using the TRACE macro, which takes a set of printf +arguments inside two sets of parenthesis. Example: +"TRACE(("n=%d0, n))". The double parenthesis are necessary be- +cause the preprocessor can't handle functions with a variable +number of arguments. Defining DEBUG also causes the shell to +generate a core dump if it is sent a quit signal. The tracing +code is in show.c. diff --git a/shell_cmds/sh/alias.c b/shell_cmds/sh/alias.c new file mode 100644 index 0000000..a8fe82c --- /dev/null +++ b/shell_cmds/sh/alias.c @@ -0,0 +1,256 @@ +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)alias.c 8.3 (Berkeley) 5/4/95"; +#endif +#endif /* not lint */ +#include <sys/cdefs.h> +__FBSDID("$FreeBSD: head/bin/sh/alias.c 317039 2017-04-16 22:10:02Z jilles $"); + +#include <stdlib.h> +#include "shell.h" +#include "output.h" +#include "error.h" +#include "memalloc.h" +#include "mystring.h" +#include "alias.h" +#include "options.h" +#include "builtins.h" + +#define ATABSIZE 39 + +static struct alias *atab[ATABSIZE]; +static int aliases; + +static void setalias(const char *, const char *); +static int unalias(const char *); +static struct alias **hashalias(const char *); + +static +void +setalias(const char *name, const char *val) +{ + struct alias *ap, **app; + + unalias(name); + app = hashalias(name); + INTOFF; + ap = ckmalloc(sizeof (struct alias)); + ap->name = savestr(name); + ap->val = savestr(val); + ap->flag = 0; + ap->next = *app; + *app = ap; + aliases++; + INTON; +} + +static void +freealias(struct alias *ap) +{ + ckfree(ap->name); + ckfree(ap->val); + ckfree(ap); +} + +static int +unalias(const char *name) +{ + struct alias *ap, **app; + + app = hashalias(name); + + for (ap = *app; ap; app = &(ap->next), ap = ap->next) { + if (equal(name, ap->name)) { + /* + * if the alias is currently in use (i.e. its + * buffer is being used by the input routine) we + * just null out the name instead of freeing it. + * We could clear it out later, but this situation + * is so rare that it hardly seems worth it. + */ + if (ap->flag & ALIASINUSE) + *ap->name = '\0'; + else { + INTOFF; + *app = ap->next; + freealias(ap); + INTON; + } + aliases--; + return (0); + } + } + + return (1); +} + +static void +rmaliases(void) +{ + struct alias *ap, **app; + int i; + + INTOFF; + for (i = 0; i < ATABSIZE; i++) { + app = &atab[i]; + while (*app) { + ap = *app; + if (ap->flag & ALIASINUSE) { + *ap->name = '\0'; + app = &(*app)->next; + } else { + *app = ap->next; + freealias(ap); + } + } + } + aliases = 0; + INTON; +} + +struct alias * +lookupalias(const char *name, int check) +{ + struct alias *ap; + + if (aliases == 0) + return (NULL); + for (ap = *hashalias(name); ap; ap = ap->next) { + if (equal(name, ap->name)) { + if (check && (ap->flag & ALIASINUSE)) + return (NULL); + return (ap); + } + } + + return (NULL); +} + +static int +comparealiases(const void *p1, const void *p2) +{ + const struct alias *const *a1 = p1; + const struct alias *const *a2 = p2; + + return strcmp((*a1)->name, (*a2)->name); +} + +static void +printalias(const struct alias *a) +{ + out1fmt("%s=", a->name); + out1qstr(a->val); + out1c('\n'); +} + +static void +printaliases(void) +{ + int i, j; + struct alias **sorted, *ap; + + INTOFF; + sorted = ckmalloc(aliases * sizeof(*sorted)); + j = 0; + for (i = 0; i < ATABSIZE; i++) + for (ap = atab[i]; ap; ap = ap->next) + if (*ap->name != '\0') + sorted[j++] = ap; + qsort(sorted, aliases, sizeof(*sorted), comparealiases); + for (i = 0; i < aliases; i++) { + printalias(sorted[i]); + if (int_pending()) + break; + } + ckfree(sorted); + INTON; +} + +int +aliascmd(int argc __unused, char **argv __unused) +{ + char *n, *v; + int ret = 0; + struct alias *ap; + + nextopt(""); + + if (*argptr == NULL) { + printaliases(); + return (0); + } + while ((n = *argptr++) != NULL) { + if ((v = strchr(n+1, '=')) == NULL) /* n+1: funny ksh stuff */ + if ((ap = lookupalias(n, 0)) == NULL) { + warning("%s: not found", n); + ret = 1; + } else + printalias(ap); + else { + *v++ = '\0'; + setalias(n, v); + } + } + + return (ret); +} + +int +unaliascmd(int argc __unused, char **argv __unused) +{ + int i; + + while ((i = nextopt("a")) != '\0') { + if (i == 'a') { + rmaliases(); + return (0); + } + } + for (i = 0; *argptr; argptr++) + i |= unalias(*argptr); + + return (i); +} + +static struct alias ** +hashalias(const char *p) +{ + unsigned int hashval; + + hashval = (unsigned char)*p << 4; + while (*p) + hashval+= *p++; + return &atab[hashval % ATABSIZE]; +} diff --git a/shell_cmds/sh/alias.h b/shell_cmds/sh/alias.h new file mode 100644 index 0000000..e08a9e8 --- /dev/null +++ b/shell_cmds/sh/alias.h @@ -0,0 +1,45 @@ +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)alias.h 8.2 (Berkeley) 5/4/95 + * $FreeBSD: head/bin/sh/alias.h 314436 2017-02-28 23:42:47Z imp $ + */ + +#define ALIASINUSE 1 + +struct alias { + struct alias *next; + char *name; + char *val; + int flag; +}; + +struct alias *lookupalias(const char *, int); diff --git a/shell_cmds/sh/arith.h b/shell_cmds/sh/arith.h new file mode 100644 index 0000000..9c2ef39 --- /dev/null +++ b/shell_cmds/sh/arith.h @@ -0,0 +1,37 @@ +/*- + * Copyright (c) 1995 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)arith.h 1.1 (Berkeley) 5/4/95 + * $FreeBSD: head/bin/sh/arith.h 315511 2017-03-18 20:41:07Z jilles $ + */ + +#include "shell.h" + +#define DIGITS(var) (3 + (2 + CHAR_BIT * sizeof((var))) / 3) + +arith_t arith(const char *); diff --git a/shell_cmds/sh/arith_yacc.c b/shell_cmds/sh/arith_yacc.c new file mode 100644 index 0000000..20e1e43 --- /dev/null +++ b/shell_cmds/sh/arith_yacc.c @@ -0,0 +1,381 @@ +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * Copyright (c) 2007 + * Herbert Xu <herbert@gondor.apana.org.au>. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD: head/bin/sh/arith_yacc.c 270246 2014-08-20 20:15:43Z jilles $"); + +#include <limits.h> +#include <errno.h> +#include <inttypes.h> +#include <stdlib.h> +#include <stdio.h> +#include "arith.h" +#include "arith_yacc.h" +#include "expand.h" +#include "shell.h" +#include "error.h" +#include "memalloc.h" +#include "output.h" +#include "options.h" +#include "var.h" + +#if ARITH_BOR + 11 != ARITH_BORASS || ARITH_ASS + 11 != ARITH_EQ +#error Arithmetic tokens are out of order. +#endif + +static const char *arith_startbuf; + +const char *arith_buf; +union yystype yylval; + +static int last_token; + +#define ARITH_PRECEDENCE(op, prec) [op - ARITH_BINOP_MIN] = prec + +static const char prec[ARITH_BINOP_MAX - ARITH_BINOP_MIN] = { + ARITH_PRECEDENCE(ARITH_MUL, 0), + ARITH_PRECEDENCE(ARITH_DIV, 0), + ARITH_PRECEDENCE(ARITH_REM, 0), + ARITH_PRECEDENCE(ARITH_ADD, 1), + ARITH_PRECEDENCE(ARITH_SUB, 1), + ARITH_PRECEDENCE(ARITH_LSHIFT, 2), + ARITH_PRECEDENCE(ARITH_RSHIFT, 2), + ARITH_PRECEDENCE(ARITH_LT, 3), + ARITH_PRECEDENCE(ARITH_LE, 3), + ARITH_PRECEDENCE(ARITH_GT, 3), + ARITH_PRECEDENCE(ARITH_GE, 3), + ARITH_PRECEDENCE(ARITH_EQ, 4), + ARITH_PRECEDENCE(ARITH_NE, 4), + ARITH_PRECEDENCE(ARITH_BAND, 5), + ARITH_PRECEDENCE(ARITH_BXOR, 6), + ARITH_PRECEDENCE(ARITH_BOR, 7), +}; + +#define ARITH_MAX_PREC 8 + +int letcmd(int, char **); + +static __dead2 void yyerror(const char *s) +{ + error("arithmetic expression: %s: \"%s\"", s, arith_startbuf); + /* NOTREACHED */ +} + +static arith_t arith_lookupvarint(char *varname) +{ + const char *str; + char *p; + arith_t result; + + str = lookupvar(varname); + if (uflag && str == NULL) + yyerror("variable not set"); + if (str == NULL || *str == '\0') + str = "0"; + errno = 0; + result = strtoarith_t(str, &p, 0); + if (errno != 0 || *p != '\0') + yyerror("variable conversion error"); + return result; +} + +static inline int arith_prec(int op) +{ + return prec[op - ARITH_BINOP_MIN]; +} + +static inline int higher_prec(int op1, int op2) +{ + return arith_prec(op1) < arith_prec(op2); +} + +static arith_t do_binop(int op, arith_t a, arith_t b) +{ + + switch (op) { + default: + case ARITH_REM: + case ARITH_DIV: + if (!b) + yyerror("division by zero"); + if (a == ARITH_MIN && b == -1) + yyerror("divide error"); + return op == ARITH_REM ? a % b : a / b; + case ARITH_MUL: + return (uintmax_t)a * (uintmax_t)b; + case ARITH_ADD: + return (uintmax_t)a + (uintmax_t)b; + case ARITH_SUB: + return (uintmax_t)a - (uintmax_t)b; + case ARITH_LSHIFT: + return (uintmax_t)a << (b & (sizeof(uintmax_t) * CHAR_BIT - 1)); + case ARITH_RSHIFT: + return a >> (b & (sizeof(uintmax_t) * CHAR_BIT - 1)); + case ARITH_LT: + return a < b; + case ARITH_LE: + return a <= b; + case ARITH_GT: + return a > b; + case ARITH_GE: + return a >= b; + case ARITH_EQ: + return a == b; + case ARITH_NE: + return a != b; + case ARITH_BAND: + return a & b; + case ARITH_BXOR: + return a ^ b; + case ARITH_BOR: + return a | b; + } +} + +static arith_t assignment(int var, int noeval); + +static arith_t primary(int token, union yystype *val, int op, int noeval) +{ + arith_t result; + +again: + switch (token) { + case ARITH_LPAREN: + result = assignment(op, noeval); + if (last_token != ARITH_RPAREN) + yyerror("expecting ')'"); + last_token = yylex(); + return result; + case ARITH_NUM: + last_token = op; + return val->val; + case ARITH_VAR: + last_token = op; + return noeval ? val->val : arith_lookupvarint(val->name); + case ARITH_ADD: + token = op; + *val = yylval; + op = yylex(); + goto again; + case ARITH_SUB: + *val = yylval; + return -primary(op, val, yylex(), noeval); + case ARITH_NOT: + *val = yylval; + return !primary(op, val, yylex(), noeval); + case ARITH_BNOT: + *val = yylval; + return ~primary(op, val, yylex(), noeval); + default: + yyerror("expecting primary"); + } +} + +static arith_t binop2(arith_t a, int op, int precedence, int noeval) +{ + for (;;) { + union yystype val; + arith_t b; + int op2; + int token; + + token = yylex(); + val = yylval; + + b = primary(token, &val, yylex(), noeval); + + op2 = last_token; + if (op2 >= ARITH_BINOP_MIN && op2 < ARITH_BINOP_MAX && + higher_prec(op2, op)) { + b = binop2(b, op2, arith_prec(op), noeval); + op2 = last_token; + } + + a = noeval ? b : do_binop(op, a, b); + + if (op2 < ARITH_BINOP_MIN || op2 >= ARITH_BINOP_MAX || + arith_prec(op2) >= precedence) + return a; + + op = op2; + } +} + +static arith_t binop(int token, union yystype *val, int op, int noeval) +{ + arith_t a = primary(token, val, op, noeval); + + op = last_token; + if (op < ARITH_BINOP_MIN || op >= ARITH_BINOP_MAX) + return a; + + return binop2(a, op, ARITH_MAX_PREC, noeval); +} + +static arith_t and(int token, union yystype *val, int op, int noeval) +{ + arith_t a = binop(token, val, op, noeval); + arith_t b; + + op = last_token; + if (op != ARITH_AND) + return a; + + token = yylex(); + *val = yylval; + + b = and(token, val, yylex(), noeval | !a); + + return a && b; +} + +static arith_t or(int token, union yystype *val, int op, int noeval) +{ + arith_t a = and(token, val, op, noeval); + arith_t b; + + op = last_token; + if (op != ARITH_OR) + return a; + + token = yylex(); + *val = yylval; + + b = or(token, val, yylex(), noeval | !!a); + + return a || b; +} + +static arith_t cond(int token, union yystype *val, int op, int noeval) +{ + arith_t a = or(token, val, op, noeval); + arith_t b; + arith_t c; + + if (last_token != ARITH_QMARK) + return a; + + b = assignment(yylex(), noeval | !a); + + if (last_token != ARITH_COLON) + yyerror("expecting ':'"); + + token = yylex(); + *val = yylval; + + c = cond(token, val, yylex(), noeval | !!a); + + return a ? b : c; +} + +static arith_t assignment(int var, int noeval) +{ + union yystype val = yylval; + int op = yylex(); + arith_t result; + char sresult[DIGITS(result) + 1]; + + if (var != ARITH_VAR) + return cond(var, &val, op, noeval); + + if (op != ARITH_ASS && (op < ARITH_ASS_MIN || op >= ARITH_ASS_MAX)) + return cond(var, &val, op, noeval); + + result = assignment(yylex(), noeval); + if (noeval) + return result; + + if (op != ARITH_ASS) + result = do_binop(op - 11, arith_lookupvarint(val.name), result); + snprintf(sresult, sizeof(sresult), ARITH_FORMAT_STR, result); + setvar(val.name, sresult, 0); + return result; +} + +arith_t arith(const char *s) +{ + struct stackmark smark; + arith_t result; + + setstackmark(&smark); + + arith_buf = arith_startbuf = s; + + result = assignment(yylex(), 0); + + if (last_token) + yyerror("expecting EOF"); + + popstackmark(&smark); + + return result; +} + +/* + * The exp(1) builtin. + */ +int +letcmd(int argc, char **argv) +{ + const char *p; + char *concat; + char **ap; + arith_t i; + + if (argc > 1) { + p = argv[1]; + if (argc > 2) { + /* + * Concatenate arguments. + */ + STARTSTACKSTR(concat); + ap = argv + 2; + for (;;) { + while (*p) + STPUTC(*p++, concat); + if ((p = *ap++) == NULL) + break; + STPUTC(' ', concat); + } + STPUTC('\0', concat); + p = grabstackstr(concat); + } + } else + p = ""; + + i = arith(p); + + out1fmt(ARITH_FORMAT_STR "\n", i); + return !i; +} diff --git a/shell_cmds/sh/arith_yacc.h b/shell_cmds/sh/arith_yacc.h new file mode 100644 index 0000000..d386886 --- /dev/null +++ b/shell_cmds/sh/arith_yacc.h @@ -0,0 +1,93 @@ +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * Copyright (c) 2007 + * Herbert Xu <herbert@gondor.apana.org.au>. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD: head/bin/sh/arith_yacc.h 279503 2015-03-01 21:46:55Z jilles $ + */ + +#define ARITH_ASS 1 + +#define ARITH_OR 2 +#define ARITH_AND 3 +#define ARITH_BAD 4 +#define ARITH_NUM 5 +#define ARITH_VAR 6 +#define ARITH_NOT 7 + +#define ARITH_BINOP_MIN 8 +#define ARITH_LE 8 +#define ARITH_GE 9 +#define ARITH_LT 10 +#define ARITH_GT 11 +#define ARITH_EQ 12 +#define ARITH_REM 13 +#define ARITH_BAND 14 +#define ARITH_LSHIFT 15 +#define ARITH_RSHIFT 16 +#define ARITH_MUL 17 +#define ARITH_ADD 18 +#define ARITH_BOR 19 +#define ARITH_SUB 20 +#define ARITH_BXOR 21 +#define ARITH_DIV 22 +#define ARITH_NE 23 +#define ARITH_BINOP_MAX 24 + +#define ARITH_ASS_MIN 24 +#define ARITH_REMASS 24 +#define ARITH_BANDASS 25 +#define ARITH_LSHIFTASS 26 +#define ARITH_RSHIFTASS 27 +#define ARITH_MULASS 28 +#define ARITH_ADDASS 29 +#define ARITH_BORASS 30 +#define ARITH_SUBASS 31 +#define ARITH_BXORASS 32 +#define ARITH_DIVASS 33 +#define ARITH_ASS_MAX 34 + +#define ARITH_LPAREN 34 +#define ARITH_RPAREN 35 +#define ARITH_BNOT 36 +#define ARITH_QMARK 37 +#define ARITH_COLON 38 + +extern const char *arith_buf; + +union yystype { + arith_t val; + char *name; +}; + +extern union yystype yylval; + +int yylex(void); diff --git a/shell_cmds/sh/arith_yylex.c b/shell_cmds/sh/arith_yylex.c new file mode 100644 index 0000000..9936852 --- /dev/null +++ b/shell_cmds/sh/arith_yylex.c @@ -0,0 +1,248 @@ +/*- + * Copyright (c) 2002 + * Herbert Xu. + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD: head/bin/sh/arith_yylex.c 279503 2015-03-01 21:46:55Z jilles $"); + +#include <inttypes.h> +#include <stdlib.h> +#include <string.h> +#include "shell.h" +#include "arith_yacc.h" +#include "expand.h" +#include "error.h" +#include "memalloc.h" +#include "parser.h" +#include "syntax.h" + +#if ARITH_BOR + 11 != ARITH_BORASS || ARITH_ASS + 11 != ARITH_EQ +#error Arithmetic tokens are out of order. +#endif + +int +yylex(void) +{ + int value; + const char *buf = arith_buf; + char *end; + const char *p; + + for (;;) { + value = *buf; + switch (value) { + case ' ': + case '\t': + case '\n': + buf++; + continue; + default: + return ARITH_BAD; + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + yylval.val = strtoarith_t(buf, &end, 0); + arith_buf = end; + return ARITH_NUM; + case 'A': + case 'B': + case 'C': + case 'D': + case 'E': + case 'F': + case 'G': + case 'H': + case 'I': + case 'J': + case 'K': + case 'L': + case 'M': + case 'N': + case 'O': + case 'P': + case 'Q': + case 'R': + case 'S': + case 'T': + case 'U': + case 'V': + case 'W': + case 'X': + case 'Y': + case 'Z': + case '_': + case 'a': + case 'b': + case 'c': + case 'd': + case 'e': + case 'f': + case 'g': + case 'h': + case 'i': + case 'j': + case 'k': + case 'l': + case 'm': + case 'n': + case 'o': + case 'p': + case 'q': + case 'r': + case 's': + case 't': + case 'u': + case 'v': + case 'w': + case 'x': + case 'y': + case 'z': + p = buf; + while (buf++, is_in_name(*buf)) + ; + yylval.name = stalloc(buf - p + 1); + memcpy(yylval.name, p, buf - p); + yylval.name[buf - p] = '\0'; + value = ARITH_VAR; + goto out; + case '=': + value += ARITH_ASS - '='; +checkeq: + buf++; +checkeqcur: + if (*buf != '=') + goto out; + value += 11; + break; + case '>': + switch (*++buf) { + case '=': + value += ARITH_GE - '>'; + break; + case '>': + value += ARITH_RSHIFT - '>'; + goto checkeq; + default: + value += ARITH_GT - '>'; + goto out; + } + break; + case '<': + switch (*++buf) { + case '=': + value += ARITH_LE - '<'; + break; + case '<': + value += ARITH_LSHIFT - '<'; + goto checkeq; + default: + value += ARITH_LT - '<'; + goto out; + } + break; + case '|': + if (*++buf != '|') { + value += ARITH_BOR - '|'; + goto checkeqcur; + } + value += ARITH_OR - '|'; + break; + case '&': + if (*++buf != '&') { + value += ARITH_BAND - '&'; + goto checkeqcur; + } + value += ARITH_AND - '&'; + break; + case '!': + if (*++buf != '=') { + value += ARITH_NOT - '!'; + goto out; + } + value += ARITH_NE - '!'; + break; + case 0: + goto out; + case '(': + value += ARITH_LPAREN - '('; + break; + case ')': + value += ARITH_RPAREN - ')'; + break; + case '*': + value += ARITH_MUL - '*'; + goto checkeq; + case '/': + value += ARITH_DIV - '/'; + goto checkeq; + case '%': + value += ARITH_REM - '%'; + goto checkeq; + case '+': + if (buf[1] == '+') + return ARITH_BAD; + value += ARITH_ADD - '+'; + goto checkeq; + case '-': + if (buf[1] == '-') + return ARITH_BAD; + value += ARITH_SUB - '-'; + goto checkeq; + case '~': + value += ARITH_BNOT - '~'; + break; + case '^': + value += ARITH_BXOR - '^'; + goto checkeq; + case '?': + value += ARITH_QMARK - '?'; + break; + case ':': + value += ARITH_COLON - ':'; + break; + } + break; + } + + buf++; +out: + arith_buf = buf; + return value; +} diff --git a/shell_cmds/sh/bltin/bltin.h b/shell_cmds/sh/bltin/bltin.h new file mode 100644 index 0000000..b8c2087 --- /dev/null +++ b/shell_cmds/sh/bltin/bltin.h @@ -0,0 +1,81 @@ +/*- + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)bltin.h 8.2 (Berkeley) 5/4/95 + * $FreeBSD: head/bin/sh/bltin/bltin.h 326025 2017-11-20 19:49:47Z pfg $ + */ + +/* + * This file is included by programs which are optionally built into the + * shell. If SHELL is defined, we try to map the standard UNIX library + * routines to ash routines using defines. + */ + +#include "../shell.h" +#include "../mystring.h" +#ifdef SHELL +#include "../error.h" +#include "../output.h" +#include "builtins.h" +#define FILE struct output +#undef stdout +#define stdout out1 +#undef stderr +#define stderr out2 +#define printf out1fmt +#undef putc +#define putc(c, file) outc(c, file) +#undef putchar +#define putchar(c) out1c(c) +#define fprintf outfmt +#define fputs outstr +#define fwrite(ptr, size, nmemb, file) outbin(ptr, (size) * (nmemb), file) +#define fflush flushout +#define INITARGS(argv) +#define warnx warning +#define warn(fmt, ...) warning(fmt ": %s", __VA_ARGS__, strerror(errno)) +#define errx(exitstatus, ...) error(__VA_ARGS__) + +#else +#undef NULL +#include <stdio.h> +#undef main +#define INITARGS(argv) if ((commandname = argv[0]) == NULL) {fputs("Argc is zero\n", stderr); exit(2);} else +#endif + +#include <unistd.h> + +pointer stalloc(int); +int killjob(const char *, int); + +extern char *commandname; diff --git a/shell_cmds/sh/bltin/echo.c b/shell_cmds/sh/bltin/echo.c new file mode 100644 index 0000000..1bb64c6 --- /dev/null +++ b/shell_cmds/sh/bltin/echo.c @@ -0,0 +1,109 @@ +/*- + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)echo.c 8.2 (Berkeley) 5/4/95 + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD: head/bin/sh/bltin/echo.c 326025 2017-11-20 19:49:47Z pfg $"); + +/* + * Echo command. + */ + +#define main echocmd + +#include "bltin.h" + +/* #define eflag 1 */ + +int +main(int argc, char *argv[]) +{ + char **ap; + char *p; + char c; + int count; + int nflag = 0; +#ifndef eflag + int eflag = 0; +#endif + + ap = argv; + if (argc) + ap++; + if ((p = *ap) != NULL) { + if (equal(p, "-n")) { + nflag++; + ap++; + } else if (equal(p, "-e")) { +#ifndef eflag + eflag++; +#endif + ap++; + } + } + while ((p = *ap++) != NULL) { + while ((c = *p++) != '\0') { + if (c == '\\' && eflag) { + switch (*p++) { + case 'a': c = '\a'; break; + case 'b': c = '\b'; break; + case 'c': return 0; /* exit */ + case 'e': c = '\033'; break; + case 'f': c = '\f'; break; + case 'n': c = '\n'; break; + case 'r': c = '\r'; break; + case 't': c = '\t'; break; + case 'v': c = '\v'; break; + case '\\': break; /* c = '\\' */ + case '0': + c = 0; + count = 3; + while (--count >= 0 && (unsigned)(*p - '0') < 8) + c = (c << 3) + (*p++ - '0'); + break; + default: + p--; + break; + } + } + putchar(c); + } + if (*ap) + putchar(' '); + } + if (! nflag) + putchar('\n'); + return 0; +} diff --git a/shell_cmds/sh/builtins.def b/shell_cmds/sh/builtins.def new file mode 100644 index 0000000..39cab13 --- /dev/null +++ b/shell_cmds/sh/builtins.def @@ -0,0 +1,96 @@ +#!/bin/sh - + +#- +# Copyright (c) 1991, 1993 +# The Regents of the University of California. All rights reserved. +# +# This code is derived from software contributed to Berkeley by +# Kenneth Almquist. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# @(#)builtins.def 8.4 (Berkeley) 5/4/95 +# $FreeBSD: head/bin/sh/builtins.def 319576 2017-06-04 21:02:48Z bdrewery $ + +# +# This file lists all the builtin commands. The first column is the name +# of a C routine. +# The -j flag specifies that this command is to be excluded from systems +# without job control. +# The -h flag specifies that this command is to be excluded from systems +# based on the NO_HISTORY compile-time symbol. +# The -n flag specifies that this command can safely be run in the same +# process when it is the only command in a command substitution. Some +# commands have special logic defined in safe_builtin(). +# The -s flag specifies that this is a POSIX 'special built-in' command. +# The rest of the line specifies the command name or names used to run the +# command. The entry for bltincmd, which is run when the user does not specify +# a command, must come first. +# +# NOTE: bltincmd must come first! + +bltincmd -n builtin +aliascmd alias +bgcmd -j bg +bindcmd bind +breakcmd -s break -s continue +cdcmd cd chdir +commandcmd -n command +dotcmd -s . +echocmd -n echo +evalcmd -s eval +execcmd -s exec +exitcmd -s exit +letcmd let +exportcmd -s export -s readonly +#exprcmd expr +falsecmd -n false +fgcmd -j fg +freebsd_wordexpcmd freebsd_wordexp +getoptscmd getopts +hashcmd hash +histcmd -h fc +jobidcmd -n jobid +jobscmd -n jobs +killcmd -n kill +localcmd local +printfcmd -n printf +pwdcmd -n pwd +readcmd read +returncmd -s return +setcmd -s set +setvarcmd setvar +shiftcmd -s shift +testcmd -n test [ +timescmd -n -s times +trapcmd -s trap +truecmd -n -s : true +typecmd -n type +ulimitcmd ulimit +umaskcmd umask +unaliascmd unalias +unsetcmd -s unset +waitcmd wait +wordexpcmd wordexp diff --git a/shell_cmds/sh/cd.c b/shell_cmds/sh/cd.c new file mode 100644 index 0000000..cf63681 --- /dev/null +++ b/shell_cmds/sh/cd.c @@ -0,0 +1,430 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)cd.c 8.2 (Berkeley) 5/4/95"; +#endif +#endif /* not lint */ +#include <sys/cdefs.h> +__FBSDID("$FreeBSD: head/bin/sh/cd.c 320340 2017-06-25 21:53:08Z jilles $"); + +#include <sys/types.h> +#include <sys/stat.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <errno.h> +#include <limits.h> + +/* + * The cd and pwd commands. + */ + +#include "shell.h" +#include "var.h" +#include "nodes.h" /* for jobs.h */ +#include "jobs.h" +#include "options.h" +#include "output.h" +#include "memalloc.h" +#include "error.h" +#include "exec.h" +#include "redir.h" +#include "mystring.h" +#include "show.h" +#include "cd.h" +#include "builtins.h" + +static int cdlogical(char *); +static int cdphysical(char *); +static int docd(char *, int, int); +static char *getcomponent(char **); +static char *findcwd(char *); +static void updatepwd(char *); +static char *getpwd(void); +static char *getpwd2(void); + +static char *curdir = NULL; /* current working directory */ + +int +cdcmd(int argc __unused, char **argv __unused) +{ + const char *dest; + const char *path; + char *p; + struct stat statb; + int ch, phys, print = 0, getcwderr = 0; + int rc; + int errno1 = ENOENT; + + phys = Pflag; + while ((ch = nextopt("eLP")) != '\0') { + switch (ch) { + case 'e': + getcwderr = 1; + break; + case 'L': + phys = 0; + break; + case 'P': + phys = 1; + break; + } + } + + if (*argptr != NULL && argptr[1] != NULL) + error("too many arguments"); + + if ((dest = *argptr) == NULL && (dest = bltinlookup("HOME", 1)) == NULL) + error("HOME not set"); + if (*dest == '\0') + dest = "."; + if (dest[0] == '-' && dest[1] == '\0') { + dest = bltinlookup("OLDPWD", 1); + if (dest == NULL) + error("OLDPWD not set"); + print = 1; + } + if (dest[0] == '/' || + (dest[0] == '.' && (dest[1] == '/' || dest[1] == '\0')) || + (dest[0] == '.' && dest[1] == '.' && (dest[2] == '/' || dest[2] == '\0')) || + (path = bltinlookup("CDPATH", 1)) == NULL) + path = ""; + while ((p = padvance(&path, dest)) != NULL) { + if (stat(p, &statb) < 0) { + if (errno != ENOENT) + errno1 = errno; + } else if (!S_ISDIR(statb.st_mode)) + errno1 = ENOTDIR; + else { + if (!print) { + /* + * XXX - rethink + */ + if (p[0] == '.' && p[1] == '/' && p[2] != '\0') + print = strcmp(p + 2, dest); + else + print = strcmp(p, dest); + } + rc = docd(p, print, phys); + if (rc >= 0) + return getcwderr ? rc : 0; + if (errno != ENOENT) + errno1 = errno; + } + } + error("%s: %s", dest, strerror(errno1)); + /*NOTREACHED*/ + return 0; +} + + +/* + * Actually change the directory. In an interactive shell, print the + * directory name if "print" is nonzero. + */ +static int +docd(char *dest, int print, int phys) +{ + int rc; + + TRACE(("docd(\"%s\", %d, %d) called\n", dest, print, phys)); + + /* If logical cd fails, fall back to physical. */ + if ((phys || (rc = cdlogical(dest)) < 0) && (rc = cdphysical(dest)) < 0) + return (-1); + + if (print && iflag && curdir) { + out1fmt("%s\n", curdir); + /* + * Ignore write errors to preserve the invariant that the + * current directory is changed iff the exit status is 0 + * (or 1 if -e was given and the full pathname could not be + * determined). + */ + flushout(out1); + outclearerror(out1); + } + + return (rc); +} + +static int +cdlogical(char *dest) +{ + char *p; + char *q; + char *component; + char *path; + struct stat statb; + int first; + int badstat; + + /* + * Check each component of the path. If we find a symlink or + * something we can't stat, clear curdir to force a getcwd() + * next time we get the value of the current directory. + */ + badstat = 0; + path = stsavestr(dest); + STARTSTACKSTR(p); + if (*dest == '/') { + STPUTC('/', p); + path++; + } + first = 1; + while ((q = getcomponent(&path)) != NULL) { + if (q[0] == '\0' || (q[0] == '.' && q[1] == '\0')) + continue; + if (! first) + STPUTC('/', p); + first = 0; + component = q; + STPUTS(q, p); + if (equal(component, "..")) + continue; + STACKSTRNUL(p); + if (lstat(stackblock(), &statb) < 0) { + badstat = 1; + break; + } + } + + INTOFF; + if ((p = findcwd(badstat ? NULL : dest)) == NULL || chdir(p) < 0) { + INTON; + return (-1); + } + updatepwd(p); + INTON; + return (0); +} + +static int +cdphysical(char *dest) +{ + char *p; + int rc = 0; + + INTOFF; + if (chdir(dest) < 0) { + INTON; + return (-1); + } + p = findcwd(NULL); + if (p == NULL) { + warning("warning: failed to get name of current directory"); + rc = 1; + } + updatepwd(p); + INTON; + return (rc); +} + +/* + * Get the next component of the path name pointed to by *path. + * This routine overwrites *path and the string pointed to by it. + */ +static char * +getcomponent(char **path) +{ + char *p; + char *start; + + if ((p = *path) == NULL) + return NULL; + start = *path; + while (*p != '/' && *p != '\0') + p++; + if (*p == '\0') { + *path = NULL; + } else { + *p++ = '\0'; + *path = p; + } + return start; +} + + +static char * +findcwd(char *dir) +{ + char *new; + char *p; + char *path; + + /* + * If our argument is NULL, we don't know the current directory + * any more because we traversed a symbolic link or something + * we couldn't stat(). + */ + if (dir == NULL || curdir == NULL) + return getpwd2(); + path = stsavestr(dir); + STARTSTACKSTR(new); + if (*dir != '/') { + STPUTS(curdir, new); + if (STTOPC(new) == '/') + STUNPUTC(new); + } + while ((p = getcomponent(&path)) != NULL) { + if (equal(p, "..")) { + while (new > stackblock() && (STUNPUTC(new), *new) != '/'); + } else if (*p != '\0' && ! equal(p, ".")) { + STPUTC('/', new); + STPUTS(p, new); + } + } + if (new == stackblock()) + STPUTC('/', new); + STACKSTRNUL(new); + return stackblock(); +} + +/* + * Update curdir (the name of the current directory) in response to a + * cd command. We also call hashcd to let the routines in exec.c know + * that the current directory has changed. + */ +static void +updatepwd(char *dir) +{ + char *prevdir; + + hashcd(); /* update command hash table */ + + setvar("PWD", dir, VEXPORT); + setvar("OLDPWD", curdir, VEXPORT); + prevdir = curdir; + curdir = dir ? savestr(dir) : NULL; + ckfree(prevdir); +} + +int +pwdcmd(int argc __unused, char **argv __unused) +{ + char *p; + int ch, phys; + + phys = Pflag; + while ((ch = nextopt("LP")) != '\0') { + switch (ch) { + case 'L': + phys = 0; + break; + case 'P': + phys = 1; + break; + } + } + + if (*argptr != NULL) + error("too many arguments"); + + if (!phys && getpwd()) { + out1str(curdir); + out1c('\n'); + } else { + if ((p = getpwd2()) == NULL) + error(".: %s", strerror(errno)); + out1str(p); + out1c('\n'); + } + + return 0; +} + +/* + * Get the current directory and cache the result in curdir. + */ +static char * +getpwd(void) +{ + char *p; + + if (curdir) + return curdir; + + p = getpwd2(); + if (p != NULL) + curdir = savestr(p); + + return curdir; +} + +#define MAXPWD 256 + +/* + * Return the current directory. + */ +static char * +getpwd2(void) +{ + char *pwd; + int i; + + for (i = MAXPWD;; i *= 2) { + pwd = stalloc(i); + if (getcwd(pwd, i) != NULL) + return pwd; + stunalloc(pwd); + if (errno != ERANGE) + break; + } + + return NULL; +} + +/* + * Initialize PWD in a new shell. + * If the shell is interactive, we need to warn if this fails. + */ +void +pwd_init(int warn) +{ + char *pwd; + struct stat stdot, stpwd; + + pwd = lookupvar("PWD"); + if (pwd && *pwd == '/' && stat(".", &stdot) != -1 && + stat(pwd, &stpwd) != -1 && + stdot.st_dev == stpwd.st_dev && + stdot.st_ino == stpwd.st_ino) { + if (curdir) + ckfree(curdir); + curdir = savestr(pwd); + } + if (getpwd() == NULL && warn) + out2fmt_flush("sh: cannot determine working directory\n"); + setvar("PWD", curdir, VEXPORT); +} diff --git a/shell_cmds/sh/cd.h b/shell_cmds/sh/cd.h new file mode 100644 index 0000000..0745d97 --- /dev/null +++ b/shell_cmds/sh/cd.h @@ -0,0 +1,32 @@ +/*- + * Copyright (c) 1995 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD: head/bin/sh/cd.h 314436 2017-02-28 23:42:47Z imp $ + */ + +void pwd_init(int); diff --git a/shell_cmds/sh/error.c b/shell_cmds/sh/error.c new file mode 100644 index 0000000..7f184ac --- /dev/null +++ b/shell_cmds/sh/error.c @@ -0,0 +1,199 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)error.c 8.2 (Berkeley) 5/4/95"; +#endif +#endif /* not lint */ +#include <sys/cdefs.h> +__FBSDID("$FreeBSD: head/bin/sh/error.c 314436 2017-02-28 23:42:47Z imp $"); + +/* + * Errors and exceptions. + */ + +#include "shell.h" +#include "eval.h" +#include "main.h" +#include "options.h" +#include "output.h" +#include "error.h" +#include "nodes.h" /* show.h needs nodes.h */ +#include "show.h" +#include "trap.h" +#include <signal.h> +#include <stdlib.h> +#include <unistd.h> +#include <errno.h> + + +/* + * Code to handle exceptions in C. + */ + +struct jmploc *handler; +volatile sig_atomic_t exception; +volatile sig_atomic_t suppressint; +volatile sig_atomic_t intpending; + + +static void exverror(int, const char *, va_list) __printf0like(2, 0) __dead2; + +/* + * Called to raise an exception. Since C doesn't include exceptions, we + * just do a longjmp to the exception handler. The type of exception is + * stored in the global variable "exception". + * + * Interrupts are disabled; they should be reenabled when the exception is + * caught. + */ + +void +exraise(int e) +{ + INTOFF; + if (handler == NULL) + abort(); + exception = e; + longjmp(handler->loc, 1); +} + + +/* + * Called from trap.c when a SIGINT is received and not suppressed, or when + * an interrupt is pending and interrupts are re-enabled using INTON. + * (If the user specifies that SIGINT is to be trapped or ignored using the + * trap builtin, then this routine is not called.) Suppressint is nonzero + * when interrupts are held using the INTOFF macro. If SIGINTs are not + * suppressed and the shell is not a root shell, then we want to be + * terminated if we get here, as if we were terminated directly by a SIGINT. + * Arrange for this here. + */ + +void +onint(void) +{ + sigset_t sigs; + + intpending = 0; + sigemptyset(&sigs); + sigprocmask(SIG_SETMASK, &sigs, NULL); + + /* + * This doesn't seem to be needed, since main() emits a newline. + */ +#if 0 + if (tcgetpgrp(0) == getpid()) + write(STDERR_FILENO, "\n", 1); +#endif + if (rootshell && iflag) + exraise(EXINT); + else { + signal(SIGINT, SIG_DFL); + kill(getpid(), SIGINT); + _exit(128 + SIGINT); + } +} + + +static void +vwarning(const char *msg, va_list ap) +{ + if (commandname) + outfmt(out2, "%s: ", commandname); + else if (arg0) + outfmt(out2, "%s: ", arg0); + doformat(out2, msg, ap); + out2fmt_flush("\n"); +} + + +void +warning(const char *msg, ...) +{ + va_list ap; + va_start(ap, msg); + vwarning(msg, ap); + va_end(ap); +} + + +/* + * Exverror is called to raise the error exception. If the first argument + * is not NULL then error prints an error message using printf style + * formatting. It then raises the error exception. + */ +static void +exverror(int cond, const char *msg, va_list ap) +{ + /* + * An interrupt trumps an error. Certain places catch error + * exceptions or transform them to a plain nonzero exit code + * in child processes, and if an error exception can be handled, + * an interrupt can be handled as well. + * + * exraise() will disable interrupts for the exception handler. + */ + FORCEINTON; + +#ifdef DEBUG + if (msg) + TRACE(("exverror(%d, \"%s\") pid=%d\n", cond, msg, getpid())); + else + TRACE(("exverror(%d, NULL) pid=%d\n", cond, getpid())); +#endif + if (msg) + vwarning(msg, ap); + flushall(); + exraise(cond); +} + + +void +error(const char *msg, ...) +{ + va_list ap; + va_start(ap, msg); + exverror(EXERROR, msg, ap); + va_end(ap); +} + + +void +exerror(int cond, const char *msg, ...) +{ + va_list ap; + va_start(ap, msg); + exverror(cond, msg, ap); + va_end(ap); +} diff --git a/shell_cmds/sh/error.h b/shell_cmds/sh/error.h new file mode 100644 index 0000000..e962c4f --- /dev/null +++ b/shell_cmds/sh/error.h @@ -0,0 +1,95 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)error.h 8.2 (Berkeley) 5/4/95 + * $FreeBSD: head/bin/sh/error.h 319591 2017-06-04 21:58:02Z jilles $ + */ + +/* + * We enclose jmp_buf in a structure so that we can declare pointers to + * jump locations. The global variable handler contains the location to + * jump to when an exception occurs, and the global variable exception + * contains a code identifying the exception. To implement nested + * exception handlers, the user should save the value of handler on entry + * to an inner scope, set handler to point to a jmploc structure for the + * inner scope, and restore handler on exit from the scope. + */ + +#include <setjmp.h> +#include <signal.h> + +struct jmploc { + jmp_buf loc; +}; + +extern struct jmploc *handler; +extern volatile sig_atomic_t exception; + +/* exceptions */ +#define EXINT 0 /* SIGINT received */ +#define EXERROR 1 /* a generic error */ +#define EXEXEC 2 /* command execution failed */ +#define EXEXIT 3 /* call exitshell(exitstatus) */ + + +/* + * These macros allow the user to suspend the handling of interrupt signals + * over a period of time. This is similar to SIGHOLD to or sigblock, but + * much more efficient and portable. (But hacking the kernel is so much + * more fun than worrying about efficiency and portability. :-)) + */ + +extern volatile sig_atomic_t suppressint; +extern volatile sig_atomic_t intpending; + +#define INTOFF suppressint++ +#define INTON { if (--suppressint == 0 && intpending) onint(); } +#define is_int_on() suppressint +#define SETINTON(s) do { suppressint = (s); if (suppressint == 0 && intpending) onint(); } while (0) +#define FORCEINTON {suppressint = 0; if (intpending) onint();} +#define SET_PENDING_INT intpending = 1 +#define CLEAR_PENDING_INT intpending = 0 +#define int_pending() intpending + +void exraise(int) __dead2; +void onint(void) __dead2; +void warning(const char *, ...) __printflike(1, 2); +void error(const char *, ...) __printf0like(1, 2) __dead2; +void exerror(int, const char *, ...) __printf0like(2, 3) __dead2; + + +/* + * BSD setjmp saves the signal mask, which violates ANSI C and takes time, + * so we use _setjmp instead. + */ + +#define setjmp(jmploc) _setjmp(jmploc) +#define longjmp(jmploc, val) _longjmp(jmploc, val) diff --git a/shell_cmds/sh/eval.c b/shell_cmds/sh/eval.c new file mode 100644 index 0000000..bb5f3cc --- /dev/null +++ b/shell_cmds/sh/eval.c @@ -0,0 +1,1381 @@ +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)eval.c 8.9 (Berkeley) 6/8/95"; +#endif +#endif /* not lint */ +#include <sys/cdefs.h> +__FBSDID("$FreeBSD: head/bin/sh/eval.c 327212 2017-12-26 16:23:18Z jilles $"); + +#include <paths.h> +#include <signal.h> +#include <stdlib.h> +#include <unistd.h> +#include <sys/resource.h> +#include <errno.h> + +/* + * Evaluate a command. + */ + +#include "shell.h" +#include "nodes.h" +#include "syntax.h" +#include "expand.h" +#include "parser.h" +#include "jobs.h" +#include "eval.h" +#include "builtins.h" +#include "options.h" +#include "exec.h" +#include "redir.h" +#include "input.h" +#include "output.h" +#include "trap.h" +#include "var.h" +#include "memalloc.h" +#include "error.h" +#include "show.h" +#include "mystring.h" +#ifndef NO_HISTORY +#include "myhistedit.h" +#endif + + +int evalskip; /* set if we are skipping commands */ +int skipcount; /* number of levels to skip */ +static int loopnest; /* current loop nesting level */ +int funcnest; /* depth of function calls */ +static int builtin_flags; /* evalcommand flags for builtins */ + + +char *commandname; +struct arglist *cmdenviron; +int exitstatus; /* exit status of last command */ +int oexitstatus; /* saved exit status */ + + +static void evalloop(union node *, int); +static void evalfor(union node *, int); +static union node *evalcase(union node *); +static void evalsubshell(union node *, int); +static void evalredir(union node *, int); +static void exphere(union node *, struct arglist *); +static void expredir(union node *); +static void evalpipe(union node *); +static int is_valid_fast_cmdsubst(union node *n); +static void evalcommand(union node *, int, struct backcmd *); +static void prehash(union node *); + + +/* + * Called to reset things after an exception. + */ + +void +reseteval(void) +{ + evalskip = 0; + loopnest = 0; +} + + +/* + * The eval command. + */ + +int +evalcmd(int argc, char **argv) +{ + char *p; + char *concat; + char **ap; + + if (argc > 1) { + p = argv[1]; + if (argc > 2) { + STARTSTACKSTR(concat); + ap = argv + 2; + for (;;) { + STPUTS(p, concat); + if ((p = *ap++) == NULL) + break; + STPUTC(' ', concat); + } + STPUTC('\0', concat); + p = grabstackstr(concat); + } + evalstring(p, builtin_flags); + } else + exitstatus = 0; + return exitstatus; +} + + +/* + * Execute a command or commands contained in a string. + */ + +void +evalstring(const char *s, int flags) +{ + union node *n; + struct stackmark smark; + int flags_exit; + int any; + + flags_exit = flags & EV_EXIT; + flags &= ~EV_EXIT; + any = 0; + setstackmark(&smark); + setinputstring(s, 1); + while ((n = parsecmd(0)) != NEOF) { + if (n != NULL && !nflag) { + if (flags_exit && preadateof()) + evaltree(n, flags | EV_EXIT); + else + evaltree(n, flags); + any = 1; + if (evalskip) + break; + } + popstackmark(&smark); + setstackmark(&smark); + } + popfile(); + popstackmark(&smark); + if (!any) + exitstatus = 0; + if (flags_exit) + exraise(EXEXIT); +} + + +/* + * Evaluate a parse tree. The value is left in the global variable + * exitstatus. + */ + +void +evaltree(union node *n, int flags) +{ + int do_etest; + union node *next; + struct stackmark smark; + + setstackmark(&smark); + do_etest = 0; + if (n == NULL) { + TRACE(("evaltree(NULL) called\n")); + exitstatus = 0; + goto out; + } + do { + next = NULL; +#ifndef NO_HISTORY + displayhist = 1; /* show history substitutions done with fc */ +#endif + TRACE(("evaltree(%p: %d) called\n", (void *)n, n->type)); + switch (n->type) { + case NSEMI: + evaltree(n->nbinary.ch1, flags & ~EV_EXIT); + if (evalskip) + goto out; + next = n->nbinary.ch2; + break; + case NAND: + evaltree(n->nbinary.ch1, EV_TESTED); + if (evalskip || exitstatus != 0) { + goto out; + } + next = n->nbinary.ch2; + break; + case NOR: + evaltree(n->nbinary.ch1, EV_TESTED); + if (evalskip || exitstatus == 0) + goto out; + next = n->nbinary.ch2; + break; + case NREDIR: + evalredir(n, flags); + break; + case NSUBSHELL: + evalsubshell(n, flags); + do_etest = !(flags & EV_TESTED); + break; + case NBACKGND: + evalsubshell(n, flags); + break; + case NIF: { + evaltree(n->nif.test, EV_TESTED); + if (evalskip) + goto out; + if (exitstatus == 0) + next = n->nif.ifpart; + else if (n->nif.elsepart) + next = n->nif.elsepart; + else + exitstatus = 0; + break; + } + case NWHILE: + case NUNTIL: + evalloop(n, flags & ~EV_EXIT); + break; + case NFOR: + evalfor(n, flags & ~EV_EXIT); + break; + case NCASE: + next = evalcase(n); + break; + case NCLIST: + next = n->nclist.body; + break; + case NCLISTFALLTHRU: + if (n->nclist.body) { + evaltree(n->nclist.body, flags & ~EV_EXIT); + if (evalskip) + goto out; + } + next = n->nclist.next; + break; + case NDEFUN: + defun(n->narg.text, n->narg.next); + exitstatus = 0; + break; + case NNOT: + evaltree(n->nnot.com, EV_TESTED); + if (evalskip) + goto out; + exitstatus = !exitstatus; + break; + + case NPIPE: + evalpipe(n); + do_etest = !(flags & EV_TESTED); + break; + case NCMD: + evalcommand(n, flags, (struct backcmd *)NULL); + do_etest = !(flags & EV_TESTED); + break; + default: + out1fmt("Node type = %d\n", n->type); + flushout(&output); + break; + } + n = next; + popstackmark(&smark); + setstackmark(&smark); + } while (n != NULL); +out: + popstackmark(&smark); + if (pendingsig) + dotrap(); + if (eflag && exitstatus != 0 && do_etest) + exitshell(exitstatus); + if (flags & EV_EXIT) + exraise(EXEXIT); +} + + +static void +evalloop(union node *n, int flags) +{ + int status; + + loopnest++; + status = 0; + for (;;) { + if (!evalskip) + evaltree(n->nbinary.ch1, EV_TESTED); + if (evalskip) { + if (evalskip == SKIPCONT && --skipcount <= 0) { + evalskip = 0; + continue; + } + if (evalskip == SKIPBREAK && --skipcount <= 0) + evalskip = 0; + if (evalskip == SKIPRETURN) + status = exitstatus; + break; + } + if (n->type == NWHILE) { + if (exitstatus != 0) + break; + } else { + if (exitstatus == 0) + break; + } + evaltree(n->nbinary.ch2, flags); + status = exitstatus; + } + loopnest--; + exitstatus = status; +} + + + +static void +evalfor(union node *n, int flags) +{ + struct arglist arglist; + union node *argp; + int i; + int status; + + emptyarglist(&arglist); + for (argp = n->nfor.args ; argp ; argp = argp->narg.next) { + oexitstatus = exitstatus; + expandarg(argp, &arglist, EXP_FULL | EXP_TILDE); + } + + loopnest++; + status = 0; + for (i = 0; i < arglist.count; i++) { + setvar(n->nfor.var, arglist.args[i], 0); + evaltree(n->nfor.body, flags); + status = exitstatus; + if (evalskip) { + if (evalskip == SKIPCONT && --skipcount <= 0) { + evalskip = 0; + continue; + } + if (evalskip == SKIPBREAK && --skipcount <= 0) + evalskip = 0; + break; + } + } + loopnest--; + exitstatus = status; +} + + +/* + * Evaluate a case statement, returning the selected tree. + * + * The exit status needs care to get right. + */ + +static union node * +evalcase(union node *n) +{ + union node *cp; + union node *patp; + struct arglist arglist; + + emptyarglist(&arglist); + oexitstatus = exitstatus; + expandarg(n->ncase.expr, &arglist, EXP_TILDE); + for (cp = n->ncase.cases ; cp ; cp = cp->nclist.next) { + for (patp = cp->nclist.pattern ; patp ; patp = patp->narg.next) { + if (casematch(patp, arglist.args[0])) { + while (cp->nclist.next && + cp->type == NCLISTFALLTHRU && + cp->nclist.body == NULL) + cp = cp->nclist.next; + if (cp->nclist.next && + cp->type == NCLISTFALLTHRU) + return (cp); + if (cp->nclist.body == NULL) + exitstatus = 0; + return (cp->nclist.body); + } + } + } + exitstatus = 0; + return (NULL); +} + + + +/* + * Kick off a subshell to evaluate a tree. + */ + +static void +evalsubshell(union node *n, int flags) +{ + struct job *jp; + int backgnd = (n->type == NBACKGND); + + oexitstatus = exitstatus; + expredir(n->nredir.redirect); + if ((!backgnd && flags & EV_EXIT && !have_traps()) || + forkshell(jp = makejob(n, 1), n, backgnd) == 0) { + if (backgnd) + flags &=~ EV_TESTED; + redirect(n->nredir.redirect, 0); + evaltree(n->nredir.n, flags | EV_EXIT); /* never returns */ + } else if (! backgnd) { + INTOFF; + exitstatus = waitforjob(jp, (int *)NULL); + INTON; + } else + exitstatus = 0; +} + + +/* + * Evaluate a redirected compound command. + */ + +static void +evalredir(union node *n, int flags) +{ + struct jmploc jmploc; + struct jmploc *savehandler; + volatile int in_redirect = 1; + + oexitstatus = exitstatus; + expredir(n->nredir.redirect); + savehandler = handler; + if (setjmp(jmploc.loc)) { + int e; + + handler = savehandler; + e = exception; + popredir(); + if (e == EXERROR || e == EXEXEC) { + if (in_redirect) { + exitstatus = 2; + FORCEINTON; + return; + } + } + longjmp(handler->loc, 1); + } else { + INTOFF; + handler = &jmploc; + redirect(n->nredir.redirect, REDIR_PUSH); + in_redirect = 0; + INTON; + evaltree(n->nredir.n, flags); + } + INTOFF; + handler = savehandler; + popredir(); + INTON; +} + + +static void +exphere(union node *redir, struct arglist *fn) +{ + struct jmploc jmploc; + struct jmploc *savehandler; + struct localvar *savelocalvars; + int need_longjmp = 0; + unsigned char saveoptreset; + + redir->nhere.expdoc = ""; + savelocalvars = localvars; + localvars = NULL; + saveoptreset = shellparam.reset; + forcelocal++; + savehandler = handler; + if (setjmp(jmploc.loc)) + need_longjmp = exception != EXERROR && exception != EXEXEC; + else { + handler = &jmploc; + expandarg(redir->nhere.doc, fn, 0); + redir->nhere.expdoc = fn->args[0]; + INTOFF; + } + handler = savehandler; + forcelocal--; + poplocalvars(); + localvars = savelocalvars; + shellparam.reset = saveoptreset; + if (need_longjmp) + longjmp(handler->loc, 1); + INTON; +} + + +/* + * Compute the names of the files in a redirection list. + */ + +static void +expredir(union node *n) +{ + union node *redir; + + for (redir = n ; redir ; redir = redir->nfile.next) { + struct arglist fn; + emptyarglist(&fn); + switch (redir->type) { + case NFROM: + case NTO: + case NFROMTO: + case NAPPEND: + case NCLOBBER: + expandarg(redir->nfile.fname, &fn, EXP_TILDE); + redir->nfile.expfname = fn.args[0]; + break; + case NFROMFD: + case NTOFD: + if (redir->ndup.vname) { + expandarg(redir->ndup.vname, &fn, EXP_TILDE); + fixredir(redir, fn.args[0], 1); + } + break; + case NXHERE: + exphere(redir, &fn); + break; + } + } +} + + + +/* + * Evaluate a pipeline. All the processes in the pipeline are children + * of the process creating the pipeline. (This differs from some versions + * of the shell, which make the last process in a pipeline the parent + * of all the rest.) + */ + +static void +evalpipe(union node *n) +{ + struct job *jp; + struct nodelist *lp; + int pipelen; + int prevfd; + int pip[2]; + + TRACE(("evalpipe(%p) called\n", (void *)n)); + pipelen = 0; + for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) + pipelen++; + INTOFF; + jp = makejob(n, pipelen); + prevfd = -1; + for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) { + prehash(lp->n); + pip[1] = -1; + if (lp->next) { + if (pipe(pip) < 0) { + if (prevfd >= 0) + close(prevfd); + error("Pipe call failed: %s", strerror(errno)); + } + } + if (forkshell(jp, lp->n, n->npipe.backgnd) == 0) { + INTON; + if (prevfd > 0) { + dup2(prevfd, 0); + close(prevfd); + } + if (pip[1] >= 0) { + if (!(prevfd >= 0 && pip[0] == 0)) + close(pip[0]); + if (pip[1] != 1) { + dup2(pip[1], 1); + close(pip[1]); + } + } + evaltree(lp->n, EV_EXIT); + } + if (prevfd >= 0) + close(prevfd); + prevfd = pip[0]; + if (pip[1] != -1) + close(pip[1]); + } + INTON; + if (n->npipe.backgnd == 0) { + INTOFF; + exitstatus = waitforjob(jp, (int *)NULL); + TRACE(("evalpipe: job done exit status %d\n", exitstatus)); + INTON; + } else + exitstatus = 0; +} + + + +static int +is_valid_fast_cmdsubst(union node *n) +{ + + return (n->type == NCMD); +} + +/* + * Execute a command inside back quotes. If it's a builtin command, we + * want to save its output in a block obtained from malloc. Otherwise + * we fork off a subprocess and get the output of the command via a pipe. + * Should be called with interrupts off. + */ + +void +evalbackcmd(union node *n, struct backcmd *result) +{ + int pip[2]; + struct job *jp; + struct stackmark smark; + struct jmploc jmploc; + struct jmploc *savehandler; + struct localvar *savelocalvars; + unsigned char saveoptreset; + + result->fd = -1; + result->buf = NULL; + result->nleft = 0; + result->jp = NULL; + if (n == NULL) { + exitstatus = 0; + return; + } + setstackmark(&smark); + exitstatus = oexitstatus; + if (is_valid_fast_cmdsubst(n)) { + savelocalvars = localvars; + localvars = NULL; + saveoptreset = shellparam.reset; + forcelocal++; + savehandler = handler; + if (setjmp(jmploc.loc)) { + if (exception == EXERROR || exception == EXEXEC) + exitstatus = 2; + else if (exception != 0) { + handler = savehandler; + forcelocal--; + poplocalvars(); + localvars = savelocalvars; + shellparam.reset = saveoptreset; + longjmp(handler->loc, 1); + } + } else { + handler = &jmploc; + evalcommand(n, EV_BACKCMD, result); + } + handler = savehandler; + forcelocal--; + poplocalvars(); + localvars = savelocalvars; + shellparam.reset = saveoptreset; + } else { + if (pipe(pip) < 0) + error("Pipe call failed: %s", strerror(errno)); + jp = makejob(n, 1); + if (forkshell(jp, n, FORK_NOJOB) == 0) { + FORCEINTON; + close(pip[0]); + if (pip[1] != 1) { + dup2(pip[1], 1); + close(pip[1]); + } + evaltree(n, EV_EXIT); + } + close(pip[1]); + result->fd = pip[0]; + result->jp = jp; + } + popstackmark(&smark); + TRACE(("evalbackcmd done: fd=%d buf=%p nleft=%d jp=%p\n", + result->fd, result->buf, result->nleft, result->jp)); +} + +static int +mustexpandto(const char *argtext, const char *mask) +{ + for (;;) { + if (*argtext == CTLQUOTEMARK || *argtext == CTLQUOTEEND) { + argtext++; + continue; + } + if (*argtext == CTLESC) + argtext++; + else if (BASESYNTAX[(int)*argtext] == CCTL) + return (0); + if (*argtext != *mask) + return (0); + if (*argtext == '\0') + return (1); + argtext++; + mask++; + } +} + +static int +isdeclarationcmd(struct narg *arg) +{ + int have_command = 0; + + if (arg == NULL) + return (0); + while (mustexpandto(arg->text, "command")) { + have_command = 1; + arg = &arg->next->narg; + if (arg == NULL) + return (0); + /* + * To also allow "command -p" and "command --" as part of + * a declaration command, add code here. + * We do not do this, as ksh does not do it either and it + * is not required by POSIX. + */ + } + return (mustexpandto(arg->text, "export") || + mustexpandto(arg->text, "readonly") || + (mustexpandto(arg->text, "local") && + (have_command || !isfunc("local")))); +} + +static void +xtracecommand(struct arglist *varlist, int argc, char **argv) +{ + char sep = 0; + const char *text, *p, *ps4; + int i; + + ps4 = expandstr(ps4val()); + out2str(ps4 != NULL ? ps4 : ps4val()); + for (i = 0; i < varlist->count; i++) { + text = varlist->args[i]; + if (sep != 0) + out2c(' '); + p = strchr(text, '='); + if (p != NULL) { + p++; + outbin(text, p - text, out2); + out2qstr(p); + } else + out2qstr(text); + sep = ' '; + } + for (i = 0; i < argc; i++) { + text = argv[i]; + if (sep != 0) + out2c(' '); + out2qstr(text); + sep = ' '; + } + out2c('\n'); + flushout(&errout); +} + +/* + * Check if a builtin can safely be executed in the same process, + * even though it should be in a subshell (command substitution). + * Note that jobid, jobs, times and trap can show information not + * available in a child process; this is deliberate. + * The arguments should already have been expanded. + */ +static int +safe_builtin(int idx, int argc, char **argv) +{ + /* Generated from builtins.def. */ + if (safe_builtin_always(idx)) + return (1); + if (idx == EXPORTCMD || idx == TRAPCMD || idx == ULIMITCMD || + idx == UMASKCMD) + return (argc <= 1 || (argc == 2 && argv[1][0] == '-')); + if (idx == SETCMD) + return (argc <= 1 || (argc == 2 && (argv[1][0] == '-' || + argv[1][0] == '+') && argv[1][1] == 'o' && + argv[1][2] == '\0')); + return (0); +} + +/* + * Execute a simple command. + * Note: This may or may not return if (flags & EV_EXIT). + */ + +static void +evalcommand(union node *cmd, int flags, struct backcmd *backcmd) +{ + union node *argp; + struct arglist arglist; + struct arglist varlist; + char **argv; + int argc; + char **envp; + int varflag; + int mode; + int pip[2]; + struct cmdentry cmdentry; + struct job *jp; + struct jmploc jmploc; + struct jmploc *savehandler; + char *savecmdname; + struct shparam saveparam; + struct localvar *savelocalvars; + struct parsefile *savetopfile; + volatile int e; + char *lastarg; + int signaled; + int do_clearcmdentry; + const char *path = pathval(); + int i; + + /* First expand the arguments. */ + TRACE(("evalcommand(%p, %d) called\n", (void *)cmd, flags)); + emptyarglist(&arglist); + emptyarglist(&varlist); + varflag = 1; + jp = NULL; + do_clearcmdentry = 0; + oexitstatus = exitstatus; + exitstatus = 0; + /* Add one slot at the beginning for tryexec(). */ + appendarglist(&arglist, nullstr); + for (argp = cmd->ncmd.args ; argp ; argp = argp->narg.next) { + if (varflag && isassignment(argp->narg.text)) { + expandarg(argp, varflag == 1 ? &varlist : &arglist, + EXP_VARTILDE); + continue; + } else if (varflag == 1) + varflag = isdeclarationcmd(&argp->narg) ? 2 : 0; + expandarg(argp, &arglist, EXP_FULL | EXP_TILDE); + } + appendarglist(&arglist, nullstr); + expredir(cmd->ncmd.redirect); + argc = arglist.count - 2; + argv = &arglist.args[1]; + + argv[argc] = NULL; + lastarg = NULL; + if (iflag && funcnest == 0 && argc > 0) + lastarg = argv[argc - 1]; + + /* Print the command if xflag is set. */ + if (xflag) + xtracecommand(&varlist, argc, argv); + + /* Now locate the command. */ + if (argc == 0) { + /* Variable assignment(s) without command */ + cmdentry.cmdtype = CMDBUILTIN; + cmdentry.u.index = BLTINCMD; + cmdentry.special = 0; + } else { + static const char PATH[] = "PATH="; + int cmd_flags = 0, bltinonly = 0; + + /* + * Modify the command lookup path, if a PATH= assignment + * is present + */ + for (i = 0; i < varlist.count; i++) + if (strncmp(varlist.args[i], PATH, sizeof(PATH) - 1) == 0) { + path = varlist.args[i] + sizeof(PATH) - 1; + /* + * On `PATH=... command`, we need to make + * sure that the command isn't using the + * non-updated hash table of the outer PATH + * setting and we need to make sure that + * the hash table isn't filled with items + * from the temporary setting. + * + * It would be better to forbit using and + * updating the table while this command + * runs, by the command finding mechanism + * is heavily integrated with hash handling, + * so we just delete the hash before and after + * the command runs. Partly deleting like + * changepatch() does doesn't seem worth the + * bookinging effort, since most such runs add + * directories in front of the new PATH. + */ + clearcmdentry(); + do_clearcmdentry = 1; + } + + for (;;) { + if (bltinonly) { + cmdentry.u.index = find_builtin(*argv, &cmdentry.special); + if (cmdentry.u.index < 0) { + cmdentry.u.index = BLTINCMD; + argv--; + argc++; + break; + } + } else + find_command(argv[0], &cmdentry, cmd_flags, path); + /* implement the bltin and command builtins here */ + if (cmdentry.cmdtype != CMDBUILTIN) + break; + if (cmdentry.u.index == BLTINCMD) { + if (argc == 1) + break; + argv++; + argc--; + bltinonly = 1; + } else if (cmdentry.u.index == COMMANDCMD) { + if (argc == 1) + break; + if (!strcmp(argv[1], "-p")) { + if (argc == 2) + break; + if (argv[2][0] == '-') { + if (strcmp(argv[2], "--")) + break; + if (argc == 3) + break; + argv += 3; + argc -= 3; + } else { + argv += 2; + argc -= 2; + } + path = _PATH_STDPATH; + clearcmdentry(); + do_clearcmdentry = 1; + } else if (!strcmp(argv[1], "--")) { + if (argc == 2) + break; + argv += 2; + argc -= 2; + } else if (argv[1][0] == '-') + break; + else { + argv++; + argc--; + } + cmd_flags |= DO_NOFUNC; + bltinonly = 0; + } else + break; + } + /* + * Special builtins lose their special properties when + * called via 'command'. + */ + if (cmd_flags & DO_NOFUNC) + cmdentry.special = 0; + } + + /* Fork off a child process if necessary. */ + if (((cmdentry.cmdtype == CMDNORMAL || cmdentry.cmdtype == CMDUNKNOWN) + && ((flags & EV_EXIT) == 0 || have_traps())) + || ((flags & EV_BACKCMD) != 0 + && (cmdentry.cmdtype != CMDBUILTIN || + !safe_builtin(cmdentry.u.index, argc, argv)))) { + jp = makejob(cmd, 1); + mode = FORK_FG; + if (flags & EV_BACKCMD) { + mode = FORK_NOJOB; + if (pipe(pip) < 0) + error("Pipe call failed: %s", strerror(errno)); + } + if (cmdentry.cmdtype == CMDNORMAL && + cmd->ncmd.redirect == NULL && + varlist.count == 0 && + (mode == FORK_FG || mode == FORK_NOJOB) && + !disvforkset() && !iflag && !mflag) { + vforkexecshell(jp, argv, environment(), path, + cmdentry.u.index, flags & EV_BACKCMD ? pip : NULL); + goto parent; + } + if (forkshell(jp, cmd, mode) != 0) + goto parent; /* at end of routine */ + if (flags & EV_BACKCMD) { + FORCEINTON; + close(pip[0]); + if (pip[1] != 1) { + dup2(pip[1], 1); + close(pip[1]); + } + flags &= ~EV_BACKCMD; + } + flags |= EV_EXIT; + } + + /* This is the child process if a fork occurred. */ + /* Execute the command. */ + if (cmdentry.cmdtype == CMDFUNCTION) { +#ifdef DEBUG + trputs("Shell function: "); trargs(argv); +#endif + saveparam = shellparam; + shellparam.malloc = 0; + shellparam.reset = 1; + shellparam.nparam = argc - 1; + shellparam.p = argv + 1; + shellparam.optp = NULL; + shellparam.optnext = NULL; + INTOFF; + savelocalvars = localvars; + localvars = NULL; + reffunc(cmdentry.u.func); + savehandler = handler; + if (setjmp(jmploc.loc)) { + popredir(); + unreffunc(cmdentry.u.func); + poplocalvars(); + localvars = savelocalvars; + freeparam(&shellparam); + shellparam = saveparam; + funcnest--; + handler = savehandler; + longjmp(handler->loc, 1); + } + handler = &jmploc; + funcnest++; + redirect(cmd->ncmd.redirect, REDIR_PUSH); + INTON; + for (i = 0; i < varlist.count; i++) + mklocal(varlist.args[i]); + exitstatus = oexitstatus; + evaltree(getfuncnode(cmdentry.u.func), + flags & (EV_TESTED | EV_EXIT)); + INTOFF; + unreffunc(cmdentry.u.func); + poplocalvars(); + localvars = savelocalvars; + freeparam(&shellparam); + shellparam = saveparam; + handler = savehandler; + funcnest--; + popredir(); + INTON; + if (evalskip == SKIPRETURN) { + evalskip = 0; + skipcount = 0; + } + if (jp) + exitshell(exitstatus); + } else if (cmdentry.cmdtype == CMDBUILTIN) { +#ifdef DEBUG + trputs("builtin command: "); trargs(argv); +#endif + mode = (cmdentry.u.index == EXECCMD)? 0 : REDIR_PUSH; + if (flags == EV_BACKCMD) { + memout.nextc = memout.buf; + mode |= REDIR_BACKQ; + } + savecmdname = commandname; + savetopfile = getcurrentfile(); + cmdenviron = &varlist; + e = -1; + savehandler = handler; + if (setjmp(jmploc.loc)) { + e = exception; + if (e == EXINT) + exitstatus = SIGINT+128; + else if (e != EXEXIT) + exitstatus = 2; + goto cmddone; + } + handler = &jmploc; + redirect(cmd->ncmd.redirect, mode); + outclearerror(out1); + /* + * If there is no command word, redirection errors should + * not be fatal but assignment errors should. + */ + if (argc == 0) + cmdentry.special = 1; + listsetvar(cmdenviron, cmdentry.special ? 0 : VNOSET); + if (argc > 0) + bltinsetlocale(); + commandname = argv[0]; + argptr = argv + 1; + nextopt_optptr = NULL; /* initialize nextopt */ + builtin_flags = flags; + exitstatus = (*builtinfunc[cmdentry.u.index])(argc, argv); + flushall(); + if (outiserror(out1)) { + warning("write error on stdout"); + if (exitstatus == 0 || exitstatus == 1) + exitstatus = 2; + } +cmddone: + if (argc > 0) + bltinunsetlocale(); + cmdenviron = NULL; + out1 = &output; + out2 = &errout; + freestdout(); + handler = savehandler; + commandname = savecmdname; + if (jp) + exitshell(exitstatus); + if (flags == EV_BACKCMD) { + backcmd->buf = memout.buf; + backcmd->nleft = memout.buf != NULL ? + memout.nextc - memout.buf : 0; + memout.buf = NULL; + memout.nextc = NULL; + memout.bufend = NULL; + memout.bufsize = 64; + } + if (cmdentry.u.index != EXECCMD) + popredir(); + if (e != -1) { + if ((e != EXERROR && e != EXEXEC) + || cmdentry.special) + exraise(e); + popfilesupto(savetopfile); + if (flags != EV_BACKCMD) + FORCEINTON; + } + } else { +#ifdef DEBUG + trputs("normal command: "); trargs(argv); +#endif + redirect(cmd->ncmd.redirect, 0); + for (i = 0; i < varlist.count; i++) + setvareq(varlist.args[i], VEXPORT|VSTACK); + envp = environment(); + shellexec(argv, envp, path, cmdentry.u.index); + /*NOTREACHED*/ + } + goto out; + +parent: /* parent process gets here (if we forked) */ + if (mode == FORK_FG) { /* argument to fork */ + INTOFF; + exitstatus = waitforjob(jp, &signaled); + INTON; + if (iflag && loopnest > 0 && signaled) { + evalskip = SKIPBREAK; + skipcount = loopnest; + } + } else if (mode == FORK_NOJOB) { + backcmd->fd = pip[0]; + close(pip[1]); + backcmd->jp = jp; + } + +out: + if (lastarg) + setvar("_", lastarg, 0); + if (do_clearcmdentry) + clearcmdentry(); +} + + + +/* + * Search for a command. This is called before we fork so that the + * location of the command will be available in the parent as well as + * the child. The check for "goodname" is an overly conservative + * check that the name will not be subject to expansion. + */ + +static void +prehash(union node *n) +{ + struct cmdentry entry; + + if (n && n->type == NCMD && n->ncmd.args) + if (goodname(n->ncmd.args->narg.text)) + find_command(n->ncmd.args->narg.text, &entry, 0, + pathval()); +} + + + +/* + * Builtin commands. Builtin commands whose functions are closely + * tied to evaluation are implemented here. + */ + +/* + * No command given, a bltin command with no arguments, or a bltin command + * with an invalid name. + */ + +int +bltincmd(int argc, char **argv) +{ + if (argc > 1) { + out2fmt_flush("%s: not found\n", argv[1]); + return 127; + } + /* + * Preserve exitstatus of a previous possible command substitution + * as POSIX mandates + */ + return exitstatus; +} + + +/* + * Handle break and continue commands. Break, continue, and return are + * all handled by setting the evalskip flag. The evaluation routines + * above all check this flag, and if it is set they start skipping + * commands rather than executing them. The variable skipcount is + * the number of loops to break/continue, or the number of function + * levels to return. (The latter is always 1.) It should probably + * be an error to break out of more loops than exist, but it isn't + * in the standard shell so we don't make it one here. + */ + +int +breakcmd(int argc, char **argv) +{ + long n; + char *end; + + if (argc > 1) { + /* Allow arbitrarily large numbers. */ + n = strtol(argv[1], &end, 10); + if (!is_digit(argv[1][0]) || *end != '\0') + error("Illegal number: %s", argv[1]); + } else + n = 1; + if (n > loopnest) + n = loopnest; + if (n > 0) { + evalskip = (**argv == 'c')? SKIPCONT : SKIPBREAK; + skipcount = n; + } + return 0; +} + +/* + * The `command' command. + */ +int +commandcmd(int argc __unused, char **argv __unused) +{ + const char *path; + int ch; + int cmd = -1; + + path = bltinlookup("PATH", 1); + + while ((ch = nextopt("pvV")) != '\0') { + switch (ch) { + case 'p': + path = _PATH_STDPATH; + break; + case 'v': + cmd = TYPECMD_SMALLV; + break; + case 'V': + cmd = TYPECMD_BIGV; + break; + } + } + + if (cmd != -1) { + if (*argptr == NULL || argptr[1] != NULL) + error("wrong number of arguments"); + return typecmd_impl(2, argptr - 1, cmd, path); + } + if (*argptr != NULL) + error("commandcmd bad call"); + + /* + * Do nothing successfully if no command was specified; + * ksh also does this. + */ + return 0; +} + + +/* + * The return command. + */ + +int +returncmd(int argc, char **argv) +{ + int ret = argc > 1 ? number(argv[1]) : oexitstatus; + + evalskip = SKIPRETURN; + skipcount = 1; + return ret; +} + + +int +falsecmd(int argc __unused, char **argv __unused) +{ + return 1; +} + + +int +truecmd(int argc __unused, char **argv __unused) +{ + return 0; +} + + +int +execcmd(int argc, char **argv) +{ + int i; + + /* + * Because we have historically not supported any options, + * only treat "--" specially. + */ + if (argc > 1 && strcmp(argv[1], "--") == 0) + argc--, argv++; + if (argc > 1) { + iflag = 0; /* exit on error */ + mflag = 0; + optschanged(); + for (i = 0; i < cmdenviron->count; i++) + setvareq(cmdenviron->args[i], VEXPORT|VSTACK); + shellexec(argv + 1, environment(), pathval(), 0); + + } + return 0; +} + + +int +timescmd(int argc __unused, char **argv __unused) +{ + struct rusage ru; + long shumins, shsmins, chumins, chsmins; + double shusecs, shssecs, chusecs, chssecs; + + if (getrusage(RUSAGE_SELF, &ru) < 0) + return 1; + shumins = ru.ru_utime.tv_sec / 60; + shusecs = ru.ru_utime.tv_sec % 60 + ru.ru_utime.tv_usec / 1000000.; + shsmins = ru.ru_stime.tv_sec / 60; + shssecs = ru.ru_stime.tv_sec % 60 + ru.ru_stime.tv_usec / 1000000.; + if (getrusage(RUSAGE_CHILDREN, &ru) < 0) + return 1; + chumins = ru.ru_utime.tv_sec / 60; + chusecs = ru.ru_utime.tv_sec % 60 + ru.ru_utime.tv_usec / 1000000.; + chsmins = ru.ru_stime.tv_sec / 60; + chssecs = ru.ru_stime.tv_sec % 60 + ru.ru_stime.tv_usec / 1000000.; + out1fmt("%ldm%.3fs %ldm%.3fs\n%ldm%.3fs %ldm%.3fs\n", shumins, + shusecs, shsmins, shssecs, chumins, chusecs, chsmins, chssecs); + return 0; +} diff --git a/shell_cmds/sh/eval.h b/shell_cmds/sh/eval.h new file mode 100644 index 0000000..18b39f9 --- /dev/null +++ b/shell_cmds/sh/eval.h @@ -0,0 +1,70 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)eval.h 8.2 (Berkeley) 5/4/95 + * $FreeBSD: head/bin/sh/eval.h 314436 2017-02-28 23:42:47Z imp $ + */ + +extern char *commandname; /* currently executing command */ +extern int exitstatus; /* exit status of last command */ +extern int oexitstatus; /* saved exit status */ +extern struct arglist *cmdenviron; /* environment for builtin command */ + + +struct backcmd { /* result of evalbackcmd */ + int fd; /* file descriptor to read from */ + char *buf; /* buffer */ + int nleft; /* number of chars in buffer */ + struct job *jp; /* job structure for command */ +}; + +void reseteval(void); + +/* flags in argument to evaltree/evalstring */ +#define EV_EXIT 01 /* exit after evaluating tree */ +#define EV_TESTED 02 /* exit status is checked; ignore -e flag */ +#define EV_BACKCMD 04 /* command executing within back quotes */ + +void evalstring(const char *, int); +union node; /* BLETCH for ansi C */ +void evaltree(union node *, int); +void evalbackcmd(union node *, struct backcmd *); + +/* in_function returns nonzero if we are currently evaluating a function */ +#define in_function() funcnest +extern int funcnest; +extern int evalskip; +extern int skipcount; + +/* reasons for skipping commands (see comment on breakcmd routine) */ +#define SKIPBREAK 1 +#define SKIPCONT 2 +#define SKIPRETURN 3 diff --git a/shell_cmds/sh/exec.c b/shell_cmds/sh/exec.c new file mode 100644 index 0000000..ae3cf6e --- /dev/null +++ b/shell_cmds/sh/exec.c @@ -0,0 +1,780 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)exec.c 8.4 (Berkeley) 6/8/95"; +#endif +#endif /* not lint */ +#include <sys/cdefs.h> +__FBSDID("$FreeBSD: head/bin/sh/exec.c 317882 2017-05-06 13:28:42Z jilles $"); + +#include <sys/types.h> +#include <sys/stat.h> +#include <unistd.h> +#include <fcntl.h> +#include <errno.h> +#include <paths.h> +#include <stdlib.h> + +/* + * When commands are first encountered, they are entered in a hash table. + * This ensures that a full path search will not have to be done for them + * on each invocation. + * + * We should investigate converting to a linear search, even though that + * would make the command name "hash" a misnomer. + */ + +#include "shell.h" +#include "main.h" +#include "nodes.h" +#include "parser.h" +#include "redir.h" +#include "eval.h" +#include "exec.h" +#include "builtins.h" +#include "var.h" +#include "options.h" +#include "input.h" +#include "output.h" +#include "syntax.h" +#include "memalloc.h" +#include "error.h" +#include "mystring.h" +#include "show.h" +#include "jobs.h" +#include "alias.h" + + +#ifdef __APPLE__ +#define eaccess(path, mode) faccessat(AT_FDCWD, path, mode, AT_EACCESS) +#endif /* __APPLE__ */ + +#define CMDTABLESIZE 31 /* should be prime */ + + + +struct tblentry { + struct tblentry *next; /* next entry in hash chain */ + union param param; /* definition of builtin function */ + int special; /* flag for special builtin commands */ + signed char cmdtype; /* index identifying command */ + char cmdname[]; /* name of command */ +}; + + +static struct tblentry *cmdtable[CMDTABLESIZE]; +static int cmdtable_cd = 0; /* cmdtable contains cd-dependent entries */ +int exerrno = 0; /* Last exec error */ + + +static void tryexec(char *, char **, char **); +static void printentry(struct tblentry *, int); +static struct tblentry *cmdlookup(const char *, int); +static void delete_cmd_entry(void); +static void addcmdentry(const char *, struct cmdentry *); + + + +/* + * Exec a program. Never returns. If you change this routine, you may + * have to change the find_command routine as well. + * + * The argv array may be changed and element argv[-1] should be writable. + */ + +void +shellexec(char **argv, char **envp, const char *path, int idx) +{ + char *cmdname; + int e; + + if (strchr(argv[0], '/') != NULL) { + tryexec(argv[0], argv, envp); + e = errno; + } else { + e = ENOENT; + while ((cmdname = padvance(&path, argv[0])) != NULL) { + if (--idx < 0 && pathopt == NULL) { + tryexec(cmdname, argv, envp); + if (errno != ENOENT && errno != ENOTDIR) + e = errno; + if (e == ENOEXEC) + break; + } + stunalloc(cmdname); + } + } + + /* Map to POSIX errors */ + if (e == ENOENT || e == ENOTDIR) { + exerrno = 127; + exerror(EXEXEC, "%s: not found", argv[0]); + } else { + exerrno = 126; + exerror(EXEXEC, "%s: %s", argv[0], strerror(e)); + } +} + + +static void +tryexec(char *cmd, char **argv, char **envp) +{ + int e, in; + ssize_t n; + char buf[256]; + + execve(cmd, argv, envp); + e = errno; + if (e == ENOEXEC) { + INTOFF; + in = open(cmd, O_RDONLY | O_NONBLOCK); + if (in != -1) { + n = pread(in, buf, sizeof buf, 0); + close(in); + if (n > 0 && memchr(buf, '\0', n) != NULL) { + errno = ENOEXEC; + return; + } + } + *argv = cmd; + *--argv = __DECONST(char *, _PATH_BSHELL); + execve(_PATH_BSHELL, argv, envp); + } + errno = e; +} + +/* + * Do a path search. The variable path (passed by reference) should be + * set to the start of the path before the first call; padvance will update + * this value as it proceeds. Successive calls to padvance will return + * the possible path expansions in sequence. If an option (indicated by + * a percent sign) appears in the path entry then the global variable + * pathopt will be set to point to it; otherwise pathopt will be set to + * NULL. + */ + +const char *pathopt; + +char * +padvance(const char **path, const char *name) +{ + const char *p, *start; + char *q; + size_t len, namelen; + + if (*path == NULL) + return NULL; + start = *path; + for (p = start; *p && *p != ':' && *p != '%'; p++) + ; /* nothing */ + namelen = strlen(name); + len = p - start + namelen + 2; /* "2" is for '/' and '\0' */ + STARTSTACKSTR(q); + CHECKSTRSPACE(len, q); + if (p != start) { + memcpy(q, start, p - start); + q += p - start; + *q++ = '/'; + } + memcpy(q, name, namelen + 1); + pathopt = NULL; + if (*p == '%') { + pathopt = ++p; + while (*p && *p != ':') p++; + } + if (*p == ':') + *path = p + 1; + else + *path = NULL; + return stalloc(len); +} + + + +/*** Command hashing code ***/ + + +int +hashcmd(int argc __unused, char **argv __unused) +{ + struct tblentry **pp; + struct tblentry *cmdp; + int c; + int verbose; + struct cmdentry entry; + char *name; + int errors; + + errors = 0; + verbose = 0; + while ((c = nextopt("rv")) != '\0') { + if (c == 'r') { + clearcmdentry(); + } else if (c == 'v') { + verbose++; + } + } + if (*argptr == NULL) { + for (pp = cmdtable ; pp < &cmdtable[CMDTABLESIZE] ; pp++) { + for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) { + if (cmdp->cmdtype == CMDNORMAL) + printentry(cmdp, verbose); + } + } + return 0; + } + while ((name = *argptr) != NULL) { + if ((cmdp = cmdlookup(name, 0)) != NULL + && cmdp->cmdtype == CMDNORMAL) + delete_cmd_entry(); + find_command(name, &entry, DO_ERR, pathval()); + if (entry.cmdtype == CMDUNKNOWN) + errors = 1; + else if (verbose) { + cmdp = cmdlookup(name, 0); + if (cmdp != NULL) + printentry(cmdp, verbose); + else { + outfmt(out2, "%s: not found\n", name); + errors = 1; + } + flushall(); + } + argptr++; + } + return errors; +} + + +static void +printentry(struct tblentry *cmdp, int verbose) +{ + int idx; + const char *path; + char *name; + + if (cmdp->cmdtype == CMDNORMAL) { + idx = cmdp->param.index; + path = pathval(); + do { + name = padvance(&path, cmdp->cmdname); + stunalloc(name); + } while (--idx >= 0); + out1str(name); + } else if (cmdp->cmdtype == CMDBUILTIN) { + out1fmt("builtin %s", cmdp->cmdname); + } else if (cmdp->cmdtype == CMDFUNCTION) { + out1fmt("function %s", cmdp->cmdname); + if (verbose) { + INTOFF; + name = commandtext(getfuncnode(cmdp->param.func)); + out1c(' '); + out1str(name); + ckfree(name); + INTON; + } +#ifdef DEBUG + } else { + error("internal error: cmdtype %d", cmdp->cmdtype); +#endif + } + out1c('\n'); +} + + + +/* + * Resolve a command name. If you change this routine, you may have to + * change the shellexec routine as well. + */ + +void +find_command(const char *name, struct cmdentry *entry, int act, + const char *path) +{ + struct tblentry *cmdp, loc_cmd; + int idx; + char *fullname; + struct stat statb; + int e; + int i; + int spec; + int cd; + + /* If name contains a slash, don't use the hash table */ + if (strchr(name, '/') != NULL) { + entry->cmdtype = CMDNORMAL; + entry->u.index = 0; + entry->special = 0; + return; + } + + cd = 0; + + /* If name is in the table, we're done */ + if ((cmdp = cmdlookup(name, 0)) != NULL) { + if (cmdp->cmdtype == CMDFUNCTION && act & DO_NOFUNC) + cmdp = NULL; + else + goto success; + } + + /* Check for builtin next */ + if ((i = find_builtin(name, &spec)) >= 0) { + INTOFF; + cmdp = cmdlookup(name, 1); + if (cmdp->cmdtype == CMDFUNCTION) + cmdp = &loc_cmd; + cmdp->cmdtype = CMDBUILTIN; + cmdp->param.index = i; + cmdp->special = spec; + INTON; + goto success; + } + + /* We have to search path. */ + + e = ENOENT; + idx = -1; + for (;(fullname = padvance(&path, name)) != NULL; stunalloc(fullname)) { + idx++; + if (pathopt) { + if (strncmp(pathopt, "func", 4) == 0) { + /* handled below */ + } else { + continue; /* ignore unimplemented options */ + } + } + if (fullname[0] != '/') + cd = 1; + if (stat(fullname, &statb) < 0) { + if (errno != ENOENT && errno != ENOTDIR) + e = errno; + continue; + } + e = EACCES; /* if we fail, this will be the error */ + if (!S_ISREG(statb.st_mode)) + continue; + if (pathopt) { /* this is a %func directory */ + readcmdfile(fullname); + if ((cmdp = cmdlookup(name, 0)) == NULL || cmdp->cmdtype != CMDFUNCTION) + error("%s not defined in %s", name, fullname); + stunalloc(fullname); + goto success; + } +#ifdef notdef + if (statb.st_uid == geteuid()) { + if ((statb.st_mode & 0100) == 0) + goto loop; + } else if (statb.st_gid == getegid()) { + if ((statb.st_mode & 010) == 0) + goto loop; + } else { + if ((statb.st_mode & 01) == 0) + goto loop; + } +#endif + TRACE(("searchexec \"%s\" returns \"%s\"\n", name, fullname)); + INTOFF; + stunalloc(fullname); + cmdp = cmdlookup(name, 1); + if (cmdp->cmdtype == CMDFUNCTION) + cmdp = &loc_cmd; + cmdp->cmdtype = CMDNORMAL; + cmdp->param.index = idx; + cmdp->special = 0; + INTON; + goto success; + } + + if (act & DO_ERR) { + if (e == ENOENT || e == ENOTDIR) + outfmt(out2, "%s: not found\n", name); + else + outfmt(out2, "%s: %s\n", name, strerror(e)); + } + entry->cmdtype = CMDUNKNOWN; + entry->u.index = 0; + entry->special = 0; + return; + +success: + if (cd) + cmdtable_cd = 1; + entry->cmdtype = cmdp->cmdtype; + entry->u = cmdp->param; + entry->special = cmdp->special; +} + + + +/* + * Search the table of builtin commands. + */ + +int +find_builtin(const char *name, int *special) +{ + const unsigned char *bp; + size_t len; + + len = strlen(name); + for (bp = builtincmd ; *bp ; bp += 2 + bp[0]) { + if (bp[0] == len && memcmp(bp + 2, name, len) == 0) { + *special = (bp[1] & BUILTIN_SPECIAL) != 0; + return bp[1] & ~BUILTIN_SPECIAL; + } + } + return -1; +} + + + +/* + * Called when a cd is done. If any entry in cmdtable depends on the current + * directory, simply clear cmdtable completely. + */ + +void +hashcd(void) +{ + if (cmdtable_cd) + clearcmdentry(); +} + + + +/* + * Called before PATH is changed. The argument is the new value of PATH; + * pathval() still returns the old value at this point. Called with + * interrupts off. + */ + +void +changepath(const char *newval __unused) +{ + clearcmdentry(); +} + + +/* + * Clear out cached utility locations. + */ + +void +clearcmdentry(void) +{ + struct tblentry **tblp; + struct tblentry **pp; + struct tblentry *cmdp; + + INTOFF; + for (tblp = cmdtable ; tblp < &cmdtable[CMDTABLESIZE] ; tblp++) { + pp = tblp; + while ((cmdp = *pp) != NULL) { + if (cmdp->cmdtype == CMDNORMAL) { + *pp = cmdp->next; + ckfree(cmdp); + } else { + pp = &cmdp->next; + } + } + } + cmdtable_cd = 0; + INTON; +} + + +/* + * Locate a command in the command hash table. If "add" is nonzero, + * add the command to the table if it is not already present. The + * variable "lastcmdentry" is set to point to the address of the link + * pointing to the entry, so that delete_cmd_entry can delete the + * entry. + */ + +static struct tblentry **lastcmdentry; + + +static struct tblentry * +cmdlookup(const char *name, int add) +{ + unsigned int hashval; + const char *p; + struct tblentry *cmdp; + struct tblentry **pp; + size_t len; + + p = name; + hashval = (unsigned char)*p << 4; + while (*p) + hashval += *p++; + pp = &cmdtable[hashval % CMDTABLESIZE]; + for (cmdp = *pp ; cmdp ; cmdp = cmdp->next) { + if (equal(cmdp->cmdname, name)) + break; + pp = &cmdp->next; + } + if (add && cmdp == NULL) { + INTOFF; + len = strlen(name); + cmdp = *pp = ckmalloc(sizeof (struct tblentry) + len + 1); + cmdp->next = NULL; + cmdp->cmdtype = CMDUNKNOWN; + memcpy(cmdp->cmdname, name, len + 1); + INTON; + } + lastcmdentry = pp; + return cmdp; +} + +/* + * Delete the command entry returned on the last lookup. + */ + +static void +delete_cmd_entry(void) +{ + struct tblentry *cmdp; + + INTOFF; + cmdp = *lastcmdentry; + *lastcmdentry = cmdp->next; + ckfree(cmdp); + INTON; +} + + + +/* + * Add a new command entry, replacing any existing command entry for + * the same name. + */ + +static void +addcmdentry(const char *name, struct cmdentry *entry) +{ + struct tblentry *cmdp; + + INTOFF; + cmdp = cmdlookup(name, 1); + if (cmdp->cmdtype == CMDFUNCTION) { + unreffunc(cmdp->param.func); + } + cmdp->cmdtype = entry->cmdtype; + cmdp->param = entry->u; + cmdp->special = entry->special; + INTON; +} + + +/* + * Define a shell function. + */ + +void +defun(const char *name, union node *func) +{ + struct cmdentry entry; + + INTOFF; + entry.cmdtype = CMDFUNCTION; + entry.u.func = copyfunc(func); + entry.special = 0; + addcmdentry(name, &entry); + INTON; +} + + +/* + * Delete a function if it exists. + * Called with interrupts off. + */ + +int +unsetfunc(const char *name) +{ + struct tblentry *cmdp; + + if ((cmdp = cmdlookup(name, 0)) != NULL && cmdp->cmdtype == CMDFUNCTION) { + unreffunc(cmdp->param.func); + delete_cmd_entry(); + return (0); + } + return (0); +} + + +/* + * Check if a function by a certain name exists. + */ +int +isfunc(const char *name) +{ + struct tblentry *cmdp; + cmdp = cmdlookup(name, 0); + return (cmdp != NULL && cmdp->cmdtype == CMDFUNCTION); +} + + +/* + * Shared code for the following builtin commands: + * type, command -v, command -V + */ + +int +typecmd_impl(int argc, char **argv, int cmd, const char *path) +{ + struct cmdentry entry; + struct tblentry *cmdp; + const char *const *pp; + struct alias *ap; + int i; + int error1 = 0; + + if (path != pathval()) + clearcmdentry(); + + for (i = 1; i < argc; i++) { + /* First look at the keywords */ + for (pp = parsekwd; *pp; pp++) + if (**pp == *argv[i] && equal(*pp, argv[i])) + break; + + if (*pp) { + if (cmd == TYPECMD_SMALLV) + out1fmt("%s\n", argv[i]); + else + out1fmt("%s is a shell keyword\n", argv[i]); + continue; + } + + /* Then look at the aliases */ + if ((ap = lookupalias(argv[i], 1)) != NULL) { + if (cmd == TYPECMD_SMALLV) { + out1fmt("alias %s=", argv[i]); + out1qstr(ap->val); + outcslow('\n', out1); + } else + out1fmt("%s is an alias for %s\n", argv[i], + ap->val); + continue; + } + + /* Then check if it is a tracked alias */ + if ((cmdp = cmdlookup(argv[i], 0)) != NULL) { + entry.cmdtype = cmdp->cmdtype; + entry.u = cmdp->param; + entry.special = cmdp->special; + } + else { + /* Finally use brute force */ + find_command(argv[i], &entry, 0, path); + } + + switch (entry.cmdtype) { + case CMDNORMAL: { + if (strchr(argv[i], '/') == NULL) { + const char *path2 = path; + char *name; + int j = entry.u.index; + do { + name = padvance(&path2, argv[i]); + stunalloc(name); + } while (--j >= 0); + if (cmd == TYPECMD_SMALLV) + out1fmt("%s\n", name); + else + out1fmt("%s is%s %s\n", argv[i], + (cmdp && cmd == TYPECMD_TYPE) ? + " a tracked alias for" : "", + name); + } else { + if (eaccess(argv[i], X_OK) == 0) { + if (cmd == TYPECMD_SMALLV) + out1fmt("%s\n", argv[i]); + else + out1fmt("%s is %s\n", argv[i], + argv[i]); + } else { + if (cmd != TYPECMD_SMALLV) + outfmt(out2, "%s: %s\n", + argv[i], strerror(errno)); + error1 |= 127; + } + } + break; + } + case CMDFUNCTION: + if (cmd == TYPECMD_SMALLV) + out1fmt("%s\n", argv[i]); + else + out1fmt("%s is a shell function\n", argv[i]); + break; + + case CMDBUILTIN: + if (cmd == TYPECMD_SMALLV) + out1fmt("%s\n", argv[i]); + else if (entry.special) + out1fmt("%s is a special shell builtin\n", + argv[i]); + else + out1fmt("%s is a shell builtin\n", argv[i]); + break; + + default: + if (cmd != TYPECMD_SMALLV) + outfmt(out2, "%s: not found\n", argv[i]); + error1 |= 127; + break; + } + } + + if (path != pathval()) + clearcmdentry(); + + return error1; +} + +/* + * Locate and print what a word is... + */ + +int +typecmd(int argc, char **argv) +{ + if (argc > 2 && strcmp(argv[1], "--") == 0) + argc--, argv++; + return typecmd_impl(argc, argv, TYPECMD_TYPE, bltinlookup("PATH", 1)); +} diff --git a/shell_cmds/sh/exec.h b/shell_cmds/sh/exec.h new file mode 100644 index 0000000..352077f --- /dev/null +++ b/shell_cmds/sh/exec.h @@ -0,0 +1,77 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)exec.h 8.3 (Berkeley) 6/8/95 + * $FreeBSD: head/bin/sh/exec.h 314436 2017-02-28 23:42:47Z imp $ + */ + +/* values of cmdtype */ +#define CMDUNKNOWN -1 /* no entry in table for command */ +#define CMDNORMAL 0 /* command is an executable program */ +#define CMDBUILTIN 1 /* command is a shell builtin */ +#define CMDFUNCTION 2 /* command is a shell function */ + +/* values for typecmd_impl's third parameter */ +enum { + TYPECMD_SMALLV, /* command -v */ + TYPECMD_BIGV, /* command -V */ + TYPECMD_TYPE /* type */ +}; + +union node; +struct cmdentry { + int cmdtype; + union param { + int index; + struct funcdef *func; + } u; + int special; +}; + + +/* action to find_command() */ +#define DO_ERR 0x01 /* prints errors */ +#define DO_NOFUNC 0x02 /* don't return shell functions, for command */ + +extern const char *pathopt; /* set by padvance */ +extern int exerrno; /* last exec error */ + +void shellexec(char **, char **, const char *, int) __dead2; +char *padvance(const char **, const char *); +void find_command(const char *, struct cmdentry *, int, const char *); +int find_builtin(const char *, int *); +void hashcd(void); +void changepath(const char *); +void defun(const char *, union node *); +int unsetfunc(const char *); +int isfunc(const char *); +int typecmd_impl(int, char **, int, const char *); +void clearcmdentry(void); diff --git a/shell_cmds/sh/expand.c b/shell_cmds/sh/expand.c new file mode 100644 index 0000000..7a31c95 --- /dev/null +++ b/shell_cmds/sh/expand.c @@ -0,0 +1,1550 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * Copyright (c) 1997-2005 + * Herbert Xu <herbert@gondor.apana.org.au>. All rights reserved. + * Copyright (c) 2010-2015 + * Jilles Tjoelker <jilles@stack.nl>. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)expand.c 8.5 (Berkeley) 5/15/95"; +#endif +#endif /* not lint */ +#include <sys/cdefs.h> +__FBSDID("$FreeBSD: head/bin/sh/expand.c 318269 2017-05-14 13:14:19Z jilles $"); + +#include <sys/types.h> +#include <sys/time.h> +#include <sys/stat.h> +#include <dirent.h> +#include <errno.h> +#include <inttypes.h> +#include <limits.h> +#include <pwd.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <wchar.h> +#include <wctype.h> + +/* + * Routines to expand arguments to commands. We have to deal with + * backquotes, shell variables, and file metacharacters. + */ + +#include "shell.h" +#include "main.h" +#include "nodes.h" +#include "eval.h" +#include "expand.h" +#include "syntax.h" +#include "parser.h" +#include "jobs.h" +#include "options.h" +#include "var.h" +#include "input.h" +#include "output.h" +#include "memalloc.h" +#include "error.h" +#include "mystring.h" +#include "arith.h" +#include "show.h" +#include "builtins.h" + +enum wordstate { WORD_IDLE, WORD_WS_DELIMITED, WORD_QUOTEMARK }; + +struct worddest { + struct arglist *list; + enum wordstate state; +}; + +static char *expdest; /* output of current string */ + +static const char *argstr(const char *, struct nodelist **restrict, int, + struct worddest *); +static const char *exptilde(const char *, int); +static const char *expari(const char *, struct nodelist **restrict, int, + struct worddest *); +static void expbackq(union node *, int, int, struct worddest *); +static const char *subevalvar_trim(const char *, struct nodelist **restrict, + int, int, int); +static const char *subevalvar_misc(const char *, struct nodelist **restrict, + const char *, int, int, int); +static const char *evalvar(const char *, struct nodelist **restrict, int, + struct worddest *); +static int varisset(const char *, int); +static void strtodest(const char *, int, int, int, struct worddest *); +static void reprocess(int, int, int, int, struct worddest *); +static void varvalue(const char *, int, int, int, struct worddest *); +static void expandmeta(char *, struct arglist *); +static void expmeta(char *, char *, struct arglist *); +static int expsortcmp(const void *, const void *); +static int patmatch(const char *, const char *); +static void cvtnum(int, char *); +static int collate_range_cmp(wchar_t, wchar_t); + +void +emptyarglist(struct arglist *list) +{ + + list->args = list->smallarg; + list->count = 0; + list->capacity = sizeof(list->smallarg) / sizeof(list->smallarg[0]); +} + +void +appendarglist(struct arglist *list, char *str) +{ + char **newargs; + int newcapacity; + + if (list->count >= list->capacity) { + newcapacity = list->capacity * 2; + if (newcapacity < 16) + newcapacity = 16; + if (newcapacity > INT_MAX / (int)sizeof(newargs[0])) + error("Too many entries in arglist"); + newargs = stalloc(newcapacity * sizeof(newargs[0])); + memcpy(newargs, list->args, list->count * sizeof(newargs[0])); + list->args = newargs; + list->capacity = newcapacity; + } + list->args[list->count++] = str; +} + +static int +collate_range_cmp(wchar_t c1, wchar_t c2) +{ + wchar_t s1[2], s2[2]; + + s1[0] = c1; + s1[1] = L'\0'; + s2[0] = c2; + s2[1] = L'\0'; + return (wcscoll(s1, s2)); +} + +static char * +stputs_quotes(const char *data, const char *syntax, char *p) +{ + while (*data) { + CHECKSTRSPACE(2, p); + if (syntax[(int)*data] == CCTL) + USTPUTC(CTLESC, p); + USTPUTC(*data++, p); + } + return (p); +} +#define STPUTS_QUOTES(data, syntax, p) p = stputs_quotes((data), syntax, p) + +static char * +nextword(char c, int flag, char *p, struct worddest *dst) +{ + int is_ws; + + is_ws = c == '\t' || c == '\n' || c == ' '; + if (p != stackblock() || (is_ws ? dst->state == WORD_QUOTEMARK : + dst->state != WORD_WS_DELIMITED) || c == '\0') { + STPUTC('\0', p); + if (flag & EXP_GLOB) + expandmeta(grabstackstr(p), dst->list); + else + appendarglist(dst->list, grabstackstr(p)); + dst->state = is_ws ? WORD_WS_DELIMITED : WORD_IDLE; + } else if (!is_ws && dst->state == WORD_WS_DELIMITED) + dst->state = WORD_IDLE; + /* Reserve space while the stack string is empty. */ + appendarglist(dst->list, NULL); + dst->list->count--; + STARTSTACKSTR(p); + return p; +} +#define NEXTWORD(c, flag, p, dstlist) p = nextword(c, flag, p, dstlist) + +static char * +stputs_split(const char *data, const char *syntax, int flag, char *p, + struct worddest *dst) +{ + const char *ifs; + char c; + + ifs = ifsset() ? ifsval() : " \t\n"; + while (*data) { + CHECKSTRSPACE(2, p); + c = *data++; + if (strchr(ifs, c) != NULL) { + NEXTWORD(c, flag, p, dst); + continue; + } + if (flag & EXP_GLOB && syntax[(int)c] == CCTL) + USTPUTC(CTLESC, p); + USTPUTC(c, p); + } + return (p); +} +#define STPUTS_SPLIT(data, syntax, flag, p, dst) p = stputs_split((data), syntax, flag, p, dst) + +/* + * Perform expansions on an argument, placing the resulting list of arguments + * in arglist. Parameter expansion, command substitution and arithmetic + * expansion are always performed; additional expansions can be requested + * via flag (EXP_*). + * The result is left in the stack string. + * When arglist is NULL, perform here document expansion. + * + * When doing something that may cause this to be re-entered, make sure + * the stack string is empty via grabstackstr() and do not assume expdest + * remains valid. + */ +void +expandarg(union node *arg, struct arglist *arglist, int flag) +{ + struct worddest exparg; + struct nodelist *argbackq; + + if (fflag) + flag &= ~EXP_GLOB; + argbackq = arg->narg.backquote; + exparg.list = arglist; + exparg.state = WORD_IDLE; + STARTSTACKSTR(expdest); + argstr(arg->narg.text, &argbackq, flag, &exparg); + if (arglist == NULL) { + STACKSTRNUL(expdest); + return; /* here document expanded */ + } + if ((flag & EXP_SPLIT) == 0 || expdest != stackblock() || + exparg.state == WORD_QUOTEMARK) { + STPUTC('\0', expdest); + if (flag & EXP_SPLIT) { + if (flag & EXP_GLOB) + expandmeta(grabstackstr(expdest), exparg.list); + else + appendarglist(exparg.list, grabstackstr(expdest)); + } + } + if ((flag & EXP_SPLIT) == 0) + appendarglist(arglist, grabstackstr(expdest)); +} + + + +/* + * Perform parameter expansion, command substitution and arithmetic + * expansion, and tilde expansion if requested via EXP_TILDE/EXP_VARTILDE. + * Processing ends at a CTLENDVAR or CTLENDARI character as well as '\0'. + * This is used to expand word in ${var+word} etc. + * If EXP_GLOB or EXP_CASE are set, keep and/or generate CTLESC + * characters to allow for further processing. + * + * If EXP_SPLIT is set, dst receives any complete words produced. + */ +static const char * +argstr(const char *p, struct nodelist **restrict argbackq, int flag, + struct worddest *dst) +{ + char c; + int quotes = flag & (EXP_GLOB | EXP_CASE); /* do CTLESC */ + int firsteq = 1; + int split_lit; + int lit_quoted; + + split_lit = flag & EXP_SPLIT_LIT; + lit_quoted = flag & EXP_LIT_QUOTED; + flag &= ~(EXP_SPLIT_LIT | EXP_LIT_QUOTED); + if (*p == '~' && (flag & (EXP_TILDE | EXP_VARTILDE))) + p = exptilde(p, flag); + for (;;) { + CHECKSTRSPACE(2, expdest); + switch (c = *p++) { + case '\0': + return (p - 1); + case CTLENDVAR: + case CTLENDARI: + return (p); + case CTLQUOTEMARK: + lit_quoted = 1; + /* "$@" syntax adherence hack */ + if (p[0] == CTLVAR && (p[1] & VSQUOTE) != 0 && + p[2] == '@' && p[3] == '=') + break; + if ((flag & EXP_SPLIT) != 0 && expdest == stackblock()) + dst->state = WORD_QUOTEMARK; + break; + case CTLQUOTEEND: + lit_quoted = 0; + break; + case CTLESC: + c = *p++; + if (split_lit && !lit_quoted && + strchr(ifsset() ? ifsval() : " \t\n", c) != NULL) { + NEXTWORD(c, flag, expdest, dst); + break; + } + if (quotes) + USTPUTC(CTLESC, expdest); + USTPUTC(c, expdest); + break; + case CTLVAR: + p = evalvar(p, argbackq, flag, dst); + break; + case CTLBACKQ: + case CTLBACKQ|CTLQUOTE: + expbackq((*argbackq)->n, c & CTLQUOTE, flag, dst); + *argbackq = (*argbackq)->next; + break; + case CTLARI: + p = expari(p, argbackq, flag, dst); + break; + case ':': + case '=': + /* + * sort of a hack - expand tildes in variable + * assignments (after the first '=' and after ':'s). + */ + if (split_lit && !lit_quoted && + strchr(ifsset() ? ifsval() : " \t\n", c) != NULL) { + NEXTWORD(c, flag, expdest, dst); + break; + } + USTPUTC(c, expdest); + if (flag & EXP_VARTILDE && *p == '~' && + (c != '=' || firsteq)) { + if (c == '=') + firsteq = 0; + p = exptilde(p, flag); + } + break; + default: + if (split_lit && !lit_quoted && + strchr(ifsset() ? ifsval() : " \t\n", c) != NULL) { + NEXTWORD(c, flag, expdest, dst); + break; + } + USTPUTC(c, expdest); + } + } +} + +/* + * Perform tilde expansion, placing the result in the stack string and + * returning the next position in the input string to process. + */ +static const char * +exptilde(const char *p, int flag) +{ + char c; + const char *startp = p; + const char *user; + struct passwd *pw; + char *home; + int len; + + for (;;) { + c = *p; + switch(c) { + case CTLESC: /* This means CTL* are always considered quoted. */ + case CTLVAR: + case CTLBACKQ: + case CTLBACKQ | CTLQUOTE: + case CTLARI: + case CTLENDARI: + case CTLQUOTEMARK: + return (startp); + case ':': + if ((flag & EXP_VARTILDE) == 0) + break; + /* FALLTHROUGH */ + case '\0': + case '/': + case CTLENDVAR: + len = p - startp - 1; + STPUTBIN(startp + 1, len, expdest); + STACKSTRNUL(expdest); + user = expdest - len; + if (*user == '\0') { + home = lookupvar("HOME"); + } else { + pw = getpwnam(user); + home = pw != NULL ? pw->pw_dir : NULL; + } + STADJUST(-len, expdest); + if (home == NULL || *home == '\0') + return (startp); + strtodest(home, flag, VSNORMAL, 1, NULL); + return (p); + } + p++; + } +} + + +/* + * Expand arithmetic expression. + */ +static const char * +expari(const char *p, struct nodelist **restrict argbackq, int flag, + struct worddest *dst) +{ + char *q, *start; + arith_t result; + int begoff; + int quoted; + int adj; + + quoted = *p++ == '"'; + begoff = expdest - stackblock(); + p = argstr(p, argbackq, 0, NULL); + STPUTC('\0', expdest); + start = stackblock() + begoff; + + q = grabstackstr(expdest); + result = arith(start); + ungrabstackstr(q, expdest); + + start = stackblock() + begoff; + adj = start - expdest; + STADJUST(adj, expdest); + + CHECKSTRSPACE((int)(DIGITS(result) + 1), expdest); + fmtstr(expdest, DIGITS(result), ARITH_FORMAT_STR, result); + adj = strlen(expdest); + STADJUST(adj, expdest); + /* + * If this is quoted, a '-' must not indicate a range in [...]. + * If this is not quoted, splitting may occur. + */ + if (quoted ? + result < 0 && begoff > 1 && flag & (EXP_GLOB | EXP_CASE) : + flag & EXP_SPLIT) + reprocess(expdest - adj - stackblock(), flag, VSNORMAL, quoted, + dst); + return p; +} + + +/* + * Perform command substitution. + */ +static void +expbackq(union node *cmd, int quoted, int flag, struct worddest *dst) +{ + struct backcmd in; + int i; + char buf[128]; + char *p; + char *dest = expdest; + char lastc; + char const *syntax = quoted? DQSYNTAX : BASESYNTAX; + int quotes = flag & (EXP_GLOB | EXP_CASE); + size_t nnl; + const char *ifs; + int startloc; + + INTOFF; + p = grabstackstr(dest); + evalbackcmd(cmd, &in); + ungrabstackstr(p, dest); + + p = in.buf; + startloc = dest - stackblock(); + nnl = 0; + if (!quoted && flag & EXP_SPLIT) + ifs = ifsset() ? ifsval() : " \t\n"; + else + ifs = ""; + /* Remove trailing newlines */ + for (;;) { + if (--in.nleft < 0) { + if (in.fd < 0) + break; + while ((i = read(in.fd, buf, sizeof buf)) < 0 && errno == EINTR) + ; + TRACE(("expbackq: read returns %d\n", i)); + if (i <= 0) + break; + p = buf; + in.nleft = i - 1; + } + lastc = *p++; + if (lastc == '\0') + continue; + if (nnl > 0 && lastc != '\n') { + NEXTWORD('\n', flag, dest, dst); + nnl = 0; + } + if (strchr(ifs, lastc) != NULL) { + if (lastc == '\n') + nnl++; + else + NEXTWORD(lastc, flag, dest, dst); + } else { + CHECKSTRSPACE(2, dest); + if (quotes && syntax[(int)lastc] == CCTL) + USTPUTC(CTLESC, dest); + USTPUTC(lastc, dest); + } + } + while (dest > stackblock() + startloc && STTOPC(dest) == '\n') + STUNPUTC(dest); + + if (in.fd >= 0) + close(in.fd); + if (in.buf) + ckfree(in.buf); + if (in.jp) { + p = grabstackstr(dest); + exitstatus = waitforjob(in.jp, (int *)NULL); + ungrabstackstr(p, dest); + } + TRACE(("expbackq: done\n")); + expdest = dest; + INTON; +} + + + +static void +recordleft(const char *str, const char *loc, char *startp) +{ + int amount; + + amount = ((str - 1) - (loc - startp)) - expdest; + STADJUST(amount, expdest); + while (loc != str - 1) + *startp++ = *loc++; +} + +static const char * +subevalvar_trim(const char *p, struct nodelist **restrict argbackq, int strloc, + int subtype, int startloc) +{ + char *startp; + char *loc = NULL; + char *str; + int c = 0; + int amount; + + p = argstr(p, argbackq, EXP_CASE | EXP_TILDE, NULL); + STACKSTRNUL(expdest); + startp = stackblock() + startloc; + str = stackblock() + strloc; + + switch (subtype) { + case VSTRIMLEFT: + for (loc = startp; loc < str; loc++) { + c = *loc; + *loc = '\0'; + if (patmatch(str, startp)) { + *loc = c; + recordleft(str, loc, startp); + return p; + } + *loc = c; + } + break; + + case VSTRIMLEFTMAX: + for (loc = str - 1; loc >= startp;) { + c = *loc; + *loc = '\0'; + if (patmatch(str, startp)) { + *loc = c; + recordleft(str, loc, startp); + return p; + } + *loc = c; + loc--; + } + break; + + case VSTRIMRIGHT: + for (loc = str - 1; loc >= startp;) { + if (patmatch(str, loc)) { + amount = loc - expdest; + STADJUST(amount, expdest); + return p; + } + loc--; + } + break; + + case VSTRIMRIGHTMAX: + for (loc = startp; loc < str - 1; loc++) { + if (patmatch(str, loc)) { + amount = loc - expdest; + STADJUST(amount, expdest); + return p; + } + } + break; + + + default: + abort(); + } + amount = (expdest - stackblock() - strloc) + 1; + STADJUST(-amount, expdest); + return p; +} + + +static const char * +subevalvar_misc(const char *p, struct nodelist **restrict argbackq, + const char *var, int subtype, int startloc, int varflags) +{ + char *startp; + int amount; + + p = argstr(p, argbackq, EXP_TILDE, NULL); + STACKSTRNUL(expdest); + startp = stackblock() + startloc; + + switch (subtype) { + case VSASSIGN: + setvar(var, startp, 0); + amount = startp - expdest; + STADJUST(amount, expdest); + return p; + + case VSQUESTION: + if (*p != CTLENDVAR) { + outfmt(out2, "%s\n", startp); + error((char *)NULL); + } + error("%.*s: parameter %snot set", (int)(p - var - 1), + var, (varflags & VSNUL) ? "null or " : ""); + + default: + abort(); + } +} + + +/* + * Expand a variable, and return a pointer to the next character in the + * input string. + */ + +static const char * +evalvar(const char *p, struct nodelist **restrict argbackq, int flag, + struct worddest *dst) +{ + int subtype; + int varflags; + const char *var; + const char *val; + int patloc; + int c; + int set; + int special; + int startloc; + int varlen; + int varlenb; + char buf[21]; + + varflags = (unsigned char)*p++; + subtype = varflags & VSTYPE; + var = p; + special = 0; + if (! is_name(*p)) + special = 1; + p = strchr(p, '=') + 1; + if (varflags & VSLINENO) { + set = 1; + special = 1; + val = NULL; + } else if (special) { + set = varisset(var, varflags & VSNUL); + val = NULL; + } else { + val = bltinlookup(var, 1); + if (val == NULL || ((varflags & VSNUL) && val[0] == '\0')) { + val = NULL; + set = 0; + } else + set = 1; + } + varlen = 0; + startloc = expdest - stackblock(); + if (!set && uflag && *var != '@' && *var != '*') { + switch (subtype) { + case VSNORMAL: + case VSTRIMLEFT: + case VSTRIMLEFTMAX: + case VSTRIMRIGHT: + case VSTRIMRIGHTMAX: + case VSLENGTH: + error("%.*s: parameter not set", (int)(p - var - 1), + var); + } + } + if (set && subtype != VSPLUS) { + /* insert the value of the variable */ + if (special) { + if (varflags & VSLINENO) { + if (p - var > (ptrdiff_t)sizeof(buf)) + abort(); + memcpy(buf, var, p - var - 1); + buf[p - var - 1] = '\0'; + strtodest(buf, flag, subtype, + varflags & VSQUOTE, dst); + } else + varvalue(var, varflags & VSQUOTE, subtype, flag, + dst); + if (subtype == VSLENGTH) { + varlenb = expdest - stackblock() - startloc; + varlen = varlenb; + if (localeisutf8) { + val = stackblock() + startloc; + for (;val != expdest; val++) + if ((*val & 0xC0) == 0x80) + varlen--; + } + STADJUST(-varlenb, expdest); + } + } else { + if (subtype == VSLENGTH) { + for (;*val; val++) + if (!localeisutf8 || + (*val & 0xC0) != 0x80) + varlen++; + } + else + strtodest(val, flag, subtype, + varflags & VSQUOTE, dst); + } + } + + if (subtype == VSPLUS) + set = ! set; + + switch (subtype) { + case VSLENGTH: + cvtnum(varlen, buf); + strtodest(buf, flag, VSNORMAL, varflags & VSQUOTE, dst); + break; + + case VSNORMAL: + return p; + + case VSPLUS: + case VSMINUS: + if (!set) { + return argstr(p, argbackq, + flag | (flag & EXP_SPLIT ? EXP_SPLIT_LIT : 0) | + (varflags & VSQUOTE ? EXP_LIT_QUOTED : 0), dst); + } + break; + + case VSTRIMLEFT: + case VSTRIMLEFTMAX: + case VSTRIMRIGHT: + case VSTRIMRIGHTMAX: + if (!set) + break; + /* + * Terminate the string and start recording the pattern + * right after it + */ + STPUTC('\0', expdest); + patloc = expdest - stackblock(); + p = subevalvar_trim(p, argbackq, patloc, subtype, startloc); + reprocess(startloc, flag, VSNORMAL, varflags & VSQUOTE, dst); + if (flag & EXP_SPLIT && *var == '@' && varflags & VSQUOTE) + dst->state = WORD_QUOTEMARK; + return p; + + case VSASSIGN: + case VSQUESTION: + if (!set) { + p = subevalvar_misc(p, argbackq, var, subtype, + startloc, varflags); + /* assert(subtype == VSASSIGN); */ + val = lookupvar(var); + strtodest(val, flag, subtype, varflags & VSQUOTE, dst); + return p; + } + break; + + case VSERROR: + c = p - var - 1; + error("${%.*s%s}: Bad substitution", c, var, + (c > 0 && *p != CTLENDVAR) ? "..." : ""); + + default: + abort(); + } + + { /* skip to end of alternative */ + int nesting = 1; + for (;;) { + if ((c = *p++) == CTLESC) + p++; + else if (c == CTLBACKQ || c == (CTLBACKQ|CTLQUOTE)) + *argbackq = (*argbackq)->next; + else if (c == CTLVAR) { + if ((*p++ & VSTYPE) != VSNORMAL) + nesting++; + } else if (c == CTLENDVAR) { + if (--nesting == 0) + break; + } + } + } + return p; +} + + + +/* + * Test whether a special or positional parameter is set. + */ + +static int +varisset(const char *name, int nulok) +{ + + if (*name == '!') + return backgndpidset(); + else if (*name == '@' || *name == '*') { + if (*shellparam.p == NULL) + return 0; + + if (nulok) { + char **av; + + for (av = shellparam.p; *av; av++) + if (**av != '\0') + return 1; + return 0; + } + } else if (is_digit(*name)) { + char *ap; + long num; + + errno = 0; + num = strtol(name, NULL, 10); + if (errno != 0 || num > shellparam.nparam) + return 0; + + if (num == 0) + ap = arg0; + else + ap = shellparam.p[num - 1]; + + if (nulok && (ap == NULL || *ap == '\0')) + return 0; + } + return 1; +} + +static void +strtodest(const char *p, int flag, int subtype, int quoted, + struct worddest *dst) +{ + if (subtype == VSLENGTH || subtype == VSTRIMLEFT || + subtype == VSTRIMLEFTMAX || subtype == VSTRIMRIGHT || + subtype == VSTRIMRIGHTMAX) + STPUTS(p, expdest); + else if (flag & EXP_SPLIT && !quoted && dst != NULL) + STPUTS_SPLIT(p, BASESYNTAX, flag, expdest, dst); + else if (flag & (EXP_GLOB | EXP_CASE)) + STPUTS_QUOTES(p, quoted ? DQSYNTAX : BASESYNTAX, expdest); + else + STPUTS(p, expdest); +} + +static void +reprocess(int startloc, int flag, int subtype, int quoted, + struct worddest *dst) +{ + static char *buf = NULL; + static size_t buflen = 0; + char *startp; + size_t len, zpos, zlen; + + startp = stackblock() + startloc; + len = expdest - startp; + if (len >= SIZE_MAX / 2) + abort(); + INTOFF; + if (len >= buflen) { + ckfree(buf); + buf = NULL; + } + if (buflen < 128) + buflen = 128; + while (len >= buflen) + buflen <<= 1; + if (buf == NULL) + buf = ckmalloc(buflen); + INTON; + memcpy(buf, startp, len); + buf[len] = '\0'; + STADJUST(-len, expdest); + for (zpos = 0;;) { + zlen = strlen(buf + zpos); + strtodest(buf + zpos, flag, subtype, quoted, dst); + zpos += zlen + 1; + if (zpos == len + 1) + break; + if (flag & EXP_SPLIT && (quoted || (zlen > 0 && zpos < len))) + NEXTWORD('\0', flag, expdest, dst); + } +} + +/* + * Add the value of a special or positional parameter to the stack string. + */ + +static void +varvalue(const char *name, int quoted, int subtype, int flag, + struct worddest *dst) +{ + int num; + char *p; + int i; + int splitlater; + char sep[2]; + char **ap; + char buf[(NSHORTOPTS > 10 ? NSHORTOPTS : 10) + 1]; + + if (subtype == VSLENGTH) + flag &= ~EXP_FULL; + splitlater = subtype == VSTRIMLEFT || subtype == VSTRIMLEFTMAX || + subtype == VSTRIMRIGHT || subtype == VSTRIMRIGHTMAX; + + switch (*name) { + case '$': + num = rootpid; + break; + case '?': + num = oexitstatus; + break; + case '#': + num = shellparam.nparam; + break; + case '!': + num = backgndpidval(); + break; + case '-': + p = buf; + for (i = 0 ; i < NSHORTOPTS ; i++) { + if (optval[i]) + *p++ = optletter[i]; + } + *p = '\0'; + strtodest(buf, flag, subtype, quoted, dst); + return; + case '@': + if (flag & EXP_SPLIT && quoted) { + for (ap = shellparam.p ; (p = *ap++) != NULL ; ) { + strtodest(p, flag, subtype, quoted, dst); + if (*ap) { + if (splitlater) + STPUTC('\0', expdest); + else + NEXTWORD('\0', flag, expdest, + dst); + } + } + if (shellparam.nparam > 0) + dst->state = WORD_QUOTEMARK; + return; + } + /* FALLTHROUGH */ + case '*': + if (ifsset()) + sep[0] = ifsval()[0]; + else + sep[0] = ' '; + sep[1] = '\0'; + for (ap = shellparam.p ; (p = *ap++) != NULL ; ) { + strtodest(p, flag, subtype, quoted, dst); + if (!*ap) + break; + if (sep[0]) + strtodest(sep, flag, subtype, quoted, dst); + else if (flag & EXP_SPLIT && !quoted && **ap != '\0') { + if (splitlater) + STPUTC('\0', expdest); + else + NEXTWORD('\0', flag, expdest, dst); + } + } + return; + default: + if (is_digit(*name)) { + num = atoi(name); + if (num == 0) + p = arg0; + else if (num > 0 && num <= shellparam.nparam) + p = shellparam.p[num - 1]; + else + return; + strtodest(p, flag, subtype, quoted, dst); + } + return; + } + cvtnum(num, buf); + strtodest(buf, flag, subtype, quoted, dst); +} + + + +static char expdir[PATH_MAX]; +#define expdir_end (expdir + sizeof(expdir)) + +/* + * Perform pathname generation and remove control characters. + * At this point, the only control characters should be CTLESC. + * The results are stored in the list dstlist. + */ +static void +expandmeta(char *pattern, struct arglist *dstlist) +{ + char *p; + int firstmatch; + char c; + + firstmatch = dstlist->count; + p = pattern; + for (; (c = *p) != '\0'; p++) { + /* fast check for meta chars */ + if (c == '*' || c == '?' || c == '[') { + INTOFF; + expmeta(expdir, pattern, dstlist); + INTON; + break; + } + } + if (dstlist->count == firstmatch) { + /* + * no matches + */ + rmescapes(pattern); + appendarglist(dstlist, pattern); + } else { + qsort(&dstlist->args[firstmatch], + dstlist->count - firstmatch, + sizeof(dstlist->args[0]), expsortcmp); + } +} + + +/* + * Do metacharacter (i.e. *, ?, [...]) expansion. + */ + +static void +expmeta(char *enddir, char *name, struct arglist *arglist) +{ + const char *p; + const char *q; + const char *start; + char *endname; + int metaflag; + struct stat statb; + DIR *dirp; + struct dirent *dp; + int atend; + int matchdot; + int esc; + int namlen; + + metaflag = 0; + start = name; + for (p = name; esc = 0, *p; p += esc + 1) { + if (*p == '*' || *p == '?') + metaflag = 1; + else if (*p == '[') { + q = p + 1; + if (*q == '!' || *q == '^') + q++; + for (;;) { + if (*q == CTLESC) + q++; + if (*q == '/' || *q == '\0') + break; + if (*++q == ']') { + metaflag = 1; + break; + } + } + } else if (*p == '\0') + break; + else { + if (*p == CTLESC) + esc++; + if (p[esc] == '/') { + if (metaflag) + break; + start = p + esc + 1; + } + } + } + if (metaflag == 0) { /* we've reached the end of the file name */ + if (enddir != expdir) + metaflag++; + for (p = name ; ; p++) { + if (*p == CTLESC) + p++; + *enddir++ = *p; + if (*p == '\0') + break; + if (enddir == expdir_end) + return; + } + if (metaflag == 0 || lstat(expdir, &statb) >= 0) + appendarglist(arglist, stsavestr(expdir)); + return; + } + endname = name + (p - name); + if (start != name) { + p = name; + while (p < start) { + if (*p == CTLESC) + p++; + *enddir++ = *p++; + if (enddir == expdir_end) + return; + } + } + if (enddir == expdir) { + p = "."; + } else if (enddir == expdir + 1 && *expdir == '/') { + p = "/"; + } else { + p = expdir; + enddir[-1] = '\0'; + } + if ((dirp = opendir(p)) == NULL) + return; + if (enddir != expdir) + enddir[-1] = '/'; + if (*endname == 0) { + atend = 1; + } else { + atend = 0; + *endname = '\0'; + endname += esc + 1; + } + matchdot = 0; + p = start; + if (*p == CTLESC) + p++; + if (*p == '.') + matchdot++; + while (! int_pending() && (dp = readdir(dirp)) != NULL) { + if (dp->d_name[0] == '.' && ! matchdot) + continue; + if (patmatch(start, dp->d_name)) { + namlen = dp->d_namlen; + if (enddir + namlen + 1 > expdir_end) + continue; + memcpy(enddir, dp->d_name, namlen + 1); + if (atend) + appendarglist(arglist, stsavestr(expdir)); + else { + if (dp->d_type != DT_UNKNOWN && + dp->d_type != DT_DIR && + dp->d_type != DT_LNK) + continue; + if (enddir + namlen + 2 > expdir_end) + continue; + enddir[namlen] = '/'; + enddir[namlen + 1] = '\0'; + expmeta(enddir + namlen + 1, endname, arglist); + } + } + } + closedir(dirp); + if (! atend) + endname[-esc - 1] = esc ? CTLESC : '/'; +} + + +static int +expsortcmp(const void *p1, const void *p2) +{ + const char *s1 = *(const char * const *)p1; + const char *s2 = *(const char * const *)p2; + + return (strcoll(s1, s2)); +} + + + +static wchar_t +get_wc(const char **p) +{ + wchar_t c; + int chrlen; + + chrlen = mbtowc(&c, *p, 4); + if (chrlen == 0) + return 0; + else if (chrlen == -1) + c = 0; + else + *p += chrlen; + return c; +} + + +/* + * See if a character matches a character class, starting at the first colon + * of "[:class:]". + * If a valid character class is recognized, a pointer to the next character + * after the final closing bracket is stored into *end, otherwise a null + * pointer is stored into *end. + */ +static int +match_charclass(const char *p, wchar_t chr, const char **end) +{ + char name[20]; + const char *nameend; + wctype_t cclass; + + *end = NULL; + p++; + nameend = strstr(p, ":]"); + if (nameend == NULL || (size_t)(nameend - p) >= sizeof(name) || + nameend == p) + return 0; + memcpy(name, p, nameend - p); + name[nameend - p] = '\0'; + *end = nameend + 2; + cclass = wctype(name); + /* An unknown class matches nothing but is valid nevertheless. */ + if (cclass == 0) + return 0; + return iswctype(chr, cclass); +} + + +/* + * Returns true if the pattern matches the string. + */ + +static int +patmatch(const char *pattern, const char *string) +{ + const char *p, *q, *end; + const char *bt_p, *bt_q; + char c; + wchar_t wc, wc2; + + p = pattern; + q = string; + bt_p = NULL; + bt_q = NULL; + for (;;) { + switch (c = *p++) { + case '\0': + if (*q != '\0') + goto backtrack; + return 1; + case CTLESC: + if (*q++ != *p++) + goto backtrack; + break; + case '?': + if (*q == '\0') + return 0; + if (localeisutf8) { + wc = get_wc(&q); + /* + * A '?' does not match invalid UTF-8 but a + * '*' does, so backtrack. + */ + if (wc == 0) + goto backtrack; + } else + q++; + break; + case '*': + c = *p; + while (c == '*') + c = *++p; + /* + * If the pattern ends here, we know the string + * matches without needing to look at the rest of it. + */ + if (c == '\0') + return 1; + /* + * First try the shortest match for the '*' that + * could work. We can forget any earlier '*' since + * there is no way having it match more characters + * can help us, given that we are already here. + */ + bt_p = p; + bt_q = q; + break; + case '[': { + const char *savep, *saveq; + int invert, found; + wchar_t chr; + + savep = p, saveq = q; + invert = 0; + if (*p == '!' || *p == '^') { + invert++; + p++; + } + found = 0; + if (*q == '\0') + return 0; + if (localeisutf8) { + chr = get_wc(&q); + if (chr == 0) + goto backtrack; + } else + chr = (unsigned char)*q++; + c = *p++; + do { + if (c == '\0') { + p = savep, q = saveq; + c = '['; + goto dft; + } + if (c == '[' && *p == ':') { + found |= match_charclass(p, chr, &end); + if (end != NULL) + p = end; + } + if (c == CTLESC) + c = *p++; + if (localeisutf8 && c & 0x80) { + p--; + wc = get_wc(&p); + if (wc == 0) /* bad utf-8 */ + return 0; + } else + wc = (unsigned char)c; + if (*p == '-' && p[1] != ']') { + p++; + if (*p == CTLESC) + p++; + if (localeisutf8) { + wc2 = get_wc(&p); + if (wc2 == 0) /* bad utf-8 */ + return 0; + } else + wc2 = (unsigned char)*p++; + if ( collate_range_cmp(chr, wc) >= 0 + && collate_range_cmp(chr, wc2) <= 0 + ) + found = 1; + } else { + if (chr == wc) + found = 1; + } + } while ((c = *p++) != ']'); + if (found == invert) + goto backtrack; + break; + } +dft: default: + if (*q == '\0') + return 0; + if (*q++ == c) + break; +backtrack: + /* + * If we have a mismatch (other than hitting the end + * of the string), go back to the last '*' seen and + * have it match one additional character. + */ + if (bt_p == NULL) + return 0; + if (*bt_q == '\0') + return 0; + bt_q++; + p = bt_p; + q = bt_q; + break; + } + } +} + + + +/* + * Remove any CTLESC and CTLQUOTEMARK characters from a string. + */ + +void +rmescapes(char *str) +{ + char *p, *q; + + p = str; + while (*p != CTLESC && *p != CTLQUOTEMARK && *p != CTLQUOTEEND) { + if (*p++ == '\0') + return; + } + q = p; + while (*p) { + if (*p == CTLQUOTEMARK || *p == CTLQUOTEEND) { + p++; + continue; + } + if (*p == CTLESC) + p++; + *q++ = *p++; + } + *q = '\0'; +} + + + +/* + * See if a pattern matches in a case statement. + */ + +int +casematch(union node *pattern, const char *val) +{ + struct stackmark smark; + struct nodelist *argbackq; + int result; + char *p; + + setstackmark(&smark); + argbackq = pattern->narg.backquote; + STARTSTACKSTR(expdest); + argstr(pattern->narg.text, &argbackq, EXP_TILDE | EXP_CASE, NULL); + STPUTC('\0', expdest); + p = grabstackstr(expdest); + result = patmatch(p, val); + popstackmark(&smark); + return result; +} + +/* + * Our own itoa(). + */ + +static void +cvtnum(int num, char *buf) +{ + char temp[32]; + int neg = num < 0; + char *p = temp + 31; + + temp[31] = '\0'; + + do { + *--p = num % 10 + '0'; + } while ((num /= 10) != 0); + + if (neg) + *--p = '-'; + + memcpy(buf, p, temp + 32 - p); +} + +/* + * Do most of the work for wordexp(3). + */ + +int +wordexpcmd(int argc, char **argv) +{ + size_t len; + int i; + + out1fmt("%08x", argc - 1); + for (i = 1, len = 0; i < argc; i++) + len += strlen(argv[i]); + out1fmt("%08x", (int)len); + for (i = 1; i < argc; i++) + outbin(argv[i], strlen(argv[i]) + 1, out1); + return (0); +} + +/* + * Do most of the work for wordexp(3), new version. + */ + +int +freebsd_wordexpcmd(int argc __unused, char **argv __unused) +{ + struct arglist arglist; + union node *args, *n; + size_t len; + int ch; + int protected = 0; + int fd = -1; + int i; + + while ((ch = nextopt("f:p")) != '\0') { + switch (ch) { + case 'f': + fd = number(shoptarg); + break; + case 'p': + protected = 1; + break; + } + } + if (*argptr != NULL) + error("wrong number of arguments"); + if (fd < 0) + error("missing fd"); + INTOFF; + setinputfd(fd, 1); + INTON; + args = parsewordexp(); + popfile(); /* will also close fd */ + if (protected) + for (n = args; n != NULL; n = n->narg.next) { + if (n->narg.backquote != NULL) { + outcslow('C', out1); + error("command substitution disabled"); + } + } + outcslow(' ', out1); + emptyarglist(&arglist); + for (n = args; n != NULL; n = n->narg.next) + expandarg(n, &arglist, EXP_FULL | EXP_TILDE); + for (i = 0, len = 0; i < arglist.count; i++) + len += strlen(arglist.args[i]); + out1fmt("%016x %016zx", arglist.count, len); + for (i = 0; i < arglist.count; i++) + outbin(arglist.args[i], strlen(arglist.args[i]) + 1, out1); + return (0); +} diff --git a/shell_cmds/sh/expand.h b/shell_cmds/sh/expand.h new file mode 100644 index 0000000..e959174 --- /dev/null +++ b/shell_cmds/sh/expand.h @@ -0,0 +1,62 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)expand.h 8.2 (Berkeley) 5/4/95 + * $FreeBSD: head/bin/sh/expand.h 314436 2017-02-28 23:42:47Z imp $ + */ + +struct arglist { + char **args; + int count; + int capacity; + char *smallarg[1]; +}; + +/* + * expandarg() flags + */ +#define EXP_SPLIT 0x1 /* perform word splitting */ +#define EXP_TILDE 0x2 /* do normal tilde expansion */ +#define EXP_VARTILDE 0x4 /* expand tildes in an assignment */ +#define EXP_CASE 0x10 /* keeps quotes around for CASE pattern */ +#define EXP_SPLIT_LIT 0x20 /* IFS split literal text ${v+-a b c} */ +#define EXP_LIT_QUOTED 0x40 /* for EXP_SPLIT_LIT, start off quoted */ +#define EXP_GLOB 0x80 /* perform file globbing */ + +#define EXP_FULL (EXP_SPLIT | EXP_GLOB) + + +void emptyarglist(struct arglist *); +void appendarglist(struct arglist *, char *); +union node; +void expandarg(union node *, struct arglist *, int); +void rmescapes(char *); +int casematch(union node *, const char *); diff --git a/shell_cmds/sh/funcs/cmv b/shell_cmds/sh/funcs/cmv new file mode 100644 index 0000000..01728ca --- /dev/null +++ b/shell_cmds/sh/funcs/cmv @@ -0,0 +1,49 @@ +#!/bin/sh + +#- +# Copyright (c) 1991, 1993 +# The Regents of the University of California. All rights reserved. +# +# This code is derived from software contributed to Berkeley by +# Kenneth Almquist. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# @(#)cmv 8.2 (Berkeley) 5/4/95 +# $FreeBSD: head/bin/sh/funcs/cmv 314436 2017-02-28 23:42:47Z imp $ + +# Conditional move--don't replace an existing file. + +cmv() { + if test $# != 2 + then echo "cmv: arg count" + return 2 + fi + if test -f "$2" -o -w "$2" + then echo "$2 exists" + return 2 + fi + /bin/mv "$1" "$2" +} diff --git a/shell_cmds/sh/funcs/dirs b/shell_cmds/sh/funcs/dirs new file mode 100644 index 0000000..88c20f4 --- /dev/null +++ b/shell_cmds/sh/funcs/dirs @@ -0,0 +1,73 @@ +#!/bin/sh + +#- +# Copyright (c) 1991, 1993 +# The Regents of the University of California. All rights reserved. +# +# This code is derived from software contributed to Berkeley by +# Kenneth Almquist. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# @(#)dirs 8.2 (Berkeley) 5/4/95 +# $FreeBSD: head/bin/sh/funcs/dirs 314436 2017-02-28 23:42:47Z imp $ + +# pushd, popd, and dirs --- written by Chris Bertin +# Pixel Computer Inc. ...!wjh12!pixel!pixutl!chris +# as modified by Patrick Elam of GTRI and Kenneth Almquist at UW + +pushd () { + SAVE=`pwd` + if [ "$1" = "" ] + then if [ "$DSTACK" = "" ] + then echo "pushd: directory stack empty." + return 1 + fi + set $DSTACK + cd $1 || return + shift 1 + DSTACK="$*" + else cd $1 > /dev/null || return + fi + DSTACK="$SAVE $DSTACK" + dirs +} + +popd () { + if [ "$DSTACK" = "" ] + then echo "popd: directory stack empty." + return 1 + fi + set $DSTACK + cd $1 + shift + DSTACK=$* + dirs +} + +dirs () { + echo "`pwd` $DSTACK" + return 0 +} diff --git a/shell_cmds/sh/funcs/login b/shell_cmds/sh/funcs/login new file mode 100644 index 0000000..e3c1154 --- /dev/null +++ b/shell_cmds/sh/funcs/login @@ -0,0 +1,38 @@ +#!/bin/sh + +#- +# Copyright (c) 1991, 1993 +# The Regents of the University of California. All rights reserved. +# +# This code is derived from software contributed to Berkeley by +# Kenneth Almquist. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# @(#)login 8.2 (Berkeley) 5/4/95 +# $FreeBSD: head/bin/sh/funcs/login 314436 2017-02-28 23:42:47Z imp $ + +# replaces the login builtin in the BSD shell +login () exec login "$@" diff --git a/shell_cmds/sh/funcs/newgrp b/shell_cmds/sh/funcs/newgrp new file mode 100644 index 0000000..5f9f861 --- /dev/null +++ b/shell_cmds/sh/funcs/newgrp @@ -0,0 +1,37 @@ +#!/bin/sh + +#- +# Copyright (c) 1991, 1993 +# The Regents of the University of California. All rights reserved. +# +# This code is derived from software contributed to Berkeley by +# Kenneth Almquist. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# @(#)newgrp 8.2 (Berkeley) 5/4/95 +# $FreeBSD: head/bin/sh/funcs/newgrp 314436 2017-02-28 23:42:47Z imp $ + +newgrp() exec newgrp "$@" diff --git a/shell_cmds/sh/funcs/popd b/shell_cmds/sh/funcs/popd new file mode 100644 index 0000000..7a46b4e --- /dev/null +++ b/shell_cmds/sh/funcs/popd @@ -0,0 +1,73 @@ +#!/bin/sh + +#- +# Copyright (c) 1991, 1993 +# The Regents of the University of California. All rights reserved. +# +# This code is derived from software contributed to Berkeley by +# Kenneth Almquist. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# @(#)popd 8.2 (Berkeley) 5/4/95 +# $FreeBSD: head/bin/sh/funcs/popd 314436 2017-02-28 23:42:47Z imp $ + +# pushd, popd, and dirs --- written by Chris Bertin +# Pixel Computer Inc. ...!wjh12!pixel!pixutl!chris +# as modified by Patrick Elam of GTRI and Kenneth Almquist at UW + +pushd () { + SAVE=`pwd` + if [ "$1" = "" ] + then if [ "$DSTACK" = "" ] + then echo "pushd: directory stack empty." + return 1 + fi + set $DSTACK + cd $1 || return + shift 1 + DSTACK="$*" + else cd $1 > /dev/null || return + fi + DSTACK="$SAVE $DSTACK" + dirs +} + +popd () { + if [ "$DSTACK" = "" ] + then echo "popd: directory stack empty." + return 1 + fi + set $DSTACK + cd $1 + shift + DSTACK=$* + dirs +} + +dirs () { + echo "`pwd` $DSTACK" + return 0 +} diff --git a/shell_cmds/sh/funcs/pushd b/shell_cmds/sh/funcs/pushd new file mode 100644 index 0000000..8d4f10d --- /dev/null +++ b/shell_cmds/sh/funcs/pushd @@ -0,0 +1,73 @@ +#!/bin/sh + +#- +# Copyright (c) 1991, 1993 +# The Regents of the University of California. All rights reserved. +# +# This code is derived from software contributed to Berkeley by +# Kenneth Almquist. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# @(#)pushd 8.2 (Berkeley) 5/4/95 +# $FreeBSD: head/bin/sh/funcs/pushd 314436 2017-02-28 23:42:47Z imp $ + +# pushd, popd, and dirs --- written by Chris Bertin +# Pixel Computer Inc. ...!wjh12!pixel!pixutl!chris +# as modified by Patrick Elam of GTRI and Kenneth Almquist at UW + +pushd () { + SAVE=`pwd` + if [ "$1" = "" ] + then if [ "$DSTACK" = "" ] + then echo "pushd: directory stack empty." + return 1 + fi + set $DSTACK + cd $1 || return + shift 1 + DSTACK="$*" + else cd $1 > /dev/null || return + fi + DSTACK="$SAVE $DSTACK" + dirs +} + +popd () { + if [ "$DSTACK" = "" ] + then echo "popd: directory stack empty." + return 1 + fi + set $DSTACK + cd $1 + shift + DSTACK=$* + dirs +} + +dirs () { + echo "`pwd` $DSTACK" + return 0 +} diff --git a/shell_cmds/sh/funcs/suspend b/shell_cmds/sh/funcs/suspend new file mode 100644 index 0000000..99d3109 --- /dev/null +++ b/shell_cmds/sh/funcs/suspend @@ -0,0 +1,39 @@ +#- +# Copyright (c) 1991, 1993 +# The Regents of the University of California. All rights reserved. +# +# This code is derived from software contributed to Berkeley by +# Kenneth Almquist. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# @(#)suspend 8.2 (Berkeley) 5/4/95 +# $FreeBSD: head/bin/sh/funcs/suspend 314436 2017-02-28 23:42:47Z imp $ + +suspend() { + local - + set +m + kill -TSTP 0 +} diff --git a/shell_cmds/sh/histedit.c b/shell_cmds/sh/histedit.c new file mode 100644 index 0000000..f01cabd --- /dev/null +++ b/shell_cmds/sh/histedit.c @@ -0,0 +1,502 @@ +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)histedit.c 8.2 (Berkeley) 5/4/95"; +#endif +#endif /* not lint */ +#include <sys/cdefs.h> +__FBSDID("$FreeBSD: head/bin/sh/histedit.c 319635 2017-06-06 21:08:05Z jilles $"); + +#include <sys/param.h> +#include <limits.h> +#include <paths.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +/* + * Editline and history functions (and glue). + */ +#include "shell.h" +#include "parser.h" +#include "var.h" +#include "options.h" +#include "main.h" +#include "output.h" +#include "mystring.h" +#ifndef NO_HISTORY +#include "myhistedit.h" +#include "error.h" +#include "eval.h" +#include "memalloc.h" +#include "builtins.h" + +#define MAXHISTLOOPS 4 /* max recursions through fc */ +#define DEFEDITOR "ed" /* default editor *should* be $EDITOR */ + +History *hist; /* history cookie */ +EditLine *el; /* editline cookie */ +int displayhist; +static FILE *el_in, *el_out, *el_err; + +static char *fc_replace(const char *, char *, char *); +static int not_fcnumber(const char *); +static int str_to_event(const char *, int); + +/* + * Set history and editing status. Called whenever the status may + * have changed (figures out what to do). + */ +void +histedit(void) +{ + +#define editing (Eflag || Vflag) + + if (iflag) { + if (!hist) { + /* + * turn history on + */ + INTOFF; + hist = history_init(); + INTON; + + if (hist != NULL) + sethistsize(histsizeval()); + else + out2fmt_flush("sh: can't initialize history\n"); + } + if (editing && !el && isatty(0)) { /* && isatty(2) ??? */ + /* + * turn editing on + */ + char *term; + + INTOFF; + if (el_in == NULL) + el_in = fdopen(0, "r"); + if (el_err == NULL) + el_err = fdopen(1, "w"); + if (el_out == NULL) + el_out = fdopen(2, "w"); + if (el_in == NULL || el_err == NULL || el_out == NULL) + goto bad; + term = lookupvar("TERM"); + if (term) + setenv("TERM", term, 1); + else + unsetenv("TERM"); + el = el_init(arg0, el_in, el_out, el_err); + if (el != NULL) { + if (hist) + el_set(el, EL_HIST, history, hist); + el_set(el, EL_PROMPT, getprompt); + el_set(el, EL_ADDFN, "sh-complete", + "Filename completion", + _el_fn_sh_complete); + } else { +bad: + out2fmt_flush("sh: can't initialize editing\n"); + } + INTON; + } else if (!editing && el) { + INTOFF; + el_end(el); + el = NULL; + INTON; + } + if (el) { + if (Vflag) + el_set(el, EL_EDITOR, "vi"); + else if (Eflag) + el_set(el, EL_EDITOR, "emacs"); + el_set(el, EL_BIND, "^I", "sh-complete", NULL); + el_source(el, NULL); + } + } else { + INTOFF; + if (el) { /* no editing if not interactive */ + el_end(el); + el = NULL; + } + if (hist) { + history_end(hist); + hist = NULL; + } + INTON; + } +} + + +void +sethistsize(const char *hs) +{ + int histsize; + HistEvent he; + + if (hist != NULL) { + if (hs == NULL || !is_number(hs)) + histsize = 100; + else + histsize = atoi(hs); + history(hist, &he, H_SETSIZE, histsize); + history(hist, &he, H_SETUNIQUE, 1); + } +} + +void +setterm(const char *term) +{ + if (rootshell && el != NULL && term != NULL) + el_set(el, EL_TERMINAL, term); +} + +int +histcmd(int argc, char **argv __unused) +{ + int ch; + const char *editor = NULL; + HistEvent he; + int lflg = 0, nflg = 0, rflg = 0, sflg = 0; + int i, retval; + const char *firststr, *laststr; + int first, last, direction; + char *pat = NULL, *repl = NULL; + static int active = 0; + struct jmploc jmploc; + struct jmploc *savehandler; + char editfilestr[PATH_MAX]; + char *volatile editfile; + FILE *efp = NULL; + int oldhistnum; + + if (hist == NULL) + error("history not active"); + + if (argc == 1) + error("missing history argument"); + + while (not_fcnumber(*argptr) && (ch = nextopt("e:lnrs")) != '\0') + switch ((char)ch) { + case 'e': + editor = shoptarg; + break; + case 'l': + lflg = 1; + break; + case 'n': + nflg = 1; + break; + case 'r': + rflg = 1; + break; + case 's': + sflg = 1; + break; + } + + savehandler = handler; + /* + * If executing... + */ + if (lflg == 0 || editor || sflg) { + lflg = 0; /* ignore */ + editfile = NULL; + /* + * Catch interrupts to reset active counter and + * cleanup temp files. + */ + if (setjmp(jmploc.loc)) { + active = 0; + if (editfile) + unlink(editfile); + handler = savehandler; + longjmp(handler->loc, 1); + } + handler = &jmploc; + if (++active > MAXHISTLOOPS) { + active = 0; + displayhist = 0; + error("called recursively too many times"); + } + /* + * Set editor. + */ + if (sflg == 0) { + if (editor == NULL && + (editor = bltinlookup("FCEDIT", 1)) == NULL && + (editor = bltinlookup("EDITOR", 1)) == NULL) + editor = DEFEDITOR; + if (editor[0] == '-' && editor[1] == '\0') { + sflg = 1; /* no edit */ + editor = NULL; + } + } + } + + /* + * If executing, parse [old=new] now + */ + if (lflg == 0 && *argptr != NULL && + ((repl = strchr(*argptr, '=')) != NULL)) { + pat = *argptr; + *repl++ = '\0'; + argptr++; + } + /* + * determine [first] and [last] + */ + if (*argptr == NULL) { + firststr = lflg ? "-16" : "-1"; + laststr = "-1"; + } else if (argptr[1] == NULL) { + firststr = argptr[0]; + laststr = lflg ? "-1" : argptr[0]; + } else if (argptr[2] == NULL) { + firststr = argptr[0]; + laststr = argptr[1]; + } else + error("too many arguments"); + /* + * Turn into event numbers. + */ + first = str_to_event(firststr, 0); + last = str_to_event(laststr, 1); + + if (rflg) { + i = last; + last = first; + first = i; + } + /* + * XXX - this should not depend on the event numbers + * always increasing. Add sequence numbers or offset + * to the history element in next (diskbased) release. + */ + direction = first < last ? H_PREV : H_NEXT; + + /* + * If editing, grab a temp file. + */ + if (editor) { + int fd; + INTOFF; /* easier */ + sprintf(editfilestr, "%s/_shXXXXXX", _PATH_TMP); + if ((fd = mkstemp(editfilestr)) < 0) + error("can't create temporary file %s", editfile); + editfile = editfilestr; + if ((efp = fdopen(fd, "w")) == NULL) { + close(fd); + error("Out of space"); + } + } + + /* + * Loop through selected history events. If listing or executing, + * do it now. Otherwise, put into temp file and call the editor + * after. + * + * The history interface needs rethinking, as the following + * convolutions will demonstrate. + */ + history(hist, &he, H_FIRST); + retval = history(hist, &he, H_NEXT_EVENT, first); + for (;retval != -1; retval = history(hist, &he, direction)) { + if (lflg) { + if (!nflg) + out1fmt("%5d ", he.num); + out1str(he.str); + } else { + const char *s = pat ? + fc_replace(he.str, pat, repl) : he.str; + + if (sflg) { + if (displayhist) { + out2str(s); + flushout(out2); + } + evalstring(s, 0); + if (displayhist && hist) { + /* + * XXX what about recursive and + * relative histnums. + */ + oldhistnum = he.num; + history(hist, &he, H_ENTER, s); + /* + * XXX H_ENTER moves the internal + * cursor, set it back to the current + * entry. + */ + history(hist, &he, + H_NEXT_EVENT, oldhistnum); + } + } else + fputs(s, efp); + } + /* + * At end? (if we were to lose last, we'd sure be + * messed up). + */ + if (he.num == last) + break; + } + if (editor) { + char *editcmd; + + fclose(efp); + INTON; + editcmd = stalloc(strlen(editor) + strlen(editfile) + 2); + sprintf(editcmd, "%s %s", editor, editfile); + evalstring(editcmd, 0); /* XXX - should use no JC command */ + readcmdfile(editfile); /* XXX - should read back - quick tst */ + unlink(editfile); + } + + if (lflg == 0 && active > 0) + --active; + if (displayhist) + displayhist = 0; + handler = savehandler; + return 0; +} + +static char * +fc_replace(const char *s, char *p, char *r) +{ + char *dest; + int plen = strlen(p); + + STARTSTACKSTR(dest); + while (*s) { + if (*s == *p && strncmp(s, p, plen) == 0) { + STPUTS(r, dest); + s += plen; + *p = '\0'; /* so no more matches */ + } else + STPUTC(*s++, dest); + } + STPUTC('\0', dest); + dest = grabstackstr(dest); + + return (dest); +} + +static int +not_fcnumber(const char *s) +{ + if (s == NULL) + return (0); + if (*s == '-') + s++; + return (!is_number(s)); +} + +static int +str_to_event(const char *str, int last) +{ + HistEvent he; + const char *s = str; + int relative = 0; + int i, retval; + + retval = history(hist, &he, H_FIRST); + switch (*s) { + case '-': + relative = 1; + /*FALLTHROUGH*/ + case '+': + s++; + } + if (is_number(s)) { + i = atoi(s); + if (relative) { + while (retval != -1 && i--) { + retval = history(hist, &he, H_NEXT); + } + if (retval == -1) + retval = history(hist, &he, H_LAST); + } else { + retval = history(hist, &he, H_NEXT_EVENT, i); + if (retval == -1) { + /* + * the notion of first and last is + * backwards to that of the history package + */ + retval = history(hist, &he, last ? H_FIRST : H_LAST); + } + } + if (retval == -1) + error("history number %s not found (internal error)", + str); + } else { + /* + * pattern + */ + retval = history(hist, &he, H_PREV_STR, str); + if (retval == -1) + error("history pattern not found: %s", str); + } + return (he.num); +} + +int +bindcmd(int argc, char **argv) +{ + + if (el == NULL) + error("line editing is disabled"); + return (el_parse(el, argc, __DECONST(const char **, argv))); +} + +#else +#include "error.h" + +int +histcmd(int argc, char **argv) +{ + + error("not compiled with history support"); + /*NOTREACHED*/ + return (0); +} + +int +bindcmd(int argc, char **argv) +{ + + error("not compiled with line editing support"); + return (0); +} +#endif diff --git a/shell_cmds/sh/input.c b/shell_cmds/sh/input.c new file mode 100644 index 0000000..53376f8 --- /dev/null +++ b/shell_cmds/sh/input.c @@ -0,0 +1,536 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)input.c 8.3 (Berkeley) 6/9/95"; +#endif +#endif /* not lint */ +#include <sys/cdefs.h> +__FBSDID("$FreeBSD: head/bin/sh/input.c 314436 2017-02-28 23:42:47Z imp $"); + +#include <stdio.h> /* defines BUFSIZ */ +#include <fcntl.h> +#include <errno.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> + +/* + * This file implements the input routines used by the parser. + */ + +#include "shell.h" +#include "redir.h" +#include "syntax.h" +#include "input.h" +#include "output.h" +#include "options.h" +#include "memalloc.h" +#include "error.h" +#include "alias.h" +#include "parser.h" +#include "myhistedit.h" +#include "trap.h" + +#define EOF_NLEFT -99 /* value of parsenleft when EOF pushed back */ + +struct strpush { + struct strpush *prev; /* preceding string on stack */ + const char *prevstring; + int prevnleft; + int prevlleft; + struct alias *ap; /* if push was associated with an alias */ +}; + +/* + * The parsefile structure pointed to by the global variable parsefile + * contains information about the current file being read. + */ + +struct parsefile { + struct parsefile *prev; /* preceding file on stack */ + int linno; /* current line */ + int fd; /* file descriptor (or -1 if string) */ + int nleft; /* number of chars left in this line */ + int lleft; /* number of lines left in this buffer */ + const char *nextc; /* next char in buffer */ + char *buf; /* input buffer */ + struct strpush *strpush; /* for pushing strings at this level */ + struct strpush basestrpush; /* so pushing one is fast */ +}; + + +int plinno = 1; /* input line number */ +int parsenleft; /* copy of parsefile->nleft */ +static int parselleft; /* copy of parsefile->lleft */ +const char *parsenextc; /* copy of parsefile->nextc */ +static char basebuf[BUFSIZ + 1];/* buffer for top level input file */ +static struct parsefile basepf = { /* top level input file */ + .nextc = basebuf, + .buf = basebuf +}; +static struct parsefile *parsefile = &basepf; /* current input file */ +int whichprompt; /* 1 == PS1, 2 == PS2 */ + +#ifndef __APPLE__ +EditLine *el; /* cookie for editline package */ +#endif /* !__APPLE__ */ + +static void pushfile(void); +static int preadfd(void); +static void popstring(void); + +#ifdef __APPLE__ +// Remove when 28988691 is fixed. Added _ to avoid conflict with future libc definition. +static char * +strchrnul_(const char *p, int ch) +{ + char c; + + c = ch; + for (;; ++p) { + if (*p == c || *p == '\0') + return ((char *)p); + } + /* NOTREACHED */ +} +#endif /* __APPLE__ */ + +void +resetinput(void) +{ + popallfiles(); + parselleft = parsenleft = 0; /* clear input buffer */ +} + + + +/* + * Read a character from the script, returning PEOF on end of file. + * Nul characters in the input are silently discarded. + */ + +int +pgetc(void) +{ + return pgetc_macro(); +} + + +static int +preadfd(void) +{ + int nr; + parsenextc = parsefile->buf; + +retry: +#ifndef NO_HISTORY + if (parsefile->fd == 0 && el) { + static const char *rl_cp; + static int el_len; + + if (rl_cp == NULL) { + el_resize(el); + rl_cp = el_gets(el, &el_len); + } + if (rl_cp == NULL) + nr = el_len == 0 ? 0 : -1; + else { + nr = el_len; + if (nr > BUFSIZ) + nr = BUFSIZ; + memcpy(parsefile->buf, rl_cp, nr); + if (nr != el_len) { + el_len -= nr; + rl_cp += nr; + } else + rl_cp = NULL; + } + } else +#endif + nr = read(parsefile->fd, parsefile->buf, BUFSIZ); + + if (nr <= 0) { + if (nr < 0) { + if (errno == EINTR) + goto retry; + if (parsefile->fd == 0 && errno == EWOULDBLOCK) { + int flags = fcntl(0, F_GETFL, 0); + if (flags >= 0 && flags & O_NONBLOCK) { + flags &=~ O_NONBLOCK; + if (fcntl(0, F_SETFL, flags) >= 0) { + out2fmt_flush("sh: turning off NDELAY mode\n"); + goto retry; + } + } + } + } + nr = -1; + } + return nr; +} + +/* + * Refill the input buffer and return the next input character: + * + * 1) If a string was pushed back on the input, pop it; + * 2) If an EOF was pushed back (parsenleft == EOF_NLEFT) or we are reading + * from a string so we can't refill the buffer, return EOF. + * 3) If there is more in this buffer, use it else call read to fill it. + * 4) Process input up to the next newline, deleting nul characters. + */ + +int +preadbuffer(void) +{ + char *p, *q, *r, *end; + char savec; + + while (parsefile->strpush) { + /* + * Add a space to the end of an alias to ensure that the + * alias remains in use while parsing its last word. + * This avoids alias recursions. + */ + if (parsenleft == -1 && parsefile->strpush->ap != NULL) + return ' '; + popstring(); + if (--parsenleft >= 0) + return (*parsenextc++); + } + if (parsenleft == EOF_NLEFT || parsefile->buf == NULL) + return PEOF; + +again: + if (parselleft <= 0) { + if ((parselleft = preadfd()) == -1) { + parselleft = parsenleft = EOF_NLEFT; + return PEOF; + } + } + + p = parsefile->buf + (parsenextc - parsefile->buf); + end = p + parselleft; + *end = '\0'; + q = strchrnul_(p, '\n'); + if (q != end && *q == '\0') { + /* delete nul characters */ + for (r = q; q != end; q++) { + if (*q != '\0') + *r++ = *q; + } + parselleft -= end - r; + if (parselleft == 0) + goto again; + end = p + parselleft; + *end = '\0'; + q = strchrnul_(p, '\n'); + } + if (q == end) { + parsenleft = parselleft; + parselleft = 0; + } else /* *q == '\n' */ { + q++; + parsenleft = q - parsenextc; + parselleft -= parsenleft; + } + parsenleft--; + + savec = *q; + *q = '\0'; + +#ifndef NO_HISTORY + if (parsefile->fd == 0 && hist && + parsenextc[strspn(parsenextc, " \t\n")] != '\0') { + HistEvent he; + INTOFF; + history(hist, &he, whichprompt == 1 ? H_ENTER : H_ADD, + parsenextc); + INTON; + } +#endif + + if (vflag) { + out2str(parsenextc); + flushout(out2); + } + + *q = savec; + + return *parsenextc++; +} + +/* + * Returns if we are certain we are at EOF. Does not cause any more input + * to be read from the outside world. + */ + +int +preadateof(void) +{ + if (parsenleft > 0) + return 0; + if (parsefile->strpush) + return 0; + if (parsenleft == EOF_NLEFT || parsefile->buf == NULL) + return 1; + return 0; +} + +/* + * Undo the last call to pgetc. Only one character may be pushed back. + * PEOF may be pushed back. + */ + +void +pungetc(void) +{ + parsenleft++; + parsenextc--; +} + +/* + * Push a string back onto the input at this current parsefile level. + * We handle aliases this way. + */ +void +pushstring(const char *s, int len, struct alias *ap) +{ + struct strpush *sp; + + INTOFF; +/*out2fmt_flush("*** calling pushstring: %s, %d\n", s, len);*/ + if (parsefile->strpush) { + sp = ckmalloc(sizeof (struct strpush)); + sp->prev = parsefile->strpush; + parsefile->strpush = sp; + } else + sp = parsefile->strpush = &(parsefile->basestrpush); + sp->prevstring = parsenextc; + sp->prevnleft = parsenleft; + sp->prevlleft = parselleft; + sp->ap = ap; + if (ap) + ap->flag |= ALIASINUSE; + parsenextc = s; + parsenleft = len; + INTON; +} + +static void +popstring(void) +{ + struct strpush *sp = parsefile->strpush; + + INTOFF; + if (sp->ap) { + if (parsenextc != sp->ap->val && + (parsenextc[-1] == ' ' || parsenextc[-1] == '\t')) + forcealias(); + sp->ap->flag &= ~ALIASINUSE; + } + parsenextc = sp->prevstring; + parsenleft = sp->prevnleft; + parselleft = sp->prevlleft; +/*out2fmt_flush("*** calling popstring: restoring to '%s'\n", parsenextc);*/ + parsefile->strpush = sp->prev; + if (sp != &(parsefile->basestrpush)) + ckfree(sp); + INTON; +} + +/* + * Set the input to take input from a file. If push is set, push the + * old input onto the stack first. + */ + +void +setinputfile(const char *fname, int push) +{ + int fd; + int fd2; + + INTOFF; + if ((fd = open(fname, O_RDONLY | O_CLOEXEC)) < 0) + error("cannot open %s: %s", fname, strerror(errno)); + if (fd < 10) { + fd2 = fcntl(fd, F_DUPFD_CLOEXEC, 10); + close(fd); + if (fd2 < 0) + error("Out of file descriptors"); + fd = fd2; + } + setinputfd(fd, push); + INTON; +} + + +/* + * Like setinputfile, but takes an open file descriptor (which should have + * its FD_CLOEXEC flag already set). Call this with interrupts off. + */ + +void +setinputfd(int fd, int push) +{ + if (push) { + pushfile(); + parsefile->buf = ckmalloc(BUFSIZ + 1); + } + if (parsefile->fd > 0) + close(parsefile->fd); + parsefile->fd = fd; + if (parsefile->buf == NULL) + parsefile->buf = ckmalloc(BUFSIZ + 1); + parselleft = parsenleft = 0; + plinno = 1; +} + + +/* + * Like setinputfile, but takes input from a string. + */ + +void +setinputstring(const char *string, int push) +{ + INTOFF; + if (push) + pushfile(); + parsenextc = string; + parselleft = parsenleft = strlen(string); + parsefile->buf = NULL; + plinno = 1; + INTON; +} + + + +/* + * To handle the "." command, a stack of input files is used. Pushfile + * adds a new entry to the stack and popfile restores the previous level. + */ + +static void +pushfile(void) +{ + struct parsefile *pf; + + parsefile->nleft = parsenleft; + parsefile->lleft = parselleft; + parsefile->nextc = parsenextc; + parsefile->linno = plinno; + pf = (struct parsefile *)ckmalloc(sizeof (struct parsefile)); + pf->prev = parsefile; + pf->fd = -1; + pf->strpush = NULL; + pf->basestrpush.prev = NULL; + parsefile = pf; +} + + +void +popfile(void) +{ + struct parsefile *pf = parsefile; + + INTOFF; + if (pf->fd >= 0) + close(pf->fd); + if (pf->buf) + ckfree(pf->buf); + while (pf->strpush) + popstring(); + parsefile = pf->prev; + ckfree(pf); + parsenleft = parsefile->nleft; + parselleft = parsefile->lleft; + parsenextc = parsefile->nextc; + plinno = parsefile->linno; + INTON; +} + + +/* + * Return current file (to go back to it later using popfilesupto()). + */ + +struct parsefile * +getcurrentfile(void) +{ + return parsefile; +} + + +/* + * Pop files until the given file is on top again. Useful for regular + * builtins that read shell commands from files or strings. + * If the given file is not an active file, an error is raised. + */ + +void +popfilesupto(struct parsefile *file) +{ + while (parsefile != file && parsefile != &basepf) + popfile(); + if (parsefile != file) + error("popfilesupto() misused"); +} + +/* + * Return to top level. + */ + +void +popallfiles(void) +{ + while (parsefile != &basepf) + popfile(); +} + + + +/* + * Close the file(s) that the shell is reading commands from. Called + * after a fork is done. + */ + +void +closescript(void) +{ + popallfiles(); + if (parsefile->fd > 0) { + close(parsefile->fd); + parsefile->fd = 0; + } +} diff --git a/shell_cmds/sh/input.h b/shell_cmds/sh/input.h new file mode 100644 index 0000000..5a47e4b --- /dev/null +++ b/shell_cmds/sh/input.h @@ -0,0 +1,65 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)input.h 8.2 (Berkeley) 5/4/95 + * $FreeBSD: head/bin/sh/input.h 314436 2017-02-28 23:42:47Z imp $ + */ + +/* PEOF (the end of file marker) is defined in syntax.h */ + +/* + * The input line number. Input.c just defines this variable, and saves + * and restores it when files are pushed and popped. The user of this + * package must set its value. + */ +extern int plinno; +extern int parsenleft; /* number of characters left in input buffer */ +extern const char *parsenextc; /* next character in input buffer */ + +struct alias; +struct parsefile; + +void resetinput(void); +int pgetc(void); +int preadbuffer(void); +int preadateof(void); +void pungetc(void); +void pushstring(const char *, int, struct alias *); +void setinputfile(const char *, int); +void setinputfd(int, int); +void setinputstring(const char *, int); +void popfile(void); +struct parsefile *getcurrentfile(void); +void popfilesupto(struct parsefile *); +void popallfiles(void); +void closescript(void); + +#define pgetc_macro() (--parsenleft >= 0? *parsenextc++ : preadbuffer()) diff --git a/shell_cmds/sh/jobs.c b/shell_cmds/sh/jobs.c new file mode 100644 index 0000000..358153b --- /dev/null +++ b/shell_cmds/sh/jobs.c @@ -0,0 +1,1552 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)jobs.c 8.5 (Berkeley) 5/4/95"; +#endif +#endif /* not lint */ +#include <sys/cdefs.h> +__FBSDID("$FreeBSD: head/bin/sh/jobs.c 328818 2018-02-02 22:53:58Z jilles $"); + +#include <sys/ioctl.h> +#include <sys/param.h> +#include <sys/resource.h> +#include <sys/time.h> +#include <sys/wait.h> +#include <errno.h> +#include <fcntl.h> +#include <paths.h> +#include <signal.h> +#include <stddef.h> +#include <stdlib.h> +#include <unistd.h> + +#include "shell.h" +#if JOBS +#include <termios.h> +#undef CEOF /* syntax.h redefines this */ +#endif +#include "redir.h" +#include "exec.h" +#include "show.h" +#include "main.h" +#include "parser.h" +#include "nodes.h" +#include "jobs.h" +#include "options.h" +#include "trap.h" +#include "syntax.h" +#include "input.h" +#include "output.h" +#include "memalloc.h" +#include "error.h" +#include "mystring.h" +#include "var.h" +#include "builtins.h" + + +/* + * A job structure contains information about a job. A job is either a + * single process or a set of processes contained in a pipeline. In the + * latter case, pidlist will be non-NULL, and will point to a -1 terminated + * array of pids. + */ + +struct procstat { + pid_t pid; /* process id */ + int status; /* status flags (defined above) */ + char *cmd; /* text of command being run */ +}; + + +/* states */ +#define JOBSTOPPED 1 /* all procs are stopped */ +#define JOBDONE 2 /* all procs are completed */ + + +struct job { + struct procstat ps0; /* status of process */ + struct procstat *ps; /* status or processes when more than one */ + short nprocs; /* number of processes */ + pid_t pgrp; /* process group of this job */ + char state; /* true if job is finished */ + char used; /* true if this entry is in used */ + char changed; /* true if status has changed */ + char foreground; /* true if running in the foreground */ + char remembered; /* true if $! referenced */ +#if JOBS + char jobctl; /* job running under job control */ + struct job *next; /* job used after this one */ +#endif +}; + + +static struct job *jobtab; /* array of jobs */ +static int njobs; /* size of array */ +static pid_t backgndpid = -1; /* pid of last background process */ +static struct job *bgjob = NULL; /* last background process */ +#if JOBS +static struct job *jobmru; /* most recently used job list */ +static pid_t initialpgrp; /* pgrp of shell on invocation */ +#endif +static int ttyfd = -1; + +/* mode flags for dowait */ +#define DOWAIT_BLOCK 0x1 /* wait until a child exits */ +#define DOWAIT_SIG 0x2 /* if DOWAIT_BLOCK, abort on signal */ +#define DOWAIT_SIG_TRAP 0x4 /* if DOWAIT_SIG, abort on trapped signal only */ + +#if JOBS +static void restartjob(struct job *); +#endif +static void freejob(struct job *); +static int waitcmdloop(struct job *); +static struct job *getjob_nonotfound(const char *); +static struct job *getjob(const char *); +pid_t killjob(const char *, int); +static pid_t dowait(int, struct job *); +static void checkzombies(void); +static void cmdtxt(union node *); +static void cmdputs(const char *); +#if JOBS +static void setcurjob(struct job *); +static void deljob(struct job *); +static struct job *getcurjob(struct job *); +#endif +static void printjobcmd(struct job *); +static void showjob(struct job *, int); + + +/* + * Turn job control on and off. + */ + +static int jobctl; + +#if JOBS +static void +jobctl_notty(void) +{ + if (ttyfd >= 0) { + close(ttyfd); + ttyfd = -1; + } + if (!iflag) { + setsignal(SIGTSTP); + setsignal(SIGTTOU); + setsignal(SIGTTIN); + jobctl = 1; + return; + } + out2fmt_flush("sh: can't access tty; job control turned off\n"); + mflag = 0; +} + +void +setjobctl(int on) +{ + int i; + + if (on == jobctl || rootshell == 0) + return; + if (on) { + if (ttyfd != -1) + close(ttyfd); + if ((ttyfd = open(_PATH_TTY, O_RDWR | O_CLOEXEC)) < 0) { + i = 0; + while (i <= 2 && !isatty(i)) + i++; + if (i > 2 || + (ttyfd = fcntl(i, F_DUPFD_CLOEXEC, 10)) < 0) { + jobctl_notty(); + return; + } + } + if (ttyfd < 10) { + /* + * Keep our TTY file descriptor out of the way of + * the user's redirections. + */ + if ((i = fcntl(ttyfd, F_DUPFD_CLOEXEC, 10)) < 0) { + jobctl_notty(); + return; + } + close(ttyfd); + ttyfd = i; + } + do { /* while we are in the background */ + initialpgrp = tcgetpgrp(ttyfd); + if (initialpgrp < 0) { + jobctl_notty(); + return; + } + if (initialpgrp != getpgrp()) { + if (!iflag) { + initialpgrp = -1; + jobctl_notty(); + return; + } + kill(0, SIGTTIN); + continue; + } + } while (0); + setsignal(SIGTSTP); + setsignal(SIGTTOU); + setsignal(SIGTTIN); + setpgid(0, rootpid); + tcsetpgrp(ttyfd, rootpid); + } else { /* turning job control off */ + setpgid(0, initialpgrp); + if (ttyfd >= 0) { + tcsetpgrp(ttyfd, initialpgrp); + close(ttyfd); + ttyfd = -1; + } + setsignal(SIGTSTP); + setsignal(SIGTTOU); + setsignal(SIGTTIN); + } + jobctl = on; +} +#endif + + +#if JOBS +int +fgcmd(int argc __unused, char **argv __unused) +{ + struct job *jp; + pid_t pgrp; + int status; + + nextopt(""); + jp = getjob(*argptr); + if (jp->jobctl == 0) + error("job not created under job control"); + printjobcmd(jp); + flushout(&output); + pgrp = jp->ps[0].pid; + if (ttyfd >= 0) + tcsetpgrp(ttyfd, pgrp); + restartjob(jp); + jp->foreground = 1; + INTOFF; + status = waitforjob(jp, (int *)NULL); + INTON; + return status; +} + + +int +bgcmd(int argc __unused, char **argv __unused) +{ + struct job *jp; + + nextopt(""); + do { + jp = getjob(*argptr); + if (jp->jobctl == 0) + error("job not created under job control"); + if (jp->state == JOBDONE) + continue; + restartjob(jp); + jp->foreground = 0; + out1fmt("[%td] ", jp - jobtab + 1); + printjobcmd(jp); + } while (*argptr != NULL && *++argptr != NULL); + return 0; +} + + +static void +restartjob(struct job *jp) +{ + struct procstat *ps; + int i; + + if (jp->state == JOBDONE) + return; + setcurjob(jp); + INTOFF; + kill(-jp->ps[0].pid, SIGCONT); + for (ps = jp->ps, i = jp->nprocs ; --i >= 0 ; ps++) { + if (WIFSTOPPED(ps->status)) { + ps->status = -1; + jp->state = 0; + } + } + INTON; +} +#endif + + +int +jobscmd(int argc __unused, char *argv[] __unused) +{ + char *id; + int ch, mode; + + mode = SHOWJOBS_DEFAULT; + while ((ch = nextopt("lps")) != '\0') { + switch (ch) { + case 'l': + mode = SHOWJOBS_VERBOSE; + break; + case 'p': + mode = SHOWJOBS_PGIDS; + break; + case 's': + mode = SHOWJOBS_PIDS; + break; + } + } + + if (*argptr == NULL) + showjobs(0, mode); + else + while ((id = *argptr++) != NULL) + showjob(getjob(id), mode); + + return (0); +} + +static void +printjobcmd(struct job *jp) +{ + struct procstat *ps; + int i; + + for (ps = jp->ps, i = jp->nprocs ; --i >= 0 ; ps++) { + out1str(ps->cmd); + if (i > 0) + out1str(" | "); + } + out1c('\n'); +} + +static void +showjob(struct job *jp, int mode) +{ + char s[64]; + char statebuf[16]; + const char *statestr, *coredump; + struct procstat *ps; + struct job *j; + int col, curr, i, jobno, prev, procno, status; + char c; + + procno = (mode == SHOWJOBS_PGIDS) ? 1 : jp->nprocs; + jobno = jp - jobtab + 1; + curr = prev = 0; +#if JOBS + if ((j = getcurjob(NULL)) != NULL) { + curr = j - jobtab + 1; + if ((j = getcurjob(j)) != NULL) + prev = j - jobtab + 1; + } +#endif + coredump = ""; + status = jp->ps[jp->nprocs - 1].status; + if (jp->state == 0) { + statestr = "Running"; +#if JOBS + } else if (jp->state == JOBSTOPPED) { + ps = jp->ps + jp->nprocs - 1; + while (!WIFSTOPPED(ps->status) && ps > jp->ps) + ps--; + if (WIFSTOPPED(ps->status)) + i = WSTOPSIG(ps->status); + else + i = -1; + statestr = strsignal(i); + if (statestr == NULL) + statestr = "Suspended"; +#endif + } else if (WIFEXITED(status)) { + if (WEXITSTATUS(status) == 0) + statestr = "Done"; + else { + fmtstr(statebuf, sizeof(statebuf), "Done(%d)", + WEXITSTATUS(status)); + statestr = statebuf; + } + } else { + i = WTERMSIG(status); + statestr = strsignal(i); + if (statestr == NULL) + statestr = "Unknown signal"; + if (WCOREDUMP(status)) + coredump = " (core dumped)"; + } + + for (ps = jp->ps ; procno > 0 ; ps++, procno--) { /* for each process */ + if (mode == SHOWJOBS_PIDS || mode == SHOWJOBS_PGIDS) { + out1fmt("%d\n", (int)ps->pid); + continue; + } + if (mode != SHOWJOBS_VERBOSE && ps != jp->ps) + continue; + if (jobno == curr && ps == jp->ps) + c = '+'; + else if (jobno == prev && ps == jp->ps) + c = '-'; + else + c = ' '; + if (ps == jp->ps) + fmtstr(s, 64, "[%d] %c ", jobno, c); + else + fmtstr(s, 64, " %c ", c); + out1str(s); + col = strlen(s); + if (mode == SHOWJOBS_VERBOSE) { + fmtstr(s, 64, "%d ", (int)ps->pid); + out1str(s); + col += strlen(s); + } + if (ps == jp->ps) { + out1str(statestr); + out1str(coredump); + col += strlen(statestr) + strlen(coredump); + } + do { + out1c(' '); + col++; + } while (col < 30); + if (mode == SHOWJOBS_VERBOSE) { + out1str(ps->cmd); + out1c('\n'); + } else + printjobcmd(jp); + } +} + +/* + * Print a list of jobs. If "change" is nonzero, only print jobs whose + * statuses have changed since the last call to showjobs. + * + * If the shell is interrupted in the process of creating a job, the + * result may be a job structure containing zero processes. Such structures + * will be freed here. + */ + +void +showjobs(int change, int mode) +{ + int jobno; + struct job *jp; + + TRACE(("showjobs(%d) called\n", change)); + checkzombies(); + for (jobno = 1, jp = jobtab ; jobno <= njobs ; jobno++, jp++) { + if (! jp->used) + continue; + if (jp->nprocs == 0) { + freejob(jp); + continue; + } + if (change && ! jp->changed) + continue; + showjob(jp, mode); + if (mode == SHOWJOBS_DEFAULT || mode == SHOWJOBS_VERBOSE) { + jp->changed = 0; + /* Hack: discard jobs for which $! has not been + * referenced in interactive mode when they terminate. + */ + if (jp->state == JOBDONE && !jp->remembered && + (iflag || jp != bgjob)) { + freejob(jp); + } + } + } +} + + +/* + * Mark a job structure as unused. + */ + +static void +freejob(struct job *jp) +{ + struct procstat *ps; + int i; + + INTOFF; + if (bgjob == jp) + bgjob = NULL; + for (i = jp->nprocs, ps = jp->ps ; --i >= 0 ; ps++) { + if (ps->cmd != nullstr) + ckfree(ps->cmd); + } + if (jp->ps != &jp->ps0) + ckfree(jp->ps); + jp->used = 0; +#if JOBS + deljob(jp); +#endif + INTON; +} + + + +int +waitcmd(int argc __unused, char **argv __unused) +{ + struct job *job; + int retval; + + nextopt(""); + if (*argptr == NULL) + return (waitcmdloop(NULL)); + + do { + job = getjob_nonotfound(*argptr); + if (job == NULL) + retval = 127; + else + retval = waitcmdloop(job); + argptr++; + } while (*argptr != NULL); + + return (retval); +} + +static int +waitcmdloop(struct job *job) +{ + int status, retval, sig; + struct job *jp; + + /* + * Loop until a process is terminated or stopped, or a SIGINT is + * received. + */ + + do { + if (job != NULL) { + if (job->state == JOBDONE) { + status = job->ps[job->nprocs - 1].status; + if (WIFEXITED(status)) + retval = WEXITSTATUS(status); + else + retval = WTERMSIG(status) + 128; + if (! iflag || ! job->changed) + freejob(job); + else { + job->remembered = 0; + if (job == bgjob) + bgjob = NULL; + } + return retval; + } + } else { + for (jp = jobtab ; jp < jobtab + njobs; jp++) + if (jp->used && jp->state == JOBDONE) { + if (! iflag || ! jp->changed) + freejob(jp); + else { + jp->remembered = 0; + if (jp == bgjob) + bgjob = NULL; + } + } + for (jp = jobtab ; ; jp++) { + if (jp >= jobtab + njobs) { /* no running procs */ + return 0; + } + if (jp->used && jp->state == 0) + break; + } + } + } while (dowait(DOWAIT_BLOCK | DOWAIT_SIG, (struct job *)NULL) != -1); + + sig = pendingsig_waitcmd; + pendingsig_waitcmd = 0; + return sig + 128; +} + + + +int +jobidcmd(int argc __unused, char **argv __unused) +{ + struct job *jp; + int i; + + nextopt(""); + jp = getjob(*argptr); + for (i = 0 ; i < jp->nprocs ; ) { + out1fmt("%d", (int)jp->ps[i].pid); + out1c(++i < jp->nprocs? ' ' : '\n'); + } + return 0; +} + + + +/* + * Convert a job name to a job structure. + */ + +static struct job * +getjob_nonotfound(const char *name) +{ + int jobno; + struct job *found, *jp; + size_t namelen; + pid_t pid; + int i; + + if (name == NULL) { +#if JOBS + name = "%+"; +#else + error("No current job"); +#endif + } + if (name[0] == '%') { + if (is_digit(name[1])) { + jobno = number(name + 1); + if (jobno > 0 && jobno <= njobs + && jobtab[jobno - 1].used != 0) + return &jobtab[jobno - 1]; +#if JOBS + } else if ((name[1] == '%' || name[1] == '+') && + name[2] == '\0') { + if ((jp = getcurjob(NULL)) == NULL) + error("No current job"); + return (jp); + } else if (name[1] == '-' && name[2] == '\0') { + if ((jp = getcurjob(NULL)) == NULL || + (jp = getcurjob(jp)) == NULL) + error("No previous job"); + return (jp); +#endif + } else if (name[1] == '?') { + found = NULL; + for (jp = jobtab, i = njobs ; --i >= 0 ; jp++) { + if (jp->used && jp->nprocs > 0 + && strstr(jp->ps[0].cmd, name + 2) != NULL) { + if (found) + error("%s: ambiguous", name); + found = jp; + } + } + if (found != NULL) + return (found); + } else { + namelen = strlen(name); + found = NULL; + for (jp = jobtab, i = njobs ; --i >= 0 ; jp++) { + if (jp->used && jp->nprocs > 0 + && strncmp(jp->ps[0].cmd, name + 1, + namelen - 1) == 0) { + if (found) + error("%s: ambiguous", name); + found = jp; + } + } + if (found) + return found; + } + } else if (is_number(name)) { + pid = (pid_t)number(name); + for (jp = jobtab, i = njobs ; --i >= 0 ; jp++) { + if (jp->used && jp->nprocs > 0 + && jp->ps[jp->nprocs - 1].pid == pid) + return jp; + } + } + return NULL; +} + + +static struct job * +getjob(const char *name) +{ + struct job *jp; + + jp = getjob_nonotfound(name); + if (jp == NULL) + error("No such job: %s", name); + return (jp); +} + + +int +killjob(const char *name, int sig) +{ + struct job *jp; + int i, ret; + + jp = getjob(name); + if (jp->state == JOBDONE) + return 0; + if (jp->jobctl) + return kill(-jp->ps[0].pid, sig); + ret = -1; + errno = ESRCH; + for (i = 0; i < jp->nprocs; i++) + if (jp->ps[i].status == -1 || WIFSTOPPED(jp->ps[i].status)) { + if (kill(jp->ps[i].pid, sig) == 0) + ret = 0; + } else + ret = 0; + return ret; +} + +/* + * Return a new job structure, + */ + +struct job * +makejob(union node *node __unused, int nprocs) +{ + int i; + struct job *jp; + + for (i = njobs, jp = jobtab ; ; jp++) { + if (--i < 0) { + INTOFF; + if (njobs == 0) { + jobtab = ckmalloc(4 * sizeof jobtab[0]); +#if JOBS + jobmru = NULL; +#endif + } else { + jp = ckmalloc((njobs + 4) * sizeof jobtab[0]); + memcpy(jp, jobtab, njobs * sizeof jp[0]); +#if JOBS + /* Relocate `next' pointers and list head */ + if (jobmru != NULL) + jobmru = &jp[jobmru - jobtab]; + for (i = 0; i < njobs; i++) + if (jp[i].next != NULL) + jp[i].next = &jp[jp[i].next - + jobtab]; +#endif + if (bgjob != NULL) + bgjob = &jp[bgjob - jobtab]; + /* Relocate `ps' pointers */ + for (i = 0; i < njobs; i++) + if (jp[i].ps == &jobtab[i].ps0) + jp[i].ps = &jp[i].ps0; + ckfree(jobtab); + jobtab = jp; + } + jp = jobtab + njobs; + for (i = 4 ; --i >= 0 ; jobtab[njobs++].used = 0) + ; + INTON; + break; + } + if (jp->used == 0) + break; + } + INTOFF; + jp->state = 0; + jp->used = 1; + jp->changed = 0; + jp->nprocs = 0; + jp->foreground = 0; + jp->remembered = 0; +#if JOBS + jp->jobctl = jobctl; + jp->next = NULL; +#endif + if (nprocs > 1) { + jp->ps = ckmalloc(nprocs * sizeof (struct procstat)); + } else { + jp->ps = &jp->ps0; + } + INTON; + TRACE(("makejob(%p, %d) returns %%%td\n", (void *)node, nprocs, + jp - jobtab + 1)); + return jp; +} + +#if JOBS +static void +setcurjob(struct job *cj) +{ + struct job *jp, *prev; + + for (prev = NULL, jp = jobmru; jp != NULL; prev = jp, jp = jp->next) { + if (jp == cj) { + if (prev != NULL) + prev->next = jp->next; + else + jobmru = jp->next; + jp->next = jobmru; + jobmru = cj; + return; + } + } + cj->next = jobmru; + jobmru = cj; +} + +static void +deljob(struct job *j) +{ + struct job *jp, *prev; + + for (prev = NULL, jp = jobmru; jp != NULL; prev = jp, jp = jp->next) { + if (jp == j) { + if (prev != NULL) + prev->next = jp->next; + else + jobmru = jp->next; + return; + } + } +} + +/* + * Return the most recently used job that isn't `nj', and preferably one + * that is stopped. + */ +static struct job * +getcurjob(struct job *nj) +{ + struct job *jp; + + /* Try to find a stopped one.. */ + for (jp = jobmru; jp != NULL; jp = jp->next) + if (jp->used && jp != nj && jp->state == JOBSTOPPED) + return (jp); + /* Otherwise the most recently used job that isn't `nj' */ + for (jp = jobmru; jp != NULL; jp = jp->next) + if (jp->used && jp != nj) + return (jp); + + return (NULL); +} + +#endif + +/* + * Fork of a subshell. If we are doing job control, give the subshell its + * own process group. Jp is a job structure that the job is to be added to. + * N is the command that will be evaluated by the child. Both jp and n may + * be NULL. The mode parameter can be one of the following: + * FORK_FG - Fork off a foreground process. + * FORK_BG - Fork off a background process. + * FORK_NOJOB - Like FORK_FG, but don't give the process its own + * process group even if job control is on. + * + * When job control is turned off, background processes have their standard + * input redirected to /dev/null (except for the second and later processes + * in a pipeline). + */ + +pid_t +forkshell(struct job *jp, union node *n, int mode) +{ + pid_t pid; + pid_t pgrp; + + TRACE(("forkshell(%%%td, %p, %d) called\n", jp - jobtab, (void *)n, + mode)); + INTOFF; + if (mode == FORK_BG && (jp == NULL || jp->nprocs == 0)) + checkzombies(); + flushall(); + pid = fork(); + if (pid == -1) { + TRACE(("Fork failed, errno=%d\n", errno)); + INTON; + error("Cannot fork: %s", strerror(errno)); + } + if (pid == 0) { + struct job *p; + int wasroot; + int i; + + TRACE(("Child shell %d\n", (int)getpid())); + wasroot = rootshell; + rootshell = 0; + handler = &main_handler; + closescript(); + INTON; + forcelocal = 0; + clear_traps(); +#if JOBS + jobctl = 0; /* do job control only in root shell */ + if (wasroot && mode != FORK_NOJOB && mflag) { + if (jp == NULL || jp->nprocs == 0) + pgrp = getpid(); + else + pgrp = jp->ps[0].pid; + if (setpgid(0, pgrp) == 0 && mode == FORK_FG && + ttyfd >= 0) { + /*** this causes superfluous TIOCSPGRPS ***/ + if (tcsetpgrp(ttyfd, pgrp) < 0) + error("tcsetpgrp failed, errno=%d", errno); + } + setsignal(SIGTSTP); + setsignal(SIGTTOU); + } else if (mode == FORK_BG) { + ignoresig(SIGINT); + ignoresig(SIGQUIT); + if ((jp == NULL || jp->nprocs == 0) && + ! fd0_redirected_p ()) { + close(0); + if (open(_PATH_DEVNULL, O_RDONLY) != 0) + error("cannot open %s: %s", + _PATH_DEVNULL, strerror(errno)); + } + } +#else + if (mode == FORK_BG) { + ignoresig(SIGINT); + ignoresig(SIGQUIT); + if ((jp == NULL || jp->nprocs == 0) && + ! fd0_redirected_p ()) { + close(0); + if (open(_PATH_DEVNULL, O_RDONLY) != 0) + error("cannot open %s: %s", + _PATH_DEVNULL, strerror(errno)); + } + } +#endif + INTOFF; + for (i = njobs, p = jobtab ; --i >= 0 ; p++) + if (p->used) + freejob(p); + INTON; + if (wasroot && iflag) { + setsignal(SIGINT); + setsignal(SIGQUIT); + setsignal(SIGTERM); + } + return pid; + } + if (rootshell && mode != FORK_NOJOB && mflag) { + if (jp == NULL || jp->nprocs == 0) + pgrp = pid; + else + pgrp = jp->ps[0].pid; + setpgid(pid, pgrp); + } + if (mode == FORK_BG) { + if (bgjob != NULL && bgjob->state == JOBDONE && + !bgjob->remembered && !iflag) + freejob(bgjob); + backgndpid = pid; /* set $! */ + bgjob = jp; + } + if (jp) { + struct procstat *ps = &jp->ps[jp->nprocs++]; + ps->pid = pid; + ps->status = -1; + ps->cmd = nullstr; + if (iflag && rootshell && n) + ps->cmd = commandtext(n); + jp->foreground = mode == FORK_FG; +#if JOBS + setcurjob(jp); +#endif + } + INTON; + TRACE(("In parent shell: child = %d\n", (int)pid)); + return pid; +} + + +pid_t +vforkexecshell(struct job *jp, char **argv, char **envp, const char *path, int idx, int pip[2]) +{ + pid_t pid; + struct jmploc jmploc; + struct jmploc *savehandler; + + TRACE(("vforkexecshell(%%%td, %s, %p) called\n", jp - jobtab, argv[0], + (void *)pip)); + INTOFF; + flushall(); + savehandler = handler; + pid = vfork(); + if (pid == -1) { + TRACE(("Vfork failed, errno=%d\n", errno)); + INTON; + error("Cannot fork: %s", strerror(errno)); + } + if (pid == 0) { + TRACE(("Child shell %d\n", (int)getpid())); + if (setjmp(jmploc.loc)) + _exit(exception == EXEXEC ? exerrno : 2); + if (pip != NULL) { + close(pip[0]); + if (pip[1] != 1) { + dup2(pip[1], 1); + close(pip[1]); + } + } + handler = &jmploc; + shellexec(argv, envp, path, idx); + } + handler = savehandler; + if (jp) { + struct procstat *ps = &jp->ps[jp->nprocs++]; + ps->pid = pid; + ps->status = -1; + ps->cmd = nullstr; + jp->foreground = 1; +#if JOBS + setcurjob(jp); +#endif + } + INTON; + TRACE(("In parent shell: child = %d\n", (int)pid)); + return pid; +} + + +/* + * Wait for job to finish. + * + * Under job control we have the problem that while a child process is + * running interrupts generated by the user are sent to the child but not + * to the shell. This means that an infinite loop started by an inter- + * active user may be hard to kill. With job control turned off, an + * interactive user may place an interactive program inside a loop. If + * the interactive program catches interrupts, the user doesn't want + * these interrupts to also abort the loop. The approach we take here + * is to have the shell ignore interrupt signals while waiting for a + * foreground process to terminate, and then send itself an interrupt + * signal if the child process was terminated by an interrupt signal. + * Unfortunately, some programs want to do a bit of cleanup and then + * exit on interrupt; unless these processes terminate themselves by + * sending a signal to themselves (instead of calling exit) they will + * confuse this approach. + */ + +int +waitforjob(struct job *jp, int *signaled) +{ +#if JOBS + int propagate_int = jp->jobctl && jp->foreground; +#endif + int status; + int st; + + INTOFF; + TRACE(("waitforjob(%%%td) called\n", jp - jobtab + 1)); + while (jp->state == 0) + if (dowait(DOWAIT_BLOCK | (Tflag ? DOWAIT_SIG | + DOWAIT_SIG_TRAP : 0), jp) == -1) + dotrap(); +#if JOBS + if (jp->jobctl) { + if (ttyfd >= 0 && tcsetpgrp(ttyfd, rootpid) < 0) + error("tcsetpgrp failed, errno=%d\n", errno); + } + if (jp->state == JOBSTOPPED) + setcurjob(jp); +#endif + status = jp->ps[jp->nprocs - 1].status; + if (signaled != NULL) + *signaled = WIFSIGNALED(status); + /* convert to 8 bits */ + if (WIFEXITED(status)) + st = WEXITSTATUS(status); +#if JOBS + else if (WIFSTOPPED(status)) + st = WSTOPSIG(status) + 128; +#endif + else + st = WTERMSIG(status) + 128; + if (! JOBS || jp->state == JOBDONE) + freejob(jp); + if (int_pending()) { + if (!WIFSIGNALED(status) || WTERMSIG(status) != SIGINT) + CLEAR_PENDING_INT; + } +#if JOBS + else if (rootshell && propagate_int && + WIFSIGNALED(status) && WTERMSIG(status) == SIGINT) + kill(getpid(), SIGINT); +#endif + INTON; + return st; +} + + +static void +dummy_handler(int sig __unused) +{ +} + +/* + * Wait for a process to terminate. + */ + +static pid_t +dowait(int mode, struct job *job) +{ + struct sigaction sa, osa; + sigset_t mask, omask; + pid_t pid; + int status; + struct procstat *sp; + struct job *jp; + struct job *thisjob; + const char *sigstr; + int done; + int stopped; + int sig; + int coredump; + int wflags; + int restore_sigchld; + + TRACE(("dowait(%d, %p) called\n", mode, job)); + restore_sigchld = 0; + if ((mode & DOWAIT_SIG) != 0) { + sigfillset(&mask); + sigprocmask(SIG_BLOCK, &mask, &omask); + INTOFF; + if (!issigchldtrapped()) { + restore_sigchld = 1; + sa.sa_handler = dummy_handler; + sa.sa_flags = 0; + sigemptyset(&sa.sa_mask); + sigaction(SIGCHLD, &sa, &osa); + } + } + do { +#if JOBS + if (iflag) + wflags = WUNTRACED | WCONTINUED; + else +#endif + wflags = 0; + if ((mode & (DOWAIT_BLOCK | DOWAIT_SIG)) != DOWAIT_BLOCK) + wflags |= WNOHANG; + pid = wait3(&status, wflags, (struct rusage *)NULL); + TRACE(("wait returns %d, status=%d\n", (int)pid, status)); + if (pid == 0 && (mode & DOWAIT_SIG) != 0) { + pid = -1; + if (((mode & DOWAIT_SIG_TRAP) != 0 ? + pendingsig : pendingsig_waitcmd) != 0) { + errno = EINTR; + break; + } + sigsuspend(&omask); + if (int_pending()) + break; + } + } while (pid == -1 && errno == EINTR); + if (pid == -1 && errno == ECHILD && job != NULL) + job->state = JOBDONE; + if ((mode & DOWAIT_SIG) != 0) { + if (restore_sigchld) + sigaction(SIGCHLD, &osa, NULL); + sigprocmask(SIG_SETMASK, &omask, NULL); + INTON; + } + if (pid <= 0) + return pid; + INTOFF; + thisjob = NULL; + for (jp = jobtab ; jp < jobtab + njobs ; jp++) { + if (jp->used && jp->nprocs > 0) { + done = 1; + stopped = 1; + for (sp = jp->ps ; sp < jp->ps + jp->nprocs ; sp++) { + if (sp->pid == -1) + continue; + if (sp->pid == pid && (sp->status == -1 || + WIFSTOPPED(sp->status))) { + TRACE(("Changing status of proc %d from 0x%x to 0x%x\n", + (int)pid, sp->status, + status)); + if (WIFCONTINUED(status)) { + sp->status = -1; + jp->state = 0; + } else + sp->status = status; + thisjob = jp; + } + if (sp->status == -1) + stopped = 0; + else if (WIFSTOPPED(sp->status)) + done = 0; + } + if (stopped) { /* stopped or done */ + int state = done? JOBDONE : JOBSTOPPED; + if (jp->state != state) { + TRACE(("Job %td: changing state from %d to %d\n", jp - jobtab + 1, jp->state, state)); + jp->state = state; + if (jp != job) { + if (done && !jp->remembered && + !iflag && jp != bgjob) + freejob(jp); +#if JOBS + else if (done) + deljob(jp); +#endif + } + } + } + } + } + INTON; + if (!thisjob || thisjob->state == 0) + ; + else if ((!rootshell || !iflag || thisjob == job) && + thisjob->foreground && thisjob->state != JOBSTOPPED) { + sig = 0; + coredump = 0; + for (sp = thisjob->ps; sp < thisjob->ps + thisjob->nprocs; sp++) + if (WIFSIGNALED(sp->status)) { + sig = WTERMSIG(sp->status); + coredump = WCOREDUMP(sp->status); + } + if (sig > 0 && sig != SIGINT && sig != SIGPIPE) { + sigstr = strsignal(sig); + if (sigstr != NULL) + out2str(sigstr); + else + out2str("Unknown signal"); + if (coredump) + out2str(" (core dumped)"); + out2c('\n'); + flushout(out2); + } + } else { + TRACE(("Not printing status, rootshell=%d, job=%p\n", rootshell, job)); + thisjob->changed = 1; + } + return pid; +} + + + +/* + * return 1 if there are stopped jobs, otherwise 0 + */ +int job_warning = 0; +int +stoppedjobs(void) +{ + int jobno; + struct job *jp; + + if (job_warning) + return (0); + for (jobno = 1, jp = jobtab; jobno <= njobs; jobno++, jp++) { + if (jp->used == 0) + continue; + if (jp->state == JOBSTOPPED) { + out2fmt_flush("You have stopped jobs.\n"); + job_warning = 2; + return (1); + } + } + + return (0); +} + + +static void +checkzombies(void) +{ + while (njobs > 0 && dowait(0, NULL) > 0) + ; +} + + +int +backgndpidset(void) +{ + return backgndpid != -1; +} + + +pid_t +backgndpidval(void) +{ + if (bgjob != NULL && !forcelocal) + bgjob->remembered = 1; + return backgndpid; +} + +/* + * Return a string identifying a command (to be printed by the + * jobs command. + */ + +static char *cmdnextc; +static int cmdnleft; +#define MAXCMDTEXT 200 + +char * +commandtext(union node *n) +{ + char *name; + + cmdnextc = name = ckmalloc(MAXCMDTEXT); + cmdnleft = MAXCMDTEXT - 4; + cmdtxt(n); + *cmdnextc = '\0'; + return name; +} + + +static void +cmdtxtdogroup(union node *n) +{ + cmdputs("; do "); + cmdtxt(n); + cmdputs("; done"); +} + + +static void +cmdtxtredir(union node *n, const char *op, int deffd) +{ + char s[2]; + + if (n->nfile.fd != deffd) { + s[0] = n->nfile.fd + '0'; + s[1] = '\0'; + cmdputs(s); + } + cmdputs(op); + if (n->type == NTOFD || n->type == NFROMFD) { + if (n->ndup.dupfd >= 0) + s[0] = n->ndup.dupfd + '0'; + else + s[0] = '-'; + s[1] = '\0'; + cmdputs(s); + } else { + cmdtxt(n->nfile.fname); + } +} + + +static void +cmdtxt(union node *n) +{ + union node *np; + struct nodelist *lp; + + if (n == NULL) + return; + switch (n->type) { + case NSEMI: + cmdtxt(n->nbinary.ch1); + cmdputs("; "); + cmdtxt(n->nbinary.ch2); + break; + case NAND: + cmdtxt(n->nbinary.ch1); + cmdputs(" && "); + cmdtxt(n->nbinary.ch2); + break; + case NOR: + cmdtxt(n->nbinary.ch1); + cmdputs(" || "); + cmdtxt(n->nbinary.ch2); + break; + case NPIPE: + for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) { + cmdtxt(lp->n); + if (lp->next) + cmdputs(" | "); + } + break; + case NSUBSHELL: + cmdputs("("); + cmdtxt(n->nredir.n); + cmdputs(")"); + break; + case NREDIR: + case NBACKGND: + cmdtxt(n->nredir.n); + break; + case NIF: + cmdputs("if "); + cmdtxt(n->nif.test); + cmdputs("; then "); + cmdtxt(n->nif.ifpart); + cmdputs("..."); + break; + case NWHILE: + cmdputs("while "); + cmdtxt(n->nbinary.ch1); + cmdtxtdogroup(n->nbinary.ch2); + break; + case NUNTIL: + cmdputs("until "); + cmdtxt(n->nbinary.ch1); + cmdtxtdogroup(n->nbinary.ch2); + break; + case NFOR: + cmdputs("for "); + cmdputs(n->nfor.var); + cmdputs(" in ..."); + break; + case NCASE: + cmdputs("case "); + cmdputs(n->ncase.expr->narg.text); + cmdputs(" in ..."); + break; + case NDEFUN: + cmdputs(n->narg.text); + cmdputs("() ..."); + break; + case NNOT: + cmdputs("! "); + cmdtxt(n->nnot.com); + break; + case NCMD: + for (np = n->ncmd.args ; np ; np = np->narg.next) { + cmdtxt(np); + if (np->narg.next) + cmdputs(" "); + } + for (np = n->ncmd.redirect ; np ; np = np->nfile.next) { + cmdputs(" "); + cmdtxt(np); + } + break; + case NARG: + cmdputs(n->narg.text); + break; + case NTO: + cmdtxtredir(n, ">", 1); + break; + case NAPPEND: + cmdtxtredir(n, ">>", 1); + break; + case NTOFD: + cmdtxtredir(n, ">&", 1); + break; + case NCLOBBER: + cmdtxtredir(n, ">|", 1); + break; + case NFROM: + cmdtxtredir(n, "<", 0); + break; + case NFROMTO: + cmdtxtredir(n, "<>", 0); + break; + case NFROMFD: + cmdtxtredir(n, "<&", 0); + break; + case NHERE: + case NXHERE: + cmdputs("<<..."); + break; + default: + cmdputs("???"); + break; + } +} + + + +static void +cmdputs(const char *s) +{ + const char *p; + char *q; + char c; + int subtype = 0; + + if (cmdnleft <= 0) + return; + p = s; + q = cmdnextc; + while ((c = *p++) != '\0') { + if (c == CTLESC) + *q++ = *p++; + else if (c == CTLVAR) { + *q++ = '$'; + if (--cmdnleft > 0) + *q++ = '{'; + subtype = *p++; + if ((subtype & VSTYPE) == VSLENGTH && --cmdnleft > 0) + *q++ = '#'; + } else if (c == '=' && subtype != 0) { + *q = "}-+?=##%%\0X"[(subtype & VSTYPE) - VSNORMAL]; + if (*q) + q++; + else + cmdnleft++; + if (((subtype & VSTYPE) == VSTRIMLEFTMAX || + (subtype & VSTYPE) == VSTRIMRIGHTMAX) && + --cmdnleft > 0) + *q = q[-1], q++; + subtype = 0; + } else if (c == CTLENDVAR) { + *q++ = '}'; + } else if (c == CTLBACKQ || c == CTLBACKQ+CTLQUOTE) { + cmdnleft -= 5; + if (cmdnleft > 0) { + *q++ = '$'; + *q++ = '('; + *q++ = '.'; + *q++ = '.'; + *q++ = '.'; + *q++ = ')'; + } + } else if (c == CTLARI) { + cmdnleft -= 2; + if (cmdnleft > 0) { + *q++ = '$'; + *q++ = '('; + *q++ = '('; + } + p++; + } else if (c == CTLENDARI) { + if (--cmdnleft > 0) { + *q++ = ')'; + *q++ = ')'; + } + } else if (c == CTLQUOTEMARK || c == CTLQUOTEEND) + cmdnleft++; /* ignore */ + else + *q++ = c; + if (--cmdnleft <= 0) { + *q++ = '.'; + *q++ = '.'; + *q++ = '.'; + break; + } + } + cmdnextc = q; +} diff --git a/shell_cmds/sh/jobs.h b/shell_cmds/sh/jobs.h new file mode 100644 index 0000000..f6d90a8 --- /dev/null +++ b/shell_cmds/sh/jobs.h @@ -0,0 +1,67 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)jobs.h 8.2 (Berkeley) 5/4/95 + * $FreeBSD: head/bin/sh/jobs.h 327475 2018-01-01 22:31:52Z jilles $ + */ + +/* Mode argument to forkshell. Don't change FORK_FG or FORK_BG. */ +#define FORK_FG 0 +#define FORK_BG 1 +#define FORK_NOJOB 2 + +#include <signal.h> /* for sig_atomic_t */ + +struct job; + +enum { + SHOWJOBS_DEFAULT, /* job number, status, command */ + SHOWJOBS_VERBOSE, /* job number, PID, status, command */ + SHOWJOBS_PIDS, /* PID only */ + SHOWJOBS_PGIDS /* PID of the group leader only */ +}; + +extern int job_warning; /* user was warned about stopped jobs */ + +void setjobctl(int); +void showjobs(int, int); +struct job *makejob(union node *, int); +pid_t forkshell(struct job *, union node *, int); +pid_t vforkexecshell(struct job *, char **, char **, const char *, int, int []); +int waitforjob(struct job *, int *); +int stoppedjobs(void); +int backgndpidset(void); +pid_t backgndpidval(void); +char *commandtext(union node *); + +#if ! JOBS +#define setjobctl(on) /* do nothing */ +#endif diff --git a/shell_cmds/sh/mail.c b/shell_cmds/sh/mail.c new file mode 100644 index 0000000..607b407 --- /dev/null +++ b/shell_cmds/sh/mail.c @@ -0,0 +1,117 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)mail.c 8.2 (Berkeley) 5/4/95"; +#endif +#endif /* not lint */ +#include <sys/cdefs.h> +__FBSDID("$FreeBSD: head/bin/sh/mail.c 314436 2017-02-28 23:42:47Z imp $"); + +/* + * Routines to check for mail. (Perhaps make part of main.c?) + */ + +#include "shell.h" +#include "exec.h" /* defines padvance() */ +#include "mail.h" +#include "var.h" +#include "output.h" +#include "memalloc.h" +#include "error.h" +#include <sys/types.h> +#include <sys/stat.h> +#include <stdlib.h> + + +#define MAXMBOXES 10 + + +static int nmboxes; /* number of mailboxes */ +static time_t mailtime[MAXMBOXES]; /* times of mailboxes */ + + + +/* + * Print appropriate message(s) if mail has arrived. If the argument is + * nozero, then the value of MAIL has changed, so we just update the + * values. + */ + +void +chkmail(int silent) +{ + int i; + const char *mpath; + char *p; + char *q; + struct stackmark smark; + struct stat statb; + + if (silent) + nmboxes = 10; + if (nmboxes == 0) + return; + setstackmark(&smark); + mpath = mpathset()? mpathval() : mailval(); + for (i = 0 ; i < nmboxes ; i++) { + p = padvance(&mpath, ""); + if (p == NULL) + break; + if (*p == '\0') + continue; + for (q = p ; *q ; q++); + if (q[-1] != '/') + abort(); + q[-1] = '\0'; /* delete trailing '/' */ +#ifdef notdef /* this is what the System V shell claims to do (it lies) */ + if (stat(p, &statb) < 0) + statb.st_mtime = 0; + if (statb.st_mtime > mailtime[i] && ! silent) { + out2str(pathopt? pathopt : "you have mail"); + out2c('\n'); + } + mailtime[i] = statb.st_mtime; +#else /* this is what it should do */ + if (stat(p, &statb) < 0) + statb.st_size = 0; + if (statb.st_size > mailtime[i] && ! silent) { + out2str(pathopt? pathopt : "you have mail"); + out2c('\n'); + } + mailtime[i] = statb.st_size; +#endif + } + nmboxes = i; + popstackmark(&smark); +} diff --git a/shell_cmds/sh/mail.h b/shell_cmds/sh/mail.h new file mode 100644 index 0000000..509f11c --- /dev/null +++ b/shell_cmds/sh/mail.h @@ -0,0 +1,38 @@ +/*- + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)mail.h 8.2 (Berkeley) 5/4/95 + * $FreeBSD: head/bin/sh/mail.h 326025 2017-11-20 19:49:47Z pfg $ + */ + +void chkmail(int); diff --git a/shell_cmds/sh/main.c b/shell_cmds/sh/main.c new file mode 100644 index 0000000..338f97c --- /dev/null +++ b/shell_cmds/sh/main.c @@ -0,0 +1,351 @@ +/*- + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static char const copyright[] = +"@(#) Copyright (c) 1991, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)main.c 8.6 (Berkeley) 5/28/95"; +#endif +#endif /* not lint */ +#include <sys/cdefs.h> +__FBSDID("$FreeBSD: head/bin/sh/main.c 326025 2017-11-20 19:49:47Z pfg $"); + +#include <stdio.h> +#include <signal.h> +#include <sys/stat.h> +#include <unistd.h> +#include <fcntl.h> +#include <locale.h> +#include <errno.h> + +#include "shell.h" +#include "main.h" +#include "mail.h" +#include "options.h" +#include "output.h" +#include "parser.h" +#include "nodes.h" +#include "expand.h" +#include "eval.h" +#include "jobs.h" +#include "input.h" +#include "trap.h" +#include "var.h" +#include "show.h" +#include "memalloc.h" +#include "error.h" +#include "mystring.h" +#include "exec.h" +#include "cd.h" +#include "redir.h" +#include "builtins.h" + +int rootpid; +int rootshell; +struct jmploc main_handler; +int localeisutf8, initial_localeisutf8; + +static void reset(void); +static void cmdloop(int); +static void read_profile(const char *); +static char *find_dot_file(char *); + +/* + * Main routine. We initialize things, parse the arguments, execute + * profiles if we're a login shell, and then call cmdloop to execute + * commands. The setjmp call sets up the location to jump to when an + * exception occurs. When an exception occurs the variable "state" + * is used to figure out how far we had gotten. + */ + +int +main(int argc, char *argv[]) +{ + struct stackmark smark, smark2; + volatile int state; + char *shinit; + + (void) setlocale(LC_ALL, ""); + initcharset(); + state = 0; + if (setjmp(main_handler.loc)) { + switch (exception) { + case EXEXEC: + exitstatus = exerrno; + break; + + case EXERROR: + exitstatus = 2; + break; + + default: + break; + } + + if (state == 0 || iflag == 0 || ! rootshell || + exception == EXEXIT) + exitshell(exitstatus); + reset(); + if (exception == EXINT) + out2fmt_flush("\n"); + popstackmark(&smark); + FORCEINTON; /* enable interrupts */ + if (state == 1) + goto state1; + else if (state == 2) + goto state2; + else if (state == 3) + goto state3; + else + goto state4; + } + handler = &main_handler; +#ifdef DEBUG + opentrace(); + trputs("Shell args: "); trargs(argv); +#endif + rootpid = getpid(); + rootshell = 1; + INTOFF; + initvar(); + setstackmark(&smark); + setstackmark(&smark2); + procargs(argc, argv); + pwd_init(iflag); + INTON; + if (iflag) + chkmail(1); + if (argv[0] && argv[0][0] == '-') { + state = 1; + read_profile("/etc/profile"); +state1: + state = 2; + if (privileged == 0) + read_profile("${HOME-}/.profile"); + else + read_profile("/etc/suid_profile"); + } +state2: + state = 3; + if (!privileged && iflag) { + if ((shinit = lookupvar("ENV")) != NULL && *shinit != '\0') { + state = 3; + read_profile(shinit); + } + } +state3: + state = 4; + popstackmark(&smark2); + if (minusc) { + evalstring(minusc, sflag ? 0 : EV_EXIT); + } +state4: + if (sflag || minusc == NULL) { + cmdloop(1); + } + exitshell(exitstatus); + /*NOTREACHED*/ + return 0; +} + +static void +reset(void) +{ + reseteval(); + resetinput(); +} + +/* + * Read and execute commands. "Top" is nonzero for the top level command + * loop; it turns on prompting if the shell is interactive. + */ + +static void +cmdloop(int top) +{ + union node *n; + struct stackmark smark; + int inter; + int numeof = 0; + + TRACE(("cmdloop(%d) called\n", top)); + setstackmark(&smark); + for (;;) { + if (pendingsig) + dotrap(); + inter = 0; + if (iflag && top) { + inter++; + showjobs(1, SHOWJOBS_DEFAULT); + chkmail(0); + flushout(&output); + } + n = parsecmd(inter); + /* showtree(n); DEBUG */ + if (n == NEOF) { + if (!top || numeof >= 50) + break; + if (!stoppedjobs()) { + if (!Iflag) + break; + out2fmt_flush("\nUse \"exit\" to leave shell.\n"); + } + numeof++; + } else if (n != NULL && nflag == 0) { + job_warning = (job_warning == 2) ? 1 : 0; + numeof = 0; + evaltree(n, 0); + } + popstackmark(&smark); + setstackmark(&smark); + if (evalskip != 0) { + if (evalskip == SKIPRETURN) + evalskip = 0; + break; + } + } + popstackmark(&smark); +} + + + +/* + * Read /etc/profile or .profile. Return on error. + */ + +static void +read_profile(const char *name) +{ + int fd; + const char *expandedname; + + expandedname = expandstr(name); + if (expandedname == NULL) + return; + INTOFF; + if ((fd = open(expandedname, O_RDONLY | O_CLOEXEC)) >= 0) + setinputfd(fd, 1); + INTON; + if (fd < 0) + return; + cmdloop(0); + popfile(); +} + + + +/* + * Read a file containing shell functions. + */ + +void +readcmdfile(const char *name) +{ + setinputfile(name, 1); + cmdloop(0); + popfile(); +} + + + +/* + * Take commands from a file. To be compatible we should do a path + * search for the file, which is necessary to find sub-commands. + */ + + +static char * +find_dot_file(char *basename) +{ + char *fullname; + const char *path = pathval(); + struct stat statb; + + /* don't try this for absolute or relative paths */ + if( strchr(basename, '/')) + return basename; + + while ((fullname = padvance(&path, basename)) != NULL) { + if ((stat(fullname, &statb) == 0) && S_ISREG(statb.st_mode)) { + /* + * Don't bother freeing here, since it will + * be freed by the caller. + */ + return fullname; + } + stunalloc(fullname); + } + return basename; +} + +int +dotcmd(int argc, char **argv) +{ + char *filename, *fullname; + + if (argc < 2) + error("missing filename"); + + exitstatus = 0; + + /* + * Because we have historically not supported any options, + * only treat "--" specially. + */ + filename = argc > 2 && strcmp(argv[1], "--") == 0 ? argv[2] : argv[1]; + + fullname = find_dot_file(filename); + setinputfile(fullname, 1); + commandname = fullname; + cmdloop(0); + popfile(); + return exitstatus; +} + + +int +exitcmd(int argc, char **argv) +{ + if (stoppedjobs()) + return 0; + if (argc > 1) + exitshell(number(argv[1])); + else + exitshell_savedstatus(); +} diff --git a/shell_cmds/sh/main.h b/shell_cmds/sh/main.h new file mode 100644 index 0000000..ed160cd --- /dev/null +++ b/shell_cmds/sh/main.h @@ -0,0 +1,42 @@ +/*- + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)main.h 8.2 (Berkeley) 5/4/95 + * $FreeBSD: head/bin/sh/main.h 326025 2017-11-20 19:49:47Z pfg $ + */ + +extern int rootpid; /* pid of main shell */ +extern int rootshell; /* true if we aren't a child of the main shell */ +extern struct jmploc main_handler; /* top level exception handler */ + +void readcmdfile(const char *); diff --git a/shell_cmds/sh/memalloc.c b/shell_cmds/sh/memalloc.c new file mode 100644 index 0000000..7b5f325 --- /dev/null +++ b/shell_cmds/sh/memalloc.c @@ -0,0 +1,344 @@ +/*- + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)memalloc.c 8.3 (Berkeley) 5/4/95"; +#endif +#endif /* not lint */ +#include <sys/cdefs.h> +__FBSDID("$FreeBSD: head/bin/sh/memalloc.c 326025 2017-11-20 19:49:47Z pfg $"); + +#include <sys/param.h> +#include "shell.h" +#include "output.h" +#include "memalloc.h" +#include "error.h" +#include "mystring.h" +#include "expand.h" +#include <stdlib.h> +#include <unistd.h> + +/* + * Like malloc, but returns an error when out of space. + */ + +pointer +ckmalloc(size_t nbytes) +{ + pointer p; + + INTOFF; + p = malloc(nbytes); + INTON; + if (p == NULL) + error("Out of space"); + return p; +} + + +/* + * Same for realloc. + */ + +pointer +ckrealloc(pointer p, int nbytes) +{ + INTOFF; + p = realloc(p, nbytes); + INTON; + if (p == NULL) + error("Out of space"); + return p; +} + +void +ckfree(pointer p) +{ + INTOFF; + free(p); + INTON; +} + + +/* + * Make a copy of a string in safe storage. + */ + +char * +savestr(const char *s) +{ + char *p; + size_t len; + + len = strlen(s); + p = ckmalloc(len + 1); + memcpy(p, s, len + 1); + return p; +} + + +/* + * Parse trees for commands are allocated in lifo order, so we use a stack + * to make this more efficient, and also to avoid all sorts of exception + * handling code to handle interrupts in the middle of a parse. + * + * The size 496 was chosen because with 16-byte alignment the total size + * for the allocated block is 512. + */ + +#define MINSIZE 496 /* minimum size of a block. */ + + +struct stack_block { + struct stack_block *prev; + /* Data follows */ +}; +#define SPACE(sp) ((char*)(sp) + ALIGN(sizeof(struct stack_block))) + +static struct stack_block *stackp; +char *stacknxt; +int stacknleft; +char *sstrend; + + +static void +stnewblock(int nbytes) +{ + struct stack_block *sp; + int allocsize; + + if (nbytes < MINSIZE) + nbytes = MINSIZE; + + allocsize = ALIGN(sizeof(struct stack_block)) + ALIGN(nbytes); + + INTOFF; + sp = ckmalloc(allocsize); + sp->prev = stackp; + stacknxt = SPACE(sp); + stacknleft = allocsize - (stacknxt - (char*)sp); + sstrend = stacknxt + stacknleft; + stackp = sp; + INTON; +} + + +pointer +stalloc(int nbytes) +{ + char *p; + + nbytes = ALIGN(nbytes); + if (nbytes > stacknleft) + stnewblock(nbytes); + p = stacknxt; + stacknxt += nbytes; + stacknleft -= nbytes; + return p; +} + + +void +stunalloc(pointer p) +{ + if (p == NULL) { /*DEBUG */ + write(STDERR_FILENO, "stunalloc\n", 10); + abort(); + } + stacknleft += stacknxt - (char *)p; + stacknxt = p; +} + + +char * +stsavestr(const char *s) +{ + char *p; + size_t len; + + len = strlen(s); + p = stalloc(len + 1); + memcpy(p, s, len + 1); + return p; +} + + +void +setstackmark(struct stackmark *mark) +{ + mark->stackp = stackp; + mark->stacknxt = stacknxt; + mark->stacknleft = stacknleft; + /* Ensure this block stays in place. */ + if (stackp != NULL && stacknxt == SPACE(stackp)) + stalloc(1); +} + + +void +popstackmark(struct stackmark *mark) +{ + struct stack_block *sp; + + INTOFF; + while (stackp != mark->stackp) { + sp = stackp; + stackp = sp->prev; + ckfree(sp); + } + stacknxt = mark->stacknxt; + stacknleft = mark->stacknleft; + sstrend = stacknxt + stacknleft; + INTON; +} + + +/* + * When the parser reads in a string, it wants to stick the string on the + * stack and only adjust the stack pointer when it knows how big the + * string is. Stackblock (defined in stack.h) returns a pointer to a block + * of space on top of the stack and stackblocklen returns the length of + * this block. Growstackblock will grow this space by at least one byte, + * possibly moving it (like realloc). Grabstackblock actually allocates the + * part of the block that has been used. + */ + +static void +growstackblock(int min) +{ + char *p; + int newlen; + char *oldspace; + int oldlen; + struct stack_block *sp; + struct stack_block *oldstackp; + + if (min < stacknleft) + min = stacknleft; + if ((unsigned int)min >= + INT_MAX / 2 - ALIGN(sizeof(struct stack_block))) + error("Out of space"); + min += stacknleft; + min += ALIGN(sizeof(struct stack_block)); + newlen = 512; + while (newlen < min) + newlen <<= 1; + oldspace = stacknxt; + oldlen = stacknleft; + + if (stackp != NULL && stacknxt == SPACE(stackp)) { + INTOFF; + oldstackp = stackp; + stackp = oldstackp->prev; + sp = ckrealloc((pointer)oldstackp, newlen); + sp->prev = stackp; + stackp = sp; + stacknxt = SPACE(sp); + stacknleft = newlen - (stacknxt - (char*)sp); + sstrend = stacknxt + stacknleft; + INTON; + } else { + newlen -= ALIGN(sizeof(struct stack_block)); + p = stalloc(newlen); + if (oldlen != 0) + memcpy(p, oldspace, oldlen); + stunalloc(p); + } +} + + + +/* + * The following routines are somewhat easier to use that the above. + * The user declares a variable of type STACKSTR, which may be declared + * to be a register. The macro STARTSTACKSTR initializes things. Then + * the user uses the macro STPUTC to add characters to the string. In + * effect, STPUTC(c, p) is the same as *p++ = c except that the stack is + * grown as necessary. When the user is done, she can just leave the + * string there and refer to it using stackblock(). Or she can allocate + * the space for it using grabstackstr(). If it is necessary to allow + * someone else to use the stack temporarily and then continue to grow + * the string, the user should use grabstack to allocate the space, and + * then call ungrabstr(p) to return to the previous mode of operation. + * + * USTPUTC is like STPUTC except that it doesn't check for overflow. + * CHECKSTACKSPACE can be called before USTPUTC to ensure that there + * is space for at least one character. + */ + +static char * +growstrstackblock(int n, int min) +{ + growstackblock(min); + return stackblock() + n; +} + +char * +growstackstr(void) +{ + int len; + + len = stackblocksize(); + return (growstrstackblock(len, 0)); +} + + +/* + * Called from CHECKSTRSPACE. + */ + +char * +makestrspace(int min, char *p) +{ + int len; + + len = p - stackblock(); + return (growstrstackblock(len, min)); +} + + +char * +stputbin(const char *data, size_t len, char *p) +{ + CHECKSTRSPACE(len, p); + memcpy(p, data, len); + return (p + len); +} + +char * +stputs(const char *data, char *p) +{ + return (stputbin(data, strlen(data), p)); +} diff --git a/shell_cmds/sh/memalloc.h b/shell_cmds/sh/memalloc.h new file mode 100644 index 0000000..180abfd --- /dev/null +++ b/shell_cmds/sh/memalloc.h @@ -0,0 +1,88 @@ +/*- + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)memalloc.h 8.2 (Berkeley) 5/4/95 + * $FreeBSD: head/bin/sh/memalloc.h 326025 2017-11-20 19:49:47Z pfg $ + */ + +#include <string.h> + +struct stackmark { + struct stack_block *stackp; + char *stacknxt; + int stacknleft; +}; + + +extern char *stacknxt; +extern int stacknleft; +extern char *sstrend; + +pointer ckmalloc(size_t); +pointer ckrealloc(pointer, int); +void ckfree(pointer); +char *savestr(const char *); +pointer stalloc(int); +void stunalloc(pointer); +char *stsavestr(const char *); +void setstackmark(struct stackmark *); +void popstackmark(struct stackmark *); +char *growstackstr(void); +char *makestrspace(int, char *); +char *stputbin(const char *data, size_t len, char *p); +char *stputs(const char *data, char *p); + + + +#define stackblock() stacknxt +#define stackblocksize() stacknleft +#define grabstackblock(n) stalloc(n) +#define STARTSTACKSTR(p) p = stackblock() +#define STPUTC(c, p) do { if (p == sstrend) p = growstackstr(); *p++ = (c); } while(0) +#define CHECKSTRSPACE(n, p) { if ((size_t)(sstrend - p) < n) p = makestrspace(n, p); } +#define USTPUTC(c, p) (*p++ = (c)) +/* + * STACKSTRNUL's use is where we want to be able to turn a stack + * (non-sentinel, character counting string) into a C string, + * and later pretend the NUL is not there. + * Note: Because of STACKSTRNUL's semantics, STACKSTRNUL cannot be used + * on a stack that will grabstackstr()ed. + */ +#define STACKSTRNUL(p) (p == sstrend ? (p = growstackstr(), *p = '\0') : (*p = '\0')) +#define STUNPUTC(p) (--p) +#define STTOPC(p) p[-1] +#define STADJUST(amount, p) (p += (amount)) +#define grabstackstr(p) stalloc((char *)p - stackblock()) +#define ungrabstackstr(s, p) stunalloc((s)) +#define STPUTBIN(s, len, p) p = stputbin((s), (len), p) +#define STPUTS(s, p) p = stputs((s), p) diff --git a/shell_cmds/sh/miscbltin.c b/shell_cmds/sh/miscbltin.c new file mode 100644 index 0000000..52a810a --- /dev/null +++ b/shell_cmds/sh/miscbltin.c @@ -0,0 +1,534 @@ +/*- + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)miscbltin.c 8.4 (Berkeley) 5/4/95"; +#endif +#endif /* not lint */ +#include <sys/cdefs.h> +__FBSDID("$FreeBSD: head/bin/sh/miscbltin.c 326025 2017-11-20 19:49:47Z pfg $"); + +/* + * Miscellaneous builtins. + */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/time.h> +#include <sys/resource.h> +#include <unistd.h> +#include <errno.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> + +#include "shell.h" +#include "options.h" +#include "var.h" +#include "output.h" +#include "memalloc.h" +#include "error.h" +#include "mystring.h" +#include "syntax.h" +#include "trap.h" + +#undef eflag + +int readcmd(int, char **); +int umaskcmd(int, char **); +int ulimitcmd(int, char **); + +/* + * The read builtin. The -r option causes backslashes to be treated like + * ordinary characters. + * + * This uses unbuffered input, which may be avoidable in some cases. + * + * Note that if IFS=' :' then read x y should work so that: + * 'a b' x='a', y='b' + * ' a b ' x='a', y='b' + * ':b' x='', y='b' + * ':' x='', y='' + * '::' x='', y='' + * ': :' x='', y='' + * ':::' x='', y='::' + * ':b c:' x='', y='b c:' + */ + +int +readcmd(int argc __unused, char **argv __unused) +{ + char **ap; + int backslash; + char c; + int rflag; + char *prompt; + const char *ifs; + char *p; + int startword; + int status; + int i; + int is_ifs; + int saveall = 0; + ptrdiff_t lastnonifs, lastnonifsws; + struct timeval tv; + char *tvptr; + fd_set ifds; + ssize_t nread; + int sig; + + rflag = 0; + prompt = NULL; + tv.tv_sec = -1; + tv.tv_usec = 0; + while ((i = nextopt("erp:t:")) != '\0') { + switch(i) { + case 'p': + prompt = shoptarg; + break; + case 'e': + break; + case 'r': + rflag = 1; + break; + case 't': + tv.tv_sec = strtol(shoptarg, &tvptr, 0); + if (tvptr == shoptarg) + error("timeout value"); + switch(*tvptr) { + case 0: + case 's': + break; + case 'h': + tv.tv_sec *= 60; + /* FALLTHROUGH */ + case 'm': + tv.tv_sec *= 60; + break; + default: + error("timeout unit"); + } + break; + } + } + if (prompt && isatty(0)) { + out2str(prompt); + flushall(); + } + if (*(ap = argptr) == NULL) + error("arg count"); + if ((ifs = bltinlookup("IFS", 1)) == NULL) + ifs = " \t\n"; + + if (tv.tv_sec >= 0) { + /* + * Wait for something to become available. + */ + FD_ZERO(&ifds); + FD_SET(0, &ifds); + status = select(1, &ifds, NULL, NULL, &tv); + /* + * If there's nothing ready, return an error. + */ + if (status <= 0) { + sig = pendingsig; + return (128 + (sig != 0 ? sig : SIGALRM)); + } + } + + status = 0; + startword = 2; + backslash = 0; + STARTSTACKSTR(p); + lastnonifs = lastnonifsws = -1; + for (;;) { + nread = read(STDIN_FILENO, &c, 1); + if (nread == -1) { + if (errno == EINTR) { + sig = pendingsig; + if (sig == 0) + continue; + status = 128 + sig; + break; + } + warning("read error: %s", strerror(errno)); + status = 2; + break; + } else if (nread != 1) { + status = 1; + break; + } + if (c == '\0') + continue; + CHECKSTRSPACE(1, p); + if (backslash) { + backslash = 0; + if (c != '\n') { + startword = 0; + lastnonifs = lastnonifsws = p - stackblock(); + USTPUTC(c, p); + } + continue; + } + if (!rflag && c == '\\') { + backslash++; + continue; + } + if (c == '\n') + break; + if (strchr(ifs, c)) + is_ifs = strchr(" \t\n", c) ? 1 : 2; + else + is_ifs = 0; + + if (startword != 0) { + if (is_ifs == 1) { + /* Ignore leading IFS whitespace */ + if (saveall) + USTPUTC(c, p); + continue; + } + if (is_ifs == 2 && startword == 1) { + /* Only one non-whitespace IFS per word */ + startword = 2; + if (saveall) { + lastnonifsws = p - stackblock(); + USTPUTC(c, p); + } + continue; + } + } + + if (is_ifs == 0) { + /* append this character to the current variable */ + startword = 0; + if (saveall) + /* Not just a spare terminator */ + saveall++; + lastnonifs = lastnonifsws = p - stackblock(); + USTPUTC(c, p); + continue; + } + + /* end of variable... */ + startword = is_ifs; + + if (ap[1] == NULL) { + /* Last variable needs all IFS chars */ + saveall++; + if (is_ifs == 2) + lastnonifsws = p - stackblock(); + USTPUTC(c, p); + continue; + } + + STACKSTRNUL(p); + setvar(*ap, stackblock(), 0); + ap++; + STARTSTACKSTR(p); + lastnonifs = lastnonifsws = -1; + } + STACKSTRNUL(p); + + /* + * Remove trailing IFS chars: always remove whitespace, don't remove + * non-whitespace unless it was naked + */ + if (saveall <= 1) + lastnonifsws = lastnonifs; + stackblock()[lastnonifsws + 1] = '\0'; + setvar(*ap, stackblock(), 0); + + /* Set any remaining args to "" */ + while (*++ap != NULL) + setvar(*ap, "", 0); + return status; +} + + + +int +umaskcmd(int argc __unused, char **argv __unused) +{ + char *ap; + int mask; + int i; + int symbolic_mode = 0; + + while ((i = nextopt("S")) != '\0') { + symbolic_mode = 1; + } + + INTOFF; + mask = umask(0); + umask(mask); + INTON; + + if ((ap = *argptr) == NULL) { + if (symbolic_mode) { + char u[4], g[4], o[4]; + + i = 0; + if ((mask & S_IRUSR) == 0) + u[i++] = 'r'; + if ((mask & S_IWUSR) == 0) + u[i++] = 'w'; + if ((mask & S_IXUSR) == 0) + u[i++] = 'x'; + u[i] = '\0'; + + i = 0; + if ((mask & S_IRGRP) == 0) + g[i++] = 'r'; + if ((mask & S_IWGRP) == 0) + g[i++] = 'w'; + if ((mask & S_IXGRP) == 0) + g[i++] = 'x'; + g[i] = '\0'; + + i = 0; + if ((mask & S_IROTH) == 0) + o[i++] = 'r'; + if ((mask & S_IWOTH) == 0) + o[i++] = 'w'; + if ((mask & S_IXOTH) == 0) + o[i++] = 'x'; + o[i] = '\0'; + + out1fmt("u=%s,g=%s,o=%s\n", u, g, o); + } else { + out1fmt("%.4o\n", mask); + } + } else { + if (is_digit(*ap)) { + mask = 0; + do { + if (*ap >= '8' || *ap < '0') + error("Illegal number: %s", *argptr); + mask = (mask << 3) + (*ap - '0'); + } while (*++ap != '\0'); + umask(mask); + } else { + void *set; + INTOFF; + if ((set = setmode (ap)) == NULL) + error("Illegal number: %s", ap); + + mask = getmode (set, ~mask & 0777); + umask(~mask & 0777); + free(set); + INTON; + } + } + return 0; +} + +/* + * ulimit builtin + * + * This code, originally by Doug Gwyn, Doug Kingston, Eric Gisin, and + * Michael Rendell was ripped from pdksh 5.0.8 and hacked for use with + * ash by J.T. Conklin. + * + * Public domain. + */ + +struct limits { + const char *name; + const char *units; + int cmd; + short factor; /* multiply by to get rlim_{cur,max} values */ + char option; +}; + +static const struct limits limits[] = { +#ifdef RLIMIT_CPU + { "cpu time", "seconds", RLIMIT_CPU, 1, 't' }, +#endif +#ifdef RLIMIT_FSIZE + { "file size", "512-blocks", RLIMIT_FSIZE, 512, 'f' }, +#endif +#ifdef RLIMIT_DATA + { "data seg size", "kbytes", RLIMIT_DATA, 1024, 'd' }, +#endif +#ifdef RLIMIT_STACK + { "stack size", "kbytes", RLIMIT_STACK, 1024, 's' }, +#endif +#ifdef RLIMIT_CORE + { "core file size", "512-blocks", RLIMIT_CORE, 512, 'c' }, +#endif +#ifdef RLIMIT_RSS + { "max memory size", "kbytes", RLIMIT_RSS, 1024, 'm' }, +#endif +#ifdef RLIMIT_MEMLOCK + { "locked memory", "kbytes", RLIMIT_MEMLOCK, 1024, 'l' }, +#endif +#ifdef RLIMIT_NPROC + { "max user processes", (char *)0, RLIMIT_NPROC, 1, 'u' }, +#endif +#ifdef RLIMIT_NOFILE + { "open files", (char *)0, RLIMIT_NOFILE, 1, 'n' }, +#endif +#ifdef RLIMIT_VMEM + { "virtual mem size", "kbytes", RLIMIT_VMEM, 1024, 'v' }, +#endif +#ifdef RLIMIT_SWAP + { "swap limit", "kbytes", RLIMIT_SWAP, 1024, 'w' }, +#endif +#ifdef RLIMIT_SBSIZE + { "socket buffer size", "bytes", RLIMIT_SBSIZE, 1, 'b' }, +#endif +#ifdef RLIMIT_NPTS + { "pseudo-terminals", (char *)0, RLIMIT_NPTS, 1, 'p' }, +#endif +#ifdef RLIMIT_KQUEUES + { "kqueues", (char *)0, RLIMIT_KQUEUES, 1, 'k' }, +#endif +#ifdef RLIMIT_UMTXP + { "umtx shared locks", (char *)0, RLIMIT_UMTXP, 1, 'o' }, +#endif + { (char *) 0, (char *)0, 0, 0, '\0' } +}; + +enum limithow { SOFT = 0x1, HARD = 0x2 }; + +static void +printlimit(enum limithow how, const struct rlimit *limit, + const struct limits *l) +{ + rlim_t val = 0; + + if (how & SOFT) + val = limit->rlim_cur; + else if (how & HARD) + val = limit->rlim_max; + if (val == RLIM_INFINITY) + out1str("unlimited\n"); + else + { + val /= l->factor; + out1fmt("%jd\n", (intmax_t)val); + } +} + +int +ulimitcmd(int argc __unused, char **argv __unused) +{ + rlim_t val = 0; + enum limithow how = SOFT | HARD; + const struct limits *l; + int set, all = 0; + int optc, what; + struct rlimit limit; + + what = 'f'; + while ((optc = nextopt("HSatfdsmcnuvlbpwko")) != '\0') + switch (optc) { + case 'H': + how = HARD; + break; + case 'S': + how = SOFT; + break; + case 'a': + all = 1; + break; + default: + what = optc; + } + + for (l = limits; l->name && l->option != what; l++) + ; + if (!l->name) + error("internal error (%c)", what); + + set = *argptr ? 1 : 0; + if (set) { + char *p = *argptr; + + if (all || argptr[1]) + error("too many arguments"); + if (strcmp(p, "unlimited") == 0) + val = RLIM_INFINITY; + else { + char *end; + uintmax_t uval; + + if (*p < '0' || *p > '9') + error("bad number"); + errno = 0; + uval = strtoumax(p, &end, 10); + if (errno != 0 || *end != '\0') + error("bad number"); + if (uval > UINTMAX_MAX / l->factor) + error("bad number"); + uval *= l->factor; + val = (rlim_t)uval; + if ((uintmax_t)val != uval || + val == RLIM_INFINITY) + error("bad number"); + } + } + if (all) { + for (l = limits; l->name; l++) { + char optbuf[40]; + if (getrlimit(l->cmd, &limit) < 0) + error("can't get limit: %s", strerror(errno)); + + if (l->units) + snprintf(optbuf, sizeof(optbuf), + "(%s, -%c) ", l->units, l->option); + else + snprintf(optbuf, sizeof(optbuf), + "(-%c) ", l->option); + out1fmt("%-18s %18s ", l->name, optbuf); + printlimit(how, &limit, l); + } + return 0; + } + + if (getrlimit(l->cmd, &limit) < 0) + error("can't get limit: %s", strerror(errno)); + if (set) { + if (how & SOFT) + limit.rlim_cur = val; + if (how & HARD) + limit.rlim_max = val; + if (setrlimit(l->cmd, &limit) < 0) + error("bad limit: %s", strerror(errno)); + } else + printlimit(how, &limit, l); + return 0; +} diff --git a/shell_cmds/sh/mkbuiltins b/shell_cmds/sh/mkbuiltins new file mode 100755 index 0000000..7c2cef1 --- /dev/null +++ b/shell_cmds/sh/mkbuiltins @@ -0,0 +1,137 @@ +#!/bin/sh - + +#- +# Copyright (c) 1991, 1993 +# The Regents of the University of California. All rights reserved. +# +# This code is derived from software contributed to Berkeley by +# Kenneth Almquist. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# @(#)mkbuiltins 8.2 (Berkeley) 5/4/95 +# $FreeBSD: head/bin/sh/mkbuiltins 328934 2018-02-06 15:41:35Z arichardson $ + +temp=`mktemp -t ka` +havehist=1 +if [ "X$1" = "X-h" ]; then + havehist=0 + shift +fi +srcdir=$1 +havejobs=0 +if grep '^#define[ ]*JOBS[ ]*1' $srcdir/shell.h > /dev/null +then havejobs=1 +fi +exec > builtins.c +cat <<\! +/* + * This file was generated by the mkbuiltins program. + */ + +#include <stdlib.h> +#include "shell.h" +#include "builtins.h" + +! +awk '/^[^#]/ {if(('$havejobs' || $2 != "-j") && ('$havehist' || $2 != "-h")) \ + print $0}' $srcdir/builtins.def | sed 's/-[hj]//' > $temp +echo 'int (*const builtinfunc[])(int, char **) = {' +awk '/^[^#]/ { printf "\t%s,\n", $1}' $temp +echo '}; + +const unsigned char builtincmd[] = {' +awk '{ for (i = 2 ; i <= NF ; i++) { + if ($i == "-s") { + spc = 1; + } else if ($i == "-n") { + # Handled later for builtins.h + continue + } else { + printf "\t\"\\%03o\\%03o%s\"\n", length($i), (spc ? 128 : 0) + NR-1, $i + spc = 0; + } + }}' $temp +echo '};' + +exec > builtins.h +cat <<\! +/* + * This file was generated by the mkbuiltins program. + */ + +#include <sys/cdefs.h> +! +tr abcdefghijklmnopqrstuvwxyz ABCDEFGHIJKLMNOPQRSTUVWXYZ < $temp | + awk '{ printf "#define %s %d\n", $1, NR-1}' +echo ' +#define BUILTIN_SPECIAL 0x80 + +extern int (*const builtinfunc[])(int, char **); +extern const unsigned char builtincmd[]; +' +awk '{ printf "int %s(int, char **);\n", $1}' $temp + +# Build safe_builtin_always() +cat <<EOF + +static inline int +safe_builtin_always(int idx) +{ +EOF +awk ' +BEGIN { printed = 0 } +{ + for (i = 2 ; i <= NF ; i++) { + if ($i == "-s") { + continue + } else if ($i == "-n") { + nofork = 1; + } else { + if (nofork == 0) { + continue + } + if (printed == 1) { + printf " || \n\t " + } else { + printf "\tif (" + } + printf "idx == " toupper($1) + printed = 1 + nofork = 0; + # Only need to check each once + break + } + } +}' $temp + +cat << EOF +) + return (1); + return(0); +} +EOF + +rm -f $temp diff --git a/shell_cmds/sh/mknodes.c b/shell_cmds/sh/mknodes.c new file mode 100644 index 0000000..f289890 --- /dev/null +++ b/shell_cmds/sh/mknodes.c @@ -0,0 +1,467 @@ +/*- + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if 0 +#ifndef lint +static char const copyright[] = +"@(#) Copyright (c) 1991, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)mknodes.c 8.2 (Berkeley) 5/4/95"; +#endif /* not lint */ +#endif +#include <sys/cdefs.h> +__FBSDID("$FreeBSD: head/bin/sh/mknodes.c 326025 2017-11-20 19:49:47Z pfg $"); + +/* + * This program reads the nodetypes file and nodes.c.pat file. It generates + * the files nodes.h and nodes.c. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <stdarg.h> + +// When building for iOS, we don't have a new enough OS X SDK. +#ifndef __printf0like +#define __printf0like(fmtarg, firstvararg) \ +__attribute__((__format__ (__printf0__, fmtarg, firstvararg))) +#endif + +#define MAXTYPES 50 /* max number of node types */ +#define MAXFIELDS 20 /* max fields in a structure */ +#define BUFLEN 100 /* size of character buffers */ + +/* field types */ +#define T_NODE 1 /* union node *field */ +#define T_NODELIST 2 /* struct nodelist *field */ +#define T_STRING 3 +#define T_INT 4 /* int field */ +#define T_OTHER 5 /* other */ +#define T_TEMP 6 /* don't copy this field */ + + +struct field { /* a structure field */ + char *name; /* name of field */ + int type; /* type of field */ + char *decl; /* declaration of field */ +}; + + +struct str { /* struct representing a node structure */ + char *tag; /* structure tag */ + int nfields; /* number of fields in the structure */ + struct field field[MAXFIELDS]; /* the fields of the structure */ + int done; /* set if fully parsed */ +}; + + +static int ntypes; /* number of node types */ +static char *nodename[MAXTYPES]; /* names of the nodes */ +static struct str *nodestr[MAXTYPES]; /* type of structure used by the node */ +static int nstr; /* number of structures */ +static struct str str[MAXTYPES]; /* the structures */ +static struct str *curstr; /* current structure */ +static char line[1024]; +static int linno; +static char *linep; + +static void parsenode(void); +static void parsefield(void); +static void output(char *); +static void outsizes(FILE *); +static void outfunc(FILE *, int); +static void indent(int, FILE *); +static int nextfield(char *); +static void skipbl(void); +static int readline(FILE *); +static void error(const char *, ...) __printf0like(1, 2) __dead2; +static char *savestr(const char *); + + +int +main(int argc, char *argv[]) +{ + FILE *infp; + + if (argc != 3) + error("usage: mknodes file"); + if ((infp = fopen(argv[1], "r")) == NULL) + error("Can't open %s: %s", argv[1], strerror(errno)); + while (readline(infp)) { + if (line[0] == ' ' || line[0] == '\t') + parsefield(); + else if (line[0] != '\0') + parsenode(); + } + fclose(infp); + output(argv[2]); + exit(0); +} + + + +static void +parsenode(void) +{ + char name[BUFLEN]; + char tag[BUFLEN]; + struct str *sp; + + if (curstr && curstr->nfields > 0) + curstr->done = 1; + nextfield(name); + if (! nextfield(tag)) + error("Tag expected"); + if (*linep != '\0') + error("Garbage at end of line"); + nodename[ntypes] = savestr(name); + for (sp = str ; sp < str + nstr ; sp++) { + if (strcmp(sp->tag, tag) == 0) + break; + } + if (sp >= str + nstr) { + sp->tag = savestr(tag); + sp->nfields = 0; + curstr = sp; + nstr++; + } + nodestr[ntypes] = sp; + ntypes++; +} + + +static void +parsefield(void) +{ + char name[BUFLEN]; + char type[BUFLEN]; + char decl[2 * BUFLEN]; + struct field *fp; + + if (curstr == NULL || curstr->done) + error("No current structure to add field to"); + if (! nextfield(name)) + error("No field name"); + if (! nextfield(type)) + error("No field type"); + fp = &curstr->field[curstr->nfields]; + fp->name = savestr(name); + if (strcmp(type, "nodeptr") == 0) { + fp->type = T_NODE; + sprintf(decl, "union node *%s", name); + } else if (strcmp(type, "nodelist") == 0) { + fp->type = T_NODELIST; + sprintf(decl, "struct nodelist *%s", name); + } else if (strcmp(type, "string") == 0) { + fp->type = T_STRING; + sprintf(decl, "char *%s", name); + } else if (strcmp(type, "int") == 0) { + fp->type = T_INT; + sprintf(decl, "int %s", name); + } else if (strcmp(type, "other") == 0) { + fp->type = T_OTHER; + } else if (strcmp(type, "temp") == 0) { + fp->type = T_TEMP; + } else { + error("Unknown type %s", type); + } + if (fp->type == T_OTHER || fp->type == T_TEMP) { + skipbl(); + fp->decl = savestr(linep); + } else { + if (*linep) + error("Garbage at end of line"); + fp->decl = savestr(decl); + } + curstr->nfields++; +} + + +static const char writer[] = "\ +/*\n\ + * This file was generated by the mknodes program.\n\ + */\n\ +\n"; + +static void +output(char *file) +{ + FILE *hfile; + FILE *cfile; + FILE *patfile; + int i; + struct str *sp; + struct field *fp; + char *p; + + if ((patfile = fopen(file, "r")) == NULL) + error("Can't open %s: %s", file, strerror(errno)); + if ((hfile = fopen("nodes.h", "w")) == NULL) + error("Can't create nodes.h: %s", strerror(errno)); + if ((cfile = fopen("nodes.c", "w")) == NULL) + error("Can't create nodes.c"); + fputs(writer, hfile); + for (i = 0 ; i < ntypes ; i++) + fprintf(hfile, "#define %s %d\n", nodename[i], i); + fputs("\n\n\n", hfile); + for (sp = str ; sp < &str[nstr] ; sp++) { + fprintf(hfile, "struct %s {\n", sp->tag); + for (i = sp->nfields, fp = sp->field ; --i >= 0 ; fp++) { + fprintf(hfile, " %s;\n", fp->decl); + } + fputs("};\n\n\n", hfile); + } + fputs("union node {\n", hfile); + fprintf(hfile, " int type;\n"); + for (sp = str ; sp < &str[nstr] ; sp++) { + fprintf(hfile, " struct %s %s;\n", sp->tag, sp->tag); + } + fputs("};\n\n\n", hfile); + fputs("struct nodelist {\n", hfile); + fputs("\tstruct nodelist *next;\n", hfile); + fputs("\tunion node *n;\n", hfile); + fputs("};\n\n\n", hfile); + fputs("struct funcdef;\n", hfile); + fputs("struct funcdef *copyfunc(union node *);\n", hfile); + fputs("union node *getfuncnode(struct funcdef *);\n", hfile); + fputs("void reffunc(struct funcdef *);\n", hfile); + fputs("void unreffunc(struct funcdef *);\n", hfile); + if (ferror(hfile)) + error("Can't write to nodes.h"); + if (fclose(hfile)) + error("Can't close nodes.h"); + + fputs(writer, cfile); + while (fgets(line, sizeof line, patfile) != NULL) { + for (p = line ; *p == ' ' || *p == '\t' ; p++); + if (strcmp(p, "%SIZES\n") == 0) + outsizes(cfile); + else if (strcmp(p, "%CALCSIZE\n") == 0) + outfunc(cfile, 1); + else if (strcmp(p, "%COPY\n") == 0) + outfunc(cfile, 0); + else + fputs(line, cfile); + } + fclose(patfile); + if (ferror(cfile)) + error("Can't write to nodes.c"); + if (fclose(cfile)) + error("Can't close nodes.c"); +} + + + +static void +outsizes(FILE *cfile) +{ + int i; + + fprintf(cfile, "static const short nodesize[%d] = {\n", ntypes); + for (i = 0 ; i < ntypes ; i++) { + fprintf(cfile, " ALIGN(sizeof (struct %s)),\n", nodestr[i]->tag); + } + fprintf(cfile, "};\n"); +} + + +static void +outfunc(FILE *cfile, int calcsize) +{ + struct str *sp; + struct field *fp; + int i; + + fputs(" if (n == NULL)\n", cfile); + if (calcsize) + fputs(" return;\n", cfile); + else + fputs(" return NULL;\n", cfile); + if (calcsize) + fputs(" result->blocksize += nodesize[n->type];\n", cfile); + else { + fputs(" new = state->block;\n", cfile); + fputs(" state->block = (char *)state->block + nodesize[n->type];\n", cfile); + } + fputs(" switch (n->type) {\n", cfile); + for (sp = str ; sp < &str[nstr] ; sp++) { + for (i = 0 ; i < ntypes ; i++) { + if (nodestr[i] == sp) + fprintf(cfile, " case %s:\n", nodename[i]); + } + for (i = sp->nfields ; --i >= 1 ; ) { + fp = &sp->field[i]; + switch (fp->type) { + case T_NODE: + if (calcsize) { + indent(12, cfile); + fprintf(cfile, "calcsize(n->%s.%s, result);\n", + sp->tag, fp->name); + } else { + indent(12, cfile); + fprintf(cfile, "new->%s.%s = copynode(n->%s.%s, state);\n", + sp->tag, fp->name, sp->tag, fp->name); + } + break; + case T_NODELIST: + if (calcsize) { + indent(12, cfile); + fprintf(cfile, "sizenodelist(n->%s.%s, result);\n", + sp->tag, fp->name); + } else { + indent(12, cfile); + fprintf(cfile, "new->%s.%s = copynodelist(n->%s.%s, state);\n", + sp->tag, fp->name, sp->tag, fp->name); + } + break; + case T_STRING: + if (calcsize) { + indent(12, cfile); + fprintf(cfile, "result->stringsize += strlen(n->%s.%s) + 1;\n", + sp->tag, fp->name); + } else { + indent(12, cfile); + fprintf(cfile, "new->%s.%s = nodesavestr(n->%s.%s, state);\n", + sp->tag, fp->name, sp->tag, fp->name); + } + break; + case T_INT: + case T_OTHER: + if (! calcsize) { + indent(12, cfile); + fprintf(cfile, "new->%s.%s = n->%s.%s;\n", + sp->tag, fp->name, sp->tag, fp->name); + } + break; + } + } + indent(12, cfile); + fputs("break;\n", cfile); + } + fputs(" };\n", cfile); + if (! calcsize) + fputs(" new->type = n->type;\n", cfile); +} + + +static void +indent(int amount, FILE *fp) +{ + while (amount >= 8) { + putc('\t', fp); + amount -= 8; + } + while (--amount >= 0) { + putc(' ', fp); + } +} + + +static int +nextfield(char *buf) +{ + char *p, *q; + + p = linep; + while (*p == ' ' || *p == '\t') + p++; + q = buf; + while (*p != ' ' && *p != '\t' && *p != '\0') + *q++ = *p++; + *q = '\0'; + linep = p; + return (q > buf); +} + + +static void +skipbl(void) +{ + while (*linep == ' ' || *linep == '\t') + linep++; +} + + +static int +readline(FILE *infp) +{ + char *p; + + if (fgets(line, 1024, infp) == NULL) + return 0; + for (p = line ; *p != '#' && *p != '\n' && *p != '\0' ; p++); + while (p > line && (p[-1] == ' ' || p[-1] == '\t')) + p--; + *p = '\0'; + linep = line; + linno++; + if (p - line > BUFLEN) + error("Line too long"); + return 1; +} + + + +static void +error(const char *msg, ...) +{ + va_list va; + va_start(va, msg); + + (void) fprintf(stderr, "line %d: ", linno); + (void) vfprintf(stderr, msg, va); + (void) fputc('\n', stderr); + + va_end(va); + + exit(2); +} + + + +static char * +savestr(const char *s) +{ + char *p; + + if ((p = malloc(strlen(s) + 1)) == NULL) + error("Out of space"); + (void) strcpy(p, s); + return p; +} diff --git a/shell_cmds/sh/mksyntax.c b/shell_cmds/sh/mksyntax.c new file mode 100644 index 0000000..52c5631 --- /dev/null +++ b/shell_cmds/sh/mksyntax.c @@ -0,0 +1,331 @@ +/*- + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if 0 +#ifndef lint +static char const copyright[] = +"@(#) Copyright (c) 1991, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)mksyntax.c 8.2 (Berkeley) 5/4/95"; +#endif /* not lint */ +#endif +#include <sys/cdefs.h> +__FBSDID("$FreeBSD: head/bin/sh/mksyntax.c 326025 2017-11-20 19:49:47Z pfg $"); + +/* + * This program creates syntax.h and syntax.c. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include "parser.h" + + +struct synclass { + const char *name; + const char *comment; +}; + +/* Syntax classes */ +static const struct synclass synclass[] = { + { "CWORD", "character is nothing special" }, + { "CNL", "newline character" }, + { "CBACK", "a backslash character" }, + { "CSBACK", "a backslash character in single quotes" }, + { "CSQUOTE", "single quote" }, + { "CDQUOTE", "double quote" }, + { "CENDQUOTE", "a terminating quote" }, + { "CBQUOTE", "backwards single quote" }, + { "CVAR", "a dollar sign" }, + { "CENDVAR", "a '}' character" }, + { "CLP", "a left paren in arithmetic" }, + { "CRP", "a right paren in arithmetic" }, + { "CEOF", "end of file" }, + { "CCTL", "like CWORD, except it must be escaped" }, + { "CSPCL", "these terminate a word" }, + { "CIGN", "character should be ignored" }, + { NULL, NULL } +}; + + +/* + * Syntax classes for is_ functions. Warning: if you add new classes + * you may have to change the definition of the is_in_name macro. + */ +static const struct synclass is_entry[] = { + { "ISDIGIT", "a digit" }, + { "ISUPPER", "an upper case letter" }, + { "ISLOWER", "a lower case letter" }, + { "ISUNDER", "an underscore" }, + { "ISSPECL", "the name of a special parameter" }, + { NULL, NULL } +}; + +static const char writer[] = "\ +/*\n\ + * This file was generated by the mksyntax program.\n\ + */\n\ +\n"; + + +static FILE *cfile; +static FILE *hfile; + +static void add_default(void); +static void finish(void); +static void init(const char *); +static void add(const char *, const char *); +static void output_type_macros(void); + +int +main(int argc __unused, char **argv __unused) +{ + int i; + char buf[80]; + int pos; + + /* Create output files */ + if ((cfile = fopen("syntax.c", "w")) == NULL) { + perror("syntax.c"); + exit(2); + } + if ((hfile = fopen("syntax.h", "w")) == NULL) { + perror("syntax.h"); + exit(2); + } + fputs(writer, hfile); + fputs(writer, cfile); + + fputs("#include <sys/cdefs.h>\n", hfile); + fputs("#include <limits.h>\n\n", hfile); + + /* Generate the #define statements in the header file */ + fputs("/* Syntax classes */\n", hfile); + for (i = 0 ; synclass[i].name ; i++) { + sprintf(buf, "#define %s %d", synclass[i].name, i); + fputs(buf, hfile); + for (pos = strlen(buf) ; pos < 32 ; pos = (pos + 8) & ~07) + putc('\t', hfile); + fprintf(hfile, "/* %s */\n", synclass[i].comment); + } + putc('\n', hfile); + fputs("/* Syntax classes for is_ functions */\n", hfile); + for (i = 0 ; is_entry[i].name ; i++) { + sprintf(buf, "#define %s %#o", is_entry[i].name, 1 << i); + fputs(buf, hfile); + for (pos = strlen(buf) ; pos < 32 ; pos = (pos + 8) & ~07) + putc('\t', hfile); + fprintf(hfile, "/* %s */\n", is_entry[i].comment); + } + putc('\n', hfile); + fputs("#define SYNBASE (1 - CHAR_MIN)\n", hfile); + fputs("#define PEOF -SYNBASE\n\n", hfile); + putc('\n', hfile); + fputs("#define BASESYNTAX (basesyntax + SYNBASE)\n", hfile); + fputs("#define DQSYNTAX (dqsyntax + SYNBASE)\n", hfile); + fputs("#define SQSYNTAX (sqsyntax + SYNBASE)\n", hfile); + fputs("#define ARISYNTAX (arisyntax + SYNBASE)\n", hfile); + putc('\n', hfile); + output_type_macros(); /* is_digit, etc. */ + putc('\n', hfile); + + /* Generate the syntax tables. */ + fputs("#include \"parser.h\"\n", cfile); + fputs("#include \"shell.h\"\n", cfile); + fputs("#include \"syntax.h\"\n\n", cfile); + + fputs("/* syntax table used when not in quotes */\n", cfile); + init("basesyntax"); + add_default(); + add("\n", "CNL"); + add("\\", "CBACK"); + add("'", "CSQUOTE"); + add("\"", "CDQUOTE"); + add("`", "CBQUOTE"); + add("$", "CVAR"); + add("}", "CENDVAR"); + add("<>();&| \t", "CSPCL"); + finish(); + + fputs("\n/* syntax table used when in double quotes */\n", cfile); + init("dqsyntax"); + add_default(); + add("\n", "CNL"); + add("\\", "CBACK"); + add("\"", "CENDQUOTE"); + add("`", "CBQUOTE"); + add("$", "CVAR"); + add("}", "CENDVAR"); + /* ':/' for tilde expansion, '-^]' for [a\-x] pattern ranges */ + add("!*?[]=~:/-^", "CCTL"); + finish(); + + fputs("\n/* syntax table used when in single quotes */\n", cfile); + init("sqsyntax"); + add_default(); + add("\n", "CNL"); + add("\\", "CSBACK"); + add("'", "CENDQUOTE"); + /* ':/' for tilde expansion, '-^]' for [a\-x] pattern ranges */ + add("!*?[]=~:/-^", "CCTL"); + finish(); + + fputs("\n/* syntax table used when in arithmetic */\n", cfile); + init("arisyntax"); + add_default(); + add("\n", "CNL"); + add("\\", "CBACK"); + add("`", "CBQUOTE"); + add("\"", "CIGN"); + add("$", "CVAR"); + add("}", "CENDVAR"); + add("(", "CLP"); + add(")", "CRP"); + finish(); + + fputs("\n/* character classification table */\n", cfile); + init("is_type"); + add("0123456789", "ISDIGIT"); + add("abcdefghijklmnopqrstuvwxyz", "ISLOWER"); + add("ABCDEFGHIJKLMNOPQRSTUVWXYZ", "ISUPPER"); + add("_", "ISUNDER"); + add("#?$!-*@", "ISSPECL"); + finish(); + + exit(0); +} + + +/* + * Output the header and declaration of a syntax table. + */ + +static void +init(const char *name) +{ + fprintf(hfile, "extern const char %s[];\n", name); + fprintf(cfile, "const char %s[SYNBASE + CHAR_MAX + 1] = {\n", name); +} + + +static void +add_one(const char *key, const char *type) +{ + fprintf(cfile, "\t[SYNBASE + %s] = %s,\n", key, type); +} + + +/* + * Add default values to the syntax table. + */ + +static void +add_default(void) +{ + add_one("PEOF", "CEOF"); + add_one("CTLESC", "CCTL"); + add_one("CTLVAR", "CCTL"); + add_one("CTLENDVAR", "CCTL"); + add_one("CTLBACKQ", "CCTL"); + add_one("CTLBACKQ + CTLQUOTE", "CCTL"); + add_one("CTLARI", "CCTL"); + add_one("CTLENDARI", "CCTL"); + add_one("CTLQUOTEMARK", "CCTL"); + add_one("CTLQUOTEEND", "CCTL"); +} + + +/* + * Output the footer of a syntax table. + */ + +static void +finish(void) +{ + fputs("};\n", cfile); +} + + +/* + * Add entries to the syntax table. + */ + +static void +add(const char *p, const char *type) +{ + for (; *p; ++p) { + char c = *p; + switch (c) { + case '\t': c = 't'; break; + case '\n': c = 'n'; break; + case '\'': c = '\''; break; + case '\\': c = '\\'; break; + + default: + fprintf(cfile, "\t[SYNBASE + '%c'] = %s,\n", c, type); + continue; + } + fprintf(cfile, "\t[SYNBASE + '\\%c'] = %s,\n", c, type); + } +} + + +/* + * Output character classification macros (e.g. is_digit). If digits are + * contiguous, we can test for them quickly. + */ + +static const char *macro[] = { + "#define is_digit(c)\t((unsigned int)((c) - '0') <= 9)", + "#define is_eof(c)\t((c) == PEOF)", + "#define is_alpha(c)\t((is_type+SYNBASE)[(int)c] & (ISUPPER|ISLOWER))", + "#define is_name(c)\t((is_type+SYNBASE)[(int)c] & (ISUPPER|ISLOWER|ISUNDER))", + "#define is_in_name(c)\t((is_type+SYNBASE)[(int)c] & (ISUPPER|ISLOWER|ISUNDER|ISDIGIT))", + "#define is_special(c)\t((is_type+SYNBASE)[(int)c] & (ISSPECL|ISDIGIT))", + "#define digit_val(c)\t((c) - '0')", + NULL +}; + +static void +output_type_macros(void) +{ + const char **pp; + + for (pp = macro ; *pp ; pp++) + fprintf(hfile, "%s\n", *pp); +} diff --git a/shell_cmds/sh/mktokens b/shell_cmds/sh/mktokens new file mode 100644 index 0000000..98bcc26 --- /dev/null +++ b/shell_cmds/sh/mktokens @@ -0,0 +1,93 @@ +#!/bin/sh - + +#- +# Copyright (c) 1991, 1993 +# The Regents of the University of California. All rights reserved. +# +# This code is derived from software contributed to Berkeley by +# Kenneth Almquist. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# @(#)mktokens 8.1 (Berkeley) 5/31/93 +# $FreeBSD: head/bin/sh/mktokens 328934 2018-02-06 15:41:35Z arichardson $ + +# The following is a list of tokens. The second column is nonzero if the +# token marks the end of a list. The third column is the name to print in +# error messages. + +temp=`mktemp -t ka` +cat > $temp <<\! +TEOF 1 end of file +TNL 0 newline +TSEMI 0 ";" +TBACKGND 0 "&" +TAND 0 "&&" +TOR 0 "||" +TPIPE 0 "|" +TLP 0 "(" +TRP 1 ")" +TENDCASE 1 ";;" +TFALLTHRU 1 ";&" +TREDIR 0 redirection +TWORD 0 word +TIF 0 "if" +TTHEN 1 "then" +TELSE 1 "else" +TELIF 1 "elif" +TFI 1 "fi" +TWHILE 0 "while" +TUNTIL 0 "until" +TFOR 0 "for" +TDO 1 "do" +TDONE 1 "done" +TBEGIN 0 "{" +TEND 1 "}" +TCASE 0 "case" +TESAC 1 "esac" +TNOT 0 "!" +! +nl=`wc -l $temp` +exec > token.h +awk '{print "#define " $1 " " NR-1}' $temp +echo ' +/* Array indicating which tokens mark the end of a list */ +static const char tokendlist[] = {' +awk '{print "\t" $2 ","}' $temp +echo '}; + +static const char *const tokname[] = {' +sed -e 's/"/\\"/g' \ + -e 's/[^ ]*[ ][ ]*[^ ]*[ ][ ]*\(.*\)/ "\1",/' \ + $temp +echo '}; +' +sed 's/"//g' $temp | awk ' +/TIF/{print "#define KWDOFFSET " NR-1; print ""; print "const char *const parsekwd[] = {"} +/TIF/,/neverfound/{print " \"" $3 "\","}' +echo ' 0 +};' + +rm $temp diff --git a/shell_cmds/sh/myhistedit.h b/shell_cmds/sh/myhistedit.h new file mode 100644 index 0000000..3dbad7e --- /dev/null +++ b/shell_cmds/sh/myhistedit.h @@ -0,0 +1,44 @@ +/*- + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)myhistedit.h 8.2 (Berkeley) 5/4/95 + * $FreeBSD: head/bin/sh/myhistedit.h 326025 2017-11-20 19:49:47Z pfg $ + */ + +#include <histedit.h> + +extern History *hist; +extern EditLine *el; +extern int displayhist; + +void histedit(void); +void sethistsize(const char *); +void setterm(const char *); + diff --git a/shell_cmds/sh/mystring.c b/shell_cmds/sh/mystring.c new file mode 100644 index 0000000..7e17e23 --- /dev/null +++ b/shell_cmds/sh/mystring.c @@ -0,0 +1,100 @@ +/*- + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)mystring.c 8.2 (Berkeley) 5/4/95"; +#endif +#endif /* not lint */ +#include <sys/cdefs.h> +__FBSDID("$FreeBSD: head/bin/sh/mystring.c 326025 2017-11-20 19:49:47Z pfg $"); + +/* + * String functions. + * + * equal(s1, s2) Return true if strings are equal. + * number(s) Convert a string of digits to an integer. + * is_number(s) Return true if s is a string of digits. + */ + +#include <stdlib.h> +#include "shell.h" +#include "syntax.h" +#include "error.h" +#include "mystring.h" + + +char nullstr[1]; /* zero length string */ + +/* + * equal - #defined in mystring.h + */ + + +/* + * Convert a string of digits to an integer, printing an error message on + * failure. + */ + +int +number(const char *s) +{ + if (! is_number(s)) + error("Illegal number: %s", s); + return atoi(s); +} + + + +/* + * Check for a valid number. This should be elsewhere. + */ + +int +is_number(const char *p) +{ + const char *q; + + if (*p == '\0') + return 0; + while (*p == '0') + p++; + for (q = p; *q != '\0'; q++) + if (! is_digit(*q)) + return 0; + if (q - p > 10 || + (q - p == 10 && memcmp(p, "2147483647", 10) > 0)) + return 0; + return 1; +} diff --git a/shell_cmds/sh/mystring.h b/shell_cmds/sh/mystring.h new file mode 100644 index 0000000..31d7891 --- /dev/null +++ b/shell_cmds/sh/mystring.h @@ -0,0 +1,43 @@ +/*- + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)mystring.h 8.2 (Berkeley) 5/4/95 + * $FreeBSD: head/bin/sh/mystring.h 326025 2017-11-20 19:49:47Z pfg $ + */ + +#include <string.h> + +int number(const char *); +int is_number(const char *); + +#define equal(s1, s2) (strcmp(s1, s2) == 0) diff --git a/shell_cmds/sh/nodes.c.pat b/shell_cmds/sh/nodes.c.pat new file mode 100644 index 0000000..29d1563 --- /dev/null +++ b/shell_cmds/sh/nodes.c.pat @@ -0,0 +1,193 @@ +/*- + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)nodes.c.pat 8.2 (Berkeley) 5/4/95 + * $FreeBSD: head/bin/sh/nodes.c.pat 314436 2017-02-28 23:42:47Z imp $ + */ + +#include <sys/param.h> +#include <stdlib.h> +#include <stddef.h> +/* + * Routine for dealing with parsed shell commands. + */ + +#include "shell.h" +#include "nodes.h" +#include "memalloc.h" +#include "mystring.h" + + +struct nodesize { + int blocksize; /* size of structures in function */ + int stringsize; /* size of strings in node */ +}; + +struct nodecopystate { + pointer block; /* block to allocate function from */ + char *string; /* block to allocate strings from */ +}; + +%SIZES + + +static void calcsize(union node *, struct nodesize *); +static void sizenodelist(struct nodelist *, struct nodesize *); +static union node *copynode(union node *, struct nodecopystate *); +static struct nodelist *copynodelist(struct nodelist *, struct nodecopystate *); +static char *nodesavestr(const char *, struct nodecopystate *); + + +struct funcdef { + unsigned int refcount; + union node n; +}; + +/* + * Make a copy of a parse tree. + */ + +struct funcdef * +copyfunc(union node *n) +{ + struct nodesize sz; + struct nodecopystate st; + struct funcdef *fn; + + if (n == NULL) + return NULL; + sz.blocksize = offsetof(struct funcdef, n); + sz.stringsize = 0; + calcsize(n, &sz); + fn = ckmalloc(sz.blocksize + sz.stringsize); + fn->refcount = 1; + st.block = (char *)fn + offsetof(struct funcdef, n); + st.string = (char *)fn + sz.blocksize; + copynode(n, &st); + return fn; +} + + +union node * +getfuncnode(struct funcdef *fn) +{ + return fn == NULL ? NULL : &fn->n; +} + + +static void +calcsize(union node *n, struct nodesize *result) +{ + %CALCSIZE +} + + + +static void +sizenodelist(struct nodelist *lp, struct nodesize *result) +{ + while (lp) { + result->blocksize += ALIGN(sizeof(struct nodelist)); + calcsize(lp->n, result); + lp = lp->next; + } +} + + + +static union node * +copynode(union node *n, struct nodecopystate *state) +{ + union node *new; + + %COPY + return new; +} + + +static struct nodelist * +copynodelist(struct nodelist *lp, struct nodecopystate *state) +{ + struct nodelist *start; + struct nodelist **lpp; + + lpp = &start; + while (lp) { + *lpp = state->block; + state->block = (char *)state->block + + ALIGN(sizeof(struct nodelist)); + (*lpp)->n = copynode(lp->n, state); + lp = lp->next; + lpp = &(*lpp)->next; + } + *lpp = NULL; + return start; +} + + + +static char * +nodesavestr(const char *s, struct nodecopystate *state) +{ + const char *p = s; + char *q = state->string; + char *rtn = state->string; + + while ((*q++ = *p++) != '\0') + continue; + state->string = q; + return rtn; +} + + +void +reffunc(struct funcdef *fn) +{ + if (fn) + fn->refcount++; +} + + +/* + * Decrement the reference count of a function definition, freeing it + * if it falls to 0. + */ + +void +unreffunc(struct funcdef *fn) +{ + if (fn) { + fn->refcount--; + if (fn->refcount > 0) + return; + ckfree(fn); + } +} diff --git a/shell_cmds/sh/nodetypes b/shell_cmds/sh/nodetypes new file mode 100644 index 0000000..3ad95d6 --- /dev/null +++ b/shell_cmds/sh/nodetypes @@ -0,0 +1,145 @@ +#- +# Copyright (c) 1991, 1993 +# The Regents of the University of California. All rights reserved. +# +# This code is derived from software contributed to Berkeley by +# Kenneth Almquist. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# 3. Neither the name of the University nor the names of its contributors +# may be used to endorse or promote products derived from this software +# without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. +# +# @(#)nodetypes 8.2 (Berkeley) 5/4/95 +# $FreeBSD: head/bin/sh/nodetypes 314436 2017-02-28 23:42:47Z imp $ + +# This file describes the nodes used in parse trees. Unindented lines +# contain a node type followed by a structure tag. Subsequent indented +# lines specify the fields of the structure. Several node types can share +# the same structure, in which case the fields of the structure should be +# specified only once. +# +# A field of a structure is described by the name of the field followed +# by a type. The currently implemented types are: +# nodeptr - a pointer to a node +# nodelist - a pointer to a list of nodes +# string - a pointer to a nul terminated string +# int - an integer +# other - any type that can be copied by assignment +# temp - a field that doesn't have to be copied when the node is copied +# The last two types should be followed by the text of a C declaration for +# the field. + +NSEMI nbinary # two commands separated by a semicolon + type int + ch1 nodeptr # the first child + ch2 nodeptr # the second child + +NCMD ncmd # a simple command + type int + args nodeptr # the arguments + redirect nodeptr # list of file redirections + +NPIPE npipe # a pipeline + type int + backgnd int # set to run pipeline in background + cmdlist nodelist # the commands in the pipeline + +NREDIR nredir # redirection (of a compex command) + type int + n nodeptr # the command + redirect nodeptr # list of file redirections + +NBACKGND nredir # run command in background +NSUBSHELL nredir # run command in a subshell + +NAND nbinary # the && operator +NOR nbinary # the || operator + +NIF nif # the if statement. Elif clauses are handled + type int # using multiple if nodes. + test nodeptr # if test + ifpart nodeptr # then ifpart + elsepart nodeptr # else elsepart + +NWHILE nbinary # the while statement. First child is the test +NUNTIL nbinary # the until statement + +NFOR nfor # the for statement + type int + args nodeptr # for var in args + body nodeptr # do body; done + var string # the for variable + +NCASE ncase # a case statement + type int + expr nodeptr # the word to switch on + cases nodeptr # the list of cases (NCLIST nodes) + +NCLIST nclist # a case ending with ;; + type int + next nodeptr # the next case in list + pattern nodeptr # list of patterns for this case + body nodeptr # code to execute for this case + +NCLISTFALLTHRU nclist # a case ending with ;& + +NDEFUN narg # define a function. The "next" field contains + # the body of the function. + +NARG narg # represents a word + type int + next nodeptr # next word in list + text string # the text of the word + backquote nodelist # list of commands in back quotes + +NTO nfile # fd> fname +NFROM nfile # fd< fname +NFROMTO nfile # fd<> fname +NAPPEND nfile # fd>> fname +NCLOBBER nfile # fd>| fname + type int + fd int # file descriptor being redirected + next nodeptr # next redirection in list + fname nodeptr # file name, in a NARG node + expfname temp char *expfname # actual file name + +NTOFD ndup # fd<&dupfd +NFROMFD ndup # fd>&dupfd + type int + fd int # file descriptor being redirected + next nodeptr # next redirection in list + dupfd int # file descriptor to duplicate + vname nodeptr # file name if fd>&$var + + +NHERE nhere # fd<<\! +NXHERE nhere # fd<<! + type int + fd int # file descriptor being redirected + next nodeptr # next redirection in list + doc nodeptr # input to command (NARG node) + expdoc temp const char *expdoc # actual document (for NXHERE) + +NNOT nnot # ! command (actually pipeline) + type int + com nodeptr diff --git a/shell_cmds/sh/options.c b/shell_cmds/sh/options.c new file mode 100644 index 0000000..0628e73 --- /dev/null +++ b/shell_cmds/sh/options.c @@ -0,0 +1,594 @@ +/*- + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)options.c 8.2 (Berkeley) 5/4/95"; +#endif +#endif /* not lint */ +#include <sys/cdefs.h> +__FBSDID("$FreeBSD: head/bin/sh/options.c 326025 2017-11-20 19:49:47Z pfg $"); + +#include <signal.h> +#include <unistd.h> +#include <stdlib.h> + +#include "shell.h" +#define DEFINE_OPTIONS +#include "options.h" +#undef DEFINE_OPTIONS +#include "nodes.h" /* for other header files */ +#include "eval.h" +#include "jobs.h" +#include "input.h" +#include "output.h" +#include "trap.h" +#include "var.h" +#include "memalloc.h" +#include "error.h" +#include "mystring.h" +#include "builtins.h" +#ifndef NO_HISTORY +#include "myhistedit.h" +#endif + +char *arg0; /* value of $0 */ +struct shparam shellparam; /* current positional parameters */ +char **argptr; /* argument list for builtin commands */ +char *shoptarg; /* set by nextopt (like getopt) */ +char *nextopt_optptr; /* used by nextopt */ + +char *minusc; /* argument to -c option */ + + +static void options(int); +static void minus_o(char *, int); +static void setoption(int, int); +static void setoptionbyindex(int, int); +static void setparam(int, char **); +static int getopts(char *, char *, char **, char ***, char **); + + +/* + * Process the shell command line arguments. + */ + +void +procargs(int argc, char **argv) +{ + int i; + char *scriptname; + + argptr = argv; + if (argc > 0) + argptr++; + for (i = 0; i < NOPTS; i++) + optval[i] = 2; + privileged = (getuid() != geteuid() || getgid() != getegid()); + options(1); + if (*argptr == NULL && minusc == NULL) + sflag = 1; + if (iflag != 0 && sflag == 1 && isatty(0) && isatty(1)) { + iflag = 1; + if (Eflag == 2) + Eflag = 1; + } + if (mflag == 2) + mflag = iflag; + for (i = 0; i < NOPTS; i++) + if (optval[i] == 2) + optval[i] = 0; + arg0 = argv[0]; + if (sflag == 0 && minusc == NULL) { + scriptname = *argptr++; + setinputfile(scriptname, 0); + commandname = arg0 = scriptname; + } + /* POSIX 1003.2: first arg after -c cmd is $0, remainder $1... */ + if (argptr && minusc && *argptr) + arg0 = *argptr++; + + shellparam.p = argptr; + shellparam.reset = 1; + /* assert(shellparam.malloc == 0 && shellparam.nparam == 0); */ + while (*argptr) { + shellparam.nparam++; + argptr++; + } + optschanged(); +} + + +void +optschanged(void) +{ + setinteractive(); +#ifndef NO_HISTORY + histedit(); +#endif + setjobctl(mflag); +} + +/* + * Process shell options. The global variable argptr contains a pointer + * to the argument list; we advance it past the options. + * If cmdline is true, process the shell's argv; otherwise, process arguments + * to the set special builtin. + */ + +static void +options(int cmdline) +{ + char *kp, *p; + int val; + int c; + + if (cmdline) + minusc = NULL; + while ((p = *argptr) != NULL) { + argptr++; + if ((c = *p++) == '-') { + val = 1; + /* A "-" or "--" terminates options */ + if (p[0] == '\0') + goto end_options1; + if (p[0] == '-' && p[1] == '\0') + goto end_options2; + /** + * For the benefit of `#!' lines in shell scripts, + * treat a string of '-- *#.*' the same as '--'. + * This is needed so that a script starting with: + * #!/bin/sh -- # -*- perl -*- + * will continue to work after a change is made to + * kern/imgact_shell.c to NOT token-ize the options + * specified on a '#!' line. A bit of a kludge, + * but that trick is recommended in documentation + * for some scripting languages, and we might as + * well continue to support it. + */ + if (p[0] == '-') { + kp = p + 1; + while (*kp == ' ' || *kp == '\t') + kp++; + if (*kp == '#' || *kp == '\0') + goto end_options2; + } + } else if (c == '+') { + val = 0; + } else { + argptr--; + break; + } + while ((c = *p++) != '\0') { + if (c == 'c' && cmdline) { + char *q; + + q = *argptr++; + if (q == NULL || minusc != NULL) + error("Bad -c option"); + minusc = q; + } else if (c == 'o') { + minus_o(*argptr, val); + if (*argptr) + argptr++; + } else + setoption(c, val); + } + } + return; + + /* When processing `set', a single "-" means turn off -x and -v */ +end_options1: + if (!cmdline) { + xflag = vflag = 0; + return; + } + + /* + * When processing `set', a "--" means the remaining arguments + * replace the positional parameters in the active shell. If + * there are no remaining options, then all the positional + * parameters are cleared (equivalent to doing ``shift $#''). + */ +end_options2: + if (!cmdline) { + if (*argptr == NULL) + setparam(0, argptr); + return; + } + + /* + * At this point we are processing options given to 'sh' on a command + * line. If an end-of-options marker ("-" or "--") is followed by an + * arg of "#", then skip over all remaining arguments. Some scripting + * languages (e.g.: perl) document that /bin/sh will implement this + * behavior, and they recommend that users take advantage of it to + * solve certain issues that can come up when writing a perl script. + * Yes, this feature is in /bin/sh to help users write perl scripts. + */ + p = *argptr; + if (p != NULL && p[0] == '#' && p[1] == '\0') { + while (*argptr != NULL) + argptr++; + /* We need to keep the final argument */ + argptr--; + } +} + +static void +minus_o(char *name, int val) +{ + int i; + const unsigned char *on; + size_t len; + + if (name == NULL) { + if (val) { + /* "Pretty" output. */ + out1str("Current option settings\n"); + for (i = 0, on = optname; i < NOPTS; i++, on += *on + 1) + out1fmt("%-16.*s%s\n", *on, on + 1, + optval[i] ? "on" : "off"); + } else { + /* Output suitable for re-input to shell. */ + for (i = 0, on = optname; i < NOPTS; i++, on += *on + 1) + out1fmt("%s %co %.*s%s", + i % 6 == 0 ? "set" : "", + optval[i] ? '-' : '+', + *on, on + 1, + i % 6 == 5 || i == NOPTS - 1 ? "\n" : ""); + } + } else { + len = strlen(name); + for (i = 0, on = optname; i < NOPTS; i++, on += *on + 1) + if (*on == len && memcmp(on + 1, name, len) == 0) { + setoptionbyindex(i, val); + return; + } + error("Illegal option -o %s", name); + } +} + + +static void +setoptionbyindex(int idx, int val) +{ + if (&optval[idx] == &privileged && !val && privileged) { + if (setgid(getgid()) == -1) + error("setgid"); + if (setuid(getuid()) == -1) + error("setuid"); + } + optval[idx] = val; + if (val) { + /* #%$ hack for ksh semantics */ + if (&optval[idx] == &Vflag) + Eflag = 0; + else if (&optval[idx] == &Eflag) + Vflag = 0; + } +} + +static void +setoption(int flag, int val) +{ + int i; + + for (i = 0; i < NSHORTOPTS; i++) + if (optletter[i] == flag) { + setoptionbyindex(i, val); + return; + } + error("Illegal option -%c", flag); +} + + +/* + * Set the shell parameters. + */ + +static void +setparam(int argc, char **argv) +{ + char **newparam; + char **ap; + + ap = newparam = ckmalloc((argc + 1) * sizeof *ap); + while (*argv) { + *ap++ = savestr(*argv++); + } + *ap = NULL; + freeparam(&shellparam); + shellparam.malloc = 1; + shellparam.nparam = argc; + shellparam.p = newparam; + shellparam.optp = NULL; + shellparam.reset = 1; + shellparam.optnext = NULL; +} + + +/* + * Free the list of positional parameters. + */ + +void +freeparam(struct shparam *param) +{ + char **ap; + + if (param->malloc) { + for (ap = param->p ; *ap ; ap++) + ckfree(*ap); + ckfree(param->p); + } + if (param->optp) { + for (ap = param->optp ; *ap ; ap++) + ckfree(*ap); + ckfree(param->optp); + } +} + + + +/* + * The shift builtin command. + */ + +int +shiftcmd(int argc, char **argv) +{ + int i, n; + + n = 1; + if (argc > 1) + n = number(argv[1]); + if (n > shellparam.nparam) + return 1; + INTOFF; + shellparam.nparam -= n; + if (shellparam.malloc) + for (i = 0; i < n; i++) + ckfree(shellparam.p[i]); + memmove(shellparam.p, shellparam.p + n, + (shellparam.nparam + 1) * sizeof(shellparam.p[0])); + shellparam.reset = 1; + INTON; + return 0; +} + + + +/* + * The set builtin command. + */ + +int +setcmd(int argc, char **argv) +{ + if (argc == 1) + return showvarscmd(argc, argv); + INTOFF; + options(0); + optschanged(); + if (*argptr != NULL) { + setparam(argc - (argptr - argv), argptr); + } + INTON; + return 0; +} + + +void +getoptsreset(const char *value) +{ + while (*value == '0') + value++; + if (strcmp(value, "1") == 0) + shellparam.reset = 1; +} + +/* + * The getopts builtin. Shellparam.optnext points to the next argument + * to be processed. Shellparam.optptr points to the next character to + * be processed in the current argument. If shellparam.optnext is NULL, + * then it's the first time getopts has been called. + */ + +int +getoptscmd(int argc, char **argv) +{ + char **optbase = NULL, **ap; + int i; + + if (argc < 3) + error("usage: getopts optstring var [arg]"); + + if (shellparam.reset == 1) { + INTOFF; + if (shellparam.optp) { + for (ap = shellparam.optp ; *ap ; ap++) + ckfree(*ap); + ckfree(shellparam.optp); + shellparam.optp = NULL; + } + if (argc > 3) { + shellparam.optp = ckmalloc((argc - 2) * sizeof *ap); + memset(shellparam.optp, '\0', (argc - 2) * sizeof *ap); + for (i = 0; i < argc - 3; i++) + shellparam.optp[i] = savestr(argv[i + 3]); + } + INTON; + optbase = argc == 3 ? shellparam.p : shellparam.optp; + shellparam.optnext = optbase; + shellparam.optptr = NULL; + shellparam.reset = 0; + } else + optbase = shellparam.optp ? shellparam.optp : shellparam.p; + + return getopts(argv[1], argv[2], optbase, &shellparam.optnext, + &shellparam.optptr); +} + +static int +getopts(char *optstr, char *optvar, char **optfirst, char ***optnext, + char **optptr) +{ + char *p, *q; + char c = '?'; + int done = 0; + int ind = 0; + int err = 0; + char s[10]; + const char *newoptarg = NULL; + + if ((p = *optptr) == NULL || *p == '\0') { + /* Current word is done, advance */ + if (*optnext == NULL) + return 1; + p = **optnext; + if (p == NULL || *p != '-' || *++p == '\0') { +atend: + ind = *optnext - optfirst + 1; + *optnext = NULL; + p = NULL; + done = 1; + goto out; + } + (*optnext)++; + if (p[0] == '-' && p[1] == '\0') /* check for "--" */ + goto atend; + } + + c = *p++; + for (q = optstr; *q != c; ) { + if (*q == '\0') { + if (optstr[0] == ':') { + s[0] = c; + s[1] = '\0'; + newoptarg = s; + } + else + out2fmt_flush("Illegal option -%c\n", c); + c = '?'; + goto out; + } + if (*++q == ':') + q++; + } + + if (*++q == ':') { + if (*p == '\0' && (p = **optnext) == NULL) { + if (optstr[0] == ':') { + s[0] = c; + s[1] = '\0'; + newoptarg = s; + c = ':'; + } + else { + out2fmt_flush("No arg for -%c option\n", c); + c = '?'; + } + goto out; + } + + if (p == **optnext) + (*optnext)++; + newoptarg = p; + p = NULL; + } + +out: + if (*optnext != NULL) + ind = *optnext - optfirst + 1; + *optptr = p; + if (newoptarg != NULL) + err |= setvarsafe("OPTARG", newoptarg, 0); + else { + INTOFF; + err |= unsetvar("OPTARG"); + INTON; + } + fmtstr(s, sizeof(s), "%d", ind); + err |= setvarsafe("OPTIND", s, VNOFUNC); + s[0] = c; + s[1] = '\0'; + err |= setvarsafe(optvar, s, 0); + if (err) { + *optnext = NULL; + *optptr = NULL; + flushall(); + exraise(EXERROR); + } + return done; +} + +/* + * Standard option processing (a la getopt) for builtin routines. The + * only argument that is passed to nextopt is the option string; the + * other arguments are unnecessary. It returns the option, or '\0' on + * end of input. + */ + +int +nextopt(const char *optstring) +{ + char *p; + const char *q; + char c; + + if ((p = nextopt_optptr) == NULL || *p == '\0') { + p = *argptr; + if (p == NULL || *p != '-' || *++p == '\0') + return '\0'; + argptr++; + if (p[0] == '-' && p[1] == '\0') /* check for "--" */ + return '\0'; + } + c = *p++; + for (q = optstring ; *q != c ; ) { + if (*q == '\0') + error("Illegal option -%c", c); + if (*++q == ':') + q++; + } + if (*++q == ':') { + if (*p == '\0' && (p = *argptr++) == NULL) + error("No arg for -%c option", c); + shoptarg = p; + p = NULL; + } + nextopt_optptr = p; + return c; +} diff --git a/shell_cmds/sh/options.h b/shell_cmds/sh/options.h new file mode 100644 index 0000000..233e9c6 --- /dev/null +++ b/shell_cmds/sh/options.h @@ -0,0 +1,115 @@ +/*- + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)options.h 8.2 (Berkeley) 5/4/95 + * $FreeBSD: head/bin/sh/options.h 326025 2017-11-20 19:49:47Z pfg $ + */ + +struct shparam { + int nparam; /* # of positional parameters (without $0) */ + unsigned char malloc; /* if parameter list dynamically allocated */ + unsigned char reset; /* if getopts has been reset */ + char **p; /* parameter list */ + char **optp; /* parameter list for getopts */ + char **optnext; /* next parameter to be processed by getopts */ + char *optptr; /* used by getopts */ +}; + + + +#define eflag optval[0] +#define fflag optval[1] +#define Iflag optval[2] +#define iflag optval[3] +#define mflag optval[4] +#define nflag optval[5] +#define sflag optval[6] +#define xflag optval[7] +#define vflag optval[8] +#define Vflag optval[9] +#define Eflag optval[10] +#define Cflag optval[11] +#define aflag optval[12] +#define bflag optval[13] +#define uflag optval[14] +#define privileged optval[15] +#define Tflag optval[16] +#define Pflag optval[17] +#define hflag optval[18] +#define nologflag optval[19] + +#define NSHORTOPTS 19 +#define NOPTS 20 + +extern char optval[NOPTS]; +extern const char optletter[NSHORTOPTS]; +#ifdef DEFINE_OPTIONS +char optval[NOPTS]; +const char optletter[NSHORTOPTS] = "efIimnsxvVECabupTPh"; +static const unsigned char optname[] = + "\007errexit" + "\006noglob" + "\011ignoreeof" + "\013interactive" + "\007monitor" + "\006noexec" + "\005stdin" + "\006xtrace" + "\007verbose" + "\002vi" + "\005emacs" + "\011noclobber" + "\011allexport" + "\006notify" + "\007nounset" + "\012privileged" + "\012trapsasync" + "\010physical" + "\010trackall" + "\005nolog" +; +#endif + + +extern char *minusc; /* argument to -c option */ +extern char *arg0; /* $0 */ +extern struct shparam shellparam; /* $@ */ +extern char **argptr; /* argument list for builtin commands */ +extern char *shoptarg; /* set by nextopt */ +extern char *nextopt_optptr; /* used by nextopt */ + +void procargs(int, char **); +void optschanged(void); +void freeparam(struct shparam *); +int nextopt(const char *); +void getoptsreset(const char *); diff --git a/shell_cmds/sh/output.c b/shell_cmds/sh/output.c new file mode 100644 index 0000000..737eb91 --- /dev/null +++ b/shell_cmds/sh/output.c @@ -0,0 +1,370 @@ +/*- + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)output.c 8.2 (Berkeley) 5/4/95"; +#endif +#endif /* not lint */ +#include <sys/cdefs.h> +__FBSDID("$FreeBSD: head/bin/sh/output.c 326025 2017-11-20 19:49:47Z pfg $"); + +/* + * Shell output routines. We use our own output routines because: + * When a builtin command is interrupted we have to discard + * any pending output. + * When a builtin command appears in back quotes, we want to + * save the output of the command in a region obtained + * via malloc, rather than doing a fork and reading the + * output of the command via a pipe. + */ + +#include <stdio.h> /* defines BUFSIZ */ +#include <string.h> +#include <stdarg.h> +#include <errno.h> +#include <unistd.h> +#include <stdlib.h> +#include <wchar.h> +#include <wctype.h> + +#include "shell.h" +#include "syntax.h" +#include "output.h" +#include "memalloc.h" +#include "error.h" +#include "var.h" + + +#define OUTBUFSIZ BUFSIZ +#define MEM_OUT -2 /* output to dynamically allocated memory */ +#define OUTPUT_ERR 01 /* error occurred on output */ + +static int doformat_wr(void *, const char *, int); + +struct output output = {NULL, NULL, NULL, OUTBUFSIZ, 1, 0}; +struct output errout = {NULL, NULL, NULL, 256, 2, 0}; +struct output memout = {NULL, NULL, NULL, 64, MEM_OUT, 0}; +struct output *out1 = &output; +struct output *out2 = &errout; + +void +outcslow(int c, struct output *file) +{ + outc(c, file); +} + +void +out1str(const char *p) +{ + outstr(p, out1); +} + +void +out1qstr(const char *p) +{ + outqstr(p, out1); +} + +void +out2str(const char *p) +{ + outstr(p, out2); +} + +void +out2qstr(const char *p) +{ + outqstr(p, out2); +} + +void +outstr(const char *p, struct output *file) +{ + outbin(p, strlen(p), file); +} + +static void +byteseq(int ch, struct output *file) +{ + char seq[4]; + + seq[0] = '\\'; + seq[1] = (ch >> 6 & 0x3) + '0'; + seq[2] = (ch >> 3 & 0x7) + '0'; + seq[3] = (ch & 0x7) + '0'; + outbin(seq, 4, file); +} + +static void +outdqstr(const char *p, struct output *file) +{ + const char *end; + mbstate_t mbs; + size_t clen; + wchar_t wc; + + memset(&mbs, '\0', sizeof(mbs)); + end = p + strlen(p); + outstr("$'", file); + while ((clen = mbrtowc(&wc, p, end - p + 1, &mbs)) != 0) { + if (clen == (size_t)-2) { + while (p < end) + byteseq(*p++, file); + break; + } + if (clen == (size_t)-1) { + memset(&mbs, '\0', sizeof(mbs)); + byteseq(*p++, file); + continue; + } + if (wc == L'\n') + outcslow('\n', file), p++; + else if (wc == L'\r') + outstr("\\r", file), p++; + else if (wc == L'\t') + outstr("\\t", file), p++; + else if (!iswprint(wc)) { + for (; clen > 0; clen--) + byteseq(*p++, file); + } else { + if (wc == L'\'' || wc == L'\\') + outcslow('\\', file); + outbin(p, clen, file); + p += clen; + } + } + outcslow('\'', file); +} + +/* Like outstr(), but quote for re-input into the shell. */ +void +outqstr(const char *p, struct output *file) +{ + int i; + + if (p[0] == '\0') { + outstr("''", file); + return; + } + for (i = 0; p[i] != '\0'; i++) { + if ((p[i] > '\0' && p[i] < ' ' && p[i] != '\n') || + (p[i] & 0x80) != 0 || p[i] == '\'') { + outdqstr(p, file); + return; + } + } + + if (p[strcspn(p, "|&;<>()$`\\\" \n*?[~#=")] == '\0' || + strcmp(p, "[") == 0) { + outstr(p, file); + return; + } + + outcslow('\'', file); + outstr(p, file); + outcslow('\'', file); +} + +void +outbin(const void *data, size_t len, struct output *file) +{ + const char *p; + + p = data; + while (len-- > 0) + outc(*p++, file); +} + +void +emptyoutbuf(struct output *dest) +{ + int offset, newsize; + + if (dest->buf == NULL) { + INTOFF; + dest->buf = ckmalloc(dest->bufsize); + dest->nextc = dest->buf; + dest->bufend = dest->buf + dest->bufsize; + INTON; + } else if (dest->fd == MEM_OUT) { + offset = dest->nextc - dest->buf; + newsize = dest->bufsize << 1; + INTOFF; + dest->buf = ckrealloc(dest->buf, newsize); + dest->bufsize = newsize; + dest->bufend = dest->buf + newsize; + dest->nextc = dest->buf + offset; + INTON; + } else { + flushout(dest); + } +} + + +void +flushall(void) +{ + flushout(&output); + flushout(&errout); +} + + +void +flushout(struct output *dest) +{ + + if (dest->buf == NULL || dest->nextc == dest->buf || dest->fd < 0) + return; + if (xwrite(dest->fd, dest->buf, dest->nextc - dest->buf) < 0) + dest->flags |= OUTPUT_ERR; + dest->nextc = dest->buf; +} + + +void +freestdout(void) +{ + output.nextc = output.buf; +} + + +int +outiserror(struct output *file) +{ + return (file->flags & OUTPUT_ERR); +} + + +void +outclearerror(struct output *file) +{ + file->flags &= ~OUTPUT_ERR; +} + + +void +outfmt(struct output *file, const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + doformat(file, fmt, ap); + va_end(ap); +} + + +void +out1fmt(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + doformat(out1, fmt, ap); + va_end(ap); +} + +void +out2fmt_flush(const char *fmt, ...) +{ + va_list ap; + + va_start(ap, fmt); + doformat(out2, fmt, ap); + va_end(ap); + flushout(out2); +} + +void +fmtstr(char *outbuf, int length, const char *fmt, ...) +{ + va_list ap; + + INTOFF; + va_start(ap, fmt); + vsnprintf(outbuf, length, fmt, ap); + va_end(ap); + INTON; +} + +static int +doformat_wr(void *cookie, const char *buf, int len) +{ + struct output *o; + + o = (struct output *)cookie; + outbin(buf, len, o); + + return (len); +} + +void +doformat(struct output *dest, const char *f, va_list ap) +{ + FILE *fp; + + if ((fp = fwopen(dest, doformat_wr)) != NULL) { + vfprintf(fp, f, ap); + fclose(fp); + } +} + +/* + * Version of write which resumes after a signal is caught. + */ + +int +xwrite(int fd, const char *buf, int nbytes) +{ + int ntry; + int i; + int n; + + n = nbytes; + ntry = 0; + for (;;) { + i = write(fd, buf, n); + if (i > 0) { + if ((n -= i) <= 0) + return nbytes; + buf += i; + ntry = 0; + } else if (i == 0) { + if (++ntry > 10) + return nbytes - n; + } else if (errno != EINTR) { + return -1; + } + } +} diff --git a/shell_cmds/sh/output.h b/shell_cmds/sh/output.h new file mode 100644 index 0000000..652187a --- /dev/null +++ b/shell_cmds/sh/output.h @@ -0,0 +1,85 @@ +/*- + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)output.h 8.2 (Berkeley) 5/4/95 + * $FreeBSD: head/bin/sh/output.h 326025 2017-11-20 19:49:47Z pfg $ + */ + +#ifndef OUTPUT_INCL + +#include <stdarg.h> +#include <stddef.h> + +struct output { + char *nextc; + char *bufend; + char *buf; + int bufsize; + short fd; + short flags; +}; + +extern struct output output; /* to fd 1 */ +extern struct output errout; /* to fd 2 */ +extern struct output memout; +extern struct output *out1; /* &memout if backquote, otherwise &output */ +extern struct output *out2; /* &memout if backquote with 2>&1, otherwise + &errout */ + +void outcslow(int, struct output *); +void out1str(const char *); +void out1qstr(const char *); +void out2str(const char *); +void out2qstr(const char *); +void outstr(const char *, struct output *); +void outqstr(const char *, struct output *); +void outbin(const void *, size_t, struct output *); +void emptyoutbuf(struct output *); +void flushall(void); +void flushout(struct output *); +void freestdout(void); +int outiserror(struct output *); +void outclearerror(struct output *); +void outfmt(struct output *, const char *, ...) __printflike(2, 3); +void out1fmt(const char *, ...) __printflike(1, 2); +void out2fmt_flush(const char *, ...) __printflike(1, 2); +void fmtstr(char *, int, const char *, ...) __printflike(3, 4); +void doformat(struct output *, const char *, va_list) __printflike(2, 0); +int xwrite(int, const char *, int); + +#define outc(c, file) ((file)->nextc == (file)->bufend ? (emptyoutbuf(file), *(file)->nextc++ = (c)) : (*(file)->nextc++ = (c))) +#define out1c(c) outc(c, out1); +#define out2c(c) outcslow(c, out2); + +#define OUTPUT_INCL +#endif diff --git a/shell_cmds/sh/parser.c b/shell_cmds/sh/parser.c new file mode 100644 index 0000000..799e3b5 --- /dev/null +++ b/shell_cmds/sh/parser.c @@ -0,0 +1,2120 @@ +/*- + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)parser.c 8.7 (Berkeley) 5/16/95"; +#endif +#endif /* not lint */ +#include <sys/cdefs.h> +__FBSDID("$FreeBSD: head/bin/sh/parser.c 326025 2017-11-20 19:49:47Z pfg $"); + +#include <stdlib.h> +#include <unistd.h> +#include <stdio.h> + +#include "shell.h" +#include "parser.h" +#include "nodes.h" +#include "expand.h" /* defines rmescapes() */ +#include "syntax.h" +#include "options.h" +#include "input.h" +#include "output.h" +#include "var.h" +#include "error.h" +#include "memalloc.h" +#include "mystring.h" +#include "alias.h" +#include "show.h" +#include "eval.h" +#include "exec.h" /* to check for special builtins */ +#ifndef NO_HISTORY +#include "myhistedit.h" +#endif + +/* + * Shell command parser. + */ + +#define PROMPTLEN 128 + +/* values of checkkwd variable */ +#define CHKALIAS 0x1 +#define CHKKWD 0x2 +#define CHKNL 0x4 + +/* values returned by readtoken */ +#include "token.h" + + + +struct heredoc { + struct heredoc *next; /* next here document in list */ + union node *here; /* redirection node */ + char *eofmark; /* string indicating end of input */ + int striptabs; /* if set, strip leading tabs */ +}; + +struct parser_temp { + struct parser_temp *next; + void *data; +}; + + +static struct heredoc *heredoclist; /* list of here documents to read */ +static int doprompt; /* if set, prompt the user */ +static int needprompt; /* true if interactive and at start of line */ +static int lasttoken; /* last token read */ +static int tokpushback; /* last token pushed back */ +static char *wordtext; /* text of last word returned by readtoken */ +static int checkkwd; +static struct nodelist *backquotelist; +static union node *redirnode; +static struct heredoc *heredoc; +static int quoteflag; /* set if (part of) last token was quoted */ +static int startlinno; /* line # where last token started */ +static int funclinno; /* line # where the current function started */ +static struct parser_temp *parser_temp; + +#define NOEOFMARK ((const char *)&heredoclist) + + +static union node *list(int); +static union node *andor(void); +static union node *pipeline(void); +static union node *command(void); +static union node *simplecmd(union node **, union node *); +static union node *makename(void); +static union node *makebinary(int type, union node *n1, union node *n2); +static void parsefname(void); +static void parseheredoc(void); +static int peektoken(void); +static int readtoken(void); +static int xxreadtoken(void); +static int readtoken1(int, const char *, const char *, int); +static int noexpand(char *); +static void consumetoken(int); +static void synexpect(int) __dead2; +static void synerror(const char *) __dead2; +static void setprompt(int); +static int pgetc_linecont(void); + + +static void * +parser_temp_alloc(size_t len) +{ + struct parser_temp *t; + + INTOFF; + t = ckmalloc(sizeof(*t)); + t->data = NULL; + t->next = parser_temp; + parser_temp = t; + t->data = ckmalloc(len); + INTON; + return t->data; +} + + +static void * +parser_temp_realloc(void *ptr, size_t len) +{ + struct parser_temp *t; + + INTOFF; + t = parser_temp; + if (ptr != t->data) + error("bug: parser_temp_realloc misused"); + t->data = ckrealloc(t->data, len); + INTON; + return t->data; +} + + +static void +parser_temp_free_upto(void *ptr) +{ + struct parser_temp *t; + int done = 0; + + INTOFF; + while (parser_temp != NULL && !done) { + t = parser_temp; + parser_temp = t->next; + done = t->data == ptr; + ckfree(t->data); + ckfree(t); + } + INTON; + if (!done) + error("bug: parser_temp_free_upto misused"); +} + + +static void +parser_temp_free_all(void) +{ + struct parser_temp *t; + + INTOFF; + while (parser_temp != NULL) { + t = parser_temp; + parser_temp = t->next; + ckfree(t->data); + ckfree(t); + } + INTON; +} + + +/* + * Read and parse a command. Returns NEOF on end of file. (NULL is a + * valid parse tree indicating a blank line.) + */ + +union node * +parsecmd(int interact) +{ + int t; + + /* This assumes the parser is not re-entered, + * which could happen if we add command substitution on PS1/PS2. + */ + parser_temp_free_all(); + heredoclist = NULL; + + tokpushback = 0; + checkkwd = 0; + doprompt = interact; + if (doprompt) + setprompt(1); + else + setprompt(0); + needprompt = 0; + t = readtoken(); + if (t == TEOF) + return NEOF; + if (t == TNL) + return NULL; + tokpushback++; + return list(1); +} + + +/* + * Read and parse words for wordexp. + * Returns a list of NARG nodes; NULL if there are no words. + */ +union node * +parsewordexp(void) +{ + union node *n, *first = NULL, **pnext; + int t; + + /* This assumes the parser is not re-entered, + * which could happen if we add command substitution on PS1/PS2. + */ + parser_temp_free_all(); + heredoclist = NULL; + + tokpushback = 0; + checkkwd = 0; + doprompt = 0; + setprompt(0); + needprompt = 0; + pnext = &first; + while ((t = readtoken()) != TEOF) { + if (t != TWORD) + synexpect(TWORD); + n = makename(); + *pnext = n; + pnext = &n->narg.next; + } + return first; +} + + +static union node * +list(int nlflag) +{ + union node *ntop, *n1, *n2, *n3; + int tok; + + checkkwd = CHKNL | CHKKWD | CHKALIAS; + if (!nlflag && tokendlist[peektoken()]) + return NULL; + ntop = n1 = NULL; + for (;;) { + n2 = andor(); + tok = readtoken(); + if (tok == TBACKGND) { + if (n2 != NULL && n2->type == NPIPE) { + n2->npipe.backgnd = 1; + } else if (n2 != NULL && n2->type == NREDIR) { + n2->type = NBACKGND; + } else { + n3 = (union node *)stalloc(sizeof (struct nredir)); + n3->type = NBACKGND; + n3->nredir.n = n2; + n3->nredir.redirect = NULL; + n2 = n3; + } + } + if (ntop == NULL) + ntop = n2; + else if (n1 == NULL) { + n1 = makebinary(NSEMI, ntop, n2); + ntop = n1; + } + else { + n3 = makebinary(NSEMI, n1->nbinary.ch2, n2); + n1->nbinary.ch2 = n3; + n1 = n3; + } + switch (tok) { + case TBACKGND: + case TSEMI: + tok = readtoken(); + /* FALLTHROUGH */ + case TNL: + if (tok == TNL) { + parseheredoc(); + if (nlflag) + return ntop; + } else if (tok == TEOF && nlflag) { + parseheredoc(); + return ntop; + } else { + tokpushback++; + } + checkkwd = CHKNL | CHKKWD | CHKALIAS; + if (!nlflag && tokendlist[peektoken()]) + return ntop; + break; + case TEOF: + if (heredoclist) + parseheredoc(); + else + pungetc(); /* push back EOF on input */ + return ntop; + default: + if (nlflag) + synexpect(-1); + tokpushback++; + return ntop; + } + } +} + + + +static union node * +andor(void) +{ + union node *n; + int t; + + n = pipeline(); + for (;;) { + if ((t = readtoken()) == TAND) { + t = NAND; + } else if (t == TOR) { + t = NOR; + } else { + tokpushback++; + return n; + } + n = makebinary(t, n, pipeline()); + } +} + + + +static union node * +pipeline(void) +{ + union node *n1, *n2, *pipenode; + struct nodelist *lp, *prev; + int negate, t; + + negate = 0; + checkkwd = CHKNL | CHKKWD | CHKALIAS; + TRACE(("pipeline: entered\n")); + while (readtoken() == TNOT) + negate = !negate; + tokpushback++; + n1 = command(); + if (readtoken() == TPIPE) { + pipenode = (union node *)stalloc(sizeof (struct npipe)); + pipenode->type = NPIPE; + pipenode->npipe.backgnd = 0; + lp = (struct nodelist *)stalloc(sizeof (struct nodelist)); + pipenode->npipe.cmdlist = lp; + lp->n = n1; + do { + prev = lp; + lp = (struct nodelist *)stalloc(sizeof (struct nodelist)); + checkkwd = CHKNL | CHKKWD | CHKALIAS; + t = readtoken(); + tokpushback++; + if (t == TNOT) + lp->n = pipeline(); + else + lp->n = command(); + prev->next = lp; + } while (readtoken() == TPIPE); + lp->next = NULL; + n1 = pipenode; + } + tokpushback++; + if (negate) { + n2 = (union node *)stalloc(sizeof (struct nnot)); + n2->type = NNOT; + n2->nnot.com = n1; + return n2; + } else + return n1; +} + + + +static union node * +command(void) +{ + union node *n1, *n2; + union node *ap, **app; + union node *cp, **cpp; + union node *redir, **rpp; + int t; + int is_subshell; + + checkkwd = CHKNL | CHKKWD | CHKALIAS; + is_subshell = 0; + redir = NULL; + n1 = NULL; + rpp = &redir; + + /* Check for redirection which may precede command */ + while (readtoken() == TREDIR) { + *rpp = n2 = redirnode; + rpp = &n2->nfile.next; + parsefname(); + } + tokpushback++; + + switch (readtoken()) { + case TIF: + n1 = (union node *)stalloc(sizeof (struct nif)); + n1->type = NIF; + if ((n1->nif.test = list(0)) == NULL) + synexpect(-1); + consumetoken(TTHEN); + n1->nif.ifpart = list(0); + n2 = n1; + while (readtoken() == TELIF) { + n2->nif.elsepart = (union node *)stalloc(sizeof (struct nif)); + n2 = n2->nif.elsepart; + n2->type = NIF; + if ((n2->nif.test = list(0)) == NULL) + synexpect(-1); + consumetoken(TTHEN); + n2->nif.ifpart = list(0); + } + if (lasttoken == TELSE) + n2->nif.elsepart = list(0); + else { + n2->nif.elsepart = NULL; + tokpushback++; + } + consumetoken(TFI); + checkkwd = CHKKWD | CHKALIAS; + break; + case TWHILE: + case TUNTIL: + t = lasttoken; + if ((n1 = list(0)) == NULL) + synexpect(-1); + consumetoken(TDO); + n1 = makebinary((t == TWHILE)? NWHILE : NUNTIL, n1, list(0)); + consumetoken(TDONE); + checkkwd = CHKKWD | CHKALIAS; + break; + case TFOR: + if (readtoken() != TWORD || quoteflag || ! goodname(wordtext)) + synerror("Bad for loop variable"); + n1 = (union node *)stalloc(sizeof (struct nfor)); + n1->type = NFOR; + n1->nfor.var = wordtext; + while (readtoken() == TNL) + ; + if (lasttoken == TWORD && ! quoteflag && equal(wordtext, "in")) { + app = ≈ + while (readtoken() == TWORD) { + n2 = makename(); + *app = n2; + app = &n2->narg.next; + } + *app = NULL; + n1->nfor.args = ap; + if (lasttoken != TNL && lasttoken != TSEMI) + synexpect(-1); + } else { + static char argvars[5] = { + CTLVAR, VSNORMAL|VSQUOTE, '@', '=', '\0' + }; + n2 = (union node *)stalloc(sizeof (struct narg)); + n2->type = NARG; + n2->narg.text = argvars; + n2->narg.backquote = NULL; + n2->narg.next = NULL; + n1->nfor.args = n2; + /* + * Newline or semicolon here is optional (but note + * that the original Bourne shell only allowed NL). + */ + if (lasttoken != TNL && lasttoken != TSEMI) + tokpushback++; + } + checkkwd = CHKNL | CHKKWD | CHKALIAS; + if ((t = readtoken()) == TDO) + t = TDONE; + else if (t == TBEGIN) + t = TEND; + else + synexpect(-1); + n1->nfor.body = list(0); + consumetoken(t); + checkkwd = CHKKWD | CHKALIAS; + break; + case TCASE: + n1 = (union node *)stalloc(sizeof (struct ncase)); + n1->type = NCASE; + consumetoken(TWORD); + n1->ncase.expr = makename(); + while (readtoken() == TNL); + if (lasttoken != TWORD || ! equal(wordtext, "in")) + synerror("expecting \"in\""); + cpp = &n1->ncase.cases; + checkkwd = CHKNL | CHKKWD, readtoken(); + while (lasttoken != TESAC) { + *cpp = cp = (union node *)stalloc(sizeof (struct nclist)); + cp->type = NCLIST; + app = &cp->nclist.pattern; + if (lasttoken == TLP) + readtoken(); + for (;;) { + *app = ap = makename(); + checkkwd = CHKNL | CHKKWD; + if (readtoken() != TPIPE) + break; + app = &ap->narg.next; + readtoken(); + } + ap->narg.next = NULL; + if (lasttoken != TRP) + synexpect(TRP); + cp->nclist.body = list(0); + + checkkwd = CHKNL | CHKKWD | CHKALIAS; + if ((t = readtoken()) != TESAC) { + if (t == TENDCASE) + ; + else if (t == TFALLTHRU) + cp->type = NCLISTFALLTHRU; + else + synexpect(TENDCASE); + checkkwd = CHKNL | CHKKWD, readtoken(); + } + cpp = &cp->nclist.next; + } + *cpp = NULL; + checkkwd = CHKKWD | CHKALIAS; + break; + case TLP: + n1 = (union node *)stalloc(sizeof (struct nredir)); + n1->type = NSUBSHELL; + n1->nredir.n = list(0); + n1->nredir.redirect = NULL; + consumetoken(TRP); + checkkwd = CHKKWD | CHKALIAS; + is_subshell = 1; + break; + case TBEGIN: + n1 = list(0); + consumetoken(TEND); + checkkwd = CHKKWD | CHKALIAS; + break; + /* A simple command must have at least one redirection or word. */ + case TBACKGND: + case TSEMI: + case TAND: + case TOR: + case TPIPE: + case TENDCASE: + case TFALLTHRU: + case TEOF: + case TNL: + case TRP: + if (!redir) + synexpect(-1); + case TWORD: + tokpushback++; + n1 = simplecmd(rpp, redir); + return n1; + default: + synexpect(-1); + } + + /* Now check for redirection which may follow command */ + while (readtoken() == TREDIR) { + *rpp = n2 = redirnode; + rpp = &n2->nfile.next; + parsefname(); + } + tokpushback++; + *rpp = NULL; + if (redir) { + if (!is_subshell) { + n2 = (union node *)stalloc(sizeof (struct nredir)); + n2->type = NREDIR; + n2->nredir.n = n1; + n1 = n2; + } + n1->nredir.redirect = redir; + } + + return n1; +} + + +static union node * +simplecmd(union node **rpp, union node *redir) +{ + union node *args, **app; + union node **orig_rpp = rpp; + union node *n = NULL; + int special; + int savecheckkwd; + + /* If we don't have any redirections already, then we must reset */ + /* rpp to be the address of the local redir variable. */ + if (redir == NULL) + rpp = &redir; + + args = NULL; + app = &args; + /* + * We save the incoming value, because we need this for shell + * functions. There can not be a redirect or an argument between + * the function name and the open parenthesis. + */ + orig_rpp = rpp; + + savecheckkwd = CHKALIAS; + + for (;;) { + checkkwd = savecheckkwd; + if (readtoken() == TWORD) { + n = makename(); + *app = n; + app = &n->narg.next; + if (savecheckkwd != 0 && !isassignment(wordtext)) + savecheckkwd = 0; + } else if (lasttoken == TREDIR) { + *rpp = n = redirnode; + rpp = &n->nfile.next; + parsefname(); /* read name of redirection file */ + } else if (lasttoken == TLP && app == &args->narg.next + && rpp == orig_rpp) { + /* We have a function */ + consumetoken(TRP); + funclinno = plinno; + /* + * - Require plain text. + * - Functions with '/' cannot be called. + * - Reject name=(). + * - Reject ksh extended glob patterns. + */ + if (!noexpand(n->narg.text) || quoteflag || + strchr(n->narg.text, '/') || + strchr("!%*+-=?@}~", + n->narg.text[strlen(n->narg.text) - 1])) + synerror("Bad function name"); + rmescapes(n->narg.text); + if (find_builtin(n->narg.text, &special) >= 0 && + special) + synerror("Cannot override a special builtin with a function"); + n->type = NDEFUN; + n->narg.next = command(); + funclinno = 0; + return n; + } else { + tokpushback++; + break; + } + } + *app = NULL; + *rpp = NULL; + n = (union node *)stalloc(sizeof (struct ncmd)); + n->type = NCMD; + n->ncmd.args = args; + n->ncmd.redirect = redir; + return n; +} + +static union node * +makename(void) +{ + union node *n; + + n = (union node *)stalloc(sizeof (struct narg)); + n->type = NARG; + n->narg.next = NULL; + n->narg.text = wordtext; + n->narg.backquote = backquotelist; + return n; +} + +static union node * +makebinary(int type, union node *n1, union node *n2) +{ + union node *n; + + n = (union node *)stalloc(sizeof (struct nbinary)); + n->type = type; + n->nbinary.ch1 = n1; + n->nbinary.ch2 = n2; + return (n); +} + +void +forcealias(void) +{ + checkkwd |= CHKALIAS; +} + +void +fixredir(union node *n, const char *text, int err) +{ + TRACE(("Fix redir %s %d\n", text, err)); + if (!err) + n->ndup.vname = NULL; + + if (is_digit(text[0]) && text[1] == '\0') + n->ndup.dupfd = digit_val(text[0]); + else if (text[0] == '-' && text[1] == '\0') + n->ndup.dupfd = -1; + else { + + if (err) + synerror("Bad fd number"); + else + n->ndup.vname = makename(); + } +} + + +static void +parsefname(void) +{ + union node *n = redirnode; + + consumetoken(TWORD); + if (n->type == NHERE) { + struct heredoc *here = heredoc; + struct heredoc *p; + + if (quoteflag == 0) + n->type = NXHERE; + TRACE(("Here document %d\n", n->type)); + if (here->striptabs) { + while (*wordtext == '\t') + wordtext++; + } + if (! noexpand(wordtext)) + synerror("Illegal eof marker for << redirection"); + rmescapes(wordtext); + here->eofmark = wordtext; + here->next = NULL; + if (heredoclist == NULL) + heredoclist = here; + else { + for (p = heredoclist ; p->next ; p = p->next); + p->next = here; + } + } else if (n->type == NTOFD || n->type == NFROMFD) { + fixredir(n, wordtext, 0); + } else { + n->nfile.fname = makename(); + } +} + + +/* + * Input any here documents. + */ + +static void +parseheredoc(void) +{ + struct heredoc *here; + union node *n; + + while (heredoclist) { + here = heredoclist; + heredoclist = here->next; + if (needprompt) { + setprompt(2); + needprompt = 0; + } + readtoken1(pgetc(), here->here->type == NHERE? SQSYNTAX : DQSYNTAX, + here->eofmark, here->striptabs); + n = makename(); + here->here->nhere.doc = n; + } +} + +static int +peektoken(void) +{ + int t; + + t = readtoken(); + tokpushback++; + return (t); +} + +static int +readtoken(void) +{ + int t; + struct alias *ap; +#ifdef DEBUG + int alreadyseen = tokpushback; +#endif + + top: + t = xxreadtoken(); + + /* + * eat newlines + */ + if (checkkwd & CHKNL) { + while (t == TNL) { + parseheredoc(); + t = xxreadtoken(); + } + } + + /* + * check for keywords and aliases + */ + if (t == TWORD && !quoteflag) + { + const char * const *pp; + + if (checkkwd & CHKKWD) + for (pp = parsekwd; *pp; pp++) { + if (**pp == *wordtext && equal(*pp, wordtext)) + { + lasttoken = t = pp - parsekwd + KWDOFFSET; + TRACE(("keyword %s recognized\n", tokname[t])); + goto out; + } + } + if (checkkwd & CHKALIAS && + (ap = lookupalias(wordtext, 1)) != NULL) { + pushstring(ap->val, strlen(ap->val), ap); + goto top; + } + } +out: + if (t != TNOT) + checkkwd = 0; + +#ifdef DEBUG + if (!alreadyseen) + TRACE(("token %s %s\n", tokname[t], t == TWORD ? wordtext : "")); + else + TRACE(("reread token %s %s\n", tokname[t], t == TWORD ? wordtext : "")); +#endif + return (t); +} + + +/* + * Read the next input token. + * If the token is a word, we set backquotelist to the list of cmds in + * backquotes. We set quoteflag to true if any part of the word was + * quoted. + * If the token is TREDIR, then we set redirnode to a structure containing + * the redirection. + * In all cases, the variable startlinno is set to the number of the line + * on which the token starts. + * + * [Change comment: here documents and internal procedures] + * [Readtoken shouldn't have any arguments. Perhaps we should make the + * word parsing code into a separate routine. In this case, readtoken + * doesn't need to have any internal procedures, but parseword does. + * We could also make parseoperator in essence the main routine, and + * have parseword (readtoken1?) handle both words and redirection.] + */ + +#define RETURN(token) return lasttoken = token + +static int +xxreadtoken(void) +{ + int c; + + if (tokpushback) { + tokpushback = 0; + return lasttoken; + } + if (needprompt) { + setprompt(2); + needprompt = 0; + } + startlinno = plinno; + for (;;) { /* until token or start of word found */ + c = pgetc_macro(); + switch (c) { + case ' ': case '\t': + continue; + case '#': + while ((c = pgetc()) != '\n' && c != PEOF); + pungetc(); + continue; + case '\\': + if (pgetc() == '\n') { + startlinno = ++plinno; + if (doprompt) + setprompt(2); + else + setprompt(0); + continue; + } + pungetc(); + /* FALLTHROUGH */ + default: + return readtoken1(c, BASESYNTAX, (char *)NULL, 0); + case '\n': + plinno++; + needprompt = doprompt; + RETURN(TNL); + case PEOF: + RETURN(TEOF); + case '&': + if (pgetc_linecont() == '&') + RETURN(TAND); + pungetc(); + RETURN(TBACKGND); + case '|': + if (pgetc_linecont() == '|') + RETURN(TOR); + pungetc(); + RETURN(TPIPE); + case ';': + c = pgetc_linecont(); + if (c == ';') + RETURN(TENDCASE); + else if (c == '&') + RETURN(TFALLTHRU); + pungetc(); + RETURN(TSEMI); + case '(': + RETURN(TLP); + case ')': + RETURN(TRP); + } + } +#undef RETURN +} + + +#define MAXNEST_static 8 +struct tokenstate +{ + const char *syntax; /* *SYNTAX */ + int parenlevel; /* levels of parentheses in arithmetic */ + enum tokenstate_category + { + TSTATE_TOP, + TSTATE_VAR_OLD, /* ${var+-=?}, inherits dquotes */ + TSTATE_VAR_NEW, /* other ${var...}, own dquote state */ + TSTATE_ARITH + } category; +}; + + +/* + * Check to see whether we are at the end of the here document. When this + * is called, c is set to the first character of the next input line. If + * we are at the end of the here document, this routine sets the c to PEOF. + * The new value of c is returned. + */ + +static int +checkend(int c, const char *eofmark, int striptabs) +{ + if (striptabs) { + while (c == '\t') + c = pgetc(); + } + if (c == *eofmark) { + int c2; + const char *q; + + for (q = eofmark + 1; c2 = pgetc(), *q != '\0' && c2 == *q; q++) + ; + if ((c2 == PEOF || c2 == '\n') && *q == '\0') { + c = PEOF; + if (c2 == '\n') { + plinno++; + needprompt = doprompt; + } + } else { + pungetc(); + pushstring(eofmark + 1, q - (eofmark + 1), NULL); + } + } else if (c == '\n' && *eofmark == '\0') { + c = PEOF; + plinno++; + needprompt = doprompt; + } + return (c); +} + + +/* + * Parse a redirection operator. The variable "out" points to a string + * specifying the fd to be redirected. The variable "c" contains the + * first character of the redirection operator. + */ + +static void +parseredir(char *out, int c) +{ + char fd = *out; + union node *np; + + np = (union node *)stalloc(sizeof (struct nfile)); + if (c == '>') { + np->nfile.fd = 1; + c = pgetc_linecont(); + if (c == '>') + np->type = NAPPEND; + else if (c == '&') + np->type = NTOFD; + else if (c == '|') + np->type = NCLOBBER; + else { + np->type = NTO; + pungetc(); + } + } else { /* c == '<' */ + np->nfile.fd = 0; + c = pgetc_linecont(); + if (c == '<') { + if (sizeof (struct nfile) != sizeof (struct nhere)) { + np = (union node *)stalloc(sizeof (struct nhere)); + np->nfile.fd = 0; + } + np->type = NHERE; + heredoc = (struct heredoc *)stalloc(sizeof (struct heredoc)); + heredoc->here = np; + if ((c = pgetc_linecont()) == '-') { + heredoc->striptabs = 1; + } else { + heredoc->striptabs = 0; + pungetc(); + } + } else if (c == '&') + np->type = NFROMFD; + else if (c == '>') + np->type = NFROMTO; + else { + np->type = NFROM; + pungetc(); + } + } + if (fd != '\0') + np->nfile.fd = digit_val(fd); + redirnode = np; +} + +/* + * Called to parse command substitutions. + */ + +static char * +parsebackq(char *out, struct nodelist **pbqlist, + int oldstyle, int dblquote, int quoted) +{ + struct nodelist **nlpp; + union node *n; + char *volatile str; + struct jmploc jmploc; + struct jmploc *const savehandler = handler; + size_t savelen; + int saveprompt; + const int bq_startlinno = plinno; + char *volatile ostr = NULL; + struct parsefile *const savetopfile = getcurrentfile(); + struct heredoc *const saveheredoclist = heredoclist; + struct heredoc *here; + + str = NULL; + if (setjmp(jmploc.loc)) { + popfilesupto(savetopfile); + if (str) + ckfree(str); + if (ostr) + ckfree(ostr); + heredoclist = saveheredoclist; + handler = savehandler; + if (exception == EXERROR) { + startlinno = bq_startlinno; + synerror("Error in command substitution"); + } + longjmp(handler->loc, 1); + } + INTOFF; + savelen = out - stackblock(); + if (savelen > 0) { + str = ckmalloc(savelen); + memcpy(str, stackblock(), savelen); + } + handler = &jmploc; + heredoclist = NULL; + INTON; + if (oldstyle) { + /* We must read until the closing backquote, giving special + treatment to some slashes, and then push the string and + reread it as input, interpreting it normally. */ + char *oout; + int c; + int olen; + + + STARTSTACKSTR(oout); + for (;;) { + if (needprompt) { + setprompt(2); + needprompt = 0; + } + CHECKSTRSPACE(2, oout); + c = pgetc_linecont(); + if (c == '`') + break; + switch (c) { + case '\\': + c = pgetc(); + if (c != '\\' && c != '`' && c != '$' + && (!dblquote || c != '"')) + USTPUTC('\\', oout); + break; + + case '\n': + plinno++; + needprompt = doprompt; + break; + + case PEOF: + startlinno = plinno; + synerror("EOF in backquote substitution"); + break; + + default: + break; + } + USTPUTC(c, oout); + } + USTPUTC('\0', oout); + olen = oout - stackblock(); + INTOFF; + ostr = ckmalloc(olen); + memcpy(ostr, stackblock(), olen); + setinputstring(ostr, 1); + INTON; + } + nlpp = pbqlist; + while (*nlpp) + nlpp = &(*nlpp)->next; + *nlpp = (struct nodelist *)stalloc(sizeof (struct nodelist)); + (*nlpp)->next = NULL; + + if (oldstyle) { + saveprompt = doprompt; + doprompt = 0; + } + + n = list(0); + + if (oldstyle) { + if (peektoken() != TEOF) + synexpect(-1); + doprompt = saveprompt; + } else + consumetoken(TRP); + + (*nlpp)->n = n; + if (oldstyle) { + /* + * Start reading from old file again, ignoring any pushed back + * tokens left from the backquote parsing + */ + popfile(); + tokpushback = 0; + } + STARTSTACKSTR(out); + CHECKSTRSPACE(savelen + 1, out); + INTOFF; + if (str) { + memcpy(out, str, savelen); + STADJUST(savelen, out); + ckfree(str); + str = NULL; + } + if (ostr) { + ckfree(ostr); + ostr = NULL; + } + here = saveheredoclist; + if (here != NULL) { + while (here->next != NULL) + here = here->next; + here->next = heredoclist; + heredoclist = saveheredoclist; + } + handler = savehandler; + INTON; + if (quoted) + USTPUTC(CTLBACKQ | CTLQUOTE, out); + else + USTPUTC(CTLBACKQ, out); + return out; +} + + +/* + * Called to parse a backslash escape sequence inside $'...'. + * The backslash has already been read. + */ +static char * +readcstyleesc(char *out) +{ + int c, vc, i, n; + unsigned int v; + + c = pgetc(); + switch (c) { + case '\0': + synerror("Unterminated quoted string"); + case '\n': + plinno++; + if (doprompt) + setprompt(2); + else + setprompt(0); + return out; + case '\\': + case '\'': + case '"': + v = c; + break; + case 'a': v = '\a'; break; + case 'b': v = '\b'; break; + case 'e': v = '\033'; break; + case 'f': v = '\f'; break; + case 'n': v = '\n'; break; + case 'r': v = '\r'; break; + case 't': v = '\t'; break; + case 'v': v = '\v'; break; + case 'x': + v = 0; + for (;;) { + c = pgetc(); + if (c >= '0' && c <= '9') + v = (v << 4) + c - '0'; + else if (c >= 'A' && c <= 'F') + v = (v << 4) + c - 'A' + 10; + else if (c >= 'a' && c <= 'f') + v = (v << 4) + c - 'a' + 10; + else + break; + } + pungetc(); + break; + case '0': case '1': case '2': case '3': + case '4': case '5': case '6': case '7': + v = c - '0'; + c = pgetc(); + if (c >= '0' && c <= '7') { + v <<= 3; + v += c - '0'; + c = pgetc(); + if (c >= '0' && c <= '7') { + v <<= 3; + v += c - '0'; + } else + pungetc(); + } else + pungetc(); + break; + case 'c': + c = pgetc(); + if (c < 0x3f || c > 0x7a || c == 0x60) + synerror("Bad escape sequence"); + if (c == '\\' && pgetc() != '\\') + synerror("Bad escape sequence"); + if (c == '?') + v = 127; + else + v = c & 0x1f; + break; + case 'u': + case 'U': + n = c == 'U' ? 8 : 4; + v = 0; + for (i = 0; i < n; i++) { + c = pgetc(); + if (c >= '0' && c <= '9') + v = (v << 4) + c - '0'; + else if (c >= 'A' && c <= 'F') + v = (v << 4) + c - 'A' + 10; + else if (c >= 'a' && c <= 'f') + v = (v << 4) + c - 'a' + 10; + else + synerror("Bad escape sequence"); + } + if (v == 0 || (v >= 0xd800 && v <= 0xdfff)) + synerror("Bad escape sequence"); + /* We really need iconv here. */ + if (initial_localeisutf8 && v > 127) { + CHECKSTRSPACE(4, out); + /* + * We cannot use wctomb() as the locale may have + * changed. + */ + if (v <= 0x7ff) { + USTPUTC(0xc0 | v >> 6, out); + USTPUTC(0x80 | (v & 0x3f), out); + return out; + } else if (v <= 0xffff) { + USTPUTC(0xe0 | v >> 12, out); + USTPUTC(0x80 | ((v >> 6) & 0x3f), out); + USTPUTC(0x80 | (v & 0x3f), out); + return out; + } else if (v <= 0x10ffff) { + USTPUTC(0xf0 | v >> 18, out); + USTPUTC(0x80 | ((v >> 12) & 0x3f), out); + USTPUTC(0x80 | ((v >> 6) & 0x3f), out); + USTPUTC(0x80 | (v & 0x3f), out); + return out; + } + } + if (v > 127) + v = '?'; + break; + default: + synerror("Bad escape sequence"); + } + vc = (char)v; + /* + * We can't handle NUL bytes. + * POSIX says we should skip till the closing quote. + */ + if (vc == '\0') { + while ((c = pgetc()) != '\'') { + if (c == '\\') + c = pgetc(); + if (c == PEOF) + synerror("Unterminated quoted string"); + if (c == '\n') { + plinno++; + if (doprompt) + setprompt(2); + else + setprompt(0); + } + } + pungetc(); + return out; + } + if (SQSYNTAX[vc] == CCTL) + USTPUTC(CTLESC, out); + USTPUTC(vc, out); + return out; +} + + +/* + * If eofmark is NULL, read a word or a redirection symbol. If eofmark + * is not NULL, read a here document. In the latter case, eofmark is the + * word which marks the end of the document and striptabs is true if + * leading tabs should be stripped from the document. The argument firstc + * is the first character of the input token or document. + * + * Because C does not have internal subroutines, I have simulated them + * using goto's to implement the subroutine linkage. The following macros + * will run code that appears at the end of readtoken1. + */ + +#define PARSESUB() {goto parsesub; parsesub_return:;} +#define PARSEARITH() {goto parsearith; parsearith_return:;} + +static int +readtoken1(int firstc, char const *initialsyntax, const char *eofmark, + int striptabs) +{ + int c = firstc; + char *out; + int len; + struct nodelist *bqlist; + int quotef; + int newvarnest; + int level; + int synentry; + struct tokenstate state_static[MAXNEST_static]; + int maxnest = MAXNEST_static; + struct tokenstate *state = state_static; + int sqiscstyle = 0; + + startlinno = plinno; + quotef = 0; + bqlist = NULL; + newvarnest = 0; + level = 0; + state[level].syntax = initialsyntax; + state[level].parenlevel = 0; + state[level].category = TSTATE_TOP; + + STARTSTACKSTR(out); + loop: { /* for each line, until end of word */ + if (eofmark && eofmark != NOEOFMARK) + /* set c to PEOF if at end of here document */ + c = checkend(c, eofmark, striptabs); + for (;;) { /* until end of line or end of word */ + CHECKSTRSPACE(4, out); /* permit 4 calls to USTPUTC */ + + synentry = state[level].syntax[c]; + + switch(synentry) { + case CNL: /* '\n' */ + if (state[level].syntax == BASESYNTAX) + goto endword; /* exit outer loop */ + USTPUTC(c, out); + plinno++; + if (doprompt) + setprompt(2); + else + setprompt(0); + c = pgetc(); + goto loop; /* continue outer loop */ + case CSBACK: + if (sqiscstyle) { + out = readcstyleesc(out); + break; + } + /* FALLTHROUGH */ + case CWORD: + USTPUTC(c, out); + break; + case CCTL: + if (eofmark == NULL || initialsyntax != SQSYNTAX) + USTPUTC(CTLESC, out); + USTPUTC(c, out); + break; + case CBACK: /* backslash */ + c = pgetc(); + if (c == PEOF) { + USTPUTC('\\', out); + pungetc(); + } else if (c == '\n') { + plinno++; + if (doprompt) + setprompt(2); + else + setprompt(0); + } else { + if (state[level].syntax == DQSYNTAX && + c != '\\' && c != '`' && c != '$' && + (c != '"' || (eofmark != NULL && + newvarnest == 0)) && + (c != '}' || state[level].category != TSTATE_VAR_OLD)) + USTPUTC('\\', out); + if ((eofmark == NULL || + newvarnest > 0) && + state[level].syntax == BASESYNTAX) + USTPUTC(CTLQUOTEMARK, out); + if (SQSYNTAX[c] == CCTL) + USTPUTC(CTLESC, out); + USTPUTC(c, out); + if ((eofmark == NULL || + newvarnest > 0) && + state[level].syntax == BASESYNTAX && + state[level].category == TSTATE_VAR_OLD) + USTPUTC(CTLQUOTEEND, out); + quotef++; + } + break; + case CSQUOTE: + USTPUTC(CTLQUOTEMARK, out); + state[level].syntax = SQSYNTAX; + sqiscstyle = 0; + break; + case CDQUOTE: + USTPUTC(CTLQUOTEMARK, out); + state[level].syntax = DQSYNTAX; + break; + case CENDQUOTE: + if (eofmark != NULL && newvarnest == 0) + USTPUTC(c, out); + else { + if (state[level].category == TSTATE_VAR_OLD) + USTPUTC(CTLQUOTEEND, out); + state[level].syntax = BASESYNTAX; + quotef++; + } + break; + case CVAR: /* '$' */ + PARSESUB(); /* parse substitution */ + break; + case CENDVAR: /* '}' */ + if (level > 0 && + ((state[level].category == TSTATE_VAR_OLD && + state[level].syntax == + state[level - 1].syntax) || + (state[level].category == TSTATE_VAR_NEW && + state[level].syntax == BASESYNTAX))) { + if (state[level].category == TSTATE_VAR_NEW) + newvarnest--; + level--; + USTPUTC(CTLENDVAR, out); + } else { + USTPUTC(c, out); + } + break; + case CLP: /* '(' in arithmetic */ + state[level].parenlevel++; + USTPUTC(c, out); + break; + case CRP: /* ')' in arithmetic */ + if (state[level].parenlevel > 0) { + USTPUTC(c, out); + --state[level].parenlevel; + } else { + if (pgetc_linecont() == ')') { + if (level > 0 && + state[level].category == TSTATE_ARITH) { + level--; + USTPUTC(CTLENDARI, out); + } else + USTPUTC(')', out); + } else { + /* + * unbalanced parens + * (don't 2nd guess - no error) + */ + pungetc(); + USTPUTC(')', out); + } + } + break; + case CBQUOTE: /* '`' */ + out = parsebackq(out, &bqlist, 1, + state[level].syntax == DQSYNTAX && + (eofmark == NULL || newvarnest > 0), + state[level].syntax == DQSYNTAX || state[level].syntax == ARISYNTAX); + break; + case CEOF: + goto endword; /* exit outer loop */ + case CIGN: + break; + default: + if (level == 0) + goto endword; /* exit outer loop */ + USTPUTC(c, out); + } + c = pgetc_macro(); + } + } +endword: + if (state[level].syntax == ARISYNTAX) + synerror("Missing '))'"); + if (state[level].syntax != BASESYNTAX && eofmark == NULL) + synerror("Unterminated quoted string"); + if (state[level].category == TSTATE_VAR_OLD || + state[level].category == TSTATE_VAR_NEW) { + startlinno = plinno; + synerror("Missing '}'"); + } + if (state != state_static) + parser_temp_free_upto(state); + USTPUTC('\0', out); + len = out - stackblock(); + out = stackblock(); + if (eofmark == NULL) { + if ((c == '>' || c == '<') + && quotef == 0 + && len <= 2 + && (*out == '\0' || is_digit(*out))) { + parseredir(out, c); + return lasttoken = TREDIR; + } else { + pungetc(); + } + } + quoteflag = quotef; + backquotelist = bqlist; + grabstackblock(len); + wordtext = out; + return lasttoken = TWORD; +/* end of readtoken routine */ + + +/* + * Parse a substitution. At this point, we have read the dollar sign + * and nothing else. + */ + +parsesub: { + int subtype; + int typeloc; + int flags; + char *p; + static const char types[] = "}-+?="; + int linno; + int length; + int c1; + + c = pgetc_linecont(); + if (c == '(') { /* $(command) or $((arith)) */ + if (pgetc_linecont() == '(') { + PARSEARITH(); + } else { + pungetc(); + out = parsebackq(out, &bqlist, 0, + state[level].syntax == DQSYNTAX && + (eofmark == NULL || newvarnest > 0), + state[level].syntax == DQSYNTAX || + state[level].syntax == ARISYNTAX); + } + } else if (c == '{' || is_name(c) || is_special(c)) { + USTPUTC(CTLVAR, out); + typeloc = out - stackblock(); + USTPUTC(VSNORMAL, out); + subtype = VSNORMAL; + flags = 0; + if (c == '{') { + c = pgetc_linecont(); + subtype = 0; + } +varname: + if (!is_eof(c) && is_name(c)) { + length = 0; + do { + STPUTC(c, out); + c = pgetc_linecont(); + length++; + } while (!is_eof(c) && is_in_name(c)); + if (length == 6 && + strncmp(out - length, "LINENO", length) == 0) { + /* Replace the variable name with the + * current line number. */ + STADJUST(-6, out); + CHECKSTRSPACE(11, out); + linno = plinno; + if (funclinno != 0) + linno -= funclinno - 1; + length = snprintf(out, 11, "%d", linno); + if (length > 10) + length = 10; + out += length; + flags |= VSLINENO; + } + } else if (is_digit(c)) { + if (subtype != VSNORMAL) { + do { + STPUTC(c, out); + c = pgetc_linecont(); + } while (is_digit(c)); + } else { + USTPUTC(c, out); + c = pgetc_linecont(); + } + } else if (is_special(c)) { + c1 = c; + c = pgetc_linecont(); + if (subtype == 0 && c1 == '#') { + subtype = VSLENGTH; + if (strchr(types, c) == NULL && c != ':' && + c != '#' && c != '%') + goto varname; + c1 = c; + c = pgetc_linecont(); + if (c1 != '}' && c == '}') { + pungetc(); + c = c1; + goto varname; + } + pungetc(); + c = c1; + c1 = '#'; + subtype = 0; + } + USTPUTC(c1, out); + } else { + subtype = VSERROR; + if (c == '}') + pungetc(); + else if (c == '\n' || c == PEOF) + synerror("Unexpected end of line in substitution"); + else if (BASESYNTAX[c] != CCTL) + USTPUTC(c, out); + } + if (subtype == 0) { + switch (c) { + case ':': + flags |= VSNUL; + c = pgetc_linecont(); + /*FALLTHROUGH*/ + default: + p = strchr(types, c); + if (p == NULL) { + if (c == '\n' || c == PEOF) + synerror("Unexpected end of line in substitution"); + if (flags == VSNUL) + STPUTC(':', out); + if (BASESYNTAX[c] != CCTL) + STPUTC(c, out); + subtype = VSERROR; + } else + subtype = p - types + VSNORMAL; + break; + case '%': + case '#': + { + int cc = c; + subtype = c == '#' ? VSTRIMLEFT : + VSTRIMRIGHT; + c = pgetc_linecont(); + if (c == cc) + subtype++; + else + pungetc(); + break; + } + } + } else if (subtype != VSERROR) { + if (subtype == VSLENGTH && c != '}') + subtype = VSERROR; + pungetc(); + } + STPUTC('=', out); + if (state[level].syntax == DQSYNTAX || + state[level].syntax == ARISYNTAX) + flags |= VSQUOTE; + *(stackblock() + typeloc) = subtype | flags; + if (subtype != VSNORMAL) { + if (level + 1 >= maxnest) { + maxnest *= 2; + if (state == state_static) { + state = parser_temp_alloc( + maxnest * sizeof(*state)); + memcpy(state, state_static, + MAXNEST_static * sizeof(*state)); + } else + state = parser_temp_realloc(state, + maxnest * sizeof(*state)); + } + level++; + state[level].parenlevel = 0; + if (subtype == VSMINUS || subtype == VSPLUS || + subtype == VSQUESTION || subtype == VSASSIGN) { + /* + * For operators that were in the Bourne shell, + * inherit the double-quote state. + */ + state[level].syntax = state[level - 1].syntax; + state[level].category = TSTATE_VAR_OLD; + } else { + /* + * The other operators take a pattern, + * so go to BASESYNTAX. + * Also, ' and " are now special, even + * in here documents. + */ + state[level].syntax = BASESYNTAX; + state[level].category = TSTATE_VAR_NEW; + newvarnest++; + } + } + } else if (c == '\'' && state[level].syntax == BASESYNTAX) { + /* $'cstylequotes' */ + USTPUTC(CTLQUOTEMARK, out); + state[level].syntax = SQSYNTAX; + sqiscstyle = 1; + } else { + USTPUTC('$', out); + pungetc(); + } + goto parsesub_return; +} + + +/* + * Parse an arithmetic expansion (indicate start of one and set state) + */ +parsearith: { + + if (level + 1 >= maxnest) { + maxnest *= 2; + if (state == state_static) { + state = parser_temp_alloc( + maxnest * sizeof(*state)); + memcpy(state, state_static, + MAXNEST_static * sizeof(*state)); + } else + state = parser_temp_realloc(state, + maxnest * sizeof(*state)); + } + level++; + state[level].syntax = ARISYNTAX; + state[level].parenlevel = 0; + state[level].category = TSTATE_ARITH; + USTPUTC(CTLARI, out); + if (state[level - 1].syntax == DQSYNTAX) + USTPUTC('"',out); + else + USTPUTC(' ',out); + goto parsearith_return; +} + +} /* end of readtoken */ + + +/* + * Returns true if the text contains nothing to expand (no dollar signs + * or backquotes). + */ + +static int +noexpand(char *text) +{ + char *p; + char c; + + p = text; + while ((c = *p++) != '\0') { + if ( c == CTLQUOTEMARK) + continue; + if (c == CTLESC) + p++; + else if (BASESYNTAX[(int)c] == CCTL) + return 0; + } + return 1; +} + + +/* + * Return true if the argument is a legal variable name (a letter or + * underscore followed by zero or more letters, underscores, and digits). + */ + +int +goodname(const char *name) +{ + const char *p; + + p = name; + if (! is_name(*p)) + return 0; + while (*++p) { + if (! is_in_name(*p)) + return 0; + } + return 1; +} + + +int +isassignment(const char *p) +{ + if (!is_name(*p)) + return 0; + p++; + for (;;) { + if (*p == '=') + return 1; + else if (!is_in_name(*p)) + return 0; + p++; + } +} + + +static void +consumetoken(int token) +{ + if (readtoken() != token) + synexpect(token); +} + + +/* + * Called when an unexpected token is read during the parse. The argument + * is the token that is expected, or -1 if more than one type of token can + * occur at this point. + */ + +static void +synexpect(int token) +{ + char msg[64]; + + if (token >= 0) { + fmtstr(msg, 64, "%s unexpected (expecting %s)", + tokname[lasttoken], tokname[token]); + } else { + fmtstr(msg, 64, "%s unexpected", tokname[lasttoken]); + } + synerror(msg); +} + + +static void +synerror(const char *msg) +{ + if (commandname) + outfmt(out2, "%s: %d: ", commandname, startlinno); + else if (arg0) + outfmt(out2, "%s: ", arg0); + outfmt(out2, "Syntax error: %s\n", msg); + error((char *)NULL); +} + +static void +setprompt(int which) +{ + whichprompt = which; + if (which == 0) + return; + +#ifndef NO_HISTORY + if (!el) +#endif + { + out2str(getprompt(NULL)); + flushout(out2); + } +} + +static int +pgetc_linecont(void) +{ + int c; + + while ((c = pgetc_macro()) == '\\') { + c = pgetc(); + if (c == '\n') { + plinno++; + if (doprompt) + setprompt(2); + else + setprompt(0); + } else { + pungetc(); + /* Allow the backslash to be pushed back. */ + pushstring("\\", 1, NULL); + return (pgetc()); + } + } + return (c); +} + +/* + * called by editline -- any expansions to the prompt + * should be added here. + */ +char * +getprompt(void *unused __unused) +{ + static char ps[PROMPTLEN]; + const char *fmt; + const char *pwd; + int i, trim; + static char internal_error[] = "??"; + + /* + * Select prompt format. + */ + switch (whichprompt) { + case 0: + fmt = ""; + break; + case 1: + fmt = ps1val(); + break; + case 2: + fmt = ps2val(); + break; + default: + return internal_error; + } + + /* + * Format prompt string. + */ + for (i = 0; (i < PROMPTLEN - 1) && (*fmt != '\0'); i++, fmt++) + if (*fmt == '\\') + switch (*++fmt) { + + /* + * Hostname. + * + * \h specifies just the local hostname, + * \H specifies fully-qualified hostname. + */ + case 'h': + case 'H': + ps[i] = '\0'; + gethostname(&ps[i], PROMPTLEN - i - 1); + ps[PROMPTLEN - 1] = '\0'; + /* Skip to end of hostname. */ + trim = (*fmt == 'h') ? '.' : '\0'; + while ((ps[i] != '\0') && (ps[i] != trim)) + i++; + --i; + break; + + /* + * Working directory. + * + * \W specifies just the final component, + * \w specifies the entire path. + */ + case 'W': + case 'w': + pwd = lookupvar("PWD"); + if (pwd == NULL || *pwd == '\0') + pwd = "?"; + if (*fmt == 'W' && + *pwd == '/' && pwd[1] != '\0') + strlcpy(&ps[i], strrchr(pwd, '/') + 1, + PROMPTLEN - i); + else + strlcpy(&ps[i], pwd, PROMPTLEN - i); + /* Skip to end of path. */ + while (ps[i + 1] != '\0') + i++; + break; + + /* + * Superuser status. + * + * '$' for normal users, '#' for root. + */ + case '$': + ps[i] = (geteuid() != 0) ? '$' : '#'; + break; + + /* + * A literal \. + */ + case '\\': + ps[i] = '\\'; + break; + + /* + * Emit unrecognized formats verbatim. + */ + default: + ps[i] = '\\'; + if (i < PROMPTLEN - 2) + ps[++i] = *fmt; + break; + } + else + ps[i] = *fmt; + ps[i] = '\0'; + return (ps); +} + + +const char * +expandstr(const char *ps) +{ + union node n; + struct jmploc jmploc; + struct jmploc *const savehandler = handler; + const int saveprompt = doprompt; + struct parsefile *const savetopfile = getcurrentfile(); + struct parser_temp *const saveparser_temp = parser_temp; + const char *result = NULL; + + if (!setjmp(jmploc.loc)) { + handler = &jmploc; + parser_temp = NULL; + setinputstring(ps, 1); + doprompt = 0; + readtoken1(pgetc(), DQSYNTAX, NOEOFMARK, 0); + if (backquotelist != NULL) + error("Command substitution not allowed here"); + + n.narg.type = NARG; + n.narg.next = NULL; + n.narg.text = wordtext; + n.narg.backquote = backquotelist; + + expandarg(&n, NULL, 0); + result = stackblock(); + INTOFF; + } + handler = savehandler; + doprompt = saveprompt; + popfilesupto(savetopfile); + if (parser_temp != saveparser_temp) { + parser_temp_free_all(); + parser_temp = saveparser_temp; + } + if (result != NULL) { + INTON; + } else if (exception == EXINT) + raise(SIGINT); + return result; +} diff --git a/shell_cmds/sh/parser.h b/shell_cmds/sh/parser.h new file mode 100644 index 0000000..210726b --- /dev/null +++ b/shell_cmds/sh/parser.h @@ -0,0 +1,87 @@ +/*- + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)parser.h 8.3 (Berkeley) 5/4/95 + * $FreeBSD: head/bin/sh/parser.h 326025 2017-11-20 19:49:47Z pfg $ + */ + +/* control characters in argument strings */ +#define CTLESC '\300' +#define CTLVAR '\301' +#define CTLENDVAR '\371' +#define CTLBACKQ '\372' +#define CTLQUOTE 01 /* ored with CTLBACKQ code if in quotes */ +/* CTLBACKQ | CTLQUOTE == '\373' */ +#define CTLARI '\374' +#define CTLENDARI '\375' +#define CTLQUOTEMARK '\376' +#define CTLQUOTEEND '\377' /* only for ${v+-...} */ + +/* variable substitution byte (follows CTLVAR) */ +#define VSTYPE 0x0f /* type of variable substitution */ +#define VSNUL 0x10 /* colon--treat the empty string as unset */ +#define VSLINENO 0x20 /* expansion of $LINENO, the line number \ + follows immediately */ +#define VSQUOTE 0x80 /* inside double quotes--suppress splitting */ + +/* values of VSTYPE field */ +#define VSNORMAL 0x1 /* normal variable: $var or ${var} */ +#define VSMINUS 0x2 /* ${var-text} */ +#define VSPLUS 0x3 /* ${var+text} */ +#define VSQUESTION 0x4 /* ${var?message} */ +#define VSASSIGN 0x5 /* ${var=text} */ +#define VSTRIMLEFT 0x6 /* ${var#pattern} */ +#define VSTRIMLEFTMAX 0x7 /* ${var##pattern} */ +#define VSTRIMRIGHT 0x8 /* ${var%pattern} */ +#define VSTRIMRIGHTMAX 0x9 /* ${var%%pattern} */ +#define VSLENGTH 0xa /* ${#var} */ +#define VSERROR 0xb /* Syntax error, issue when expanded */ + + +/* + * NEOF is returned by parsecmd when it encounters an end of file. It + * must be distinct from NULL. + */ +#define NEOF ((union node *)-1) +extern int whichprompt; /* 1 == PS1, 2 == PS2 */ +extern const char *const parsekwd[]; + + +union node *parsecmd(int); +union node *parsewordexp(void); +void forcealias(void); +void fixredir(union node *, const char *, int); +int goodname(const char *); +int isassignment(const char *); +char *getprompt(void *); +const char *expandstr(const char *); diff --git a/shell_cmds/sh/redir.c b/shell_cmds/sh/redir.c new file mode 100644 index 0000000..8f415a6 --- /dev/null +++ b/shell_cmds/sh/redir.c @@ -0,0 +1,365 @@ +/*- + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)redir.c 8.2 (Berkeley) 5/4/95"; +#endif +#endif /* not lint */ +#include <sys/cdefs.h> +__FBSDID("$FreeBSD: head/bin/sh/redir.c 326025 2017-11-20 19:49:47Z pfg $"); + +#include <sys/types.h> +#include <sys/stat.h> +#include <signal.h> +#include <string.h> +#include <fcntl.h> +#include <errno.h> +#include <unistd.h> +#include <stdlib.h> + +/* + * Code for dealing with input/output redirection. + */ + +#include "shell.h" +#include "nodes.h" +#include "jobs.h" +#include "expand.h" +#include "redir.h" +#include "output.h" +#include "memalloc.h" +#include "error.h" +#include "options.h" + + +#define EMPTY -2 /* marks an unused slot in redirtab */ +#define CLOSED -1 /* fd was not open before redir */ + + +struct redirtab { + struct redirtab *next; + int renamed[10]; + int fd0_redirected; + unsigned int empty_redirs; +}; + + +static struct redirtab *redirlist; + +/* + * We keep track of whether or not fd0 has been redirected. This is for + * background commands, where we want to redirect fd0 to /dev/null only + * if it hasn't already been redirected. +*/ +static int fd0_redirected = 0; + +/* Number of redirtabs that have not been allocated. */ +static unsigned int empty_redirs = 0; + +static void openredirect(union node *, char[10 ]); +static int openhere(union node *); + + +/* + * Process a list of redirection commands. If the REDIR_PUSH flag is set, + * old file descriptors are stashed away so that the redirection can be + * undone by calling popredir. If the REDIR_BACKQ flag is set, then the + * standard output, and the standard error if it becomes a duplicate of + * stdout, is saved in memory. +* + * We suppress interrupts so that we won't leave open file + * descriptors around. Because the signal handler remains + * installed and we do not use system call restart, interrupts + * will still abort blocking opens such as fifos (they will fail + * with EINTR). There is, however, a race condition if an interrupt + * arrives after INTOFF and before open blocks. + */ + +void +redirect(union node *redir, int flags) +{ + union node *n; + struct redirtab *sv = NULL; + int i; + int fd; + char memory[10]; /* file descriptors to write to memory */ + + INTOFF; + for (i = 10 ; --i >= 0 ; ) + memory[i] = 0; + memory[1] = flags & REDIR_BACKQ; + if (flags & REDIR_PUSH) { + empty_redirs++; + if (redir != NULL) { + sv = ckmalloc(sizeof (struct redirtab)); + for (i = 0 ; i < 10 ; i++) + sv->renamed[i] = EMPTY; + sv->fd0_redirected = fd0_redirected; + sv->empty_redirs = empty_redirs - 1; + sv->next = redirlist; + redirlist = sv; + empty_redirs = 0; + } + } + for (n = redir ; n ; n = n->nfile.next) { + fd = n->nfile.fd; + if (fd == 0) + fd0_redirected = 1; + if ((n->nfile.type == NTOFD || n->nfile.type == NFROMFD) && + n->ndup.dupfd == fd) + continue; /* redirect from/to same file descriptor */ + + if ((flags & REDIR_PUSH) && sv->renamed[fd] == EMPTY) { + INTOFF; + if ((i = fcntl(fd, F_DUPFD_CLOEXEC, 10)) == -1) { + switch (errno) { + case EBADF: + i = CLOSED; + break; + default: + INTON; + error("%d: %s", fd, strerror(errno)); + break; + } + } + sv->renamed[fd] = i; + INTON; + } + openredirect(n, memory); + INTON; + INTOFF; + } + if (memory[1]) + out1 = &memout; + if (memory[2]) + out2 = &memout; + INTON; +} + + +static void +openredirect(union node *redir, char memory[10]) +{ + struct stat sb; + int fd = redir->nfile.fd; + const char *fname; + int f; + int e; + + memory[fd] = 0; + switch (redir->nfile.type) { + case NFROM: + fname = redir->nfile.expfname; + if ((f = open(fname, O_RDONLY)) < 0) + error("cannot open %s: %s", fname, strerror(errno)); + break; + case NFROMTO: + fname = redir->nfile.expfname; + if ((f = open(fname, O_RDWR|O_CREAT, 0666)) < 0) + error("cannot create %s: %s", fname, strerror(errno)); + break; + case NTO: + if (Cflag) { + fname = redir->nfile.expfname; + if (stat(fname, &sb) == -1) { + if ((f = open(fname, O_WRONLY|O_CREAT|O_EXCL, 0666)) < 0) + error("cannot create %s: %s", fname, strerror(errno)); + } else if (!S_ISREG(sb.st_mode)) { + if ((f = open(fname, O_WRONLY, 0666)) < 0) + error("cannot create %s: %s", fname, strerror(errno)); + if (fstat(f, &sb) != -1 && S_ISREG(sb.st_mode)) { + close(f); + error("cannot create %s: %s", fname, + strerror(EEXIST)); + } + } else + error("cannot create %s: %s", fname, + strerror(EEXIST)); + break; + } + /* FALLTHROUGH */ + case NCLOBBER: + fname = redir->nfile.expfname; + if ((f = open(fname, O_WRONLY|O_CREAT|O_TRUNC, 0666)) < 0) + error("cannot create %s: %s", fname, strerror(errno)); + break; + case NAPPEND: + fname = redir->nfile.expfname; + if ((f = open(fname, O_WRONLY|O_CREAT|O_APPEND, 0666)) < 0) + error("cannot create %s: %s", fname, strerror(errno)); + break; + case NTOFD: + case NFROMFD: + if (redir->ndup.dupfd >= 0) { /* if not ">&-" */ + if (memory[redir->ndup.dupfd]) + memory[fd] = 1; + else { + if (dup2(redir->ndup.dupfd, fd) < 0) + error("%d: %s", redir->ndup.dupfd, + strerror(errno)); + } + } else { + close(fd); + } + return; + case NHERE: + case NXHERE: + f = openhere(redir); + break; + default: + abort(); + } + if (f != fd) { + if (dup2(f, fd) == -1) { + e = errno; + close(f); + error("%d: %s", fd, strerror(e)); + } + close(f); + } +} + + +/* + * Handle here documents. Normally we fork off a process to write the + * data to a pipe. If the document is short, we can stuff the data in + * the pipe without forking. + */ + +static int +openhere(union node *redir) +{ + const char *p; + int pip[2]; + size_t len = 0; + int flags; + ssize_t written = 0; + + if (pipe(pip) < 0) + error("Pipe call failed: %s", strerror(errno)); + + if (redir->type == NXHERE) + p = redir->nhere.expdoc; + else + p = redir->nhere.doc->narg.text; + len = strlen(p); + if (len == 0) + goto out; + flags = fcntl(pip[1], F_GETFL, 0); + if (flags != -1 && fcntl(pip[1], F_SETFL, flags | O_NONBLOCK) != -1) { + written = write(pip[1], p, len); + if (written < 0) + written = 0; + if ((size_t)written == len) + goto out; + fcntl(pip[1], F_SETFL, flags); + } + + if (forkshell((struct job *)NULL, (union node *)NULL, FORK_NOJOB) == 0) { + close(pip[0]); + signal(SIGINT, SIG_IGN); + signal(SIGQUIT, SIG_IGN); + signal(SIGHUP, SIG_IGN); + signal(SIGTSTP, SIG_IGN); + signal(SIGPIPE, SIG_DFL); + xwrite(pip[1], p + written, len - written); + _exit(0); + } +out: + close(pip[1]); + return pip[0]; +} + + + +/* + * Undo the effects of the last redirection. + */ + +void +popredir(void) +{ + struct redirtab *rp = redirlist; + int i; + + INTOFF; + if (empty_redirs > 0) { + empty_redirs--; + INTON; + return; + } + for (i = 0 ; i < 10 ; i++) { + if (rp->renamed[i] != EMPTY) { + if (rp->renamed[i] >= 0) { + dup2(rp->renamed[i], i); + close(rp->renamed[i]); + } else { + close(i); + } + } + } + fd0_redirected = rp->fd0_redirected; + empty_redirs = rp->empty_redirs; + redirlist = rp->next; + ckfree(rp); + INTON; +} + +/* Return true if fd 0 has already been redirected at least once. */ +int +fd0_redirected_p(void) +{ + return fd0_redirected != 0; +} + +/* + * Discard all saved file descriptors. + */ + +void +clearredir(void) +{ + struct redirtab *rp; + int i; + + for (rp = redirlist ; rp ; rp = rp->next) { + for (i = 0 ; i < 10 ; i++) { + if (rp->renamed[i] >= 0) { + close(rp->renamed[i]); + } + rp->renamed[i] = EMPTY; + } + } +} diff --git a/shell_cmds/sh/redir.h b/shell_cmds/sh/redir.h new file mode 100644 index 0000000..74d17b1 --- /dev/null +++ b/shell_cmds/sh/redir.h @@ -0,0 +1,47 @@ +/*- + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)redir.h 8.2 (Berkeley) 5/4/95 + * $FreeBSD: head/bin/sh/redir.h 326025 2017-11-20 19:49:47Z pfg $ + */ + +/* flags passed to redirect */ +#define REDIR_PUSH 01 /* save previous values of file descriptors */ +#define REDIR_BACKQ 02 /* save the command output in memory */ + +union node; +void redirect(union node *, int); +void popredir(void); +int fd0_redirected_p(void); +void clearredir(void); + diff --git a/shell_cmds/sh/sh.1 b/shell_cmds/sh/sh.1 new file mode 100644 index 0000000..5c2e87d --- /dev/null +++ b/shell_cmds/sh/sh.1 @@ -0,0 +1,2879 @@ +.\"- +.\" Copyright (c) 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" Kenneth Almquist. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" from: @(#)sh.1 8.6 (Berkeley) 5/4/95 +.\" $FreeBSD: head/bin/sh/sh.1 327121 2017-12-23 22:58:19Z jilles $ +.\" +.Dd October 8, 2016 +.Dt SH 1 +.Os +.Sh NAME +.Nm sh +.Nd command interpreter (shell) +.Sh SYNOPSIS +.Nm +.Op Fl /+abCEefhIimnPpTuVvx +.Op Fl /+o Ar longname +.Oo +.Ar script +.Op Ar arg ... +.Oc +.Nm +.Op Fl /+abCEefhIimnPpTuVvx +.Op Fl /+o Ar longname +.Fl c Ar string +.Oo +.Ar name +.Op Ar arg ... +.Oc +.Nm +.Op Fl /+abCEefhIimnPpTuVvx +.Op Fl /+o Ar longname +.Fl s +.Op Ar arg ... +.Sh DESCRIPTION +The +.Nm +utility is the standard command interpreter for the system. +The current version of +.Nm +is close to the +.St -p1003.1 +specification for the shell. +It only supports features +designated by +.Tn POSIX , +plus a few Berkeley extensions. +This man page is not intended to be a tutorial nor a complete +specification of the shell. +.Ss Overview +The shell is a command that reads lines from +either a file or the terminal, interprets them, and +generally executes other commands. +It is the program that is started when a user logs into the system, +although a user can select a different shell with the +.Xr chsh 1 +command. +The shell +implements a language that has flow control constructs, +a macro facility that provides a variety of features in +addition to data storage, along with built-in history and line +editing capabilities. +It incorporates many features to +aid interactive use and has the advantage that the interpretative +language is common to both interactive and non-interactive +use (shell scripts). +That is, commands can be typed directly +to the running shell or can be put into a file, +which can be executed directly by the shell. +.Ss Invocation +.\" +.\" XXX This next sentence is incredibly confusing. +.\" +If no arguments are present and if the standard input of the shell +is connected to a terminal +(or if the +.Fl i +option is set), +the shell is considered an interactive shell. +An interactive shell +generally prompts before each command and handles programming +and command errors differently (as described below). +When first starting, the shell inspects argument 0, and +if it begins with a dash +.Pq Ql - , +the shell is also considered a login shell. +This is normally done automatically by the system +when the user first logs in. +A login shell first reads commands +from the files +.Pa /etc/profile +and then +.Pa .profile +in a user's home directory, +if they exist. +If the environment variable +.Ev ENV +is set on entry to a shell, or is set in the +.Pa .profile +of a login shell, the shell then subjects its value to parameter expansion +and arithmetic expansion and reads commands from the named file. +Therefore, a user should place commands that are to be executed only +at login time in the +.Pa .profile +file, and commands that are executed for every shell inside the +.Ev ENV +file. +The user can set the +.Ev ENV +variable to some file by placing the following line in the file +.Pa .profile +in the home directory, +substituting for +.Pa .shrc +the filename desired: +.Pp +.Dl "ENV=$HOME/.shrc; export ENV" +.Pp +The first non-option argument specified on the command line +will be treated as the +name of a file from which to read commands (a shell script), and +the remaining arguments are set as the positional parameters +of the shell +.Li ( $1 , $2 , +etc.). +Otherwise, the shell reads commands +from its standard input. +.Pp +Unlike older versions of +.Nm +the +.Ev ENV +script is only sourced on invocation of interactive shells. +This +closes a well-known, and sometimes easily exploitable security +hole related to poorly thought out +.Ev ENV +scripts. +.Ss Argument List Processing +All of the single letter options to +.Nm +have a corresponding long name, +with the exception of +.Fl c +and +.Fl /+o . +These long names are provided next to the single letter options +in the descriptions below. +The long name for an option may be specified as an argument to the +.Fl /+o +option of +.Nm . +Once the shell is running, +the long name for an option may be specified as an argument to the +.Fl /+o +option of the +.Ic set +built-in command +(described later in the section called +.Sx Built-in Commands ) . +Introducing an option with a dash +.Pq Ql - +enables the option, +while using a plus +.Pq Ql + +disables the option. +A +.Dq Li -- +or plain +.Ql - +will stop option processing and will force the remaining +words on the command line to be treated as arguments. +The +.Fl /+o +and +.Fl c +options do not have long names. +They take arguments and are described after the single letter options. +.Bl -tag -width indent +.It Fl a Li allexport +Flag variables for export when assignments are made to them. +.It Fl b Li notify +Enable asynchronous notification of background job +completion. +(UNIMPLEMENTED) +.It Fl C Li noclobber +Do not overwrite existing files with +.Ql > . +.It Fl E Li emacs +Enable the built-in +.Xr emacs 1 +command line editor (disables the +.Fl V +option if it has been set; +set automatically when interactive on terminals). +.It Fl e Li errexit +Exit immediately if any untested command fails in non-interactive mode. +The exit status of a command is considered to be +explicitly tested if the command is part of the list used to control +an +.Ic if , elif , while , +or +.Ic until ; +if the command is the left +hand operand of an +.Dq Li && +or +.Dq Li || +operator; or if the command is a pipeline preceded by the +.Ic !\& +keyword. +If a shell function is executed and its exit status is explicitly +tested, all commands of the function are considered to be tested as +well. +.Pp +It is recommended to check for failures explicitly +instead of relying on +.Fl e +because it tends to behave in unexpected ways, +particularly in larger scripts. +.It Fl f Li noglob +Disable pathname expansion. +.It Fl h Li trackall +A do-nothing option for +.Tn POSIX +compliance. +.It Fl I Li ignoreeof +Ignore +.Dv EOF Ap s +from input when in interactive mode. +.It Fl i Li interactive +Force the shell to behave interactively. +.It Fl m Li monitor +Turn on job control (set automatically when interactive). +A new process group is created for each pipeline (called a job). +It is possible to suspend jobs or to have them run in the foreground or +in the background. +In a non-interactive shell, +this option can be set even if no terminal is available +and is useful to place processes in separate process groups. +.It Fl n Li noexec +If not interactive, read commands but do not +execute them. +This is useful for checking the +syntax of shell scripts. +.It Fl P Li physical +Change the default for the +.Ic cd +and +.Ic pwd +commands from +.Fl L +(logical directory layout) +to +.Fl P +(physical directory layout). +.It Fl p Li privileged +Turn on privileged mode. +This mode is enabled on startup +if either the effective user or group ID is not equal to the +real user or group ID. +Turning this mode off sets the +effective user and group IDs to the real user and group IDs. +When this mode is enabled for interactive shells, the file +.Pa /etc/suid_profile +is sourced instead of +.Pa ~/.profile +after +.Pa /etc/profile +is sourced, and the contents of the +.Ev ENV +variable are ignored. +.It Fl s Li stdin +Read commands from standard input (set automatically +if no file arguments are present). +This option has +no effect when set after the shell has already started +running (i.e., when set with the +.Ic set +command). +.It Fl T Li trapsasync +When waiting for a child, execute traps immediately. +If this option is not set, +traps are executed after the child exits, +as specified in +.St -p1003.2 . +This nonstandard option is useful for putting guarding shells around +children that block signals. +The surrounding shell may kill the child +or it may just return control to the tty and leave the child alone, +like this: +.Bd -literal -offset indent +sh -T -c "trap 'exit 1' 2 ; some-blocking-program" +.Ed +.It Fl u Li nounset +Write a message to standard error when attempting +to expand a variable, a positional parameter or +the special parameter +.Va \&! +that is not set, and if the +shell is not interactive, exit immediately. +.It Fl V Li vi +Enable the built-in +.Xr vi 1 +command line editor (disables +.Fl E +if it has been set). +.It Fl v Li verbose +The shell writes its input to standard error +as it is read. +Useful for debugging. +.It Fl x Li xtrace +Write each command +(preceded by the value of the +.Va PS4 +variable subjected to parameter expansion and arithmetic expansion) +to standard error before it is executed. +Useful for debugging. +.It Li nolog +Another do-nothing option for +.Tn POSIX +compliance. +It only has a long name. +.El +.Pp +The +.Fl c +option causes the commands to be read from the +.Ar string +operand instead of from the standard input. +Keep in mind that this option only accepts a single string as its +argument, hence multi-word strings must be quoted. +.Pp +The +.Fl /+o +option takes as its only argument the long name of an option +to be enabled or disabled. +For example, the following two invocations of +.Nm +both enable the built-in +.Xr emacs 1 +command line editor: +.Bd -literal -offset indent +set -E +set -o emacs +.Ed +.Pp +If used without an argument, the +.Fl o +option displays the current option settings in a human-readable format. +If +.Cm +o +is used without an argument, the current option settings are output +in a format suitable for re-input into the shell. +.Ss Lexical Structure +The shell reads input in terms of lines from a file and breaks +it up into words at whitespace (blanks and tabs), and at +certain sequences of +characters called +.Dq operators , +which are special to the shell. +There are two types of operators: control operators and +redirection operators (their meaning is discussed later). +The following is a list of valid operators: +.Bl -tag -width indent +.It Control operators: +.Bl -column "XXX" "XXX" "XXX" "XXX" "XXX" -offset center -compact +.It Li & Ta Li && Ta Li \&( Ta Li \&) Ta Li \en +.It Li ;; Ta Li ;& Ta Li \&; Ta Li \&| Ta Li || +.El +.It Redirection operators: +.Bl -column "XXX" "XXX" "XXX" "XXX" "XXX" -offset center -compact +.It Li < Ta Li > Ta Li << Ta Li >> Ta Li <> +.It Li <& Ta Li >& Ta Li <<- Ta Li >| Ta \& +.El +.El +.Pp +The character +.Ql # +introduces a comment if used at the beginning of a word. +The word starting with +.Ql # +and the rest of the line are ignored. +.Pp +.Tn ASCII +.Dv NUL +characters (character code 0) are not allowed in shell input. +.Ss Quoting +Quoting is used to remove the special meaning of certain characters +or words to the shell, such as operators, whitespace, keywords, +or alias names. +.Pp +There are four types of quoting: matched single quotes, +dollar-single quotes, +matched double quotes, and backslash. +.Bl -tag -width indent +.It Single Quotes +Enclosing characters in single quotes preserves the literal +meaning of all the characters (except single quotes, making +it impossible to put single-quotes in a single-quoted string). +.It Dollar-Single Quotes +Enclosing characters between +.Li $' +and +.Li ' +preserves the literal meaning of all characters +except backslashes and single quotes. +A backslash introduces a C-style escape sequence: +.Bl -tag -width xUnnnnnnnn +.It \ea +Alert (ring the terminal bell) +.It \eb +Backspace +.It \ec Ns Ar c +The control character denoted by +.Li ^ Ns Ar c +in +.Xr stty 1 . +If +.Ar c +is a backslash, it must be doubled. +.It \ee +The ESC character +.Tn ( ASCII +0x1b) +.It \ef +Formfeed +.It \en +Newline +.It \er +Carriage return +.It \et +Horizontal tab +.It \ev +Vertical tab +.It \e\e +Literal backslash +.It \e\&' +Literal single-quote +.It \e\&" +Literal double-quote +.It \e Ns Ar nnn +The byte whose octal value is +.Ar nnn +(one to three digits) +.It \ex Ns Ar nn +The byte whose hexadecimal value is +.Ar nn +(one or more digits only the last two of which are used) +.It \eu Ns Ar nnnn +The Unicode code point +.Ar nnnn +(four hexadecimal digits) +.It \eU Ns Ar nnnnnnnn +The Unicode code point +.Ar nnnnnnnn +(eight hexadecimal digits) +.El +.Pp +The sequences for Unicode code points are currently only useful with +UTF-8 locales. +They reject code point 0 and UTF-16 surrogates. +.Pp +If an escape sequence would produce a byte with value 0, +that byte and the rest of the string until the matching single-quote +are ignored. +.Pp +Any other string starting with a backslash is an error. +.It Double Quotes +Enclosing characters within double quotes preserves the literal +meaning of all characters except dollar sign +.Pq Ql $ , +backquote +.Pq Ql ` , +and backslash +.Pq Ql \e . +The backslash inside double quotes is historically weird. +It remains literal unless it precedes the following characters, +which it serves to quote: +.Pp +.Bl -column "XXX" "XXX" "XXX" "XXX" "XXX" -offset center -compact +.It Li $ Ta Li ` Ta Li \&" Ta Li \e Ta Li \en +.El +.It Backslash +A backslash preserves the literal meaning of the following +character, with the exception of the newline character +.Pq Ql \en . +A backslash preceding a newline is treated as a line continuation. +.El +.Ss Keywords +Keywords or reserved words are words that have special meaning to the +shell and are recognized at the beginning of a line and +after a control operator. +The following are keywords: +.Bl -column "doneXX" "elifXX" "elseXX" "untilXX" "whileX" -offset center +.It Li \&! Ta { Ta } Ta Ic case Ta Ic do +.It Ic done Ta Ic elif Ta Ic else Ta Ic esac Ta Ic fi +.It Ic for Ta Ic if Ta Ic then Ta Ic until Ta Ic while +.El +.Ss Aliases +An alias is a name and corresponding value set using the +.Ic alias +built-in command. +Wherever the command word of a simple command may occur, +and after checking for keywords if a keyword may occur, the shell +checks the word to see if it matches an alias. +If it does, it replaces it in the input stream with its value. +For example, if there is an alias called +.Dq Li lf +with the value +.Dq Li "ls -F" , +then the input +.Pp +.Dl "lf foobar" +.Pp +would become +.Pp +.Dl "ls -F foobar" +.Pp +Aliases are also recognized after an alias +whose value ends with a space or tab. +For example, if there is also an alias called +.Dq Li nohup +with the value +.Dq Li "nohup " , +then the input +.Pp +.Dl "nohup lf foobar" +.Pp +would become +.Pp +.Dl "nohup ls -F foobar" +.Pp +Aliases provide a convenient way for naive users to +create shorthands for commands without having to learn how +to create functions with arguments. +Using aliases in scripts is discouraged +because the command that defines them must be executed +before the code that uses them is parsed. +This is fragile and not portable. +.Pp +An alias name may be escaped in a command line, so that it is not +replaced by its alias value, by using quoting characters within or +adjacent to the alias name. +This is most often done by prefixing +an alias name with a backslash to execute a function, built-in, or +normal program with the same name. +See the +.Sx Quoting +subsection. +.Ss Commands +The shell interprets the words it reads according to a +language, the specification of which is outside the scope +of this man page (refer to the BNF in the +.St -p1003.2 +document). +Essentially though, a line is read and if +the first word of the line (or after a control operator) +is not a keyword, then the shell has recognized a +simple command. +Otherwise, a complex command or some +other special construct may have been recognized. +.Ss Simple Commands +If a simple command has been recognized, the shell performs +the following actions: +.Bl -enum +.It +Leading words of the form +.Dq Li name=value +are stripped off and assigned to the environment of +the simple command +(they do not affect expansions). +Redirection operators and +their arguments (as described below) are stripped +off and saved for processing. +.It +The remaining words are expanded as described in +the section called +.Sx Word Expansions , +and the first remaining word is considered the command +name and the command is located. +The remaining +words are considered the arguments of the command. +If no command name resulted, then the +.Dq Li name=value +variable assignments recognized in 1) affect the +current shell. +.It +Redirections are performed as described in +the next section. +.El +.Ss Redirections +Redirections are used to change where a command reads its input +or sends its output. +In general, redirections open, close, or +duplicate an existing reference to a file. +The overall format +used for redirection is: +.Pp +.D1 Oo Ar n Oc Ar redir-op file +.Pp +The +.Ar redir-op +is one of the redirection operators mentioned +previously. +The following gives some examples of how these +operators can be used. +Note that stdin and stdout are commonly used abbreviations +for standard input and standard output respectively. +.Bl -tag -width "1234567890XX" -offset indent +.It Oo Ar n Oc Ns Li > Ar file +redirect stdout (or file descriptor +.Ar n ) +to +.Ar file +.It Oo Ar n Oc Ns Li >| Ar file +same as above, but override the +.Fl C +option +.It Oo Ar n Oc Ns Li >> Ar file +append stdout (or file descriptor +.Ar n ) +to +.Ar file +.It Oo Ar n Oc Ns Li < Ar file +redirect stdin (or file descriptor +.Ar n ) +from +.Ar file +.It Oo Ar n Oc Ns Li <> Ar file +redirect stdin (or file descriptor +.Ar n ) +to and from +.Ar file +.It Oo Ar n1 Oc Ns Li <& Ns Ar n2 +duplicate stdin (or file descriptor +.Ar n1 ) +from file descriptor +.Ar n2 +.It Oo Ar n Oc Ns Li <&- +close stdin (or file descriptor +.Ar n ) +.It Oo Ar n1 Oc Ns Li >& Ns Ar n2 +duplicate stdout (or file descriptor +.Ar n1 ) +to file descriptor +.Ar n2 +.It Oo Ar n Oc Ns Li >&- +close stdout (or file descriptor +.Ar n ) +.El +.Pp +The following redirection is often called a +.Dq here-document . +.Bd -unfilled -offset indent +.Oo Ar n Oc Ns Li << Ar delimiter +.Ar here-doc-text +.Ar ... +.Ar delimiter +.Ed +.Pp +All the text on successive lines up to the delimiter is +saved away and made available to the command on standard +input, or file descriptor +.Ar n +if it is specified. +If the +.Ar delimiter +as specified on the initial line is quoted, then the +.Ar here-doc-text +is treated literally, otherwise the text is subjected to +parameter expansion, command substitution, and arithmetic +expansion (as described in the section on +.Sx Word Expansions ) . +If the operator is +.Dq Li <<- +instead of +.Dq Li << , +then leading tabs +in the +.Ar here-doc-text +are stripped. +.Ss Search and Execution +There are three types of commands: shell functions, +built-in commands, and normal programs. +The command is searched for (by name) in that order. +The three types of commands are all executed in a different way. +.Pp +When a shell function is executed, all of the shell positional +parameters (except +.Li $0 , +which remains unchanged) are +set to the arguments of the shell function. +The variables which are explicitly placed in the environment of +the command (by placing assignments to them before the +function name) are made local to the function and are set +to the values given. +Then the command given in the function definition is executed. +The positional parameters are restored to their original values +when the command completes. +This all occurs within the current shell. +.Pp +Shell built-in commands are executed internally to the shell, without +spawning a new process. +There are two kinds of built-in commands: regular and special. +Assignments before special builtins persist after they finish +executing and assignment errors, redirection errors and certain +operand errors cause a script to be aborted. +Special builtins cannot be overridden with a function. +Both regular and special builtins can affect the shell in ways +normal programs cannot. +.Pp +Otherwise, if the command name does not match a function +or built-in command, the command is searched for as a normal +program in the file system (as described in the next section). +When a normal program is executed, the shell runs the program, +passing the arguments and the environment to the program. +If the program is not a normal executable file +(i.e., if it does not begin with the +.Dq "magic number" +whose +.Tn ASCII +representation is +.Dq Li #! , +resulting in an +.Er ENOEXEC +return value from +.Xr execve 2 ) +but appears to be a text file, +the shell will run a new instance of +.Nm +to interpret it. +.Pp +Note that previous versions of this document +and the source code itself misleadingly and sporadically +refer to a shell script without a magic number +as a +.Dq "shell procedure" . +.Ss Path Search +When locating a command, the shell first looks to see if +it has a shell function by that name. +Then it looks for a +built-in command by that name. +If a built-in command is not found, +one of two things happen: +.Bl -enum +.It +Command names containing a slash are simply executed without +performing any searches. +.It +The shell searches each entry in the +.Va PATH +variable +in turn for the command. +The value of the +.Va PATH +variable should be a series of +entries separated by colons. +Each entry consists of a +directory name. +The current directory +may be indicated implicitly by an empty directory name, +or explicitly by a single period. +.El +.Ss Command Exit Status +Each command has an exit status that can influence the behavior +of other shell commands. +The paradigm is that a command exits +with zero for normal or success, and non-zero for failure, +error, or a false indication. +The man page for each command +should indicate the various exit codes and what they mean. +Additionally, the built-in commands return exit codes, as does +an executed shell function. +.Pp +If a command is terminated by a signal, its exit status is greater than 128. +The signal name can be found by passing the exit status to +.Li kill -l . +.Pp +If there is no command word, +the exit status is the exit status of the last command substitution executed, +or zero if the command does not contain any command substitutions. +.Ss Complex Commands +Complex commands are combinations of simple commands +with control operators or keywords, together creating a larger complex +command. +More generally, a command is one of the following: +.Bl -item -offset indent +.It +simple command +.It +pipeline +.It +list or compound-list +.It +compound command +.It +function definition +.El +.Pp +Unless otherwise stated, the exit status of a command is +that of the last simple command executed by the command, +or zero if no simple command was executed. +.Ss Pipelines +A pipeline is a sequence of one or more commands separated +by the control operator +.Ql \&| . +The standard output of all but +the last command is connected to the standard input +of the next command. +The standard output of the last +command is inherited from the shell, as usual. +.Pp +The format for a pipeline is: +.Pp +.D1 Oo Li \&! Oc Ar command1 Op Li \&| Ar command2 ... +.Pp +The standard output of +.Ar command1 +is connected to the standard input of +.Ar command2 . +The standard input, standard output, or +both of a command is considered to be assigned by the +pipeline before any redirection specified by redirection +operators that are part of the command. +.Pp +Note that unlike some other shells, +.Nm +executes each process in a pipeline with more than one command +in a subshell environment and as a child of the +.Nm +process. +.Pp +If the pipeline is not in the background (discussed later), +the shell waits for all commands to complete. +.Pp +If the keyword +.Ic !\& +does not precede the pipeline, the +exit status is the exit status of the last command specified +in the pipeline. +Otherwise, the exit status is the logical +NOT of the exit status of the last command. +That is, if +the last command returns zero, the exit status is 1; if +the last command returns greater than zero, the exit status +is zero. +.Pp +Because pipeline assignment of standard input or standard +output or both takes place before redirection, it can be +modified by redirection. +For example: +.Pp +.Dl "command1 2>&1 | command2" +.Pp +sends both the standard output and standard error of +.Ar command1 +to the standard input of +.Ar command2 . +.Pp +A +.Ql \&; +or newline terminator causes the preceding +AND-OR-list +(described below in the section called +.Sx Short-Circuit List Operators ) +to be executed sequentially; +an +.Ql & +causes asynchronous execution of the preceding AND-OR-list. +.Ss Background Commands (&) +If a command is terminated by the control operator ampersand +.Pq Ql & , +the shell executes the command in a subshell environment (see +.Sx Grouping Commands Together +below) and asynchronously; +the shell does not wait for the command to finish +before executing the next command. +.Pp +The format for running a command in background is: +.Pp +.D1 Ar command1 Li & Op Ar command2 Li & Ar ... +.Pp +If the shell is not interactive, the standard input of an +asynchronous command is set to +.Pa /dev/null . +.Pp +The exit status is zero. +.Ss Lists (Generally Speaking) +A list is a sequence of zero or more commands separated by +newlines, semicolons, or ampersands, +and optionally terminated by one of these three characters. +The commands in a +list are executed in the order they are written. +If command is followed by an ampersand, the shell starts the +command and immediately proceeds onto the next command; +otherwise it waits for the command to terminate before +proceeding to the next one. +.Ss Short-Circuit List Operators +.Dq Li && +and +.Dq Li || +are AND-OR list operators. +.Dq Li && +executes the first command, and then executes the second command +if the exit status of the first command is zero. +.Dq Li || +is similar, but executes the second command if the exit +status of the first command is nonzero. +.Dq Li && +and +.Dq Li || +both have the same priority. +.Ss Flow-Control Constructs (if, while, for, case) +The syntax of the +.Ic if +command is: +.Bd -unfilled -offset indent -compact +.Ic if Ar list +.Ic then Ar list +.Oo Ic elif Ar list +.Ic then Ar list Oc Ar ... +.Op Ic else Ar list +.Ic fi +.Ed +.Pp +The exit status is that of selected +.Ic then +or +.Ic else +list, +or zero if no list was selected. +.Pp +The syntax of the +.Ic while +command is: +.Bd -unfilled -offset indent -compact +.Ic while Ar list +.Ic do Ar list +.Ic done +.Ed +.Pp +The two lists are executed repeatedly while the exit status of the +first list is zero. +The +.Ic until +command is similar, but has the word +.Ic until +in place of +.Ic while , +which causes it to +repeat until the exit status of the first list is zero. +.Pp +The exit status is that of the last execution of the second list, +or zero if it was never executed. +.Pp +The syntax of the +.Ic for +command is: +.Bd -unfilled -offset indent -compact +.Ic for Ar variable Op Ic in Ar word ... +.Ic do Ar list +.Ic done +.Ed +.Pp +If +.Ic in +and the following words are omitted, +.Ic in Li \&"$@\&" +is used instead. +The words are expanded, and then the list is executed +repeatedly with the variable set to each word in turn. +The +.Ic do +and +.Ic done +commands may be replaced with +.Ql { +and +.Ql } . +.Pp +The syntax of the +.Ic break +and +.Ic continue +commands is: +.D1 Ic break Op Ar num +.D1 Ic continue Op Ar num +.Pp +The +.Ic break +command terminates the +.Ar num +innermost +.Ic for +or +.Ic while +loops. +The +.Ic continue +command continues with the next iteration of the innermost loop. +These are implemented as special built-in commands. +.Pp +The syntax of the +.Ic case +command is: +.Bd -unfilled -offset indent -compact +.Ic case Ar word Ic in +.Ar pattern ) Ar list Li ;; +.Ar ... +.Ic esac +.Ed +.Pp +The pattern can actually be one or more patterns +(see +.Sx Shell Patterns +described later), +separated by +.Ql \&| +characters. +Tilde expansion, parameter expansion, command substitution, +arithmetic expansion and quote removal are applied to the word. +Then, each pattern is expanded in turn using tilde expansion, +parameter expansion, command substitution and arithmetic expansion and +the expanded form of the word is checked against it. +If a match is found, the corresponding list is executed. +If the selected list is terminated by the control operator +.Ql ;& +instead of +.Ql ;; , +execution continues with the next list, +continuing until a list terminated with +.Ql ;; +or the end of the +.Ic case +command. +.Ss Grouping Commands Together +Commands may be grouped by writing either +.Pp +.D1 Li \&( Ns Ar list Ns Li \%) +.Pp +or +.Pp +.D1 Li { Ar list Ns Li \&; } +.Pp +The first form executes the commands in a subshell environment. +A subshell environment has its own copy of: +.Bl -enum +.It +The current working directory as set by +.Ic cd . +.It +The file creation mask as set by +.Ic umask . +.It +Resource limits as set by +.Ic ulimit . +.It +References to open files. +.It +Traps as set by +.Ic trap . +.It +Known jobs. +.It +Positional parameters and variables. +.It +Shell options. +.It +Shell functions. +.It +Shell aliases. +.El +.Pp +These are copied from the parent shell environment, +except that trapped (but not ignored) signals are reset to the default action +and known jobs are cleared. +Any changes do not affect the parent shell environment. +.Pp +A subshell environment may be implemented as a child process or differently. +If job control is enabled in an interactive shell, +commands grouped in parentheses can be suspended and continued as a unit. +.Pp +For compatibility with other shells, +two open parentheses in sequence should be separated by whitespace. +.Pp +The second form never forks another shell, +so it is slightly more efficient. +Grouping commands together this way allows the user to +redirect their output as though they were one program: +.Bd -literal -offset indent +{ echo -n "hello"; echo " world"; } > greeting +.Ed +.Ss Functions +The syntax of a function definition is +.Pp +.D1 Ar name Li \&( \&) Ar command +.Pp +A function definition is an executable statement; when +executed it installs a function named +.Ar name +and returns an +exit status of zero. +The +.Ar command +is normally a list +enclosed between +.Ql { +and +.Ql } . +.Pp +Variables may be declared to be local to a function by +using the +.Ic local +command. +This should appear as the first statement of a function, +and the syntax is: +.Pp +.D1 Ic local Oo Ar variable ... Oc Op Fl +.Pp +The +.Ic local +command is implemented as a built-in command. +The exit status is zero +unless the command is not in a function or a variable name is invalid. +.Pp +When a variable is made local, it inherits the initial +value and exported and readonly flags from the variable +with the same name in the surrounding scope, if there is +one. +Otherwise, the variable is initially unset. +The shell +uses dynamic scoping, so that if the variable +.Va x +is made local to function +.Em f , +which then calls function +.Em g , +references to the variable +.Va x +made inside +.Em g +will refer to the variable +.Va x +declared inside +.Em f , +not to the global variable named +.Va x . +.Pp +The only special parameter that can be made local is +.Ql - . +Making +.Ql - +local causes any shell options +(including those that only have long names) +that are +changed via the +.Ic set +command inside the function to be +restored to their original values when the function +returns. +.Pp +The syntax of the +.Ic return +command is +.Pp +.D1 Ic return Op Ar exitstatus +.Pp +It terminates the current executional scope, returning from the closest +nested function or sourced script; +if no function or sourced script is being executed, +it exits the shell instance. +The +.Ic return +command is implemented as a special built-in command. +.Ss Variables and Parameters +The shell maintains a set of parameters. +A parameter +denoted by a name +(consisting solely +of alphabetics, numerics, and underscores, +and starting with an alphabetic or an underscore) +is called a variable. +When starting up, +the shell turns all environment variables with valid names into shell +variables. +New variables can be set using the form +.Pp +.D1 Ar name Ns = Ns Ar value +.Pp +A parameter can also be denoted by a number +or a special character as explained below. +.Pp +Assignments are expanded differently from other words: +tilde expansion is also performed after the equals sign and after any colon +and usernames are also terminated by colons, +and field splitting and pathname expansion are not performed. +.Pp +This special expansion applies not only to assignments that form a simple +command by themselves or precede a command word, +but also to words passed to the +.Ic export , +.Ic local +or +.Ic readonly +built-in commands that have this form. +For this, the builtin's name must be literal +(not the result of an expansion) +and may optionally be preceded by one or more literal instances of +.Ic command +without options. +.Ss Positional Parameters +A positional parameter is a parameter denoted by a number greater than zero. +The shell sets these initially to the values of its command line +arguments that follow the name of the shell script. +The +.Ic set +built-in command can also be used to set or reset them. +.Ss Special Parameters +Special parameters are parameters denoted by a single special character +or the digit zero. +They are shown in the following list, exactly as they would appear in input +typed by the user or in the source of a shell script. +.Bl -hang +.It Li $* +Expands to the positional parameters, starting from one. +When +the expansion occurs within a double-quoted string +it expands to a single field with the value of each parameter +separated by the first character of the +.Va IFS +variable, +or by a space if +.Va IFS +is unset. +.It Li $@ +Expands to the positional parameters, starting from one. +When +the expansion occurs within double-quotes, each positional +parameter expands as a separate argument. +If there are no positional parameters, the +expansion of +.Li @ +generates zero arguments, even when +.Li @ +is double-quoted. +What this basically means, for example, is +if +.Li $1 +is +.Dq Li abc +and +.Li $2 +is +.Dq Li "def ghi" , +then +.Li \&"$@\&" +expands to +the two arguments: +.Bd -literal -offset indent +"abc" "def ghi" +.Ed +.It Li $# +Expands to the number of positional parameters. +.It Li $? +Expands to the exit status of the most recent pipeline. +.It Li $- +(hyphen) Expands to the current option flags (the single-letter +option names concatenated into a string) as specified on +invocation, by the +.Ic set +built-in command, or implicitly +by the shell. +.It Li $$ +Expands to the process ID of the invoked shell. +A subshell +retains the same value of +.Va $ +as its parent. +.It Li $! +Expands to the process ID of the most recent background +command executed from the current shell. +For a +pipeline, the process ID is that of the last command in the +pipeline. +If this parameter is referenced, the shell will remember +the process ID and its exit status until the +.Ic wait +built-in command reports completion of the process. +.It Li $0 +(zero) Expands to the name of the shell script if passed on the command line, +the +.Ar name +operand if given (with +.Fl c ) +or otherwise argument 0 passed to the shell. +.El +.Ss Special Variables +The following variables are set by the shell or +have special meaning to it: +.Bl -tag -width ".Va HISTSIZE" +.It Va CDPATH +The search path used with the +.Ic cd +built-in. +.It Va EDITOR +The fallback editor used with the +.Ic fc +built-in. +If not set, the default editor is +.Xr ed 1 . +.It Va FCEDIT +The default editor used with the +.Ic fc +built-in. +.It Va HISTSIZE +The number of previous commands that are accessible. +.It Va HOME +The user's home directory, +used in tilde expansion and as a default directory for the +.Ic cd +built-in. +.It Va IFS +Input Field Separators. +This is initialized at startup to +.Aq space , +.Aq tab , +and +.Aq newline +in that order. +This value also applies if +.Va IFS +is unset, but not if it is set to the empty string. +See the +.Sx White Space Splitting +section for more details. +.It Va LINENO +The current line number in the script or function. +.It Va MAIL +The name of a mail file, that will be checked for the arrival of new +mail. +Overridden by +.Va MAILPATH . +.It Va MAILPATH +A colon +.Pq Ql \&: +separated list of file names, for the shell to check for incoming +mail. +This variable overrides the +.Va MAIL +setting. +There is a maximum of 10 mailboxes that can be monitored at once. +.It Va OPTIND +The index of the next argument to be processed by +.Ic getopts . +This is initialized to 1 at startup. +.It Va PATH +The default search path for executables. +See the +.Sx Path Search +section for details. +.It Va PPID +The parent process ID of the invoked shell. +This is set at startup +unless this variable is in the environment. +A later change of parent process ID is not reflected. +A subshell retains the same value of +.Va PPID . +.It Va PS1 +The primary prompt string, which defaults to +.Dq Li "$ " , +unless you are the superuser, in which case it defaults to +.Dq Li "# " . +.Va PS1 +may include any of the following formatting sequences, +which are replaced by the given information: +.Bl -tag -width indent +.It Li \eH +This system's fully-qualified hostname (FQDN). +.It Li \eh +This system's hostname. +.It Li \eW +The final component of the current working directory. +.It Li \ew +The entire path of the current working directory. +.It Li \e$ +Superuser status. +.Dq Li "$ " +for normal users and +.Dq Li "# " +for superusers. +.It Li \e\e +A literal backslash. +.El +.It Va PS2 +The secondary prompt string, which defaults to +.Dq Li "> " . +.Va PS2 +may include any of the formatting sequences from +.Va PS1 . +.It Va PS4 +The prefix for the trace output (if +.Fl x +is active). +The default is +.Dq Li "+ " . +.El +.Ss Word Expansions +This clause describes the various expansions that are +performed on words. +Not all expansions are performed on +every word, as explained later. +.Pp +Tilde expansions, parameter expansions, command substitutions, +arithmetic expansions, and quote removals that occur within +a single word expand to a single field. +It is only field +splitting or pathname expansion that can create multiple +fields from a single word. +The single exception to this rule is +the expansion of the special parameter +.Va @ +within double-quotes, +as was described above. +.Pp +The order of word expansion is: +.Bl -enum +.It +Tilde Expansion, Parameter Expansion, Command Substitution, +Arithmetic Expansion (these all occur at the same time). +.It +Field Splitting is performed on fields generated by step (1) +unless the +.Va IFS +variable is null. +.It +Pathname Expansion (unless the +.Fl f +option is in effect). +.It +Quote Removal. +.El +.Pp +The +.Ql $ +character is used to introduce parameter expansion, command +substitution, or arithmetic expansion. +.Ss Tilde Expansion (substituting a user's home directory) +A word beginning with an unquoted tilde character +.Pq Ql ~ +is +subjected to tilde expansion. +All the characters up to a slash +.Pq Ql / +or the end of the word are treated as a username +and are replaced with the user's home directory. +If the +username is missing (as in +.Pa ~/foobar ) , +the tilde is replaced with the value of the +.Va HOME +variable (the current user's home directory). +.Ss Parameter Expansion +The format for parameter expansion is as follows: +.Pp +.D1 Li ${ Ns Ar expression Ns Li } +.Pp +where +.Ar expression +consists of all characters until the matching +.Ql } . +Any +.Ql } +escaped by a backslash or within a single-quoted or double-quoted +string, and characters in +embedded arithmetic expansions, command substitutions, and variable +expansions, are not examined in determining the matching +.Ql } . +If the variants with +.Ql + , +.Ql - , +.Ql = +or +.Ql ?\& +occur within a double-quoted string, +as an extension there may be unquoted parts +(via double-quotes inside the expansion); +.Ql } +within such parts are also not examined in determining the matching +.Ql } . +.Pp +The simplest form for parameter expansion is: +.Pp +.D1 Li ${ Ns Ar parameter Ns Li } +.Pp +The value, if any, of +.Ar parameter +is substituted. +.Pp +The parameter name or symbol can be enclosed in braces, which are +optional except for positional parameters with more than one digit or +when parameter is followed by a character that could be interpreted as +part of the name. +If a parameter expansion occurs inside double-quotes: +.Bl -enum +.It +Field splitting is not performed on the results of the +expansion, with the exception of the special parameter +.Va @ . +.It +Pathname expansion is not performed on the results of the +expansion. +.El +.Pp +In addition, a parameter expansion can be modified by using one of the +following formats. +.Bl -tag -width indent +.It Li ${ Ns Ar parameter Ns Li :- Ns Ar word Ns Li } +Use Default Values. +If +.Ar parameter +is unset or null, the expansion of +.Ar word +is substituted; otherwise, the value of +.Ar parameter +is substituted. +.It Li ${ Ns Ar parameter Ns Li := Ns Ar word Ns Li } +Assign Default Values. +If +.Ar parameter +is unset or null, the expansion of +.Ar word +is assigned to +.Ar parameter . +In all cases, the +final value of +.Ar parameter +is substituted. +Quoting inside +.Ar word +does not prevent field splitting or pathname expansion. +Only variables, not positional +parameters or special parameters, can be +assigned in this way. +.It Li ${ Ns Ar parameter Ns Li :? Ns Oo Ar word Oc Ns Li } +Indicate Error if Null or Unset. +If +.Ar parameter +is unset or null, the expansion of +.Ar word +(or a message indicating it is unset if +.Ar word +is omitted) is written to standard +error and the shell exits with a nonzero +exit status. +Otherwise, the value of +.Ar parameter +is substituted. +An +interactive shell need not exit. +.It Li ${ Ns Ar parameter Ns Li :+ Ns Ar word Ns Li } +Use Alternate Value. +If +.Ar parameter +is unset or null, null is substituted; +otherwise, the expansion of +.Ar word +is substituted. +.El +.Pp +In the parameter expansions shown previously, use of the colon in the +format results in a test for a parameter that is unset or null; omission +of the colon results in a test for a parameter that is only unset. +.Pp +The +.Ar word +inherits the type of quoting +(unquoted, double-quoted or here-document) +from the surroundings, +with the exception that a backslash that quotes a closing brace is removed +during quote removal. +.Bl -tag -width indent +.It Li ${# Ns Ar parameter Ns Li } +String Length. +The length in characters of +the value of +.Ar parameter . +.El +.Pp +The following four varieties of parameter expansion provide for substring +processing. +In each case, pattern matching notation +(see +.Sx Shell Patterns ) , +rather than regular expression notation, +is used to evaluate the patterns. +If parameter is one of the special parameters +.Va * +or +.Va @ , +the result of the expansion is unspecified. +Enclosing the full parameter expansion string in double-quotes does not +cause the following four varieties of pattern characters to be quoted, +whereas quoting characters within the braces has this effect. +.Bl -tag -width indent +.It Li ${ Ns Ar parameter Ns Li % Ns Ar word Ns Li } +Remove Smallest Suffix Pattern. +The +.Ar word +is expanded to produce a pattern. +The +parameter expansion then results in +.Ar parameter , +with the smallest portion of the +suffix matched by the pattern deleted. +.It Li ${ Ns Ar parameter Ns Li %% Ns Ar word Ns Li } +Remove Largest Suffix Pattern. +The +.Ar word +is expanded to produce a pattern. +The +parameter expansion then results in +.Ar parameter , +with the largest portion of the +suffix matched by the pattern deleted. +.It Li ${ Ns Ar parameter Ns Li # Ns Ar word Ns Li } +Remove Smallest Prefix Pattern. +The +.Ar word +is expanded to produce a pattern. +The +parameter expansion then results in +.Ar parameter , +with the smallest portion of the +prefix matched by the pattern deleted. +.It Li ${ Ns Ar parameter Ns Li ## Ns Ar word Ns Li } +Remove Largest Prefix Pattern. +The +.Ar word +is expanded to produce a pattern. +The +parameter expansion then results in +.Ar parameter , +with the largest portion of the +prefix matched by the pattern deleted. +.El +.Ss Command Substitution +Command substitution allows the output of a command to be substituted in +place of the command name itself. +Command substitution occurs when +the command is enclosed as follows: +.Pp +.D1 Li $( Ns Ar command Ns Li )\& +.Pp +or the backquoted version: +.Pp +.D1 Li ` Ns Ar command Ns Li ` +.Pp +The shell expands the command substitution by executing command +and replacing the command substitution +with the standard output of the command, +removing sequences of one or more newlines at the end of the substitution. +Embedded newlines before the end of the output are not removed; +however, during field splitting, they may be translated into spaces +depending on the value of +.Va IFS +and the quoting that is in effect. +The command is executed in a subshell environment, +except that the built-in commands +.Ic jobid , +.Ic jobs , +and +.Ic trap +return information about the parent shell environment +and +.Ic times +returns information about the same process +if they are the only command in a command substitution. +.Pp +If a command substitution of the +.Li $( +form begins with a subshell, +the +.Li $( +and +.Li (\& +must be separated by whitespace +to avoid ambiguity with arithmetic expansion. +.Ss Arithmetic Expansion +Arithmetic expansion provides a mechanism for evaluating an arithmetic +expression and substituting its value. +The format for arithmetic expansion is as follows: +.Pp +.D1 Li $(( Ns Ar expression Ns Li )) +.Pp +The +.Ar expression +is treated as if it were in double-quotes, except +that a double-quote inside the expression is not treated specially. +The +shell expands all tokens in the +.Ar expression +for parameter expansion, +command substitution, +arithmetic expansion +and quote removal. +.Pp +The allowed expressions are a subset of C expressions, +summarized below. +.Bl -tag -width "Variables" -offset indent +.It Values +All values are of type +.Ft intmax_t . +.It Constants +Decimal, octal (starting with +.Li 0 ) +and hexadecimal (starting with +.Li 0x ) +integer constants. +.It Variables +Shell variables can be read and written +and contain integer constants. +.It Unary operators +.Li "! ~ + -" +.It Binary operators +.Li "* / % + - << >> < <= > >= == != & ^ | && ||" +.It Assignment operators +.Li "= += -= *= /= %= <<= >>= &= ^= |=" +.It Conditional operator +.Li "? :" +.El +.Pp +The result of the expression is substituted in decimal. +.Ss White Space Splitting (Field Splitting) +In certain contexts, +after parameter expansion, command substitution, and +arithmetic expansion the shell scans the results of +expansions and substitutions that did not occur in double-quotes for +field splitting and multiple fields can result. +.Pp +Characters in +.Va IFS +that are whitespace +.Po +.Aq space , +.Aq tab , +and +.Aq newline +.Pc +are treated differently from other characters in +.Va IFS . +.Pp +Whitespace in +.Va IFS +at the beginning or end of a word is discarded. +.Pp +Subsequently, a field is delimited by either +.Bl -enum +.It +a non-whitespace character in +.Va IFS +with any whitespace in +.Va IFS +surrounding it, or +.It +one or more whitespace characters in +.Va IFS . +.El +.Pp +If a word ends with a non-whitespace character in +.Va IFS , +there is no empty field after this character. +.Pp +If no field is delimited, the word is discarded. +In particular, if a word consists solely of an unquoted substitution +and the result of the substitution is null, +it is removed by field splitting even if +.Va IFS +is null. +.Ss Pathname Expansion (File Name Generation) +Unless the +.Fl f +option is set, +file name generation is performed +after word splitting is complete. +Each word is +viewed as a series of patterns, separated by slashes. +The +process of expansion replaces the word with the names of +all existing files whose names can be formed by replacing +each pattern with a string that matches the specified pattern. +There are two restrictions on this: first, a pattern cannot match +a string containing a slash, and second, +a pattern cannot match a string starting with a period +unless the first character of the pattern is a period. +The next section describes the patterns used for +Pathname Expansion, +the four varieties of parameter expansion for substring processing and the +.Ic case +command. +.Ss Shell Patterns +A pattern consists of normal characters, which match themselves, +and meta-characters. +The meta-characters are +.Ql * , +.Ql \&? , +and +.Ql \&[ . +These characters lose their special meanings if they are quoted. +When command or variable substitution is performed and the dollar sign +or back quotes are not double-quoted, the value of the +variable or the output of the command is scanned for these +characters and they are turned into meta-characters. +.Pp +An asterisk +.Pq Ql * +matches any string of characters. +A question mark +.Pq Ql \&? +matches any single character. +A left bracket +.Pq Ql \&[ +introduces a character class. +The end of the character class is indicated by a +.Ql \&] ; +if the +.Ql \&] +is missing then the +.Ql \&[ +matches a +.Ql \&[ +rather than introducing a character class. +A character class matches any of the characters between the square brackets. +A locale-dependent range of characters may be specified using a minus sign. +A named class of characters (see +.Xr wctype 3 ) +may be specified by surrounding the name with +.Ql \&[: +and +.Ql :\&] . +For example, +.Ql \&[\&[:alpha:\&]\&] +is a shell pattern that matches a single letter. +The character class may be complemented by making an exclamation point +.Pq Ql !\& +the first character of the character class. +A caret +.Pq Ql ^ +has the same effect but is non-standard. +.Pp +To include a +.Ql \&] +in a character class, make it the first character listed +(after the +.Ql \&! +or +.Ql ^ , +if any). +To include a +.Ql - , +make it the first or last character listed. +.Ss Built-in Commands +This section lists the built-in commands. +.Bl -tag -width indent +.It Ic \&: +A null command that returns a 0 (true) exit value. +.It Ic \&. Ar file +The commands in the specified file are read and executed by the shell. +The +.Ic return +command may be used to return to the +.Ic \&. +command's caller. +If +.Ar file +contains any +.Ql / +characters, it is used as is. +Otherwise, the shell searches the +.Va PATH +for the file. +If it is not found in the +.Va PATH , +it is sought in the current working directory. +.It Ic \&[ +A built-in equivalent of +.Xr test 1 . +.It Ic alias Oo Ar name Ns Oo = Ns Ar string Oc ... Oc +If +.Ar name Ns = Ns Ar string +is specified, the shell defines the alias +.Ar name +with value +.Ar string . +If just +.Ar name +is specified, the value of the alias +.Ar name +is printed. +With no arguments, the +.Ic alias +built-in command prints the names and values of all defined aliases +(see +.Ic unalias ) . +Alias values are written with appropriate quoting so that they are +suitable for re-input to the shell. +Also see the +.Sx Aliases +subsection. +.It Ic bg Op Ar job ... +Continue the specified jobs +(or the current job if no jobs are given) +in the background. +.It Ic bind Oo Fl aeklrsv Oc Oo Ar key Oo Ar command Oc Oc +List or alter key bindings for the line editor. +This command is documented in +.Xr editrc 5 . +.It Ic break Op Ar num +See the +.Sx Flow-Control Constructs +subsection. +.It Ic builtin Ar cmd Op Ar arg ... +Execute the specified built-in command, +.Ar cmd . +This is useful when the user wishes to override a shell function +with the same name as a built-in command. +.It Ic cd Oo Fl L | P Oc Oo Fl e Oc Op Ar directory +.It Ic cd Fl +Switch to the specified +.Ar directory , +to the directory specified in the +.Va HOME +environment variable if no +.Ar directory +is specified or +to the directory specified in the +.Va OLDPWD +environment variable if +.Ar directory +is +.Fl . +If +.Ar directory +does not begin with +.Pa / , \&. , +or +.Pa .. , +then the directories listed in the +.Va CDPATH +variable will be +searched for the specified +.Ar directory . +If +.Va CDPATH +is unset, the current directory is searched. +The format of +.Va CDPATH +is the same as that of +.Va PATH . +In an interactive shell, +the +.Ic cd +command will print out the name of the directory +that it actually switched to +if the +.Va CDPATH +mechanism was used or if +.Ar directory +was +.Fl . +.Pp +If the +.Fl P +option is specified, +.Pa .. +is handled physically and symbolic links are resolved before +.Pa .. +components are processed. +If the +.Fl L +option is specified, +.Pa .. +is handled logically. +This is the default. +.Pp +The +.Fl e +option causes +.Ic cd +to return exit status 1 if the full pathname of the new directory +cannot be determined reliably or at all. +Normally this is not considered an error, +although a warning is printed. +.Pp +If changing the directory fails, the exit status is greater than 1. +If the directory is changed, the exit status is 0, or also 1 if +.Fl e +was given. +.It Ic chdir +A synonym for the +.Ic cd +built-in command. +.It Ic command Oo Fl p Oc Op Ar utility Op Ar argument ... +.It Ic command Oo Fl p Oc Fl v Ar utility +.It Ic command Oo Fl p Oc Fl V Ar utility +The first form of invocation executes the specified +.Ar utility , +ignoring shell functions in the search. +If +.Ar utility +is a special builtin, +it is executed as if it were a regular builtin. +.Pp +If the +.Fl p +option is specified, the command search is performed using a +default value of +.Va PATH +that is guaranteed to find all of the standard utilities. +.Pp +If the +.Fl v +option is specified, +.Ar utility +is not executed but a description of its interpretation by the shell is +printed. +For ordinary commands the output is the path name; for shell built-in +commands, shell functions and keywords only the name is written. +Aliases are printed as +.Dq Ic alias Ar name Ns = Ns Ar value . +.Pp +The +.Fl V +option is identical to +.Fl v +except for the output. +It prints +.Dq Ar utility Ic is Ar description +where +.Ar description +is either +the path name to +.Ar utility , +a special shell builtin, +a shell builtin, +a shell function, +a shell keyword +or +an alias for +.Ar value . +.It Ic continue Op Ar num +See the +.Sx Flow-Control Constructs +subsection. +.It Ic echo Oo Fl e | n Oc Op Ar string ... +Print a space-separated list of the arguments to the standard output +and append a newline character. +.Bl -tag -width indent +.It Fl n +Suppress the output of the trailing newline. +.It Fl e +Process C-style backslash escape sequences. +The +.Ic echo +command understands the following character escapes: +.Bl -tag -width indent +.It \ea +Alert (ring the terminal bell) +.It \eb +Backspace +.It \ec +Suppress the trailing newline (this has the side-effect of truncating the +line if it is not the last character) +.It \ee +The ESC character +.Tn ( ASCII +0x1b) +.It \ef +Formfeed +.It \en +Newline +.It \er +Carriage return +.It \et +Horizontal tab +.It \ev +Vertical tab +.It \e\e +Literal backslash +.It \e0nnn +(Zero) The character whose octal value is +.Ar nnn +.El +.Pp +If +.Ar string +is not enclosed in quotes then the backslash itself must be escaped +with a backslash to protect it from the shell. +For example +.Bd -literal -offset indent +$ echo -e "a\evb" +a + b +$ echo -e a\e\evb +a + b +$ echo -e "a\e\eb" +a\eb +$ echo -e a\e\e\e\eb +a\eb +.Ed +.El +.Pp +Only one of the +.Fl e +and +.Fl n +options may be specified. +.It Ic eval Ar string ... +Concatenate all the arguments with spaces. +Then re-parse and execute the command. +.It Ic exec Op Ar command Op arg ... +Unless +.Ar command +is omitted, +the shell process is replaced with the specified program +(which must be a real program, not a shell built-in command or function). +Any redirections on the +.Ic exec +command are marked as permanent, +so that they are not undone when the +.Ic exec +command finishes. +.It Ic exit Op Ar exitstatus +Terminate the shell process. +If +.Ar exitstatus +is given +it is used as the exit status of the shell. +Otherwise, if the shell is executing an +.Cm EXIT +trap, the exit status of the last command before the trap is used; +if the shell is executing a trap for a signal, +the shell exits by resending the signal to itself. +Otherwise, the exit status of the preceding command is used. +The exit status should be an integer between 0 and 255. +.It Ic export Ar name ... +.It Ic export Op Fl p +The specified names are exported so that they will +appear in the environment of subsequent commands. +The only way to un-export a variable is to +.Ic unset +it. +The shell allows the value of a variable to be set +at the same time as it is exported by writing +.Pp +.D1 Ic export Ar name Ns = Ns Ar value +.Pp +With no arguments the +.Ic export +command lists the names +of all exported variables. +If the +.Fl p +option is specified, the exported variables are printed as +.Dq Ic export Ar name Ns = Ns Ar value +lines, suitable for re-input to the shell. +.It Ic false +A null command that returns a non-zero (false) exit value. +.It Ic fc Oo Fl e Ar editor Oc Op Ar first Op Ar last +.It Ic fc Fl l Oo Fl nr Oc Op Ar first Op Ar last +.It Ic fc Fl s Oo Ar old Ns = Ns Ar new Oc Op Ar first +The +.Ic fc +built-in command lists, or edits and re-executes, +commands previously entered to an interactive shell. +.Bl -tag -width indent +.It Fl e Ar editor +Use the editor named by +.Ar editor +to edit the commands. +The +.Ar editor +string is a command name, +subject to search via the +.Va PATH +variable. +The value in the +.Va FCEDIT +variable is used as a default when +.Fl e +is not specified. +If +.Va FCEDIT +is null or unset, the value of the +.Va EDITOR +variable is used. +If +.Va EDITOR +is null or unset, +.Xr ed 1 +is used as the editor. +.It Fl l No (ell) +List the commands rather than invoking +an editor on them. +The commands are written in the +sequence indicated by the +.Ar first +and +.Ar last +operands, as affected by +.Fl r , +with each command preceded by the command number. +.It Fl n +Suppress command numbers when listing with +.Fl l . +.It Fl r +Reverse the order of the commands listed +(with +.Fl l ) +or edited +(with neither +.Fl l +nor +.Fl s ) . +.It Fl s +Re-execute the command without invoking an editor. +.It Ar first +.It Ar last +Select the commands to list or edit. +The number of previous commands that can be accessed +are determined by the value of the +.Va HISTSIZE +variable. +The value of +.Ar first +or +.Ar last +or both are one of the following: +.Bl -tag -width indent +.It Oo Cm + Oc Ns Ar num +A positive number representing a command number; +command numbers can be displayed with the +.Fl l +option. +.It Fl Ar num +A negative decimal number representing the +command that was executed +.Ar num +of +commands previously. +For example, \-1 is the immediately previous command. +.It Ar string +A string indicating the most recently entered command +that begins with that string. +If the +.Ar old Ns = Ns Ar new +operand is not also specified with +.Fl s , +the string form of the first operand cannot contain an embedded equal sign. +.El +.El +.Pp +The following variables affect the execution of +.Ic fc : +.Bl -tag -width ".Va HISTSIZE" +.It Va FCEDIT +Name of the editor to use for history editing. +.It Va HISTSIZE +The number of previous commands that are accessible. +.El +.It Ic fg Op Ar job +Move the specified +.Ar job +or the current job to the foreground. +.It Ic getopts Ar optstring var +The +.Tn POSIX +.Ic getopts +command. +The +.Ic getopts +command deprecates the older +.Xr getopt 1 +command. +The first argument should be a series of letters, each possibly +followed by a colon which indicates that the option takes an argument. +The specified variable is set to the parsed option. +The index of +the next argument is placed into the shell variable +.Va OPTIND . +If an option takes an argument, it is placed into the shell variable +.Va OPTARG . +If an invalid option is encountered, +.Ar var +is set to +.Ql \&? . +It returns a false value (1) when it encounters the end of the options. +A new set of arguments may be parsed by assigning +.Li OPTIND=1 . +.It Ic hash Oo Fl rv Oc Op Ar command ... +The shell maintains a hash table which remembers the locations of commands. +With no arguments whatsoever, the +.Ic hash +command prints out the contents of this table. +.Pp +With arguments, the +.Ic hash +command removes each specified +.Ar command +from the hash table (unless they are functions) and then locates it. +With the +.Fl v +option, +.Ic hash +prints the locations of the commands as it finds them. +The +.Fl r +option causes the +.Ic hash +command to delete all the entries in the hash table except for functions. +.It Ic jobid Op Ar job +Print the process IDs of the processes in the specified +.Ar job . +If the +.Ar job +argument is omitted, use the current job. +.It Ic jobs Oo Fl lps Oc Op Ar job ... +Print information about the specified jobs, or all jobs if no +.Ar job +argument is given. +The information printed includes job ID, status and command name. +.Pp +If the +.Fl l +option is specified, the PID of each job is also printed. +If the +.Fl p +option is specified, only the process IDs for the process group leaders +are printed, one per line. +If the +.Fl s +option is specified, only the PIDs of the job commands are printed, one per +line. +.It Ic kill +A built-in equivalent of +.Xr kill 1 +that additionally supports sending signals to jobs. +.It Ic local Oo Ar variable ... Oc Op Fl +See the +.Sx Functions +subsection. +.It Ic printf +A built-in equivalent of +.Xr printf 1 . +.It Ic pwd Op Fl L | P +Print the path of the current directory. +The built-in command may +differ from the program of the same name because the +built-in command remembers what the current directory +is rather than recomputing it each time. +This makes +it faster. +However, if the current directory is +renamed, +the built-in version of +.Xr pwd 1 +will continue to print the old name for the directory. +.Pp +If the +.Fl P +option is specified, symbolic links are resolved. +If the +.Fl L +option is specified, the shell's notion of the current directory +is printed (symbolic links are not resolved). +This is the default. +.It Ic read Oo Fl p Ar prompt Oc Oo +.Fl t Ar timeout Oc Oo Fl er Oc Ar variable ... +The +.Ar prompt +is printed if the +.Fl p +option is specified +and the standard input is a terminal. +Then a line is +read from the standard input. +The trailing newline +is deleted from the line and the line is split as +described in the section on +.Sx White Space Splitting (Field Splitting) +above, and +the pieces are assigned to the variables in order. +If there are more pieces than variables, the remaining +pieces (along with the characters in +.Va IFS +that separated them) +are assigned to the last variable. +If there are more variables than pieces, the remaining +variables are assigned the null string. +.Pp +Backslashes are treated specially, unless the +.Fl r +option is +specified. +If a backslash is followed by +a newline, the backslash and the newline will be +deleted. +If a backslash is followed by any other +character, the backslash will be deleted and the following +character will be treated as though it were not in +.Va IFS , +even if it is. +.Pp +If the +.Fl t +option is specified and the +.Ar timeout +elapses before a complete line of input is supplied, +the +.Ic read +command will return an exit status as if terminated by +.Dv SIGALRM +without assigning any values. +The +.Ar timeout +value may optionally be followed by one of +.Ql s , +.Ql m +or +.Ql h +to explicitly specify seconds, minutes or hours. +If none is supplied, +.Ql s +is assumed. +.Pp +The +.Fl e +option exists only for backward compatibility with older scripts. +.Pp +The exit status is 0 on success, 1 on end of file, +between 2 and 128 if an error occurs +and greater than 128 if a trapped signal interrupts +.Ic read . +.It Ic readonly Oo Fl p Oc Op Ar name ... +Each specified +.Ar name +is marked as read only, +so that it cannot be subsequently modified or unset. +The shell allows the value of a variable to be set +at the same time as it is marked read only +by using the following form: +.Pp +.D1 Ic readonly Ar name Ns = Ns Ar value +.Pp +With no arguments the +.Ic readonly +command lists the names of all read only variables. +If the +.Fl p +option is specified, the read-only variables are printed as +.Dq Ic readonly Ar name Ns = Ns Ar value +lines, suitable for re-input to the shell. +.It Ic return Op Ar exitstatus +See the +.Sx Functions +subsection. +.It Ic set Oo Fl /+abCEefIimnpTuVvx Oc Oo Fl /+o Ar longname Oc Oo +.Fl c Ar string Oc Op Fl - Ar arg ... +The +.Ic set +command performs three different functions: +.Bl -item +.It +With no arguments, it lists the values of all shell variables. +.It +If options are given, +either in short form or using the long +.Dq Fl /+o Ar longname +form, +it sets or clears the specified options as described in the section called +.Sx Argument List Processing . +.It +If the +.Dq Fl - +option is specified, +.Ic set +will replace the shell's positional parameters with the subsequent +arguments. +If no arguments follow the +.Dq Fl - +option, +all the positional parameters will be cleared, +which is equivalent to executing the command +.Dq Li "shift $#" . +The +.Dq Fl - +flag may be omitted when specifying arguments to be used +as positional replacement parameters. +This is not recommended, +because the first argument may begin with a dash +.Pq Ql - +or a plus +.Pq Ql + , +which the +.Ic set +command will interpret as a request to enable or disable options. +.El +.It Ic setvar Ar variable value +Assigns the specified +.Ar value +to the specified +.Ar variable . +The +.Ic setvar +command is intended to be used in functions that +assign values to variables whose names are passed as parameters. +In general it is better to write +.Dq Ar variable Ns = Ns Ar value +rather than using +.Ic setvar . +.It Ic shift Op Ar n +Shift the positional parameters +.Ar n +times, or once if +.Ar n +is not specified. +A shift sets the value of +.Li $1 +to the value of +.Li $2 , +the value of +.Li $2 +to the value of +.Li $3 , +and so on, +decreasing the value of +.Li $# +by one. +For portability, shifting if there are zero positional parameters +should be avoided, since the shell may abort. +.It Ic test +A built-in equivalent of +.Xr test 1 . +.It Ic times +Print the amount of time spent executing the shell process and its children. +The first output line shows the user and system times for the shell process +itself, the second one contains the user and system times for the +children. +.It Ic trap Oo Ar action Oc Ar signal ... +.It Ic trap Fl l +Cause the shell to parse and execute +.Ar action +when any specified +.Ar signal +is received. +The signals are specified by name or number. +In addition, the pseudo-signal +.Cm EXIT +may be used to specify an +.Ar action +that is performed when the shell terminates. +The +.Ar action +may be an empty string or a dash +.Pq Ql - ; +the former causes the specified signal to be ignored +and the latter causes the default action to be taken. +Omitting the +.Ar action +and using only signal numbers is another way to request the default action. +In a subshell or utility environment, +the shell resets trapped (but not ignored) signals to the default action. +The +.Ic trap +command has no effect on signals that were ignored on entry to the shell. +.Pp +Option +.Fl l +causes the +.Ic trap +command to display a list of valid signal names. +.It Ic true +A null command that returns a 0 (true) exit value. +.It Ic type Op Ar name ... +Interpret each +.Ar name +as a command and print the resolution of the command search. +Possible resolutions are: +shell keyword, alias, special shell builtin, shell builtin, command, +tracked alias +and not found. +For aliases the alias expansion is printed; +for commands and tracked aliases +the complete pathname of the command is printed. +.It Ic ulimit Oo Fl HSabcdfklmnopstuvw Oc Op Ar limit +Set or display resource limits (see +.Xr getrlimit 2 ) . +If +.Ar limit +is specified, the named resource will be set; +otherwise the current resource value will be displayed. +.Pp +If +.Fl H +is specified, the hard limits will be set or displayed. +While everybody is allowed to reduce a hard limit, +only the superuser can increase it. +The +.Fl S +option +specifies the soft limits instead. +When displaying limits, +only one of +.Fl S +or +.Fl H +can be given. +The default is to display the soft limits, +and to set both the hard and the soft limits. +.Pp +Option +.Fl a +causes the +.Ic ulimit +command to display all resources. +The parameter +.Ar limit +is not acceptable in this mode. +.Pp +The remaining options specify which resource value is to be +displayed or modified. +They are mutually exclusive. +.Bl -tag -width indent +.It Fl b Ar sbsize +The maximum size of socket buffer usage, in bytes. +.It Fl c Ar coredumpsize +The maximal size of core dump files, in 512-byte blocks. +.It Fl d Ar datasize +The maximal size of the data segment of a process, in kilobytes. +.It Fl f Ar filesize +The maximal size of a file, in 512-byte blocks. +.It Fl k Ar kqueues +The maximal number of kqueues +(see +.Xr kqueue 2 ) +for this user ID. +.It Fl l Ar lockedmem +The maximal size of memory that can be locked by a process, in +kilobytes. +.It Fl m Ar memoryuse +The maximal resident set size of a process, in kilobytes. +.It Fl n Ar nofiles +The maximal number of descriptors that could be opened by a process. +.It Fl o Ar umtxp +The maximal number of process-shared locks +(see +.Xr pthread 3 ) +for this user ID. +.It Fl p Ar pseudoterminals +The maximal number of pseudo-terminals for this user ID. +.It Fl s Ar stacksize +The maximal size of the stack segment, in kilobytes. +.It Fl t Ar time +The maximal amount of CPU time to be used by each process, in seconds. +.It Fl u Ar userproc +The maximal number of simultaneous processes for this user ID. +.It Fl v Ar virtualmem +The maximal virtual size of a process, in kilobytes. +.It Fl w Ar swapuse +The maximum amount of swap space reserved or used for this user ID, +in kilobytes. +.El +.It Ic umask Oo Fl S Oc Op Ar mask +Set the file creation mask (see +.Xr umask 2 ) +to the octal or symbolic (see +.Xr chmod 1 ) +value specified by +.Ar mask . +If the argument is omitted, the current mask value is printed. +If the +.Fl S +option is specified, the output is symbolic, otherwise the output is octal. +.It Ic unalias Oo Fl a Oc Op Ar name ... +The specified alias names are removed. +If +.Fl a +is specified, all aliases are removed. +.It Ic unset Oo Fl fv Oc Ar name ... +The specified variables or functions are unset and unexported. +If the +.Fl v +option is specified or no options are given, the +.Ar name +arguments are treated as variable names. +If the +.Fl f +option is specified, the +.Ar name +arguments are treated as function names. +.It Ic wait Op Ar job ... +Wait for each specified +.Ar job +to complete and return the exit status of the last process in the +last specified +.Ar job . +If any +.Ar job +specified is unknown to the shell, it is treated as if it +were a known job that exited with exit status 127. +If no operands are given, wait for all jobs to complete +and return an exit status of zero. +.El +.Ss Command Line Editing +When +.Nm +is being used interactively from a terminal, the current command +and the command history +(see +.Ic fc +in +.Sx Built-in Commands ) +can be edited using +.Nm vi Ns -mode +command line editing. +This mode uses commands similar +to a subset of those described in the +.Xr vi 1 +man page. +The command +.Dq Li "set -o vi" +(or +.Dq Li "set -V" ) +enables +.Nm vi Ns -mode +editing and places +.Nm +into +.Nm vi +insert mode. +With +.Nm vi Ns -mode +enabled, +.Nm +can be switched between insert mode and command mode by typing +.Aq ESC . +Hitting +.Aq return +while in command mode will pass the line to the shell. +.Pp +Similarly, the +.Dq Li "set -o emacs" +(or +.Dq Li "set -E" ) +command can be used to enable a subset of +.Nm emacs Ns -style +command line editing features. +.Sh ENVIRONMENT +The following environment variables affect the execution of +.Nm : +.Bl -tag -width ".Ev LANGXXXXXX" +.It Ev ENV +Initialization file for interactive shells. +.It Ev LANG , Ev LC_* +Locale settings. +These are inherited by children of the shell, +and is used in a limited manner by the shell itself. +.It Ev OLDPWD +The previous current directory. +This is used and updated by +.Ic cd . +.It Ev PWD +An absolute pathname for the current directory, +possibly containing symbolic links. +This is used and updated by the shell. +.It Ev TERM +The default terminal setting for the shell. +This is inherited by children of the shell, and is used in the history +editing modes. +.El +.Pp +Additionally, environment variables are turned into shell variables +at startup, +which may affect the shell as described under +.Sx Special Variables . +.Sh FILES +.Bl -tag -width "/etc/suid_profileXX" -compact +.It Pa ~/.profile +User's login profile. +.It Pa /etc/profile +System login profile. +.It Pa /etc/shells +Shell database. +.It Pa /etc/suid_profile +Privileged shell profile. +.El +.Sh EXIT STATUS +Errors that are detected by the shell, such as a syntax error, will +cause the shell to exit with a non-zero exit status. +If the shell is not an interactive shell, the execution of the shell +file will be aborted. +Otherwise the shell will return the exit status of the last command +executed, or if the +.Ic exit +builtin is used with a numeric argument, it +will return the argument. +.Sh SEE ALSO +.Xr builtin 1 , +.Xr chsh 1 , +.Xr echo 1 , +.Xr ed 1 , +.Xr emacs 1 , +.Xr kill 1 , +.Xr printf 1 , +.Xr pwd 1 , +.Xr test 1 , +.Xr vi 1 , +.Xr execve 2 , +.Xr getrlimit 2 , +.Xr umask 2 , +.Xr wctype 3 , +.Xr editrc 5 , +.Xr shells 5 +.Sh HISTORY +A +.Nm +command, the Thompson shell, appeared in +.At v1 . +It was superseded in +.At v7 +by the Bourne shell, which inherited the name +.Nm . +.Pp +This version of +.Nm +was rewritten in 1989 under the +.Bx +license after the Bourne shell from +.At V.4 . +.Sh AUTHORS +This version of +.Nm +was originally written by +.An Kenneth Almquist . +.Sh BUGS +The +.Nm +utility does not recognize multibyte characters other than UTF-8. +Splitting using +.Va IFS +does not recognize multibyte characters. diff --git a/shell_cmds/sh/sh.plist.part b/shell_cmds/sh/sh.plist.part new file mode 100644 index 0000000..f023fca --- /dev/null +++ b/shell_cmds/sh/sh.plist.part @@ -0,0 +1,23 @@ + <dict> + <key>OpenSourceProject</key> + <string>sh</string> + <key>OpenSourceVersion</key> + <string>2016-10-08</string> + <key>OpenSourceWebsiteURL</key> + <string>http://svnweb.freebsd.org/base/head/bin/sh/</string> + <key>OpenSourceSCM</key> + <string>svn co http://svn.freebsd.org/base/head/bin/sh/</string> + <key>OpenSourceImportDate</key> + <string>2016-10-27</string> + <key>OpenSourceModifications</key> + <array> + <string>exec.c: eaccess</string> + <string>input.c: remove multiple definition</string> + <string>input.c: strchrnul</string> + <string>miscbltin.c: warning fix</string> + <string>mknodes.c: __printf0like</string> + <string>trap.c: sys_nsig</string> + </array> + <key>OpenSourceLicense</key> + <string>bsd</string> + </dict> diff --git a/shell_cmds/sh/shell.h b/shell_cmds/sh/shell.h new file mode 100644 index 0000000..1c7a766 --- /dev/null +++ b/shell_cmds/sh/shell.h @@ -0,0 +1,79 @@ +/*- + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)shell.h 8.2 (Berkeley) 5/4/95 + * $FreeBSD: head/bin/sh/shell.h 326025 2017-11-20 19:49:47Z pfg $ + */ + +#ifndef SHELL_H_ +#define SHELL_H_ + +#include <inttypes.h> + +/* + * The follow should be set to reflect the type of system you have: + * JOBS -> 1 if you have Berkeley job control, 0 otherwise. + * define DEBUG=1 to compile in debugging (set global "debug" to turn on) + * define DEBUG=2 to compile in and turn on debugging. + * + * When debugging is on, debugging info will be written to ./trace and + * a quit signal will generate a core dump. + */ + + +#define JOBS 1 +/* #define DEBUG 1 */ + +/* + * Type of used arithmetics. SUSv3 requires us to have at least signed long. + */ +typedef intmax_t arith_t; +#define ARITH_FORMAT_STR "%" PRIdMAX +#define atoarith_t(arg) strtoimax(arg, NULL, 0) +#define strtoarith_t(nptr, endptr, base) strtoimax(nptr, endptr, base) +#define ARITH_MIN INTMAX_MIN +#define ARITH_MAX INTMAX_MAX + +typedef void *pointer; + +#include <sys/cdefs.h> + +extern char nullstr[1]; /* null string */ + +#ifdef DEBUG +#define TRACE(param) sh_trace param +#else +#define TRACE(param) +#endif + +#endif /* !SHELL_H_ */ diff --git a/shell_cmds/sh/show.c b/shell_cmds/sh/show.c new file mode 100644 index 0000000..9b55708 --- /dev/null +++ b/shell_cmds/sh/show.c @@ -0,0 +1,410 @@ +/*- + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)show.c 8.3 (Berkeley) 5/4/95"; +#endif +#endif /* not lint */ +#include <sys/cdefs.h> +__FBSDID("$FreeBSD: head/bin/sh/show.c 326025 2017-11-20 19:49:47Z pfg $"); + +#include <fcntl.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <errno.h> + +#include "shell.h" +#include "parser.h" +#include "nodes.h" +#include "mystring.h" +#include "show.h" + + +#ifdef DEBUG +static void shtree(union node *, int, char *, FILE*); +static void shcmd(union node *, FILE *); +static void sharg(union node *, FILE *); +static void indent(int, char *, FILE *); +static void trstring(char *); + + +void +showtree(union node *n) +{ + trputs("showtree called\n"); + shtree(n, 1, NULL, stdout); +} + + +static void +shtree(union node *n, int ind, char *pfx, FILE *fp) +{ + struct nodelist *lp; + char *s; + + if (n == NULL) + return; + + indent(ind, pfx, fp); + switch(n->type) { + case NSEMI: + s = "; "; + goto binop; + case NAND: + s = " && "; + goto binop; + case NOR: + s = " || "; +binop: + shtree(n->nbinary.ch1, ind, NULL, fp); + /* if (ind < 0) */ + fputs(s, fp); + shtree(n->nbinary.ch2, ind, NULL, fp); + break; + case NCMD: + shcmd(n, fp); + if (ind >= 0) + putc('\n', fp); + break; + case NPIPE: + for (lp = n->npipe.cmdlist ; lp ; lp = lp->next) { + shcmd(lp->n, fp); + if (lp->next) + fputs(" | ", fp); + } + if (n->npipe.backgnd) + fputs(" &", fp); + if (ind >= 0) + putc('\n', fp); + break; + default: + fprintf(fp, "<node type %d>", n->type); + if (ind >= 0) + putc('\n', fp); + break; + } +} + + + +static void +shcmd(union node *cmd, FILE *fp) +{ + union node *np; + int first; + char *s; + int dftfd; + + first = 1; + for (np = cmd->ncmd.args ; np ; np = np->narg.next) { + if (! first) + putchar(' '); + sharg(np, fp); + first = 0; + } + for (np = cmd->ncmd.redirect ; np ; np = np->nfile.next) { + if (! first) + putchar(' '); + switch (np->nfile.type) { + case NTO: s = ">"; dftfd = 1; break; + case NAPPEND: s = ">>"; dftfd = 1; break; + case NTOFD: s = ">&"; dftfd = 1; break; + case NCLOBBER: s = ">|"; dftfd = 1; break; + case NFROM: s = "<"; dftfd = 0; break; + case NFROMTO: s = "<>"; dftfd = 0; break; + case NFROMFD: s = "<&"; dftfd = 0; break; + case NHERE: s = "<<"; dftfd = 0; break; + case NXHERE: s = "<<"; dftfd = 0; break; + default: s = "*error*"; dftfd = 0; break; + } + if (np->nfile.fd != dftfd) + fprintf(fp, "%d", np->nfile.fd); + fputs(s, fp); + if (np->nfile.type == NTOFD || np->nfile.type == NFROMFD) { + if (np->ndup.dupfd >= 0) + fprintf(fp, "%d", np->ndup.dupfd); + else + fprintf(fp, "-"); + } else if (np->nfile.type == NHERE) { + fprintf(fp, "HERE"); + } else if (np->nfile.type == NXHERE) { + fprintf(fp, "XHERE"); + } else { + sharg(np->nfile.fname, fp); + } + first = 0; + } +} + + + +static void +sharg(union node *arg, FILE *fp) +{ + char *p; + struct nodelist *bqlist; + int subtype; + + if (arg->type != NARG) { + printf("<node type %d>\n", arg->type); + fflush(stdout); + abort(); + } + bqlist = arg->narg.backquote; + for (p = arg->narg.text ; *p ; p++) { + switch (*p) { + case CTLESC: + putc(*++p, fp); + break; + case CTLVAR: + putc('$', fp); + putc('{', fp); + subtype = *++p; + if (subtype == VSLENGTH) + putc('#', fp); + + while (*p != '=') + putc(*p++, fp); + + if (subtype & VSNUL) + putc(':', fp); + + switch (subtype & VSTYPE) { + case VSNORMAL: + putc('}', fp); + break; + case VSMINUS: + putc('-', fp); + break; + case VSPLUS: + putc('+', fp); + break; + case VSQUESTION: + putc('?', fp); + break; + case VSASSIGN: + putc('=', fp); + break; + case VSTRIMLEFT: + putc('#', fp); + break; + case VSTRIMLEFTMAX: + putc('#', fp); + putc('#', fp); + break; + case VSTRIMRIGHT: + putc('%', fp); + break; + case VSTRIMRIGHTMAX: + putc('%', fp); + putc('%', fp); + break; + case VSLENGTH: + break; + default: + printf("<subtype %d>", subtype); + } + break; + case CTLENDVAR: + putc('}', fp); + break; + case CTLBACKQ: + case CTLBACKQ|CTLQUOTE: + putc('$', fp); + putc('(', fp); + shtree(bqlist->n, -1, NULL, fp); + putc(')', fp); + break; + default: + putc(*p, fp); + break; + } + } +} + + +static void +indent(int amount, char *pfx, FILE *fp) +{ + int i; + + for (i = 0 ; i < amount ; i++) { + if (pfx && i == amount - 1) + fputs(pfx, fp); + putc('\t', fp); + } +} + + +/* + * Debugging stuff. + */ + + +FILE *tracefile; + +#if DEBUG >= 2 +int debug = 1; +#else +int debug = 0; +#endif + + +void +trputc(int c) +{ + if (tracefile == NULL) + return; + putc(c, tracefile); + if (c == '\n') + fflush(tracefile); +} + + +void +sh_trace(const char *fmt, ...) +{ + va_list va; + va_start(va, fmt); + if (tracefile != NULL) { + (void) vfprintf(tracefile, fmt, va); + if (strchr(fmt, '\n')) + (void) fflush(tracefile); + } + va_end(va); +} + + +void +trputs(const char *s) +{ + if (tracefile == NULL) + return; + fputs(s, tracefile); + if (strchr(s, '\n')) + fflush(tracefile); +} + + +static void +trstring(char *s) +{ + char *p; + char c; + + if (tracefile == NULL) + return; + putc('"', tracefile); + for (p = s ; *p ; p++) { + switch (*p) { + case '\n': c = 'n'; goto backslash; + case '\t': c = 't'; goto backslash; + case '\r': c = 'r'; goto backslash; + case '"': c = '"'; goto backslash; + case '\\': c = '\\'; goto backslash; + case CTLESC: c = 'e'; goto backslash; + case CTLVAR: c = 'v'; goto backslash; + case CTLVAR+CTLQUOTE: c = 'V'; goto backslash; + case CTLBACKQ: c = 'q'; goto backslash; + case CTLBACKQ+CTLQUOTE: c = 'Q'; goto backslash; +backslash: putc('\\', tracefile); + putc(c, tracefile); + break; + default: + if (*p >= ' ' && *p <= '~') + putc(*p, tracefile); + else { + putc('\\', tracefile); + putc(*p >> 6 & 03, tracefile); + putc(*p >> 3 & 07, tracefile); + putc(*p & 07, tracefile); + } + break; + } + } + putc('"', tracefile); +} + + +void +trargs(char **ap) +{ + if (tracefile == NULL) + return; + while (*ap) { + trstring(*ap++); + if (*ap) + putc(' ', tracefile); + else + putc('\n', tracefile); + } + fflush(tracefile); +} + + +void +opentrace(void) +{ + char s[100]; + int flags; + + if (!debug) + return; +#ifdef not_this_way + { + char *p; + if ((p = getenv("HOME")) == NULL) { + if (geteuid() == 0) + p = "/"; + else + p = "/tmp"; + } + strcpy(s, p); + strcat(s, "/trace"); + } +#else + strcpy(s, "./trace"); +#endif /* not_this_way */ + if ((tracefile = fopen(s, "a")) == NULL) { + fprintf(stderr, "Can't open %s: %s\n", s, strerror(errno)); + return; + } + if ((flags = fcntl(fileno(tracefile), F_GETFL, 0)) >= 0) + fcntl(fileno(tracefile), F_SETFL, flags | O_APPEND); + fputs("\nTracing started.\n", tracefile); + fflush(tracefile); +} +#endif /* DEBUG */ diff --git a/shell_cmds/sh/show.h b/shell_cmds/sh/show.h new file mode 100644 index 0000000..fc5ef76 --- /dev/null +++ b/shell_cmds/sh/show.h @@ -0,0 +1,42 @@ +/*- + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright (c) 1995 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)show.h 1.1 (Berkeley) 5/4/95 + * $FreeBSD: head/bin/sh/show.h 326025 2017-11-20 19:49:47Z pfg $ + */ + +void showtree(union node *); +#ifdef DEBUG +void sh_trace(const char *, ...) __printflike(1, 2); +void trargs(char **); +void trputc(int); +void trputs(const char *); +void opentrace(void); +#endif diff --git a/shell_cmds/sh/tests/Makefile b/shell_cmds/sh/tests/Makefile new file mode 100644 index 0000000..b7d7b7d --- /dev/null +++ b/shell_cmds/sh/tests/Makefile @@ -0,0 +1,14 @@ +# $FreeBSD: head/bin/sh/tests/Makefile 322438 2017-08-12 19:17:48Z jilles $ + +.include <bsd.own.mk> + +TESTS_SUBDIRS+= builtins +TESTS_SUBDIRS+= errors +TESTS_SUBDIRS+= execution +TESTS_SUBDIRS+= expansion +TESTS_SUBDIRS+= invocation +TESTS_SUBDIRS+= parameters +TESTS_SUBDIRS+= parser +TESTS_SUBDIRS+= set-e + +.include <bsd.test.mk> diff --git a/shell_cmds/sh/tests/Makefile.depend b/shell_cmds/sh/tests/Makefile.depend new file mode 100644 index 0000000..9ff4aed --- /dev/null +++ b/shell_cmds/sh/tests/Makefile.depend @@ -0,0 +1,11 @@ +# $FreeBSD: head/bin/sh/tests/Makefile.depend 296587 2016-03-09 22:46:01Z bdrewery $ +# Autogenerated - do NOT edit! + +DIRDEPS = \ + + +.include <dirdeps.mk> + +.if ${DEP_RELDIR} == ${_DEP_RELDIR} +# local dependencies - needed for -jN in clean tree +.endif diff --git a/shell_cmds/sh/tests/builtins/Makefile b/shell_cmds/sh/tests/builtins/Makefile new file mode 100644 index 0000000..98c3841 --- /dev/null +++ b/shell_cmds/sh/tests/builtins/Makefile @@ -0,0 +1,185 @@ +# $FreeBSD: head/bin/sh/tests/builtins/Makefile 320340 2017-06-25 21:53:08Z jilles $ + +PACKAGE= tests + +.include <src.opts.mk> + +TESTSDIR= ${TESTSBASE}/bin/sh/${.CURDIR:T} + +.PATH: ${.CURDIR:H} +ATF_TESTS_SH= functional_test + +${PACKAGE}FILES+= alias.0 alias.0.stdout +${PACKAGE}FILES+= alias.1 alias.1.stderr +${PACKAGE}FILES+= alias3.0 alias3.0.stdout +${PACKAGE}FILES+= alias4.0 +${PACKAGE}FILES+= break1.0 +${PACKAGE}FILES+= break2.0 break2.0.stdout +${PACKAGE}FILES+= break3.0 +${PACKAGE}FILES+= break4.4 +${PACKAGE}FILES+= break5.4 +${PACKAGE}FILES+= break6.0 +${PACKAGE}FILES+= builtin1.0 +${PACKAGE}FILES+= case1.0 +${PACKAGE}FILES+= case2.0 +${PACKAGE}FILES+= case3.0 +${PACKAGE}FILES+= case4.0 +${PACKAGE}FILES+= case5.0 +${PACKAGE}FILES+= case6.0 +${PACKAGE}FILES+= case7.0 +${PACKAGE}FILES+= case8.0 +${PACKAGE}FILES+= case9.0 +${PACKAGE}FILES+= case10.0 +${PACKAGE}FILES+= case11.0 +${PACKAGE}FILES+= case12.0 +${PACKAGE}FILES+= case13.0 +${PACKAGE}FILES+= case14.0 +${PACKAGE}FILES+= case15.0 +${PACKAGE}FILES+= case16.0 +${PACKAGE}FILES+= case17.0 +${PACKAGE}FILES+= case18.0 +${PACKAGE}FILES+= case19.0 +${PACKAGE}FILES+= case20.0 +${PACKAGE}FILES+= case21.0 +${PACKAGE}FILES+= case22.0 +${PACKAGE}FILES+= cd1.0 +${PACKAGE}FILES+= cd2.0 +${PACKAGE}FILES+= cd3.0 +${PACKAGE}FILES+= cd4.0 +${PACKAGE}FILES+= cd5.0 +${PACKAGE}FILES+= cd6.0 +${PACKAGE}FILES+= cd7.0 +${PACKAGE}FILES+= cd8.0 +${PACKAGE}FILES+= cd9.0 cd9.0.stdout +${PACKAGE}FILES+= cd10.0 +${PACKAGE}FILES+= command1.0 +${PACKAGE}FILES+= command2.0 +${PACKAGE}FILES+= command3.0 +${PACKAGE}FILES+= command3.0.stdout +${PACKAGE}FILES+= command4.0 +${PACKAGE}FILES+= command5.0 +${PACKAGE}FILES+= command5.0.stdout +${PACKAGE}FILES+= command6.0 +${PACKAGE}FILES+= command6.0.stdout +${PACKAGE}FILES+= command7.0 +${PACKAGE}FILES+= command8.0 +${PACKAGE}FILES+= command9.0 +${PACKAGE}FILES+= command10.0 +${PACKAGE}FILES+= command11.0 +${PACKAGE}FILES+= command12.0 +${PACKAGE}FILES+= dot1.0 +${PACKAGE}FILES+= dot2.0 +${PACKAGE}FILES+= dot3.0 +${PACKAGE}FILES+= dot4.0 +${PACKAGE}FILES+= echo1.0 +${PACKAGE}FILES+= echo2.0 +${PACKAGE}FILES+= echo3.0 +${PACKAGE}FILES+= eval1.0 +${PACKAGE}FILES+= eval2.0 +${PACKAGE}FILES+= eval3.0 +${PACKAGE}FILES+= eval4.0 +${PACKAGE}FILES+= eval5.0 +${PACKAGE}FILES+= eval6.0 +${PACKAGE}FILES+= eval7.0 +${PACKAGE}FILES+= eval8.7 +${PACKAGE}FILES+= exec1.0 +${PACKAGE}FILES+= exec2.0 +${PACKAGE}FILES+= exit1.0 +${PACKAGE}FILES+= exit2.8 +${PACKAGE}FILES+= exit3.0 +${PACKAGE}FILES+= export1.0 +${PACKAGE}FILES+= fc1.0 +${PACKAGE}FILES+= fc2.0 +${PACKAGE}FILES+= for1.0 +${PACKAGE}FILES+= for2.0 +${PACKAGE}FILES+= for3.0 +${PACKAGE}FILES+= getopts1.0 getopts1.0.stdout +${PACKAGE}FILES+= getopts2.0 getopts2.0.stdout +${PACKAGE}FILES+= getopts3.0 +${PACKAGE}FILES+= getopts4.0 +${PACKAGE}FILES+= getopts5.0 +${PACKAGE}FILES+= getopts6.0 +${PACKAGE}FILES+= getopts7.0 +${PACKAGE}FILES+= getopts8.0 getopts8.0.stdout +${PACKAGE}FILES+= getopts9.0 getopts9.0.stdout +${PACKAGE}FILES+= getopts10.0 +${PACKAGE}FILES+= hash1.0 hash1.0.stdout +${PACKAGE}FILES+= hash2.0 hash2.0.stdout +${PACKAGE}FILES+= hash3.0 hash3.0.stdout +${PACKAGE}FILES+= hash4.0 +${PACKAGE}FILES+= jobid1.0 +${PACKAGE}FILES+= jobid2.0 +${PACKAGE}FILES+= kill1.0 kill2.0 +${PACKAGE}FILES+= lineno.0 lineno.0.stdout +${PACKAGE}FILES+= lineno2.0 +${PACKAGE}FILES+= lineno3.0 lineno3.0.stdout +${PACKAGE}FILES+= local1.0 +${PACKAGE}FILES+= local2.0 +${PACKAGE}FILES+= local3.0 +${PACKAGE}FILES+= local4.0 +${PACKAGE}FILES+= local5.0 +${PACKAGE}FILES+= local6.0 +${PACKAGE}FILES+= local7.0 +.if ${MK_NLS} != "no" +${PACKAGE}FILES+= locale1.0 +.endif +${PACKAGE}FILES+= locale2.0 +${PACKAGE}FILES+= printf1.0 +${PACKAGE}FILES+= printf2.0 +${PACKAGE}FILES+= printf3.0 +${PACKAGE}FILES+= printf4.0 +${PACKAGE}FILES+= read1.0 read1.0.stdout +${PACKAGE}FILES+= read2.0 +${PACKAGE}FILES+= read3.0 read3.0.stdout +${PACKAGE}FILES+= read4.0 read4.0.stdout +${PACKAGE}FILES+= read5.0 +${PACKAGE}FILES+= read6.0 +${PACKAGE}FILES+= read7.0 +${PACKAGE}FILES+= read8.0 +${PACKAGE}FILES+= read9.0 +${PACKAGE}FILES+= return1.0 +${PACKAGE}FILES+= return2.1 +${PACKAGE}FILES+= return3.1 +${PACKAGE}FILES+= return4.0 +${PACKAGE}FILES+= return5.0 +${PACKAGE}FILES+= return6.4 +${PACKAGE}FILES+= return7.4 +${PACKAGE}FILES+= return8.0 +${PACKAGE}FILES+= set1.0 +${PACKAGE}FILES+= set2.0 +${PACKAGE}FILES+= set3.0 +${PACKAGE}FILES+= trap1.0 +${PACKAGE}FILES+= trap10.0 +${PACKAGE}FILES+= trap11.0 +${PACKAGE}FILES+= trap12.0 +${PACKAGE}FILES+= trap13.0 +${PACKAGE}FILES+= trap14.0 +${PACKAGE}FILES+= trap15.0 +${PACKAGE}FILES+= trap16.0 +${PACKAGE}FILES+= trap17.0 +${PACKAGE}FILES+= trap2.0 +${PACKAGE}FILES+= trap3.0 +${PACKAGE}FILES+= trap4.0 +${PACKAGE}FILES+= trap5.0 +${PACKAGE}FILES+= trap6.0 +${PACKAGE}FILES+= trap7.0 +${PACKAGE}FILES+= trap8.0 +${PACKAGE}FILES+= trap9.0 +${PACKAGE}FILES+= type1.0 type1.0.stderr +${PACKAGE}FILES+= type2.0 +${PACKAGE}FILES+= type3.0 +${PACKAGE}FILES+= unalias.0 +${PACKAGE}FILES+= var-assign.0 +${PACKAGE}FILES+= var-assign2.0 +${PACKAGE}FILES+= wait1.0 +${PACKAGE}FILES+= wait2.0 +${PACKAGE}FILES+= wait3.0 +${PACKAGE}FILES+= wait4.0 +${PACKAGE}FILES+= wait5.0 +${PACKAGE}FILES+= wait6.0 +${PACKAGE}FILES+= wait7.0 +${PACKAGE}FILES+= wait8.0 +${PACKAGE}FILES+= wait9.127 +${PACKAGE}FILES+= wait10.0 + +.include <bsd.test.mk> diff --git a/shell_cmds/sh/tests/builtins/Makefile.depend b/shell_cmds/sh/tests/builtins/Makefile.depend new file mode 100644 index 0000000..21c5642 --- /dev/null +++ b/shell_cmds/sh/tests/builtins/Makefile.depend @@ -0,0 +1,11 @@ +# $FreeBSD: head/bin/sh/tests/builtins/Makefile.depend 296587 2016-03-09 22:46:01Z bdrewery $ +# Autogenerated - do NOT edit! + +DIRDEPS = \ + + +.include <dirdeps.mk> + +.if ${DEP_RELDIR} == ${_DEP_RELDIR} +# local dependencies - needed for -jN in clean tree +.endif diff --git a/shell_cmds/sh/tests/builtins/alias.0 b/shell_cmds/sh/tests/builtins/alias.0 new file mode 100644 index 0000000..067a890 --- /dev/null +++ b/shell_cmds/sh/tests/builtins/alias.0 @@ -0,0 +1,9 @@ +# $FreeBSD: head/bin/sh/tests/builtins/alias.0 190285 2009-03-22 21:12:00Z stefanf $ +set -e + +unalias -a +alias foo=bar +alias bar= +alias quux="1 2 3" +alias +alias foo diff --git a/shell_cmds/sh/tests/builtins/alias.0.stdout b/shell_cmds/sh/tests/builtins/alias.0.stdout new file mode 100644 index 0000000..52efaf0 --- /dev/null +++ b/shell_cmds/sh/tests/builtins/alias.0.stdout @@ -0,0 +1,4 @@ +bar='' +foo=bar +quux='1 2 3' +foo=bar diff --git a/shell_cmds/sh/tests/builtins/alias.1 b/shell_cmds/sh/tests/builtins/alias.1 new file mode 100644 index 0000000..22093dd --- /dev/null +++ b/shell_cmds/sh/tests/builtins/alias.1 @@ -0,0 +1,3 @@ +# $FreeBSD: head/bin/sh/tests/builtins/alias.1 149781 2005-09-04 11:59:59Z stefanf $ +unalias -a +alias foo diff --git a/shell_cmds/sh/tests/builtins/alias.1.stderr b/shell_cmds/sh/tests/builtins/alias.1.stderr new file mode 100644 index 0000000..c9f4011 --- /dev/null +++ b/shell_cmds/sh/tests/builtins/alias.1.stderr @@ -0,0 +1 @@ +alias: foo: not found diff --git a/shell_cmds/sh/tests/builtins/alias3.0 b/shell_cmds/sh/tests/builtins/alias3.0 new file mode 100644 index 0000000..e2e215a --- /dev/null +++ b/shell_cmds/sh/tests/builtins/alias3.0 @@ -0,0 +1,12 @@ +# $FreeBSD: head/bin/sh/tests/builtins/alias3.0 242767 2012-11-08 13:36:19Z jilles $ +set -e + +unalias -a +alias foo=bar +alias bar= +alias quux="1 2 3" +alias foo=bar +alias bar= +alias quux="1 2 3" +alias +alias foo diff --git a/shell_cmds/sh/tests/builtins/alias3.0.stdout b/shell_cmds/sh/tests/builtins/alias3.0.stdout new file mode 100644 index 0000000..52efaf0 --- /dev/null +++ b/shell_cmds/sh/tests/builtins/alias3.0.stdout @@ -0,0 +1,4 @@ +bar='' +foo=bar +quux='1 2 3' +foo=bar diff --git a/shell_cmds/sh/tests/builtins/alias4.0 b/shell_cmds/sh/tests/builtins/alias4.0 new file mode 100644 index 0000000..d86559b --- /dev/null +++ b/shell_cmds/sh/tests/builtins/alias4.0 @@ -0,0 +1,4 @@ +# $FreeBSD: head/bin/sh/tests/builtins/alias4.0 254849 2013-08-25 11:42:53Z jilles $ + +unalias -a +alias -- diff --git a/shell_cmds/sh/tests/builtins/break1.0 b/shell_cmds/sh/tests/builtins/break1.0 new file mode 100644 index 0000000..219cbf2 --- /dev/null +++ b/shell_cmds/sh/tests/builtins/break1.0 @@ -0,0 +1,16 @@ +# $FreeBSD: head/bin/sh/tests/builtins/break1.0 211349 2010-08-15 21:06:53Z jilles $ + +if [ "$1" != nested ]; then + while :; do + set -- nested + . "$0" + echo bad2 + exit 2 + done + exit 0 +fi +# To trigger the bug, the following commands must be at the top level, +# with newlines in between. +break +echo bad1 +exit 1 diff --git a/shell_cmds/sh/tests/builtins/break2.0 b/shell_cmds/sh/tests/builtins/break2.0 new file mode 100644 index 0000000..f836e31 --- /dev/null +++ b/shell_cmds/sh/tests/builtins/break2.0 @@ -0,0 +1,12 @@ +# $FreeBSD: head/bin/sh/tests/builtins/break2.0 211467 2010-08-18 20:26:50Z jilles $ + +# It is not immediately obvious that this should work, and someone probably +# relies on it. + +while :; do + trap 'break' USR1 + kill -USR1 $$ + echo bad + exit 1 +done +echo good diff --git a/shell_cmds/sh/tests/builtins/break2.0.stdout b/shell_cmds/sh/tests/builtins/break2.0.stdout new file mode 100644 index 0000000..12799cc --- /dev/null +++ b/shell_cmds/sh/tests/builtins/break2.0.stdout @@ -0,0 +1 @@ +good diff --git a/shell_cmds/sh/tests/builtins/break3.0 b/shell_cmds/sh/tests/builtins/break3.0 new file mode 100644 index 0000000..1a6985c --- /dev/null +++ b/shell_cmds/sh/tests/builtins/break3.0 @@ -0,0 +1,15 @@ +# $FreeBSD: head/bin/sh/tests/builtins/break3.0 211612 2010-08-22 11:07:46Z jilles $ + +# We accept this and people might rely on it. +# However, various other shells do not accept it. + +f() { + break + echo bad1 +} + +while :; do + f + echo bad2 + exit 2 +done diff --git a/shell_cmds/sh/tests/builtins/break4.4 b/shell_cmds/sh/tests/builtins/break4.4 new file mode 100644 index 0000000..3b1bb0c --- /dev/null +++ b/shell_cmds/sh/tests/builtins/break4.4 @@ -0,0 +1,7 @@ +# $FreeBSD: head/bin/sh/tests/builtins/break4.4 251180 2013-05-31 14:45:25Z jilles $ + +# Although this is not specified by POSIX, some configure scripts (gawk 4.1.0) +# appear to depend on it. + +break +exit 4 diff --git a/shell_cmds/sh/tests/builtins/break5.4 b/shell_cmds/sh/tests/builtins/break5.4 new file mode 100644 index 0000000..a29f317 --- /dev/null +++ b/shell_cmds/sh/tests/builtins/break5.4 @@ -0,0 +1,12 @@ +# $FreeBSD: head/bin/sh/tests/builtins/break5.4 251180 2013-05-31 14:45:25Z jilles $ + +# Although this is not specified by POSIX, some configure scripts (gawk 4.1.0) +# appear to depend on it. +# In some uncommitted code, the subshell environment corrupted the outer +# shell environment's state. + +(for i in a b c; do + exit 3 +done) +break +exit 4 diff --git a/shell_cmds/sh/tests/builtins/break6.0 b/shell_cmds/sh/tests/builtins/break6.0 new file mode 100644 index 0000000..b5fc63b --- /dev/null +++ b/shell_cmds/sh/tests/builtins/break6.0 @@ -0,0 +1,8 @@ +# $FreeBSD: head/bin/sh/tests/builtins/break6.0 268927 2014-07-20 20:29:09Z jilles $ +# Per POSIX, this need only work if LONG_MAX > 4294967295. + +while :; do + break 4294967296 + echo bad + exit 3 +done diff --git a/shell_cmds/sh/tests/builtins/builtin1.0 b/shell_cmds/sh/tests/builtins/builtin1.0 new file mode 100644 index 0000000..dd77422 --- /dev/null +++ b/shell_cmds/sh/tests/builtins/builtin1.0 @@ -0,0 +1,31 @@ +# $FreeBSD: head/bin/sh/tests/builtins/builtin1.0 201431 2010-01-03 15:01:38Z jilles $ + +failures=0 + +check() { + if ! eval "[ $* ]"; then + echo "Failed: $*" + : $((failures += 1)) + fi +} + +builtin : || echo "Bad return code at $LINENO" +builtin true || echo "Bad return code at $LINENO" +builtin ls 2>/dev/null && echo "Bad return code at $LINENO" +check '"$(builtin pwd)" = "$(pwd)"' +check '-z "$(builtin :)"' +check '-z "$(builtin true)"' +check '-z "$( (builtin nosuchtool) 2>/dev/null)"' +check '-z "$(builtin nosuchtool 2>/dev/null)"' +check '-z "$(builtin nosuchtool 2>/dev/null; :)"' +check '-z "$( (builtin ls) 2>/dev/null)"' +check '-z "$(builtin ls 2>/dev/null)"' +check '-z "$(builtin ls 2>/dev/null; :)"' +check '-n "$( (builtin nosuchtool) 2>&1)"' +check '-n "$(builtin nosuchtool 2>&1)"' +check '-n "$(builtin nosuchtool 2>&1; :)"' +check '-n "$( (builtin ls) 2>&1)"' +check '-n "$(builtin ls 2>&1)"' +check '-n "$(builtin ls 2>&1; :)"' + +exit $((failures > 0)) diff --git a/shell_cmds/sh/tests/builtins/case1.0 b/shell_cmds/sh/tests/builtins/case1.0 new file mode 100644 index 0000000..b8e8d9b --- /dev/null +++ b/shell_cmds/sh/tests/builtins/case1.0 @@ -0,0 +1,13 @@ +#$FreeBSD: head/bin/sh/tests/builtins/case1.0 172440 2007-10-04 16:14:48Z stefanf $ +f() +{ + false + case $1 in + foo) true ;; + bar) false ;; + esac +} + +f foo || exit 1 +f bar && exit 1 +f quux || exit 1 diff --git a/shell_cmds/sh/tests/builtins/case10.0 b/shell_cmds/sh/tests/builtins/case10.0 new file mode 100644 index 0000000..e8cd933 --- /dev/null +++ b/shell_cmds/sh/tests/builtins/case10.0 @@ -0,0 +1,16 @@ +# $FreeBSD: head/bin/sh/tests/builtins/case10.0 223546 2011-06-25 20:37:43Z jilles $ + +case ! in +[\!!]) ;; +*) echo Failed at $LINENO ;; +esac + +case ! in +['!'!]) ;; +*) echo Failed at $LINENO ;; +esac + +case ! in +["!"!]) ;; +*) echo Failed at $LINENO ;; +esac diff --git a/shell_cmds/sh/tests/builtins/case11.0 b/shell_cmds/sh/tests/builtins/case11.0 new file mode 100644 index 0000000..afcfa43 --- /dev/null +++ b/shell_cmds/sh/tests/builtins/case11.0 @@ -0,0 +1,6 @@ +# $FreeBSD: head/bin/sh/tests/builtins/case11.0 228007 2011-11-26 22:28:25Z jilles $ + +false +case x in +*) +esac diff --git a/shell_cmds/sh/tests/builtins/case12.0 b/shell_cmds/sh/tests/builtins/case12.0 new file mode 100644 index 0000000..630bee5 --- /dev/null +++ b/shell_cmds/sh/tests/builtins/case12.0 @@ -0,0 +1,6 @@ +# $FreeBSD: head/bin/sh/tests/builtins/case12.0 228007 2011-11-26 22:28:25Z jilles $ + +false +case x in +y) +esac diff --git a/shell_cmds/sh/tests/builtins/case13.0 b/shell_cmds/sh/tests/builtins/case13.0 new file mode 100644 index 0000000..669ce62 --- /dev/null +++ b/shell_cmds/sh/tests/builtins/case13.0 @@ -0,0 +1,12 @@ +# $FreeBSD: head/bin/sh/tests/builtins/case13.0 228943 2011-12-28 23:51:17Z jilles $ + +case ^ in +[\^^]) ;; +*) echo Failed at $LINENO ;; +esac + +case s in +[\^^]) echo Failed at $LINENO ;; +[s\]]) ;; +*) echo Failed at $LINENO ;; +esac diff --git a/shell_cmds/sh/tests/builtins/case14.0 b/shell_cmds/sh/tests/builtins/case14.0 new file mode 100644 index 0000000..f6a68cb --- /dev/null +++ b/shell_cmds/sh/tests/builtins/case14.0 @@ -0,0 +1,5 @@ +# $FreeBSD: head/bin/sh/tests/builtins/case14.0 230154 2012-01-15 20:04:05Z jilles $ + +case `false` in +no) exit 3 ;; +esac diff --git a/shell_cmds/sh/tests/builtins/case15.0 b/shell_cmds/sh/tests/builtins/case15.0 new file mode 100644 index 0000000..083aaa6 --- /dev/null +++ b/shell_cmds/sh/tests/builtins/case15.0 @@ -0,0 +1,5 @@ +# $FreeBSD: head/bin/sh/tests/builtins/case15.0 230154 2012-01-15 20:04:05Z jilles $ + +case x in +`false`) exit 3 ;; +esac diff --git a/shell_cmds/sh/tests/builtins/case16.0 b/shell_cmds/sh/tests/builtins/case16.0 new file mode 100644 index 0000000..7031a92 --- /dev/null +++ b/shell_cmds/sh/tests/builtins/case16.0 @@ -0,0 +1,7 @@ +# $FreeBSD: head/bin/sh/tests/builtins/case16.0 230154 2012-01-15 20:04:05Z jilles $ + +f() { return 42; } +f +case x in +x) [ $? = 42 ] ;; +esac diff --git a/shell_cmds/sh/tests/builtins/case17.0 b/shell_cmds/sh/tests/builtins/case17.0 new file mode 100644 index 0000000..10d8d9c --- /dev/null +++ b/shell_cmds/sh/tests/builtins/case17.0 @@ -0,0 +1,3 @@ +# $FreeBSD: head/bin/sh/tests/builtins/case17.0 230161 2012-01-15 21:39:38Z jilles $ + +! case x in x) false ;& y) esac diff --git a/shell_cmds/sh/tests/builtins/case18.0 b/shell_cmds/sh/tests/builtins/case18.0 new file mode 100644 index 0000000..b0e601f --- /dev/null +++ b/shell_cmds/sh/tests/builtins/case18.0 @@ -0,0 +1,7 @@ +# $FreeBSD: head/bin/sh/tests/builtins/case18.0 230161 2012-01-15 21:39:38Z jilles $ + +case x$(false) in +x) ;& +y) [ $? != 0 ] ;; +z) false ;; +esac diff --git a/shell_cmds/sh/tests/builtins/case19.0 b/shell_cmds/sh/tests/builtins/case19.0 new file mode 100644 index 0000000..1a9b09a --- /dev/null +++ b/shell_cmds/sh/tests/builtins/case19.0 @@ -0,0 +1,7 @@ +# $FreeBSD: head/bin/sh/tests/builtins/case19.0 230161 2012-01-15 21:39:38Z jilles $ + +[ "`case x in +x) false ;& +y) ;& +z) echo $? ;; +esac`" != 0 ] diff --git a/shell_cmds/sh/tests/builtins/case2.0 b/shell_cmds/sh/tests/builtins/case2.0 new file mode 100644 index 0000000..ae3f356 --- /dev/null +++ b/shell_cmds/sh/tests/builtins/case2.0 @@ -0,0 +1,106 @@ +# Generated by ./test-fnmatch -s 1, do not edit. +# $FreeBSD: head/bin/sh/tests/builtins/case2.0 207821 2010-05-09 16:15:40Z jilles $ +failures= +failed() { printf '%s\n' "Failed: $1 '$2' '$3'"; failures=x$failures; } +testmatch() { eval "case \$2 in ''$1) ;; *) failed testmatch \"\$@\";; esac"; } +testnomatch() { eval "case \$2 in ''$1) failed testnomatch \"\$@\";; esac"; } +testmatch '' '' +testmatch 'a' 'a' +testnomatch 'a' 'b' +testnomatch 'a' 'A' +testmatch '*' 'a' +testmatch '*' 'aa' +testmatch '*a' 'a' +testnomatch '*a' 'b' +testnomatch '*a*' 'b' +testmatch '*a*b*' 'ab' +testmatch '*a*b*' 'qaqbq' +testmatch '*a*bb*' 'qaqbqbbq' +testmatch '*a*bc*' 'qaqbqbcq' +testmatch '*a*bb*' 'qaqbqbb' +testmatch '*a*bc*' 'qaqbqbc' +testmatch '*a*bb' 'qaqbqbb' +testmatch '*a*bc' 'qaqbqbc' +testnomatch '*a*bb' 'qaqbqbbq' +testnomatch '*a*bc' 'qaqbqbcq' +testnomatch '*a*a*a*a*a*a*a*a*a*a*' 'aaaaaaaaa' +testmatch '*a*a*a*a*a*a*a*a*a*a*' 'aaaaaaaaaa' +testmatch '*a*a*a*a*a*a*a*a*a*a*' 'aaaaaaaaaaa' +testnomatch '.*.*.*.*.*.*.*.*.*.*' '.........' +testmatch '.*.*.*.*.*.*.*.*.*.*' '..........' +testmatch '.*.*.*.*.*.*.*.*.*.*' '...........' +testnomatch '*?*?*?*?*?*?*?*?*?*?*' '123456789' +testnomatch '??????????*' '123456789' +testnomatch '*??????????' '123456789' +testmatch '*?*?*?*?*?*?*?*?*?*?*' '1234567890' +testmatch '??????????*' '1234567890' +testmatch '*??????????' '1234567890' +testmatch '*?*?*?*?*?*?*?*?*?*?*' '12345678901' +testmatch '??????????*' '12345678901' +testmatch '*??????????' '12345678901' +testmatch '[x]' 'x' +testmatch '[*]' '*' +testmatch '[?]' '?' +testmatch '[' '[' +testmatch '[[]' '[' +testnomatch '[[]' 'x' +testnomatch '[*]' '' +testnomatch '[*]' 'x' +testnomatch '[?]' 'x' +testmatch '*[*]*' 'foo*foo' +testnomatch '*[*]*' 'foo' +testmatch '[0-9]' '0' +testmatch '[0-9]' '5' +testmatch '[0-9]' '9' +testnomatch '[0-9]' '/' +testnomatch '[0-9]' ':' +testnomatch '[0-9]' '*' +testnomatch '[!0-9]' '0' +testnomatch '[!0-9]' '5' +testnomatch '[!0-9]' '9' +testmatch '[!0-9]' '/' +testmatch '[!0-9]' ':' +testmatch '[!0-9]' '*' +testmatch '*[0-9]' 'a0' +testmatch '*[0-9]' 'a5' +testmatch '*[0-9]' 'a9' +testnomatch '*[0-9]' 'a/' +testnomatch '*[0-9]' 'a:' +testnomatch '*[0-9]' 'a*' +testnomatch '*[!0-9]' 'a0' +testnomatch '*[!0-9]' 'a5' +testnomatch '*[!0-9]' 'a9' +testmatch '*[!0-9]' 'a/' +testmatch '*[!0-9]' 'a:' +testmatch '*[!0-9]' 'a*' +testmatch '*[0-9]' 'a00' +testmatch '*[0-9]' 'a55' +testmatch '*[0-9]' 'a99' +testmatch '*[0-9]' 'a0a0' +testmatch '*[0-9]' 'a5a5' +testmatch '*[0-9]' 'a9a9' +testmatch '\*' '*' +testmatch '\?' '?' +testmatch '\[x]' '[x]' +testmatch '\[' '[' +testmatch '\\' '\' +testmatch '*\**' 'foo*foo' +testnomatch '*\**' 'foo' +testmatch '*\\*' 'foo\foo' +testnomatch '*\\*' 'foo' +testmatch '\(' '(' +testmatch '\a' 'a' +testnomatch '\*' 'a' +testnomatch '\?' 'a' +testnomatch '\*' '\*' +testnomatch '\?' '\?' +testnomatch '\[x]' '\[x]' +testnomatch '\[x]' '\x' +testnomatch '\[' '\[' +testnomatch '\(' '\(' +testnomatch '\a' '\a' +testmatch '.*' '.' +testmatch '.*' '..' +testmatch '.*' '.a' +testmatch 'a*' 'a.' +[ -z "$failures" ] diff --git a/shell_cmds/sh/tests/builtins/case20.0 b/shell_cmds/sh/tests/builtins/case20.0 new file mode 100644 index 0000000..bd91e5b --- /dev/null +++ b/shell_cmds/sh/tests/builtins/case20.0 @@ -0,0 +1,9 @@ +# $FreeBSD: head/bin/sh/tests/builtins/case20.0 287148 2015-08-25 21:55:15Z jilles $ + +# Shells do not agree about what this pattern should match, but it is +# certain that it must not crash and the missing close bracket must not +# be simply ignored. + +case B in +[[:alpha:]) echo bad ;; +esac diff --git a/shell_cmds/sh/tests/builtins/case21.0 b/shell_cmds/sh/tests/builtins/case21.0 new file mode 100644 index 0000000..7d865f0 --- /dev/null +++ b/shell_cmds/sh/tests/builtins/case21.0 @@ -0,0 +1,10 @@ +# $FreeBSD: head/bin/sh/tests/builtins/case21.0 318258 2017-05-13 20:28:32Z jilles $ + +case 5 in +[0$((-9))]) ;; +*) echo bad1 ;; +esac + +case - in +[0$((-9))]) echo bad2 ;; +esac diff --git a/shell_cmds/sh/tests/builtins/case22.0 b/shell_cmds/sh/tests/builtins/case22.0 new file mode 100644 index 0000000..a89c0bd --- /dev/null +++ b/shell_cmds/sh/tests/builtins/case22.0 @@ -0,0 +1,10 @@ +# $FreeBSD: head/bin/sh/tests/builtins/case22.0 318269 2017-05-14 13:14:19Z jilles $ + +case 5 in +[0"$((-9))"]) echo bad1 ;; +esac + +case - in +[0"$((-9))"]) ;; +*) echo bad2 ;; +esac diff --git a/shell_cmds/sh/tests/builtins/case3.0 b/shell_cmds/sh/tests/builtins/case3.0 new file mode 100644 index 0000000..a2efd87 --- /dev/null +++ b/shell_cmds/sh/tests/builtins/case3.0 @@ -0,0 +1,95 @@ +# Generated by ./test-fnmatch -s 2, do not edit. +# $FreeBSD: head/bin/sh/tests/builtins/case3.0 207821 2010-05-09 16:15:40Z jilles $ +failures= +failed() { printf '%s\n' "Failed: $1 '$2' '$3'"; failures=x$failures; } +# We do not treat a backslash specially in this case, +# but this is not the case in all shells. +netestmatch() { case $2 in $1) ;; *) failed netestmatch "$@";; esac; } +netestnomatch() { case $2 in $1) failed netestnomatch "$@";; esac; } +netestmatch '' '' +netestmatch 'a' 'a' +netestnomatch 'a' 'b' +netestnomatch 'a' 'A' +netestmatch '*' 'a' +netestmatch '*' 'aa' +netestmatch '*a' 'a' +netestnomatch '*a' 'b' +netestnomatch '*a*' 'b' +netestmatch '*a*b*' 'ab' +netestmatch '*a*b*' 'qaqbq' +netestmatch '*a*bb*' 'qaqbqbbq' +netestmatch '*a*bc*' 'qaqbqbcq' +netestmatch '*a*bb*' 'qaqbqbb' +netestmatch '*a*bc*' 'qaqbqbc' +netestmatch '*a*bb' 'qaqbqbb' +netestmatch '*a*bc' 'qaqbqbc' +netestnomatch '*a*bb' 'qaqbqbbq' +netestnomatch '*a*bc' 'qaqbqbcq' +netestnomatch '*a*a*a*a*a*a*a*a*a*a*' 'aaaaaaaaa' +netestmatch '*a*a*a*a*a*a*a*a*a*a*' 'aaaaaaaaaa' +netestmatch '*a*a*a*a*a*a*a*a*a*a*' 'aaaaaaaaaaa' +netestnomatch '.*.*.*.*.*.*.*.*.*.*' '.........' +netestmatch '.*.*.*.*.*.*.*.*.*.*' '..........' +netestmatch '.*.*.*.*.*.*.*.*.*.*' '...........' +netestnomatch '*?*?*?*?*?*?*?*?*?*?*' '123456789' +netestnomatch '??????????*' '123456789' +netestnomatch '*??????????' '123456789' +netestmatch '*?*?*?*?*?*?*?*?*?*?*' '1234567890' +netestmatch '??????????*' '1234567890' +netestmatch '*??????????' '1234567890' +netestmatch '*?*?*?*?*?*?*?*?*?*?*' '12345678901' +netestmatch '??????????*' '12345678901' +netestmatch '*??????????' '12345678901' +netestmatch '[x]' 'x' +netestmatch '[*]' '*' +netestmatch '[?]' '?' +netestmatch '[' '[' +netestmatch '[[]' '[' +netestnomatch '[[]' 'x' +netestnomatch '[*]' '' +netestnomatch '[*]' 'x' +netestnomatch '[?]' 'x' +netestmatch '*[*]*' 'foo*foo' +netestnomatch '*[*]*' 'foo' +netestmatch '[0-9]' '0' +netestmatch '[0-9]' '5' +netestmatch '[0-9]' '9' +netestnomatch '[0-9]' '/' +netestnomatch '[0-9]' ':' +netestnomatch '[0-9]' '*' +netestnomatch '[!0-9]' '0' +netestnomatch '[!0-9]' '5' +netestnomatch '[!0-9]' '9' +netestmatch '[!0-9]' '/' +netestmatch '[!0-9]' ':' +netestmatch '[!0-9]' '*' +netestmatch '*[0-9]' 'a0' +netestmatch '*[0-9]' 'a5' +netestmatch '*[0-9]' 'a9' +netestnomatch '*[0-9]' 'a/' +netestnomatch '*[0-9]' 'a:' +netestnomatch '*[0-9]' 'a*' +netestnomatch '*[!0-9]' 'a0' +netestnomatch '*[!0-9]' 'a5' +netestnomatch '*[!0-9]' 'a9' +netestmatch '*[!0-9]' 'a/' +netestmatch '*[!0-9]' 'a:' +netestmatch '*[!0-9]' 'a*' +netestmatch '*[0-9]' 'a00' +netestmatch '*[0-9]' 'a55' +netestmatch '*[0-9]' 'a99' +netestmatch '*[0-9]' 'a0a0' +netestmatch '*[0-9]' 'a5a5' +netestmatch '*[0-9]' 'a9a9' +netestmatch '\*' '\*' +netestmatch '\?' '\?' +netestmatch '\' '\' +netestnomatch '\\' '\' +netestmatch '\\' '\\' +netestmatch '*\*' 'foo\foo' +netestnomatch '*\*' 'foo' +netestmatch '.*' '.' +netestmatch '.*' '..' +netestmatch '.*' '.a' +netestmatch 'a*' 'a.' +[ -z "$failures" ] diff --git a/shell_cmds/sh/tests/builtins/case4.0 b/shell_cmds/sh/tests/builtins/case4.0 new file mode 100644 index 0000000..7ba29ee --- /dev/null +++ b/shell_cmds/sh/tests/builtins/case4.0 @@ -0,0 +1,6 @@ +# $FreeBSD: head/bin/sh/tests/builtins/case4.0 220654 2011-04-15 15:14:58Z jilles $ + +set -- "*" +case x in +"$1") echo failed ;; +esac diff --git a/shell_cmds/sh/tests/builtins/case5.0 b/shell_cmds/sh/tests/builtins/case5.0 new file mode 100644 index 0000000..e8b141a --- /dev/null +++ b/shell_cmds/sh/tests/builtins/case5.0 @@ -0,0 +1,57 @@ +# $FreeBSD: head/bin/sh/tests/builtins/case5.0 221646 2011-05-08 11:32:20Z jilles $ + +unset LC_ALL +LC_CTYPE=en_US.UTF-8 +export LC_CTYPE + +c1=e +# a umlaut +c2=$(printf '\303\244') +# euro sign +c3=$(printf '\342\202\254') +# some sort of 't' outside BMP +c4=$(printf '\360\235\225\245') + +ok=0 +case $c1$c2$c3$c4 in +*) ok=1 ;; +esac +if [ $ok = 0 ]; then + echo wrong at $LINENO + exit 3 +fi + +case $c1$c2$c3$c4 in +$c1$c2$c3$c4) ;; +*) echo wrong at $LINENO ;; +esac + +case $c1$c2$c3$c4 in +"$c1$c2$c3$c4") ;; +*) echo wrong at $LINENO ;; +esac + +case $c1$c2$c3$c4 in +????) ;; +*) echo wrong at $LINENO ;; +esac + +case $c1.$c2.$c3.$c4 in +?.?.?.?) ;; +*) echo wrong at $LINENO ;; +esac + +case $c1$c2$c3$c4 in +[!a][!b][!c][!d]) ;; +*) echo wrong at $LINENO ;; +esac + +case $c1$c2$c3$c4 in +[$c1][$c2][$c3][$c4]) ;; +*) echo wrong at $LINENO ;; +esac + +case $c1$c2$c3$c4 in +["$c1"]["$c2"]["$c3"]["$c4"]) ;; +*) echo wrong at $LINENO ;; +esac diff --git a/shell_cmds/sh/tests/builtins/case6.0 b/shell_cmds/sh/tests/builtins/case6.0 new file mode 100644 index 0000000..7bc1da8 --- /dev/null +++ b/shell_cmds/sh/tests/builtins/case6.0 @@ -0,0 +1,52 @@ +# $FreeBSD: head/bin/sh/tests/builtins/case6.0 223007 2011-06-12 12:27:17Z jilles $ + +unset LC_ALL +LC_CTYPE=de_DE.ISO8859-1 +export LC_CTYPE + +c1=e +# o umlaut +c2=$(printf '\366') +# non-break space +c3=$(printf '\240') +c4=$(printf '\240') +# $c2$c3$c4 form one utf-8 character + +ok=0 +case $c1$c2$c3$c4 in +*) ok=1 ;; +esac +if [ $ok = 0 ]; then + echo wrong at $LINENO + exit 3 +fi + +case $c1$c2$c3$c4 in +$c1$c2$c3$c4) ;; +*) echo wrong at $LINENO ;; +esac + +case $c1$c2$c3$c4 in +"$c1$c2$c3$c4") ;; +*) echo wrong at $LINENO ;; +esac + +case $c1$c2$c3$c4 in +????) ;; +*) echo wrong at $LINENO ;; +esac + +case $c1$c2$c3$c4 in +[!$c2][!b][!c][!d]) ;; +*) echo wrong at $LINENO ;; +esac + +case $c1$c2$c3$c4 in +[$c1][$c2][$c3][$c4]) ;; +*) echo wrong at $LINENO ;; +esac + +case $c1$c2$c3$c4 in +["$c1"]["$c2"]["$c3"]["$c4"]) ;; +*) echo wrong at $LINENO ;; +esac diff --git a/shell_cmds/sh/tests/builtins/case7.0 b/shell_cmds/sh/tests/builtins/case7.0 new file mode 100644 index 0000000..b351ad5 --- /dev/null +++ b/shell_cmds/sh/tests/builtins/case7.0 @@ -0,0 +1,24 @@ +# $FreeBSD: head/bin/sh/tests/builtins/case7.0 302829 2016-07-14 09:34:42Z ache $ + +# Character ranges in a locale other than the POSIX locale, not specified +# by POSIX. + +unset LC_ALL +LC_CTYPE=de_DE.ISO8859-1 +export LC_CTYPE +LC_COLLATE=de_DE.ISO8859-1 +export LC_COLLATE + +c1=e +# o umlaut +c2=$(printf '\366') + +case $c1$c2 in +[a-z][a-z]) ;; +*) echo wrong at $LINENO ;; +esac + +case $c1$c2 in +[a-f][n-p]) ;; +*) echo wrong at $LINENO ;; +esac diff --git a/shell_cmds/sh/tests/builtins/case8.0 b/shell_cmds/sh/tests/builtins/case8.0 new file mode 100644 index 0000000..c63c6e7 --- /dev/null +++ b/shell_cmds/sh/tests/builtins/case8.0 @@ -0,0 +1,32 @@ +# $FreeBSD: head/bin/sh/tests/builtins/case8.0 223120 2011-06-15 21:48:10Z jilles $ + +case aZ_ in +[[:alpha:]_][[:upper:]_][[:alpha:]_]) ;; +*) echo Failed at $LINENO ;; +esac + +case ' ' in +[[:alpha:][:digit:]]) echo Failed at $LINENO ;; +[![:alpha:][:digit:]]) ;; +*) echo Failed at $LINENO ;; +esac + +case '.X.' in +*[[:lower:]]*) echo Failed at $LINENO ;; +*[[:upper:]]*) ;; +*) echo Failed at $LINENO ;; +esac + +case ' ' in +[![:print:]]) echo Failed at $LINENO ;; +[![:alnum:][:punct:]]) ;; +*) echo Failed at $LINENO ;; +esac + +case ' +' in +[[:print:]]) echo Failed at $LINENO ;; +[' +'[:digit:]]) ;; +*) echo Failed at $LINENO ;; +esac diff --git a/shell_cmds/sh/tests/builtins/case9.0 b/shell_cmds/sh/tests/builtins/case9.0 new file mode 100644 index 0000000..e374ef0 --- /dev/null +++ b/shell_cmds/sh/tests/builtins/case9.0 @@ -0,0 +1,39 @@ +# $FreeBSD: head/bin/sh/tests/builtins/case9.0 223186 2011-06-17 13:03:49Z jilles $ + +errors=0 + +f() { + result= + case $1 in + a) result=${result}a ;; + b) result=${result}b ;& + c) result=${result}c ;& + d) result=${result}d ;; + e) result=${result}e ;& + esac +} + +check() { + f "$1" + if [ "$result" != "$2" ]; then + printf "For %s, expected %s got %s\n" "$1" "$2" "$result" + errors=$((errors + 1)) + fi +} + +check '' '' +check a a +check b bcd +check c cd +check d d +check e e + +if ! (case 1 in + 1) false ;& + 2) true ;; +esac) then + echo "Subshell bad" + errors=$((errors + 1)) +fi + +exit $((errors != 0)) diff --git a/shell_cmds/sh/tests/builtins/cd1.0 b/shell_cmds/sh/tests/builtins/cd1.0 new file mode 100644 index 0000000..d797157 --- /dev/null +++ b/shell_cmds/sh/tests/builtins/cd1.0 @@ -0,0 +1,30 @@ +# $FreeBSD: head/bin/sh/tests/builtins/cd1.0 228975 2011-12-30 00:04:11Z uqs $ +set -e + +P=${TMPDIR:-/tmp} +cd $P +T=$(mktemp -d sh-test.XXXXXX) + +chmod 0 $T +if [ `id -u` -ne 0 ]; then + # Root can always cd, regardless of directory permissions. + cd -L $T 2>/dev/null && exit 1 + [ "$PWD" = "$P" ] + [ "$(pwd)" = "$P" ] + cd -P $T 2>/dev/null && exit 1 + [ "$PWD" = "$P" ] + [ "$(pwd)" = "$P" ] +fi + +chmod 755 $T +cd $T +mkdir -p 1/2/3 +ln -s 1/2 link1 +ln -s 2/3 1/link2 +(cd -L 1/../1 && [ "$(pwd -L)" = "$P/$T/1" ]) +(cd -L link1 && [ "$(pwd -L)" = "$P/$T/link1" ]) +(cd -L link1 && [ "$(pwd -P)" = "$P/$T/1/2" ]) +(cd -P link1 && [ "$(pwd -L)" = "$P/$T/1/2" ]) +(cd -P link1 && [ "$(pwd -P)" = "$P/$T/1/2" ]) + +rm -rf ${P}/${T} diff --git a/shell_cmds/sh/tests/builtins/cd10.0 b/shell_cmds/sh/tests/builtins/cd10.0 new file mode 100644 index 0000000..79d540f --- /dev/null +++ b/shell_cmds/sh/tests/builtins/cd10.0 @@ -0,0 +1,6 @@ +# $FreeBSD: head/bin/sh/tests/builtins/cd10.0 320340 2017-06-25 21:53:08Z jilles $ + +# Precondition +(cd /bin) || exit +# Verify write error is ignored. +$SH +m -ic 'CDPATH=/:; cd bin 1</dev/null' diff --git a/shell_cmds/sh/tests/builtins/cd2.0 b/shell_cmds/sh/tests/builtins/cd2.0 new file mode 100644 index 0000000..b7d39f3 --- /dev/null +++ b/shell_cmds/sh/tests/builtins/cd2.0 @@ -0,0 +1,16 @@ +# $FreeBSD: head/bin/sh/tests/builtins/cd2.0 216019 2010-11-28 22:49:58Z jilles $ +set -e + +L=$(getconf PATH_MAX / 2>/dev/null) || L=4096 +[ "$L" -lt 100000 ] 2>/dev/null || L=4096 +L=$((L+100)) +T=$(mktemp -d ${TMPDIR:-/tmp}/sh-test.XXXXXX) +trap 'rm -rf ${T}' 0 +cd $T +D=$T +while [ ${#D} -lt $L ]; do + mkdir veryverylongdirectoryname + cd veryverylongdirectoryname + D=$D/veryverylongdirectoryname +done +[ $(pwd | wc -c) -eq $((${#D} + 1)) ] # +\n diff --git a/shell_cmds/sh/tests/builtins/cd3.0 b/shell_cmds/sh/tests/builtins/cd3.0 new file mode 100644 index 0000000..aab8bc0 --- /dev/null +++ b/shell_cmds/sh/tests/builtins/cd3.0 @@ -0,0 +1,21 @@ +# $FreeBSD: head/bin/sh/tests/builtins/cd3.0 222154 2011-05-20 22:55:18Z jilles $ + +# If fully successful, cd -Pe must be like cd -P. + +set -e + +cd "${TMPDIR:-/tmp}" +cd -Pe / +[ "$PWD" = / ] +[ "$(pwd)" = / ] +cd "${TMPDIR:-/tmp}" +cd -eP / +[ "$PWD" = / ] +[ "$(pwd)" = / ] + +set +e + +# If cd -Pe cannot chdir, the exit status must be greater than 1. + +v=$( (cd -Pe /var/empty/nonexistent) 2>&1 >/dev/null) +[ $? -gt 1 ] && [ -n "$v" ] diff --git a/shell_cmds/sh/tests/builtins/cd4.0 b/shell_cmds/sh/tests/builtins/cd4.0 new file mode 100644 index 0000000..da01b31 --- /dev/null +++ b/shell_cmds/sh/tests/builtins/cd4.0 @@ -0,0 +1,38 @@ +# $FreeBSD: head/bin/sh/tests/builtins/cd4.0 222154 2011-05-20 22:55:18Z jilles $ + +# This test assumes that whatever mechanism cd -P uses to determine the +# pathname to the current directory if it is longer than PATH_MAX requires +# read permission on all parent directories. It also works if this +# requirement always applies. + +set -e +L=$(getconf PATH_MAX / 2>/dev/null) || L=4096 +[ "$L" -lt 100000 ] 2>/dev/null || L=4096 +L=$((L+100)) +T=$(mktemp -d ${TMPDIR:-/tmp}/sh-test.XXXXXX) +trap 'chmod u+r ${T}; rm -rf ${T}' 0 +cd -Pe $T +D=$(pwd) +chmod u-r "$D" +if [ -r "$D" ]; then + # Running as root, cannot test. + exit 0 +fi +set +e +while [ ${#D} -lt $L ]; do + mkdir veryverylongdirectoryname || exit + cd -Pe veryverylongdirectoryname 2>/dev/null + r=$? + [ $r -gt 1 ] && exit $r + if [ $r -eq 1 ]; then + # Verify that the directory was changed correctly. + cd -Pe .. || exit + [ "$(pwd)" = "$D" ] || exit + # Verify that omitting -e results in success. + cd -P veryverylongdirectoryname 2>/dev/null || exit + exit 0 + fi + D=$D/veryverylongdirectoryname +done +echo "cd -Pe never returned 1" +exit 0 diff --git a/shell_cmds/sh/tests/builtins/cd5.0 b/shell_cmds/sh/tests/builtins/cd5.0 new file mode 100644 index 0000000..8a285e0 --- /dev/null +++ b/shell_cmds/sh/tests/builtins/cd5.0 @@ -0,0 +1,23 @@ +# $FreeBSD: head/bin/sh/tests/builtins/cd5.0 222379 2011-05-27 19:36:07Z jilles $ + +set -e +T=$(mktemp -d "${TMPDIR:-/tmp}/sh-test.XXXXXX") +trap 'rm -rf "$T"' 0 + +cd -P "$T" +D=$(pwd) + +mkdir a a/1 b b/1 b/2 + +CDPATH=$D/a: +# Basic test. +cd 1 >/dev/null +[ "$(pwd)" = "$D/a/1" ] +# Test that the current directory is not checked before CDPATH. +cd "$D/b" +cd 1 >/dev/null +[ "$(pwd)" = "$D/a/1" ] +# Test not using a CDPATH entry. +cd "$D/b" +cd 2 +[ "$(pwd)" = "$D/b/2" ] diff --git a/shell_cmds/sh/tests/builtins/cd6.0 b/shell_cmds/sh/tests/builtins/cd6.0 new file mode 100644 index 0000000..171c996 --- /dev/null +++ b/shell_cmds/sh/tests/builtins/cd6.0 @@ -0,0 +1,10 @@ +# $FreeBSD: head/bin/sh/tests/builtins/cd6.0 222381 2011-05-27 20:01:46Z jilles $ + +set -e +cd -P /bin +d=$PWD +CDPATH=/: +cd -P . +[ "$d" = "$PWD" ] +cd -P ./ +[ "$d" = "$PWD" ] diff --git a/shell_cmds/sh/tests/builtins/cd7.0 b/shell_cmds/sh/tests/builtins/cd7.0 new file mode 100644 index 0000000..d4cae39 --- /dev/null +++ b/shell_cmds/sh/tests/builtins/cd7.0 @@ -0,0 +1,15 @@ +# $FreeBSD: head/bin/sh/tests/builtins/cd7.0 222381 2011-05-27 20:01:46Z jilles $ + +set -e +cd /usr/bin +[ "$PWD" = /usr/bin ] +CDPATH=/: +cd . +[ "$PWD" = /usr/bin ] +cd ./ +[ "$PWD" = /usr/bin ] +cd .. +[ "$PWD" = /usr ] +cd /usr/bin +cd ../ +[ "$PWD" = /usr ] diff --git a/shell_cmds/sh/tests/builtins/cd8.0 b/shell_cmds/sh/tests/builtins/cd8.0 new file mode 100644 index 0000000..ab17d38 --- /dev/null +++ b/shell_cmds/sh/tests/builtins/cd8.0 @@ -0,0 +1,26 @@ +# $FreeBSD: head/bin/sh/tests/builtins/cd8.0 230095 2012-01-13 23:32:27Z jilles $ + +# The exact wording of the error message is not standardized, but giving +# a description of the errno is useful. + +LC_ALL=C +export LC_ALL +r=0 + +t() { + exec 3>&1 + errmsg=`cd "$1" 2>&1 >&3 3>&-` + exec 3>&- + case $errmsg in + *[Nn]ot\ a\ directory*) + ;; + *) + printf "Wrong error message for %s: %s\n" "$1" "$errmsg" + r=3 + ;; + esac +} + +t /dev/tty +t /dev/tty/x +exit $r diff --git a/shell_cmds/sh/tests/builtins/cd9.0 b/shell_cmds/sh/tests/builtins/cd9.0 new file mode 100644 index 0000000..e929878 --- /dev/null +++ b/shell_cmds/sh/tests/builtins/cd9.0 @@ -0,0 +1,8 @@ +# $FreeBSD: head/bin/sh/tests/builtins/cd9.0 293371 2016-01-07 21:46:07Z jilles $ + +cd /dev +cd /bin +cd - >/dev/null +pwd +cd - >/dev/null +pwd diff --git a/shell_cmds/sh/tests/builtins/cd9.0.stdout b/shell_cmds/sh/tests/builtins/cd9.0.stdout new file mode 100644 index 0000000..dac16a7 --- /dev/null +++ b/shell_cmds/sh/tests/builtins/cd9.0.stdout @@ -0,0 +1,2 @@ +/dev +/bin diff --git a/shell_cmds/sh/tests/builtins/command1.0 b/shell_cmds/sh/tests/builtins/command1.0 new file mode 100644 index 0000000..5454c3f --- /dev/null +++ b/shell_cmds/sh/tests/builtins/command1.0 @@ -0,0 +1,5 @@ +# $FreeBSD: head/bin/sh/tests/builtins/command1.0 151797 2005-10-28 14:02:42Z stefanf $ +true() { + false +} +command true diff --git a/shell_cmds/sh/tests/builtins/command10.0 b/shell_cmds/sh/tests/builtins/command10.0 new file mode 100644 index 0000000..08bddaf --- /dev/null +++ b/shell_cmds/sh/tests/builtins/command10.0 @@ -0,0 +1,14 @@ +# $FreeBSD: head/bin/sh/tests/builtins/command10.0 204802 2010-03-06 17:31:09Z jilles $ + +failures=0 + +check() { + if ! eval "[ $* ]"; then + echo "Failed: $*" + : $((failures += 1)) + fi +} + +check '"$(f() { shift x; }; { command eval f 2>/dev/null; } >/dev/null; echo hi)" = hi' + +exit $((failures > 0)) diff --git a/shell_cmds/sh/tests/builtins/command11.0 b/shell_cmds/sh/tests/builtins/command11.0 new file mode 100644 index 0000000..0eb2047 --- /dev/null +++ b/shell_cmds/sh/tests/builtins/command11.0 @@ -0,0 +1,14 @@ +# $FreeBSD: head/bin/sh/tests/builtins/command11.0 205154 2010-03-14 14:24:35Z jilles $ + +failures=0 + +check() { + if ! eval "[ $* ]"; then + echo "Failed: $*" + : $((failures += 1)) + fi +} + +check '"$({ command eval \{ shift x\; \} 2\>/dev/null; } >/dev/null; echo hi)" = hi' + +exit $((failures > 0)) diff --git a/shell_cmds/sh/tests/builtins/command12.0 b/shell_cmds/sh/tests/builtins/command12.0 new file mode 100644 index 0000000..6d9e8e7 --- /dev/null +++ b/shell_cmds/sh/tests/builtins/command12.0 @@ -0,0 +1,7 @@ +# $FreeBSD: head/bin/sh/tests/builtins/command12.0 257929 2013-11-10 23:00:39Z jilles $ + +alias aa=echo\ \'\"\' +cmd=$(command -v aa) +alias aa=echo\ bad +eval "$cmd" +[ "$(eval aa)" = \" ] diff --git a/shell_cmds/sh/tests/builtins/command2.0 b/shell_cmds/sh/tests/builtins/command2.0 new file mode 100644 index 0000000..7b49092 --- /dev/null +++ b/shell_cmds/sh/tests/builtins/command2.0 @@ -0,0 +1,3 @@ +# $FreeBSD: head/bin/sh/tests/builtins/command2.0 151797 2005-10-28 14:02:42Z stefanf $ +PATH= +command -p cat < /dev/null diff --git a/shell_cmds/sh/tests/builtins/command3.0 b/shell_cmds/sh/tests/builtins/command3.0 new file mode 100644 index 0000000..6d6355d --- /dev/null +++ b/shell_cmds/sh/tests/builtins/command3.0 @@ -0,0 +1,14 @@ +# $FreeBSD: head/bin/sh/tests/builtins/command3.0 211399 2010-08-16 17:18:08Z jilles $ +command -v ls +command -v true +command -v /bin/ls + +fun() { + : +} +command -v fun +command -v break +command -v if + +alias foo=bar +command -v foo diff --git a/shell_cmds/sh/tests/builtins/command3.0.stdout b/shell_cmds/sh/tests/builtins/command3.0.stdout new file mode 100644 index 0000000..67b8691 --- /dev/null +++ b/shell_cmds/sh/tests/builtins/command3.0.stdout @@ -0,0 +1,7 @@ +/bin/ls +true +/bin/ls +fun +break +if +alias foo=bar diff --git a/shell_cmds/sh/tests/builtins/command4.0 b/shell_cmds/sh/tests/builtins/command4.0 new file mode 100644 index 0000000..2349669 --- /dev/null +++ b/shell_cmds/sh/tests/builtins/command4.0 @@ -0,0 +1,2 @@ +# $FreeBSD: head/bin/sh/tests/builtins/command4.0 211973 2010-08-29 20:53:24Z jilles $ +! command -v nonexisting diff --git a/shell_cmds/sh/tests/builtins/command5.0 b/shell_cmds/sh/tests/builtins/command5.0 new file mode 100644 index 0000000..7c99b52 --- /dev/null +++ b/shell_cmds/sh/tests/builtins/command5.0 @@ -0,0 +1,15 @@ +# $FreeBSD: head/bin/sh/tests/builtins/command5.0 211399 2010-08-16 17:18:08Z jilles $ +command -V ls +command -V true +command -V /bin/ls + +fun() { + : +} +command -V fun +command -V break +command -V if +command -V { + +alias foo=bar +command -V foo diff --git a/shell_cmds/sh/tests/builtins/command5.0.stdout b/shell_cmds/sh/tests/builtins/command5.0.stdout new file mode 100644 index 0000000..523f7b2 --- /dev/null +++ b/shell_cmds/sh/tests/builtins/command5.0.stdout @@ -0,0 +1,8 @@ +ls is /bin/ls +true is a shell builtin +/bin/ls is /bin/ls +fun is a shell function +break is a special shell builtin +if is a shell keyword +{ is a shell keyword +foo is an alias for bar diff --git a/shell_cmds/sh/tests/builtins/command6.0 b/shell_cmds/sh/tests/builtins/command6.0 new file mode 100644 index 0000000..d1902ed --- /dev/null +++ b/shell_cmds/sh/tests/builtins/command6.0 @@ -0,0 +1,22 @@ +# $FreeBSD: head/bin/sh/tests/builtins/command6.0 218356 2011-02-05 23:00:24Z jilles $ +PATH=/var/empty +case $(command -pV ls) in +*/var/empty/ls*) + echo "Failed: \$(command -pV ls) should not match */var/empty/ls*" ;; +"ls is"*" "/*/ls) ;; +*) + echo "Failed: \$(command -pV ls) match \"ls is\"*\" \"/*/ls" ;; +esac +command -pV true +command -pV /bin/ls + +fun() { + : +} +command -pV fun +command -pV break +command -pV if +command -pV { + +alias foo=bar +command -pV foo diff --git a/shell_cmds/sh/tests/builtins/command6.0.stdout b/shell_cmds/sh/tests/builtins/command6.0.stdout new file mode 100644 index 0000000..3180207 --- /dev/null +++ b/shell_cmds/sh/tests/builtins/command6.0.stdout @@ -0,0 +1,7 @@ +true is a shell builtin +/bin/ls is /bin/ls +fun is a shell function +break is a special shell builtin +if is a shell keyword +{ is a shell keyword +foo is an alias for bar diff --git a/shell_cmds/sh/tests/builtins/command7.0 b/shell_cmds/sh/tests/builtins/command7.0 new file mode 100644 index 0000000..b8351f2 --- /dev/null +++ b/shell_cmds/sh/tests/builtins/command7.0 @@ -0,0 +1,34 @@ +# $FreeBSD: head/bin/sh/tests/builtins/command7.0 218356 2011-02-05 23:00:24Z jilles $ + +failures=0 + +check() { + if ! eval "[ $* ]"; then + echo "Failed: $*" + : $((failures += 1)) + fi +} + +check '"$(PATH=/libexec command -V ld-elf.so.1)" = "ld-elf.so.1 is /libexec/ld-elf.so.1"' +check '"$(PATH=/libexec command -V ld-elf.so.1; :)" = "ld-elf.so.1 is /libexec/ld-elf.so.1"' +check '"$(PATH=/libexec command -pv ld-elf.so.1)" = ""' +check '"$(PATH=/libexec command -pv ld-elf.so.1; :)" = ""' + +PATH=/libexec:$PATH + +check '"$(command -V ld-elf.so.1)" = "ld-elf.so.1 is /libexec/ld-elf.so.1"' +check '"$(command -V ld-elf.so.1; :)" = "ld-elf.so.1 is /libexec/ld-elf.so.1"' +check '"$(command -pv ld-elf.so.1)" = ""' +check '"$(command -pv ld-elf.so.1; :)" = ""' + +PATH=/libexec + +check '"$(command -v ls)" = ""' +case $(command -pv ls) in +/*/ls) ;; +*) + echo "Failed: \$(command -pv ls) match /*/ls" + : $((failures += 1)) ;; +esac + +exit $((failures > 0)) diff --git a/shell_cmds/sh/tests/builtins/command8.0 b/shell_cmds/sh/tests/builtins/command8.0 new file mode 100644 index 0000000..54848d1 --- /dev/null +++ b/shell_cmds/sh/tests/builtins/command8.0 @@ -0,0 +1,45 @@ +# $FreeBSD: head/bin/sh/tests/builtins/command8.0 213738 2010-10-12 18:20:38Z obrien $ +IFS=, + +SPECIAL="break,\ + :,\ + continue,\ + . /dev/null,\ + eval,\ + exec,\ + export -p,\ + readonly -p,\ + set,\ + shift 0,\ + times,\ + trap,\ + unset foo" + +set -e + +# Check that special builtins can be executed via "command". + +set -- ${SPECIAL} +for cmd in "$@" +do + ${SH} -c "v=:; while \$v; do v=false; command ${cmd}; done" >/dev/null +done + +while :; do + command break + echo Error on line $LINENO +done + +set p q r +command shift 2 +if [ $# -ne 1 ]; then + echo Error on line $LINENO +fi + +( + command exec >/dev/null + echo Error on line $LINENO +) + +set +e +! command shift 2 2>/dev/null diff --git a/shell_cmds/sh/tests/builtins/command9.0 b/shell_cmds/sh/tests/builtins/command9.0 new file mode 100644 index 0000000..40f98c1 --- /dev/null +++ b/shell_cmds/sh/tests/builtins/command9.0 @@ -0,0 +1,14 @@ +# $FreeBSD: head/bin/sh/tests/builtins/command9.0 204801 2010-03-06 17:09:22Z jilles $ + +failures=0 + +check() { + if ! eval "[ $* ]"; then + echo "Failed: $*" + : $((failures += 1)) + fi +} + +check '"$({ command eval shift x 2>/dev/null; } >/dev/null; echo hi)" = hi' + +exit $((failures > 0)) diff --git a/shell_cmds/sh/tests/builtins/dot1.0 b/shell_cmds/sh/tests/builtins/dot1.0 new file mode 100644 index 0000000..0aa69a8 --- /dev/null +++ b/shell_cmds/sh/tests/builtins/dot1.0 @@ -0,0 +1,21 @@ +# $FreeBSD: head/bin/sh/tests/builtins/dot1.0 208629 2010-05-28 22:08:34Z jilles $ + +failures= +failure() { + echo "Error at line $1" >&2 + failures=x$failures +} + +T=$(mktemp -d ${TMPDIR:-/tmp}/sh-test.XXXXXX) || exit +trap 'rm -rf $T' 0 +cd $T || exit 3 +unset x +echo 'x=2' >testscript +. ./testscript +[ "$x" = 2 ] || failure $LINENO +cd / || exit 3 +x=1 +PATH=$T:$PATH . testscript +[ "$x" = 2 ] || failure $LINENO + +test -z "$failures" diff --git a/shell_cmds/sh/tests/builtins/dot2.0 b/shell_cmds/sh/tests/builtins/dot2.0 new file mode 100644 index 0000000..dba591e --- /dev/null +++ b/shell_cmds/sh/tests/builtins/dot2.0 @@ -0,0 +1,21 @@ +# $FreeBSD: head/bin/sh/tests/builtins/dot2.0 208630 2010-05-28 22:40:24Z jilles $ + +failures= +failure() { + echo "Error at line $1" >&2 + failures=x$failures +} + +T=$(mktemp -d ${TMPDIR:-/tmp}/sh-test.XXXXXX) || exit +trap 'rm -rf $T' 0 +cd $T || exit 3 +unset x +echo 'x=2' >testscript +. -- ./testscript +[ "$x" = 2 ] || failure $LINENO +cd / || exit 3 +x=1 +PATH=$T:$PATH . -- testscript +[ "$x" = 2 ] || failure $LINENO + +test -z "$failures" diff --git a/shell_cmds/sh/tests/builtins/dot3.0 b/shell_cmds/sh/tests/builtins/dot3.0 new file mode 100644 index 0000000..d1589a0 --- /dev/null +++ b/shell_cmds/sh/tests/builtins/dot3.0 @@ -0,0 +1,10 @@ +# $FreeBSD: head/bin/sh/tests/builtins/dot3.0 219390 2011-03-07 23:52:23Z jilles $ + +# . should return 0 if no command was executed. + +if false; then + exit 3 +else + . /dev/null + exit $? +fi diff --git a/shell_cmds/sh/tests/builtins/dot4.0 b/shell_cmds/sh/tests/builtins/dot4.0 new file mode 100644 index 0000000..d280f35 --- /dev/null +++ b/shell_cmds/sh/tests/builtins/dot4.0 @@ -0,0 +1,12 @@ +# $FreeBSD: head/bin/sh/tests/builtins/dot4.0 222174 2011-05-22 12:15:14Z jilles $ + +v=abcd +v=$v$v$v$v +v=$v$v$v$v +v=$v$v$v$v +v=$v$v$v$v +v=$v$v$v$v +r=$( ( + trap 'exit 0' 0 + . "$v" +) 2>&1 >/dev/null) && [ -n "$r" ] diff --git a/shell_cmds/sh/tests/builtins/echo1.0 b/shell_cmds/sh/tests/builtins/echo1.0 new file mode 100644 index 0000000..34d75a6 --- /dev/null +++ b/shell_cmds/sh/tests/builtins/echo1.0 @@ -0,0 +1,6 @@ +# $FreeBSD: head/bin/sh/tests/builtins/echo1.0 305305 2016-09-02 21:13:46Z jilles $ + +# Not specified by POSIX. + +[ "`echo -n a b; echo c d; echo e f`" = "a bc d +e f" ] diff --git a/shell_cmds/sh/tests/builtins/echo2.0 b/shell_cmds/sh/tests/builtins/echo2.0 new file mode 100644 index 0000000..3eae51b --- /dev/null +++ b/shell_cmds/sh/tests/builtins/echo2.0 @@ -0,0 +1,7 @@ +# $FreeBSD: head/bin/sh/tests/builtins/echo2.0 305305 2016-09-02 21:13:46Z jilles $ + +# Not specified by POSIX. + +a=`echo -e '\a\b\e\f\n\r\t\v\\\\\0041\c'; echo .` +b=`printf '\a\b\033\f\n\r\t\v\\\\!.'` +[ "$a" = "$b" ] diff --git a/shell_cmds/sh/tests/builtins/echo3.0 b/shell_cmds/sh/tests/builtins/echo3.0 new file mode 100644 index 0000000..aa86443 --- /dev/null +++ b/shell_cmds/sh/tests/builtins/echo3.0 @@ -0,0 +1,5 @@ +# $FreeBSD: head/bin/sh/tests/builtins/echo3.0 305305 2016-09-02 21:13:46Z jilles $ + +# Not specified by POSIX. + +[ "`echo -e 'a\cb' c; echo d`" = "ad" ] diff --git a/shell_cmds/sh/tests/builtins/eval1.0 b/shell_cmds/sh/tests/builtins/eval1.0 new file mode 100644 index 0000000..ad099ce --- /dev/null +++ b/shell_cmds/sh/tests/builtins/eval1.0 @@ -0,0 +1,9 @@ +# $FreeBSD: head/bin/sh/tests/builtins/eval1.0 193178 2009-05-31 17:23:27Z stefanf $ +set -e + +eval +eval "" "" +eval "true" +! eval "false + +" diff --git a/shell_cmds/sh/tests/builtins/eval2.0 b/shell_cmds/sh/tests/builtins/eval2.0 new file mode 100644 index 0000000..4df41ea --- /dev/null +++ b/shell_cmds/sh/tests/builtins/eval2.0 @@ -0,0 +1,7 @@ +# $FreeBSD: head/bin/sh/tests/builtins/eval2.0 194897 2009-06-24 20:22:54Z jilles $ + +eval ' +false + +' && exit 1 +exit 0 diff --git a/shell_cmds/sh/tests/builtins/eval3.0 b/shell_cmds/sh/tests/builtins/eval3.0 new file mode 100644 index 0000000..04f9720 --- /dev/null +++ b/shell_cmds/sh/tests/builtins/eval3.0 @@ -0,0 +1,9 @@ +# $FreeBSD: head/bin/sh/tests/builtins/eval3.0 196607 2009-08-27 22:23:23Z jilles $ + +eval 'false;' && exit 1 +eval 'true;' || exit 1 +eval 'false; +' && exit 1 +eval 'true; +' || exit 1 +exit 0 diff --git a/shell_cmds/sh/tests/builtins/eval4.0 b/shell_cmds/sh/tests/builtins/eval4.0 new file mode 100644 index 0000000..82c3ea9 --- /dev/null +++ b/shell_cmds/sh/tests/builtins/eval4.0 @@ -0,0 +1,5 @@ +# $FreeBSD: head/bin/sh/tests/builtins/eval4.0 210738 2010-08-01 22:39:07Z jilles $ + +# eval should preserve $? from command substitutions when starting +# the parsed command. +[ $(eval 'echo $?' $(false)) = 1 ] diff --git a/shell_cmds/sh/tests/builtins/eval5.0 b/shell_cmds/sh/tests/builtins/eval5.0 new file mode 100644 index 0000000..89e79b8 --- /dev/null +++ b/shell_cmds/sh/tests/builtins/eval5.0 @@ -0,0 +1,4 @@ +# $FreeBSD: head/bin/sh/tests/builtins/eval5.0 210829 2010-08-03 22:17:29Z jilles $ + +# eval should return 0 if no command was executed. +eval $(false) diff --git a/shell_cmds/sh/tests/builtins/eval6.0 b/shell_cmds/sh/tests/builtins/eval6.0 new file mode 100644 index 0000000..bfce89f --- /dev/null +++ b/shell_cmds/sh/tests/builtins/eval6.0 @@ -0,0 +1,5 @@ +# $FreeBSD: head/bin/sh/tests/builtins/eval6.0 249220 2013-04-06 22:30:46Z jilles $ + +# eval should preserve $? from command substitutions when starting +# the parsed command. +[ $(false; eval 'echo $?' $(:)) = 0 ] diff --git a/shell_cmds/sh/tests/builtins/eval7.0 b/shell_cmds/sh/tests/builtins/eval7.0 new file mode 100644 index 0000000..f694e73 --- /dev/null +++ b/shell_cmds/sh/tests/builtins/eval7.0 @@ -0,0 +1,9 @@ +# $FreeBSD: head/bin/sh/tests/builtins/eval7.0 272983 2014-10-12 13:12:06Z jilles $ +# Assumes that break can break out of a loop outside eval. + +while :; do + eval "break +echo bad1" + echo bad2 + exit 3 +done diff --git a/shell_cmds/sh/tests/builtins/eval8.7 b/shell_cmds/sh/tests/builtins/eval8.7 new file mode 100644 index 0000000..30902cc --- /dev/null +++ b/shell_cmds/sh/tests/builtins/eval8.7 @@ -0,0 +1,7 @@ +# $FreeBSD: head/bin/sh/tests/builtins/eval8.7 272983 2014-10-12 13:12:06Z jilles $ + +f() { + eval "return 7 +echo bad2" +} +f diff --git a/shell_cmds/sh/tests/builtins/exec1.0 b/shell_cmds/sh/tests/builtins/exec1.0 new file mode 100644 index 0000000..8c5bb01 --- /dev/null +++ b/shell_cmds/sh/tests/builtins/exec1.0 @@ -0,0 +1,25 @@ +# $FreeBSD: head/bin/sh/tests/builtins/exec1.0 213738 2010-10-12 18:20:38Z obrien $ + +failures= +failure() { + echo "Error at line $1" >&2 + failures=x$failures +} + +( + exec >/dev/null + echo bad +) +[ $? = 0 ] || failure $LINENO +( + exec ${SH} -c 'exit 42' + echo bad +) +[ $? = 42 ] || failure $LINENO +( + exec /var/empty/nosuch + echo bad +) 2>/dev/null +[ $? = 127 ] || failure $LINENO + +test -z "$failures" diff --git a/shell_cmds/sh/tests/builtins/exec2.0 b/shell_cmds/sh/tests/builtins/exec2.0 new file mode 100644 index 0000000..5395ba6 --- /dev/null +++ b/shell_cmds/sh/tests/builtins/exec2.0 @@ -0,0 +1,25 @@ +# $FreeBSD: head/bin/sh/tests/builtins/exec2.0 213738 2010-10-12 18:20:38Z obrien $ + +failures= +failure() { + echo "Error at line $1" >&2 + failures=x$failures +} + +( + exec -- >/dev/null + echo bad +) +[ $? = 0 ] || failure $LINENO +( + exec -- ${SH} -c 'exit 42' + echo bad +) +[ $? = 42 ] || failure $LINENO +( + exec -- /var/empty/nosuch + echo bad +) 2>/dev/null +[ $? = 127 ] || failure $LINENO + +test -z "$failures" diff --git a/shell_cmds/sh/tests/builtins/exit1.0 b/shell_cmds/sh/tests/builtins/exit1.0 new file mode 100644 index 0000000..4c378f2 --- /dev/null +++ b/shell_cmds/sh/tests/builtins/exit1.0 @@ -0,0 +1,6 @@ +# $FreeBSD: head/bin/sh/tests/builtins/exit1.0 216871 2011-01-01 15:25:15Z jilles $ + +# exit with an argument should overwrite the exit status in an EXIT trap. + +trap 'true; exit $?' 0 +false diff --git a/shell_cmds/sh/tests/builtins/exit2.8 b/shell_cmds/sh/tests/builtins/exit2.8 new file mode 100644 index 0000000..51d38f7 --- /dev/null +++ b/shell_cmds/sh/tests/builtins/exit2.8 @@ -0,0 +1,7 @@ +# $FreeBSD: head/bin/sh/tests/builtins/exit2.8 217172 2011-01-08 23:00:38Z jilles $ + +# exit without arguments is the same as exit $? outside a trap. + +trap 'true; true' 0 +(exit 8) +exit diff --git a/shell_cmds/sh/tests/builtins/exit3.0 b/shell_cmds/sh/tests/builtins/exit3.0 new file mode 100644 index 0000000..4e0c96c --- /dev/null +++ b/shell_cmds/sh/tests/builtins/exit3.0 @@ -0,0 +1,5 @@ +# $FreeBSD: head/bin/sh/tests/builtins/exit3.0 217175 2011-01-08 23:08:13Z jilles $ + +# exit without arguments differs from exit $? in an EXIT trap. + +trap 'false; exit' 0 diff --git a/shell_cmds/sh/tests/builtins/export1.0 b/shell_cmds/sh/tests/builtins/export1.0 new file mode 100644 index 0000000..a09263a --- /dev/null +++ b/shell_cmds/sh/tests/builtins/export1.0 @@ -0,0 +1,3 @@ +# $FreeBSD: head/bin/sh/tests/builtins/export1.0 223183 2011-06-17 10:21:24Z jilles $ + +env @badness=1 ${SH} -c 'v=`export -p`; eval "$v"' diff --git a/shell_cmds/sh/tests/builtins/fc1.0 b/shell_cmds/sh/tests/builtins/fc1.0 new file mode 100644 index 0000000..042cb33 --- /dev/null +++ b/shell_cmds/sh/tests/builtins/fc1.0 @@ -0,0 +1,27 @@ +# $FreeBSD: head/bin/sh/tests/builtins/fc1.0 213738 2010-10-12 18:20:38Z obrien $ +set -e +trap 'echo Broken pipe -- test failed' PIPE + +P=${TMPDIR:-/tmp} +cd $P +T=$(mktemp -d sh-test.XXXXXX) +cd $T + +mkfifo input output error +HISTFILE=/dev/null ${SH} +m -i <input >output 2>error & +{ + # Syntax error + echo ')' >&3 + # Read error message, shell will read new input now + read dummy <&5 + # Execute bad command again + echo 'fc -e true' >&3 + # Verify that the shell is still running + echo 'echo continued' >&3 || rc=3 + echo 'exit' >&3 || rc=3 + read line <&4 && [ "$line" = continued ] && : ${rc:=0} +} 3>input 4<output 5<error + +rm input output error +rmdir ${P}/${T} +exit ${rc:-3} diff --git a/shell_cmds/sh/tests/builtins/fc2.0 b/shell_cmds/sh/tests/builtins/fc2.0 new file mode 100644 index 0000000..a6151f7 --- /dev/null +++ b/shell_cmds/sh/tests/builtins/fc2.0 @@ -0,0 +1,34 @@ +# $FreeBSD: head/bin/sh/tests/builtins/fc2.0 213738 2010-10-12 18:20:38Z obrien $ +set -e +trap 'echo Broken pipe -- test failed' PIPE + +P=${TMPDIR:-/tmp} +cd $P +T=$(mktemp -d sh-test.XXXXXX) +cd $T + +mkfifo input output error +HISTFILE=/dev/null ${SH} +m -i <input >output 2>error & +exec 3>input +{ + # Command not found, containing slash + echo '/var/empty/nonexistent' >&3 + # Read error message, shell will read new input now + read dummy <&5 + # Execute bad command again + echo 'fc -e true; echo continued' >&3 + read dummy <&5 + read line <&4 && [ "$line" = continued ] && : ${rc:=0} + exec 3>&- + # Old sh duplicates itself after the fc, producing another line + # of output. + if read line <&4; then + echo "Extraneous output: $line" + rc=1 + fi +} 4<output 5<error +exec 3>&- + +rm input output error +rmdir ${P}/${T} +exit ${rc:-3} diff --git a/shell_cmds/sh/tests/builtins/for1.0 b/shell_cmds/sh/tests/builtins/for1.0 new file mode 100644 index 0000000..276cb0a --- /dev/null +++ b/shell_cmds/sh/tests/builtins/for1.0 @@ -0,0 +1,4 @@ +# $FreeBSD: head/bin/sh/tests/builtins/for1.0 226892 2011-10-28 23:02:21Z jilles $ + +false +for i in `false`; do exit 3; done diff --git a/shell_cmds/sh/tests/builtins/for2.0 b/shell_cmds/sh/tests/builtins/for2.0 new file mode 100644 index 0000000..2b702ce --- /dev/null +++ b/shell_cmds/sh/tests/builtins/for2.0 @@ -0,0 +1,9 @@ +# $FreeBSD: head/bin/sh/tests/builtins/for2.0 230463 2012-01-22 14:00:33Z jilles $ + +r=x +f() { return 42; } +f +for i in x; do + r=$? +done +[ "$r" = 42 ] diff --git a/shell_cmds/sh/tests/builtins/for3.0 b/shell_cmds/sh/tests/builtins/for3.0 new file mode 100644 index 0000000..a345caa --- /dev/null +++ b/shell_cmds/sh/tests/builtins/for3.0 @@ -0,0 +1,8 @@ +# $FreeBSD: head/bin/sh/tests/builtins/for3.0 230463 2012-01-22 14:00:33Z jilles $ + +r=x +f() { return 42; } +for i in x`f`; do + r=$? +done +[ "$r" = 42 ] diff --git a/shell_cmds/sh/tests/builtins/getopts1.0 b/shell_cmds/sh/tests/builtins/getopts1.0 new file mode 100644 index 0000000..d3e7415 --- /dev/null +++ b/shell_cmds/sh/tests/builtins/getopts1.0 @@ -0,0 +1,25 @@ +# $FreeBSD: head/bin/sh/tests/builtins/getopts1.0 297752 2016-04-09 16:06:13Z jilles $ + +printf -- '-1-\n' +set -- -abc +getopts "ab:" OPTION +printf '%s\n' "${OPTION}" + +# In this case 'getopts' should realize that we have not provided the +# required argument for "-b". +# Note that Solaris 10's (UNIX 03) /usr/xpg4/bin/sh, /bin/sh, and /bin/ksh; +# ksh93 20090505; pdksh 5.2.14p2; mksh R39c; bash 4.1 PL7; and zsh 4.3.10. +# all recognize that "b" is missing its argument on the *first* iteration +# of 'getopts' and do not produce the "a" in $OPTION. +printf -- '-2-\n' +set -- -ab +getopts "ab:" OPTION +printf '%s\n' "${OPTION}" +getopts "ab:" OPTION 3>&2 2>&1 >&3 3>&- +printf '%s\n' "${OPTION}" + +# The 'shift' is aimed at causing an error. +printf -- '-3-\n' +shift 1 +getopts "ab:" OPTION +printf '%s\n' "${OPTION}" diff --git a/shell_cmds/sh/tests/builtins/getopts1.0.stdout b/shell_cmds/sh/tests/builtins/getopts1.0.stdout new file mode 100644 index 0000000..a0a347e --- /dev/null +++ b/shell_cmds/sh/tests/builtins/getopts1.0.stdout @@ -0,0 +1,8 @@ +-1- +a +-2- +a +No arg for -b option +? +-3- +? diff --git a/shell_cmds/sh/tests/builtins/getopts10.0 b/shell_cmds/sh/tests/builtins/getopts10.0 new file mode 100644 index 0000000..7d34ddc --- /dev/null +++ b/shell_cmds/sh/tests/builtins/getopts10.0 @@ -0,0 +1,11 @@ +# $FreeBSD: head/bin/sh/tests/builtins/getopts10.0 293359 2016-01-07 20:48:24Z jilles $ + +set -- -x arg +opt=not +getopts x opt +r1=$? OPTIND1=$OPTIND opt1=$opt +: $(: $((OPTIND = 1))) +getopts x opt +r2=$? OPTIND2=$OPTIND +[ "$r1" = 0 ] && [ "$OPTIND1" = 2 ] && [ "$opt1" = x ] && [ "$r2" != 0 ] && + [ "$OPTIND2" = 2 ] diff --git a/shell_cmds/sh/tests/builtins/getopts2.0 b/shell_cmds/sh/tests/builtins/getopts2.0 new file mode 100644 index 0000000..607d35e --- /dev/null +++ b/shell_cmds/sh/tests/builtins/getopts2.0 @@ -0,0 +1,6 @@ +# $FreeBSD: head/bin/sh/tests/builtins/getopts2.0 297752 2016-04-09 16:06:13Z jilles $ +set - -ax +getopts ax option +set -C +getopts ax option +printf '%s\n' "$option" diff --git a/shell_cmds/sh/tests/builtins/getopts2.0.stdout b/shell_cmds/sh/tests/builtins/getopts2.0.stdout new file mode 100644 index 0000000..587be6b --- /dev/null +++ b/shell_cmds/sh/tests/builtins/getopts2.0.stdout @@ -0,0 +1 @@ +x diff --git a/shell_cmds/sh/tests/builtins/getopts3.0 b/shell_cmds/sh/tests/builtins/getopts3.0 new file mode 100644 index 0000000..6087d73 --- /dev/null +++ b/shell_cmds/sh/tests/builtins/getopts3.0 @@ -0,0 +1,6 @@ +# $FreeBSD: head/bin/sh/tests/builtins/getopts3.0 265616 2014-05-07 21:45:25Z jilles $ + +shift $# +getopts x opt +r=$? +[ "$r" != 0 ] && [ "$OPTIND" = 1 ] diff --git a/shell_cmds/sh/tests/builtins/getopts4.0 b/shell_cmds/sh/tests/builtins/getopts4.0 new file mode 100644 index 0000000..ea8cc3f --- /dev/null +++ b/shell_cmds/sh/tests/builtins/getopts4.0 @@ -0,0 +1,10 @@ +# $FreeBSD: head/bin/sh/tests/builtins/getopts4.0 265616 2014-05-07 21:45:25Z jilles $ + +set -- -x +opt=not +getopts x opt +r1=$? OPTIND1=$OPTIND opt1=$opt +getopts x opt +r2=$? OPTIND2=$OPTIND +[ "$r1" = 0 ] && [ "$OPTIND1" = 2 ] && [ "$opt1" = x ] && [ "$r2" != 0 ] && + [ "$OPTIND2" = 2 ] diff --git a/shell_cmds/sh/tests/builtins/getopts5.0 b/shell_cmds/sh/tests/builtins/getopts5.0 new file mode 100644 index 0000000..cd4eca6 --- /dev/null +++ b/shell_cmds/sh/tests/builtins/getopts5.0 @@ -0,0 +1,10 @@ +# $FreeBSD: head/bin/sh/tests/builtins/getopts5.0 265616 2014-05-07 21:45:25Z jilles $ + +set -- -x arg +opt=not +getopts x opt +r1=$? OPTIND1=$OPTIND opt1=$opt +getopts x opt +r2=$? OPTIND2=$OPTIND +[ "$r1" = 0 ] && [ "$OPTIND1" = 2 ] && [ "$opt1" = x ] && [ "$r2" != 0 ] && + [ "$OPTIND2" = 2 ] diff --git a/shell_cmds/sh/tests/builtins/getopts6.0 b/shell_cmds/sh/tests/builtins/getopts6.0 new file mode 100644 index 0000000..c415eda --- /dev/null +++ b/shell_cmds/sh/tests/builtins/getopts6.0 @@ -0,0 +1,7 @@ +# $FreeBSD: head/bin/sh/tests/builtins/getopts6.0 265844 2014-05-10 17:42:21Z jilles $ + +set -- -x -y +getopts :x var || echo "First getopts bad: $?" +getopts :x var +r=$? +[ r != 0 ] && [ "$OPTIND" = 3 ] diff --git a/shell_cmds/sh/tests/builtins/getopts7.0 b/shell_cmds/sh/tests/builtins/getopts7.0 new file mode 100644 index 0000000..58afa50 --- /dev/null +++ b/shell_cmds/sh/tests/builtins/getopts7.0 @@ -0,0 +1,6 @@ +# $FreeBSD: head/bin/sh/tests/builtins/getopts7.0 265844 2014-05-10 17:42:21Z jilles $ + +set -- -x +getopts :x: var +r=$? +[ r != 0 ] && [ "$OPTIND" = 2 ] diff --git a/shell_cmds/sh/tests/builtins/getopts8.0 b/shell_cmds/sh/tests/builtins/getopts8.0 new file mode 100644 index 0000000..35814b9 --- /dev/null +++ b/shell_cmds/sh/tests/builtins/getopts8.0 @@ -0,0 +1,8 @@ +# $FreeBSD: head/bin/sh/tests/builtins/getopts8.0 265849 2014-05-10 19:18:49Z jilles $ + +set -- -yz -wx +opt=wrong1 OPTARG=wrong2 +while getopts :x opt; do + echo "$opt:${OPTARG-unset}" +done +echo "OPTIND=$OPTIND" diff --git a/shell_cmds/sh/tests/builtins/getopts8.0.stdout b/shell_cmds/sh/tests/builtins/getopts8.0.stdout new file mode 100644 index 0000000..f10cefc --- /dev/null +++ b/shell_cmds/sh/tests/builtins/getopts8.0.stdout @@ -0,0 +1,5 @@ +?:y +?:z +?:w +x:unset +OPTIND=3 diff --git a/shell_cmds/sh/tests/builtins/getopts9.0 b/shell_cmds/sh/tests/builtins/getopts9.0 new file mode 100644 index 0000000..aa4abbc --- /dev/null +++ b/shell_cmds/sh/tests/builtins/getopts9.0 @@ -0,0 +1,9 @@ +# $FreeBSD: head/bin/sh/tests/builtins/getopts9.0 297752 2016-04-09 16:06:13Z jilles $ + +args='-ab' +getopts ab opt $args +printf '%s\n' "$?:$opt:$OPTARG" +for dummy in dummy1 dummy2; do + getopts ab opt $args + printf '%s\n' "$?:$opt:$OPTARG" +done diff --git a/shell_cmds/sh/tests/builtins/getopts9.0.stdout b/shell_cmds/sh/tests/builtins/getopts9.0.stdout new file mode 100644 index 0000000..4d32063 --- /dev/null +++ b/shell_cmds/sh/tests/builtins/getopts9.0.stdout @@ -0,0 +1,3 @@ +0:a: +0:b: +1:?: diff --git a/shell_cmds/sh/tests/builtins/hash1.0 b/shell_cmds/sh/tests/builtins/hash1.0 new file mode 100644 index 0000000..a5cae20 --- /dev/null +++ b/shell_cmds/sh/tests/builtins/hash1.0 @@ -0,0 +1,5 @@ +# $FreeBSD: head/bin/sh/tests/builtins/hash1.0 149791 2005-09-05 09:42:10Z stefanf $ +cat /dev/null +hash +hash -r +hash diff --git a/shell_cmds/sh/tests/builtins/hash1.0.stdout b/shell_cmds/sh/tests/builtins/hash1.0.stdout new file mode 100644 index 0000000..3afc3e7 --- /dev/null +++ b/shell_cmds/sh/tests/builtins/hash1.0.stdout @@ -0,0 +1 @@ +/bin/cat diff --git a/shell_cmds/sh/tests/builtins/hash2.0 b/shell_cmds/sh/tests/builtins/hash2.0 new file mode 100644 index 0000000..4ef151a --- /dev/null +++ b/shell_cmds/sh/tests/builtins/hash2.0 @@ -0,0 +1,4 @@ +# $FreeBSD: head/bin/sh/tests/builtins/hash2.0 149791 2005-09-05 09:42:10Z stefanf $ +hash +hash cat +hash diff --git a/shell_cmds/sh/tests/builtins/hash2.0.stdout b/shell_cmds/sh/tests/builtins/hash2.0.stdout new file mode 100644 index 0000000..3afc3e7 --- /dev/null +++ b/shell_cmds/sh/tests/builtins/hash2.0.stdout @@ -0,0 +1 @@ +/bin/cat diff --git a/shell_cmds/sh/tests/builtins/hash3.0 b/shell_cmds/sh/tests/builtins/hash3.0 new file mode 100644 index 0000000..6a155f4 --- /dev/null +++ b/shell_cmds/sh/tests/builtins/hash3.0 @@ -0,0 +1,3 @@ +# $FreeBSD: head/bin/sh/tests/builtins/hash3.0 149791 2005-09-05 09:42:10Z stefanf $ +hash -v cat +hash diff --git a/shell_cmds/sh/tests/builtins/hash3.0.stdout b/shell_cmds/sh/tests/builtins/hash3.0.stdout new file mode 100644 index 0000000..a34864c --- /dev/null +++ b/shell_cmds/sh/tests/builtins/hash3.0.stdout @@ -0,0 +1,2 @@ +/bin/cat +/bin/cat diff --git a/shell_cmds/sh/tests/builtins/hash4.0 b/shell_cmds/sh/tests/builtins/hash4.0 new file mode 100644 index 0000000..9e85f92 --- /dev/null +++ b/shell_cmds/sh/tests/builtins/hash4.0 @@ -0,0 +1,6 @@ +# $FreeBSD: head/bin/sh/tests/builtins/hash4.0 231535 2012-02-11 21:06:45Z jilles $ + +exec 3>&1 +m=`hash nosuchtool 2>&1 >&3` +r=$? +[ "$r" != 0 ] && [ -n "$m" ] diff --git a/shell_cmds/sh/tests/builtins/jobid1.0 b/shell_cmds/sh/tests/builtins/jobid1.0 new file mode 100644 index 0000000..8ba11c1 --- /dev/null +++ b/shell_cmds/sh/tests/builtins/jobid1.0 @@ -0,0 +1,7 @@ +# $FreeBSD: head/bin/sh/tests/builtins/jobid1.0 254412 2013-08-16 13:48:11Z jilles $ +# Non-standard builtin. + +: & +p1=$! +p2=$(jobid) +[ "${p1:?}" = "${p2:?}" ] diff --git a/shell_cmds/sh/tests/builtins/jobid2.0 b/shell_cmds/sh/tests/builtins/jobid2.0 new file mode 100644 index 0000000..4aca7d5 --- /dev/null +++ b/shell_cmds/sh/tests/builtins/jobid2.0 @@ -0,0 +1,9 @@ +# $FreeBSD: head/bin/sh/tests/builtins/jobid2.0 254413 2013-08-16 13:56:43Z jilles $ + +: & +p1=$(jobid) +p2=$(jobid --) +p3=$(jobid %+) +p4=$(jobid -- %+) +[ "${p1:?}" = "${p2:?}" ] && [ "${p2:?}" = "${p3:?}" ] && +[ "${p3:?}" = "${p4:?}" ] && [ "${p4:?}" = "${p1:?}" ] diff --git a/shell_cmds/sh/tests/builtins/kill1.0 b/shell_cmds/sh/tests/builtins/kill1.0 new file mode 100644 index 0000000..c15c86d --- /dev/null +++ b/shell_cmds/sh/tests/builtins/kill1.0 @@ -0,0 +1,8 @@ +# $FreeBSD: head/bin/sh/tests/builtins/kill1.0 262931 2014-03-08 19:44:34Z jilles $ + +: & +p1=$! +: & +p2=$! +wait $p2 +kill %1 diff --git a/shell_cmds/sh/tests/builtins/kill2.0 b/shell_cmds/sh/tests/builtins/kill2.0 new file mode 100644 index 0000000..3b2360b --- /dev/null +++ b/shell_cmds/sh/tests/builtins/kill2.0 @@ -0,0 +1,7 @@ +# $FreeBSD: head/bin/sh/tests/builtins/kill2.0 263206 2014-03-15 14:58:48Z jilles $ + +sleep 1 | sleep 1 & +kill %+ +wait "$!" +r=$? +[ "$r" -gt 128 ] && [ "$(kill -l "$r")" = TERM ] diff --git a/shell_cmds/sh/tests/builtins/lineno.0 b/shell_cmds/sh/tests/builtins/lineno.0 new file mode 100644 index 0000000..e678ad8 --- /dev/null +++ b/shell_cmds/sh/tests/builtins/lineno.0 @@ -0,0 +1,16 @@ +# $FreeBSD: head/bin/sh/tests/builtins/lineno.0 179023 2008-05-15 19:58:44Z stefanf $ +echo $LINENO +echo $LINENO + +f() { + echo $LINENO + echo $LINENO +} + +f + +echo ${LINENO:-foo} +echo ${LINENO=foo} +echo ${LINENO:+foo} +echo ${LINENO+foo} +echo ${#LINENO} diff --git a/shell_cmds/sh/tests/builtins/lineno.0.stdout b/shell_cmds/sh/tests/builtins/lineno.0.stdout new file mode 100644 index 0000000..82583a9 --- /dev/null +++ b/shell_cmds/sh/tests/builtins/lineno.0.stdout @@ -0,0 +1,9 @@ +2 +3 +2 +3 +12 +13 +foo +foo +2 diff --git a/shell_cmds/sh/tests/builtins/lineno2.0 b/shell_cmds/sh/tests/builtins/lineno2.0 new file mode 100644 index 0000000..f60ed11 --- /dev/null +++ b/shell_cmds/sh/tests/builtins/lineno2.0 @@ -0,0 +1,10 @@ +# $FreeBSD: head/bin/sh/tests/builtins/lineno2.0 262565 2014-02-27 16:54:43Z jilles $ + +f() { + : ${LINENO+${x?}} +} + +unset -v x +command eval f 2>/dev/null && exit 3 +x=1 +f diff --git a/shell_cmds/sh/tests/builtins/lineno3.0 b/shell_cmds/sh/tests/builtins/lineno3.0 new file mode 100644 index 0000000..3271262 --- /dev/null +++ b/shell_cmds/sh/tests/builtins/lineno3.0 @@ -0,0 +1,6 @@ +# $FreeBSD: head/bin/sh/tests/builtins/lineno3.0 272482 2014-10-03 20:24:56Z jilles $ + +echo before: $LINENO +dummy=$'a\0 +' +echo after: $LINENO diff --git a/shell_cmds/sh/tests/builtins/lineno3.0.stdout b/shell_cmds/sh/tests/builtins/lineno3.0.stdout new file mode 100644 index 0000000..6e0e4ac --- /dev/null +++ b/shell_cmds/sh/tests/builtins/lineno3.0.stdout @@ -0,0 +1,2 @@ +before: 3 +after: 6 diff --git a/shell_cmds/sh/tests/builtins/local1.0 b/shell_cmds/sh/tests/builtins/local1.0 new file mode 100644 index 0000000..6100274 --- /dev/null +++ b/shell_cmds/sh/tests/builtins/local1.0 @@ -0,0 +1,13 @@ +# $FreeBSD: head/bin/sh/tests/builtins/local1.0 238469 2012-07-15 10:22:13Z jilles $ +# A commonly used but non-POSIX builtin. + +f() { + local x + x=2 + [ "$x" = 2 ] +} +x=1 +f || exit 3 +[ "$x" = 1 ] || exit 3 +f || exit 3 +[ "$x" = 1 ] || exit 3 diff --git a/shell_cmds/sh/tests/builtins/local2.0 b/shell_cmds/sh/tests/builtins/local2.0 new file mode 100644 index 0000000..ed78d93 --- /dev/null +++ b/shell_cmds/sh/tests/builtins/local2.0 @@ -0,0 +1,17 @@ +# $FreeBSD: head/bin/sh/tests/builtins/local2.0 251797 2013-06-15 22:22:03Z jilles $ + +f() { + local - + set -a + case $- in + *a*) : ;; + *) echo In-function \$- bad + esac +} +case $- in +*a*) echo Initial \$- bad +esac +f +case $- in +*a*) echo Final \$- bad +esac diff --git a/shell_cmds/sh/tests/builtins/local3.0 b/shell_cmds/sh/tests/builtins/local3.0 new file mode 100644 index 0000000..f1d135d --- /dev/null +++ b/shell_cmds/sh/tests/builtins/local3.0 @@ -0,0 +1,26 @@ +# $FreeBSD: head/bin/sh/tests/builtins/local3.0 251797 2013-06-15 22:22:03Z jilles $ + +f() { + local "$@" + set -a + x=7 + case $- in + *a*) : ;; + *) echo In-function \$- bad + esac + [ "$x" = 7 ] || echo In-function \$x bad +} +x=1 +case $- in +*a*) echo Initial \$- bad +esac +f x - +case $- in +*a*) echo Intermediate \$- bad +esac +[ "$x" = 1 ] || echo Intermediate \$x bad +f - x +case $- in +*a*) echo Final \$- bad +esac +[ "$x" = 1 ] || echo Final \$x bad diff --git a/shell_cmds/sh/tests/builtins/local4.0 b/shell_cmds/sh/tests/builtins/local4.0 new file mode 100644 index 0000000..7d35e6e --- /dev/null +++ b/shell_cmds/sh/tests/builtins/local4.0 @@ -0,0 +1,12 @@ +# $FreeBSD: head/bin/sh/tests/builtins/local4.0 254339 2013-08-14 21:59:48Z jilles $ + +f() { + local -- x + x=2 + [ "$x" = 2 ] +} +x=1 +f || exit 3 +[ "$x" = 1 ] || exit 3 +f || exit 3 +[ "$x" = 1 ] || exit 3 diff --git a/shell_cmds/sh/tests/builtins/local5.0 b/shell_cmds/sh/tests/builtins/local5.0 new file mode 100644 index 0000000..912a77b --- /dev/null +++ b/shell_cmds/sh/tests/builtins/local5.0 @@ -0,0 +1,15 @@ +# $FreeBSD: head/bin/sh/tests/builtins/local5.0 293635 2016-01-10 16:31:28Z jilles $ + +f() { + local PATH IFS elem + IFS=: + for elem in ''$PATH''; do + PATH=/var/empty/$elem:$PATH + done + ls -d / >/dev/null +} + +p1=$(command -v ls) +f +p2=$(command -v ls) +[ "$p1" = "$p2" ] diff --git a/shell_cmds/sh/tests/builtins/local6.0 b/shell_cmds/sh/tests/builtins/local6.0 new file mode 100644 index 0000000..b2e4c68 --- /dev/null +++ b/shell_cmds/sh/tests/builtins/local6.0 @@ -0,0 +1,10 @@ +# $FreeBSD: head/bin/sh/tests/builtins/local6.0 294582 2016-01-22 18:10:36Z jilles $ + +f() { + local x + readonly x=2 +} +x=3 +f +x=4 +[ "$x" = 4 ] diff --git a/shell_cmds/sh/tests/builtins/local7.0 b/shell_cmds/sh/tests/builtins/local7.0 new file mode 100644 index 0000000..3819442 --- /dev/null +++ b/shell_cmds/sh/tests/builtins/local7.0 @@ -0,0 +1,10 @@ +# $FreeBSD: head/bin/sh/tests/builtins/local7.0 294593 2016-01-22 20:10:08Z jilles $ + +f() { + local x + readonly x=2 +} +unset x +f +x=4 +[ "$x" = 4 ] diff --git a/shell_cmds/sh/tests/builtins/locale1.0 b/shell_cmds/sh/tests/builtins/locale1.0 new file mode 100644 index 0000000..a560232 --- /dev/null +++ b/shell_cmds/sh/tests/builtins/locale1.0 @@ -0,0 +1,134 @@ +# $FreeBSD: head/bin/sh/tests/builtins/locale1.0 218819 2011-02-18 20:37:09Z jilles $ +# Note: this test depends on strerror() using locale. + +failures=0 + +check() { + if ! eval "[ $1 ]"; then + echo "Failed: $1 at $2" + : $((failures += 1)) + fi +} + +unset LANG LC_ALL LC_COLLATE LC_CTYPE LC_MONETARY LC_NUMERIC LC_TIME LC_MESSAGES +unset LANGUAGE + +msgeng="No such file or directory" +msgdut="Bestand of map niet gevonden" + +# Verify C locale error message. +case $(command . /var/empty/foo 2>&1) in + *"$msgeng"*) ok=1 ;; + *) ok=0 ;; +esac +check '$ok -eq 1' $LINENO + +# Various locale variables that should not affect the message. +case $(LC_ALL=C command . /var/empty/foo 2>&1) in + *"$msgeng"*) ok=1 ;; + *) ok=0 ;; +esac +check '$ok -eq 1' $LINENO + +case $(LC_ALL=C LANG=nl_NL.ISO8859-1 command . /var/empty/foo 2>&1) in + *"$msgeng"*) ok=1 ;; + *) ok=0 ;; +esac +check '$ok -eq 1' $LINENO + +case $(LC_ALL=C LC_MESSAGES=nl_NL.ISO8859-1 command . /var/empty/foo 2>&1) in + *"$msgeng"*) ok=1 ;; + *) ok=0 ;; +esac +check '$ok -eq 1' $LINENO + +case $(LC_CTYPE=nl_NL.ISO8859-1 command . /var/empty/foo 2>&1) in + *"$msgeng"*) ok=1 ;; + *) ok=0 ;; +esac +check '$ok -eq 1' $LINENO + +# Verify Dutch message. +case $(export LANG=nl_NL.ISO8859-1; command . /var/empty/foo 2>&1) in + *"$msgdut"*) ok=1 ;; + *) ok=0 ;; +esac +check '$ok -eq 1' $LINENO + +case $(export LC_MESSAGES=nl_NL.ISO8859-1; command . /var/empty/foo 2>&1) in + *"$msgdut"*) ok=1 ;; + *) ok=0 ;; +esac +check '$ok -eq 1' $LINENO + +case $(export LC_ALL=nl_NL.ISO8859-1; command . /var/empty/foo 2>&1) in + *"$msgdut"*) ok=1 ;; + *) ok=0 ;; +esac +check '$ok -eq 1' $LINENO + +case $(LANG=nl_NL.ISO8859-1 command . /var/empty/foo 2>&1) in + *"$msgdut"*) ok=1 ;; + *) ok=0 ;; +esac +check '$ok -eq 1' $LINENO + +case $(LC_MESSAGES=nl_NL.ISO8859-1 command . /var/empty/foo 2>&1) in + *"$msgdut"*) ok=1 ;; + *) ok=0 ;; +esac +check '$ok -eq 1' $LINENO + +case $(LC_ALL=nl_NL.ISO8859-1 command . /var/empty/foo 2>&1) in + *"$msgdut"*) ok=1 ;; + *) ok=0 ;; +esac +check '$ok -eq 1' $LINENO + +# Verify that command assignments do not set the locale persistently. +case $(command . /var/empty/foo 2>&1) in + *"$msgeng"*) ok=1 ;; + *) ok=0 ;; +esac +check '$ok -eq 1' $LINENO + +case $(LANG=nl_NL.ISO8859-1 command . /var/empty/foo 2>&1; command . /var/empty/foo 2>&1) in + *"$msgdut"*"$msgeng"*) ok=1 ;; + *) ok=0 ;; +esac +check '$ok -eq 1' $LINENO + +case $(LC_MESSAGES=nl_NL.ISO8859-1 command . /var/empty/foo 2>&1; command . /var/empty/foo 2>&1) in + *"$msgdut"*"$msgeng"*) ok=1 ;; + *) ok=0 ;; +esac +check '$ok -eq 1' $LINENO + +case $(LC_ALL=nl_NL.ISO8859-1 command . /var/empty/foo 2>&1; command . /var/empty/foo 2>&1) in + *"$msgdut"*"$msgeng"*) ok=1 ;; + *) ok=0 ;; +esac +check '$ok -eq 1' $LINENO + +# Check special builtin; add colon invocation to avoid depending on certain fix. +case $(LC_ALL=nl_NL.ISO8859-1 . /var/empty/foo 2>&1; :) in + *"$msgdut"*) ok=1 ;; + *) ok=0 ;; +esac +check '$ok -eq 1' $LINENO + +# Assignments on special builtins are exported to that builtin; the export +# is not persistent. +case $(LC_ALL=nl_NL.ISO8859-1 . /dev/null; . /var/empty/foo 2>&1) in + *"$msgeng"*) ok=1 ;; + *) ok=0 ;; +esac +check '$ok -eq 1' $LINENO + +case $(export LC_ALL; LC_ALL=nl_NL.ISO8859-1 . /dev/null; . /var/empty/foo 2>&1) in + *"$msgdut"*) ok=1 ;; + *) ok=0 ;; +esac +check '$ok -eq 1' $LINENO + +exit $((failures > 0)) diff --git a/shell_cmds/sh/tests/builtins/locale2.0 b/shell_cmds/sh/tests/builtins/locale2.0 new file mode 100644 index 0000000..fc1b432 --- /dev/null +++ b/shell_cmds/sh/tests/builtins/locale2.0 @@ -0,0 +1,5 @@ +# $FreeBSD: head/bin/sh/tests/builtins/locale2.0 317912 2017-05-07 19:49:46Z jilles $ + +$SH -c 'LC_ALL=C true; kill -INT $$; echo continued' +r=$? +[ "$r" -gt 128 ] && [ "$(kill -l "$r")" = INT ] diff --git a/shell_cmds/sh/tests/builtins/printf1.0 b/shell_cmds/sh/tests/builtins/printf1.0 new file mode 100644 index 0000000..5730c5b --- /dev/null +++ b/shell_cmds/sh/tests/builtins/printf1.0 @@ -0,0 +1,3 @@ +# $FreeBSD: head/bin/sh/tests/builtins/printf1.0 214853 2010-11-05 21:47:58Z jilles $ + +[ "$(printf '%c\0%s%d' x '\' 010 | tr '\0' Z)" = 'xZ\8' ] diff --git a/shell_cmds/sh/tests/builtins/printf2.0 b/shell_cmds/sh/tests/builtins/printf2.0 new file mode 100644 index 0000000..7084db7 --- /dev/null +++ b/shell_cmds/sh/tests/builtins/printf2.0 @@ -0,0 +1,3 @@ +# $FreeBSD: head/bin/sh/tests/builtins/printf2.0 214853 2010-11-05 21:47:58Z jilles $ + +[ "$(printf '%cZ%s%d' x '\' 010)" = 'xZ\8' ] diff --git a/shell_cmds/sh/tests/builtins/printf3.0 b/shell_cmds/sh/tests/builtins/printf3.0 new file mode 100644 index 0000000..ba54866 --- /dev/null +++ b/shell_cmds/sh/tests/builtins/printf3.0 @@ -0,0 +1,5 @@ +# $FreeBSD: head/bin/sh/tests/builtins/printf3.0 216606 2010-12-20 23:06:57Z jilles $ + +set -e +v=$(! printf "%d" @wrong 2>/dev/null) +[ "$v" = "0" ] diff --git a/shell_cmds/sh/tests/builtins/printf4.0 b/shell_cmds/sh/tests/builtins/printf4.0 new file mode 100644 index 0000000..5213dd6 --- /dev/null +++ b/shell_cmds/sh/tests/builtins/printf4.0 @@ -0,0 +1,5 @@ +# $FreeBSD: head/bin/sh/tests/builtins/printf4.0 216606 2010-12-20 23:06:57Z jilles $ + +set -e +v=$(! printf "%d" 4wrong 2>/dev/null) +[ "$v" = "4" ] diff --git a/shell_cmds/sh/tests/builtins/read1.0 b/shell_cmds/sh/tests/builtins/read1.0 new file mode 100644 index 0000000..b32272c --- /dev/null +++ b/shell_cmds/sh/tests/builtins/read1.0 @@ -0,0 +1,26 @@ +# $FreeBSD: head/bin/sh/tests/builtins/read1.0 190300 2009-03-22 23:00:52Z stefanf $ +set -e + +echo "1 2 3" | { read a; echo "x${a}x"; } +echo "1 2 3" | { read a b; echo "x${a}x${b}x"; } +echo "1 2 3" | { read a b c; echo "x${a}x${b}x${c}x"; } +echo "1 2 3" | { read a b c d; echo "x${a}x${b}x${c}x${d}x"; } + +echo " 1 2 3 " | { read a b c; echo "x${a}x${b}x${c}x"; } +echo " 1 2 3 " | { unset IFS; read a b c; echo "x${a}x${b}x${c}x"; } +echo " 1 2 3 " | { IFS=$(printf ' \t\n') read a b c; echo "x${a}x${b}x${c}x"; } +echo " 1 2 3 " | { IFS= read a b; echo "x${a}x${b}x"; } + +echo " 1,2 3 " | { IFS=' ,' read a b c; echo "x${a}x${b}x${c}x"; } +echo ", 2 ,3" | { IFS=' ,' read a b c; echo "x${a}x${b}x${c}x"; } +echo " 1 ,,3" | { IFS=' ,' read a b c; echo "x${a}x${b}x${c}x"; } +echo " 1 , , 3" | { IFS=' ,' read a b c; echo "x${a}x${b}x${c}x"; } +echo " 1 ,2 3," | { IFS=' ,' read a b c; echo "x${a}x${b}x${c}x"; } +echo " 1 ,2 3,," | { IFS=' ,' read a b c; echo "x${a}x${b}x${c}x"; } + +echo " 1,2 3 " | { IFS=', ' read a b c; echo "x${a}x${b}x${c}x"; } +echo ", 2 ,3" | { IFS=', ' read a b c; echo "x${a}x${b}x${c}x"; } +echo " 1 ,,3" | { IFS=', ' read a b c; echo "x${a}x${b}x${c}x"; } +echo " 1 , , 3" | { IFS=', ' read a b c; echo "x${a}x${b}x${c}x"; } +echo " 1 ,2 3," | { IFS=', ' read a b c; echo "x${a}x${b}x${c}x"; } +echo " 1 ,2 3,," | { IFS=', ' read a b c; echo "x${a}x${b}x${c}x"; } diff --git a/shell_cmds/sh/tests/builtins/read1.0.stdout b/shell_cmds/sh/tests/builtins/read1.0.stdout new file mode 100644 index 0000000..dbcb1af --- /dev/null +++ b/shell_cmds/sh/tests/builtins/read1.0.stdout @@ -0,0 +1,20 @@ +x1 2 3x +x1x2 3x +x1x2x3x +x1x2x3xx +x1x2x3x +x1x2x3x +x1x2x3x +x 1 2 3 xx +x1x2x3x +xx2x3x +x1xx3x +x1xx3x +x1x2x3x +x1x2x3,,x +x1x2x3x +xx2x3x +x1xx3x +x1xx3x +x1x2x3x +x1x2x3,,x diff --git a/shell_cmds/sh/tests/builtins/read2.0 b/shell_cmds/sh/tests/builtins/read2.0 new file mode 100644 index 0000000..3255a48 --- /dev/null +++ b/shell_cmds/sh/tests/builtins/read2.0 @@ -0,0 +1,31 @@ +# $FreeBSD: head/bin/sh/tests/builtins/read2.0 212187 2010-09-03 21:17:33Z jilles $ + +set -e +{ + echo 1 + echo two + echo three +} | { + read x + [ "$x" = 1 ] + (read x + [ "$x" = two ]) + read x + [ "$x" = three ] +} + +T=`mktemp sh-test.XXXXXX` +trap 'rm -f "$T"' 0 +{ + echo 1 + echo two + echo three +} >$T +{ + read x + [ "$x" = 1 ] + (read x + [ "$x" = two ]) + read x + [ "$x" = three ] +} <$T diff --git a/shell_cmds/sh/tests/builtins/read3.0 b/shell_cmds/sh/tests/builtins/read3.0 new file mode 100644 index 0000000..7cd5b4b --- /dev/null +++ b/shell_cmds/sh/tests/builtins/read3.0 @@ -0,0 +1,11 @@ +# $FreeBSD: head/bin/sh/tests/builtins/read3.0 212330 2010-09-08 18:32:23Z jilles $ + +printf '%s\n' 'a\ b c' | { read a b; printf '%s\n' "x${a}x${b}x"; } +printf '%s\n' 'a b\ c' | { read a b; printf '%s\n' "x${a}x${b}x"; } +printf '%s\n' 'a\:b:c' | { IFS=: read a b; printf '%s\n' "x${a}x${b}x"; } +printf '%s\n' 'a:b\:c' | { IFS=: read a b; printf '%s\n' "x${a}x${b}x"; } +printf '%s\n' '\ a' | { read a b; printf '%s\n' "x${a}x${b}x"; } +printf '%s\n' '\:a' | { IFS=: read a b; printf '%s\n' "x${a}x${b}x"; } +printf '%s\n' '\\' | { read a b; printf '%s\n' "x${a}x${b}x"; } +printf '%s\n' '\\\ a' | { read a b; printf '%s\n' "x${a}x${b}x"; } +printf '%s\n' '\\\ a' | { read -r a b; printf '%s\n' "x${a}x${b}x"; } diff --git a/shell_cmds/sh/tests/builtins/read3.0.stdout b/shell_cmds/sh/tests/builtins/read3.0.stdout new file mode 100644 index 0000000..8ed98ca --- /dev/null +++ b/shell_cmds/sh/tests/builtins/read3.0.stdout @@ -0,0 +1,9 @@ +xa bxcx +xaxb cx +xa:bxcx +xaxb:cx +x axx +x:axx +x\xx +x\ axx +x\\\xax diff --git a/shell_cmds/sh/tests/builtins/read4.0 b/shell_cmds/sh/tests/builtins/read4.0 new file mode 100644 index 0000000..abd08af --- /dev/null +++ b/shell_cmds/sh/tests/builtins/read4.0 @@ -0,0 +1,10 @@ +# $FreeBSD: head/bin/sh/tests/builtins/read4.0 212339 2010-09-08 20:35:43Z jilles $ + +printf '%s\n' '\a\ b c' | { read a b; printf '%s\n' "x${a}x${b}x"; } +printf '%s\n' '\a b\ c' | { read a b; printf '%s\n' "x${a}x${b}x"; } +printf '%s\n' '\a\:b:c' | { IFS=: read a b; printf '%s\n' "x${a}x${b}x"; } +printf '%s\n' '\a:b\:c' | { IFS=: read a b; printf '%s\n' "x${a}x${b}x"; } +printf '%s\n' '\\ a' | { read a b; printf '%s\n' "x${a}x${b}x"; } +printf '%s\n' '\\:a' | { IFS=: read a b; printf '%s\n' "x${a}x${b}x"; } +printf '%s\n' '\\\ a' | { read a b; printf '%s\n' "x${a}x${b}x"; } +printf '%s\n' '\\\:a' | { IFS=: read a b; printf '%s\n' "x${a}x${b}x"; } diff --git a/shell_cmds/sh/tests/builtins/read4.0.stdout b/shell_cmds/sh/tests/builtins/read4.0.stdout new file mode 100644 index 0000000..a8747a4 --- /dev/null +++ b/shell_cmds/sh/tests/builtins/read4.0.stdout @@ -0,0 +1,8 @@ +xa bxcx +xaxb cx +xa:bxcx +xaxb:cx +x\xax +x\xax +x\ axx +x\:axx diff --git a/shell_cmds/sh/tests/builtins/read5.0 b/shell_cmds/sh/tests/builtins/read5.0 new file mode 100644 index 0000000..659aebb --- /dev/null +++ b/shell_cmds/sh/tests/builtins/read5.0 @@ -0,0 +1,32 @@ +# $FreeBSD: head/bin/sh/tests/builtins/read5.0 218821 2011-02-18 20:51:13Z jilles $ + +unset LC_ALL +LC_CTYPE=en_US.ISO8859-1 +export LC_CTYPE + +# Note: the first and last characters are not whitespace. +# Exclude backslash and newline. +bad1=`printf %03o \'\\\\` +bad2=`printf %03o \'' +'` +e= +for i in 0 1 2 3; do + for j in 0 1 2 3 4 5 6 7; do + for k in 0 1 2 3 4 5 6 7; do + case $i$j$k in + 000|$bad1|$bad2) continue ;; + esac + e="$e\\$i$j$k" + done + done +done +e=`printf "$e"` +[ "${#e}" = 253 ] || echo length bad + +r1=`printf '%s\n' "$e" | { read -r x; printf '%s' "$x"; }` +[ "$r1" = "$e" ] || echo "read with -r bad" +r2=`printf '%s\n' "$e" | { read x; printf '%s' "$x"; }` +[ "$r2" = "$e" ] || echo "read without -r bad 1" +IFS= +r3=`printf '%s\n' "$e" | { read x; printf '%s' "$x"; }` +[ "$r3" = "$e" ] || echo "read without -r bad 2" diff --git a/shell_cmds/sh/tests/builtins/read6.0 b/shell_cmds/sh/tests/builtins/read6.0 new file mode 100644 index 0000000..3f78ee1 --- /dev/null +++ b/shell_cmds/sh/tests/builtins/read6.0 @@ -0,0 +1,5 @@ +# $FreeBSD: head/bin/sh/tests/builtins/read6.0 247190 2013-02-23 15:15:41Z jilles $ + +: | read x +r=$? +[ "$r" = 1 ] diff --git a/shell_cmds/sh/tests/builtins/read7.0 b/shell_cmds/sh/tests/builtins/read7.0 new file mode 100644 index 0000000..a01082d --- /dev/null +++ b/shell_cmds/sh/tests/builtins/read7.0 @@ -0,0 +1,5 @@ +# $FreeBSD: head/bin/sh/tests/builtins/read7.0 250214 2013-05-03 15:28:31Z jilles $ + +{ errmsg=`read x <&- 2>&1 >&3`; } 3>&1 +r=$? +[ "$r" -ge 2 ] && [ "$r" -le 128 ] && [ -n "$errmsg" ] diff --git a/shell_cmds/sh/tests/builtins/read8.0 b/shell_cmds/sh/tests/builtins/read8.0 new file mode 100644 index 0000000..b488279 --- /dev/null +++ b/shell_cmds/sh/tests/builtins/read8.0 @@ -0,0 +1,17 @@ +# $FreeBSD: head/bin/sh/tests/builtins/read8.0 286826 2015-08-16 12:57:17Z jilles $ + +read a b c <<\EOF +\ +A\ + \ + \ + \ +B\ + \ + \ +C\ + \ + \ + \ +EOF +[ "$a.$b.$c" = "A.B.C" ] diff --git a/shell_cmds/sh/tests/builtins/read9.0 b/shell_cmds/sh/tests/builtins/read9.0 new file mode 100644 index 0000000..16f0687 --- /dev/null +++ b/shell_cmds/sh/tests/builtins/read9.0 @@ -0,0 +1,10 @@ +# $FreeBSD: head/bin/sh/tests/builtins/read9.0 287308 2015-08-30 17:24:22Z jilles $ + +empty='' +read a b c <<EOF +\ \ A B\ \ B C\ \ $empty +EOF +read d e <<EOF +D\ $empty +EOF +[ "$a.$b.$c.$d.$e" = " A.B B.C .D ." ] diff --git a/shell_cmds/sh/tests/builtins/return1.0 b/shell_cmds/sh/tests/builtins/return1.0 new file mode 100644 index 0000000..46de0ae --- /dev/null +++ b/shell_cmds/sh/tests/builtins/return1.0 @@ -0,0 +1,7 @@ +# $FreeBSD: head/bin/sh/tests/builtins/return1.0 149791 2005-09-05 09:42:10Z stefanf $ +f() { + return 0 + exit 1 +} + +f diff --git a/shell_cmds/sh/tests/builtins/return2.1 b/shell_cmds/sh/tests/builtins/return2.1 new file mode 100644 index 0000000..4da86f5 --- /dev/null +++ b/shell_cmds/sh/tests/builtins/return2.1 @@ -0,0 +1,7 @@ +# $FreeBSD: head/bin/sh/tests/builtins/return2.1 149791 2005-09-05 09:42:10Z stefanf $ +f() { + true && return 1 + return 0 +} + +f diff --git a/shell_cmds/sh/tests/builtins/return3.1 b/shell_cmds/sh/tests/builtins/return3.1 new file mode 100644 index 0000000..bc65c1f --- /dev/null +++ b/shell_cmds/sh/tests/builtins/return3.1 @@ -0,0 +1,3 @@ +# $FreeBSD: head/bin/sh/tests/builtins/return3.1 149791 2005-09-05 09:42:10Z stefanf $ +return 1 +exit 0 diff --git a/shell_cmds/sh/tests/builtins/return4.0 b/shell_cmds/sh/tests/builtins/return4.0 new file mode 100644 index 0000000..1a702ef --- /dev/null +++ b/shell_cmds/sh/tests/builtins/return4.0 @@ -0,0 +1,16 @@ +# $FreeBSD: head/bin/sh/tests/builtins/return4.0 208629 2010-05-28 22:08:34Z jilles $ + +failures= +failure() { + echo "Error at line $1" >&2 + failures=x$failures +} + +T=$(mktemp -d ${TMPDIR:-/tmp}/sh-test.XXXXXX) || exit +trap 'rm -rf $T' 0 +cd $T || exit 3 +echo 'return 42; exit 4' >testscript +. ./testscript +[ "$?" = 42 ] || failure $LINENO + +test -z "$failures" diff --git a/shell_cmds/sh/tests/builtins/return5.0 b/shell_cmds/sh/tests/builtins/return5.0 new file mode 100644 index 0000000..476ae7b --- /dev/null +++ b/shell_cmds/sh/tests/builtins/return5.0 @@ -0,0 +1,17 @@ +# $FreeBSD: head/bin/sh/tests/builtins/return5.0 211349 2010-08-15 21:06:53Z jilles $ + +if [ "$1" != nested ]; then + f() { + set -- nested + . "$0" + # Allow return to return from the function or the dot script. + return 4 + } + f + exit $(($? ^ 4)) +fi +# To trigger the bug, the following commands must be at the top level, +# with newlines in between. +return 4 +echo bad +exit 1 diff --git a/shell_cmds/sh/tests/builtins/return6.4 b/shell_cmds/sh/tests/builtins/return6.4 new file mode 100644 index 0000000..1bddbeb --- /dev/null +++ b/shell_cmds/sh/tests/builtins/return6.4 @@ -0,0 +1,3 @@ +# $FreeBSD: head/bin/sh/tests/builtins/return6.4 212475 2010-09-11 15:07:40Z jilles $ + +while return 4; do exit 3; done diff --git a/shell_cmds/sh/tests/builtins/return7.4 b/shell_cmds/sh/tests/builtins/return7.4 new file mode 100644 index 0000000..3efdd7f --- /dev/null +++ b/shell_cmds/sh/tests/builtins/return7.4 @@ -0,0 +1,6 @@ +# $FreeBSD: head/bin/sh/tests/builtins/return7.4 212475 2010-09-11 15:07:40Z jilles $ + +f() { + while return 4; do exit 3; done +} +f diff --git a/shell_cmds/sh/tests/builtins/return8.0 b/shell_cmds/sh/tests/builtins/return8.0 new file mode 100644 index 0000000..025f068 --- /dev/null +++ b/shell_cmds/sh/tests/builtins/return8.0 @@ -0,0 +1,13 @@ +# $FreeBSD: head/bin/sh/tests/builtins/return8.0 255215 2013-09-04 22:10:16Z jilles $ + +if [ "$1" = nested ]; then + return 17 +fi + +f() { + set -- nested + . "$0" + return $(($? ^ 1)) +} +f +exit $(($? ^ 16)) diff --git a/shell_cmds/sh/tests/builtins/set1.0 b/shell_cmds/sh/tests/builtins/set1.0 new file mode 100644 index 0000000..fc39fad --- /dev/null +++ b/shell_cmds/sh/tests/builtins/set1.0 @@ -0,0 +1,32 @@ +# $FreeBSD$ + +set +C +set +f +set -e + +settings=$(set +o) +set -C +set -f +set +e +case $- in +*C*) ;; +*) echo missing C ;; +esac +case $- in +*f*) ;; +*) echo missing C ;; +esac +case $- in +*e*) echo bad e ;; +esac +eval "$settings" +case $- in +*C*) echo bad C ;; +esac +case $- in +*f*) echo bad f ;; +esac +case $- in +*e*) ;; +*) echo missing e ;; +esac diff --git a/shell_cmds/sh/tests/builtins/set2.0 b/shell_cmds/sh/tests/builtins/set2.0 new file mode 100644 index 0000000..844da91 --- /dev/null +++ b/shell_cmds/sh/tests/builtins/set2.0 @@ -0,0 +1,3 @@ +# $FreeBSD: head/bin/sh/tests/builtins/set2.0 223183 2011-06-17 10:21:24Z jilles $ + +! env @badness=1 ${SH} -c 'v=`set`; eval "$v"' 2>&1 | grep @badness diff --git a/shell_cmds/sh/tests/builtins/set3.0 b/shell_cmds/sh/tests/builtins/set3.0 new file mode 100644 index 0000000..419f702 --- /dev/null +++ b/shell_cmds/sh/tests/builtins/set3.0 @@ -0,0 +1,4 @@ +# $FreeBSD: head/bin/sh/tests/builtins/set3.0 296578 2016-03-09 21:05:21Z jilles $ + +settings1=$(set +o) && set -o nolog && settings2=$(set +o) && +[ "$settings1" != "$settings2" ] diff --git a/shell_cmds/sh/tests/builtins/trap1.0 b/shell_cmds/sh/tests/builtins/trap1.0 new file mode 100644 index 0000000..9f60be4 --- /dev/null +++ b/shell_cmds/sh/tests/builtins/trap1.0 @@ -0,0 +1,22 @@ +# $FreeBSD: head/bin/sh/tests/builtins/trap1.0 213738 2010-10-12 18:20:38Z obrien $ + +test "$(trap 'echo trapped' EXIT; :)" = trapped || exit 1 + +test "$(trap 'echo trapped' EXIT; /usr/bin/true)" = trapped || exit 1 + +result=$(${SH} -c 'trap "echo trapped" EXIT; /usr/bin/false') +test $? -eq 1 || exit 1 +test "$result" = trapped || exit 1 + +result=$(${SH} -c 'trap "echo trapped" EXIT; exec /usr/bin/false') +test $? -eq 1 || exit 1 +test -z "$result" || exit 1 + +result=0 +trap 'result=$((result+1))' INT +kill -INT $$ +test "$result" -eq 1 || exit 1 +(kill -INT $$) +test "$result" -eq 2 || exit 1 + +exit 0 diff --git a/shell_cmds/sh/tests/builtins/trap10.0 b/shell_cmds/sh/tests/builtins/trap10.0 new file mode 100644 index 0000000..66c5106 --- /dev/null +++ b/shell_cmds/sh/tests/builtins/trap10.0 @@ -0,0 +1,6 @@ +# $FreeBSD: head/bin/sh/tests/builtins/trap10.0 230212 2012-01-16 11:07:46Z dumbbell $ + +# Check that the return statement will not break the EXIT trap, ie. all +# trap commands are executed before the script exits. + +test "$(trap 'printf trap; echo ped' EXIT; f() { return; }; f)" = trapped || exit 1 diff --git a/shell_cmds/sh/tests/builtins/trap11.0 b/shell_cmds/sh/tests/builtins/trap11.0 new file mode 100644 index 0000000..acb4d0f --- /dev/null +++ b/shell_cmds/sh/tests/builtins/trap11.0 @@ -0,0 +1,8 @@ +# $FreeBSD: head/bin/sh/tests/builtins/trap11.0 230212 2012-01-16 11:07:46Z dumbbell $ + +# Check that the return statement will not break the USR1 trap, ie. all +# trap commands are executed before the script resumes. + +result=$(${SH} -c 'trap "printf trap; echo ped" USR1; f() { return $(kill -USR1 $$); }; f') +test $? -eq 0 || exit 1 +test "$result" = trapped || exit 1 diff --git a/shell_cmds/sh/tests/builtins/trap12.0 b/shell_cmds/sh/tests/builtins/trap12.0 new file mode 100644 index 0000000..a7a953c --- /dev/null +++ b/shell_cmds/sh/tests/builtins/trap12.0 @@ -0,0 +1,10 @@ +# $FreeBSD: head/bin/sh/tests/builtins/trap12.0 247720 2013-03-03 17:33:59Z jilles $ + +f() { + trap 'return 42' USR1 + kill -USR1 $$ + return 3 +} +f +r=$? +[ "$r" = 42 ] diff --git a/shell_cmds/sh/tests/builtins/trap13.0 b/shell_cmds/sh/tests/builtins/trap13.0 new file mode 100644 index 0000000..1069b8f --- /dev/null +++ b/shell_cmds/sh/tests/builtins/trap13.0 @@ -0,0 +1,8 @@ +# $FreeBSD: head/bin/sh/tests/builtins/trap13.0 257399 2013-10-30 21:36:15Z jilles $ + +{ + trap 'exit 0' INT + ${SH} -c 'kill -INT $PPID' + exit 3 +} & +wait $! diff --git a/shell_cmds/sh/tests/builtins/trap14.0 b/shell_cmds/sh/tests/builtins/trap14.0 new file mode 100644 index 0000000..8c982b2 --- /dev/null +++ b/shell_cmds/sh/tests/builtins/trap14.0 @@ -0,0 +1,10 @@ +# $FreeBSD: head/bin/sh/tests/builtins/trap14.0 257399 2013-10-30 21:36:15Z jilles $ + +{ + trap - INT + ${SH} -c 'kill -INT $PPID' & + wait +} & +wait $! +r=$? +[ "$r" -gt 128 ] && [ "$(kill -l "$r")" = INT ] diff --git a/shell_cmds/sh/tests/builtins/trap15.0 b/shell_cmds/sh/tests/builtins/trap15.0 new file mode 100644 index 0000000..eaf8e8e --- /dev/null +++ b/shell_cmds/sh/tests/builtins/trap15.0 @@ -0,0 +1,5 @@ +# $FreeBSD: head/bin/sh/tests/builtins/trap15.0 281718 2015-04-18 23:49:57Z bdrewery $ + +(${SH} -c 'term(){ exit 5;}; trap term TERM; kill -TERM $$') & +wait >/dev/null 2>&1 $! +[ $? -eq 5 ] diff --git a/shell_cmds/sh/tests/builtins/trap16.0 b/shell_cmds/sh/tests/builtins/trap16.0 new file mode 100644 index 0000000..b72fa11 --- /dev/null +++ b/shell_cmds/sh/tests/builtins/trap16.0 @@ -0,0 +1,20 @@ +# $FreeBSD: head/bin/sh/tests/builtins/trap16.0 281718 2015-04-18 23:49:57Z bdrewery $ + +traps=$(${SH} -c 'trap "echo bad" 0; trap - 0; trap') +[ -z "$traps" ] || exit 1 +traps=$(${SH} -c 'trap "echo bad" 0; trap "" 0; trap') +expected_traps=$(${SH} -c 'trap "" EXIT; trap') +[ "$traps" = "$expected_traps" ] || exit 2 +traps=$(${SH} -c 'trap "echo bad" 0; trap 0; trap') +[ -z "$traps" ] || exit 3 +traps=$(${SH} -c 'trap "echo bad" 0; trap -- 0; trap') +[ -z "$traps" ] || exit 4 +traps=$(${SH} -c 'trap "echo bad" 0 1 2; trap - 0 1 2; trap') +[ -z "$traps" ] || exit 5 +traps=$(${SH} -c 'trap "echo bad" 0 1 2; trap "" 0 1 2; trap') +expected_traps=$(${SH} -c 'trap "" EXIT HUP INT; trap') +[ "$traps" = "$expected_traps" ] || exit 6 +traps=$(${SH} -c 'trap "echo bad" 0 1 2; trap 0 1 2; trap') +[ -z "$traps" ] || exit 7 +traps=$(${SH} -c 'trap "echo bad" 0 1 2; trap -- 0 1 2; trap') +[ -z "$traps" ] || exit 8 diff --git a/shell_cmds/sh/tests/builtins/trap17.0 b/shell_cmds/sh/tests/builtins/trap17.0 new file mode 100644 index 0000000..35aafe7 --- /dev/null +++ b/shell_cmds/sh/tests/builtins/trap17.0 @@ -0,0 +1,10 @@ +# $FreeBSD: head/bin/sh/tests/builtins/trap17.0 297360 2016-03-28 18:58:40Z jilles $ +# This use-after-free bug probably needs non-default settings to show up. + +v1=nothing v2=nothing +trap 'trap "echo bad" USR1 +v1=trap_received +v2=trap_invoked +:' USR1 +kill -USR1 "$$" +[ "$v1.$v2" = trap_received.trap_invoked ] diff --git a/shell_cmds/sh/tests/builtins/trap2.0 b/shell_cmds/sh/tests/builtins/trap2.0 new file mode 100644 index 0000000..b64bb24 --- /dev/null +++ b/shell_cmds/sh/tests/builtins/trap2.0 @@ -0,0 +1,52 @@ +# $FreeBSD: head/bin/sh/tests/builtins/trap2.0 194517 2009-06-19 22:15:59Z jilles $ +# This is really a test for outqstr(), which is readily accessible via trap. + +runtest() +{ + teststring=$1 + trap -- "$teststring" USR1 + traps=$(trap) + if [ "$teststring" != "-" ] && [ -z "$traps" ]; then + # One possible reading of POSIX requires the above to return an + # empty string because backquote commands are executed in a + # subshell and subshells shall reset traps. However, an example + # in the normative description of the trap builtin shows the + # same usage as here, it is useful and our /bin/sh allows it. + echo '$(trap) is broken' + exit 1 + fi + trap - USR1 + eval "$traps" + traps2=$(trap) + if [ "$traps" != "$traps2" ]; then + echo "Mismatch for '$teststring'" + exit 1 + fi +} + +runtest 'echo' +runtest 'echo hi' +runtest "'echo' 'hi'" +runtest '"echo" $PATH' +runtest '\echo "$PATH"' +runtest ' 0' +runtest '0 ' +runtest ' 1' +runtest '1 ' +i=1 +while [ $i -le 127 ]; do + c=$(printf \\"$(printf %o $i)") + if [ $i -lt 48 ] || [ $i -gt 57 ]; then + runtest "$c" + fi + runtest " $c$c" + runtest "a$c" + i=$((i+1)) +done +IFS=, +runtest ' ' +runtest ',' +unset IFS +runtest ' ' + +exit 0 diff --git a/shell_cmds/sh/tests/builtins/trap3.0 b/shell_cmds/sh/tests/builtins/trap3.0 new file mode 100644 index 0000000..d442f8b --- /dev/null +++ b/shell_cmds/sh/tests/builtins/trap3.0 @@ -0,0 +1,11 @@ +# $FreeBSD: head/bin/sh/tests/builtins/trap3.0 218889 2011-02-20 14:18:58Z jilles $ + +{ + trap '' garbage && exit 3 + trap - garbage && exit 3 + trap true garbage && exit 3 + trap '' 99999 && exit 3 + trap - 99999 && exit 3 + trap true 99999 && exit 3 +} 2>/dev/null +exit 0 diff --git a/shell_cmds/sh/tests/builtins/trap4.0 b/shell_cmds/sh/tests/builtins/trap4.0 new file mode 100644 index 0000000..9fdab81 --- /dev/null +++ b/shell_cmds/sh/tests/builtins/trap4.0 @@ -0,0 +1,17 @@ +# $FreeBSD: head/bin/sh/tests/builtins/trap4.0 217035 2011-01-05 23:17:29Z jilles $ + +T=$(mktemp -d ${TMPDIR:-/tmp}/sh-test.XXXXXX) +trap 'rm -rf $T' 0 +cd $T || exit 3 +mkfifo fifo1 + +v=$( + exec 3>&1 + : <fifo1 & + { + wait $! + trap 'trap "" PIPE; echo trapped >&3 2>/dev/null' PIPE + echo x 2>/dev/null + } >fifo1 +) +test "$v" = trapped diff --git a/shell_cmds/sh/tests/builtins/trap5.0 b/shell_cmds/sh/tests/builtins/trap5.0 new file mode 100644 index 0000000..26eeb50 --- /dev/null +++ b/shell_cmds/sh/tests/builtins/trap5.0 @@ -0,0 +1,19 @@ +# $FreeBSD: head/bin/sh/tests/builtins/trap5.0 217461 2011-01-15 21:09:00Z jilles $ + +set -e +trap - USR1 +initial=$(trap) +trap -- -l USR1 +added=$(trap) +[ -n "$added" ] +trap - USR1 +second=$(trap) +[ "$initial" = "$second" ] +eval "$added" +added2=$(trap) +added3=$(trap --) +[ "$added" = "$added2" ] +[ "$added2" = "$added3" ] +trap -- - USR1 +third=$(trap) +[ "$initial" = "$third" ] diff --git a/shell_cmds/sh/tests/builtins/trap6.0 b/shell_cmds/sh/tests/builtins/trap6.0 new file mode 100644 index 0000000..1e79ae7 --- /dev/null +++ b/shell_cmds/sh/tests/builtins/trap6.0 @@ -0,0 +1,9 @@ +# $FreeBSD: head/bin/sh/tests/builtins/trap6.0 217472 2011-01-16 13:56:41Z jilles $ + +v=$( + ${SH} -c 'trap "echo ok; exit" USR1; kill -USR1 $$' & + # Suppress possible message about exit on signal + wait $! >/dev/null 2>&1 +) +r=$(kill -l $?) +[ "$v" = "ok" ] && { [ "$r" = "USR1" ] || [ "$r" = "usr1" ]; } diff --git a/shell_cmds/sh/tests/builtins/trap7.0 b/shell_cmds/sh/tests/builtins/trap7.0 new file mode 100644 index 0000000..b62ffb7 --- /dev/null +++ b/shell_cmds/sh/tests/builtins/trap7.0 @@ -0,0 +1,3 @@ +# $FreeBSD: head/bin/sh/tests/builtins/trap7.0 217996 2011-01-27 23:08:20Z jilles $ + +[ "$(trap 'echo trapped' EXIT)" = trapped ] diff --git a/shell_cmds/sh/tests/builtins/trap8.0 b/shell_cmds/sh/tests/builtins/trap8.0 new file mode 100644 index 0000000..24752b1 --- /dev/null +++ b/shell_cmds/sh/tests/builtins/trap8.0 @@ -0,0 +1,7 @@ +# $FreeBSD: head/bin/sh/tests/builtins/trap8.0 218889 2011-02-20 14:18:58Z jilles $ + +# I am not sure if POSIX requires the shell to continue processing +# further trap names in the same trap command after an invalid one. + +test -n "$(trap true garbage TERM 2>/dev/null || trap)" || exit 3 +exit 0 diff --git a/shell_cmds/sh/tests/builtins/trap9.0 b/shell_cmds/sh/tests/builtins/trap9.0 new file mode 100644 index 0000000..bbc35e9 --- /dev/null +++ b/shell_cmds/sh/tests/builtins/trap9.0 @@ -0,0 +1,3 @@ +# $FreeBSD: head/bin/sh/tests/builtins/trap9.0 230211 2012-01-16 10:59:44Z dumbbell $ + +test "$(trap 'printf trap; echo ped' EXIT; f() { :; }; f)" = trapped || exit 1 diff --git a/shell_cmds/sh/tests/builtins/type1.0 b/shell_cmds/sh/tests/builtins/type1.0 new file mode 100644 index 0000000..2f04997 --- /dev/null +++ b/shell_cmds/sh/tests/builtins/type1.0 @@ -0,0 +1,8 @@ +# $FreeBSD: head/bin/sh/tests/builtins/type1.0 165931 2007-01-11 00:25:20Z stefanf $ +command -v not-here && exit 1 +command -v /not-here && exit 1 +command -V not-here && exit 1 +command -V /not-here && exit 1 +type not-here && exit 1 +type /not-here && exit 1 +exit 0 diff --git a/shell_cmds/sh/tests/builtins/type1.0.stderr b/shell_cmds/sh/tests/builtins/type1.0.stderr new file mode 100644 index 0000000..7853418 --- /dev/null +++ b/shell_cmds/sh/tests/builtins/type1.0.stderr @@ -0,0 +1,4 @@ +not-here: not found +/not-here: No such file or directory +not-here: not found +/not-here: No such file or directory diff --git a/shell_cmds/sh/tests/builtins/type2.0 b/shell_cmds/sh/tests/builtins/type2.0 new file mode 100644 index 0000000..7e0581e --- /dev/null +++ b/shell_cmds/sh/tests/builtins/type2.0 @@ -0,0 +1,26 @@ +# $FreeBSD: head/bin/sh/tests/builtins/type2.0 201344 2009-12-31 17:44:24Z jilles $ + +failures=0 + +check() { + if ! eval "$*"; then + echo "Failed: $*" + : $((failures += 1)) + fi +} + +check 'PATH=/libexec type ld-elf.so.1 >/dev/null' +check '! PATH=/libexec type ls 2>/dev/null' + +PATH=/libexec:$PATH + +check 'type ld-elf.so.1 >/dev/null' + +PATH=/libexec + +check 'type ld-elf.so.1 >/dev/null' +check '! type ls 2>/dev/null' +check 'PATH=/bin type ls >/dev/null' +check '! PATH=/bin type ld-elf.so.1 2>/dev/null' + +exit $((failures > 0)) diff --git a/shell_cmds/sh/tests/builtins/type3.0 b/shell_cmds/sh/tests/builtins/type3.0 new file mode 100644 index 0000000..dad07e9 --- /dev/null +++ b/shell_cmds/sh/tests/builtins/type3.0 @@ -0,0 +1,3 @@ +# $FreeBSD: head/bin/sh/tests/builtins/type3.0 255072 2013-08-30 12:09:59Z jilles $ + +[ "$(type type)" = "$(type -- type)" ] diff --git a/shell_cmds/sh/tests/builtins/unalias.0 b/shell_cmds/sh/tests/builtins/unalias.0 new file mode 100644 index 0000000..ffdc756 --- /dev/null +++ b/shell_cmds/sh/tests/builtins/unalias.0 @@ -0,0 +1,21 @@ +# $FreeBSD: head/bin/sh/tests/builtins/unalias.0 149781 2005-09-04 11:59:59Z stefanf $ +set -e + +alias false=true +false +unalias false +false && exit 1 +unalias false && exit 1 + +alias a1=foo a2=bar +unalias a1 a2 +unalias a1 && exit 1 +unalias a2 && exit 1 +alias a2=bar +unalias a1 a2 && exit 1 + +alias a1=foo a2=bar +unalias -a +unalias a1 && exit 1 +unalias a2 && exit 1 +exit 0 diff --git a/shell_cmds/sh/tests/builtins/var-assign.0 b/shell_cmds/sh/tests/builtins/var-assign.0 new file mode 100644 index 0000000..41b59b1 --- /dev/null +++ b/shell_cmds/sh/tests/builtins/var-assign.0 @@ -0,0 +1,55 @@ +# $FreeBSD: head/bin/sh/tests/builtins/var-assign.0 327281 2017-12-28 08:22:26Z eadler $ +IFS=, + +SPECIAL="break,\ + :,\ + continue,\ + . /dev/null, + eval, + exec, + export -p, + readonly -p, + set, + shift 0, + times, + trap, + unset foo" + +UTILS="alias,\ + bg,\ + bind,\ + cd,\ + command echo,\ + echo,\ + false,\ + fc -l,\ + fg,\ + getopts a var,\ + hash,\ + jobs,\ + printf a,\ + pwd,\ + read var < /dev/null,\ + test,\ + true,\ + type ls,\ + ulimit,\ + umask,\ + unalias -a,\ + wait" + +set -e + +# For special built-ins variable assignments affect the shell environment. +set -- ${SPECIAL} +for cmd in "$@" +do + ${SH} -c "VAR=1; VAR=0 ${cmd}; exit \${VAR}" >/dev/null 2>&1 +done + +# For other built-ins and utilities they do not. +set -- ${UTILS} +for cmd in "$@" +do + ${SH} -c "VAR=0; VAR=1 ${cmd}; exit \${VAR}" >/dev/null 2>&1 +done diff --git a/shell_cmds/sh/tests/builtins/var-assign2.0 b/shell_cmds/sh/tests/builtins/var-assign2.0 new file mode 100644 index 0000000..b9afb55 --- /dev/null +++ b/shell_cmds/sh/tests/builtins/var-assign2.0 @@ -0,0 +1,55 @@ +# $FreeBSD: head/bin/sh/tests/builtins/var-assign2.0 213738 2010-10-12 18:20:38Z obrien $ +IFS=, + +SPECIAL="break,\ + :,\ + continue,\ + . /dev/null,\ + eval,\ + exec,\ + export -p,\ + readonly -p,\ + set,\ + shift 0,\ + times,\ + trap,\ + unset foo" + +UTILS="alias,\ + bg,\ + bind,\ + cd,\ + command echo,\ + echo,\ + false,\ + fc -l,\ + fg,\ + getopts a var,\ + hash,\ + jobs,\ + printf a,\ + pwd,\ + read var < /dev/null,\ + test,\ + true,\ + type ls,\ + ulimit,\ + umask,\ + unalias -a,\ + wait" + +set -e + +# With 'command', variable assignments do not affect the shell environment. + +set -- ${SPECIAL} +for cmd in "$@" +do + ${SH} -c "VAR=0; VAR=1 command ${cmd}; exit \${VAR}" >/dev/null 2>&1 +done + +set -- ${UTILS} +for cmd in "$@" +do + ${SH} -c "VAR=0; VAR=1 command ${cmd}; exit \${VAR}" >/dev/null 2>&1 +done diff --git a/shell_cmds/sh/tests/builtins/wait1.0 b/shell_cmds/sh/tests/builtins/wait1.0 new file mode 100644 index 0000000..8538415 --- /dev/null +++ b/shell_cmds/sh/tests/builtins/wait1.0 @@ -0,0 +1,23 @@ +# $FreeBSD: head/bin/sh/tests/builtins/wait1.0 208476 2010-05-23 22:10:20Z jilles $ + +failures= +failure() { + echo "Error at line $1" >&2 + failures=x$failures +} + +exit 4 & p4=$! +exit 8 & p8=$! +wait $p4 +[ $? = 4 ] || failure $LINENO +wait $p8 +[ $? = 8 ] || failure $LINENO + +exit 3 & p3=$! +exit 7 & p7=$! +wait $p7 +[ $? = 7 ] || failure $LINENO +wait $p3 +[ $? = 3 ] || failure $LINENO + +test -z "$failures" diff --git a/shell_cmds/sh/tests/builtins/wait10.0 b/shell_cmds/sh/tests/builtins/wait10.0 new file mode 100644 index 0000000..c7e3975 --- /dev/null +++ b/shell_cmds/sh/tests/builtins/wait10.0 @@ -0,0 +1,5 @@ +# $FreeBSD: head/bin/sh/tests/builtins/wait10.0 251430 2013-06-05 19:40:52Z jilles $ +# Init cannot be a child of the shell. +exit 49 & p49=$! +wait 1 "$p49" +[ "$?" = 49 ] diff --git a/shell_cmds/sh/tests/builtins/wait2.0 b/shell_cmds/sh/tests/builtins/wait2.0 new file mode 100644 index 0000000..71adfde --- /dev/null +++ b/shell_cmds/sh/tests/builtins/wait2.0 @@ -0,0 +1,15 @@ +# $FreeBSD: head/bin/sh/tests/builtins/wait2.0 208476 2010-05-23 22:10:20Z jilles $ + +failures= +failure() { + echo "Error at line $1" >&2 + failures=x$failures +} + +for i in 1 2 3 4 5 6 7 8 9 10; do + exit $i & +done +wait || failure $LINENO +wait || failure $LINENO + +test -z "$failures" diff --git a/shell_cmds/sh/tests/builtins/wait3.0 b/shell_cmds/sh/tests/builtins/wait3.0 new file mode 100644 index 0000000..d3561ae --- /dev/null +++ b/shell_cmds/sh/tests/builtins/wait3.0 @@ -0,0 +1,21 @@ +# $FreeBSD: head/bin/sh/tests/builtins/wait3.0 236771 2012-06-08 22:54:25Z jilles $ + +failures= +failure() { + echo "Error at line $1" >&2 + failures=x$failures +} + +T=$(mktemp -d ${TMPDIR:-/tmp}/sh-test.XXXXXX) +trap 'rm -rf $T' 0 +cd $T || exit 3 +mkfifo fifo1 +for i in 1 2 3 4 5 6 7 8 9 10; do + exit $i 4<fifo1 & +done +exec 3>fifo1 +wait || failure $LINENO +(${SH} -c echo >&3) 2>/dev/null && failure $LINENO +wait || failure $LINENO + +test -z "$failures" diff --git a/shell_cmds/sh/tests/builtins/wait4.0 b/shell_cmds/sh/tests/builtins/wait4.0 new file mode 100644 index 0000000..358513c --- /dev/null +++ b/shell_cmds/sh/tests/builtins/wait4.0 @@ -0,0 +1,12 @@ +# $FreeBSD: head/bin/sh/tests/builtins/wait4.0 247206 2013-02-23 22:50:57Z jilles $ + +T=`mktemp -d ${TMPDIR:-/tmp}/sh-test.XXXXXX` +trap 'rm -rf $T' 0 +cd $T || exit 3 +mkfifo fifo1 +trapped= +trap trapped=1 QUIT +{ kill -QUIT $$; sleep 1; exit 4; } >fifo1 & +wait $! <fifo1 +r=$? +[ "$r" -gt 128 ] && [ -n "$trapped" ] diff --git a/shell_cmds/sh/tests/builtins/wait5.0 b/shell_cmds/sh/tests/builtins/wait5.0 new file mode 100644 index 0000000..48e1d2e --- /dev/null +++ b/shell_cmds/sh/tests/builtins/wait5.0 @@ -0,0 +1,12 @@ +# $FreeBSD: head/bin/sh/tests/builtins/wait5.0 247206 2013-02-23 22:50:57Z jilles $ + +T=`mktemp -d ${TMPDIR:-/tmp}/sh-test.XXXXXX` +trap 'rm -rf $T' 0 +cd $T || exit 3 +mkfifo fifo1 +trapped= +trap trapped=1 QUIT +{ kill -QUIT $$; sleep 1; exit 4; } >fifo1 & +wait <fifo1 +r=$? +[ "$r" -gt 128 ] && [ -n "$trapped" ] diff --git a/shell_cmds/sh/tests/builtins/wait6.0 b/shell_cmds/sh/tests/builtins/wait6.0 new file mode 100644 index 0000000..e87b9c9 --- /dev/null +++ b/shell_cmds/sh/tests/builtins/wait6.0 @@ -0,0 +1,3 @@ +# $FreeBSD: head/bin/sh/tests/builtins/wait6.0 248349 2013-03-15 20:29:31Z jilles $ + +wait -- diff --git a/shell_cmds/sh/tests/builtins/wait7.0 b/shell_cmds/sh/tests/builtins/wait7.0 new file mode 100644 index 0000000..2c50674 --- /dev/null +++ b/shell_cmds/sh/tests/builtins/wait7.0 @@ -0,0 +1,4 @@ +# $FreeBSD: head/bin/sh/tests/builtins/wait7.0 248349 2013-03-15 20:29:31Z jilles $ + +: & +wait -- $! diff --git a/shell_cmds/sh/tests/builtins/wait8.0 b/shell_cmds/sh/tests/builtins/wait8.0 new file mode 100644 index 0000000..15e23b7 --- /dev/null +++ b/shell_cmds/sh/tests/builtins/wait8.0 @@ -0,0 +1,7 @@ +# $FreeBSD: head/bin/sh/tests/builtins/wait8.0 251429 2013-06-05 19:08:22Z jilles $ + +exit 44 & p44=$! +exit 45 & p45=$! +exit 7 & p7=$! +wait "$p44" "$p7" "$p45" +[ "$?" = 45 ] diff --git a/shell_cmds/sh/tests/builtins/wait9.127 b/shell_cmds/sh/tests/builtins/wait9.127 new file mode 100644 index 0000000..9bed737 --- /dev/null +++ b/shell_cmds/sh/tests/builtins/wait9.127 @@ -0,0 +1,3 @@ +# $FreeBSD: head/bin/sh/tests/builtins/wait9.127 251430 2013-06-05 19:40:52Z jilles $ +# Init cannot be a child of the shell. +wait 1 diff --git a/shell_cmds/sh/tests/errors/Makefile b/shell_cmds/sh/tests/errors/Makefile new file mode 100644 index 0000000..a234a05 --- /dev/null +++ b/shell_cmds/sh/tests/errors/Makefile @@ -0,0 +1,35 @@ +# $FreeBSD: head/bin/sh/tests/errors/Makefile 319575 2017-06-04 20:52:55Z jilles $ + +PACKAGE= tests + +TESTSDIR= ${TESTSBASE}/bin/sh/${.CURDIR:T} + +.PATH: ${.CURDIR:H} +ATF_TESTS_SH= functional_test + +${PACKAGE}FILES+= assignment-error1.0 +${PACKAGE}FILES+= assignment-error2.0 +${PACKAGE}FILES+= backquote-error1.0 +${PACKAGE}FILES+= backquote-error2.0 +${PACKAGE}FILES+= bad-binary1.126 +${PACKAGE}FILES+= bad-keyword1.0 +${PACKAGE}FILES+= bad-parm-exp1.0 +${PACKAGE}FILES+= bad-parm-exp2.2 bad-parm-exp2.2.stderr +${PACKAGE}FILES+= bad-parm-exp3.2 bad-parm-exp3.2.stderr +${PACKAGE}FILES+= bad-parm-exp4.2 bad-parm-exp4.2.stderr +${PACKAGE}FILES+= bad-parm-exp5.2 bad-parm-exp5.2.stderr +${PACKAGE}FILES+= bad-parm-exp6.2 bad-parm-exp6.2.stderr +${PACKAGE}FILES+= bad-parm-exp7.0 +${PACKAGE}FILES+= bad-parm-exp8.0 +${PACKAGE}FILES+= option-error.0 +${PACKAGE}FILES+= redirection-error.0 +${PACKAGE}FILES+= redirection-error2.2 +${PACKAGE}FILES+= redirection-error3.0 +${PACKAGE}FILES+= redirection-error4.0 +${PACKAGE}FILES+= redirection-error5.0 +${PACKAGE}FILES+= redirection-error6.0 +${PACKAGE}FILES+= redirection-error7.0 +${PACKAGE}FILES+= redirection-error8.0 +${PACKAGE}FILES+= write-error1.0 + +.include <bsd.test.mk> diff --git a/shell_cmds/sh/tests/errors/Makefile.depend b/shell_cmds/sh/tests/errors/Makefile.depend new file mode 100644 index 0000000..dbfdc34 --- /dev/null +++ b/shell_cmds/sh/tests/errors/Makefile.depend @@ -0,0 +1,11 @@ +# $FreeBSD: head/bin/sh/tests/errors/Makefile.depend 296587 2016-03-09 22:46:01Z bdrewery $ +# Autogenerated - do NOT edit! + +DIRDEPS = \ + + +.include <dirdeps.mk> + +.if ${DEP_RELDIR} == ${_DEP_RELDIR} +# local dependencies - needed for -jN in clean tree +.endif diff --git a/shell_cmds/sh/tests/errors/assignment-error1.0 b/shell_cmds/sh/tests/errors/assignment-error1.0 new file mode 100644 index 0000000..2106b5a --- /dev/null +++ b/shell_cmds/sh/tests/errors/assignment-error1.0 @@ -0,0 +1,30 @@ +# $FreeBSD: head/bin/sh/tests/errors/assignment-error1.0 213738 2010-10-12 18:20:38Z obrien $ +IFS=, + +SPECIAL="break,\ + :,\ + continue,\ + . /dev/null,\ + eval,\ + exec,\ + export -p,\ + readonly -p,\ + set,\ + shift,\ + times,\ + trap,\ + unset foo" + +# If there is no command word, the shell must abort on an assignment error. +${SH} -c "readonly a=0; a=2; exit 0" 2>/dev/null && exit 1 + +# Special built-in utilities must abort on an assignment error. +set -- ${SPECIAL} +for cmd in "$@" +do + ${SH} -c "readonly a=0; a=2 ${cmd}; exit 0" 2>/dev/null && exit 1 +done + +# Other utilities must not abort; we currently still execute them. +${SH} -c 'readonly a=0; a=1 true; exit $a' 2>/dev/null || exit 1 +${SH} -c 'readonly a=0; a=1 command :; exit $a' 2>/dev/null || exit 1 diff --git a/shell_cmds/sh/tests/errors/assignment-error2.0 b/shell_cmds/sh/tests/errors/assignment-error2.0 new file mode 100644 index 0000000..68f2d48 --- /dev/null +++ b/shell_cmds/sh/tests/errors/assignment-error2.0 @@ -0,0 +1,8 @@ +# $FreeBSD: head/bin/sh/tests/errors/assignment-error2.0 216870 2011-01-01 13:26:18Z jilles $ + +set -e +HOME=/ +readonly HOME +cd /sbin +{ HOME=/bin cd; } 2>/dev/null || : +[ "$(pwd)" != /bin ] diff --git a/shell_cmds/sh/tests/errors/backquote-error1.0 b/shell_cmds/sh/tests/errors/backquote-error1.0 new file mode 100644 index 0000000..d35d0b7 --- /dev/null +++ b/shell_cmds/sh/tests/errors/backquote-error1.0 @@ -0,0 +1,4 @@ +# $FreeBSD: head/bin/sh/tests/errors/backquote-error1.0 213738 2010-10-12 18:20:38Z obrien $ + +echo 'echo `for` echo ".BAD"CODE.' | ${SH} +m -i 2>&1 | grep -q BADCODE && exit 1 +exit 0 diff --git a/shell_cmds/sh/tests/errors/backquote-error2.0 b/shell_cmds/sh/tests/errors/backquote-error2.0 new file mode 100644 index 0000000..a5955dd --- /dev/null +++ b/shell_cmds/sh/tests/errors/backquote-error2.0 @@ -0,0 +1,7 @@ +# $FreeBSD: head/bin/sh/tests/errors/backquote-error2.0 213738 2010-10-12 18:20:38Z obrien $ + +${SH} -c 'echo `echo .BA"DCODE.` +echo ".BAD"CODE.' 2>&1 | grep -q BADCODE && exit 1 +echo '`"`' | ${SH} -n 2>/dev/null && exit 1 +echo '`'"'"'`' | ${SH} -n 2>/dev/null && exit 1 +exit 0 diff --git a/shell_cmds/sh/tests/errors/bad-binary1.126 b/shell_cmds/sh/tests/errors/bad-binary1.126 new file mode 100644 index 0000000..7c7c67f --- /dev/null +++ b/shell_cmds/sh/tests/errors/bad-binary1.126 @@ -0,0 +1,12 @@ +# $FreeBSD: head/bin/sh/tests/errors/bad-binary1.126 218320 2011-02-05 12:54:59Z jilles $ +# Checking for binary "scripts" without magic number is permitted but not +# required by POSIX. However, it is preferable to getting errors like +# Syntax error: word unexpected (expecting ")") +# from trying to execute ELF binaries for the wrong architecture. + +T=`mktemp -d "${TMPDIR:-/tmp}/sh-test.XXXXXXXX"` || exit +trap 'rm -rf "${T}"' 0 +printf '\0echo bad\n' >"$T/testshellproc" +chmod 755 "$T/testshellproc" +PATH=$T:$PATH +testshellproc 2>/dev/null diff --git a/shell_cmds/sh/tests/errors/bad-keyword1.0 b/shell_cmds/sh/tests/errors/bad-keyword1.0 new file mode 100644 index 0000000..d2680d6 --- /dev/null +++ b/shell_cmds/sh/tests/errors/bad-keyword1.0 @@ -0,0 +1,4 @@ +# $FreeBSD: head/bin/sh/tests/errors/bad-keyword1.0 216398 2010-12-12 21:18:16Z jilles $ + +echo ':; fi' | ${SH} -n 2>/dev/null && exit 1 +exit 0 diff --git a/shell_cmds/sh/tests/errors/bad-parm-exp1.0 b/shell_cmds/sh/tests/errors/bad-parm-exp1.0 new file mode 100644 index 0000000..f288294 --- /dev/null +++ b/shell_cmds/sh/tests/errors/bad-parm-exp1.0 @@ -0,0 +1,7 @@ +# $FreeBSD: head/bin/sh/tests/errors/bad-parm-exp1.0 164004 2006-11-05 18:41:23Z stefanf $ +false && { + ${} + ${foo/} + ${foo@bar} +} +: diff --git a/shell_cmds/sh/tests/errors/bad-parm-exp2.2 b/shell_cmds/sh/tests/errors/bad-parm-exp2.2 new file mode 100644 index 0000000..7424840 --- /dev/null +++ b/shell_cmds/sh/tests/errors/bad-parm-exp2.2 @@ -0,0 +1,2 @@ +# $FreeBSD: head/bin/sh/tests/errors/bad-parm-exp2.2 270101 2014-08-17 14:26:12Z jilles $ +eval '${}' diff --git a/shell_cmds/sh/tests/errors/bad-parm-exp2.2.stderr b/shell_cmds/sh/tests/errors/bad-parm-exp2.2.stderr new file mode 100644 index 0000000..51ea69c --- /dev/null +++ b/shell_cmds/sh/tests/errors/bad-parm-exp2.2.stderr @@ -0,0 +1 @@ +eval: ${}: Bad substitution diff --git a/shell_cmds/sh/tests/errors/bad-parm-exp3.2 b/shell_cmds/sh/tests/errors/bad-parm-exp3.2 new file mode 100644 index 0000000..56a52df --- /dev/null +++ b/shell_cmds/sh/tests/errors/bad-parm-exp3.2 @@ -0,0 +1,2 @@ +# $FreeBSD: head/bin/sh/tests/errors/bad-parm-exp3.2 270101 2014-08-17 14:26:12Z jilles $ +eval '${foo/}' diff --git a/shell_cmds/sh/tests/errors/bad-parm-exp3.2.stderr b/shell_cmds/sh/tests/errors/bad-parm-exp3.2.stderr new file mode 100644 index 0000000..70473f9 --- /dev/null +++ b/shell_cmds/sh/tests/errors/bad-parm-exp3.2.stderr @@ -0,0 +1 @@ +eval: ${foo/}: Bad substitution diff --git a/shell_cmds/sh/tests/errors/bad-parm-exp4.2 b/shell_cmds/sh/tests/errors/bad-parm-exp4.2 new file mode 100644 index 0000000..e0a7099 --- /dev/null +++ b/shell_cmds/sh/tests/errors/bad-parm-exp4.2 @@ -0,0 +1,2 @@ +# $FreeBSD: head/bin/sh/tests/errors/bad-parm-exp4.2 270101 2014-08-17 14:26:12Z jilles $ +eval '${foo:@abc}' diff --git a/shell_cmds/sh/tests/errors/bad-parm-exp4.2.stderr b/shell_cmds/sh/tests/errors/bad-parm-exp4.2.stderr new file mode 100644 index 0000000..3363f51 --- /dev/null +++ b/shell_cmds/sh/tests/errors/bad-parm-exp4.2.stderr @@ -0,0 +1 @@ +eval: ${foo:@...}: Bad substitution diff --git a/shell_cmds/sh/tests/errors/bad-parm-exp5.2 b/shell_cmds/sh/tests/errors/bad-parm-exp5.2 new file mode 100644 index 0000000..fa26259 --- /dev/null +++ b/shell_cmds/sh/tests/errors/bad-parm-exp5.2 @@ -0,0 +1,2 @@ +# $FreeBSD: head/bin/sh/tests/errors/bad-parm-exp5.2 270101 2014-08-17 14:26:12Z jilles $ +eval '${/}' diff --git a/shell_cmds/sh/tests/errors/bad-parm-exp5.2.stderr b/shell_cmds/sh/tests/errors/bad-parm-exp5.2.stderr new file mode 100644 index 0000000..13763f8 --- /dev/null +++ b/shell_cmds/sh/tests/errors/bad-parm-exp5.2.stderr @@ -0,0 +1 @@ +eval: ${/}: Bad substitution diff --git a/shell_cmds/sh/tests/errors/bad-parm-exp6.2 b/shell_cmds/sh/tests/errors/bad-parm-exp6.2 new file mode 100644 index 0000000..51858e8 --- /dev/null +++ b/shell_cmds/sh/tests/errors/bad-parm-exp6.2 @@ -0,0 +1,2 @@ +# $FreeBSD: head/bin/sh/tests/errors/bad-parm-exp6.2 270101 2014-08-17 14:26:12Z jilles $ +eval '${#foo^}' diff --git a/shell_cmds/sh/tests/errors/bad-parm-exp6.2.stderr b/shell_cmds/sh/tests/errors/bad-parm-exp6.2.stderr new file mode 100644 index 0000000..cc56f65 --- /dev/null +++ b/shell_cmds/sh/tests/errors/bad-parm-exp6.2.stderr @@ -0,0 +1 @@ +eval: ${foo...}: Bad substitution diff --git a/shell_cmds/sh/tests/errors/bad-parm-exp7.0 b/shell_cmds/sh/tests/errors/bad-parm-exp7.0 new file mode 100644 index 0000000..d7bff92 --- /dev/null +++ b/shell_cmds/sh/tests/errors/bad-parm-exp7.0 @@ -0,0 +1,4 @@ +# $FreeBSD: head/bin/sh/tests/errors/bad-parm-exp7.0 287081 2015-08-23 20:44:53Z jilles $ + +v=1 +eval ": $(printf '${v-${\372}}')" diff --git a/shell_cmds/sh/tests/errors/bad-parm-exp8.0 b/shell_cmds/sh/tests/errors/bad-parm-exp8.0 new file mode 100644 index 0000000..24f6bff --- /dev/null +++ b/shell_cmds/sh/tests/errors/bad-parm-exp8.0 @@ -0,0 +1,4 @@ +# $FreeBSD: head/bin/sh/tests/errors/bad-parm-exp8.0 287081 2015-08-23 20:44:53Z jilles $ + +v=1 +eval ": $(printf '${v-${w\372}}')" diff --git a/shell_cmds/sh/tests/errors/option-error.0 b/shell_cmds/sh/tests/errors/option-error.0 new file mode 100644 index 0000000..473c0b9 --- /dev/null +++ b/shell_cmds/sh/tests/errors/option-error.0 @@ -0,0 +1,46 @@ +# $FreeBSD: head/bin/sh/tests/errors/option-error.0 213738 2010-10-12 18:20:38Z obrien $ +IFS=, + +SPECIAL="break abc,\ + continue abc,\ + ., + exit abc, + export -x, + readonly -x, + return abc, + set -z, + shift abc, + trap -y, + unset -y" + +UTILS="alias -y,\ + cat -z,\ + cd abc def,\ + command break abc,\ + expr 1 +,\ + fc -z,\ + getopts,\ + hash -z,\ + jobs -z,\ + printf,\ + pwd abc,\ + read,\ + test abc =,\ + ulimit -z,\ + umask -z,\ + unalias -z,\ + wait abc" + +# Special built-in utilities must abort on an option or operand error. +set -- ${SPECIAL} +for cmd in "$@" +do + ${SH} -c "${cmd}; exit 0" 2>/dev/null && exit 1 +done + +# Other utilities must not abort. +set -- ${UTILS} +for cmd in "$@" +do + ${SH} -c "${cmd}; exit 0" 2>/dev/null || exit 1 +done diff --git a/shell_cmds/sh/tests/errors/redirection-error.0 b/shell_cmds/sh/tests/errors/redirection-error.0 new file mode 100644 index 0000000..2a67698 --- /dev/null +++ b/shell_cmds/sh/tests/errors/redirection-error.0 @@ -0,0 +1,53 @@ +# $FreeBSD: head/bin/sh/tests/errors/redirection-error.0 213738 2010-10-12 18:20:38Z obrien $ +IFS=, + +SPECIAL="break,\ + :,\ + continue,\ + . /dev/null, + eval, + exec, + export -p, + readonly -p, + set, + shift, + times, + trap, + unset foo" + +UTILS="alias,\ + bg,\ + bind,\ + cd,\ + command echo,\ + echo,\ + false,\ + fc -l,\ + fg,\ + getopts a -a,\ + hash,\ + jobs,\ + printf a,\ + pwd,\ + read var < /dev/null,\ + test,\ + true,\ + type ls,\ + ulimit,\ + umask,\ + unalias -a,\ + wait" + +# Special built-in utilities must abort on a redirection error. +set -- ${SPECIAL} +for cmd in "$@" +do + ${SH} -c "${cmd} > /; exit 0" 2>/dev/null && exit 1 +done + +# Other utilities must not abort. +set -- ${UTILS} +for cmd in "$@" +do + ${SH} -c "${cmd} > /; exit 0" 2>/dev/null || exit 1 +done diff --git a/shell_cmds/sh/tests/errors/redirection-error2.2 b/shell_cmds/sh/tests/errors/redirection-error2.2 new file mode 100644 index 0000000..595e959 --- /dev/null +++ b/shell_cmds/sh/tests/errors/redirection-error2.2 @@ -0,0 +1,4 @@ +# $FreeBSD: head/bin/sh/tests/errors/redirection-error2.2 213738 2010-10-12 18:20:38Z obrien $ + +# sh should fail gracefully on this bad redirect +${SH} -c 'echo 1 >&$a' 2>/dev/null diff --git a/shell_cmds/sh/tests/errors/redirection-error3.0 b/shell_cmds/sh/tests/errors/redirection-error3.0 new file mode 100644 index 0000000..fd7a334 --- /dev/null +++ b/shell_cmds/sh/tests/errors/redirection-error3.0 @@ -0,0 +1,54 @@ +# $FreeBSD: head/bin/sh/tests/errors/redirection-error3.0 213738 2010-10-12 18:20:38Z obrien $ +IFS=, + +SPECIAL="break,\ + :,\ + continue,\ + . /dev/null,\ + eval,\ + exec,\ + export -p,\ + readonly -p,\ + set,\ + shift,\ + times,\ + trap,\ + unset foo" + +UTILS="alias,\ + bg,\ + bind,\ + cd,\ + command echo,\ + echo,\ + false,\ + fc -l,\ + fg,\ + getopts a -a,\ + hash,\ + jobs,\ + printf a,\ + pwd,\ + read var < /dev/null,\ + test,\ + true,\ + type ls,\ + ulimit,\ + umask,\ + unalias -a,\ + wait" + +# When used with 'command', neither special built-in utilities nor other +# utilities must abort on a redirection error. + +set -- ${SPECIAL} +for cmd in "$@" +do + ${SH} -c "command ${cmd} > /; exit 0" 2>/dev/null || exit 1 +done + +set -- ${UTILS} +for cmd in "$@" +do + ${SH} -c "command ${cmd} > /; exit 0" 2>/dev/null || exit 1 +done diff --git a/shell_cmds/sh/tests/errors/redirection-error4.0 b/shell_cmds/sh/tests/errors/redirection-error4.0 new file mode 100644 index 0000000..ed58499 --- /dev/null +++ b/shell_cmds/sh/tests/errors/redirection-error4.0 @@ -0,0 +1,7 @@ +# $FreeBSD: head/bin/sh/tests/errors/redirection-error4.0 205138 2010-03-13 22:53:17Z jilles $ +# A redirection error should not abort the shell if there is no command word. +exec 2>/dev/null +</var/empty/x +</var/empty/x y=2 +y=2 </var/empty/x +exit 0 diff --git a/shell_cmds/sh/tests/errors/redirection-error5.0 b/shell_cmds/sh/tests/errors/redirection-error5.0 new file mode 100644 index 0000000..b96c14e --- /dev/null +++ b/shell_cmds/sh/tests/errors/redirection-error5.0 @@ -0,0 +1,5 @@ +# $FreeBSD: head/bin/sh/tests/errors/redirection-error5.0 205153 2010-03-14 13:51:12Z jilles $ +# A redirection error on a subshell should not abort the shell. +exec 2>/dev/null +( echo bad ) </var/empty/x +exit 0 diff --git a/shell_cmds/sh/tests/errors/redirection-error6.0 b/shell_cmds/sh/tests/errors/redirection-error6.0 new file mode 100644 index 0000000..1d84e6c --- /dev/null +++ b/shell_cmds/sh/tests/errors/redirection-error6.0 @@ -0,0 +1,12 @@ +# $FreeBSD: head/bin/sh/tests/errors/redirection-error6.0 205154 2010-03-14 14:24:35Z jilles $ +# A redirection error on a compound command should not abort the shell. +exec 2>/dev/null +{ echo bad; } </var/empty/x +if :; then echo bad; fi </var/empty/x +for i in 1; do echo bad; done </var/empty/x +i=0 +while [ $i = 0 ]; do echo bad; i=1; done </var/empty/x +i=0 +until [ $i != 0 ]; do echo bad; i=1; done </var/empty/x +case i in *) echo bad ;; esac </var/empty/x +exit 0 diff --git a/shell_cmds/sh/tests/errors/redirection-error7.0 b/shell_cmds/sh/tests/errors/redirection-error7.0 new file mode 100644 index 0000000..ef56434 --- /dev/null +++ b/shell_cmds/sh/tests/errors/redirection-error7.0 @@ -0,0 +1,7 @@ +# $FreeBSD: head/bin/sh/tests/errors/redirection-error7.0 216851 2010-12-31 18:20:17Z jilles $ + +! dummy=$( + exec 3>&1 >&2 2>&3 + ulimit -n 9 + exec 9<. +) && [ -n "$dummy" ] diff --git a/shell_cmds/sh/tests/errors/redirection-error8.0 b/shell_cmds/sh/tests/errors/redirection-error8.0 new file mode 100644 index 0000000..97364ac --- /dev/null +++ b/shell_cmds/sh/tests/errors/redirection-error8.0 @@ -0,0 +1,5 @@ +# $FreeBSD: head/bin/sh/tests/errors/redirection-error8.0 319575 2017-06-04 20:52:55Z jilles $ + +$SH -c '{ { :; } </var/empty/x; } 2>/dev/null || kill -INT $$; echo continued' +r=$? +[ "$r" -gt 128 ] && [ "$(kill -l "$r")" = INT ] diff --git a/shell_cmds/sh/tests/errors/write-error1.0 b/shell_cmds/sh/tests/errors/write-error1.0 new file mode 100644 index 0000000..3001e5c --- /dev/null +++ b/shell_cmds/sh/tests/errors/write-error1.0 @@ -0,0 +1,3 @@ +# $FreeBSD: head/bin/sh/tests/errors/write-error1.0 244924 2013-01-01 12:48:24Z jilles $ + +! echo >&- 2>/dev/null diff --git a/shell_cmds/sh/tests/execution/Makefile b/shell_cmds/sh/tests/execution/Makefile new file mode 100644 index 0000000..6e80719 --- /dev/null +++ b/shell_cmds/sh/tests/execution/Makefile @@ -0,0 +1,57 @@ +# $FreeBSD: head/bin/sh/tests/execution/Makefile 308229 2016-11-02 22:33:37Z jilles $ + +PACKAGE= tests + +TESTSDIR= ${TESTSBASE}/bin/sh/${.CURDIR:T} + +.PATH: ${.CURDIR:H} +ATF_TESTS_SH= functional_test + +${PACKAGE}FILES+= bg1.0 +${PACKAGE}FILES+= bg2.0 +${PACKAGE}FILES+= bg3.0 +${PACKAGE}FILES+= bg4.0 +${PACKAGE}FILES+= bg5.0 +${PACKAGE}FILES+= bg6.0 bg6.0.stdout +${PACKAGE}FILES+= bg7.0 +${PACKAGE}FILES+= bg8.0 +${PACKAGE}FILES+= bg9.0 +${PACKAGE}FILES+= bg10.0 bg10.0.stdout +${PACKAGE}FILES+= fork1.0 +${PACKAGE}FILES+= fork2.0 +${PACKAGE}FILES+= fork3.0 +${PACKAGE}FILES+= func1.0 +${PACKAGE}FILES+= func2.0 +${PACKAGE}FILES+= func3.0 +${PACKAGE}FILES+= hash1.0 +${PACKAGE}FILES+= int-cmd1.0 +${PACKAGE}FILES+= killed1.0 +${PACKAGE}FILES+= killed2.0 +${PACKAGE}FILES+= not1.0 +${PACKAGE}FILES+= not2.0 +${PACKAGE}FILES+= path1.0 +${PACKAGE}FILES+= redir1.0 +${PACKAGE}FILES+= redir2.0 +${PACKAGE}FILES+= redir3.0 +${PACKAGE}FILES+= redir4.0 +${PACKAGE}FILES+= redir5.0 +${PACKAGE}FILES+= redir6.0 +${PACKAGE}FILES+= redir7.0 +${PACKAGE}FILES+= set-C1.0 +${PACKAGE}FILES+= set-n1.0 +${PACKAGE}FILES+= set-n2.0 +${PACKAGE}FILES+= set-n3.0 +${PACKAGE}FILES+= set-n4.0 +${PACKAGE}FILES+= set-x1.0 +${PACKAGE}FILES+= set-x2.0 +${PACKAGE}FILES+= set-x3.0 +${PACKAGE}FILES+= set-x4.0 +${PACKAGE}FILES+= shellproc1.0 +${PACKAGE}FILES+= subshell1.0 subshell1.0.stdout +${PACKAGE}FILES+= subshell2.0 +${PACKAGE}FILES+= subshell3.0 +${PACKAGE}FILES+= subshell4.0 +${PACKAGE}FILES+= unknown1.0 +${PACKAGE}FILES+= var-assign1.0 + +.include <bsd.test.mk> diff --git a/shell_cmds/sh/tests/execution/Makefile.depend b/shell_cmds/sh/tests/execution/Makefile.depend new file mode 100644 index 0000000..fac3607 --- /dev/null +++ b/shell_cmds/sh/tests/execution/Makefile.depend @@ -0,0 +1,11 @@ +# $FreeBSD: head/bin/sh/tests/execution/Makefile.depend 296587 2016-03-09 22:46:01Z bdrewery $ +# Autogenerated - do NOT edit! + +DIRDEPS = \ + + +.include <dirdeps.mk> + +.if ${DEP_RELDIR} == ${_DEP_RELDIR} +# local dependencies - needed for -jN in clean tree +.endif diff --git a/shell_cmds/sh/tests/execution/bg1.0 b/shell_cmds/sh/tests/execution/bg1.0 new file mode 100644 index 0000000..95b51c6 --- /dev/null +++ b/shell_cmds/sh/tests/execution/bg1.0 @@ -0,0 +1,3 @@ +# $FreeBSD: head/bin/sh/tests/execution/bg1.0 221027 2011-04-25 20:54:12Z jilles $ + +: `false` & diff --git a/shell_cmds/sh/tests/execution/bg10.0 b/shell_cmds/sh/tests/execution/bg10.0 new file mode 100644 index 0000000..06a27f4 --- /dev/null +++ b/shell_cmds/sh/tests/execution/bg10.0 @@ -0,0 +1,4 @@ +# $FreeBSD: head/bin/sh/tests/execution/bg10.0 258535 2013-11-24 23:12:13Z jilles $ +# The redirection overrides the </dev/null implicit in a background command. + +echo yes | ${SH} -c '{ cat & wait; } <&0' diff --git a/shell_cmds/sh/tests/execution/bg10.0.stdout b/shell_cmds/sh/tests/execution/bg10.0.stdout new file mode 100644 index 0000000..7cfab5b --- /dev/null +++ b/shell_cmds/sh/tests/execution/bg10.0.stdout @@ -0,0 +1 @@ +yes diff --git a/shell_cmds/sh/tests/execution/bg2.0 b/shell_cmds/sh/tests/execution/bg2.0 new file mode 100644 index 0000000..74ee54a --- /dev/null +++ b/shell_cmds/sh/tests/execution/bg2.0 @@ -0,0 +1,5 @@ +# $FreeBSD: head/bin/sh/tests/execution/bg2.0 221027 2011-04-25 20:54:12Z jilles $ + +f() { return 42; } +f +: | : & diff --git a/shell_cmds/sh/tests/execution/bg3.0 b/shell_cmds/sh/tests/execution/bg3.0 new file mode 100644 index 0000000..95f37d3 --- /dev/null +++ b/shell_cmds/sh/tests/execution/bg3.0 @@ -0,0 +1,5 @@ +# $FreeBSD: head/bin/sh/tests/execution/bg3.0 221027 2011-04-25 20:54:12Z jilles $ + +f() { return 42; } +f +(:) & diff --git a/shell_cmds/sh/tests/execution/bg4.0 b/shell_cmds/sh/tests/execution/bg4.0 new file mode 100644 index 0000000..1220fd3 --- /dev/null +++ b/shell_cmds/sh/tests/execution/bg4.0 @@ -0,0 +1,6 @@ +# $FreeBSD: head/bin/sh/tests/execution/bg4.0 223283 2011-06-19 00:00:36Z jilles $ + +x='' +: ${x:=1} & +wait +exit ${x:-0} diff --git a/shell_cmds/sh/tests/execution/bg5.0 b/shell_cmds/sh/tests/execution/bg5.0 new file mode 100644 index 0000000..e820035 --- /dev/null +++ b/shell_cmds/sh/tests/execution/bg5.0 @@ -0,0 +1,4 @@ +# $FreeBSD: head/bin/sh/tests/execution/bg5.0 258489 2013-11-22 21:50:13Z jilles $ +# A background command has an implicit </dev/null redirection. + +echo bad | ${SH} -c '{ cat & wait; }' diff --git a/shell_cmds/sh/tests/execution/bg6.0 b/shell_cmds/sh/tests/execution/bg6.0 new file mode 100644 index 0000000..466adc2 --- /dev/null +++ b/shell_cmds/sh/tests/execution/bg6.0 @@ -0,0 +1,4 @@ +# $FreeBSD: head/bin/sh/tests/execution/bg6.0 258489 2013-11-22 21:50:13Z jilles $ +# The redirection overrides the </dev/null implicit in a background command. + +echo yes | ${SH} -c '{ cat & wait; } </dev/stdin' diff --git a/shell_cmds/sh/tests/execution/bg6.0.stdout b/shell_cmds/sh/tests/execution/bg6.0.stdout new file mode 100644 index 0000000..7cfab5b --- /dev/null +++ b/shell_cmds/sh/tests/execution/bg6.0.stdout @@ -0,0 +1 @@ +yes diff --git a/shell_cmds/sh/tests/execution/bg7.0 b/shell_cmds/sh/tests/execution/bg7.0 new file mode 100644 index 0000000..788161e --- /dev/null +++ b/shell_cmds/sh/tests/execution/bg7.0 @@ -0,0 +1,5 @@ +# $FreeBSD: head/bin/sh/tests/execution/bg7.0 258533 2013-11-24 22:45:49Z jilles $ +# The redirection does not apply to the background command, and therefore +# does not override the implicit </dev/null. + +echo bad | ${SH} -c '</dev/null; { cat & wait; }' diff --git a/shell_cmds/sh/tests/execution/bg8.0 b/shell_cmds/sh/tests/execution/bg8.0 new file mode 100644 index 0000000..aa571dc --- /dev/null +++ b/shell_cmds/sh/tests/execution/bg8.0 @@ -0,0 +1,5 @@ +# $FreeBSD: head/bin/sh/tests/execution/bg8.0 258533 2013-11-24 22:45:49Z jilles $ +# The redirection does not apply to the background command, and therefore +# does not override the implicit </dev/null. + +echo bad | ${SH} -c 'command eval \) </dev/null 2>/dev/null; { cat & wait; }' diff --git a/shell_cmds/sh/tests/execution/bg9.0 b/shell_cmds/sh/tests/execution/bg9.0 new file mode 100644 index 0000000..4af1bae --- /dev/null +++ b/shell_cmds/sh/tests/execution/bg9.0 @@ -0,0 +1,5 @@ +# $FreeBSD: head/bin/sh/tests/execution/bg9.0 258533 2013-11-24 22:45:49Z jilles $ +# The redirection does not apply to the background command, and therefore +# does not override the implicit </dev/null. + +echo bad | ${SH} -c 'command eval eval \\\) \</dev/null 2>/dev/null; { cat & wait; }' diff --git a/shell_cmds/sh/tests/execution/fork1.0 b/shell_cmds/sh/tests/execution/fork1.0 new file mode 100644 index 0000000..80b013b --- /dev/null +++ b/shell_cmds/sh/tests/execution/fork1.0 @@ -0,0 +1,10 @@ +# $FreeBSD: head/bin/sh/tests/execution/fork1.0 218850 2011-02-19 13:22:18Z jilles $ + +shname=${SH%% *} +shname=${shname##*/} + +result=$(${SH} -c 'ps -p $$ -o comm=') +test "$result" = "ps" || exit 1 + +result=$(${SH} -c 'ps -p $$ -o comm=; :') +test "$result" = "$shname" || exit 1 diff --git a/shell_cmds/sh/tests/execution/fork2.0 b/shell_cmds/sh/tests/execution/fork2.0 new file mode 100644 index 0000000..2639165 --- /dev/null +++ b/shell_cmds/sh/tests/execution/fork2.0 @@ -0,0 +1,9 @@ +# $FreeBSD: head/bin/sh/tests/execution/fork2.0 213738 2010-10-12 18:20:38Z obrien $ + +result=$(${SH} -c '(/bin/sleep 1)& sleep 0.1; ps -p $! -o comm=; kill $!') +test "$result" = sleep || exit 1 + +result=$(${SH} -c '{ trap "echo trapped" EXIT; (/usr/bin/true); } & wait') +test "$result" = trapped || exit 1 + +exit 0 diff --git a/shell_cmds/sh/tests/execution/fork3.0 b/shell_cmds/sh/tests/execution/fork3.0 new file mode 100644 index 0000000..008be7b --- /dev/null +++ b/shell_cmds/sh/tests/execution/fork3.0 @@ -0,0 +1,4 @@ +# $FreeBSD: head/bin/sh/tests/execution/fork3.0 220978 2011-04-23 22:28:56Z jilles $ + +result=$(${SH} -c 'f() { ps -p $$ -o comm=; }; f') +test "$result" = "ps" diff --git a/shell_cmds/sh/tests/execution/func1.0 b/shell_cmds/sh/tests/execution/func1.0 new file mode 100644 index 0000000..1af5495 --- /dev/null +++ b/shell_cmds/sh/tests/execution/func1.0 @@ -0,0 +1,4 @@ +# $FreeBSD: head/bin/sh/tests/execution/func1.0 213738 2010-10-12 18:20:38Z obrien $ + +MALLOC_OPTIONS=J ${SH} -c 'g() { g() { :; }; :; }; g' && +MALLOC_OPTIONS=J ${SH} -c 'g() { unset -f g; :; }; g' diff --git a/shell_cmds/sh/tests/execution/func2.0 b/shell_cmds/sh/tests/execution/func2.0 new file mode 100644 index 0000000..eb48cc8 --- /dev/null +++ b/shell_cmds/sh/tests/execution/func2.0 @@ -0,0 +1,12 @@ +# $FreeBSD: head/bin/sh/tests/execution/func2.0 211399 2010-08-16 17:18:08Z jilles $ +# The empty pairs of braces here are to test that this does not cause a crash. + +f() { } +f +hash -v f >/dev/null +f() { { }; } +f +hash -v f >/dev/null +f() { { } } +f +hash -v f >/dev/null diff --git a/shell_cmds/sh/tests/execution/func3.0 b/shell_cmds/sh/tests/execution/func3.0 new file mode 100644 index 0000000..209e255 --- /dev/null +++ b/shell_cmds/sh/tests/execution/func3.0 @@ -0,0 +1,7 @@ +# $FreeBSD: head/bin/sh/tests/execution/func3.0 216398 2010-12-12 21:18:16Z jilles $ + +# This may fail when parsing or when defining the function, or the definition +# may silently do nothing. In no event may the function be executed. + +${SH} -c 'unset() { echo overriding function executed, bad; }; v=1; unset v; exit "${v-0}"' 2>/dev/null +: diff --git a/shell_cmds/sh/tests/execution/hash1.0 b/shell_cmds/sh/tests/execution/hash1.0 new file mode 100644 index 0000000..d019296 --- /dev/null +++ b/shell_cmds/sh/tests/execution/hash1.0 @@ -0,0 +1,12 @@ +# $FreeBSD: head/bin/sh/tests/execution/hash1.0 218323 2011-02-05 14:01:46Z jilles $ + +T=`mktemp -d "${TMPDIR:-/tmp}/sh-test.XXXXXXXX"` || exit +trap 'rm -rf "${T}"' 0 +PATH=$T:$PATH +ls -ld . >/dev/null +cat <<EOF >"$T/ls" +: +EOF +chmod 755 "$T/ls" +PATH=$PATH +ls -ld . diff --git a/shell_cmds/sh/tests/execution/int-cmd1.0 b/shell_cmds/sh/tests/execution/int-cmd1.0 new file mode 100644 index 0000000..e980461 --- /dev/null +++ b/shell_cmds/sh/tests/execution/int-cmd1.0 @@ -0,0 +1,3 @@ +# $FreeBSD: head/bin/sh/tests/execution/int-cmd1.0 253271 2013-07-12 15:29:41Z jilles $ + +! echo echo bad | $SH -ic 'fi' 2>/dev/null diff --git a/shell_cmds/sh/tests/execution/killed1.0 b/shell_cmds/sh/tests/execution/killed1.0 new file mode 100644 index 0000000..ef9221d --- /dev/null +++ b/shell_cmds/sh/tests/execution/killed1.0 @@ -0,0 +1,8 @@ +# $FreeBSD: head/bin/sh/tests/execution/killed1.0 217557 2011-01-18 21:18:31Z jilles $ +# Sometimes the "Killed" message is not flushed soon enough and it +# is redirected along with the output of a builtin. +# Do not change the semicolon to a newline as it would hide the bug. + +exec 3>&1 +exec >/dev/null 2>&1 +${SH} -c 'kill -9 $$'; : >&3 2>&3 diff --git a/shell_cmds/sh/tests/execution/killed2.0 b/shell_cmds/sh/tests/execution/killed2.0 new file mode 100644 index 0000000..4d6de16 --- /dev/null +++ b/shell_cmds/sh/tests/execution/killed2.0 @@ -0,0 +1,10 @@ +# $FreeBSD: head/bin/sh/tests/execution/killed2.0 218105 2011-01-30 22:57:52Z jilles $ +# Most shells print a message when a foreground job is killed by a signal. +# POSIX allows this, provided the message is sent to stderr, not stdout. +# Some trickery is needed to capture the message as redirecting stderr of +# the command itself does not affect it. The colon command ensures that +# the subshell forks for ${SH}. + +exec 3>&1 +r=`(${SH} -c 'kill $$'; :) 2>&1 >&3` +[ -n "$r" ] diff --git a/shell_cmds/sh/tests/execution/not1.0 b/shell_cmds/sh/tests/execution/not1.0 new file mode 100644 index 0000000..c825e13 --- /dev/null +++ b/shell_cmds/sh/tests/execution/not1.0 @@ -0,0 +1,4 @@ +# $FreeBSD: head/bin/sh/tests/execution/not1.0 249407 2013-04-12 15:19:35Z jilles $ + +f() { ! return $1; } +f 0 && ! f 1 diff --git a/shell_cmds/sh/tests/execution/not2.0 b/shell_cmds/sh/tests/execution/not2.0 new file mode 100644 index 0000000..e4155a9 --- /dev/null +++ b/shell_cmds/sh/tests/execution/not2.0 @@ -0,0 +1,6 @@ +# $FreeBSD: head/bin/sh/tests/execution/not2.0 249407 2013-04-12 15:19:35Z jilles $ + +while :; do + ! break + exit 3 +done diff --git a/shell_cmds/sh/tests/execution/path1.0 b/shell_cmds/sh/tests/execution/path1.0 new file mode 100644 index 0000000..3d5c67b --- /dev/null +++ b/shell_cmds/sh/tests/execution/path1.0 @@ -0,0 +1,15 @@ +# $FreeBSD: head/bin/sh/tests/execution/path1.0 217206 2011-01-09 21:07:30Z jilles $ +# Some builtins should not be overridable via PATH. + +set -e +T=$(mktemp -d ${TMPDIR:-/tmp}/sh-test.XXXXXX) +trap 'rm -rf ${T}' 0 +echo '#!/bin/sh +echo bad' >"$T/cd" +chmod 755 "$T/cd" +cd /bin +oPATH=$PATH +PATH=$T:$PATH:%builtin +cd / +PATH=$oPATH +[ "$(pwd)" = / ] diff --git a/shell_cmds/sh/tests/execution/redir1.0 b/shell_cmds/sh/tests/execution/redir1.0 new file mode 100644 index 0000000..6310478 --- /dev/null +++ b/shell_cmds/sh/tests/execution/redir1.0 @@ -0,0 +1,27 @@ +# $FreeBSD: head/bin/sh/tests/execution/redir1.0 211408 2010-08-16 22:23:19Z jilles $ +trap ': $((brokenpipe+=1))' PIPE + +P=${TMPDIR:-/tmp} +cd $P +T=$(mktemp -d sh-test.XXXXXX) +cd $T + +brokenpipe=0 +mkfifo fifo1 fifo2 +read dummy >fifo2 <fifo1 & +{ + exec 4>fifo2 +} 3<fifo2 # Formerly, sh would keep fd 3 and a duplicate of it open. +echo dummy >fifo1 +if [ $brokenpipe -ne 0 ]; then + rc=3 +fi +wait +echo dummy >&4 2>/dev/null +if [ $brokenpipe -eq 1 ]; then + : ${rc:=0} +fi + +rm fifo1 fifo2 +rmdir ${P}/${T} +exit ${rc:-3} diff --git a/shell_cmds/sh/tests/execution/redir2.0 b/shell_cmds/sh/tests/execution/redir2.0 new file mode 100644 index 0000000..27509c7 --- /dev/null +++ b/shell_cmds/sh/tests/execution/redir2.0 @@ -0,0 +1,29 @@ +# $FreeBSD: head/bin/sh/tests/execution/redir2.0 213738 2010-10-12 18:20:38Z obrien $ +trap ': $((brokenpipe+=1))' PIPE + +P=${TMPDIR:-/tmp} +cd $P +T=$(mktemp -d sh-test.XXXXXX) +cd $T + +brokenpipe=0 +mkfifo fifo1 fifo2 +{ + { + exec ${SH} -c 'exec <fifo1; read dummy' + } 7<&- # fifo2 should be kept open, but not passed to programs + true +} 7<fifo2 & + +exec 4>fifo2 +exec 3>fifo1 +echo dummy >&4 2>/dev/null +if [ $brokenpipe -eq 1 ]; then + : ${rc:=0} +fi +echo dummy >&3 +wait + +rm fifo1 fifo2 +rmdir ${P}/${T} +exit ${rc:-3} diff --git a/shell_cmds/sh/tests/execution/redir3.0 b/shell_cmds/sh/tests/execution/redir3.0 new file mode 100644 index 0000000..1b72098 --- /dev/null +++ b/shell_cmds/sh/tests/execution/redir3.0 @@ -0,0 +1,3 @@ +# $FreeBSD: head/bin/sh/tests/execution/redir3.0 214289 2010-10-24 19:56:34Z jilles $ + +3>&- 3>&- diff --git a/shell_cmds/sh/tests/execution/redir4.0 b/shell_cmds/sh/tests/execution/redir4.0 new file mode 100644 index 0000000..01b1f87 --- /dev/null +++ b/shell_cmds/sh/tests/execution/redir4.0 @@ -0,0 +1,4 @@ +# $FreeBSD: head/bin/sh/tests/execution/redir4.0 214290 2010-10-24 20:09:49Z jilles $ + +{ echo bad 0>&3; } 2>/dev/null 3>/dev/null 3>&- +exit 0 diff --git a/shell_cmds/sh/tests/execution/redir5.0 b/shell_cmds/sh/tests/execution/redir5.0 new file mode 100644 index 0000000..346c132 --- /dev/null +++ b/shell_cmds/sh/tests/execution/redir5.0 @@ -0,0 +1,3 @@ +# $FreeBSD: head/bin/sh/tests/execution/redir5.0 218325 2011-02-05 15:02:19Z jilles $ + +{ (echo bad) >/dev/null; } </dev/null diff --git a/shell_cmds/sh/tests/execution/redir6.0 b/shell_cmds/sh/tests/execution/redir6.0 new file mode 100644 index 0000000..f663e51 --- /dev/null +++ b/shell_cmds/sh/tests/execution/redir6.0 @@ -0,0 +1,21 @@ +# $FreeBSD: head/bin/sh/tests/execution/redir6.0 220978 2011-04-23 22:28:56Z jilles $ + +failures=0 + +check() { + if [ "$2" != "$3" ]; then + echo "Failure at $1" >&2 + failures=$((failures + 1)) + fi +} + +check $LINENO "$(trap "echo bye" EXIT; : >/dev/null)" bye +check $LINENO "$(trap "echo bye" EXIT; { :; } >/dev/null)" bye +check $LINENO "$(trap "echo bye" EXIT; (:) >/dev/null)" bye +check $LINENO "$(trap "echo bye" EXIT; (: >/dev/null))" bye +check $LINENO "$(${SH} -c 'trap "echo bye" EXIT; : >/dev/null')" bye +check $LINENO "$(${SH} -c 'trap "echo bye" EXIT; { :; } >/dev/null')" bye +check $LINENO "$(${SH} -c 'trap "echo bye" EXIT; (:) >/dev/null')" bye +check $LINENO "$(${SH} -c 'trap "echo bye" EXIT; (: >/dev/null)')" bye + +exit $((failures > 0)) diff --git a/shell_cmds/sh/tests/execution/redir7.0 b/shell_cmds/sh/tests/execution/redir7.0 new file mode 100644 index 0000000..0e309a7 --- /dev/null +++ b/shell_cmds/sh/tests/execution/redir7.0 @@ -0,0 +1,21 @@ +# $FreeBSD: head/bin/sh/tests/execution/redir7.0 220978 2011-04-23 22:28:56Z jilles $ + +failures=0 + +check() { + if [ "$2" != "$3" ]; then + echo "Failure at $1" >&2 + failures=$((failures + 1)) + fi +} + +check $LINENO "$(trap "echo bye" EXIT; f() { :; }; f >/dev/null)" bye +check $LINENO "$(trap "echo bye" EXIT; f() { :; }; { f; } >/dev/null)" bye +check $LINENO "$(trap "echo bye" EXIT; f() { :; }; (f) >/dev/null)" bye +check $LINENO "$(trap "echo bye" EXIT; f() { :; }; (f >/dev/null))" bye +check $LINENO "$(${SH} -c 'trap "echo bye" EXIT; f() { :; }; f >/dev/null')" bye +check $LINENO "$(${SH} -c 'trap "echo bye" EXIT; f() { :; }; { f; } >/dev/null')" bye +check $LINENO "$(${SH} -c 'trap "echo bye" EXIT; f() { :; }; (f) >/dev/null')" bye +check $LINENO "$(${SH} -c 'trap "echo bye" EXIT; f() { :; }; (f >/dev/null)')" bye + +exit $((failures > 0)) diff --git a/shell_cmds/sh/tests/execution/set-C1.0 b/shell_cmds/sh/tests/execution/set-C1.0 new file mode 100644 index 0000000..b7d5a4c --- /dev/null +++ b/shell_cmds/sh/tests/execution/set-C1.0 @@ -0,0 +1,12 @@ +# $FreeBSD: head/bin/sh/tests/execution/set-C1.0 308229 2016-11-02 22:33:37Z jilles $ + +T=$(mktemp -d "${TMPDIR:-/tmp}/sh-test.XXXXXXXX") || exit +trap 'rm -rf "$T"' 0 + +set -C +echo . >"$T/a" && +[ -s "$T/a" ] && +{ ! true >"$T/a"; } 2>/dev/null && +[ -s "$T/a" ] && +ln -s /dev/null "$T/b" && +true >"$T/b" diff --git a/shell_cmds/sh/tests/execution/set-n1.0 b/shell_cmds/sh/tests/execution/set-n1.0 new file mode 100644 index 0000000..95eb98c --- /dev/null +++ b/shell_cmds/sh/tests/execution/set-n1.0 @@ -0,0 +1,7 @@ +# $FreeBSD: head/bin/sh/tests/execution/set-n1.0 222661 2011-06-03 21:17:42Z jilles $ + +v=$( ($SH -n <<'EOF' +for +EOF +) 2>&1 >/dev/null) +[ $? -ne 0 ] && [ -n "$v" ] diff --git a/shell_cmds/sh/tests/execution/set-n2.0 b/shell_cmds/sh/tests/execution/set-n2.0 new file mode 100644 index 0000000..d64fcca --- /dev/null +++ b/shell_cmds/sh/tests/execution/set-n2.0 @@ -0,0 +1,5 @@ +# $FreeBSD: head/bin/sh/tests/execution/set-n2.0 222661 2011-06-03 21:17:42Z jilles $ + +$SH -n <<'EOF' +echo bad +EOF diff --git a/shell_cmds/sh/tests/execution/set-n3.0 b/shell_cmds/sh/tests/execution/set-n3.0 new file mode 100644 index 0000000..5af1bc4 --- /dev/null +++ b/shell_cmds/sh/tests/execution/set-n3.0 @@ -0,0 +1,4 @@ +# $FreeBSD: head/bin/sh/tests/execution/set-n3.0 222661 2011-06-03 21:17:42Z jilles $ + +v=$( ($SH -nc 'for') 2>&1 >/dev/null) +[ $? -ne 0 ] && [ -n "$v" ] diff --git a/shell_cmds/sh/tests/execution/set-n4.0 b/shell_cmds/sh/tests/execution/set-n4.0 new file mode 100644 index 0000000..2904a34 --- /dev/null +++ b/shell_cmds/sh/tests/execution/set-n4.0 @@ -0,0 +1,3 @@ +# $FreeBSD: head/bin/sh/tests/execution/set-n4.0 222676 2011-06-04 11:28:42Z jilles $ + +$SH -nc 'echo bad' diff --git a/shell_cmds/sh/tests/execution/set-x1.0 b/shell_cmds/sh/tests/execution/set-x1.0 new file mode 100644 index 0000000..e498a3f --- /dev/null +++ b/shell_cmds/sh/tests/execution/set-x1.0 @@ -0,0 +1,8 @@ +# $FreeBSD: head/bin/sh/tests/execution/set-x1.0 222882 2011-06-08 21:58:19Z jilles $ + +key='must_contain_this' +{ r=`set -x; { : "$key"; } 2>&1 >/dev/null`; } 2>/dev/null +case $r in +*"$key"*) true ;; +*) false ;; +esac diff --git a/shell_cmds/sh/tests/execution/set-x2.0 b/shell_cmds/sh/tests/execution/set-x2.0 new file mode 100644 index 0000000..42c66a6 --- /dev/null +++ b/shell_cmds/sh/tests/execution/set-x2.0 @@ -0,0 +1,9 @@ +# $FreeBSD: head/bin/sh/tests/execution/set-x2.0 222882 2011-06-08 21:58:19Z jilles $ + +key='must contain this' +PS4="$key+ " +{ r=`set -x; { :; } 2>&1 >/dev/null`; } 2>/dev/null +case $r in +*"$key"*) true ;; +*) false ;; +esac diff --git a/shell_cmds/sh/tests/execution/set-x3.0 b/shell_cmds/sh/tests/execution/set-x3.0 new file mode 100644 index 0000000..29a917e --- /dev/null +++ b/shell_cmds/sh/tests/execution/set-x3.0 @@ -0,0 +1,9 @@ +# $FreeBSD: head/bin/sh/tests/execution/set-x3.0 222907 2011-06-09 23:12:23Z jilles $ + +key='must contain this' +PS4='$key+ ' +{ r=`set -x; { :; } 2>&1 >/dev/null`; } 2>/dev/null +case $r in +*"$key"*) true ;; +*) false ;; +esac diff --git a/shell_cmds/sh/tests/execution/set-x4.0 b/shell_cmds/sh/tests/execution/set-x4.0 new file mode 100644 index 0000000..90dd59a --- /dev/null +++ b/shell_cmds/sh/tests/execution/set-x4.0 @@ -0,0 +1,7 @@ +# $FreeBSD: head/bin/sh/tests/execution/set-x4.0 275766 2014-12-14 16:26:19Z jilles $ + +key=`printf '\r\t\001\200\300'` +r=`{ set -x; : "$key"; } 2>&1 >/dev/null` +case $r in +*[![:print:]]*) echo fail; exit 3 +esac diff --git a/shell_cmds/sh/tests/execution/shellproc1.0 b/shell_cmds/sh/tests/execution/shellproc1.0 new file mode 100644 index 0000000..5150b73 --- /dev/null +++ b/shell_cmds/sh/tests/execution/shellproc1.0 @@ -0,0 +1,11 @@ +# $FreeBSD: head/bin/sh/tests/execution/shellproc1.0 218205 2011-02-02 22:03:18Z jilles $ + +T=`mktemp -d "${TMPDIR:-/tmp}/sh-test.XXXXXXXX"` || exit +trap 'rm -rf "${T}"' 0 +cat <<EOF >"$T/testshellproc" +printf 'this ' +echo is a test +EOF +chmod 755 "$T/testshellproc" +PATH=$T:$PATH +[ "`testshellproc`" = "this is a test" ] diff --git a/shell_cmds/sh/tests/execution/subshell1.0 b/shell_cmds/sh/tests/execution/subshell1.0 new file mode 100644 index 0000000..68b0123 --- /dev/null +++ b/shell_cmds/sh/tests/execution/subshell1.0 @@ -0,0 +1,6 @@ +# $FreeBSD: head/bin/sh/tests/execution/subshell1.0 245383 2013-01-13 19:39:13Z jilles $ + +(eval "cd / +v=$(printf %0100000d 1) +echo \${#v}") +echo end diff --git a/shell_cmds/sh/tests/execution/subshell1.0.stdout b/shell_cmds/sh/tests/execution/subshell1.0.stdout new file mode 100644 index 0000000..8c71af3 --- /dev/null +++ b/shell_cmds/sh/tests/execution/subshell1.0.stdout @@ -0,0 +1,2 @@ +100000 +end diff --git a/shell_cmds/sh/tests/execution/subshell2.0 b/shell_cmds/sh/tests/execution/subshell2.0 new file mode 100644 index 0000000..2d87730 --- /dev/null +++ b/shell_cmds/sh/tests/execution/subshell2.0 @@ -0,0 +1,10 @@ +# $FreeBSD: head/bin/sh/tests/execution/subshell2.0 245383 2013-01-13 19:39:13Z jilles $ + +f() { + x=2 +} +( + x=1 + f + [ "$x" = 2 ] +) diff --git a/shell_cmds/sh/tests/execution/subshell3.0 b/shell_cmds/sh/tests/execution/subshell3.0 new file mode 100644 index 0000000..697ee0b --- /dev/null +++ b/shell_cmds/sh/tests/execution/subshell3.0 @@ -0,0 +1,4 @@ +# $FreeBSD: head/bin/sh/tests/execution/subshell3.0 245383 2013-01-13 19:39:13Z jilles $ + +(false; exit) && exit 3 +exit 0 diff --git a/shell_cmds/sh/tests/execution/subshell4.0 b/shell_cmds/sh/tests/execution/subshell4.0 new file mode 100644 index 0000000..009db3d --- /dev/null +++ b/shell_cmds/sh/tests/execution/subshell4.0 @@ -0,0 +1,3 @@ +# $FreeBSD: head/bin/sh/tests/execution/subshell4.0 245383 2013-01-13 19:39:13Z jilles $ + +(eval "set v=1"; false) && echo bad; : diff --git a/shell_cmds/sh/tests/execution/unknown1.0 b/shell_cmds/sh/tests/execution/unknown1.0 new file mode 100644 index 0000000..dd4149e --- /dev/null +++ b/shell_cmds/sh/tests/execution/unknown1.0 @@ -0,0 +1,29 @@ +# $FreeBSD: head/bin/sh/tests/execution/unknown1.0 197820 2009-10-06 22:00:14Z jilles $ + +nosuchtool 2>/dev/null +[ $? -ne 127 ] && exit 1 +/var/empty/nosuchtool 2>/dev/null +[ $? -ne 127 ] && exit 1 +(nosuchtool) 2>/dev/null +[ $? -ne 127 ] && exit 1 +(/var/empty/nosuchtool) 2>/dev/null +[ $? -ne 127 ] && exit 1 +/ 2>/dev/null +[ $? -ne 126 ] && exit 1 +PATH=/usr bin 2>/dev/null +[ $? -ne 126 ] && exit 1 + +dummy=$(nosuchtool 2>/dev/null) +[ $? -ne 127 ] && exit 1 +dummy=$(/var/empty/nosuchtool 2>/dev/null) +[ $? -ne 127 ] && exit 1 +dummy=$( (nosuchtool) 2>/dev/null) +[ $? -ne 127 ] && exit 1 +dummy=$( (/var/empty/nosuchtool) 2>/dev/null) +[ $? -ne 127 ] && exit 1 +dummy=$(/ 2>/dev/null) +[ $? -ne 126 ] && exit 1 +dummy=$(PATH=/usr bin 2>/dev/null) +[ $? -ne 126 ] && exit 1 + +exit 0 diff --git a/shell_cmds/sh/tests/execution/var-assign1.0 b/shell_cmds/sh/tests/execution/var-assign1.0 new file mode 100644 index 0000000..b75f969 --- /dev/null +++ b/shell_cmds/sh/tests/execution/var-assign1.0 @@ -0,0 +1,3 @@ +# $FreeBSD: head/bin/sh/tests/execution/var-assign1.0 212467 2010-09-11 14:15:50Z jilles $ + +[ "$(HOME=/etc HOME=/ cd && pwd)" = / ] diff --git a/shell_cmds/sh/tests/expansion/Makefile b/shell_cmds/sh/tests/expansion/Makefile new file mode 100644 index 0000000..1c44a55 --- /dev/null +++ b/shell_cmds/sh/tests/expansion/Makefile @@ -0,0 +1,105 @@ +# $FreeBSD: head/bin/sh/tests/expansion/Makefile 317514 2017-04-27 18:52:18Z jilles $ + +PACKAGE= tests + +TESTSDIR= ${TESTSBASE}/bin/sh/${.CURDIR:T} + +.PATH: ${.CURDIR:H} +ATF_TESTS_SH= functional_test + +${PACKAGE}FILES+= arith1.0 +${PACKAGE}FILES+= arith2.0 +${PACKAGE}FILES+= arith3.0 +${PACKAGE}FILES+= arith4.0 +${PACKAGE}FILES+= arith5.0 +${PACKAGE}FILES+= arith6.0 +${PACKAGE}FILES+= arith7.0 +${PACKAGE}FILES+= arith8.0 +${PACKAGE}FILES+= arith9.0 +${PACKAGE}FILES+= arith10.0 +${PACKAGE}FILES+= arith11.0 +${PACKAGE}FILES+= arith12.0 +${PACKAGE}FILES+= arith13.0 +${PACKAGE}FILES+= arith14.0 +${PACKAGE}FILES+= assign1.0 +${PACKAGE}FILES+= cmdsubst1.0 +${PACKAGE}FILES+= cmdsubst2.0 +${PACKAGE}FILES+= cmdsubst3.0 +${PACKAGE}FILES+= cmdsubst4.0 +${PACKAGE}FILES+= cmdsubst5.0 +${PACKAGE}FILES+= cmdsubst6.0 +${PACKAGE}FILES+= cmdsubst7.0 +${PACKAGE}FILES+= cmdsubst8.0 +${PACKAGE}FILES+= cmdsubst9.0 +${PACKAGE}FILES+= cmdsubst10.0 +${PACKAGE}FILES+= cmdsubst11.0 +${PACKAGE}FILES+= cmdsubst12.0 +${PACKAGE}FILES+= cmdsubst13.0 +${PACKAGE}FILES+= cmdsubst14.0 +${PACKAGE}FILES+= cmdsubst15.0 +${PACKAGE}FILES+= cmdsubst16.0 +${PACKAGE}FILES+= cmdsubst17.0 +${PACKAGE}FILES+= cmdsubst18.0 +${PACKAGE}FILES+= cmdsubst19.0 +${PACKAGE}FILES+= cmdsubst20.0 +${PACKAGE}FILES+= cmdsubst21.0 +${PACKAGE}FILES+= cmdsubst22.0 +${PACKAGE}FILES+= cmdsubst23.0 +${PACKAGE}FILES+= cmdsubst24.0 +${PACKAGE}FILES+= cmdsubst25.0 +${PACKAGE}FILES+= cmdsubst26.0 +${PACKAGE}FILES+= export1.0 +${PACKAGE}FILES+= export2.0 +${PACKAGE}FILES+= export3.0 +${PACKAGE}FILES+= heredoc1.0 +${PACKAGE}FILES+= heredoc2.0 +${PACKAGE}FILES+= ifs1.0 +${PACKAGE}FILES+= ifs2.0 +${PACKAGE}FILES+= ifs3.0 +${PACKAGE}FILES+= ifs4.0 +${PACKAGE}FILES+= ifs5.0 +${PACKAGE}FILES+= ifs6.0 +${PACKAGE}FILES+= ifs7.0 +${PACKAGE}FILES+= length1.0 +${PACKAGE}FILES+= length2.0 +${PACKAGE}FILES+= length3.0 +${PACKAGE}FILES+= length4.0 +${PACKAGE}FILES+= length5.0 +${PACKAGE}FILES+= length6.0 +${PACKAGE}FILES+= length7.0 +${PACKAGE}FILES+= length8.0 +${PACKAGE}FILES+= local1.0 +${PACKAGE}FILES+= local2.0 +${PACKAGE}FILES+= pathname1.0 +${PACKAGE}FILES+= pathname2.0 +${PACKAGE}FILES+= pathname3.0 +${PACKAGE}FILES+= pathname4.0 +${PACKAGE}FILES+= pathname5.0 +${PACKAGE}FILES+= pathname6.0 +${PACKAGE}FILES+= plus-minus1.0 +${PACKAGE}FILES+= plus-minus2.0 +${PACKAGE}FILES+= plus-minus3.0 +${PACKAGE}FILES+= plus-minus4.0 +${PACKAGE}FILES+= plus-minus5.0 +${PACKAGE}FILES+= plus-minus6.0 +${PACKAGE}FILES+= plus-minus7.0 +${PACKAGE}FILES+= plus-minus8.0 +${PACKAGE}FILES+= question1.0 +${PACKAGE}FILES+= readonly1.0 +${PACKAGE}FILES+= redir1.0 +${PACKAGE}FILES+= set-u1.0 +${PACKAGE}FILES+= set-u2.0 +${PACKAGE}FILES+= set-u3.0 +${PACKAGE}FILES+= tilde1.0 +${PACKAGE}FILES+= tilde2.0 +${PACKAGE}FILES+= trim1.0 +${PACKAGE}FILES+= trim2.0 +${PACKAGE}FILES+= trim3.0 +${PACKAGE}FILES+= trim4.0 +${PACKAGE}FILES+= trim5.0 +${PACKAGE}FILES+= trim6.0 +${PACKAGE}FILES+= trim7.0 +${PACKAGE}FILES+= trim8.0 +${PACKAGE}FILES+= trim9.0 + +.include <bsd.test.mk> diff --git a/shell_cmds/sh/tests/expansion/Makefile.depend b/shell_cmds/sh/tests/expansion/Makefile.depend new file mode 100644 index 0000000..547cbb3 --- /dev/null +++ b/shell_cmds/sh/tests/expansion/Makefile.depend @@ -0,0 +1,11 @@ +# $FreeBSD: head/bin/sh/tests/expansion/Makefile.depend 296587 2016-03-09 22:46:01Z bdrewery $ +# Autogenerated - do NOT edit! + +DIRDEPS = \ + + +.include <dirdeps.mk> + +.if ${DEP_RELDIR} == ${_DEP_RELDIR} +# local dependencies - needed for -jN in clean tree +.endif diff --git a/shell_cmds/sh/tests/expansion/arith1.0 b/shell_cmds/sh/tests/expansion/arith1.0 new file mode 100644 index 0000000..c301600 --- /dev/null +++ b/shell_cmds/sh/tests/expansion/arith1.0 @@ -0,0 +1,30 @@ +# $FreeBSD: head/bin/sh/tests/expansion/arith1.0 201259 2009-12-30 15:59:40Z jilles $ + +failures=0 + +check() { + if [ $(($1)) != $2 ]; then + failures=$((failures+1)) + echo "For $1, expected $2 actual $(($1))" + fi +} + +check "0&&0" 0 +check "1&&0" 0 +check "0&&1" 0 +check "1&&1" 1 +check "2&&2" 1 +check "1&&2" 1 +check "1<<40&&1<<40" 1 +check "1<<40&&4" 1 + +check "0||0" 0 +check "1||0" 1 +check "0||1" 1 +check "1||1" 1 +check "2||2" 1 +check "1||2" 1 +check "1<<40||1<<40" 1 +check "1<<40||4" 1 + +exit $((failures != 0)) diff --git a/shell_cmds/sh/tests/expansion/arith10.0 b/shell_cmds/sh/tests/expansion/arith10.0 new file mode 100644 index 0000000..9e3db28 --- /dev/null +++ b/shell_cmds/sh/tests/expansion/arith10.0 @@ -0,0 +1,35 @@ +# $FreeBSD: head/bin/sh/tests/expansion/arith10.0 218469 2011-02-08 23:23:55Z jilles $ + +failures=0 + +check() { + if [ $(($1)) != $2 ]; then + failures=$((failures+1)) + echo "For $1, expected $2 actual $(($1))" + fi +} + +readonly ro=4 +rw=1 +check "0 && 0 / 0" 0 +check "1 || 0 / 0" 1 +check "0 && (ro = 2)" 0 +check "ro" 4 +check "1 || (ro = -1)" 1 +check "ro" 4 +check "0 && (rw += 1)" 0 +check "rw" 1 +check "1 || (rw += 1)" 1 +check "rw" 1 +check "0 ? 44 / 0 : 51" 51 +check "0 ? ro = 3 : 52" 52 +check "ro" 4 +check "0 ? rw += 1 : 52" 52 +check "rw" 1 +check "1 ? 68 : 30 / 0" 68 +check "2 ? 1 : (ro += 2)" 1 +check "ro" 4 +check "4 ? 1 : (rw += 1)" 1 +check "rw" 1 + +exit $((failures != 0)) diff --git a/shell_cmds/sh/tests/expansion/arith11.0 b/shell_cmds/sh/tests/expansion/arith11.0 new file mode 100644 index 0000000..4a33831 --- /dev/null +++ b/shell_cmds/sh/tests/expansion/arith11.0 @@ -0,0 +1,12 @@ +# $FreeBSD: head/bin/sh/tests/expansion/arith11.0 218626 2011-02-12 23:44:05Z jilles $ +# Try to divide the smallest integer by -1. +# On amd64 this causes SIGFPE, so make sure the shell checks. + +# Calculate the minimum possible value, assuming two's complement and +# a certain interpretation of overflow when shifting left. +minint=1 +while [ $((minint <<= 1)) -gt 0 ]; do + : +done +v=$( eval ': $((minint / -1))' 2>&1 >/dev/null) +[ $? -ne 0 ] && [ -n "$v" ] diff --git a/shell_cmds/sh/tests/expansion/arith12.0 b/shell_cmds/sh/tests/expansion/arith12.0 new file mode 100644 index 0000000..7d79d7e --- /dev/null +++ b/shell_cmds/sh/tests/expansion/arith12.0 @@ -0,0 +1,4 @@ +# $FreeBSD: head/bin/sh/tests/expansion/arith12.0 232839 2012-03-11 22:12:05Z jilles $ + +_x=4 y_=5 z_z=6 +[ "$((_x*100+y_*10+z_z))" = 456 ] diff --git a/shell_cmds/sh/tests/expansion/arith13.0 b/shell_cmds/sh/tests/expansion/arith13.0 new file mode 100644 index 0000000..5421cb0 --- /dev/null +++ b/shell_cmds/sh/tests/expansion/arith13.0 @@ -0,0 +1,6 @@ +# $FreeBSD: head/bin/sh/tests/expansion/arith13.0 254806 2013-08-24 20:06:00Z jilles $ +# Pre-increment and pre-decrement in arithmetic expansion are not in POSIX. +# Require either an error or a correct implementation. + +! (eval 'x=4; [ $((++x)) != 5 ] || [ $x != 5 ]') 2>/dev/null && +! (eval 'x=2; [ $((--x)) != 1 ] || [ $x != 1 ]') 2>/dev/null diff --git a/shell_cmds/sh/tests/expansion/arith14.0 b/shell_cmds/sh/tests/expansion/arith14.0 new file mode 100644 index 0000000..a51e916 --- /dev/null +++ b/shell_cmds/sh/tests/expansion/arith14.0 @@ -0,0 +1,40 @@ +# $FreeBSD: head/bin/sh/tests/expansion/arith14.0 270029 2014-08-15 22:36:41Z jilles $ +# Check that <</>> use the low bits of the shift count. + +if [ $((1<<16<<16)) = 0 ]; then + width=32 +elif [ $((1<<32<<32)) = 0 ]; then + width=64 +elif [ $((1<<64<<64)) = 0 ]; then + width=128 +elif [ $((1<<64>>64)) = 1 ]; then + # Integers are wider than 128 bits; assume arbitrary precision. + # Nothing to test here. + exit 0 +else + echo "Cannot determine integer width" + exit 2 +fi + +twowidth=$((width * 2)) +j=43 k=$((1 << (width - 2))) r=0 + +i=0 +while [ $i -lt $twowidth ]; do + if [ "$((j << i))" != "$((j << (i + width)))" ]; then + echo "Problem with $j << $i" + r=2 + fi + i=$((i + 1)) +done + +i=0 +while [ $i -lt $twowidth ]; do + if [ "$((k >> i))" != "$((k >> (i + width)))" ]; then + echo "Problem with $k >> $i" + r=2 + fi + i=$((i + 1)) +done + +exit $r diff --git a/shell_cmds/sh/tests/expansion/arith2.0 b/shell_cmds/sh/tests/expansion/arith2.0 new file mode 100644 index 0000000..b5874e5 --- /dev/null +++ b/shell_cmds/sh/tests/expansion/arith2.0 @@ -0,0 +1,77 @@ +# $FreeBSD: head/bin/sh/tests/expansion/arith2.0 209652 2010-07-02 21:31:24Z jilles $ + +failures=0 + +check() { + if [ $(($1)) != $2 ]; then + failures=$((failures+1)) + echo "For $1, expected $2 actual $(($1))" + fi +} + +# variables +unset v +check "v=2" 2 +check "v" 2 +check "$(($v))" 2 +check "v+=1" 3 +check "v" 3 + +# constants +check "4611686018427387904" 4611686018427387904 +check "0x4000000000000000" 4611686018427387904 +check "0400000000000000000000" 4611686018427387904 +check "0x4Ab0000000000000" 5381801554707742720 +check "010" 8 + +# try out all operators +v=42 +check "!v" 0 +check "!!v" 1 +check "!0" 1 +check "~0" -1 +check "~(-1)" 0 +check "-0" 0 +check "-v" -42 +check "v*v" 1764 +check "v/2" 21 +check "v%10" 2 +check "v+v" 84 +check "v-4" 38 +check "v<<1" 84 +check "v>>1" 21 +check "v<43" 1 +check "v>42" 0 +check "v<=43" 1 +check "v>=43" 0 +check "v==41" 0 +check "v!=42" 0 +check "v&3" 2 +check "v^3" 41 +check "v|3" 43 +check "v>=40&&v<=44" 1 +check "v<40||v>44" 0 +check "(v=42)&&(v+=1)==43" 1 +check "v" 43 +check "(v=42)&&(v-=1)==41" 1 +check "v" 41 +check "(v=42)&&(v*=2)==84" 1 +check "v" 84 +check "(v=42)&&(v/=10)==4" 1 +check "v" 4 +check "(v=42)&&(v%=10)==2" 1 +check "v" 2 +check "(v=42)&&(v<<=1)==84" 1 +check "v" 84 +check "(v=42)&&(v>>=2)==10" 1 +check "v" 10 +check "(v=42)&&(v&=32)==32" 1 +check "v" 32 +check "(v=42)&&(v^=32)==10" 1 +check "v" 10 +check "(v=42)&&(v|=32)==42" 1 +check "v" 42 + +# missing: ternary + +exit $((failures != 0)) diff --git a/shell_cmds/sh/tests/expansion/arith3.0 b/shell_cmds/sh/tests/expansion/arith3.0 new file mode 100644 index 0000000..6b60b51 --- /dev/null +++ b/shell_cmds/sh/tests/expansion/arith3.0 @@ -0,0 +1,14 @@ +# $FreeBSD: head/bin/sh/tests/expansion/arith3.0 204017 2010-02-17 22:25:22Z jilles $ + +failures=0 + +check() { + if [ $(($1)) != $2 ]; then + failures=$((failures+1)) + echo "For $1, expected $2 actual $(($1))" + fi +} + +check "1 << 1 + 1 | 1" 5 + +exit $((failures != 0)) diff --git a/shell_cmds/sh/tests/expansion/arith4.0 b/shell_cmds/sh/tests/expansion/arith4.0 new file mode 100644 index 0000000..a41a0f7 --- /dev/null +++ b/shell_cmds/sh/tests/expansion/arith4.0 @@ -0,0 +1,20 @@ +# $FreeBSD: head/bin/sh/tests/expansion/arith4.0 206167 2010-04-04 16:29:48Z jilles $ + +failures=0 + +check() { + if [ $(($1)) != $2 ]; then + failures=$((failures+1)) + echo "For $1, expected $2 actual $(($1))" + fi +} + +check '20 / 2 / 2' 5 +check '20 - 2 - 2' 16 +unset a b c d +check "a = b = c = d = 1" 1 +check "a == 1 && b == 1 && c == 1 && d == 1" 1 +check "a += b += c += d" 4 +check "a == 4 && b == 3 && c == 2 && d == 1" 1 + +exit $((failures != 0)) diff --git a/shell_cmds/sh/tests/expansion/arith5.0 b/shell_cmds/sh/tests/expansion/arith5.0 new file mode 100644 index 0000000..1cd021f --- /dev/null +++ b/shell_cmds/sh/tests/expansion/arith5.0 @@ -0,0 +1,17 @@ +# $FreeBSD: head/bin/sh/tests/expansion/arith5.0 206168 2010-04-04 16:48:33Z jilles $ + +failures=0 + +check() { + if [ "$2" != "$3" ]; then + failures=$((failures+1)) + echo "For $1, expected $3 actual $2" + fi +} + +unset a +check '$((1+${a:-$((7+2))}))' "$((1+${a:-$((7+2))}))" 10 +check '$((1+${a:=$((2+2))}))' "$((1+${a:=$((2+2))}))" 5 +check '$a' "$a" 4 + +exit $((failures != 0)) diff --git a/shell_cmds/sh/tests/expansion/arith6.0 b/shell_cmds/sh/tests/expansion/arith6.0 new file mode 100644 index 0000000..528f4e8 --- /dev/null +++ b/shell_cmds/sh/tests/expansion/arith6.0 @@ -0,0 +1,16 @@ +# $FreeBSD: head/bin/sh/tests/expansion/arith6.0 215550 2010-11-19 22:25:32Z jilles $ + +v1=1\ +\ 1 +v2=D +v3=C123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 +f() { v4="$*"; } + +while [ ${#v2} -lt 1250 ]; do + eval $v2=$((3+${#v2})) $v3=$((4-${#v2})) + eval f $(($v2+ $v1 +$v3)) + if [ $v4 -ne 9 ]; then + echo bad: $v4 -ne 9 at ${#v2} + fi + v2=x$v2 + v3=y$v3 +done diff --git a/shell_cmds/sh/tests/expansion/arith7.0 b/shell_cmds/sh/tests/expansion/arith7.0 new file mode 100644 index 0000000..f31dba1 --- /dev/null +++ b/shell_cmds/sh/tests/expansion/arith7.0 @@ -0,0 +1,12 @@ +# $FreeBSD: head/bin/sh/tests/expansion/arith7.0 216395 2010-12-12 16:56:16Z jilles $ + +v=1+ +v=$v$v$v$v +v=$v$v$v$v +v=$v$v$v$v +v=$v$v$v$v +v=$v$v$v$v +[ "$(cat <<EOF +$(($v 1)) +EOF +)" = 1025 ] diff --git a/shell_cmds/sh/tests/expansion/arith8.0 b/shell_cmds/sh/tests/expansion/arith8.0 new file mode 100644 index 0000000..e0739f8 --- /dev/null +++ b/shell_cmds/sh/tests/expansion/arith8.0 @@ -0,0 +1,4 @@ +# $FreeBSD: head/bin/sh/tests/expansion/arith8.0 216547 2010-12-18 23:03:51Z jilles $ + +v=$( (eval ': $((08))') 2>&1 >/dev/null) +[ $? -ne 0 ] && [ -n "$v" ] diff --git a/shell_cmds/sh/tests/expansion/arith9.0 b/shell_cmds/sh/tests/expansion/arith9.0 new file mode 100644 index 0000000..e47d003 --- /dev/null +++ b/shell_cmds/sh/tests/expansion/arith9.0 @@ -0,0 +1,20 @@ +# $FreeBSD: head/bin/sh/tests/expansion/arith9.0 218469 2011-02-08 23:23:55Z jilles $ + +failures=0 + +check() { + if [ $(($1)) != $2 ]; then + failures=$((failures+1)) + echo "For $1, expected $2 actual $(($1))" + fi +} + +check "0 ? 44 : 51" 51 +check "1 ? 68 : 30" 68 +check "2 ? 1 : -5" 1 +check "0 ? 4 : 0 ? 5 : 6" 6 +check "0 ? 4 : 1 ? 5 : 6" 5 +check "1 ? 4 : 0 ? 5 : 6" 4 +check "1 ? 4 : 1 ? 5 : 6" 4 + +exit $((failures != 0)) diff --git a/shell_cmds/sh/tests/expansion/assign1.0 b/shell_cmds/sh/tests/expansion/assign1.0 new file mode 100644 index 0000000..ee4c0d8 --- /dev/null +++ b/shell_cmds/sh/tests/expansion/assign1.0 @@ -0,0 +1,37 @@ +# $FreeBSD: head/bin/sh/tests/expansion/assign1.0 204842 2010-03-07 18:43:29Z jilles $ + +e= q='?' a='*' t=texttext s='ast*que?non' p='/et[c]/' w='a b c' b='{{(#)}}' +h='##' +failures='' +ok='' + +testcase() { + code="$1" + expected="$2" + oIFS="$IFS" + eval "$code" + IFS='|' + result="$#|$*" + IFS="$oIFS" + if [ "x$result" = "x$expected" ]; then + ok=x$ok + else + failures=x$failures + echo "For $code, expected $expected actual $result" + fi +} + +testcase 'v=; set -- ${v=a b} $v' '0|' +testcase 'unset v; set -- ${v=a b} $v' '4|a|b|a|b' +testcase 'v=; set -- ${v:=a b} $v' '4|a|b|a|b' +testcase 'v=; set -- "${v:=a b}" "$v"' '2|a b|a b' +# expect sensible behaviour, although it disagrees with POSIX +testcase 'v=; set -- ${v:=a\ b} $v' '4|a|b|a|b' +testcase 'v=; set -- ${v:=$p} $v' '2|/etc/|/etc/' +testcase 'v=; set -- "${v:=$p}" "$v"' '2|/et[c]/|/et[c]/' +testcase 'v=; set -- "${v:=a\ b}" "$v"' '2|a\ b|a\ b' +testcase 'v=; set -- ${v:="$p"} $v' '2|/etc/|/etc/' +# whether $p is quoted or not shouldn't really matter +testcase 'v=; set -- "${v:="$p"}" "$v"' '2|/et[c]/|/et[c]/' + +test "x$failures" = x diff --git a/shell_cmds/sh/tests/expansion/cmdsubst1.0 b/shell_cmds/sh/tests/expansion/cmdsubst1.0 new file mode 100644 index 0000000..ea89f42 --- /dev/null +++ b/shell_cmds/sh/tests/expansion/cmdsubst1.0 @@ -0,0 +1,48 @@ +# $FreeBSD: head/bin/sh/tests/expansion/cmdsubst1.0 201366 2010-01-01 18:17:46Z jilles $ + +failures=0 + +check() { + if ! eval "[ $* ]"; then + echo "Failed: $*" + : $((failures += 1)) + fi +} + +check '"$(echo abcde)" = "abcde"' +check '"$(echo abcde; :)" = "abcde"' + +check '"$(printf abcde)" = "abcde"' +check '"$(printf abcde; :)" = "abcde"' + +# regular +check '-n "$(umask)"' +check '-n "$(umask; :)"' +check '-n "$(umask 2>&1)"' +check '-n "$(umask 2>&1; :)"' + +# special +check '-n "$(times)"' +check '-n "$(times; :)"' +check '-n "$(times 2>&1)"' +check '-n "$(times 2>&1; :)"' + +# regular +check '".$(umask -@ 2>&1)." = ".umask: Illegal option -@."' +check '".$(umask -@ 2>&1; :)." = ".umask: Illegal option -@."' +check '".$({ umask -@; } 2>&1)." = ".umask: Illegal option -@."' + +# special +check '".$(shift xyz 2>&1)." = ".shift: Illegal number: xyz."' +check '".$(shift xyz 2>&1; :)." = ".shift: Illegal number: xyz."' +check '".$({ shift xyz; } 2>&1)." = ".shift: Illegal number: xyz."' + +v=1 +check '-z "$(v=2 :)"' +check '"$v" = 1' +check '-z "$(v=3)"' +check '"$v" = 1' +check '"$(v=4 eval echo \$v)" = 4' +check '"$v" = 1' + +exit $((failures > 0)) diff --git a/shell_cmds/sh/tests/expansion/cmdsubst10.0 b/shell_cmds/sh/tests/expansion/cmdsubst10.0 new file mode 100644 index 0000000..dc13d39 --- /dev/null +++ b/shell_cmds/sh/tests/expansion/cmdsubst10.0 @@ -0,0 +1,51 @@ +# $FreeBSD: head/bin/sh/tests/expansion/cmdsubst10.0 216826 2010-12-30 22:33:55Z jilles $ + +a1=$(alias) +: $(alias testalias=abcd) +a2=$(alias) +[ "$a1" = "$a2" ] || echo Error at line $LINENO + +alias testalias2=abcd +a1=$(alias) +: $(unalias testalias2) +a2=$(alias) +[ "$a1" = "$a2" ] || echo Error at line $LINENO + +[ "$(command -V pwd)" = "$(command -V pwd; exit $?)" ] || echo Error at line $LINENO + +v=1 +: $(export v=2) +[ "$v" = 1 ] || echo Error at line $LINENO + +rotest=1 +: $(readonly rotest=2) +[ "$rotest" = 1 ] || echo Error at line $LINENO + +set +u +: $(set -u) +case $- in +*u*) echo Error at line $LINENO ;; +esac +set +u + +set +u +: $(set -o nounset) +case $- in +*u*) echo Error at line $LINENO ;; +esac +set +u + +set +u +: $(command set -u) +case $- in +*u*) echo Error at line $LINENO ;; +esac +set +u + +umask 77 +u1=$(umask) +: $(umask 022) +u2=$(umask) +[ "$u1" = "$u2" ] || echo Error at line $LINENO + +dummy=$(exit 3); [ $? -eq 3 ] || echo Error at line $LINENO diff --git a/shell_cmds/sh/tests/expansion/cmdsubst11.0 b/shell_cmds/sh/tests/expansion/cmdsubst11.0 new file mode 100644 index 0000000..cb035ae --- /dev/null +++ b/shell_cmds/sh/tests/expansion/cmdsubst11.0 @@ -0,0 +1,5 @@ +# $FreeBSD: head/bin/sh/tests/expansion/cmdsubst11.0 223163 2011-06-16 21:50:28Z jilles $ + +# Not required by POSIX but useful for efficiency. + +[ $$ = $(eval '${SH} -c echo\ \$PPID') ] diff --git a/shell_cmds/sh/tests/expansion/cmdsubst12.0 b/shell_cmds/sh/tests/expansion/cmdsubst12.0 new file mode 100644 index 0000000..4d330ef --- /dev/null +++ b/shell_cmds/sh/tests/expansion/cmdsubst12.0 @@ -0,0 +1,6 @@ +# $FreeBSD: head/bin/sh/tests/expansion/cmdsubst12.0 230121 2012-01-14 23:10:18Z jilles $ + +f() { + echo x$(printf foo >&2)y +} +[ "$(f 2>&1)" = "fooxy" ] diff --git a/shell_cmds/sh/tests/expansion/cmdsubst13.0 b/shell_cmds/sh/tests/expansion/cmdsubst13.0 new file mode 100644 index 0000000..d3fc399 --- /dev/null +++ b/shell_cmds/sh/tests/expansion/cmdsubst13.0 @@ -0,0 +1,12 @@ +# $FreeBSD: head/bin/sh/tests/expansion/cmdsubst13.0 230121 2012-01-14 23:10:18Z jilles $ + +x=1 y=2 +[ "$( + case $((x+=1)) in + ($((y+=1))) echo bad1 ;; + ($((y-1))) echo $x.$y ;; + ($((y=2))) echo bad2 ;; + (*) echo bad3 ;; + esac +)" = "2.3" ] || echo "Error at $LINENO" +[ "$x.$y" = "1.2" ] || echo "Error at $LINENO" diff --git a/shell_cmds/sh/tests/expansion/cmdsubst14.0 b/shell_cmds/sh/tests/expansion/cmdsubst14.0 new file mode 100644 index 0000000..8a916af --- /dev/null +++ b/shell_cmds/sh/tests/expansion/cmdsubst14.0 @@ -0,0 +1,5 @@ +# $FreeBSD: head/bin/sh/tests/expansion/cmdsubst14.0 245381 2013-01-13 19:19:40Z jilles $ + +! v=`false + +` diff --git a/shell_cmds/sh/tests/expansion/cmdsubst15.0 b/shell_cmds/sh/tests/expansion/cmdsubst15.0 new file mode 100644 index 0000000..9599ef8 --- /dev/null +++ b/shell_cmds/sh/tests/expansion/cmdsubst15.0 @@ -0,0 +1,5 @@ +# $FreeBSD: head/bin/sh/tests/expansion/cmdsubst15.0 245381 2013-01-13 19:19:40Z jilles $ + +! v=`false; + +` diff --git a/shell_cmds/sh/tests/expansion/cmdsubst16.0 b/shell_cmds/sh/tests/expansion/cmdsubst16.0 new file mode 100644 index 0000000..2483601 --- /dev/null +++ b/shell_cmds/sh/tests/expansion/cmdsubst16.0 @@ -0,0 +1,5 @@ +# $FreeBSD: head/bin/sh/tests/expansion/cmdsubst16.0 245392 2013-01-13 22:35:51Z jilles $ + +f() { return 3; } +f +[ `echo $?` = 3 ] diff --git a/shell_cmds/sh/tests/expansion/cmdsubst17.0 b/shell_cmds/sh/tests/expansion/cmdsubst17.0 new file mode 100644 index 0000000..d3a2b54 --- /dev/null +++ b/shell_cmds/sh/tests/expansion/cmdsubst17.0 @@ -0,0 +1,5 @@ +# $FreeBSD: head/bin/sh/tests/expansion/cmdsubst17.0 245422 2013-01-14 12:20:55Z jilles $ + +f() { return 3; } +f +[ `echo $?; :` = 3 ] diff --git a/shell_cmds/sh/tests/expansion/cmdsubst18.0 b/shell_cmds/sh/tests/expansion/cmdsubst18.0 new file mode 100644 index 0000000..2c3a1be --- /dev/null +++ b/shell_cmds/sh/tests/expansion/cmdsubst18.0 @@ -0,0 +1,6 @@ +# $FreeBSD: head/bin/sh/tests/expansion/cmdsubst18.0 314637 2017-03-03 22:46:20Z jilles $ + +x=X +unset n +r=${x+$(echo a)}${x-$(echo b)}${n+$(echo c)}${n-$(echo d)}$(echo e) +[ "$r" = aXde ] diff --git a/shell_cmds/sh/tests/expansion/cmdsubst19.0 b/shell_cmds/sh/tests/expansion/cmdsubst19.0 new file mode 100644 index 0000000..7813c88 --- /dev/null +++ b/shell_cmds/sh/tests/expansion/cmdsubst19.0 @@ -0,0 +1,5 @@ +# $FreeBSD: head/bin/sh/tests/expansion/cmdsubst19.0 314637 2017-03-03 22:46:20Z jilles $ + +b=200 c=30 d=5 x=4 +r=$(echo a)$(($(echo b) + ${x+$(echo c)} + ${x-$(echo d)}))$(echo e) +[ "$r" = a234e ] diff --git a/shell_cmds/sh/tests/expansion/cmdsubst2.0 b/shell_cmds/sh/tests/expansion/cmdsubst2.0 new file mode 100644 index 0000000..35453be --- /dev/null +++ b/shell_cmds/sh/tests/expansion/cmdsubst2.0 @@ -0,0 +1,43 @@ +# $FreeBSD: head/bin/sh/tests/expansion/cmdsubst2.0 205105 2010-03-12 23:23:46Z jilles $ + +failures=0 + +check() { + if ! eval "[ $* ]"; then + echo "Failed: $*" + : $((failures += 1)) + fi +} + +check '`echo /et[c]/` = "/etc/"' +check '`printf /var/empty%s /et[c]/` = "/var/empty/etc/"' +check '"`echo /et[c]/`" = "/etc/"' +check '`echo "/et[c]/"` = "/etc/"' +check '`printf /var/empty%s "/et[c]/"` = "/var/empty/et[c]/"' +check '`printf /var/empty/%s \"/et[c]/\"` = "/var/empty/\"/et[c]/\""' +check '"`echo \"/et[c]/\"`" = "/et[c]/"' +check '"`echo "/et[c]/"`" = "/et[c]/"' +check '`echo $$` = $$' +check '"`echo $$`" = $$' +check '`echo \$\$` = $$' +check '"`echo \$\$`" = $$' + +# Command substitutions consisting of a single builtin may be treated +# differently. +check '`:; echo /et[c]/` = "/etc/"' +check '`:; printf /var/empty%s /et[c]/` = "/var/empty/etc/"' +check '"`:; echo /et[c]/`" = "/etc/"' +check '`:; echo "/et[c]/"` = "/etc/"' +check '`:; printf /var/empty%s "/et[c]/"` = "/var/empty/et[c]/"' +check '`:; printf /var/empty/%s \"/et[c]/\"` = "/var/empty/\"/et[c]/\""' +check '"`:; echo \"/et[c]/\"`" = "/et[c]/"' +check '"`:; echo "/et[c]/"`" = "/et[c]/"' +check '`:; echo $$` = $$' +check '"`:; echo $$`" = $$' +check '`:; echo \$\$` = $$' +check '"`:; echo \$\$`" = $$' + +check '`set -f; echo /et[c]/` = "/etc/"' +check '"`set -f; echo /et[c]/`" = "/et[c]/"' + +exit $((failures > 0)) diff --git a/shell_cmds/sh/tests/expansion/cmdsubst20.0 b/shell_cmds/sh/tests/expansion/cmdsubst20.0 new file mode 100644 index 0000000..a92eaac --- /dev/null +++ b/shell_cmds/sh/tests/expansion/cmdsubst20.0 @@ -0,0 +1,6 @@ +# $FreeBSD: head/bin/sh/tests/expansion/cmdsubst20.0 314637 2017-03-03 22:46:20Z jilles $ + +set -T +trapped='' +trap "trapped=x$trapped" USR1 +[ "x$(kill -USR1 $$)y" = xy ] && [ "$trapped" = x ] diff --git a/shell_cmds/sh/tests/expansion/cmdsubst21.0 b/shell_cmds/sh/tests/expansion/cmdsubst21.0 new file mode 100644 index 0000000..02f938f --- /dev/null +++ b/shell_cmds/sh/tests/expansion/cmdsubst21.0 @@ -0,0 +1,6 @@ +# $FreeBSD: head/bin/sh/tests/expansion/cmdsubst21.0 314686 2017-03-04 22:58:34Z jilles $ + +set -T +trapped='' +trap "trapped=x$trapped" TERM +[ "x$($SH -c "kill $$")y" = xy ] && [ "$trapped" = x ] diff --git a/shell_cmds/sh/tests/expansion/cmdsubst22.0 b/shell_cmds/sh/tests/expansion/cmdsubst22.0 new file mode 100644 index 0000000..b33f6d5 --- /dev/null +++ b/shell_cmds/sh/tests/expansion/cmdsubst22.0 @@ -0,0 +1,6 @@ +# $FreeBSD: head/bin/sh/tests/expansion/cmdsubst22.0 314686 2017-03-04 22:58:34Z jilles $ + +set -T +trapped='' +trap "trapped=x$trapped" TERM +[ "x$(:; kill $$)y" = xy ] && [ "$trapped" = x ] diff --git a/shell_cmds/sh/tests/expansion/cmdsubst23.0 b/shell_cmds/sh/tests/expansion/cmdsubst23.0 new file mode 100644 index 0000000..54b087e --- /dev/null +++ b/shell_cmds/sh/tests/expansion/cmdsubst23.0 @@ -0,0 +1,5 @@ +# $FreeBSD: head/bin/sh/tests/expansion/cmdsubst23.0 315005 2017-03-10 16:04:00Z jilles $ + +unset n +x=abcd +[ "X${n#$(echo a)}X${x#$(echo ab)}X$(echo abc)X" = XXcdXabcX ] diff --git a/shell_cmds/sh/tests/expansion/cmdsubst24.0 b/shell_cmds/sh/tests/expansion/cmdsubst24.0 new file mode 100644 index 0000000..60da04a --- /dev/null +++ b/shell_cmds/sh/tests/expansion/cmdsubst24.0 @@ -0,0 +1,24 @@ +# $FreeBSD: head/bin/sh/tests/expansion/cmdsubst24.0 317347 2017-04-23 21:58:17Z jilles $ +# POSIX leaves the effect of NUL bytes in command substitution output +# unspecified but we have always discarded them. + +failures=0 + +check() { + if [ "$2" != "$3" ]; then + printf "Failed at line %s: got \"%s\" expected \"%s\"\n" "$1" "$2" "$3" + : $((failures += 1)) + fi +} + +fmt='\0a\0 \0b\0c d\0' +assign_builtin=$(printf "$fmt") +check "$LINENO" "$assign_builtin" "a bc d" +assign_pipeline=$(printf "$fmt" | cat) +check "$LINENO" "$assign_pipeline" "a bc d" +set -- $(printf "$fmt") $(printf "$fmt" | cat) "$(printf "$fmt")" "$(printf "$fmt" | cat)" +IFS=@ +splits="$*" +check "$LINENO" "$splits" "a@bc@d@a@bc@d@a bc d@a bc d" + +[ "$failures" = 0 ] diff --git a/shell_cmds/sh/tests/expansion/cmdsubst25.0 b/shell_cmds/sh/tests/expansion/cmdsubst25.0 new file mode 100644 index 0000000..0654847 --- /dev/null +++ b/shell_cmds/sh/tests/expansion/cmdsubst25.0 @@ -0,0 +1,7 @@ +# $FreeBSD: head/bin/sh/tests/expansion/cmdsubst25.0 317514 2017-04-27 18:52:18Z jilles $ + +IFS=' ' +set -- `printf '\n '` +IFS=: +[ "$*" = ' +' ] diff --git a/shell_cmds/sh/tests/expansion/cmdsubst26.0 b/shell_cmds/sh/tests/expansion/cmdsubst26.0 new file mode 100644 index 0000000..c0d600a --- /dev/null +++ b/shell_cmds/sh/tests/expansion/cmdsubst26.0 @@ -0,0 +1,6 @@ +# $FreeBSD: head/bin/sh/tests/expansion/cmdsubst26.0 317514 2017-04-27 18:52:18Z jilles $ + +nl=' +' +v=$nl`printf '\n'` +[ "$v" = "$nl" ] diff --git a/shell_cmds/sh/tests/expansion/cmdsubst3.0 b/shell_cmds/sh/tests/expansion/cmdsubst3.0 new file mode 100644 index 0000000..221b11b --- /dev/null +++ b/shell_cmds/sh/tests/expansion/cmdsubst3.0 @@ -0,0 +1,23 @@ +# $FreeBSD: head/bin/sh/tests/expansion/cmdsubst3.0 218819 2011-02-18 20:37:09Z jilles $ + +unset LC_ALL +export LC_CTYPE=en_US.ISO8859-1 + +e= +for i in 0 1 2 3; do + for j in 0 1 2 3 4 5 6 7; do + for k in 0 1 2 3 4 5 6 7; do + case $i$j$k in + 000) continue ;; + esac + e="$e\n\\$i$j$k" + done + done +done +e1=$(printf "$e") +e2="$(printf "$e")" +[ "${#e1}" = 510 ] || echo length bad +[ "$e1" = "$e2" ] || echo e1 != e2 +[ "$e1" = "$(printf "$e")" ] || echo quoted bad +IFS= +[ "$e1" = $(printf "$e") ] || echo unquoted bad diff --git a/shell_cmds/sh/tests/expansion/cmdsubst4.0 b/shell_cmds/sh/tests/expansion/cmdsubst4.0 new file mode 100644 index 0000000..4d5a134 --- /dev/null +++ b/shell_cmds/sh/tests/expansion/cmdsubst4.0 @@ -0,0 +1,4 @@ +# $FreeBSD: head/bin/sh/tests/expansion/cmdsubst4.0 216747 2010-12-27 23:56:03Z jilles $ + +exec 2>/dev/null +! y=$(: </var/empty/nonexistent) diff --git a/shell_cmds/sh/tests/expansion/cmdsubst5.0 b/shell_cmds/sh/tests/expansion/cmdsubst5.0 new file mode 100644 index 0000000..70b4298 --- /dev/null +++ b/shell_cmds/sh/tests/expansion/cmdsubst5.0 @@ -0,0 +1,5 @@ +# $FreeBSD: head/bin/sh/tests/expansion/cmdsubst5.0 216761 2010-12-28 13:28:24Z jilles $ + +unset v +exec 2>/dev/null +! y=$(: ${v?}) diff --git a/shell_cmds/sh/tests/expansion/cmdsubst6.0 b/shell_cmds/sh/tests/expansion/cmdsubst6.0 new file mode 100644 index 0000000..0e47bce --- /dev/null +++ b/shell_cmds/sh/tests/expansion/cmdsubst6.0 @@ -0,0 +1,53 @@ +# $FreeBSD: head/bin/sh/tests/expansion/cmdsubst6.0 216763 2010-12-28 14:58:08Z jilles $ +# This tests if the cmdsubst optimization is still used if possible. + +failures='' +ok='' + +testcase() { + code="$1" + + unset v + eval "pid=\$(dummy=$code echo \$(\$SH -c echo\ \\\$PPID))" + + if [ "$pid" = "$$" ]; then + ok=x$ok + else + failures=x$failures + echo "Failure for $code" + fi +} + +unset v +w=1 +testcase '$w' +testcase '1${w+1}' +testcase '1${w-1}' +testcase '1${v+1}' +testcase '1${v-1}' +testcase '1${w:+1}' +testcase '1${w:-1}' +testcase '1${v:+1}' +testcase '1${v:-1}' +testcase '${w?}' +testcase '${w:?}' +testcase '${w#x}' +testcase '${w##x}' +testcase '${w%x}' +testcase '${w%%x}' + +testcase '$((w))' +testcase '$(((w+4)*2/3))' +testcase '$((w==1))' +testcase '$((w>=0 && w<=5 && w!=2))' +testcase '$((${#w}))' +testcase '$((${#IFS}))' +testcase '$((${#w}>=1))' +testcase '$(($$))' +testcase '$(($#))' +testcase '$(($?))' + +testcase '$(: $((w=4)))' +testcase '$(: ${v=2})' + +test "x$failures" = x diff --git a/shell_cmds/sh/tests/expansion/cmdsubst7.0 b/shell_cmds/sh/tests/expansion/cmdsubst7.0 new file mode 100644 index 0000000..6b4cd1d --- /dev/null +++ b/shell_cmds/sh/tests/expansion/cmdsubst7.0 @@ -0,0 +1,31 @@ +# $FreeBSD: head/bin/sh/tests/expansion/cmdsubst7.0 216778 2010-12-28 21:27:08Z jilles $ + +failures='' +ok='' + +testcase() { + code="$1" + + unset v + eval ": \$($code)" + + if [ "${v:+bad}" = "" ]; then + ok=x$ok + else + failures=x$failures + echo "Failure for $code" + fi +} + +testcase ': ${v=0}' +testcase ': ${v:=0}' +testcase ': $((v=1))' +testcase ': $((v+=1))' +w='v=1' +testcase ': $(($w))' +testcase ': $((${$+v=1}))' +testcase ': $((v${$+=1}))' +testcase ': $((v $(echo =) 1))' +testcase ': $(($(echo $w)))' + +test "x$failures" = x diff --git a/shell_cmds/sh/tests/expansion/cmdsubst8.0 b/shell_cmds/sh/tests/expansion/cmdsubst8.0 new file mode 100644 index 0000000..19ff803 --- /dev/null +++ b/shell_cmds/sh/tests/expansion/cmdsubst8.0 @@ -0,0 +1,17 @@ +# $FreeBSD: head/bin/sh/tests/expansion/cmdsubst8.0 216819 2010-12-30 15:04:59Z jilles $ +# Not required by POSIX (although referenced in a non-normative section), +# but possibly useful. + +: hi there & +p=$! +q=$(jobs -l $p) + +# Change tabs to spaces. +set -f +set -- $q +r="$*" + +case $r in +*" $p "*) ;; +*) echo Pid missing; exit 3 ;; +esac diff --git a/shell_cmds/sh/tests/expansion/cmdsubst9.0 b/shell_cmds/sh/tests/expansion/cmdsubst9.0 new file mode 100644 index 0000000..8bbf2b9 --- /dev/null +++ b/shell_cmds/sh/tests/expansion/cmdsubst9.0 @@ -0,0 +1,11 @@ +# $FreeBSD: head/bin/sh/tests/expansion/cmdsubst9.0 216819 2010-12-30 15:04:59Z jilles $ + +set -e + +cd / +dummy=$(cd /bin) +[ "$(pwd)" = / ] + +v=1 +dummy=$(eval v=2) +[ "$v" = 1 ] diff --git a/shell_cmds/sh/tests/expansion/export1.0 b/shell_cmds/sh/tests/expansion/export1.0 new file mode 100644 index 0000000..3286fb9 --- /dev/null +++ b/shell_cmds/sh/tests/expansion/export1.0 @@ -0,0 +1,13 @@ +# $FreeBSD: head/bin/sh/tests/expansion/export1.0 238430 2012-07-13 22:29:02Z jilles $ + +w='@ vv=6' + +v=0 vv=0 +export \v=$w +[ "$v" = "@" ] || echo "Expected @ got $v" +[ "$vv" = "6" ] || echo "Expected 6 got $vv" + +HOME=/known/value + +export \v=~ +[ "$v" = \~ ] || echo "Expected ~ got $v" diff --git a/shell_cmds/sh/tests/expansion/export2.0 b/shell_cmds/sh/tests/expansion/export2.0 new file mode 100644 index 0000000..e8862b8 --- /dev/null +++ b/shell_cmds/sh/tests/expansion/export2.0 @@ -0,0 +1,24 @@ +# $FreeBSD: head/bin/sh/tests/expansion/export2.0 238468 2012-07-15 10:19:43Z jilles $ + +w='@ @' +check() { + [ "$v" = "$w" ] || echo "Expected $w got $v" +} + +export v=$w +check + +HOME=/known/value +check() { + [ "$v" = ~ ] || echo "Expected $HOME got $v" +} + +export v=~ +check + +check() { + [ "$v" = "x:$HOME" ] || echo "Expected x:$HOME got $v" +} + +export v=x:~ +check diff --git a/shell_cmds/sh/tests/expansion/export3.0 b/shell_cmds/sh/tests/expansion/export3.0 new file mode 100644 index 0000000..b82fcc1 --- /dev/null +++ b/shell_cmds/sh/tests/expansion/export3.0 @@ -0,0 +1,30 @@ +# $FreeBSD: head/bin/sh/tests/expansion/export3.0 238468 2012-07-15 10:19:43Z jilles $ + +w='@ @' +check() { + [ "$v" = "$w" ] || echo "Expected $w got $v" +} + +command export v=$w +check +command command export v=$w +check + +HOME=/known/value +check() { + [ "$v" = ~ ] || echo "Expected $HOME got $v" +} + +command export v=~ +check +command command export v=~ +check + +check() { + [ "$v" = "x:$HOME" ] || echo "Expected x:$HOME got $v" +} + +command export v=x:~ +check +command command export v=x:~ +check diff --git a/shell_cmds/sh/tests/expansion/heredoc1.0 b/shell_cmds/sh/tests/expansion/heredoc1.0 new file mode 100644 index 0000000..94328c0 --- /dev/null +++ b/shell_cmds/sh/tests/expansion/heredoc1.0 @@ -0,0 +1,25 @@ +# $FreeBSD: head/bin/sh/tests/expansion/heredoc1.0 222715 2011-06-05 12:46:26Z jilles $ + +f() { return $1; } + +[ `f 42; { cat; } <<EOF +$? +EOF +` = 42 ] || echo compound command bad + +[ `f 42; (cat) <<EOF +$? +EOF +` = 42 ] || echo subshell bad + +long=`printf %08192d 0` + +[ `f 42; { cat; } <<EOF +$long.$? +EOF +` = $long.42 ] || echo long compound command bad + +[ `f 42; (cat) <<EOF +$long.$? +EOF +` = $long.42 ] || echo long subshell bad diff --git a/shell_cmds/sh/tests/expansion/heredoc2.0 b/shell_cmds/sh/tests/expansion/heredoc2.0 new file mode 100644 index 0000000..24c4cc6 --- /dev/null +++ b/shell_cmds/sh/tests/expansion/heredoc2.0 @@ -0,0 +1,15 @@ +# $FreeBSD: head/bin/sh/tests/expansion/heredoc2.0 222716 2011-06-05 14:13:15Z jilles $ + +f() { return $1; } + +[ `f 42; cat <<EOF +$? +EOF +` = 42 ] || echo simple command bad + +long=`printf %08192d 0` + +[ `f 42; cat <<EOF +$long.$? +EOF +` = $long.42 ] || echo long simple command bad diff --git a/shell_cmds/sh/tests/expansion/ifs1.0 b/shell_cmds/sh/tests/expansion/ifs1.0 new file mode 100644 index 0000000..4d5157a --- /dev/null +++ b/shell_cmds/sh/tests/expansion/ifs1.0 @@ -0,0 +1,35 @@ +# $FreeBSD: head/bin/sh/tests/expansion/ifs1.0 194981 2009-06-25 17:36:08Z jilles $ + +c=: e= s=' ' +failures='' +ok='' + +check_result() { + if [ "x$2" = "x$3" ]; then + ok=x$ok + else + failures=x$failures + echo "For $1, expected $3 actual $2" + fi +} + +IFS=' +' +set -- a '' +set -- "$@" +check_result 'set -- "$@"' "($#)($1)($2)" "(2)(a)()" + +set -- a '' +set -- "$@"$e +check_result 'set -- "$@"$e' "($#)($1)($2)" "(2)(a)()" + +set -- a '' +set -- "$@"$s +check_result 'set -- "$@"$s' "($#)($1)($2)" "(2)(a)()" + +IFS="$c" +set -- a '' +set -- "$@"$c +check_result 'set -- "$@"$c' "($#)($1)($2)" "(2)(a)()" + +test "x$failures" = x diff --git a/shell_cmds/sh/tests/expansion/ifs2.0 b/shell_cmds/sh/tests/expansion/ifs2.0 new file mode 100644 index 0000000..3d71b52 --- /dev/null +++ b/shell_cmds/sh/tests/expansion/ifs2.0 @@ -0,0 +1,24 @@ +# $FreeBSD: head/bin/sh/tests/expansion/ifs2.0 211341 2010-08-15 17:14:05Z jilles $ + +failures=0 +i=1 +set -f +while [ "$i" -le 127 ]; do + # A different byte still in the range 1..127. + i2=$((i^2+(i==2))) + # Add a character to work around command substitution's removal of + # final newlines, then remove it again. + c=$(printf \\"$(printf %o@ "$i")") + c=${c%@} + c2=$(printf \\"$(printf %o@ "$i2")") + c2=${c2%@} + IFS=$c + set -- $c2$c$c2$c$c2 + if [ "$#" -ne 3 ] || [ "$1" != "$c2" ] || [ "$2" != "$c2" ] || + [ "$3" != "$c2" ]; then + echo "Bad results for separator $i (word $i2)" >&2 + : $((failures += 1)) + fi + i=$((i+1)) +done +exit $((failures > 0)) diff --git a/shell_cmds/sh/tests/expansion/ifs3.0 b/shell_cmds/sh/tests/expansion/ifs3.0 new file mode 100644 index 0000000..d034654 --- /dev/null +++ b/shell_cmds/sh/tests/expansion/ifs3.0 @@ -0,0 +1,21 @@ +# $FreeBSD: head/bin/sh/tests/expansion/ifs3.0 211622 2010-08-22 13:09:12Z jilles $ + +failures=0 +unset LC_ALL +export LC_CTYPE=en_US.ISO8859-1 +i=128 +set -f +while [ "$i" -le 255 ]; do + i2=$((i^2)) + c=$(printf \\"$(printf %o "$i")") + c2=$(printf \\"$(printf %o "$i2")") + IFS=$c + set -- $c2$c$c2$c$c2 + if [ "$#" -ne 3 ] || [ "$1" != "$c2" ] || [ "$2" != "$c2" ] || + [ "$3" != "$c2" ]; then + echo "Bad results for separator $i (word $i2)" >&2 + : $((failures += 1)) + fi + i=$((i+1)) +done +exit $((failures > 0)) diff --git a/shell_cmds/sh/tests/expansion/ifs4.0 b/shell_cmds/sh/tests/expansion/ifs4.0 new file mode 100644 index 0000000..a307ec5 --- /dev/null +++ b/shell_cmds/sh/tests/expansion/ifs4.0 @@ -0,0 +1,39 @@ +# $FreeBSD: head/bin/sh/tests/expansion/ifs4.0 222361 2011-05-27 15:56:13Z jilles $ + +c=: e= s=' ' +failures='' +ok='' + +check_result() { + if [ "x$2" = "x$3" ]; then + ok=x$ok + else + failures=x$failures + echo "For $1, expected $3 actual $2" + fi +} + +IFS=' +' +set -- a b '' c +set -- $@ +check_result 'set -- $@' "($#)($1)($2)($3)($4)" "(3)(a)(b)(c)()" + +IFS='' +set -- a b '' c +set -- $@ +check_result 'set -- $@' "($#)($1)($2)($3)($4)" "(3)(a)(b)(c)()" + +set -- a b '' c +set -- $* +check_result 'set -- $*' "($#)($1)($2)($3)($4)" "(3)(a)(b)(c)()" + +set -- a b '' c +set -- "$@" +check_result 'set -- "$@"' "($#)($1)($2)($3)($4)" "(4)(a)(b)()(c)" + +set -- a b '' c +set -- "$*" +check_result 'set -- "$*"' "($#)($1)($2)($3)($4)" "(1)(abc)()()()" + +test "x$failures" = x diff --git a/shell_cmds/sh/tests/expansion/ifs5.0 b/shell_cmds/sh/tests/expansion/ifs5.0 new file mode 100644 index 0000000..fc5b4b7 --- /dev/null +++ b/shell_cmds/sh/tests/expansion/ifs5.0 @@ -0,0 +1,4 @@ +# $FreeBSD: head/bin/sh/tests/expansion/ifs5.0 278806 2015-02-15 19:48:29Z jilles $ + +set -- $(echo a b c d) +[ "$#" = 4 ] diff --git a/shell_cmds/sh/tests/expansion/ifs6.0 b/shell_cmds/sh/tests/expansion/ifs6.0 new file mode 100644 index 0000000..3ec5dbc --- /dev/null +++ b/shell_cmds/sh/tests/expansion/ifs6.0 @@ -0,0 +1,6 @@ +# $FreeBSD: head/bin/sh/tests/expansion/ifs6.0 280920 2015-03-31 20:59:37Z jilles $ + +IFS=': ' +x=': :' +set -- $x +[ "$#|$1|$2|$3" = "2|||" ] diff --git a/shell_cmds/sh/tests/expansion/ifs7.0 b/shell_cmds/sh/tests/expansion/ifs7.0 new file mode 100644 index 0000000..a59f313 --- /dev/null +++ b/shell_cmds/sh/tests/expansion/ifs7.0 @@ -0,0 +1,5 @@ +# $FreeBSD: head/bin/sh/tests/expansion/ifs7.0 280920 2015-03-31 20:59:37Z jilles $ + +IFS=2 +set -- $((123)) +[ "$#|$1|$2|$3" = "2|1|3|" ] diff --git a/shell_cmds/sh/tests/expansion/length1.0 b/shell_cmds/sh/tests/expansion/length1.0 new file mode 100644 index 0000000..cee5808 --- /dev/null +++ b/shell_cmds/sh/tests/expansion/length1.0 @@ -0,0 +1,12 @@ +# $FreeBSD: head/bin/sh/tests/expansion/length1.0 219611 2011-03-13 16:20:38Z jilles $ + +v=abcd +[ "${#v}" = 4 ] || echo '${#v} wrong' +v=$$ +[ "${#$}" = "${#v}" ] || echo '${#$} wrong' +[ "${#!}" = 0 ] || echo '${#!} wrong' +set -- 01 2 3 4 5 6 7 8 9 10 11 12 0013 +[ "${#1}" = 2 ] || echo '${#1} wrong' +[ "${#13}" = 4 ] || echo '${#13} wrong' +v=$0 +[ "${#0}" = "${#v}" ] || echo '${#0} wrong' diff --git a/shell_cmds/sh/tests/expansion/length2.0 b/shell_cmds/sh/tests/expansion/length2.0 new file mode 100644 index 0000000..77fd6ad --- /dev/null +++ b/shell_cmds/sh/tests/expansion/length2.0 @@ -0,0 +1,4 @@ +# $FreeBSD: head/bin/sh/tests/expansion/length2.0 219611 2011-03-13 16:20:38Z jilles $ + +v=$- +[ "${#-}" = "${#v}" ] || echo '${#-} wrong' diff --git a/shell_cmds/sh/tests/expansion/length3.0 b/shell_cmds/sh/tests/expansion/length3.0 new file mode 100644 index 0000000..27045b7 --- /dev/null +++ b/shell_cmds/sh/tests/expansion/length3.0 @@ -0,0 +1,10 @@ +# $FreeBSD: head/bin/sh/tests/expansion/length3.0 219611 2011-03-13 16:20:38Z jilles $ + +set -- 1 2 3 4 5 6 7 8 9 10 11 12 13 +[ "$#" = 13 ] || echo '$# wrong' +[ "${#}" = 13 ] || echo '${#} wrong' +[ "${##}" = 2 ] || echo '${##} wrong' +set -- +[ "$#" = 0 ] || echo '$# wrong' +[ "${#}" = 0 ] || echo '${#} wrong' +[ "${##}" = 1 ] || echo '${##} wrong' diff --git a/shell_cmds/sh/tests/expansion/length4.0 b/shell_cmds/sh/tests/expansion/length4.0 new file mode 100644 index 0000000..c1ff021 --- /dev/null +++ b/shell_cmds/sh/tests/expansion/length4.0 @@ -0,0 +1,11 @@ +# $FreeBSD: head/bin/sh/tests/expansion/length4.0 220655 2011-04-15 15:26:05Z jilles $ + +# The construct ${#?} is ambiguous in POSIX.1-2008: it could be the length +# of $? or it could be $# giving an error in the (impossible) case that it +# is not set. +# We use the former interpretation; it seems more useful. + +: +[ "${#?}" = 1 ] || echo '${#?} wrong' +(exit 42) +[ "${#?}" = 2 ] || echo '${#?} wrong' diff --git a/shell_cmds/sh/tests/expansion/length5.0 b/shell_cmds/sh/tests/expansion/length5.0 new file mode 100644 index 0000000..da8ebb5 --- /dev/null +++ b/shell_cmds/sh/tests/expansion/length5.0 @@ -0,0 +1,27 @@ +# $FreeBSD: head/bin/sh/tests/expansion/length5.0 220656 2011-04-15 15:33:24Z jilles $ + +unset LC_ALL +LC_CTYPE=en_US.ISO8859-1 +export LC_CTYPE + +e= +for i in 0 1 2 3; do + for j in 0 1 2 3 4 5 6 7; do + for k in 0 1 2 3 4 5 6 7; do + case $i$j$k in + 000) continue ;; + esac + e="$e\\$i$j$k" + done + done +done +ee=`printf "$e"` +[ ${#ee} = 255 ] || echo bad 1 +[ "${#ee}" = 255 ] || echo bad 2 +[ $((${#ee})) = 255 ] || echo bad 3 +[ "$((${#ee}))" = 255 ] || echo bad 4 +set -- "$ee" +[ ${#1} = 255 ] || echo bad 5 +[ "${#1}" = 255 ] || echo bad 6 +[ $((${#1})) = 255 ] || echo bad 7 +[ "$((${#1}))" = 255 ] || echo bad 8 diff --git a/shell_cmds/sh/tests/expansion/length6.0 b/shell_cmds/sh/tests/expansion/length6.0 new file mode 100644 index 0000000..8da2da8 --- /dev/null +++ b/shell_cmds/sh/tests/expansion/length6.0 @@ -0,0 +1,8 @@ +# $FreeBSD: head/bin/sh/tests/expansion/length6.0 220903 2011-04-20 22:24:54Z jilles $ + +x='!@#$%^&*()[]' +[ ${#x} = 12 ] || echo bad 1 +[ "${#x}" = 12 ] || echo bad 2 +IFS=2 +[ ${#x} = 1 ] || echo bad 3 +[ "${#x}" = 12 ] || echo bad 4 diff --git a/shell_cmds/sh/tests/expansion/length7.0 b/shell_cmds/sh/tests/expansion/length7.0 new file mode 100644 index 0000000..500b7e7 --- /dev/null +++ b/shell_cmds/sh/tests/expansion/length7.0 @@ -0,0 +1,14 @@ +# $FreeBSD: head/bin/sh/tests/expansion/length7.0 221602 2011-05-07 14:32:16Z jilles $ + +unset LC_ALL +LC_CTYPE=en_US.UTF-8 +export LC_CTYPE + +# a umlaut +s=$(printf '\303\244') +# euro sign +s=$s$(printf '\342\202\254') +# some sort of 't' outside BMP +s=$s$(printf '\360\235\225\245') +set -- "$s" +[ ${#s} = 3 ] && [ ${#1} = 3 ] diff --git a/shell_cmds/sh/tests/expansion/length8.0 b/shell_cmds/sh/tests/expansion/length8.0 new file mode 100644 index 0000000..d4f91d8 --- /dev/null +++ b/shell_cmds/sh/tests/expansion/length8.0 @@ -0,0 +1,14 @@ +# $FreeBSD: head/bin/sh/tests/expansion/length8.0 221602 2011-05-07 14:32:16Z jilles $ + +unset LC_ALL +LC_CTYPE=en_US.ISO8859-1 +export LC_CTYPE + +# a umlaut +s=$(printf '\303\244') +# euro sign +s=$s$(printf '\342\202\254') +# some sort of 't' outside BMP +s=$s$(printf '\360\235\225\245') +set -- "$s" +[ ${#s} = 9 ] && [ ${#1} = 9 ] diff --git a/shell_cmds/sh/tests/expansion/local1.0 b/shell_cmds/sh/tests/expansion/local1.0 new file mode 100644 index 0000000..9f16eb0 --- /dev/null +++ b/shell_cmds/sh/tests/expansion/local1.0 @@ -0,0 +1,28 @@ +# $FreeBSD: head/bin/sh/tests/expansion/local1.0 238468 2012-07-15 10:19:43Z jilles $ + +run_test() { + w='@ @' + check() { + [ "$v" = "$w" ] || echo "Expected $w got $v" + } + + local v=$w + check + + HOME=/known/value + check() { + [ "$v" = ~ ] || echo "Expected $HOME got $v" + } + + local v=~ + check + + check() { + [ "$v" = "x:$HOME" ] || echo "Expected x:$HOME got $v" + } + + local v=x:~ + check +} + +run_test diff --git a/shell_cmds/sh/tests/expansion/local2.0 b/shell_cmds/sh/tests/expansion/local2.0 new file mode 100644 index 0000000..9d1a38a --- /dev/null +++ b/shell_cmds/sh/tests/expansion/local2.0 @@ -0,0 +1,34 @@ +# $FreeBSD: head/bin/sh/tests/expansion/local2.0 238468 2012-07-15 10:19:43Z jilles $ + +run_test() { + w='@ @' + check() { + [ "$v" = "$w" ] || echo "Expected $w got $v" + } + + command local v=$w + check + command command local v=$w + check + + HOME=/known/value + check() { + [ "$v" = ~ ] || echo "Expected $HOME got $v" + } + + command local v=~ + check + command command local v=~ + check + + check() { + [ "$v" = "x:$HOME" ] || echo "Expected x:$HOME got $v" + } + + command local v=x:~ + check + command command local v=x:~ + check +} + +run_test diff --git a/shell_cmds/sh/tests/expansion/pathname1.0 b/shell_cmds/sh/tests/expansion/pathname1.0 new file mode 100644 index 0000000..6f40004 --- /dev/null +++ b/shell_cmds/sh/tests/expansion/pathname1.0 @@ -0,0 +1,65 @@ +# $FreeBSD: head/bin/sh/tests/expansion/pathname1.0 302937 2016-07-16 13:26:18Z ache $ + +unset LC_ALL +LC_COLLATE=C +export LC_COLLATE + +failures=0 + +check() { + testcase=$1 + expect=$2 + eval "set -- $testcase" + actual="$*" + if [ "$actual" != "$expect" ]; then + failures=$((failures+1)) + printf '%s\n' "For $testcase, expected $expect actual $actual" + fi +} + +set -e +T=$(mktemp -d ${TMPDIR:-/tmp}/sh-test.XXXXXX) +trap 'rm -rf $T' 0 +cd -P $T + +mkdir testdir testdir2 'testdir/*' 'testdir/?' testdir/a testdir/b testdir2/b +mkdir testdir2/.c +touch testf 'testdir/*/1' 'testdir/?/1' testdir/a/1 testdir/b/1 testdir2/b/.a + +check '' '' +check 'testdir/b' 'testdir/b' +check 'testdir/c' 'testdir/c' +check '\*' '*' +check '\?' '?' +check '*' 'testdir testdir2 testf' +check '*""' 'testdir testdir2 testf' +check '""*' 'testdir testdir2 testf' +check '*/' 'testdir/ testdir2/' +check 'testdir*/a' 'testdir/a' +check 'testdir*/b' 'testdir/b testdir2/b' +check '*/.c' 'testdir2/.c' +check 'testdir2/*' 'testdir2/b' +check 'testdir2/b/*' 'testdir2/b/*' +check 'testdir/*' 'testdir/* testdir/? testdir/a testdir/b' +check 'testdir/*/1' 'testdir/*/1 testdir/?/1 testdir/a/1 testdir/b/1' +check '"testdir/"*/1' 'testdir/*/1 testdir/?/1 testdir/a/1 testdir/b/1' +check 'testdir/\*/*' 'testdir/*/1' +check 'testdir/\?/*' 'testdir/?/1' +check 'testdir/"?"/*' 'testdir/?/1' +check '"testdir"/"?"/*' 'testdir/?/1' +check '"testdir"/"?"*/*' 'testdir/?/1' +check '"testdir"/*"?"/*' 'testdir/?/1' +check '"testdir/?"*/*' 'testdir/?/1' +check 'testdir/\*/' 'testdir/*/' +check 'testdir/\?/' 'testdir/?/' +check 'testdir/"?"/' 'testdir/?/' +check '"testdir"/"?"/' 'testdir/?/' +check '"testdir"/"?"*/' 'testdir/?/' +check '"testdir"/*"?"/' 'testdir/?/' +check '"testdir/?"*/' 'testdir/?/' +check 'testdir/[*]/' 'testdir/*/' +check 'testdir/[?]/' 'testdir/?/' +check 'testdir/[*?]/' 'testdir/*/ testdir/?/' +check '[tz]estdir/[*]/' 'testdir/*/' + +exit $((failures != 0)) diff --git a/shell_cmds/sh/tests/expansion/pathname2.0 b/shell_cmds/sh/tests/expansion/pathname2.0 new file mode 100644 index 0000000..3efffb7 --- /dev/null +++ b/shell_cmds/sh/tests/expansion/pathname2.0 @@ -0,0 +1,35 @@ +# $FreeBSD: head/bin/sh/tests/expansion/pathname2.0 302937 2016-07-16 13:26:18Z ache $ + +unset LC_ALL +LC_COLLATE=C +export LC_COLLATE + +failures=0 + +check() { + testcase=$1 + expect=$2 + eval "set -- $testcase" + actual="$*" + if [ "$actual" != "$expect" ]; then + failures=$((failures+1)) + printf '%s\n' "For $testcase, expected $expect actual $actual" + fi +} + +set -e +T=$(mktemp -d ${TMPDIR:-/tmp}/sh-test.XXXXXX) +trap 'rm -rf $T' 0 +cd -P $T + +mkdir testdir testdir2 'testdir/*' 'testdir/?' testdir/a testdir/b testdir2/b +mkdir testdir2/.c +touch testf 'testdir/*/1' 'testdir/?/1' testdir/a/1 testdir/b/1 testdir2/b/.a + +check '*\/' 'testdir/ testdir2/' +check '"testdir/"*"/1"' 'testdir/*/1 testdir/?/1 testdir/a/1 testdir/b/1' +check '"testdir/"*"/"*' 'testdir/*/1 testdir/?/1 testdir/a/1 testdir/b/1' +check '"testdir/"*\/*' 'testdir/*/1 testdir/?/1 testdir/a/1 testdir/b/1' +check '"testdir"*"/"*"/"*' 'testdir/*/1 testdir/?/1 testdir/a/1 testdir/b/1' + +exit $((failures != 0)) diff --git a/shell_cmds/sh/tests/expansion/pathname3.0 b/shell_cmds/sh/tests/expansion/pathname3.0 new file mode 100644 index 0000000..80d6478 --- /dev/null +++ b/shell_cmds/sh/tests/expansion/pathname3.0 @@ -0,0 +1,29 @@ +# $FreeBSD: head/bin/sh/tests/expansion/pathname3.0 211155 2010-08-10 22:45:59Z jilles $ + +v=12345678 +v=$v$v$v$v +v=$v$v$v$v +v=$v$v$v$v +v=$v$v$v$v +v=$v$v$v$v +# 8192 bytes +v=${v##???} +[ /*/$v = "/*/$v" ] || exit 1 + +s=//// +s=$s$s$s$s +s=$s$s$s$s +s=$s$s$s$s +s=$s$s$s$s +# 1024 bytes +s=${s##??????????} +[ /var/empt[y]/$s/$v = "/var/empt[y]/$s/$v" ] || exit 2 +while [ ${#s} -lt 1034 ]; do + set -- /.${s}et[c] + [ ${#s} -gt 1018 ] || [ "$1" = /.${s}etc ] || exit 3 + set -- /.${s}et[c]/ + [ ${#s} -gt 1017 ] || [ "$1" = /.${s}etc/ ] || exit 4 + set -- /.${s}et[c]/. + [ ${#s} -gt 1016 ] || [ "$1" = /.${s}etc/. ] || exit 5 + s=$s/ +done diff --git a/shell_cmds/sh/tests/expansion/pathname4.0 b/shell_cmds/sh/tests/expansion/pathname4.0 new file mode 100644 index 0000000..d316c0a --- /dev/null +++ b/shell_cmds/sh/tests/expansion/pathname4.0 @@ -0,0 +1,28 @@ +# $FreeBSD: head/bin/sh/tests/expansion/pathname4.0 211646 2010-08-22 21:18:21Z jilles $ + +failures=0 + +check() { + testcase=$1 + expect=$2 + eval "set -- $testcase" + actual="$*" + if [ "$actual" != "$expect" ]; then + failures=$((failures+1)) + printf '%s\n' "For $testcase, expected $expect actual $actual" + fi +} + +set -e +T=$(mktemp -d ${TMPDIR:-/tmp}/sh-test.XXXXXX) +trap 'rm -rf $T' 0 +cd -P $T + +mkdir !!a +touch !!a/fff + +chmod u-r . +check '!!a/ff*' '!!a/fff' +chmod u+r . + +exit $((failures != 0)) diff --git a/shell_cmds/sh/tests/expansion/pathname5.0 b/shell_cmds/sh/tests/expansion/pathname5.0 new file mode 100644 index 0000000..42cd119 --- /dev/null +++ b/shell_cmds/sh/tests/expansion/pathname5.0 @@ -0,0 +1,3 @@ +# $FreeBSD: head/bin/sh/tests/expansion/pathname5.0 278806 2015-02-15 19:48:29Z jilles $ + +[ `echo '/[e]tc'` = /etc ] diff --git a/shell_cmds/sh/tests/expansion/pathname6.0 b/shell_cmds/sh/tests/expansion/pathname6.0 new file mode 100644 index 0000000..e763c6a --- /dev/null +++ b/shell_cmds/sh/tests/expansion/pathname6.0 @@ -0,0 +1,29 @@ +# $FreeBSD: head/bin/sh/tests/expansion/pathname6.0 302937 2016-07-16 13:26:18Z ache $ + +unset LC_ALL +LC_COLLATE=en_US.US-ASCII +export LC_COLLATE + +failures=0 + +check() { + testcase=$1 + expect=$2 + eval "set -- $testcase" + actual="$*" + if [ "$actual" != "$expect" ]; then + failures=$((failures+1)) + printf '%s\n' "For $testcase, expected $expect actual $actual" + fi +} + +set -e +T=$(mktemp -d ${TMPDIR:-/tmp}/sh-test.XXXXXX) +trap 'rm -rf $T' 0 +cd -P $T + +touch A B a b + +check '*' 'a A b B' + +exit $((failures != 0)) diff --git a/shell_cmds/sh/tests/expansion/plus-minus1.0 b/shell_cmds/sh/tests/expansion/plus-minus1.0 new file mode 100644 index 0000000..088ed7f --- /dev/null +++ b/shell_cmds/sh/tests/expansion/plus-minus1.0 @@ -0,0 +1,76 @@ +# $FreeBSD: head/bin/sh/tests/expansion/plus-minus1.0 216738 2010-12-27 15:57:41Z emaste $ + +e= q='?' a='*' t=texttext s='ast*que?non' p='/et[c]/' w='a b c' b='{{(#)}}' +h='##' +failures='' +ok='' + +testcase() { + code="$1" + expected="$2" + oIFS="$IFS" + eval "$code" + IFS='|' + result="$#|$*" + IFS="$oIFS" + if [ "x$result" = "x$expected" ]; then + ok=x$ok + else + failures=x$failures + echo "For $code, expected $expected actual $result" + fi +} + +testcase 'set -- a b' '2|a|b' +testcase 'set --' '0|' +testcase 'set -- ${e}' '0|' +testcase 'set -- "${e}"' '1|' + +testcase 'set -- $p' '1|/etc/' +testcase 'set -- "$p"' '1|/et[c]/' +testcase 'set -- ${s+$p}' '1|/etc/' +testcase 'set -- "${s+$p}"' '1|/et[c]/' +testcase 'set -- ${s+"$p"}' '1|/et[c]/' +# Dquotes in dquotes is undefined for Bourne shell operators +#testcase 'set -- "${s+"$p"}"' '1|/et[c]/' +testcase 'set -- ${e:-$p}' '1|/etc/' +testcase 'set -- "${e:-$p}"' '1|/et[c]/' +testcase 'set -- ${e:-"$p"}' '1|/et[c]/' +# Dquotes in dquotes is undefined for Bourne shell operators +#testcase 'set -- "${e:-"$p"}"' '1|/et[c]/' +testcase 'set -- ${e:+"$e"}' '0|' +testcase 'set -- ${e:+$w"$e"}' '0|' +testcase 'set -- ${w:+"$w"}' '1|a b c' +testcase 'set -- ${w:+$w"$w"}' '3|a|b|ca b c' + +testcase 'set -- "${s+a b}"' '1|a b' +testcase 'set -- "${e:-a b}"' '1|a b' +testcase 'set -- ${e:-\}}' '1|}' +testcase 'set -- ${e:+{}}' '1|}' +testcase 'set -- "${e:+{}}"' '1|}' + +testcase 'set -- ${e+x}${e+x}' '1|xx' +testcase 'set -- "${e+x}"${e+x}' '1|xx' +testcase 'set -- ${e+x}"${e+x}"' '1|xx' +testcase 'set -- "${e+x}${e+x}"' '1|xx' +testcase 'set -- "${e+x}""${e+x}"' '1|xx' + +testcase 'set -- ${e:-${e:-$p}}' '1|/etc/' +testcase 'set -- "${e:-${e:-$p}}"' '1|/et[c]/' +testcase 'set -- ${e:-"${e:-$p}"}' '1|/et[c]/' +testcase 'set -- ${e:-${e:-"$p"}}' '1|/et[c]/' +testcase 'set -- ${e:-${e:-${e:-$w}}}' '3|a|b|c' +testcase 'set -- ${e:-${e:-${e:-"$w"}}}' '1|a b c' +testcase 'set -- ${e:-${e:-"${e:-$w}"}}' '1|a b c' +testcase 'set -- ${e:-"${e:-${e:-$w}}"}' '1|a b c' +testcase 'set -- "${e:-${e:-${e:-$w}}}"' '1|a b c' + +testcase 'shift $#; set -- ${1+"$@"}' '0|' +testcase 'set -- ""; set -- ${1+"$@"}' '1|' +testcase 'set -- "" a; set -- ${1+"$@"}' '2||a' +testcase 'set -- a ""; set -- ${1+"$@"}' '2|a|' +testcase 'set -- a b; set -- ${1+"$@"}' '2|a|b' +testcase 'set -- a\ b; set -- ${1+"$@"}' '1|a b' +testcase 'set -- " " ""; set -- ${1+"$@"}' '2| |' + +test "x$failures" = x diff --git a/shell_cmds/sh/tests/expansion/plus-minus2.0 b/shell_cmds/sh/tests/expansion/plus-minus2.0 new file mode 100644 index 0000000..2f22988 --- /dev/null +++ b/shell_cmds/sh/tests/expansion/plus-minus2.0 @@ -0,0 +1,4 @@ +# $FreeBSD: head/bin/sh/tests/expansion/plus-minus2.0 206145 2010-04-03 20:55:56Z jilles $ + +e= +test "${e:-\}}" = '}' diff --git a/shell_cmds/sh/tests/expansion/plus-minus3.0 b/shell_cmds/sh/tests/expansion/plus-minus3.0 new file mode 100644 index 0000000..d7fc923 --- /dev/null +++ b/shell_cmds/sh/tests/expansion/plus-minus3.0 @@ -0,0 +1,44 @@ +# $FreeBSD: head/bin/sh/tests/expansion/plus-minus3.0 206817 2010-04-18 22:13:45Z jilles $ + +e= q='?' a='*' t=texttext s='ast*que?non' p='/et[c]/' w='a b c' b='{{(#)}}' +h='##' +failures='' +ok='' + +testcase() { + code="$1" + expected="$2" + oIFS="$IFS" + eval "$code" + IFS='|' + result="$#|$*" + IFS="$oIFS" + if [ "x$result" = "x$expected" ]; then + ok=x$ok + else + failures=x$failures + echo "For $code, expected $expected actual $result" + fi +} + +# We follow original ash behaviour for quoted ${var+-=?} expansions: +# a double-quote in one switches back to unquoted state. +# This allows expanding a variable as a single word if it is set +# and substituting multiple words otherwise. +# It is also close to the Bourne and Korn shells. +# POSIX leaves this undefined, and various other shells treat +# such double-quotes as introducing a second level of quoting +# which does not do much except quoting close braces. + +testcase 'set -- "${p+"/et[c]/"}"' '1|/etc/' +testcase 'set -- "${p-"/et[c]/"}"' '1|/et[c]/' +testcase 'set -- "${p+"$p"}"' '1|/etc/' +testcase 'set -- "${p-"$p"}"' '1|/et[c]/' +testcase 'set -- "${p+"""/et[c]/"}"' '1|/etc/' +testcase 'set -- "${p-"""/et[c]/"}"' '1|/et[c]/' +testcase 'set -- "${p+"""$p"}"' '1|/etc/' +testcase 'set -- "${p-"""$p"}"' '1|/et[c]/' +testcase 'set -- "${p+"\@"}"' '1|@' +testcase 'set -- "${p+"'\''/et[c]/'\''"}"' '1|/et[c]/' + +test "x$failures" = x diff --git a/shell_cmds/sh/tests/expansion/plus-minus4.0 b/shell_cmds/sh/tests/expansion/plus-minus4.0 new file mode 100644 index 0000000..4d2d37d --- /dev/null +++ b/shell_cmds/sh/tests/expansion/plus-minus4.0 @@ -0,0 +1,38 @@ +# $FreeBSD: head/bin/sh/tests/expansion/plus-minus4.0 211080 2010-08-08 17:03:23Z jilles $ + +# These may be a bit unclear in the POSIX spec or the proposed revisions, +# and conflict with bash's interpretation, but I think ksh93's interpretation +# makes most sense. In particular, it makes no sense to me that single-quotes +# must match but are not removed. + +e= q='?' a='*' t=texttext s='ast*que?non' p='/et[c]/' w='a b c' b='{{(#)}}' +h='##' +failures='' +ok='' + +testcase() { + code="$1" + expected="$2" + oIFS="$IFS" + eval "$code" + IFS='|' + result="$#|$*" + IFS="$oIFS" + if [ "x$result" = "x$expected" ]; then + ok=x$ok + else + failures=x$failures + echo "For $code, expected $expected actual $result" + fi +} + +testcase 'set -- ${e:-'"'"'}'"'"'}' '1|}' +testcase "set -- \${e:-\\'}" "1|'" +testcase "set -- \${e:-\\'\\'}" "1|''" +testcase "set -- \"\${e:-'}\"" "1|'" +testcase "set -- \"\${e:-'}'}\"" "1|''}" +testcase "set -- \"\${e:-''}\"" "1|''" +testcase 'set -- ${e:-\a}' '1|a' +testcase 'set -- "${e:-\a}"' '1|\a' + +test "x$failures" = x diff --git a/shell_cmds/sh/tests/expansion/plus-minus5.0 b/shell_cmds/sh/tests/expansion/plus-minus5.0 new file mode 100644 index 0000000..02db26c --- /dev/null +++ b/shell_cmds/sh/tests/expansion/plus-minus5.0 @@ -0,0 +1,31 @@ +# $FreeBSD: head/bin/sh/tests/expansion/plus-minus5.0 214492 2010-10-28 22:34:49Z jilles $ + +e= q='?' a='*' t=texttext s='ast*que?non' p='/et[c]/' w='a b c' b='{{(#)}}' +h='##' +failures='' +ok='' + +testcase() { + code="$1" + expected="$2" + oIFS="$IFS" + eval "$code" + IFS='|' + result="$#|$*" + IFS="$oIFS" + if [ "x$result" = "x$expected" ]; then + ok=x$ok + else + failures=x$failures + echo "For $code, expected $expected actual $result" + fi +} + +testcase 'set -- ${e:-"{x}"}' '1|{x}' +testcase 'set -- "${e:-"{x}"}"' '1|{x}' +testcase 'set -- ${h+"{x}"}' '1|{x}' +testcase 'set -- "${h+"{x}"}"' '1|{x}' +testcase 'set -- ${h:-"{x}"}' '1|##' +testcase 'set -- "${h:-"{x}"}"' '1|##' + +test "x$failures" = x diff --git a/shell_cmds/sh/tests/expansion/plus-minus6.0 b/shell_cmds/sh/tests/expansion/plus-minus6.0 new file mode 100644 index 0000000..1bd3f7a --- /dev/null +++ b/shell_cmds/sh/tests/expansion/plus-minus6.0 @@ -0,0 +1,34 @@ +# $FreeBSD: head/bin/sh/tests/expansion/plus-minus6.0 214512 2010-10-29 13:42:18Z jilles $ + +failures=0 +unset LC_ALL +export LC_CTYPE=en_US.ISO8859-1 +nl=' +' +i=1 +set -f +while [ "$i" -le 255 ]; do + # A different byte still in the range 1..255. + i2=$((i^2+(i==2))) + # Add a character to work around command substitution's removal of + # final newlines, then remove it again. + c=$(printf \\"$(printf %o@ "$i")") + c=${c%@} + c2=$(printf \\"$(printf %o@ "$i2")") + c2=${c2%@} + case $c in + [\'$nl'$}();&|\"`']) c=M + esac + case $c2 in + [\'$nl'$}();&|\"`']) c2=N + esac + IFS=$c + command eval "set -- \${\$+$c2$c$c2$c$c2}" + if [ "$#" -ne 3 ] || [ "$1" != "$c2" ] || [ "$2" != "$c2" ] || + [ "$3" != "$c2" ]; then + echo "Bad results for separator $i (word $i2)" >&2 + : $((failures += 1)) + fi + i=$((i+1)) +done +exit $((failures > 0)) diff --git a/shell_cmds/sh/tests/expansion/plus-minus7.0 b/shell_cmds/sh/tests/expansion/plus-minus7.0 new file mode 100644 index 0000000..440cfdd --- /dev/null +++ b/shell_cmds/sh/tests/expansion/plus-minus7.0 @@ -0,0 +1,26 @@ +# $FreeBSD: head/bin/sh/tests/expansion/plus-minus7.0 216738 2010-12-27 15:57:41Z emaste $ + +e= s='foo' +failures='' +ok='' + +testcase() { + code="$1" + expected="$2" + oIFS="$IFS" + eval "$code" + IFS='|' + result="$#|$*" + IFS="$oIFS" + if [ "x$result" = "x$expected" ]; then + ok=x$ok + else + failures=x$failures + echo "For $code, expected $expected actual $result" + fi +} + +testcase 'set -- ${s+a b}' '2|a|b' +testcase 'set -- ${e:-a b}' '2|a|b' + +test "x$failures" = x diff --git a/shell_cmds/sh/tests/expansion/plus-minus8.0 b/shell_cmds/sh/tests/expansion/plus-minus8.0 new file mode 100644 index 0000000..dd7e770 --- /dev/null +++ b/shell_cmds/sh/tests/expansion/plus-minus8.0 @@ -0,0 +1,5 @@ +# $FreeBSD: head/bin/sh/tests/expansion/plus-minus8.0 219623 2011-03-13 20:02:39Z jilles $ + +set -- 1 2 3 4 5 6 7 8 9 10 11 12 13 +[ "${#+hi}" = hi ] || echo '${#+hi} wrong' +[ "${#-hi}" = 13 ] || echo '${#-hi} wrong' diff --git a/shell_cmds/sh/tests/expansion/question1.0 b/shell_cmds/sh/tests/expansion/question1.0 new file mode 100644 index 0000000..2f07597 --- /dev/null +++ b/shell_cmds/sh/tests/expansion/question1.0 @@ -0,0 +1,22 @@ +# $FreeBSD: head/bin/sh/tests/expansion/question1.0 213738 2010-10-12 18:20:38Z obrien $ + +x=a\ b +[ "$x" = "${x?}" ] || exit 1 +set -- ${x?} +{ [ "$#" = 2 ] && [ "$1" = a ] && [ "$2" = b ]; } || exit 1 +unset x +(echo ${x?abcdefg}) 2>&1 | grep -q abcdefg || exit 1 +${SH} -c 'unset foo; echo ${foo?}' 2>/dev/null && exit 1 +${SH} -c 'foo=; echo ${foo:?}' 2>/dev/null && exit 1 +${SH} -c 'foo=; echo ${foo?}' >/dev/null || exit 1 +${SH} -c 'foo=1; echo ${foo:?}' >/dev/null || exit 1 +${SH} -c 'echo ${!?}' 2>/dev/null && exit 1 +${SH} -c ':& echo ${!?}' >/dev/null || exit 1 +${SH} -c 'echo ${#?}' >/dev/null || exit 1 +${SH} -c 'echo ${*?}' 2>/dev/null && exit 1 +${SH} -c 'echo ${*?}' ${SH} x >/dev/null || exit 1 +${SH} -c 'echo ${1?}' 2>/dev/null && exit 1 +${SH} -c 'echo ${1?}' ${SH} x >/dev/null || exit 1 +${SH} -c 'echo ${2?}' ${SH} x 2>/dev/null && exit 1 +${SH} -c 'echo ${2?}' ${SH} x y >/dev/null || exit 1 +exit 0 diff --git a/shell_cmds/sh/tests/expansion/readonly1.0 b/shell_cmds/sh/tests/expansion/readonly1.0 new file mode 100644 index 0000000..e1e950a --- /dev/null +++ b/shell_cmds/sh/tests/expansion/readonly1.0 @@ -0,0 +1,7 @@ +# $FreeBSD: head/bin/sh/tests/expansion/readonly1.0 238468 2012-07-15 10:19:43Z jilles $ + +w='@ @' + +v=0 HOME=/known/value +readonly v=~:~/:$w +[ "$v" = "$HOME:$HOME/:$w" ] || echo "Expected $HOME/:$w got $v" diff --git a/shell_cmds/sh/tests/expansion/redir1.0 b/shell_cmds/sh/tests/expansion/redir1.0 new file mode 100644 index 0000000..fd5c981 --- /dev/null +++ b/shell_cmds/sh/tests/expansion/redir1.0 @@ -0,0 +1,26 @@ +# $FreeBSD: head/bin/sh/tests/expansion/redir1.0 273920 2014-10-31 22:28:10Z jilles $ + +bad=0 +for i in 0 1 2 3; do + for j in 0 1 2 3 4 5 6 7; do + for k in 0 1 2 3 4 5 6 7; do + case $i$j$k in + 000) continue ;; + esac + set -- "$(printf \\$i$j$k@)" + set -- "${1%@}" + ff= + for f in /dev/null /dev/zero /; do + if [ -e "$f" ] && [ ! -e "$f$1" ]; then + ff=$f + fi + done + [ -n "$ff" ] || continue + if { true <$ff$1; } 2>/dev/null; then + echo "Bad: $i$j$k ($ff)" >&2 + : $((bad += 1)) + fi + done + done +done +exit $((bad ? 2 : 0)) diff --git a/shell_cmds/sh/tests/expansion/set-u1.0 b/shell_cmds/sh/tests/expansion/set-u1.0 new file mode 100644 index 0000000..045d6ad --- /dev/null +++ b/shell_cmds/sh/tests/expansion/set-u1.0 @@ -0,0 +1,29 @@ +# $FreeBSD: head/bin/sh/tests/expansion/set-u1.0 213738 2010-10-12 18:20:38Z obrien $ + +${SH} -uc 'unset foo; echo $foo' 2>/dev/null && exit 1 +${SH} -uc 'foo=; echo $foo' >/dev/null || exit 1 +${SH} -uc 'foo=1; echo $foo' >/dev/null || exit 1 +# -/+/= are unaffected by set -u +${SH} -uc 'unset foo; echo ${foo-}' >/dev/null || exit 1 +${SH} -uc 'unset foo; echo ${foo+}' >/dev/null || exit 1 +${SH} -uc 'unset foo; echo ${foo=}' >/dev/null || exit 1 +# length/trimming are affected +${SH} -uc 'unset foo; echo ${#foo}' 2>/dev/null && exit 1 +${SH} -uc 'foo=; echo ${#foo}' >/dev/null || exit 1 +${SH} -uc 'unset foo; echo ${foo#?}' 2>/dev/null && exit 1 +${SH} -uc 'foo=1; echo ${foo#?}' >/dev/null || exit 1 +${SH} -uc 'unset foo; echo ${foo##?}' 2>/dev/null && exit 1 +${SH} -uc 'foo=1; echo ${foo##?}' >/dev/null || exit 1 +${SH} -uc 'unset foo; echo ${foo%?}' 2>/dev/null && exit 1 +${SH} -uc 'foo=1; echo ${foo%?}' >/dev/null || exit 1 +${SH} -uc 'unset foo; echo ${foo%%?}' 2>/dev/null && exit 1 +${SH} -uc 'foo=1; echo ${foo%%?}' >/dev/null || exit 1 + +${SH} -uc 'echo $!' 2>/dev/null && exit 1 +${SH} -uc ':& echo $!' >/dev/null || exit 1 +${SH} -uc 'echo $#' >/dev/null || exit 1 +${SH} -uc 'echo $1' 2>/dev/null && exit 1 +${SH} -uc 'echo $1' ${SH} x >/dev/null || exit 1 +${SH} -uc 'echo $2' ${SH} x 2>/dev/null && exit 1 +${SH} -uc 'echo $2' ${SH} x y >/dev/null || exit 1 +exit 0 diff --git a/shell_cmds/sh/tests/expansion/set-u2.0 b/shell_cmds/sh/tests/expansion/set-u2.0 new file mode 100644 index 0000000..55da8aa --- /dev/null +++ b/shell_cmds/sh/tests/expansion/set-u2.0 @@ -0,0 +1,12 @@ +# $FreeBSD: head/bin/sh/tests/expansion/set-u2.0 198454 2009-10-24 21:20:04Z jilles $ + +set -u +: $* $@ "$@" "$*" +set -- x +: $* $@ "$@" "$*" +shift $# +: $* $@ "$@" "$*" +set -- y +set -- +: $* $@ "$@" "$*" +exit 0 diff --git a/shell_cmds/sh/tests/expansion/set-u3.0 b/shell_cmds/sh/tests/expansion/set-u3.0 new file mode 100644 index 0000000..ea848f7 --- /dev/null +++ b/shell_cmds/sh/tests/expansion/set-u3.0 @@ -0,0 +1,6 @@ +# $FreeBSD: head/bin/sh/tests/expansion/set-u3.0 221463 2011-05-04 22:12:22Z jilles $ + +set -u +unset x +v=$( (eval ': $((x))') 2>&1 >/dev/null) +[ $? -ne 0 ] && [ -n "$v" ] diff --git a/shell_cmds/sh/tests/expansion/tilde1.0 b/shell_cmds/sh/tests/expansion/tilde1.0 new file mode 100644 index 0000000..c0a7f5b --- /dev/null +++ b/shell_cmds/sh/tests/expansion/tilde1.0 @@ -0,0 +1,56 @@ +# $FreeBSD: head/bin/sh/tests/expansion/tilde1.0 206149 2010-04-03 21:56:24Z jilles $ + +HOME=/tmp +roothome=~root +if [ "$roothome" = "~root" ]; then + echo "~root is not expanded!" + exit 2 +fi + +testcase() { + code="$1" + expected="$2" + oIFS="$IFS" + eval "$code" + IFS='|' + result="$#|$*" + IFS="$oIFS" + if [ "x$result" = "x$expected" ]; then + ok=x$ok + else + failures=x$failures + echo "For $code, expected $expected actual $result" + fi +} + +testcase 'set -- ~' '1|/tmp' +testcase 'set -- ~/foo' '1|/tmp/foo' +testcase 'set -- x~' '1|x~' +testcase 'set -- ~root' "1|$roothome" +h=~ +testcase 'set -- "$h"' '1|/tmp' +ooIFS=$IFS +IFS=m +testcase 'set -- ~' '1|/tmp' +testcase 'set -- ~/foo' '1|/tmp/foo' +testcase 'set -- $h' '2|/t|p' +IFS=$ooIFS +t=\~ +testcase 'set -- $t' '1|~' +r=$(cat <<EOF +~ +EOF +) +testcase 'set -- $r' '1|~' +r=$(cat <<EOF +${t+~} +EOF +) +testcase 'set -- $r' '1|~' +r=$(cat <<EOF +${t+~/.} +EOF +) +testcase 'set -- $r' '1|~/.' + +test "x$failures" = x diff --git a/shell_cmds/sh/tests/expansion/tilde2.0 b/shell_cmds/sh/tests/expansion/tilde2.0 new file mode 100644 index 0000000..7827c22 --- /dev/null +++ b/shell_cmds/sh/tests/expansion/tilde2.0 @@ -0,0 +1,90 @@ +# $FreeBSD: head/bin/sh/tests/expansion/tilde2.0 206150 2010-04-03 22:04:44Z jilles $ + +HOME=/tmp +roothome=~root +if [ "$roothome" = "~root" ]; then + echo "~root is not expanded!" + exit 2 +fi + +testcase() { + code="$1" + expected="$2" + oIFS="$IFS" + eval "$code" + IFS='|' + result="$#|$*" + IFS="$oIFS" + if [ "x$result" = "x$expected" ]; then + ok=x$ok + else + failures=x$failures + echo "For $code, expected $expected actual $result" + fi +} + +testcase 'set -- ${$+~}' '1|/tmp' +testcase 'set -- ${$+~/}' '1|/tmp/' +testcase 'set -- ${$+~/foo}' '1|/tmp/foo' +testcase 'set -- ${$+x~}' '1|x~' +testcase 'set -- ${$+~root}' "1|$roothome" +testcase 'set -- ${$+"~"}' '1|~' +testcase 'set -- ${$+"~/"}' '1|~/' +testcase 'set -- ${$+"~/foo"}' '1|~/foo' +testcase 'set -- ${$+"x~"}' '1|x~' +testcase 'set -- ${$+"~root"}' "1|~root" +testcase 'set -- "${$+~}"' '1|~' +testcase 'set -- "${$+~/}"' '1|~/' +testcase 'set -- "${$+~/foo}"' '1|~/foo' +testcase 'set -- "${$+x~}"' '1|x~' +testcase 'set -- "${$+~root}"' "1|~root" +testcase 'set -- ${HOME#~}' '0|' +h=~ +testcase 'set -- "$h"' '1|/tmp' +f=~/foo +testcase 'set -- "$f"' '1|/tmp/foo' +testcase 'set -- ${f#~}' '1|/foo' +testcase 'set -- ${f#~/}' '1|foo' + +ooIFS=$IFS +IFS=m +testcase 'set -- ${$+~}' '1|/tmp' +testcase 'set -- ${$+~/foo}' '1|/tmp/foo' +testcase 'set -- ${$+$h}' '2|/t|p' +testcase 'set -- ${HOME#~}' '0|' +IFS=$ooIFS + +t=\~ +testcase 'set -- ${$+$t}' '1|~' +r=$(cat <<EOF +${HOME#~} +EOF +) +testcase 'set -- $r' '0|' +r=$(cat <<EOF +${HOME#'~'} +EOF +) +testcase 'set -- $r' '1|/tmp' +r=$(cat <<EOF +${t#'~'} +EOF +) +testcase 'set -- $r' '0|' +r=$(cat <<EOF +${roothome#~root} +EOF +) +testcase 'set -- $r' '0|' +r=$(cat <<EOF +${f#~} +EOF +) +testcase 'set -- $r' '1|/foo' +r=$(cat <<EOF +${f#~/} +EOF +) +testcase 'set -- $r' '1|foo' + +test "x$failures" = x diff --git a/shell_cmds/sh/tests/expansion/trim1.0 b/shell_cmds/sh/tests/expansion/trim1.0 new file mode 100644 index 0000000..16dd838 --- /dev/null +++ b/shell_cmds/sh/tests/expansion/trim1.0 @@ -0,0 +1,85 @@ +# $FreeBSD: head/bin/sh/tests/expansion/trim1.0 206143 2010-04-03 20:14:10Z jilles $ + +e= q='?' a='*' t=texttext s='ast*que?non' p='/et[c]/' w='a b c' b='{{(#)}}' +h='##' +failures='' +ok='' + +testcase() { + code="$1" + expected="$2" + oIFS="$IFS" + eval "$code" + IFS='|' + result="$#|$*" + IFS="$oIFS" + if [ "x$result" = "x$expected" ]; then + ok=x$ok + else + failures=x$failures + echo "For $code, expected $expected actual $result" + fi +} + +testcase 'set -- ${t%t}' '1|texttex' +testcase 'set -- "${t%t}"' '1|texttex' +testcase 'set -- ${t%e*}' '1|textt' +testcase 'set -- "${t%e*}"' '1|textt' +testcase 'set -- ${t%%e*}' '1|t' +testcase 'set -- "${t%%e*}"' '1|t' +testcase 'set -- ${t%%*}' '0|' +testcase 'set -- "${t%%*}"' '1|' +testcase 'set -- ${t#t}' '1|exttext' +testcase 'set -- "${t#t}"' '1|exttext' +testcase 'set -- ${t#*x}' '1|ttext' +testcase 'set -- "${t#*x}"' '1|ttext' +testcase 'set -- ${t##*x}' '1|t' +testcase 'set -- "${t##*x}"' '1|t' +testcase 'set -- ${t##*}' '0|' +testcase 'set -- "${t##*}"' '1|' +testcase 'set -- ${t%e$a}' '1|textt' + +set -f +testcase 'set -- ${s%[?]*}' '1|ast*que' +testcase 'set -- "${s%[?]*}"' '1|ast*que' +testcase 'set -- ${s%[*]*}' '1|ast' +testcase 'set -- "${s%[*]*}"' '1|ast' +set +f + +testcase 'set -- $b' '1|{{(#)}}' +testcase 'set -- ${b%\}}' '1|{{(#)}' +testcase 'set -- ${b#{}' '1|{(#)}}' +testcase 'set -- "${b#{}"' '1|{(#)}}' +# Parentheses are special in ksh, check that they can be escaped +testcase 'set -- ${b%\)*}' '1|{{(#' +testcase 'set -- ${b#{}' '1|{(#)}}' +testcase 'set -- $h' '1|##' +testcase 'set -- ${h#\#}' '1|#' +testcase 'set -- ${h###}' '1|#' +testcase 'set -- "${h###}"' '1|#' +testcase 'set -- ${h%#}' '1|#' +testcase 'set -- "${h%#}"' '1|#' + +set -f +testcase 'set -- ${s%"${s#?}"}' '1|a' +testcase 'set -- ${s%"${s#????}"}' '1|ast*' +testcase 'set -- ${s%"${s#????????}"}' '1|ast*que?' +testcase 'set -- ${s#"${s%?}"}' '1|n' +testcase 'set -- ${s#"${s%????}"}' '1|?non' +testcase 'set -- ${s#"${s%????????}"}' '1|*que?non' +set +f +testcase 'set -- "${s%"${s#?}"}"' '1|a' +testcase 'set -- "${s%"${s#????}"}"' '1|ast*' +testcase 'set -- "${s%"${s#????????}"}"' '1|ast*que?' +testcase 'set -- "${s#"${s%?}"}"' '1|n' +testcase 'set -- "${s#"${s%????}"}"' '1|?non' +testcase 'set -- "${s#"${s%????????}"}"' '1|*que?non' +testcase 'set -- ${p#${p}}' '1|/etc/' +testcase 'set -- "${p#${p}}"' '1|/et[c]/' +testcase 'set -- ${p#*[[]}' '1|c]/' +testcase 'set -- "${p#*[[]}"' '1|c]/' +testcase 'set -- ${p#*\[}' '1|c]/' +testcase 'set -- ${p#*"["}' '1|c]/' +testcase 'set -- "${p#*"["}"' '1|c]/' + +test "x$failures" = x diff --git a/shell_cmds/sh/tests/expansion/trim2.0 b/shell_cmds/sh/tests/expansion/trim2.0 new file mode 100644 index 0000000..15651b9 --- /dev/null +++ b/shell_cmds/sh/tests/expansion/trim2.0 @@ -0,0 +1,55 @@ +# $FreeBSD: head/bin/sh/tests/expansion/trim2.0 206147 2010-04-03 21:07:50Z jilles $ + +e= q='?' a='*' t=texttext s='ast*que?non' p='/et[c]/' w='a b c' b='{{(#)}}' +h='##' +failures='' +ok='' + +testcase() { + code="$1" + expected="$2" + oIFS="$IFS" + eval "$code" + IFS='|' + result="$#|$*" + IFS="$oIFS" + if [ "x$result" = "x$expected" ]; then + ok=x$ok + else + failures=x$failures + echo "For $code, expected $expected actual $result" + fi +} + +set -f +testcase 'set -- $s' '1|ast*que?non' +testcase 'set -- ${s%\?*}' '1|ast*que' +testcase 'set -- "${s%\?*}"' '1|ast*que' +testcase 'set -- ${s%\**}' '1|ast' +testcase 'set -- "${s%\**}"' '1|ast' +testcase 'set -- ${s%"$q"*}' '1|ast*que' +testcase 'set -- "${s%"$q"*}"' '1|ast*que' +testcase 'set -- ${s%"$a"*}' '1|ast' +testcase 'set -- "${s%"$a"*}"' '1|ast' +testcase 'set -- ${s%"$q"$a}' '1|ast*que' +testcase 'set -- "${s%"$q"$a}"' '1|ast*que' +testcase 'set -- ${s%"$a"$a}' '1|ast' +testcase 'set -- "${s%"$a"$a}"' '1|ast' +set +f + +testcase 'set -- "${b%\}}"' '1|{{(#)}' +# Parentheses are special in ksh, check that they can be escaped +testcase 'set -- "${b%\)*}"' '1|{{(#' +testcase 'set -- "${h#\#}"' '1|#' + +testcase 'set -- ${p%"${p#?}"}' '1|/' +testcase 'set -- ${p%"${p#??????}"}' '1|/etc' +testcase 'set -- ${p%"${p#???????}"}' '1|/etc/' +testcase 'set -- "${p%"${p#?}"}"' '1|/' +testcase 'set -- "${p%"${p#??????}"}"' '1|/et[c]' +testcase 'set -- "${p%"${p#???????}"}"' '1|/et[c]/' +testcase 'set -- ${p#"${p}"}' '0|' +testcase 'set -- "${p#"${p}"}"' '1|' +testcase 'set -- "${p#*\[}"' '1|c]/' + +test "x$failures" = x diff --git a/shell_cmds/sh/tests/expansion/trim3.0 b/shell_cmds/sh/tests/expansion/trim3.0 new file mode 100644 index 0000000..28c59ae --- /dev/null +++ b/shell_cmds/sh/tests/expansion/trim3.0 @@ -0,0 +1,46 @@ +# $FreeBSD: head/bin/sh/tests/expansion/trim3.0 207127 2010-04-23 17:26:49Z jilles $ + +e= q='?' a='*' t=texttext s='ast*que?non' p='/et[c]/' w='a b c' b='{{(#)}}' +h='##' c='\\\\' +failures='' +ok='' + +testcase() { + code="$1" + expected="$2" + oIFS="$IFS" + eval "$code" + IFS='|' + result="$#|$*" + IFS="$oIFS" + if [ "x$result" = "x$expected" ]; then + ok=x$ok + else + failures=x$failures + echo "For $code, expected $expected actual $result" + fi +} + +# This doesn't make much sense, but it fails in dash so I'm adding it here: +testcase 'set -- "${w%${w#???}}"' '1|a b' + +testcase 'set -- ${p#/et[}' '1|c]/' +testcase 'set -- "${p#/et[}"' '1|c]/' +testcase 'set -- "${p%${p#????}}"' '1|/et[' + +testcase 'set -- ${b%'\'}\''}' '1|{{(#)}' + +testcase 'set -- ${c#\\}' '1|\\\' +testcase 'set -- ${c#\\\\}' '1|\\' +testcase 'set -- ${c#\\\\\\}' '1|\' +testcase 'set -- ${c#\\\\\\\\}' '0|' +testcase 'set -- "${c#\\}"' '1|\\\' +testcase 'set -- "${c#\\\\}"' '1|\\' +testcase 'set -- "${c#\\\\\\}"' '1|\' +testcase 'set -- "${c#\\\\\\\\}"' '1|' +testcase 'set -- "${c#"$c"}"' '1|' +testcase 'set -- ${c#"$c"}' '0|' +testcase 'set -- "${c%"$c"}"' '1|' +testcase 'set -- ${c%"$c"}' '0|' + +test "x$failures" = x diff --git a/shell_cmds/sh/tests/expansion/trim4.0 b/shell_cmds/sh/tests/expansion/trim4.0 new file mode 100644 index 0000000..f79b6fd --- /dev/null +++ b/shell_cmds/sh/tests/expansion/trim4.0 @@ -0,0 +1,15 @@ +# $FreeBSD: head/bin/sh/tests/expansion/trim4.0 213814 2010-10-13 23:29:09Z obrien $ + +v1=/homes/SOME_USER +v2= +v3=C123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789 + +# Trigger bug in VSTRIMRIGHT processing STADJUST() call in expand.c:subevalvar() +while [ ${#v2} -lt 2000 ]; do + v4="${v2} ${v1%/*} $v3" + if [ ${#v4} -ne $((${#v2} + ${#v3} + 8)) ]; then + echo bad: ${#v4} -ne $((${#v2} + ${#v3} + 8)) + fi + v2=x$v2 + v3=y$v3 +done diff --git a/shell_cmds/sh/tests/expansion/trim5.0 b/shell_cmds/sh/tests/expansion/trim5.0 new file mode 100644 index 0000000..8045eb6 --- /dev/null +++ b/shell_cmds/sh/tests/expansion/trim5.0 @@ -0,0 +1,28 @@ +# $FreeBSD: head/bin/sh/tests/expansion/trim5.0 214490 2010-10-28 21:51:14Z jilles $ + +e= q='?' a='*' t=texttext s='ast*que?non' p='/et[c]/' w='a b c' b='{{(#)}}' +h='##' +failures='' +ok='' + +testcase() { + code="$1" + expected="$2" + oIFS="$IFS" + eval "$code" + IFS='|' + result="$#|$*" + IFS="$oIFS" + if [ "x$result" = "x$expected" ]; then + ok=x$ok + else + failures=x$failures + echo "For $code, expected $expected actual $result" + fi +} + +testcase 'set -- "${b%'\'}\''}"' '1|{{(#)}' +testcase 'set -- ${b%"}"}' '1|{{(#)}' +testcase 'set -- "${b%"}"}"' '1|{{(#)}' + +test "x$failures" = x diff --git a/shell_cmds/sh/tests/expansion/trim6.0 b/shell_cmds/sh/tests/expansion/trim6.0 new file mode 100644 index 0000000..e34a09f --- /dev/null +++ b/shell_cmds/sh/tests/expansion/trim6.0 @@ -0,0 +1,22 @@ +# $FreeBSD: head/bin/sh/tests/expansion/trim6.0 214524 2010-10-29 19:34:57Z jilles $ + +e= +for i in 0 1 2 3; do + for j in 0 1 2 3 4 5 6 7; do + for k in 0 1 2 3 4 5 6 7; do + case $i$j$k in + 000) continue ;; + esac + e="$e\\$i$j$k" + done + done +done +e=$(printf "$e") +v=@$e@$e@ +y=${v##*"$e"} +yq="${v##*"$e"}" +[ "$y" = @ ] || echo "error when unquoted in non-splitting context" +[ "$yq" = @ ] || echo "error when quoted in non-splitting context" +[ "${v##*"$e"}" = @ ] || echo "error when quoted in splitting context" +IFS= +[ ${v##*"$e"} = @ ] || echo "error when unquoted in splitting context" diff --git a/shell_cmds/sh/tests/expansion/trim7.0 b/shell_cmds/sh/tests/expansion/trim7.0 new file mode 100644 index 0000000..4f237cc --- /dev/null +++ b/shell_cmds/sh/tests/expansion/trim7.0 @@ -0,0 +1,16 @@ +# $FreeBSD: head/bin/sh/tests/expansion/trim7.0 219623 2011-03-13 20:02:39Z jilles $ + +set -- 1 2 3 4 5 6 7 8 9 10 11 12 13 +[ "${##1}" = 3 ] || echo '${##1} wrong' +[ "${###1}" = 3 ] || echo '${###1} wrong' +[ "${###}" = 13 ] || echo '${###} wrong' +[ "${#%3}" = 1 ] || echo '${#%3} wrong' +[ "${#%%3}" = 1 ] || echo '${#%%3} wrong' +[ "${#%%}" = 13 ] || echo '${#%%} wrong' +set -- +[ "${##0}" = "" ] || echo '${##0} wrong' +[ "${###0}" = "" ] || echo '${###0} wrong' +[ "${###}" = 0 ] || echo '${###} wrong' +[ "${#%0}" = "" ] || echo '${#%0} wrong' +[ "${#%%0}" = "" ] || echo '${#%%0} wrong' +[ "${#%%}" = 0 ] || echo '${#%%} wrong' diff --git a/shell_cmds/sh/tests/expansion/trim8.0 b/shell_cmds/sh/tests/expansion/trim8.0 new file mode 100644 index 0000000..c4b6ce4 --- /dev/null +++ b/shell_cmds/sh/tests/expansion/trim8.0 @@ -0,0 +1,75 @@ +# $FreeBSD: head/bin/sh/tests/expansion/trim8.0 221646 2011-05-08 11:32:20Z jilles $ + +unset LC_ALL +LC_CTYPE=en_US.UTF-8 +export LC_CTYPE + +c1=e +# a umlaut +c2=$(printf '\303\244') +# euro sign +c3=$(printf '\342\202\254') +# some sort of 't' outside BMP +c4=$(printf '\360\235\225\245') + +s=$c1$c2$c3$c4 + +testcase() { + code="$1" + expected="$2" + oIFS="$IFS" + eval "$code" + IFS='|' + result="$#|$*" + IFS="$oIFS" + if [ "x$result" = "x$expected" ]; then + ok=x$ok + else + failures=x$failures + echo "For $code, expected $expected actual $result" + fi +} + +testcase 'set -- "$s"' "1|$s" +testcase 'set -- "${s#$c2}"' "1|$s" +testcase 'set -- "${s#*}"' "1|$s" +testcase 'set -- "${s#$c1}"' "1|$c2$c3$c4" +testcase 'set -- "${s#$c1$c2}"' "1|$c3$c4" +testcase 'set -- "${s#$c1$c2$c3}"' "1|$c4" +testcase 'set -- "${s#$c1$c2$c3$c4}"' "1|" +testcase 'set -- "${s#?}"' "1|$c2$c3$c4" +testcase 'set -- "${s#??}"' "1|$c3$c4" +testcase 'set -- "${s#???}"' "1|$c4" +testcase 'set -- "${s#????}"' "1|" +testcase 'set -- "${s#*$c3}"' "1|$c4" +testcase 'set -- "${s%$c4}"' "1|$c1$c2$c3" +testcase 'set -- "${s%$c3$c4}"' "1|$c1$c2" +testcase 'set -- "${s%$c2$c3$c4}"' "1|$c1" +testcase 'set -- "${s%$c1$c2$c3$c4}"' "1|" +testcase 'set -- "${s%?}"' "1|$c1$c2$c3" +testcase 'set -- "${s%??}"' "1|$c1$c2" +testcase 'set -- "${s%???}"' "1|$c1" +testcase 'set -- "${s%????}"' "1|" +testcase 'set -- "${s%$c2*}"' "1|$c1" +testcase 'set -- "${s##$c2}"' "1|$s" +testcase 'set -- "${s##*}"' "1|" +testcase 'set -- "${s##$c1}"' "1|$c2$c3$c4" +testcase 'set -- "${s##$c1$c2}"' "1|$c3$c4" +testcase 'set -- "${s##$c1$c2$c3}"' "1|$c4" +testcase 'set -- "${s##$c1$c2$c3$c4}"' "1|" +testcase 'set -- "${s##?}"' "1|$c2$c3$c4" +testcase 'set -- "${s##??}"' "1|$c3$c4" +testcase 'set -- "${s##???}"' "1|$c4" +testcase 'set -- "${s##????}"' "1|" +testcase 'set -- "${s##*$c3}"' "1|$c4" +testcase 'set -- "${s%%$c4}"' "1|$c1$c2$c3" +testcase 'set -- "${s%%$c3$c4}"' "1|$c1$c2" +testcase 'set -- "${s%%$c2$c3$c4}"' "1|$c1" +testcase 'set -- "${s%%$c1$c2$c3$c4}"' "1|" +testcase 'set -- "${s%%?}"' "1|$c1$c2$c3" +testcase 'set -- "${s%%??}"' "1|$c1$c2" +testcase 'set -- "${s%%???}"' "1|$c1" +testcase 'set -- "${s%%????}"' "1|" +testcase 'set -- "${s%%$c2*}"' "1|$c1" + +test "x$failures" = x diff --git a/shell_cmds/sh/tests/expansion/trim9.0 b/shell_cmds/sh/tests/expansion/trim9.0 new file mode 100644 index 0000000..f0b08ab --- /dev/null +++ b/shell_cmds/sh/tests/expansion/trim9.0 @@ -0,0 +1,61 @@ +# $FreeBSD: head/bin/sh/tests/expansion/trim9.0 292758 2015-12-26 22:27:48Z jilles $ + +# POSIX does not specify these but they occasionally occur in the wild. +# This just serves to keep working what currently works. + +failures='' +ok='' + +testcase() { + code="$1" + expected="$2" + oIFS="$IFS" + eval "$code" + IFS='|' + result="$#|$*" + IFS="$oIFS" + if [ "x$result" = "x$expected" ]; then + ok=x$ok + else + failures=x$failures + echo "For $code, expected $expected actual $result" + fi +} + +testcase 'shift $#; set -- "${*#Q}"' '1|' +testcase 'shift $#; set -- "${*##Q}"' '1|' +testcase 'shift $#; set -- "${*%Q}"' '1|' +testcase 'shift $#; set -- "${*%%Q}"' '1|' +testcase 'set -- Q R; set -- "${*#Q}"' '1| R' +testcase 'set -- Q R; set -- "${*##Q}"' '1| R' +testcase 'set -- Q R; set -- "${*%R}"' '1|Q ' +testcase 'set -- Q R; set -- "${*%%R}"' '1|Q ' +testcase 'set -- Q R; set -- "${*#S}"' '1|Q R' +testcase 'set -- Q R; set -- "${*##S}"' '1|Q R' +testcase 'set -- Q R; set -- "${*%S}"' '1|Q R' +testcase 'set -- Q R; set -- "${*%%S}"' '1|Q R' +testcase 'set -- Q R; set -- ${*#Q}' '1|R' +testcase 'set -- Q R; set -- ${*##Q}' '1|R' +testcase 'set -- Q R; set -- ${*%R}' '1|Q' +testcase 'set -- Q R; set -- ${*%%R}' '1|Q' +testcase 'set -- Q R; set -- ${*#S}' '2|Q|R' +testcase 'set -- Q R; set -- ${*##S}' '2|Q|R' +testcase 'set -- Q R; set -- ${*%S}' '2|Q|R' +testcase 'set -- Q R; set -- ${*%%S}' '2|Q|R' +testcase 'set -- Q R; set -- ${@#Q}' '1|R' +testcase 'set -- Q R; set -- ${@##Q}' '1|R' +testcase 'set -- Q R; set -- ${@%R}' '1|Q' +testcase 'set -- Q R; set -- ${@%%R}' '1|Q' +testcase 'set -- Q R; set -- ${@#S}' '2|Q|R' +testcase 'set -- Q R; set -- ${@##S}' '2|Q|R' +testcase 'set -- Q R; set -- ${@%S}' '2|Q|R' +testcase 'set -- Q R; set -- ${@%%S}' '2|Q|R' +testcase 'set -- Q R; set -- "${@#Q}"' '2||R' +testcase 'set -- Q R; set -- "${@%R}"' '2|Q|' +testcase 'set -- Q R; set -- "${@%%R}"' '2|Q|' +testcase 'set -- Q R; set -- "${@#S}"' '2|Q|R' +testcase 'set -- Q R; set -- "${@##S}"' '2|Q|R' +testcase 'set -- Q R; set -- "${@%S}"' '2|Q|R' +testcase 'set -- Q R; set -- "${@%%S}"' '2|Q|R' + +test "x$failures" = x diff --git a/shell_cmds/sh/tests/functional_test.sh b/shell_cmds/sh/tests/functional_test.sh new file mode 100755 index 0000000..c2a1f81 --- /dev/null +++ b/shell_cmds/sh/tests/functional_test.sh @@ -0,0 +1,72 @@ +# +# Copyright 2014 EMC Corp. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# +# $FreeBSD: head/bin/sh/tests/functional_test.sh 270101 2014-08-17 14:26:12Z jilles $ + +SRCDIR=$(atf_get_srcdir) + +check() +{ + local tc=${1}; shift + + export SH=$(atf_config_get bin.sh.test_shell /bin/sh) + + local err_file="${SRCDIR}/${tc}.stderr" + [ -f "${err_file}" ] && err_flag="-e file:${err_file}" + local out_file="${SRCDIR}/${tc}.stdout" + [ -f "${out_file}" ] && out_flag="-o file:${out_file}" + + atf_check -s exit:${tc##*.} ${err_flag} ${out_flag} ${SH} "${SRCDIR}/${tc}" +} + +add_testcase() +{ + local tc=${1} + local tc_escaped word + + case "${tc%.*}" in + *-*) + local IFS="-" + for word in ${tc%.*}; do + tc_escaped="${tc_escaped:+${tc_escaped}_}${word}" + done + ;; + *) + tc_escaped=${tc%.*} + ;; + esac + + atf_test_case ${tc_escaped} + eval "${tc_escaped}_body() { check ${tc}; }" + atf_add_test_case ${tc_escaped} +} + +atf_init_test_cases() +{ + for path in $(find -Es "${SRCDIR}" -regex '.*\.[0-9]+$'); do + add_testcase ${path##*/} + done +} diff --git a/shell_cmds/sh/tests/invocation/Makefile b/shell_cmds/sh/tests/invocation/Makefile new file mode 100644 index 0000000..473990b --- /dev/null +++ b/shell_cmds/sh/tests/invocation/Makefile @@ -0,0 +1,16 @@ +# $FreeBSD: head/bin/sh/tests/invocation/Makefile 322455 2017-08-13 14:36:10Z jilles $ + +PACKAGE= tests + +TESTSDIR= ${TESTSBASE}/bin/sh/${.CURDIR:T} + +.PATH: ${.CURDIR:H} +ATF_TESTS_SH= functional_test + +${PACKAGE}FILES+= sh-ac1.0 +${PACKAGE}FILES+= sh-c-missing1.0 +${PACKAGE}FILES+= sh-c1.0 +${PACKAGE}FILES+= sh-ca1.0 +${PACKAGE}FILES+= sh-fca1.0 + +.include <bsd.test.mk> diff --git a/shell_cmds/sh/tests/invocation/Makefile.depend b/shell_cmds/sh/tests/invocation/Makefile.depend new file mode 100644 index 0000000..d01d96a --- /dev/null +++ b/shell_cmds/sh/tests/invocation/Makefile.depend @@ -0,0 +1,11 @@ +# $FreeBSD: head/bin/sh/tests/invocation/Makefile.depend 325187 2017-10-31 00:04:07Z bdrewery $ +# Autogenerated - do NOT edit! + +DIRDEPS = \ + + +.include <dirdeps.mk> + +.if ${DEP_RELDIR} == ${_DEP_RELDIR} +# local dependencies - needed for -jN in clean tree +.endif diff --git a/shell_cmds/sh/tests/invocation/sh-ac1.0 b/shell_cmds/sh/tests/invocation/sh-ac1.0 new file mode 100644 index 0000000..fa7bdac --- /dev/null +++ b/shell_cmds/sh/tests/invocation/sh-ac1.0 @@ -0,0 +1,7 @@ +# $FreeBSD: head/bin/sh/tests/invocation/sh-ac1.0 322438 2017-08-12 19:17:48Z jilles $ +# Test that attached options before c are processed + +case `${SH} -ac 'echo $-:$0' moo` in +*a*:moo) true ;; +*) false ;; +esac diff --git a/shell_cmds/sh/tests/invocation/sh-c-missing1.0 b/shell_cmds/sh/tests/invocation/sh-c-missing1.0 new file mode 100644 index 0000000..8f6a850 --- /dev/null +++ b/shell_cmds/sh/tests/invocation/sh-c-missing1.0 @@ -0,0 +1,3 @@ +# $FreeBSD: head/bin/sh/tests/invocation/sh-c-missing1.0 322455 2017-08-13 14:36:10Z jilles $ + +! echo echo bad | ${SH} -c 2>/dev/null diff --git a/shell_cmds/sh/tests/invocation/sh-c1.0 b/shell_cmds/sh/tests/invocation/sh-c1.0 new file mode 100644 index 0000000..6142225 --- /dev/null +++ b/shell_cmds/sh/tests/invocation/sh-c1.0 @@ -0,0 +1,4 @@ +# $FreeBSD: head/bin/sh/tests/invocation/sh-c1.0 322438 2017-08-12 19:17:48Z jilles $ +# Test that -c executes command_string with the given name and arg + +${SH} -c 'echo $0 $@' moo foo | grep -qx -- "moo foo" diff --git a/shell_cmds/sh/tests/invocation/sh-ca1.0 b/shell_cmds/sh/tests/invocation/sh-ca1.0 new file mode 100644 index 0000000..c743a1e --- /dev/null +++ b/shell_cmds/sh/tests/invocation/sh-ca1.0 @@ -0,0 +1,7 @@ +# $FreeBSD: head/bin/sh/tests/invocation/sh-ca1.0 322438 2017-08-12 19:17:48Z jilles $ +# Test that attached options after c are processed + +case `${SH} -ca 'echo $-:$0' moo` in +*a*:moo) true ;; +*) false ;; +esac diff --git a/shell_cmds/sh/tests/invocation/sh-fca1.0 b/shell_cmds/sh/tests/invocation/sh-fca1.0 new file mode 100644 index 0000000..f28b761 --- /dev/null +++ b/shell_cmds/sh/tests/invocation/sh-fca1.0 @@ -0,0 +1,7 @@ +# $FreeBSD: head/bin/sh/tests/invocation/sh-fca1.0 322438 2017-08-12 19:17:48Z jilles $ +# Test that attached options before and after c are processed + +case `${SH} -fca 'echo $-:$-:$0:$@' foo -bar` in +*f*:*a*:foo:-bar) true ;; +*) false ;; +esac diff --git a/shell_cmds/sh/tests/parameters/Makefile b/shell_cmds/sh/tests/parameters/Makefile new file mode 100644 index 0000000..30708db --- /dev/null +++ b/shell_cmds/sh/tests/parameters/Makefile @@ -0,0 +1,29 @@ +# $FreeBSD: head/bin/sh/tests/parameters/Makefile 306843 2016-10-08 13:40:12Z jilles $ + +PACKAGE= tests + +TESTSDIR= ${TESTSBASE}/bin/sh/${.CURDIR:T} + +.PATH: ${.CURDIR:H} +ATF_TESTS_SH= functional_test + +${PACKAGE}FILES+= env1.0 +${PACKAGE}FILES+= exitstatus1.0 +${PACKAGE}FILES+= ifs1.0 +${PACKAGE}FILES+= mail1.0 +${PACKAGE}FILES+= mail2.0 +${PACKAGE}FILES+= optind1.0 +${PACKAGE}FILES+= optind2.0 +${PACKAGE}FILES+= positional1.0 +${PACKAGE}FILES+= positional2.0 +${PACKAGE}FILES+= positional3.0 +${PACKAGE}FILES+= positional4.0 +${PACKAGE}FILES+= positional5.0 +${PACKAGE}FILES+= positional6.0 +${PACKAGE}FILES+= positional7.0 +${PACKAGE}FILES+= positional8.0 +${PACKAGE}FILES+= positional9.0 +${PACKAGE}FILES+= pwd1.0 +${PACKAGE}FILES+= pwd2.0 + +.include <bsd.test.mk> diff --git a/shell_cmds/sh/tests/parameters/Makefile.depend b/shell_cmds/sh/tests/parameters/Makefile.depend new file mode 100644 index 0000000..41114a3 --- /dev/null +++ b/shell_cmds/sh/tests/parameters/Makefile.depend @@ -0,0 +1,11 @@ +# $FreeBSD: head/bin/sh/tests/parameters/Makefile.depend 296587 2016-03-09 22:46:01Z bdrewery $ +# Autogenerated - do NOT edit! + +DIRDEPS = \ + + +.include <dirdeps.mk> + +.if ${DEP_RELDIR} == ${_DEP_RELDIR} +# local dependencies - needed for -jN in clean tree +.endif diff --git a/shell_cmds/sh/tests/parameters/env1.0 b/shell_cmds/sh/tests/parameters/env1.0 new file mode 100644 index 0000000..0842856 --- /dev/null +++ b/shell_cmds/sh/tests/parameters/env1.0 @@ -0,0 +1,11 @@ +# $FreeBSD: head/bin/sh/tests/parameters/env1.0 222957 2011-06-10 22:42:00Z jilles $ + +export key='must contain this' +unset x +r=$(ENV="\${x?\$key}" ${SH} -i +m 2>&1 >/dev/null <<\EOF +exit 0 +EOF +) && case $r in +*"$key"*) true ;; +*) false ;; +esac diff --git a/shell_cmds/sh/tests/parameters/exitstatus1.0 b/shell_cmds/sh/tests/parameters/exitstatus1.0 new file mode 100644 index 0000000..709600e --- /dev/null +++ b/shell_cmds/sh/tests/parameters/exitstatus1.0 @@ -0,0 +1,9 @@ +# $FreeBSD: head/bin/sh/tests/parameters/exitstatus1.0 185232 2008-11-23 20:27:03Z stefanf $ +f() { + [ $? = $1 ] || exit 1 +} + +true +f 0 +false +f 1 diff --git a/shell_cmds/sh/tests/parameters/ifs1.0 b/shell_cmds/sh/tests/parameters/ifs1.0 new file mode 100644 index 0000000..e88d7a4 --- /dev/null +++ b/shell_cmds/sh/tests/parameters/ifs1.0 @@ -0,0 +1,10 @@ +# $FreeBSD: head/bin/sh/tests/parameters/ifs1.0 306843 2016-10-08 13:40:12Z jilles $ + +env IFS=_ ${SH} -c ' +rc=2 +nosuchtool_function() { + rc=0 +} +v=nosuchtool_function +$v && exit "$rc" +' diff --git a/shell_cmds/sh/tests/parameters/mail1.0 b/shell_cmds/sh/tests/parameters/mail1.0 new file mode 100644 index 0000000..af69211 --- /dev/null +++ b/shell_cmds/sh/tests/parameters/mail1.0 @@ -0,0 +1,15 @@ +# $FreeBSD: head/bin/sh/tests/parameters/mail1.0 213738 2010-10-12 18:20:38Z obrien $ +# Test that a non-interactive shell does not access $MAIL. + +goodfile=/var/empty/sh-test-goodfile +mailfile=/var/empty/sh-test-mailfile +T=$(mktemp sh-test.XXXXXX) || exit +MAIL=$mailfile ktrace -i -f "$T" ${SH} -c "[ -s $goodfile ]" 2>/dev/null +if ! grep -q $goodfile "$T"; then + # ktrace problem + rc=0 +elif ! grep -q $mailfile "$T"; then + rc=0 +fi +rm "$T" +exit ${rc:-3} diff --git a/shell_cmds/sh/tests/parameters/mail2.0 b/shell_cmds/sh/tests/parameters/mail2.0 new file mode 100644 index 0000000..74dc416 --- /dev/null +++ b/shell_cmds/sh/tests/parameters/mail2.0 @@ -0,0 +1,15 @@ +# $FreeBSD: head/bin/sh/tests/parameters/mail2.0 213738 2010-10-12 18:20:38Z obrien $ +# Test that an interactive shell accesses $MAIL. + +goodfile=/var/empty/sh-test-goodfile +mailfile=/var/empty/sh-test-mailfile +T=$(mktemp sh-test.XXXXXX) || exit +ENV=$goodfile MAIL=$mailfile ktrace -i -f "$T" ${SH} +m -i </dev/null >/dev/null 2>&1 +if ! grep -q $goodfile "$T"; then + # ktrace problem + rc=0 +elif grep -q $mailfile "$T"; then + rc=0 +fi +rm "$T" +exit ${rc:-3} diff --git a/shell_cmds/sh/tests/parameters/optind1.0 b/shell_cmds/sh/tests/parameters/optind1.0 new file mode 100644 index 0000000..30fc9d8 --- /dev/null +++ b/shell_cmds/sh/tests/parameters/optind1.0 @@ -0,0 +1,3 @@ +# $FreeBSD: head/bin/sh/tests/parameters/optind1.0 227773 2011-11-20 21:48:50Z jilles $ + +unset OPTIND && [ -z "$OPTIND" ] diff --git a/shell_cmds/sh/tests/parameters/optind2.0 b/shell_cmds/sh/tests/parameters/optind2.0 new file mode 100644 index 0000000..0837a17 --- /dev/null +++ b/shell_cmds/sh/tests/parameters/optind2.0 @@ -0,0 +1,3 @@ +# $FreeBSD: head/bin/sh/tests/parameters/optind2.0 259846 2013-12-24 22:38:24Z jilles $ + +[ "$(OPTIND=42 ${SH} -c 'printf %s "$OPTIND"')" = 1 ] diff --git a/shell_cmds/sh/tests/parameters/positional1.0 b/shell_cmds/sh/tests/parameters/positional1.0 new file mode 100644 index 0000000..ef95be3 --- /dev/null +++ b/shell_cmds/sh/tests/parameters/positional1.0 @@ -0,0 +1,13 @@ +# $FreeBSD: head/bin/sh/tests/parameters/positional1.0 222158 2011-05-21 14:52:26Z jilles $ + +set -- a b c d e f g h i j +[ "$1" = a ] || echo "error at line $LINENO" +[ "${1}" = a ] || echo "error at line $LINENO" +[ "${1-foo}" = a ] || echo "error at line $LINENO" +[ "${1+foo}" = foo ] || echo "error at line $LINENO" +[ "$1+foo" = a+foo ] || echo "error at line $LINENO" +[ "$10" = a0 ] || echo "error at line $LINENO" +[ "$100" = a00 ] || echo "error at line $LINENO" +[ "${10}" = j ] || echo "error at line $LINENO" +[ "${10-foo}" = j ] || echo "error at line $LINENO" +[ "${100-foo}" = foo ] || echo "error at line $LINENO" diff --git a/shell_cmds/sh/tests/parameters/positional2.0 b/shell_cmds/sh/tests/parameters/positional2.0 new file mode 100644 index 0000000..ea29250 --- /dev/null +++ b/shell_cmds/sh/tests/parameters/positional2.0 @@ -0,0 +1,65 @@ +# $FreeBSD: head/bin/sh/tests/parameters/positional2.0 228873 2011-12-25 13:24:48Z jilles $ + +failures='' +ok='' + +testcase() { + code="$1" + expected="$2" + oIFS="$IFS" + eval "$code" + IFS='|' + result="$#|$*" + IFS="$oIFS" + if [ "x$result" = "x$expected" ]; then + ok=x$ok + else + failures=x$failures + echo "For $code, expected $expected actual $result" + fi +} + +testcase 'set -- a b; set -- p$@q' '2|pa|bq' +testcase 'set -- a b; set -- $@q' '2|a|bq' +testcase 'set -- a b; set -- p$@' '2|pa|b' +testcase 'set -- a b; set -- p$@q' '2|pa|bq' +testcase 'set -- a b; set -- $@q' '2|a|bq' +testcase 'set -- a b; set -- p$@' '2|pa|b' +testcase 'set -- a b; set -- p$*q' '2|pa|bq' +testcase 'set -- a b; set -- $*q' '2|a|bq' +testcase 'set -- a b; set -- p$*' '2|pa|b' +testcase 'set -- a b; set -- p$*q' '2|pa|bq' +testcase 'set -- a b; set -- $*q' '2|a|bq' +testcase 'set -- a b; set -- p$*' '2|pa|b' +testcase 'set -- a b; set -- "p$@q"' '2|pa|bq' +testcase 'set -- a b; set -- "$@q"' '2|a|bq' +testcase 'set -- a b; set -- "p$@"' '2|pa|b' +testcase 'set -- a b; set -- p"$@"q' '2|pa|bq' +testcase 'set -- a b; set -- "$@"q' '2|a|bq' +testcase 'set -- a b; set -- p"$@"' '2|pa|b' +testcase 'set -- "" a b; set -- "p$@q"' '3|p|a|bq' +testcase 'set -- "" a b; set -- "$@q"' '3||a|bq' +testcase 'set -- "" a b; set -- "p$@"' '3|p|a|b' +testcase 'set -- "" a b; set -- p"$@"q' '3|p|a|bq' +testcase 'set -- "" a b; set -- "$@"q' '3||a|bq' +testcase 'set -- "" a b; set -- p"$@"' '3|p|a|b' +testcase 'set -- a; set -- p$@q' '1|paq' +testcase 'set -- a; set -- $@q' '1|aq' +testcase 'set -- a; set -- p$@' '1|pa' +testcase 'set -- a; set -- p$@q' '1|paq' +testcase 'set -- a; set -- $@q' '1|aq' +testcase 'set -- a; set -- p$@' '1|pa' +testcase 'set -- a; set -- p$*q' '1|paq' +testcase 'set -- a; set -- $*q' '1|aq' +testcase 'set -- a; set -- p$*' '1|pa' +testcase 'set -- a; set -- p$*q' '1|paq' +testcase 'set -- a; set -- $*q' '1|aq' +testcase 'set -- a; set -- p$*' '1|pa' +testcase 'set -- a; set -- "p$@q"' '1|paq' +testcase 'set -- a; set -- "$@q"' '1|aq' +testcase 'set -- a; set -- "p$@"' '1|pa' +testcase 'set -- a; set -- p"$@"q' '1|paq' +testcase 'set -- a; set -- "$@"q' '1|aq' +testcase 'set -- a; set -- p"$@"' '1|pa' + +test "x$failures" = x diff --git a/shell_cmds/sh/tests/parameters/positional3.0 b/shell_cmds/sh/tests/parameters/positional3.0 new file mode 100644 index 0000000..d164ed9 --- /dev/null +++ b/shell_cmds/sh/tests/parameters/positional3.0 @@ -0,0 +1,4 @@ +# $FreeBSD: head/bin/sh/tests/parameters/positional3.0 268436 2014-07-08 22:04:44Z jilles $ + +r=$(${SH} -c 'echo ${01:+yes}${010:+yes}' '' a '' '' '' '' '' '' '' '' b) +[ "$r" = yesyes ] diff --git a/shell_cmds/sh/tests/parameters/positional4.0 b/shell_cmds/sh/tests/parameters/positional4.0 new file mode 100644 index 0000000..48c2f01 --- /dev/null +++ b/shell_cmds/sh/tests/parameters/positional4.0 @@ -0,0 +1,4 @@ +# $FreeBSD: head/bin/sh/tests/parameters/positional4.0 268568 2014-07-12 10:27:30Z jilles $ + +set -- "x$0" 2 3 4 5 6 7 8 9 "y$0" +[ "${01}.${010}" = "$1.${10}" ] diff --git a/shell_cmds/sh/tests/parameters/positional5.0 b/shell_cmds/sh/tests/parameters/positional5.0 new file mode 100644 index 0000000..80ef805 --- /dev/null +++ b/shell_cmds/sh/tests/parameters/positional5.0 @@ -0,0 +1,14 @@ +# $FreeBSD: head/bin/sh/tests/parameters/positional5.0 268576 2014-07-12 21:54:11Z jilles $ + +i=1 +r=0 +while [ $i -lt $((0x100000000)) ]; do + t= + eval t=\${$i-x} + case $t in + x) ;; + *) echo "Problem with \${$i}" >&2; r=1 ;; + esac + i=$((i + 0x10000000)) +done +exit $r diff --git a/shell_cmds/sh/tests/parameters/positional6.0 b/shell_cmds/sh/tests/parameters/positional6.0 new file mode 100644 index 0000000..c1d5e4c --- /dev/null +++ b/shell_cmds/sh/tests/parameters/positional6.0 @@ -0,0 +1,7 @@ +# $FreeBSD: head/bin/sh/tests/parameters/positional6.0 273802 2014-10-28 22:14:31Z jilles $ + +IFS=? +set p r +v=pqrs +r=${v#"$*"} +[ "$r" = pqrs ] diff --git a/shell_cmds/sh/tests/parameters/positional7.0 b/shell_cmds/sh/tests/parameters/positional7.0 new file mode 100644 index 0000000..df36b6f --- /dev/null +++ b/shell_cmds/sh/tests/parameters/positional7.0 @@ -0,0 +1,8 @@ +# $FreeBSD: head/bin/sh/tests/parameters/positional7.0 273802 2014-10-28 22:14:31Z jilles $ + +set -- / '' +IFS=* +set -- "$*" +IFS=: +args="$*" +[ "$#:$args" = "1:/*" ] diff --git a/shell_cmds/sh/tests/parameters/positional8.0 b/shell_cmds/sh/tests/parameters/positional8.0 new file mode 100644 index 0000000..69717ce --- /dev/null +++ b/shell_cmds/sh/tests/parameters/positional8.0 @@ -0,0 +1,31 @@ +# $FreeBSD: head/bin/sh/tests/parameters/positional8.0 291025 2015-11-18 21:09:03Z jilles $ + +failures='' +ok='' + +testcase() { + code="$1" + expected="$2" + oIFS="$IFS" + eval "$code" + IFS='|' + result="$#|$*" + IFS="$oIFS" + if [ "x$result" = "x$expected" ]; then + ok=x$ok + else + failures=x$failures + echo "For $code, expected $expected actual $result" + fi +} + +testcase 'shift $#; set -- ""$*' '1|' +testcase 'shift $#; set -- $*""' '1|' +testcase 'shift $#; set -- ""$@' '1|' +testcase 'shift $#; set -- $@""' '1|' +testcase 'shift $#; set -- """$*"' '1|' +testcase 'shift $#; set -- "$*"""' '1|' +testcase 'shift $#; set -- """$@"' '1|' +testcase 'shift $#; set -- "$@"""' '1|' + +test "x$failures" = x diff --git a/shell_cmds/sh/tests/parameters/positional9.0 b/shell_cmds/sh/tests/parameters/positional9.0 new file mode 100644 index 0000000..c353e85 --- /dev/null +++ b/shell_cmds/sh/tests/parameters/positional9.0 @@ -0,0 +1,18 @@ +# $FreeBSD: head/bin/sh/tests/parameters/positional9.0 291903 2015-12-06 14:09:31Z jilles $ +# Although POSIX leaves the result of expanding ${#@} and ${#*} unspecified, +# make sure it is at least numeric. + +set -- bb cc ddd +set -f +lengths=${#*}${#@}"${#*}${#@}"$(echo ${#*}${#@}"${#*}${#@}") +IFS= +lengths=$lengths${#*}${#@}"${#*}${#@}"$(echo ${#*}${#@}"${#*}${#@}") +case $lengths in +*[!0-9]*) + printf 'bad: %s\n' "$lengths" + exit 3 ;; +????????????????*) ;; +*) + printf 'too short: %s\n' "$lengths" + exit 3 ;; +esac diff --git a/shell_cmds/sh/tests/parameters/pwd1.0 b/shell_cmds/sh/tests/parameters/pwd1.0 new file mode 100644 index 0000000..8393ef6 --- /dev/null +++ b/shell_cmds/sh/tests/parameters/pwd1.0 @@ -0,0 +1,11 @@ +# $FreeBSD: head/bin/sh/tests/parameters/pwd1.0 213738 2010-10-12 18:20:38Z obrien $ +# Check that bogus PWD values are not accepted from the environment. + +cd / || exit 3 +failures=0 +[ "$(PWD=foo ${SH} -c 'pwd')" = / ] || : $((failures += 1)) +[ "$(PWD=/var/empty ${SH} -c 'pwd')" = / ] || : $((failures += 1)) +[ "$(PWD=/var/empty/foo ${SH} -c 'pwd')" = / ] || : $((failures += 1)) +[ "$(PWD=/bin/ls ${SH} -c 'pwd')" = / ] || : $((failures += 1)) + +exit $((failures != 0)) diff --git a/shell_cmds/sh/tests/parameters/pwd2.0 b/shell_cmds/sh/tests/parameters/pwd2.0 new file mode 100644 index 0000000..d0cecf5 --- /dev/null +++ b/shell_cmds/sh/tests/parameters/pwd2.0 @@ -0,0 +1,24 @@ +# $FreeBSD: head/bin/sh/tests/parameters/pwd2.0 213738 2010-10-12 18:20:38Z obrien $ +# Check that PWD is exported and accepted from the environment. +set -e + +T=$(mktemp -d ${TMPDIR:-/tmp}/sh-test.XXXXXX) +trap 'rm -rf $T' 0 +cd -P $T +TP=$(pwd) +mkdir test1 +ln -s test1 link +cd link +[ "$PWD" = "$TP/link" ] +[ "$(pwd)" = "$TP/link" ] +[ "$(pwd -P)" = "$TP/test1" ] +[ "$(${SH} -c pwd)" = "$TP/link" ] +[ "$(${SH} -c pwd\ -P)" = "$TP/test1" ] +cd .. +[ "$(pwd)" = "$TP" ] +cd -P link +[ "$PWD" = "$TP/test1" ] +[ "$(pwd)" = "$TP/test1" ] +[ "$(pwd -P)" = "$TP/test1" ] +[ "$(${SH} -c pwd)" = "$TP/test1" ] +[ "$(${SH} -c pwd\ -P)" = "$TP/test1" ] diff --git a/shell_cmds/sh/tests/parser/Makefile b/shell_cmds/sh/tests/parser/Makefile new file mode 100644 index 0000000..cd83a50 --- /dev/null +++ b/shell_cmds/sh/tests/parser/Makefile @@ -0,0 +1,88 @@ +# $FreeBSD: head/bin/sh/tests/parser/Makefile 317039 2017-04-16 22:10:02Z jilles $ + +PACKAGE= tests + +TESTSDIR= ${TESTSBASE}/bin/sh/${.CURDIR:T} + +.PATH: ${.CURDIR:H} +ATF_TESTS_SH= functional_test + +${PACKAGE}FILES+= alias1.0 +${PACKAGE}FILES+= alias2.0 +${PACKAGE}FILES+= alias3.0 +${PACKAGE}FILES+= alias4.0 +${PACKAGE}FILES+= alias5.0 +${PACKAGE}FILES+= alias6.0 +${PACKAGE}FILES+= alias7.0 +${PACKAGE}FILES+= alias8.0 +${PACKAGE}FILES+= alias9.0 +${PACKAGE}FILES+= alias10.0 +${PACKAGE}FILES+= alias11.0 +${PACKAGE}FILES+= alias12.0 +${PACKAGE}FILES+= alias13.0 +${PACKAGE}FILES+= alias14.0 +${PACKAGE}FILES+= alias15.0 alias15.0.stdout +${PACKAGE}FILES+= alias16.0 +${PACKAGE}FILES+= alias17.0 +${PACKAGE}FILES+= alias18.0 +${PACKAGE}FILES+= and-pipe-not.0 +${PACKAGE}FILES+= case1.0 +${PACKAGE}FILES+= case2.0 +${PACKAGE}FILES+= comment1.0 +${PACKAGE}FILES+= comment2.42 +${PACKAGE}FILES+= dollar-quote1.0 +${PACKAGE}FILES+= dollar-quote2.0 +${PACKAGE}FILES+= dollar-quote3.0 +${PACKAGE}FILES+= dollar-quote4.0 +${PACKAGE}FILES+= dollar-quote5.0 +${PACKAGE}FILES+= dollar-quote6.0 +${PACKAGE}FILES+= dollar-quote7.0 +${PACKAGE}FILES+= dollar-quote8.0 +${PACKAGE}FILES+= dollar-quote9.0 +${PACKAGE}FILES+= dollar-quote10.0 +${PACKAGE}FILES+= dollar-quote11.0 +${PACKAGE}FILES+= dollar-quote12.0 +${PACKAGE}FILES+= dollar-quote13.0 +${PACKAGE}FILES+= empty-braces1.0 +${PACKAGE}FILES+= empty-cmd1.0 +${PACKAGE}FILES+= for1.0 +${PACKAGE}FILES+= for2.0 +${PACKAGE}FILES+= func1.0 +${PACKAGE}FILES+= func2.0 +${PACKAGE}FILES+= func3.0 +${PACKAGE}FILES+= heredoc1.0 +${PACKAGE}FILES+= heredoc2.0 +${PACKAGE}FILES+= heredoc3.0 +${PACKAGE}FILES+= heredoc4.0 +${PACKAGE}FILES+= heredoc5.0 +${PACKAGE}FILES+= heredoc6.0 +${PACKAGE}FILES+= heredoc7.0 +${PACKAGE}FILES+= heredoc8.0 +${PACKAGE}FILES+= heredoc9.0 +${PACKAGE}FILES+= heredoc10.0 +${PACKAGE}FILES+= heredoc11.0 +${PACKAGE}FILES+= heredoc12.0 +${PACKAGE}FILES+= heredoc13.0 +${PACKAGE}FILES+= line-cont1.0 +${PACKAGE}FILES+= line-cont2.0 +${PACKAGE}FILES+= line-cont3.0 +${PACKAGE}FILES+= line-cont4.0 +${PACKAGE}FILES+= line-cont5.0 +${PACKAGE}FILES+= line-cont6.0 +${PACKAGE}FILES+= line-cont7.0 +${PACKAGE}FILES+= line-cont8.0 +${PACKAGE}FILES+= line-cont9.0 +${PACKAGE}FILES+= line-cont10.0 +${PACKAGE}FILES+= line-cont11.0 +${PACKAGE}FILES+= no-space1.0 +${PACKAGE}FILES+= no-space2.0 +${PACKAGE}FILES+= nul1.0 +${PACKAGE}FILES+= only-redir1.0 +${PACKAGE}FILES+= only-redir2.0 +${PACKAGE}FILES+= only-redir3.0 +${PACKAGE}FILES+= only-redir4.0 +${PACKAGE}FILES+= pipe-not1.0 +${PACKAGE}FILES+= set-v1.0 set-v1.0.stderr +${PACKAGE}FILES+= var-assign1.0 + +.include <bsd.test.mk> diff --git a/shell_cmds/sh/tests/parser/Makefile.depend b/shell_cmds/sh/tests/parser/Makefile.depend new file mode 100644 index 0000000..af589a4 --- /dev/null +++ b/shell_cmds/sh/tests/parser/Makefile.depend @@ -0,0 +1,11 @@ +# $FreeBSD: head/bin/sh/tests/parser/Makefile.depend 296587 2016-03-09 22:46:01Z bdrewery $ +# Autogenerated - do NOT edit! + +DIRDEPS = \ + + +.include <dirdeps.mk> + +.if ${DEP_RELDIR} == ${_DEP_RELDIR} +# local dependencies - needed for -jN in clean tree +.endif diff --git a/shell_cmds/sh/tests/parser/alias1.0 b/shell_cmds/sh/tests/parser/alias1.0 new file mode 100644 index 0000000..9b67211 --- /dev/null +++ b/shell_cmds/sh/tests/parser/alias1.0 @@ -0,0 +1,5 @@ +# $FreeBSD: head/bin/sh/tests/parser/alias1.0 214280 2010-10-24 16:55:17Z jilles $ + +alias alias0=exit +eval 'alias0 0' +exit 1 diff --git a/shell_cmds/sh/tests/parser/alias10.0 b/shell_cmds/sh/tests/parser/alias10.0 new file mode 100644 index 0000000..b8684f4 --- /dev/null +++ b/shell_cmds/sh/tests/parser/alias10.0 @@ -0,0 +1,9 @@ +# $FreeBSD: head/bin/sh/tests/parser/alias10.0 243252 2012-11-18 23:15:22Z jilles $ + +# This test may start consuming memory indefinitely if it fails. +ulimit -t 5 2>/dev/null +ulimit -v 100000 2>/dev/null + +alias echo='echo' +alias echo='echo' +[ "`eval echo b`" = b ] diff --git a/shell_cmds/sh/tests/parser/alias11.0 b/shell_cmds/sh/tests/parser/alias11.0 new file mode 100644 index 0000000..10fd827 --- /dev/null +++ b/shell_cmds/sh/tests/parser/alias11.0 @@ -0,0 +1,6 @@ +# $FreeBSD: head/bin/sh/tests/parser/alias11.0 261141 2014-01-24 23:00:35Z jilles $ + +alias alias0=alias1 +alias alias1=exit +eval 'alias0 0' +exit 3 diff --git a/shell_cmds/sh/tests/parser/alias12.0 b/shell_cmds/sh/tests/parser/alias12.0 new file mode 100644 index 0000000..52cc4b0 --- /dev/null +++ b/shell_cmds/sh/tests/parser/alias12.0 @@ -0,0 +1,6 @@ +# $FreeBSD: head/bin/sh/tests/parser/alias12.0 261160 2014-01-25 14:59:08Z jilles $ + +unalias -a +alias alias0=command +alias true='echo bad' +eval 'alias0 true' diff --git a/shell_cmds/sh/tests/parser/alias13.0 b/shell_cmds/sh/tests/parser/alias13.0 new file mode 100644 index 0000000..9f45067 --- /dev/null +++ b/shell_cmds/sh/tests/parser/alias13.0 @@ -0,0 +1,6 @@ +# $FreeBSD: head/bin/sh/tests/parser/alias13.0 261160 2014-01-25 14:59:08Z jilles $ + +unalias -a +alias command=command +alias true='echo bad' +eval 'command true' diff --git a/shell_cmds/sh/tests/parser/alias14.0 b/shell_cmds/sh/tests/parser/alias14.0 new file mode 100644 index 0000000..2d1bc51 --- /dev/null +++ b/shell_cmds/sh/tests/parser/alias14.0 @@ -0,0 +1,6 @@ +# $FreeBSD: head/bin/sh/tests/parser/alias14.0 261192 2014-01-26 21:19:33Z jilles $ + +alias command='command ' +alias alias0=exit +eval 'command alias0 0' +exit 3 diff --git a/shell_cmds/sh/tests/parser/alias15.0 b/shell_cmds/sh/tests/parser/alias15.0 new file mode 100644 index 0000000..c2e6117 --- /dev/null +++ b/shell_cmds/sh/tests/parser/alias15.0 @@ -0,0 +1,12 @@ +# $FreeBSD: head/bin/sh/tests/parser/alias15.0 261192 2014-01-26 21:19:33Z jilles $ + +f_echoanddo() { + printf '%s\n' "$*" + "$@" +} + +alias echoanddo='f_echoanddo ' +alias alias0='echo test2' +eval 'echoanddo echo test1' +eval 'echoanddo alias0' +exit 0 diff --git a/shell_cmds/sh/tests/parser/alias15.0.stdout b/shell_cmds/sh/tests/parser/alias15.0.stdout new file mode 100644 index 0000000..6dd179c --- /dev/null +++ b/shell_cmds/sh/tests/parser/alias15.0.stdout @@ -0,0 +1,4 @@ +echo test1 +test1 +echo test2 +test2 diff --git a/shell_cmds/sh/tests/parser/alias16.0 b/shell_cmds/sh/tests/parser/alias16.0 new file mode 100644 index 0000000..b551216 --- /dev/null +++ b/shell_cmds/sh/tests/parser/alias16.0 @@ -0,0 +1,7 @@ +# $FreeBSD: head/bin/sh/tests/parser/alias16.0 316646 2017-04-08 21:57:59Z jilles $ + +v=1 +alias a='unalias a +v=2' +eval a +[ "$v" = 2 ] diff --git a/shell_cmds/sh/tests/parser/alias17.0 b/shell_cmds/sh/tests/parser/alias17.0 new file mode 100644 index 0000000..fd233ef --- /dev/null +++ b/shell_cmds/sh/tests/parser/alias17.0 @@ -0,0 +1,7 @@ +# $FreeBSD: head/bin/sh/tests/parser/alias17.0 317037 2017-04-16 21:42:43Z jilles $ + +v=1 +alias a='unalias -a +v=2' +eval a +[ "$v" = 2 ] diff --git a/shell_cmds/sh/tests/parser/alias18.0 b/shell_cmds/sh/tests/parser/alias18.0 new file mode 100644 index 0000000..4b072e8 --- /dev/null +++ b/shell_cmds/sh/tests/parser/alias18.0 @@ -0,0 +1,8 @@ +# $FreeBSD: head/bin/sh/tests/parser/alias18.0 317039 2017-04-16 22:10:02Z jilles $ + +v=1 +alias a='alias a=v=2 +v=3 +a' +eval a +[ "$v" = 2 ] diff --git a/shell_cmds/sh/tests/parser/alias2.0 b/shell_cmds/sh/tests/parser/alias2.0 new file mode 100644 index 0000000..f412756 --- /dev/null +++ b/shell_cmds/sh/tests/parser/alias2.0 @@ -0,0 +1,6 @@ +# $FreeBSD: head/bin/sh/tests/parser/alias2.0 214280 2010-10-24 16:55:17Z jilles $ + +alias alias0=exit +x=alias0 +eval 'case $x in alias0) exit 0;; esac' +exit 1 diff --git a/shell_cmds/sh/tests/parser/alias3.0 b/shell_cmds/sh/tests/parser/alias3.0 new file mode 100644 index 0000000..97aac19 --- /dev/null +++ b/shell_cmds/sh/tests/parser/alias3.0 @@ -0,0 +1,6 @@ +# $FreeBSD: head/bin/sh/tests/parser/alias3.0 214709 2010-11-02 23:44:29Z jilles $ + +alias alias0=exit +x=alias0 +eval 'case $x in "alias0") alias0 0;; esac' +exit 1 diff --git a/shell_cmds/sh/tests/parser/alias4.0 b/shell_cmds/sh/tests/parser/alias4.0 new file mode 100644 index 0000000..4c82c6c --- /dev/null +++ b/shell_cmds/sh/tests/parser/alias4.0 @@ -0,0 +1,5 @@ +# $FreeBSD: head/bin/sh/tests/parser/alias4.0 222165 2011-05-21 22:03:06Z jilles $ + +alias alias0=exit +eval 'x=1 alias0 0' +exit 1 diff --git a/shell_cmds/sh/tests/parser/alias5.0 b/shell_cmds/sh/tests/parser/alias5.0 new file mode 100644 index 0000000..c649562 --- /dev/null +++ b/shell_cmds/sh/tests/parser/alias5.0 @@ -0,0 +1,5 @@ +# $FreeBSD: head/bin/sh/tests/parser/alias5.0 222165 2011-05-21 22:03:06Z jilles $ + +alias alias0=exit +eval '</dev/null alias0 0' +exit 1 diff --git a/shell_cmds/sh/tests/parser/alias6.0 b/shell_cmds/sh/tests/parser/alias6.0 new file mode 100644 index 0000000..86e1bfb --- /dev/null +++ b/shell_cmds/sh/tests/parser/alias6.0 @@ -0,0 +1,6 @@ +# $FreeBSD: head/bin/sh/tests/parser/alias6.0 224104 2011-07-16 16:14:14Z jilles $ + +alias alias0='| cat >/dev/null' + +eval '{ echo bad; } alias0' +eval '(echo bad)alias0' diff --git a/shell_cmds/sh/tests/parser/alias7.0 b/shell_cmds/sh/tests/parser/alias7.0 new file mode 100644 index 0000000..a5b9077 --- /dev/null +++ b/shell_cmds/sh/tests/parser/alias7.0 @@ -0,0 +1,4 @@ +# $FreeBSD: head/bin/sh/tests/parser/alias7.0 240825 2012-09-22 12:52:41Z jilles $ + +alias echo='echo a' +[ "`eval echo b`" = "a b" ] diff --git a/shell_cmds/sh/tests/parser/alias8.0 b/shell_cmds/sh/tests/parser/alias8.0 new file mode 100644 index 0000000..f22dfd4 --- /dev/null +++ b/shell_cmds/sh/tests/parser/alias8.0 @@ -0,0 +1,4 @@ +# $FreeBSD: head/bin/sh/tests/parser/alias8.0 240825 2012-09-22 12:52:41Z jilles $ + +alias echo='echo' +[ "`eval echo b`" = b ] diff --git a/shell_cmds/sh/tests/parser/alias9.0 b/shell_cmds/sh/tests/parser/alias9.0 new file mode 100644 index 0000000..bbcb4ce --- /dev/null +++ b/shell_cmds/sh/tests/parser/alias9.0 @@ -0,0 +1,6 @@ +# $FreeBSD: head/bin/sh/tests/parser/alias9.0 242721 2012-11-07 23:15:36Z jilles $ + +alias alias0=: +alias alias0=exit +eval 'alias0 0' +exit 1 diff --git a/shell_cmds/sh/tests/parser/and-pipe-not.0 b/shell_cmds/sh/tests/parser/and-pipe-not.0 new file mode 100644 index 0000000..fffb1d4 --- /dev/null +++ b/shell_cmds/sh/tests/parser/and-pipe-not.0 @@ -0,0 +1,2 @@ +# $FreeBSD: head/bin/sh/tests/parser/and-pipe-not.0 191010 2009-04-13 19:12:28Z stefanf $ +true && ! true | false diff --git a/shell_cmds/sh/tests/parser/case1.0 b/shell_cmds/sh/tests/parser/case1.0 new file mode 100644 index 0000000..d146399 --- /dev/null +++ b/shell_cmds/sh/tests/parser/case1.0 @@ -0,0 +1,14 @@ +# $FreeBSD: head/bin/sh/tests/parser/case1.0 207824 2010-05-09 17:10:50Z jilles $ + +keywords='if then else elif fi while until for do done { } case esac ! in' + +# Keywords can be used unquoted in case statements, except the keyword +# esac as the first pattern of a '|' alternation without a starting '('. +# (POSIX doesn't seem to require (esac) to work.) +for k in $keywords; do + eval "case $k in (foo|$k) ;; *) echo bad ;; esac" + eval "case $k in ($k) ;; *) echo bad ;; esac" + eval "case $k in foo|$k) ;; *) echo bad ;; esac" + [ "$k" = esac ] && continue + eval "case $k in $k) ;; *) echo bad ;; esac" +done diff --git a/shell_cmds/sh/tests/parser/case2.0 b/shell_cmds/sh/tests/parser/case2.0 new file mode 100644 index 0000000..df5a319 --- /dev/null +++ b/shell_cmds/sh/tests/parser/case2.0 @@ -0,0 +1,32 @@ +# $FreeBSD: head/bin/sh/tests/parser/case2.0 207824 2010-05-09 17:10:50Z jilles $ + +# Pretty much only ash derivatives can parse all of this. + +f1() { + x=$(case x in + (x|esac) ;; + (*) echo bad >&2 ;; + esac) +} +f1 +f2() { + x=$(case x in + (x|esac) ;; + (*) echo bad >&2 + esac) +} +f2 +f3() { + x=$(case x in + x|esac) ;; + *) echo bad >&2 ;; + esac) +} +f3 +f4() { + x=$(case x in + x|esac) ;; + *) echo bad >&2 + esac) +} +f4 diff --git a/shell_cmds/sh/tests/parser/comment1.0 b/shell_cmds/sh/tests/parser/comment1.0 new file mode 100644 index 0000000..bdf9c3b --- /dev/null +++ b/shell_cmds/sh/tests/parser/comment1.0 @@ -0,0 +1,3 @@ +# $FreeBSD: head/bin/sh/tests/parser/comment1.0 295818 2016-02-19 16:56:07Z jilles $ + +${SH} -c '#' diff --git a/shell_cmds/sh/tests/parser/comment2.42 b/shell_cmds/sh/tests/parser/comment2.42 new file mode 100644 index 0000000..7e623c9 --- /dev/null +++ b/shell_cmds/sh/tests/parser/comment2.42 @@ -0,0 +1,4 @@ +# $FreeBSD: head/bin/sh/tests/parser/comment2.42 295818 2016-02-19 16:56:07Z jilles $ + +${SH} -c '# +exit 42' diff --git a/shell_cmds/sh/tests/parser/dollar-quote1.0 b/shell_cmds/sh/tests/parser/dollar-quote1.0 new file mode 100644 index 0000000..1206141 --- /dev/null +++ b/shell_cmds/sh/tests/parser/dollar-quote1.0 @@ -0,0 +1,12 @@ +# $FreeBSD$ + +set -e + +[ $'hi' = hi ] +[ $'hi +there' = 'hi +there' ] +[ $'\"\'\\\a\b\f\t\v' = "\"'\\$(printf "\a\b\f\t\v")" ] +[ $'hi\nthere' = 'hi +there' ] +[ $'a\rb' = "$(printf "a\rb")" ] diff --git a/shell_cmds/sh/tests/parser/dollar-quote10.0 b/shell_cmds/sh/tests/parser/dollar-quote10.0 new file mode 100644 index 0000000..745d3aa --- /dev/null +++ b/shell_cmds/sh/tests/parser/dollar-quote10.0 @@ -0,0 +1,10 @@ +# $FreeBSD: head/bin/sh/tests/parser/dollar-quote10.0 221669 2011-05-08 17:40:10Z jilles $ + +# a umlaut +s=$(printf '\303\244') +# euro sign +s=$s$(printf '\342\202\254') + +# Start a new shell so the locale change is picked up. +ss="$(LC_ALL=en_US.UTF-8 ${SH} -c "printf %s \$'\u00e4\u20ac'")" +[ "$s" = "$ss" ] diff --git a/shell_cmds/sh/tests/parser/dollar-quote11.0 b/shell_cmds/sh/tests/parser/dollar-quote11.0 new file mode 100644 index 0000000..b9db067 --- /dev/null +++ b/shell_cmds/sh/tests/parser/dollar-quote11.0 @@ -0,0 +1,8 @@ +# $FreeBSD: head/bin/sh/tests/parser/dollar-quote11.0 221669 2011-05-08 17:40:10Z jilles $ + +# some sort of 't' outside BMP +s=$s$(printf '\360\235\225\245') + +# Start a new shell so the locale change is picked up. +ss="$(LC_ALL=en_US.UTF-8 ${SH} -c "printf %s \$'\U0001d565'")" +[ "$s" = "$ss" ] diff --git a/shell_cmds/sh/tests/parser/dollar-quote12.0 b/shell_cmds/sh/tests/parser/dollar-quote12.0 new file mode 100644 index 0000000..bb06e3c --- /dev/null +++ b/shell_cmds/sh/tests/parser/dollar-quote12.0 @@ -0,0 +1,7 @@ +# $FreeBSD: head/bin/sh/tests/parser/dollar-quote12.0 286971 2015-08-20 21:31:36Z jilles $ + +# \u without any digits at all remains invalid. +# Our choice is a parse error. + +v=$( (eval ": \$'\u'") 2>&1 >/dev/null) +[ $? -ne 0 ] && [ -n "$v" ] diff --git a/shell_cmds/sh/tests/parser/dollar-quote13.0 b/shell_cmds/sh/tests/parser/dollar-quote13.0 new file mode 100644 index 0000000..fe31d9d --- /dev/null +++ b/shell_cmds/sh/tests/parser/dollar-quote13.0 @@ -0,0 +1,8 @@ +# $FreeBSD: head/bin/sh/tests/parser/dollar-quote13.0 286973 2015-08-20 22:05:55Z jilles $ + +# This Unicode escape sequence that has never been in range should either +# fail to expand or expand to a fallback. + +c=$(eval printf %s \$\'\\Uffffff41\' 2>/dev/null) +r=$(($? != 0)) +[ "$r.$c" = '1.' ] || [ "$r.$c" = '0.?' ] || [ "$r.$c" = $'0.\u2222' ] diff --git a/shell_cmds/sh/tests/parser/dollar-quote2.0 b/shell_cmds/sh/tests/parser/dollar-quote2.0 new file mode 100644 index 0000000..4617ed8 --- /dev/null +++ b/shell_cmds/sh/tests/parser/dollar-quote2.0 @@ -0,0 +1,5 @@ +# $FreeBSD$ + +# This depends on the ASCII character set. + +[ $'\e' = "$(printf "\033")" ] diff --git a/shell_cmds/sh/tests/parser/dollar-quote3.0 b/shell_cmds/sh/tests/parser/dollar-quote3.0 new file mode 100644 index 0000000..a7e6852 --- /dev/null +++ b/shell_cmds/sh/tests/parser/dollar-quote3.0 @@ -0,0 +1,22 @@ +# $FreeBSD$ + +unset LC_ALL +LC_CTYPE=en_US.ISO8859-1 +export LC_CTYPE + +e= +for i in 0 1 2 3; do + for j in 0 1 2 3 4 5 6 7; do + for k in 0 1 2 3 4 5 6 7; do + case $i$j$k in + 000) continue ;; + esac + e="$e\\$i$j$k" + done + done +done +ee=`printf "$e"` +[ "${#ee}" = 255 ] || echo length bad + +# Start a new shell so the locale change is picked up. +[ "$(${SH} -c "printf %s \$'$e'")" = "$ee" ] diff --git a/shell_cmds/sh/tests/parser/dollar-quote4.0 b/shell_cmds/sh/tests/parser/dollar-quote4.0 new file mode 100644 index 0000000..f620af5 --- /dev/null +++ b/shell_cmds/sh/tests/parser/dollar-quote4.0 @@ -0,0 +1,19 @@ +# $FreeBSD$ + +unset LC_ALL +LC_CTYPE=en_US.ISO8859-1 +export LC_CTYPE + +e= +for i in 0 1 2 3 4 5 6 7 8 9 a b c d e f; do + for j in 0 1 2 3 4 5 6 7 8 9 a b c d e f; do + case $i$j in + 00) continue ;; + esac + e="$e\x$i$j" + done +done + +# Start a new shell so the locale change is picked up. +ee="$(${SH} -c "printf %s \$'$e'")" +[ "${#ee}" = 255 ] || echo length bad diff --git a/shell_cmds/sh/tests/parser/dollar-quote5.0 b/shell_cmds/sh/tests/parser/dollar-quote5.0 new file mode 100644 index 0000000..c2c44ca --- /dev/null +++ b/shell_cmds/sh/tests/parser/dollar-quote5.0 @@ -0,0 +1,12 @@ +# $FreeBSD$ + +# This depends on the ASCII character set. + +set -e + +[ $'\ca\cb\cc\cd\ce\cf\cg\ch\ci\cj\ck\cl\cm\cn\co\cp\cq\cr\cs\ct\cu\cv\cw\cx\cy\cz' = $'\001\002\003\004\005\006\007\010\011\012\013\014\015\016\017\020\021\022\023\024\025\026\027\030\031\032' ] +[ $'\cA\cB\cC\cD\cE\cF\cG\cH\cI\cJ\cK\cL\cM\cN\cO\cP\cQ\cR\cS\cT\cU\cV\cW\cX\cY\cZ' = $'\001\002\003\004\005\006\007\010\011\012\013\014\015\016\017\020\021\022\023\024\025\026\027\030\031\032' ] +[ $'\c[' = $'\033' ] +[ $'\c]' = $'\035' ] +[ $'\c^' = $'\036' ] +[ $'\c_' = $'\037' ] diff --git a/shell_cmds/sh/tests/parser/dollar-quote6.0 b/shell_cmds/sh/tests/parser/dollar-quote6.0 new file mode 100644 index 0000000..a4b1e3f --- /dev/null +++ b/shell_cmds/sh/tests/parser/dollar-quote6.0 @@ -0,0 +1,5 @@ +# $FreeBSD$ + +# This depends on the ASCII character set. + +[ $'\c\\' = $'\034' ] diff --git a/shell_cmds/sh/tests/parser/dollar-quote7.0 b/shell_cmds/sh/tests/parser/dollar-quote7.0 new file mode 100644 index 0000000..c866b1a --- /dev/null +++ b/shell_cmds/sh/tests/parser/dollar-quote7.0 @@ -0,0 +1,6 @@ +# $FreeBSD$ + +set -e + +[ $'\u0024\u0040\u0060' = '$@`' ] +[ $'\U00000024\U00000040\U00000060' = '$@`' ] diff --git a/shell_cmds/sh/tests/parser/dollar-quote8.0 b/shell_cmds/sh/tests/parser/dollar-quote8.0 new file mode 100644 index 0000000..8f0b41a --- /dev/null +++ b/shell_cmds/sh/tests/parser/dollar-quote8.0 @@ -0,0 +1,11 @@ +# $FreeBSD$ + +[ $'hello\0' = hello ] +[ $'hello\0world' = hello ] +[ $'hello\0'$'world' = helloworld ] +[ $'hello\000' = hello ] +[ $'hello\000world' = hello ] +[ $'hello\000'$'world' = helloworld ] +[ $'hello\x00' = hello ] +[ $'hello\x00world' = hello ] +[ $'hello\x00'$'world' = helloworld ] diff --git a/shell_cmds/sh/tests/parser/dollar-quote9.0 b/shell_cmds/sh/tests/parser/dollar-quote9.0 new file mode 100644 index 0000000..df64b7d --- /dev/null +++ b/shell_cmds/sh/tests/parser/dollar-quote9.0 @@ -0,0 +1,8 @@ +# $FreeBSD$ + +# POSIX and C99 say D800-DFFF are undefined in a universal character name. +# We reject this but many other shells expand to something that looks like +# CESU-8. + +v=$( (eval ": \$'\uD800'") 2>&1 >/dev/null) +[ $? -ne 0 ] && [ -n "$v" ] diff --git a/shell_cmds/sh/tests/parser/empty-braces1.0 b/shell_cmds/sh/tests/parser/empty-braces1.0 new file mode 100644 index 0000000..4aac806 --- /dev/null +++ b/shell_cmds/sh/tests/parser/empty-braces1.0 @@ -0,0 +1,7 @@ +# $FreeBSD: head/bin/sh/tests/parser/empty-braces1.0 245382 2013-01-13 19:26:33Z jilles $ + +# Unfortunately, some scripts depend on the extension of allowing an empty +# pair of braces. + +{ } & +wait $! diff --git a/shell_cmds/sh/tests/parser/empty-cmd1.0 b/shell_cmds/sh/tests/parser/empty-cmd1.0 new file mode 100644 index 0000000..60aaa00 --- /dev/null +++ b/shell_cmds/sh/tests/parser/empty-cmd1.0 @@ -0,0 +1,3 @@ +# $FreeBSD: head/bin/sh/tests/parser/empty-cmd1.0 254843 2013-08-25 10:57:48Z jilles $ + +! (eval ': || f()') 2>/dev/null diff --git a/shell_cmds/sh/tests/parser/for1.0 b/shell_cmds/sh/tests/parser/for1.0 new file mode 100644 index 0000000..d863f0a --- /dev/null +++ b/shell_cmds/sh/tests/parser/for1.0 @@ -0,0 +1,29 @@ +# $FreeBSD: head/bin/sh/tests/parser/for1.0 218889 2011-02-20 14:18:58Z jilles $ + +nl=' +' +list=' a b c' +for s1 in "$nl" " "; do + for s2 in "$nl" ";" ";$nl"; do + for s3 in "$nl" " "; do + r='' + eval "for i${s1}in ${list}${s2}do${s3}r=\"\$r \$i\"; done" + [ "$r" = "$list" ] || exit 1 + done + done +done +set -- $list +for s2 in "$nl" " "; do + for s3 in "$nl" " "; do + r='' + eval "for i${s2}do${s3}r=\"\$r \$i\"; done" + [ "$r" = "$list" ] || exit 1 + done +done +for s1 in "$nl" " "; do + for s2 in "$nl" ";" ";$nl"; do + for s3 in "$nl" " "; do + eval "for i${s1}in${s2}do${s3}exit 1; done" + done + done +done diff --git a/shell_cmds/sh/tests/parser/for2.0 b/shell_cmds/sh/tests/parser/for2.0 new file mode 100644 index 0000000..8ed92dc --- /dev/null +++ b/shell_cmds/sh/tests/parser/for2.0 @@ -0,0 +1,15 @@ +# $FreeBSD: head/bin/sh/tests/parser/for2.0 218889 2011-02-20 14:18:58Z jilles $ + +# Common extensions to the 'for' syntax. + +nl=' +' +list=' a b c' +set -- $list +for s2 in ";" ";$nl"; do + for s3 in "$nl" " "; do + r='' + eval "for i${s2}do${s3}r=\"\$r \$i\"; done" + [ "$r" = "$list" ] || exit 1 + done +done diff --git a/shell_cmds/sh/tests/parser/func1.0 b/shell_cmds/sh/tests/parser/func1.0 new file mode 100644 index 0000000..4e82da4 --- /dev/null +++ b/shell_cmds/sh/tests/parser/func1.0 @@ -0,0 +1,25 @@ +# $FreeBSD: head/bin/sh/tests/parser/func1.0 214291 2010-10-24 20:45:13Z jilles $ +# POSIX does not require these bytes to work in function names, +# but making them all work seems a good goal. + +failures=0 +unset LC_ALL +export LC_CTYPE=en_US.ISO8859-1 +i=128 +set -f +while [ "$i" -le 255 ]; do + c=$(printf \\"$(printf %o "$i")") + ok=0 + eval "$c() { ok=1; }" + $c + ok1=$ok + ok=0 + "$c" + if [ "$ok" != 1 ] || [ "$ok1" != 1 ]; then + echo "Bad results for character $i" >&2 + : $((failures += 1)) + fi + unset -f $c + i=$((i+1)) +done +exit $((failures > 0)) diff --git a/shell_cmds/sh/tests/parser/func2.0 b/shell_cmds/sh/tests/parser/func2.0 new file mode 100644 index 0000000..5b86522 --- /dev/null +++ b/shell_cmds/sh/tests/parser/func2.0 @@ -0,0 +1,6 @@ +# $FreeBSD: head/bin/sh/tests/parser/func2.0 222512 2011-05-30 21:49:59Z jilles $ + +f() { return 42; } +f() { return 3; } & +f +[ $? -eq 42 ] diff --git a/shell_cmds/sh/tests/parser/func3.0 b/shell_cmds/sh/tests/parser/func3.0 new file mode 100644 index 0000000..c4f4922 --- /dev/null +++ b/shell_cmds/sh/tests/parser/func3.0 @@ -0,0 +1,6 @@ +# $FreeBSD: head/bin/sh/tests/parser/func3.0 222512 2011-05-30 21:49:59Z jilles $ + +name=/var/empty/nosuch +f() { true; } <$name +name=/dev/null +f diff --git a/shell_cmds/sh/tests/parser/heredoc1.0 b/shell_cmds/sh/tests/parser/heredoc1.0 new file mode 100644 index 0000000..4020249 --- /dev/null +++ b/shell_cmds/sh/tests/parser/heredoc1.0 @@ -0,0 +1,85 @@ +# $FreeBSD: head/bin/sh/tests/parser/heredoc1.0 204836 2010-03-07 15:08:42Z jilles $ + +failures=0 + +check() { + if ! eval "[ $* ]"; then + echo "Failed: $*" + : $((failures += 1)) + fi +} + +check '"$(cat <<EOF +hi +EOF +)" = hi' + +check '"$(cat <<EOF +${$+hi} +EOF +)" = hi' + +unset yy +check '"$(cat <<EOF +${yy-hi} +EOF +)" = hi' + +check '"$(cat <<EOF +${$+hi +there} +EOF +)" = "hi +there"' + +check '"$(cat <<EOF +$((1+1)) +EOF +)" = 2' + +check '"$(cat <<EOF +$(echo hi) +EOF +)" = hi' + +check '"$(cat <<EOF +`echo hi` +EOF +)" = hi' + +check '"$(cat <<\EOF +${$+hi} +EOF +)" = "\${\$+hi}"' + +check '"$(cat <<\EOF +$( +EOF +)" = \$\(' + +check '"$(cat <<\EOF +` +EOF +)" = \`' + +check '"$(cat <<EOF +" +EOF +)" = \"' + +check '"$(cat <<\EOF +" +EOF +)" = \"' + +check '"$(cat <<esac +'"'"' +esac +)" = "'"'"'"' + +check '"$(cat <<\) +'"'"' +) +)" = "'"'"'"' + +exit $((failures != 0)) diff --git a/shell_cmds/sh/tests/parser/heredoc10.0 b/shell_cmds/sh/tests/parser/heredoc10.0 new file mode 100644 index 0000000..f5133b9 --- /dev/null +++ b/shell_cmds/sh/tests/parser/heredoc10.0 @@ -0,0 +1,49 @@ +# $FreeBSD: head/bin/sh/tests/parser/heredoc10.0 221887 2011-05-14 14:19:30Z jilles $ + +# It may be argued that +# x=$(cat <<EOF +# foo +# EOF) +# is a valid complete command that sets x to foo, because +# cat <<EOF +# foo +# EOF +# is a valid script even without the final newline. +# However, if the here-document is not within a new-style command substitution +# or there are other constructs nested inside the command substitution that +# need terminators, the delimiter at the start of a line followed by a close +# parenthesis is clearly a literal part of the here-document. + +# This file contains tests that may not work with simplistic $(...) parsers. +# The open parentheses in comments help mksh, but not zsh. + +failures=0 + +check() { + if ! eval "[ $* ]"; then + echo "Failed: $*" + : $((failures += 1)) + fi +} + +check '"$(cat <<EOF # ( +EOF ) +EOF +)" = "EOF )"' + +check '"$({ cat <<EOF # ( +EOF) +EOF +})" = "EOF)"' + +check '"$(if :; then cat <<EOF # ( +EOF) +EOF +fi)" = "EOF)"' + +check '"$( (cat <<EOF # ( +EOF) +EOF +))" = "EOF)"' + +exit $((failures != 0)) diff --git a/shell_cmds/sh/tests/parser/heredoc11.0 b/shell_cmds/sh/tests/parser/heredoc11.0 new file mode 100644 index 0000000..3e74aea --- /dev/null +++ b/shell_cmds/sh/tests/parser/heredoc11.0 @@ -0,0 +1,26 @@ +# $FreeBSD: head/bin/sh/tests/parser/heredoc11.0 222134 2011-05-20 16:03:36Z jilles $ + +failures='' + +check() { + if eval "[ $* ]"; then + : + else + echo "Failed: $*" + failures=x$failures + fi +} + +check '`cat <<EOF +foo +EOF` = foo' + +check '"`cat <<EOF +foo +EOF`" = foo' + +check '`eval "cat <<EOF +foo +EOF"` = foo' + +test "x$failures" = x diff --git a/shell_cmds/sh/tests/parser/heredoc12.0 b/shell_cmds/sh/tests/parser/heredoc12.0 new file mode 100644 index 0000000..644cae1 --- /dev/null +++ b/shell_cmds/sh/tests/parser/heredoc12.0 @@ -0,0 +1,47 @@ +# $FreeBSD: head/bin/sh/tests/parser/heredoc12.0 271593 2014-09-14 16:46:30Z jilles $ + +failures=0 + +check() { + if ! eval "[ $* ]"; then + echo "Failed: $*" + : $((failures += 1)) + fi +} + +longmark=`printf %01000d 4` +longmarkstripped=`printf %0999d 0` + +check '"$(cat <<'"$longmark +$longmark"' +echo yes)" = "yes"' + +check '"$(cat <<\'"$longmark +$longmark"' +echo yes)" = "yes"' + +check '"$(cat <<'"$longmark +yes +$longmark"' +)" = "yes"' + +check '"$(cat <<\'"$longmark +yes +$longmark"' +)" = "yes"' + +check '"$(cat <<'"$longmark +$longmarkstripped +$longmark. +$longmark"' +)" = "'"$longmarkstripped +$longmark."'"' + +check '"$(cat <<\'"$longmark +$longmarkstripped +$longmark. +$longmark"' +)" = "'"$longmarkstripped +$longmark."'"' + +exit $((failures != 0)) diff --git a/shell_cmds/sh/tests/parser/heredoc13.0 b/shell_cmds/sh/tests/parser/heredoc13.0 new file mode 100644 index 0000000..4eb45e0 --- /dev/null +++ b/shell_cmds/sh/tests/parser/heredoc13.0 @@ -0,0 +1,21 @@ +# $FreeBSD: head/bin/sh/tests/parser/heredoc13.0 287408 2015-09-02 19:49:55Z jilles $ + +failures=0 + +check() { + if ! eval "[ $* ]"; then + echo "Failed: $*" + : $((failures += 1)) + fi +} + +check '"$(cat <<"" + +echo yes)" = "yes"' + +check '"$(cat <<"" +yes + +)" = "yes"' + +exit $((failures != 0)) diff --git a/shell_cmds/sh/tests/parser/heredoc2.0 b/shell_cmds/sh/tests/parser/heredoc2.0 new file mode 100644 index 0000000..f07f5d4 --- /dev/null +++ b/shell_cmds/sh/tests/parser/heredoc2.0 @@ -0,0 +1,39 @@ +# $FreeBSD: head/bin/sh/tests/parser/heredoc2.0 211405 2010-08-16 21:14:49Z jilles $ + +failures=0 + +check() { + if ! eval "[ $* ]"; then + echo "Failed: $*" + : $((failures += 1)) + fi +} + +s='ast*que?non' sq=\' dq=\" + +check '"$(cat <<EOF +${s} +EOF +)" = "ast*que?non"' + +check '"$(cat <<EOF +${s+'$sq'x'$sq'} +EOF +)" = ${sq}x${sq}' + +check '"$(cat <<EOF +${s#ast} +EOF +)" = "*que?non"' + +check '"$(cat <<EOF +${s##"ast"} +EOF +)" = "*que?non"' + +check '"$(cat <<EOF +${s##'$sq'ast'$sq'} +EOF +)" = "*que?non"' + +exit $((failures != 0)) diff --git a/shell_cmds/sh/tests/parser/heredoc3.0 b/shell_cmds/sh/tests/parser/heredoc3.0 new file mode 100644 index 0000000..15c0a1c --- /dev/null +++ b/shell_cmds/sh/tests/parser/heredoc3.0 @@ -0,0 +1,7 @@ +# $FreeBSD: head/bin/sh/tests/parser/heredoc3.0 207824 2010-05-09 17:10:50Z jilles $ + +# This may be expected to work, but pretty much only ash derivatives allow it. + +test "$(cat <<EOF)" = "hi there" +hi there +EOF diff --git a/shell_cmds/sh/tests/parser/heredoc4.0 b/shell_cmds/sh/tests/parser/heredoc4.0 new file mode 100644 index 0000000..a544354 --- /dev/null +++ b/shell_cmds/sh/tests/parser/heredoc4.0 @@ -0,0 +1,44 @@ +# $FreeBSD: head/bin/sh/tests/parser/heredoc4.0 208655 2010-05-30 14:11:27Z jilles $ + +failures=0 + +check() { + if ! eval "[ $* ]"; then + echo "Failed: $*" + : $((failures += 1)) + fi +} + +f() { + cat <<EOF && echo `echo bar` +foo +EOF +} +check '"`f`" = "foo +bar"' + +f() { + cat <<EOF && echo $(echo bar) +foo +EOF +} +check '"$(f)" = "foo +bar"' + +f() { + echo `echo bar` && cat <<EOF +foo +EOF +} +check '"`f`" = "bar +foo"' + +f() { + echo $(echo bar) && cat <<EOF +foo +EOF +} +check '"$(f)" = "bar +foo"' + +exit $((failures != 0)) diff --git a/shell_cmds/sh/tests/parser/heredoc5.0 b/shell_cmds/sh/tests/parser/heredoc5.0 new file mode 100644 index 0000000..0bcc617 --- /dev/null +++ b/shell_cmds/sh/tests/parser/heredoc5.0 @@ -0,0 +1,56 @@ +# $FreeBSD: head/bin/sh/tests/parser/heredoc5.0 208655 2010-05-30 14:11:27Z jilles $ + +failures=0 + +check() { + if ! eval "[ $* ]"; then + echo "Failed: $*" + : $((failures += 1)) + fi +} + +f() { + cat <<EOF && echo `cat <<EOF +bar +EOF +` +foo +EOF +} +check '"`f`" = "foo +bar"' + +f() { + cat <<EOF && echo $(cat <<EOF +bar +EOF +) +foo +EOF +} +check '"$(f)" = "foo +bar"' + +f() { + echo `cat <<EOF +bar +EOF +` && cat <<EOF +foo +EOF +} +check '"`f`" = "bar +foo"' + +f() { + echo $(cat <<EOF +bar +EOF +) && cat <<EOF +foo +EOF +} +check '"$(f)" = "bar +foo"' + +exit $((failures != 0)) diff --git a/shell_cmds/sh/tests/parser/heredoc6.0 b/shell_cmds/sh/tests/parser/heredoc6.0 new file mode 100644 index 0000000..003f548 --- /dev/null +++ b/shell_cmds/sh/tests/parser/heredoc6.0 @@ -0,0 +1,5 @@ +# $FreeBSD: head/bin/sh/tests/parser/heredoc6.0 208656 2010-05-30 14:20:32Z jilles $ + +r= +! command eval ": <<EOF; )" 2>/dev/null; command eval : hi \${r:=0} +exit ${r:-3} diff --git a/shell_cmds/sh/tests/parser/heredoc7.0 b/shell_cmds/sh/tests/parser/heredoc7.0 new file mode 100644 index 0000000..28d867c --- /dev/null +++ b/shell_cmds/sh/tests/parser/heredoc7.0 @@ -0,0 +1,19 @@ +# $FreeBSD: head/bin/sh/tests/parser/heredoc7.0 210488 2010-07-25 22:25:52Z jilles $ + +# Some of these created malformed parse trees with null pointers for here +# documents, causing the here document writing process to segfault. +eval ': <<EOF' +eval ': <<EOF;' +eval '`: <<EOF`' +eval '`: <<EOF;`' +eval '`: <<EOF`;' +eval '`: <<EOF;`;' + +# Some of these created malformed parse trees with null pointers for here +# documents, causing sh to segfault. +eval ': <<\EOF' +eval ': <<\EOF;' +eval '`: <<\EOF`' +eval '`: <<\EOF;`' +eval '`: <<\EOF`;' +eval '`: <<\EOF;`;' diff --git a/shell_cmds/sh/tests/parser/heredoc8.0 b/shell_cmds/sh/tests/parser/heredoc8.0 new file mode 100644 index 0000000..482c060 --- /dev/null +++ b/shell_cmds/sh/tests/parser/heredoc8.0 @@ -0,0 +1,20 @@ +# $FreeBSD: head/bin/sh/tests/parser/heredoc8.0 211405 2010-08-16 21:14:49Z jilles $ + +failures=0 + +check() { + if ! eval "[ $* ]"; then + echo "Failed: $*" + : $((failures += 1)) + fi +} + +s='ast*que?non' sq=\' dq=\" + +# This is possibly useful but differs from other shells. +check '"$(cat <<EOF +${s+"x"} +EOF +)" = ${dq}x${dq}' + +exit $((failures != 0)) diff --git a/shell_cmds/sh/tests/parser/heredoc9.0 b/shell_cmds/sh/tests/parser/heredoc9.0 new file mode 100644 index 0000000..bb4a04c --- /dev/null +++ b/shell_cmds/sh/tests/parser/heredoc9.0 @@ -0,0 +1,58 @@ +# $FreeBSD: head/bin/sh/tests/parser/heredoc9.0 221887 2011-05-14 14:19:30Z jilles $ + +# It may be argued that +# x=$(cat <<EOF +# foo +# EOF) +# is a valid complete command that sets x to foo, because +# cat <<EOF +# foo +# EOF +# is a valid script even without the final newline. +# However, if the here-document is not within a new-style command substitution +# or there are other constructs nested inside the command substitution that +# need terminators, the delimiter at the start of a line followed by a close +# parenthesis is clearly a literal part of the here-document. + +# This file contains tests that also work with simplistic $(...) parsers. + +failures=0 + +check() { + if ! eval "[ $* ]"; then + echo "Failed: $*" + : $((failures += 1)) + fi +} + +check '`${SH} -c "cat <<EOF +EOF) +EOF +"` = "EOF)"' + +check '`${SH} -c "(cat <<EOF +EOF) +EOF +)"` = "EOF)"' + +check '"`cat <<EOF +EOF x +EOF +`" = "EOF x"' + +check '"`cat <<EOF +EOF ) +EOF +`" = "EOF )"' + +check '"`cat <<EOF +EOF) +EOF +`" = "EOF)"' + +check '"$(cat <<EOF +EOF x +EOF +)" = "EOF x"' + +exit $((failures != 0)) diff --git a/shell_cmds/sh/tests/parser/line-cont1.0 b/shell_cmds/sh/tests/parser/line-cont1.0 new file mode 100644 index 0000000..717c2c2 --- /dev/null +++ b/shell_cmds/sh/tests/parser/line-cont1.0 @@ -0,0 +1,16 @@ +# $FreeBSD: head/bin/sh/tests/parser/line-cont1.0 273243 2014-10-17 21:52:57Z jilles $ + +i\ +f +t\ +r\ +u\ +e +t\ +h\ +e\ +n +: +\ +f\ +i diff --git a/shell_cmds/sh/tests/parser/line-cont10.0 b/shell_cmds/sh/tests/parser/line-cont10.0 new file mode 100644 index 0000000..dbb064b --- /dev/null +++ b/shell_cmds/sh/tests/parser/line-cont10.0 @@ -0,0 +1,18 @@ +# $FreeBSD: head/bin/sh/tests/parser/line-cont10.0 273276 2014-10-19 11:59:15Z jilles $ + +v=XaaaXbbbX +[ "${v\ +#\ +*\ +a}.${v\ +#\ +#\ +*\ +a}.${v\ +%\ +b\ +*}.${v\ +%\ +%\ +b\ +*}" = aaXbbbX.XbbbX.XaaaXbb.XaaaX ] diff --git a/shell_cmds/sh/tests/parser/line-cont11.0 b/shell_cmds/sh/tests/parser/line-cont11.0 new file mode 100644 index 0000000..f398a98 --- /dev/null +++ b/shell_cmds/sh/tests/parser/line-cont11.0 @@ -0,0 +1,23 @@ +# $FreeBSD: head/bin/sh/tests/parser/line-cont11.0 273276 2014-10-19 11:59:15Z jilles $ + +T=$(mktemp "${TMPDIR:-/tmp}/sh-test.XXXXXXXX") || exit +trap 'rm -f -- "$T"' 0 +w='#A' +# A naive pgetc_linecont() would push back two characters here, which +# fails if a new buffer is read between the two characters. +c='${w#\#}' +c=$c$c$c$c +c=$c$c$c$c +c=$c$c$c$c +c=$c$c$c$c +c=$c$c$c$c +c=$c$c$c$c +printf 'v=%s\n' "$c" >"$T" +. "$T" +if [ "${#v}" != 4096 ]; then + echo "Length is bad (${#v})" + exit 3 +fi +case $v in +*[!A]*) echo "Content is bad"; exit 3 ;; +esac diff --git a/shell_cmds/sh/tests/parser/line-cont2.0 b/shell_cmds/sh/tests/parser/line-cont2.0 new file mode 100644 index 0000000..83928cf --- /dev/null +++ b/shell_cmds/sh/tests/parser/line-cont2.0 @@ -0,0 +1,4 @@ +# $FreeBSD: head/bin/sh/tests/parser/line-cont2.0 273243 2014-10-17 21:52:57Z jilles $ + +[ "a\ +b" = ab ] diff --git a/shell_cmds/sh/tests/parser/line-cont3.0 b/shell_cmds/sh/tests/parser/line-cont3.0 new file mode 100644 index 0000000..cc2af7b --- /dev/null +++ b/shell_cmds/sh/tests/parser/line-cont3.0 @@ -0,0 +1,7 @@ +# $FreeBSD: head/bin/sh/tests/parser/line-cont3.0 273243 2014-10-17 21:52:57Z jilles $ + +v=`printf %s 'a\ +b'` +w="`printf %s 'c\ +d'`" +[ "$v$w" = abcd ] diff --git a/shell_cmds/sh/tests/parser/line-cont4.0 b/shell_cmds/sh/tests/parser/line-cont4.0 new file mode 100644 index 0000000..7d59c19 --- /dev/null +++ b/shell_cmds/sh/tests/parser/line-cont4.0 @@ -0,0 +1,8 @@ +# $FreeBSD: head/bin/sh/tests/parser/line-cont4.0 273276 2014-10-19 11:59:15Z jilles $ + +v=abcd +[ "$\ +v.$\ +{v}.${\ +v}.${v\ +}" = abcd.abcd.abcd.abcd ] diff --git a/shell_cmds/sh/tests/parser/line-cont5.0 b/shell_cmds/sh/tests/parser/line-cont5.0 new file mode 100644 index 0000000..23a4d94 --- /dev/null +++ b/shell_cmds/sh/tests/parser/line-cont5.0 @@ -0,0 +1,14 @@ +# $FreeBSD: head/bin/sh/tests/parser/line-cont5.0 273276 2014-10-19 11:59:15Z jilles $ + +bad=1 +case x in +x\ +) ;\ +; *) exit 7 +esac &\ +& bad= &\ +& : >\ +>/dev/null + +false |\ +| [ -z "$bad" ] diff --git a/shell_cmds/sh/tests/parser/line-cont6.0 b/shell_cmds/sh/tests/parser/line-cont6.0 new file mode 100644 index 0000000..66838e1 --- /dev/null +++ b/shell_cmds/sh/tests/parser/line-cont6.0 @@ -0,0 +1,23 @@ +# $FreeBSD: head/bin/sh/tests/parser/line-cont6.0 273276 2014-10-19 11:59:15Z jilles $ + +v0\ +=abc + +v=$(cat <\ +<\ +E\ +O\ +F +${v0}d +EOF +) + +w=$(cat <\ +<\ +-\ +EOF + efgh +EOF +) + +[ "$v.$w" = "abcd.efgh" ] diff --git a/shell_cmds/sh/tests/parser/line-cont7.0 b/shell_cmds/sh/tests/parser/line-cont7.0 new file mode 100644 index 0000000..6c48642 --- /dev/null +++ b/shell_cmds/sh/tests/parser/line-cont7.0 @@ -0,0 +1,7 @@ +# $FreeBSD: head/bin/sh/tests/parser/line-cont7.0 273276 2014-10-19 11:59:15Z jilles $ + +[ "$(\ +( +1\ ++ 1)\ +)" = 2 ] diff --git a/shell_cmds/sh/tests/parser/line-cont8.0 b/shell_cmds/sh/tests/parser/line-cont8.0 new file mode 100644 index 0000000..8fc84bb --- /dev/null +++ b/shell_cmds/sh/tests/parser/line-cont8.0 @@ -0,0 +1,6 @@ +# $FreeBSD: head/bin/sh/tests/parser/line-cont8.0 273276 2014-10-19 11:59:15Z jilles $ + +set -- a b c d e f g h i j +[ "${1\ +0\ +}" = j ] diff --git a/shell_cmds/sh/tests/parser/line-cont9.0 b/shell_cmds/sh/tests/parser/line-cont9.0 new file mode 100644 index 0000000..9618ae2 --- /dev/null +++ b/shell_cmds/sh/tests/parser/line-cont9.0 @@ -0,0 +1,6 @@ +# $FreeBSD: head/bin/sh/tests/parser/line-cont9.0 273276 2014-10-19 11:59:15Z jilles $ + +[ "${$\ +:\ ++\ +xyz}" = xyz ] diff --git a/shell_cmds/sh/tests/parser/no-space1.0 b/shell_cmds/sh/tests/parser/no-space1.0 new file mode 100644 index 0000000..5b15bfe --- /dev/null +++ b/shell_cmds/sh/tests/parser/no-space1.0 @@ -0,0 +1,18 @@ +# $FreeBSD: head/bin/sh/tests/parser/no-space1.0 218891 2011-02-20 17:28:58Z jilles $ + +# These are ugly but are required to work. + +set -e + +while(false)do(:)done +if(false)then(:)fi +if(false)then(:)else(:)fi +(:&&:)||: +until(:)do(:)done +case x in(x);;*)exit 1;(:)esac +case x in(x);;*)exit 1;;esac +for i do(:)done +{(:)} +f(){(:)} +:|: +(:)|(:) diff --git a/shell_cmds/sh/tests/parser/no-space2.0 b/shell_cmds/sh/tests/parser/no-space2.0 new file mode 100644 index 0000000..0f63e6e --- /dev/null +++ b/shell_cmds/sh/tests/parser/no-space2.0 @@ -0,0 +1,7 @@ +# $FreeBSD: head/bin/sh/tests/parser/no-space2.0 218891 2011-02-20 17:28:58Z jilles $ + +# This conflicts with ksh extended patterns but occurs in the wild. + +set -e + +!(false) diff --git a/shell_cmds/sh/tests/parser/nul1.0 b/shell_cmds/sh/tests/parser/nul1.0 new file mode 100644 index 0000000..bb5442e --- /dev/null +++ b/shell_cmds/sh/tests/parser/nul1.0 @@ -0,0 +1,12 @@ +# $FreeBSD: head/bin/sh/tests/parser/nul1.0 295825 2016-02-19 21:53:12Z jilles $ +# Although POSIX does not specify the effect of NUL bytes in scripts, +# we ignore them. + +{ + printf 'v=%03000d\0%02000d' 7 2 + dd if=/dev/zero bs=1000 count=1 status=none + printf '1 w=%03000d%02000d1\0\n' 7 2 + printf '\0l\0v\0=\0$\0{\0#\0v\0}\n' + printf '\0l\0w\0=\0\0$\0{\0#\0w}\0\0\0\n' + printf '[ "$lv.$lw.$v" = "5001.5001.$w" ]\n' +} | ${SH} diff --git a/shell_cmds/sh/tests/parser/only-redir1.0 b/shell_cmds/sh/tests/parser/only-redir1.0 new file mode 100644 index 0000000..7aedde7 --- /dev/null +++ b/shell_cmds/sh/tests/parser/only-redir1.0 @@ -0,0 +1,3 @@ +# $FreeBSD: head/bin/sh/tests/parser/only-redir1.0 210221 2010-07-18 12:45:31Z jilles $ +</dev/null & +wait $! diff --git a/shell_cmds/sh/tests/parser/only-redir2.0 b/shell_cmds/sh/tests/parser/only-redir2.0 new file mode 100644 index 0000000..b55be16 --- /dev/null +++ b/shell_cmds/sh/tests/parser/only-redir2.0 @@ -0,0 +1,2 @@ +# $FreeBSD: head/bin/sh/tests/parser/only-redir2.0 254335 2013-08-14 19:34:13Z jilles $ +</dev/null | : diff --git a/shell_cmds/sh/tests/parser/only-redir3.0 b/shell_cmds/sh/tests/parser/only-redir3.0 new file mode 100644 index 0000000..f51617b --- /dev/null +++ b/shell_cmds/sh/tests/parser/only-redir3.0 @@ -0,0 +1,2 @@ +# $FreeBSD: head/bin/sh/tests/parser/only-redir3.0 254335 2013-08-14 19:34:13Z jilles $ +case x in x) </dev/null ;; esac diff --git a/shell_cmds/sh/tests/parser/only-redir4.0 b/shell_cmds/sh/tests/parser/only-redir4.0 new file mode 100644 index 0000000..a3ce893 --- /dev/null +++ b/shell_cmds/sh/tests/parser/only-redir4.0 @@ -0,0 +1,2 @@ +# $FreeBSD: head/bin/sh/tests/parser/only-redir4.0 254335 2013-08-14 19:34:13Z jilles $ +case x in x) </dev/null ;& esac diff --git a/shell_cmds/sh/tests/parser/pipe-not1.0 b/shell_cmds/sh/tests/parser/pipe-not1.0 new file mode 100644 index 0000000..421e235 --- /dev/null +++ b/shell_cmds/sh/tests/parser/pipe-not1.0 @@ -0,0 +1,3 @@ +# $FreeBSD: head/bin/sh/tests/parser/pipe-not1.0 214281 2010-10-24 17:06:49Z jilles $ + +: | ! : | false diff --git a/shell_cmds/sh/tests/parser/set-v1.0 b/shell_cmds/sh/tests/parser/set-v1.0 new file mode 100644 index 0000000..c5ed944 --- /dev/null +++ b/shell_cmds/sh/tests/parser/set-v1.0 @@ -0,0 +1,8 @@ +# $FreeBSD: head/bin/sh/tests/parser/set-v1.0 295937 2016-02-23 22:44:01Z jilles $ + +${SH} <<\EOF +echo one >&2 +set -v +echo two >&2 +echo three >&2 +EOF diff --git a/shell_cmds/sh/tests/parser/set-v1.0.stderr b/shell_cmds/sh/tests/parser/set-v1.0.stderr new file mode 100644 index 0000000..d904fa5 --- /dev/null +++ b/shell_cmds/sh/tests/parser/set-v1.0.stderr @@ -0,0 +1,5 @@ +one +echo two >&2 +two +echo three >&2 +three diff --git a/shell_cmds/sh/tests/parser/var-assign1.0 b/shell_cmds/sh/tests/parser/var-assign1.0 new file mode 100644 index 0000000..4949bfd --- /dev/null +++ b/shell_cmds/sh/tests/parser/var-assign1.0 @@ -0,0 +1,19 @@ +# $FreeBSD: head/bin/sh/tests/parser/var-assign1.0 257920 2013-11-10 18:46:59Z jilles $ +# In a variable assignment, both the name and the equals sign must be entirely +# unquoted. Therefore, there is only one assignment below; the other words +# containing equals signs are command words. + +abc=0 +\abc=1 2>/dev/null +a\bc=2 2>/dev/null +abc\=3 2>/dev/null +a\bc\=4 2>/dev/null +'abc'=5 2>/dev/null +a'b'c=6 2>/dev/null +abc'='7 2>/dev/null +'abc=8' 2>/dev/null +"abc"=9 2>/dev/null +a"b"c=10 2>/dev/null +abc"="11 2>/dev/null +"abc=12" 2>/dev/null +[ "$abc" = 0 ] diff --git a/shell_cmds/sh/tests/set-e/Makefile b/shell_cmds/sh/tests/set-e/Makefile new file mode 100644 index 0000000..2f13991 --- /dev/null +++ b/shell_cmds/sh/tests/set-e/Makefile @@ -0,0 +1,46 @@ +# $FreeBSD: head/bin/sh/tests/set-e/Makefile 299094 2016-05-04 23:20:53Z ngie $ + +PACKAGE= tests + +TESTSDIR= ${TESTSBASE}/bin/sh/${.CURDIR:T} + +.PATH: ${.CURDIR:H} +ATF_TESTS_SH= functional_test + +${PACKAGE}FILES+= and1.0 +${PACKAGE}FILES+= and2.1 +${PACKAGE}FILES+= and3.0 +${PACKAGE}FILES+= and4.0 +${PACKAGE}FILES+= background1.0 +${PACKAGE}FILES+= cmd1.0 +${PACKAGE}FILES+= cmd2.1 +${PACKAGE}FILES+= elif1.0 +${PACKAGE}FILES+= elif2.0 +${PACKAGE}FILES+= eval1.0 +${PACKAGE}FILES+= eval2.1 +${PACKAGE}FILES+= for1.0 +${PACKAGE}FILES+= func1.0 +${PACKAGE}FILES+= func2.1 +${PACKAGE}FILES+= if1.0 +${PACKAGE}FILES+= if2.0 +${PACKAGE}FILES+= if3.0 +${PACKAGE}FILES+= not1.0 +${PACKAGE}FILES+= not2.0 +${PACKAGE}FILES+= or1.0 +${PACKAGE}FILES+= or2.0 +${PACKAGE}FILES+= or3.1 +${PACKAGE}FILES+= pipe1.1 +${PACKAGE}FILES+= pipe2.0 +${PACKAGE}FILES+= return1.0 +${PACKAGE}FILES+= semi1.1 +${PACKAGE}FILES+= semi2.1 +${PACKAGE}FILES+= subshell1.0 +${PACKAGE}FILES+= subshell2.1 +${PACKAGE}FILES+= until1.0 +${PACKAGE}FILES+= until2.0 +${PACKAGE}FILES+= until3.0 +${PACKAGE}FILES+= while1.0 +${PACKAGE}FILES+= while2.0 +${PACKAGE}FILES+= while3.0 + +.include <bsd.test.mk> diff --git a/shell_cmds/sh/tests/set-e/Makefile.depend b/shell_cmds/sh/tests/set-e/Makefile.depend new file mode 100644 index 0000000..ea8dbbe --- /dev/null +++ b/shell_cmds/sh/tests/set-e/Makefile.depend @@ -0,0 +1,11 @@ +# $FreeBSD: head/bin/sh/tests/set-e/Makefile.depend 296587 2016-03-09 22:46:01Z bdrewery $ +# Autogenerated - do NOT edit! + +DIRDEPS = \ + + +.include <dirdeps.mk> + +.if ${DEP_RELDIR} == ${_DEP_RELDIR} +# local dependencies - needed for -jN in clean tree +.endif diff --git a/shell_cmds/sh/tests/set-e/and1.0 b/shell_cmds/sh/tests/set-e/and1.0 new file mode 100644 index 0000000..4518124 --- /dev/null +++ b/shell_cmds/sh/tests/set-e/and1.0 @@ -0,0 +1,3 @@ +# $FreeBSD: head/bin/sh/tests/set-e/and1.0 149781 2005-09-04 11:59:59Z stefanf $ +set -e +true && true diff --git a/shell_cmds/sh/tests/set-e/and2.1 b/shell_cmds/sh/tests/set-e/and2.1 new file mode 100644 index 0000000..2067f10 --- /dev/null +++ b/shell_cmds/sh/tests/set-e/and2.1 @@ -0,0 +1,4 @@ +# $FreeBSD: head/bin/sh/tests/set-e/and2.1 149781 2005-09-04 11:59:59Z stefanf $ +set -e +true && false +exit 0 diff --git a/shell_cmds/sh/tests/set-e/and3.0 b/shell_cmds/sh/tests/set-e/and3.0 new file mode 100644 index 0000000..59dccce --- /dev/null +++ b/shell_cmds/sh/tests/set-e/and3.0 @@ -0,0 +1,4 @@ +# $FreeBSD: head/bin/sh/tests/set-e/and3.0 149781 2005-09-04 11:59:59Z stefanf $ +set -e +false && true +exit 0 diff --git a/shell_cmds/sh/tests/set-e/and4.0 b/shell_cmds/sh/tests/set-e/and4.0 new file mode 100644 index 0000000..fd1cc72 --- /dev/null +++ b/shell_cmds/sh/tests/set-e/and4.0 @@ -0,0 +1,4 @@ +# $FreeBSD: head/bin/sh/tests/set-e/and4.0 149781 2005-09-04 11:59:59Z stefanf $ +set -e +false && false +exit 0 diff --git a/shell_cmds/sh/tests/set-e/background1.0 b/shell_cmds/sh/tests/set-e/background1.0 new file mode 100644 index 0000000..e31b7a3 --- /dev/null +++ b/shell_cmds/sh/tests/set-e/background1.0 @@ -0,0 +1,3 @@ +# $FreeBSD: head/bin/sh/tests/set-e/background1.0 149781 2005-09-04 11:59:59Z stefanf $ +set -e +false & diff --git a/shell_cmds/sh/tests/set-e/cmd1.0 b/shell_cmds/sh/tests/set-e/cmd1.0 new file mode 100644 index 0000000..7c6c843 --- /dev/null +++ b/shell_cmds/sh/tests/set-e/cmd1.0 @@ -0,0 +1,3 @@ +# $FreeBSD: head/bin/sh/tests/set-e/cmd1.0 149781 2005-09-04 11:59:59Z stefanf $ +set -e +true diff --git a/shell_cmds/sh/tests/set-e/cmd2.1 b/shell_cmds/sh/tests/set-e/cmd2.1 new file mode 100644 index 0000000..05a4243 --- /dev/null +++ b/shell_cmds/sh/tests/set-e/cmd2.1 @@ -0,0 +1,4 @@ +# $FreeBSD: head/bin/sh/tests/set-e/cmd2.1 149781 2005-09-04 11:59:59Z stefanf $ +set -e +false +exit 0 diff --git a/shell_cmds/sh/tests/set-e/elif1.0 b/shell_cmds/sh/tests/set-e/elif1.0 new file mode 100644 index 0000000..38ccaec --- /dev/null +++ b/shell_cmds/sh/tests/set-e/elif1.0 @@ -0,0 +1,7 @@ +# $FreeBSD: head/bin/sh/tests/set-e/elif1.0 211399 2010-08-16 17:18:08Z jilles $ +set -e +if false; then + : +elif false; then + : +fi diff --git a/shell_cmds/sh/tests/set-e/elif2.0 b/shell_cmds/sh/tests/set-e/elif2.0 new file mode 100644 index 0000000..694b3f0 --- /dev/null +++ b/shell_cmds/sh/tests/set-e/elif2.0 @@ -0,0 +1,7 @@ +# $FreeBSD: head/bin/sh/tests/set-e/elif2.0 211399 2010-08-16 17:18:08Z jilles $ +set -e +if false; then + : +elif false; false; then + : +fi diff --git a/shell_cmds/sh/tests/set-e/eval1.0 b/shell_cmds/sh/tests/set-e/eval1.0 new file mode 100644 index 0000000..cefaf91 --- /dev/null +++ b/shell_cmds/sh/tests/set-e/eval1.0 @@ -0,0 +1,3 @@ +# $FreeBSD: head/bin/sh/tests/set-e/eval1.0 193178 2009-05-31 17:23:27Z stefanf $ +set -e +eval false || true diff --git a/shell_cmds/sh/tests/set-e/eval2.1 b/shell_cmds/sh/tests/set-e/eval2.1 new file mode 100644 index 0000000..e396e1c --- /dev/null +++ b/shell_cmds/sh/tests/set-e/eval2.1 @@ -0,0 +1,4 @@ +# $FreeBSD: head/bin/sh/tests/set-e/eval2.1 193178 2009-05-31 17:23:27Z stefanf $ +set -e +eval false +exit 0 diff --git a/shell_cmds/sh/tests/set-e/for1.0 b/shell_cmds/sh/tests/set-e/for1.0 new file mode 100644 index 0000000..d0f7d8f --- /dev/null +++ b/shell_cmds/sh/tests/set-e/for1.0 @@ -0,0 +1,9 @@ +# $FreeBSD: head/bin/sh/tests/set-e/for1.0 149791 2005-09-05 09:42:10Z stefanf $ +set -e +f() { + for i in a b c; do + false + true + done +} +f || true diff --git a/shell_cmds/sh/tests/set-e/func1.0 b/shell_cmds/sh/tests/set-e/func1.0 new file mode 100644 index 0000000..c76d689 --- /dev/null +++ b/shell_cmds/sh/tests/set-e/func1.0 @@ -0,0 +1,7 @@ +# $FreeBSD: head/bin/sh/tests/set-e/func1.0 149791 2005-09-05 09:42:10Z stefanf $ +set -e +f() { + false + true +} +f || true diff --git a/shell_cmds/sh/tests/set-e/func2.1 b/shell_cmds/sh/tests/set-e/func2.1 new file mode 100644 index 0000000..267966d --- /dev/null +++ b/shell_cmds/sh/tests/set-e/func2.1 @@ -0,0 +1,7 @@ +# $FreeBSD: head/bin/sh/tests/set-e/func2.1 149791 2005-09-05 09:42:10Z stefanf $ +set -e +f() { + false + exit 0 +} +f diff --git a/shell_cmds/sh/tests/set-e/if1.0 b/shell_cmds/sh/tests/set-e/if1.0 new file mode 100644 index 0000000..5697e2f --- /dev/null +++ b/shell_cmds/sh/tests/set-e/if1.0 @@ -0,0 +1,5 @@ +# $FreeBSD: head/bin/sh/tests/set-e/if1.0 211399 2010-08-16 17:18:08Z jilles $ +set -e +if false; then + : +fi diff --git a/shell_cmds/sh/tests/set-e/if2.0 b/shell_cmds/sh/tests/set-e/if2.0 new file mode 100644 index 0000000..188361b --- /dev/null +++ b/shell_cmds/sh/tests/set-e/if2.0 @@ -0,0 +1,7 @@ +# $FreeBSD: head/bin/sh/tests/set-e/if2.0 149781 2005-09-04 11:59:59Z stefanf $ +set -e +# PR 28852 +if true; then + false && true +fi +exit 0 diff --git a/shell_cmds/sh/tests/set-e/if3.0 b/shell_cmds/sh/tests/set-e/if3.0 new file mode 100644 index 0000000..e3d8d6e --- /dev/null +++ b/shell_cmds/sh/tests/set-e/if3.0 @@ -0,0 +1,5 @@ +# $FreeBSD: head/bin/sh/tests/set-e/if3.0 211399 2010-08-16 17:18:08Z jilles $ +set -e +if false; false; then + : +fi diff --git a/shell_cmds/sh/tests/set-e/not1.0 b/shell_cmds/sh/tests/set-e/not1.0 new file mode 100644 index 0000000..b694be8 --- /dev/null +++ b/shell_cmds/sh/tests/set-e/not1.0 @@ -0,0 +1,4 @@ +# $FreeBSD: head/bin/sh/tests/set-e/not1.0 149781 2005-09-04 11:59:59Z stefanf $ +set -e +! true +exit 0 diff --git a/shell_cmds/sh/tests/set-e/not2.0 b/shell_cmds/sh/tests/set-e/not2.0 new file mode 100644 index 0000000..d9ea5e9 --- /dev/null +++ b/shell_cmds/sh/tests/set-e/not2.0 @@ -0,0 +1,4 @@ +# $FreeBSD: head/bin/sh/tests/set-e/not2.0 193178 2009-05-31 17:23:27Z stefanf $ +set -e +! false +! eval false diff --git a/shell_cmds/sh/tests/set-e/or1.0 b/shell_cmds/sh/tests/set-e/or1.0 new file mode 100644 index 0000000..5000f0b --- /dev/null +++ b/shell_cmds/sh/tests/set-e/or1.0 @@ -0,0 +1,3 @@ +# $FreeBSD: head/bin/sh/tests/set-e/or1.0 149781 2005-09-04 11:59:59Z stefanf $ +set -e +true || false diff --git a/shell_cmds/sh/tests/set-e/or2.0 b/shell_cmds/sh/tests/set-e/or2.0 new file mode 100644 index 0000000..6964220 --- /dev/null +++ b/shell_cmds/sh/tests/set-e/or2.0 @@ -0,0 +1,3 @@ +# $FreeBSD: head/bin/sh/tests/set-e/or2.0 149781 2005-09-04 11:59:59Z stefanf $ +set -e +false || true diff --git a/shell_cmds/sh/tests/set-e/or3.1 b/shell_cmds/sh/tests/set-e/or3.1 new file mode 100644 index 0000000..29af2c2 --- /dev/null +++ b/shell_cmds/sh/tests/set-e/or3.1 @@ -0,0 +1,4 @@ +# $FreeBSD: head/bin/sh/tests/set-e/or3.1 149781 2005-09-04 11:59:59Z stefanf $ +set -e +false || false +exit 0 diff --git a/shell_cmds/sh/tests/set-e/pipe1.1 b/shell_cmds/sh/tests/set-e/pipe1.1 new file mode 100644 index 0000000..122af07 --- /dev/null +++ b/shell_cmds/sh/tests/set-e/pipe1.1 @@ -0,0 +1,4 @@ +# $FreeBSD: head/bin/sh/tests/set-e/pipe1.1 149781 2005-09-04 11:59:59Z stefanf $ +set -e +true | false +exit 0 diff --git a/shell_cmds/sh/tests/set-e/pipe2.0 b/shell_cmds/sh/tests/set-e/pipe2.0 new file mode 100644 index 0000000..d80fd93 --- /dev/null +++ b/shell_cmds/sh/tests/set-e/pipe2.0 @@ -0,0 +1,3 @@ +# $FreeBSD: head/bin/sh/tests/set-e/pipe2.0 149781 2005-09-04 11:59:59Z stefanf $ +set -e +false | true diff --git a/shell_cmds/sh/tests/set-e/return1.0 b/shell_cmds/sh/tests/set-e/return1.0 new file mode 100644 index 0000000..d157059 --- /dev/null +++ b/shell_cmds/sh/tests/set-e/return1.0 @@ -0,0 +1,11 @@ +# $FreeBSD: head/bin/sh/tests/set-e/return1.0 149788 2005-09-04 21:29:09Z stefanf $ +set -e + +# PR 77067, 85267 +f() { + return 1 + true +} + +f || true +exit 0 diff --git a/shell_cmds/sh/tests/set-e/semi1.1 b/shell_cmds/sh/tests/set-e/semi1.1 new file mode 100644 index 0000000..5991f8d --- /dev/null +++ b/shell_cmds/sh/tests/set-e/semi1.1 @@ -0,0 +1,4 @@ +# $FreeBSD: head/bin/sh/tests/set-e/semi1.1 149781 2005-09-04 11:59:59Z stefanf $ +set -e +false; true +exit 0 diff --git a/shell_cmds/sh/tests/set-e/semi2.1 b/shell_cmds/sh/tests/set-e/semi2.1 new file mode 100644 index 0000000..6851700 --- /dev/null +++ b/shell_cmds/sh/tests/set-e/semi2.1 @@ -0,0 +1,4 @@ +# $FreeBSD: head/bin/sh/tests/set-e/semi2.1 149781 2005-09-04 11:59:59Z stefanf $ +set -e +true; false +exit 0 diff --git a/shell_cmds/sh/tests/set-e/subshell1.0 b/shell_cmds/sh/tests/set-e/subshell1.0 new file mode 100644 index 0000000..fa2ec82 --- /dev/null +++ b/shell_cmds/sh/tests/set-e/subshell1.0 @@ -0,0 +1,3 @@ +# $FreeBSD: head/bin/sh/tests/set-e/subshell1.0 149781 2005-09-04 11:59:59Z stefanf $ +set -e +(true) diff --git a/shell_cmds/sh/tests/set-e/subshell2.1 b/shell_cmds/sh/tests/set-e/subshell2.1 new file mode 100644 index 0000000..03b09dc --- /dev/null +++ b/shell_cmds/sh/tests/set-e/subshell2.1 @@ -0,0 +1,4 @@ +# $FreeBSD: head/bin/sh/tests/set-e/subshell2.1 149781 2005-09-04 11:59:59Z stefanf $ +set -e +(false) +exit 0 diff --git a/shell_cmds/sh/tests/set-e/until1.0 b/shell_cmds/sh/tests/set-e/until1.0 new file mode 100644 index 0000000..c4d2edd --- /dev/null +++ b/shell_cmds/sh/tests/set-e/until1.0 @@ -0,0 +1,5 @@ +# $FreeBSD: head/bin/sh/tests/set-e/until1.0 149781 2005-09-04 11:59:59Z stefanf $ +set -e +until false; do + break +done diff --git a/shell_cmds/sh/tests/set-e/until2.0 b/shell_cmds/sh/tests/set-e/until2.0 new file mode 100644 index 0000000..d19a740 --- /dev/null +++ b/shell_cmds/sh/tests/set-e/until2.0 @@ -0,0 +1,5 @@ +# $FreeBSD: head/bin/sh/tests/set-e/until2.0 149781 2005-09-04 11:59:59Z stefanf $ +set -e +until false; false; do + break +done diff --git a/shell_cmds/sh/tests/set-e/until3.0 b/shell_cmds/sh/tests/set-e/until3.0 new file mode 100644 index 0000000..2f77903 --- /dev/null +++ b/shell_cmds/sh/tests/set-e/until3.0 @@ -0,0 +1,9 @@ +# $FreeBSD: head/bin/sh/tests/set-e/until3.0 149791 2005-09-05 09:42:10Z stefanf $ +set -e +f() { + until false; do + false + break + done +} +f || true diff --git a/shell_cmds/sh/tests/set-e/while1.0 b/shell_cmds/sh/tests/set-e/while1.0 new file mode 100644 index 0000000..6d7fe0a --- /dev/null +++ b/shell_cmds/sh/tests/set-e/while1.0 @@ -0,0 +1,5 @@ +# $FreeBSD: head/bin/sh/tests/set-e/while1.0 211399 2010-08-16 17:18:08Z jilles $ +set -e +while false; do + : +done diff --git a/shell_cmds/sh/tests/set-e/while2.0 b/shell_cmds/sh/tests/set-e/while2.0 new file mode 100644 index 0000000..fe2f2ba --- /dev/null +++ b/shell_cmds/sh/tests/set-e/while2.0 @@ -0,0 +1,5 @@ +# $FreeBSD: head/bin/sh/tests/set-e/while2.0 211399 2010-08-16 17:18:08Z jilles $ +set -e +while false; false; do + : +done diff --git a/shell_cmds/sh/tests/set-e/while3.0 b/shell_cmds/sh/tests/set-e/while3.0 new file mode 100644 index 0000000..bf87175 --- /dev/null +++ b/shell_cmds/sh/tests/set-e/while3.0 @@ -0,0 +1,9 @@ +# $FreeBSD: head/bin/sh/tests/set-e/while3.0 149791 2005-09-05 09:42:10Z stefanf $ +set -e +f() { + while true; do + false + break + done +} +f || true diff --git a/shell_cmds/sh/trap.c b/shell_cmds/sh/trap.c new file mode 100644 index 0000000..0f84892 --- /dev/null +++ b/shell_cmds/sh/trap.c @@ -0,0 +1,556 @@ +/*- + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)trap.c 8.5 (Berkeley) 6/5/95"; +#endif +#endif /* not lint */ +#include <sys/cdefs.h> +__FBSDID("$FreeBSD: head/bin/sh/trap.c 326025 2017-11-20 19:49:47Z pfg $"); + +#include <signal.h> +#include <unistd.h> +#include <stdlib.h> + +#include "shell.h" +#include "main.h" +#include "nodes.h" /* for other headers */ +#include "eval.h" +#include "jobs.h" +#include "show.h" +#include "options.h" +#include "syntax.h" +#include "output.h" +#include "memalloc.h" +#include "error.h" +#include "trap.h" +#include "mystring.h" +#include "builtins.h" +#include "myhistedit.h" + +#ifdef __APPLE__ +#define sys_nsig (NSIG) +#endif /* __APPLE__ */ + +/* + * Sigmode records the current value of the signal handlers for the various + * modes. A value of zero means that the current handler is not known. + * S_HARD_IGN indicates that the signal was ignored on entry to the shell, + */ + +#define S_DFL 1 /* default signal handling (SIG_DFL) */ +#define S_CATCH 2 /* signal is caught */ +#define S_IGN 3 /* signal is ignored (SIG_IGN) */ +#define S_HARD_IGN 4 /* signal is ignored permanently */ +#define S_RESET 5 /* temporary - to reset a hard ignored sig */ + + +static char sigmode[NSIG]; /* current value of signal */ +volatile sig_atomic_t pendingsig; /* indicates some signal received */ +volatile sig_atomic_t pendingsig_waitcmd; /* indicates wait builtin should be interrupted */ +static int in_dotrap; /* do we execute in a trap handler? */ +static char *volatile trap[NSIG]; /* trap handler commands */ +static volatile sig_atomic_t gotsig[NSIG]; + /* indicates specified signal received */ +static int ignore_sigchld; /* Used while handling SIGCHLD traps. */ +static int last_trapsig; + +static int exiting; /* exitshell() has been called */ +static int exiting_exitstatus; /* value passed to exitshell() */ + +static int getsigaction(int, sig_t *); + + +/* + * Map a string to a signal number. + * + * Note: the signal number may exceed NSIG. + */ +static int +sigstring_to_signum(char *sig) +{ + + if (is_number(sig)) { + int signo; + + signo = atoi(sig); + return ((signo >= 0 && signo < NSIG) ? signo : (-1)); + } else if (strcasecmp(sig, "EXIT") == 0) { + return (0); + } else { + int n; + + if (strncasecmp(sig, "SIG", 3) == 0) + sig += 3; + for (n = 1; n < sys_nsig; n++) + if (sys_signame[n] && + strcasecmp(sys_signame[n], sig) == 0) + return (n); + } + return (-1); +} + + +/* + * Print a list of valid signal names. + */ +static void +printsignals(void) +{ + int n, outlen; + + outlen = 0; + for (n = 1; n < sys_nsig; n++) { + if (sys_signame[n]) { + out1fmt("%s", sys_signame[n]); + outlen += strlen(sys_signame[n]); + } else { + out1fmt("%d", n); + outlen += 3; /* good enough */ + } + ++outlen; + if (outlen > 71 || n == sys_nsig - 1) { + out1str("\n"); + outlen = 0; + } else { + out1c(' '); + } + } +} + + +/* + * The trap builtin. + */ +int +trapcmd(int argc __unused, char **argv) +{ + char *action; + int signo; + int errors = 0; + int i; + + while ((i = nextopt("l")) != '\0') { + switch (i) { + case 'l': + printsignals(); + return (0); + } + } + argv = argptr; + + if (*argv == NULL) { + for (signo = 0 ; signo < sys_nsig ; signo++) { + if (signo < NSIG && trap[signo] != NULL) { + out1str("trap -- "); + out1qstr(trap[signo]); + if (signo == 0) { + out1str(" EXIT\n"); + } else if (sys_signame[signo]) { + out1fmt(" %s\n", sys_signame[signo]); + } else { + out1fmt(" %d\n", signo); + } + } + } + return 0; + } + action = NULL; + if (*argv && !is_number(*argv)) { + if (strcmp(*argv, "-") == 0) + argv++; + else { + action = *argv; + argv++; + } + } + for (; *argv; argv++) { + if ((signo = sigstring_to_signum(*argv)) == -1) { + warning("bad signal %s", *argv); + errors = 1; + continue; + } + INTOFF; + if (action) + action = savestr(action); + if (trap[signo]) + ckfree(trap[signo]); + trap[signo] = action; + if (signo != 0) + setsignal(signo); + INTON; + } + return errors; +} + + +/* + * Clear traps on a fork. + */ +void +clear_traps(void) +{ + char *volatile *tp; + + for (tp = trap ; tp <= &trap[NSIG - 1] ; tp++) { + if (*tp && **tp) { /* trap not NULL or SIG_IGN */ + INTOFF; + ckfree(*tp); + *tp = NULL; + if (tp != &trap[0]) + setsignal(tp - trap); + INTON; + } + } +} + + +/* + * Check if we have any traps enabled. + */ +int +have_traps(void) +{ + char *volatile *tp; + + for (tp = trap ; tp <= &trap[NSIG - 1] ; tp++) { + if (*tp && **tp) /* trap not NULL or SIG_IGN */ + return 1; + } + return 0; +} + +/* + * Set the signal handler for the specified signal. The routine figures + * out what it should be set to. + */ +void +setsignal(int signo) +{ + int action; + sig_t sigact = SIG_DFL; + struct sigaction sa; + char *t; + + if ((t = trap[signo]) == NULL) + action = S_DFL; + else if (*t != '\0') + action = S_CATCH; + else + action = S_IGN; + if (action == S_DFL) { + switch (signo) { + case SIGINT: + action = S_CATCH; + break; + case SIGQUIT: +#ifdef DEBUG + { + extern int debug; + + if (debug) + break; + } +#endif + action = S_CATCH; + break; + case SIGTERM: + if (rootshell && iflag) + action = S_IGN; + break; +#if JOBS + case SIGTSTP: + case SIGTTOU: + if (rootshell && mflag) + action = S_IGN; + break; +#endif + } + } + + t = &sigmode[signo]; + if (*t == 0) { + /* + * current setting unknown + */ + if (!getsigaction(signo, &sigact)) { + /* + * Pretend it worked; maybe we should give a warning + * here, but other shells don't. We don't alter + * sigmode, so that we retry every time. + */ + return; + } + if (sigact == SIG_IGN) { + if (mflag && (signo == SIGTSTP || + signo == SIGTTIN || signo == SIGTTOU)) { + *t = S_IGN; /* don't hard ignore these */ + } else + *t = S_HARD_IGN; + } else { + *t = S_RESET; /* force to be set */ + } + } + if (*t == S_HARD_IGN || *t == action) + return; + switch (action) { + case S_DFL: sigact = SIG_DFL; break; + case S_CATCH: sigact = onsig; break; + case S_IGN: sigact = SIG_IGN; break; + } + *t = action; + sa.sa_handler = sigact; + sa.sa_flags = 0; + sigemptyset(&sa.sa_mask); + sigaction(signo, &sa, NULL); +} + + +/* + * Return the current setting for sig w/o changing it. + */ +static int +getsigaction(int signo, sig_t *sigact) +{ + struct sigaction sa; + + if (sigaction(signo, (struct sigaction *)0, &sa) == -1) + return 0; + *sigact = (sig_t) sa.sa_handler; + return 1; +} + + +/* + * Ignore a signal. + */ +void +ignoresig(int signo) +{ + + if (sigmode[signo] == 0) + setsignal(signo); + if (sigmode[signo] != S_IGN && sigmode[signo] != S_HARD_IGN) { + signal(signo, SIG_IGN); + sigmode[signo] = S_IGN; + } +} + + +int +issigchldtrapped(void) +{ + + return (trap[SIGCHLD] != NULL && *trap[SIGCHLD] != '\0'); +} + + +/* + * Signal handler. + */ +void +onsig(int signo) +{ + + if (signo == SIGINT && trap[SIGINT] == NULL) { + /* + * The !in_dotrap here is safe. The only way we can arrive + * here with in_dotrap set is that a trap handler set SIGINT to + * SIG_DFL and killed itself. + */ + if (suppressint && !in_dotrap) + SET_PENDING_INT; + else + onint(); + return; + } + + /* If we are currently in a wait builtin, prepare to break it */ + if (signo == SIGINT || signo == SIGQUIT) + pendingsig_waitcmd = signo; + + if (trap[signo] != NULL && trap[signo][0] != '\0' && + (signo != SIGCHLD || !ignore_sigchld)) { + gotsig[signo] = 1; + pendingsig = signo; + pendingsig_waitcmd = signo; + } +} + + +/* + * Called to execute a trap. Perhaps we should avoid entering new trap + * handlers while we are executing a trap handler. + */ +void +dotrap(void) +{ + struct stackmark smark; + int i; + int savestatus, prev_evalskip, prev_skipcount; + + in_dotrap++; + for (;;) { + pendingsig = 0; + pendingsig_waitcmd = 0; + for (i = 1; i < NSIG; i++) { + if (gotsig[i]) { + gotsig[i] = 0; + if (trap[i]) { + /* + * Ignore SIGCHLD to avoid infinite + * recursion if the trap action does + * a fork. + */ + if (i == SIGCHLD) + ignore_sigchld++; + + /* + * Backup current evalskip + * state and reset it before + * executing a trap, so that the + * trap is not disturbed by an + * ongoing break/continue/return + * statement. + */ + prev_evalskip = evalskip; + prev_skipcount = skipcount; + evalskip = 0; + + last_trapsig = i; + savestatus = exitstatus; + setstackmark(&smark); + evalstring(stsavestr(trap[i]), 0); + popstackmark(&smark); + + /* + * If such a command was not + * already in progress, allow a + * break/continue/return in the + * trap action to have an effect + * outside of it. + */ + if (evalskip == 0 || + prev_evalskip != 0) { + evalskip = prev_evalskip; + skipcount = prev_skipcount; + exitstatus = savestatus; + } + + if (i == SIGCHLD) + ignore_sigchld--; + } + break; + } + } + if (i >= NSIG) + break; + } + in_dotrap--; +} + + +/* + * Controls whether the shell is interactive or not based on iflag. + */ +void +setinteractive(void) +{ + setsignal(SIGINT); + setsignal(SIGQUIT); + setsignal(SIGTERM); +} + + +/* + * Called to exit the shell. + */ +void +exitshell(int status) +{ + TRACE(("exitshell(%d) pid=%d\n", status, getpid())); + exiting = 1; + exiting_exitstatus = status; + exitshell_savedstatus(); +} + +void +exitshell_savedstatus(void) +{ + struct jmploc loc1, loc2; + char *p; + int sig = 0; + sigset_t sigs; + + if (!exiting) { + if (in_dotrap && last_trapsig) { + sig = last_trapsig; + exiting_exitstatus = sig + 128; + } else + exiting_exitstatus = oexitstatus; + } + exitstatus = oexitstatus = exiting_exitstatus; + if (!setjmp(loc1.loc)) { + handler = &loc1; + if ((p = trap[0]) != NULL && *p != '\0') { + /* + * Reset evalskip, or the trap on EXIT could be + * interrupted if the last command was a "return". + */ + evalskip = 0; + trap[0] = NULL; + FORCEINTON; + evalstring(p, 0); + } + } + if (!setjmp(loc2.loc)) { + handler = &loc2; /* probably unnecessary */ + FORCEINTON; + flushall(); +#if JOBS + setjobctl(0); +#endif + } + if (sig != 0 && sig != SIGSTOP && sig != SIGTSTP && sig != SIGTTIN && + sig != SIGTTOU) { + signal(sig, SIG_DFL); + sigemptyset(&sigs); + sigaddset(&sigs, sig); + sigprocmask(SIG_UNBLOCK, &sigs, NULL); + kill(getpid(), sig); + /* If the default action is to ignore, fall back to _exit(). */ + } + _exit(exiting_exitstatus); +} diff --git a/shell_cmds/sh/trap.h b/shell_cmds/sh/trap.h new file mode 100644 index 0000000..90ba054 --- /dev/null +++ b/shell_cmds/sh/trap.h @@ -0,0 +1,50 @@ +/*- + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)trap.h 8.3 (Berkeley) 6/5/95 + * $FreeBSD: head/bin/sh/trap.h 326025 2017-11-20 19:49:47Z pfg $ + */ + +extern volatile sig_atomic_t pendingsig; +extern volatile sig_atomic_t pendingsig_waitcmd; + +void clear_traps(void); +int have_traps(void); +void setsignal(int); +void ignoresig(int); +int issigchldtrapped(void); +void onsig(int); +void dotrap(void); +void setinteractive(void); +void exitshell(int) __dead2; +void exitshell_savedstatus(void) __dead2; diff --git a/shell_cmds/sh/var.c b/shell_cmds/sh/var.c new file mode 100644 index 0000000..feeea03 --- /dev/null +++ b/shell_cmds/sh/var.c @@ -0,0 +1,971 @@ +/*- + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)var.c 8.3 (Berkeley) 5/4/95"; +#endif +#endif /* not lint */ +#include <sys/cdefs.h> +__FBSDID("$FreeBSD: head/bin/sh/var.c 329221 2018-02-13 16:48:57Z bdrewery $"); + +#include <unistd.h> +#include <stdlib.h> +#include <paths.h> + +/* + * Shell variables. + */ + +#include <locale.h> +#include <langinfo.h> + +#include "shell.h" +#include "output.h" +#include "expand.h" +#include "nodes.h" /* for other headers */ +#include "eval.h" /* defines cmdenviron */ +#include "exec.h" +#include "syntax.h" +#include "options.h" +#include "mail.h" +#include "var.h" +#include "memalloc.h" +#include "error.h" +#include "mystring.h" +#include "parser.h" +#include "builtins.h" +#ifndef NO_HISTORY +#include "myhistedit.h" +#endif + + +#ifndef VTABSIZE +#define VTABSIZE 39 +#endif + + +struct varinit { + struct var *var; + int flags; + const char *text; + void (*func)(const char *); +}; + + +#ifndef NO_HISTORY +struct var vhistsize; +struct var vterm; +#endif +struct var vifs; +struct var vmail; +struct var vmpath; +struct var vpath; +struct var vps1; +struct var vps2; +struct var vps4; +static struct var voptind; +struct var vdisvfork; + +struct localvar *localvars; +int forcelocal; + +static const struct varinit varinit[] = { +#ifndef NO_HISTORY + { &vhistsize, VUNSET, "HISTSIZE=", + sethistsize }, +#endif + { &vifs, 0, "IFS= \t\n", + NULL }, + { &vmail, VUNSET, "MAIL=", + NULL }, + { &vmpath, VUNSET, "MAILPATH=", + NULL }, + { &vpath, 0, "PATH=" _PATH_DEFPATH, + changepath }, + /* + * vps1 depends on uid + */ + { &vps2, 0, "PS2=> ", + NULL }, + { &vps4, 0, "PS4=+ ", + NULL }, +#ifndef NO_HISTORY + { &vterm, VUNSET, "TERM=", + setterm }, +#endif + { &voptind, 0, "OPTIND=1", + getoptsreset }, + { &vdisvfork, VUNSET, "SH_DISABLE_VFORK=", + NULL }, + { NULL, 0, NULL, + NULL } +}; + +static struct var *vartab[VTABSIZE]; + +static const char *const locale_names[7] = { + "LC_COLLATE", "LC_CTYPE", "LC_MONETARY", + "LC_NUMERIC", "LC_TIME", "LC_MESSAGES", NULL +}; +static const int locale_categories[7] = { + LC_COLLATE, LC_CTYPE, LC_MONETARY, LC_NUMERIC, LC_TIME, LC_MESSAGES, 0 +}; + +static int varequal(const char *, const char *); +static struct var *find_var(const char *, struct var ***, int *); +static int localevar(const char *); +static void setvareq_const(const char *s, int flags); + +extern char **environ; + +/* + * This routine initializes the builtin variables and imports the environment. + * It is called when the shell is initialized. + */ + +void +initvar(void) +{ + char ppid[20]; + const struct varinit *ip; + struct var *vp; + struct var **vpp; + char **envp; + + for (ip = varinit ; (vp = ip->var) != NULL ; ip++) { + if (find_var(ip->text, &vpp, &vp->name_len) != NULL) + continue; + vp->next = *vpp; + *vpp = vp; + vp->text = __DECONST(char *, ip->text); + vp->flags = ip->flags | VSTRFIXED | VTEXTFIXED; + vp->func = ip->func; + } + /* + * PS1 depends on uid + */ + if (find_var("PS1", &vpp, &vps1.name_len) == NULL) { + vps1.next = *vpp; + *vpp = &vps1; + vps1.text = __DECONST(char *, geteuid() ? "PS1=$ " : "PS1=# "); + vps1.flags = VSTRFIXED|VTEXTFIXED; + } + fmtstr(ppid, sizeof(ppid), "%d", (int)getppid()); + setvarsafe("PPID", ppid, 0); + for (envp = environ ; *envp ; envp++) { + if (strchr(*envp, '=')) { + setvareq(*envp, VEXPORT|VTEXTFIXED); + } + } + setvareq_const("OPTIND=1", 0); + setvareq_const("IFS= \t\n", 0); +} + +/* + * Safe version of setvar, returns 1 on success 0 on failure. + */ + +int +setvarsafe(const char *name, const char *val, int flags) +{ + struct jmploc jmploc; + struct jmploc *const savehandler = handler; + int err = 0; + int inton; + + inton = is_int_on(); + if (setjmp(jmploc.loc)) + err = 1; + else { + handler = &jmploc; + setvar(name, val, flags); + } + handler = savehandler; + SETINTON(inton); + return err; +} + +/* + * Set the value of a variable. The flags argument is stored with the + * flags of the variable. If val is NULL, the variable is unset. + */ + +void +setvar(const char *name, const char *val, int flags) +{ + const char *p; + size_t len; + size_t namelen; + size_t vallen; + char *nameeq; + int isbad; + + isbad = 0; + p = name; + if (! is_name(*p)) + isbad = 1; + p++; + for (;;) { + if (! is_in_name(*p)) { + if (*p == '\0' || *p == '=') + break; + isbad = 1; + } + p++; + } + namelen = p - name; + if (isbad) + error("%.*s: bad variable name", (int)namelen, name); + len = namelen + 2; /* 2 is space for '=' and '\0' */ + if (val == NULL) { + flags |= VUNSET; + vallen = 0; + } else { + vallen = strlen(val); + len += vallen; + } + INTOFF; + nameeq = ckmalloc(len); + memcpy(nameeq, name, namelen); + nameeq[namelen] = '='; + if (val) + memcpy(nameeq + namelen + 1, val, vallen + 1); + else + nameeq[namelen + 1] = '\0'; + setvareq(nameeq, flags); + INTON; +} + +static int +localevar(const char *s) +{ + const char *const *ss; + + if (*s != 'L') + return 0; + if (varequal(s + 1, "ANG")) + return 1; + if (strncmp(s + 1, "C_", 2) != 0) + return 0; + if (varequal(s + 3, "ALL")) + return 1; + for (ss = locale_names; *ss ; ss++) + if (varequal(s + 3, *ss + 3)) + return 1; + return 0; +} + + +/* + * Sets/unsets an environment variable from a pointer that may actually be a + * pointer into environ where the string should not be manipulated. + */ +static void +change_env(const char *s, int set) +{ + char *eqp; + char *ss; + + INTOFF; + ss = savestr(s); + if ((eqp = strchr(ss, '=')) != NULL) + *eqp = '\0'; + if (set && eqp != NULL) + (void) setenv(ss, eqp + 1, 1); + else + (void) unsetenv(ss); + ckfree(ss); + INTON; + + return; +} + + +/* + * Same as setvar except that the variable and value are passed in + * the first argument as name=value. Since the first argument will + * be actually stored in the table, it should not be a string that + * will go away. + */ + +void +setvareq(char *s, int flags) +{ + struct var *vp, **vpp; + int nlen; + + if (aflag) + flags |= VEXPORT; + if (forcelocal && !(flags & (VNOSET | VNOLOCAL))) + mklocal(s); + vp = find_var(s, &vpp, &nlen); + if (vp != NULL) { + if (vp->flags & VREADONLY) { + if ((flags & (VTEXTFIXED|VSTACK)) == 0) + ckfree(s); + error("%.*s: is read only", vp->name_len, vp->text); + } + if (flags & VNOSET) { + if ((flags & (VTEXTFIXED|VSTACK)) == 0) + ckfree(s); + return; + } + INTOFF; + + if (vp->func && (flags & VNOFUNC) == 0) + (*vp->func)(s + vp->name_len + 1); + + if ((vp->flags & (VTEXTFIXED|VSTACK)) == 0) + ckfree(vp->text); + + vp->flags &= ~(VTEXTFIXED|VSTACK|VUNSET); + vp->flags |= flags; + vp->text = s; + + /* + * We could roll this to a function, to handle it as + * a regular variable function callback, but why bother? + * + * Note: this assumes iflag is not set to 1 initially. + * As part of initvar(), this is called before arguments + * are looked at. + */ + if ((vp == &vmpath || (vp == &vmail && ! mpathset())) && + iflag == 1) + chkmail(1); + if ((vp->flags & VEXPORT) && localevar(s)) { + change_env(s, 1); + (void) setlocale(LC_ALL, ""); + updatecharset(); + } + INTON; + return; + } + /* not found */ + if (flags & VNOSET) { + if ((flags & (VTEXTFIXED|VSTACK)) == 0) + ckfree(s); + return; + } + INTOFF; + vp = ckmalloc(sizeof (*vp)); + vp->flags = flags; + vp->text = s; + vp->name_len = nlen; + vp->next = *vpp; + vp->func = NULL; + *vpp = vp; + if ((vp->flags & VEXPORT) && localevar(s)) { + change_env(s, 1); + (void) setlocale(LC_ALL, ""); + updatecharset(); + } + INTON; +} + + +static void +setvareq_const(const char *s, int flags) +{ + setvareq(__DECONST(char *, s), flags | VTEXTFIXED); +} + + +/* + * Process a linked list of variable assignments. + */ + +void +listsetvar(struct arglist *list, int flags) +{ + int i; + + INTOFF; + for (i = 0; i < list->count; i++) + setvareq(savestr(list->args[i]), flags); + INTON; +} + + + +/* + * Find the value of a variable. Returns NULL if not set. + */ + +char * +lookupvar(const char *name) +{ + struct var *v; + + v = find_var(name, NULL, NULL); + if (v == NULL || v->flags & VUNSET) + return NULL; + return v->text + v->name_len + 1; +} + + + +/* + * Search the environment of a builtin command. If the second argument + * is nonzero, return the value of a variable even if it hasn't been + * exported. + */ + +char * +bltinlookup(const char *name, int doall) +{ + struct var *v; + char *result; + int i; + + result = NULL; + if (cmdenviron) for (i = 0; i < cmdenviron->count; i++) { + if (varequal(cmdenviron->args[i], name)) + result = strchr(cmdenviron->args[i], '=') + 1; + } + if (result != NULL) + return result; + + v = find_var(name, NULL, NULL); + if (v == NULL || v->flags & VUNSET || + (!doall && (v->flags & VEXPORT) == 0)) + return NULL; + return v->text + v->name_len + 1; +} + + +/* + * Set up locale for a builtin (LANG/LC_* assignments). + */ +void +bltinsetlocale(void) +{ + int act = 0; + char *loc, *locdef; + int i; + + if (cmdenviron) for (i = 0; i < cmdenviron->count; i++) { + if (localevar(cmdenviron->args[i])) { + act = 1; + break; + } + } + if (!act) + return; + loc = bltinlookup("LC_ALL", 0); + INTOFF; + if (loc != NULL) { + setlocale(LC_ALL, loc); + INTON; + updatecharset(); + return; + } + locdef = bltinlookup("LANG", 0); + for (i = 0; locale_names[i] != NULL; i++) { + loc = bltinlookup(locale_names[i], 0); + if (loc == NULL) + loc = locdef; + if (loc != NULL) + setlocale(locale_categories[i], loc); + } + INTON; + updatecharset(); +} + +/* + * Undo the effect of bltinlocaleset(). + */ +void +bltinunsetlocale(void) +{ + int i; + + INTOFF; + if (cmdenviron) for (i = 0; i < cmdenviron->count; i++) { + if (localevar(cmdenviron->args[i])) { + setlocale(LC_ALL, ""); + updatecharset(); + break; + } + } + INTON; +} + +/* + * Update the localeisutf8 flag. + */ +void +updatecharset(void) +{ + char *charset; + + charset = nl_langinfo(CODESET); + localeisutf8 = !strcmp(charset, "UTF-8"); +} + +void +initcharset(void) +{ + updatecharset(); + initial_localeisutf8 = localeisutf8; +} + +/* + * Generate a list of exported variables. This routine is used to construct + * the third argument to execve when executing a program. + */ + +char ** +environment(void) +{ + int nenv; + struct var **vpp; + struct var *vp; + char **env, **ep; + + nenv = 0; + for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) { + for (vp = *vpp ; vp ; vp = vp->next) + if (vp->flags & VEXPORT) + nenv++; + } + ep = env = stalloc((nenv + 1) * sizeof *env); + for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) { + for (vp = *vpp ; vp ; vp = vp->next) + if (vp->flags & VEXPORT) + *ep++ = vp->text; + } + *ep = NULL; + return env; +} + + +static int +var_compare(const void *a, const void *b) +{ + const char *const *sa, *const *sb; + + sa = a; + sb = b; + /* + * This compares two var=value strings which creates a different + * order from what you would probably expect. POSIX is somewhat + * ambiguous on what should be sorted exactly. + */ + return strcoll(*sa, *sb); +} + + +/* + * Command to list all variables which are set. This is invoked from the + * set command when it is called without any options or operands. + */ + +int +showvarscmd(int argc __unused, char **argv __unused) +{ + struct var **vpp; + struct var *vp; + const char *s; + const char **vars; + int i, n; + + /* + * POSIX requires us to sort the variables. + */ + n = 0; + for (vpp = vartab; vpp < vartab + VTABSIZE; vpp++) { + for (vp = *vpp; vp; vp = vp->next) { + if (!(vp->flags & VUNSET)) + n++; + } + } + + INTOFF; + vars = ckmalloc(n * sizeof(*vars)); + i = 0; + for (vpp = vartab; vpp < vartab + VTABSIZE; vpp++) { + for (vp = *vpp; vp; vp = vp->next) { + if (!(vp->flags & VUNSET)) + vars[i++] = vp->text; + } + } + + qsort(vars, n, sizeof(*vars), var_compare); + for (i = 0; i < n; i++) { + /* + * Skip improper variable names so the output remains usable as + * shell input. + */ + if (!isassignment(vars[i])) + continue; + s = strchr(vars[i], '='); + s++; + outbin(vars[i], s - vars[i], out1); + out1qstr(s); + out1c('\n'); + } + ckfree(vars); + INTON; + + return 0; +} + + + +/* + * The export and readonly commands. + */ + +int +exportcmd(int argc __unused, char **argv) +{ + struct var **vpp; + struct var *vp; + char **ap; + char *name; + char *p; + char *cmdname; + int ch, values; + int flag = argv[0][0] == 'r'? VREADONLY : VEXPORT; + + cmdname = argv[0]; + values = 0; + while ((ch = nextopt("p")) != '\0') { + switch (ch) { + case 'p': + values = 1; + break; + } + } + + if (values && *argptr != NULL) + error("-p requires no arguments"); + if (*argptr != NULL) { + for (ap = argptr; (name = *ap) != NULL; ap++) { + if ((p = strchr(name, '=')) != NULL) { + p++; + } else { + vp = find_var(name, NULL, NULL); + if (vp != NULL) { + vp->flags |= flag; + if ((vp->flags & VEXPORT) && localevar(vp->text)) { + change_env(vp->text, 1); + (void) setlocale(LC_ALL, ""); + updatecharset(); + } + continue; + } + } + setvar(name, p, flag); + } + } else { + for (vpp = vartab ; vpp < vartab + VTABSIZE ; vpp++) { + for (vp = *vpp ; vp ; vp = vp->next) { + if (vp->flags & flag) { + if (values) { + /* + * Skip improper variable names + * so the output remains usable + * as shell input. + */ + if (!isassignment(vp->text)) + continue; + out1str(cmdname); + out1c(' '); + } + if (values && !(vp->flags & VUNSET)) { + outbin(vp->text, + vp->name_len + 1, out1); + out1qstr(vp->text + + vp->name_len + 1); + } else + outbin(vp->text, vp->name_len, + out1); + out1c('\n'); + } + } + } + } + return 0; +} + + +/* + * The "local" command. + */ + +int +localcmd(int argc __unused, char **argv __unused) +{ + char *name; + + nextopt(""); + if (! in_function()) + error("Not in a function"); + while ((name = *argptr++) != NULL) { + mklocal(name); + } + return 0; +} + + +/* + * Make a variable a local variable. When a variable is made local, it's + * value and flags are saved in a localvar structure. The saved values + * will be restored when the shell function returns. We handle the name + * "-" as a special case. + */ + +void +mklocal(char *name) +{ + struct localvar *lvp; + struct var **vpp; + struct var *vp; + + INTOFF; + lvp = ckmalloc(sizeof (struct localvar)); + if (name[0] == '-' && name[1] == '\0') { + lvp->text = ckmalloc(sizeof optval); + memcpy(lvp->text, optval, sizeof optval); + vp = NULL; + } else { + vp = find_var(name, &vpp, NULL); + if (vp == NULL) { + if (strchr(name, '=')) + setvareq(savestr(name), VSTRFIXED | VNOLOCAL); + else + setvar(name, NULL, VSTRFIXED | VNOLOCAL); + vp = *vpp; /* the new variable */ + lvp->text = NULL; + lvp->flags = VUNSET; + } else { + lvp->text = vp->text; + lvp->flags = vp->flags; + vp->flags |= VSTRFIXED|VTEXTFIXED; + if (name[vp->name_len] == '=') + setvareq(savestr(name), VNOLOCAL); + } + } + lvp->vp = vp; + lvp->next = localvars; + localvars = lvp; + INTON; +} + + +/* + * Called after a function returns. + */ + +void +poplocalvars(void) +{ + struct localvar *lvp; + struct var *vp; + int islocalevar; + + INTOFF; + while ((lvp = localvars) != NULL) { + localvars = lvp->next; + vp = lvp->vp; + if (vp == NULL) { /* $- saved */ + memcpy(optval, lvp->text, sizeof optval); + ckfree(lvp->text); + optschanged(); + } else if ((lvp->flags & (VUNSET|VSTRFIXED)) == VUNSET) { + vp->flags &= ~VREADONLY; + (void)unsetvar(vp->text); + } else { + islocalevar = (vp->flags | lvp->flags) & VEXPORT && + localevar(lvp->text); + if ((vp->flags & VTEXTFIXED) == 0) + ckfree(vp->text); + vp->flags = lvp->flags; + vp->text = lvp->text; + if (vp->func) + (*vp->func)(vp->text + vp->name_len + 1); + if (islocalevar) { + change_env(vp->text, vp->flags & VEXPORT && + (vp->flags & VUNSET) == 0); + setlocale(LC_ALL, ""); + updatecharset(); + } + } + ckfree(lvp); + } + INTON; +} + + +int +setvarcmd(int argc, char **argv) +{ + if (argc <= 2) + return unsetcmd(argc, argv); + else if (argc == 3) + setvar(argv[1], argv[2], 0); + else + error("too many arguments"); + return 0; +} + + +/* + * The unset builtin command. + */ + +int +unsetcmd(int argc __unused, char **argv __unused) +{ + char **ap; + int i; + int flg_func = 0; + int flg_var = 0; + int ret = 0; + + while ((i = nextopt("vf")) != '\0') { + if (i == 'f') + flg_func = 1; + else + flg_var = 1; + } + if (flg_func == 0 && flg_var == 0) + flg_var = 1; + + INTOFF; + for (ap = argptr; *ap ; ap++) { + if (flg_func) + ret |= unsetfunc(*ap); + if (flg_var) + ret |= unsetvar(*ap); + } + INTON; + return ret; +} + + +/* + * Unset the specified variable. + * Called with interrupts off. + */ + +int +unsetvar(const char *s) +{ + struct var **vpp; + struct var *vp; + + vp = find_var(s, &vpp, NULL); + if (vp == NULL) + return (0); + if (vp->flags & VREADONLY) + return (1); + if (vp->text[vp->name_len + 1] != '\0') + setvar(s, "", 0); + if ((vp->flags & VEXPORT) && localevar(vp->text)) { + change_env(s, 0); + setlocale(LC_ALL, ""); + updatecharset(); + } + vp->flags &= ~VEXPORT; + vp->flags |= VUNSET; + if ((vp->flags & VSTRFIXED) == 0) { + if ((vp->flags & VTEXTFIXED) == 0) + ckfree(vp->text); + *vpp = vp->next; + ckfree(vp); + } + return (0); +} + + + +/* + * Returns true if the two strings specify the same variable. The first + * variable name is terminated by '='; the second may be terminated by + * either '=' or '\0'. + */ + +static int +varequal(const char *p, const char *q) +{ + while (*p == *q++) { + if (*p++ == '=') + return 1; + } + if (*p == '=' && *(q - 1) == '\0') + return 1; + return 0; +} + +/* + * Search for a variable. + * 'name' may be terminated by '=' or a NUL. + * vppp is set to the pointer to vp, or the list head if vp isn't found + * lenp is set to the number of characters in 'name' + */ + +static struct var * +find_var(const char *name, struct var ***vppp, int *lenp) +{ + unsigned int hashval; + int len; + struct var *vp, **vpp; + const char *p = name; + + hashval = 0; + while (*p && *p != '=') + hashval = 2 * hashval + (unsigned char)*p++; + len = p - name; + + if (lenp) + *lenp = len; + vpp = &vartab[hashval % VTABSIZE]; + if (vppp) + *vppp = vpp; + + for (vp = *vpp ; vp ; vpp = &vp->next, vp = *vpp) { + if (vp->name_len != len) + continue; + if (memcmp(vp->text, name, len) != 0) + continue; + if (vppp) + *vppp = vpp; + return vp; + } + return NULL; +} diff --git a/shell_cmds/sh/var.h b/shell_cmds/sh/var.h new file mode 100644 index 0000000..c9b5a99 --- /dev/null +++ b/shell_cmds/sh/var.h @@ -0,0 +1,132 @@ +/*- + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright (c) 1991, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Kenneth Almquist. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)var.h 8.2 (Berkeley) 5/4/95 + * $FreeBSD: head/bin/sh/var.h 326025 2017-11-20 19:49:47Z pfg $ + */ + +/* + * Shell variables. + */ + +/* flags */ +#define VEXPORT 0x01 /* variable is exported */ +#define VREADONLY 0x02 /* variable cannot be modified */ +#define VSTRFIXED 0x04 /* variable struct is statically allocated */ +#define VTEXTFIXED 0x08 /* text is statically allocated */ +#define VSTACK 0x10 /* text is allocated on the stack */ +#define VUNSET 0x20 /* the variable is not set */ +#define VNOFUNC 0x40 /* don't call the callback function */ +#define VNOSET 0x80 /* do not set variable - just readonly test */ +#define VNOLOCAL 0x100 /* ignore forcelocal */ + + +struct var { + struct var *next; /* next entry in hash list */ + int flags; /* flags are defined above */ + int name_len; /* length of name */ + char *text; /* name=value */ + void (*func)(const char *); + /* function to be called when */ + /* the variable gets set/unset */ +}; + + +struct localvar { + struct localvar *next; /* next local variable in list */ + struct var *vp; /* the variable that was made local */ + int flags; /* saved flags */ + char *text; /* saved text */ +}; + + +extern struct localvar *localvars; +extern int forcelocal; + +extern struct var vifs; +extern struct var vmail; +extern struct var vmpath; +extern struct var vpath; +extern struct var vps1; +extern struct var vps2; +extern struct var vps4; +extern struct var vdisvfork; +#ifndef NO_HISTORY +extern struct var vhistsize; +extern struct var vterm; +#endif + +extern int localeisutf8; +/* The parser uses the locale that was in effect at startup. */ +extern int initial_localeisutf8; + +/* + * The following macros access the values of the above variables. + * They have to skip over the name. They return the null string + * for unset variables. + */ + +#define ifsval() (vifs.text + 4) +#define ifsset() ((vifs.flags & VUNSET) == 0) +#define mailval() (vmail.text + 5) +#define mpathval() (vmpath.text + 9) +#define pathval() (vpath.text + 5) +#define ps1val() (vps1.text + 4) +#define ps2val() (vps2.text + 4) +#define ps4val() (vps4.text + 4) +#define optindval() (voptind.text + 7) +#ifndef NO_HISTORY +#define histsizeval() (vhistsize.text + 9) +#define termval() (vterm.text + 5) +#endif + +#define mpathset() ((vmpath.flags & VUNSET) == 0) +#define disvforkset() ((vdisvfork.flags & VUNSET) == 0) + +void initvar(void); +void setvar(const char *, const char *, int); +void setvareq(char *, int); +struct arglist; +void listsetvar(struct arglist *, int); +char *lookupvar(const char *); +char *bltinlookup(const char *, int); +void bltinsetlocale(void); +void bltinunsetlocale(void); +void updatecharset(void); +void initcharset(void); +char **environment(void); +int showvarscmd(int, char **); +void mklocal(char *); +void poplocalvars(void); +int unsetvar(const char *); +int setvarsafe(const char *, const char *, int); diff --git a/shell_cmds/shell_cmds.xcodeproj/project.pbxproj b/shell_cmds/shell_cmds.xcodeproj/project.pbxproj new file mode 100644 index 0000000..9a52eeb --- /dev/null +++ b/shell_cmds/shell_cmds.xcodeproj/project.pbxproj @@ -0,0 +1,5903 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 46; + objects = { + +/* Begin PBXAggregateTarget section */ + FCBA14FD14A144EC00AA698B /* All_OSX */ = { + isa = PBXAggregateTarget; + buildConfigurationList = FCBA14FE14A144EC00AA698B /* Build configuration list for PBXAggregateTarget "All_OSX" */; + buildPhases = ( + FCE30EB814B531EC00CC0294 /* ShellScript */, + ); + dependencies = ( + C6868583154725A30025D623 /* PBXTargetDependency */, + FCBA150114A144F700AA698B /* PBXTargetDependency */, + FCBA150314A144F700AA698B /* PBXTargetDependency */, + FCBA150514A144F700AA698B /* PBXTargetDependency */, + FCBA150714A144F700AA698B /* PBXTargetDependency */, + FCBA150914A144F700AA698B /* PBXTargetDependency */, + FCBA150B14A144F700AA698B /* PBXTargetDependency */, + FCBA150D14A144F700AA698B /* PBXTargetDependency */, + FCBA165514A146D000AA698B /* PBXTargetDependency */, + FCBA165714A146D000AA698B /* PBXTargetDependency */, + FCBA165914A146D000AA698B /* PBXTargetDependency */, + FCBA165B14A146D000AA698B /* PBXTargetDependency */, + FC5D63A214B9864400123E48 /* PBXTargetDependency */, + FCBA165D14A146D000AA698B /* PBXTargetDependency */, + FCBA165F14A146D000AA698B /* PBXTargetDependency */, + FCBA166114A146D000AA698B /* PBXTargetDependency */, + FCBA166314A146D000AA698B /* PBXTargetDependency */, + FCBA166514A146D000AA698B /* PBXTargetDependency */, + FCBA166714A146D000AA698B /* PBXTargetDependency */, + FCBA166914A146D000AA698B /* PBXTargetDependency */, + FCE30EE314B539BF00CC0294 /* PBXTargetDependency */, + FCE30EE514B539BF00CC0294 /* PBXTargetDependency */, + FCBA166B14A146D000AA698B /* PBXTargetDependency */, + FCBA166F14A146D000AA698B /* PBXTargetDependency */, + FCBA167114A146D000AA698B /* PBXTargetDependency */, + FCBA167314A146D000AA698B /* PBXTargetDependency */, + FCBA167514A146D000AA698B /* PBXTargetDependency */, + FCBA167714A146D000AA698B /* PBXTargetDependency */, + FCBA167914A146D000AA698B /* PBXTargetDependency */, + FCBA167B14A146D000AA698B /* PBXTargetDependency */, + FCBA167D14A146D000AA698B /* PBXTargetDependency */, + FCBA167F14A146D000AA698B /* PBXTargetDependency */, + FCBA168114A146D000AA698B /* PBXTargetDependency */, + FD6060BB1B7C03BC004BCA6A /* PBXTargetDependency */, + FCBA168314A146D000AA698B /* PBXTargetDependency */, + FCBA168514A146D000AA698B /* PBXTargetDependency */, + FCBA168714A146D000AA698B /* PBXTargetDependency */, + FCBA168914A146D000AA698B /* PBXTargetDependency */, + FCBA168B14A146D000AA698B /* PBXTargetDependency */, + FCBA168D14A146D000AA698B /* PBXTargetDependency */, + FCBA168F14A146D000AA698B /* PBXTargetDependency */, + FCBA169114A146D000AA698B /* PBXTargetDependency */, + FCBA169314A146D000AA698B /* PBXTargetDependency */, + FCBA169514A146D000AA698B /* PBXTargetDependency */, + FC5D63A414B9864400123E48 /* PBXTargetDependency */, + FCBA169714A146D000AA698B /* PBXTargetDependency */, + FCBA169914A146D000AA698B /* PBXTargetDependency */, + FCBA169B14A146D000AA698B /* PBXTargetDependency */, + FCBA169D14A146D000AA698B /* PBXTargetDependency */, + FCBA169F14A146D000AA698B /* PBXTargetDependency */, + ); + name = All_OSX; + productName = executables; + }; + FCE30F4A14B619B000CC0294 /* All_iOS */ = { + isa = PBXAggregateTarget; + buildConfigurationList = FCE30F4B14B619B000CC0294 /* Build configuration list for PBXAggregateTarget "All_iOS" */; + buildPhases = ( + FCE30F4D14B619C900CC0294 /* Run Script */, + ); + dependencies = ( + C6868581154725990025D623 /* PBXTargetDependency */, + FCE30F4F14B619E600CC0294 /* PBXTargetDependency */, + FCE30F5114B619E600CC0294 /* PBXTargetDependency */, + FCE30F5314B619E600CC0294 /* PBXTargetDependency */, + FCE30F5514B619E600CC0294 /* PBXTargetDependency */, + FCE30F5714B619E600CC0294 /* PBXTargetDependency */, + FCE30F5914B619E600CC0294 /* PBXTargetDependency */, + FCE30F5B14B619E600CC0294 /* PBXTargetDependency */, + FCE30F5D14B619E600CC0294 /* PBXTargetDependency */, + FCE30F5F14B619E600CC0294 /* PBXTargetDependency */, + FCE30F6114B619E600CC0294 /* PBXTargetDependency */, + FCE30F6314B619E600CC0294 /* PBXTargetDependency */, + FC5D63A614B9866500123E48 /* PBXTargetDependency */, + FCE30F6514B619E600CC0294 /* PBXTargetDependency */, + FCE30F6714B619E600CC0294 /* PBXTargetDependency */, + FCE30F6914B619E600CC0294 /* PBXTargetDependency */, + FCE30F6B14B619E600CC0294 /* PBXTargetDependency */, + FCE30F6D14B619E600CC0294 /* PBXTargetDependency */, + FCE30F6F14B619E600CC0294 /* PBXTargetDependency */, + FCE30F7114B619E600CC0294 /* PBXTargetDependency */, + FCE30F7314B619E600CC0294 /* PBXTargetDependency */, + FCE30F7514B619E600CC0294 /* PBXTargetDependency */, + FCE30F7714B619E600CC0294 /* PBXTargetDependency */, + FCE30F7914B619E600CC0294 /* PBXTargetDependency */, + FCE30F7B14B619E600CC0294 /* PBXTargetDependency */, + FCE30F7D14B619E600CC0294 /* PBXTargetDependency */, + FCE30F7F14B619E600CC0294 /* PBXTargetDependency */, + FCE30F8114B619E600CC0294 /* PBXTargetDependency */, + FCE30F8314B619E600CC0294 /* PBXTargetDependency */, + FCE30F8514B619E600CC0294 /* PBXTargetDependency */, + FD6060B91B7C03B3004BCA6A /* PBXTargetDependency */, + FCE30F8714B619E600CC0294 /* PBXTargetDependency */, + FCE30F8914B619E600CC0294 /* PBXTargetDependency */, + FCE30F8B14B619E600CC0294 /* PBXTargetDependency */, + FCE30F8D14B619E600CC0294 /* PBXTargetDependency */, + FCE30F8F14B619E600CC0294 /* PBXTargetDependency */, + FCE30F9114B619E600CC0294 /* PBXTargetDependency */, + FCE30F9314B619E600CC0294 /* PBXTargetDependency */, + FCE30F9514B619E600CC0294 /* PBXTargetDependency */, + FCE30F9714B619E600CC0294 /* PBXTargetDependency */, + FC5D63A814B9866500123E48 /* PBXTargetDependency */, + FCE30F9914B619E600CC0294 /* PBXTargetDependency */, + FCE30F9B14B619E600CC0294 /* PBXTargetDependency */, + FCE30F9D14B619E600CC0294 /* PBXTargetDependency */, + FCE30F9F14B619E600CC0294 /* PBXTargetDependency */, + FCE30FA114B619E600CC0294 /* PBXTargetDependency */, + ); + name = All_iOS; + productName = embedded; + }; +/* End PBXAggregateTarget section */ + +/* Begin PBXBuildFile section */ + C686857A154725700025D623 /* systime.c in Sources */ = {isa = PBXBuildFile; fileRef = C6868579154725700025D623 /* systime.c */; }; + C686857C154725700025D623 /* systime.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = C686857B154725700025D623 /* systime.1 */; }; + FC2B5BFB14B3CCC600ECF511 /* uname.c in Sources */ = {isa = PBXBuildFile; fileRef = FCBA140914A141A300AA698B /* uname.c */; }; + FC2B5BFD14B3CCD200ECF511 /* uname.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FCBA140814A141A300AA698B /* uname.1 */; }; + FC2B5BFE14B3CCD700ECF511 /* true.c in Sources */ = {isa = PBXBuildFile; fileRef = FCBA140414A141A300AA698B /* true.c */; }; + FC2B5BFF14B3CCEF00ECF511 /* true.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FCBA140314A141A300AA698B /* true.1 */; }; + FC2B5C0014B3CCF800ECF511 /* time.c in Sources */ = {isa = PBXBuildFile; fileRef = FCBA140014A141A300AA698B /* time.c */; }; + FC2B5C0114B3CD0300ECF511 /* time.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FCBA13FF14A141A300AA698B /* time.1 */; }; + FC2B5C0214B3CD0C00ECF511 /* test.c in Sources */ = {isa = PBXBuildFile; fileRef = FCBA13FB14A141A300AA698B /* test.c */; }; + FC2B5C0314B3CD1400ECF511 /* [.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FCBA13F814A141A300AA698B /* [.1 */; }; + FC2B5C0414B3CD1400ECF511 /* test.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FCBA13FA14A141A300AA698B /* test.1 */; }; + FC2B5C0514B3CD1D00ECF511 /* tee.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FCBA13F514A141A300AA698B /* tee.1 */; }; + FC2B5C0614B3CD2800ECF511 /* tee.c in Sources */ = {isa = PBXBuildFile; fileRef = FCBA13F614A141A300AA698B /* tee.c */; }; + FC2B5C0714B3CD2F00ECF511 /* su.c in Sources */ = {isa = PBXBuildFile; fileRef = FCBA13F114A141A300AA698B /* su.c */; }; + FC2B5C0814B3CD3500ECF511 /* su.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FCBA13F014A141A300AA698B /* su.1 */; }; + FC2B5C0914B3CD3E00ECF511 /* sleep.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FCBA13EC14A141A300AA698B /* sleep.1 */; }; + FC2B5C0A14B3CD4400ECF511 /* sleep.c in Sources */ = {isa = PBXBuildFile; fileRef = FCBA13ED14A141A300AA698B /* sleep.c */; }; + FC2B5C0B14B3CD4F00ECF511 /* shlock.c in Sources */ = {isa = PBXBuildFile; fileRef = FCBA13E914A141A300AA698B /* shlock.c */; }; + FC2B5C0C14B3CD5B00ECF511 /* shlock.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FCBA13E814A141A300AA698B /* shlock.1 */; }; + FC5D638D14B981D200123E48 /* what.c in Sources */ = {isa = PBXBuildFile; fileRef = FC5D637314B9808E00123E48 /* what.c */; }; + FC5D638E14B981D800123E48 /* what.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FC5D637214B9808E00123E48 /* what.1 */; }; + FC5D639914B9825B00123E48 /* hexdump.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FC5D636A14B9808E00123E48 /* hexdump.1 */; }; + FC5D639A14B9825B00123E48 /* od.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FC5D636E14B9808E00123E48 /* od.1 */; }; + FC5D639B14B9826500123E48 /* conv.c in Sources */ = {isa = PBXBuildFile; fileRef = FC5D636814B9808E00123E48 /* conv.c */; }; + FC5D639C14B9826500123E48 /* display.c in Sources */ = {isa = PBXBuildFile; fileRef = FC5D636914B9808E00123E48 /* display.c */; }; + FC5D639D14B9826500123E48 /* hexdump.c in Sources */ = {isa = PBXBuildFile; fileRef = FC5D636B14B9808E00123E48 /* hexdump.c */; }; + FC5D639E14B9826500123E48 /* hexsyntax.c in Sources */ = {isa = PBXBuildFile; fileRef = FC5D636D14B9808E00123E48 /* hexsyntax.c */; }; + FC5D639F14B9826500123E48 /* odsyntax.c in Sources */ = {isa = PBXBuildFile; fileRef = FC5D636F14B9808E00123E48 /* odsyntax.c */; }; + FC5D63A014B9826500123E48 /* parse.c in Sources */ = {isa = PBXBuildFile; fileRef = FC5D637014B9808E00123E48 /* parse.c */; }; + FCBA02E714B5061B0030BEB3 /* seq.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FCBA13E414A141A300AA698B /* seq.1 */; }; + FCBA02E814B506230030BEB3 /* seq.c in Sources */ = {isa = PBXBuildFile; fileRef = FCBA13E514A141A300AA698B /* seq.c */; }; + FCBA02E914B5062E0030BEB3 /* script.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FCBA13E014A141A300AA698B /* script.1 */; }; + FCBA02EA14B506340030BEB3 /* script.c in Sources */ = {isa = PBXBuildFile; fileRef = FCBA13E114A141A300AA698B /* script.c */; }; + FCBA02EB14B5063E0030BEB3 /* renice.8 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FCBA13DC14A141A300AA698B /* renice.8 */; }; + FCBA02EC14B5064C0030BEB3 /* renice.c in Sources */ = {isa = PBXBuildFile; fileRef = FCBA13DD14A141A300AA698B /* renice.c */; }; + FCBA02ED14B506530030BEB3 /* pwd.c in Sources */ = {isa = PBXBuildFile; fileRef = FCBA13D914A141A300AA698B /* pwd.c */; }; + FCBA02EF14B5065D0030BEB3 /* printf.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FCBA13D414A141A300AA698B /* printf.1 */; }; + FCBA02F014B506660030BEB3 /* printf.c in Sources */ = {isa = PBXBuildFile; fileRef = FCBA13D514A141A300AA698B /* printf.c */; }; + FCBA02F114B506720030BEB3 /* printenv.c in Sources */ = {isa = PBXBuildFile; fileRef = FCBA13D114A141A300AA698B /* printenv.c */; }; + FCBA02F214B5067C0030BEB3 /* printenv.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FCBA13D014A141A300AA698B /* printenv.1 */; }; + FCBA02F314B506840030BEB3 /* path_helper.c in Sources */ = {isa = PBXBuildFile; fileRef = FCBA13CD14A141A300AA698B /* path_helper.c */; }; + FCBA02F414B5068C0030BEB3 /* path_helper.8 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FCBA13CC14A141A300AA698B /* path_helper.8 */; }; + FCBA02F514B506940030BEB3 /* nohup.c in Sources */ = {isa = PBXBuildFile; fileRef = FCBA13C914A141A300AA698B /* nohup.c */; }; + FCBA02F614B5069C0030BEB3 /* nohup.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FCBA13C814A141A300AA698B /* nohup.1 */; }; + FCBA02F714B506B10030BEB3 /* nice.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FCBA13C414A141A300AA698B /* nice.1 */; }; + FCBA02F814B506BB0030BEB3 /* nice.c in Sources */ = {isa = PBXBuildFile; fileRef = FCBA13C514A141A300AA698B /* nice.c */; }; + FCBA02F914B506CB0030BEB3 /* mktemp.c in Sources */ = {isa = PBXBuildFile; fileRef = FCBA13C114A141A300AA698B /* mktemp.c */; }; + FCBA02FA14B506DA0030BEB3 /* mktemp.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FCBA13C014A141A300AA698B /* mktemp.1 */; }; + FCBA02FD14B506FA0030BEB3 /* logname.c in Sources */ = {isa = PBXBuildFile; fileRef = FCBA13B514A141A300AA698B /* logname.c */; }; + FCBA02FE14B506FF0030BEB3 /* logname.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FCBA13B414A141A300AA698B /* logname.1 */; }; + FCBA02FF14B507130030BEB3 /* expr.y in Sources */ = {isa = PBXBuildFile; fileRef = FCBA136714A141A300AA698B /* expr.y */; }; + FCBA030014B5071A0030BEB3 /* expr.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FCBA136614A141A300AA698B /* expr.1 */; }; + FCBA030114B507220030BEB3 /* false.c in Sources */ = {isa = PBXBuildFile; fileRef = FCBA136B14A141A300AA698B /* false.c */; }; + FCBA030214B5072C0030BEB3 /* false.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FCBA136A14A141A300AA698B /* false.1 */; }; + FCBA030314B5073F0030BEB3 /* find.c in Sources */ = {isa = PBXBuildFile; fileRef = FCBA137114A141A300AA698B /* find.c */; }; + FCBA030414B5073F0030BEB3 /* function.c in Sources */ = {isa = PBXBuildFile; fileRef = FCBA137314A141A300AA698B /* function.c */; }; + FCBA030514B5073F0030BEB3 /* getdate.y in Sources */ = {isa = PBXBuildFile; fileRef = FCBA137414A141A300AA698B /* getdate.y */; }; + FCBA030614B5073F0030BEB3 /* ls.c in Sources */ = {isa = PBXBuildFile; fileRef = FCBA137514A141A300AA698B /* ls.c */; }; + FCBA030714B5073F0030BEB3 /* main.c in Sources */ = {isa = PBXBuildFile; fileRef = FCBA137614A141A300AA698B /* main.c */; }; + FCBA030814B5073F0030BEB3 /* misc.c in Sources */ = {isa = PBXBuildFile; fileRef = FCBA137814A141A300AA698B /* misc.c */; }; + FCBA030914B5073F0030BEB3 /* operator.c in Sources */ = {isa = PBXBuildFile; fileRef = FCBA137914A141A300AA698B /* operator.c */; }; + FCBA030A14B5073F0030BEB3 /* option.c in Sources */ = {isa = PBXBuildFile; fileRef = FCBA137A14A141A300AA698B /* option.c */; }; + FCBA030B14B507450030BEB3 /* find.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FCBA137014A141A300AA698B /* find.1 */; }; + FCBA030C14B5074D0030BEB3 /* getopt.c in Sources */ = {isa = PBXBuildFile; fileRef = FCBA137D14A141A300AA698B /* getopt.c */; }; + FCBA030D14B507580030BEB3 /* getopt.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FCBA137C14A141A300AA698B /* getopt.1 */; }; + FCBA030E14B5076A0030BEB3 /* hostname.c in Sources */ = {isa = PBXBuildFile; fileRef = FCBA138114A141A300AA698B /* hostname.c */; }; + FCBA030F14B507700030BEB3 /* hostname.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FCBA138014A141A300AA698B /* hostname.1 */; }; + FCBA031014B507790030BEB3 /* id.c in Sources */ = {isa = PBXBuildFile; fileRef = FCBA138614A141A300AA698B /* id.c */; }; + FCBA031214B507870030BEB3 /* groups.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FCBA138414A141A300AA698B /* groups.1 */; }; + FCBA031314B507870030BEB3 /* id.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FCBA138514A141A300AA698B /* id.1 */; }; + FCBA031414B507870030BEB3 /* whoami.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FCBA138A14A141A300AA698B /* whoami.1 */; }; + FCBA031514B507900030BEB3 /* jot.c in Sources */ = {isa = PBXBuildFile; fileRef = FCBA138D14A141A300AA698B /* jot.c */; }; + FCBA031614B507950030BEB3 /* jot.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FCBA138C14A141A300AA698B /* jot.1 */; }; + FCBA031714B507A10030BEB3 /* kill.c in Sources */ = {isa = PBXBuildFile; fileRef = FCBA139114A141A300AA698B /* kill.c */; }; + FCBA031814B507A50030BEB3 /* kill.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FCBA139014A141A300AA698B /* kill.1 */; }; + FCBA031914B507B00030BEB3 /* killall.c in Sources */ = {isa = PBXBuildFile; fileRef = FCBA139514A141A300AA698B /* killall.c */; }; + FCBA031A14B507B60030BEB3 /* killall.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FCBA139414A141A300AA698B /* killall.1 */; }; + FCBA031B14B507BD0030BEB3 /* lastcomm.c in Sources */ = {isa = PBXBuildFile; fileRef = FCBA139914A141A300AA698B /* lastcomm.c */; }; + FCBA031C14B507C40030BEB3 /* lastcomm.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FCBA139814A141A300AA698B /* lastcomm.1 */; }; + FCBA031E14B507D10030BEB3 /* locate.c in Sources */ = {isa = PBXBuildFile; fileRef = FCBA13A814A141A300AA698B /* locate.c */; }; + FCBA031F14B507D10030BEB3 /* util.c in Sources */ = {isa = PBXBuildFile; fileRef = FCBA13B014A141A300AA698B /* util.c */; }; + FCBA032014B507E50030BEB3 /* locate.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FCBA13A714A141A300AA698B /* locate.1 */; }; + FCBA14EB14A1444900AA698B /* apply.c in Sources */ = {isa = PBXBuildFile; fileRef = FCBA134714A141A300AA698B /* apply.c */; }; + FCBA14ED14A1444E00AA698B /* apply.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FCBA134614A141A300AA698B /* apply.1 */; }; + FCBA14EE14A1446700AA698B /* basename.c in Sources */ = {isa = PBXBuildFile; fileRef = FCBA134B14A141A300AA698B /* basename.c */; }; + FCBA14EF14A1446B00AA698B /* basename.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FCBA134A14A141A300AA698B /* basename.1 */; }; + FCBA14F114A1448C00AA698B /* chroot.c in Sources */ = {isa = PBXBuildFile; fileRef = FCBA135014A141A300AA698B /* chroot.c */; }; + FCBA14F214A1448F00AA698B /* chroot.8 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FCBA134F14A141A300AA698B /* chroot.8 */; }; + FCBA14F314A1449A00AA698B /* date.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FCBA135314A141A300AA698B /* date.1 */; }; + FCBA14F414A1449C00AA698B /* date.c in Sources */ = {isa = PBXBuildFile; fileRef = FCBA135414A141A300AA698B /* date.c */; }; + FCBA14F514A144AF00AA698B /* netdate.c in Sources */ = {isa = PBXBuildFile; fileRef = FCBA135714A141A300AA698B /* netdate.c */; }; + FCBA14F614A144B200AA698B /* vary.c in Sources */ = {isa = PBXBuildFile; fileRef = FCBA135814A141A300AA698B /* vary.c */; }; + FCBA14F714A144C700AA698B /* dirname.c in Sources */ = {isa = PBXBuildFile; fileRef = FCBA135B14A141A300AA698B /* dirname.c */; }; + FCBA14F814A144CB00AA698B /* dirname.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FCBA134C14A141A300AA698B /* dirname.1 */; }; + FCBA14F914A144D000AA698B /* echo.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FCBA135E14A141A300AA698B /* echo.1 */; }; + FCBA14FA14A144D300AA698B /* echo.c in Sources */ = {isa = PBXBuildFile; fileRef = FCBA135F14A141A300AA698B /* echo.c */; }; + FCBA14FB14A144E300AA698B /* env.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FCBA136214A141A300AA698B /* env.1 */; }; + FCBA14FC14A144E500AA698B /* env.c in Sources */ = {isa = PBXBuildFile; fileRef = FCBA136314A141A300AA698B /* env.c */; }; + FCBA163E14A1466500AA698B /* yes.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FCBA142D14A141A300AA698B /* yes.1 */; }; + FCBA163F14A1466900AA698B /* yes.c in Sources */ = {isa = PBXBuildFile; fileRef = FCBA142E14A141A300AA698B /* yes.c */; }; + FCBA164014A1466F00AA698B /* xargs.c in Sources */ = {isa = PBXBuildFile; fileRef = FCBA142A14A141A300AA698B /* xargs.c */; }; + FCBA164114A1467200AA698B /* strnsubst.c in Sources */ = {isa = PBXBuildFile; fileRef = FCBA142814A141A300AA698B /* strnsubst.c */; }; + FCBA164214A1467500AA698B /* xargs.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FCBA142914A141A300AA698B /* xargs.1 */; }; + FCBA164314A1467A00AA698B /* utmpentry.c in Sources */ = {isa = PBXBuildFile; fileRef = FCBA142114A141A300AA698B /* utmpentry.c */; }; + FCBA164414A1467D00AA698B /* who.c in Sources */ = {isa = PBXBuildFile; fileRef = FCBA142414A141A300AA698B /* who.c */; }; + FCBA164514A1468000AA698B /* who.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FCBA142314A141A300AA698B /* who.1 */; }; + FCBA164614A1468600AA698B /* which.c in Sources */ = {isa = PBXBuildFile; fileRef = FCBA141E14A141A300AA698B /* which.c */; }; + FCBA164714A1468900AA698B /* which.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FCBA141D14A141A300AA698B /* which.1 */; }; + FCBA164814A1469000AA698B /* whereis.c in Sources */ = {isa = PBXBuildFile; fileRef = FCBA141A14A141A300AA698B /* whereis.c */; }; + FCBA164914A1469300AA698B /* whereis.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FCBA141914A141A300AA698B /* whereis.1 */; }; + FCBA164A14A1469A00AA698B /* fmt.c in Sources */ = {isa = PBXBuildFile; fileRef = FCBA141014A141A300AA698B /* fmt.c */; }; + FCBA164B14A1469D00AA698B /* pr_time.c in Sources */ = {isa = PBXBuildFile; fileRef = FCBA141214A141A300AA698B /* pr_time.c */; }; + FCBA164C14A1469F00AA698B /* proc_compare.c in Sources */ = {isa = PBXBuildFile; fileRef = FCBA141314A141A300AA698B /* proc_compare.c */; }; + FCBA164D14A146A200AA698B /* w.c in Sources */ = {isa = PBXBuildFile; fileRef = FCBA141614A141A300AA698B /* w.c */; }; + FCBA164E14A146A500AA698B /* uptime.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FCBA141414A141A300AA698B /* uptime.1 */; }; + FCBA164F14A146A700AA698B /* w.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FCBA141514A141A300AA698B /* w.1 */; }; + FCBA165014A146BE00AA698B /* users.c in Sources */ = {isa = PBXBuildFile; fileRef = FCBA140D14A141A300AA698B /* users.c */; }; + FCBA165114A146C100AA698B /* users.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FCBA140C14A141A300AA698B /* users.1 */; }; + FCE30EA814B5105200CC0294 /* pwd.1 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FCBA13D814A141A300AA698B /* pwd.1 */; }; + FCE30EBA14B532D900CC0294 /* com.apple.locate.plist in CopyFiles */ = {isa = PBXBuildFile; fileRef = FCBA13A414A141A300AA698B /* com.apple.locate.plist */; }; + FCE30EBD14B533A200CC0294 /* locate.rc in CopyFiles */ = {isa = PBXBuildFile; fileRef = FCBA13AA14A141A300AA698B /* locate.rc */; }; + FCE30EBF14B533E200CC0294 /* locate.updatedb.8 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FCBA13AB14A141A300AA698B /* locate.updatedb.8 */; }; + FCE30ED414B536A600CC0294 /* locate.bigram.c in Sources */ = {isa = PBXBuildFile; fileRef = FCBA139E14A141A300AA698B /* locate.bigram.c */; }; + FCE30ED514B536B200CC0294 /* locate.bigram.8 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FCE30EC114B5347A00CC0294 /* locate.bigram.8 */; }; + FCE30EE014B536F200CC0294 /* locate.code.c in Sources */ = {isa = PBXBuildFile; fileRef = FCBA13A114A141A300AA698B /* locate.code.c */; }; + FCE30EE114B536F800CC0294 /* locate.code.8 in CopyFiles */ = {isa = PBXBuildFile; fileRef = FCBA13B114A141A300AA698B /* locate.code.8 */; }; + FCED3AF614B4FC1800C313C3 /* libpam.dylib in Frameworks */ = {isa = PBXBuildFile; fileRef = FCED3AF514B4FC1800C313C3 /* libpam.dylib */; }; + FD155C751D6E37E5005A53CA /* legacy_test.sh in CopyFiles */ = {isa = PBXBuildFile; fileRef = FD155C721D6E37B5005A53CA /* legacy_test.sh */; }; + FD155C891D6E38C0005A53CA /* legacy_test.sh in CopyFiles */ = {isa = PBXBuildFile; fileRef = FD155C771D6E3847005A53CA /* legacy_test.sh */; }; + FD155C8A1D6E38C0005A53CA /* regress.b.out in CopyFiles */ = {isa = PBXBuildFile; fileRef = FD155C7A1D6E3847005A53CA /* regress.b.out */; }; + FD155C8B1D6E38C0005A53CA /* regress.d.out in CopyFiles */ = {isa = PBXBuildFile; fileRef = FD155C7B1D6E3847005A53CA /* regress.d.out */; }; + FD155C8C1D6E38C0005A53CA /* regress.f.out in CopyFiles */ = {isa = PBXBuildFile; fileRef = FD155C7C1D6E3847005A53CA /* regress.f.out */; }; + FD155C8D1D6E38C0005A53CA /* regress.l1.out in CopyFiles */ = {isa = PBXBuildFile; fileRef = FD155C7D1D6E3847005A53CA /* regress.l1.out */; }; + FD155C8E1D6E38C0005A53CA /* regress.l2.out in CopyFiles */ = {isa = PBXBuildFile; fileRef = FD155C7E1D6E3847005A53CA /* regress.l2.out */; }; + FD155C8F1D6E38C0005A53CA /* regress.m1.out in CopyFiles */ = {isa = PBXBuildFile; fileRef = FD155C7F1D6E3847005A53CA /* regress.m1.out */; }; + FD155C901D6E38C0005A53CA /* regress.m2.out in CopyFiles */ = {isa = PBXBuildFile; fileRef = FD155C801D6E3847005A53CA /* regress.m2.out */; }; + FD155C911D6E38C0005A53CA /* regress.m3.out in CopyFiles */ = {isa = PBXBuildFile; fileRef = FD155C811D6E3847005A53CA /* regress.m3.out */; }; + FD155C921D6E38C0005A53CA /* regress.m4.out in CopyFiles */ = {isa = PBXBuildFile; fileRef = FD155C821D6E3847005A53CA /* regress.m4.out */; }; + FD155C931D6E38C0005A53CA /* regress.m5.out in CopyFiles */ = {isa = PBXBuildFile; fileRef = FD155C831D6E3847005A53CA /* regress.m5.out */; }; + FD155C941D6E38C0005A53CA /* regress.missingpos1.out in CopyFiles */ = {isa = PBXBuildFile; fileRef = FD155C841D6E3847005A53CA /* regress.missingpos1.out */; }; + FD155C951D6E38C0005A53CA /* regress.s.out in CopyFiles */ = {isa = PBXBuildFile; fileRef = FD155C851D6E3847005A53CA /* regress.s.out */; }; + FD155C961D6E38C0005A53CA /* regress.sh in CopyFiles */ = {isa = PBXBuildFile; fileRef = FD155C861D6E3847005A53CA /* regress.sh */; }; + FD155C971D6E38C0005A53CA /* regress.zero.out in CopyFiles */ = {isa = PBXBuildFile; fileRef = FD155C871D6E3847005A53CA /* regress.zero.out */; }; + FD6060B51B7C0388004BCA6A /* main.c in Sources */ = {isa = PBXBuildFile; fileRef = FD6060B41B7C0388004BCA6A /* main.c */; }; + FD6060D21B7C0471004BCA6A /* alias.c in Sources */ = {isa = PBXBuildFile; fileRef = FD6060BC1B7C0471004BCA6A /* alias.c */; }; + FD6060D31B7C0471004BCA6A /* arith_yacc.c in Sources */ = {isa = PBXBuildFile; fileRef = FD6060BD1B7C0471004BCA6A /* arith_yacc.c */; }; + FD6060D41B7C0471004BCA6A /* arith_yylex.c in Sources */ = {isa = PBXBuildFile; fileRef = FD6060BE1B7C0471004BCA6A /* arith_yylex.c */; }; + FD6060D51B7C0471004BCA6A /* cd.c in Sources */ = {isa = PBXBuildFile; fileRef = FD6060BF1B7C0471004BCA6A /* cd.c */; }; + FD6060D61B7C0471004BCA6A /* error.c in Sources */ = {isa = PBXBuildFile; fileRef = FD6060C01B7C0471004BCA6A /* error.c */; }; + FD6060D71B7C0471004BCA6A /* eval.c in Sources */ = {isa = PBXBuildFile; fileRef = FD6060C11B7C0471004BCA6A /* eval.c */; }; + FD6060D81B7C0471004BCA6A /* exec.c in Sources */ = {isa = PBXBuildFile; fileRef = FD6060C21B7C0471004BCA6A /* exec.c */; }; + FD6060D91B7C0471004BCA6A /* expand.c in Sources */ = {isa = PBXBuildFile; fileRef = FD6060C31B7C0471004BCA6A /* expand.c */; }; + FD6060DA1B7C0471004BCA6A /* histedit.c in Sources */ = {isa = PBXBuildFile; fileRef = FD6060C41B7C0471004BCA6A /* histedit.c */; }; + FD6060DB1B7C0471004BCA6A /* input.c in Sources */ = {isa = PBXBuildFile; fileRef = FD6060C51B7C0471004BCA6A /* input.c */; }; + FD6060DC1B7C0471004BCA6A /* jobs.c in Sources */ = {isa = PBXBuildFile; fileRef = FD6060C61B7C0471004BCA6A /* jobs.c */; }; + FD6060DD1B7C0471004BCA6A /* mail.c in Sources */ = {isa = PBXBuildFile; fileRef = FD6060C71B7C0471004BCA6A /* mail.c */; }; + FD6060DE1B7C0471004BCA6A /* memalloc.c in Sources */ = {isa = PBXBuildFile; fileRef = FD6060C81B7C0471004BCA6A /* memalloc.c */; }; + FD6060DF1B7C0471004BCA6A /* miscbltin.c in Sources */ = {isa = PBXBuildFile; fileRef = FD6060C91B7C0471004BCA6A /* miscbltin.c */; }; + FD6060E01B7C0471004BCA6A /* mystring.c in Sources */ = {isa = PBXBuildFile; fileRef = FD6060CA1B7C0471004BCA6A /* mystring.c */; }; + FD6060E11B7C0471004BCA6A /* options.c in Sources */ = {isa = PBXBuildFile; fileRef = FD6060CB1B7C0471004BCA6A /* options.c */; }; + FD6060E21B7C0471004BCA6A /* output.c in Sources */ = {isa = PBXBuildFile; fileRef = FD6060CC1B7C0471004BCA6A /* output.c */; }; + FD6060E31B7C0471004BCA6A /* parser.c in Sources */ = {isa = PBXBuildFile; fileRef = FD6060CD1B7C0471004BCA6A /* parser.c */; }; + FD6060E41B7C0471004BCA6A /* redir.c in Sources */ = {isa = PBXBuildFile; fileRef = FD6060CE1B7C0471004BCA6A /* redir.c */; }; + FD6060E51B7C0471004BCA6A /* show.c in Sources */ = {isa = PBXBuildFile; fileRef = FD6060CF1B7C0471004BCA6A /* show.c */; }; + FD6060E61B7C0471004BCA6A /* trap.c in Sources */ = {isa = PBXBuildFile; fileRef = FD6060D01B7C0471004BCA6A /* trap.c */; }; + FD6060E71B7C0471004BCA6A /* var.c in Sources */ = {isa = PBXBuildFile; fileRef = FD6060D11B7C0471004BCA6A /* var.c */; }; + FD6060EB1B7C04E4004BCA6A /* echo.c in Sources */ = {isa = PBXBuildFile; fileRef = FD6060EA1B7C04E4004BCA6A /* echo.c */; }; + FD6060EC1B7C0506004BCA6A /* kill.c in Sources */ = {isa = PBXBuildFile; fileRef = FCBA139114A141A300AA698B /* kill.c */; }; + FD6060ED1B7C0518004BCA6A /* printf.c in Sources */ = {isa = PBXBuildFile; fileRef = FCBA13D514A141A300AA698B /* printf.c */; }; + FD6060EE1B7C0521004BCA6A /* test.c in Sources */ = {isa = PBXBuildFile; fileRef = FCBA13FB14A141A300AA698B /* test.c */; }; + FD6060FD1B7C0CAF004BCA6A /* builtins.c in Sources */ = {isa = PBXBuildFile; fileRef = FD6060FA1B7C0CAF004BCA6A /* builtins.c */; }; + FD6060FE1B7C0CAF004BCA6A /* nodes.c in Sources */ = {isa = PBXBuildFile; fileRef = FD6060FB1B7C0CAF004BCA6A /* nodes.c */; }; + FD6060FF1B7C0CAF004BCA6A /* syntax.c in Sources */ = {isa = PBXBuildFile; fileRef = FD6060FC1B7C0CAF004BCA6A /* syntax.c */; }; + FD88EB03198C5257006B7EFD /* envopts.c in Sources */ = {isa = PBXBuildFile; fileRef = FD88EB01198C5257006B7EFD /* envopts.c */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + C6868580154725990025D623 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FC80BF5714A05A2F00C6F7F5 /* Project object */; + proxyType = 1; + remoteGlobalIDString = C6868575154725700025D623; + remoteInfo = systime; + }; + C6868582154725A30025D623 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FC80BF5714A05A2F00C6F7F5 /* Project object */; + proxyType = 1; + remoteGlobalIDString = C6868575154725700025D623; + remoteInfo = systime; + }; + FC5D63A114B9864400123E48 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FC80BF5714A05A2F00C6F7F5 /* Project object */; + proxyType = 1; + remoteGlobalIDString = FC5D638F14B9822D00123E48; + remoteInfo = hexdump; + }; + FC5D63A314B9864400123E48 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FC80BF5714A05A2F00C6F7F5 /* Project object */; + proxyType = 1; + remoteGlobalIDString = FC5D637F14B9819E00123E48; + remoteInfo = what; + }; + FC5D63A514B9866500123E48 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FC80BF5714A05A2F00C6F7F5 /* Project object */; + proxyType = 1; + remoteGlobalIDString = FC5D638F14B9822D00123E48; + remoteInfo = hexdump; + }; + FC5D63A714B9866500123E48 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FC80BF5714A05A2F00C6F7F5 /* Project object */; + proxyType = 1; + remoteGlobalIDString = FC5D637F14B9819E00123E48; + remoteInfo = what; + }; + FCBA150014A144F700AA698B /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FC80BF5714A05A2F00C6F7F5 /* Project object */; + proxyType = 1; + remoteGlobalIDString = FCBA149814A141FF00AA698B; + remoteInfo = apply; + }; + FCBA150214A144F700AA698B /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FC80BF5714A05A2F00C6F7F5 /* Project object */; + proxyType = 1; + remoteGlobalIDString = FCBA14AE14A1422D00AA698B; + remoteInfo = basename; + }; + FCBA150414A144F700AA698B /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FC80BF5714A05A2F00C6F7F5 /* Project object */; + proxyType = 1; + remoteGlobalIDString = FCBA14B714A1423E00AA698B; + remoteInfo = chroot; + }; + FCBA150614A144F700AA698B /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FC80BF5714A05A2F00C6F7F5 /* Project object */; + proxyType = 1; + remoteGlobalIDString = FCBA14BF14A1426800AA698B; + remoteInfo = date; + }; + FCBA150814A144F700AA698B /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FC80BF5714A05A2F00C6F7F5 /* Project object */; + proxyType = 1; + remoteGlobalIDString = FCBA14C714A1428000AA698B; + remoteInfo = dirname; + }; + FCBA150A14A144F700AA698B /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FC80BF5714A05A2F00C6F7F5 /* Project object */; + proxyType = 1; + remoteGlobalIDString = FCBA14CF14A1433F00AA698B; + remoteInfo = echo; + }; + FCBA150C14A144F700AA698B /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FC80BF5714A05A2F00C6F7F5 /* Project object */; + proxyType = 1; + remoteGlobalIDString = FCBA14D714A1434A00AA698B; + remoteInfo = env; + }; + FCBA165414A146D000AA698B /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FC80BF5714A05A2F00C6F7F5 /* Project object */; + proxyType = 1; + remoteGlobalIDString = FCBA150E14A1453A00AA698B; + remoteInfo = expr; + }; + FCBA165614A146D000AA698B /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FC80BF5714A05A2F00C6F7F5 /* Project object */; + proxyType = 1; + remoteGlobalIDString = FCBA151614A1454D00AA698B; + remoteInfo = false; + }; + FCBA165814A146D000AA698B /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FC80BF5714A05A2F00C6F7F5 /* Project object */; + proxyType = 1; + remoteGlobalIDString = FCBA151E14A1455600AA698B; + remoteInfo = find; + }; + FCBA165A14A146D000AA698B /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FC80BF5714A05A2F00C6F7F5 /* Project object */; + proxyType = 1; + remoteGlobalIDString = FCBA152614A1455900AA698B; + remoteInfo = getopt; + }; + FCBA165C14A146D000AA698B /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FC80BF5714A05A2F00C6F7F5 /* Project object */; + proxyType = 1; + remoteGlobalIDString = FCBA152E14A1455C00AA698B; + remoteInfo = hostname; + }; + FCBA165E14A146D000AA698B /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FC80BF5714A05A2F00C6F7F5 /* Project object */; + proxyType = 1; + remoteGlobalIDString = FCBA153614A1455F00AA698B; + remoteInfo = id; + }; + FCBA166014A146D000AA698B /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FC80BF5714A05A2F00C6F7F5 /* Project object */; + proxyType = 1; + remoteGlobalIDString = FCBA153E14A1456100AA698B; + remoteInfo = jot; + }; + FCBA166214A146D000AA698B /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FC80BF5714A05A2F00C6F7F5 /* Project object */; + proxyType = 1; + remoteGlobalIDString = FCBA154614A1456400AA698B; + remoteInfo = kill; + }; + FCBA166414A146D000AA698B /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FC80BF5714A05A2F00C6F7F5 /* Project object */; + proxyType = 1; + remoteGlobalIDString = FCBA154E14A1456700AA698B; + remoteInfo = killall; + }; + FCBA166614A146D000AA698B /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FC80BF5714A05A2F00C6F7F5 /* Project object */; + proxyType = 1; + remoteGlobalIDString = FCBA155614A1456A00AA698B; + remoteInfo = lastcomm; + }; + FCBA166814A146D000AA698B /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FC80BF5714A05A2F00C6F7F5 /* Project object */; + proxyType = 1; + remoteGlobalIDString = FCBA155E14A1456E00AA698B; + remoteInfo = locate; + }; + FCBA166A14A146D000AA698B /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FC80BF5714A05A2F00C6F7F5 /* Project object */; + proxyType = 1; + remoteGlobalIDString = FCBA156614A1457100AA698B; + remoteInfo = logname; + }; + FCBA166E14A146D000AA698B /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FC80BF5714A05A2F00C6F7F5 /* Project object */; + proxyType = 1; + remoteGlobalIDString = FCBA157614A1457B00AA698B; + remoteInfo = mktemp; + }; + FCBA167014A146D000AA698B /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FC80BF5714A05A2F00C6F7F5 /* Project object */; + proxyType = 1; + remoteGlobalIDString = FCBA157E14A1457F00AA698B; + remoteInfo = nice; + }; + FCBA167214A146D000AA698B /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FC80BF5714A05A2F00C6F7F5 /* Project object */; + proxyType = 1; + remoteGlobalIDString = FCBA158614A1458500AA698B; + remoteInfo = nohup; + }; + FCBA167414A146D000AA698B /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FC80BF5714A05A2F00C6F7F5 /* Project object */; + proxyType = 1; + remoteGlobalIDString = FCBA158E14A1458800AA698B; + remoteInfo = path_helper; + }; + FCBA167614A146D000AA698B /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FC80BF5714A05A2F00C6F7F5 /* Project object */; + proxyType = 1; + remoteGlobalIDString = FCBA159614A1458C00AA698B; + remoteInfo = printenv; + }; + FCBA167814A146D000AA698B /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FC80BF5714A05A2F00C6F7F5 /* Project object */; + proxyType = 1; + remoteGlobalIDString = FCBA159E14A1459000AA698B; + remoteInfo = printf; + }; + FCBA167A14A146D000AA698B /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FC80BF5714A05A2F00C6F7F5 /* Project object */; + proxyType = 1; + remoteGlobalIDString = FCBA15A614A1459500AA698B; + remoteInfo = pwd; + }; + FCBA167C14A146D000AA698B /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FC80BF5714A05A2F00C6F7F5 /* Project object */; + proxyType = 1; + remoteGlobalIDString = FCBA15AE14A1459700AA698B; + remoteInfo = renice; + }; + FCBA167E14A146D000AA698B /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FC80BF5714A05A2F00C6F7F5 /* Project object */; + proxyType = 1; + remoteGlobalIDString = FCBA15B614A1459900AA698B; + remoteInfo = script; + }; + FCBA168014A146D000AA698B /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FC80BF5714A05A2F00C6F7F5 /* Project object */; + proxyType = 1; + remoteGlobalIDString = FCBA15BE14A1459E00AA698B; + remoteInfo = seq; + }; + FCBA168214A146D000AA698B /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FC80BF5714A05A2F00C6F7F5 /* Project object */; + proxyType = 1; + remoteGlobalIDString = FCBA15C614A145A200AA698B; + remoteInfo = shlock; + }; + FCBA168414A146D000AA698B /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FC80BF5714A05A2F00C6F7F5 /* Project object */; + proxyType = 1; + remoteGlobalIDString = FCBA15CE14A145A500AA698B; + remoteInfo = sleep; + }; + FCBA168614A146D000AA698B /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FC80BF5714A05A2F00C6F7F5 /* Project object */; + proxyType = 1; + remoteGlobalIDString = FCBA15D614A145A700AA698B; + remoteInfo = su; + }; + FCBA168814A146D000AA698B /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FC80BF5714A05A2F00C6F7F5 /* Project object */; + proxyType = 1; + remoteGlobalIDString = FCBA15DE14A145A900AA698B; + remoteInfo = tee; + }; + FCBA168A14A146D000AA698B /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FC80BF5714A05A2F00C6F7F5 /* Project object */; + proxyType = 1; + remoteGlobalIDString = FCBA15E614A145AE00AA698B; + remoteInfo = test; + }; + FCBA168C14A146D000AA698B /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FC80BF5714A05A2F00C6F7F5 /* Project object */; + proxyType = 1; + remoteGlobalIDString = FCBA15EE14A145B000AA698B; + remoteInfo = time; + }; + FCBA168E14A146D000AA698B /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FC80BF5714A05A2F00C6F7F5 /* Project object */; + proxyType = 1; + remoteGlobalIDString = FCBA15F614A145B200AA698B; + remoteInfo = true; + }; + FCBA169014A146D000AA698B /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FC80BF5714A05A2F00C6F7F5 /* Project object */; + proxyType = 1; + remoteGlobalIDString = FCBA15FE14A145B500AA698B; + remoteInfo = uname; + }; + FCBA169214A146D000AA698B /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FC80BF5714A05A2F00C6F7F5 /* Project object */; + proxyType = 1; + remoteGlobalIDString = FCBA160614A145B800AA698B; + remoteInfo = users; + }; + FCBA169414A146D000AA698B /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FC80BF5714A05A2F00C6F7F5 /* Project object */; + proxyType = 1; + remoteGlobalIDString = FCBA160E14A145BA00AA698B; + remoteInfo = w; + }; + FCBA169614A146D000AA698B /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FC80BF5714A05A2F00C6F7F5 /* Project object */; + proxyType = 1; + remoteGlobalIDString = FCBA161614A145BC00AA698B; + remoteInfo = whereis; + }; + FCBA169814A146D000AA698B /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FC80BF5714A05A2F00C6F7F5 /* Project object */; + proxyType = 1; + remoteGlobalIDString = FCBA161E14A145C000AA698B; + remoteInfo = which; + }; + FCBA169A14A146D000AA698B /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FC80BF5714A05A2F00C6F7F5 /* Project object */; + proxyType = 1; + remoteGlobalIDString = FCBA162614A145C500AA698B; + remoteInfo = who; + }; + FCBA169C14A146D000AA698B /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FC80BF5714A05A2F00C6F7F5 /* Project object */; + proxyType = 1; + remoteGlobalIDString = FCBA162E14A145C700AA698B; + remoteInfo = xargs; + }; + FCBA169E14A146D000AA698B /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FC80BF5714A05A2F00C6F7F5 /* Project object */; + proxyType = 1; + remoteGlobalIDString = FCBA163614A145CA00AA698B; + remoteInfo = yes; + }; + FCE30EE214B539BF00CC0294 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FC80BF5714A05A2F00C6F7F5 /* Project object */; + proxyType = 1; + remoteGlobalIDString = FCE30EC214B5368A00CC0294; + remoteInfo = locate.bigram; + }; + FCE30EE414B539BF00CC0294 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FC80BF5714A05A2F00C6F7F5 /* Project object */; + proxyType = 1; + remoteGlobalIDString = FCE30ED614B536C900CC0294; + remoteInfo = locate.code; + }; + FCE30F4E14B619E600CC0294 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FC80BF5714A05A2F00C6F7F5 /* Project object */; + proxyType = 1; + remoteGlobalIDString = FCBA149814A141FF00AA698B; + remoteInfo = apply; + }; + FCE30F5014B619E600CC0294 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FC80BF5714A05A2F00C6F7F5 /* Project object */; + proxyType = 1; + remoteGlobalIDString = FCBA14AE14A1422D00AA698B; + remoteInfo = basename; + }; + FCE30F5214B619E600CC0294 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FC80BF5714A05A2F00C6F7F5 /* Project object */; + proxyType = 1; + remoteGlobalIDString = FCBA14B714A1423E00AA698B; + remoteInfo = chroot; + }; + FCE30F5414B619E600CC0294 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FC80BF5714A05A2F00C6F7F5 /* Project object */; + proxyType = 1; + remoteGlobalIDString = FCBA14BF14A1426800AA698B; + remoteInfo = date; + }; + FCE30F5614B619E600CC0294 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FC80BF5714A05A2F00C6F7F5 /* Project object */; + proxyType = 1; + remoteGlobalIDString = FCBA14C714A1428000AA698B; + remoteInfo = dirname; + }; + FCE30F5814B619E600CC0294 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FC80BF5714A05A2F00C6F7F5 /* Project object */; + proxyType = 1; + remoteGlobalIDString = FCBA14CF14A1433F00AA698B; + remoteInfo = echo; + }; + FCE30F5A14B619E600CC0294 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FC80BF5714A05A2F00C6F7F5 /* Project object */; + proxyType = 1; + remoteGlobalIDString = FCBA14D714A1434A00AA698B; + remoteInfo = env; + }; + FCE30F5C14B619E600CC0294 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FC80BF5714A05A2F00C6F7F5 /* Project object */; + proxyType = 1; + remoteGlobalIDString = FCBA150E14A1453A00AA698B; + remoteInfo = expr; + }; + FCE30F5E14B619E600CC0294 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FC80BF5714A05A2F00C6F7F5 /* Project object */; + proxyType = 1; + remoteGlobalIDString = FCBA151614A1454D00AA698B; + remoteInfo = false; + }; + FCE30F6014B619E600CC0294 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FC80BF5714A05A2F00C6F7F5 /* Project object */; + proxyType = 1; + remoteGlobalIDString = FCBA151E14A1455600AA698B; + remoteInfo = find; + }; + FCE30F6214B619E600CC0294 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FC80BF5714A05A2F00C6F7F5 /* Project object */; + proxyType = 1; + remoteGlobalIDString = FCBA152614A1455900AA698B; + remoteInfo = getopt; + }; + FCE30F6414B619E600CC0294 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FC80BF5714A05A2F00C6F7F5 /* Project object */; + proxyType = 1; + remoteGlobalIDString = FCBA152E14A1455C00AA698B; + remoteInfo = hostname; + }; + FCE30F6614B619E600CC0294 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FC80BF5714A05A2F00C6F7F5 /* Project object */; + proxyType = 1; + remoteGlobalIDString = FCBA153614A1455F00AA698B; + remoteInfo = id; + }; + FCE30F6814B619E600CC0294 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FC80BF5714A05A2F00C6F7F5 /* Project object */; + proxyType = 1; + remoteGlobalIDString = FCBA153E14A1456100AA698B; + remoteInfo = jot; + }; + FCE30F6A14B619E600CC0294 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FC80BF5714A05A2F00C6F7F5 /* Project object */; + proxyType = 1; + remoteGlobalIDString = FCBA154614A1456400AA698B; + remoteInfo = kill; + }; + FCE30F6C14B619E600CC0294 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FC80BF5714A05A2F00C6F7F5 /* Project object */; + proxyType = 1; + remoteGlobalIDString = FCBA154E14A1456700AA698B; + remoteInfo = killall; + }; + FCE30F6E14B619E600CC0294 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FC80BF5714A05A2F00C6F7F5 /* Project object */; + proxyType = 1; + remoteGlobalIDString = FCBA155614A1456A00AA698B; + remoteInfo = lastcomm; + }; + FCE30F7014B619E600CC0294 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FC80BF5714A05A2F00C6F7F5 /* Project object */; + proxyType = 1; + remoteGlobalIDString = FCBA156614A1457100AA698B; + remoteInfo = logname; + }; + FCE30F7214B619E600CC0294 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FC80BF5714A05A2F00C6F7F5 /* Project object */; + proxyType = 1; + remoteGlobalIDString = FCBA157614A1457B00AA698B; + remoteInfo = mktemp; + }; + FCE30F7414B619E600CC0294 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FC80BF5714A05A2F00C6F7F5 /* Project object */; + proxyType = 1; + remoteGlobalIDString = FCBA157E14A1457F00AA698B; + remoteInfo = nice; + }; + FCE30F7614B619E600CC0294 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FC80BF5714A05A2F00C6F7F5 /* Project object */; + proxyType = 1; + remoteGlobalIDString = FCBA158614A1458500AA698B; + remoteInfo = nohup; + }; + FCE30F7814B619E600CC0294 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FC80BF5714A05A2F00C6F7F5 /* Project object */; + proxyType = 1; + remoteGlobalIDString = FCBA158E14A1458800AA698B; + remoteInfo = path_helper; + }; + FCE30F7A14B619E600CC0294 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FC80BF5714A05A2F00C6F7F5 /* Project object */; + proxyType = 1; + remoteGlobalIDString = FCBA159614A1458C00AA698B; + remoteInfo = printenv; + }; + FCE30F7C14B619E600CC0294 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FC80BF5714A05A2F00C6F7F5 /* Project object */; + proxyType = 1; + remoteGlobalIDString = FCBA159E14A1459000AA698B; + remoteInfo = printf; + }; + FCE30F7E14B619E600CC0294 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FC80BF5714A05A2F00C6F7F5 /* Project object */; + proxyType = 1; + remoteGlobalIDString = FCBA15A614A1459500AA698B; + remoteInfo = pwd; + }; + FCE30F8014B619E600CC0294 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FC80BF5714A05A2F00C6F7F5 /* Project object */; + proxyType = 1; + remoteGlobalIDString = FCBA15AE14A1459700AA698B; + remoteInfo = renice; + }; + FCE30F8214B619E600CC0294 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FC80BF5714A05A2F00C6F7F5 /* Project object */; + proxyType = 1; + remoteGlobalIDString = FCBA15B614A1459900AA698B; + remoteInfo = script; + }; + FCE30F8414B619E600CC0294 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FC80BF5714A05A2F00C6F7F5 /* Project object */; + proxyType = 1; + remoteGlobalIDString = FCBA15BE14A1459E00AA698B; + remoteInfo = seq; + }; + FCE30F8614B619E600CC0294 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FC80BF5714A05A2F00C6F7F5 /* Project object */; + proxyType = 1; + remoteGlobalIDString = FCBA15C614A145A200AA698B; + remoteInfo = shlock; + }; + FCE30F8814B619E600CC0294 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FC80BF5714A05A2F00C6F7F5 /* Project object */; + proxyType = 1; + remoteGlobalIDString = FCBA15CE14A145A500AA698B; + remoteInfo = sleep; + }; + FCE30F8A14B619E600CC0294 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FC80BF5714A05A2F00C6F7F5 /* Project object */; + proxyType = 1; + remoteGlobalIDString = FCBA15DE14A145A900AA698B; + remoteInfo = tee; + }; + FCE30F8C14B619E600CC0294 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FC80BF5714A05A2F00C6F7F5 /* Project object */; + proxyType = 1; + remoteGlobalIDString = FCBA15E614A145AE00AA698B; + remoteInfo = test; + }; + FCE30F8E14B619E600CC0294 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FC80BF5714A05A2F00C6F7F5 /* Project object */; + proxyType = 1; + remoteGlobalIDString = FCBA15EE14A145B000AA698B; + remoteInfo = time; + }; + FCE30F9014B619E600CC0294 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FC80BF5714A05A2F00C6F7F5 /* Project object */; + proxyType = 1; + remoteGlobalIDString = FCBA15F614A145B200AA698B; + remoteInfo = true; + }; + FCE30F9214B619E600CC0294 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FC80BF5714A05A2F00C6F7F5 /* Project object */; + proxyType = 1; + remoteGlobalIDString = FCBA15FE14A145B500AA698B; + remoteInfo = uname; + }; + FCE30F9414B619E600CC0294 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FC80BF5714A05A2F00C6F7F5 /* Project object */; + proxyType = 1; + remoteGlobalIDString = FCBA160614A145B800AA698B; + remoteInfo = users; + }; + FCE30F9614B619E600CC0294 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FC80BF5714A05A2F00C6F7F5 /* Project object */; + proxyType = 1; + remoteGlobalIDString = FCBA160E14A145BA00AA698B; + remoteInfo = w; + }; + FCE30F9814B619E600CC0294 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FC80BF5714A05A2F00C6F7F5 /* Project object */; + proxyType = 1; + remoteGlobalIDString = FCBA161614A145BC00AA698B; + remoteInfo = whereis; + }; + FCE30F9A14B619E600CC0294 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FC80BF5714A05A2F00C6F7F5 /* Project object */; + proxyType = 1; + remoteGlobalIDString = FCBA161E14A145C000AA698B; + remoteInfo = which; + }; + FCE30F9C14B619E600CC0294 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FC80BF5714A05A2F00C6F7F5 /* Project object */; + proxyType = 1; + remoteGlobalIDString = FCBA162614A145C500AA698B; + remoteInfo = who; + }; + FCE30F9E14B619E600CC0294 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FC80BF5714A05A2F00C6F7F5 /* Project object */; + proxyType = 1; + remoteGlobalIDString = FCBA162E14A145C700AA698B; + remoteInfo = xargs; + }; + FCE30FA014B619E600CC0294 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FC80BF5714A05A2F00C6F7F5 /* Project object */; + proxyType = 1; + remoteGlobalIDString = FCBA163614A145CA00AA698B; + remoteInfo = yes; + }; + FD6060B81B7C03B3004BCA6A /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FC80BF5714A05A2F00C6F7F5 /* Project object */; + proxyType = 1; + remoteGlobalIDString = FD6060B11B7C0388004BCA6A; + remoteInfo = sh; + }; + FD6060BA1B7C03BC004BCA6A /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = FC80BF5714A05A2F00C6F7F5 /* Project object */; + proxyType = 1; + remoteGlobalIDString = FD6060B11B7C0388004BCA6A; + remoteInfo = sh; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXCopyFilesBuildPhase section */ + C6868574154725700025D623 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/local/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + C686857C154725700025D623 /* systime.1 in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; + FC5D638614B9819E00123E48 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + FC5D638E14B981D800123E48 /* what.1 in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; + FC5D639314B9822D00123E48 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + FC5D639914B9825B00123E48 /* hexdump.1 in CopyFiles */, + FC5D639A14B9825B00123E48 /* od.1 in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; + FCBA149B14A141FF00AA698B /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + FCBA14ED14A1444E00AA698B /* apply.1 in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; + FCBA14B114A1422D00AA698B /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + FCBA14EF14A1446B00AA698B /* basename.1 in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; + FCBA14BA14A1423E00AA698B /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man8/; + dstSubfolderSpec = 0; + files = ( + FCBA14F214A1448F00AA698B /* chroot.8 in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; + FCBA14C214A1426800AA698B /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + FCBA14F314A1449A00AA698B /* date.1 in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; + FCBA14CA14A1428000AA698B /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + FCBA14F814A144CB00AA698B /* dirname.1 in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; + FCBA14D214A1433F00AA698B /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + FCBA14F914A144D000AA698B /* echo.1 in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; + FCBA14DA14A1434A00AA698B /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + FCBA14FB14A144E300AA698B /* env.1 in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; + FCBA151114A1453A00AA698B /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + FCBA030014B5071A0030BEB3 /* expr.1 in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; + FCBA151914A1454D00AA698B /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + FCBA030214B5072C0030BEB3 /* false.1 in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; + FCBA152114A1455600AA698B /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + FCBA030B14B507450030BEB3 /* find.1 in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; + FCBA152914A1455900AA698B /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + FCBA030D14B507580030BEB3 /* getopt.1 in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; + FCBA153114A1455C00AA698B /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + FCBA030F14B507700030BEB3 /* hostname.1 in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; + FCBA153914A1455F00AA698B /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + FCBA031214B507870030BEB3 /* groups.1 in CopyFiles */, + FCBA031314B507870030BEB3 /* id.1 in CopyFiles */, + FCBA031414B507870030BEB3 /* whoami.1 in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; + FCBA154114A1456100AA698B /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + FCBA031614B507950030BEB3 /* jot.1 in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; + FCBA154914A1456400AA698B /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + FCBA031814B507A50030BEB3 /* kill.1 in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; + FCBA155114A1456700AA698B /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + FCBA031A14B507B60030BEB3 /* killall.1 in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; + FCBA155914A1456A00AA698B /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + FCBA031C14B507C40030BEB3 /* lastcomm.1 in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; + FCBA156114A1456E00AA698B /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + FCBA032014B507E50030BEB3 /* locate.1 in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; + FCBA156914A1457100AA698B /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + FCBA02FE14B506FF0030BEB3 /* logname.1 in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; + FCBA157914A1457B00AA698B /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + FCBA02FA14B506DA0030BEB3 /* mktemp.1 in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; + FCBA158114A1457F00AA698B /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + FCBA02F714B506B10030BEB3 /* nice.1 in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; + FCBA158914A1458500AA698B /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + FCBA02F614B5069C0030BEB3 /* nohup.1 in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; + FCBA159114A1458800AA698B /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man8/; + dstSubfolderSpec = 0; + files = ( + FCBA02F414B5068C0030BEB3 /* path_helper.8 in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; + FCBA159914A1458C00AA698B /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + FCBA02F214B5067C0030BEB3 /* printenv.1 in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; + FCBA15A114A1459000AA698B /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + FCBA02EF14B5065D0030BEB3 /* printf.1 in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; + FCBA15A914A1459500AA698B /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + FCE30EA814B5105200CC0294 /* pwd.1 in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; + FCBA15B114A1459700AA698B /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man8/; + dstSubfolderSpec = 0; + files = ( + FCBA02EB14B5063E0030BEB3 /* renice.8 in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; + FCBA15B914A1459900AA698B /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + FCBA02E914B5062E0030BEB3 /* script.1 in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; + FCBA15C114A1459E00AA698B /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + FCBA02E714B5061B0030BEB3 /* seq.1 in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; + FCBA15C914A145A200AA698B /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + FC2B5C0C14B3CD5B00ECF511 /* shlock.1 in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; + FCBA15D114A145A500AA698B /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + FC2B5C0914B3CD3E00ECF511 /* sleep.1 in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; + FCBA15D914A145A700AA698B /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + FC2B5C0814B3CD3500ECF511 /* su.1 in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; + FCBA15E114A145A900AA698B /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + FC2B5C0514B3CD1D00ECF511 /* tee.1 in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; + FCBA15E914A145AE00AA698B /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + FC2B5C0314B3CD1400ECF511 /* [.1 in CopyFiles */, + FC2B5C0414B3CD1400ECF511 /* test.1 in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; + FCBA15F114A145B000AA698B /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + FC2B5C0114B3CD0300ECF511 /* time.1 in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; + FCBA15F914A145B200AA698B /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + FC2B5BFF14B3CCEF00ECF511 /* true.1 in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; + FCBA160114A145B500AA698B /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + FC2B5BFD14B3CCD200ECF511 /* uname.1 in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; + FCBA160914A145B800AA698B /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + FCBA165114A146C100AA698B /* users.1 in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; + FCBA161114A145BA00AA698B /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + FCBA164F14A146A700AA698B /* w.1 in CopyFiles */, + FCBA164E14A146A500AA698B /* uptime.1 in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; + FCBA161914A145BC00AA698B /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + FCBA164914A1469300AA698B /* whereis.1 in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; + FCBA162114A145C000AA698B /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + FCBA164714A1468900AA698B /* which.1 in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; + FCBA162914A145C500AA698B /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + FCBA164514A1468000AA698B /* who.1 in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; + FCBA163114A145C700AA698B /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + FCBA164214A1467500AA698B /* xargs.1 in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; + FCBA163914A145CA00AA698B /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man1/; + dstSubfolderSpec = 0; + files = ( + FCBA163E14A1466500AA698B /* yes.1 in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; + FCE30EB914B532C700CC0294 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 8; + dstPath = /System/Library/LaunchDaemons/; + dstSubfolderSpec = 0; + files = ( + FCE30EBA14B532D900CC0294 /* com.apple.locate.plist in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; + FCE30EBC14B5339000CC0294 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 8; + dstPath = /private/etc/; + dstSubfolderSpec = 0; + files = ( + FCE30EBD14B533A200CC0294 /* locate.rc in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; + FCE30EBE14B533B500CC0294 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 8; + dstPath = /usr/share/man/man8/; + dstSubfolderSpec = 0; + files = ( + FCE30EBF14B533E200CC0294 /* locate.updatedb.8 in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; + FCE30EC714B5368A00CC0294 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man8/; + dstSubfolderSpec = 0; + files = ( + FCE30ED514B536B200CC0294 /* locate.bigram.8 in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; + FCE30EDA14B536C900CC0294 /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /usr/share/man/man8/; + dstSubfolderSpec = 0; + files = ( + FCE30EE114B536F800CC0294 /* locate.code.8 in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 1; + }; + FD155C741D6E37D7005A53CA /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /AppleInternal/Tests/shell_cmds/test; + dstSubfolderSpec = 0; + files = ( + FD155C751D6E37E5005A53CA /* legacy_test.sh in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FD155C881D6E38AB005A53CA /* CopyFiles */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = /AppleInternal/Tests/shell_cmds/printf; + dstSubfolderSpec = 0; + files = ( + FD155C891D6E38C0005A53CA /* legacy_test.sh in CopyFiles */, + FD155C8A1D6E38C0005A53CA /* regress.b.out in CopyFiles */, + FD155C8B1D6E38C0005A53CA /* regress.d.out in CopyFiles */, + FD155C8C1D6E38C0005A53CA /* regress.f.out in CopyFiles */, + FD155C8D1D6E38C0005A53CA /* regress.l1.out in CopyFiles */, + FD155C8E1D6E38C0005A53CA /* regress.l2.out in CopyFiles */, + FD155C8F1D6E38C0005A53CA /* regress.m1.out in CopyFiles */, + FD155C901D6E38C0005A53CA /* regress.m2.out in CopyFiles */, + FD155C911D6E38C0005A53CA /* regress.m3.out in CopyFiles */, + FD155C921D6E38C0005A53CA /* regress.m4.out in CopyFiles */, + FD155C931D6E38C0005A53CA /* regress.m5.out in CopyFiles */, + FD155C941D6E38C0005A53CA /* regress.missingpos1.out in CopyFiles */, + FD155C951D6E38C0005A53CA /* regress.s.out in CopyFiles */, + FD155C961D6E38C0005A53CA /* regress.sh in CopyFiles */, + FD155C971D6E38C0005A53CA /* regress.zero.out in CopyFiles */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + +/* Begin PBXFileReference section */ + 6C1C283E22FB822900E0AC67 /* su_entitlements.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = su_entitlements.plist; sourceTree = "<group>"; }; + C6868576154725700025D623 /* systime */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = systime; sourceTree = BUILT_PRODUCTS_DIR; }; + C6868579154725700025D623 /* systime.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = systime.c; sourceTree = "<group>"; }; + C686857B154725700025D623 /* systime.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = systime.1; sourceTree = "<group>"; }; + CE799E6024AD3B1B00E73238 /* test_time.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; path = test_time.sh; sourceTree = "<group>"; usesTabs = 1; }; + CE799E6224B3982200E73238 /* install-files.sh */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; path = "install-files.sh"; sourceTree = "<group>"; }; + CE799E6324B3982200E73238 /* builtins.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = builtins.txt; sourceTree = "<group>"; }; + CE799E6424B3982200E73238 /* builtins-manpages.txt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = "builtins-manpages.txt"; sourceTree = "<group>"; }; + FC5D636814B9808E00123E48 /* conv.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = conv.c; sourceTree = "<group>"; }; + FC5D636914B9808E00123E48 /* display.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = display.c; sourceTree = "<group>"; }; + FC5D636A14B9808E00123E48 /* hexdump.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = hexdump.1; sourceTree = "<group>"; }; + FC5D636B14B9808E00123E48 /* hexdump.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = hexdump.c; sourceTree = "<group>"; }; + FC5D636C14B9808E00123E48 /* hexdump.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = hexdump.h; sourceTree = "<group>"; }; + FC5D636D14B9808E00123E48 /* hexsyntax.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = hexsyntax.c; sourceTree = "<group>"; }; + FC5D636E14B9808E00123E48 /* od.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = od.1; sourceTree = "<group>"; }; + FC5D636F14B9808E00123E48 /* odsyntax.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = odsyntax.c; sourceTree = "<group>"; }; + FC5D637014B9808E00123E48 /* parse.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = parse.c; sourceTree = "<group>"; }; + FC5D637214B9808E00123E48 /* what.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = what.1; sourceTree = "<group>"; }; + FC5D637314B9808E00123E48 /* what.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = what.c; sourceTree = "<group>"; }; + FC5D638B14B9819E00123E48 /* what */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = what; sourceTree = BUILT_PRODUCTS_DIR; }; + FC5D639714B9822D00123E48 /* hexdump */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = hexdump; sourceTree = BUILT_PRODUCTS_DIR; }; + FCBA02E514B505A50030BEB3 /* libresolv.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libresolv.dylib; path = /usr/lib/libresolv.dylib; sourceTree = "<absolute>"; }; + FCBA134114A141A300AA698B /* alias.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = alias.1; sourceTree = "<group>"; }; + FCBA134314A141A300AA698B /* generic.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = generic.sh; sourceTree = "<group>"; }; + FCBA134614A141A300AA698B /* apply.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = apply.1; sourceTree = "<group>"; }; + FCBA134714A141A300AA698B /* apply.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = apply.c; sourceTree = "<group>"; }; + FCBA134A14A141A300AA698B /* basename.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = basename.1; sourceTree = "<group>"; }; + FCBA134B14A141A300AA698B /* basename.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = basename.c; sourceTree = "<group>"; }; + FCBA134C14A141A300AA698B /* dirname.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; name = dirname.1; path = ../basename/dirname.1; sourceTree = "<group>"; }; + FCBA134F14A141A300AA698B /* chroot.8 */ = {isa = PBXFileReference; lastKnownFileType = text; path = chroot.8; sourceTree = "<group>"; }; + FCBA135014A141A300AA698B /* chroot.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = chroot.c; sourceTree = "<group>"; }; + FCBA135314A141A300AA698B /* date.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = date.1; sourceTree = "<group>"; }; + FCBA135414A141A300AA698B /* date.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = date.c; sourceTree = "<group>"; }; + FCBA135514A141A300AA698B /* extern.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = extern.h; sourceTree = "<group>"; }; + FCBA135714A141A300AA698B /* netdate.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = netdate.c; sourceTree = "<group>"; }; + FCBA135814A141A300AA698B /* vary.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = vary.c; sourceTree = "<group>"; }; + FCBA135914A141A300AA698B /* vary.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = vary.h; sourceTree = "<group>"; }; + FCBA135B14A141A300AA698B /* dirname.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = dirname.c; sourceTree = "<group>"; }; + FCBA135E14A141A300AA698B /* echo.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = echo.1; sourceTree = "<group>"; }; + FCBA135F14A141A300AA698B /* echo.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = echo.c; sourceTree = "<group>"; }; + FCBA136214A141A300AA698B /* env.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = env.1; sourceTree = "<group>"; }; + FCBA136314A141A300AA698B /* env.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = env.c; sourceTree = "<group>"; }; + FCBA136614A141A300AA698B /* expr.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = expr.1; sourceTree = "<group>"; }; + FCBA136714A141A300AA698B /* expr.y */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.yacc; path = expr.y; sourceTree = "<group>"; }; + FCBA136A14A141A300AA698B /* false.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = false.1; sourceTree = "<group>"; }; + FCBA136B14A141A300AA698B /* false.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = false.c; sourceTree = "<group>"; }; + FCBA136F14A141A300AA698B /* extern.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = extern.h; sourceTree = "<group>"; }; + FCBA137014A141A300AA698B /* find.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = find.1; sourceTree = "<group>"; }; + FCBA137114A141A300AA698B /* find.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = find.c; sourceTree = "<group>"; }; + FCBA137214A141A300AA698B /* find.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = find.h; sourceTree = "<group>"; }; + FCBA137314A141A300AA698B /* function.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = function.c; sourceTree = "<group>"; }; + FCBA137414A141A300AA698B /* getdate.y */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.yacc; path = getdate.y; sourceTree = "<group>"; }; + FCBA137514A141A300AA698B /* ls.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = ls.c; sourceTree = "<group>"; }; + FCBA137614A141A300AA698B /* main.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = main.c; sourceTree = "<group>"; }; + FCBA137814A141A300AA698B /* misc.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = misc.c; sourceTree = "<group>"; }; + FCBA137914A141A300AA698B /* operator.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = operator.c; sourceTree = "<group>"; }; + FCBA137A14A141A300AA698B /* option.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = option.c; sourceTree = "<group>"; }; + FCBA137C14A141A300AA698B /* getopt.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = getopt.1; sourceTree = "<group>"; }; + FCBA137D14A141A300AA698B /* getopt.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = getopt.c; sourceTree = "<group>"; }; + FCBA138014A141A300AA698B /* hostname.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = hostname.1; sourceTree = "<group>"; }; + FCBA138114A141A300AA698B /* hostname.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = hostname.c; sourceTree = "<group>"; }; + FCBA138414A141A300AA698B /* groups.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = groups.1; sourceTree = "<group>"; }; + FCBA138514A141A300AA698B /* id.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = id.1; sourceTree = "<group>"; }; + FCBA138614A141A300AA698B /* id.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = id.c; sourceTree = "<group>"; }; + FCBA138A14A141A300AA698B /* whoami.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = whoami.1; sourceTree = "<group>"; }; + FCBA138C14A141A300AA698B /* jot.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = jot.1; sourceTree = "<group>"; }; + FCBA138D14A141A300AA698B /* jot.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = jot.c; sourceTree = "<group>"; }; + FCBA139014A141A300AA698B /* kill.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = kill.1; sourceTree = "<group>"; }; + FCBA139114A141A300AA698B /* kill.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = kill.c; sourceTree = "<group>"; }; + FCBA139414A141A300AA698B /* killall.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = killall.1; sourceTree = "<group>"; }; + FCBA139514A141A300AA698B /* killall.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = killall.c; sourceTree = "<group>"; }; + FCBA139814A141A300AA698B /* lastcomm.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = lastcomm.1; sourceTree = "<group>"; }; + FCBA139914A141A300AA698B /* lastcomm.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = lastcomm.c; sourceTree = "<group>"; }; + FCBA139B14A141A300AA698B /* pathnames.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = pathnames.h; sourceTree = "<group>"; }; + FCBA139E14A141A300AA698B /* locate.bigram.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = locate.bigram.c; sourceTree = "<group>"; }; + FCBA13A114A141A300AA698B /* locate.code.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = locate.code.c; sourceTree = "<group>"; }; + FCBA13A414A141A300AA698B /* com.apple.locate.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = com.apple.locate.plist; sourceTree = "<group>"; }; + FCBA13A514A141A300AA698B /* concatdb.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = concatdb.sh; sourceTree = "<group>"; }; + FCBA13A714A141A300AA698B /* locate.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = locate.1; sourceTree = "<group>"; }; + FCBA13A814A141A300AA698B /* locate.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = locate.c; sourceTree = "<group>"; }; + FCBA13A914A141A300AA698B /* locate.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = locate.h; sourceTree = "<group>"; }; + FCBA13AA14A141A300AA698B /* locate.rc */ = {isa = PBXFileReference; lastKnownFileType = text; path = locate.rc; sourceTree = "<group>"; }; + FCBA13AB14A141A300AA698B /* locate.updatedb.8 */ = {isa = PBXFileReference; lastKnownFileType = text; path = locate.updatedb.8; sourceTree = "<group>"; }; + FCBA13AD14A141A300AA698B /* mklocatedb.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = mklocatedb.sh; sourceTree = "<group>"; }; + FCBA13AE14A141A300AA698B /* pathnames.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = pathnames.h; sourceTree = "<group>"; }; + FCBA13AF14A141A300AA698B /* updatedb.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = updatedb.sh; sourceTree = "<group>"; }; + FCBA13B014A141A300AA698B /* util.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = util.c; sourceTree = "<group>"; }; + FCBA13B114A141A300AA698B /* locate.code.8 */ = {isa = PBXFileReference; lastKnownFileType = text; path = locate.code.8; sourceTree = "<group>"; }; + FCBA13B414A141A300AA698B /* logname.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = logname.1; sourceTree = "<group>"; }; + FCBA13B514A141A300AA698B /* logname.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = logname.c; sourceTree = "<group>"; }; + FCBA13C014A141A300AA698B /* mktemp.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = mktemp.1; sourceTree = "<group>"; }; + FCBA13C114A141A300AA698B /* mktemp.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = mktemp.c; sourceTree = "<group>"; }; + FCBA13C414A141A300AA698B /* nice.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = nice.1; sourceTree = "<group>"; }; + FCBA13C514A141A300AA698B /* nice.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = nice.c; sourceTree = "<group>"; }; + FCBA13C814A141A300AA698B /* nohup.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = nohup.1; sourceTree = "<group>"; }; + FCBA13C914A141A300AA698B /* nohup.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = nohup.c; sourceTree = "<group>"; }; + FCBA13CC14A141A300AA698B /* path_helper.8 */ = {isa = PBXFileReference; lastKnownFileType = text; path = path_helper.8; sourceTree = "<group>"; }; + FCBA13CD14A141A300AA698B /* path_helper.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = path_helper.c; sourceTree = "<group>"; }; + FCBA13D014A141A300AA698B /* printenv.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = printenv.1; sourceTree = "<group>"; }; + FCBA13D114A141A300AA698B /* printenv.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = printenv.c; sourceTree = "<group>"; }; + FCBA13D414A141A300AA698B /* printf.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = printf.1; sourceTree = "<group>"; }; + FCBA13D514A141A300AA698B /* printf.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = printf.c; sourceTree = "<group>"; }; + FCBA13D814A141A300AA698B /* pwd.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = pwd.1; sourceTree = "<group>"; }; + FCBA13D914A141A300AA698B /* pwd.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = pwd.c; sourceTree = "<group>"; }; + FCBA13DC14A141A300AA698B /* renice.8 */ = {isa = PBXFileReference; lastKnownFileType = text; path = renice.8; sourceTree = "<group>"; }; + FCBA13DD14A141A300AA698B /* renice.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = renice.c; sourceTree = "<group>"; }; + FCBA13E014A141A300AA698B /* script.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = script.1; sourceTree = "<group>"; }; + FCBA13E114A141A300AA698B /* script.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = script.c; sourceTree = "<group>"; }; + FCBA13E414A141A300AA698B /* seq.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = seq.1; sourceTree = "<group>"; }; + FCBA13E514A141A300AA698B /* seq.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = seq.c; sourceTree = "<group>"; }; + FCBA13E814A141A300AA698B /* shlock.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = shlock.1; sourceTree = "<group>"; }; + FCBA13E914A141A300AA698B /* shlock.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = shlock.c; sourceTree = "<group>"; }; + FCBA13EC14A141A300AA698B /* sleep.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = sleep.1; sourceTree = "<group>"; }; + FCBA13ED14A141A300AA698B /* sleep.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = sleep.c; sourceTree = "<group>"; }; + FCBA13F014A141A300AA698B /* su.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = su.1; sourceTree = "<group>"; }; + FCBA13F114A141A300AA698B /* su.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = su.c; sourceTree = "<group>"; }; + FCBA13F214A141A300AA698B /* su.pam */ = {isa = PBXFileReference; lastKnownFileType = text; path = su.pam; sourceTree = "<group>"; }; + FCBA13F514A141A300AA698B /* tee.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = tee.1; sourceTree = "<group>"; }; + FCBA13F614A141A300AA698B /* tee.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = tee.c; sourceTree = "<group>"; }; + FCBA13F814A141A300AA698B /* [.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = "[.1"; sourceTree = "<group>"; }; + FCBA13FA14A141A300AA698B /* test.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = test.1; sourceTree = "<group>"; }; + FCBA13FB14A141A300AA698B /* test.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = test.c; sourceTree = "<group>"; }; + FCBA13FF14A141A300AA698B /* time.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = time.1; sourceTree = "<group>"; }; + FCBA140014A141A300AA698B /* time.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = time.c; sourceTree = "<group>"; usesTabs = 1; }; + FCBA140314A141A300AA698B /* true.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = true.1; sourceTree = "<group>"; }; + FCBA140414A141A300AA698B /* true.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = true.c; sourceTree = "<group>"; }; + FCBA140814A141A300AA698B /* uname.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = uname.1; sourceTree = "<group>"; }; + FCBA140914A141A300AA698B /* uname.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = uname.c; sourceTree = "<group>"; }; + FCBA140C14A141A300AA698B /* users.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = users.1; sourceTree = "<group>"; }; + FCBA140D14A141A300AA698B /* users.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = users.c; sourceTree = "<group>"; }; + FCBA140F14A141A300AA698B /* extern.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = extern.h; sourceTree = "<group>"; }; + FCBA141014A141A300AA698B /* fmt.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = fmt.c; sourceTree = "<group>"; }; + FCBA141214A141A300AA698B /* pr_time.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = pr_time.c; sourceTree = "<group>"; }; + FCBA141314A141A300AA698B /* proc_compare.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = proc_compare.c; sourceTree = "<group>"; }; + FCBA141414A141A300AA698B /* uptime.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = uptime.1; sourceTree = "<group>"; }; + FCBA141514A141A300AA698B /* w.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = w.1; sourceTree = "<group>"; }; + FCBA141614A141A300AA698B /* w.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = w.c; sourceTree = "<group>"; }; + FCBA141914A141A300AA698B /* whereis.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = whereis.1; sourceTree = "<group>"; }; + FCBA141A14A141A300AA698B /* whereis.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = whereis.c; sourceTree = "<group>"; }; + FCBA141D14A141A300AA698B /* which.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = which.1; sourceTree = "<group>"; }; + FCBA141E14A141A300AA698B /* which.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = which.c; sourceTree = "<group>"; }; + FCBA142114A141A300AA698B /* utmpentry.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = utmpentry.c; sourceTree = "<group>"; }; + FCBA142214A141A300AA698B /* utmpentry.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = utmpentry.h; sourceTree = "<group>"; }; + FCBA142314A141A300AA698B /* who.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = who.1; sourceTree = "<group>"; }; + FCBA142414A141A300AA698B /* who.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = who.c; sourceTree = "<group>"; }; + FCBA142714A141A300AA698B /* pathnames.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = pathnames.h; sourceTree = "<group>"; }; + FCBA142814A141A300AA698B /* strnsubst.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = strnsubst.c; sourceTree = "<group>"; }; + FCBA142914A141A300AA698B /* xargs.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = xargs.1; sourceTree = "<group>"; }; + FCBA142A14A141A300AA698B /* xargs.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = xargs.c; sourceTree = "<group>"; }; + FCBA142D14A141A300AA698B /* yes.1 */ = {isa = PBXFileReference; lastKnownFileType = text.man; path = yes.1; sourceTree = "<group>"; }; + FCBA142E14A141A300AA698B /* yes.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = yes.c; sourceTree = "<group>"; }; + FCBA149E14A141FF00AA698B /* apply */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = apply; sourceTree = BUILT_PRODUCTS_DIR; }; + FCBA14B414A1422D00AA698B /* basename */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = basename; sourceTree = BUILT_PRODUCTS_DIR; }; + FCBA14BD14A1423E00AA698B /* chroot */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = chroot; sourceTree = BUILT_PRODUCTS_DIR; }; + FCBA14C514A1426800AA698B /* date */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = date; sourceTree = BUILT_PRODUCTS_DIR; }; + FCBA14CD14A1428000AA698B /* dirname */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = dirname; sourceTree = BUILT_PRODUCTS_DIR; }; + FCBA14D514A1433F00AA698B /* echo */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = echo; sourceTree = BUILT_PRODUCTS_DIR; }; + FCBA14DD14A1434A00AA698B /* env */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = env; sourceTree = BUILT_PRODUCTS_DIR; }; + FCBA151414A1453A00AA698B /* expr */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = expr; sourceTree = BUILT_PRODUCTS_DIR; }; + FCBA151C14A1454D00AA698B /* false */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = false; sourceTree = BUILT_PRODUCTS_DIR; }; + FCBA152414A1455600AA698B /* find */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = find; sourceTree = BUILT_PRODUCTS_DIR; }; + FCBA152C14A1455900AA698B /* getopt */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = getopt; sourceTree = BUILT_PRODUCTS_DIR; }; + FCBA153414A1455C00AA698B /* hostname */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = hostname; sourceTree = BUILT_PRODUCTS_DIR; }; + FCBA153C14A1455F00AA698B /* id */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = id; sourceTree = BUILT_PRODUCTS_DIR; }; + FCBA154414A1456100AA698B /* jot */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = jot; sourceTree = BUILT_PRODUCTS_DIR; }; + FCBA154C14A1456400AA698B /* kill */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = kill; sourceTree = BUILT_PRODUCTS_DIR; }; + FCBA155414A1456700AA698B /* killall */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = killall; sourceTree = BUILT_PRODUCTS_DIR; }; + FCBA155C14A1456A00AA698B /* lastcomm */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = lastcomm; sourceTree = BUILT_PRODUCTS_DIR; }; + FCBA156414A1456E00AA698B /* locate */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = locate; sourceTree = BUILT_PRODUCTS_DIR; }; + FCBA156C14A1457100AA698B /* logname */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = logname; sourceTree = BUILT_PRODUCTS_DIR; }; + FCBA157C14A1457B00AA698B /* mktemp */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = mktemp; sourceTree = BUILT_PRODUCTS_DIR; }; + FCBA158414A1457F00AA698B /* nice */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = nice; sourceTree = BUILT_PRODUCTS_DIR; }; + FCBA158C14A1458500AA698B /* nohup */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = nohup; sourceTree = BUILT_PRODUCTS_DIR; }; + FCBA159414A1458800AA698B /* path_helper */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = path_helper; sourceTree = BUILT_PRODUCTS_DIR; }; + FCBA159C14A1458C00AA698B /* printenv */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = printenv; sourceTree = BUILT_PRODUCTS_DIR; }; + FCBA15A414A1459000AA698B /* printf */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = printf; sourceTree = BUILT_PRODUCTS_DIR; }; + FCBA15AC14A1459500AA698B /* pwd */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = pwd; sourceTree = BUILT_PRODUCTS_DIR; }; + FCBA15B414A1459700AA698B /* renice */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = renice; sourceTree = BUILT_PRODUCTS_DIR; }; + FCBA15BC14A1459900AA698B /* script */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = script; sourceTree = BUILT_PRODUCTS_DIR; }; + FCBA15C414A1459E00AA698B /* seq */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = seq; sourceTree = BUILT_PRODUCTS_DIR; }; + FCBA15CC14A145A200AA698B /* shlock */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = shlock; sourceTree = BUILT_PRODUCTS_DIR; }; + FCBA15D414A145A500AA698B /* sleep */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = sleep; sourceTree = BUILT_PRODUCTS_DIR; }; + FCBA15DC14A145A700AA698B /* su */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = su; sourceTree = BUILT_PRODUCTS_DIR; }; + FCBA15E414A145A900AA698B /* tee */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = tee; sourceTree = BUILT_PRODUCTS_DIR; }; + FCBA15EC14A145AE00AA698B /* test */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = test; sourceTree = BUILT_PRODUCTS_DIR; }; + FCBA15F414A145B000AA698B /* time */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = time; sourceTree = BUILT_PRODUCTS_DIR; }; + FCBA15FC14A145B200AA698B /* true */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = true; sourceTree = BUILT_PRODUCTS_DIR; }; + FCBA160414A145B500AA698B /* uname */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = uname; sourceTree = BUILT_PRODUCTS_DIR; }; + FCBA160C14A145B800AA698B /* users */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = users; sourceTree = BUILT_PRODUCTS_DIR; }; + FCBA161414A145BA00AA698B /* w */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = w; sourceTree = BUILT_PRODUCTS_DIR; }; + FCBA161C14A145BC00AA698B /* whereis */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = whereis; sourceTree = BUILT_PRODUCTS_DIR; }; + FCBA162414A145C000AA698B /* which */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = which; sourceTree = BUILT_PRODUCTS_DIR; }; + FCBA162C14A145C500AA698B /* who */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = who; sourceTree = BUILT_PRODUCTS_DIR; }; + FCBA163414A145C700AA698B /* xargs */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = xargs; sourceTree = BUILT_PRODUCTS_DIR; }; + FCBA163C14A145CA00AA698B /* yes */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = yes; sourceTree = BUILT_PRODUCTS_DIR; }; + FCE30EC114B5347A00CC0294 /* locate.bigram.8 */ = {isa = PBXFileReference; lastKnownFileType = text; path = locate.bigram.8; sourceTree = "<group>"; }; + FCE30ED214B5368A00CC0294 /* locate.bigram */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = locate.bigram; sourceTree = BUILT_PRODUCTS_DIR; }; + FCE30EDE14B536C900CC0294 /* locate.code */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = locate.code; sourceTree = BUILT_PRODUCTS_DIR; }; + FCED3AF514B4FC1800C313C3 /* libpam.dylib */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; name = libpam.dylib; path = /usr/lib/libpam.dylib; sourceTree = "<absolute>"; }; + FD155C721D6E37B5005A53CA /* legacy_test.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = legacy_test.sh; sourceTree = "<group>"; }; + FD155C771D6E3847005A53CA /* legacy_test.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = legacy_test.sh; sourceTree = "<group>"; }; + FD155C7A1D6E3847005A53CA /* regress.b.out */ = {isa = PBXFileReference; lastKnownFileType = text; path = regress.b.out; sourceTree = "<group>"; }; + FD155C7B1D6E3847005A53CA /* regress.d.out */ = {isa = PBXFileReference; lastKnownFileType = text; path = regress.d.out; sourceTree = "<group>"; }; + FD155C7C1D6E3847005A53CA /* regress.f.out */ = {isa = PBXFileReference; lastKnownFileType = text; path = regress.f.out; sourceTree = "<group>"; }; + FD155C7D1D6E3847005A53CA /* regress.l1.out */ = {isa = PBXFileReference; lastKnownFileType = text; path = regress.l1.out; sourceTree = "<group>"; }; + FD155C7E1D6E3847005A53CA /* regress.l2.out */ = {isa = PBXFileReference; lastKnownFileType = text; path = regress.l2.out; sourceTree = "<group>"; }; + FD155C7F1D6E3847005A53CA /* regress.m1.out */ = {isa = PBXFileReference; lastKnownFileType = file; path = regress.m1.out; sourceTree = "<group>"; }; + FD155C801D6E3847005A53CA /* regress.m2.out */ = {isa = PBXFileReference; lastKnownFileType = text; path = regress.m2.out; sourceTree = "<group>"; }; + FD155C811D6E3847005A53CA /* regress.m3.out */ = {isa = PBXFileReference; lastKnownFileType = text; path = regress.m3.out; sourceTree = "<group>"; }; + FD155C821D6E3847005A53CA /* regress.m4.out */ = {isa = PBXFileReference; lastKnownFileType = text; path = regress.m4.out; sourceTree = "<group>"; }; + FD155C831D6E3847005A53CA /* regress.m5.out */ = {isa = PBXFileReference; lastKnownFileType = text; path = regress.m5.out; sourceTree = "<group>"; }; + FD155C841D6E3847005A53CA /* regress.missingpos1.out */ = {isa = PBXFileReference; lastKnownFileType = text; path = regress.missingpos1.out; sourceTree = "<group>"; }; + FD155C851D6E3847005A53CA /* regress.s.out */ = {isa = PBXFileReference; lastKnownFileType = text; path = regress.s.out; sourceTree = "<group>"; }; + FD155C861D6E3847005A53CA /* regress.sh */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; path = regress.sh; sourceTree = "<group>"; }; + FD155C871D6E3847005A53CA /* regress.zero.out */ = {isa = PBXFileReference; lastKnownFileType = text; path = regress.zero.out; sourceTree = "<group>"; }; + FD6060B21B7C0388004BCA6A /* ash */ = {isa = PBXFileReference; explicitFileType = "compiled.mach-o.executable"; includeInIndex = 0; path = ash; sourceTree = BUILT_PRODUCTS_DIR; }; + FD6060B41B7C0388004BCA6A /* main.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = main.c; sourceTree = "<group>"; }; + FD6060BC1B7C0471004BCA6A /* alias.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = alias.c; sourceTree = "<group>"; }; + FD6060BD1B7C0471004BCA6A /* arith_yacc.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = arith_yacc.c; sourceTree = "<group>"; }; + FD6060BE1B7C0471004BCA6A /* arith_yylex.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = arith_yylex.c; sourceTree = "<group>"; }; + FD6060BF1B7C0471004BCA6A /* cd.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = cd.c; sourceTree = "<group>"; }; + FD6060C01B7C0471004BCA6A /* error.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = error.c; sourceTree = "<group>"; }; + FD6060C11B7C0471004BCA6A /* eval.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = eval.c; sourceTree = "<group>"; }; + FD6060C21B7C0471004BCA6A /* exec.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = exec.c; sourceTree = "<group>"; }; + FD6060C31B7C0471004BCA6A /* expand.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = expand.c; sourceTree = "<group>"; }; + FD6060C41B7C0471004BCA6A /* histedit.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = histedit.c; sourceTree = "<group>"; }; + FD6060C51B7C0471004BCA6A /* input.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = input.c; sourceTree = "<group>"; }; + FD6060C61B7C0471004BCA6A /* jobs.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = jobs.c; sourceTree = "<group>"; }; + FD6060C71B7C0471004BCA6A /* mail.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = mail.c; sourceTree = "<group>"; }; + FD6060C81B7C0471004BCA6A /* memalloc.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = memalloc.c; sourceTree = "<group>"; }; + FD6060C91B7C0471004BCA6A /* miscbltin.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = miscbltin.c; sourceTree = "<group>"; }; + FD6060CA1B7C0471004BCA6A /* mystring.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = mystring.c; sourceTree = "<group>"; }; + FD6060CB1B7C0471004BCA6A /* options.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = options.c; sourceTree = "<group>"; }; + FD6060CC1B7C0471004BCA6A /* output.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = output.c; sourceTree = "<group>"; }; + FD6060CD1B7C0471004BCA6A /* parser.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = parser.c; sourceTree = "<group>"; }; + FD6060CE1B7C0471004BCA6A /* redir.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = redir.c; sourceTree = "<group>"; }; + FD6060CF1B7C0471004BCA6A /* show.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = show.c; sourceTree = "<group>"; }; + FD6060D01B7C0471004BCA6A /* trap.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = trap.c; sourceTree = "<group>"; }; + FD6060D11B7C0471004BCA6A /* var.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = var.c; sourceTree = "<group>"; }; + FD6060EA1B7C04E4004BCA6A /* echo.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = echo.c; sourceTree = "<group>"; }; + FD6060FA1B7C0CAF004BCA6A /* builtins.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = builtins.c; sourceTree = "<group>"; }; + FD6060FB1B7C0CAF004BCA6A /* nodes.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = nodes.c; sourceTree = "<group>"; }; + FD6060FC1B7C0CAF004BCA6A /* syntax.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = syntax.c; sourceTree = "<group>"; }; + FD6061011B7D2B6D004BCA6A /* builtins.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = builtins.h; sourceTree = "<group>"; }; + FD6061021B7D2B6D004BCA6A /* nodes.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = nodes.h; sourceTree = "<group>"; }; + FD6061031B7D2B6D004BCA6A /* syntax.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = syntax.h; sourceTree = "<group>"; }; + FD6061041B7D2B6D004BCA6A /* token.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = token.h; sourceTree = "<group>"; }; + FD6061051B7D2BF8004BCA6A /* bltin.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = bltin.h; sourceTree = "<group>"; }; + FD6061061B7D2C8C004BCA6A /* alias.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = alias.h; sourceTree = "<group>"; }; + FD6061071B7D2C8C004BCA6A /* arith_yacc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = arith_yacc.h; sourceTree = "<group>"; }; + FD6061081B7D2C8C004BCA6A /* arith.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = arith.h; sourceTree = "<group>"; }; + FD6061091B7D2C8C004BCA6A /* builtins.def */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; path = builtins.def; sourceTree = "<group>"; }; + FD60610A1B7D2C8C004BCA6A /* cd.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = cd.h; sourceTree = "<group>"; }; + FD60610B1B7D2C8C004BCA6A /* error.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = error.h; sourceTree = "<group>"; }; + FD60610C1B7D2C8C004BCA6A /* eval.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = eval.h; sourceTree = "<group>"; }; + FD60610D1B7D2C8C004BCA6A /* exec.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = exec.h; sourceTree = "<group>"; }; + FD60610E1B7D2C8C004BCA6A /* expand.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = expand.h; sourceTree = "<group>"; }; + FD60610F1B7D2C8C004BCA6A /* input.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = input.h; sourceTree = "<group>"; }; + FD6061101B7D2C8C004BCA6A /* jobs.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = jobs.h; sourceTree = "<group>"; }; + FD6061111B7D2C8C004BCA6A /* mail.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mail.h; sourceTree = "<group>"; }; + FD6061121B7D2C8C004BCA6A /* main.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = main.h; sourceTree = "<group>"; }; + FD6061131B7D2C8C004BCA6A /* memalloc.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = memalloc.h; sourceTree = "<group>"; }; + FD6061141B7D2C8C004BCA6A /* mkbuiltins */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; path = mkbuiltins; sourceTree = "<group>"; }; + FD6061151B7D2C8C004BCA6A /* mknodes.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = mknodes.c; sourceTree = "<group>"; }; + FD6061161B7D2C8C004BCA6A /* mksyntax.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = mksyntax.c; sourceTree = "<group>"; }; + FD6061171B7D2C8C004BCA6A /* mktokens */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; path = mktokens; sourceTree = "<group>"; }; + FD6061181B7D2C8C004BCA6A /* myhistedit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = myhistedit.h; sourceTree = "<group>"; }; + FD6061191B7D2C8C004BCA6A /* mystring.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mystring.h; sourceTree = "<group>"; }; + FD60611A1B7D2C8C004BCA6A /* nodes.c.pat */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = nodes.c.pat; sourceTree = "<group>"; }; + FD60611B1B7D2C8C004BCA6A /* nodetypes */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = nodetypes; sourceTree = "<group>"; }; + FD60611C1B7D2C8C004BCA6A /* options.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = options.h; sourceTree = "<group>"; }; + FD60611D1B7D2C8C004BCA6A /* output.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = output.h; sourceTree = "<group>"; }; + FD60611E1B7D2C8C004BCA6A /* parser.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = parser.h; sourceTree = "<group>"; }; + FD60611F1B7D2C8C004BCA6A /* redir.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = redir.h; sourceTree = "<group>"; }; + FD6061201B7D2C8C004BCA6A /* sh.1 */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.man; path = sh.1; sourceTree = "<group>"; }; + FD6061211B7D2C8C004BCA6A /* shell.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = shell.h; sourceTree = "<group>"; }; + FD6061221B7D2C8C004BCA6A /* show.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = show.h; sourceTree = "<group>"; }; + FD6061231B7D2C8C004BCA6A /* trap.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = trap.h; sourceTree = "<group>"; }; + FD6061241B7D2C8C004BCA6A /* var.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = var.h; sourceTree = "<group>"; }; + FD6061281B7D2D1D004BCA6A /* cmv */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; path = cmv; sourceTree = "<group>"; }; + FD6061291B7D2D1D004BCA6A /* dirs */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; path = dirs; sourceTree = "<group>"; }; + FD60612A1B7D2D1D004BCA6A /* login */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; path = login; sourceTree = "<group>"; }; + FD60612B1B7D2D1D004BCA6A /* newgrp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; path = newgrp; sourceTree = "<group>"; }; + FD60612C1B7D2D1D004BCA6A /* popd */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; path = popd; sourceTree = "<group>"; }; + FD60612D1B7D2D1D004BCA6A /* pushd */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.sh; path = pushd; sourceTree = "<group>"; }; + FD60612E1B7D2D1D004BCA6A /* suspend */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = suspend; sourceTree = "<group>"; }; + FD6061301B7D2DDE004BCA6A /* base.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = base.xcconfig; sourceTree = "<group>"; }; + FD6061311B7D2DDE004BCA6A /* sh.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = sh.xcconfig; sourceTree = "<group>"; }; + FD88EB01198C5257006B7EFD /* envopts.c */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.c; path = envopts.c; sourceTree = "<group>"; }; + FD88EB02198C5257006B7EFD /* envopts.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = envopts.h; sourceTree = "<group>"; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + C6868573154725700025D623 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FC5D638514B9819E00123E48 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FC5D639214B9822D00123E48 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FCBA149A14A141FF00AA698B /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FCBA14B014A1422D00AA698B /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FCBA14B914A1423E00AA698B /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FCBA14C114A1426800AA698B /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FCBA14C914A1428000AA698B /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FCBA14D114A1433F00AA698B /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FCBA14D914A1434A00AA698B /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FCBA151014A1453A00AA698B /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FCBA151814A1454D00AA698B /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FCBA152014A1455600AA698B /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FCBA152814A1455900AA698B /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FCBA153014A1455C00AA698B /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FCBA153814A1455F00AA698B /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FCBA154014A1456100AA698B /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FCBA154814A1456400AA698B /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FCBA155014A1456700AA698B /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FCBA155814A1456A00AA698B /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FCBA156014A1456E00AA698B /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FCBA156814A1457100AA698B /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FCBA157814A1457B00AA698B /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FCBA158014A1457F00AA698B /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FCBA158814A1458500AA698B /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FCBA159014A1458800AA698B /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FCBA159814A1458C00AA698B /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FCBA15A014A1459000AA698B /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FCBA15A814A1459500AA698B /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FCBA15B014A1459700AA698B /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FCBA15B814A1459900AA698B /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FCBA15C014A1459E00AA698B /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FCBA15C814A145A200AA698B /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FCBA15D014A145A500AA698B /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FCBA15D814A145A700AA698B /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + FCED3AF614B4FC1800C313C3 /* libpam.dylib in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FCBA15E014A145A900AA698B /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FCBA15E814A145AE00AA698B /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FCBA15F014A145B000AA698B /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FCBA15F814A145B200AA698B /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FCBA160014A145B500AA698B /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FCBA160814A145B800AA698B /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FCBA161014A145BA00AA698B /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FCBA161814A145BC00AA698B /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FCBA162014A145C000AA698B /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FCBA162814A145C500AA698B /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FCBA163014A145C700AA698B /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FCBA163814A145CA00AA698B /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FCE30EC614B5368A00CC0294 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FCE30ED914B536C900CC0294 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FD6060AF1B7C0388004BCA6A /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + C6868578154725700025D623 /* systime */ = { + isa = PBXGroup; + children = ( + C6868579154725700025D623 /* systime.c */, + C686857B154725700025D623 /* systime.1 */, + ); + path = systime; + sourceTree = "<group>"; + }; + CE799E5F24AD3B1B00E73238 /* tests */ = { + isa = PBXGroup; + children = ( + CE799E6024AD3B1B00E73238 /* test_time.sh */, + ); + path = tests; + sourceTree = "<group>"; + }; + CE799E6124B3982200E73238 /* xcodescripts */ = { + isa = PBXGroup; + children = ( + CE799E6224B3982200E73238 /* install-files.sh */, + CE799E6324B3982200E73238 /* builtins.txt */, + CE799E6424B3982200E73238 /* builtins-manpages.txt */, + ); + path = xcodescripts; + sourceTree = "<group>"; + }; + FC5D636714B9808E00123E48 /* hexdump */ = { + isa = PBXGroup; + children = ( + FC5D636814B9808E00123E48 /* conv.c */, + FC5D636914B9808E00123E48 /* display.c */, + FC5D636A14B9808E00123E48 /* hexdump.1 */, + FC5D636B14B9808E00123E48 /* hexdump.c */, + FC5D636C14B9808E00123E48 /* hexdump.h */, + FC5D636D14B9808E00123E48 /* hexsyntax.c */, + FC5D636E14B9808E00123E48 /* od.1 */, + FC5D636F14B9808E00123E48 /* odsyntax.c */, + FC5D637014B9808E00123E48 /* parse.c */, + ); + path = hexdump; + sourceTree = "<group>"; + }; + FC5D637114B9808E00123E48 /* what */ = { + isa = PBXGroup; + children = ( + FC5D637214B9808E00123E48 /* what.1 */, + FC5D637314B9808E00123E48 /* what.c */, + ); + path = what; + sourceTree = "<group>"; + }; + FC80BF5514A05A2F00C6F7F5 = { + isa = PBXGroup; + children = ( + CE799E6124B3982200E73238 /* xcodescripts */, + FD60612F1B7D2DDE004BCA6A /* xcconfigs */, + FCBA134014A141A300AA698B /* alias */, + FCBA134514A141A300AA698B /* apply */, + FCBA134914A141A300AA698B /* basename */, + FCBA134E14A141A300AA698B /* chroot */, + FCBA135214A141A300AA698B /* date */, + FCBA135A14A141A300AA698B /* dirname */, + FCBA135D14A141A300AA698B /* echo */, + FCBA136114A141A300AA698B /* env */, + FCBA136514A141A300AA698B /* expr */, + FCBA136914A141A300AA698B /* false */, + FCBA136E14A141A300AA698B /* find */, + FCBA137B14A141A300AA698B /* getopt */, + FC5D636714B9808E00123E48 /* hexdump */, + FCBA137F14A141A300AA698B /* hostname */, + FCBA138314A141A300AA698B /* id */, + FCBA138B14A141A300AA698B /* jot */, + FCBA138F14A141A300AA698B /* kill */, + FCBA139314A141A300AA698B /* killall */, + FCBA139714A141A300AA698B /* lastcomm */, + FCBA139C14A141A300AA698B /* locate */, + FCBA13B314A141A300AA698B /* logname */, + FCBA13BE14A141A300AA698B /* mktemp */, + FCBA13C214A141A300AA698B /* nice */, + FCBA13C614A141A300AA698B /* nohup */, + FCBA13CA14A141A300AA698B /* path_helper */, + FCBA13CE14A141A300AA698B /* printenv */, + FCBA13D214A141A300AA698B /* printf */, + FCBA13D614A141A300AA698B /* pwd */, + FCBA13DA14A141A300AA698B /* renice */, + FCBA13DE14A141A300AA698B /* script */, + FCBA13E214A141A300AA698B /* seq */, + FD6060B31B7C0388004BCA6A /* sh */, + FCBA13E614A141A300AA698B /* shlock */, + FCBA13EA14A141A300AA698B /* sleep */, + FCBA13EE14A141A300AA698B /* su */, + C6868578154725700025D623 /* systime */, + FCBA13F314A141A300AA698B /* tee */, + FCBA13F714A141A300AA698B /* test */, + FCBA13FD14A141A300AA698B /* time */, + FCBA140114A141A300AA698B /* true */, + FCBA140614A141A300AA698B /* uname */, + FCBA140A14A141A300AA698B /* users */, + FCBA140E14A141A300AA698B /* w */, + FC5D637114B9808E00123E48 /* what */, + FCBA141714A141A300AA698B /* whereis */, + FCBA141B14A141A300AA698B /* which */, + FCBA141F14A141A300AA698B /* who */, + FCBA142514A141A300AA698B /* xargs */, + FCBA142B14A141A300AA698B /* yes */, + FC80BF6114A05A2F00C6F7F5 /* Products */, + ); + sourceTree = "<group>"; + }; + FC80BF6114A05A2F00C6F7F5 /* Products */ = { + isa = PBXGroup; + children = ( + FCBA149E14A141FF00AA698B /* apply */, + FCBA14B414A1422D00AA698B /* basename */, + FCBA14BD14A1423E00AA698B /* chroot */, + FCBA14C514A1426800AA698B /* date */, + FCBA14CD14A1428000AA698B /* dirname */, + FCBA14D514A1433F00AA698B /* echo */, + FCBA14DD14A1434A00AA698B /* env */, + FCBA151414A1453A00AA698B /* expr */, + FCBA151C14A1454D00AA698B /* false */, + FCBA152414A1455600AA698B /* find */, + FCBA152C14A1455900AA698B /* getopt */, + FCBA153414A1455C00AA698B /* hostname */, + FCBA153C14A1455F00AA698B /* id */, + FCBA154414A1456100AA698B /* jot */, + FCBA154C14A1456400AA698B /* kill */, + FCBA155414A1456700AA698B /* killall */, + FCBA155C14A1456A00AA698B /* lastcomm */, + FCBA156414A1456E00AA698B /* locate */, + FCBA156C14A1457100AA698B /* logname */, + FCBA157C14A1457B00AA698B /* mktemp */, + FCBA158414A1457F00AA698B /* nice */, + FCBA158C14A1458500AA698B /* nohup */, + FCBA159414A1458800AA698B /* path_helper */, + FCBA159C14A1458C00AA698B /* printenv */, + FCBA15A414A1459000AA698B /* printf */, + FCBA15AC14A1459500AA698B /* pwd */, + FCBA15B414A1459700AA698B /* renice */, + FCBA15BC14A1459900AA698B /* script */, + FCBA15C414A1459E00AA698B /* seq */, + FD6060B21B7C0388004BCA6A /* ash */, + FCBA15CC14A145A200AA698B /* shlock */, + FCBA15D414A145A500AA698B /* sleep */, + FCBA15DC14A145A700AA698B /* su */, + FCBA15E414A145A900AA698B /* tee */, + FCBA15EC14A145AE00AA698B /* test */, + FCBA15F414A145B000AA698B /* time */, + FCBA15FC14A145B200AA698B /* true */, + FCBA160414A145B500AA698B /* uname */, + FCBA160C14A145B800AA698B /* users */, + FCBA161414A145BA00AA698B /* w */, + FCBA161C14A145BC00AA698B /* whereis */, + FCBA162414A145C000AA698B /* which */, + FCBA162C14A145C500AA698B /* who */, + FCBA163414A145C700AA698B /* xargs */, + FCBA163C14A145CA00AA698B /* yes */, + FCE30ED214B5368A00CC0294 /* locate.bigram */, + FCE30EDE14B536C900CC0294 /* locate.code */, + FC5D638B14B9819E00123E48 /* what */, + FC5D639714B9822D00123E48 /* hexdump */, + C6868576154725700025D623 /* systime */, + ); + name = Products; + sourceTree = "<group>"; + }; + FCBA134014A141A300AA698B /* alias */ = { + isa = PBXGroup; + children = ( + FCBA134114A141A300AA698B /* alias.1 */, + FCBA134314A141A300AA698B /* generic.sh */, + ); + path = alias; + sourceTree = "<group>"; + }; + FCBA134514A141A300AA698B /* apply */ = { + isa = PBXGroup; + children = ( + FCBA134614A141A300AA698B /* apply.1 */, + FCBA134714A141A300AA698B /* apply.c */, + ); + path = apply; + sourceTree = "<group>"; + }; + FCBA134914A141A300AA698B /* basename */ = { + isa = PBXGroup; + children = ( + FCBA134A14A141A300AA698B /* basename.1 */, + FCBA134B14A141A300AA698B /* basename.c */, + ); + path = basename; + sourceTree = "<group>"; + }; + FCBA134E14A141A300AA698B /* chroot */ = { + isa = PBXGroup; + children = ( + FCBA134F14A141A300AA698B /* chroot.8 */, + FCBA135014A141A300AA698B /* chroot.c */, + ); + path = chroot; + sourceTree = "<group>"; + }; + FCBA135214A141A300AA698B /* date */ = { + isa = PBXGroup; + children = ( + FCBA135314A141A300AA698B /* date.1 */, + FCBA135414A141A300AA698B /* date.c */, + FCBA135514A141A300AA698B /* extern.h */, + FCBA135714A141A300AA698B /* netdate.c */, + FCBA135814A141A300AA698B /* vary.c */, + FCBA135914A141A300AA698B /* vary.h */, + ); + path = date; + sourceTree = "<group>"; + }; + FCBA135A14A141A300AA698B /* dirname */ = { + isa = PBXGroup; + children = ( + FCBA135B14A141A300AA698B /* dirname.c */, + FCBA134C14A141A300AA698B /* dirname.1 */, + ); + path = dirname; + sourceTree = "<group>"; + }; + FCBA135D14A141A300AA698B /* echo */ = { + isa = PBXGroup; + children = ( + FCBA135E14A141A300AA698B /* echo.1 */, + FCBA135F14A141A300AA698B /* echo.c */, + ); + path = echo; + sourceTree = "<group>"; + }; + FCBA136114A141A300AA698B /* env */ = { + isa = PBXGroup; + children = ( + FCBA136214A141A300AA698B /* env.1 */, + FCBA136314A141A300AA698B /* env.c */, + FD88EB01198C5257006B7EFD /* envopts.c */, + FD88EB02198C5257006B7EFD /* envopts.h */, + ); + path = env; + sourceTree = "<group>"; + }; + FCBA136514A141A300AA698B /* expr */ = { + isa = PBXGroup; + children = ( + FCBA136614A141A300AA698B /* expr.1 */, + FCBA136714A141A300AA698B /* expr.y */, + ); + path = expr; + sourceTree = "<group>"; + }; + FCBA136914A141A300AA698B /* false */ = { + isa = PBXGroup; + children = ( + FCBA136A14A141A300AA698B /* false.1 */, + FCBA136B14A141A300AA698B /* false.c */, + ); + path = false; + sourceTree = "<group>"; + }; + FCBA136E14A141A300AA698B /* find */ = { + isa = PBXGroup; + children = ( + FCBA136F14A141A300AA698B /* extern.h */, + FCBA137014A141A300AA698B /* find.1 */, + FCBA137114A141A300AA698B /* find.c */, + FCBA137214A141A300AA698B /* find.h */, + FCBA137314A141A300AA698B /* function.c */, + FCBA137414A141A300AA698B /* getdate.y */, + FCBA137514A141A300AA698B /* ls.c */, + FCBA137614A141A300AA698B /* main.c */, + FCBA137814A141A300AA698B /* misc.c */, + FCBA137914A141A300AA698B /* operator.c */, + FCBA137A14A141A300AA698B /* option.c */, + ); + path = find; + sourceTree = "<group>"; + }; + FCBA137B14A141A300AA698B /* getopt */ = { + isa = PBXGroup; + children = ( + FCBA137C14A141A300AA698B /* getopt.1 */, + FCBA137D14A141A300AA698B /* getopt.c */, + ); + path = getopt; + sourceTree = "<group>"; + }; + FCBA137F14A141A300AA698B /* hostname */ = { + isa = PBXGroup; + children = ( + FCBA138014A141A300AA698B /* hostname.1 */, + FCBA138114A141A300AA698B /* hostname.c */, + ); + path = hostname; + sourceTree = "<group>"; + }; + FCBA138314A141A300AA698B /* id */ = { + isa = PBXGroup; + children = ( + FCBA138414A141A300AA698B /* groups.1 */, + FCBA138514A141A300AA698B /* id.1 */, + FCBA138614A141A300AA698B /* id.c */, + FCBA138A14A141A300AA698B /* whoami.1 */, + ); + path = id; + sourceTree = "<group>"; + }; + FCBA138B14A141A300AA698B /* jot */ = { + isa = PBXGroup; + children = ( + FCBA138C14A141A300AA698B /* jot.1 */, + FCBA138D14A141A300AA698B /* jot.c */, + ); + path = jot; + sourceTree = "<group>"; + }; + FCBA138F14A141A300AA698B /* kill */ = { + isa = PBXGroup; + children = ( + FCBA139014A141A300AA698B /* kill.1 */, + FCBA139114A141A300AA698B /* kill.c */, + ); + path = kill; + sourceTree = "<group>"; + }; + FCBA139314A141A300AA698B /* killall */ = { + isa = PBXGroup; + children = ( + FCBA139414A141A300AA698B /* killall.1 */, + FCBA139514A141A300AA698B /* killall.c */, + ); + path = killall; + sourceTree = "<group>"; + }; + FCBA139714A141A300AA698B /* lastcomm */ = { + isa = PBXGroup; + children = ( + FCBA139814A141A300AA698B /* lastcomm.1 */, + FCBA139914A141A300AA698B /* lastcomm.c */, + FCBA139B14A141A300AA698B /* pathnames.h */, + ); + path = lastcomm; + sourceTree = "<group>"; + }; + FCBA139C14A141A300AA698B /* locate */ = { + isa = PBXGroup; + children = ( + FCBA139D14A141A300AA698B /* bigram */, + FCBA13A014A141A300AA698B /* code */, + FCBA13A314A141A300AA698B /* locate */, + ); + path = locate; + sourceTree = "<group>"; + }; + FCBA139D14A141A300AA698B /* bigram */ = { + isa = PBXGroup; + children = ( + FCE30EC114B5347A00CC0294 /* locate.bigram.8 */, + FCBA139E14A141A300AA698B /* locate.bigram.c */, + ); + path = bigram; + sourceTree = "<group>"; + }; + FCBA13A014A141A300AA698B /* code */ = { + isa = PBXGroup; + children = ( + FCBA13B114A141A300AA698B /* locate.code.8 */, + FCBA13A114A141A300AA698B /* locate.code.c */, + ); + path = code; + sourceTree = "<group>"; + }; + FCBA13A314A141A300AA698B /* locate */ = { + isa = PBXGroup; + children = ( + FCBA13A414A141A300AA698B /* com.apple.locate.plist */, + FCBA13A514A141A300AA698B /* concatdb.sh */, + FCBA13A714A141A300AA698B /* locate.1 */, + FCBA13A814A141A300AA698B /* locate.c */, + FCBA13A914A141A300AA698B /* locate.h */, + FCBA13AA14A141A300AA698B /* locate.rc */, + FCBA13AB14A141A300AA698B /* locate.updatedb.8 */, + FCBA13AD14A141A300AA698B /* mklocatedb.sh */, + FCBA13AE14A141A300AA698B /* pathnames.h */, + FCBA13AF14A141A300AA698B /* updatedb.sh */, + FCBA13B014A141A300AA698B /* util.c */, + ); + path = locate; + sourceTree = "<group>"; + }; + FCBA13B314A141A300AA698B /* logname */ = { + isa = PBXGroup; + children = ( + FCBA13B414A141A300AA698B /* logname.1 */, + FCBA13B514A141A300AA698B /* logname.c */, + ); + path = logname; + sourceTree = "<group>"; + }; + FCBA13BE14A141A300AA698B /* mktemp */ = { + isa = PBXGroup; + children = ( + FCBA13C014A141A300AA698B /* mktemp.1 */, + FCBA13C114A141A300AA698B /* mktemp.c */, + ); + path = mktemp; + sourceTree = "<group>"; + }; + FCBA13C214A141A300AA698B /* nice */ = { + isa = PBXGroup; + children = ( + FCBA13C414A141A300AA698B /* nice.1 */, + FCBA13C514A141A300AA698B /* nice.c */, + ); + path = nice; + sourceTree = "<group>"; + }; + FCBA13C614A141A300AA698B /* nohup */ = { + isa = PBXGroup; + children = ( + FCBA13C814A141A300AA698B /* nohup.1 */, + FCBA13C914A141A300AA698B /* nohup.c */, + ); + path = nohup; + sourceTree = "<group>"; + }; + FCBA13CA14A141A300AA698B /* path_helper */ = { + isa = PBXGroup; + children = ( + FCBA13CC14A141A300AA698B /* path_helper.8 */, + FCBA13CD14A141A300AA698B /* path_helper.c */, + ); + path = path_helper; + sourceTree = "<group>"; + }; + FCBA13CE14A141A300AA698B /* printenv */ = { + isa = PBXGroup; + children = ( + FCBA13D014A141A300AA698B /* printenv.1 */, + FCBA13D114A141A300AA698B /* printenv.c */, + ); + path = printenv; + sourceTree = "<group>"; + }; + FCBA13D214A141A300AA698B /* printf */ = { + isa = PBXGroup; + children = ( + FCBA13D414A141A300AA698B /* printf.1 */, + FCBA13D514A141A300AA698B /* printf.c */, + FD155C761D6E3847005A53CA /* tests */, + ); + path = printf; + sourceTree = "<group>"; + }; + FCBA13D614A141A300AA698B /* pwd */ = { + isa = PBXGroup; + children = ( + FCBA13D814A141A300AA698B /* pwd.1 */, + FCBA13D914A141A300AA698B /* pwd.c */, + ); + path = pwd; + sourceTree = "<group>"; + }; + FCBA13DA14A141A300AA698B /* renice */ = { + isa = PBXGroup; + children = ( + FCBA13DC14A141A300AA698B /* renice.8 */, + FCBA13DD14A141A300AA698B /* renice.c */, + ); + path = renice; + sourceTree = "<group>"; + }; + FCBA13DE14A141A300AA698B /* script */ = { + isa = PBXGroup; + children = ( + FCBA13E014A141A300AA698B /* script.1 */, + FCBA13E114A141A300AA698B /* script.c */, + ); + path = script; + sourceTree = "<group>"; + }; + FCBA13E214A141A300AA698B /* seq */ = { + isa = PBXGroup; + children = ( + FCBA13E414A141A300AA698B /* seq.1 */, + FCBA13E514A141A300AA698B /* seq.c */, + ); + path = seq; + sourceTree = "<group>"; + }; + FCBA13E614A141A300AA698B /* shlock */ = { + isa = PBXGroup; + children = ( + FCBA13E814A141A300AA698B /* shlock.1 */, + FCBA13E914A141A300AA698B /* shlock.c */, + ); + path = shlock; + sourceTree = "<group>"; + }; + FCBA13EA14A141A300AA698B /* sleep */ = { + isa = PBXGroup; + children = ( + FCBA13EC14A141A300AA698B /* sleep.1 */, + FCBA13ED14A141A300AA698B /* sleep.c */, + ); + path = sleep; + sourceTree = "<group>"; + }; + FCBA13EE14A141A300AA698B /* su */ = { + isa = PBXGroup; + children = ( + 6C1C283E22FB822900E0AC67 /* su_entitlements.plist */, + FCBA13F014A141A300AA698B /* su.1 */, + FCBA13F114A141A300AA698B /* su.c */, + FCBA13F214A141A300AA698B /* su.pam */, + FCED3AF514B4FC1800C313C3 /* libpam.dylib */, + ); + path = su; + sourceTree = "<group>"; + }; + FCBA13F314A141A300AA698B /* tee */ = { + isa = PBXGroup; + children = ( + FCBA13F514A141A300AA698B /* tee.1 */, + FCBA13F614A141A300AA698B /* tee.c */, + ); + path = tee; + sourceTree = "<group>"; + }; + FCBA13F714A141A300AA698B /* test */ = { + isa = PBXGroup; + children = ( + FCBA13F814A141A300AA698B /* [.1 */, + FCBA13FA14A141A300AA698B /* test.1 */, + FCBA13FB14A141A300AA698B /* test.c */, + FD155C711D6E37B5005A53CA /* tests */, + ); + path = test; + sourceTree = "<group>"; + }; + FCBA13FD14A141A300AA698B /* time */ = { + isa = PBXGroup; + children = ( + CE799E5F24AD3B1B00E73238 /* tests */, + FCBA13FF14A141A300AA698B /* time.1 */, + FCBA140014A141A300AA698B /* time.c */, + ); + path = time; + sourceTree = "<group>"; + }; + FCBA140114A141A300AA698B /* true */ = { + isa = PBXGroup; + children = ( + FCBA140314A141A300AA698B /* true.1 */, + FCBA140414A141A300AA698B /* true.c */, + ); + path = true; + sourceTree = "<group>"; + }; + FCBA140614A141A300AA698B /* uname */ = { + isa = PBXGroup; + children = ( + FCBA140814A141A300AA698B /* uname.1 */, + FCBA140914A141A300AA698B /* uname.c */, + ); + path = uname; + sourceTree = "<group>"; + }; + FCBA140A14A141A300AA698B /* users */ = { + isa = PBXGroup; + children = ( + FCBA140C14A141A300AA698B /* users.1 */, + FCBA140D14A141A300AA698B /* users.c */, + ); + path = users; + sourceTree = "<group>"; + }; + FCBA140E14A141A300AA698B /* w */ = { + isa = PBXGroup; + children = ( + FCBA140F14A141A300AA698B /* extern.h */, + FCBA141014A141A300AA698B /* fmt.c */, + FCBA141214A141A300AA698B /* pr_time.c */, + FCBA141314A141A300AA698B /* proc_compare.c */, + FCBA141414A141A300AA698B /* uptime.1 */, + FCBA141514A141A300AA698B /* w.1 */, + FCBA141614A141A300AA698B /* w.c */, + ); + path = w; + sourceTree = "<group>"; + }; + FCBA141714A141A300AA698B /* whereis */ = { + isa = PBXGroup; + children = ( + FCBA141914A141A300AA698B /* whereis.1 */, + FCBA141A14A141A300AA698B /* whereis.c */, + ); + path = whereis; + sourceTree = "<group>"; + }; + FCBA141B14A141A300AA698B /* which */ = { + isa = PBXGroup; + children = ( + FCBA141D14A141A300AA698B /* which.1 */, + FCBA141E14A141A300AA698B /* which.c */, + ); + path = which; + sourceTree = "<group>"; + }; + FCBA141F14A141A300AA698B /* who */ = { + isa = PBXGroup; + children = ( + FCBA142114A141A300AA698B /* utmpentry.c */, + FCBA142214A141A300AA698B /* utmpentry.h */, + FCBA142314A141A300AA698B /* who.1 */, + FCBA142414A141A300AA698B /* who.c */, + FCBA02E514B505A50030BEB3 /* libresolv.dylib */, + ); + path = who; + sourceTree = "<group>"; + }; + FCBA142514A141A300AA698B /* xargs */ = { + isa = PBXGroup; + children = ( + FCBA142714A141A300AA698B /* pathnames.h */, + FCBA142814A141A300AA698B /* strnsubst.c */, + FCBA142914A141A300AA698B /* xargs.1 */, + FCBA142A14A141A300AA698B /* xargs.c */, + ); + path = xargs; + sourceTree = "<group>"; + }; + FCBA142B14A141A300AA698B /* yes */ = { + isa = PBXGroup; + children = ( + FCBA142D14A141A300AA698B /* yes.1 */, + FCBA142E14A141A300AA698B /* yes.c */, + ); + path = yes; + sourceTree = "<group>"; + }; + FD155C711D6E37B5005A53CA /* tests */ = { + isa = PBXGroup; + children = ( + FD155C721D6E37B5005A53CA /* legacy_test.sh */, + ); + path = tests; + sourceTree = "<group>"; + }; + FD155C761D6E3847005A53CA /* tests */ = { + isa = PBXGroup; + children = ( + FD155C771D6E3847005A53CA /* legacy_test.sh */, + FD155C7A1D6E3847005A53CA /* regress.b.out */, + FD155C7B1D6E3847005A53CA /* regress.d.out */, + FD155C7C1D6E3847005A53CA /* regress.f.out */, + FD155C7D1D6E3847005A53CA /* regress.l1.out */, + FD155C7E1D6E3847005A53CA /* regress.l2.out */, + FD155C7F1D6E3847005A53CA /* regress.m1.out */, + FD155C801D6E3847005A53CA /* regress.m2.out */, + FD155C811D6E3847005A53CA /* regress.m3.out */, + FD155C821D6E3847005A53CA /* regress.m4.out */, + FD155C831D6E3847005A53CA /* regress.m5.out */, + FD155C841D6E3847005A53CA /* regress.missingpos1.out */, + FD155C851D6E3847005A53CA /* regress.s.out */, + FD155C861D6E3847005A53CA /* regress.sh */, + FD155C871D6E3847005A53CA /* regress.zero.out */, + ); + path = tests; + sourceTree = "<group>"; + }; + FD6060B31B7C0388004BCA6A /* sh */ = { + isa = PBXGroup; + children = ( + FD6061001B7C0D01004BCA6A /* BUILT */, + FD6060BC1B7C0471004BCA6A /* alias.c */, + FD6061061B7D2C8C004BCA6A /* alias.h */, + FD6061081B7D2C8C004BCA6A /* arith.h */, + FD6060BD1B7C0471004BCA6A /* arith_yacc.c */, + FD6061071B7D2C8C004BCA6A /* arith_yacc.h */, + FD6060BE1B7C0471004BCA6A /* arith_yylex.c */, + FD6060E81B7C04E4004BCA6A /* bltin */, + FD6061091B7D2C8C004BCA6A /* builtins.def */, + FD6060BF1B7C0471004BCA6A /* cd.c */, + FD60610A1B7D2C8C004BCA6A /* cd.h */, + FD6060C01B7C0471004BCA6A /* error.c */, + FD60610B1B7D2C8C004BCA6A /* error.h */, + FD6060C11B7C0471004BCA6A /* eval.c */, + FD60610C1B7D2C8C004BCA6A /* eval.h */, + FD6060C21B7C0471004BCA6A /* exec.c */, + FD60610D1B7D2C8C004BCA6A /* exec.h */, + FD6060C31B7C0471004BCA6A /* expand.c */, + FD60610E1B7D2C8C004BCA6A /* expand.h */, + FD6061271B7D2D1D004BCA6A /* funcs */, + FD6060C41B7C0471004BCA6A /* histedit.c */, + FD6060C51B7C0471004BCA6A /* input.c */, + FD60610F1B7D2C8C004BCA6A /* input.h */, + FD6060C61B7C0471004BCA6A /* jobs.c */, + FD6061101B7D2C8C004BCA6A /* jobs.h */, + FD6060C71B7C0471004BCA6A /* mail.c */, + FD6061111B7D2C8C004BCA6A /* mail.h */, + FD6060B41B7C0388004BCA6A /* main.c */, + FD6061121B7D2C8C004BCA6A /* main.h */, + FD6060C81B7C0471004BCA6A /* memalloc.c */, + FD6061131B7D2C8C004BCA6A /* memalloc.h */, + FD6060C91B7C0471004BCA6A /* miscbltin.c */, + FD6061141B7D2C8C004BCA6A /* mkbuiltins */, + FD6061151B7D2C8C004BCA6A /* mknodes.c */, + FD6061161B7D2C8C004BCA6A /* mksyntax.c */, + FD6061171B7D2C8C004BCA6A /* mktokens */, + FD6061181B7D2C8C004BCA6A /* myhistedit.h */, + FD6060CA1B7C0471004BCA6A /* mystring.c */, + FD6061191B7D2C8C004BCA6A /* mystring.h */, + FD60611A1B7D2C8C004BCA6A /* nodes.c.pat */, + FD60611B1B7D2C8C004BCA6A /* nodetypes */, + FD6060CB1B7C0471004BCA6A /* options.c */, + FD60611C1B7D2C8C004BCA6A /* options.h */, + FD6060CC1B7C0471004BCA6A /* output.c */, + FD60611D1B7D2C8C004BCA6A /* output.h */, + FD6060CD1B7C0471004BCA6A /* parser.c */, + FD60611E1B7D2C8C004BCA6A /* parser.h */, + FD6060CE1B7C0471004BCA6A /* redir.c */, + FD60611F1B7D2C8C004BCA6A /* redir.h */, + FD6061201B7D2C8C004BCA6A /* sh.1 */, + FD6061211B7D2C8C004BCA6A /* shell.h */, + FD6060CF1B7C0471004BCA6A /* show.c */, + FD6061221B7D2C8C004BCA6A /* show.h */, + FD6060D01B7C0471004BCA6A /* trap.c */, + FD6061231B7D2C8C004BCA6A /* trap.h */, + FD6060D11B7C0471004BCA6A /* var.c */, + FD6061241B7D2C8C004BCA6A /* var.h */, + ); + path = sh; + sourceTree = "<group>"; + }; + FD6060E81B7C04E4004BCA6A /* bltin */ = { + isa = PBXGroup; + children = ( + FD6061051B7D2BF8004BCA6A /* bltin.h */, + FD6060EA1B7C04E4004BCA6A /* echo.c */, + ); + path = bltin; + sourceTree = "<group>"; + }; + FD6061001B7C0D01004BCA6A /* BUILT */ = { + isa = PBXGroup; + children = ( + FD6060FA1B7C0CAF004BCA6A /* builtins.c */, + FD6061011B7D2B6D004BCA6A /* builtins.h */, + FD6060FB1B7C0CAF004BCA6A /* nodes.c */, + FD6061021B7D2B6D004BCA6A /* nodes.h */, + FD6060FC1B7C0CAF004BCA6A /* syntax.c */, + FD6061031B7D2B6D004BCA6A /* syntax.h */, + FD6061041B7D2B6D004BCA6A /* token.h */, + ); + name = BUILT; + sourceTree = BUILT_PRODUCTS_DIR; + }; + FD6061271B7D2D1D004BCA6A /* funcs */ = { + isa = PBXGroup; + children = ( + FD6061281B7D2D1D004BCA6A /* cmv */, + FD6061291B7D2D1D004BCA6A /* dirs */, + FD60612A1B7D2D1D004BCA6A /* login */, + FD60612B1B7D2D1D004BCA6A /* newgrp */, + FD60612C1B7D2D1D004BCA6A /* popd */, + FD60612D1B7D2D1D004BCA6A /* pushd */, + FD60612E1B7D2D1D004BCA6A /* suspend */, + ); + path = funcs; + sourceTree = "<group>"; + }; + FD60612F1B7D2DDE004BCA6A /* xcconfigs */ = { + isa = PBXGroup; + children = ( + FD6061301B7D2DDE004BCA6A /* base.xcconfig */, + FD6061311B7D2DDE004BCA6A /* sh.xcconfig */, + ); + path = xcconfigs; + sourceTree = "<group>"; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + C6868575154725700025D623 /* systime */ = { + isa = PBXNativeTarget; + buildConfigurationList = C686857E154725700025D623 /* Build configuration list for PBXNativeTarget "systime" */; + buildPhases = ( + C6868572154725700025D623 /* Sources */, + C6868573154725700025D623 /* Frameworks */, + C6868574154725700025D623 /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = systime; + productName = systime; + productReference = C6868576154725700025D623 /* systime */; + productType = "com.apple.product-type.tool"; + }; + FC5D637F14B9819E00123E48 /* what */ = { + isa = PBXNativeTarget; + buildConfigurationList = FC5D638914B9819E00123E48 /* Build configuration list for PBXNativeTarget "what" */; + buildPhases = ( + FC5D638014B9819E00123E48 /* Sources */, + FC5D638514B9819E00123E48 /* Frameworks */, + FC5D638614B9819E00123E48 /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = what; + productName = shell_cmds; + productReference = FC5D638B14B9819E00123E48 /* what */; + productType = "com.apple.product-type.tool"; + }; + FC5D638F14B9822D00123E48 /* hexdump */ = { + isa = PBXNativeTarget; + buildConfigurationList = FC5D639514B9822D00123E48 /* Build configuration list for PBXNativeTarget "hexdump" */; + buildPhases = ( + FC5D639014B9822D00123E48 /* Sources */, + FC5D639214B9822D00123E48 /* Frameworks */, + FC5D639314B9822D00123E48 /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = hexdump; + productName = shell_cmds; + productReference = FC5D639714B9822D00123E48 /* hexdump */; + productType = "com.apple.product-type.tool"; + }; + FCBA149814A141FF00AA698B /* apply */ = { + isa = PBXNativeTarget; + buildConfigurationList = FCBA149C14A141FF00AA698B /* Build configuration list for PBXNativeTarget "apply" */; + buildPhases = ( + FCBA149914A141FF00AA698B /* Sources */, + FCBA149A14A141FF00AA698B /* Frameworks */, + FCBA149B14A141FF00AA698B /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = apply; + productName = shell_cmds; + productReference = FCBA149E14A141FF00AA698B /* apply */; + productType = "com.apple.product-type.tool"; + }; + FCBA14AE14A1422D00AA698B /* basename */ = { + isa = PBXNativeTarget; + buildConfigurationList = FCBA14B214A1422D00AA698B /* Build configuration list for PBXNativeTarget "basename" */; + buildPhases = ( + FCBA14AF14A1422D00AA698B /* Sources */, + FCBA14B014A1422D00AA698B /* Frameworks */, + FCBA14B114A1422D00AA698B /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = basename; + productName = shell_cmds; + productReference = FCBA14B414A1422D00AA698B /* basename */; + productType = "com.apple.product-type.tool"; + }; + FCBA14B714A1423E00AA698B /* chroot */ = { + isa = PBXNativeTarget; + buildConfigurationList = FCBA14BB14A1423E00AA698B /* Build configuration list for PBXNativeTarget "chroot" */; + buildPhases = ( + FCBA14B814A1423E00AA698B /* Sources */, + FCBA14B914A1423E00AA698B /* Frameworks */, + FCBA14BA14A1423E00AA698B /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = chroot; + productName = shell_cmds; + productReference = FCBA14BD14A1423E00AA698B /* chroot */; + productType = "com.apple.product-type.tool"; + }; + FCBA14BF14A1426800AA698B /* date */ = { + isa = PBXNativeTarget; + buildConfigurationList = FCBA14C314A1426800AA698B /* Build configuration list for PBXNativeTarget "date" */; + buildPhases = ( + FCBA14C014A1426800AA698B /* Sources */, + FCBA14C114A1426800AA698B /* Frameworks */, + FCBA14C214A1426800AA698B /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = date; + productName = shell_cmds; + productReference = FCBA14C514A1426800AA698B /* date */; + productType = "com.apple.product-type.tool"; + }; + FCBA14C714A1428000AA698B /* dirname */ = { + isa = PBXNativeTarget; + buildConfigurationList = FCBA14CB14A1428000AA698B /* Build configuration list for PBXNativeTarget "dirname" */; + buildPhases = ( + FCBA14C814A1428000AA698B /* Sources */, + FCBA14C914A1428000AA698B /* Frameworks */, + FCBA14CA14A1428000AA698B /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = dirname; + productName = shell_cmds; + productReference = FCBA14CD14A1428000AA698B /* dirname */; + productType = "com.apple.product-type.tool"; + }; + FCBA14CF14A1433F00AA698B /* echo */ = { + isa = PBXNativeTarget; + buildConfigurationList = FCBA14D314A1433F00AA698B /* Build configuration list for PBXNativeTarget "echo" */; + buildPhases = ( + FCBA14D014A1433F00AA698B /* Sources */, + FCBA14D114A1433F00AA698B /* Frameworks */, + FCBA14D214A1433F00AA698B /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = echo; + productName = shell_cmds; + productReference = FCBA14D514A1433F00AA698B /* echo */; + productType = "com.apple.product-type.tool"; + }; + FCBA14D714A1434A00AA698B /* env */ = { + isa = PBXNativeTarget; + buildConfigurationList = FCBA14DB14A1434A00AA698B /* Build configuration list for PBXNativeTarget "env" */; + buildPhases = ( + FCBA14D814A1434A00AA698B /* Sources */, + FCBA14D914A1434A00AA698B /* Frameworks */, + FCBA14DA14A1434A00AA698B /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = env; + productName = shell_cmds; + productReference = FCBA14DD14A1434A00AA698B /* env */; + productType = "com.apple.product-type.tool"; + }; + FCBA150E14A1453A00AA698B /* expr */ = { + isa = PBXNativeTarget; + buildConfigurationList = FCBA151214A1453A00AA698B /* Build configuration list for PBXNativeTarget "expr" */; + buildPhases = ( + FCBA150F14A1453A00AA698B /* Sources */, + FCBA151014A1453A00AA698B /* Frameworks */, + FCBA151114A1453A00AA698B /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = expr; + productName = shell_cmds; + productReference = FCBA151414A1453A00AA698B /* expr */; + productType = "com.apple.product-type.tool"; + }; + FCBA151614A1454D00AA698B /* false */ = { + isa = PBXNativeTarget; + buildConfigurationList = FCBA151A14A1454D00AA698B /* Build configuration list for PBXNativeTarget "false" */; + buildPhases = ( + FCBA151714A1454D00AA698B /* Sources */, + FCBA151814A1454D00AA698B /* Frameworks */, + FCBA151914A1454D00AA698B /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = false; + productName = shell_cmds; + productReference = FCBA151C14A1454D00AA698B /* false */; + productType = "com.apple.product-type.tool"; + }; + FCBA151E14A1455600AA698B /* find */ = { + isa = PBXNativeTarget; + buildConfigurationList = FCBA152214A1455600AA698B /* Build configuration list for PBXNativeTarget "find" */; + buildPhases = ( + FCBA151F14A1455600AA698B /* Sources */, + FCBA152014A1455600AA698B /* Frameworks */, + FCBA152114A1455600AA698B /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = find; + productName = shell_cmds; + productReference = FCBA152414A1455600AA698B /* find */; + productType = "com.apple.product-type.tool"; + }; + FCBA152614A1455900AA698B /* getopt */ = { + isa = PBXNativeTarget; + buildConfigurationList = FCBA152A14A1455900AA698B /* Build configuration list for PBXNativeTarget "getopt" */; + buildPhases = ( + FCBA152714A1455900AA698B /* Sources */, + FCBA152814A1455900AA698B /* Frameworks */, + FCBA152914A1455900AA698B /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = getopt; + productName = shell_cmds; + productReference = FCBA152C14A1455900AA698B /* getopt */; + productType = "com.apple.product-type.tool"; + }; + FCBA152E14A1455C00AA698B /* hostname */ = { + isa = PBXNativeTarget; + buildConfigurationList = FCBA153214A1455C00AA698B /* Build configuration list for PBXNativeTarget "hostname" */; + buildPhases = ( + FCBA152F14A1455C00AA698B /* Sources */, + FCBA153014A1455C00AA698B /* Frameworks */, + FCBA153114A1455C00AA698B /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = hostname; + productName = shell_cmds; + productReference = FCBA153414A1455C00AA698B /* hostname */; + productType = "com.apple.product-type.tool"; + }; + FCBA153614A1455F00AA698B /* id */ = { + isa = PBXNativeTarget; + buildConfigurationList = FCBA153A14A1455F00AA698B /* Build configuration list for PBXNativeTarget "id" */; + buildPhases = ( + FCBA153714A1455F00AA698B /* Sources */, + FCBA153814A1455F00AA698B /* Frameworks */, + FCBA153914A1455F00AA698B /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = id; + productName = shell_cmds; + productReference = FCBA153C14A1455F00AA698B /* id */; + productType = "com.apple.product-type.tool"; + }; + FCBA153E14A1456100AA698B /* jot */ = { + isa = PBXNativeTarget; + buildConfigurationList = FCBA154214A1456100AA698B /* Build configuration list for PBXNativeTarget "jot" */; + buildPhases = ( + FCBA153F14A1456100AA698B /* Sources */, + FCBA154014A1456100AA698B /* Frameworks */, + FCBA154114A1456100AA698B /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = jot; + productName = shell_cmds; + productReference = FCBA154414A1456100AA698B /* jot */; + productType = "com.apple.product-type.tool"; + }; + FCBA154614A1456400AA698B /* kill */ = { + isa = PBXNativeTarget; + buildConfigurationList = FCBA154A14A1456400AA698B /* Build configuration list for PBXNativeTarget "kill" */; + buildPhases = ( + FCBA154714A1456400AA698B /* Sources */, + FCBA154814A1456400AA698B /* Frameworks */, + FCBA154914A1456400AA698B /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = kill; + productName = shell_cmds; + productReference = FCBA154C14A1456400AA698B /* kill */; + productType = "com.apple.product-type.tool"; + }; + FCBA154E14A1456700AA698B /* killall */ = { + isa = PBXNativeTarget; + buildConfigurationList = FCBA155214A1456700AA698B /* Build configuration list for PBXNativeTarget "killall" */; + buildPhases = ( + FCBA154F14A1456700AA698B /* Sources */, + FCBA155014A1456700AA698B /* Frameworks */, + FCBA155114A1456700AA698B /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = killall; + productName = shell_cmds; + productReference = FCBA155414A1456700AA698B /* killall */; + productType = "com.apple.product-type.tool"; + }; + FCBA155614A1456A00AA698B /* lastcomm */ = { + isa = PBXNativeTarget; + buildConfigurationList = FCBA155A14A1456A00AA698B /* Build configuration list for PBXNativeTarget "lastcomm" */; + buildPhases = ( + FCBA155714A1456A00AA698B /* Sources */, + FCBA155814A1456A00AA698B /* Frameworks */, + FCBA155914A1456A00AA698B /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = lastcomm; + productName = shell_cmds; + productReference = FCBA155C14A1456A00AA698B /* lastcomm */; + productType = "com.apple.product-type.tool"; + }; + FCBA155E14A1456E00AA698B /* locate */ = { + isa = PBXNativeTarget; + buildConfigurationList = FCBA156214A1456E00AA698B /* Build configuration list for PBXNativeTarget "locate" */; + buildPhases = ( + FCBA155F14A1456E00AA698B /* Sources */, + FCBA156014A1456E00AA698B /* Frameworks */, + FCBA156114A1456E00AA698B /* CopyFiles */, + FCE30EBE14B533B500CC0294 /* CopyFiles */, + FCE30EB914B532C700CC0294 /* CopyFiles */, + FCE30EBC14B5339000CC0294 /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = locate; + productName = shell_cmds; + productReference = FCBA156414A1456E00AA698B /* locate */; + productType = "com.apple.product-type.tool"; + }; + FCBA156614A1457100AA698B /* logname */ = { + isa = PBXNativeTarget; + buildConfigurationList = FCBA156A14A1457100AA698B /* Build configuration list for PBXNativeTarget "logname" */; + buildPhases = ( + FCBA156714A1457100AA698B /* Sources */, + FCBA156814A1457100AA698B /* Frameworks */, + FCBA156914A1457100AA698B /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = logname; + productName = shell_cmds; + productReference = FCBA156C14A1457100AA698B /* logname */; + productType = "com.apple.product-type.tool"; + }; + FCBA157614A1457B00AA698B /* mktemp */ = { + isa = PBXNativeTarget; + buildConfigurationList = FCBA157A14A1457B00AA698B /* Build configuration list for PBXNativeTarget "mktemp" */; + buildPhases = ( + FCBA157714A1457B00AA698B /* Sources */, + FCBA157814A1457B00AA698B /* Frameworks */, + FCBA157914A1457B00AA698B /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = mktemp; + productName = shell_cmds; + productReference = FCBA157C14A1457B00AA698B /* mktemp */; + productType = "com.apple.product-type.tool"; + }; + FCBA157E14A1457F00AA698B /* nice */ = { + isa = PBXNativeTarget; + buildConfigurationList = FCBA158214A1457F00AA698B /* Build configuration list for PBXNativeTarget "nice" */; + buildPhases = ( + FCBA157F14A1457F00AA698B /* Sources */, + FCBA158014A1457F00AA698B /* Frameworks */, + FCBA158114A1457F00AA698B /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = nice; + productName = shell_cmds; + productReference = FCBA158414A1457F00AA698B /* nice */; + productType = "com.apple.product-type.tool"; + }; + FCBA158614A1458500AA698B /* nohup */ = { + isa = PBXNativeTarget; + buildConfigurationList = FCBA158A14A1458500AA698B /* Build configuration list for PBXNativeTarget "nohup" */; + buildPhases = ( + FCBA158714A1458500AA698B /* Sources */, + FCBA158814A1458500AA698B /* Frameworks */, + FCBA158914A1458500AA698B /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = nohup; + productName = shell_cmds; + productReference = FCBA158C14A1458500AA698B /* nohup */; + productType = "com.apple.product-type.tool"; + }; + FCBA158E14A1458800AA698B /* path_helper */ = { + isa = PBXNativeTarget; + buildConfigurationList = FCBA159214A1458800AA698B /* Build configuration list for PBXNativeTarget "path_helper" */; + buildPhases = ( + FCBA158F14A1458800AA698B /* Sources */, + FCBA159014A1458800AA698B /* Frameworks */, + FCBA159114A1458800AA698B /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = path_helper; + productName = shell_cmds; + productReference = FCBA159414A1458800AA698B /* path_helper */; + productType = "com.apple.product-type.tool"; + }; + FCBA159614A1458C00AA698B /* printenv */ = { + isa = PBXNativeTarget; + buildConfigurationList = FCBA159A14A1458C00AA698B /* Build configuration list for PBXNativeTarget "printenv" */; + buildPhases = ( + FCBA159714A1458C00AA698B /* Sources */, + FCBA159814A1458C00AA698B /* Frameworks */, + FCBA159914A1458C00AA698B /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = printenv; + productName = shell_cmds; + productReference = FCBA159C14A1458C00AA698B /* printenv */; + productType = "com.apple.product-type.tool"; + }; + FCBA159E14A1459000AA698B /* printf */ = { + isa = PBXNativeTarget; + buildConfigurationList = FCBA15A214A1459000AA698B /* Build configuration list for PBXNativeTarget "printf" */; + buildPhases = ( + FCBA159F14A1459000AA698B /* Sources */, + FCBA15A014A1459000AA698B /* Frameworks */, + FCBA15A114A1459000AA698B /* CopyFiles */, + FD155C881D6E38AB005A53CA /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = printf; + productName = shell_cmds; + productReference = FCBA15A414A1459000AA698B /* printf */; + productType = "com.apple.product-type.tool"; + }; + FCBA15A614A1459500AA698B /* pwd */ = { + isa = PBXNativeTarget; + buildConfigurationList = FCBA15AA14A1459500AA698B /* Build configuration list for PBXNativeTarget "pwd" */; + buildPhases = ( + FCBA15A714A1459500AA698B /* Sources */, + FCBA15A814A1459500AA698B /* Frameworks */, + FCBA15A914A1459500AA698B /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = pwd; + productName = shell_cmds; + productReference = FCBA15AC14A1459500AA698B /* pwd */; + productType = "com.apple.product-type.tool"; + }; + FCBA15AE14A1459700AA698B /* renice */ = { + isa = PBXNativeTarget; + buildConfigurationList = FCBA15B214A1459700AA698B /* Build configuration list for PBXNativeTarget "renice" */; + buildPhases = ( + FCBA15AF14A1459700AA698B /* Sources */, + FCBA15B014A1459700AA698B /* Frameworks */, + FCBA15B114A1459700AA698B /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = renice; + productName = shell_cmds; + productReference = FCBA15B414A1459700AA698B /* renice */; + productType = "com.apple.product-type.tool"; + }; + FCBA15B614A1459900AA698B /* script */ = { + isa = PBXNativeTarget; + buildConfigurationList = FCBA15BA14A1459900AA698B /* Build configuration list for PBXNativeTarget "script" */; + buildPhases = ( + FCBA15B714A1459900AA698B /* Sources */, + FCBA15B814A1459900AA698B /* Frameworks */, + FCBA15B914A1459900AA698B /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = script; + productName = shell_cmds; + productReference = FCBA15BC14A1459900AA698B /* script */; + productType = "com.apple.product-type.tool"; + }; + FCBA15BE14A1459E00AA698B /* seq */ = { + isa = PBXNativeTarget; + buildConfigurationList = FCBA15C214A1459E00AA698B /* Build configuration list for PBXNativeTarget "seq" */; + buildPhases = ( + FCBA15BF14A1459E00AA698B /* Sources */, + FCBA15C014A1459E00AA698B /* Frameworks */, + FCBA15C114A1459E00AA698B /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = seq; + productName = shell_cmds; + productReference = FCBA15C414A1459E00AA698B /* seq */; + productType = "com.apple.product-type.tool"; + }; + FCBA15C614A145A200AA698B /* shlock */ = { + isa = PBXNativeTarget; + buildConfigurationList = FCBA15CA14A145A200AA698B /* Build configuration list for PBXNativeTarget "shlock" */; + buildPhases = ( + FCBA15C714A145A200AA698B /* Sources */, + FCBA15C814A145A200AA698B /* Frameworks */, + FCBA15C914A145A200AA698B /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = shlock; + productName = shell_cmds; + productReference = FCBA15CC14A145A200AA698B /* shlock */; + productType = "com.apple.product-type.tool"; + }; + FCBA15CE14A145A500AA698B /* sleep */ = { + isa = PBXNativeTarget; + buildConfigurationList = FCBA15D214A145A500AA698B /* Build configuration list for PBXNativeTarget "sleep" */; + buildPhases = ( + FCBA15CF14A145A500AA698B /* Sources */, + FCBA15D014A145A500AA698B /* Frameworks */, + FCBA15D114A145A500AA698B /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = sleep; + productName = shell_cmds; + productReference = FCBA15D414A145A500AA698B /* sleep */; + productType = "com.apple.product-type.tool"; + }; + FCBA15D614A145A700AA698B /* su */ = { + isa = PBXNativeTarget; + buildConfigurationList = FCBA15DA14A145A700AA698B /* Build configuration list for PBXNativeTarget "su" */; + buildPhases = ( + FCBA15D714A145A700AA698B /* Sources */, + FCBA15D814A145A700AA698B /* Frameworks */, + FCBA15D914A145A700AA698B /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = su; + productName = shell_cmds; + productReference = FCBA15DC14A145A700AA698B /* su */; + productType = "com.apple.product-type.tool"; + }; + FCBA15DE14A145A900AA698B /* tee */ = { + isa = PBXNativeTarget; + buildConfigurationList = FCBA15E214A145A900AA698B /* Build configuration list for PBXNativeTarget "tee" */; + buildPhases = ( + FCBA15DF14A145A900AA698B /* Sources */, + FCBA15E014A145A900AA698B /* Frameworks */, + FCBA15E114A145A900AA698B /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = tee; + productName = shell_cmds; + productReference = FCBA15E414A145A900AA698B /* tee */; + productType = "com.apple.product-type.tool"; + }; + FCBA15E614A145AE00AA698B /* test */ = { + isa = PBXNativeTarget; + buildConfigurationList = FCBA15EA14A145AE00AA698B /* Build configuration list for PBXNativeTarget "test" */; + buildPhases = ( + FCBA15E714A145AE00AA698B /* Sources */, + FCBA15E814A145AE00AA698B /* Frameworks */, + FCBA15E914A145AE00AA698B /* CopyFiles */, + FD155C741D6E37D7005A53CA /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = test; + productName = shell_cmds; + productReference = FCBA15EC14A145AE00AA698B /* test */; + productType = "com.apple.product-type.tool"; + }; + FCBA15EE14A145B000AA698B /* time */ = { + isa = PBXNativeTarget; + buildConfigurationList = FCBA15F214A145B000AA698B /* Build configuration list for PBXNativeTarget "time" */; + buildPhases = ( + FCBA15EF14A145B000AA698B /* Sources */, + FCBA15F014A145B000AA698B /* Frameworks */, + FCBA15F114A145B000AA698B /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = time; + productName = shell_cmds; + productReference = FCBA15F414A145B000AA698B /* time */; + productType = "com.apple.product-type.tool"; + }; + FCBA15F614A145B200AA698B /* true */ = { + isa = PBXNativeTarget; + buildConfigurationList = FCBA15FA14A145B200AA698B /* Build configuration list for PBXNativeTarget "true" */; + buildPhases = ( + FCBA15F714A145B200AA698B /* Sources */, + FCBA15F814A145B200AA698B /* Frameworks */, + FCBA15F914A145B200AA698B /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = true; + productName = shell_cmds; + productReference = FCBA15FC14A145B200AA698B /* true */; + productType = "com.apple.product-type.tool"; + }; + FCBA15FE14A145B500AA698B /* uname */ = { + isa = PBXNativeTarget; + buildConfigurationList = FCBA160214A145B500AA698B /* Build configuration list for PBXNativeTarget "uname" */; + buildPhases = ( + FCBA15FF14A145B500AA698B /* Sources */, + FCBA160014A145B500AA698B /* Frameworks */, + FCBA160114A145B500AA698B /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = uname; + productName = shell_cmds; + productReference = FCBA160414A145B500AA698B /* uname */; + productType = "com.apple.product-type.tool"; + }; + FCBA160614A145B800AA698B /* users */ = { + isa = PBXNativeTarget; + buildConfigurationList = FCBA160A14A145B800AA698B /* Build configuration list for PBXNativeTarget "users" */; + buildPhases = ( + FCBA160714A145B800AA698B /* Sources */, + FCBA160814A145B800AA698B /* Frameworks */, + FCBA160914A145B800AA698B /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = users; + productName = shell_cmds; + productReference = FCBA160C14A145B800AA698B /* users */; + productType = "com.apple.product-type.tool"; + }; + FCBA160E14A145BA00AA698B /* w */ = { + isa = PBXNativeTarget; + buildConfigurationList = FCBA161214A145BA00AA698B /* Build configuration list for PBXNativeTarget "w" */; + buildPhases = ( + FCBA160F14A145BA00AA698B /* Sources */, + FCBA161014A145BA00AA698B /* Frameworks */, + FCBA161114A145BA00AA698B /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = w; + productName = shell_cmds; + productReference = FCBA161414A145BA00AA698B /* w */; + productType = "com.apple.product-type.tool"; + }; + FCBA161614A145BC00AA698B /* whereis */ = { + isa = PBXNativeTarget; + buildConfigurationList = FCBA161A14A145BC00AA698B /* Build configuration list for PBXNativeTarget "whereis" */; + buildPhases = ( + FCBA161714A145BC00AA698B /* Sources */, + FCBA161814A145BC00AA698B /* Frameworks */, + FCBA161914A145BC00AA698B /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = whereis; + productName = shell_cmds; + productReference = FCBA161C14A145BC00AA698B /* whereis */; + productType = "com.apple.product-type.tool"; + }; + FCBA161E14A145C000AA698B /* which */ = { + isa = PBXNativeTarget; + buildConfigurationList = FCBA162214A145C000AA698B /* Build configuration list for PBXNativeTarget "which" */; + buildPhases = ( + FCBA161F14A145C000AA698B /* Sources */, + FCBA162014A145C000AA698B /* Frameworks */, + FCBA162114A145C000AA698B /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = which; + productName = shell_cmds; + productReference = FCBA162414A145C000AA698B /* which */; + productType = "com.apple.product-type.tool"; + }; + FCBA162614A145C500AA698B /* who */ = { + isa = PBXNativeTarget; + buildConfigurationList = FCBA162A14A145C500AA698B /* Build configuration list for PBXNativeTarget "who" */; + buildPhases = ( + FCBA162714A145C500AA698B /* Sources */, + FCBA162814A145C500AA698B /* Frameworks */, + FCBA162914A145C500AA698B /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = who; + productName = shell_cmds; + productReference = FCBA162C14A145C500AA698B /* who */; + productType = "com.apple.product-type.tool"; + }; + FCBA162E14A145C700AA698B /* xargs */ = { + isa = PBXNativeTarget; + buildConfigurationList = FCBA163214A145C700AA698B /* Build configuration list for PBXNativeTarget "xargs" */; + buildPhases = ( + FCBA162F14A145C700AA698B /* Sources */, + FCBA163014A145C700AA698B /* Frameworks */, + FCBA163114A145C700AA698B /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = xargs; + productName = shell_cmds; + productReference = FCBA163414A145C700AA698B /* xargs */; + productType = "com.apple.product-type.tool"; + }; + FCBA163614A145CA00AA698B /* yes */ = { + isa = PBXNativeTarget; + buildConfigurationList = FCBA163A14A145CA00AA698B /* Build configuration list for PBXNativeTarget "yes" */; + buildPhases = ( + FCBA163714A145CA00AA698B /* Sources */, + FCBA163814A145CA00AA698B /* Frameworks */, + FCBA163914A145CA00AA698B /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = yes; + productName = shell_cmds; + productReference = FCBA163C14A145CA00AA698B /* yes */; + productType = "com.apple.product-type.tool"; + }; + FCE30EC214B5368A00CC0294 /* locate.bigram */ = { + isa = PBXNativeTarget; + buildConfigurationList = FCE30ED014B5368A00CC0294 /* Build configuration list for PBXNativeTarget "locate.bigram" */; + buildPhases = ( + FCE30EC314B5368A00CC0294 /* Sources */, + FCE30EC614B5368A00CC0294 /* Frameworks */, + FCE30EC714B5368A00CC0294 /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = locate.bigram; + productName = shell_cmds; + productReference = FCE30ED214B5368A00CC0294 /* locate.bigram */; + productType = "com.apple.product-type.tool"; + }; + FCE30ED614B536C900CC0294 /* locate.code */ = { + isa = PBXNativeTarget; + buildConfigurationList = FCE30EDC14B536C900CC0294 /* Build configuration list for PBXNativeTarget "locate.code" */; + buildPhases = ( + FCE30ED714B536C900CC0294 /* Sources */, + FCE30ED914B536C900CC0294 /* Frameworks */, + FCE30EDA14B536C900CC0294 /* CopyFiles */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = locate.code; + productName = shell_cmds; + productReference = FCE30EDE14B536C900CC0294 /* locate.code */; + productType = "com.apple.product-type.tool"; + }; + FD6060B11B7C0388004BCA6A /* sh */ = { + isa = PBXNativeTarget; + buildConfigurationList = FD6060B71B7C0388004BCA6A /* Build configuration list for PBXNativeTarget "sh" */; + buildPhases = ( + FD6060EF1B7C0590004BCA6A /* mkbuiltins */, + FD6060F11B7C0742004BCA6A /* mknodes */, + FD6060F21B7C0744004BCA6A /* mksyntax */, + FD6060F31B7C0964004BCA6A /* mktokens */, + FD6060AE1B7C0388004BCA6A /* Sources */, + FD6060AF1B7C0388004BCA6A /* Frameworks */, + FD6061321B7D32AD004BCA6A /* sh.1 */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = sh; + productName = sh; + productReference = FD6060B21B7C0388004BCA6A /* ash */; + productType = "com.apple.product-type.tool"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + FC80BF5714A05A2F00C6F7F5 /* Project object */ = { + isa = PBXProject; + attributes = { + LastUpgradeCheck = 1100; + ORGANIZATIONNAME = "Apple Inc."; + TargetAttributes = { + FD6060B11B7C0388004BCA6A = { + CreatedOnToolsVersion = 7.0; + }; + }; + }; + buildConfigurationList = FC80BF5A14A05A2F00C6F7F5 /* Build configuration list for PBXProject "shell_cmds" */; + compatibilityVersion = "Xcode 3.2"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = FC80BF5514A05A2F00C6F7F5; + productRefGroup = FC80BF6114A05A2F00C6F7F5 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + FCBA14FD14A144EC00AA698B /* All_OSX */, + FCE30F4A14B619B000CC0294 /* All_iOS */, + FCBA149814A141FF00AA698B /* apply */, + FCBA14AE14A1422D00AA698B /* basename */, + FCBA14B714A1423E00AA698B /* chroot */, + FCBA14BF14A1426800AA698B /* date */, + FCBA14C714A1428000AA698B /* dirname */, + FCBA14CF14A1433F00AA698B /* echo */, + FCBA14D714A1434A00AA698B /* env */, + FCBA150E14A1453A00AA698B /* expr */, + FCBA151614A1454D00AA698B /* false */, + FCBA151E14A1455600AA698B /* find */, + FCBA152614A1455900AA698B /* getopt */, + FC5D638F14B9822D00123E48 /* hexdump */, + FCBA152E14A1455C00AA698B /* hostname */, + FCBA153614A1455F00AA698B /* id */, + FCBA153E14A1456100AA698B /* jot */, + FCBA154614A1456400AA698B /* kill */, + FCBA154E14A1456700AA698B /* killall */, + FCBA155614A1456A00AA698B /* lastcomm */, + FCBA155E14A1456E00AA698B /* locate */, + FCE30EC214B5368A00CC0294 /* locate.bigram */, + FCE30ED614B536C900CC0294 /* locate.code */, + FCBA156614A1457100AA698B /* logname */, + FCBA157614A1457B00AA698B /* mktemp */, + FCBA157E14A1457F00AA698B /* nice */, + FCBA158614A1458500AA698B /* nohup */, + FCBA158E14A1458800AA698B /* path_helper */, + FCBA159614A1458C00AA698B /* printenv */, + FCBA159E14A1459000AA698B /* printf */, + FCBA15A614A1459500AA698B /* pwd */, + FCBA15AE14A1459700AA698B /* renice */, + FCBA15B614A1459900AA698B /* script */, + FCBA15BE14A1459E00AA698B /* seq */, + FD6060B11B7C0388004BCA6A /* sh */, + FCBA15C614A145A200AA698B /* shlock */, + FCBA15CE14A145A500AA698B /* sleep */, + FCBA15D614A145A700AA698B /* su */, + C6868575154725700025D623 /* systime */, + FCBA15DE14A145A900AA698B /* tee */, + FCBA15E614A145AE00AA698B /* test */, + FCBA15EE14A145B000AA698B /* time */, + FCBA15F614A145B200AA698B /* true */, + FCBA15FE14A145B500AA698B /* uname */, + FCBA160614A145B800AA698B /* users */, + FCBA160E14A145BA00AA698B /* w */, + FC5D637F14B9819E00123E48 /* what */, + FCBA161614A145BC00AA698B /* whereis */, + FCBA161E14A145C000AA698B /* which */, + FCBA162614A145C500AA698B /* who */, + FCBA162E14A145C700AA698B /* xargs */, + FCBA163614A145CA00AA698B /* yes */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXShellScriptBuildPhase section */ + FCE30EB814B531EC00CC0294 /* ShellScript */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 8; + files = ( + ); + inputPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 1; + shellPath = /bin/sh; + shellScript = ". \"$PROJECT_DIR\"/xcodescripts/install-files.sh\n"; + showEnvVarsInLog = 0; + }; + FCE30F4D14B619C900CC0294 /* Run Script */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 8; + files = ( + ); + inputPaths = ( + ); + name = "Run Script"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 1; + shellPath = /bin/sh; + shellScript = ". \"$PROJECT_DIR\"/xcodescripts/install-files.sh\n"; + showEnvVarsInLog = 0; + }; + FD6060EF1B7C0590004BCA6A /* mkbuiltins */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "$(SRCROOT)/sh/mkbuiltins", + "$(SRCROOT)/sh/builtins.def", + ); + name = mkbuiltins; + outputPaths = ( + "$(BUILT_PRODUCTS_DIR)/builtins.c", + "$(BUILT_PRODUCTS_DIR)/builtins.h", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "cd ${BUILT_PRODUCTS_DIR} && sh ${SRCROOT}/sh/mkbuiltins ${SRCROOT}/sh"; + }; + FD6060F11B7C0742004BCA6A /* mknodes */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "$(SRCROOT)/sh/mknodes.c", + "$(SRCROOT)/sh/nodetypes", + "$(SRCROOT)/sh/nodes.c.get", + ); + name = mknodes; + outputPaths = ( + "$(BUILT_PRODUCTS_DIR)/nodes.c", + "$(BUILT_PRODUCTS_DIR)/nodes.h", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "mkdir -p ${DERIVED_FILE_DIR}\nenv -i xcrun -sdk macosx cc ${SRCROOT}/sh/mknodes.c -o ${DERIVED_FILE_DIR}/mknodes\ncd ${BUILT_PRODUCTS_DIR} && ${DERIVED_FILE_DIR}/mknodes ${SRCROOT}/sh/nodetypes ${SRCROOT}/sh/nodes.c.pat\n"; + }; + FD6060F21B7C0744004BCA6A /* mksyntax */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "$(SRCROOT)/sh/mksyntax.c", + ); + name = mksyntax; + outputPaths = ( + "$(BUILT_PRODUCTS_DIR)/syntax.c", + "$(BUILT_PRODUCTS_DIR)/syntax.h", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "env -i xcrun -sdk macosx cc ${SRCROOT}/sh/mksyntax.c -o ${DERIVED_FILE_DIR}/mksyntax\ncd ${BUILT_PRODUCTS_DIR} && ${DERIVED_FILE_DIR}/mksyntax"; + }; + FD6060F31B7C0964004BCA6A /* mktokens */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "$(SRCROOT)/sh/mktokens", + ); + name = mktokens; + outputPaths = ( + "$(BUILT_PRODUCTS_DIR)/token.h", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "cd ${BUILT_PRODUCTS_DIR} && sh ${SRCROOT}/sh/mktokens"; + }; + FD6061321B7D32AD004BCA6A /* sh.1 */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "$(SRCROOT)/sh/sh.1", + ); + name = sh.1; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "install -d -m 0755 ${DSTROOT}${SH_MAN_PREFIX}/share/man/man1\ninstall -m 0644 ${SRCROOT}/sh/sh.1 ${DSTROOT}${SH_MAN_PREFIX}/share/man/man1/${PRODUCT_NAME}.1"; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + C6868572154725700025D623 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + C686857A154725700025D623 /* systime.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FC5D638014B9819E00123E48 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + FC5D638D14B981D200123E48 /* what.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FC5D639014B9822D00123E48 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + FC5D639B14B9826500123E48 /* conv.c in Sources */, + FC5D639C14B9826500123E48 /* display.c in Sources */, + FC5D639D14B9826500123E48 /* hexdump.c in Sources */, + FC5D639E14B9826500123E48 /* hexsyntax.c in Sources */, + FC5D639F14B9826500123E48 /* odsyntax.c in Sources */, + FC5D63A014B9826500123E48 /* parse.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FCBA149914A141FF00AA698B /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + FCBA14EB14A1444900AA698B /* apply.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FCBA14AF14A1422D00AA698B /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + FCBA14EE14A1446700AA698B /* basename.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FCBA14B814A1423E00AA698B /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + FCBA14F114A1448C00AA698B /* chroot.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FCBA14C014A1426800AA698B /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + FCBA14F614A144B200AA698B /* vary.c in Sources */, + FCBA14F514A144AF00AA698B /* netdate.c in Sources */, + FCBA14F414A1449C00AA698B /* date.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FCBA14C814A1428000AA698B /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + FCBA14F714A144C700AA698B /* dirname.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FCBA14D014A1433F00AA698B /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + FCBA14FA14A144D300AA698B /* echo.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FCBA14D814A1434A00AA698B /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + FCBA14FC14A144E500AA698B /* env.c in Sources */, + FD88EB03198C5257006B7EFD /* envopts.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FCBA150F14A1453A00AA698B /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + FCBA02FF14B507130030BEB3 /* expr.y in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FCBA151714A1454D00AA698B /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + FCBA030114B507220030BEB3 /* false.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FCBA151F14A1455600AA698B /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + FCBA030314B5073F0030BEB3 /* find.c in Sources */, + FCBA030414B5073F0030BEB3 /* function.c in Sources */, + FCBA030514B5073F0030BEB3 /* getdate.y in Sources */, + FCBA030614B5073F0030BEB3 /* ls.c in Sources */, + FCBA030714B5073F0030BEB3 /* main.c in Sources */, + FCBA030814B5073F0030BEB3 /* misc.c in Sources */, + FCBA030914B5073F0030BEB3 /* operator.c in Sources */, + FCBA030A14B5073F0030BEB3 /* option.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FCBA152714A1455900AA698B /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + FCBA030C14B5074D0030BEB3 /* getopt.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FCBA152F14A1455C00AA698B /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + FCBA030E14B5076A0030BEB3 /* hostname.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FCBA153714A1455F00AA698B /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + FCBA031014B507790030BEB3 /* id.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FCBA153F14A1456100AA698B /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + FCBA031514B507900030BEB3 /* jot.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FCBA154714A1456400AA698B /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + FCBA031714B507A10030BEB3 /* kill.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FCBA154F14A1456700AA698B /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + FCBA031914B507B00030BEB3 /* killall.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FCBA155714A1456A00AA698B /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + FCBA031B14B507BD0030BEB3 /* lastcomm.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FCBA155F14A1456E00AA698B /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + FCBA031E14B507D10030BEB3 /* locate.c in Sources */, + FCBA031F14B507D10030BEB3 /* util.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FCBA156714A1457100AA698B /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + FCBA02FD14B506FA0030BEB3 /* logname.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FCBA157714A1457B00AA698B /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + FCBA02F914B506CB0030BEB3 /* mktemp.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FCBA157F14A1457F00AA698B /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + FCBA02F814B506BB0030BEB3 /* nice.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FCBA158714A1458500AA698B /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + FCBA02F514B506940030BEB3 /* nohup.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FCBA158F14A1458800AA698B /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + FCBA02F314B506840030BEB3 /* path_helper.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FCBA159714A1458C00AA698B /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + FCBA02F114B506720030BEB3 /* printenv.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FCBA159F14A1459000AA698B /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + FCBA02F014B506660030BEB3 /* printf.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FCBA15A714A1459500AA698B /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + FCBA02ED14B506530030BEB3 /* pwd.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FCBA15AF14A1459700AA698B /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + FCBA02EC14B5064C0030BEB3 /* renice.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FCBA15B714A1459900AA698B /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + FCBA02EA14B506340030BEB3 /* script.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FCBA15BF14A1459E00AA698B /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + FCBA02E814B506230030BEB3 /* seq.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FCBA15C714A145A200AA698B /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + FC2B5C0B14B3CD4F00ECF511 /* shlock.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FCBA15CF14A145A500AA698B /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + FC2B5C0A14B3CD4400ECF511 /* sleep.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FCBA15D714A145A700AA698B /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + FC2B5C0714B3CD2F00ECF511 /* su.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FCBA15DF14A145A900AA698B /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + FC2B5C0614B3CD2800ECF511 /* tee.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FCBA15E714A145AE00AA698B /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + FC2B5C0214B3CD0C00ECF511 /* test.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FCBA15EF14A145B000AA698B /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + FC2B5C0014B3CCF800ECF511 /* time.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FCBA15F714A145B200AA698B /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + FC2B5BFE14B3CCD700ECF511 /* true.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FCBA15FF14A145B500AA698B /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + FC2B5BFB14B3CCC600ECF511 /* uname.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FCBA160714A145B800AA698B /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + FCBA165014A146BE00AA698B /* users.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FCBA160F14A145BA00AA698B /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + FCBA164D14A146A200AA698B /* w.c in Sources */, + FCBA164C14A1469F00AA698B /* proc_compare.c in Sources */, + FCBA164B14A1469D00AA698B /* pr_time.c in Sources */, + FCBA164A14A1469A00AA698B /* fmt.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FCBA161714A145BC00AA698B /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + FCBA164814A1469000AA698B /* whereis.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FCBA161F14A145C000AA698B /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + FCBA164614A1468600AA698B /* which.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FCBA162714A145C500AA698B /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + FCBA164414A1467D00AA698B /* who.c in Sources */, + FCBA164314A1467A00AA698B /* utmpentry.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FCBA162F14A145C700AA698B /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + FCBA164114A1467200AA698B /* strnsubst.c in Sources */, + FCBA164014A1466F00AA698B /* xargs.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FCBA163714A145CA00AA698B /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + FCBA163F14A1466900AA698B /* yes.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FCE30EC314B5368A00CC0294 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + FCE30ED414B536A600CC0294 /* locate.bigram.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FCE30ED714B536C900CC0294 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + FCE30EE014B536F200CC0294 /* locate.code.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + FD6060AE1B7C0388004BCA6A /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + FD6060D21B7C0471004BCA6A /* alias.c in Sources */, + FD6060D31B7C0471004BCA6A /* arith_yacc.c in Sources */, + FD6060D41B7C0471004BCA6A /* arith_yylex.c in Sources */, + FD6060D51B7C0471004BCA6A /* cd.c in Sources */, + FD6060EB1B7C04E4004BCA6A /* echo.c in Sources */, + FD6060D61B7C0471004BCA6A /* error.c in Sources */, + FD6060D71B7C0471004BCA6A /* eval.c in Sources */, + FD6060D81B7C0471004BCA6A /* exec.c in Sources */, + FD6060D91B7C0471004BCA6A /* expand.c in Sources */, + FD6060DA1B7C0471004BCA6A /* histedit.c in Sources */, + FD6060DB1B7C0471004BCA6A /* input.c in Sources */, + FD6060DC1B7C0471004BCA6A /* jobs.c in Sources */, + FD6060EC1B7C0506004BCA6A /* kill.c in Sources */, + FD6060DD1B7C0471004BCA6A /* mail.c in Sources */, + FD6060B51B7C0388004BCA6A /* main.c in Sources */, + FD6060DE1B7C0471004BCA6A /* memalloc.c in Sources */, + FD6060DF1B7C0471004BCA6A /* miscbltin.c in Sources */, + FD6060E01B7C0471004BCA6A /* mystring.c in Sources */, + FD6060E11B7C0471004BCA6A /* options.c in Sources */, + FD6060E21B7C0471004BCA6A /* output.c in Sources */, + FD6060E31B7C0471004BCA6A /* parser.c in Sources */, + FD6060ED1B7C0518004BCA6A /* printf.c in Sources */, + FD6060E41B7C0471004BCA6A /* redir.c in Sources */, + FD6060E51B7C0471004BCA6A /* show.c in Sources */, + FD6060EE1B7C0521004BCA6A /* test.c in Sources */, + FD6060E61B7C0471004BCA6A /* trap.c in Sources */, + FD6060E71B7C0471004BCA6A /* var.c in Sources */, + FD6060FD1B7C0CAF004BCA6A /* builtins.c in Sources */, + FD6060FE1B7C0CAF004BCA6A /* nodes.c in Sources */, + FD6060FF1B7C0CAF004BCA6A /* syntax.c in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + C6868581154725990025D623 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = C6868575154725700025D623 /* systime */; + targetProxy = C6868580154725990025D623 /* PBXContainerItemProxy */; + }; + C6868583154725A30025D623 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = C6868575154725700025D623 /* systime */; + targetProxy = C6868582154725A30025D623 /* PBXContainerItemProxy */; + }; + FC5D63A214B9864400123E48 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FC5D638F14B9822D00123E48 /* hexdump */; + targetProxy = FC5D63A114B9864400123E48 /* PBXContainerItemProxy */; + }; + FC5D63A414B9864400123E48 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FC5D637F14B9819E00123E48 /* what */; + targetProxy = FC5D63A314B9864400123E48 /* PBXContainerItemProxy */; + }; + FC5D63A614B9866500123E48 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FC5D638F14B9822D00123E48 /* hexdump */; + targetProxy = FC5D63A514B9866500123E48 /* PBXContainerItemProxy */; + }; + FC5D63A814B9866500123E48 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FC5D637F14B9819E00123E48 /* what */; + targetProxy = FC5D63A714B9866500123E48 /* PBXContainerItemProxy */; + }; + FCBA150114A144F700AA698B /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FCBA149814A141FF00AA698B /* apply */; + targetProxy = FCBA150014A144F700AA698B /* PBXContainerItemProxy */; + }; + FCBA150314A144F700AA698B /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FCBA14AE14A1422D00AA698B /* basename */; + targetProxy = FCBA150214A144F700AA698B /* PBXContainerItemProxy */; + }; + FCBA150514A144F700AA698B /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FCBA14B714A1423E00AA698B /* chroot */; + targetProxy = FCBA150414A144F700AA698B /* PBXContainerItemProxy */; + }; + FCBA150714A144F700AA698B /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FCBA14BF14A1426800AA698B /* date */; + targetProxy = FCBA150614A144F700AA698B /* PBXContainerItemProxy */; + }; + FCBA150914A144F700AA698B /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FCBA14C714A1428000AA698B /* dirname */; + targetProxy = FCBA150814A144F700AA698B /* PBXContainerItemProxy */; + }; + FCBA150B14A144F700AA698B /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FCBA14CF14A1433F00AA698B /* echo */; + targetProxy = FCBA150A14A144F700AA698B /* PBXContainerItemProxy */; + }; + FCBA150D14A144F700AA698B /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FCBA14D714A1434A00AA698B /* env */; + targetProxy = FCBA150C14A144F700AA698B /* PBXContainerItemProxy */; + }; + FCBA165514A146D000AA698B /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FCBA150E14A1453A00AA698B /* expr */; + targetProxy = FCBA165414A146D000AA698B /* PBXContainerItemProxy */; + }; + FCBA165714A146D000AA698B /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FCBA151614A1454D00AA698B /* false */; + targetProxy = FCBA165614A146D000AA698B /* PBXContainerItemProxy */; + }; + FCBA165914A146D000AA698B /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FCBA151E14A1455600AA698B /* find */; + targetProxy = FCBA165814A146D000AA698B /* PBXContainerItemProxy */; + }; + FCBA165B14A146D000AA698B /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FCBA152614A1455900AA698B /* getopt */; + targetProxy = FCBA165A14A146D000AA698B /* PBXContainerItemProxy */; + }; + FCBA165D14A146D000AA698B /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FCBA152E14A1455C00AA698B /* hostname */; + targetProxy = FCBA165C14A146D000AA698B /* PBXContainerItemProxy */; + }; + FCBA165F14A146D000AA698B /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FCBA153614A1455F00AA698B /* id */; + targetProxy = FCBA165E14A146D000AA698B /* PBXContainerItemProxy */; + }; + FCBA166114A146D000AA698B /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FCBA153E14A1456100AA698B /* jot */; + targetProxy = FCBA166014A146D000AA698B /* PBXContainerItemProxy */; + }; + FCBA166314A146D000AA698B /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FCBA154614A1456400AA698B /* kill */; + targetProxy = FCBA166214A146D000AA698B /* PBXContainerItemProxy */; + }; + FCBA166514A146D000AA698B /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FCBA154E14A1456700AA698B /* killall */; + targetProxy = FCBA166414A146D000AA698B /* PBXContainerItemProxy */; + }; + FCBA166714A146D000AA698B /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FCBA155614A1456A00AA698B /* lastcomm */; + targetProxy = FCBA166614A146D000AA698B /* PBXContainerItemProxy */; + }; + FCBA166914A146D000AA698B /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FCBA155E14A1456E00AA698B /* locate */; + targetProxy = FCBA166814A146D000AA698B /* PBXContainerItemProxy */; + }; + FCBA166B14A146D000AA698B /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FCBA156614A1457100AA698B /* logname */; + targetProxy = FCBA166A14A146D000AA698B /* PBXContainerItemProxy */; + }; + FCBA166F14A146D000AA698B /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FCBA157614A1457B00AA698B /* mktemp */; + targetProxy = FCBA166E14A146D000AA698B /* PBXContainerItemProxy */; + }; + FCBA167114A146D000AA698B /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FCBA157E14A1457F00AA698B /* nice */; + targetProxy = FCBA167014A146D000AA698B /* PBXContainerItemProxy */; + }; + FCBA167314A146D000AA698B /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FCBA158614A1458500AA698B /* nohup */; + targetProxy = FCBA167214A146D000AA698B /* PBXContainerItemProxy */; + }; + FCBA167514A146D000AA698B /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FCBA158E14A1458800AA698B /* path_helper */; + targetProxy = FCBA167414A146D000AA698B /* PBXContainerItemProxy */; + }; + FCBA167714A146D000AA698B /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FCBA159614A1458C00AA698B /* printenv */; + targetProxy = FCBA167614A146D000AA698B /* PBXContainerItemProxy */; + }; + FCBA167914A146D000AA698B /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FCBA159E14A1459000AA698B /* printf */; + targetProxy = FCBA167814A146D000AA698B /* PBXContainerItemProxy */; + }; + FCBA167B14A146D000AA698B /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FCBA15A614A1459500AA698B /* pwd */; + targetProxy = FCBA167A14A146D000AA698B /* PBXContainerItemProxy */; + }; + FCBA167D14A146D000AA698B /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FCBA15AE14A1459700AA698B /* renice */; + targetProxy = FCBA167C14A146D000AA698B /* PBXContainerItemProxy */; + }; + FCBA167F14A146D000AA698B /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FCBA15B614A1459900AA698B /* script */; + targetProxy = FCBA167E14A146D000AA698B /* PBXContainerItemProxy */; + }; + FCBA168114A146D000AA698B /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FCBA15BE14A1459E00AA698B /* seq */; + targetProxy = FCBA168014A146D000AA698B /* PBXContainerItemProxy */; + }; + FCBA168314A146D000AA698B /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FCBA15C614A145A200AA698B /* shlock */; + targetProxy = FCBA168214A146D000AA698B /* PBXContainerItemProxy */; + }; + FCBA168514A146D000AA698B /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FCBA15CE14A145A500AA698B /* sleep */; + targetProxy = FCBA168414A146D000AA698B /* PBXContainerItemProxy */; + }; + FCBA168714A146D000AA698B /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FCBA15D614A145A700AA698B /* su */; + targetProxy = FCBA168614A146D000AA698B /* PBXContainerItemProxy */; + }; + FCBA168914A146D000AA698B /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FCBA15DE14A145A900AA698B /* tee */; + targetProxy = FCBA168814A146D000AA698B /* PBXContainerItemProxy */; + }; + FCBA168B14A146D000AA698B /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FCBA15E614A145AE00AA698B /* test */; + targetProxy = FCBA168A14A146D000AA698B /* PBXContainerItemProxy */; + }; + FCBA168D14A146D000AA698B /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FCBA15EE14A145B000AA698B /* time */; + targetProxy = FCBA168C14A146D000AA698B /* PBXContainerItemProxy */; + }; + FCBA168F14A146D000AA698B /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FCBA15F614A145B200AA698B /* true */; + targetProxy = FCBA168E14A146D000AA698B /* PBXContainerItemProxy */; + }; + FCBA169114A146D000AA698B /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FCBA15FE14A145B500AA698B /* uname */; + targetProxy = FCBA169014A146D000AA698B /* PBXContainerItemProxy */; + }; + FCBA169314A146D000AA698B /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FCBA160614A145B800AA698B /* users */; + targetProxy = FCBA169214A146D000AA698B /* PBXContainerItemProxy */; + }; + FCBA169514A146D000AA698B /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FCBA160E14A145BA00AA698B /* w */; + targetProxy = FCBA169414A146D000AA698B /* PBXContainerItemProxy */; + }; + FCBA169714A146D000AA698B /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FCBA161614A145BC00AA698B /* whereis */; + targetProxy = FCBA169614A146D000AA698B /* PBXContainerItemProxy */; + }; + FCBA169914A146D000AA698B /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FCBA161E14A145C000AA698B /* which */; + targetProxy = FCBA169814A146D000AA698B /* PBXContainerItemProxy */; + }; + FCBA169B14A146D000AA698B /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FCBA162614A145C500AA698B /* who */; + targetProxy = FCBA169A14A146D000AA698B /* PBXContainerItemProxy */; + }; + FCBA169D14A146D000AA698B /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FCBA162E14A145C700AA698B /* xargs */; + targetProxy = FCBA169C14A146D000AA698B /* PBXContainerItemProxy */; + }; + FCBA169F14A146D000AA698B /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FCBA163614A145CA00AA698B /* yes */; + targetProxy = FCBA169E14A146D000AA698B /* PBXContainerItemProxy */; + }; + FCE30EE314B539BF00CC0294 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FCE30EC214B5368A00CC0294 /* locate.bigram */; + targetProxy = FCE30EE214B539BF00CC0294 /* PBXContainerItemProxy */; + }; + FCE30EE514B539BF00CC0294 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FCE30ED614B536C900CC0294 /* locate.code */; + targetProxy = FCE30EE414B539BF00CC0294 /* PBXContainerItemProxy */; + }; + FCE30F4F14B619E600CC0294 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FCBA149814A141FF00AA698B /* apply */; + targetProxy = FCE30F4E14B619E600CC0294 /* PBXContainerItemProxy */; + }; + FCE30F5114B619E600CC0294 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FCBA14AE14A1422D00AA698B /* basename */; + targetProxy = FCE30F5014B619E600CC0294 /* PBXContainerItemProxy */; + }; + FCE30F5314B619E600CC0294 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FCBA14B714A1423E00AA698B /* chroot */; + targetProxy = FCE30F5214B619E600CC0294 /* PBXContainerItemProxy */; + }; + FCE30F5514B619E600CC0294 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FCBA14BF14A1426800AA698B /* date */; + targetProxy = FCE30F5414B619E600CC0294 /* PBXContainerItemProxy */; + }; + FCE30F5714B619E600CC0294 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FCBA14C714A1428000AA698B /* dirname */; + targetProxy = FCE30F5614B619E600CC0294 /* PBXContainerItemProxy */; + }; + FCE30F5914B619E600CC0294 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FCBA14CF14A1433F00AA698B /* echo */; + targetProxy = FCE30F5814B619E600CC0294 /* PBXContainerItemProxy */; + }; + FCE30F5B14B619E600CC0294 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FCBA14D714A1434A00AA698B /* env */; + targetProxy = FCE30F5A14B619E600CC0294 /* PBXContainerItemProxy */; + }; + FCE30F5D14B619E600CC0294 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FCBA150E14A1453A00AA698B /* expr */; + targetProxy = FCE30F5C14B619E600CC0294 /* PBXContainerItemProxy */; + }; + FCE30F5F14B619E600CC0294 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FCBA151614A1454D00AA698B /* false */; + targetProxy = FCE30F5E14B619E600CC0294 /* PBXContainerItemProxy */; + }; + FCE30F6114B619E600CC0294 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FCBA151E14A1455600AA698B /* find */; + targetProxy = FCE30F6014B619E600CC0294 /* PBXContainerItemProxy */; + }; + FCE30F6314B619E600CC0294 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FCBA152614A1455900AA698B /* getopt */; + targetProxy = FCE30F6214B619E600CC0294 /* PBXContainerItemProxy */; + }; + FCE30F6514B619E600CC0294 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FCBA152E14A1455C00AA698B /* hostname */; + targetProxy = FCE30F6414B619E600CC0294 /* PBXContainerItemProxy */; + }; + FCE30F6714B619E600CC0294 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FCBA153614A1455F00AA698B /* id */; + targetProxy = FCE30F6614B619E600CC0294 /* PBXContainerItemProxy */; + }; + FCE30F6914B619E600CC0294 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FCBA153E14A1456100AA698B /* jot */; + targetProxy = FCE30F6814B619E600CC0294 /* PBXContainerItemProxy */; + }; + FCE30F6B14B619E600CC0294 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FCBA154614A1456400AA698B /* kill */; + targetProxy = FCE30F6A14B619E600CC0294 /* PBXContainerItemProxy */; + }; + FCE30F6D14B619E600CC0294 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FCBA154E14A1456700AA698B /* killall */; + targetProxy = FCE30F6C14B619E600CC0294 /* PBXContainerItemProxy */; + }; + FCE30F6F14B619E600CC0294 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FCBA155614A1456A00AA698B /* lastcomm */; + targetProxy = FCE30F6E14B619E600CC0294 /* PBXContainerItemProxy */; + }; + FCE30F7114B619E600CC0294 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FCBA156614A1457100AA698B /* logname */; + targetProxy = FCE30F7014B619E600CC0294 /* PBXContainerItemProxy */; + }; + FCE30F7314B619E600CC0294 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FCBA157614A1457B00AA698B /* mktemp */; + targetProxy = FCE30F7214B619E600CC0294 /* PBXContainerItemProxy */; + }; + FCE30F7514B619E600CC0294 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FCBA157E14A1457F00AA698B /* nice */; + targetProxy = FCE30F7414B619E600CC0294 /* PBXContainerItemProxy */; + }; + FCE30F7714B619E600CC0294 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FCBA158614A1458500AA698B /* nohup */; + targetProxy = FCE30F7614B619E600CC0294 /* PBXContainerItemProxy */; + }; + FCE30F7914B619E600CC0294 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FCBA158E14A1458800AA698B /* path_helper */; + targetProxy = FCE30F7814B619E600CC0294 /* PBXContainerItemProxy */; + }; + FCE30F7B14B619E600CC0294 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FCBA159614A1458C00AA698B /* printenv */; + targetProxy = FCE30F7A14B619E600CC0294 /* PBXContainerItemProxy */; + }; + FCE30F7D14B619E600CC0294 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FCBA159E14A1459000AA698B /* printf */; + targetProxy = FCE30F7C14B619E600CC0294 /* PBXContainerItemProxy */; + }; + FCE30F7F14B619E600CC0294 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FCBA15A614A1459500AA698B /* pwd */; + targetProxy = FCE30F7E14B619E600CC0294 /* PBXContainerItemProxy */; + }; + FCE30F8114B619E600CC0294 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FCBA15AE14A1459700AA698B /* renice */; + targetProxy = FCE30F8014B619E600CC0294 /* PBXContainerItemProxy */; + }; + FCE30F8314B619E600CC0294 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FCBA15B614A1459900AA698B /* script */; + targetProxy = FCE30F8214B619E600CC0294 /* PBXContainerItemProxy */; + }; + FCE30F8514B619E600CC0294 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FCBA15BE14A1459E00AA698B /* seq */; + targetProxy = FCE30F8414B619E600CC0294 /* PBXContainerItemProxy */; + }; + FCE30F8714B619E600CC0294 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FCBA15C614A145A200AA698B /* shlock */; + targetProxy = FCE30F8614B619E600CC0294 /* PBXContainerItemProxy */; + }; + FCE30F8914B619E600CC0294 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FCBA15CE14A145A500AA698B /* sleep */; + targetProxy = FCE30F8814B619E600CC0294 /* PBXContainerItemProxy */; + }; + FCE30F8B14B619E600CC0294 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FCBA15DE14A145A900AA698B /* tee */; + targetProxy = FCE30F8A14B619E600CC0294 /* PBXContainerItemProxy */; + }; + FCE30F8D14B619E600CC0294 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FCBA15E614A145AE00AA698B /* test */; + targetProxy = FCE30F8C14B619E600CC0294 /* PBXContainerItemProxy */; + }; + FCE30F8F14B619E600CC0294 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FCBA15EE14A145B000AA698B /* time */; + targetProxy = FCE30F8E14B619E600CC0294 /* PBXContainerItemProxy */; + }; + FCE30F9114B619E600CC0294 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FCBA15F614A145B200AA698B /* true */; + targetProxy = FCE30F9014B619E600CC0294 /* PBXContainerItemProxy */; + }; + FCE30F9314B619E600CC0294 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FCBA15FE14A145B500AA698B /* uname */; + targetProxy = FCE30F9214B619E600CC0294 /* PBXContainerItemProxy */; + }; + FCE30F9514B619E600CC0294 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FCBA160614A145B800AA698B /* users */; + targetProxy = FCE30F9414B619E600CC0294 /* PBXContainerItemProxy */; + }; + FCE30F9714B619E600CC0294 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FCBA160E14A145BA00AA698B /* w */; + targetProxy = FCE30F9614B619E600CC0294 /* PBXContainerItemProxy */; + }; + FCE30F9914B619E600CC0294 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FCBA161614A145BC00AA698B /* whereis */; + targetProxy = FCE30F9814B619E600CC0294 /* PBXContainerItemProxy */; + }; + FCE30F9B14B619E600CC0294 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FCBA161E14A145C000AA698B /* which */; + targetProxy = FCE30F9A14B619E600CC0294 /* PBXContainerItemProxy */; + }; + FCE30F9D14B619E600CC0294 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FCBA162614A145C500AA698B /* who */; + targetProxy = FCE30F9C14B619E600CC0294 /* PBXContainerItemProxy */; + }; + FCE30F9F14B619E600CC0294 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FCBA162E14A145C700AA698B /* xargs */; + targetProxy = FCE30F9E14B619E600CC0294 /* PBXContainerItemProxy */; + }; + FCE30FA114B619E600CC0294 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FCBA163614A145CA00AA698B /* yes */; + targetProxy = FCE30FA014B619E600CC0294 /* PBXContainerItemProxy */; + }; + FD6060B91B7C03B3004BCA6A /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FD6060B11B7C0388004BCA6A /* sh */; + targetProxy = FD6060B81B7C03B3004BCA6A /* PBXContainerItemProxy */; + }; + FD6060BB1B7C03BC004BCA6A /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = FD6060B11B7C0388004BCA6A /* sh */; + targetProxy = FD6060BA1B7C03BC004BCA6A /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin XCBuildConfiguration section */ + C686857D154725700025D623 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_OBJC_WEAK = YES; + INSTALL_PATH = /usr/local/bin; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; + FC5D638A14B9819E00123E48 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_OBJC_WEAK = YES; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; + FC5D639614B9822D00123E48 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_OBJC_WEAK = YES; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; + FC80BF6914A05A2F00C6F7F5 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + APPLY_RULES_IN_COPY_FILES = YES; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "-"; + COPY_PHASE_STRIP = YES; + CURRENT_PROJECT_VERSION = "$(RC_ProjectSourceVersion)"; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu99; + GCC_ENABLE_OBJC_EXCEPTIONS = YES; + GCC_NO_COMMON_BLOCKS = YES; + GCC_PREPROCESSOR_DEFINITIONS = "__FBSDID=__RCSID"; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + INSTALL_PATH = /usr/bin; + SDKROOT = macosx.internal; + VERSIONING_SYSTEM = "apple-generic"; + }; + name = Release; + }; + FCBA149D14A141FF00AA698B /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_OBJC_WEAK = YES; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; + FCBA14B314A1422D00AA698B /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_OBJC_WEAK = YES; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; + FCBA14BC14A1423E00AA698B /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_OBJC_WEAK = YES; + INSTALL_PATH = /usr/sbin; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; + FCBA14C414A1426800AA698B /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_OBJC_WEAK = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = NO; + INSTALL_PATH = /bin; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; + FCBA14CC14A1428000AA698B /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_OBJC_WEAK = YES; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; + FCBA14D414A1433F00AA698B /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_OBJC_WEAK = YES; + INSTALL_PATH = /bin; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; + FCBA14DC14A1434A00AA698B /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_OBJC_WEAK = YES; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; + FCBA14FF14A144EC00AA698B /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_OBJC_WEAK = YES; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; + FCBA151314A1453A00AA698B /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_OBJC_WEAK = YES; + INSTALL_PATH = /bin; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; + FCBA151B14A1454D00AA698B /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_OBJC_WEAK = YES; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; + FCBA152314A1455600AA698B /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_OBJC_WEAK = YES; + GCC_PREPROCESSOR_DEFINITIONS = ( + "__FBSDID=__RCSID", + _DARWIN_USE_64_BIT_INODE, + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = NO; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; + FCBA152B14A1455900AA698B /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_OBJC_WEAK = YES; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; + FCBA153314A1455C00AA698B /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_OBJC_WEAK = YES; + INSTALL_PATH = /bin; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; + FCBA153B14A1455F00AA698B /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_OBJC_WEAK = YES; + GCC_PREPROCESSOR_DEFINITIONS = ( + "__FBSDID=__RCSID", + USE_BSM_AUDIT, + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = NO; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; + FCBA154314A1456100AA698B /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_OBJC_WEAK = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = NO; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; + FCBA154B14A1456400AA698B /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_OBJC_WEAK = YES; + INSTALL_PATH = /bin; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; + FCBA155314A1456700AA698B /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_OBJC_WEAK = YES; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; + FCBA155B14A1456A00AA698B /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_OBJC_WEAK = YES; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; + FCBA156314A1456E00AA698B /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_OBJC_WEAK = YES; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; + FCBA156B14A1457100AA698B /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_OBJC_WEAK = YES; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; + FCBA157B14A1457B00AA698B /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_OBJC_WEAK = YES; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; + FCBA158314A1457F00AA698B /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_OBJC_WEAK = YES; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; + FCBA158B14A1458500AA698B /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_OBJC_WEAK = YES; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; + FCBA159314A1458800AA698B /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_OBJC_WEAK = YES; + INSTALL_PATH = /usr/libexec; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; + FCBA159B14A1458C00AA698B /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_OBJC_WEAK = YES; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; + FCBA15A314A1459000AA698B /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_OBJC_WEAK = YES; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; + FCBA15AB14A1459500AA698B /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_OBJC_WEAK = YES; + INSTALL_PATH = /bin; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; + FCBA15B314A1459700AA698B /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_OBJC_WEAK = YES; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; + FCBA15BB14A1459900AA698B /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_OBJC_WEAK = YES; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; + FCBA15C314A1459E00AA698B /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_OBJC_WEAK = YES; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; + FCBA15CB14A145A200AA698B /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_OBJC_WEAK = YES; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; + FCBA15D314A145A500AA698B /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_OBJC_WEAK = YES; + INSTALL_PATH = /bin; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; + FCBA15DB14A145A700AA698B /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_OBJC_WEAK = YES; + CODE_SIGN_ENTITLEMENTS = su/su_entitlements.plist; + INSTALL_MODE_FLAG = "u+s,u+w,go-w,a+rX"; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; + FCBA15E314A145A900AA698B /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_OBJC_WEAK = YES; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; + FCBA15EB14A145AE00AA698B /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_OBJC_WEAK = YES; + INSTALL_PATH = /bin; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; + FCBA15F314A145B000AA698B /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_OBJC_WEAK = YES; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; + FCBA15FB14A145B200AA698B /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_OBJC_WEAK = YES; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; + FCBA160314A145B500AA698B /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_OBJC_WEAK = YES; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; + FCBA160B14A145B800AA698B /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_OBJC_WEAK = YES; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; + FCBA161314A145BA00AA698B /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_OBJC_WEAK = YES; + GCC_PREPROCESSOR_DEFINITIONS = ( + "__FBSDID=__RCSID", + "HAVE_KVM=0", + "HAVE_UTMPX=1", + ); + "OTHER_LDFLAGS[sdk=macosx*][arch=*]" = "-lresolv"; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; + FCBA161B14A145BC00AA698B /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_OBJC_WEAK = YES; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; + FCBA162314A145C000AA698B /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_OBJC_WEAK = YES; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; + FCBA162B14A145C500AA698B /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_OBJC_WEAK = YES; + GCC_PREPROCESSOR_DEFINITIONS = ( + "__FBSDID=__RCSID", + _UTMPX_COMPAT, + SUPPORT_UTMPX, + ); + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; + FCBA163314A145C700AA698B /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_OBJC_WEAK = YES; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; + FCBA163B14A145CA00AA698B /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_OBJC_WEAK = YES; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; + FCE30ED114B5368A00CC0294 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_OBJC_WEAK = YES; + INSTALL_PATH = /usr/libexec; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; + FCE30EDD14B536C900CC0294 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_OBJC_WEAK = YES; + INSTALL_PATH = /usr/libexec; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; + FCE30F4C14B619B000CC0294 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ENABLE_OBJC_WEAK = YES; + PRODUCT_NAME = "$(TARGET_NAME)"; + }; + name = Release; + }; + FD6060B61B7C0388004BCA6A /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = FD6061311B7D2DDE004BCA6A /* sh.xcconfig */; + buildSettings = { + CLANG_ENABLE_OBJC_WEAK = YES; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + C686857E154725700025D623 /* Build configuration list for PBXNativeTarget "systime" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + C686857D154725700025D623 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + FC5D638914B9819E00123E48 /* Build configuration list for PBXNativeTarget "what" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + FC5D638A14B9819E00123E48 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + FC5D639514B9822D00123E48 /* Build configuration list for PBXNativeTarget "hexdump" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + FC5D639614B9822D00123E48 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + FC80BF5A14A05A2F00C6F7F5 /* Build configuration list for PBXProject "shell_cmds" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + FC80BF6914A05A2F00C6F7F5 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + FCBA149C14A141FF00AA698B /* Build configuration list for PBXNativeTarget "apply" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + FCBA149D14A141FF00AA698B /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + FCBA14B214A1422D00AA698B /* Build configuration list for PBXNativeTarget "basename" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + FCBA14B314A1422D00AA698B /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + FCBA14BB14A1423E00AA698B /* Build configuration list for PBXNativeTarget "chroot" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + FCBA14BC14A1423E00AA698B /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + FCBA14C314A1426800AA698B /* Build configuration list for PBXNativeTarget "date" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + FCBA14C414A1426800AA698B /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + FCBA14CB14A1428000AA698B /* Build configuration list for PBXNativeTarget "dirname" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + FCBA14CC14A1428000AA698B /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + FCBA14D314A1433F00AA698B /* Build configuration list for PBXNativeTarget "echo" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + FCBA14D414A1433F00AA698B /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + FCBA14DB14A1434A00AA698B /* Build configuration list for PBXNativeTarget "env" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + FCBA14DC14A1434A00AA698B /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + FCBA14FE14A144EC00AA698B /* Build configuration list for PBXAggregateTarget "All_OSX" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + FCBA14FF14A144EC00AA698B /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + FCBA151214A1453A00AA698B /* Build configuration list for PBXNativeTarget "expr" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + FCBA151314A1453A00AA698B /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + FCBA151A14A1454D00AA698B /* Build configuration list for PBXNativeTarget "false" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + FCBA151B14A1454D00AA698B /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + FCBA152214A1455600AA698B /* Build configuration list for PBXNativeTarget "find" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + FCBA152314A1455600AA698B /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + FCBA152A14A1455900AA698B /* Build configuration list for PBXNativeTarget "getopt" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + FCBA152B14A1455900AA698B /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + FCBA153214A1455C00AA698B /* Build configuration list for PBXNativeTarget "hostname" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + FCBA153314A1455C00AA698B /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + FCBA153A14A1455F00AA698B /* Build configuration list for PBXNativeTarget "id" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + FCBA153B14A1455F00AA698B /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + FCBA154214A1456100AA698B /* Build configuration list for PBXNativeTarget "jot" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + FCBA154314A1456100AA698B /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + FCBA154A14A1456400AA698B /* Build configuration list for PBXNativeTarget "kill" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + FCBA154B14A1456400AA698B /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + FCBA155214A1456700AA698B /* Build configuration list for PBXNativeTarget "killall" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + FCBA155314A1456700AA698B /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + FCBA155A14A1456A00AA698B /* Build configuration list for PBXNativeTarget "lastcomm" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + FCBA155B14A1456A00AA698B /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + FCBA156214A1456E00AA698B /* Build configuration list for PBXNativeTarget "locate" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + FCBA156314A1456E00AA698B /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + FCBA156A14A1457100AA698B /* Build configuration list for PBXNativeTarget "logname" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + FCBA156B14A1457100AA698B /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + FCBA157A14A1457B00AA698B /* Build configuration list for PBXNativeTarget "mktemp" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + FCBA157B14A1457B00AA698B /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + FCBA158214A1457F00AA698B /* Build configuration list for PBXNativeTarget "nice" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + FCBA158314A1457F00AA698B /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + FCBA158A14A1458500AA698B /* Build configuration list for PBXNativeTarget "nohup" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + FCBA158B14A1458500AA698B /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + FCBA159214A1458800AA698B /* Build configuration list for PBXNativeTarget "path_helper" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + FCBA159314A1458800AA698B /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + FCBA159A14A1458C00AA698B /* Build configuration list for PBXNativeTarget "printenv" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + FCBA159B14A1458C00AA698B /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + FCBA15A214A1459000AA698B /* Build configuration list for PBXNativeTarget "printf" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + FCBA15A314A1459000AA698B /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + FCBA15AA14A1459500AA698B /* Build configuration list for PBXNativeTarget "pwd" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + FCBA15AB14A1459500AA698B /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + FCBA15B214A1459700AA698B /* Build configuration list for PBXNativeTarget "renice" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + FCBA15B314A1459700AA698B /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + FCBA15BA14A1459900AA698B /* Build configuration list for PBXNativeTarget "script" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + FCBA15BB14A1459900AA698B /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + FCBA15C214A1459E00AA698B /* Build configuration list for PBXNativeTarget "seq" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + FCBA15C314A1459E00AA698B /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + FCBA15CA14A145A200AA698B /* Build configuration list for PBXNativeTarget "shlock" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + FCBA15CB14A145A200AA698B /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + FCBA15D214A145A500AA698B /* Build configuration list for PBXNativeTarget "sleep" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + FCBA15D314A145A500AA698B /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + FCBA15DA14A145A700AA698B /* Build configuration list for PBXNativeTarget "su" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + FCBA15DB14A145A700AA698B /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + FCBA15E214A145A900AA698B /* Build configuration list for PBXNativeTarget "tee" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + FCBA15E314A145A900AA698B /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + FCBA15EA14A145AE00AA698B /* Build configuration list for PBXNativeTarget "test" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + FCBA15EB14A145AE00AA698B /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + FCBA15F214A145B000AA698B /* Build configuration list for PBXNativeTarget "time" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + FCBA15F314A145B000AA698B /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + FCBA15FA14A145B200AA698B /* Build configuration list for PBXNativeTarget "true" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + FCBA15FB14A145B200AA698B /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + FCBA160214A145B500AA698B /* Build configuration list for PBXNativeTarget "uname" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + FCBA160314A145B500AA698B /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + FCBA160A14A145B800AA698B /* Build configuration list for PBXNativeTarget "users" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + FCBA160B14A145B800AA698B /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + FCBA161214A145BA00AA698B /* Build configuration list for PBXNativeTarget "w" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + FCBA161314A145BA00AA698B /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + FCBA161A14A145BC00AA698B /* Build configuration list for PBXNativeTarget "whereis" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + FCBA161B14A145BC00AA698B /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + FCBA162214A145C000AA698B /* Build configuration list for PBXNativeTarget "which" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + FCBA162314A145C000AA698B /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + FCBA162A14A145C500AA698B /* Build configuration list for PBXNativeTarget "who" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + FCBA162B14A145C500AA698B /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + FCBA163214A145C700AA698B /* Build configuration list for PBXNativeTarget "xargs" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + FCBA163314A145C700AA698B /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + FCBA163A14A145CA00AA698B /* Build configuration list for PBXNativeTarget "yes" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + FCBA163B14A145CA00AA698B /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + FCE30ED014B5368A00CC0294 /* Build configuration list for PBXNativeTarget "locate.bigram" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + FCE30ED114B5368A00CC0294 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + FCE30EDC14B536C900CC0294 /* Build configuration list for PBXNativeTarget "locate.code" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + FCE30EDD14B536C900CC0294 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + FCE30F4B14B619B000CC0294 /* Build configuration list for PBXAggregateTarget "All_iOS" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + FCE30F4C14B619B000CC0294 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + FD6060B71B7C0388004BCA6A /* Build configuration list for PBXNativeTarget "sh" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + FD6060B61B7C0388004BCA6A /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = FC80BF5714A05A2F00C6F7F5 /* Project object */; +} diff --git a/shell_cmds/shell_cmds.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/shell_cmds/shell_cmds.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..919434a --- /dev/null +++ b/shell_cmds/shell_cmds.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ +<?xml version="1.0" encoding="UTF-8"?> +<Workspace + version = "1.0"> + <FileRef + location = "self:"> + </FileRef> +</Workspace> diff --git a/shell_cmds/shlock/shlock.1 b/shell_cmds/shlock/shlock.1 new file mode 100644 index 0000000..9b1667d --- /dev/null +++ b/shell_cmds/shlock/shlock.1 @@ -0,0 +1,147 @@ +.\" $NetBSD: shlock.1,v 1.11 2008/04/30 13:11:01 martin Exp $ +.\" +.\" Copyright (c) 2006 The NetBSD Foundation, Inc. +.\" All rights reserved. +.\" +.\" This code is derived from software contributed to The NetBSD Foundation +.\" by Erik E. Fair. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS +.\" ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED +.\" TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +.\" PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS +.\" BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +.\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +.\" POSSIBILITY OF SUCH DAMAGE. +.\" +.Dd June 29, 1997 +.Dt SHLOCK 1 +.Os +.Sh NAME +.Nm shlock +.Nd create or verify a lock file for shell scripts +.Sh SYNOPSIS +.Nm +.Op Fl du +.Op Fl p Ar PID +.Fl f +.Ar lockfile +.Sh DESCRIPTION +The +.Nm +command can create or verify a lock file on behalf of a shell or +other script program. +When it attempts to create a lock file, if one already exists, +.Nm +verifies that it is or is not valid. +If valid, +.Nm +will exit with a non-zero exit code. +If invalid, +.Nm +will remove the lock file, and +create a new one. +.Pp +.Nm +uses the +.Xr link 2 +system call to make the final target lock file, which is an atomic +operation (i.e. "dot locking", so named for this mechanism's original +use for locking system mailboxes). +It puts the process ID ("PID") from the command line into the +requested lock file. +.Pp +.Nm +verifies that an extant lock file is still valid by +using +.Xr kill 2 +with a zero signal to check for the existence of the process that +holds the lock. +.Pp +The +.Fl d +option causes +.Nm +to be verbose about what it is doing. +.Pp +The +.Fl f +argument with +.Ar lockfile +is always required. +.Pp +The +.Fl p +option with +.Ar PID +is given when the program is to create a lock file; when absent, +.Nm +will simply check for the validity of the lock file. +.Pp +The +.Fl u +option causes +.Nm +to read and write the PID as a binary pid_t, instead of as ASCII, +to be compatible with the locks created by UUCP. +.Sh EXIT STATUS +A zero exit code indicates a valid lock file. +.Sh EXAMPLES +.Ss BOURNE SHELL +.Bd -literal +#!/bin/sh +lckfile=/tmp/foo.lock +if shlock -f ${lckfile} -p $$ +then +# do what required the lock + rm ${lckfile} +else + echo Lock ${lckfile} already held by `cat ${lckfile}` +fi +.Ed +.Ss C SHELL +.Bd -literal +#!/bin/csh -f +set lckfile=/tmp/foo.lock +shlock -f ${lckfile} -p $$ +if ($status == 0) then +# do what required the lock + rm ${lckfile} +else + echo Lock ${lckfile} already held by `cat ${lckfile}` +endif +.Ed +.Pp +The examples assume that the file system where the lock file is to +be created is writable by the user, and has space available. +.Sh HISTORY +.Nm +was written for the first Network News Transfer Protocol (NNTP) +software distribution, released in March 1986. +The algorithm was suggested by Peter Honeyman, +from work he did on HoneyDanBer UUCP. +.Sh AUTHORS +.An Erik E. Fair Aq fair@clock.org +.Sh BUGS +Does not work on NFS or other network file system on different +systems because the disparate systems have disjoint PID spaces. +.Pp +Cannot handle the case where a lock file was not deleted, the +process that created it has exited, and the system has created a +new process with the same PID as in the dead lock file. +The lock file will appear to be valid even though the process is +unrelated to the one that created the lock in the first place. +Always remove your lock files after you're done. diff --git a/shell_cmds/shlock/shlock.c b/shell_cmds/shlock/shlock.c new file mode 100644 index 0000000..9dcb645 --- /dev/null +++ b/shell_cmds/shlock/shlock.c @@ -0,0 +1,364 @@ +/* $NetBSD: shlock.c,v 1.10 2008/04/28 20:24:14 martin Exp $ */ + +/*- + * Copyright (c) 2006 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Erik E. Fair. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +/* +** Program to produce reliable locks for shell scripts. +** Algorithm suggested by Peter Honeyman, January 1984, +** in connection with HoneyDanBer UUCP. +** +** I tried extending this to handle shared locks in November 1987, +** and ran into to some fundamental problems: +** +** Neither 4.3 BSD nor System V have an open(2) with locking, +** so that you can open a file and have it locked as soon as +** it's real; you have to make two system calls, and there's +** a race... +** +** When removing dead process id's from a list in a file, +** you need to truncate the file (you don't want to create a +** new one; see above); unfortunately for the portability of +** this program, only 4.3 BSD has ftruncate(2). +** +** Erik E. Fair <fair@ucbarpa.berkeley.edu>, November 8, 1987 +** +** Extensions for UUCP style locks (i.e. pid is an int in the file, +** rather than an ASCII string). Also fix long standing bug with +** full file systems and temporary files. +** +** Erik E. Fair <fair@apple.com>, November 12, 1989 +** +** ANSIfy the code somewhat to make gcc -Wall happy with the code. +** Submit to NetBSD +** +** Erik E. Fair <fair@clock.org>, May 20, 1997 +*/ + +#include <sys/cdefs.h> + +#ifndef lint +__RCSID("$NetBSD: shlock.c,v 1.10 2008/04/28 20:24:14 martin Exp $"); +#endif + +#include <sys/types.h> +#include <sys/file.h> +#include <fcntl.h> /* Needed on hpux */ +#include <stdio.h> +#include <signal.h> +#include <errno.h> +#include <string.h> +#include <unistd.h> +#include <stdlib.h> + +#define LOCK_SET 0 +#define LOCK_FAIL 1 + +#define LOCK_GOOD 0 +#define LOCK_BAD 1 + +#define FAIL (-1) + +#define TRUE 1 +#define FALSE 0 + +int Debug = FALSE; +char *Pname; +const char USAGE[] = "%s: USAGE: %s [-du] [-p PID] -f file\n"; +const char E_unlk[] = "%s: unlink(%s): %s\n"; +const char E_open[] = "%s: open(%s): %s\n"; + +#define dprintf if (Debug) printf + +/* +** Prototypes to make the ANSI compilers happy +** Didn't lint used to do type and argument checking? +** (and wasn't that sufficient?) +*/ + +/* the following is in case you need to make the prototypes go away. */ +char *xtmpfile(char *, pid_t, int); +int p_exists(pid_t); +int cklock(char *, int); +int mklock(char *, pid_t, int); +void bad_usage(void); +int main(int, char **); + +/* +** Create a temporary file, all ready to lock with. +** The file arg is so we get the filename right, if he +** gave us a full path, instead of using the current directory +** which might not be in the same filesystem. +*/ +char * +xtmpfile(char *file, pid_t pid, int uucpstyle) +{ + int fd; + int len; + char *cp, buf[BUFSIZ]; + static char tempname[BUFSIZ]; + + sprintf(buf, "shlock%ld", (u_long)getpid()); + if ((cp = strrchr(strcpy(tempname, file), '/')) != (char *)NULL) { + *++cp = '\0'; + (void) strcat(tempname, buf); + } else + (void) strcpy(tempname, buf); + dprintf("%s: temporary filename: %s\n", Pname, tempname); + + sprintf(buf, "%ld\n", (u_long)pid); + len = strlen(buf); +openloop: + if ((fd = open(tempname, O_RDWR|O_CREAT|O_EXCL, 0644)) < 0) { + switch(errno) { + case EEXIST: + dprintf("%s: file %s exists already.\n", + Pname, tempname); + if (unlink(tempname) < 0) { + fprintf(stderr, E_unlk, + Pname, tempname, strerror(errno)); + return((char *)NULL); + } + /* + ** Further profanity + */ + goto openloop; + default: + fprintf(stderr, E_open, + Pname, tempname, strerror(errno)); + return((char *)NULL); + } + } + + /* + ** Write the PID into the temporary file before attempting to link + ** to the actual lock file. That way we have a valid lock the instant + ** the link succeeds. + */ + if (uucpstyle ? + (write(fd, &pid, sizeof(pid)) != sizeof(pid)) : + (write(fd, buf, len) < 0)) + { + fprintf(stderr, "%s: write(%s,%ld): %s\n", + Pname, tempname, (u_long)pid, strerror(errno)); + (void) close(fd); + if (unlink(tempname) < 0) { + fprintf(stderr, E_unlk, + Pname, tempname, strerror(errno)); + } + return((char *)NULL); + } + (void) close(fd); + return(tempname); +} + +/* +** Does the PID exist? +** Send null signal to find out. +*/ +int +p_exists(pid_t pid) +{ + dprintf("%s: process %ld is ", Pname, (u_long)pid); + if (pid <= 0) { + dprintf("invalid\n"); + return(FALSE); + } + if (kill(pid, 0) < 0) { + switch(errno) { + case ESRCH: + dprintf("dead\n"); + return(FALSE); /* pid does not exist */ + case EPERM: + dprintf("alive\n"); + return(TRUE); /* pid exists */ + default: + dprintf("state unknown: %s\n", strerror(errno)); + return(TRUE); /* be conservative */ + } + } + dprintf("alive\n"); + return(TRUE); /* pid exists */ +} + +/* +** Check the validity of an existing lock file. +** +** Read the PID out of the lock +** Send a null signal to determine whether that PID still exists +** Existence (or not) determines the validity of the lock. +** +** Two bigs wins to this algorithm: +** +** o Locks do not survive crashes of either the system or the +** application by any appreciable period of time. +** +** o No clean up to do if the system or application crashes. +** +*/ +int +cklock(char *file, int uucpstyle) +{ + int fd = open(file, O_RDONLY); + ssize_t len; + pid_t pid; + char buf[BUFSIZ]; + + dprintf("%s: checking extant lock <%s>\n", Pname, file); + if (fd < 0) { + if (errno != ENOENT) + fprintf(stderr, E_open, Pname, file, strerror(errno)); + return(TRUE); /* might or might not; conservatism */ + } + + if (uucpstyle ? + ((len = read(fd, &pid, sizeof(pid))) != sizeof(pid)) : + ((len = read(fd, buf, sizeof(buf))) <= 0)) + { + close(fd); + dprintf("%s: lock file format error\n", Pname); + return(FALSE); + } + close(fd); + buf[len + 1] = '\0'; + return(p_exists(uucpstyle ? pid : atoi(buf))); +} + +int +mklock(char *file, pid_t pid, int uucpstyle) +{ + char *tmp; + int retcode = FALSE; + + dprintf("%s: trying lock <%s> for process %ld\n", Pname, file, + (u_long)pid); + if ((tmp = xtmpfile(file, pid, uucpstyle)) == (char *)NULL) + return(FALSE); + +linkloop: + if (link(tmp, file) < 0) { + switch(errno) { + case EEXIST: + dprintf("%s: lock <%s> already exists\n", Pname, file); + if (cklock(file, uucpstyle)) { + dprintf("%s: extant lock is valid\n", Pname); + break; + } else { + dprintf("%s: lock is invalid, removing\n", + Pname); + if (unlink(file) < 0) { + fprintf(stderr, E_unlk, + Pname, file, strerror(errno)); + break; + } + } + /* + ** I hereby profane the god of structured programming, + ** Edsgar Dijkstra + */ + goto linkloop; + default: + fprintf(stderr, "%s: link(%s, %s): %s\n", + Pname, tmp, file, strerror(errno)); + break; + } + } else { + dprintf("%s: got lock <%s>\n", Pname, file); + retcode = TRUE; + } + if (unlink(tmp) < 0) { + fprintf(stderr, E_unlk, Pname, tmp, strerror(errno)); + } + return(retcode); +} + +void +bad_usage(void) +{ + fprintf(stderr, USAGE, Pname, Pname); + exit(LOCK_FAIL); +} + +int +main(int ac, char **av) +{ + int x; + char *file = (char *)NULL; + pid_t pid = 0; + int uucpstyle = FALSE; /* indicating UUCP style locks */ + int only_check = TRUE; /* don't make a lock */ + + Pname = ((Pname = strrchr(av[0], '/')) ? Pname + 1 : av[0]); + + for(x = 1; x < ac; x++) { + if (av[x][0] == '-') { + switch(av[x][1]) { + case 'u': + uucpstyle = TRUE; + break; + case 'd': + Debug = TRUE; + break; + case 'p': + if (strlen(av[x]) > 2) { + pid = atoi(&av[x][2]); + } else { + if (++x >= ac) { + bad_usage(); + } + pid = atoi(av[x]); + } + only_check = FALSE; /* wants one */ + break; + case 'f': + if (strlen(av[x]) > 2) { + file = &av[x][2]; + } else { + if (++x >= ac) { + bad_usage(); + } + file = av[x]; + } + break; + default: + bad_usage(); + } + } + } + + if (file == (char *)NULL || (!only_check && pid <= 0)) { + bad_usage(); + } + + if (only_check) { + exit(cklock(file, uucpstyle) ? LOCK_GOOD : LOCK_BAD); + } + + exit(mklock(file, pid, uucpstyle) ? LOCK_SET : LOCK_FAIL); +} diff --git a/shell_cmds/sleep/sleep.1 b/shell_cmds/sleep/sleep.1 new file mode 100644 index 0000000..f5215be --- /dev/null +++ b/shell_cmds/sleep/sleep.1 @@ -0,0 +1,124 @@ +.\"- +.\" Copyright (c) 1990, 1993, 1994 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" the Institute of Electrical and Electronics Engineers, Inc. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)sleep.1 8.3 (Berkeley) 4/18/94 +.\" $FreeBSD: src/bin/sleep/sleep.1,v 1.22 2005/01/16 16:41:59 ru Exp $ +.\" +.Dd April 18, 1994 +.Dt SLEEP 1 +.Os +.Sh NAME +.Nm sleep +.Nd suspend execution for an interval of time +.Sh SYNOPSIS +.Nm +.Ar seconds +.Sh DESCRIPTION +The +.Nm +command +suspends execution for a minimum of +.Ar seconds . +.Pp +If the +.Nm +command receives a signal, it takes the standard action. +.Sh IMPLEMENTATION NOTES +The +.Dv SIGALRM +signal is not handled specially by this implementation. +.Pp +The +.Nm +command will accept and honor a non-integer number of specified seconds +(with a +.Ql .\& +character as a decimal point). +.Bf Sy +This is a non-portable extension, and its use will nearly guarantee that +a shell script will not execute properly on another system. +.Ef +.Sh EXIT STATUS +.Ex -std +.Sh EXAMPLES +To schedule the execution of a command for +.Va x +number seconds later (with +.Xr csh 1 ) : +.Pp +.Dl (sleep 1800; sh command_file >& errors)& +.Pp +This incantation would wait a half hour before +running the script command_file. +(See the +.Xr at 1 +utility.) +.Pp +To reiteratively run a command (with the +.Xr csh 1 ) : +.Pp +.Bd -literal -offset indent -compact +while (1) + if (! -r zzz.rawdata) then + sleep 300 + else + foreach i (`ls *.rawdata`) + sleep 70 + awk -f collapse_data $i >> results + end + break + endif +end +.Ed +.Pp +The scenario for a script such as this might be: a program currently +running is taking longer than expected to process a series of +files, and it would be nice to have +another program start processing the files created by the first +program as soon as it is finished (when zzz.rawdata is created). +The script checks every five minutes for the file zzz.rawdata, +when the file is found, then another portion processing +is done courteously by sleeping for 70 seconds in between each +awk job. +.Sh SEE ALSO +.Xr nanosleep 2 , +.Xr sleep 3 +.Sh STANDARDS +The +.Nm +command is expected to be +.St -p1003.2 +compatible. +.Sh HISTORY +A +.Nm +command appeared in +.At v4 . diff --git a/shell_cmds/sleep/sleep.c b/shell_cmds/sleep/sleep.c new file mode 100644 index 0000000..d41556c --- /dev/null +++ b/shell_cmds/sleep/sleep.c @@ -0,0 +1,131 @@ +/*- + * Copyright (c) 1988, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if 0 +#ifndef lint +static char const copyright[] = +"@(#) Copyright (c) 1988, 1993, 1994\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)sleep.c 8.3 (Berkeley) 4/2/94"; +#endif /* not lint */ +#endif +#include <sys/cdefs.h> +__FBSDID("$FreeBSD: src/bin/sleep/sleep.c,v 1.20 2005/08/07 09:11:38 stefanf Exp $"); + +#include <ctype.h> +#include <limits.h> +#include <stdio.h> +#include <stdlib.h> +#include <time.h> +#include <unistd.h> +#include <string.h> + +void usage(void); + +int +main(int argc, char *argv[]) +{ + struct timespec time_to_sleep; + long l; + int neg; + char *p; + + if (argc == 2) { + p = argv[1]; + } else if (argc == 3 && !strcmp("--", argv[1])) { + // POSIX issue: "sleep -- 3" should be the same as "sleep 3", + // normally getopt makes this kind of thing work, but sleep has + // no options, so we do it "the easy way" + p = argv[2]; + } else { + usage(); + return(1); + } + + /* Skip over leading whitespaces. */ + while (isspace((unsigned char)*p)) + ++p; + + /* Check for optional `+' or `-' sign. */ + neg = 0; + if (*p == '-') { + neg = 1; + ++p; + if (!isdigit((unsigned char)*p) && *p != '.') { + usage(); + return(1); + } + } + else if (*p == '+') + ++p; + + /* Calculate seconds. */ + if (isdigit((unsigned char)*p)) { + l = strtol(p, &p, 10); + if (l > INT_MAX) { + /* + * Avoid overflow when `seconds' is huge. This assumes + * that the maximum value for a time_t is <= INT_MAX. + */ + l = INT_MAX; + } + } else + l = 0; + time_to_sleep.tv_sec = (time_t)l; + + /* Calculate nanoseconds. */ + time_to_sleep.tv_nsec = 0; + + if (*p == '.') { /* Decimal point. */ + l = 100000000L; + do { + if (isdigit((unsigned char)*++p)) + time_to_sleep.tv_nsec += (*p - '0') * l; + else + break; + l /= 10; + } while (l); + } + + if (!neg && (time_to_sleep.tv_sec > 0 || time_to_sleep.tv_nsec > 0)) + (void)nanosleep(&time_to_sleep, (struct timespec *)NULL); + + return(0); +} + +void +usage(void) +{ + const char msg[] = "usage: sleep seconds\n"; + + write(STDERR_FILENO, msg, sizeof(msg) - 1); +} diff --git a/shell_cmds/su/su.1 b/shell_cmds/su/su.1 new file mode 100644 index 0000000..014a567 --- /dev/null +++ b/shell_cmds/su/su.1 @@ -0,0 +1,213 @@ +.\" Copyright (c) 1988, 1990, 1993, 1994 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)su.1 8.2 (Berkeley) 4/18/94 +.\" $FreeBSD: src/usr.bin/su/su.1,v 1.40 2007/07/24 06:41:07 delphij Exp $ +.\" +.Dd September 13, 2006 +.Dt SU 1 +.Os +.Sh NAME +.Nm su +.Nd substitute user identity +.Sh SYNOPSIS +.Nm +.Op Fl +.Op Fl flm +.Op Ar login Op Ar args +.Sh DESCRIPTION +The +.Nm +utility requests appropriate user credentials via PAM +and switches to that user ID +(the default user is the superuser). +A shell is then executed. +.Pp +PAM is used to set the policy +.Xr su 1 +will use. +In particular, by default only users in the +.Dq Li admin +or +.Dq Li wheel +groups can switch to UID 0 +.Pq Dq Li root . +This group requirement may be changed by modifying the +.Dq Li pam_group +section of +.Pa /etc/pam.d/su . +See +.Xr pam_group 8 +for details on how to modify this setting. +.Pp +By default, the environment is unmodified with the exception of +.Ev USER , +.Ev HOME , +and +.Ev SHELL . +.Ev HOME +and +.Ev SHELL +are set to the target login's default values. +.Ev USER +is set to the target login, unless the target login has a user ID of 0, +in which case it is unmodified. +The invoked shell is the one belonging to the target login. +This is the traditional behavior of +.Nm . +.Pp +The options are as follows: +.Bl -tag -width Ds +.It Fl f +If the invoked shell is +.Xr csh 1 , +this option prevents it from reading the +.Dq Pa .cshrc +file. +.It Fl l +Simulate a full login. +The environment is discarded except for +.Ev HOME , +.Ev SHELL , +.Ev PATH , +.Ev TERM , +and +.Ev USER . +.Ev HOME +and +.Ev SHELL +are modified as above. +.Ev USER +is set to the target login. +.Ev PATH +is set to +.Dq Pa /bin:/usr/bin . +.Ev TERM +is imported from your current environment. +The invoked shell is the target login's, and +.Nm +will change directory to the target login's home directory. +.It Fl +(no letter) The same as +.Fl l . +.It Fl m +Leave the environment unmodified. +The invoked shell is your login shell, and no directory changes are made. +As a security precaution, if the target user's shell is a non-standard +shell (as defined by +.Xr getusershell 3 ) +and the caller's real uid is +non-zero, +.Nm +will fail. +.El +.Pp +The +.Fl l +(or +.Fl ) +and +.Fl m +options are mutually exclusive; the last one specified +overrides any previous ones. +.Pp +If the optional +.Ar args +are provided on the command line, they are passed to the login shell of +the target login. +Note that all command line arguments before the target login name are +processed by +.Nm +itself, everything after the target login name gets passed to the login +shell. +.Pp +By default (unless the prompt is reset by a startup file) the super-user +prompt is set to +.Dq Sy \&# +to remind one of its awesome power. +.Sh ENVIRONMENT +Environment variables used by +.Nm : +.Bl -tag -width HOME +.It Ev HOME +Default home directory of real user ID unless modified as +specified above. +.It Ev PATH +Default search path of real user ID unless modified as specified above. +.It Ev TERM +Provides terminal type which may be retained for the substituted +user ID. +.It Ev USER +The user ID is always the effective ID (the target user ID) after an +.Nm +unless the user ID is 0 (root). +.El +.Sh FILES +.Bl -tag -width ".Pa /etc/pam.d/su" -compact +.It Pa /etc/pam.d/su +PAM configuration for +.Nm . +.El +.Sh EXAMPLES +.Bl -tag -width 5n -compact +.It Li "su man -c catman" +Runs the command +.Li catman +as user +.Li man . +You will be asked for man's password unless your real UID is 0. +.It Li "su man -c 'catman /usr/share/man /usr/local/man'" +Same as above, but the target command consists of more than a +single word and hence is quoted for use with the +.Fl c +option being passed to the shell. +(Most shells expect the argument to +.Fl c +to be a single word). +.It Li "su -l foo" +Simulate a login for user foo. +.It Li "su - foo" +Same as above. +.It Li "su -" +Simulate a login for root. +.El +.Sh SEE ALSO +.Xr csh 1 , +.Xr sh 1 , +.Xr group 5 , +.Xr passwd 5 , +.Xr environ 7 , +.Xr pam_group 8 +.Sh HISTORY +A +.Nm +command appeared in +.At v1 . diff --git a/shell_cmds/su/su.c b/shell_cmds/su/su.c new file mode 100644 index 0000000..8ad7434 --- /dev/null +++ b/shell_cmds/su/su.c @@ -0,0 +1,737 @@ +/* + * Copyright (c) 2002, 2005 Networks Associates Technologies, Inc. + * All rights reserved. + * + * Portions of this software were developed for the FreeBSD Project by + * ThinkSec AS and NAI Labs, the Security Research Division of Network + * Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 + * ("CBOSS"), as part of the DARPA CHATS research program. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ +/*- + * Copyright (c) 1988, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#ifndef lint +static const char copyright[] = +"@(#) Copyright (c) 1988, 1993, 1994\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#if 0 +#ifndef lint +static char sccsid[] = "@(#)su.c 8.3 (Berkeley) 4/2/94"; +#endif /* not lint */ +#endif + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD: src/usr.bin/su/su.c,v 1.91 2009/12/13 03:14:06 delphij Exp $"); + +#include <sys/param.h> +#include <sys/time.h> +#include <sys/resource.h> +#include <sys/wait.h> + +#ifdef USE_BSM_AUDIT +#include <bsm/libbsm.h> +#include <bsm/audit_uevents.h> +#endif + +#include <err.h> +#include <errno.h> +#include <grp.h> +#ifndef __APPLE__ +#include <login_cap.h> +#endif /* !__APPLE__ */ +#include <paths.h> +#include <pwd.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> +#include <unistd.h> +#include <stdarg.h> + +#include <security/pam_appl.h> +#include <security/openpam.h> + +#ifdef __APPLE__ +#include <bsm/audit_session.h> +#endif /* __APPLE__ */ + +#define PAM_END() do { \ + int local_ret; \ + if (pamh != NULL) { \ + local_ret = pam_setcred(pamh, PAM_DELETE_CRED); \ + if (local_ret != PAM_SUCCESS) \ + syslog(LOG_ERR, "pam_setcred: %s", \ + pam_strerror(pamh, local_ret)); \ + if (asthem) { \ + local_ret = pam_close_session(pamh, 0); \ + if (local_ret != PAM_SUCCESS) \ + syslog(LOG_ERR, "pam_close_session: %s",\ + pam_strerror(pamh, local_ret)); \ + } \ + local_ret = pam_end(pamh, local_ret); \ + if (local_ret != PAM_SUCCESS) \ + syslog(LOG_ERR, "pam_end: %s", \ + pam_strerror(pamh, local_ret)); \ + } \ +} while (0) + + +#define PAM_SET_ITEM(what, item) do { \ + int local_ret; \ + local_ret = pam_set_item(pamh, what, item); \ + if (local_ret != PAM_SUCCESS) { \ + syslog(LOG_ERR, "pam_set_item(" #what "): %s", \ + pam_strerror(pamh, local_ret)); \ + errx(1, "pam_set_item(" #what "): %s", \ + pam_strerror(pamh, local_ret)); \ + /* NOTREACHED */ \ + } \ +} while (0) + +enum tristate { UNSET, YES, NO }; + +static pam_handle_t *pamh = NULL; +static char **environ_pam; + +static char *ontty(void); +static int chshell(const char *); +static void usage(void) __dead2; +static void export_pam_environment(void); +static int ok_to_export(const char *); + +extern char **environ; + +int +main(int argc, char *argv[]) +{ + static char *cleanenv; + struct passwd *pwd; + struct pam_conv conv = { openpam_ttyconv, NULL }; + enum tristate iscsh; +#ifndef __APPLE__ + login_cap_t *lc; +#endif /* !__APPLE__ */ + union { + const char **a; + char * const *b; + } np; + uid_t ruid; + pid_t child_pid, child_pgrp, pid; + int asme, ch, asthem, fastlogin, prio, i, retcode, + statusp, setmaclabel; +#ifndef __APPLE__ + u_int setwhat; +#endif /* !__APPLE__ */ + char *username, *class, shellbuf[MAXPATHLEN]; + const char *p, *user, *shell, *mytty, **nargv; + const void *v; + struct sigaction sa, sa_int, sa_quit, sa_pipe; + int temp, fds[2]; +#ifdef USE_BSM_AUDIT + const char *aerr; + au_id_t auid; +#endif +#ifdef __APPLE__ + /* 4043304 */ + const char *avshell; + char avshellbuf[MAXPATHLEN]; +#endif /* __APPLE__ */ + + shell = class = cleanenv = NULL; + asme = asthem = fastlogin = statusp = 0; + user = "root"; + iscsh = UNSET; + setmaclabel = 0; + +#ifdef __APPLE__ + while ((ch = getopt(argc, argv, "-flm")) != -1) +#else + while ((ch = getopt(argc, argv, "-flmsc:")) != -1) +#endif /* __APPLE__ */ + switch ((char)ch) { + case 'f': + fastlogin = 1; + break; + case '-': + case 'l': + asme = 0; + asthem = 1; + break; + case 'm': + asme = 1; + asthem = 0; + break; +#ifndef __APPLE__ + case 's': + setmaclabel = 1; + break; + case 'c': + class = optarg; + break; +#endif /* !__APPLE__ */ + case '?': + default: + usage(); + /* NOTREACHED */ + } + + if (optind < argc) + user = argv[optind++]; + + if (user == NULL) + usage(); + /* NOTREACHED */ + + /* + * Try to provide more helpful debugging output if su(1) is running + * non-setuid, or was run from a file system not mounted setuid. + */ + if (geteuid() != 0) + errx(1, "not running setuid"); + +#ifdef USE_BSM_AUDIT + if (getauid(&auid) < 0 && errno != ENOSYS) { + syslog(LOG_AUTH | LOG_ERR, "getauid: %s", strerror(errno)); + errx(1, "Permission denied"); + } +#endif + if (strlen(user) > MAXLOGNAME - 1) { +#ifdef USE_BSM_AUDIT + if (audit_submit(AUE_su, auid, + EPERM, 1, "username too long: '%s'", user)) + errx(1, "Permission denied"); +#endif + errx(1, "username too long"); + } + + nargv = malloc(sizeof(char *) * (size_t)(argc + 4)); + if (nargv == NULL) + errx(1, "malloc failure"); + + nargv[argc + 3] = NULL; + for (i = argc; i >= optind; i--) + nargv[i + 3] = argv[i]; + np.a = &nargv[i + 3]; + + argv += optind; + + errno = 0; + prio = getpriority(PRIO_PROCESS, 0); + if (errno) + prio = 0; + + setpriority(PRIO_PROCESS, 0, -2); + openlog("su", LOG_CONS, LOG_AUTH); + + /* get current login name, real uid and shell */ + ruid = getuid(); + username = getlogin(); + pwd = getpwnam(username); + if (username == NULL || pwd == NULL || pwd->pw_uid != ruid) + pwd = getpwuid(ruid); + if (pwd == NULL) { +#ifdef USE_BSM_AUDIT + if (audit_submit(AUE_su, auid, EPERM, 1, + "unable to determine invoking subject: '%s'", username)) + errx(1, "Permission denied"); +#endif + errx(1, "who are you?"); + } + + username = strdup(pwd->pw_name); + if (username == NULL) + err(1, "strdup failure"); + + if (asme) { + if (pwd->pw_shell != NULL && *pwd->pw_shell != '\0') { + /* must copy - pwd memory is recycled */ + shell = strncpy(shellbuf, pwd->pw_shell, + sizeof(shellbuf)); + shellbuf[sizeof(shellbuf) - 1] = '\0'; + } + else { + shell = _PATH_BSHELL; + iscsh = NO; + } + } + + /* Do the whole PAM startup thing */ + retcode = pam_start("su", user, &conv, &pamh); + if (retcode != PAM_SUCCESS) { + syslog(LOG_ERR, "pam_start: %s", pam_strerror(pamh, retcode)); + errx(1, "pam_start: %s", pam_strerror(pamh, retcode)); + } + + PAM_SET_ITEM(PAM_RUSER, username); + + mytty = ttyname(STDERR_FILENO); + if (!mytty) + mytty = "tty"; + PAM_SET_ITEM(PAM_TTY, mytty); + + retcode = pam_authenticate(pamh, 0); + if (retcode != PAM_SUCCESS) { +#ifdef USE_BSM_AUDIT + if (audit_submit(AUE_su, auid, EPERM, 1, "bad su %s to %s on %s", + username, user, mytty)) + errx(1, "Permission denied"); +#endif + syslog(LOG_AUTH|LOG_WARNING, "BAD SU %s to %s on %s", + username, user, mytty); + errx(1, "Sorry"); + } +#ifdef USE_BSM_AUDIT + if (audit_submit(AUE_su, auid, 0, 0, "successful authentication")) + errx(1, "Permission denied"); +#endif + retcode = pam_get_item(pamh, PAM_USER, &v); + if (retcode == PAM_SUCCESS) + user = v; + else + syslog(LOG_ERR, "pam_get_item(PAM_USER): %s", + pam_strerror(pamh, retcode)); + pwd = getpwnam(user); + if (pwd == NULL) { +#ifdef USE_BSM_AUDIT + if (audit_submit(AUE_su, auid, EPERM, 1, + "unknown subject: %s", user)) + errx(1, "Permission denied"); +#endif + errx(1, "unknown login: %s", user); + } + + retcode = pam_acct_mgmt(pamh, 0); + if (retcode == PAM_NEW_AUTHTOK_REQD) { + retcode = pam_chauthtok(pamh, + PAM_CHANGE_EXPIRED_AUTHTOK); + if (retcode != PAM_SUCCESS) { +#ifdef USE_BSM_AUDIT + aerr = pam_strerror(pamh, retcode); + if (aerr == NULL) + aerr = "Unknown PAM error"; + if (audit_submit(AUE_su, auid, EPERM, 1, + "pam_chauthtok: %s", aerr)) + errx(1, "Permission denied"); +#endif + syslog(LOG_ERR, "pam_chauthtok: %s", + pam_strerror(pamh, retcode)); + errx(1, "Sorry"); + } + } + if (retcode != PAM_SUCCESS) { +#ifdef USE_BSM_AUDIT + if (audit_submit(AUE_su, auid, EPERM, 1, "pam_acct_mgmt: %s", + pam_strerror(pamh, retcode))) + errx(1, "Permission denied"); +#endif + syslog(LOG_ERR, "pam_acct_mgmt: %s", + pam_strerror(pamh, retcode)); + errx(1, "Sorry"); + } + +#ifndef __APPLE__ + /* get target login information */ + if (class == NULL) + lc = login_getpwclass(pwd); + else { + if (ruid != 0) { +#ifdef USE_BSM_AUDIT + if (audit_submit(AUE_su, auid, EPERM, 1, + "only root may use -c")) + errx(1, "Permission denied"); +#endif + errx(1, "only root may use -c"); + } + lc = login_getclass(class); + if (lc == NULL) + errx(1, "unknown class: %s", class); + } +#endif /* !__APPLE__ */ + + /* if asme and non-standard target shell, must be root */ + if (asme) { + if (ruid != 0 && !chshell(pwd->pw_shell)) + errx(1, "permission denied (shell)"); + } + else if (pwd->pw_shell && *pwd->pw_shell) { +#ifdef __APPLE__ + /* 3825554 */ + shell = strncpy(shellbuf, pwd->pw_shell, sizeof(shellbuf)); + shellbuf[sizeof(shellbuf) - 1] = '\0'; +#else + shell = pwd->pw_shell; +#endif /* __APPLE__ */ + iscsh = UNSET; + } + else { + shell = _PATH_BSHELL; + iscsh = NO; + } + + /* if we're forking a csh, we want to slightly muck the args */ + if (iscsh == UNSET) { + p = strrchr(shell, '/'); + if (p) + ++p; + else + p = shell; + iscsh = strcmp(p, "csh") ? (strcmp(p, "tcsh") ? NO : YES) : YES; + } + setpriority(PRIO_PROCESS, 0, prio); + + /* + * PAM modules might add supplementary groups in pam_setcred(), so + * initialize them first. + */ +#ifdef __APPLE__ + if (initgroups(user, pwd->pw_gid)) + err(1, "initgroups"); +#else + if (setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETGROUP) < 0) + err(1, "setusercontext"); +#endif /* __APPLE__ */ + +#ifdef __APPLE__ + /* 8530846 */ + if (asthem) { + auditinfo_addr_t auinfo = { + .ai_termid = { .at_type = AU_IPv4 }, + .ai_asid = AU_ASSIGN_ASID, + .ai_auid = getuid(), + .ai_flags = 0, + }; + if (setaudit_addr(&auinfo, sizeof(auinfo)) == 0) { + char session[16]; + snprintf(session, sizeof(session), "%x", auinfo.ai_asid); + setenv("SECURITYSESSIONID", session, 1); + } else { + errx(1, "failed to create session."); + } + } +#endif /* __APPLE__ */ + + retcode = pam_setcred(pamh, PAM_ESTABLISH_CRED); + if (retcode != PAM_SUCCESS) { + syslog(LOG_ERR, "pam_setcred: %s", + pam_strerror(pamh, retcode)); + errx(1, "failed to establish credentials."); + } + if (asthem) { + retcode = pam_open_session(pamh, 0); + if (retcode != PAM_SUCCESS) { + syslog(LOG_ERR, "pam_open_session: %s", + pam_strerror(pamh, retcode)); + errx(1, "failed to open session."); + } + } + + /* + * We must fork() before setuid() because we need to call + * pam_setcred(pamh, PAM_DELETE_CRED) as root. + */ + sa.sa_flags = SA_RESTART; + sa.sa_handler = SIG_IGN; + sigemptyset(&sa.sa_mask); + sigaction(SIGINT, &sa, &sa_int); + sigaction(SIGQUIT, &sa, &sa_quit); + sigaction(SIGPIPE, &sa, &sa_pipe); + sa.sa_handler = SIG_DFL; + sigaction(SIGTSTP, &sa, NULL); + statusp = 1; + if (pipe(fds) == -1) { + PAM_END(); + err(1, "pipe"); + } + child_pid = fork(); + switch (child_pid) { + default: + sa.sa_handler = SIG_IGN; + sigaction(SIGTTOU, &sa, NULL); + close(fds[0]); + setpgid(child_pid, child_pid); + if (tcgetpgrp(STDERR_FILENO) == getpgrp()) + tcsetpgrp(STDERR_FILENO, child_pid); + close(fds[1]); + sigaction(SIGPIPE, &sa_pipe, NULL); + while ((pid = waitpid(child_pid, &statusp, WUNTRACED)) != -1) { + if (WIFSTOPPED(statusp)) { + child_pgrp = getpgid(child_pid); + if (tcgetpgrp(STDERR_FILENO) == child_pgrp) + tcsetpgrp(STDERR_FILENO, getpgrp()); + kill(getpid(), SIGSTOP); + if (tcgetpgrp(STDERR_FILENO) == getpgrp()) { + child_pgrp = getpgid(child_pid); + tcsetpgrp(STDERR_FILENO, child_pgrp); + } + kill(child_pid, SIGCONT); + statusp = 1; + continue; + } + break; + } + tcsetpgrp(STDERR_FILENO, getpgrp()); + if (pid == -1) + err(1, "waitpid"); + PAM_END(); + exit(WEXITSTATUS(statusp)); + case -1: + PAM_END(); + err(1, "fork"); + case 0: + close(fds[1]); + read(fds[0], &temp, 1); + close(fds[0]); + sigaction(SIGPIPE, &sa_pipe, NULL); + sigaction(SIGINT, &sa_int, NULL); + sigaction(SIGQUIT, &sa_quit, NULL); + +#ifdef __APPLE__ + if (setgid(pwd->pw_gid)) + err(1, "setgid"); + /* Call initgroups(2) after setgid(2) to re-establish memberd */ + if (initgroups(user, pwd->pw_gid)) + err(1, "initgroups"); + if (setuid(pwd->pw_uid)) + err(1, "setuid"); +#else + /* + * Set all user context except for: Environmental variables + * Umask Login records (wtmp, etc) Path + */ + setwhat = LOGIN_SETALL & ~(LOGIN_SETENV | LOGIN_SETUMASK | + LOGIN_SETLOGIN | LOGIN_SETPATH | LOGIN_SETGROUP | + LOGIN_SETMAC); + /* + * If -s is present, also set the MAC label. + */ + if (setmaclabel) + setwhat |= LOGIN_SETMAC; + /* + * Don't touch resource/priority settings if -m has been used + * or -l and -c hasn't, and we're not su'ing to root. + */ + if ((asme || (!asthem && class == NULL)) && pwd->pw_uid) + setwhat &= ~(LOGIN_SETPRIORITY | LOGIN_SETRESOURCES); + if (setusercontext(lc, pwd, pwd->pw_uid, setwhat) < 0) + err(1, "setusercontext"); +#endif /* __APPLE__ */ + + if (!asme) { + if (asthem) { + p = getenv("TERM"); + environ = &cleanenv; + } + + if (asthem || pwd->pw_uid) + setenv("USER", pwd->pw_name, 1); + setenv("HOME", pwd->pw_dir, 1); + setenv("SHELL", shell, 1); +#ifdef __APPLE__ + unsetenv("TMPDIR"); +#endif /* __APPLE__ */ + + if (asthem) { + /* + * Add any environmental variables that the + * PAM modules may have set. + */ + environ_pam = pam_getenvlist(pamh); + if (environ_pam) + export_pam_environment(); + +#ifdef __APPLE__ + /* 5276965: As documented, set $PATH. */ + setenv("PATH", "/bin:/usr/bin", 1); +#else + /* set the su'd user's environment & umask */ + setusercontext(lc, pwd, pwd->pw_uid, + LOGIN_SETPATH | LOGIN_SETUMASK | + LOGIN_SETENV); +#endif /* __APPLE__ */ + if (p) + setenv("TERM", p, 1); + + p = pam_getenv(pamh, "HOME"); + if (chdir(p ? p : pwd->pw_dir) < 0) + errx(1, "no directory"); + } + } +#ifndef __APPLE__ + login_close(lc); +#endif /* !__APPLE__ */ + + if (iscsh == YES) { + if (fastlogin) + *np.a-- = "-f"; + if (asme) + *np.a-- = "-m"; + } +#ifdef __APPLE__ + /* 4043304 */ + if ((p = strrchr(shell, '/')) != NULL) + avshell = p + 1; + else + avshell = shell; + + if (asthem) { + avshellbuf[0] = '-'; + strlcpy(avshellbuf+1, avshell, sizeof(avshellbuf) - 1); + avshell = avshellbuf; + } + + /* csh *no longer* strips the first character... */ + *np.a = avshell; +#else + /* csh strips the first character... */ + *np.a = asthem ? "-su" : iscsh == YES ? "_su" : "su"; +#endif /* __APPLE__ */ + + if (ruid != 0) + syslog(LOG_NOTICE, "%s to %s%s", username, user, + ontty()); + + execv(shell, np.b); + err(1, "%s", shell); + } +} + +static void +export_pam_environment(void) +{ + char **pp; + char *p; + + for (pp = environ_pam; *pp != NULL; pp++) { + if (ok_to_export(*pp)) { + p = strchr(*pp, '='); + *p = '\0'; + setenv(*pp, p + 1, 1); + } + free(*pp); + } +} + +/* + * Sanity checks on PAM environmental variables: + * - Make sure there is an '=' in the string. + * - Make sure the string doesn't run on too long. + * - Do not export certain variables. This list was taken from the + * Solaris pam_putenv(3) man page. + * Note that if the user is chrooted, PAM may have a better idea than we + * do of where her home directory is. + */ +static int +ok_to_export(const char *s) +{ + static const char *noexport[] = { + "SHELL", /* "HOME", */ "LOGNAME", "MAIL", "CDPATH", + "IFS", "PATH", NULL + }; + const char **pp; + size_t n; + + if (strlen(s) > 1024 || strchr(s, '=') == NULL) + return 0; + if (strncmp(s, "LD_", 3) == 0) + return 0; + for (pp = noexport; *pp != NULL; pp++) { + n = strlen(*pp); + if (s[n] == '=' && strncmp(s, *pp, n) == 0) + return 0; + } + return 1; +} + +static void +usage(void) +{ + +#ifdef __APPLE__ + fprintf(stderr, "usage: su [-] [-flm] [login [args]]\n"); +#else + fprintf(stderr, "usage: su [-] [-flms] [-c class] [login [args]]\n"); +#endif /* __APPLE__ */ + exit(1); + /* NOTREACHED */ +} + +static int +chshell(const char *sh) +{ + int r; + char *cp; + + r = 0; + setusershell(); + while ((cp = getusershell()) != NULL && !r) + r = (strcmp(cp, sh) == 0); + endusershell(); + return r; +} + +static char * +ontty(void) +{ + char *p; + static char buf[MAXPATHLEN + 4]; + + buf[0] = 0; + p = ttyname(STDERR_FILENO); + if (p) + snprintf(buf, sizeof(buf), " on %s", p); + return buf; +} diff --git a/shell_cmds/su/su.pam b/shell_cmds/su/su.pam new file mode 100644 index 0000000..fd8a789 --- /dev/null +++ b/shell_cmds/su/su.pam @@ -0,0 +1,7 @@ +# su: auth account session +auth sufficient pam_rootok.so +auth required pam_opendirectory.so +account required pam_group.so no_warn group=admin,wheel ruser root_only fail_safe +account required pam_opendirectory.so no_check_shell +password required pam_opendirectory.so +session required pam_launchd.so diff --git a/shell_cmds/su/su_entitlements.plist b/shell_cmds/su/su_entitlements.plist new file mode 100644 index 0000000..a96cb44 --- /dev/null +++ b/shell_cmds/su/su_entitlements.plist @@ -0,0 +1,10 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>com.apple.keystore.console</key> + <true/> + <key>com.apple.private.security.clear-library-validation</key> + <true/> +</dict> +</plist> diff --git a/shell_cmds/systime/systime.1 b/shell_cmds/systime/systime.1 new file mode 100644 index 0000000..ce296b6 --- /dev/null +++ b/shell_cmds/systime/systime.1 @@ -0,0 +1,76 @@ +.Dd April 24, 2012
+.Dt systime 1
+.Os Darwin
+.Sh NAME
+.Nm systime
+.Nd gather system-wide time during program execution
+.Sh SYNOPSIS
+.Nm
+.Op Fl P
+.Op Fl r
+.Op Fl t Ar sleep_time
+.Ar utility Op Ar argument ...
+.Nm
+.Fl p
+.Op Fl r
+.Op Fl t Ar sleep_time
+.Nm
+.Op Fl P
+.Fl u Ar user
+.Fl s Ar sys
+.Fl i Ar idle
+.Nm
+.Op Fl P
+.Op Fl r
+.Op Fl t Ar sleep_time
+.Fl T Ar target_pid
+.Sh DESCRIPTION
+.Nm
+is similar to
+.Xr time 1
+in that it allows you to execute a utility program. However after execution
+completes, it reports the system-wide time that was spent
+during that time period, instead of just the portion directly caused
+by the utility. This can be used to gather information about CPU
+utilization by kernel threads (AIO, networking) and interrupt routines
+on behalf of a program, but not directly attributed to it.
+.Pp
+The
+.Nm
+program can also be used to snapshot counters with
+.Fl p
+and then later used with
+.Fl u Fl s Fl i
+to calculate time spent during a time interval that doesn't correlate
+to a specific command execution. For example:
+.Pp
+.Bd -ragged -offset indent
+$ eval `systime -p`
+.Pp
+$ ... time passes ...
+.Pp
+$ systime -u $systime_user -s $systime_sys -i $systime_idle
+.Pp
+18.79 real 2.03 user 1.04 sys
+.Ed
+.Pp
+The
+.Nm
+program can also be used to print the usage of a process with
+.Fl T Ar target_pid
+, also printing the system wide usage
+.Pp
+.Fl P
+can be used to print time spent as a percentage of overall CPU capacity
+of the system (capped at 100%)
+.Pp
+.Fl r
+can be used to print the usage iteratively with
+.Ar sleep_time
+seconds sleep between iterations
+.Pp
+.Fl t Ar sleep_time
+can be used to specify sleep_time between iterations, defaults to 1 second if not specified.
+.Sh SEE ALSO
+.Xr time 1 ,
+.Xr top 1
diff --git a/shell_cmds/systime/systime.c b/shell_cmds/systime/systime.c new file mode 100644 index 0000000..37cee8b --- /dev/null +++ b/shell_cmds/systime/systime.c @@ -0,0 +1,365 @@ +/* + * Copyright (c) 2012 Apple Inc. All rights reserved. + * + * @APPLE_LICENSE_HEADER_START@ + * + * This file contains Original Code and/or Modifications of Original Code + * as defined in and that are subject to the Apple Public Source License + * Version 2.0 (the 'License'). You may not use this file except in + * compliance with the License. Please obtain a copy of the License at + * http://www.opensource.apple.com/apsl/ and read it before using this + * file. + * + * The Original Code and all software distributed under the License are + * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER + * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, + * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. + * Please see the License for the specific language governing rights and + * limitations under the License. + * + * @APPLE_LICENSE_HEADER_END@ + */ + +#include <mach/mach.h> +#include <err.h> +#include <errno.h> +#include <stdbool.h> +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <libproc.h> +#include <mach/mach_time.h> + +static void usage(void); +static void do_print(void); +static void do_difftime(bool usepercent, uint64_t *olduser, uint64_t *oldsystem, uint64_t *oldidle); +static void do_piddifftime(bool userpercent, int pid, uint64_t *old_pid_user, uint64_t *old_pid_system, uint64_t *old_pid_time); +static kern_return_t get_processor_time(uint64_t *user, uint64_t *sys, uint64_t *idle); +static kern_return_t get_processor_count(int *ncpu); +static mach_timebase_info_data_t timebase_info; + +int +main(int argc, char *argv[]) +{ + int ch; + const char *optu = NULL; + const char *opts = NULL; + const char *opti = NULL; + const char *tpid = NULL; + const char *opt_sleep_time = NULL; + int sleep_time = 1; + int target_pid; + bool systemwide_time = false; + int pid; + int status; + uint64_t olduser, oldsystem, oldidle; + uint64_t old_pid_time; + uint64_t old_pid_user, old_pid_system; + kern_return_t kret; + bool usepercent = false; + bool recurring = false; + + while ((ch = getopt(argc, argv, "PrT:t:pu:s:i:")) != -1) { + switch (ch) { + case 'P': + usepercent = true; + break; + case 'r': + recurring = true; + break; + case 't': + opt_sleep_time = optarg; + break; + case 'T': + tpid = optarg; + break; + case 'p': + systemwide_time = true; + break; + case 'u': + optu = optarg; + break; + case 's': + opts = optarg; + break; + case 'i': + opti = optarg; + break; + case '?': + default: + usage(); + } + } + + mach_timebase_info(&timebase_info); + + if (opt_sleep_time) { + char *endstr; + sleep_time = (int)strtoul(opt_sleep_time, &endstr, 0); + if (opt_sleep_time[0] == '\0' || endstr[0] != '\0') + usage(); + } + + if (systemwide_time) { + bool first_pass = true; + olduser = oldsystem = oldidle = 0; + + if (recurring != true) { + do_print(); + exit(0); + } + do { + if (first_pass) { + do_difftime(false, &olduser, &oldsystem, &oldidle); + first_pass = false; + } else { + do_difftime(usepercent, &olduser, &oldsystem, &oldidle); + } + sleep(sleep_time); + } while (recurring); + + exit(0); + } + + + if (tpid) { + char *endstr; + bool first_pass = true; + + target_pid = (int)strtoul(tpid, &endstr, 0); + if (tpid[0] == '\0' || endstr[0] != '\0') + usage(); + + olduser = oldsystem = oldidle = 0; + old_pid_user = old_pid_system = old_pid_time = 0; + + do { + if (first_pass) { + do_difftime(false, &olduser, &oldsystem, &oldidle); + do_piddifftime(false, target_pid, &old_pid_user, &old_pid_system, &old_pid_time); + first_pass = false; + } else { + do_difftime(usepercent, &olduser, &oldsystem, &oldidle); + do_piddifftime(usepercent, target_pid, &old_pid_user, &old_pid_system, &old_pid_time); + } + + sleep(sleep_time); + } while (recurring); + exit(0); + } + + if (optu || opts || opti) { + char *endstr; + + if (!optu) + usage(); + olduser = strtoull(optu, &endstr, 0); + if (optu[0] == '\0' || endstr[0] != '\0') + usage(); + + if (!opts) + usage(); + oldsystem = strtoull(opts, &endstr, 0); + if (opts[0] == '\0' || endstr[0] != '\0') + usage(); + + if (!opti) + usage(); + oldidle = strtoull(opti, &endstr, 0); + if (opti[0] == '\0' || endstr[0] != '\0') + usage(); + + do_difftime(usepercent, &olduser, &oldsystem, &oldidle); + exit(0); + } + + argc -= optind; + argv += optind; + + if (argc == 0) + usage(); + + do { + kret = get_processor_time(&olduser, &oldsystem, &oldidle); + if (kret) + errx(1, "Error getting processor time: %s (%d)", mach_error_string(kret), kret); + + switch(pid = vfork()) { + case -1: /* error */ + perror("time"); + exit(1); + /* NOTREACHED */ + case 0: /* child */ + execvp(*argv, argv); + perror(*argv); + _exit((errno == ENOENT) ? 127 : 126); + /* NOTREACHED */ + } + + /* parent */ + (void)signal(SIGINT, SIG_IGN); + (void)signal(SIGQUIT, SIG_IGN); + while (wait(&status) != pid); + (void)signal(SIGINT, SIG_DFL); + (void)signal(SIGQUIT, SIG_DFL); + + do_difftime(usepercent, &olduser, &oldsystem, &oldidle); + + sleep(sleep_time); + } while (recurring); + + exit (WIFEXITED(status) ? WEXITSTATUS(status) : EXIT_FAILURE); + + return 0; +} + +static void +usage(void) +{ + fprintf(stderr, "usage: systime [-P] [-r] [-t sleep_time] utility [argument ...]\n" + " systime -p [-r] [-t sleep_time]\n" + " systime [-P] -u user -s sys -i idle\n" + " systime [-P] [-r] [-t sleep_time] -T target_pid\n"); + exit(1); +} + +static void +do_print(void) +{ + uint64_t user, system, idle; + kern_return_t kret; + + kret = get_processor_time(&user, &system, &idle); + if (kret) + errx(1, "Error getting processor time: %s (%d)", mach_error_string(kret), kret); + + printf("systime_user=%llu\n", user); + printf("systime_sys=%llu\n", system); + printf("systime_idle=%llu\n", idle); +} + +static void +do_difftime(bool usepercent, uint64_t *olduser, uint64_t *oldsystem, uint64_t *oldidle) +{ + uint64_t user, system, idle; + uint64_t userelapsed, systemelapsed, idleelapsed, totalelapsed; + kern_return_t kret; + + kret = get_processor_time(&user, &system, &idle); + if (kret) + errx(1, "Error getting processor time: %s (%d)", mach_error_string(kret), kret); + + userelapsed = user - *olduser; + systemelapsed = system - *oldsystem; + idleelapsed = idle - *oldidle; + totalelapsed = userelapsed + systemelapsed + idleelapsed; + + if (usepercent) { + fprintf(stderr, "%1.02f%% user %1.02f%% sys %1.02f%% idle\n", + ((double)userelapsed * 100)/totalelapsed, + ((double)systemelapsed * 100)/totalelapsed, + ((double)idleelapsed * 100)/totalelapsed); + } else { + int ncpu; + + kret = get_processor_count(&ncpu); + if (kret) + errx(1, "Error getting processor count: %s (%d)", mach_error_string(kret), kret); + + fprintf(stderr, "%1.02f real %1.02f user %1.02f sys\n", + ((double)totalelapsed) / 1000 /* ms per sec */ / ncpu, + ((double)userelapsed) / 1000, + ((double)systemelapsed) / 1000); + } + *olduser = user; + *oldsystem = system; + *oldidle = idle; +} + +static void +do_piddifftime(bool usepercent, int pid, uint64_t *old_pid_user, uint64_t *old_pid_system, uint64_t *old_pid_time) +{ + uint64_t pid_user, pid_system, pid_time; + uint64_t userelapsed, systemelapsed, totalelapsed; + struct proc_taskinfo info; + int size; + + size = proc_pidinfo(pid, PROC_PIDTASKINFO, 0, &info, sizeof(info)); + if (size == PROC_PIDTASKINFO_SIZE) { + pid_user = info.pti_total_user; + pid_system = info.pti_total_system; + } else { + fprintf(stderr, "Error in proc_pidinfo(): %s", + strerror(errno)); + exit(1); + } + + pid_time = mach_absolute_time(); + + userelapsed = pid_user - *old_pid_user; + systemelapsed = pid_system - *old_pid_system; + totalelapsed = pid_time - *old_pid_time; + + if (usepercent) { + fprintf(stderr, "Pid %d: %1.02f%% user %1.02f%% sys\n", + pid, + ((double)userelapsed * 100)/totalelapsed, + ((double)systemelapsed * 100)/totalelapsed); + } else { + fprintf(stderr, "Pid %d: %1.02f user %1.02f sys\n", + pid, + (((double)userelapsed) * timebase_info.numer / timebase_info.denom) / 1000000000, + (((double)systemelapsed) * timebase_info.numer / timebase_info.denom) / 1000000000); + } + + *old_pid_user = pid_user; + *old_pid_system = pid_system; + *old_pid_time = pid_time; + +} + +static kern_return_t +get_processor_time(uint64_t *user, uint64_t *sys, uint64_t *idle) +{ + host_name_port_t host; + kern_return_t kret; + host_cpu_load_info_data_t host_load; + mach_msg_type_number_t count; + + host = mach_host_self(); + + count = HOST_CPU_LOAD_INFO_COUNT; + + kret = host_statistics(host, HOST_CPU_LOAD_INFO, (host_info_t)&host_load, &count); + if (kret) + return kret; + + *user = ((uint64_t)host_load.cpu_ticks[CPU_STATE_USER]) * 10 /* ms per tick */; + *sys = ((uint64_t)host_load.cpu_ticks[CPU_STATE_SYSTEM]) * 10; + *idle = ((uint64_t)host_load.cpu_ticks[CPU_STATE_IDLE]) * 10; + + return KERN_SUCCESS; +} + +static kern_return_t +get_processor_count(int *ncpu) +{ + host_name_port_t host; + kern_return_t kret; + host_basic_info_data_t hi; + mach_msg_type_number_t count; + + host = mach_host_self(); + + count = HOST_BASIC_INFO_COUNT; + + kret = host_info(host, HOST_BASIC_INFO, (host_info_t)&hi, &count); + if (kret) + return kret; + + *ncpu = hi.avail_cpus; + + return KERN_SUCCESS; +} diff --git a/shell_cmds/tee/tee.1 b/shell_cmds/tee/tee.1 new file mode 100644 index 0000000..19d2d40 --- /dev/null +++ b/shell_cmds/tee/tee.1 @@ -0,0 +1,90 @@ +.\" $NetBSD: tee.1,v 1.5 1997/10/20 00:37:11 lukem Exp $ +.\" +.\" Copyright (c) 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" the Institute of Electrical and Electronics Engineers, Inc. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)tee.1 8.1 (Berkeley) 6/6/93 +.\" +.Dd June 6, 1993 +.Dt TEE 1 +.Os +.Sh NAME +.Nm tee +.Nd pipe fitting +.Sh SYNOPSIS +.Nm +.Op Fl ai +.Op Ar file ... +.Sh DESCRIPTION +The +.Nm +utility copies standard input to standard output, +making a copy in zero or more files. +The output is unbuffered. +.Pp +The following options are available: +.Bl -tag -width Ds +.It Fl a +Append the output to the files rather than +overwriting them. +.It Fl i +Ignore the +.Dv SIGINT +signal. +.El +.Pp +The following operands are available: +.Bl -tag -width file +.It file +A pathname of an output +.Ar file . +.El +.Pp +The +.Nm +utility takes the default action for all signals, +except in the event of the +.Fl i +option. +.Pp +The +.Nm +utility exits 0 on success, and >0 if an error occurs. +.Sh STANDARDS +The +.Nm +function is expected to be +.Tn POSIX +.St -p1003.2 +compatible. diff --git a/shell_cmds/tee/tee.c b/shell_cmds/tee/tee.c new file mode 100644 index 0000000..e916d90 --- /dev/null +++ b/shell_cmds/tee/tee.c @@ -0,0 +1,156 @@ +/* $NetBSD: tee.c,v 1.6 1997/10/20 00:37:11 lukem Exp $ */ + +/* + * Copyright (c) 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +#ifndef lint +__COPYRIGHT("@(#) Copyright (c) 1988, 1993\n\ + The Regents of the University of California. All rights reserved.\n"); +#endif /* not lint */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)tee.c 8.1 (Berkeley) 6/6/93"; +#endif +__RCSID("$NetBSD: tee.c,v 1.6 1997/10/20 00:37:11 lukem Exp $"); +#endif + +#include <sys/types.h> +#include <sys/stat.h> +#include <signal.h> +#include <errno.h> +#include <fcntl.h> +#include <unistd.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <locale.h> +#include <err.h> + +typedef struct _list { + struct _list *next; + int fd; + char *name; +} LIST; +LIST *head; + +void add __P((int, char *)); +int main __P((int, char **)); + +int +main(argc, argv) + int argc; + char *argv[]; +{ + LIST *p; + int n, fd, rval, wval; + char *bp; + int append, ch, exitval; + char *buf; +#define BSIZE (8 * 1024) + + setlocale(LC_ALL, ""); + + append = 0; + while ((ch = getopt(argc, argv, "ai")) != -1) + switch((char)ch) { + case 'a': + append = 1; + break; + case 'i': + (void)signal(SIGINT, SIG_IGN); + break; + case '?': + default: + (void)fprintf(stderr, "usage: tee [-ai] [file ...]\n"); + exit(1); + } + argv += optind; + argc -= optind; + + if ((buf = malloc((size_t)BSIZE)) == NULL) + err(1, "malloc"); + + add(STDOUT_FILENO, "stdout"); + + for (exitval = 0; *argv; ++argv) + if ((fd = open(*argv, append ? O_WRONLY|O_CREAT|O_APPEND : + O_WRONLY|O_CREAT|O_TRUNC, DEFFILEMODE)) < 0) { + warn("%s", *argv); + exitval = 1; + } else + add(fd, *argv); + + while ((rval = read(STDIN_FILENO, buf, BSIZE)) > 0) + for (p = head; p; p = p->next) { + n = rval; + bp = buf; + do { + if ((wval = write(p->fd, bp, n)) == -1) { + warn("%s", p->name); + exitval = 1; + break; + } + bp += wval; + } while (n -= wval); + } + if (rval < 0) { + warn("read"); + exitval = 1; + } + + for (p = head; p; p = p->next) { + if (close(p->fd) == -1) { + warn("%s", p->name); + exitval = 1; + } + } + + exit(exitval); +} + +void +add(fd, name) + int fd; + char *name; +{ + LIST *p; + + if ((p = malloc((size_t)sizeof(LIST))) == NULL) + err(1, "malloc"); + p->fd = fd; + p->name = name; + p->next = head; + head = p; +} diff --git a/shell_cmds/test/[.1 b/shell_cmds/test/[.1 new file mode 100644 index 0000000..84e0568 --- /dev/null +++ b/shell_cmds/test/[.1 @@ -0,0 +1 @@ +.so man1/test.1 diff --git a/shell_cmds/test/test.1 b/shell_cmds/test/test.1 new file mode 100644 index 0000000..136ee57 --- /dev/null +++ b/shell_cmds/test/test.1 @@ -0,0 +1,390 @@ +.\"- +.\" Copyright (c) 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" the Institute of Electrical and Electronics Engineers, Inc. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)test.1 8.1 (Berkeley) 5/31/93 +.\" $FreeBSD$ +.\" +.Dd June 1, 2013 +.Dt TEST 1 +.Os +.Sh NAME +.Nm test , +.Nm \&[ +.Nd condition evaluation utility +.Sh SYNOPSIS +.Nm +.Ar expression +.Nm \&[ +.Ar expression Cm \&] +.Sh DESCRIPTION +The +.Nm +utility evaluates the expression and, if it evaluates +to true, returns a zero (true) exit status; otherwise +it returns 1 (false). +If there is no expression, +.Nm +also +returns 1 (false). +.Pp +All operators and flags are separate arguments to the +.Nm +utility. +.Pp +The following primaries are used to construct expression: +.Bl -tag -width Ar +.It Fl b Ar file +True if +.Ar file +exists and is a block special +file. +.It Fl c Ar file +True if +.Ar file +exists and is a character +special file. +.It Fl d Ar file +True if +.Ar file +exists and is a directory. +.It Fl e Ar file +True if +.Ar file +exists (regardless of type). +.It Fl f Ar file +True if +.Ar file +exists and is a regular file. +.It Fl g Ar file +True if +.Ar file +exists and its set group ID flag +is set. +.It Fl h Ar file +True if +.Ar file +exists and is a symbolic link. +This operator is retained for compatibility with previous versions of +this program. +Do not rely on its existence; use +.Fl L +instead. +.It Fl k Ar file +True if +.Ar file +exists and its sticky bit is set. +.It Fl n Ar string +True if the length of +.Ar string +is nonzero. +.It Fl p Ar file +True if +.Ar file +is a named pipe +.Pq Tn FIFO . +.It Fl r Ar file +True if +.Ar file +exists and is readable. +.It Fl s Ar file +True if +.Ar file +exists and has a size greater +than zero. +.It Fl t Ar file_descriptor +True if the file whose file descriptor number +is +.Ar file_descriptor +is open and is associated with a terminal. +.It Fl u Ar file +True if +.Ar file +exists and its set user ID flag +is set. +.It Fl w Ar file +True if +.Ar file +exists and is writable. +True +indicates only that the write flag is on. +The file is not writable on a read-only file +system even if this test indicates true. +.It Fl x Ar file +True if +.Ar file +exists and is executable. +True +indicates only that the execute flag is on. +If +.Ar file +is a directory, true indicates that +.Ar file +can be searched. +.It Fl z Ar string +True if the length of +.Ar string +is zero. +.It Fl L Ar file +True if +.Ar file +exists and is a symbolic link. +.It Fl O Ar file +True if +.Ar file +exists and its owner matches the effective user id of this process. +.It Fl G Ar file +True if +.Ar file +exists and its group matches the effective group id of this process. +.It Fl S Ar file +True if +.Ar file +exists and is a socket. +.It Ar file1 Fl nt Ar file2 +True if +.Ar file1 +exists and is newer than +.Ar file2 . +.It Ar file1 Fl ot Ar file2 +True if +.Ar file1 +exists and is older than +.Ar file2 . +.It Ar file1 Fl ef Ar file2 +True if +.Ar file1 +and +.Ar file2 +exist and refer to the same file. +.It Ar string +True if +.Ar string +is not the null +string. +.It Ar s1 Cm = Ar s2 +True if the strings +.Ar s1 +and +.Ar s2 +are identical. +.It Ar s1 Cm != Ar s2 +True if the strings +.Ar s1 +and +.Ar s2 +are not identical. +.It Ar s1 Cm < Ar s2 +True if string +.Ar s1 +comes before +.Ar s2 +based on the binary value of their characters. +.It Ar s1 Cm > Ar s2 +True if string +.Ar s1 +comes after +.Ar s2 +based on the binary value of their characters. +.It Ar n1 Fl eq Ar n2 +True if the integers +.Ar n1 +and +.Ar n2 +are algebraically +equal. +.It Ar n1 Fl ne Ar n2 +True if the integers +.Ar n1 +and +.Ar n2 +are not +algebraically equal. +.It Ar n1 Fl gt Ar n2 +True if the integer +.Ar n1 +is algebraically +greater than the integer +.Ar n2 . +.It Ar n1 Fl ge Ar n2 +True if the integer +.Ar n1 +is algebraically +greater than or equal to the integer +.Ar n2 . +.It Ar n1 Fl lt Ar n2 +True if the integer +.Ar n1 +is algebraically less +than the integer +.Ar n2 . +.It Ar n1 Fl le Ar n2 +True if the integer +.Ar n1 +is algebraically less +than or equal to the integer +.Ar n2 . +.El +.Pp +If +.Ar file +is a symbolic link, +.Nm +will fully dereference it and then evaluate the expression +against the file referenced, except for the +.Fl h +and +.Fl L +primaries. +.Pp +These primaries can be combined with the following operators: +.Bl -tag -width Ar +.It Cm \&! Ar expression +True if +.Ar expression +is false. +.It Ar expression1 Fl a Ar expression2 +True if both +.Ar expression1 +and +.Ar expression2 +are true. +.It Ar expression1 Fl o Ar expression2 +True if either +.Ar expression1 +or +.Ar expression2 +are true. +.It Cm \&( Ar expression Cm \&) +True if expression is true. +.El +.Pp +The +.Fl a +operator has higher precedence than the +.Fl o +operator. +.Pp +Some shells may provide a builtin +.Nm +command which is similar or identical to this utility. +Consult the +.Xr builtin 1 +manual page. +.Sh GRAMMAR AMBIGUITY +The +.Nm +grammar is inherently ambiguous. +In order to assure a degree of consistency, +the cases described in the +.St -p1003.2 , +section D11.2/4.62.4, standard +are evaluated consistently according to the rules specified in the +standards document. +All other cases are subject to the ambiguity in the +command semantics. +.Pp +In particular, only expressions containing +.Fl a , +.Fl o , +.Cm \&( +or +.Cm \&) +can be ambiguous. +.Sh EXIT STATUS +The +.Nm +utility exits with one of the following values: +.Bl -tag -width indent +.It 0 +expression evaluated to true. +.It 1 +expression evaluated to false or expression was +missing. +.It >1 +An error occurred. +.El +.Sh EXAMPLES +Implement +.Li test FILE1 -nt FILE2 +using only +.Tn POSIX +functionality: +.Pp +.Dl test -n \&"$(find -L -- FILE1 -prune -newer FILE2 2>/dev/null)\&" +.Pp +This can be modified using non-standard +.Xr find 1 +primaries like +.Cm -newerca +to compare other timestamps. +.Sh COMPATIBILITY +For compatibility with some other implementations, +the +.Cm = +primary can be substituted with +.Cm == +with the same meaning. +.Sh SEE ALSO +.Xr builtin 1 , +.Xr expr 1 , +.Xr find 1 , +.Xr sh 1 , +.Xr stat 1 , +.Xr symlink 7 +.Sh STANDARDS +The +.Nm +utility implements a superset of the +.St -p1003.2 +specification. +The primaries +.Cm < , +.Cm == , +.Cm > , +.Fl ef , +.Fl nt , +.Fl ot , +.Fl G , +and +.Fl O +are extensions. +.Sh BUGS +Both sides are always evaluated in +.Fl a +and +.Fl o . +For instance, the writable status of +.Pa file +will be tested by the following command even though the former expression +indicated false, which results in a gratuitous access to the file system: +.Dl "[ -z abc -a -w file ]" +To avoid this, write +.Dl "[ -z abc ] && [ -w file ]" diff --git a/shell_cmds/test/test.c b/shell_cmds/test/test.c new file mode 100644 index 0000000..14bd3a3 --- /dev/null +++ b/shell_cmds/test/test.c @@ -0,0 +1,611 @@ +/* $NetBSD: test.c,v 1.21 1999/04/05 09:48:38 kleink Exp $ */ + +/*- + * test(1); version 7-like -- author Erik Baalbergen + * modified by Eric Gisin to be used as built-in. + * modified by Arnold Robbins to add SVR3 compatibility + * (-x -c -b -p -u -g -k) plus Korn's -L -nt -ot -ef and new -S (socket). + * modified by J.T. Conklin for NetBSD. + * + * This program is in the Public Domain. + */ +/* + * Important: This file is used both as a standalone program /bin/test and + * as a builtin for /bin/sh (#define SHELL). + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/types.h> +#include <sys/stat.h> + +#include <ctype.h> +#include <err.h> +#include <errno.h> +#ifdef __APPLE__ +#include <fcntl.h> +#endif /* __APPLE__ */ +#include <inttypes.h> +#include <limits.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#ifdef __APPLE__ +#define eaccess(path, mode) faccessat(AT_FDCWD, path, mode, AT_EACCESS) +#define st_mtim st_mtimespec +#endif /* __APPLE__ */ + +#ifdef SHELL +#define main testcmd +#include "bltin/bltin.h" +#else +#include <locale.h> + +static void error(const char *, ...) __dead2 __printf0like(1, 2); + +static void +error(const char *msg, ...) +{ + va_list ap; + va_start(ap, msg); + verrx(2, msg, ap); + /*NOTREACHED*/ + va_end(ap); +} +#endif + +/* test(1) accepts the following grammar: + oexpr ::= aexpr | aexpr "-o" oexpr ; + aexpr ::= nexpr | nexpr "-a" aexpr ; + nexpr ::= primary | "!" primary + primary ::= unary-operator operand + | operand binary-operator operand + | operand + | "(" oexpr ")" + ; + unary-operator ::= "-r"|"-w"|"-x"|"-f"|"-d"|"-c"|"-b"|"-p"| + "-u"|"-g"|"-k"|"-s"|"-t"|"-z"|"-n"|"-o"|"-O"|"-G"|"-L"|"-S"; + + binary-operator ::= "="|"!="|"-eq"|"-ne"|"-ge"|"-gt"|"-le"|"-lt"| + "-nt"|"-ot"|"-ef"; + operand ::= <any legal UNIX file name> +*/ + +enum token_types { + UNOP = 0x100, + BINOP = 0x200, + BUNOP = 0x300, + BBINOP = 0x400, + PAREN = 0x500 +}; + +enum token { + EOI, + OPERAND, + FILRD = UNOP + 1, + FILWR, + FILEX, + FILEXIST, + FILREG, + FILDIR, + FILCDEV, + FILBDEV, + FILFIFO, + FILSOCK, + FILSYM, + FILGZ, + FILTT, + FILSUID, + FILSGID, + FILSTCK, + STREZ, + STRNZ, + FILUID, + FILGID, + FILNT = BINOP + 1, + FILOT, + FILEQ, + STREQ, + STRNE, + STRLT, + STRGT, + INTEQ, + INTNE, + INTGE, + INTGT, + INTLE, + INTLT, + UNOT = BUNOP + 1, + BAND = BBINOP + 1, + BOR, + LPAREN = PAREN + 1, + RPAREN +}; + +#define TOKEN_TYPE(token) ((token) & 0xff00) + +static struct t_op { + char op_text[4]; + short op_num; +} const ops [] = { + {"-r", FILRD}, + {"-w", FILWR}, + {"-x", FILEX}, + {"-e", FILEXIST}, + {"-f", FILREG}, + {"-d", FILDIR}, + {"-c", FILCDEV}, + {"-b", FILBDEV}, + {"-p", FILFIFO}, + {"-u", FILSUID}, + {"-g", FILSGID}, + {"-k", FILSTCK}, + {"-s", FILGZ}, + {"-t", FILTT}, + {"-z", STREZ}, + {"-n", STRNZ}, + {"-h", FILSYM}, /* for backwards compat */ + {"-O", FILUID}, + {"-G", FILGID}, + {"-L", FILSYM}, + {"-S", FILSOCK}, + {"=", STREQ}, + {"==", STREQ}, + {"!=", STRNE}, + {"<", STRLT}, + {">", STRGT}, + {"-eq", INTEQ}, + {"-ne", INTNE}, + {"-ge", INTGE}, + {"-gt", INTGT}, + {"-le", INTLE}, + {"-lt", INTLT}, + {"-nt", FILNT}, + {"-ot", FILOT}, + {"-ef", FILEQ}, + {"!", UNOT}, + {"-a", BAND}, + {"-o", BOR}, + {"(", LPAREN}, + {")", RPAREN}, + {"", 0} +}; + +static int nargc; +static char **t_wp; +static int parenlevel; + +static int aexpr(enum token); +static int binop(enum token); +static int equalf(const char *, const char *); +static int filstat(char *, enum token); +static int getn(const char *); +static intmax_t getq(const char *); +static int intcmp(const char *, const char *); +static int isunopoperand(void); +static int islparenoperand(void); +static int isrparenoperand(void); +static int newerf(const char *, const char *); +static int nexpr(enum token); +static int oexpr(enum token); +static int olderf(const char *, const char *); +static int primary(enum token); +static void syntax(const char *, const char *); +static enum token t_lex(char *); + +int +main(int argc, char **argv) +{ + int res; + char *p; + + /* radar:4689479 */ + if (argc == 0) + return 1; + + if ((p = strrchr(argv[0], '/')) == NULL) + p = argv[0]; + else + p++; + if (strcmp(p, "[") == 0) { + if (strcmp(argv[--argc], "]") != 0) + error("missing ]"); + argv[argc] = NULL; + } + + /* no expression => false */ + if (--argc <= 0) + return 1; + +#ifndef SHELL + (void)setlocale(LC_CTYPE, ""); +#endif + nargc = argc; + t_wp = &argv[1]; + parenlevel = 0; + if (nargc == 4 && strcmp(*t_wp, "!") == 0) { + /* Things like ! "" -o x do not fit in the normal grammar. */ + --nargc; + ++t_wp; + res = oexpr(t_lex(*t_wp)); + } else + res = !oexpr(t_lex(*t_wp)); + + if (--nargc > 0) + syntax(*t_wp, "unexpected operator"); + + return res; +} + +static void +syntax(const char *op, const char *msg) +{ + + if (op && *op) + error("%s: %s", op, msg); + else + error("%s", msg); +} + +static int +oexpr(enum token n) +{ + int res; + + res = aexpr(n); + if (t_lex(nargc > 0 ? (--nargc, *++t_wp) : NULL) == BOR) + return oexpr(t_lex(nargc > 0 ? (--nargc, *++t_wp) : NULL)) || + res; + t_wp--; + nargc++; + return res; +} + +static int +aexpr(enum token n) +{ + int res; + + res = nexpr(n); + if (t_lex(nargc > 0 ? (--nargc, *++t_wp) : NULL) == BAND) + return aexpr(t_lex(nargc > 0 ? (--nargc, *++t_wp) : NULL)) && + res; + t_wp--; + nargc++; + return res; +} + +static int +nexpr(enum token n) +{ + if (n == UNOT) + return !nexpr(t_lex(nargc > 0 ? (--nargc, *++t_wp) : NULL)); + return primary(n); +} + +static int +primary(enum token n) +{ + enum token nn; + int res; + + if (n == EOI) + return 0; /* missing expression */ + if (n == LPAREN) { + parenlevel++; + if ((nn = t_lex(nargc > 0 ? (--nargc, *++t_wp) : NULL)) == + RPAREN) { + parenlevel--; + return 0; /* missing expression */ + } + res = oexpr(nn); + if (t_lex(nargc > 0 ? (--nargc, *++t_wp) : NULL) != RPAREN) + syntax(NULL, "closing paren expected"); + parenlevel--; + return res; + } + if (TOKEN_TYPE(n) == UNOP) { + /* unary expression */ + if (--nargc == 0) + syntax(NULL, "argument expected"); /* impossible */ + switch (n) { + case STREZ: + return strlen(*++t_wp) == 0; + case STRNZ: + return strlen(*++t_wp) != 0; + case FILTT: + return isatty(getn(*++t_wp)); + default: + return filstat(*++t_wp, n); + } + } + + nn = t_lex(nargc > 0 ? t_wp[1] : NULL); + if (TOKEN_TYPE(nn) == BINOP) + return binop(nn); + + return strlen(*t_wp) > 0; +} + +static int +binop(enum token n) +{ + const char *opnd1, *op, *opnd2; + + opnd1 = *t_wp; + op = nargc > 0 ? (--nargc, *++t_wp) : NULL; + + if ((opnd2 = nargc > 0 ? (--nargc, *++t_wp) : NULL) == NULL) + syntax(op, "argument expected"); + + switch (n) { + case STREQ: + return strcmp(opnd1, opnd2) == 0; + case STRNE: + return strcmp(opnd1, opnd2) != 0; + case STRLT: + return strcmp(opnd1, opnd2) < 0; + case STRGT: + return strcmp(opnd1, opnd2) > 0; + case INTEQ: + return intcmp(opnd1, opnd2) == 0; + case INTNE: + return intcmp(opnd1, opnd2) != 0; + case INTGE: + return intcmp(opnd1, opnd2) >= 0; + case INTGT: + return intcmp(opnd1, opnd2) > 0; + case INTLE: + return intcmp(opnd1, opnd2) <= 0; + case INTLT: + return intcmp(opnd1, opnd2) < 0; + case FILNT: + return newerf (opnd1, opnd2); + case FILOT: + return olderf (opnd1, opnd2); + case FILEQ: + return equalf (opnd1, opnd2); + default: + abort(); + /* NOTREACHED */ + } +} + +static int +filstat(char *nm, enum token mode) +{ + struct stat s; + + if (mode == FILSYM ? lstat(nm, &s) : stat(nm, &s)) + return 0; + + switch (mode) { + case FILRD: + return (eaccess(nm, R_OK) == 0); + case FILWR: + return (eaccess(nm, W_OK) == 0); + case FILEX: + /* XXX work around eaccess(2) false positives for superuser */ + if (eaccess(nm, X_OK) != 0) + return 0; + if (S_ISDIR(s.st_mode) || geteuid() != 0) + return 1; + return (s.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) != 0; + case FILEXIST: + return (eaccess(nm, F_OK) == 0); + case FILREG: + return S_ISREG(s.st_mode); + case FILDIR: + return S_ISDIR(s.st_mode); + case FILCDEV: + return S_ISCHR(s.st_mode); + case FILBDEV: + return S_ISBLK(s.st_mode); + case FILFIFO: + return S_ISFIFO(s.st_mode); + case FILSOCK: + return S_ISSOCK(s.st_mode); + case FILSYM: + return S_ISLNK(s.st_mode); + case FILSUID: + return (s.st_mode & S_ISUID) != 0; + case FILSGID: + return (s.st_mode & S_ISGID) != 0; + case FILSTCK: + return (s.st_mode & S_ISVTX) != 0; + case FILGZ: + return s.st_size > (off_t)0; + case FILUID: + return s.st_uid == geteuid(); + case FILGID: + return s.st_gid == getegid(); + default: + return 1; + } +} + +static enum token +t_lex(char *s) +{ + struct t_op const *op = ops; + + if (s == 0) { + return EOI; + } + while (*op->op_text) { + if (strcmp(s, op->op_text) == 0) { + if (((TOKEN_TYPE(op->op_num) == UNOP || + TOKEN_TYPE(op->op_num) == BUNOP) + && isunopoperand()) || + (op->op_num == LPAREN && islparenoperand()) || + (op->op_num == RPAREN && isrparenoperand())) + break; + return op->op_num; + } + op++; + } + return OPERAND; +} + +static int +isunopoperand(void) +{ + struct t_op const *op = ops; + char *s; + char *t; + + if (nargc == 1) + return 1; + s = *(t_wp + 1); + if (nargc == 2) + return parenlevel == 1 && strcmp(s, ")") == 0; + t = *(t_wp + 2); + while (*op->op_text) { + if (strcmp(s, op->op_text) == 0) + return TOKEN_TYPE(op->op_num) == BINOP && + (parenlevel == 0 || t[0] != ')' || t[1] != '\0'); + op++; + } + return 0; +} + +static int +islparenoperand(void) +{ + struct t_op const *op = ops; + char *s; + + if (nargc == 1) + return 1; + s = *(t_wp + 1); + if (nargc == 2) + return parenlevel == 1 && strcmp(s, ")") == 0; + if (nargc != 3) + return 0; + while (*op->op_text) { + if (strcmp(s, op->op_text) == 0) + return TOKEN_TYPE(op->op_num) == BINOP; + op++; + } + return 0; +} + +static int +isrparenoperand(void) +{ + char *s; + + if (nargc == 1) + return 0; + s = *(t_wp + 1); + if (nargc == 2) + return parenlevel == 1 && strcmp(s, ")") == 0; + return 0; +} + +/* atoi with error detection */ +static int +getn(const char *s) +{ + char *p; + long r; + + errno = 0; + r = strtol(s, &p, 10); + + if (s == p) + error("%s: bad number", s); + + if (errno != 0) + error((errno == EINVAL) ? "%s: bad number" : + "%s: out of range", s); + + while (isspace((unsigned char)*p)) + p++; + + if (*p) + error("%s: bad number", s); + + return (int) r; +} + +/* atoi with error detection and 64 bit range */ +static intmax_t +getq(const char *s) +{ + char *p; + intmax_t r; + + errno = 0; + r = strtoimax(s, &p, 10); + + if (s == p) + error("%s: bad number", s); + + if (errno != 0) + error((errno == EINVAL) ? "%s: bad number" : + "%s: out of range", s); + + while (isspace((unsigned char)*p)) + p++; + + if (*p) + error("%s: bad number", s); + + return r; +} + +static int +intcmp (const char *s1, const char *s2) +{ + intmax_t q1, q2; + + + q1 = getq(s1); + q2 = getq(s2); + + if (q1 > q2) + return 1; + + if (q1 < q2) + return -1; + + return 0; +} + +static int +newerf (const char *f1, const char *f2) +{ + struct stat b1, b2; + + if (stat(f1, &b1) != 0 || stat(f2, &b2) != 0) + return 0; + + if (b1.st_mtim.tv_sec > b2.st_mtim.tv_sec) + return 1; + if (b1.st_mtim.tv_sec < b2.st_mtim.tv_sec) + return 0; + + return (b1.st_mtim.tv_nsec > b2.st_mtim.tv_nsec); +} + +static int +olderf (const char *f1, const char *f2) +{ + return (newerf(f2, f1)); +} + +static int +equalf (const char *f1, const char *f2) +{ + struct stat b1, b2; + + return (stat (f1, &b1) == 0 && + stat (f2, &b2) == 0 && + b1.st_dev == b2.st_dev && + b1.st_ino == b2.st_ino); +} diff --git a/shell_cmds/test/test.plist.part b/shell_cmds/test/test.plist.part new file mode 100644 index 0000000..e645422 --- /dev/null +++ b/shell_cmds/test/test.plist.part @@ -0,0 +1,19 @@ + <dict> + <key>OpenSourceProject</key> + <string>test</string> + <key>OpenSourceVersion</key> + <string>2013-12-05</string> + <key>OpenSourceWebsiteURL</key> + <string>http://svnweb.freebsd.org/base/head/bin/test/</string> + <key>OpenSourceSCM</key> + <string>svn co http://svn.freebsd.org/base/head/bin/test/</string> + <key>OpenSourceImportDate</key> + <string>2015-07-31</string> + <key>OpenSourceModifications</key> + <array> + <string>4689479</string> + <string>compatibility for eaccess, st_mtim</string> + </array> + <key>OpenSourceLicense</key> + <string>bsd</string> + </dict> diff --git a/shell_cmds/test/tests/Makefile b/shell_cmds/test/tests/Makefile new file mode 100644 index 0000000..5ee337a --- /dev/null +++ b/shell_cmds/test/tests/Makefile @@ -0,0 +1,15 @@ +# $FreeBSD$ + +.include <bsd.own.mk> + +TESTSDIR= ${TESTSBASE}/bin/test + +TAP_TESTS_SH= legacy_test +# Some tests in here are silently not run when the tests are executed as +# root. Explicitly tell Kyua to drop privileges. +# +# TODO(jmmv): Kyua needs to do this by default, not only when explicitly +# requested. See https://code.google.com/p/kyua/issues/detail?id=6 +TEST_METADATA.legacy_test+= required_user="unprivileged" + +.include <bsd.test.mk> diff --git a/shell_cmds/test/tests/legacy_test.sh b/shell_cmds/test/tests/legacy_test.sh new file mode 100644 index 0000000..21c74a8 --- /dev/null +++ b/shell_cmds/test/tests/legacy_test.sh @@ -0,0 +1,203 @@ +#!/bin/sh + +#- +# Copyright (c) June 1996 Wolfram Schneider <wosch@FreeBSD.org>. Berlin. +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions +# are met: +# 1. Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# 2. Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND +# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +# SUCH DAMAGE. + +# +# TEST.sh - check if test(1) or builtin test works +# +# $FreeBSD$ + +enable -n test + +# force a specified test program, e.g. `env test=/bin/test sh regress.sh' +: ${test=test} + +t () +{ + # $1 -> exit code + # $2 -> $test expression + + count=$((count+1)) + printf "[BEGIN] test $count\n" + # check for syntax errors + syntax="`eval $test $2 2>&1`" + ret=$? + if test -n "$syntax"; then + printf "not ok %s - (syntax error)\n" "$count $2" + printf "[FAIL] test $count\n" + elif [ "$ret" != "$1" ]; then + printf "not ok %s - (got $ret, expected $1)\n" "$count $2" + printf "[FAIL] test $count\n" + else + printf "ok %s\n" "$count $2" + printf "[PASS] test $count\n" + fi +} + +count=0 +echo "1..130" +printf "[TEST] shell_cmds: test\n" + +t 0 'b = b' +t 0 'b == b' +t 1 'b != b' +t 0 '\( b = b \)' +t 0 '\( b == b \)' +t 1 '! \( b = b \)' +t 1 '! \( b == b \)' +t 1 '! -f /etc/passwd' + +t 0 '-h = -h' +t 0 '-o = -o' +t 1 '-f = h' +t 1 '-h = f' +t 1 '-o = f' +t 1 'f = -o' +t 0 '\( -h = -h \)' +t 1 '\( a = -h \)' +t 1 '\( -f = h \)' +t 0 '-h = -h -o a' +t 0 '\( -h = -h \) -o 1' +t 0 '-h = -h -o -h = -h' +t 0 '\( -h = -h \) -o \( -h = -h \)' +t 0 'roedelheim = roedelheim' +t 1 'potsdam = berlin-dahlem' + +t 0 '-d /' +t 0 '-d / -a a != b' +t 1 '-z "-z"' +t 0 '-n -n' + +t 0 '0' +t 0 '\( 0 \)' +t 0 '-E' +t 0 '-X -a -X' +t 0 '-XXX' +t 0 '\( -E \)' +t 0 'true -o X' +t 0 'true -o -X' +t 0 '\( \( \( a = a \) -o 1 \) -a 1 \) -a true' +t 1 '-h /' +t 0 '-r /' +t 1 '-w /' +t 0 '-x /bin/sh' +t 0 '-c /dev/null' +t 0 '-f /etc/passwd' +t 0 '-s /etc/passwd' + +t 1 '! \( 700 -le 1000 -a -n "1" -a "20" = "20" \)' +t 0 '100 -eq 100' +t 0 '100 -lt 200' +t 1 '1000 -lt 200' +t 0 '1000 -gt 200' +t 0 '1000 -ge 200' +t 0 '1000 -ge 1000' +t 1 '2 -ne 2' +t 0 '0 -eq 0' +t 1 '-5 -eq 5' +t 0 '\( 0 -eq 0 \)' +t 1 '1 -eq 0 -o a = a -a 1 -eq 0 -o a = aa' + +t 1 '"" -o ""' +t 1 '"" -a ""' +t 1 '"a" -a ""' +t 0 '"a" -a ! ""' +t 1 '""' +t 0 '! ""' + +t 0 '!' +t 0 '\(' +t 0 '\)' + +t 1 '\( = \)' +t 0 '\( != \)' +t 0 '\( ! \)' +t 0 '\( \( \)' +t 0 '\( \) \)' +t 0 '! = !' +t 1 '! != !' +t 1 '-n = \)' +t 0 '! != \)' +t 1 '! = a' +t 0 '! != -n' +t 0 '! -c /etc/passwd' + +t 1 '! = = =' +t 0 '! = = \)' +t 0 '! "" -o ""' +t 1 '! "x" -o ""' +t 1 '! "" -o "x"' +t 1 '! "x" -o "x"' +t 0 '\( -f /etc/passwd \)' +t 0 '\( ! "" \)' +t 1 '\( ! -e \)' + +t 0 '0 -eq 0 -a -d /' +t 0 '-s = "" -o "" = ""' +t 0 '"" = "" -o -s = ""' +t 1 '-s = "" -o -s = ""' +t 0 '-z x -o x = "#" -o x = x' +t 1 '-z y -o y = "#" -o y = x' +t 0 '0 -ne 0 -o ! -f /' +t 0 '1 -ne 0 -o ! -f /etc/passwd' +t 1 '0 -ne 0 -o ! -f /etc/passwd' + +t 0 '-n =' +t 1 '-z =' +t 1 '! =' +t 0 '-n -eq' +t 1 '-z -eq' +t 1 '! -eq' +t 0 '-n -a' +t 1 '-z -a' +t 1 '! -a' +t 0 '-n -o' +t 1 '-z -o' +t 1 '! -o' +t 1 '! -n =' +t 0 '! -z =' +t 0 '! ! =' +t 1 '! -n -eq' +t 0 '! -z -eq' +t 0 '! ! -eq' +t 1 '! -n -a' +t 0 '! -z -a' +t 0 '! ! -a' +t 1 '! -n -o' +t 0 '! -z -o' +t 0 '! ! -o' +t 0 '\( -n = \)' +t 1 '\( -z = \)' +t 1 '\( ! = \)' +t 0 '\( -n -eq \)' +t 1 '\( -z -eq \)' +t 1 '\( ! -eq \)' +t 0 '\( -n -a \)' +t 1 '\( -z -a \)' +t 1 '\( ! -a \)' +t 0 '\( -n -o \)' +t 1 '\( -z -o \)' +t 1 '\( ! -o \)' diff --git a/shell_cmds/tests/regress.m4 b/shell_cmds/tests/regress.m4 new file mode 100644 index 0000000..c06caa1 --- /dev/null +++ b/shell_cmds/tests/regress.m4 @@ -0,0 +1,59 @@ +# $FreeBSD: head/usr.bin/tests/regress.m4 263227 2014-03-16 08:04:06Z jmmv $ + +dnl A library of routines for doing regression tests for userland utilities. + +dnl Start up. We initialise the exit status to 0 (no failure) and change +dnl into the directory specified by our first argument, which is the +dnl directory to run the tests inside. +define(`REGRESSION_START', +TESTDIR=$1 +if [ -z "$TESTDIR" ]; then + TESTDIR=. +fi +cd $TESTDIR + +STATUS=0) + +dnl Check $? to see if we passed or failed. The first parameter is the test +dnl which passed or failed. It may be nil. +define(`REGRESSION_PASSFAIL', +if [ $? -eq 0 ]; then + echo "ok - $1 # Test detected no regression. (in $TESTDIR)" +else + STATUS=$? + echo "not ok - $1 # Test failed: regression detected. See above. (in $TESTDIR)" +fi) + +dnl An actual test. The first parameter is the test name. The second is the +dnl command/commands to execute for the actual test. Their exit status is +dnl checked. It is assumed that the test will output to stdout, and that the +dnl output to be used to check for regression will be in regress.TESTNAME.out. +define(`REGRESSION_TEST', +$2 | diff -u ${SRCDIR:-.}/regress.$1.out - +REGRESSION_PASSFAIL($1)) + +dnl A freeform regression test. Only exit status is checked. +define(`REGRESSION_TEST_FREEFORM', +$2 +REGRESSION_PASSFAIL($1)) + +dnl A regression test like REGRESSION_TEST, except only regress.out is used +dnl for checking output differences. The first argument is the command, the +dnl second argument (which may be empty) is the test name. +define(`REGRESSION_TEST_ONE', +$1 | diff -u ${SRCDIR:-.}/regress.out - +REGRESSION_PASSFAIL($2)) + +dnl A fatal error. This will exit with the given status (first argument) and +dnl print the message (second argument) prefixed with the string "FATAL :" to +dnl the error stream. +define(`REGRESSION_FATAL', +echo "Bail out! $2 (in $TESTDIR)" > /dev/stderr +exit $1) + +dnl Cleanup. Exit with the status code of the last failure. Should probably +dnl be the number of failed tests, but hey presto, this is what it does. This +dnl could also clean up potential droppings, if some forms of regression tests +dnl end up using mktemp(1) or such. +define(`REGRESSION_END', +exit $STATUS) diff --git a/shell_cmds/tests/shell_cmds.plist b/shell_cmds/tests/shell_cmds.plist new file mode 100644 index 0000000..bdeb182 --- /dev/null +++ b/shell_cmds/tests/shell_cmds.plist @@ -0,0 +1,53 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> +<plist version="1.0"> +<dict> + <key>Project</key> + <string>libutil</string> + <key>Tests</key> + <array> + <dict> + <key>Command</key> + <array> + <string>/bin/sh</string> + <string>/AppleInternal/Tests/shell_cmds/printf/legacy_test.sh</string> + </array> + <key>IgnoreOutput</key><true/> + <key>TestName</key><string>shell_cmds.printf</string> + <key>WhenToRun</key> + <array> + <string>PRESUBMISSION</string> + <string>NIGHTLY</string> + </array> + </dict> + <dict> + <key>Command</key> + <array> + <string>/bin/sh</string> + <string>/AppleInternal/Tests/shell_cmds/test/legacy_test.sh</string> + </array> + <key>TestName</key><string>shell_cmds.test</string> + <key>WhenToRun</key> + <array> + <string>PRESUBMISSION</string> + <string>NIGHTLY</string> + </array> + </dict> + <dict> + <key>Command</key> + <array> + <string>/bin/sh</string> + <string>/AppleInternal/Tests/shell_cmds/time/test_time.sh</string> + </array> + <key>TestName</key><string>shell_cmds.time</string> + <key>WhenToRun</key> + <array> + <string>PRESUBMISSION</string> + <string>NIGHTLY</string> + </array> + </dict> + </array> + <key>Timeout</key> + <integer>30</integer> +</dict> +</plist> diff --git a/shell_cmds/time/tests/test_time.sh b/shell_cmds/time/tests/test_time.sh new file mode 100755 index 0000000..6cf002d --- /dev/null +++ b/shell_cmds/time/tests/test_time.sh @@ -0,0 +1,60 @@ +#!/bin/sh + +set -o nounset + +TIME="${TIME-/usr/bin/time}" +echo "SUITE: time(1)" +what $TIME +echo + +EXIT=0 + +echo TEST: check real time + +TIME_SLEEP=`$TIME 2>&1 sleep 1 | sed -n -E 's/[ ]+([0-9]+).*/\1/p'` +TIME_STATUS=$? + +if [ "$TIME_STATUS" -ne "0" ]; then + echo FAIL: time failed with "$TIME_STATUS" + EXIT=1 +fi + +if [ "$TIME_SLEEP" -lt "0" ]; then + echo FAIL: time mis-timed sleep + EXIT=2 +fi + +MONOTONIC=`sysctl -n kern.monotonic.task_thread_counting` +if [ "$MONOTONIC" -ne "0" ]; then + echo TEST: check instructions retired + + TIME_INSTRS=`$TIME -l 2>&1 sleep 1 | sed -E -n '/instructions/p'` + if [ -z "$TIME_INSTRS" ]; then + echo FAIL: time is not showing instructions retired + EXIT=3 + fi +else + echo SKIP: check instructions retired +fi + +# NB: SIGINT and SIGQUIT work locally, but the automated test harnesses tries to +# handle those signals itself before the fork. + +echo TEST: check child SIGUSR1 + +TIME_USR1=`$TIME 2>&1 sh -c 'kill -USR1 $$ && sleep 5 && true'` +TIME_STATUS=$? +if [ "$TIME_STATUS" -eq "0" ]; then + echo FAIL: time should allow child to receive SIGUSR1 + EXIT=4 +fi + +echo TEST: check non-existent binary +TIME_NONEXIST=`$TIME 2>&1 ./this-wont-exist` +TIME_STATUS=$? +if [ "$TIME_STATUS" -ne "127" ]; then + echo FAIL: time should error when measuring a non-existent command + EXIT=5 +fi + +exit $EXIT diff --git a/shell_cmds/time/time.1 b/shell_cmds/time/time.1 new file mode 100644 index 0000000..b4e7017 --- /dev/null +++ b/shell_cmds/time/time.1 @@ -0,0 +1,116 @@ +.\" $NetBSD: time.1,v 1.7 1997/10/20 03:28:20 lukem Exp $ +.\" +.\" Copyright (c) 1980, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)time.1 8.1 (Berkeley) 6/6/93 +.\" +.Dd June 6, 1993 +.Dt TIME 1 +.Os +.Sh NAME +.Nm time +.Nd time command execution +.Sh SYNOPSIS +.Nm +.Op Fl lp +.Ar utility +.Sh DESCRIPTION +The +.Nm +utility +executes and +times +.Ar utility . +After the +.Ar utility +finishes, +.Nm +writes the total time elapsed, +the time consumed by system overhead, +and the time used to execute +.Ar utility +to the standard error stream. +Times are reported in seconds. +.Pp +Available options: +.Bl -tag -width Ds +.It Fl l +The contents of the +.Em rusage +structure are printed. +.It Fl p +The output is formatted as specified by +.St -p1003.2-92 . +.El +.Pp +Some shells may provide a builtin +.Nm +command which is similar or identical to this utility. +Consult the +.Xr builtin 1 +manual page. +.Sh DIAGNOSTICS +The +.Nm +utility shall exit with one of the following values: +.Bl -tag -width indent +.It 1-125 +An error occurred in the +.Nm +utility. +.It 126 +The +.Ar utility +was found but could not be invoked. +.It 127 +The +.Ar utility +could not be found. +.El +.Pp +Otherwise, the exit status of +.Nm +shall be that of +.Ar utility . +.Sh SEE ALSO +.Xr builtin 1 , +.Xr csh 1 , +.Xr getrusage 2 +.Sh FILES +.Bl -tag -width /usr/include/sys/resource.h -compact +.It Pa /usr/include/sys/resource.h +.El +.Sh STANDARDS +The +.Nm +utility conforms to +.St -p1003.2-92 . diff --git a/shell_cmds/time/time.c b/shell_cmds/time/time.c new file mode 100644 index 0000000..afaaed0 --- /dev/null +++ b/shell_cmds/time/time.c @@ -0,0 +1,245 @@ +/* $NetBSD: time.c,v 1.9 1997/10/20 03:28:21 lukem Exp $ */ + +/* + * Copyright (c) 1987, 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <errno.h> +#include <err.h> +#include <inttypes.h> +#include <langinfo.h> +#include <libproc.h> +#include <locale.h> +#include <sys/cdefs.h> +#include <sysexits.h> +#include <sys/types.h> +#include <sys/time.h> +#include <sys/resource.h> +#include <sys/wait.h> +#include <signal.h> +#include <stdbool.h> +#include <stdio.h> +#include <stdint.h> +#include <stdlib.h> +#include <time.h> +#include <unistd.h> + +int lflag; +int portableflag; +bool child_running = true; + +void +child_handler(int sig) +{ + child_running = false; +} + +int +main(int argc, char **argv) +{ + int pid; + int ch, status, rusage_ret = -1; + uint64_t before_ns, after_ns, duration_ns, duration_secs, duration_frac_ns; + struct rusage ru; + struct rusage_info_v4 ruinfo; + sigset_t sigmask, suspmask, origmask; + + lflag = 0; + while ((ch = getopt(argc, argv, "lp")) != -1) { + switch((char)ch) { + case 'p': + portableflag = 1; + break; + case 'l': + lflag = 1; + break; + case '?': + default: + fprintf(stderr, "usage: time [-lp] <command>\n"); + exit(1); + } + } + + if (!(argc -= optind)) { + exit(0); + } + argv += optind; + + sigemptyset(&sigmask); + /* + * Block SIGCHLD so that the check for `child_running` doesn't miss the + * handler before calling `sigsuspend` and blocking forever. + */ + sigaddset(&sigmask, SIGCHLD); + sigprocmask(SIG_BLOCK, &sigmask, &origmask); + + /* + * Ensure child signals are handled by the parent prior to fork; otherwise, + * they could be missed between the child forking and calling `sigsuspend`. + */ + (void)signal(SIGCHLD, child_handler); + + sigemptyset(&suspmask); + + before_ns = clock_gettime_nsec_np(CLOCK_UPTIME_RAW); + /* + * NB: Don't add anything between these two lines -- measurement is + * happening now. + */ + switch (pid = vfork()) { + case -1: /* error */ + err(EX_OSERR, "time"); + __builtin_unreachable(); + case 0: /* child */ + /* + * Allow the child to respond to signals by resetting to the original + * signal handling behavior. + */ + (void)sigprocmask(SIG_SETMASK, &origmask, NULL); + execvp(*argv, argv); + perror(*argv); + _exit((errno == ENOENT) ? 127 : 126); + __builtin_unreachable(); + default: /* parent */ + break; + } + + /* + * Let the child handle signals that normally exit. + */ + (void)signal(SIGINT, SIG_IGN); + (void)signal(SIGQUIT, SIG_IGN); + + while (child_running) { + /* + * This would be racy, but SIGCHLD is blocked above (as part of + * `sigmask`. + */ + sigsuspend(&suspmask); + } + /* + * NB: Minimize what's added between these statements to preserve the + * accuracy of the time measurement. + */ + after_ns = clock_gettime_nsec_np(CLOCK_UPTIME_RAW); + if (lflag) { + rusage_ret = proc_pid_rusage(pid, RUSAGE_INFO_V4, (void **)&ruinfo); + } + while (wait3(&status, 0, &ru) != pid) { + } + if (!WIFEXITED(status)) { + fprintf(stderr, "Command terminated abnormally.\n"); + } + duration_ns = after_ns - before_ns; + duration_secs = duration_ns / (1000 * 1000 * 1000); + duration_frac_ns = duration_ns - (duration_secs * 1000 * 1000 * 1000); + + if (portableflag) { + char *radix = NULL; + + setlocale(LC_ALL, ""); + + radix = nl_langinfo(RADIXCHAR); + if (!radix || radix[0] == '\0') { + radix = "."; + } + + fprintf(stderr, "real %9" PRIu64 "%s%02" PRIu64 "\n", + duration_secs, radix, duration_frac_ns / (10 * 1000 * 1000)); + fprintf(stderr, "user %9ld%s%02ld\n", + (long)ru.ru_utime.tv_sec, radix, (long)ru.ru_utime.tv_usec/10000); + fprintf(stderr, "sys %9ld%s%02ld\n", + (long)ru.ru_stime.tv_sec, radix, (long)ru.ru_stime.tv_usec/10000); + } else { + fprintf(stderr, "%9" PRIu64 ".%02" PRIu64 " real ", + duration_secs, duration_frac_ns / (10 * 1000 * 1000)); + fprintf(stderr, "%9ld.%02ld user ", + (long)ru.ru_utime.tv_sec, (long)ru.ru_utime.tv_usec/10000); + fprintf(stderr, "%9ld.%02ld sys\n", + (long)ru.ru_stime.tv_sec, (long)ru.ru_stime.tv_usec/10000); + } + + if (lflag) { + int hz = 100; /* XXX */ + long ticks; + + ticks = hz * (ru.ru_utime.tv_sec + ru.ru_stime.tv_sec) + + hz * (ru.ru_utime.tv_usec + ru.ru_stime.tv_usec) / 1000000; + + fprintf(stderr, "%20ld %s\n", + ru.ru_maxrss, "maximum resident set size"); + fprintf(stderr, "%20ld %s\n", ticks ? ru.ru_ixrss / ticks : 0, + "average shared memory size"); + fprintf(stderr, "%20ld %s\n", ticks ? ru.ru_idrss / ticks : 0, + "average unshared data size"); + fprintf(stderr, "%20ld %s\n", ticks ? ru.ru_isrss / ticks : 0, + "average unshared stack size"); + fprintf(stderr, "%20ld %s\n", + ru.ru_minflt, "page reclaims"); + fprintf(stderr, "%20ld %s\n", + ru.ru_majflt, "page faults"); + fprintf(stderr, "%20ld %s\n", + ru.ru_nswap, "swaps"); + fprintf(stderr, "%20ld %s\n", + ru.ru_inblock, "block input operations"); + fprintf(stderr, "%20ld %s\n", + ru.ru_oublock, "block output operations"); + fprintf(stderr, "%20ld %s\n", + ru.ru_msgsnd, "messages sent"); + fprintf(stderr, "%20ld %s\n", + ru.ru_msgrcv, "messages received"); + fprintf(stderr, "%20ld %s\n", + ru.ru_nsignals, "signals received"); + fprintf(stderr, "%20ld %s\n", + ru.ru_nvcsw, "voluntary context switches"); + fprintf(stderr, "%20ld %s\n", + ru.ru_nivcsw, "involuntary context switches"); + + if (rusage_ret >= 0) { + if (ruinfo.ri_instructions > 0) { + fprintf(stderr, "%20" PRIu64 " %s\n", ruinfo.ri_instructions, + "instructions retired"); + } + if (ruinfo.ri_cycles > 0) { + fprintf(stderr, "%20" PRIu64 " %s\n", ruinfo.ri_cycles, + "cycles elapsed"); + } + if (ruinfo.ri_lifetime_max_phys_footprint > 0) { + fprintf(stderr, "%20" PRIu64 " %s\n", + ruinfo.ri_lifetime_max_phys_footprint, + "peak memory footprint"); + } + } + } + + exit(WIFEXITED(status) ? WEXITSTATUS(status) : EXIT_FAILURE); +} diff --git a/shell_cmds/true/true.1 b/shell_cmds/true/true.1 new file mode 100644 index 0000000..b022042 --- /dev/null +++ b/shell_cmds/true/true.1 @@ -0,0 +1,60 @@ +.\" $NetBSD: true.1,v 1.5 1997/10/20 01:00:41 lukem Exp $ +.\" +.\" Copyright (c) 1983, 1985, 1990 The Regents of the University of California. +.\" All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" the Institute of Electrical and Electronics Engineers, Inc. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" from: @(#)true.1 6.4 (Berkeley) 6/27/91 +.\" $NetBSD: true.1,v 1.5 1997/10/20 01:00:41 lukem Exp $ +.\" +.Dd June 27, 1991 +.Dt TRUE 1 +.Os +.Sh NAME +.Nm true +.Nd Return true value. +.Sh SYNOPSIS +.Nm +.Sh DESCRIPTION +The +.Nm +utility always returns with exit code zero. +.Sh SEE ALSO +.Xr csh 1 , +.Xr sh 1 , +.Xr false 1 +.Sh STANDARDS +The +.Nm +utility conforms to +.St -p1003.2-92 . diff --git a/shell_cmds/true/true.c b/shell_cmds/true/true.c new file mode 100644 index 0000000..d973485 --- /dev/null +++ b/shell_cmds/true/true.c @@ -0,0 +1,2 @@ +#include <stdlib.h> +int main () { exit(0); } diff --git a/shell_cmds/true/true.sh b/shell_cmds/true/true.sh new file mode 100644 index 0000000..20f87ef --- /dev/null +++ b/shell_cmds/true/true.sh @@ -0,0 +1,2 @@ +#! /bin/sh +exit 0 diff --git a/shell_cmds/uname/uname.1 b/shell_cmds/uname/uname.1 new file mode 100644 index 0000000..0c62868 --- /dev/null +++ b/shell_cmds/uname/uname.1 @@ -0,0 +1,87 @@ +.\" $NetBSD: uname.1,v 1.12 2005/03/27 18:41:22 peter Exp $ +.\" +.\" Copyright (c) 1990 The Regents of the University of California. +.\" All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" from: @(#)du.1 6.13 (Berkeley) 6/20/91 +.\" $NetBSD: uname.1,v 1.12 2005/03/27 18:41:22 peter Exp $ +.\" +.Dd November 9, 1998 +.Dt UNAME 1 +.Os +.Sh NAME +.Nm uname +.Nd Print operating system name +.Sh SYNOPSIS +.Nm +.Op Fl amnprsv +.Sh DESCRIPTION +The +.Nm +utility writes symbols representing one or more system characteristics +to the standard output. +.Pp +The following options are available: +.Bl -tag -width indent +.It Fl a +Behave as though all of the options +.Fl mnrsv +were specified. +.It Fl m +print the machine hardware name. +.It Fl n +print the nodename (the nodename may be a name +that the system is known by to a communications +network). +.It Fl p +print the machine processor architecture name. +.It Fl r +print the operating system release. +.It Fl s +print the operating system name. +.It Fl v +print the operating system version. +.El +.Pp +If no options are specified, +.Nm +prints the operating system name as if the +.Fl s +option had been specified. +.Sh SEE ALSO +.Xr hostname 1 , +.Xr machine 1 , +.Xr sw_vers 1 , +.Xr uname 3 +.Sh STANDARDS +The +.Nm +utility conforms to +.St -p1003.2-92 . +The +.Fl p +option is an extension to the standard. diff --git a/shell_cmds/uname/uname.c b/shell_cmds/uname/uname.c new file mode 100644 index 0000000..7dadd52 --- /dev/null +++ b/shell_cmds/uname/uname.c @@ -0,0 +1,208 @@ +/* $NetBSD: uname.c,v 1.10 1998/11/09 13:24:05 kleink Exp $ */ + +/* + * Copyright (c) 1994 Winning Strategies, Inc. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by Winning Strategies, Inc. + * 4. The name of Winning Strategies, Inc. may not be used to endorse or + * promote products derived from this software without specific prior + * written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +#ifndef lint +__RCSID("$NetBSD: uname.c,v 1.10 1998/11/09 13:24:05 kleink Exp $"); +#endif /* not lint */ + +#include <sys/param.h> +#include <limits.h> +#include <locale.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <err.h> +#ifdef __APPLE__ +#include <string.h> +#endif /* __APPLE__ */ + +#include <sys/sysctl.h> +#include <sys/utsname.h> + +#ifdef __APPLE__ +#include <get_compat.h> +#else /* !__APPLE__ */ +#define COMPAT_MODE(a,b) (1) +#endif /* __APPLE__ */ + +int main __P((int, char **)); +static void usage __P((void)); + +/* Note that PRINT_MACHINE_ARCH is excluded from PRINT_ALL! */ +#define PRINT_SYSNAME 0x01 +#define PRINT_NODENAME 0x02 +#define PRINT_RELEASE 0x04 +#define PRINT_VERSION 0x08 +#define PRINT_MACHINE 0x10 +#define PRINT_MACHINE_ARCH 0x20 +#define PRINT_ALL \ + (PRINT_SYSNAME|PRINT_NODENAME|PRINT_RELEASE|PRINT_VERSION|PRINT_MACHINE) + +int +main(argc, argv) + int argc; + char **argv; +{ + struct utsname u; +#ifndef __APPLE__ + char machine_arch[SYS_NMLN]; +#endif /* !__APPLE__ */ + int c; + int space = 0; + int print_mask = 0; + + (void)setlocale(LC_ALL, ""); + + while ((c = getopt(argc,argv,"amnprsv")) != -1) { + switch (c) { + case 'a': + print_mask |= PRINT_ALL; +#ifdef __APPLE__ + if (!COMPAT_MODE("bin/uname", "Unix2003")) { + print_mask |= PRINT_MACHINE_ARCH; + } +#endif /* __APPLE__ */ + break; + case 'm': + print_mask |= PRINT_MACHINE; + break; + case 'n': + print_mask |= PRINT_NODENAME; + break; + case 'p': + print_mask |= PRINT_MACHINE_ARCH; + break; + case 'r': + print_mask |= PRINT_RELEASE; + break; + case 's': + print_mask |= PRINT_SYSNAME; + break; + case 'v': + print_mask |= PRINT_VERSION; + break; + default: + usage(); + /* NOTREACHED */ + } + } + + if (optind != argc) { + usage(); + /* NOTREACHED */ + } + + if (!print_mask) { + print_mask = PRINT_SYSNAME; + } + + if (uname(&u) != 0) { + err(EXIT_FAILURE, "uname"); + /* NOTREACHED */ + } +#ifndef __APPLE__ + if (print_mask & PRINT_MACHINE_ARCH) { + int mib[2] = { CTL_HW, HW_MACHINE_ARCH }; + size_t len = sizeof (machine_arch); + + if (sysctl(mib, sizeof (mib) / sizeof (mib[0]), machine_arch, + &len, NULL, 0) < 0) + err(EXIT_FAILURE, "sysctl"); + } +#endif /* !__APPLE__ */ + +#ifdef __APPLE__ + /* + * Let's allow the user to override the output of uname via the shell environment. + * This is a useful feature for cross-compiling (eg. during an OS bringup). + * Otherwise, you have to hack your kernel with the desired strings. + */ + { + char *s; + s = getenv ("UNAME_SYSNAME"); if (s) strncpy (u.sysname, s, sizeof (u.sysname)); + s = getenv ("UNAME_NODENAME"); if (s) strncpy (u.nodename, s, sizeof (u.nodename)); + s = getenv ("UNAME_RELEASE"); if (s) strncpy (u.release, s, sizeof (u.release)); + s = getenv ("UNAME_VERSION"); if (s) strncpy (u.version, s, sizeof (u.version)); + s = getenv ("UNAME_MACHINE"); if (s) strncpy (u.machine, s, sizeof (u.machine)); + } +#endif /* __APPLE__ */ + + if (print_mask & PRINT_SYSNAME) { + space++; + fputs(u.sysname, stdout); + } + if (print_mask & PRINT_NODENAME) { + if (space++) putchar(' '); + fputs(u.nodename, stdout); + } + if (print_mask & PRINT_RELEASE) { + if (space++) putchar(' '); + fputs(u.release, stdout); + } + if (print_mask & PRINT_VERSION) { + if (space++) putchar(' '); + fputs(u.version, stdout); + } + if (print_mask & PRINT_MACHINE) { + if (space++) putchar(' '); + fputs(u.machine, stdout); + } + if (print_mask & PRINT_MACHINE_ARCH) { + if (space++) putchar(' '); +#ifndef __APPLE__ + fputs(machine_arch, stdout); +#else +#if defined(__ppc__) || defined(__ppc64__) + fputs("powerpc", stdout); +#elif defined(__i386__) || defined(__x86_64__) + fputs("i386", stdout); +#elif defined(__arm__) || defined(__arm64__) + fputs("arm", stdout); +#else + fputs("unknown", stdout); +#endif +#endif /* __APPLE__ */ + } + putchar('\n'); + + exit(EXIT_SUCCESS); + /* NOTREACHED */ +} + +static void +usage() +{ + fprintf(stderr, "usage: uname [-amnprsv]\n"); + exit(EXIT_FAILURE); +} diff --git a/shell_cmds/users/users.1 b/shell_cmds/users/users.1 new file mode 100644 index 0000000..5c4f872 --- /dev/null +++ b/shell_cmds/users/users.1 @@ -0,0 +1,61 @@ +.\" $NetBSD: users.1,v 1.5 1997/10/20 02:41:21 lukem Exp $ +.\" +.\" Copyright (c) 1980, 1990, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)users.1 8.1 (Berkeley) 6/6/93 +.\" +.Dd June 6, 1993 +.Dt USERS 1 +.Os BSD 3 +.Sh NAME +.Nm users +.Nd list current users +.Sh SYNOPSIS +.Nm +.Sh DESCRIPTION +.Nm +lists the login names of the users currently on the system, +in sorted order, space separated, on a single line. +.Sh FILES +.Bl -tag -width /var/run/utmpx +.It Pa /var/run/utmpx +.El +.Sh SEE ALSO +.Xr finger 1 , +.Xr last 1 , +.Xr who 1 , +.Xr utmpx 5 +.Sh HISTORY +The +.Nm +command appeared in +.Bx 3.0 . diff --git a/shell_cmds/users/users.c b/shell_cmds/users/users.c new file mode 100644 index 0000000..ff24ecb --- /dev/null +++ b/shell_cmds/users/users.c @@ -0,0 +1,123 @@ +/* $NetBSD: users.c,v 1.7 1998/02/03 04:19:15 perry Exp $ */ + +/* + * Copyright (c) 1980, 1987, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +#ifndef lint +__COPYRIGHT("@(#) Copyright (c) 1980, 1987, 1993\n\ + The Regents of the University of California. All rights reserved.\n"); +#endif /* not lint */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)users.c 8.1 (Berkeley) 6/6/93"; +#endif +__RCSID("$NetBSD: users.c,v 1.7 1998/02/03 04:19:15 perry Exp $"); +#endif /* not lint */ + +#include <sys/types.h> + +#include <err.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <unistd.h> +#include <utmpx.h> + +typedef char namebuf[_UTX_USERSIZE]; + +int main __P((int, char **)); +int scmp __P((const void *, const void *)); + +int +main(argc, argv) + int argc; + char **argv; +{ + namebuf *names = NULL; + int ncnt = 0; + int nmax = 0; + int cnt; + struct utmpx *ux; + int ch; + + while ((ch = getopt(argc, argv, "")) != -1) + switch(ch) { + case '?': + default: + (void)fprintf(stderr, "usage: users\n"); + exit(1); + } + argc -= optind; + argv += optind; + + setutxent(); + + while ((ux = getutxent()) != NULL) { + if (*ux->ut_user && ux->ut_type == USER_PROCESS) + { + if (ncnt >= nmax) { + nmax += 32; + names = realloc(names, + sizeof (*names) * nmax); + + if (!names) { + err(1, "realloc"); + /* NOTREACHED */ + } + } + + (void)strncpy(names[ncnt], ux->ut_user, _UTX_USERSIZE); + ++ncnt; + } + } + + if (ncnt) { + qsort(names, ncnt, _UTX_USERSIZE, scmp); + (void)printf("%.*s", _UTX_USERSIZE, names[0]); + for (cnt = 1; cnt < ncnt; ++cnt) + if (strncmp(names[cnt], names[cnt - 1], _UTX_USERSIZE)) + (void)printf(" %.*s", _UTX_USERSIZE, names[cnt]); + (void)printf("\n"); + } + exit(0); +} + +int +scmp(p, q) + const void *p, *q; +{ + return(strncmp((char *) p, (char *) q, _UTX_USERSIZE)); +} diff --git a/shell_cmds/w/extern.h b/shell_cmds/w/extern.h new file mode 100644 index 0000000..8fe24bb --- /dev/null +++ b/shell_cmds/w/extern.h @@ -0,0 +1,44 @@ +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * @(#)extern.h 8.1 (Berkeley) 6/6/93 + * $FreeBSD: src/usr.bin/w/extern.h,v 1.5 2002/03/22 01:42:43 imp Exp $ + */ + + +extern int use_ampm; + +struct kinfo_proc; + +#ifdef __APPLE__ +#define KI_PROC(ki) (&(ki)->kp->kp_proc) +#endif + +void pr_attime(time_t *, time_t *); +int pr_idle(time_t); +int proc_compare(struct kinfo_proc *, struct kinfo_proc *); diff --git a/shell_cmds/w/fmt.c b/shell_cmds/w/fmt.c new file mode 100644 index 0000000..31a6e50 --- /dev/null +++ b/shell_cmds/w/fmt.c @@ -0,0 +1,129 @@ +/*- + * Copyright (c) 1992, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> + +#if 0 +#ifndef lint +static char sccsid[] = "@(#)fmt.c 8.4 (Berkeley) 4/15/94"; +#endif +#endif + +#include <sys/types.h> +#include <sys/time.h> +#include <sys/resource.h> + +#include <err.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include <vis.h> + +//#include "ps.h" + +static char *cmdpart(char *); +static char *shquote(char **); + +/* + * XXX + * This is a stub until marc does the real one. + */ +static char * +shquote(char **argv) +{ + static long arg_max = -1; + size_t len; + char **p, *dst, *src; + static char *buf = NULL; + + if (buf == NULL) { + if ((arg_max = sysconf(_SC_ARG_MAX)) == -1) + errx(1, "sysconf _SC_ARG_MAX failed"); + if ((buf = malloc((u_int)(4 * arg_max) + 1)) == NULL) + errx(1, "malloc failed"); + } + + if (*argv == 0) { + buf[0] = 0; + return (buf); + } + dst = buf; + for (p = argv; (src = *p++) != 0; ) { + if (*src == 0) + continue; + len = (size_t)(4 * arg_max - (dst - buf)) / 4; + strvisx(dst, src, strlen(src) < len ? strlen(src) : len, + VIS_NL | VIS_CSTYLE); + while (*dst) + dst++; + if ((4 * arg_max - (dst - buf)) / 4 > 0) + *dst++ = ' '; + } + /* Chop off trailing space */ + if (dst != buf && dst[-1] == ' ') + dst--; + *dst = '\0'; + return (buf); +} + +static char * +cmdpart(char *arg0) +{ + char *cp; + + return ((cp = strrchr(arg0, '/')) != NULL ? cp + 1 : arg0); +} + +const char * +fmt_argv(char **argv, char *cmd, size_t maxlen) +{ + size_t len; + char *ap, *cp; + + if (argv == NULL || argv[0] == NULL) { + if (cmd == NULL) + return (""); + ap = NULL; + len = maxlen + 3; + } else { + ap = shquote(argv); + len = strlen(ap) + maxlen + 4; + } + cp = malloc(len); + if (cp == NULL) + return (NULL); + if (ap == NULL) + sprintf(cp, " (%.*s)", (int)maxlen, cmd); + else if (strncmp(cmdpart(argv[0]), cmd, maxlen) != 0) + sprintf(cp, "%s (%.*s)", ap, (int)maxlen, cmd); + else + (void) strcpy(cp, ap); + return (cp); +} diff --git a/shell_cmds/w/pr_time.c b/shell_cmds/w/pr_time.c new file mode 100644 index 0000000..77d506d --- /dev/null +++ b/shell_cmds/w/pr_time.c @@ -0,0 +1,117 @@ +/*- + * Copyright (c) 1990, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> + +#ifndef __APPLE__ +__FBSDID("$FreeBSD: src/usr.bin/w/pr_time.c,v 1.19 2002/06/07 01:41:54 jmallett Exp $"); +#endif + +#ifndef lint +static const char sccsid[] = "@(#)pr_time.c 8.2 (Berkeley) 4/4/94"; +#endif + +#include <sys/types.h> +#include <sys/time.h> + +#include <stdio.h> +#include <string.h> + +#include "extern.h" + +/* + * pr_attime -- + * Print the time since the user logged in. + */ +void +pr_attime(time_t *started, time_t *now) +{ + static char buf[256]; + struct tm tp, tm; + time_t diff; + char fmt[20]; + + tp = *localtime(started); + tm = *localtime(now); + diff = *now - *started; + + /* If more than a week, use day-month-year. */ + if (diff > 86400 * 7) + (void)strcpy(fmt, "%d%b%y"); + + /* If not today, use day-hour-am/pm. */ + else if (tm.tm_mday != tp.tm_mday || + tm.tm_mon != tp.tm_mon || + tm.tm_year != tp.tm_year) { + /* The line below does not take DST into consideration */ + /* else if (*now / 86400 != *started / 86400) { */ + (void)strcpy(fmt, use_ampm ? "%a%I%p" : "%a%H"); + } + + /* Default is hh:mm{am,pm}. */ + else { + (void)strcpy(fmt, use_ampm ? "%l:%M%p" : "%k:%M"); + } + + (void)strftime(buf, sizeof(buf), fmt, &tp); + (void)printf("%-7.7s", buf); +} + +/* + * pr_idle -- + * Display the idle time. + * Returns number of excess characters that were used for long idle time. + */ +int +pr_idle(time_t idle) +{ + /* If idle more than 36 hours, print as a number of days. */ + if (idle >= 36 * 3600) { + int days = idle / 86400; + (void)printf(" %dday%s ", days, days > 1 ? "s" : " " ); + if (days >= 100) + return (2); + if (days >= 10) + return (1); + } + + /* If idle more than an hour, print as HH:MM. */ + else if (idle >= 3600) + (void)printf(" %2d:%02d ", + (int)(idle / 3600), (int)((idle % 3600) / 60)); + + else if (idle / 60 == 0) + (void)printf(" - "); + + /* Else print the minutes idle. */ + else + (void)printf(" %2d ", (int)(idle / 60)); + + return (0); /* not idle longer than 9 days */ +} diff --git a/shell_cmds/w/proc_compare.c b/shell_cmds/w/proc_compare.c new file mode 100644 index 0000000..806f571 --- /dev/null +++ b/shell_cmds/w/proc_compare.c @@ -0,0 +1,144 @@ +/*- + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#if 0 +#ifndef lint +static char sccsid[] = "@(#)proc_compare.c 8.2 (Berkeley) 9/23/93"; +#endif /* not lint */ +#endif + +#include <sys/cdefs.h> +#ifndef __APPLE__ +__FBSDID("$FreeBSD: src/usr.bin/w/proc_compare.c,v 1.9 2004/04/14 09:34:17 bde Exp $"); +#endif + +#include <sys/param.h> +#ifdef __APPLE__ +#include <sys/time.h> +#endif +#include <sys/proc.h> +#include <sys/time.h> +#include <sys/user.h> + +#include "extern.h" + +/* + * Returns 1 if p2 is "better" than p1 + * + * The algorithm for picking the "interesting" process is thus: + * + * 1) Only foreground processes are eligible - implied. + * 2) Runnable processes are favored over anything else. The runner + * with the highest cpu utilization is picked (p_estcpu). Ties are + * broken by picking the highest pid. + * 3) The sleeper with the shortest sleep time is next. With ties, + * we pick out just "short-term" sleepers (TDF_SINTR == 0). + * 4) Further ties are broken by picking the highest pid. + * + * If you change this, be sure to consider making the change in the kernel + * too (^T in kern/tty.c). + * + * TODO - consider whether pctcpu should be used. + */ + +#if !HAVE_KVM +#define ki_estcpu p_estcpu +#define ki_pid p_pid +#define ki_slptime p_slptime +#define ki_stat p_stat +#define ki_tdflags p_tdflags +#endif + +#define ISRUN(p) (((p)->ki_stat == SRUN) || ((p)->ki_stat == SIDL)) +#define TESTAB(a, b) ((a)<<1 | (b)) +#define ONLYA 2 +#define ONLYB 1 +#define BOTH 3 + +#if HAVE_KVM +int +proc_compare(struct kinfo_proc *p1, struct kinfo_proc *p2) +{ +#else +int +proc_compare(struct kinfo_proc *arg1, struct kinfo_proc *arg2) +{ + struct extern_proc* p1 = &arg1->kp_proc; + struct extern_proc* p2 = &arg2->kp_proc; +#endif + + if (p1 == NULL) + return (1); + /* + * see if at least one of them is runnable + */ + switch (TESTAB(ISRUN(p1), ISRUN(p2))) { + case ONLYA: + return (0); + case ONLYB: + return (1); + case BOTH: + /* + * tie - favor one with highest recent cpu utilization + */ + if (p2->ki_estcpu > p1->ki_estcpu) + return (1); + if (p1->ki_estcpu > p2->ki_estcpu) + return (0); + return (p2->ki_pid > p1->ki_pid); /* tie - return highest pid */ + } + /* + * weed out zombies + */ + switch (TESTAB(p1->ki_stat == SZOMB, p2->ki_stat == SZOMB)) { + case ONLYA: + return (1); + case ONLYB: + return (0); + case BOTH: + return (p2->ki_pid > p1->ki_pid); /* tie - return highest pid */ + } + /* + * pick the one with the smallest sleep time + */ + if (p2->ki_slptime > p1->ki_slptime) + return (0); + if (p1->ki_slptime > p2->ki_slptime) + return (1); +#ifndef __APPLE__ + /* + * favor one sleeping in a non-interruptible sleep + */ + if (p1->ki_tdflags & TDF_SINTR && (p2->ki_tdflags & TDF_SINTR) == 0) + return (1); + if (p2->ki_tdflags & TDF_SINTR && (p1->ki_tdflags & TDF_SINTR) == 0) + return (0); +#endif + return (p2->ki_pid > p1->ki_pid); /* tie - return highest pid */ +} diff --git a/shell_cmds/w/uptime.1 b/shell_cmds/w/uptime.1 new file mode 100644 index 0000000..a2c372a --- /dev/null +++ b/shell_cmds/w/uptime.1 @@ -0,0 +1,52 @@ +.\" Copyright (c) 1980, 1990, 1993, 1994 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)uptime.1 8.2 (Berkeley) 4/18/94 +.\" $FreeBSD: src/usr.bin/w/uptime.1,v 1.8 2002/05/09 11:47:40 joe Exp $ +.\" +.Dd April 18, 1994 +.Dt UPTIME 1 +.Os +.Sh NAME +.Nm uptime +.Nd show how long system has been running +.Sh SYNOPSIS +.Nm +.Sh DESCRIPTION +The +.Nm +utility displays the current time, +the length of time the system has been up, +the number of users, and the load average of the system over the last +1, 5, and 15 minutes. +.Sh SEE ALSO +.Xr w 1 +.Sh HISTORY +The +.Nm +command appeared in +.Bx 3.0 . diff --git a/shell_cmds/w/w.1 b/shell_cmds/w/w.1 new file mode 100644 index 0000000..e94c39b --- /dev/null +++ b/shell_cmds/w/w.1 @@ -0,0 +1,127 @@ +.\" Copyright (c) 1980, 1990, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)w.1 8.1 (Berkeley) 6/6/93 +.\" $FreeBSD: src/usr.bin/w/w.1,v 1.20 2005/02/13 22:25:25 ru Exp $ +.\" +.Dd June 6, 1993 +.Dt W 1 +.Os +.Sh NAME +.Nm w +.Nd "display who is logged in and what they are doing" +.Sh SYNOPSIS +.Nm +.Op Fl hin +.Op Ar user ... +.Sh DESCRIPTION +The +.Nm +utility prints a summary of the current activity on the system, +including what each user is doing. +The first line displays the current time of day, how long the system has +been running, the number of users logged into the system, and the load +averages. +The load average numbers give the number of jobs in the run queue averaged +over 1, 5 and 15 minutes. +.Pp +The fields output are the user's login name, the name of the terminal the +user is on, the host from which the user is logged in, the time the user +logged on, the time since the user last typed anything, +and the name and arguments of the current process. +.Pp +The options are as follows: +.Bl -tag -width Ds +.It Fl h +Suppress the heading. +.It Fl i +Output is sorted by idle time. +.El +.Pp +If one or more +.Ar user +names are specified, the output is restricted to those users. +.Sh COMPATIBILITY +The +.Fl M , +.FL N , +.Fl d , +.Fl f , +.Fl l , +.Fl n , +.Fl s , +and +.Fl w +flags are no longer supported. +.Sh SEE ALSO +.Xr finger 1 , +.Xr ps 1 , +.Xr uptime 1 , +.Xr who 1 +.Sh HISTORY +The +.Nm +command appeared in +.Bx 3.0 . +.Sh BUGS +The notion of the +.Dq current process +is muddy. +The current algorithm is ``the highest numbered process on the terminal +that is not ignoring interrupts, or, if there is none, the highest numbered +process on the terminal''. +This fails, for example, in critical sections of programs like the shell +and editor, or when faulty programs running in the background fork and fail +to ignore interrupts. +(In cases where no process can be found, +.Nm +prints +.Dq \- . ) +.Pp +The +.Tn CPU +time is only an estimate, in particular, if someone leaves a background +process running after logging out, the person currently on that terminal is +.Dq charged +with the time. +.Pp +Background processes are not shown, even though they account for +much of the load on the system. +.Pp +Sometimes processes, typically those in the background, are printed with +null or garbaged arguments. +In these cases, the name of the command is printed in parentheses. +.Pp +The +.Nm +utility does not know about the new conventions for detection of background +jobs. +It will sometimes find a background job instead of the right one. +.Pp +Long hostnames and IPv6 addresses may be truncated; however, the +.Xr who 1 +utility will display full hostnames. diff --git a/shell_cmds/w/w.c b/shell_cmds/w/w.c new file mode 100644 index 0000000..e493734 --- /dev/null +++ b/shell_cmds/w/w.c @@ -0,0 +1,776 @@ +/*- + * Copyright (c) 1980, 1991, 1993, 1994 + * The Regents of the University of California. All rights reserved. + * Portions copyright (c) 2007 Apple Inc. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> + +#ifndef __APPLE__ +__FBSDID("$FreeBSD: src/usr.bin/w/w.c,v 1.58 2005/06/04 23:40:09 gad Exp $"); +#endif + +#ifndef lint +static const char copyright[] = +"@(#) Copyright (c) 1980, 1991, 1993, 1994\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif + +#ifndef lint +static const char sccsid[] = "@(#)w.c 8.4 (Berkeley) 4/16/94"; +#endif + +/* + * w - print system status (who and what) + * + * This program is similar to the systat command on Tenex/Tops 10/20 + * + */ +#include <sys/param.h> +#include <sys/time.h> +#include <sys/stat.h> +#include <sys/sysctl.h> +#include <sys/proc.h> +#include <sys/user.h> +#include <sys/ioctl.h> +#include <sys/socket.h> +#include <sys/tty.h> + +#ifndef __APPLE__ +#include <machine/cpu.h> +#endif +#include <netinet/in.h> +#include <arpa/inet.h> +#include <arpa/nameser.h> + +#include <ctype.h> +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#if HAVE_KVM +#include <kvm.h> +#endif +#include <langinfo.h> +#include <libutil.h> +#include <limits.h> +#include <locale.h> +#include <netdb.h> +#include <nlist.h> +#include <paths.h> +#include <resolv.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <timeconv.h> +#include <unistd.h> +#if HAVE_UTMPX +#include <utmpx.h> +/* use utmp values so formatting is the same */ +#define UT_NAMESIZE 8 +#define UT_LINESIZE 8 +#else /* HAVE_UTMPX */ +#include <utmp.h> +#endif /* HAVE_UTMPX */ +#include <vis.h> + +#include <TargetConditionals.h> + +#include "extern.h" + +struct timeval boottime; +#if !HAVE_UTMPX +struct utmp utmp; +#endif +struct winsize ws; +#if HAVE_KVM +kvm_t *kd; +#endif +time_t now; /* the current time of day */ +int ttywidth; /* width of tty */ +int argwidth; /* width of tty */ +int header = 1; /* true if -h flag: don't print heading */ +#if !HAVE_UTMPX +int nflag; /* true if -n flag: don't convert addrs */ +#endif +#ifndef __APPLE__ +int dflag; /* true if -d flag: output debug info */ +#endif +int sortidle; /* sort by idle time */ +int use_ampm; /* use AM/PM time */ +int use_comma; /* use comma as floats separator */ +char **sel_users; /* login array of particular users selected */ + +/* + * One of these per active utmp entry. + */ +struct entry { + struct entry *next; +#if HAVE_UTMPX + struct utmpx utmp; +#else + struct utmp utmp; +#endif + dev_t tdev; /* dev_t of terminal */ + time_t idle; /* idle time of terminal in seconds */ + struct kinfo_proc *kp; /* `most interesting' proc */ + char *args; /* arg list of interesting process */ + struct kinfo_proc *dkp; /* debug option proc list */ +} *ep, *ehead = NULL, **nextp = &ehead; + +#ifndef __APPLE__ +#define debugproc(p) *((struct kinfo_proc **)&(p)->ki_udata) +#else +#define debugproc(p) *((struct kinfo_proc **)&(p)->ki_spare[0]) +#endif + +/* W_DISPHOSTSIZE should not be greater than UT_HOSTSIZE */ +#define W_DISPHOSTSIZE 16 + +static void pr_header(time_t *, int); +static struct stat *ttystat(char *, int); +static void usage(int); +static int this_is_uptime(const char *s); +#if !HAVE_KVM +static void w_getargv(void); +#endif + +char *fmt_argv(char **, char *, int); /* ../../bin/ps/fmt.c */ + +int +main(int argc, char *argv[]) +{ + struct kinfo_proc *kp; + struct kinfo_proc *kprocbuf; + struct kinfo_proc *dkp; + struct stat *stp; +#if HAVE_UTMPX + struct utmpx *ux; +#else + FILE *ut; +#endif + time_t touched; +#if HAVE_KVM + int ch, i, nentries, nusers, wcmd, longidle, dropgid; + const char *memf, *nlistf, *p; +#else + int ch, i, nentries, nusers, wcmd, longidle; + const char *p; +#endif /* HAVE_KVM */ + char *x_suffix; +#ifdef __APPLE__ + char buf[MAXHOSTNAMELEN]; +#else + char buf[MAXHOSTNAMELEN], errbuf[_POSIX2_LINE_MAX]; + char fn[MAXHOSTNAMELEN]; +#endif /* __APPLE__ */ + char *dot; +#if !HAVE_KVM + int local_error = 0, retry_count = 0; + size_t bufSize = 0; + size_t orig_bufSize = 0; + int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_ALL, 0 }; +#endif + + (void)setlocale(LC_ALL, ""); +#ifndef __APPLE__ + use_ampm = (*nl_langinfo(T_FMT_AMPM) != '\0'); + use_comma = (*nl_langinfo(RADIXCHAR) != ','); +#endif + + /* Are we w(1) or uptime(1)? */ + if (this_is_uptime(argv[0]) == 0) { + wcmd = 0; + p = ""; + } else { + wcmd = 1; + p = "dhiflM:N:nsuw"; + } + +#if HAVE_KVM + dropgid = 0; + memf = nlistf = _PATH_DEVNULL; +#endif + while ((ch = getopt(argc, argv, p)) != -1) + switch (ch) { +#ifndef __APPLE__ + case 'd': + dflag = 1; + break; +#endif + case 'h': + header = 0; + break; + case 'i': + sortidle = 1; + break; +#if HAVE_KVM + case 'M': + header = 0; + memf = optarg; + dropgid = 1; + break; + case 'N': + nlistf = optarg; + dropgid = 1; + break; +#endif /* HAVE_KVM */ +#if !HAVE_UTMPX + case 'n': + nflag = 1; + break; +#else /* !HAVE_UTMPX */ + case 'n': +#endif /* !HAVE_UTMPX */ + case 'f': case 'l': case 's': case 'u': case 'w': +#if !HAVE_KVM + case 'M': case 'N': +#endif +#ifdef __APPLE__ + case 'd': + warnx("[-MNdflnsuw] no longer supported"); +#else + warnx("[-flsuw] no longer supported"); +#endif + /* FALLTHROUGH */ + case '?': + default: + usage(wcmd); + } + argc -= optind; + argv += optind; + +#if !(TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR) + if (!(_res.options & RES_INIT)) + res_init(); + _res.retrans = 2; /* resolver timeout to 2 seconds per try */ + _res.retry = 1; /* only try once.. */ +#endif + +#if HAVE_KVM + /* + * Discard setgid privileges if not the running kernel so that bad + * guys can't print interesting stuff from kernel memory. + */ + if (dropgid) + setgid(getgid()); + + if ((kd = kvm_openfiles(nlistf, memf, NULL, O_RDONLY, errbuf)) == NULL) + errx(1, "%s", errbuf); +#endif + + (void)time(&now); +#if HAVE_UTMPX + setutxent(); +#else + if ((ut = fopen(_PATH_UTMP, "r")) == NULL) + err(1, "%s", _PATH_UTMP); +#endif + + if (*argv) + sel_users = argv; + +#if HAVE_UTMPX + for (nusers = 0; (ux = getutxent());) { + if (ux->ut_user[0] == '\0' || ux->ut_type != USER_PROCESS) + continue; + if (!(stp = ttystat(ux->ut_line, sizeof(ux->ut_line)))) + continue; /* corrupted record */ +#else + for (nusers = 0; fread(&utmp, sizeof(utmp), 1, ut);) { + if (utmp.ut_name[0] == '\0') + continue; + if (!(stp = ttystat(utmp.ut_line, UT_LINESIZE))) + continue; /* corrupted record */ +#endif + ++nusers; + if (wcmd == 0) + continue; + if (sel_users) { + int usermatch; + char **user; + + usermatch = 0; + for (user = sel_users; !usermatch && *user; user++) +#if HAVE_UTMPX + if (!strncmp(ux->ut_user, *user, sizeof(ux->ut_user))) +#else + if (!strncmp(utmp.ut_name, *user, UT_NAMESIZE)) +#endif + usermatch = 1; + if (!usermatch) + continue; + } + if ((ep = calloc(1, sizeof(struct entry))) == NULL) + errx(1, "calloc"); + *nextp = ep; + nextp = &ep->next; +#if HAVE_UTMPX + memmove(&ep->utmp, ux, sizeof(*ux)); +#else + memmove(&ep->utmp, &utmp, sizeof(struct utmp)); +#endif + ep->tdev = stp->st_rdev; +#ifdef CPU_CONSDEV + /* + * If this is the console device, attempt to ascertain + * the true console device dev_t. + */ + if (ep->tdev == 0) { + int mib[2]; + size_t size; + + mib[0] = CTL_MACHDEP; + mib[1] = CPU_CONSDEV; + size = sizeof(dev_t); + (void)sysctl(mib, 2, &ep->tdev, &size, NULL, 0); + } +#endif + touched = stp->st_atime; +#ifdef __APPLE__ + if (touched < ep->utmp.ut_tv.tv_sec) { + /* tty untouched since before login */ + touched = ep->utmp.ut_tv.tv_sec; + } +#else + if (touched < ep->utmp.ut_time) { + /* tty untouched since before login */ + touched = ep->utmp.ut_time; + } +#endif + if ((ep->idle = now - touched) < 0) + ep->idle = 0; + } +#if HAVE_UTMPX + endutxent(); +#else + (void)fclose(ut); +#endif + + if (header || wcmd == 0) { + pr_header(&now, nusers); + if (wcmd == 0) { +#if HAVE_KVM + (void)kvm_close(kd); +#endif + exit(0); + } + +#define HEADER_USER "USER" +#define HEADER_TTY "TTY" +#define HEADER_FROM "FROM" +#define HEADER_LOGIN_IDLE "LOGIN@ IDLE " +#define HEADER_WHAT "WHAT\n" +#define WUSED (UT_NAMESIZE + UT_LINESIZE + W_DISPHOSTSIZE + \ + sizeof(HEADER_LOGIN_IDLE) + 3) /* header width incl. spaces */ + (void)printf("%-*.*s %-*.*s %-*.*s %s", + UT_NAMESIZE, UT_NAMESIZE, HEADER_USER, + UT_LINESIZE, UT_LINESIZE, HEADER_TTY, + W_DISPHOSTSIZE, W_DISPHOSTSIZE, HEADER_FROM, + HEADER_LOGIN_IDLE HEADER_WHAT); + } + +#if HAVE_KVM + if ((kp = kvm_getprocs(kd, KERN_PROC_ALL, 0, &nentries)) == NULL) + err(1, "%s", kvm_geterr(kd)); +#else + mib[0] = CTL_KERN; + mib[1] = KERN_PROC; + mib[2] = KERN_PROC_ALL; + mib[3] = 0; + + if (sysctl(mib, 4, NULL, &bufSize, NULL, 0) < 0) { + perror("Failure calling sysctl"); + exit(1); + } + + kprocbuf = kp = (struct kinfo_proc *)malloc(bufSize); + + retry_count = 0; + orig_bufSize = bufSize; + for (retry_count = 0; ; retry_count++) { + local_error = 0; + bufSize = orig_bufSize; + if ((local_error = sysctl(mib, 4, kp, &bufSize, NULL, 0)) < 0) { + if (retry_count < 1000) { + sleep(1); + continue; + } + perror("Failure calling sysctl"); + exit(1); + } else if (local_error == 0) { + break; + } + sleep(1); + } + nentries = bufSize / sizeof(struct kinfo_proc); +#endif /* !HAVE_KVM */ + +#if !HAVE_KVM +#define ki_stat kp_proc.p_stat +#define ki_pgid kp_eproc.e_pgid +#define ki_tpgid kp_eproc.e_tpgid +#define ki_tdev kp_eproc.e_tdev +#endif /* !HAVE_KVM */ + for (i = 0; i < nentries; i++, kp++) { + if (kp->ki_stat == SIDL || kp->ki_stat == SZOMB) + continue; + for (ep = ehead; ep != NULL; ep = ep->next) { + if (ep->tdev == kp->ki_tdev) { + /* + * proc is associated with this terminal + */ + if (ep->kp == NULL && kp->ki_pgid == kp->ki_tpgid) { + /* + * Proc is 'most interesting' + */ + if (proc_compare(ep->kp, kp)) + ep->kp = kp; + } + /* + * Proc debug option info; add to debug + * list using kinfo_proc ki_spare[0] + * as next pointer; ptr to ptr avoids the + * ptr = long assumption. + */ + dkp = ep->dkp; + ep->dkp = kp; +#ifndef __APPLE__ + debugproc(kp) = dkp; +#endif + } + } + } + if ((ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == -1 && + ioctl(STDERR_FILENO, TIOCGWINSZ, &ws) == -1 && + ioctl(STDIN_FILENO, TIOCGWINSZ, &ws) == -1) || ws.ws_col == 0) + ttywidth = 79; + else + ttywidth = ws.ws_col - 1; + argwidth = ttywidth - WUSED; + if (argwidth < 4) + argwidth = 8; + for (ep = ehead; ep != NULL; ep = ep->next) { + if (ep->kp == NULL) { + ep->args = strdup("-"); + continue; + } +#if HAVE_KVM + ep->args = fmt_argv(kvm_getargv(kd, ep->kp, argwidth), + ep->kp->ki_comm, MAXCOMLEN); +#else + w_getargv(); +#endif /* HAVE_KVM */ + if (ep->args == NULL) + err(1, NULL); + } + /* sort by idle time */ + if (sortidle && ehead != NULL) { + struct entry *from, *save; + + from = ehead; + ehead = NULL; + while (from != NULL) { + for (nextp = &ehead; + (*nextp) && from->idle >= (*nextp)->idle; + nextp = &(*nextp)->next) + continue; + save = from; + from = from->next; + save->next = *nextp; + *nextp = save; + } + } + + for (ep = ehead; ep != NULL; ep = ep->next) { +#if HAVE_UTMPX + char host_buf[sizeof(ep->utmp.ut_host) + 1]; + strlcpy(host_buf, ep->utmp.ut_host, sizeof(host_buf)); +#else + char host_buf[UT_HOSTSIZE + 1]; + struct sockaddr_storage ss; + struct sockaddr *sa = (struct sockaddr *)&ss; + struct sockaddr_in *lsin = (struct sockaddr_in *)&ss; + struct sockaddr_in6 *lsin6 = (struct sockaddr_in6 *)&ss; + time_t t; + int isaddr; + + host_buf[UT_HOSTSIZE] = '\0'; + strncpy(host_buf, ep->utmp.ut_host, UT_HOSTSIZE); +#endif /* HAVE_UTMPX */ + p = *host_buf ? host_buf : "-"; + if ((x_suffix = strrchr(p, ':')) != NULL) { + if ((dot = strchr(x_suffix, '.')) != NULL && + strchr(dot+1, '.') == NULL) + *x_suffix++ = '\0'; + else + x_suffix = NULL; + } +#if !HAVE_UTMPX + if (!nflag) { + /* Attempt to change an IP address into a name */ + isaddr = 0; + memset(&ss, '\0', sizeof(ss)); + if (inet_pton(AF_INET6, p, &lsin6->sin6_addr) == 1) { + lsin6->sin6_len = sizeof(*lsin6); + lsin6->sin6_family = AF_INET6; + isaddr = 1; + } else if (inet_pton(AF_INET, p, &lsin->sin_addr) == 1) { + lsin->sin_len = sizeof(*lsin); + lsin->sin_family = AF_INET; + isaddr = 1; + } + if (isaddr && realhostname_sa(fn, sizeof(fn), sa, + sa->sa_len) == HOSTNAME_FOUND) + p = fn; + } +#endif /* !HAVE_UTMPX */ + if (x_suffix) { + (void)snprintf(buf, sizeof(buf), "%s:%s", p, x_suffix); + p = buf; + } +#ifndef __APPLE__ + if (dflag) { + for (dkp = ep->dkp; dkp != NULL; dkp = debugproc(dkp)) { + const char *ptr; + + ptr = fmt_argv(kvm_getargv(kd, dkp, argwidth), + dkp->ki_comm, MAXCOMLEN); + if (ptr == NULL) + ptr = "-"; + (void)printf("\t\t%-9d %s\n", + dkp->ki_pid, ptr); + } + } +#endif /* !__APPLE__ */ + (void)printf("%-*.*s %-*.*s %-*.*s ", +#if HAVE_UTMPX + UT_NAMESIZE, (int)sizeof(ep->utmp.ut_user), ep->utmp.ut_user, + UT_LINESIZE, (int)sizeof(ep->utmp.ut_line), +#else + UT_NAMESIZE, UT_NAMESIZE, ep->utmp.ut_name, + UT_LINESIZE, UT_LINESIZE, +#endif + strncmp(ep->utmp.ut_line, "tty", 3) && + strncmp(ep->utmp.ut_line, "cua", 3) ? + ep->utmp.ut_line : ep->utmp.ut_line + 3, + W_DISPHOSTSIZE, W_DISPHOSTSIZE, *p ? p : "-"); +#ifdef __APPLE__ + pr_attime(&ep->utmp.ut_tv.tv_sec, &now); +#else + t = _time_to_time32(ep->utmp.ut_time); + pr_attime(&t, &now); +#endif + longidle = pr_idle(ep->idle); + (void)printf("%.*s\n", argwidth - longidle, ep->args); +#ifdef __APPLE__ + free(ep->args); +#endif + } +#if HAVE_KVM + (void)kvm_close(kd); +#else + free(kprocbuf); +#endif /* HAVE_KVM */ + exit(0); +} + +static void +pr_header(time_t *nowp, int nusers) +{ + double avenrun[3]; + time_t uptime; + int days, hrs, i, mins, secs; + int mib[2]; + size_t size; + char buf[256]; + + /* + * Print time of day. + */ + if (strftime(buf, sizeof(buf), + use_ampm ? "%l:%M%p" : "%k:%M", localtime(nowp)) != 0) + (void)printf("%s ", buf); + /* + * Print how long system has been up. + * (Found by looking getting "boottime" from the kernel) + */ + mib[0] = CTL_KERN; + mib[1] = KERN_BOOTTIME; + size = sizeof(boottime); + if (sysctl(mib, 2, &boottime, &size, NULL, 0) != -1 && + boottime.tv_sec != 0) { + uptime = now - boottime.tv_sec; + if (uptime > 60) + uptime += 30; + days = uptime / 86400; + uptime %= 86400; + hrs = uptime / 3600; + uptime %= 3600; + mins = uptime / 60; + secs = uptime % 60; + (void)printf(" up"); + if (days > 0) + (void)printf(" %d day%s,", days, days > 1 ? "s" : ""); + if (hrs > 0 && mins > 0) + (void)printf(" %2d:%02d,", hrs, mins); + else if (hrs > 0) + (void)printf(" %d hr%s,", hrs, hrs > 1 ? "s" : ""); + else if (mins > 0) + (void)printf(" %d min%s,", mins, mins > 1 ? "s" : ""); + else + (void)printf(" %d sec%s,", secs, secs > 1 ? "s" : ""); + } + + /* Print number of users logged in to system */ + (void)printf(" %d user%s", nusers, nusers == 1 ? "" : "s"); + + /* + * Print 1, 5, and 15 minute load averages. + */ + if (getloadavg(avenrun, sizeof(avenrun) / sizeof(avenrun[0])) == -1) + (void)printf(", no load average information available\n"); + else { + (void)printf(", load averages:"); + for (i = 0; i < (int)(sizeof(avenrun) / sizeof(avenrun[0])); i++) { + if (use_comma && i > 0) + (void)printf(","); + (void)printf(" %.2f", avenrun[i]); + } + (void)printf("\n"); + } +} + +static struct stat * +ttystat(char *line, int sz) +{ + static struct stat sb; + char ttybuf[MAXPATHLEN]; + + (void)snprintf(ttybuf, sizeof(ttybuf), "%s%.*s", _PATH_DEV, sz, line); + if (stat(ttybuf, &sb) == 0) { + return (&sb); + } else { + warn("%s", ttybuf); + return (NULL); + } +} + +static void +usage(int wcmd) +{ + if (wcmd) + (void)fprintf(stderr, + "usage: w [hi] [user ...]\n"); + else + (void)fprintf(stderr, "usage: uptime\n"); + exit(1); +} + +static int +this_is_uptime(const char *s) +{ + const char *u; + + if ((u = strrchr(s, '/')) != NULL) + ++u; + else + u = s; + if (strcmp(u, "uptime") == 0) + return (0); + return (-1); +} + +#if !HAVE_KVM +static void +w_getargv(void) +{ + int mib[3], argmax; + size_t size; + char *procargs, *sp, *np, *cp; + + mib[0] = CTL_KERN; + mib[1] = KERN_ARGMAX; + + size = sizeof(argmax); + if (sysctl(mib, 2, &argmax, &size, NULL, 0) == -1) { + goto ERROR; + } + + procargs = malloc(argmax); + if (procargs == NULL) { + goto ERROR; + } + + mib[0] = CTL_KERN; + mib[1] = KERN_PROCARGS; + mib[2] = KI_PROC(ep)->p_pid; + + size = (size_t)argmax; + if (sysctl(mib, 3, procargs, &size, NULL, 0) == -1) { + goto ERROR_FREE; + } + + for (cp = procargs; cp < &procargs[size]; cp++) { + if (*cp == '\0') { + break; + } + } + if (cp == &procargs[size]) { + goto ERROR_FREE; + } + + sp = cp; + + for (np = NULL; cp < &procargs[size]; cp++) { + if (*cp == '\0') { + if (np != NULL) { + *np = ' '; + } + np = cp; + } else if (*cp == '=') { + break; + } + } + + for (np = sp; (np < &procargs[size]) && (*np == ' '); np++); + + ep->args = strdup(np); + free(procargs); + return; + +ERROR_FREE: + free(procargs); +ERROR: +/* + ep->args = malloc(2); + ep->args[0] = '-'; + ep->args[1] = '\0'; +*/ + asprintf(&ep->args, "%s", KI_PROC(ep)->p_comm); + return; +} +#endif /* HAVE_KVM */ diff --git a/shell_cmds/what/what.1 b/shell_cmds/what/what.1 new file mode 100644 index 0000000..fff56be --- /dev/null +++ b/shell_cmds/what/what.1 @@ -0,0 +1,70 @@ +.\" $NetBSD: what.1,v 1.4 1997/10/20 03:16:30 lukem Exp $ +.\" +.\" Copyright (c) 1980, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)what.1 8.1 (Berkeley) 6/6/93 +.\" +.Dd June 6, 1993 +.Dt WHAT 1 +.Os BSD 4 +.Sh NAME +.Nm what +.Nd "show what versions of object modules were used to construct a file" +.Sh SYNOPSIS +.Nm +.Ar name Ar ... +.Sh DESCRIPTION +.Nm +reads each file +.Ar name +and searches for sequences of the form +.Dq \&@(#) , +as inserted by the source code control system. It prints the remainder +of the string following this marker, up to a null character, newline, double +quote, or +.Dq \&> character. +.Sh BUGS +As +.Bx +is not licensed to distribute +.Tn SCCS . +This is a rewrite of the +.Nm +command which is part of +.Tn SCCS ; +it may not behave in exactly the same manner as that +command does. +.Sh HISTORY +The +.Nm +command appeared in +.Bx 4.0 . diff --git a/shell_cmds/what/what.c b/shell_cmds/what/what.c new file mode 100644 index 0000000..2f70e2d --- /dev/null +++ b/shell_cmds/what/what.c @@ -0,0 +1,97 @@ +/* $NetBSD: what.c,v 1.6 1997/10/20 03:16:31 lukem Exp $ */ + +/* + * Copyright (c) 1980, 1988, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +#ifndef lint +__COPYRIGHT("@(#) Copyright (c) 1980, 1988, 1993\n\ + The Regents of the University of California. All rights reserved.\n"); +#endif /* not lint */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)what.c 8.1 (Berkeley) 6/6/93"; +#endif +__RCSID("$NetBSD: what.c,v 1.6 1997/10/20 03:16:31 lukem Exp $"); +#endif /* not lint */ + +#include <stdio.h> +#include <stdlib.h> + +void search __P((void)); +int main __P((int, char **)); + +/* + * what + */ +/* ARGSUSED */ +int +main(argc, argv) + int argc; + char **argv; +{ + if (!*++argv) + search(); + else do { + if (!freopen(*argv, "r", stdin)) { + perror(*argv); + exit(1); + } + printf("%s\n", *argv); + search(); + } while(*++argv); + exit(0); +} + +void +search() +{ + int c; + + while ((c = getchar()) != EOF) { +loop: if (c != '@') + continue; + if ((c = getchar()) != '(') + goto loop; + if ((c = getchar()) != '#') + goto loop; + if ((c = getchar()) != ')') + goto loop; + putchar('\t'); + while ((c = getchar()) != EOF && c && c != '"' && + c != '>' && c != '\n') + putchar(c); + putchar('\n'); + } +} diff --git a/shell_cmds/whereis/whereis.1 b/shell_cmds/whereis/whereis.1 new file mode 100644 index 0000000..4df4b27 --- /dev/null +++ b/shell_cmds/whereis/whereis.1 @@ -0,0 +1,70 @@ +.\" $NetBSD: whereis.1,v 1.7 1998/02/06 06:20:06 perry Exp $ +.\" +.\" Copyright (c) 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)whereis.1 8.3 (Berkeley) 4/27/95 +.\" +.Dd April 27, 1995 +.Dt WHEREIS 1 +.Os +.Sh NAME +.Nm whereis +.Nd locate programs +.Sh SYNOPSIS +.Nm +.Op Ar program ... +.Sh DESCRIPTION +The +.Nm +utility checks the standard binary directories for the specified programs, +printing out the paths of any it finds. +.Pp +The path searched is the string returned by the +.Xr sysctl 8 +utility for the +.Dq user.cs_path +string. +.Sh SEE ALSO +.Xr find 1 , +.Xr locate 1 , +.Xr man 1 , +.Xr which 1 , +.Xr sysctl 8 +.Sh COMPATIBILITY +The historic flags and arguments for the +.Nm +utility are no longer available in this version. +.Sh HISTORY +The +.Nm +command appeared in +.Bx 3.0 . diff --git a/shell_cmds/whereis/whereis.c b/shell_cmds/whereis/whereis.c new file mode 100644 index 0000000..680f1f3 --- /dev/null +++ b/shell_cmds/whereis/whereis.c @@ -0,0 +1,128 @@ +/* $NetBSD: whereis.c,v 1.8 1997/10/20 02:22:55 mrg Exp $ */ + +/*- + * Copyright (c) 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +#ifndef lint +__COPYRIGHT("@(#) Copyright (c) 1993\n\ + The Regents of the University of California. All rights reserved.\n"); +#endif /* not lint */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)whereis.c 8.3 (Berkeley) 5/4/95"; +#endif +__RCSID("$NetBSD: whereis.c,v 1.8 1997/10/20 02:22:55 mrg Exp $"); +#endif /* not lint */ + +#include <sys/param.h> +#include <sys/stat.h> +#include <sys/sysctl.h> + +#include <err.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +void usage __P((void)); +int main __P((int, char *[])); + +int +main(argc, argv) + int argc; + char *argv[]; +{ + struct stat sb; + size_t len; + int ch, sverrno, mib[2]; + char *p, *t, *std, path[MAXPATHLEN]; + + while ((ch = getopt(argc, argv, "")) != -1) + switch (ch) { + case '?': + default: + usage(); + } + argc -= optind; + argv += optind; + + if (argc == 0) + usage(); + + /* Retrieve the standard path. */ + mib[0] = CTL_USER; + mib[1] = USER_CS_PATH; + if (sysctl(mib, 2, NULL, &len, NULL, 0) == -1) + return (-1); + if (len == 0) + err(1, "user_cs_path: sysctl: zero length\n"); + if ((std = malloc(len)) == NULL) + err(1, "%s", ""); + if (sysctl(mib, 2, std, &len, NULL, 0) == -1) { + sverrno = errno; + free(std); + errno = sverrno; + err(1, "sysctl: user_cs_path"); + } + + /* For each path, for each program... */ + for (; *argv; ++argv) + for (p = std;; *p++ = ':') { + t = p; + if ((p = strchr(p, ':')) != NULL) { + *p = '\0'; + if (t == p) + t = "."; + } else + if (strlen(t) == 0) + t = "."; + (void)snprintf(path, sizeof(path), "%s/%s", t, *argv); + if (!stat(path, &sb)) + (void)printf("%s\n", path); + if (p == NULL) + break; + } + + return (0); +} + +void +usage() +{ + + (void)fprintf(stderr, "usage: whereis program [...]\n"); + exit (1); +} diff --git a/shell_cmds/which/which.1 b/shell_cmds/which/which.1 new file mode 100644 index 0000000..d0f2fcb --- /dev/null +++ b/shell_cmds/which/which.1 @@ -0,0 +1,85 @@ +.\" Manpage Copyright (c) 1995, Jordan Hubbard <jkh@FreeBSD.org> +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the FreeBSD Project +.\" its contributors. +.\" 4. Neither the name of the FreeBSD Project nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTOR ``AS IS'' AND ANY EXPRESS OR +.\" IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +.\" OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +.\" IN NO EVENT SHALL THE CONTRIBUTOR BE LIABLE FOR ANY DIRECT, INDIRECT, +.\" INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +.\" NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +.\" DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +.\" THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +.\" (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +.\" OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +.\" +.\" $FreeBSD: head/usr.bin/which/which.1 267773 2014-06-23 08:23:05Z bapt $ +.\" +.Dd December 13, 2006 +.Dt WHICH 1 +.Os +.Sh NAME +.Nm which +.Nd "locate a program file in the user's path" +.Sh SYNOPSIS +.Nm +.Op Fl as +.Ar program ... +.Sh DESCRIPTION +The +.Nm +utility +takes a list of command names and searches the path for each executable +file that would be run had these commands actually been invoked. +.Pp +The following options are available: +.Bl -tag -width indent +.It Fl a +List all instances of executables found (instead of just the first one +of each). +.It Fl s +No output, just return 0 if all of the executables are found, or 1 if +some were not found. +.El +.Pp +Some shells may provide a builtin +.Nm +command which is similar or identical to this utility. +Consult the +.Xr builtin 1 +manual page. +.Sh SEE ALSO +.Xr builtin 1 , +.Xr csh 1 , +.Xr find 1 , +.Xr locate 1 , +.Xr whereis 1 +.Sh HISTORY +The +.Nm +command first appeared in +.Fx 2.1 . +.Sh AUTHORS +.An -nosplit +The +.Nm +utility was originally written in Perl and was contributed by +.An Wolfram Schneider Aq Mt wosch@FreeBSD.org . +The current version of +.Nm +was rewritten in C by +.An Daniel Papasian Aq Mt dpapasia@andrew.cmu.edu . diff --git a/shell_cmds/which/which.c b/shell_cmds/which/which.c new file mode 100644 index 0000000..7e9d662 --- /dev/null +++ b/shell_cmds/which/which.c @@ -0,0 +1,146 @@ +/** + * Copyright (c) 2000 Dan Papasian. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/cdefs.h> + +__FBSDID("$FreeBSD: head/usr.bin/which/which.c 227245 2011-11-06 18:50:26Z ed $"); + +#include <sys/stat.h> +#include <sys/param.h> + +#include <err.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +static void usage(void); +static int print_matches(char *, char *); + +static int silent; +static int allpaths; + +int +main(int argc, char **argv) +{ + char *p, *path; + ssize_t pathlen; + int opt, status; + + status = EXIT_SUCCESS; + + while ((opt = getopt(argc, argv, "as")) != -1) { + switch (opt) { + case 'a': + allpaths = 1; + break; + case 's': + silent = 1; + break; + default: + usage(); + break; + } + } + + argv += optind; + argc -= optind; + + if (argc == 0) + usage(); + + if ((p = getenv("PATH")) == NULL) + exit(EXIT_FAILURE); + pathlen = strlen(p) + 1; + path = malloc(pathlen); + if (path == NULL) + err(EXIT_FAILURE, NULL); + + while (argc > 0) { + memcpy(path, p, pathlen); + + if (strlen(*argv) >= FILENAME_MAX || + print_matches(path, *argv) == -1) + status = EXIT_FAILURE; + + argv++; + argc--; + } + + exit(status); +} + +static void +usage(void) +{ + + (void)fprintf(stderr, "usage: which [-as] program ...\n"); + exit(EXIT_FAILURE); +} + +static int +is_there(char *candidate) +{ + struct stat fin; + + /* XXX work around access(2) false positives for superuser */ + if (access(candidate, X_OK) == 0 && + stat(candidate, &fin) == 0 && + S_ISREG(fin.st_mode) && + (getuid() != 0 || + (fin.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) != 0)) { + if (!silent) + printf("%s\n", candidate); + return (1); + } + return (0); +} + +static int +print_matches(char *path, char *filename) +{ + char candidate[PATH_MAX]; + const char *d; + int found; + + if (strchr(filename, '/') != NULL) + return (is_there(filename) ? 0 : -1); + found = 0; + while ((d = strsep(&path, ":")) != NULL) { + if (*d == '\0') + d = "."; + if (snprintf(candidate, sizeof(candidate), "%s/%s", d, + filename) >= (int)sizeof(candidate)) + continue; + if (is_there(candidate)) { + found = 1; + if (!allpaths) + break; + } + } + return (found ? 0 : -1); +} + diff --git a/shell_cmds/who/utmpentry.c b/shell_cmds/who/utmpentry.c new file mode 100644 index 0000000..b0e0914 --- /dev/null +++ b/shell_cmds/who/utmpentry.c @@ -0,0 +1,348 @@ +/* $NetBSD: utmpentry.c,v 1.15 2008/07/13 20:07:48 dholland Exp $ */ + +/*- + * Copyright (c) 2002 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Christos Zoulas. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +#ifndef lint +__RCSID("$NetBSD: utmpentry.c,v 1.15 2008/07/13 20:07:48 dholland Exp $"); +#endif + +#include <sys/stat.h> + +#include <time.h> +#include <string.h> +#include <err.h> +#include <stdlib.h> +#ifdef __APPLE__ +#include <stdint.h> +#endif /* __APPLE__ */ + +#ifdef SUPPORT_UTMP +#include <utmp.h> +#endif +#ifdef SUPPORT_UTMPX +#include <utmpx.h> +#endif + +#include "utmpentry.h" + +#ifdef __APPLE__ +#define timespecclear(tsp) (tsp)->tv_sec = (time_t)((tsp)->tv_nsec = 0L) +#define timespeccmp(tsp, usp, cmp) \ + (((tsp)->tv_sec == (usp)->tv_sec) ? \ + ((tsp)->tv_nsec cmp (usp)->tv_nsec) : \ + ((tsp)->tv_sec cmp (usp)->tv_sec)) +#endif /* __APPLE__ */ + +/* Fail the compile if x is not true, by constructing an illegal type. */ +#define COMPILE_ASSERT(x) ((void)sizeof(struct { unsigned : ((x) ? 1 : -1); })) + + +#ifdef SUPPORT_UTMP +static void getentry(struct utmpentry *, struct utmp *); +static struct timespec utmptime = {0, 0}; +#endif +#ifdef SUPPORT_UTMPX +static void getentryx(struct utmpentry *, struct utmpx *); +static struct timespec utmpxtime = {0, 0}; +#endif +#if defined(SUPPORT_UTMPX) || defined(SUPPORT_UTMP) +static int setup(const char *); +static void adjust_size(struct utmpentry *e); +#endif + +int maxname = 8, maxline = 8, maxhost = 16; +int etype = 1 << USER_PROCESS; +static int numutmp = 0; +static struct utmpentry *ehead; + +#if defined(SUPPORT_UTMPX) || defined(SUPPORT_UTMP) +static void +adjust_size(struct utmpentry *e) +{ + int max; + + if ((max = strlen(e->name)) > maxname) + maxname = max; + if ((max = strlen(e->line)) > maxline) + maxline = max; + if ((max = strlen(e->host)) > maxhost) + maxhost = max; +} + +static int +setup(const char *fname) +{ + int what = 3; + struct stat st; + const char *sfname; + + if (fname == NULL) { +#ifdef SUPPORT_UTMPX + setutxent(); +#endif +#ifdef SUPPORT_UTMP + setutent(); +#endif + } else { + size_t len = strlen(fname); + if (len == 0) + errx(1, "Filename cannot be 0 length."); +#ifdef __APPLE__ + what = 1; +#else /* !__APPLE__ */ + what = fname[len - 1] == 'x' ? 1 : 2; +#endif /* __APPLE__ */ + if (what == 1) { +#ifdef SUPPORT_UTMPX + if (utmpxname(fname) == 0) + warnx("Cannot set utmpx file to `%s'", + fname); +#else + warnx("utmpx support not compiled in"); +#endif + } else { +#ifdef SUPPORT_UTMP + if (utmpname(fname) == 0) + warnx("Cannot set utmp file to `%s'", + fname); +#else + warnx("utmp support not compiled in"); +#endif + } + } +#ifdef SUPPORT_UTMPX + if (what & 1) { + sfname = fname ? fname : _PATH_UTMPX; + if (stat(sfname, &st) == -1) { + warn("Cannot stat `%s'", sfname); + what &= ~1; + } else { + if (timespeccmp(&st.st_mtimespec, &utmpxtime, >)) + utmpxtime = st.st_mtimespec; + else + what &= ~1; + } + } +#endif +#ifdef SUPPORT_UTMP + if (what & 2) { + sfname = fname ? fname : _PATH_UTMP; + if (stat(sfname, &st) == -1) { + warn("Cannot stat `%s'", sfname); + what &= ~2; + } else { + if (timespeccmp(&st.st_mtimespec, &utmptime, >)) + utmptime = st.st_mtimespec; + else + what &= ~2; + } + } +#endif + return what; +} +#endif + +void +endutentries(void) +{ + struct utmpentry *ep; + +#ifdef SUPPORT_UTMP + timespecclear(&utmptime); +#endif +#ifdef SUPPORT_UTMPX + timespecclear(&utmpxtime); +#endif + ep = ehead; + while (ep) { + struct utmpentry *sep = ep; + ep = ep->next; + free(sep); + } + ehead = NULL; + numutmp = 0; +} + +int +getutentries(const char *fname, struct utmpentry **epp) +{ +#ifdef SUPPORT_UTMPX + struct utmpx *utx; +#endif +#ifdef SUPPORT_UTMP + struct utmp *ut; +#endif +#if defined(SUPPORT_UTMP) || defined(SUPPORT_UTMPX) + struct utmpentry *ep; + int what = setup(fname); + struct utmpentry **nextp = &ehead; + switch (what) { + case 0: + /* No updates */ + *epp = ehead; + return numutmp; + default: + /* Need to re-scan */ + ehead = NULL; + numutmp = 0; + } +#endif + +#ifdef SUPPORT_UTMPX + while ((what & 1) && (utx = getutxent()) != NULL) { +#ifdef __APPLE__ + if (((1 << utx->ut_type) & etype) == 0) +#else /* !__APPLE__ */ + if (fname == NULL && ((1 << utx->ut_type) & etype) == 0) +#endif /* __APPLE__ */ + continue; + if ((ep = calloc(1, sizeof(struct utmpentry))) == NULL) { + warn(NULL); + return 0; + } + getentryx(ep, utx); + *nextp = ep; + nextp = &(ep->next); + } +#endif + +#ifdef SUPPORT_UTMP + if ((etype & (1 << USER_PROCESS)) != 0) { + while ((what & 2) && (ut = getutent()) != NULL) { + if (fname == NULL && (*ut->ut_name == '\0' || + *ut->ut_line == '\0')) + continue; + /* Don't process entries that we have utmpx for */ + for (ep = ehead; ep != NULL; ep = ep->next) { + if (strncmp(ep->line, ut->ut_line, + sizeof(ut->ut_line)) == 0) + break; + } + if (ep != NULL) + continue; + if ((ep = calloc(1, sizeof(*ep))) == NULL) { + warn(NULL); + return 0; + } + getentry(ep, ut); + *nextp = ep; + nextp = &(ep->next); + } + } +#endif + numutmp = 0; +#if defined(SUPPORT_UTMP) || defined(SUPPORT_UTMPX) + if (ehead != NULL) { + struct utmpentry *from = ehead, *save; + + ehead = NULL; + while (from != NULL) { + for (nextp = &ehead; + (*nextp) && strcmp(from->line, (*nextp)->line) > 0; + nextp = &(*nextp)->next) + continue; + save = from; + from = from->next; + save->next = *nextp; + *nextp = save; + numutmp++; + } + } + *epp = ehead; + return numutmp; +#else + *epp = NULL; + return 0; +#endif +} + +#ifdef SUPPORT_UTMP +static void +getentry(struct utmpentry *e, struct utmp *up) +{ + COMPILE_ASSERT(sizeof(e->name) > sizeof(up->ut_name)); + COMPILE_ASSERT(sizeof(e->line) > sizeof(up->ut_line)); + COMPILE_ASSERT(sizeof(e->host) > sizeof(up->ut_host)); + + /* + * e has just been calloc'd. We don't need to clear it or + * append null-terminators, because its length is strictly + * greater than the source string. Use strncpy to _read_ + * up->ut_* because they may not be terminated. For this + * reason we use the size of the _source_ as the length + * argument. + */ + (void)strncpy(e->name, up->ut_name, sizeof(up->ut_name)); + (void)strncpy(e->line, up->ut_line, sizeof(up->ut_line)); + (void)strncpy(e->host, up->ut_host, sizeof(up->ut_host)); + + e->tv.tv_sec = up->ut_time; + e->tv.tv_usec = 0; + e->pid = 0; + e->term = 0; + e->exit = 0; + e->sess = 0; + e->type = USER_PROCESS; + adjust_size(e); +} +#endif + +#ifdef SUPPORT_UTMPX +static void +getentryx(struct utmpentry *e, struct utmpx *up) +{ + COMPILE_ASSERT(sizeof(e->name) > sizeof(up->ut_name)); + COMPILE_ASSERT(sizeof(e->line) > sizeof(up->ut_line)); + COMPILE_ASSERT(sizeof(e->host) > sizeof(up->ut_host)); + + /* + * e has just been calloc'd. We don't need to clear it or + * append null-terminators, because its length is strictly + * greater than the source string. Use strncpy to _read_ + * up->ut_* because they may not be terminated. For this + * reason we use the size of the _source_ as the length + * argument. + */ + (void)strncpy(e->name, up->ut_name, sizeof(up->ut_name)); + (void)strncpy(e->line, up->ut_line, sizeof(up->ut_line)); + (void)strncpy(e->host, up->ut_host, sizeof(up->ut_host)); + + e->tv = up->ut_tv; + e->pid = up->ut_pid; +#ifndef __APPLE__ + e->term = up->ut_exit.e_termination; + e->exit = up->ut_exit.e_exit; + e->sess = up->ut_session; +#endif /* !__APPLE__ */ + e->type = up->ut_type; + adjust_size(e); +} +#endif diff --git a/shell_cmds/who/utmpentry.h b/shell_cmds/who/utmpentry.h new file mode 100644 index 0000000..012478a --- /dev/null +++ b/shell_cmds/who/utmpentry.h @@ -0,0 +1,78 @@ +/* $NetBSD: utmpentry.h,v 1.7 2008/07/13 20:07:49 dholland Exp $ */ + +/*- + * Copyright (c) 2002 The NetBSD Foundation, Inc. + * All rights reserved. + * + * This code is derived from software contributed to The NetBSD Foundation + * by Christos Zoulas. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR + * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + * POSSIBILITY OF SUCH DAMAGE. + */ + +#if defined(SUPPORT_UTMPX) +# include <utmpx.h> +# define WHO_NAME_LEN _UTX_USERSIZE +# define WHO_LINE_LEN _UTX_LINESIZE +# define WHO_HOST_LEN _UTX_HOSTSIZE +#elif defined(SUPPORT_UTMP) +# include <utmp.h> +# define WHO_NAME_LEN UT_NAMESIZE +# define WHO_LINE_LEN UT_LINESIZE +# define WHO_HOST_LEN UT_HOSTSIZE +#else +# error Either SUPPORT_UTMPX or SUPPORT_UTMP must be defined! +#endif + + +struct utmpentry { + char name[WHO_NAME_LEN + 1]; + char line[WHO_LINE_LEN + 1]; + char host[WHO_HOST_LEN + 1]; + struct timeval tv; + pid_t pid; +#ifndef __APPLE__ + uint16_t term; + uint16_t exit; + uint16_t sess; +#endif /* !__APPLE__ */ + uint16_t type; + struct utmpentry *next; +}; + +extern int maxname, maxline, maxhost; +extern int etype; + +/* + * getutentries provides a linked list of struct utmpentry and returns + * the number of entries. The first argument, if not null, names an + * alternate utmp(x) file to look in. + * + * The memory returned by getutentries belongs to getutentries. The + * list returned (or elements of it) may be returned again later if + * utmp hasn't changed in the meantime. + * + * endutentries clears and frees the cached data. + */ + +int getutentries(const char *, struct utmpentry **); +void endutentries(void); diff --git a/shell_cmds/who/who.1 b/shell_cmds/who/who.1 new file mode 100644 index 0000000..dbcbdf1 --- /dev/null +++ b/shell_cmds/who/who.1 @@ -0,0 +1,130 @@ +.\" $NetBSD: who.1,v 1.22 2007/01/18 00:15:05 wiz Exp $ +.\" +.\" Copyright (c) 1986, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)who.1 8.2 (Berkeley) 12/30/93 +.\" +.Dd January 17, 2007 +.Dt WHO 1 +.Os +.Sh NAME +.Nm who +.Nd display who is logged in +.Sh SYNOPSIS +.Nm +.Op Fl abdHlmpqrsTtu +.Op Ar file +.Nm +.Ar am i +.Sh DESCRIPTION +The +.Nm +utility displays a list of all users currently logged on, showing for +each user the login name, tty name, the date and time of login, and +hostname if not local. +.Pp +Available options: +.Pp +.Bl -tag -width file +.It Fl a +Same as +.Fl bdlprTtu . +.It Fl b +Time of last system boot. +.It Fl d +Print dead processes. +.It Fl H +Write column headings above the regular output. +.It Fl l +Print system login processes (unsupported). +.It Fl m +Only print information about the current terminal. +This is the +.Tn POSIX +way of saying +.Nm +.Ar am i . +.It Fl p +Print active processes spawned by +.Xr launchd 8 +(unsupported). +.It Fl q +.Dq Quick mode : +List only the names and the number of users currently logged on. +When this option is used, all other options are ignored. +.It Fl r +Print the current runlevel. +This is meaningless on Mac OS X. +.It Fl s +List only the name, line and time fields. +This is the default. +.It Fl T +Print a character after the user name indicating the state of the +terminal line: +.Sq + +if the terminal is writable; +.Sq - +if it is not; +and +.Sq \&? +if a bad line is encountered. +.It Fl t +Print last system clock change (unsupported). +.It Fl u +Print the idle time for each user, and the associated process ID. +.It Ar \&am I +Returns the invoker's real user name. +.It Ar file +By default, +.Nm +gathers information from the file +.Pa /var/run/utmpx . +An alternative +.Ar file +may be specified. +.El +.Sh FILES +.Bl -tag -width /var/run/utmpx -compact +.It Pa /var/run/utmpx +.El +.Sh SEE ALSO +.Xr last 1 , +.Xr mesg 1 , +.Xr users 1 , +.Xr getuid 2 , +.Xr utmpx 5 +.Sh STANDARDS +The +.Nm +utility conforms to +.St -p1003.1-2001 . +.Sh HISTORY +A +.Nm +utility appeared in +.At v6 . diff --git a/shell_cmds/who/who.c b/shell_cmds/who/who.c new file mode 100644 index 0000000..f0e356f --- /dev/null +++ b/shell_cmds/who/who.c @@ -0,0 +1,453 @@ +/* $NetBSD: who.c,v 1.23 2008/07/24 15:35:41 christos Exp $ */ + +/* + * Copyright (c) 1989, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * Michael Fischbein. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +#ifndef lint +__COPYRIGHT("@(#) Copyright (c) 1989, 1993\ + The Regents of the University of California. All rights reserved."); +#endif /* not lint */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)who.c 8.1 (Berkeley) 6/6/93"; +#endif +__RCSID("$NetBSD: who.c,v 1.23 2008/07/24 15:35:41 christos Exp $"); +#endif /* not lint */ + +#include <sys/types.h> +#include <sys/stat.h> + +#include <err.h> +#include <locale.h> +#include <pwd.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <unistd.h> +#ifdef SUPPORT_UTMP +#include <utmp.h> +#endif +#ifdef SUPPORT_UTMPX +#include <utmpx.h> +#endif +#ifdef __APPLE__ +#include <limits.h> +#include <paths.h> +#include <stdint.h> +#endif /* __APPLE__ */ + +#include "utmpentry.h" + +#ifdef __APPLE__ +#define __UNCONST(a) ((void *)(unsigned long)(const void *)(a)) +#endif /* __APPLE__ */ + +static void output_labels(void); +static void who_am_i(const char *, int); +static void usage(void) __dead; +static void process(const char *, int); +static void eprint(const struct utmpentry *); +static void print(const char *, const char *, time_t, const char *, pid_t pid, + uint16_t term, uint16_t xit, uint16_t sess, uint16_t type); +static void quick(const char *); + +static int show_term; /* show term state */ +static int show_idle; /* show idle time */ +#ifndef __APPLE__ +static int show_details; /* show exit status etc. */ +#endif /* !__APPLE__ */ + +struct ut_type_names { + int type; + const char *name; +} ut_type_names[] = { +#ifdef SUPPORT_UTMPX + { EMPTY, "empty" }, + { RUN_LVL, "run level" }, + { BOOT_TIME, "boot time" }, + { OLD_TIME, "old time" }, + { NEW_TIME, "new time" }, + { INIT_PROCESS, "init process" }, + { LOGIN_PROCESS, "login process" }, + { USER_PROCESS, "user process" }, + { DEAD_PROCESS, "dead process" }, +#if defined(_NETBSD_SOURCE) + { ACCOUNTING, "accounting" }, + { SIGNATURE, "signature" }, + { DOWN_TIME, "down time" }, +#endif /* _NETBSD_SOURCE */ +#endif /* SUPPORT_UTMPX */ + { -1, "unknown" } +}; + +int +main(int argc, char *argv[]) +{ + int c, only_current_term, show_labels, quick_mode, default_mode; + int et = 0; + + setlocale(LC_ALL, ""); + + only_current_term = show_term = show_idle = show_labels = 0; + quick_mode = default_mode = 0; + + while ((c = getopt(argc, argv, "abdHlmpqrsTtuv")) != -1) { + switch (c) { + case 'a': + et = -1; +#ifdef __APPLE__ + show_idle = 1; +#else /* !__APPLE__ */ + show_idle = show_details = 1; +#endif /* __APPLE__ */ + break; + case 'b': + et |= (1 << BOOT_TIME); + break; + case 'd': + et |= (1 << DEAD_PROCESS); + break; + case 'H': + show_labels = 1; + break; + case 'l': + et |= (1 << LOGIN_PROCESS); + break; + case 'm': + only_current_term = 1; + break; + case 'p': + et |= (1 << INIT_PROCESS); + break; + case 'q': + quick_mode = 1; + break; + case 'r': + et |= (1 << RUN_LVL); + break; + case 's': + default_mode = 1; + break; + case 'T': + show_term = 1; + break; + case 't': + et |= (1 << NEW_TIME); + break; + case 'u': + show_idle = 1; + break; +#ifndef __APPLE__ + case 'v': + show_details = 1; + break; +#endif /* !__APPLE__ */ + default: + usage(); + /* NOTREACHED */ + } + } + argc -= optind; + argv += optind; + + if (et != 0) + etype = et; + +#ifndef __APPLE__ + if (chdir("/dev")) { + err(EXIT_FAILURE, "cannot change directory to /dev"); + /* NOTREACHED */ + } +#endif /* !__APPLE__ */ + + if (default_mode) + only_current_term = show_term = show_idle = 0; + + switch (argc) { + case 0: /* who */ + if (quick_mode) { + quick(NULL); + } else if (only_current_term) { + who_am_i(NULL, show_labels); + } else { + process(NULL, show_labels); + } + break; + case 1: /* who utmp_file */ + if (quick_mode) { + quick(*argv); + } else if (only_current_term) { + who_am_i(*argv, show_labels); + } else { + process(*argv, show_labels); + } + break; + case 2: /* who am i */ + who_am_i(NULL, show_labels); + break; + default: + usage(); + /* NOTREACHED */ + } + + return 0; +} + +static char * +strrstr(const char *str, const char *pat) +{ + const char *estr; + size_t len; + if (*pat == '\0') + return __UNCONST(str); + + len = strlen(pat); + + for (estr = str + strlen(str); str < estr; estr--) + if (strncmp(estr, pat, len) == 0) + return __UNCONST(estr); + return NULL; +} + +static void +who_am_i(const char *fname, int show_labels) +{ + struct passwd *pw; + const char *p; + char *t; + time_t now; + struct utmpentry *ehead, *ep; + + /* search through the utmp and find an entry for this tty */ + if ((p = ttyname(STDIN_FILENO)) != NULL) { + + /* strip directory prefixes for ttys */ + if ((t = strrstr(p, "/pts/")) != NULL || + (t = strrchr(p, '/')) != NULL) + p = t + 1; + + (void)getutentries(fname, &ehead); + for (ep = ehead; ep; ep = ep->next) + if (strcmp(ep->line, p) == 0) { + if (show_labels) + output_labels(); + eprint(ep); + return; + } + } else + p = "tty??"; + + (void)time(&now); + pw = getpwuid(getuid()); + if (show_labels) + output_labels(); + print(pw ? pw->pw_name : "?", p, now, "", getpid(), 0, 0, 0, 0); +} + +static void +process(const char *fname, int show_labels) +{ + struct utmpentry *ehead, *ep; + (void)getutentries(fname, &ehead); + if (show_labels) + output_labels(); + for (ep = ehead; ep != NULL; ep = ep->next) + eprint(ep); +#ifdef __APPLE__ + if ((etype & (1 << RUN_LVL)) != 0) { + printf(" . run-level 3\n"); + } +#endif /* __APPLE__ */ +} + +static void +eprint(const struct utmpentry *ep) +{ + print(ep->name, ep->line, (time_t)ep->tv.tv_sec, ep->host, ep->pid, +#ifdef __APPLE__ + 0, 0, 0, ep->type); +#else /* !__APPLE__ */ + ep->term, ep->exit, ep->sess, ep->type); +#endif /* __APPLE__ */ +} + +static void +print(const char *name, const char *line, time_t t, const char *host, + pid_t pid, uint16_t term, uint16_t xit, uint16_t sess, uint16_t type) +{ + struct stat sb; + char state; + static time_t now = 0; + time_t idle; + const char *types = NULL; + size_t i; + + state = '?'; + idle = 0; + + for (i = 0; ut_type_names[i].type >= 0; i++) { + types = ut_type_names[i].name; + if (ut_type_names[i].type == type) + break; + } + + if (show_term || show_idle) { + if (now == 0) + time(&now); + +#ifdef __APPLE__ + char tty[PATH_MAX + 1]; + snprintf(tty, sizeof(tty), "%s%s", _PATH_DEV, line); + if (stat(tty, &sb) == 0) { +#else /* !__APPLE__ */ + if (stat(line, &sb) == 0) { +#endif /* __APPLE__ */ + state = (sb.st_mode & 020) ? '+' : '-'; + idle = now - sb.st_atime; + } + + } + +#ifdef __APPLE__ + switch (type) { + case LOGIN_PROCESS: + (void)printf("%-*.*s ", maxname, maxname, "LOGIN"); + break; + case BOOT_TIME: + (void)printf("%-*.*s ", maxname, maxname, "reboot"); + break; + default: + (void)printf("%-*.*s ", maxname, maxname, name); + break; + } +#else /* !__APPLE__ */ + (void)printf("%-*.*s ", maxname, maxname, name); +#endif /* __APPLE__ */ + + if (show_term) + (void)printf("%c ", state); + +#ifdef __APPLE__ + (void)printf("%-*.*s ", maxline, maxline, type == BOOT_TIME ? "~" : line); +#else /* !__APPLE__ */ + (void)printf("%-*.*s ", maxline, maxline, line); +#endif /* __APPLE__ */ + (void)printf("%.12s ", ctime(&t) + 4); + + if (show_idle) { + if (idle < 60) + (void)printf(" . "); + else if (idle < (24 * 60 * 60)) + (void)printf("%02ld:%02ld ", + (long)(idle / (60 * 60)), + (long)(idle % (60 * 60)) / 60); + else + (void)printf(" old "); + + (void)printf("\t%6d", pid); + +#ifndef __APPLE__ + if (show_details) { + if (type == RUN_LVL) + (void)printf("\tnew=%c old=%c", term, xit); + else + (void)printf("\tterm=%d exit=%d", term, xit); + (void)printf(" sess=%d", sess); + (void)printf(" type=%s ", types); + } +#endif /* !__APPLE__ */ + } + +#ifdef __APPLE__ + /* 6179576 */ + if (type == DEAD_PROCESS) + (void)printf("\tterm=%d exit=%d", 0, 0); +#endif /* __APPLE__ */ + + if (*host) + (void)printf("\t(%.*s)", maxhost, host); + (void)putchar('\n'); +} + +static void +output_labels(void) +{ + (void)printf("%-*.*s ", maxname, maxname, "USER"); + + if (show_term) + (void)printf("S "); + + (void)printf("%-*.*s ", maxline, maxline, "LINE"); + (void)printf("WHEN "); + + if (show_idle) { + (void)printf("IDLE "); + (void)printf("\t PID"); + + (void)printf("\tCOMMENT"); + } + + (void)putchar('\n'); +} + +static void +quick(const char *fname) +{ + struct utmpentry *ehead, *ep; + int num = 0; + + (void)getutentries(fname, &ehead); + for (ep = ehead; ep != NULL; ep = ep->next) { + (void)printf("%-*s ", maxname, ep->name); + if ((++num % 8) == 0) + (void)putchar('\n'); + } + if (num % 8) + (void)putchar('\n'); + + (void)printf("# users = %d\n", num); +} + +static void +usage(void) +{ +#ifdef __APPLE__ + (void)fprintf(stderr, "Usage: %s [-abdHlmpqrsTtu] [file]\n\t%s am i\n", +#else /* !__APPLE__ */ + (void)fprintf(stderr, "Usage: %s [-abdHlmqrsTtuv] [file]\n\t%s am i\n", +#endif /* __APPLE__ */ + getprogname(), getprogname()); + exit(EXIT_FAILURE); +} diff --git a/shell_cmds/xargs/pathnames.h b/shell_cmds/xargs/pathnames.h new file mode 100644 index 0000000..45c0fff --- /dev/null +++ b/shell_cmds/xargs/pathnames.h @@ -0,0 +1,36 @@ +/*- + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + * + * @(#)pathnames.h 8.1 (Berkeley) 6/6/93 + */ + +#define _PATH_ECHO "/bin/echo" diff --git a/shell_cmds/xargs/strnsubst.c b/shell_cmds/xargs/strnsubst.c new file mode 100644 index 0000000..b00b319 --- /dev/null +++ b/shell_cmds/xargs/strnsubst.c @@ -0,0 +1,109 @@ +/* $xMach: strnsubst.c,v 1.3 2002/02/23 02:10:24 jmallett Exp $ */ + +/* + * Copyright (c) 2002 J. Mallett. All rights reserved. + * You may do whatever you want with this file as long as + * the above copyright and this notice remain intact, along + * with the following statement: + * For the man who taught me vi, and who got too old, too young. + */ + +#include <sys/cdefs.h> +__FBSDID("$FreeBSD: src/usr.bin/xargs/strnsubst.c,v 1.7 2004/10/18 15:40:47 cperciva Exp $"); + +#include <err.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +void strnsubst(char **, const char *, const char *, size_t); + +/* + * Replaces str with a string consisting of str with match replaced with + * replstr as many times as can be done before the constructed string is + * maxsize bytes large. It does not free the string pointed to by str, it + * is up to the calling program to be sure that the original contents of + * str as well as the new contents are handled in an appropriate manner. + * If replstr is NULL, then that internally is changed to a nil-string, so + * that we can still pretend to do somewhat meaningful substitution. + * No value is returned. + */ +void +strnsubst(char **str, const char *match, const char *replstr, size_t maxsize) +{ + char *s1, *s2, *this; + + s1 = *str; + if (s1 == NULL) + return; + /* + * If maxsize is 0 then set it to the length of s1, because we have + * to duplicate s1. XXX we maybe should double-check whether the match + * appears in s1. If it doesn't, then we also have to set the length + * to the length of s1, to avoid modifying the argument. It may make + * sense to check if maxsize is <= strlen(s1), because in that case we + * want to return the unmodified string, too. + */ + if (maxsize == 0) { + match = NULL; + maxsize = strlen(s1) + 1; + } + s2 = calloc(1, maxsize); + if (s2 == NULL) + err(1, "calloc"); + + if (replstr == NULL) + replstr = ""; + + if (match == NULL || replstr == NULL || maxsize == strlen(s1)) { + strlcpy(s2, s1, maxsize); + goto done; + } + + for (;;) { + this = strstr(s1, match); + if (this == NULL) + break; + if ((strlen(s2) + strlen(s1) + strlen(replstr) - + strlen(match) + 1) > maxsize) { + strlcat(s2, s1, maxsize); + goto done; + } + strncat(s2, s1, (uintptr_t)this - (uintptr_t)s1); + strcat(s2, replstr); + s1 = this + strlen(match); + } + strcat(s2, s1); +done: + *str = s2; + return; +} + +#ifdef TEST +#include <stdio.h> + +int +main(void) +{ + char *x, *y, *z, *za; + + x = "{}%$"; + strnsubst(&x, "%$", "{} enpury!", 255); + y = x; + strnsubst(&y, "}{}", "ybir", 255); + z = y; + strnsubst(&z, "{", "v ", 255); + za = z; + strnsubst(&z, NULL, za, 255); + if (strcmp(z, "v ybir enpury!") == 0) + printf("strnsubst() seems to work!\n"); + else + printf("strnsubst() is broken.\n"); + printf("%s\n", z); + free(x); + free(y); + free(z); + free(za); + return 0; +} +#endif diff --git a/shell_cmds/xargs/xargs.1 b/shell_cmds/xargs/xargs.1 new file mode 100644 index 0000000..b9d5add --- /dev/null +++ b/shell_cmds/xargs/xargs.1 @@ -0,0 +1,389 @@ +.\" Copyright (c) 1990, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" This code is derived from software contributed to Berkeley by +.\" John B. Roll Jr. and the Institute of Electrical and Electronics +.\" Engineers, Inc. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)xargs.1 8.1 (Berkeley) 6/6/93 +.\" $FreeBSD: src/usr.bin/xargs/xargs.1,v 1.34 2005/05/21 09:55:09 ru Exp $ +.\" $xMach: xargs.1,v 1.2 2002/02/23 05:23:37 tim Exp $ +.\" +.Dd August 4, 2015 +.Dt XARGS 1 +.Os +.Sh NAME +.Nm xargs +.Nd "construct argument list(s) and execute utility" +.Sh SYNOPSIS +.Nm +.Op Fl 0oprt +.Op Fl E Ar eofstr +.Oo +.Fl I Ar replstr +.Op Fl R Ar replacements +.Op Fl S Ar replsize +.Oc +.Op Fl J Ar replstr +.Op Fl L Ar number +.Oo +.Fl n Ar number +.Op Fl x +.Oc +.Op Fl P Ar maxprocs +.Op Fl s Ar size +.Op Ar utility Op Ar argument ... +.Sh DESCRIPTION +The +.Nm +utility reads space, tab, newline and end-of-file delimited strings +from the standard input and executes +.Ar utility +with the strings as +arguments. +.Pp +Any arguments specified on the command line are given to +.Ar utility +upon each invocation, followed by some number of the arguments read +from the standard input of +.Nm . +This is repeated until standard input is exhausted. +.Pp +Spaces, tabs and newlines may be embedded in arguments using single +(``\ '\ '') +or double (``"'') quotes or backslashes (``\e''). +Single quotes escape all non-single quote characters, excluding newlines, +up to the matching single quote. +Double quotes escape all non-double quote characters, excluding newlines, +up to the matching double quote. +Any single character, including newlines, may be escaped by a backslash. +.Pp +The options are as follows: +.Bl -tag -width indent +.It Fl 0 +Change +.Nm +to expect NUL +(``\\0'') +characters as separators, instead of spaces and newlines. +This is expected to be used in concert with the +.Fl print0 +function in +.Xr find 1 . +.It Fl E Ar eofstr +Use +.Ar eofstr +as a logical EOF marker. +.It Fl I Ar replstr +Execute +.Ar utility +for each input line, replacing one or more occurrences of +.Ar replstr +in up to +.Ar replacements +(or 5 if no +.Fl R +flag is specified) arguments to +.Ar utility +with the entire line of input. +The resulting arguments, after replacement is done, will not be allowed to grow +beyond +.Ar replsize +(or 255 if no +.Fl S +flag is specified) +bytes; this is implemented by concatenating as much of the argument +containing +.Ar replstr +as possible, to the constructed arguments to +.Ar utility , +up to +.Ar replsize +bytes. +The size limit does not apply to arguments to +.Ar utility +which do not contain +.Ar replstr , +and furthermore, no replacement will be done on +.Ar utility +itself. +Implies +.Fl x . +.It Fl J Ar replstr +If this option is specified, +.Nm +will use the data read from standard input to replace the first occurrence of +.Ar replstr +instead of appending that data after all other arguments. +This option will not affect how many arguments will be read from input +.Pq Fl n , +or the size of the command(s) +.Nm +will generate +.Pq Fl s . +The option just moves where those arguments will be placed in the command(s) +that are executed. +The +.Ar replstr +must show up as a distinct +.Ar argument +to +.Nm . +It will not be recognized if, for instance, it is in the middle of a +quoted string. +Furthermore, only the first occurrence of the +.Ar replstr +will be replaced. +For example, the following command will copy the list of files and +directories which start with an uppercase letter in the current +directory to +.Pa destdir : +.Pp +.Dl /bin/ls -1d [A-Z]* | xargs -J % cp -Rp % destdir +.It Fl L Ar number +Call +.Ar utility +for every +.Ar number +non-empty lines read. +A line ending with a space continues to the next non-empty line. +If EOF is reached and fewer lines have been read than +.Ar number +then +.Ar utility +will be called with the available lines. +The +.Fl L +and +.Fl n +options are mutually-exclusive; the last one given will be used. +.It Fl n Ar number +Set the maximum number of arguments taken from standard input for each +invocation of +.Ar utility . +An invocation of +.Ar utility +will use less than +.Ar number +standard input arguments if the number of bytes accumulated (see the +.Fl s +option) exceeds the specified +.Ar size +or there are fewer than +.Ar number +arguments remaining for the last invocation of +.Ar utility . +The current default value for +.Ar number +is 5000. +.It Fl o +Reopen stdin as +.Pa /dev/tty +in the child process before executing the command. +This is useful if you want +.Nm +to run an interactive application. +.It Fl P Ar maxprocs +Parallel mode: run at most +.Ar maxprocs +invocations of +.Ar utility +at once. +If +.Ar maxprocs +is set to 0, +.Nm +will run as many processes as possible. +.It Fl p +Echo each command to be executed and ask the user whether it should be +executed. +An affirmative response, +.Ql y +in the POSIX locale, +causes the command to be executed, any other response causes it to be +skipped. +No commands are executed if the process is not attached to a terminal. +.It Fl r +Compatibility with GNU +.Nm . +The GNU version of +.Nm +runs the +.Ar utility +argument at least once, even if +.Nm +input is empty, and it supports a +.Fl r +option to inhibit this behavior. +The +.Fx +version of +.Nm +does not run the +.Ar utility +argument on empty input, but it supports the +.Fl r +option for command-line compatibility with GNU +.Nm , +but the +.Fl r +option does nothing in the +.Fx +version of +.Nm . +.It Fl R Ar replacements +Specify the maximum number of arguments that +.Fl I +will do replacement in. +If +.Ar replacements +is negative, the number of arguments in which to replace is unbounded. +.It Fl S Ar replsize +Specify the amount of space (in bytes) that +.Fl I +can use for replacements. +The default for +.Ar replsize +is 255. +.It Fl s Ar size +Set the maximum number of bytes for the command line length provided to +.Ar utility . +The sum of the length of the utility name, the arguments passed to +.Ar utility +(including +.Dv NULL +terminators) and the current environment will be less than or equal to +this number. +The current default value for +.Ar size +is +.Dv ARG_MAX +- 4096. +.It Fl t +Echo the command to be executed to standard error immediately before it +is executed. +.It Fl x +Force +.Nm +to terminate immediately if a command line containing +.Ar number +arguments will not fit in the specified (or default) command line length. +.El +.Pp +If +.Ar utility +is omitted, +.Xr echo 1 +is used. +.Pp +Undefined behavior may occur if +.Ar utility +reads from the standard input. +.Pp +The +.Nm +utility exits immediately (without processing any further input) if a +command line cannot be assembled, +.Ar utility +cannot be invoked, an invocation of +.Ar utility +is terminated by a signal, +or an invocation of +.Ar utility +exits with a value of 255, the +.Nm +utility stops processing input and exits after all invocations of +.Ar utility +finish processing. +.Sh LEGACY DESCRIPTION +In legacy mode, the +.Fl L +option treats all newlines as end-of-line, regardless of whether +the line is empty or ends with a space. +In addition, the +.Fl L +and +.Fl n +options are not mutually-exclusive. +.Pp +For more information about legacy mode, see +.Xr compat 5 . +.Sh EXIT STATUS +The +.Nm +utility exits with a value of 0 if no error occurs. +If +.Ar utility +cannot be found, +.Nm +exits with a value of 127, otherwise if +.Ar utility +cannot be executed, +.Nm +exits with a value of 126. +If any other error occurs, +.Nm +exits with a value of 1. +.Sh SEE ALSO +.Xr echo 1 , +.Xr find 1 , +.Xr execvp 3 , +.Xr compat 5 +.Sh STANDARDS +The +.Nm +utility is expected to be +.St -p1003.2 +compliant. +The +.Fl J , o , P, R +and +.Fl S +options are non-standard +.Fx +extensions which may not be available on other operating systems. +.Sh HISTORY +The +.Nm +utility appeared in PWB UNIX. +.Sh BUGS +If +.Ar utility +attempts to invoke another command such that the number of arguments or the +size of the environment is increased, it risks +.Xr execvp 3 +failing with +.Er E2BIG . +.Pp +The +.Nm +utility does not take multibyte characters into account when performing +string comparisons for the +.Fl I +and +.Fl J +options, which may lead to incorrect results in some locales. diff --git a/shell_cmds/xargs/xargs.c b/shell_cmds/xargs/xargs.c new file mode 100644 index 0000000..a1ee480 --- /dev/null +++ b/shell_cmds/xargs/xargs.c @@ -0,0 +1,840 @@ +/*- + * SPDX-License-Identifier: BSD-3-Clause + * + * Copyright (c) 1990, 1993 + * The Regents of the University of California. All rights reserved. + * + * This code is derived from software contributed to Berkeley by + * John B. Roll Jr. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $xMach: xargs.c,v 1.6 2002/02/23 05:27:47 tim Exp $ + */ + +#if 0 +#ifndef lint +static const char copyright[] = +"@(#) Copyright (c) 1990, 1993\n\ + The Regents of the University of California. All rights reserved.\n"; +#endif /* not lint */ + +#ifndef lint +static char sccsid[] = "@(#)xargs.c 8.1 (Berkeley) 6/6/93"; +#endif /* not lint */ +#endif +#include <sys/cdefs.h> +__FBSDID("$FreeBSD$"); + +#include <sys/types.h> +#include <sys/wait.h> +#include <sys/time.h> +#include <sys/param.h> +#include <sys/resource.h> +#include <err.h> +#include <errno.h> +#include <fcntl.h> +#include <langinfo.h> +#include <locale.h> +#include <paths.h> +#include <regex.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> + +#include "pathnames.h" + +#ifdef __APPLE__ +#include <get_compat.h> +#else +#define COMPAT_MODE(a,b) (1) +#endif /* __APPLE__ */ + +static void parse_input(int, char *[]); +static void prerun(int, char *[]); +static int prompt(void); +static void run(char **); +static void usage(void); +void strnsubst(char **, const char *, const char *, size_t); +static pid_t xwait(int block, int *status); +static void xexit(const char *, const int); +static void waitchildren(const char *, int); +static void pids_init(void); +static int pids_empty(void); +static int pids_full(void); +static void pids_add(pid_t pid); +static int pids_remove(pid_t pid); +static int findslot(pid_t pid); +static int findfreeslot(void); +static void clearslot(int slot); + +static int last_was_newline = 1; +static int last_was_blank = 0; + +static char echo[] = _PATH_ECHO; +static char **av, **bxp, **ep, **endxp, **xp; +static char *argp, *bbp, *ebp, *inpline, *p, *replstr; +static const char *eofstr; +static int count, insingle, indouble, oflag, pflag, tflag, Rflag, rval, zflag; +static int cnt, Iflag, jfound, Lflag, Sflag, wasquoted, xflag; +static int curprocs, maxprocs; +static size_t pad9314053; +static pid_t *childpids; + +static volatile int childerr; + +extern char **environ; + +int +main(int argc, char *argv[]) +{ + long arg_max; + int ch, Jflag, nflag, nline; + size_t nargs; + size_t linelen; + struct rlimit rl; + char *endptr; + const char *errstr; + + inpline = replstr = NULL; + ep = environ; + eofstr = ""; + Jflag = nflag = 0; + + (void)setlocale(LC_ALL, ""); + + /* + * POSIX.2 limits the exec line length to ARG_MAX - 2K. Running that + * caused some E2BIG errors, so it was changed to ARG_MAX - 4K. Given + * that the smallest argument is 2 bytes in length, this means that + * the number of arguments is limited to: + * + * (ARG_MAX - 4K - LENGTH(utility + arguments)) / 2. + * + * We arbitrarily limit the number of arguments to 5000. This is + * allowed by POSIX.2 as long as the resulting minimum exec line is + * at least LINE_MAX. Realloc'ing as necessary is possible, but + * probably not worthwhile. + */ + nargs = 5000; + if ((arg_max = sysconf(_SC_ARG_MAX)) == -1) + errx(1, "sysconf(_SC_ARG_MAX) failed"); + nline = arg_max - MAXPATHLEN; /* for argv[0] from execvp() */ + pad9314053 = sizeof(char *); /* reserve for string area rounding */ + while (*ep != NULL) { + /* 1 byte for each '\0' */ + nline -= strlen(*ep++) + 1 + sizeof(*ep); + } + nline -= pad9314053; + maxprocs = 1; + while ((ch = getopt(argc, argv, "0E:I:J:L:n:oP:pR:S:s:rtx")) != -1) + switch (ch) { + case 'E': + eofstr = optarg; + break; + case 'I': + Jflag = 0; + Iflag = 1; + Lflag = 1; + replstr = optarg; + break; + case 'J': + Iflag = 0; + Jflag = 1; + replstr = optarg; + break; + case 'L': + Lflag = strtonum(optarg, 0, INT_MAX, &errstr); + if (errstr) + errx(1, "-L %s: %s", optarg, errstr); + if (COMPAT_MODE("bin/xargs", "Unix2003")) { + nflag = 0; /* Override */ + nargs = 5000; + } + break; + case 'n': + nflag = 1; + if ((nargs = strtol(optarg, NULL, 10)) <= 0) + errx(1, "illegal argument count"); + if (COMPAT_MODE("bin/xargs", "Unix2003")) { + Lflag = 0; /* Override */ + } + break; + case 'o': + oflag = 1; + break; + case 'P': + maxprocs = strtonum(optarg, 0, INT_MAX, &errstr); + if (errstr) + errx(1, "-P %s: %s", optarg, errstr); + if (getrlimit(RLIMIT_NPROC, &rl) != 0) + errx(1, "getrlimit failed"); + if (maxprocs == 0 || maxprocs > rl.rlim_cur) + maxprocs = rl.rlim_cur; + break; + case 'p': + pflag = 1; + break; + case 'R': + Rflag = strtol(optarg, &endptr, 10); + if (*endptr != '\0') + errx(1, "replacements must be a number"); + break; + case 'r': + /* GNU compatibility */ + break; + case 'S': + Sflag = strtoul(optarg, &endptr, 10); + if (*endptr != '\0') + errx(1, "replsize must be a number"); + break; + case 's': + nline = strtonum(optarg, 0, INT_MAX, &errstr); + if (errstr) + errx(1, "-s %s: %s", optarg, errstr); + pad9314053 = 0; /* assume the -s value is valid */ + break; + case 't': + tflag = 1; + break; + case 'x': + xflag = 1; + break; + case '0': + zflag = 1; + break; + case '?': + default: + usage(); + } + argc -= optind; + argv += optind; + + if (!Iflag && Rflag) + usage(); + if (!Iflag && Sflag) + usage(); + if (Iflag && !Rflag) + Rflag = 5; + if (Iflag && !Sflag) + Sflag = 255; + if (xflag && !nflag) + usage(); + if (Iflag || Lflag) + xflag = 1; + if (replstr != NULL && *replstr == '\0') + errx(1, "replstr may not be empty"); + + pids_init(); + + /* + * Allocate pointers for the utility name, the utility arguments, + * the maximum arguments to be read from stdin and the trailing + * NULL. + */ + linelen = 1 + argc + nargs + 1; + if ((av = bxp = malloc(linelen * sizeof(char *))) == NULL) + errx(1, "malloc failed"); + + /* + * Use the user's name for the utility as argv[0], just like the + * shell. Echo is the default. Set up pointers for the user's + * arguments. + */ + if (*argv == NULL) + cnt = strlen(*bxp++ = echo) + pad9314053; + else { + do { + if (Jflag && strcmp(*argv, replstr) == 0) { + char **avj; + jfound = 1; + argv++; + for (avj = argv; *avj; avj++) + cnt += strlen(*avj) + 1 + pad9314053; + break; + } + cnt += strlen(*bxp++ = *argv) + 1 + pad9314053; + } while (*++argv != NULL); + } + + /* + * Set up begin/end/traversing pointers into the array. The -n + * count doesn't include the trailing NULL pointer, so the malloc + * added in an extra slot. + */ + endxp = (xp = bxp) + nargs; + + /* + * Allocate buffer space for the arguments read from stdin and the + * trailing NULL. Buffer space is defined as the default or specified + * space, minus the length of the utility name and arguments. Set up + * begin/end/traversing pointers into the array. The -s count does + * include the trailing NULL, so the malloc didn't add in an extra + * slot. + */ + nline -= cnt; + if (nline <= 0) + errx(1, "insufficient space for command"); + + if ((bbp = malloc((size_t)(nline + 1))) == NULL) + errx(1, "malloc failed"); + ebp = (argp = p = bbp) + nline - 1; + for (;;) + parse_input(argc, argv); +} + +static void +parse_input(int argc, char *argv[]) +{ + int ch, foundeof; + char **avj; + int last_was_backslashed = 0; + + foundeof = 0; + + switch (ch = getchar()) { + case EOF: + /* No arguments since last exec. */ + if (p == bbp) + xexit(*av, rval); + goto arg1; + case ' ': + last_was_blank = 1; + case '\t': + /* Quotes escape tabs and spaces. */ + if (insingle || indouble || zflag) + goto addch; + goto arg2; + case '\0': + if (zflag) { + /* + * Increment 'count', so that nulls will be treated + * as end-of-line, as well as end-of-argument. This + * is needed so -0 works properly with -I and -L. + */ + count++; + goto arg2; + } + goto addch; + case '\n': + if (zflag) + goto addch; + if (COMPAT_MODE("bin/xargs", "Unix2003")) { + if (last_was_newline) { + /* don't count empty line */ + break; + } + if (!last_was_blank ) { + /* only count if NOT continuation line */ + count++; + } + } else { + count++; + } + last_was_newline = 1; + + /* Quotes do not escape newlines. */ +arg1: if (insingle || indouble) { + warnx("unterminated quote"); + xexit(*av, 1); + } +arg2: + foundeof = *eofstr != '\0' && + strncmp(argp, eofstr, p - argp) == 0; + +#ifdef __APPLE__ + /* 6591323: -I specifies that it processes the entire line, + * so only recognize eofstr at the end of a line. */ + if (Iflag && !last_was_newline) + foundeof = 0; + + /* 6591323: Essentially the same as the EOF handling above. */ + if (foundeof && (p - strlen(eofstr) == bbp)) { + waitchildren(*argv, 1); + exit(rval); + } +#endif + + /* Do not make empty args unless they are quoted */ + if ((argp != p || wasquoted) && !foundeof) { + *p++ = '\0'; + *xp++ = argp; + if (Iflag) { + size_t curlen; + + if (inpline == NULL) + curlen = 0; + else { + /* + * If this string is not zero + * length, append a space for + * separation before the next + * argument. + */ + if ((curlen = strlen(inpline))) + strcat(inpline, " "); + } + curlen++; + /* + * Allocate enough to hold what we will + * be holding in a second, and to append + * a space next time through, if we have + * to. + */ + inpline = realloc(inpline, curlen + 2 + + strlen(argp)); + if (inpline == NULL) { + warnx("realloc failed"); + xexit(*av, 1); + } + if (curlen == 1) + strcpy(inpline, argp); + else + strcat(inpline, argp); + } + } + + /* + * If max'd out on args or buffer, or reached EOF, + * run the command. If xflag and max'd out on buffer + * but not on args, object. Having reached the limit + * of input lines, as specified by -L is the same as + * maxing out on arguments. + */ + if (xp == endxp || p + (count * pad9314053) > ebp || ch == EOF || + (Lflag <= count && xflag) || foundeof) { + if (xflag && xp != endxp && p + (count * pad9314053) > ebp) { + warnx("insufficient space for arguments"); + xexit(*av, 1); + } + if (jfound) { + for (avj = argv; *avj; avj++) + *xp++ = *avj; + } + prerun(argc, av); + if (ch == EOF || foundeof) + xexit(*av, rval); + p = bbp; + xp = bxp; + count = 0; + } + argp = p; + wasquoted = 0; + break; + case '\'': + if (indouble || zflag) + goto addch; + insingle = !insingle; + wasquoted = 1; + break; + case '"': + if (insingle || zflag) + goto addch; + indouble = !indouble; + wasquoted = 1; + break; + case '\\': + last_was_backslashed = 1; + if (zflag) + goto addch; + /* Backslash escapes anything, is escaped by quotes. */ + if (!insingle && !indouble && (ch = getchar()) == EOF) { + warnx("backslash at EOF"); + xexit(*av, 1); + } + /* FALLTHROUGH */ + default: +addch: if (p < ebp) { + *p++ = ch; + break; + } + + /* If only one argument, not enough buffer space. */ + if (bxp == xp) { + warnx("insufficient space for argument"); + xexit(*av, 1); + } + /* Didn't hit argument limit, so if xflag object. */ + if (xflag) { + warnx("insufficient space for arguments"); + xexit(*av, 1); + } + + if (jfound) { + for (avj = argv; *avj; avj++) + *xp++ = *avj; + } + prerun(argc, av); + xp = bxp; + cnt = ebp - argp; + memcpy(bbp, argp, (size_t)cnt); + p = (argp = bbp) + cnt; + *p++ = ch; + break; + } + if (ch != ' ') + last_was_blank = 0; + if (ch != '\n' || last_was_backslashed) + last_was_newline = 0; +} + +/* + * Do things necessary before run()'ing, such as -I substitution, + * and then call run(). + */ +static void +prerun(int argc, char *argv[]) +{ + char **tmp, **tmp2, **avj; + int repls; + + repls = Rflag; + + if (argc == 0 || repls == 0) { + *xp = NULL; + run(argv); + return; + } + + avj = argv; + + /* + * Allocate memory to hold the argument list, and + * a NULL at the tail. + */ + tmp = malloc((argc + 1) * sizeof(char *)); + if (tmp == NULL) { + warnx("malloc failed"); + xexit(*argv, 1); + } + tmp2 = tmp; + + /* + * Save the first argument and iterate over it, we + * cannot do strnsubst() to it. + */ + if ((*tmp++ = strdup(*avj++)) == NULL) { + warnx("strdup failed"); + xexit(*argv, 1); + } + + /* + * For each argument to utility, if we have not used up + * the number of replacements we are allowed to do, and + * if the argument contains at least one occurrence of + * replstr, call strnsubst(), else just save the string. + * Iterations over elements of avj and tmp are done + * where appropriate. + */ + while (--argc) { + *tmp = *avj++; + if (repls && strstr(*tmp, replstr) != NULL) { + strnsubst(tmp++, replstr, inpline, (size_t)Sflag); + if (repls > 0) + repls--; + } else { + if ((*tmp = strdup(*tmp)) == NULL) { + warnx("strdup failed"); + xexit(*argv, 1); + } + tmp++; + } + } + + /* + * Run it. + */ + *tmp = NULL; + run(tmp2); + + /* + * Walk from the tail to the head, free along the way. + */ + for (; tmp2 != tmp; tmp--) + free(*tmp); + /* + * Now free the list itself. + */ + free(tmp2); + + /* + * Free the input line buffer, if we have one. + */ + if (inpline != NULL) { + free(inpline); + inpline = NULL; + } +} + +static void +run(char **argv) +{ + pid_t pid; + int fd; + char **avec; + + /* + * If the user wants to be notified of each command before it is + * executed, notify them. If they want the notification to be + * followed by a prompt, then prompt them. + */ + if (tflag || pflag) { + (void)fprintf(stderr, "%s", *argv); + for (avec = argv + 1; *avec != NULL; ++avec) + (void)fprintf(stderr, " %s", *avec); + /* + * If the user has asked to be prompted, do so. + */ + if (pflag) + /* + * If they asked not to exec, return without execution + * but if they asked to, go to the execution. If we + * could not open their tty, break the switch and drop + * back to -t behaviour. + */ + switch (prompt()) { + case 0: + return; + case 1: + goto exec; + case 2: + break; + } + (void)fprintf(stderr, "\n"); + (void)fflush(stderr); + } +exec: + childerr = 0; + switch (pid = vfork()) { + case -1: + warn("vfork"); + xexit(*argv, 1); + case 0: + if (oflag) { + if ((fd = open(_PATH_TTY, O_RDONLY)) == -1) + err(1, "can't open /dev/tty"); + } else { + fd = open(_PATH_DEVNULL, O_RDONLY); + } + if (fd > STDIN_FILENO) { + if (dup2(fd, STDIN_FILENO) != 0) + err(1, "can't dup2 to stdin"); + close(fd); + } + execvp(argv[0], argv); + childerr = errno; + _exit(1); + } + pids_add(pid); + waitchildren(*argv, 0); +} + +/* + * Wait for a tracked child to exit and return its pid and exit status. + * + * Ignores (discards) all untracked child processes. + * Returns -1 and sets errno to ECHILD if no tracked children exist. + * If block is set, waits indefinitely for a child process to exit. + * If block is not set and no children have exited, returns 0 immediately. + */ +static pid_t +xwait(int block, int *status) { + pid_t pid; + + if (pids_empty()) { + errno = ECHILD; + return (-1); + } + + while ((pid = waitpid(-1, status, block ? 0 : WNOHANG)) > 0) + if (pids_remove(pid)) + break; + + return (pid); +} + +static void +xexit(const char *name, const int exit_code) { + waitchildren(name, 1); + exit(exit_code); +} + +static void +waitchildren(const char *name, int waitall) +{ + pid_t pid; + int status; + int cause_exit = 0; + + while ((pid = xwait(waitall || pids_full(), &status)) > 0) { + /* + * If we couldn't invoke the utility or if utility exited + * because of a signal or with a value of 255, warn (per + * POSIX), and then wait until all other children have + * exited before exiting 1-125. POSIX requires us to stop + * reading if child exits because of a signal or with 255, + * but it does not require us to exit immediately; waiting + * is preferable to orphaning. + */ + if (childerr != 0 && cause_exit == 0) { + errno = childerr; + waitall = 1; + cause_exit = errno == ENOENT ? 127 : 126; + warn("%s", name); + } else if (WIFSIGNALED(status)) { + waitall = cause_exit = 1; + warnx("%s: terminated with signal %d; aborting", + name, WTERMSIG(status)); + } else if (WEXITSTATUS(status) == 255) { + waitall = cause_exit = 1; + warnx("%s: exited with status 255; aborting", name); + } else if (WEXITSTATUS(status)) + rval = 1; + } + + if (cause_exit) + exit(cause_exit); + if (pid == -1 && errno != ECHILD) + err(1, "waitpid"); +} + +#define NOPID (0) + +static void +pids_init(void) +{ + int i; + + if ((childpids = malloc(maxprocs * sizeof(*childpids))) == NULL) + errx(1, "malloc failed"); + + for (i = 0; i < maxprocs; i++) + clearslot(i); +} + +static int +pids_empty(void) +{ + + return (curprocs == 0); +} + +static int +pids_full(void) +{ + + return (curprocs >= maxprocs); +} + +static void +pids_add(pid_t pid) +{ + int slot; + + slot = findfreeslot(); + childpids[slot] = pid; + curprocs++; +} + +static int +pids_remove(pid_t pid) +{ + int slot; + + if ((slot = findslot(pid)) < 0) + return (0); + + clearslot(slot); + curprocs--; + return (1); +} + +static int +findfreeslot(void) +{ + int slot; + + if ((slot = findslot(NOPID)) < 0) + errx(1, "internal error: no free pid slot"); + return (slot); +} + +static int +findslot(pid_t pid) +{ + int slot; + + for (slot = 0; slot < maxprocs; slot++) + if (childpids[slot] == pid) + return (slot); + return (-1); +} + +static void +clearslot(int slot) +{ + + childpids[slot] = NOPID; +} + +/* + * Prompt the user about running a command. + */ +static int +prompt(void) +{ + regex_t cre; + size_t rsize; + int match; + char *response; + FILE *ttyfp; + + if ((ttyfp = fopen(_PATH_TTY, "r")) == NULL) + return (2); /* Indicate that the TTY failed to open. */ + (void)fprintf(stderr, "?..."); + (void)fflush(stderr); + if ((response = fgetln(ttyfp, &rsize)) == NULL || + regcomp(&cre, nl_langinfo(YESEXPR), REG_BASIC) != 0) { + (void)fclose(ttyfp); + return (0); + } + response[rsize - 1] = '\0'; + match = regexec(&cre, response, 0, NULL, 0); + (void)fclose(ttyfp); + regfree(&cre); + return (match == 0); +} + +static void +usage(void) +{ + + fprintf(stderr, +"usage: xargs [-0opt] [-E eofstr] [-I replstr [-R replacements] [-S replsize]]\n" +" [-J replstr] [-L number] [-n number [-x]] [-P maxprocs]\n" +" [-s size] [utility [argument ...]]\n"); + exit(1); +} diff --git a/shell_cmds/xcconfigs/base.xcconfig b/shell_cmds/xcconfigs/base.xcconfig new file mode 100644 index 0000000..72c716a --- /dev/null +++ b/shell_cmds/xcconfigs/base.xcconfig @@ -0,0 +1,59 @@ +// Build Options +DEBUG_INFORMATION_FORMAT = dwarf-with-dsym + +// Code Signing +CODE_SIGN_IDENTITY = - + +// Deployment +COPY_PHASE_STRIP = YES + +// Packaging +APPLY_RULES_IN_COPY_FILES = YES + +// Search Paths +ALWAYS_SEARCH_USER_PATHS = NO +USE_HEADERMAP = NO + +// Versioning +CURRENT_PROJECT_VERSION = $(RC_ProjectSourceVersion) +VERSIONING_SYSTEM = apple-generic +VERSION_INFO_PREFIX = __ + +// Warning Policies +GCC_TREAT_WARNINGS_AS_ERRORS = YES + +// Warnings - All languages +CLANG_WARN_ASSIGN_ENUM = YES +CLANG_WARN_BOOL_CONVERSION = YES +CLANG_WARN_CONSTANT_CONVERSION = YES +CLANG_WARN_DOCUMENTATION_COMMENTS = YES +CLANG_WARN_EMPTY_BODY = YES +CLANG_WARN_ENUM_CONVERSION = YES +CLANG_WARN_IMPLICIT_SIGN_CONVERSION = YES +CLANG_WARN_INT_CONVERSION = YES +CLANG_WARN_NULLABLE_TO_NONNULL_CONVERSION = YES +CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = YES +CLANG_WARN_UNREACHABLE_CODE = YES +GCC_TREAT_IMPLICIT_FUNCTION_DECLARATIONS_AS_ERRORS = YES +GCC_TREAT_INCOMPATIBLE_POINTER_TYPE_WARNINGS_AS_ERRORS = YES +GCC_WARN_64_TO_32_BIT_CONVERSION = YES +GCC_WARN_ABOUT_DEPRECATED_FUNCTIONS = YES +GCC_WARN_ABOUT_MISSING_FIELD_INITIALIZERS = YES +GCC_WARN_ABOUT_MISSING_NEWLINE = YES +GCC_WARN_ABOUT_MISSING_PROTOTYPES = YES +GCC_WARN_ABOUT_POINTER_SIGNEDNESS = YES +GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR +GCC_WARN_CHECK_SWITCH_STATEMENTS = YES +GCC_WARN_FOUR_CHARACTER_CONSTANTS = YES +GCC_WARN_INITIALIZER_NOT_FULLY_BRACKETED = YES +GCC_WARN_MISSING_PARENTHESES = YES +GCC_WARN_SHADOW = YES +GCC_WARN_SIGN_COMPARE = YES +GCC_WARN_TYPECHECK_CALLS_TO_PRINTF = YES +GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE +GCC_WARN_UNKNOWN_PRAGMAS = YES +GCC_WARN_UNUSED_FUNCTION = YES +GCC_WARN_UNUSED_LABEL = YES +GCC_WARN_UNUSED_PARAMETER = YES +GCC_WARN_UNUSED_VALUE = YES +GCC_WARN_UNUSED_VARIABLE = YES diff --git a/shell_cmds/xcconfigs/sh.xcconfig b/shell_cmds/xcconfigs/sh.xcconfig new file mode 100644 index 0000000..f068b26 --- /dev/null +++ b/shell_cmds/xcconfigs/sh.xcconfig @@ -0,0 +1,33 @@ +#include "base.xcconfig" + +// TODO: change to /bin, sh, /usr +SH_INSTALL_PATH = /usr/local/bin +SH_PRODUCT_NAME = ash +SH_MAN_PREFIX = /usr/local + +// Architectures +SUPPORTED_PLATFORMS = iphoneos iphonesimulator macosx + +// Deployment +INSTALL_PATH = $(SH_INSTALL_PATH) + +// Linking +OTHER_LDFLAGS = -ledit + +// Packaging +PRODUCT_NAME = $(SH_PRODUCT_NAME) + +// Search Paths +USER_HEADER_SEARCH_PATHS = $(BUILT_PRODUCTS_DIR) $(SRCROOT)/sh + +// Preprocessing +GCC_PREPROCESSOR_DEFINITIONS = SHELL + +// Warnings - All languages +GCC_WARN_64_TO_32_BIT_CONVERSION = NO +GCC_WARN_UNINITIALIZED_AUTOS = YES +GCC_WARN_UNUSED_VARIABLE = NO +CLANG_WARN_ASSIGN_ENUM = NO +CLANG_WARN_EMPTY_BODY = NO +CLANG_WARN_IMPLICIT_SIGN_CONVERSION = NO +CLANG_WARN_SUSPICIOUS_IMPLICIT_CONVERSION = NO diff --git a/shell_cmds/xcodescripts/builtins-manpages.txt b/shell_cmds/xcodescripts/builtins-manpages.txt new file mode 100644 index 0000000..ffc6f4e --- /dev/null +++ b/shell_cmds/xcodescripts/builtins-manpages.txt @@ -0,0 +1,85 @@ +alias.1 +alloc.1 +bg.1 +bind.1 +bindkey.1 +break.1 +breaksw.1 +builtins.1 +case.1 +cd.1 +chdir.1 +command.1 +complete.1 +continue.1 +default.1 +dirs.1 +do.1 +done.1 +echotc.1 +elif.1 +else.1 +end.1 +endif.1 +endsw.1 +esac.1 +eval.1 +exec.1 +exit.1 +export.1 +fc.1 +fg.1 +fi.1 +filetest.1 +for.1 +foreach.1 +getopts.1 +glob.1 +goto.1 +hash.1 +hashstat.1 +history.1 +hup.1 +if.1 +jobid.1 +jobs.1 +limit.1 +logout.1 +ls-F.1 +notify.1 +onintr.1 +popd.1 +pushd.1 +read.1 +readonly.1 +rehash.1 +repeat.1 +return.1 +sched.1 +set.1 +setenv.1 +settc.1 +setty.1 +setvar.1 +shift.1 +source.1 +stop.1 +suspend.1 +switch.1 +telltc.1 +then.1 +times.1 +trap.1 +type.1 +ulimit.1 +umask.1 +unalias.1 +uncomplete.1 +unhash.1 +unlimit.1 +unset.1 +unsetenv.1 +until.1 +wait.1 +where.1 +while.1 diff --git a/shell_cmds/xcodescripts/builtins.txt b/shell_cmds/xcodescripts/builtins.txt new file mode 100644 index 0000000..55cb408 --- /dev/null +++ b/shell_cmds/xcodescripts/builtins.txt @@ -0,0 +1,14 @@ +bg +cd +command +fc +fg +getopts +hash +jobs +read +type +ulimit +umask +unalias +wait diff --git a/shell_cmds/xcodescripts/install-files.sh b/shell_cmds/xcodescripts/install-files.sh new file mode 100644 index 0000000..3710fcb --- /dev/null +++ b/shell_cmds/xcodescripts/install-files.sh @@ -0,0 +1,66 @@ +#!/bin/sh +set -e -x + +BINDIR="$DSTROOT"/usr/bin +LIBEXECDIR="$DSTROOT"/usr/libexec +MANDIR="$DSTROOT"/usr/share/man +PAMDIR="$DSTROOT"/private/etc/pam.d + +ln -f "$BINDIR/hexdump" "$BINDIR/od" +ln -f "$BINDIR/id" "$BINDIR/groups" +ln -f "$BINDIR/id" "$BINDIR/whoami" +ln -f "$BINDIR/w" "$BINDIR/uptime" +ln -f "$DSTROOT/bin/test" "$DSTROOT/bin/[" + +install -d -o root -g wheel -m 0755 "$BINDIR" +install -d -o root -g wheel -m 0755 "$MANDIR"/man1 +install -d -o root -g wheel -m 0755 "$MANDIR"/man8 + +install -c -o root -g wheel -m 0755 "$SRCROOT"/alias/generic.sh "$BINDIR"/alias +install -c -o root -g wheel -m 0644 "$SRCROOT"/alias/builtin.1 "$MANDIR"/man1 + +set +x +for builtin in `cat "$SRCROOT/xcodescripts/builtins.txt"`; do + echo ... linking $builtin + ln -f "$BINDIR"/alias "$BINDIR/$builtin" +done + +for manpage in `cat "$SRCROOT/xcodescripts/builtins-manpages.txt"`; do + echo ... linking $manpage + echo ".so man1/builtin.1" > "$MANDIR/man1/$manpage" +done +set -x + +install -d -o root -g wheel -m 0755 "$DSTROOT"/AppleInternal/Tests/shell_cmds +install -o root -g wheel -m 0644 "$SRCROOT"/tests/regress.m4 \ + "$DSTROOT"/AppleInternal/Tests/shell_cmds + +install -d -o root -g wheel -m 0755 \ + "$DSTROOT"/AppleInternal/Tests/shell_cmds/time +install -o root -g wheel -m 0644 "$SRCROOT"/time/tests/test_time.sh \ + "$DSTROOT"/AppleInternal/Tests/shell_cmds/time + +install -d -o root -g wheel -m 0755 \ + "$DSTROOT"/AppleInternal/CoreOS/BATS/unit_tests +install -o root -g wheel -m 0644 "$SRCROOT"/tests/shell_cmds.plist \ + "$DSTROOT"/AppleInternal/CoreOS/BATS/unit_tests + +# Skip locate and su targets for iOS +if [ "$TARGET_NAME" = "All_iOS" ]; then + exit 0 +fi + +install -d -o root -g wheel -m 0755 "$LIBEXECDIR" +install -c -o root -g wheel -m 0755 "$SRCROOT"/locate/locate/updatedb.sh \ + "$LIBEXECDIR"/locate.updatedb +install -c -o root -g wheel -m 0644 "$SRCROOT"/locate/locate/locate.updatedb.8 \ + "$MANDIR"/man8 +install -c -o root -g wheel -m 0755 "$SRCROOT"/locate/locate/concatdb.sh \ + "$LIBEXECDIR"/locate.concatdb +echo ".so man8/locate.updatedb.8" > "$MANDIR"/man8/locate.concatdb.8 +install -c -o root -g wheel -m 0755 "$SRCROOT"/locate/locate/mklocatedb.sh \ + "$LIBEXECDIR"/locate.mklocatedb +echo ".so man8/locate.updatedb.8" > "$MANDIR"/man8/locate.mklocatedb.8 + +install -d -o root -g wheel -m 0755 "$PAMDIR" +install -c -o root -g wheel -m 0644 "$SRCROOT"/su/su.pam "$PAMDIR"/su diff --git a/shell_cmds/yes/yes.1 b/shell_cmds/yes/yes.1 new file mode 100644 index 0000000..eab3795 --- /dev/null +++ b/shell_cmds/yes/yes.1 @@ -0,0 +1,56 @@ +.\" $NetBSD: yes.1,v 1.4 1997/10/20 03:42:10 lukem Exp $ +.\" +.\" Copyright (c) 1980, 1991, 1993 +.\" The Regents of the University of California. All rights reserved. +.\" +.\" Redistribution and use in source and binary forms, with or without +.\" modification, are permitted provided that the following conditions +.\" are met: +.\" 1. Redistributions of source code must retain the above copyright +.\" notice, this list of conditions and the following disclaimer. +.\" 2. Redistributions in binary form must reproduce the above copyright +.\" notice, this list of conditions and the following disclaimer in the +.\" documentation and/or other materials provided with the distribution. +.\" 3. All advertising materials mentioning features or use of this software +.\" must display the following acknowledgement: +.\" This product includes software developed by the University of +.\" California, Berkeley and its contributors. +.\" 4. Neither the name of the University nor the names of its contributors +.\" may be used to endorse or promote products derived from this software +.\" without specific prior written permission. +.\" +.\" THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND +.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +.\" ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE +.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS +.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY +.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF +.\" SUCH DAMAGE. +.\" +.\" @(#)yes.1 8.1 (Berkeley) 6/6/93 +.\" +.Dd June 6, 1993 +.Dt YES 1 +.Os BSD 4 +.Sh NAME +.Nm yes +.Nd be repetitively affirmative +.Sh SYNOPSIS +.Nm +.Op Ar expletive +.Sh DESCRIPTION +.Nm +outputs +.Ar expletive , +or, by default, +.Dq y , +forever. +.Sh HISTORY +The +.Nm +command appeared in +.Bx 4.0 . diff --git a/shell_cmds/yes/yes.c b/shell_cmds/yes/yes.c new file mode 100644 index 0000000..863f363 --- /dev/null +++ b/shell_cmds/yes/yes.c @@ -0,0 +1,63 @@ +/* $NetBSD: yes.c,v 1.5 1997/10/19 14:28:27 mrg Exp $ */ + +/* + * Copyright (c) 1987, 1993 + * The Regents of the University of California. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include <sys/cdefs.h> +#ifndef lint +__COPYRIGHT("@(#) Copyright (c) 1987, 1993\n\ + The Regents of the University of California. All rights reserved.\n"); +#endif /* not lint */ + +#ifndef lint +#if 0 +static char sccsid[] = "@(#)yes.c 8.1 (Berkeley) 6/6/93"; +#endif +__RCSID("$NetBSD: yes.c,v 1.5 1997/10/19 14:28:27 mrg Exp $"); +#endif /* not lint */ + +#include <stdio.h> + +int main __P((int, char **)); + +int +main(argc, argv) + int argc; + char **argv; +{ + if (argc > 1) + for(;;) + (void)puts(argv[1]); + else for (;;) + (void)puts("y"); +} |