diff options
Diffstat (limited to '')
-rw-r--r-- | src/kash/bltin/Makefile.kup | 0 | ||||
-rw-r--r-- | src/kash/bltin/echo.1 | 109 | ||||
-rw-r--r-- | src/kash/bltin/echo.c | 120 | ||||
-rw-r--r-- | src/kash/bltin/kill.c | 236 | ||||
-rw-r--r-- | src/kash/bltin/printf.c | 659 | ||||
-rw-r--r-- | src/kash/bltin/test.c | 483 |
6 files changed, 1607 insertions, 0 deletions
diff --git a/src/kash/bltin/Makefile.kup b/src/kash/bltin/Makefile.kup new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/src/kash/bltin/Makefile.kup diff --git a/src/kash/bltin/echo.1 b/src/kash/bltin/echo.1 new file mode 100644 index 0000000..7e71fa3 --- /dev/null +++ b/src/kash/bltin/echo.1 @@ -0,0 +1,109 @@ +.\" $NetBSD: echo.1,v 1.13 2003/08/07 09:05:40 agc 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 +.\" Kenneth Almquist. +.\" Copyright 1989 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.1 8.1 (Berkeley) 5/31/93 +.\" +.Dd May 31, 1993 +.Dt ECHO 1 +.Os +.Sh NAME +.Nm echo +.Nd produce message in a shell script +.Sh SYNOPSIS +.Nm +.Op Fl n | Fl e +.Ar args ... +.Sh DESCRIPTION +.Nm +prints its arguments on the standard output, separated by spaces. +Unless the +.Fl n +option is present, a newline is output following the arguments. +The +.Fl e +option causes +.Nm +to treat the escape sequences specially, as described in the following +paragraph. +The +.Fl e +option is the default, and is provided solely for compatibility with +other systems. +Only one of the options +.Fl n +and +.Fl e +may be given. +.Pp +If any of the following sequences of characters is encountered during +output, the sequence is not output. Instead, the specified action is +performed: +.Bl -tag -width indent +.It Li \eb +A backspace character is output. +.It Li \ec +Subsequent output is suppressed. This is normally used at the end of the +last argument to suppress the trailing newline that +.Nm +would otherwise output. +.It Li \ef +Output a form feed. +.It Li \en +Output a newline character. +.It Li \er +Output a carriage return. +.It Li \et +Output a (horizontal) tab character. +.It Li \ev +Output a vertical tab. +.It Li \e0 Ns Ar digits +Output the character whose value is given by zero to three digits. +If there are zero digits, a nul character is output. +.It Li \e\e +Output a backslash. +.El +.Sh HINTS +Remember that backslash is special to the shell and needs to be escaped. +To output a message to standard error, say +.Pp +.D1 echo message \*[Gt]\*[Am]2 +.Sh BUGS +The octal character escape mechanism +.Pq Li \e0 Ns Ar digits +differs from the +C language mechanism. +.Pp +There is no way to force +.Nm +to treat its arguments literally, rather than interpreting them as +options and escape sequences. diff --git a/src/kash/bltin/echo.c b/src/kash/bltin/echo.c new file mode 100644 index 0000000..ba778b1 --- /dev/null +++ b/src/kash/bltin/echo.c @@ -0,0 +1,120 @@ +/* $NetBSD: echo.c,v 1.12 2005/02/06 04:43:43 perry 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 + * 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.1 (Berkeley) 5/31/93 + */ + +/* + * Echo command. + * + * echo is steeped in tradition - several of them! + * netbsd has supported 'echo [-n | -e] args' in spite of -e not being + * documented anywhere. + * Posix requires that -n be supported, output from strings containing + * \ is implementation defined + * The Single Unix Spec requires that \ escapes be treated as if -e + * were set, but that -n not be treated as an option. + * (ksh supports 'echo [-eEn] args', but not -- so that it is actually + * impossible to actually output '-n') + * + * It is suggested that 'printf "%b" "string"' be used to get \ sequences + * expanded. printf is now a builtin of netbsd's sh and csh. + */ + +#include "shinstance.h" +#include "builtins.h" + +int +echocmd(shinstance *psh, int argc, char **argv) +{ + char **ap; + const char *p; + int nflag = 0; + int eflag = 0; + + ap = argv; + if (argc) + ap++; + + if ((p = *ap) != NULL && *p == '-') { + if (p[1] == 'n' && !p[2]) { + nflag = 1; + ap++; + } else if (p[1] == 'e' && !p[2]) { + eflag = 1; + ap++; + } + } + + while ((p = *ap++) != NULL) { + if (!eflag) { + out1str(psh, p); + } else { + char c; + int count; + + while ((c = *p++) != '\0') { + if (c == '\\') { + switch (*p++) { + case 'a': c = '\a'; break; /* bell */ + case 'b': c = '\b'; break; + case 'c': return 0; /* exit */ + case 'e': c = 033; break; /* escape */ + 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: + /* Output the '/' and char following */ + p--; + break; + } + } + out1c(psh, c); + } + } + if (*ap) + out1c(psh, ' '); + } + if (! nflag) + out1c(psh, '\n'); + return 0; +} diff --git a/src/kash/bltin/kill.c b/src/kash/bltin/kill.c new file mode 100644 index 0000000..4b6e5d7 --- /dev/null +++ b/src/kash/bltin/kill.c @@ -0,0 +1,236 @@ +/* $NetBSD: kill.c,v 1.23 2003/08/07 09:05:13 agc Exp $ */ + +/* + * 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. 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 +#if !defined(lint) && !defined(SHELL) +__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"; +#else +__RCSID("$NetBSD: kill.c,v 1.23 2003/08/07 09:05:13 agc Exp $"); +#endif /* not lint */ +#endif + +#include <ctype.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include "shtypes.h" +#include "jobs.h" +#include "error.h" +#include "shinstance.h" + + +static int nosig(shinstance *, char *); +static void printsignals(shinstance *, struct output *); +static int signame_to_signum(char *); +static int usage(shinstance *psh); + +int +killcmd(shinstance *psh, int argc, char *argv[]) +{ + int errors, numsig; + char *ep; + + if (argc < 2) + return usage(psh); + + numsig = SIGTERM; + + argc--, argv++; + if (strcmp(*argv, "-l") == 0) { + argc--, argv++; + if (argc > 1) + return usage(psh); + if (argc == 1) { + if (isdigit((unsigned char)**argv) == 0) + return usage(psh); + numsig = strtol(*argv, &ep, 10); + if (*ep != '\0') { + sh_errx(psh, EXIT_FAILURE, "illegal signal number: %s", + *argv); + /* NOTREACHED */ + } + if (numsig >= 128) + numsig -= 128; + if (numsig <= 0 || numsig >= NSIG) + return nosig(psh, *argv); + outfmt(psh->out1, "%s\n", sys_signame[numsig]); + //sh_exit(psh, 0); + return 0; + } + printsignals(psh, psh->out1); + //sh_exit(psh, 0); + return 0; + } + + if (!strcmp(*argv, "-s")) { + argc--, argv++; + if (argc < 1) { + sh_warnx(psh, "option requires an argument -- s"); + return usage(psh); + } + if (strcmp(*argv, "0")) { + if ((numsig = signame_to_signum(*argv)) < 0) + return nosig(psh, *argv); + } else + numsig = 0; + argc--, argv++; + } else if (**argv == '-') { + ++*argv; + if (isalpha((unsigned char)**argv)) { + if ((numsig = signame_to_signum(*argv)) < 0) + return nosig(psh, *argv); + } else if (isdigit((unsigned char)**argv)) { + numsig = strtol(*argv, &ep, 10); + if (!*argv || *ep) { + sh_errx(psh, EXIT_FAILURE, "illegal signal number: %s", + *argv); + /* NOTREACHED */ + } + if (numsig < 0 || numsig >= NSIG) + return nosig(psh, *argv); + } else + return nosig(psh, *argv); + argc--, argv++; + } + + if (argc == 0) + return usage(psh); + + for (errors = 0; argc; argc--, argv++) { + const char * const strpid = argv[0]; + shpid pid; + if (*strpid == '%') { + pid = getjobpgrp(psh, strpid); + if (pid == 0) { + sh_warnx(psh, "illegal job id: %s", strpid); + errors = 1; + continue; + } + } else { +#if !defined(SH_FORKED_MODE) && defined(_MSC_VER) + pid = _strtoi64(strpid, &ep, 10); +#elif !defined(SH_FORKED_MODE) + pid = strtoll(strpid, &ep, 10); +#else + pid = strtol(strpid, &ep, 10); +#endif + if (!*strpid || *ep) { + sh_warnx(psh, "illegal process id: %s", strpid); + errors = 1; + continue; + } + } + if (sh_kill(psh, pid, numsig) == -1) { + sh_warn(psh, "%s", strpid); + errors = 1; + } + /* Wakeup the process if it was suspended, so it can + exit without an explicit 'fg'. */ + if (numsig == SIGTERM || numsig == SIGHUP) + sh_kill(psh, pid, SIGCONT); + } + + //sh_exit(psh, errors); + ///* NOTREACHED */ + return errors; +} + +static int +signame_to_signum(char *sig) +{ + int n; + if (strncasecmp(sig, "sig", 3) == 0) + sig += 3; + for (n = 1; n < NSIG; n++) { + if (!strcasecmp(sys_signame[n], sig)) + return (n); + } + return (-1); +} + +static int +nosig(shinstance *psh, char *name) +{ + sh_warnx(psh, "unknown signal %s; valid signals:", name); + printsignals(psh, psh->out2); + //sh_exit(psh, 1); + ///* NOTREACHED */ + return 1; +} + +static void +printsignals(shinstance *psh, struct output *out) +{ + int sig; + size_t len, nl; + const char *name; + unsigned termwidth = 80; + + if (shfile_isatty(&psh->fdtab, out->fd)) { + sh_winsize win; + if (shfile_ioctl(&psh->fdtab, out->fd, TIOCGWINSZ, &win) == 0 && win.ws_col > 0) + termwidth = win.ws_col; + } + + for (len = 0, sig = 1; sig < NSIG; sig++) { + name = sys_signame[sig]; + nl = 1 + strlen(name); + + if (len + nl >= termwidth) { + outfmt(out, "\n"); + len = 0; + } else if (len != 0) + outfmt(out, " "); + len += nl; + outfmt(out, "%s", name); + } + if (len != 0) + outfmt(out, "\n"); +} + +static int +usage(shinstance *psh) +{ + outfmt(psh->out2, + "usage: %s [-s signal_name] pid ...\n" + " %s -l [exit_status]\n" + " %s -signal_name pid ...\n" + " %s -signal_number pid ...\n", + psh->commandname, psh->commandname, psh->commandname, psh->commandname); + //sh_exit(psh, 1); + ///* NOTREACHED */ + return 1; +} diff --git a/src/kash/bltin/printf.c b/src/kash/bltin/printf.c new file mode 100644 index 0000000..4a6a952 --- /dev/null +++ b/src/kash/bltin/printf.c @@ -0,0 +1,659 @@ +/* $NetBSD: printf.c,v 1.31 2005/03/22 23:55:46 dsl 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. + */ + +#if 0 +#if !defined(BUILTIN) && !defined(SHELL) +__COPYRIGHT("@(#) Copyright (c) 1989, 1993\n\ + The Regents of the University of California. All rights reserved.\n"); +#endif +#ifndef lint +static char sccsid[] = "@(#)printf.c 8.2 (Berkeley) 3/22/95"; +#else +__RCSID("$NetBSD: printf.c,v 1.31 2005/03/22 23:55:46 dsl Exp $"); +#endif /* not lint */ +#endif + +#include <sys/types.h> + +#include <ctype.h> +#include <err.h> +#include <errno.h> +#include <inttypes.h> +#include <limits.h> +#include <locale.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <unistd.h> +#include "shinstance.h" + +#ifdef __GNUC__ +#define ESCAPE '\e' +#else +#define ESCAPE 033 +#endif + +static void conv_escape_str(char *, void (*)(int)); +static char *conv_escape(char *, char *); +static char *conv_expand(const char *); +static int getchr(void); +static double getdouble(void); +static int getwidth(void); +static intmax_t getintmax(void); +static uintmax_t getuintmax(void); +static char *getstr(void); +static char *mklong(const char *, int); +static void check_conversion(const char *, const char *); +static void usage(void); + +static void b_count(int); +static void b_output(int); +static size_t b_length; +static char *b_fmt; + +static int rval; +static char **gargv; + +#ifdef BUILTIN /* csh builtin */ +#define main progprintf +#endif + +#ifdef SHELL /* sh (aka ash) builtin */ +#define main printfcmd +#include "../../bin/sh/bltin/bltin.h" +#endif /* SHELL */ + +#define PF(f, func) { \ + if (fieldwidth != -1) { \ + if (precision != -1) \ + (void)printf(f, fieldwidth, precision, func); \ + else \ + (void)printf(f, fieldwidth, func); \ + } else if (precision != -1) \ + (void)printf(f, precision, func); \ + else \ + (void)printf(f, func); \ +} + +#define APF(cpp, f, func) { \ + if (fieldwidth != -1) { \ + if (precision != -1) \ + (void)asprintf(cpp, f, fieldwidth, precision, func); \ + else \ + (void)asprintf(cpp, f, fieldwidth, func); \ + } else if (precision != -1) \ + (void)asprintf(cpp, f, precision, func); \ + else \ + (void)asprintf(cpp, f, func); \ +} + +int main(int, char **); +int main(int argc, char *argv[]) +{ + char *fmt, *start; + int fieldwidth, precision; + char nextch; + char *format; + int ch; + +#if !defined(SHELL) && !defined(BUILTIN) + (void)setlocale (LC_ALL, ""); +#endif + + while ((ch = getopt(argc, argv, "")) != -1) { + switch (ch) { + case '?': + default: + usage(); + return 1; + } + } + argc -= optind; + argv += optind; + + if (argc < 1) { + usage(); + return 1; + } + + format = *argv; + gargv = ++argv; + +#define SKIP1 "#-+ 0" +#define SKIP2 "*0123456789" + do { + /* + * 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. + */ + + /* find next format specification */ + for (fmt = format; (ch = *fmt++) != '\0';) { + if (ch == '\\') { + char c_ch; + fmt = conv_escape(fmt, &c_ch); + putchar(c_ch); + continue; + } + if (ch != '%' || (*fmt == '%' && ++fmt)) { + (void)putchar(ch); + continue; + } + + /* Ok - we've found a format specification, + Save its address for a later printf(). */ + start = fmt - 1; + + /* skip to field width */ + fmt += strspn(fmt, SKIP1); + fieldwidth = *fmt == '*' ? getwidth() : -1; + + /* skip to possible '.', get following precision */ + fmt += strspn(fmt, SKIP2); + if (*fmt == '.') + ++fmt; + precision = *fmt == '*' ? getwidth() : -1; + + fmt += strspn(fmt, SKIP2); + + ch = *fmt; + if (!ch) { + warnx("missing format character"); + return (1); + } + /* null terminate format string to we can use it + as an argument to printf. */ + nextch = fmt[1]; + fmt[1] = 0; + switch (ch) { + + case 'B': { + const char *p = conv_expand(getstr()); + *fmt = 's'; + PF(start, p); + break; + } + case 'b': { + /* There has to be a better way to do this, + * but the string we generate might have + * embedded nulls. */ + static char *a, *t; + char *cp = getstr(); + /* Free on entry in case shell longjumped out */ + if (a != NULL) + free(a); + a = NULL; + if (t != NULL) + free(t); + t = NULL; + /* Count number of bytes we want to output */ + b_length = 0; + conv_escape_str(cp, b_count); + t = malloc(b_length + 1); + if (t == NULL) + break; + memset(t, 'x', b_length); + t[b_length] = 0; + /* Get printf to calculate the lengths */ + *fmt = 's'; + APF(&a, start, t); + b_fmt = a; + /* Output leading spaces and data bytes */ + conv_escape_str(cp, b_output); + /* Add any trailing spaces */ + printf("%s", b_fmt); + break; + } + case 'c': { + char p = getchr(); + PF(start, p); + break; + } + case 's': { + char *p = getstr(); + PF(start, p); + break; + } + case 'd': + case 'i': { + intmax_t p = getintmax(); + char *f = mklong(start, ch); + PF(f, p); + break; + } + case 'o': + case 'u': + case 'x': + case 'X': { + uintmax_t p = getuintmax(); + char *f = mklong(start, ch); + PF(f, p); + break; + } + case 'e': + case 'E': + case 'f': + case 'g': + case 'G': { + double p = getdouble(); + PF(start, p); + break; + } + default: + warnx("%s: invalid directive", start); + return 1; + } + *fmt++ = ch; + *fmt = nextch; + /* escape if a \c was encountered */ + if (rval & 0x100) + return rval & ~0x100; + } + } while (gargv != argv && *gargv); + + return rval; +} + +/* helper functions for conv_escape_str */ + +static void +/*ARGSUSED*/ +b_count(int ch) +{ + b_length++; +} + +/* Output one converted character for every 'x' in the 'format' */ + +static void +b_output(int ch) +{ + for (;;) { + switch (*b_fmt++) { + case 0: + b_fmt--; + return; + case ' ': + putchar(' '); + break; + default: + putchar(ch); + return; + } + } +} + + +/* + * Print SysV echo(1) style escape string + * Halts processing string if a \c escape is encountered. + */ +static void +conv_escape_str(char *str, void (*do_putchar)(int)) +{ + int value; + int ch; + char c; + + while ((ch = *str++) != '\0') { + if (ch != '\\') { + do_putchar(ch); + continue; + } + + ch = *str++; + if (ch == 'c') { + /* \c as in SYSV echo - abort all processing.... */ + rval |= 0x100; + break; + } + + /* + * %b string octal constants are not like those in C. + * They start with a \0, and are followed by 0, 1, 2, + * or 3 octal digits. + */ + if (ch == '0') { + int octnum = 0, i; + for (i = 0; i < 3; i++) { + if (!isdigit((unsigned char)*str) || *str > '7') + break; + octnum = (octnum << 3) | (*str++ - '0'); + } + do_putchar(octnum); + continue; + } + + /* \[M][^|-]C as defined by vis(3) */ + if (ch == 'M' && *str == '-') { + do_putchar(0200 | str[1]); + str += 2; + continue; + } + if (ch == 'M' && *str == '^') { + str++; + value = 0200; + ch = '^'; + } else + value = 0; + if (ch == '^') { + ch = *str++; + if (ch == '?') + value |= 0177; + else + value |= ch & 037; + do_putchar(value); + continue; + } + + /* Finally test for sequences valid in the format string */ + str = conv_escape(str - 1, &c); + do_putchar(c); + } +} + +/* + * Print "standard" escape characters + */ +static char * +conv_escape(char *str, char *conv_ch) +{ + int value; + int ch; + char num_buf[4], *num_end; + + ch = *str++; + + switch (ch) { + case '0': case '1': case '2': case '3': + case '4': case '5': case '6': case '7': + num_buf[0] = ch; + ch = str[0]; + num_buf[1] = ch; + num_buf[2] = ch ? str[1] : 0; + num_buf[3] = 0; + value = strtoul(num_buf, &num_end, 8); + str += num_end - (num_buf + 1); + break; + + case 'x': + /* Hexadecimal character constants are not required to be + supported (by SuS v1) because there is no consistent + way to detect the end of the constant. + Supporting 2 byte constants is a compromise. */ + ch = str[0]; + num_buf[0] = ch; + num_buf[1] = ch ? str[1] : 0; + num_buf[2] = 0; + value = strtoul(num_buf, &num_end, 16); + str += num_end - num_buf; + break; + + case '\\': value = '\\'; break; /* backslash */ + case '\'': value = '\''; break; /* single quote */ + case '"': value = '"'; break; /* double quote */ + case 'a': value = '\a'; break; /* alert */ + case 'b': value = '\b'; break; /* backspace */ + case 'e': value = ESCAPE; break; /* escape */ + case 'f': value = '\f'; break; /* form-feed */ + case 'n': value = '\n'; break; /* newline */ + case 'r': value = '\r'; break; /* carriage-return */ + case 't': value = '\t'; break; /* tab */ + case 'v': value = '\v'; break; /* vertical-tab */ + + default: + warnx("unknown escape sequence `\\%c'", ch); + rval = 1; + value = ch; + break; + } + + *conv_ch = value; + return str; +} + +/* expand a string so that everything is printable */ + +static char * +conv_expand(const char *str) +{ + static char *conv_str; + static char no_memory[] = "<no memory>"; + char *cp; + int ch; + + if (conv_str) + free(conv_str); + /* get a buffer that is definitely large enough.... */ + conv_str = malloc(4 * strlen(str) + 1); + if (!conv_str) + return no_memory; + cp = conv_str; + + while ((ch = *(const unsigned char *)str++) != '\0') { + switch (ch) { + /* Use C escapes for expected control characters */ + case '\\': ch = '\\'; break; /* backslash */ + case '\'': ch = '\''; break; /* single quote */ + case '"': ch = '"'; break; /* double quote */ + case '\a': ch = 'a'; break; /* alert */ + case '\b': ch = 'b'; break; /* backspace */ + case ESCAPE: ch = 'e'; break; /* escape */ + case '\f': ch = 'f'; break; /* form-feed */ + case '\n': ch = 'n'; break; /* newline */ + case '\r': ch = 'r'; break; /* carriage-return */ + case '\t': ch = 't'; break; /* tab */ + case '\v': ch = 'v'; break; /* vertical-tab */ + default: + /* Copy anything printable */ + if (isprint(ch)) { + *cp++ = ch; + continue; + } + /* Use vis(3) encodings for the rest */ + *cp++ = '\\'; + if (ch & 0200) { + *cp++ = 'M'; + ch &= ~0200; + } + if (ch == 0177) { + *cp++ = '^'; + *cp++ = '?'; + continue; + } + if (ch < 040) { + *cp++ = '^'; + *cp++ = ch | 0100; + continue; + } + *cp++ = '-'; + *cp++ = ch; + continue; + } + *cp++ = '\\'; + *cp++ = ch; + } + + *cp = 0; + return conv_str; +} + +static char * +mklong(const char *str, int ch) +{ + static char copy[64]; + size_t len; + + len = strlen(str) + 2; + if (len > sizeof copy) { + warnx("format %s too complex\n", str); + len = 4; + } + (void)memmove(copy, str, len - 3); + copy[len - 3] = 'j'; + copy[len - 2] = ch; + copy[len - 1] = '\0'; + return copy; +} + +static int +getchr(void) +{ + if (!*gargv) + return 0; + return (int)**gargv++; +} + +static char * +getstr(void) +{ + static char empty[] = ""; + if (!*gargv) + return empty; + return *gargv++; +} + +static int +getwidth(void) +{ + long val; + char *s, *ep; + + s = *gargv; + if (!*gargv) + return (0); + gargv++; + + errno = 0; + val = strtoul(s, &ep, 0); + check_conversion(s, ep); + + /* Arbitrarily 'restrict' field widths to 1Mbyte */ + if (val < 0 || val > 1 << 20) { + warnx("%s: invalid field width", s); + return 0; + } + + return val; +} + +static intmax_t +getintmax(void) +{ + intmax_t val; + char *cp, *ep; + + cp = *gargv; + if (cp == NULL) + return 0; + gargv++; + + if (*cp == '\"' || *cp == '\'') + return *(cp+1); + + errno = 0; + val = strtoimax(cp, &ep, 0); + check_conversion(cp, ep); + return val; +} + +static uintmax_t +getuintmax(void) +{ + uintmax_t val; + char *cp, *ep; + + cp = *gargv; + if (cp == NULL) + return 0; + gargv++; + + if (*cp == '\"' || *cp == '\'') + return *(cp + 1); + + /* strtoumax won't error -ve values */ + while (isspace(*(unsigned char *)cp)) + cp++; + if (*cp == '-') { + warnx("%s: expected positive numeric value", cp); + rval = 1; + return 0; + } + + errno = 0; + val = strtoumax(cp, &ep, 0); + check_conversion(cp, ep); + return val; +} + +static double +getdouble(void) +{ + double val; + char *ep; + + if (!*gargv) + return (0.0); + + if (**gargv == '\"' || **gargv == '\'') + return (double) *((*gargv++)+1); + + errno = 0; + val = strtod(*gargv, &ep); + check_conversion(*gargv++, ep); + return val; +} + +static void +check_conversion(const char *s, const char *ep) +{ + if (*ep) { + if (ep == s) + warnx("%s: expected numeric value", s); + else + warnx("%s: not completely converted", s); + rval = 1; + } else if (errno == ERANGE) { + warnx("%s: %s", s, sh_strerror(psh, ERANGE)); + rval = 1; + } +} + +static void +usage(void) +{ + (void)fprintf(stderr, "Usage: %s format [arg ...]\n", getprogname()); +} diff --git a/src/kash/bltin/test.c b/src/kash/bltin/test.c new file mode 100644 index 0000000..303a0f3 --- /dev/null +++ b/src/kash/bltin/test.c @@ -0,0 +1,483 @@ +/* $NetBSD: test.c,v 1.26 2005/02/10 06:56:55 simonb 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. + */ + +#if 0 +#ifndef lint +__RCSID("$NetBSD: test.c,v 1.26 2005/02/10 06:56:55 simonb Exp $"); +#endif +#endif + +#include <sys/types.h> +#include <ctype.h> +#include <errno.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <stdarg.h> + +#include "shell.h" +#include "error.h" +#include "shinstance.h" + + +/* 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 { + EOI, + FILRD, + FILWR, + FILEX, + FILEXIST, + FILREG, + FILDIR, + FILCDEV, + FILBDEV, + FILFIFO, + FILSOCK, + FILSYM, + FILGZ, + FILTT, + FILSUID, + FILSGID, + FILSTCK, + FILNT, + FILOT, + FILEQ, + FILUID, + FILGID, + STREZ, + STRNZ, + STREQ, + STRNE, + STRLT, + STRGT, + INTEQ, + INTNE, + INTGE, + INTGT, + INTLE, + INTLT, + UNOT, + BAND, + BOR, + LPAREN, + RPAREN, + OPERAND +}; + +enum token_types { + UNOP, + BINOP, + BUNOP, + BBINOP, + PAREN +}; + +static struct t_op { + const char *op_text; + short op_num, op_type; +} const ops [] = { + {"-r", FILRD, UNOP}, + {"-w", FILWR, UNOP}, + {"-x", FILEX, UNOP}, + {"-e", FILEXIST,UNOP}, + {"-f", FILREG, UNOP}, + {"-d", FILDIR, UNOP}, + {"-c", FILCDEV,UNOP}, + {"-b", FILBDEV,UNOP}, + {"-p", FILFIFO,UNOP}, + {"-u", FILSUID,UNOP}, + {"-g", FILSGID,UNOP}, + {"-k", FILSTCK,UNOP}, + {"-s", FILGZ, UNOP}, + {"-t", FILTT, UNOP}, + {"-z", STREZ, UNOP}, + {"-n", STRNZ, UNOP}, + {"-h", FILSYM, UNOP}, /* for backwards compat */ + {"-O", FILUID, UNOP}, + {"-G", FILGID, UNOP}, + {"-L", FILSYM, UNOP}, + {"-S", FILSOCK,UNOP}, + {"=", STREQ, BINOP}, + {"!=", STRNE, BINOP}, + {"<", STRLT, BINOP}, + {">", STRGT, BINOP}, + {"-eq", INTEQ, BINOP}, + {"-ne", INTNE, BINOP}, + {"-ge", INTGE, BINOP}, + {"-gt", INTGT, BINOP}, + {"-le", INTLE, BINOP}, + {"-lt", INTLT, BINOP}, + {"-nt", FILNT, BINOP}, + {"-ot", FILOT, BINOP}, + {"-ef", FILEQ, BINOP}, + {"!", UNOT, BUNOP}, + {"-a", BAND, BBINOP}, + {"-o", BOR, BBINOP}, + {"(", LPAREN, PAREN}, + {")", RPAREN, PAREN}, + {0, 0, 0} +}; + +//static char **t_wp; +//static struct t_op const *t_wp_op; + +static void syntax(shinstance *, const char *, const char *); +static int oexpr(shinstance *, enum token); +static int aexpr(shinstance *, enum token); +static int nexpr(shinstance *, enum token); +static int primary(shinstance *, enum token); +static int binop(shinstance *); +static int filstat(shinstance *, char *, enum token); +static enum token t_lex(shinstance *, char *); +static int isoperand(shinstance *); +static int getn(shinstance *, const char *); +static int newerf(shinstance *, const char *, const char *); +static int olderf(shinstance *, const char *, const char *); +static int equalf(shinstance *, const char *, const char *); + + +int +testcmd(shinstance *psh, int argc, char **argv) +{ + int res; + + if (strcmp(argv[0], "[") == 0) { + if (strcmp(argv[--argc], "]")) + error(psh, "missing ]"); + argv[argc] = NULL; + } + + if (argc < 2) + return 1; + + psh->t_wp_op = NULL; + psh->t_wp = &argv[1]; + res = !oexpr(psh, t_lex(psh, *psh->t_wp)); + + if (*psh->t_wp != NULL && *++psh->t_wp != NULL) + syntax(psh, *psh->t_wp, "unexpected operator"); + + return res; +} + +static void +syntax(shinstance *psh, const char *op, const char *msg) +{ + + if (op && *op) + error(psh, "%s: %s", op, msg); + else + error(psh, "%s", msg); +} + +static int +oexpr(shinstance *psh, enum token n) +{ + int res; + + res = aexpr(psh, n); + if (t_lex(psh, *++psh->t_wp) == BOR) + return oexpr(psh, t_lex(psh, *++psh->t_wp)) || res; + psh->t_wp--; + return res; +} + +static int +aexpr(shinstance *psh, enum token n) +{ + int res; + + res = nexpr(psh, n); + if (t_lex(psh, *++psh->t_wp) == BAND) + return aexpr(psh, t_lex(psh, *++psh->t_wp)) && res; + psh->t_wp--; + return res; +} + +static int +nexpr(shinstance *psh, enum token n) +{ + + if (n == UNOT) + return !nexpr(psh, t_lex(psh, *++psh->t_wp)); + return primary(psh, n); +} + +static int +primary(shinstance *psh, enum token n) +{ + enum token nn; + int res; + + if (n == EOI) + return 0; /* missing expression */ + if (n == LPAREN) { + if ((nn = t_lex(psh, *++psh->t_wp)) == RPAREN) + return 0; /* missing expression */ + res = oexpr(psh, nn); + if (t_lex(psh, *++psh->t_wp) != RPAREN) + syntax(psh, NULL, "closing paren expected"); + return res; + } + if (psh->t_wp_op && psh->t_wp_op->op_type == UNOP) { + /* unary expression */ + if (*++psh->t_wp == NULL) + syntax(psh, psh->t_wp_op->op_text, "argument expected"); + switch (n) { + case STREZ: + return strlen(*psh->t_wp) == 0; + case STRNZ: + return strlen(*psh->t_wp) != 0; + case FILTT: + return shfile_isatty(&psh->fdtab, getn(psh, *psh->t_wp)); + default: + return filstat(psh, *psh->t_wp, n); + } + } + + if (t_lex(psh, psh->t_wp[1]), psh->t_wp_op && psh->t_wp_op->op_type == BINOP) { + return binop(psh); + } + + return strlen(*psh->t_wp) > 0; +} + +static int +binop(shinstance *psh) +{ + const char *opnd1, *opnd2; + struct t_op const *op; + + opnd1 = *psh->t_wp; + (void) t_lex(psh, *++psh->t_wp); + op = psh->t_wp_op; + + if ((opnd2 = *++psh->t_wp) == NULL) + syntax(psh, op->op_text, "argument expected"); + + switch (op->op_num) { + 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 getn(psh, opnd1) == getn(psh, opnd2); + case INTNE: + return getn(psh, opnd1) != getn(psh, opnd2); + case INTGE: + return getn(psh, opnd1) >= getn(psh, opnd2); + case INTGT: + return getn(psh, opnd1) > getn(psh, opnd2); + case INTLE: + return getn(psh, opnd1) <= getn(psh, opnd2); + case INTLT: + return getn(psh, opnd1) < getn(psh, opnd2); + case FILNT: + return newerf(psh, opnd1, opnd2); + case FILOT: + return olderf(psh, opnd1, opnd2); + case FILEQ: + return equalf(psh, opnd1, opnd2); + default: + sh_abort(psh); + /* NOTREACHED */ + return -1; + } +} + +static int +filstat(shinstance *psh, char *nm, enum token mode) +{ + struct stat s; + + if (mode == FILSYM + ? shfile_lstat(&psh->fdtab, nm, &s) + : shfile_stat(&psh->fdtab, nm, &s)) + return 0; + + switch (mode) { + case FILRD: + return shfile_access(&psh->fdtab, nm, R_OK) == 0; + case FILWR: + return shfile_access(&psh->fdtab, nm, W_OK) == 0; + case FILEX: + return shfile_access(&psh->fdtab, nm, X_OK) == 0; + case FILEXIST: + return shfile_access(&psh->fdtab, nm, F_OK) == 0; + case FILREG: + return S_ISREG(s.st_mode); + case FILDIR: + return S_ISDIR(s.st_mode); + case FILCDEV: +#ifdef S_ISCHR + return S_ISCHR(s.st_mode); +#else + return 0; +#endif + case FILBDEV: +#ifdef S_ISBLK + return S_ISBLK(s.st_mode); +#else + return 0; +#endif + case FILFIFO: +#ifdef S_ISFIFO + return S_ISFIFO(s.st_mode); +#else + return 0; +#endif + case FILSOCK: +#ifdef S_ISSOCK + return S_ISSOCK(s.st_mode); +#else + return 0; +#endif + 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: +#ifdef S_ISVTX + return (s.st_mode & S_ISVTX) != 0; +#else + return 0; +#endif + case FILGZ: + return s.st_size > (off_t)0; + case FILUID: + return s.st_uid == sh_geteuid(psh); + case FILGID: + return s.st_gid == sh_getegid(psh); + default: + return 1; + } +} + +static enum token +t_lex(shinstance *psh, char *s) +{ + struct t_op const *op; + + op = ops; + + if (s == 0) { + psh->t_wp_op = NULL; + return EOI; + } + while (op->op_text) { + if (strcmp(s, op->op_text) == 0) { + if ((op->op_type == UNOP && isoperand(psh)) || + (op->op_num == LPAREN && *(psh->t_wp+1) == 0)) + break; + psh->t_wp_op = op; + return op->op_num; + } + op++; + } + psh->t_wp_op = NULL; + return OPERAND; +} + +static int +isoperand(shinstance *psh) +{ + struct t_op const *op; + char *s, *t; + + op = ops; + if ((s = *(psh->t_wp+1)) == 0) + return 1; + if ((t = *(psh->t_wp+2)) == 0) + return 0; + while (op->op_text) { + if (strcmp(s, op->op_text) == 0) + return op->op_type == BINOP && + (t[0] != ')' || t[1] != '\0'); + op++; + } + return 0; +} + +/* atoi with error detection */ +static int +getn(shinstance *psh, const char *s) +{ + char *p; + long r; + + errno = 0; + r = strtol(s, &p, 10); + + if (errno != 0) + error(psh, "%s: out of range", s); + + while (isspace((unsigned char)*p)) + p++; + + if (*p) + error(psh, "%s: bad number", s); + + return (int) r; +} + +static int +newerf(shinstance *psh, const char *f1, const char *f2) +{ + struct stat b1, b2; + + return (shfile_stat(&psh->fdtab, f1, &b1) == 0 && + shfile_stat(&psh->fdtab, f2, &b2) == 0 && + b1.st_mtime > b2.st_mtime); +} + +static int +olderf(shinstance *psh, const char *f1, const char *f2) +{ + struct stat b1, b2; + + return (shfile_stat(&psh->fdtab, f1, &b1) == 0 && + shfile_stat(&psh->fdtab, f2, &b2) == 0 && + b1.st_mtime < b2.st_mtime); +} + +static int +equalf(shinstance *psh, const char *f1, const char *f2) +{ + struct stat b1, b2; + + return (shfile_stat(&psh->fdtab, f1, &b1) == 0 && + shfile_stat(&psh->fdtab, f2, &b2) == 0 && + b1.st_dev == b2.st_dev && + b1.st_ino == b2.st_ino); +} |